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