UNPKG

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