1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.createRationalize = void 0;
|
7 |
|
8 | var _number = require("../../utils/number.js");
|
9 |
|
10 | var _factory = require("../../utils/factory.js");
|
11 |
|
12 | var _simplifyConstant = require("./simplify/simplifyConstant.js");
|
13 |
|
14 | var _simplifyCore = require("./simplify/simplifyCore.js");
|
15 |
|
16 | var name = 'rationalize';
|
17 | var dependencies = ['config', 'typed', 'equal', 'isZero', 'add', 'subtract', 'multiply', 'divide', 'pow', 'parse', 'simplify', '?bignumber', '?fraction', 'mathWithTransform', 'ConstantNode', 'OperatorNode', 'FunctionNode', 'SymbolNode', 'ParenthesisNode'];
|
18 | var 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 | });
|
881 | exports.createRationalize = createRationalize; |
\ | No newline at end of file |