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