UNPKG

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