simple-squiggle

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

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;