UNPKG

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