simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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 });