rationalize.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.createRationalize = void 0;
  6. var _number = require("../../utils/number.js");
  7. var _factory = require("../../utils/factory.js");
  8. var name = 'rationalize';
  9. var dependencies = ['config', 'typed', 'equal', 'isZero', 'add', 'subtract', 'multiply', 'divide', 'pow', 'parse', 'simplifyConstant', 'simplifyCore', 'simplify', '?bignumber', '?fraction', 'mathWithTransform', 'matrix', 'AccessorNode', 'ArrayNode', 'ConstantNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'SymbolNode', 'ParenthesisNode'];
  10. var createRationalize = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  11. var config = _ref.config,
  12. typed = _ref.typed,
  13. equal = _ref.equal,
  14. isZero = _ref.isZero,
  15. add = _ref.add,
  16. subtract = _ref.subtract,
  17. multiply = _ref.multiply,
  18. divide = _ref.divide,
  19. pow = _ref.pow,
  20. parse = _ref.parse,
  21. simplifyConstant = _ref.simplifyConstant,
  22. simplifyCore = _ref.simplifyCore,
  23. simplify = _ref.simplify,
  24. fraction = _ref.fraction,
  25. bignumber = _ref.bignumber,
  26. mathWithTransform = _ref.mathWithTransform,
  27. matrix = _ref.matrix,
  28. AccessorNode = _ref.AccessorNode,
  29. ArrayNode = _ref.ArrayNode,
  30. ConstantNode = _ref.ConstantNode,
  31. FunctionNode = _ref.FunctionNode,
  32. IndexNode = _ref.IndexNode,
  33. ObjectNode = _ref.ObjectNode,
  34. OperatorNode = _ref.OperatorNode,
  35. SymbolNode = _ref.SymbolNode,
  36. ParenthesisNode = _ref.ParenthesisNode;
  37. /**
  38. * Transform a rationalizable expression in a rational fraction.
  39. * If rational fraction is one variable polynomial then converts
  40. * the numerator and denominator in canonical form, with decreasing
  41. * exponents, returning the coefficients of numerator.
  42. *
  43. * Syntax:
  44. *
  45. * rationalize(expr)
  46. * rationalize(expr, detailed)
  47. * rationalize(expr, scope)
  48. * rationalize(expr, scope, detailed)
  49. *
  50. * Examples:
  51. *
  52. * math.rationalize('sin(x)+y')
  53. * // Error: There is an unsolved function call
  54. * math.rationalize('2x/y - y/(x+1)')
  55. * // (2*x^2-y^2+2*x)/(x*y+y)
  56. * math.rationalize('(2x+1)^6')
  57. * // 64*x^6+192*x^5+240*x^4+160*x^3+60*x^2+12*x+1
  58. * math.rationalize('2x/( (2x-1) / (3x+2) ) - 5x/ ( (3x+4) / (2x^2-5) ) + 3')
  59. * // -20*x^4+28*x^3+104*x^2+6*x-12)/(6*x^2+5*x-4)
  60. * math.rationalize('x/(1-x)/(x-2)/(x-3)/(x-4) + 2x/ ( (1-2x)/(2-3x) )/ ((3-4x)/(4-5x) )') =
  61. * // (-30*x^7+344*x^6-1506*x^5+3200*x^4-3472*x^3+1846*x^2-381*x)/
  62. * // (-8*x^6+90*x^5-383*x^4+780*x^3-797*x^2+390*x-72)
  63. *
  64. * math.rationalize('x+x+x+y',{y:1}) // 3*x+1
  65. * math.rationalize('x+x+x+y',{}) // 3*x+y
  66. *
  67. * const ret = math.rationalize('x+x+x+y',{},true)
  68. * // ret.expression=3*x+y, ret.variables = ["x","y"]
  69. * const ret = math.rationalize('-2+5x^2',{},true)
  70. * // ret.expression=5*x^2-2, ret.variables = ["x"], ret.coefficients=[-2,0,5]
  71. *
  72. * See also:
  73. *
  74. * simplify
  75. *
  76. * @param {Node|string} expr The expression to check if is a polynomial expression
  77. * @param {Object|boolean} optional scope of expression or true for already evaluated rational expression at input
  78. * @param {Boolean} detailed optional True if return an object, false if return expression node (default)
  79. *
  80. * @return {Object | Node} The rational polynomial of `expr` or an object
  81. * `{expression, numerator, denominator, variables, coefficients}`, where
  82. * `expression` is a `Node` with the node simplified expression,
  83. * `numerator` is a `Node` with the simplified numerator of expression,
  84. * `denominator` is a `Node` or `boolean` with the simplified denominator or `false` (if there is no denominator),
  85. * `variables` is an array with variable names,
  86. * and `coefficients` is an array with coefficients of numerator sorted by increased exponent
  87. * {Expression Node} node simplified expression
  88. *
  89. */
  90. function _rationalize(expr) {
  91. var scope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  92. var detailed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  93. var setRules = rulesRationalize(); // Rules for change polynomial in near canonical form
  94. var polyRet = polynomial(expr, scope, true, setRules.firstRules); // Check if expression is a rationalizable polynomial
  95. var nVars = polyRet.variables.length;
  96. var noExactFractions = {
  97. exactFractions: false
  98. };
  99. var withExactFractions = {
  100. exactFractions: true
  101. };
  102. expr = polyRet.expression;
  103. if (nVars >= 1) {
  104. // If expression in not a constant
  105. expr = expandPower(expr); // First expand power of polynomials (cannot be made from rules!)
  106. var sBefore; // Previous expression
  107. var rules;
  108. var eDistrDiv = true;
  109. var redoInic = false;
  110. // Apply the initial rules, including succ div rules:
  111. expr = simplify(expr, setRules.firstRules, {}, noExactFractions);
  112. var s;
  113. while (true) {
  114. // Alternate applying successive division rules and distr.div.rules
  115. // until there are no more changes:
  116. rules = eDistrDiv ? setRules.distrDivRules : setRules.sucDivRules;
  117. expr = simplify(expr, rules, {}, withExactFractions);
  118. eDistrDiv = !eDistrDiv; // Swap between Distr.Div and Succ. Div. Rules
  119. s = expr.toString();
  120. if (s === sBefore) {
  121. break; // No changes : end of the loop
  122. }
  123. redoInic = true;
  124. sBefore = s;
  125. }
  126. if (redoInic) {
  127. // Apply first rules again without succ div rules (if there are changes)
  128. expr = simplify(expr, setRules.firstRulesAgain, {}, noExactFractions);
  129. }
  130. // Apply final rules:
  131. expr = simplify(expr, setRules.finalRules, {}, noExactFractions);
  132. } // NVars >= 1
  133. var coefficients = [];
  134. var retRationalize = {};
  135. if (expr.type === 'OperatorNode' && expr.isBinary() && expr.op === '/') {
  136. // Separate numerator from denominator
  137. if (nVars === 1) {
  138. expr.args[0] = polyToCanonical(expr.args[0], coefficients);
  139. expr.args[1] = polyToCanonical(expr.args[1]);
  140. }
  141. if (detailed) {
  142. retRationalize.numerator = expr.args[0];
  143. retRationalize.denominator = expr.args[1];
  144. }
  145. } else {
  146. if (nVars === 1) {
  147. expr = polyToCanonical(expr, coefficients);
  148. }
  149. if (detailed) {
  150. retRationalize.numerator = expr;
  151. retRationalize.denominator = null;
  152. }
  153. }
  154. // nVars
  155. if (!detailed) return expr;
  156. retRationalize.coefficients = coefficients;
  157. retRationalize.variables = polyRet.variables;
  158. retRationalize.expression = expr;
  159. return retRationalize;
  160. }
  161. return typed(name, {
  162. Node: _rationalize,
  163. 'Node, boolean': function NodeBoolean(expr, detailed) {
  164. return _rationalize(expr, {}, detailed);
  165. },
  166. 'Node, Object': _rationalize,
  167. 'Node, Object, boolean': _rationalize
  168. }); // end of typed rationalize
  169. /**
  170. * Function to simplify an expression using an optional scope and
  171. * return it if the expression is a polynomial expression, i.e.
  172. * an expression with one or more variables and the operators
  173. * +, -, *, and ^, where the exponent can only be a positive integer.
  174. *
  175. * Syntax:
  176. *
  177. * polynomial(expr,scope,extended, rules)
  178. *
  179. * @param {Node | string} expr The expression to simplify and check if is polynomial expression
  180. * @param {object} scope Optional scope for expression simplification
  181. * @param {boolean} extended Optional. Default is false. When true allows divide operator.
  182. * @param {array} rules Optional. Default is no rule.
  183. *
  184. *
  185. * @return {Object}
  186. * {Object} node: node simplified expression
  187. * {Array} variables: variable names
  188. */
  189. function polynomial(expr, scope, extended, rules) {
  190. var variables = [];
  191. var node = simplify(expr, rules, scope, {
  192. exactFractions: false
  193. }); // Resolves any variables and functions with all defined parameters
  194. extended = !!extended;
  195. var oper = '+-*' + (extended ? '/' : '');
  196. recPoly(node);
  197. var retFunc = {};
  198. retFunc.expression = node;
  199. retFunc.variables = variables;
  200. return retFunc;
  201. // -------------------------------------------------------------------------------------------------------
  202. /**
  203. * Function to simplify an expression using an optional scope and
  204. * return it if the expression is a polynomial expression, i.e.
  205. * an expression with one or more variables and the operators
  206. * +, -, *, and ^, where the exponent can only be a positive integer.
  207. *
  208. * Syntax:
  209. *
  210. * recPoly(node)
  211. *
  212. *
  213. * @param {Node} node The current sub tree expression in recursion
  214. *
  215. * @return nothing, throw an exception if error
  216. */
  217. function recPoly(node) {
  218. var tp = node.type; // node type
  219. if (tp === 'FunctionNode') {
  220. // No function call in polynomial expression
  221. throw new Error('There is an unsolved function call');
  222. } else if (tp === 'OperatorNode') {
  223. if (node.op === '^') {
  224. // TODO: handle negative exponents like in '1/x^(-2)'
  225. if (node.args[1].type !== 'ConstantNode' || !(0, _number.isInteger)(parseFloat(node.args[1].value))) {
  226. throw new Error('There is a non-integer exponent');
  227. } else {
  228. recPoly(node.args[0]);
  229. }
  230. } else {
  231. if (oper.indexOf(node.op) === -1) {
  232. throw new Error('Operator ' + node.op + ' invalid in polynomial expression');
  233. }
  234. for (var i = 0; i < node.args.length; i++) {
  235. recPoly(node.args[i]);
  236. }
  237. } // type of operator
  238. } else if (tp === 'SymbolNode') {
  239. var _name = node.name; // variable name
  240. var pos = variables.indexOf(_name);
  241. if (pos === -1) {
  242. // new variable in expression
  243. variables.push(_name);
  244. }
  245. } else if (tp === 'ParenthesisNode') {
  246. recPoly(node.content);
  247. } else if (tp !== 'ConstantNode') {
  248. throw new Error('type ' + tp + ' is not allowed in polynomial expression');
  249. }
  250. } // end of recPoly
  251. } // end of polynomial
  252. // ---------------------------------------------------------------------------------------
  253. /**
  254. * Return a rule set to rationalize an polynomial expression in rationalize
  255. *
  256. * Syntax:
  257. *
  258. * rulesRationalize()
  259. *
  260. * @return {array} rule set to rationalize an polynomial expression
  261. */
  262. function rulesRationalize() {
  263. var oldRules = [simplifyCore,
  264. // sCore
  265. {
  266. l: 'n+n',
  267. r: '2*n'
  268. }, {
  269. l: 'n+-n',
  270. r: '0'
  271. }, simplifyConstant,
  272. // sConstant
  273. {
  274. l: 'n*(n1^-1)',
  275. r: 'n/n1'
  276. }, {
  277. l: 'n*n1^-n2',
  278. r: 'n/n1^n2'
  279. }, {
  280. l: 'n1^-1',
  281. r: '1/n1'
  282. }, {
  283. l: 'n*(n1/n2)',
  284. r: '(n*n1)/n2'
  285. }, {
  286. l: '1*n',
  287. r: 'n'
  288. }];
  289. var rulesFirst = [{
  290. l: '(-n1)/(-n2)',
  291. r: 'n1/n2'
  292. },
  293. // Unary division
  294. {
  295. l: '(-n1)*(-n2)',
  296. r: 'n1*n2'
  297. },
  298. // Unary multiplication
  299. {
  300. l: 'n1--n2',
  301. r: 'n1+n2'
  302. },
  303. // '--' elimination
  304. {
  305. l: 'n1-n2',
  306. r: 'n1+(-n2)'
  307. },
  308. // Subtraction turn into add with un�ry minus
  309. {
  310. l: '(n1+n2)*n3',
  311. r: '(n1*n3 + n2*n3)'
  312. },
  313. // Distributive 1
  314. {
  315. l: 'n1*(n2+n3)',
  316. r: '(n1*n2+n1*n3)'
  317. },
  318. // Distributive 2
  319. {
  320. l: 'c1*n + c2*n',
  321. r: '(c1+c2)*n'
  322. },
  323. // Joining constants
  324. {
  325. l: 'c1*n + n',
  326. r: '(c1+1)*n'
  327. },
  328. // Joining constants
  329. {
  330. l: 'c1*n - c2*n',
  331. r: '(c1-c2)*n'
  332. },
  333. // Joining constants
  334. {
  335. l: 'c1*n - n',
  336. r: '(c1-1)*n'
  337. },
  338. // Joining constants
  339. {
  340. l: 'v/c',
  341. r: '(1/c)*v'
  342. },
  343. // variable/constant (new!)
  344. {
  345. l: 'v/-c',
  346. r: '-(1/c)*v'
  347. },
  348. // variable/constant (new!)
  349. {
  350. l: '-v*-c',
  351. r: 'c*v'
  352. },
  353. // Inversion constant and variable 1
  354. {
  355. l: '-v*c',
  356. r: '-c*v'
  357. },
  358. // Inversion constant and variable 2
  359. {
  360. l: 'v*-c',
  361. r: '-c*v'
  362. },
  363. // Inversion constant and variable 3
  364. {
  365. l: 'v*c',
  366. r: 'c*v'
  367. },
  368. // Inversion constant and variable 4
  369. {
  370. l: '-(-n1*n2)',
  371. r: '(n1*n2)'
  372. },
  373. // Unary propagation
  374. {
  375. l: '-(n1*n2)',
  376. r: '(-n1*n2)'
  377. },
  378. // Unary propagation
  379. {
  380. l: '-(-n1+n2)',
  381. r: '(n1-n2)'
  382. },
  383. // Unary propagation
  384. {
  385. l: '-(n1+n2)',
  386. r: '(-n1-n2)'
  387. },
  388. // Unary propagation
  389. {
  390. l: '(n1^n2)^n3',
  391. r: '(n1^(n2*n3))'
  392. },
  393. // Power to Power
  394. {
  395. l: '-(-n1/n2)',
  396. r: '(n1/n2)'
  397. },
  398. // Division and Unary
  399. {
  400. l: '-(n1/n2)',
  401. r: '(-n1/n2)'
  402. }]; // Divisao and Unary
  403. var rulesDistrDiv = [{
  404. l: '(n1/n2 + n3/n4)',
  405. r: '((n1*n4 + n3*n2)/(n2*n4))'
  406. },
  407. // Sum of fractions
  408. {
  409. l: '(n1/n2 + n3)',
  410. r: '((n1 + n3*n2)/n2)'
  411. },
  412. // Sum fraction with number 1
  413. {
  414. l: '(n1 + n2/n3)',
  415. r: '((n1*n3 + n2)/n3)'
  416. }]; // Sum fraction with number 1
  417. var rulesSucDiv = [{
  418. l: '(n1/(n2/n3))',
  419. r: '((n1*n3)/n2)'
  420. },
  421. // Division simplification
  422. {
  423. l: '(n1/n2/n3)',
  424. r: '(n1/(n2*n3))'
  425. }];
  426. var setRules = {}; // rules set in 4 steps.
  427. // All rules => infinite loop
  428. // setRules.allRules =oldRules.concat(rulesFirst,rulesDistrDiv,rulesSucDiv)
  429. setRules.firstRules = oldRules.concat(rulesFirst, rulesSucDiv); // First rule set
  430. setRules.distrDivRules = rulesDistrDiv; // Just distr. div. rules
  431. setRules.sucDivRules = rulesSucDiv; // Jus succ. div. rules
  432. setRules.firstRulesAgain = oldRules.concat(rulesFirst); // Last rules set without succ. div.
  433. // Division simplification
  434. // Second rule set.
  435. // There is no aggregate expression with parentesis, but the only variable can be scattered.
  436. setRules.finalRules = [simplifyCore,
  437. // simplify.rules[0]
  438. {
  439. l: 'n*-n',
  440. r: '-n^2'
  441. },
  442. // Joining multiply with power 1
  443. {
  444. l: 'n*n',
  445. r: 'n^2'
  446. },
  447. // Joining multiply with power 2
  448. simplifyConstant,
  449. // simplify.rules[14] old 3rd index in oldRules
  450. {
  451. l: 'n*-n^n1',
  452. r: '-n^(n1+1)'
  453. },
  454. // Joining multiply with power 3
  455. {
  456. l: 'n*n^n1',
  457. r: 'n^(n1+1)'
  458. },
  459. // Joining multiply with power 4
  460. {
  461. l: 'n^n1*-n^n2',
  462. r: '-n^(n1+n2)'
  463. },
  464. // Joining multiply with power 5
  465. {
  466. l: 'n^n1*n^n2',
  467. r: 'n^(n1+n2)'
  468. },
  469. // Joining multiply with power 6
  470. {
  471. l: 'n^n1*-n',
  472. r: '-n^(n1+1)'
  473. },
  474. // Joining multiply with power 7
  475. {
  476. l: 'n^n1*n',
  477. r: 'n^(n1+1)'
  478. },
  479. // Joining multiply with power 8
  480. {
  481. l: 'n^n1/-n',
  482. r: '-n^(n1-1)'
  483. },
  484. // Joining multiply with power 8
  485. {
  486. l: 'n^n1/n',
  487. r: 'n^(n1-1)'
  488. },
  489. // Joining division with power 1
  490. {
  491. l: 'n/-n^n1',
  492. r: '-n^(1-n1)'
  493. },
  494. // Joining division with power 2
  495. {
  496. l: 'n/n^n1',
  497. r: 'n^(1-n1)'
  498. },
  499. // Joining division with power 3
  500. {
  501. l: 'n^n1/-n^n2',
  502. r: 'n^(n1-n2)'
  503. },
  504. // Joining division with power 4
  505. {
  506. l: 'n^n1/n^n2',
  507. r: 'n^(n1-n2)'
  508. },
  509. // Joining division with power 5
  510. {
  511. l: 'n1+(-n2*n3)',
  512. r: 'n1-n2*n3'
  513. },
  514. // Solving useless parenthesis 1
  515. {
  516. l: 'v*(-c)',
  517. r: '-c*v'
  518. },
  519. // Solving useless unary 2
  520. {
  521. l: 'n1+-n2',
  522. r: 'n1-n2'
  523. },
  524. // Solving +- together (new!)
  525. {
  526. l: 'v*c',
  527. r: 'c*v'
  528. },
  529. // inversion constant with variable
  530. {
  531. l: '(n1^n2)^n3',
  532. r: '(n1^(n2*n3))'
  533. } // Power to Power
  534. ];
  535. return setRules;
  536. } // End rulesRationalize
  537. // ---------------------------------------------------------------------------------------
  538. /**
  539. * Expand recursively a tree node for handling with expressions with exponents
  540. * (it's not for constants, symbols or functions with exponents)
  541. * PS: The other parameters are internal for recursion
  542. *
  543. * Syntax:
  544. *
  545. * expandPower(node)
  546. *
  547. * @param {Node} node Current expression node
  548. * @param {node} parent Parent current node inside the recursion
  549. * @param (int} Parent number of chid inside the rercursion
  550. *
  551. * @return {node} node expression with all powers expanded.
  552. */
  553. function expandPower(node, parent, indParent) {
  554. var tp = node.type;
  555. var internal = arguments.length > 1; // TRUE in internal calls
  556. if (tp === 'OperatorNode' && node.isBinary()) {
  557. var does = false;
  558. var val;
  559. if (node.op === '^') {
  560. // First operator: Parenthesis or UnaryMinus
  561. if ((node.args[0].type === 'ParenthesisNode' || node.args[0].type === 'OperatorNode') && node.args[1].type === 'ConstantNode') {
  562. // Second operator: Constant
  563. val = parseFloat(node.args[1].value);
  564. does = val >= 2 && (0, _number.isInteger)(val);
  565. }
  566. }
  567. if (does) {
  568. // Exponent >= 2
  569. // Before:
  570. // operator A --> Subtree
  571. // parent pow
  572. // constant
  573. //
  574. if (val > 2) {
  575. // Exponent > 2,
  576. // AFTER: (exponent > 2)
  577. // operator A --> Subtree
  578. // parent *
  579. // deep clone (operator A --> Subtree
  580. // pow
  581. // constant - 1
  582. //
  583. var nEsqTopo = node.args[0];
  584. var nDirTopo = new OperatorNode('^', 'pow', [node.args[0].cloneDeep(), new ConstantNode(val - 1)]);
  585. node = new OperatorNode('*', 'multiply', [nEsqTopo, nDirTopo]);
  586. } else {
  587. // Expo = 2 - no power
  588. // AFTER: (exponent = 2)
  589. // operator A --> Subtree
  590. // parent oper
  591. // deep clone (operator A --> Subtree)
  592. //
  593. node = new OperatorNode('*', 'multiply', [node.args[0], node.args[0].cloneDeep()]);
  594. }
  595. if (internal) {
  596. // Change parent references in internal recursive calls
  597. if (indParent === 'content') {
  598. parent.content = node;
  599. } else {
  600. parent.args[indParent] = node;
  601. }
  602. }
  603. } // does
  604. } // binary OperatorNode
  605. if (tp === 'ParenthesisNode') {
  606. // Recursion
  607. expandPower(node.content, node, 'content');
  608. } else if (tp !== 'ConstantNode' && tp !== 'SymbolNode') {
  609. for (var i = 0; i < node.args.length; i++) {
  610. expandPower(node.args[i], node, i);
  611. }
  612. }
  613. if (!internal) {
  614. // return the root node
  615. return node;
  616. }
  617. } // End expandPower
  618. // ---------------------------------------------------------------------------------------
  619. /**
  620. * Auxilary function for rationalize
  621. * Convert near canonical polynomial in one variable in a canonical polynomial
  622. * with one term for each exponent in decreasing order
  623. *
  624. * Syntax:
  625. *
  626. * polyToCanonical(node [, coefficients])
  627. *
  628. * @param {Node | string} expr The near canonical polynomial expression to convert in a a canonical polynomial expression
  629. *
  630. * The string or tree expression needs to be at below syntax, with free spaces:
  631. * ( (^(-)? | [+-]? )cte (*)? var (^expo)? | cte )+
  632. * Where 'var' is one variable with any valid name
  633. * 'cte' are real numeric constants with any value. It can be omitted if equal than 1
  634. * 'expo' are integers greater than 0. It can be omitted if equal than 1.
  635. *
  636. * @param {array} coefficients Optional returns coefficients sorted by increased exponent
  637. *
  638. *
  639. * @return {node} new node tree with one variable polynomial or string error.
  640. */
  641. function polyToCanonical(node, coefficients) {
  642. if (coefficients === undefined) {
  643. coefficients = [];
  644. } // coefficients.
  645. coefficients[0] = 0; // index is the exponent
  646. var o = {};
  647. o.cte = 1;
  648. o.oper = '+';
  649. // fire: mark with * or ^ when finds * or ^ down tree, reset to "" with + and -.
  650. // It is used to deduce the exponent: 1 for *, 0 for "".
  651. o.fire = '';
  652. var maxExpo = 0; // maximum exponent
  653. var varname = ''; // variable name
  654. recurPol(node, null, o);
  655. maxExpo = coefficients.length - 1;
  656. var first = true;
  657. var no;
  658. for (var i = maxExpo; i >= 0; i--) {
  659. if (coefficients[i] === 0) continue;
  660. var n1 = new ConstantNode(first ? coefficients[i] : Math.abs(coefficients[i]));
  661. var op = coefficients[i] < 0 ? '-' : '+';
  662. if (i > 0) {
  663. // Is not a constant without variable
  664. var n2 = new SymbolNode(varname);
  665. if (i > 1) {
  666. var n3 = new ConstantNode(i);
  667. n2 = new OperatorNode('^', 'pow', [n2, n3]);
  668. }
  669. if (coefficients[i] === -1 && first) {
  670. n1 = new OperatorNode('-', 'unaryMinus', [n2]);
  671. } else if (Math.abs(coefficients[i]) === 1) {
  672. n1 = n2;
  673. } else {
  674. n1 = new OperatorNode('*', 'multiply', [n1, n2]);
  675. }
  676. }
  677. if (first) {
  678. no = n1;
  679. } else if (op === '+') {
  680. no = new OperatorNode('+', 'add', [no, n1]);
  681. } else {
  682. no = new OperatorNode('-', 'subtract', [no, n1]);
  683. }
  684. first = false;
  685. } // for
  686. if (first) {
  687. return new ConstantNode(0);
  688. } else {
  689. return no;
  690. }
  691. /**
  692. * Recursive auxilary function inside polyToCanonical for
  693. * converting expression in canonical form
  694. *
  695. * Syntax:
  696. *
  697. * recurPol(node, noPai, obj)
  698. *
  699. * @param {Node} node The current subpolynomial expression
  700. * @param {Node | Null} noPai The current parent node
  701. * @param {object} obj Object with many internal flags
  702. *
  703. * @return {} No return. If error, throws an exception
  704. */
  705. function recurPol(node, noPai, o) {
  706. var tp = node.type;
  707. if (tp === 'FunctionNode') {
  708. // ***** FunctionName *****
  709. // No function call in polynomial expression
  710. throw new Error('There is an unsolved function call');
  711. } else if (tp === 'OperatorNode') {
  712. // ***** OperatorName *****
  713. if ('+-*^'.indexOf(node.op) === -1) throw new Error('Operator ' + node.op + ' invalid');
  714. if (noPai !== null) {
  715. // -(unary),^ : children of *,+,-
  716. if ((node.fn === 'unaryMinus' || node.fn === 'pow') && noPai.fn !== 'add' && noPai.fn !== 'subtract' && noPai.fn !== 'multiply') {
  717. throw new Error('Invalid ' + node.op + ' placing');
  718. }
  719. // -,+,* : children of +,-
  720. if ((node.fn === 'subtract' || node.fn === 'add' || node.fn === 'multiply') && noPai.fn !== 'add' && noPai.fn !== 'subtract') {
  721. throw new Error('Invalid ' + node.op + ' placing');
  722. }
  723. // -,+ : first child
  724. if ((node.fn === 'subtract' || node.fn === 'add' || node.fn === 'unaryMinus') && o.noFil !== 0) {
  725. throw new Error('Invalid ' + node.op + ' placing');
  726. }
  727. } // Has parent
  728. // Firers: ^,* Old: ^,&,-(unary): firers
  729. if (node.op === '^' || node.op === '*') {
  730. o.fire = node.op;
  731. }
  732. for (var _i = 0; _i < node.args.length; _i++) {
  733. // +,-: reset fire
  734. if (node.fn === 'unaryMinus') o.oper = '-';
  735. if (node.op === '+' || node.fn === 'subtract') {
  736. o.fire = '';
  737. o.cte = 1; // default if there is no constant
  738. o.oper = _i === 0 ? '+' : node.op;
  739. }
  740. o.noFil = _i; // number of son
  741. recurPol(node.args[_i], node, o);
  742. } // for in children
  743. } else if (tp === 'SymbolNode') {
  744. // ***** SymbolName *****
  745. if (node.name !== varname && varname !== '') {
  746. throw new Error('There is more than one variable');
  747. }
  748. varname = node.name;
  749. if (noPai === null) {
  750. coefficients[1] = 1;
  751. return;
  752. }
  753. // ^: Symbol is First child
  754. if (noPai.op === '^' && o.noFil !== 0) {
  755. throw new Error('In power the variable should be the first parameter');
  756. }
  757. // *: Symbol is Second child
  758. if (noPai.op === '*' && o.noFil !== 1) {
  759. throw new Error('In multiply the variable should be the second parameter');
  760. }
  761. // Symbol: firers '',* => it means there is no exponent above, so it's 1 (cte * var)
  762. if (o.fire === '' || o.fire === '*') {
  763. if (maxExpo < 1) coefficients[1] = 0;
  764. coefficients[1] += o.cte * (o.oper === '+' ? 1 : -1);
  765. maxExpo = Math.max(1, maxExpo);
  766. }
  767. } else if (tp === 'ConstantNode') {
  768. var valor = parseFloat(node.value);
  769. if (noPai === null) {
  770. coefficients[0] = valor;
  771. return;
  772. }
  773. if (noPai.op === '^') {
  774. // cte: second child of power
  775. if (o.noFil !== 1) throw new Error('Constant cannot be powered');
  776. if (!(0, _number.isInteger)(valor) || valor <= 0) {
  777. throw new Error('Non-integer exponent is not allowed');
  778. }
  779. for (var _i2 = maxExpo + 1; _i2 < valor; _i2++) {
  780. coefficients[_i2] = 0;
  781. }
  782. if (valor > maxExpo) coefficients[valor] = 0;
  783. coefficients[valor] += o.cte * (o.oper === '+' ? 1 : -1);
  784. maxExpo = Math.max(valor, maxExpo);
  785. return;
  786. }
  787. o.cte = valor;
  788. // Cte: firer '' => There is no exponent and no multiplication, so the exponent is 0.
  789. if (o.fire === '') {
  790. coefficients[0] += o.cte * (o.oper === '+' ? 1 : -1);
  791. }
  792. } else {
  793. throw new Error('Type ' + tp + ' is not allowed');
  794. }
  795. } // End of recurPol
  796. } // End of polyToCanonical
  797. });
  798. exports.createRationalize = createRationalize;