UNPKG

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