math.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /**
  2. * @license
  3. * Visual Blocks Language
  4. *
  5. * Copyright 2015 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Generating PHP for math blocks.
  22. * @author daarond@gmail.com (Daaron Dwyer)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.PHP.math');
  26. goog.require('Blockly.PHP');
  27. Blockly.PHP['math_number'] = function(block) {
  28. // Numeric value.
  29. var code = parseFloat(block.getFieldValue('NUM'));
  30. if (code == Infinity) {
  31. code = 'INF';
  32. } else if (code == -Infinity) {
  33. code = '-INF';
  34. }
  35. return [code, Blockly.PHP.ORDER_ATOMIC];
  36. };
  37. Blockly.PHP['math_arithmetic'] = function(block) {
  38. // Basic arithmetic operators, and power.
  39. var OPERATORS = {
  40. 'ADD': [' + ', Blockly.PHP.ORDER_ADDITION],
  41. 'MINUS': [' - ', Blockly.PHP.ORDER_SUBTRACTION],
  42. 'MULTIPLY': [' * ', Blockly.PHP.ORDER_MULTIPLICATION],
  43. 'DIVIDE': [' / ', Blockly.PHP.ORDER_DIVISION],
  44. 'POWER': [' ** ', Blockly.PHP.ORDER_POWER]
  45. };
  46. var tuple = OPERATORS[block.getFieldValue('OP')];
  47. var operator = tuple[0];
  48. var order = tuple[1];
  49. var argument0 = Blockly.PHP.valueToCode(block, 'A', order) || '0';
  50. var argument1 = Blockly.PHP.valueToCode(block, 'B', order) || '0';
  51. var code = argument0 + operator + argument1;
  52. return [code, order];
  53. };
  54. Blockly.PHP['math_single'] = function(block) {
  55. // Math operators with single operand.
  56. var operator = block.getFieldValue('OP');
  57. var code;
  58. var arg;
  59. if (operator == 'NEG') {
  60. // Negation is a special case given its different operator precedence.
  61. arg = Blockly.PHP.valueToCode(block, 'NUM',
  62. Blockly.PHP.ORDER_UNARY_NEGATION) || '0';
  63. if (arg[0] == '-') {
  64. // --3 is not legal in JS.
  65. arg = ' ' + arg;
  66. }
  67. code = '-' + arg;
  68. return [code, Blockly.PHP.ORDER_UNARY_NEGATION];
  69. }
  70. if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
  71. arg = Blockly.PHP.valueToCode(block, 'NUM',
  72. Blockly.PHP.ORDER_DIVISION) || '0';
  73. } else {
  74. arg = Blockly.PHP.valueToCode(block, 'NUM',
  75. Blockly.PHP.ORDER_NONE) || '0';
  76. }
  77. // First, handle cases which generate values that don't need parentheses
  78. // wrapping the code.
  79. switch (operator) {
  80. case 'ABS':
  81. code = 'abs(' + arg + ')';
  82. break;
  83. case 'ROOT':
  84. code = 'sqrt(' + arg + ')';
  85. break;
  86. case 'LN':
  87. code = 'log(' + arg + ')';
  88. break;
  89. case 'EXP':
  90. code = 'exp(' + arg + ')';
  91. break;
  92. case 'POW10':
  93. code = 'pow(10,' + arg + ')';
  94. break;
  95. case 'ROUND':
  96. code = 'round(' + arg + ')';
  97. break;
  98. case 'ROUNDUP':
  99. code = 'ceil(' + arg + ')';
  100. break;
  101. case 'ROUNDDOWN':
  102. code = 'floor(' + arg + ')';
  103. break;
  104. case 'SIN':
  105. code = 'sin(' + arg + ' / 180 * pi())';
  106. break;
  107. case 'COS':
  108. code = 'cos(' + arg + ' / 180 * pi())';
  109. break;
  110. case 'TAN':
  111. code = 'tan(' + arg + ' / 180 * pi())';
  112. break;
  113. }
  114. if (code) {
  115. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  116. }
  117. // Second, handle cases which generate values that may need parentheses
  118. // wrapping the code.
  119. switch (operator) {
  120. case 'LOG10':
  121. code = 'log(' + arg + ') / log(10)';
  122. break;
  123. case 'ASIN':
  124. code = 'asin(' + arg + ') / pi() * 180';
  125. break;
  126. case 'ACOS':
  127. code = 'acos(' + arg + ') / pi() * 180';
  128. break;
  129. case 'ATAN':
  130. code = 'atan(' + arg + ') / pi() * 180';
  131. break;
  132. default:
  133. throw 'Unknown math operator: ' + operator;
  134. }
  135. return [code, Blockly.PHP.ORDER_DIVISION];
  136. };
  137. Blockly.PHP['math_constant'] = function(block) {
  138. // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
  139. var CONSTANTS = {
  140. 'PI': ['M_PI', Blockly.PHP.ORDER_ATOMIC],
  141. 'E': ['M_E', Blockly.PHP.ORDER_ATOMIC],
  142. 'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', Blockly.PHP.ORDER_DIVISION],
  143. 'SQRT2': ['M_SQRT2', Blockly.PHP.ORDER_ATOMIC],
  144. 'SQRT1_2': ['M_SQRT1_2', Blockly.PHP.ORDER_ATOMIC],
  145. 'INFINITY': ['INF', Blockly.PHP.ORDER_ATOMIC]
  146. };
  147. return CONSTANTS[block.getFieldValue('CONSTANT')];
  148. };
  149. Blockly.PHP['math_number_property'] = function(block) {
  150. // Check if a number is even, odd, prime, whole, positive, or negative
  151. // or if it is divisible by certain number. Returns true or false.
  152. var number_to_check = Blockly.PHP.valueToCode(block, 'NUMBER_TO_CHECK',
  153. Blockly.PHP.ORDER_MODULUS) || '0';
  154. var dropdown_property = block.getFieldValue('PROPERTY');
  155. var code;
  156. if (dropdown_property == 'PRIME') {
  157. // Prime is a special case as it is not a one-liner test.
  158. var functionName = Blockly.PHP.provideFunction_(
  159. 'math_isPrime',
  160. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {',
  161. ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
  162. ' if ($n == 2 || $n == 3) {',
  163. ' return true;',
  164. ' }',
  165. ' // False if n is NaN, negative, is 1, or not whole.',
  166. ' // And false if n is divisible by 2 or 3.',
  167. ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' +
  168. ' $n % 3 == 0) {',
  169. ' return false;',
  170. ' }',
  171. ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).',
  172. ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {',
  173. ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {',
  174. ' return false;',
  175. ' }',
  176. ' }',
  177. ' return true;',
  178. '}']);
  179. code = functionName + '(' + number_to_check + ')';
  180. return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
  181. }
  182. switch (dropdown_property) {
  183. case 'EVEN':
  184. code = number_to_check + ' % 2 == 0';
  185. break;
  186. case 'ODD':
  187. code = number_to_check + ' % 2 == 1';
  188. break;
  189. case 'WHOLE':
  190. code = 'is_int(' + number_to_check + ')';
  191. break;
  192. case 'POSITIVE':
  193. code = number_to_check + ' > 0';
  194. break;
  195. case 'NEGATIVE':
  196. code = number_to_check + ' < 0';
  197. break;
  198. case 'DIVISIBLE_BY':
  199. var divisor = Blockly.PHP.valueToCode(block, 'DIVISOR',
  200. Blockly.PHP.ORDER_MODULUS) || '0';
  201. code = number_to_check + ' % ' + divisor + ' == 0';
  202. break;
  203. }
  204. return [code, Blockly.PHP.ORDER_EQUALITY];
  205. };
  206. Blockly.PHP['math_change'] = function(block) {
  207. // Add to a variable in place.
  208. var argument0 = Blockly.PHP.valueToCode(block, 'DELTA',
  209. Blockly.PHP.ORDER_ADDITION) || '0';
  210. var varName = Blockly.PHP.variableDB_.getName(
  211. block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
  212. return varName + ' += ' + argument0 + ';\n';
  213. };
  214. // Rounding functions have a single operand.
  215. Blockly.PHP['math_round'] = Blockly.PHP['math_single'];
  216. // Trigonometry functions have a single operand.
  217. Blockly.PHP['math_trig'] = Blockly.PHP['math_single'];
  218. Blockly.PHP['math_on_list'] = function(block) {
  219. // Math functions for lists.
  220. var func = block.getFieldValue('OP');
  221. var list, code;
  222. switch (func) {
  223. case 'SUM':
  224. list = Blockly.PHP.valueToCode(block, 'LIST',
  225. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  226. code = 'array_sum(' + list + ')';
  227. break;
  228. case 'MIN':
  229. list = Blockly.PHP.valueToCode(block, 'LIST',
  230. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  231. code = 'min(' + list + ')';
  232. break;
  233. case 'MAX':
  234. list = Blockly.PHP.valueToCode(block, 'LIST',
  235. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  236. code = 'max(' + list + ')';
  237. break;
  238. case 'AVERAGE':
  239. var functionName = Blockly.PHP.provideFunction_(
  240. 'math_mean',
  241. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  242. '($myList) {',
  243. ' return array_sum($myList) / count($myList);',
  244. '}']);
  245. list = Blockly.PHP.valueToCode(block, 'LIST',
  246. Blockly.PHP.ORDER_NONE) || 'array()';
  247. code = functionName + '(' + list + ')';
  248. break;
  249. case 'MEDIAN':
  250. var functionName = Blockly.PHP.provideFunction_(
  251. 'math_median',
  252. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  253. '($arr) {',
  254. ' sort($arr,SORT_NUMERIC);',
  255. ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ',
  256. ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2)' +
  257. ' - 1]) / 2;',
  258. '}']);
  259. list = Blockly.PHP.valueToCode(block, 'LIST',
  260. Blockly.PHP.ORDER_NONE) || '[]';
  261. code = functionName + '(' + list + ')';
  262. break;
  263. case 'MODE':
  264. // As a list of numbers can contain more than one mode,
  265. // the returned result is provided as an array.
  266. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1].
  267. var functionName = Blockly.PHP.provideFunction_(
  268. 'math_modes',
  269. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  270. '($values) {',
  271. ' if (empty($values)) return array();',
  272. ' $counts = array_count_values($values);',
  273. ' arsort($counts); // Sort counts in descending order',
  274. ' $modes = array_keys($counts, current($counts), true);',
  275. ' return $modes;',
  276. '}']);
  277. list = Blockly.PHP.valueToCode(block, 'LIST',
  278. Blockly.PHP.ORDER_NONE) || '[]';
  279. code = functionName + '(' + list + ')';
  280. break;
  281. case 'STD_DEV':
  282. var functionName = Blockly.PHP.provideFunction_(
  283. 'math_standard_deviation',
  284. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  285. '($numbers) {',
  286. ' $n = count($numbers);',
  287. ' if (!$n) return null;',
  288. ' $mean = array_sum($numbers) / count($numbers);',
  289. ' foreach($numbers as $key => $num) $devs[$key] = ' +
  290. 'pow($num - $mean, 2);',
  291. ' return sqrt(array_sum($devs) / (count($devs) - 1));',
  292. '}']);
  293. list = Blockly.PHP.valueToCode(block, 'LIST',
  294. Blockly.PHP.ORDER_NONE) || '[]';
  295. code = functionName + '(' + list + ')';
  296. break;
  297. case 'RANDOM':
  298. var functionName = Blockly.PHP.provideFunction_(
  299. 'math_random_list',
  300. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  301. '($list) {',
  302. ' $x = rand(0, count($list)-1);',
  303. ' return $list[$x];',
  304. '}']);
  305. list = Blockly.PHP.valueToCode(block, 'LIST',
  306. Blockly.PHP.ORDER_NONE) || '[]';
  307. code = functionName + '(' + list + ')';
  308. break;
  309. default:
  310. throw 'Unknown operator: ' + func;
  311. }
  312. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  313. };
  314. Blockly.PHP['math_modulo'] = function(block) {
  315. // Remainder computation.
  316. var argument0 = Blockly.PHP.valueToCode(block, 'DIVIDEND',
  317. Blockly.PHP.ORDER_MODULUS) || '0';
  318. var argument1 = Blockly.PHP.valueToCode(block, 'DIVISOR',
  319. Blockly.PHP.ORDER_MODULUS) || '0';
  320. var code = argument0 + ' % ' + argument1;
  321. return [code, Blockly.PHP.ORDER_MODULUS];
  322. };
  323. Blockly.PHP['math_constrain'] = function(block) {
  324. // Constrain a number between two limits.
  325. var argument0 = Blockly.PHP.valueToCode(block, 'VALUE',
  326. Blockly.PHP.ORDER_COMMA) || '0';
  327. var argument1 = Blockly.PHP.valueToCode(block, 'LOW',
  328. Blockly.PHP.ORDER_COMMA) || '0';
  329. var argument2 = Blockly.PHP.valueToCode(block, 'HIGH',
  330. Blockly.PHP.ORDER_COMMA) || 'Infinity';
  331. var code = 'min(max(' + argument0 + ', ' + argument1 + '), ' +
  332. argument2 + ')';
  333. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  334. };
  335. Blockly.PHP['math_random_int'] = function(block) {
  336. // Random integer between [X] and [Y].
  337. var argument0 = Blockly.PHP.valueToCode(block, 'FROM',
  338. Blockly.PHP.ORDER_COMMA) || '0';
  339. var argument1 = Blockly.PHP.valueToCode(block, 'TO',
  340. Blockly.PHP.ORDER_COMMA) || '0';
  341. var functionName = Blockly.PHP.provideFunction_(
  342. 'math_random_int',
  343. ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  344. '($a, $b) {',
  345. ' if ($a > $b) {',
  346. ' return rand($b, $a);',
  347. ' }',
  348. ' return rand($a, $b);',
  349. '}']);
  350. var code = functionName + '(' + argument0 + ', ' + argument1 + ')';
  351. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  352. };
  353. Blockly.PHP['math_random_float'] = function(block) {
  354. // Random fraction between 0 and 1.
  355. return ['(float)rand()/(float)getrandmax()', Blockly.PHP.ORDER_FUNCTION_CALL];
  356. };