math.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /**
  2. * @license
  3. * Visual Blocks Language
  4. *
  5. * Copyright 2014 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 Dart for math blocks.
  22. * @author q.neutron@gmail.com (Quynh Neutron)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Dart.math');
  26. goog.require('Blockly.Dart');
  27. Blockly.Dart.addReservedWords('Math');
  28. Blockly.Dart['math_number'] = function(block) {
  29. // Numeric value.
  30. var code = parseFloat(block.getFieldValue('NUM'));
  31. var order;
  32. if (code == Infinity) {
  33. code = 'double.INFINITY';
  34. order = Blockly.Dart.ORDER_UNARY_POSTFIX;
  35. } else if (code == -Infinity) {
  36. code = '-double.INFINITY';
  37. order = Blockly.Dart.ORDER_UNARY_PREFIX;
  38. } else {
  39. // -4.abs() returns -4 in Dart due to strange order of operation choices.
  40. // -4 is actually an operator and a number. Reflect this in the order.
  41. order = code < 0 ?
  42. Blockly.Dart.ORDER_UNARY_PREFIX : Blockly.Dart.ORDER_ATOMIC;
  43. }
  44. return [code, order];
  45. };
  46. Blockly.Dart['math_arithmetic'] = function(block) {
  47. // Basic arithmetic operators, and power.
  48. var OPERATORS = {
  49. 'ADD': [' + ', Blockly.Dart.ORDER_ADDITIVE],
  50. 'MINUS': [' - ', Blockly.Dart.ORDER_ADDITIVE],
  51. 'MULTIPLY': [' * ', Blockly.Dart.ORDER_MULTIPLICATIVE],
  52. 'DIVIDE': [' / ', Blockly.Dart.ORDER_MULTIPLICATIVE],
  53. 'POWER': [null, Blockly.Dart.ORDER_NONE] // Handle power separately.
  54. };
  55. var tuple = OPERATORS[block.getFieldValue('OP')];
  56. var operator = tuple[0];
  57. var order = tuple[1];
  58. var argument0 = Blockly.Dart.valueToCode(block, 'A', order) || '0';
  59. var argument1 = Blockly.Dart.valueToCode(block, 'B', order) || '0';
  60. var code;
  61. // Power in Dart requires a special case since it has no operator.
  62. if (!operator) {
  63. Blockly.Dart.definitions_['import_dart_math'] =
  64. 'import \'dart:math\' as Math;';
  65. code = 'Math.pow(' + argument0 + ', ' + argument1 + ')';
  66. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  67. }
  68. code = argument0 + operator + argument1;
  69. return [code, order];
  70. };
  71. Blockly.Dart['math_single'] = function(block) {
  72. // Math operators with single operand.
  73. var operator = block.getFieldValue('OP');
  74. var code;
  75. var arg;
  76. if (operator == 'NEG') {
  77. // Negation is a special case given its different operator precedence.
  78. arg = Blockly.Dart.valueToCode(block, 'NUM',
  79. Blockly.Dart.ORDER_UNARY_PREFIX) || '0';
  80. if (arg[0] == '-') {
  81. // --3 is not legal in Dart.
  82. arg = ' ' + arg;
  83. }
  84. code = '-' + arg;
  85. return [code, Blockly.Dart.ORDER_UNARY_PREFIX];
  86. }
  87. Blockly.Dart.definitions_['import_dart_math'] =
  88. 'import \'dart:math\' as Math;';
  89. if (operator == 'ABS' || operator.substring(0, 5) == 'ROUND') {
  90. arg = Blockly.Dart.valueToCode(block, 'NUM',
  91. Blockly.Dart.ORDER_UNARY_POSTFIX) || '0';
  92. } else if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
  93. arg = Blockly.Dart.valueToCode(block, 'NUM',
  94. Blockly.Dart.ORDER_MULTIPLICATIVE) || '0';
  95. } else {
  96. arg = Blockly.Dart.valueToCode(block, 'NUM',
  97. Blockly.Dart.ORDER_NONE) || '0';
  98. }
  99. // First, handle cases which generate values that don't need parentheses
  100. // wrapping the code.
  101. switch (operator) {
  102. case 'ABS':
  103. code = arg + '.abs()';
  104. break;
  105. case 'ROOT':
  106. code = 'Math.sqrt(' + arg + ')';
  107. break;
  108. case 'LN':
  109. code = 'Math.log(' + arg + ')';
  110. break;
  111. case 'EXP':
  112. code = 'Math.exp(' + arg + ')';
  113. break;
  114. case 'POW10':
  115. code = 'Math.pow(10,' + arg + ')';
  116. break;
  117. case 'ROUND':
  118. code = arg + '.round()';
  119. break;
  120. case 'ROUNDUP':
  121. code = arg + '.ceil()';
  122. break;
  123. case 'ROUNDDOWN':
  124. code = arg + '.floor()';
  125. break;
  126. case 'SIN':
  127. code = 'Math.sin(' + arg + ' / 180 * Math.PI)';
  128. break;
  129. case 'COS':
  130. code = 'Math.cos(' + arg + ' / 180 * Math.PI)';
  131. break;
  132. case 'TAN':
  133. code = 'Math.tan(' + arg + ' / 180 * Math.PI)';
  134. break;
  135. }
  136. if (code) {
  137. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  138. }
  139. // Second, handle cases which generate values that may need parentheses
  140. // wrapping the code.
  141. switch (operator) {
  142. case 'LOG10':
  143. code = 'Math.log(' + arg + ') / Math.log(10)';
  144. break;
  145. case 'ASIN':
  146. code = 'Math.asin(' + arg + ') / Math.PI * 180';
  147. break;
  148. case 'ACOS':
  149. code = 'Math.acos(' + arg + ') / Math.PI * 180';
  150. break;
  151. case 'ATAN':
  152. code = 'Math.atan(' + arg + ') / Math.PI * 180';
  153. break;
  154. default:
  155. throw 'Unknown math operator: ' + operator;
  156. }
  157. return [code, Blockly.Dart.ORDER_MULTIPLICATIVE];
  158. };
  159. Blockly.Dart['math_constant'] = function(block) {
  160. // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
  161. var CONSTANTS = {
  162. 'PI': ['Math.PI', Blockly.Dart.ORDER_UNARY_POSTFIX],
  163. 'E': ['Math.E', Blockly.Dart.ORDER_UNARY_POSTFIX],
  164. 'GOLDEN_RATIO':
  165. ['(1 + Math.sqrt(5)) / 2', Blockly.Dart.ORDER_MULTIPLICATIVE],
  166. 'SQRT2': ['Math.SQRT2', Blockly.Dart.ORDER_UNARY_POSTFIX],
  167. 'SQRT1_2': ['Math.SQRT1_2', Blockly.Dart.ORDER_UNARY_POSTFIX],
  168. 'INFINITY': ['double.INFINITY', Blockly.Dart.ORDER_ATOMIC]
  169. };
  170. var constant = block.getFieldValue('CONSTANT');
  171. if (constant != 'INFINITY') {
  172. Blockly.Dart.definitions_['import_dart_math'] =
  173. 'import \'dart:math\' as Math;';
  174. }
  175. return CONSTANTS[constant];
  176. };
  177. Blockly.Dart['math_number_property'] = function(block) {
  178. // Check if a number is even, odd, prime, whole, positive, or negative
  179. // or if it is divisible by certain number. Returns true or false.
  180. var number_to_check = Blockly.Dart.valueToCode(block, 'NUMBER_TO_CHECK',
  181. Blockly.Dart.ORDER_MULTIPLICATIVE);
  182. if (!number_to_check) {
  183. return ['false', Blockly.Python.ORDER_ATOMIC];
  184. }
  185. var dropdown_property = block.getFieldValue('PROPERTY');
  186. var code;
  187. if (dropdown_property == 'PRIME') {
  188. // Prime is a special case as it is not a one-liner test.
  189. Blockly.Dart.definitions_['import_dart_math'] =
  190. 'import \'dart:math\' as Math;';
  191. var functionName = Blockly.Dart.provideFunction_(
  192. 'math_isPrime',
  193. [ 'bool ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {',
  194. ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
  195. ' if (n == 2 || n == 3) {',
  196. ' return true;',
  197. ' }',
  198. ' // False if n is null, negative, is 1, or not whole.',
  199. ' // And false if n is divisible by 2 or 3.',
  200. ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' +
  201. ' n % 3 == 0) {',
  202. ' return false;',
  203. ' }',
  204. ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).',
  205. ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {',
  206. ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {',
  207. ' return false;',
  208. ' }',
  209. ' }',
  210. ' return true;',
  211. '}']);
  212. code = functionName + '(' + number_to_check + ')';
  213. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  214. }
  215. switch (dropdown_property) {
  216. case 'EVEN':
  217. code = number_to_check + ' % 2 == 0';
  218. break;
  219. case 'ODD':
  220. code = number_to_check + ' % 2 == 1';
  221. break;
  222. case 'WHOLE':
  223. code = number_to_check + ' % 1 == 0';
  224. break;
  225. case 'POSITIVE':
  226. code = number_to_check + ' > 0';
  227. break;
  228. case 'NEGATIVE':
  229. code = number_to_check + ' < 0';
  230. break;
  231. case 'DIVISIBLE_BY':
  232. var divisor = Blockly.Dart.valueToCode(block, 'DIVISOR',
  233. Blockly.Dart.ORDER_MULTIPLICATIVE);
  234. if (!divisor) {
  235. return ['false', Blockly.Python.ORDER_ATOMIC];
  236. }
  237. code = number_to_check + ' % ' + divisor + ' == 0';
  238. break;
  239. }
  240. return [code, Blockly.Dart.ORDER_EQUALITY];
  241. };
  242. Blockly.Dart['math_change'] = function(block) {
  243. // Add to a variable in place.
  244. var argument0 = Blockly.Dart.valueToCode(block, 'DELTA',
  245. Blockly.Dart.ORDER_ADDITIVE) || '0';
  246. var varName = Blockly.Dart.variableDB_.getName(block.getFieldValue('VAR'),
  247. Blockly.Variables.NAME_TYPE);
  248. return varName + ' = (' + varName + ' is num ? ' + varName + ' : 0) + ' +
  249. argument0 + ';\n';
  250. };
  251. // Rounding functions have a single operand.
  252. Blockly.Dart['math_round'] = Blockly.Dart['math_single'];
  253. // Trigonometry functions have a single operand.
  254. Blockly.Dart['math_trig'] = Blockly.Dart['math_single'];
  255. Blockly.Dart['math_on_list'] = function(block) {
  256. // Math functions for lists.
  257. var func = block.getFieldValue('OP');
  258. var list = Blockly.Dart.valueToCode(block, 'LIST',
  259. Blockly.Dart.ORDER_NONE) || '[]';
  260. var code;
  261. switch (func) {
  262. case 'SUM':
  263. var functionName = Blockly.Dart.provideFunction_(
  264. 'math_sum',
  265. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  266. '(List myList) {',
  267. ' num sumVal = 0;',
  268. ' myList.forEach((num entry) {sumVal += entry;});',
  269. ' return sumVal;',
  270. '}']);
  271. code = functionName + '(' + list + ')';
  272. break;
  273. case 'MIN':
  274. Blockly.Dart.definitions_['import_dart_math'] =
  275. 'import \'dart:math\' as Math;';
  276. var functionName = Blockly.Dart.provideFunction_(
  277. 'math_min',
  278. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  279. '(List myList) {',
  280. ' if (myList.isEmpty) return null;',
  281. ' num minVal = myList[0];',
  282. ' myList.forEach((num entry) ' +
  283. '{minVal = Math.min(minVal, entry);});',
  284. ' return minVal;',
  285. '}']);
  286. code = functionName + '(' + list + ')';
  287. break;
  288. case 'MAX':
  289. Blockly.Dart.definitions_['import_dart_math'] =
  290. 'import \'dart:math\' as Math;';
  291. var functionName = Blockly.Dart.provideFunction_(
  292. 'math_max',
  293. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  294. '(List myList) {',
  295. ' if (myList.isEmpty) return null;',
  296. ' num maxVal = myList[0];',
  297. ' myList.forEach((num entry) ' +
  298. '{maxVal = Math.max(maxVal, entry);});',
  299. ' return maxVal;',
  300. '}']);
  301. code = functionName + '(' + list + ')';
  302. break;
  303. case 'AVERAGE':
  304. // This operation exclude null and values that are not int or float:
  305. // math_mean([null,null,"aString",1,9]) == 5.0.
  306. var functionName = Blockly.Dart.provideFunction_(
  307. 'math_average',
  308. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  309. '(List myList) {',
  310. ' // First filter list for numbers only.',
  311. ' List localList = new List.from(myList);',
  312. ' localList.removeMatching((a) => a is! num);',
  313. ' if (localList.isEmpty) return null;',
  314. ' num sumVal = 0;',
  315. ' localList.forEach((num entry) {sumVal += entry;});',
  316. ' return sumVal / localList.length;',
  317. '}']);
  318. code = functionName + '(' + list + ')';
  319. break;
  320. case 'MEDIAN':
  321. var functionName = Blockly.Dart.provideFunction_(
  322. 'math_median',
  323. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  324. '(List myList) {',
  325. ' // First filter list for numbers only, then sort, ' +
  326. 'then return middle value',
  327. ' // or the average of two middle values if list has an ' +
  328. 'even number of elements.',
  329. ' List localList = new List.from(myList);',
  330. ' localList.removeMatching((a) => a is! num);',
  331. ' if (localList.isEmpty) return null;',
  332. ' localList.sort((a, b) => (a - b));',
  333. ' int index = localList.length ~/ 2;',
  334. ' if (localList.length % 2 == 1) {',
  335. ' return localList[index];',
  336. ' } else {',
  337. ' return (localList[index - 1] + localList[index]) / 2;',
  338. ' }',
  339. '}']);
  340. code = functionName + '(' + list + ')';
  341. break;
  342. case 'MODE':
  343. Blockly.Dart.definitions_['import_dart_math'] =
  344. 'import \'dart:math\' as Math;';
  345. // As a list of numbers can contain more than one mode,
  346. // the returned result is provided as an array.
  347. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1].
  348. var functionName = Blockly.Dart.provideFunction_(
  349. 'math_modes',
  350. [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  351. '(List values) {',
  352. ' List modes = [];',
  353. ' List counts = [];',
  354. ' int maxCount = 0;',
  355. ' for (int i = 0; i < values.length; i++) {',
  356. ' var value = values[i];',
  357. ' bool found = false;',
  358. ' int thisCount;',
  359. ' for (int j = 0; j < counts.length; j++) {',
  360. ' if (counts[j][0] == value) {',
  361. ' thisCount = ++counts[j][1];',
  362. ' found = true;',
  363. ' break;',
  364. ' }',
  365. ' }',
  366. ' if (!found) {',
  367. ' counts.add([value, 1]);',
  368. ' thisCount = 1;',
  369. ' }',
  370. ' maxCount = Math.max(thisCount, maxCount);',
  371. ' }',
  372. ' for (int j = 0; j < counts.length; j++) {',
  373. ' if (counts[j][1] == maxCount) {',
  374. ' modes.add(counts[j][0]);',
  375. ' }',
  376. ' }',
  377. ' return modes;',
  378. '}']);
  379. code = functionName + '(' + list + ')';
  380. break;
  381. case 'STD_DEV':
  382. Blockly.Dart.definitions_['import_dart_math'] =
  383. 'import \'dart:math\' as Math;';
  384. var functionName = Blockly.Dart.provideFunction_(
  385. 'math_standard_deviation',
  386. [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  387. '(List myList) {',
  388. ' // First filter list for numbers only.',
  389. ' List numbers = new List.from(myList);',
  390. ' numbers.removeMatching((a) => a is! num);',
  391. ' if (numbers.isEmpty) return null;',
  392. ' num n = numbers.length;',
  393. ' num sum = 0;',
  394. ' numbers.forEach((x) => sum += x);',
  395. ' num mean = sum / n;',
  396. ' num sumSquare = 0;',
  397. ' numbers.forEach((x) => sumSquare += ' +
  398. 'Math.pow(x - mean, 2));',
  399. ' return Math.sqrt(sumSquare / n);',
  400. '}']);
  401. code = functionName + '(' + list + ')';
  402. break;
  403. case 'RANDOM':
  404. Blockly.Dart.definitions_['import_dart_math'] =
  405. 'import \'dart:math\' as Math;';
  406. var functionName = Blockly.Dart.provideFunction_(
  407. 'math_random_item',
  408. [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  409. '(List myList) {',
  410. ' int x = new Math.Random().nextInt(myList.length);',
  411. ' return myList[x];',
  412. '}']);
  413. code = functionName + '(' + list + ')';
  414. break;
  415. default:
  416. throw 'Unknown operator: ' + func;
  417. }
  418. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  419. };
  420. Blockly.Dart['math_modulo'] = function(block) {
  421. // Remainder computation.
  422. var argument0 = Blockly.Dart.valueToCode(block, 'DIVIDEND',
  423. Blockly.Dart.ORDER_MULTIPLICATIVE) || '0';
  424. var argument1 = Blockly.Dart.valueToCode(block, 'DIVISOR',
  425. Blockly.Dart.ORDER_MULTIPLICATIVE) || '0';
  426. var code = argument0 + ' % ' + argument1;
  427. return [code, Blockly.Dart.ORDER_MULTIPLICATIVE];
  428. };
  429. Blockly.Dart['math_constrain'] = function(block) {
  430. // Constrain a number between two limits.
  431. Blockly.Dart.definitions_['import_dart_math'] =
  432. 'import \'dart:math\' as Math;';
  433. var argument0 = Blockly.Dart.valueToCode(block, 'VALUE',
  434. Blockly.Dart.ORDER_NONE) || '0';
  435. var argument1 = Blockly.Dart.valueToCode(block, 'LOW',
  436. Blockly.Dart.ORDER_NONE) || '0';
  437. var argument2 = Blockly.Dart.valueToCode(block, 'HIGH',
  438. Blockly.Dart.ORDER_NONE) || 'double.INFINITY';
  439. var code = 'Math.min(Math.max(' + argument0 + ', ' + argument1 + '), ' +
  440. argument2 + ')';
  441. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  442. };
  443. Blockly.Dart['math_random_int'] = function(block) {
  444. // Random integer between [X] and [Y].
  445. Blockly.Dart.definitions_['import_dart_math'] =
  446. 'import \'dart:math\' as Math;';
  447. var argument0 = Blockly.Dart.valueToCode(block, 'FROM',
  448. Blockly.Dart.ORDER_NONE) || '0';
  449. var argument1 = Blockly.Dart.valueToCode(block, 'TO',
  450. Blockly.Dart.ORDER_NONE) || '0';
  451. var functionName = Blockly.Dart.provideFunction_(
  452. 'math_random_int',
  453. [ 'int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {',
  454. ' if (a > b) {',
  455. ' // Swap a and b to ensure a is smaller.',
  456. ' num c = a;',
  457. ' a = b;',
  458. ' b = c;',
  459. ' }',
  460. ' return new Math.Random().nextInt(b - a + 1) + a;',
  461. '}']);
  462. var code = functionName + '(' + argument0 + ', ' + argument1 + ')';
  463. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  464. };
  465. Blockly.Dart['math_random_float'] = function(block) {
  466. // Random fraction between 0 and 1.
  467. Blockly.Dart.definitions_['import_dart_math'] =
  468. 'import \'dart:math\' as Math;';
  469. return ['new Math.Random().nextDouble()', Blockly.Dart.ORDER_UNARY_POSTFIX];
  470. };