math.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /**
  2. * @license
  3. * Visual Blocks Language
  4. *
  5. * Copyright 2016 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 Lua for math blocks.
  22. * @author rodrigoq@google.com (Rodrigo Queiro)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Lua.math');
  26. goog.require('Blockly.Lua');
  27. Blockly.Lua['math_number'] = function(block) {
  28. // Numeric value.
  29. var code = parseFloat(block.getFieldValue('NUM'));
  30. var order = code < 0 ? Blockly.Lua.ORDER_UNARY :
  31. Blockly.Lua.ORDER_ATOMIC;
  32. return [code, order];
  33. };
  34. Blockly.Lua['math_arithmetic'] = function(block) {
  35. // Basic arithmetic operators, and power.
  36. var OPERATORS = {
  37. ADD: [' + ', Blockly.Lua.ORDER_ADDITIVE],
  38. MINUS: [' - ', Blockly.Lua.ORDER_ADDITIVE],
  39. MULTIPLY: [' * ', Blockly.Lua.ORDER_MULTIPLICATIVE],
  40. DIVIDE: [' / ', Blockly.Lua.ORDER_MULTIPLICATIVE],
  41. POWER: [' ^ ', Blockly.Lua.ORDER_EXPONENTIATION]
  42. };
  43. var tuple = OPERATORS[block.getFieldValue('OP')];
  44. var operator = tuple[0];
  45. var order = tuple[1];
  46. var argument0 = Blockly.Lua.valueToCode(block, 'A', order) || '0';
  47. var argument1 = Blockly.Lua.valueToCode(block, 'B', order) || '0';
  48. var code = argument0 + operator + argument1;
  49. return [code, order];
  50. };
  51. Blockly.Lua['math_single'] = function(block) {
  52. // Math operators with single operand.
  53. var operator = block.getFieldValue('OP');
  54. var code;
  55. var arg;
  56. if (operator == 'NEG') {
  57. // Negation is a special case given its different operator precedence.
  58. arg = Blockly.Lua.valueToCode(block, 'NUM',
  59. Blockly.Lua.ORDER_UNARY) || '0';
  60. return ['-' + arg, Blockly.Lua.ORDER_UNARY];
  61. }
  62. if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
  63. arg = Blockly.Lua.valueToCode(block, 'NUM',
  64. Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
  65. } else {
  66. arg = Blockly.Lua.valueToCode(block, 'NUM',
  67. Blockly.Lua.ORDER_NONE) || '0';
  68. }
  69. switch (operator) {
  70. case 'ABS':
  71. code = 'math.abs(' + arg + ')';
  72. break;
  73. case 'ROOT':
  74. code = 'math.sqrt(' + arg + ')';
  75. break;
  76. case 'LN':
  77. code = 'math.log(' + arg + ')';
  78. break;
  79. case 'LOG10':
  80. code = 'math.log10(' + arg + ')';
  81. break;
  82. case 'EXP':
  83. code = 'math.exp(' + arg + ')';
  84. break;
  85. case 'POW10':
  86. code = 'math.pow(10,' + arg + ')';
  87. break;
  88. case 'ROUND':
  89. // This rounds up. Blockly does not specify rounding direction.
  90. code = 'math.floor(' + arg + ' + .5)';
  91. break;
  92. case 'ROUNDUP':
  93. code = 'math.ceil(' + arg + ')';
  94. break;
  95. case 'ROUNDDOWN':
  96. code = 'math.floor(' + arg + ')';
  97. break;
  98. case 'SIN':
  99. code = 'math.sin(math.rad(' + arg + '))';
  100. break;
  101. case 'COS':
  102. code = 'math.cos(math.rad(' + arg + '))';
  103. break;
  104. case 'TAN':
  105. code = 'math.tan(math.rad(' + arg + '))';
  106. break;
  107. case 'ASIN':
  108. code = 'math.deg(math.asin(' + arg + '))';
  109. break;
  110. case 'ACOS':
  111. code = 'math.deg(math.acos(' + arg + '))';
  112. break;
  113. case 'ATAN':
  114. code = 'math.deg(math.atan(' + arg + '))';
  115. break;
  116. default:
  117. throw 'Unknown math operator: ' + operator;
  118. }
  119. return [code, Blockly.Lua.ORDER_HIGH];
  120. };
  121. Blockly.Lua['math_constant'] = function(block) {
  122. // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
  123. var CONSTANTS = {
  124. PI: ['math.pi', Blockly.Lua.ORDER_HIGH],
  125. E: ['math.exp(1)', Blockly.Lua.ORDER_HIGH],
  126. GOLDEN_RATIO: ['(1 + math.sqrt(5)) / 2', Blockly.Lua.ORDER_MULTIPLICATIVE],
  127. SQRT2: ['math.sqrt(2)', Blockly.Lua.ORDER_HIGH],
  128. SQRT1_2: ['math.sqrt(1 / 2)', Blockly.Lua.ORDER_HIGH],
  129. INFINITY: ['math.huge', Blockly.Lua.ORDER_HIGH]
  130. };
  131. return CONSTANTS[block.getFieldValue('CONSTANT')];
  132. };
  133. Blockly.Lua['math_number_property'] = function(block) {
  134. // Check if a number is even, odd, prime, whole, positive, or negative
  135. // or if it is divisible by certain number. Returns true or false.
  136. var number_to_check = Blockly.Lua.valueToCode(block, 'NUMBER_TO_CHECK',
  137. Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
  138. var dropdown_property = block.getFieldValue('PROPERTY');
  139. var code;
  140. if (dropdown_property == 'PRIME') {
  141. // Prime is a special case as it is not a one-liner test.
  142. var functionName = Blockly.Lua.provideFunction_(
  143. 'math_isPrime',
  144. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(n)',
  145. ' -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
  146. ' if n == 2 or n == 3 then',
  147. ' return true',
  148. ' end',
  149. ' -- False if n is NaN, negative, is 1, or not whole.',
  150. ' -- And false if n is divisible by 2 or 3.',
  151. ' if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then',
  152. ' return false',
  153. ' end',
  154. ' -- Check all the numbers of form 6k +/- 1, up to sqrt(n).',
  155. ' for x = 6, math.sqrt(n) + 1.5, 6 do',
  156. ' if n % (x - 1) == 0 or n % (x + 1) == 0 then',
  157. ' return false',
  158. ' end',
  159. ' end',
  160. ' return true',
  161. 'end']);
  162. code = functionName + '(' + number_to_check + ')';
  163. return [code, Blockly.Lua.ORDER_HIGH];
  164. }
  165. switch (dropdown_property) {
  166. case 'EVEN':
  167. code = number_to_check + ' % 2 == 0';
  168. break;
  169. case 'ODD':
  170. code = number_to_check + ' % 2 == 1';
  171. break;
  172. case 'WHOLE':
  173. code = number_to_check + ' % 1 == 0';
  174. break;
  175. case 'POSITIVE':
  176. code = number_to_check + ' > 0';
  177. break;
  178. case 'NEGATIVE':
  179. code = number_to_check + ' < 0';
  180. break;
  181. case 'DIVISIBLE_BY':
  182. var divisor = Blockly.Lua.valueToCode(block, 'DIVISOR',
  183. Blockly.Lua.ORDER_MULTIPLICATIVE);
  184. // If 'divisor' is some code that evals to 0, Lua will produce a nan.
  185. // Let's produce nil if we can determine this at compile-time.
  186. if (!divisor || divisor == '0') {
  187. return ['nil', Blockly.Lua.ORDER_ATOMIC];
  188. }
  189. // The normal trick to implement ?: with and/or doesn't work here:
  190. // divisor == 0 and nil or number_to_check % divisor == 0
  191. // because nil is false, so allow a runtime failure. :-(
  192. code = number_to_check + ' % ' + divisor + ' == 0';
  193. break;
  194. }
  195. return [code, Blockly.Lua.ORDER_RELATIONAL];
  196. };
  197. Blockly.Lua['math_change'] = function(block) {
  198. // Add to a variable in place.
  199. var argument0 = Blockly.Lua.valueToCode(block, 'DELTA',
  200. Blockly.Lua.ORDER_ADDITIVE) || '0';
  201. var varName = Blockly.Lua.variableDB_.getName(
  202. block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
  203. return varName + ' = ' + varName + ' + ' + argument0 + '\n';
  204. };
  205. // Rounding functions have a single operand.
  206. Blockly.Lua['math_round'] = Blockly.Lua['math_single'];
  207. // Trigonometry functions have a single operand.
  208. Blockly.Lua['math_trig'] = Blockly.Lua['math_single'];
  209. Blockly.Lua['math_on_list'] = function(block) {
  210. // Math functions for lists.
  211. var func = block.getFieldValue('OP');
  212. var list = Blockly.Lua.valueToCode(block, 'LIST',
  213. Blockly.Lua.ORDER_NONE) || '{}';
  214. var functionName;
  215. // Functions needed in more than one case.
  216. function provideSum() {
  217. return Blockly.Lua.provideFunction_(
  218. 'math_sum',
  219. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  220. ' local result = 0',
  221. ' for _, v in ipairs(t) do',
  222. ' result = result + v',
  223. ' end',
  224. ' return result',
  225. 'end']);
  226. }
  227. switch (func) {
  228. case 'SUM':
  229. functionName = provideSum();
  230. break;
  231. case 'MIN':
  232. // Returns 0 for the empty list.
  233. functionName = Blockly.Lua.provideFunction_(
  234. 'math_min',
  235. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  236. ' if #t == 0 then',
  237. ' return 0',
  238. ' end',
  239. ' local result = math.huge',
  240. ' for _, v in ipairs(t) do',
  241. ' if v < result then',
  242. ' result = v',
  243. ' end',
  244. ' end',
  245. ' return result',
  246. 'end']);
  247. break;
  248. case 'AVERAGE':
  249. // Returns 0 for the empty list.
  250. functionName = Blockly.Lua.provideFunction_(
  251. 'math_average',
  252. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  253. ' if #t == 0 then',
  254. ' return 0',
  255. ' end',
  256. ' return ' + provideSum() + '(t) / #t',
  257. 'end']);
  258. break;
  259. case 'MAX':
  260. // Returns 0 for the empty list.
  261. functionName = Blockly.Lua.provideFunction_(
  262. 'math_max',
  263. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  264. ' if #t == 0 then',
  265. ' return 0',
  266. ' end',
  267. ' local result = -math.huge',
  268. ' for _, v in ipairs(t) do',
  269. ' if v > result then',
  270. ' result = v',
  271. ' end',
  272. ' end',
  273. ' return result',
  274. 'end']);
  275. break;
  276. case 'MEDIAN':
  277. functionName = Blockly.Lua.provideFunction_(
  278. 'math_median',
  279. // This operation excludes non-numbers.
  280. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  281. ' -- Source: http://lua-users.org/wiki/SimpleStats',
  282. ' if #t == 0 then',
  283. ' return 0',
  284. ' end',
  285. ' local temp={}',
  286. ' for _, v in ipairs(t) do',
  287. ' if type(v) == "number" then',
  288. ' table.insert(temp, v)',
  289. ' end',
  290. ' end',
  291. ' table.sort(temp)',
  292. ' if #temp % 2 == 0 then',
  293. ' return (temp[#temp/2] + temp[(#temp/2)+1]) / 2',
  294. ' else',
  295. ' return temp[math.ceil(#temp/2)]',
  296. ' end',
  297. 'end']);
  298. break;
  299. case 'MODE':
  300. functionName = Blockly.Lua.provideFunction_(
  301. 'math_modes',
  302. // As a list of numbers can contain more than one mode,
  303. // the returned result is provided as an array.
  304. // The Lua version includes non-numbers.
  305. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  306. ' -- Source: http://lua-users.org/wiki/SimpleStats',
  307. ' local counts={}',
  308. ' for _, v in ipairs(t) do',
  309. ' if counts[v] == nil then',
  310. ' counts[v] = 1',
  311. ' else',
  312. ' counts[v] = counts[v] + 1',
  313. ' end',
  314. ' end',
  315. ' local biggestCount = 0',
  316. ' for _, v in pairs(counts) do',
  317. ' if v > biggestCount then',
  318. ' biggestCount = v',
  319. ' end',
  320. ' end',
  321. ' local temp={}',
  322. ' for k, v in pairs(counts) do',
  323. ' if v == biggestCount then',
  324. ' table.insert(temp, k)',
  325. ' end',
  326. ' end',
  327. ' return temp',
  328. 'end']);
  329. break;
  330. case 'STD_DEV':
  331. functionName = Blockly.Lua.provideFunction_(
  332. 'math_standard_deviation',
  333. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  334. ' local m',
  335. ' local vm',
  336. ' local total = 0',
  337. ' local count = 0',
  338. ' local result',
  339. ' m = #t == 0 and 0 or ' + provideSum() + '(t) / #t',
  340. ' for _, v in ipairs(t) do',
  341. " if type(v) == 'number' then",
  342. ' vm = v - m',
  343. ' total = total + (vm * vm)',
  344. ' count = count + 1',
  345. ' end',
  346. ' end',
  347. ' result = math.sqrt(total / (count-1))',
  348. ' return result',
  349. 'end']);
  350. break;
  351. case 'RANDOM':
  352. functionName = Blockly.Lua.provideFunction_(
  353. 'math_random_list',
  354. ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
  355. ' if #t == 0 then',
  356. ' return nil',
  357. ' end',
  358. ' return t[math.random(#t)]',
  359. 'end']);
  360. break;
  361. default:
  362. throw 'Unknown operator: ' + func;
  363. }
  364. return [functionName + '(' + list + ')', Blockly.Lua.ORDER_HIGH];
  365. };
  366. Blockly.Lua['math_modulo'] = function(block) {
  367. // Remainder computation.
  368. var argument0 = Blockly.Lua.valueToCode(block, 'DIVIDEND',
  369. Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
  370. var argument1 = Blockly.Lua.valueToCode(block, 'DIVISOR',
  371. Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
  372. var code = argument0 + ' % ' + argument1;
  373. return [code, Blockly.Lua.ORDER_MULTIPLICATIVE];
  374. };
  375. Blockly.Lua['math_constrain'] = function(block) {
  376. // Constrain a number between two limits.
  377. var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
  378. Blockly.Lua.ORDER_NONE) || '0';
  379. var argument1 = Blockly.Lua.valueToCode(block, 'LOW',
  380. Blockly.Lua.ORDER_NONE) || '-math.huge';
  381. var argument2 = Blockly.Lua.valueToCode(block, 'HIGH',
  382. Blockly.Lua.ORDER_NONE) || 'math.huge';
  383. var code = 'math.min(math.max(' + argument0 + ', ' + argument1 + '), ' +
  384. argument2 + ')';
  385. return [code, Blockly.Lua.ORDER_HIGH];
  386. };
  387. Blockly.Lua['math_random_int'] = function(block) {
  388. // Random integer between [X] and [Y].
  389. var argument0 = Blockly.Lua.valueToCode(block, 'FROM',
  390. Blockly.Lua.ORDER_NONE) || '0';
  391. var argument1 = Blockly.Lua.valueToCode(block, 'TO',
  392. Blockly.Lua.ORDER_NONE) || '0';
  393. var code = 'math.random(' + argument0 + ', ' + argument1 + ')';
  394. return [code, Blockly.Lua.ORDER_HIGH];
  395. };
  396. Blockly.Lua['math_random_float'] = function(block) {
  397. // Random fraction between 0 and 1.
  398. return ['math.random()', Blockly.Lua.ORDER_HIGH];
  399. };