      1 /*
      2  JessieCode Computer algebra algorithms
      3
      4     Copyright 2011-2019
      5         Michael Gerhaeuser,
      6         Alfred Wassermann
      7
      8     JessieCode is free software dual licensed under the GNU LGPL or MIT License.
      9
     10     You can redistribute it and/or modify it under the terms of the
     11
     12       * GNU Lesser General Public License as published by
     13         the Free Software Foundation, either version 3 of the License, or
     14         (at your option) any later version
     15       OR
     16       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     17
     18     JessieCode is distributed in the hope that it will be useful,
     19     but WITHOUT ANY WARRANTY; without even the implied warranty of
     20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21     GNU Lesser General Public License for more details.
     22
     23     You should have received a copy of the GNU Lesser General Public License and
     24     the MIT License along with JessieCode. If not, see <https://www.gnu.org/licenses/>
     25     and <https://opensource.org/licenses/MIT/>.
     26  */
     27
     28 /*global JXG: true, define: true, window: true, console: true, self: true, document: true, parser: true*/
     29 /*jslint nomen: true, plusplus: true*/
     30 /*eslint eqeqeq: "off"*/
     31
     32 /**
     33  * @fileoverview Here, the computer algebra algorithms are implemented.
     34  */
     35
     36 import JXG from "../jxg";
     37 import Type from "../utils/type";
     38 // import Const from "../base/constants";
     39 // import Text from "../base/text";
     40 // import Mat from "../math/math";
     41 // import Geometry from "../math/geometry";
     42 // import Statistics from "../math/statistics";
     43 // import Env from "../utils/env";
     44
     45 /**
     46  * A JessieCode object provides an interface to the parser and stores all variables and objects used within a JessieCode script.
     47  * The optional argument <tt>code</tt> is interpreted after initializing. To evaluate more code after initializing a JessieCode instance
     48  * please use {@link JXG.JessieCode#parse}. For code snippets like single expressions use {@link JXG.JessieCode#snippet}.
     49  * @constructor
     50  * @param {String} [code] Code to parse.
     51  * @param {Boolean} [geonext=false] Geonext compatibility mode.
     52  */
     53 JXG.CA = function (node, createNode, parser) {
     54     this.node = node;
     55     this.createNode = createNode;
     56     this.parser = parser;
     57 };
     58
     59 JXG.extend(
     60     JXG.CA.prototype,
     61     /** @lends JXG.CA.prototype */ {
     62         findMapNode: function (mapname, node) {
     63             var i, len, ret;
     64
     65             //console.log("FINDMAP", node);
     66             if (node.value === "op_assign" && node.children[0].value === mapname) {
     67                 return node.children[1];
     68             } else if (node.children) {
     69                 len = node.children.length;
     70                 for (i = 0; i < len; ++i) {
     71                     ret = this.findMapNode(mapname, node.children[i]);
     72                     if (ret !== null) {
     73                         return ret;
     74                     }
     75                 }
     76             }
     77             return null;
     78         },
     79
     80         /**
     81          * Declare all subnodes as math nodes,
     82          * i.e recursively set node.isMath = true;
     83          */
     84         setMath: function (node) {
     85             var i, len;
     86
     87             if (
     88                 (node.type == "node_op" &&
     89                     (node.value == "op_add" ||
     90                         node.value == "op_sub" ||
     91                         node.value == "op_mul" ||
     92                         node.value == "op_div" ||
     93                         node.value == "op_neg" ||
     94                         node.value == "op_execfun" ||
     95                         node.value == "op_exp")) ||
     96                 node.type == "node_var" ||
     97                 node.type == "node_const"
     98             ) {
     99                 node.isMath = true;
    100             }
    101             if (node.children) {
    102                 len = node.children.length;
    103                 for (i = 0; i < len; ++i) {
    104                     this.setMath(node.children[i]);
    105                 }
    106             }
    107         },
    108
    109         deriveElementary: function (node, varname) {
    110             var fun = node.children[0].value,
    111                 arg = node.children[1],
    112                 newNode;
    113
    114             switch (fun) {
    115                 case "abs":
    116                     // x / sqrt(x * x)
    117                     newNode = this.createNode(
    118                         "node_op",
    119                         "op_div",
    120                         arg[0],
    121                         this.createNode(
    122                             "node_op",
    123                             "op_execfun",
    124                             this.createNode("node_var", "sqrt"),
    125                             [
    126                                 this.createNode(
    127                                     "node_op",
    128                                     "op_mul",
    129                                     Type.deepCopy(arg[0]),
    130                                     Type.deepCopy(arg[0])
    131                                 )
    132                             ]
    133                         )
    134                     );
    135                     break;
    136
    137                 case "sqrt":
    138                     newNode = this.createNode(
    139                         "node_op",
    140                         "op_div",
    141                         this.createNode("node_const", 1.0),
    142                         this.createNode(
    143                             "node_op",
    144                             "op_mul",
    145                             this.createNode("node_const", 2.0),
    146                             this.createNode(
    147                                 node.type,
    148                                 node.value,
    149                                 Type.deepCopy(node.children[0]),
    150                                 Type.deepCopy(node.children[1])
    151                             )
    152                         )
    153                     );
    154                     break;
    155
    156                 case "sin":
    157                     newNode = this.createNode(
    158                         "node_op",
    159                         "op_execfun",
    160                         this.createNode("node_var", "cos"),
    161                         Type.deepCopy(arg)
    162                     );
    163                     break;
    164
    165                 case "cos":
    166                     newNode = this.createNode(
    167                         "node_op",
    168                         "op_neg",
    169                         this.createNode(
    170                             "node_op",
    171                             "op_execfun",
    172                             this.createNode("node_var", "sin"),
    173                             Type.deepCopy(arg)
    174                         )
    175                     );
    176                     break;
    177
    178                 case "tan":
    179                     newNode = this.createNode(
    180                         "node_op",
    181                         "op_div",
    182                         this.createNode("node_const", 1.0),
    183                         this.createNode(
    184                             "node_op",
    185                             "op_exp",
    186                             this.createNode(
    187                                 "node_op",
    188                                 "op_execfun",
    189                                 this.createNode("node_var", "cos"),
    190                                 Type.deepCopy(arg)
    191                             ),
    192                             this.createNode("node_const", 2)
    193                         )
    194                     );
    195                     break;
    196
    197                 case "cot":
    198                     newNode = this.createNode(
    199                         "node_op",
    200                         "op_neg",
    201                         this.createNode(
    202                             "node_op",
    203                             "op_div",
    204                             this.createNode("node_const", 1.0),
    205                             this.createNode(
    206                                 "node_op",
    207                                 "op_exp",
    208                                 this.createNode(
    209                                     "node_op",
    210                                     "op_execfun",
    211                                     this.createNode("node_var", "sin"),
    212                                     Type.deepCopy(arg)
    213                                 ),
    214                                 this.createNode("node_const", 2)
    215                             )
    216                         )
    217                     );
    218                     break;
    219
    220                 case "exp":
    221                     newNode = this.createNode(
    222                         node.type,
    223                         node.value,
    224                         Type.deepCopy(node.children[0]),
    225                         Type.deepCopy(node.children[1])
    226                     );
    227                     break;
    228
    229                 case "pow":
    230                     // (f^g)' = f^g*(f'g/f + g' log(f))
    231                     newNode = this.createNode(
    232                         "node_op",
    233                         "op_mul",
    234                         this.createNode(
    235                             "node_op",
    236                             "op_execfun",
    237                             Type.deepCopy(node.children[0]),
    238                             Type.deepCopy(node.children[1])
    239                         ),
    240                         this.createNode(
    241                             "node_op",
    242                             "op_add",
    243                             this.createNode(
    244                                 "node_op",
    245                                 "op_mul",
    246                                 this.derivative(node.children[1][0], varname),
    247                                 this.createNode(
    248                                     "node_op",
    249                                     "op_div",
    250                                     Type.deepCopy(node.children[1][1]),
    251                                     Type.deepCopy(node.children[1][0])
    252                                 )
    253                             ),
    254                             this.createNode(
    255                                 "node_op",
    256                                 "op_mul",
    257                                 this.derivative(node.children[1][1], varname),
    258                                 this.createNode(
    259                                     "node_op",
    260                                     "op_execfun",
    261                                     this.createNode("node_var", "log"),
    262                                     [Type.deepCopy(node.children[1][0])]
    263                                 )
    264                             )
    265                         )
    266                     );
    267                     break;
    268
    269                 case "log":
    270                 case "ln":
    271                     newNode = this.createNode(
    272                         "node_op",
    273                         "op_div",
    274                         this.createNode("node_const", 1.0),
    275                         // Attention: single variable mode
    276                         Type.deepCopy(arg[0])
    277                     );
    278                     break;
    279
    280                 case "log2":
    281                 case "lb":
    282                 case "ld":
    283                     newNode = this.createNode(
    284                         "node_op",
    285                         "op_mul",
    286                         this.createNode(
    287                             "node_op",
    288                             "op_div",
    289                             this.createNode("node_const", 1.0),
    290                             // Attention: single variable mode
    291                             Type.deepCopy(arg[0])
    292                         ),
    293                         this.createNode("node_const", 1.4426950408889634) // 1/log(2)
    294                     );
    295                     break;
    296
    297                 case "log10":
    298                 case "lg":
    299                     newNode = this.createNode(
    300                         "node_op",
    301                         "op_mul",
    302                         this.createNode(
    303                             "node_op",
    304                             "op_div",
    305                             this.createNode("node_const", 1.0),
    306                             // Attention: single variable mode
    307                             Type.deepCopy(arg[0])
    308                         ),
    309                         this.createNode("node_const", 0.43429448190325176) // 1/log(10)
    310                     );
    311                     break;
    312
    313                 case "asin":
    314                     newNode = this.createNode(
    315                         "node_op",
    316                         "op_div",
    317                         this.createNode("node_const", 1.0),
    318                         this.createNode(
    319                             "node_op",
    320                             "op_execfun",
    321                             this.createNode("node_var", "sqrt"),
    322                             [
    323                                 this.createNode(
    324                                     "node_op",
    325                                     "op_sub",
    326                                     this.createNode("node_const", 1.0),
    327                                     this.createNode(
    328                                         "node_op",
    329                                         "op_mul",
    330                                         Type.deepCopy(arg[0]),
    331                                         Type.deepCopy(arg[0])
    332                                     )
    333                                 )
    334                             ]
    335                         )
    336                     );
    337                     break;
    338
    339                 case "acos":
    340                     newNode = this.createNode(
    341                         "node_op",
    342                         "op_neg",
    343                         this.createNode(
    344                             "node_op",
    345                             "op_div",
    346                             this.createNode("node_const", 1.0),
    347                             this.createNode(
    348                                 "node_op",
    349                                 "op_execfun",
    350                                 this.createNode("node_var", "sqrt"),
    351                                 [
    352                                     this.createNode(
    353                                         "node_op",
    354                                         "op_sub",
    355                                         this.createNode("node_const", 1.0),
    356                                         this.createNode(
    357                                             "node_op",
    358                                             "op_mul",
    359                                             Type.deepCopy(arg[0]),
    360                                             Type.deepCopy(arg[0])
    361                                         )
    362                                     )
    363                                 ]
    364                             )
    365                         )
    366                     );
    367                     break;
    368
    369                 //case 'atan2':
    370
    371                 case "atan":
    372                     newNode = this.createNode(
    373                         "node_op",
    374                         "op_div",
    375                         this.createNode("node_const", 1.0),
    376                         this.createNode(
    377                             "node_op",
    378                             "op_add",
    379                             this.createNode("node_const", 1.0),
    380                             this.createNode(
    381                                 "node_op",
    382                                 "op_mul",
    383                                 Type.deepCopy(arg[0]),
    384                                 Type.deepCopy(arg[0])
    385                             )
    386                         )
    387                     );
    388                     break;
    389
    390                 case "acot":
    391                     newNode = this.createNode(
    392                         "node_op",
    393                         "op_neg",
    394                         this.createNode(
    395                             "node_op",
    396                             "op_div",
    397                             this.createNode("node_const", 1.0),
    398                             this.createNode(
    399                                 "node_op",
    400                                 "op_add",
    401                                 this.createNode("node_const", 1.0),
    402                                 this.createNode(
    403                                     "node_op",
    404                                     "op_mul",
    405                                     Type.deepCopy(arg[0]),
    406                                     Type.deepCopy(arg[0])
    407                                 )
    408                             )
    409                         )
    410                     );
    411                     break;
    412
    413                 case "sinh":
    414                     newNode = this.createNode(
    415                         "node_op",
    416                         "op_execfun",
    417                         this.createNode("node_var", "cosh"),
    418                         [Type.deepCopy(arg[0])]
    419                     );
    420                     break;
    421
    422                 case "cosh":
    423                     newNode = this.createNode(
    424                         "node_op",
    425                         "op_execfun",
    426                         this.createNode("node_var", "sinh"),
    427                         [Type.deepCopy(arg[0])]
    428                     );
    429                     break;
    430
    431                 case "tanh":
    432                     newNode = this.createNode(
    433                         "node_op",
    434                         "op_sub",
    435                         this.createNode("node_const", 1.0),
    436                         this.createNode(
    437                             "node_op",
    438                             "op_exp",
    439                             this.createNode(
    440                                 "node_op",
    441                                 "op_execfun",
    442                                 this.createNode("node_var", "tanh"),
    443                                 [Type.deepCopy(arg[0])]
    444                             ),
    445                             this.createNode("node_const", 2.0)
    446                         )
    447                     );
    448                     break;
    449
    450                 case "asinh":
    451                     newNode = this.createNode(
    452                         "node_op",
    453                         "op_div",
    454                         this.createNode("node_const", 1.0),
    455                         this.createNode(
    456                             "node_op",
    457                             "op_execfun",
    458                             this.createNode("node_var", "sqrt"),
    459                             [
    460                                 this.createNode(
    461                                     "node_op",
    462                                     "op_add",
    463                                     this.createNode(
    464                                         "node_op",
    465                                         "op_mul",
    466                                         Type.deepCopy(arg[0]),
    467                                         Type.deepCopy(arg[0])
    468                                     ),
    469                                     this.createNode("node_const", 1.0)
    470                                 )
    471                             ]
    472                         )
    473                     );
    474                     break;
    475
    476                 case "acosh":
    477                     newNode = this.createNode(
    478                         "node_op",
    479                         "op_div",
    480                         this.createNode("node_const", 1.0),
    481                         this.createNode(
    482                             "node_op",
    483                             "op_execfun",
    484                             this.createNode("node_var", "sqrt"),
    485                             [
    486                                 this.createNode(
    487                                     "node_op",
    488                                     "op_sub",
    489                                     this.createNode(
    490                                         "node_op",
    491                                         "op_mul",
    492                                         Type.deepCopy(arg[0]),
    493                                         Type.deepCopy(arg[0])
    494                                     ),
    495                                     this.createNode("node_const", 1.0)
    496                                 )
    497                             ]
    498                         )
    499                     );
    500                     break;
    501
    502                 case "atanh":
    503                     newNode = this.createNode(
    504                         "node_op",
    505                         "op_div",
    506                         this.createNode("node_const", 1.0),
    507                         this.createNode(
    508                             "node_op",
    509                             "op_sub",
    510                             this.createNode("node_const", 1.0),
    511                             this.createNode(
    512                                 "node_op",
    513                                 "op_mul",
    514                                 Type.deepCopy(arg[0]),
    515                                 Type.deepCopy(arg[0])
    516                             )
    517                         )
    518                     );
    519                     break;
    520
    521                 default:
    522                     newNode = this.createNode("node_const", 0.0);
    523                     console.log('Derivative of "' + fun + '" not yet implemented');
    524                     throw new Error("Error(" + this.line + "): ");
    525                 //  this._error('Derivative of "' + fun + '" not yet implemented');
    526             }
    527
    528             return newNode;
    529         },
    530
    531         derivative: function (node, varname) {
    532             var newNode;
    533
    534             switch (node.type) {
    535                 case "node_op":
    536                     switch (node.value) {
    537                         /*
    538                         case 'op_map':
    539                             if (true) {
    540                                 newNode = this.createNode('node_op', 'op_map',
    541                                         Type.deepCopy(node.children[0]),
    542                                         this.derivative(node.children[1], varname)
    543                                     );
    544                             } else {
    545                                 newNode = this.derivative(node.children[1], varname);
    546                             }
    547                             break;
    548                         */
    549                         case "op_execfun":
    550                             // f'(g(x))g'(x)
    551                             if (node.children[0].value == "pow") {
    552                                 newNode = this.deriveElementary(node, varname);
    553                             } else {
    554                                 if (node.children[1].length === 0) {
    555                                     newNode = this.createNode("node_const", 0.0);
    556                                 } else {
    557                                     newNode = this.createNode(
    558                                         "node_op",
    559                                         "op_mul",
    560                                         this.deriveElementary(node, varname),
    561                                         // Warning: single variable mode
    562                                         this.derivative(node.children[1][0], varname)
    563                                     );
    564                                 }
    565                             }
    566                             break;
    567
    568                         case "op_div":
    569                             // (f'g − g'f )/(g*g)
    570                             newNode = this.createNode(
    571                                 "node_op",
    572                                 "op_div",
    573                                 this.createNode(
    574                                     "node_op",
    575                                     "op_sub",
    576                                     this.createNode(
    577                                         "node_op",
    578                                         "op_mul",
    579                                         this.derivative(node.children[0], varname),
    580                                         Type.deepCopy(node.children[1])
    581                                     ),
    582                                     this.createNode(
    583                                         "node_op",
    584                                         "op_mul",
    585                                         Type.deepCopy(node.children[0]),
    586                                         this.derivative(node.children[1], varname)
    587                                     )
    588                                 ),
    589                                 this.createNode(
    590                                     "node_op",
    591                                     "op_mul",
    592                                     Type.deepCopy(node.children[1]),
    593                                     Type.deepCopy(node.children[1])
    594                                 )
    595                             );
    596                             break;
    597
    598                         case "op_mul":
    599                             // fg' + f'g
    600                             newNode = this.createNode(
    601                                 "node_op",
    602                                 "op_add",
    603                                 this.createNode(
    604                                     "node_op",
    605                                     "op_mul",
    606                                     Type.deepCopy(node.children[0]),
    607                                     this.derivative(node.children[1], varname)
    608                                 ),
    609                                 this.createNode(
    610                                     "node_op",
    611                                     "op_mul",
    612                                     this.derivative(node.children[0], varname),
    613                                     Type.deepCopy(node.children[1])
    614                                 )
    615                             );
    616                             break;
    617
    618                         case "op_neg":
    619                             newNode = this.createNode(
    620                                 "node_op",
    621                                 "op_neg",
    622                                 this.derivative(node.children[0], varname)
    623                             );
    624                             break;
    625
    626                         case "op_add":
    627                         case "op_sub":
    628                             newNode = this.createNode(
    629                                 "node_op",
    630                                 node.value,
    631                                 this.derivative(node.children[0], varname),
    632                                 this.derivative(node.children[1], varname)
    633                             );
    634                             break;
    635
    636                         case "op_exp":
    637                             // (f^g)' = f^g*(f'g/f + g' log(f))
    638                             newNode = this.createNode(
    639                                 "node_op",
    640                                 "op_mul",
    641                                 Type.deepCopy(node),
    642                                 this.createNode(
    643                                     "node_op",
    644                                     "op_add",
    645                                     this.createNode(
    646                                         "node_op",
    647                                         "op_mul",
    648                                         this.derivative(node.children[0], varname),
    649                                         this.createNode(
    650                                             "node_op",
    651                                             "op_div",
    652                                             Type.deepCopy(node.children[1]),
    653                                             Type.deepCopy(node.children[0])
    654                                         )
    655                                     ),
    656                                     this.createNode(
    657                                         "node_op",
    658                                         "op_mul",
    659                                         this.derivative(node.children[1], varname),
    660                                         this.createNode(
    661                                             "node_op",
    662                                             "op_execfun",
    663                                             this.createNode("node_var", "log"),
    664                                             [Type.deepCopy(node.children[0])]
    665                                         )
    666                                     )
    667                                 )
    668                             );
    669                             break;
    670                     }
    671                     break;
    672
    673                 case "node_var":
    674                     //console.log('node_var', node);
    675                     if (node.value === varname) {
    676                         newNode = this.createNode("node_const", 1.0);
    677                     } else {
    678                         newNode = this.createNode("node_const", 0.0);
    679                     }
    680                     break;
    681
    682                 case "node_const":
    683                     newNode = this.createNode("node_const", 0.0);
    684                     break;
    685
    686                 case "node_const_bool":
    687                     break;
    688
    689                 case "node_str":
    690                     break;
    691             }
    692
    693             return newNode;
    694         },
    695
    696         /**
    697          * f = map (x) -> x*sin(x);
    698          * Usages:
    699          *   h = D(f, x);
    700          *   h = map (x) -> D(f, x);
    701          * or
    702          *   D(x^2, x);
    703          */
    704         expandDerivatives: function (node, parent, ast) {
    705             var len, i, j, mapNode, codeNode,
    706                 ret, node2, newNode, mapName,
    707                 varname, vArray, order, isMap;
    708
    709             ret = 0;
    710             if (!node) {
    711                 return ret;
    712             }
    713
    714             this.line = node.line;
    715             this.col = node.col;
    716
    717             // First we have to go down in the tree.
    718             // This ensures that in cases like D(D(f,x),x) the inner D is expanded first.
    719             len = node.children.length;
    720             for (i = 0; i < len; ++i) {
    721                 if (node.children[i] && node.children[i].type) {
    722                     node.children[i] = this.expandDerivatives(node.children[i], node, ast);
    723                 } else if (Type.isArray(node.children[i])) {
    724                     for (j = 0; j < node.children[i].length; ++j) {
    725                         if (node.children[i][j] && node.children[i][j].type) {
    726                             node.children[i][j] = this.expandDerivatives(
    727                                 node.children[i][j],
    728                                 node,
    729                                 ast
    730                             );
    731                         }
    732                     }
    733                 }
    734             }
    735
    736             switch (node.type) {
    737                 case "node_op":
    738                     switch (node.value) {
    739                         case "op_execfun":
    740                             if (node.children[0] && node.children[0].value === "D") {
    741                                 /*
    742                                  * Distinguish the cases:
    743                                  *   D(f, x) where f is map -> isMap = true
    744                                  * and
    745                                  *   D(2*x, x), D(sin(x), x), ...  -> isMap = false
    746                                  */
    747                                 isMap = false;
    748                                 if (node.children[1][0].type == "node_var") {
    749                                     mapName = node.children[1][0].value;
    750                                     mapNode = this.findMapNode(mapName, ast);
    751                                     if (mapNode !== null) {
    752                                         isMap = true;
    753                                     }
    754                                 }
    755
    756                                 if (isMap) {
    757                                     /*
    758                                      * Derivative of map, that is compute D(f,x)
    759                                      * where e.g. f = map (x) -> x^2
    760                                      *
    761                                      * First step: find node where the map is defined
    762                                      */
    763                                     // Already done above
    764                                     // mapName = node.children[1][0].value;
    765                                     // mapNode = this.findMapNode(mapName, ast);
    766                                     vArray = mapNode.children[0];
    767
    768                                     // Variable name for differentiation
    769                                     if (node.children[1].length >= 2) {
    770                                         varname = node.children[1][1].value;
    771                                     } else {
    772                                         varname = mapNode.children[0][0]; // Usually it's 'x'
    773                                     }
    774                                     codeNode = mapNode.children[1];
    775                                 } else {
    776                                     /*
    777                                      * Derivative of expression, e.g.
    778                                      *     D(2*x, x) or D(sin(x), x)
    779                                      */
    780                                     codeNode = node.children[1][0];
    781                                     vArray = ["x"];
    782
    783                                     // Variable name for differentiation and order
    784                                     if (node.children[1].length >= 2) {
    785                                         varname = node.children[1][1].value;
    786                                     } else {
    787                                         varname = "x";
    788                                     }
    789                                 }
    790
    791                                 // Differentiation order
    792                                 if (node.children[1].length >= 3) {
    793                                     order = node.children[1][2].value;
    794                                 } else {
    795                                     order = 1;
    796                                 }
    797
    798                                 // Create node which contains the derivative
    799                                 newNode = codeNode;
    800                                 //newNode = this.removeTrivialNodes(newNode);
    801                                 if (order >= 1) {
    802                                     while (order >= 1) {
    803                                         newNode = this.derivative(newNode, varname);
    804                                         newNode = this.removeTrivialNodes(newNode);
    805                                         order--;
    806                                     }
    807                                 }
    808
    809                                 // Replace the node containing e.g. D(f,x) by the derivative.
    810                                 if (parent.type == "node_op" && parent.value == "op_assign") {
    811                                     // If D is an assignment it has to be replaced by a map
    812                                     // h = D(f, x)
    813                                     node2 = this.createNode(
    814                                         "node_op",
    815                                         "op_map",
    816                                         vArray,
    817                                         newNode
    818                                     );
    819                                 } else {
    820                                     node2 = newNode;
    821                                 }
    822
    823                                 this.setMath(node2);
    824                                 node.type = node2.type;
    825                                 node.value = node2.value;
    826                                 if (node2.children.length > 0) {
    827                                     node.children[0] = node2.children[0];
    828                                 }
    829                                 if (node2.children.length > 1) {
    830                                     node.children[1] = node2.children[1];
    831                                 }
    832                             }
    833                     }
    834                     break;
    835
    836                 case "node_var":
    837                 case "node_const":
    838                 case "node_const_bool":
    839                 case "node_str":
    840                     break;
    841             }
    842
    843             return node;
    844         },
    845
    846         removeTrivialNodes: function (node) {
    847             var i, len, n0, n1, swap;
    848
    849             // In case of 'op_execfun' the children[1] node is an array.
    850             if (Type.isArray(node)) {
    851                 len = node.length;
    852                 for (i = 0; i < len; ++i) {
    853                     node[i] = this.removeTrivialNodes(node[i]);
    854                 }
    855             }
    856             if (node.type != "node_op" || !node.children) {
    857                 return node;
    858             }
    859
    860             len = node.children.length;
    861             for (i = 0; i < len; ++i) {
    862                 this.mayNotBeSimplified = false;
    863                 do {
    864                     node.children[i] = this.removeTrivialNodes(node.children[i]);
    865                 } while (this.mayNotBeSimplified);
    866             }
    867
    868             switch (node.value) {
    869                 // Allow maps of the form
    870                 //  map (x) -> x;
    871                 case "op_map":
    872                     n0 = node.children[0];
    873                     n1 = node.children[1];
    874                     if (n1.type == "node_var") {
    875                         for (i = 0; i < n0.length; ++i) {
    876                             // Allow maps of the form map(x) -> x
    877                             if (n0[i] == n1.value) {
    878                                 n1.isMath = true;
    879                                 break;
    880                             }
    881                         }
    882                     }
    883                     break;
    884
    885                 // a + 0 -> a
    886                 // 0 + a -> a
    887                 case "op_add":
    888                     n0 = node.children[0];
    889                     n1 = node.children[1];
    890                     if (n0.type == "node_const" && n0.value === 0.0) {
    891                         return n1;
    892                     }
    893                     if (n1.type == "node_const" && n1.value === 0.0) {
    894                         return n0;
    895                     }
    896
    897                     // const + const -> const
    898                     if (n0.type == "node_const" && n1.type == "node_const") {
    899                         n0.value += n1.value;
    900                         return n0;
    901                     }
    902                     break;
    903
    904                 // 1 * a = a
    905                 // a * 1 = a
    906                 // a * 0 = 0
    907                 // 0 * a = 0
    908                 // - * - = +
    909                 // Order children
    910                 case "op_mul":
    911                     n0 = node.children[0];
    912                     n1 = node.children[1];
    913                     if (n0.type == "node_const" && n0.value == 1.0) {
    914                         return n1;
    915                     }
    916                     if (n1.type == "node_const" && n1.value == 1.0) {
    917                         return n0;
    918                     }
    919                     if (n0.type == "node_const" && n0.value === 0.0) {
    920                         return n0;
    921                     }
    922                     if (n1.type == "node_const" && n1.value === 0.0) {
    923                         return n1;
    924                     }
    925                     if (n1.type == "node_const" && n1.value === 0.0) {
    926                         return n1;
    927                     }
    928
    929                     // (-a) * (-b) -> a*b
    930                     if (
    931                         n0.type == "node_op" &&
    932                         n0.value == "op_neg" &&
    933                         n1.type == "node_op" &&
    934                         n1.value == "op_neg"
    935                     ) {
    936                         node.children = [n0.children[0], n1.children[0]];
    937                         this.mayNotBeSimplified = true;
    938                         return node;
    939                     }
    940                     // (-a) * b -> -(a*b)
    941                     if (n0.value == "op_neg" && n1.value != "op_neg") {
    942                         node.type = "node_op";
    943                         node.value = "op_neg";
    944                         node.children = [
    945                             this.createNode("node_op", "op_mul", n0.children[0], n1)
    946                         ];
    947                         this.mayNotBeSimplified = true;
    948                         return node;
    949                     }
    950                     // a * (-b) -> -(a*b)
    951                     if (n0.value != "op_neg" && n1.value == "op_neg") {
    952                         node.type = "node_op";
    953                         node.value = "op_neg";
    954                         node.children = [
    955                             this.createNode("node_op", "op_mul", n0, n1.children[0])
    956                         ];
    957                         this.mayNotBeSimplified = true;
    958                         return node;
    959                     }
    960                     // (1 / a) * b -> a / b
    961                     if (
    962                         n0.value == "op_div" &&
    963                         n0.children[0].type == "node_const" &&
    964                         n0.children[0].value == 1.0
    965                     ) {
    966                         node.type = "node_op";
    967                         node.value = "op_div";
    968                         node.children = [n1, n0.children[1]];
    969                         this.mayNotBeSimplified = true;
    970                         return node;
    971                     }
    972                     // a * (1 / b) -> a / b
    973                     if (
    974                         n1.value == "op_div" &&
    975                         n1.children[0].type == "node_const" &&
    976                         n1.children[0].value == 1.0
    977                     ) {
    978                         node.type = "node_op";
    979                         node.value = "op_div";
    980                         node.children = [n0, n1.children[1]];
    981                         this.mayNotBeSimplified = true;
    982                         return node;
    983                     }
    984
    985                     // Order children
    986                     // a * const -> const * a
    987                     if (n0.type != "node_const" && n1.type == "node_const") {
    988                         node.children = [n1, n0];
    989                         this.mayNotBeSimplified = true;
    990                         return node;
    991                     }
    992                     // a + (-const) -> -const + a
    993                     if (
    994                         n0.type != "node_const" &&
    995                         n1.type == "node_op" &&
    996                         n1.value == "op_neg" &&
    997                         n1.children[0].type == "node_const"
    998                     ) {
    999                         node.children = [n1, n0];
    1000                         this.mayNotBeSimplified = true;
    1001                         return node;
    1002                     }
    1003
    1004                     // a * var -> var * a
    1005                     // a * fun -> fun * a
    1006                     if (
    1007                         n0.type == "node_op" &&
    1008                         n0.value != "op_execfun" &&
    1009                         (n1.type == "node_var" ||
    1010                             (n1.type == "node_op" && n1.value == "op_execfun"))
    1011                     ) {
    1012                         node.children = [n1, n0];
    1013                         this.mayNotBeSimplified = true;
    1014                         return node;
    1015                     }
    1016
    1017                     // a + (-var) -> -var  + a
    1018                     if (
    1019                         n0.type != "node_op" &&
    1020                         n1.type == "node_op" &&
    1021                         n1.value == "op_neg" &&
    1022                         n1.children[0].type == "node_var"
    1023                     ) {
    1024                         node.children = [n1, n0];
    1025                         this.mayNotBeSimplified = true;
    1026                         return node;
    1027                     }
    1028                     // a * (const * b) -> const * (a*b)
    1029                     // a * (const / b) -> const * (a/b)
    1030                     if (
    1031                         n0.type != "node_const" &&
    1032                         n1.type == "node_op" &&
    1033                         (n1.value == "op_mul" || n1.value == "op_div") &&
    1034                         n1.children[0].type == "node_const"
    1035                     ) {
    1036                         swap = n1.children[0];
    1037                         n1.children[0] = n0;
    1038                         node.children = [swap, n1];
    1039                         this.mayNotBeSimplified = true;
    1040                         return node;
    1041                     }
    1042
    1043                     // (const * a) * b -> const * (a * b)
    1044                     if (
    1045                         n1.type != "node_const" &&
    1046                         n0.type == "node_op" &&
    1047                         n0.value == "op_mul" &&
    1048                         n0.children[0].type == "node_const"
    1049                     ) {
    1050                         node.children = [
    1051                             n0.children[0],
    1052                             this.createNode("node_op", "op_mul", n0.children[1], n1)
    1053                         ];
    1054                         this.mayNotBeSimplified = true;
    1055                         return node;
    1056                     }
    1057
    1058                     // const * const -> const
    1059                     if (n0.type == "node_const" && n1.type == "node_const") {
    1060                         n0.value *= n1.value;
    1061                         return n0;
    1062                     }
    1063
    1064                     // const * (const * a) -> const * a
    1065                     // const * (const / a) -> const / a
    1066                     if (
    1067                         n0.type == "node_const" &&
    1068                         n1.type == "node_op" &&
    1069                         (n1.value == "op_mul" || n1.value == "op_div") &&
    1070                         n1.children[0].type == "node_const"
    1071                     ) {
    1072                         n1.children[0].value *= n0.value;
    1073                         return n1;
    1074                     }
    1075
    1076                     // a * a-> a^2
    1077                     n0.hash = this.parser.compile(n0);
    1078                     n1.hash = this.parser.compile(n1);
    1079                     if (n0.hash === n1.hash) {
    1080                         node.value = "op_exp";
    1081                         node.children[1] = this.createNode("node_const", 2.0);
    1082                         return node;
    1083                     }
    1084
    1085                     if (
    1086                         n0.type == "node_const" &&
    1087                         n1.type == "node_op" &&
    1088                         (n1.value == "op_mul" || n1.value == "op_div") &&
    1089                         n1.children[0].type == "node_const"
    1090                     ) {
    1091                         n1.children[0].value *= n0.value;
    1092                         return n1;
    1093                     }
    1094
    1095                     // a * a^b -> a^(b+1)
    1096                     if (n1.type == "node_op" && n1.value == "op_exp") {
    1097                         if (!n0.hash) {
    1098                             n0.hash = this.parser.compile(n0);
    1099                         }
    1100                         if (!n1.children[0].hash) {
    1101                             n1.children[0].hash = this.parser.compile(n1.children[0]);
    1102                         }
    1103                         if (n0.hash === n1.children[0].hash) {
    1104                             n1.children[1] = this.createNode(
    1105                                 "node_op",
    1106                                 "op_add",
    1107                                 n1.children[1],
    1108                                 this.createNode("node_const", 1.0)
    1109                             );
    1110                             this.mayNotBeSimplified = true;
    1111                             return n1;
    1112                         }
    1113                     }
    1114
    1115                     // a^b * a^c -> a^(b+c)
    1116                     if (
    1117                         n0.type == "node_op" &&
    1118                         n0.value == "op_exp" &&
    1119                         n1.type == "node_op" &&
    1120                         n1.value == "op_exp"
    1121                     ) {
    1122                         n0.children[0].hash = this.parser.compile(n0.children[0]);
    1123                         n1.children[0].hash = this.parser.compile(n1.children[0]);
    1124                         if (n0.children[0].hash === n1.children[0].hash) {
    1125                             n0.children[1] = this.createNode(
    1126                                 "node_op",
    1127                                 "op_add",
    1128                                 n0.children[1],
    1129                                 n1.children[1]
    1130                             );
    1131                             this.mayNotBeSimplified = true;
    1132                             return n0;
    1133                         }
    1134                     }
    1135
    1136                     break;
    1137
    1138                 // 0 - a -> -a
    1139                 // a - 0 -> a
    1140                 // a - a -> 0
    1141                 case "op_sub":
    1142                     n0 = node.children[0];
    1143                     n1 = node.children[1];
    1144                     if (n0.type == "node_const" && n0.value === 0.0) {
    1145                         node.value = "op_neg";
    1146                         node.children[0] = n1;
    1147                         return node;
    1148                     }
    1149                     if (n1.type == "node_const" && n1.value === 0.0) {
    1150                         return n0;
    1151                     }
    1152                     if (
    1153                         n0.type == "node_const" &&
    1154                         n1.type == "node_const" &&
    1155                         n0.value == n1.value
    1156                     ) {
    1157                         return this.createNode("node_const", 0.0);
    1158                     }
    1159                     if (
    1160                         n0.type == "node_var" &&
    1161                         n1.type == "node_var" &&
    1162                         n0.value == n1.value
    1163                     ) {
    1164                         return this.createNode("node_const", 0.0);
    1165                     }
    1166
    1167                     // const - const -> const
    1168                     if (n0.type == "node_const" && n1.type == "node_const") {
    1169                         n0.value -= n1.value;
    1170                         return n0;
    1171                     }
    1172
    1173                     // const * a - const * a -> const * a
    1174                     if (
    1175                         n0.type == "node_op" &&
    1176                         n0.value == "op_mul" &&
    1177                         n1.type == "node_op" &&
    1178                         n1.value == "op_mul"
    1179                     ) {
    1180                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1181                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1182                         if (n0.children[1].hash === n1.children[1].hash) {
    1183                             node.value = "op_mul";
    1184                             node.children = [
    1185                                 this.createNode(
    1186                                     "node_op",
    1187                                     "op_sub",
    1188                                     n0.children[0],
    1189                                     n1.children[0]
    1190                                 ),
    1191                                 n0.children[1]
    1192                             ];
    1193                             this.mayNotBeSimplified = true;
    1194                             return node;
    1195                         }
    1196                     }
    1197                     // const * a - a -> (const - 1) * a
    1198                     if (n0.type == "node_op" && n0.value == "op_mul") {
    1199                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1200                         n1.hash = this.parser.compile(n1);
    1201                         if (n0.children[1].hash === n1.hash) {
    1202                             node.value = "op_mul";
    1203                             node.children = [
    1204                                 this.createNode(
    1205                                     "node_op",
    1206                                     "op_sub",
    1207                                     n0.children[0],
    1208                                     this.createNode("node_const", 1.0)
    1209                                 ),
    1210                                 n1
    1211                             ];
    1212                             this.mayNotBeSimplified = true;
    1213                             return node;
    1214                         }
    1215                     }
    1216                     // a - const*a -> (const - 1) * a
    1217                     if (n1.type == "node_op" && n1.value == "op_mul") {
    1218                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1219                         n0.hash = this.parser.compile(n0);
    1220                         if (n1.children[1].hash === n0.hash) {
    1221                             node.value = "op_mul";
    1222                             node.children = [
    1223                                 this.createNode(
    1224                                     "node_op",
    1225                                     "op_sub",
    1226                                     this.createNode("node_const", 1.0),
    1227                                     n1.children[0]
    1228                                 ),
    1229                                 n0
    1230                             ];
    1231                             this.mayNotBeSimplified = true;
    1232                             return node;
    1233                         }
    1234                     }
    1235
    1236                     break;
    1237
    1238                 // -0 -> 0
    1239                 // -(-b) = b
    1240                 case "op_neg":
    1241                     n0 = node.children[0];
    1242                     if (n0.type == "node_const" && n0.value === 0.0) {
    1243                         return n0;
    1244                     }
    1245                     if (n0.type == "node_op" && n0.value == "op_neg") {
    1246                         return n0.children[0];
    1247                     }
    1248                     break;
    1249
    1250                 // a / a -> 1, a != 0
    1251                 // 0 / a -> 0, a != 0
    1252                 // a / 0 -> Infinity, a != 0
    1253                 // 0 / 0 -> NaN, a == 0
    1254                 case "op_div":
    1255                     n0 = node.children[0];
    1256                     n1 = node.children[1];
    1257                     if (
    1258                         n0.type == "node_const" &&
    1259                         n1.type == "node_const" &&
    1260                         n0.value == n1.value &&
    1261                         n0.value !== 0
    1262                     ) {
    1263                         n0.value = 1.0;
    1264                         return n0;
    1265                     }
    1266                     if (
    1267                         n0.type == "node_const" &&
    1268                         n0.value === 0 &&
    1269                         n1.type == "node_const" &&
    1270                         n1.value !== 0
    1271                     ) {
    1272                         n0.value = 0.0;
    1273                         return n0;
    1274                     }
    1275
    1276                     // Risky: 0 / (something != 0) -> 0.0
    1277                     if (
    1278                         n0.type == "node_const" &&
    1279                         n0.value === 0 &&
    1280                         (n1.type == "node_op" || n1.type == "node_var")
    1281                     ) {
    1282                         node.type = "node_const";
    1283                         node.value = 0.0;
    1284                         return node;
    1285                     }
    1286
    1287                     if (
    1288                         n0.type == "node_var" &&
    1289                         n1.type == "node_var" &&
    1290                         n0.value == n1.value
    1291                     ) {
    1292                         return this.createNode("node_const", 1.0);
    1293                     }
    1294                     if (
    1295                         n0.type == "node_const" &&
    1296                         n0.value !== 0 &&
    1297                         n1.type == "node_const" &&
    1298                         n1.value === 0
    1299                     ) {
    1300                         if (n0.value > 0.0) {
    1301                             n0.value = Infinity;
    1302                         } else {
    1303                             n0.value = -Infinity; // Do we ever need this?
    1304                         }
    1305                         return n0;
    1306                     }
    1307
    1308                     // (-a) / (-b) -> a/b
    1309                     if (
    1310                         n0.type == "node_op" &&
    1311                         n0.value == "op_neg" &&
    1312                         n1.type == "node_op" &&
    1313                         n1.value == "op_neg"
    1314                     ) {
    1315                         node.children = [n0.children[0], n1.children[0]];
    1316                         this.mayNotBeSimplified = true;
    1317                         return node;
    1318                     }
    1319                     // (-a) / b -> -(a/b)
    1320                     if (n0.value == "op_neg" && n1.value != "op_neg") {
    1321                         node.type = "node_op";
    1322                         node.value = "op_neg";
    1323                         node.children = [
    1324                             this.createNode("node_op", "op_div", n0.children[0], n1)
    1325                         ];
    1326                         this.mayNotBeSimplified = true;
    1327                         return node;
    1328                     }
    1329                     // a / (-b) -> -(a/b)
    1330                     if (n0.value != "op_neg" && n1.value == "op_neg") {
    1331                         node.type = "node_op";
    1332                         node.value = "op_neg";
    1333                         node.children = [
    1334                             this.createNode("node_op", "op_div", n0, n1.children[0])
    1335                         ];
    1336                         this.mayNotBeSimplified = true;
    1337                         return node;
    1338                     }
    1339
    1340                     // a^b / a -> a^(b-1)
    1341                     if (n0.type == "node_op" && n0.value == "op_exp") {
    1342                         if (!n1.hash) {
    1343                             n1.hash = this.parser.compile(n1);
    1344                         }
    1345                         if (!n0.children[0].hash) {
    1346                             n0.children[0].hash = this.parser.compile(n0.children[0]);
    1347                         }
    1348                         if (n1.hash === n0.children[0].hash) {
    1349                             n0.children[1] = this.createNode(
    1350                                 "node_op",
    1351                                 "op_sub",
    1352                                 n0.children[1],
    1353                                 this.createNode("node_const", 1.0)
    1354                             );
    1355                             this.mayNotBeSimplified = true;
    1356                             return n0;
    1357                         }
    1358                     }
    1359
    1360                     // (const * a) / b -> const * (a / b)
    1361                     if (
    1362                         n1.type != "node_const" &&
    1363                         n0.type == "node_op" &&
    1364                         n0.value == "op_mul" &&
    1365                         n0.children[0].type == "node_const"
    1366                     ) {
    1367                         node.value = "op_mul";
    1368                         node.children = [
    1369                             n0.children[0],
    1370                             this.createNode("node_op", "op_div", n0.children[1], n1)
    1371                         ];
    1372                         this.mayNotBeSimplified = true;
    1373                         return node;
    1374                     }
    1375
    1376                     // a^b / a^c -> a^(b-c)
    1377                     if (
    1378                         n0.type == "node_op" &&
    1379                         n0.value == "op_exp" &&
    1380                         n1.type == "node_op" &&
    1381                         n1.value == "op_exp"
    1382                     ) {
    1383                         n0.children[0].hash = this.parser.compile(n0.children[0]);
    1384                         n1.children[0].hash = this.parser.compile(n1.children[0]);
    1385                         if (n0.children[0].hash === n1.children[0].hash) {
    1386                             n0.children[1] = this.createNode(
    1387                                 "node_op",
    1388                                 "op_sub",
    1389                                 n0.children[1],
    1390                                 n1.children[1]
    1391                             );
    1392                             this.mayNotBeSimplified = true;
    1393                             return n0;
    1394                         }
    1395                     }
    1396
    1397                     break;
    1398
    1399                 // a^0 = 1
    1400                 // a^1 -> a
    1401                 // 1^a -> 1
    1402                 // 0^a -> 0: a const != 0
    1403                 case "op_exp":
    1404                     n0 = node.children[0];
    1405                     n1 = node.children[1];
    1406                     if (n1.type == "node_const" && n1.value === 0.0) {
    1407                         n1.value = 1.0;
    1408                         return n1;
    1409                     }
    1410                     if (n1.type == "node_const" && n1.value == 1.0) {
    1411                         return n0;
    1412                     }
    1413                     if (n0.type == "node_const" && n0.value == 1.0) {
    1414                         return n0;
    1415                     }
    1416                     if (
    1417                         n0.type == "node_const" &&
    1418                         n0.value === 0.0 &&
    1419                         n1.type == "node_const" &&
    1420                         n1.value !== 0.0
    1421                     ) {
    1422                         return n0;
    1423                     }
    1424
    1425                     // (a^b)^c -> a^(b*c)
    1426                     if (n0.type == "node_op" && n0.value == "op_exp") {
    1427                         node.children = [
    1428                             n0.children[0],
    1429                             this.createNode("node_op", "op_mul", n0.children[1], n1)
    1430                         ];
    1431                         return node;
    1432                     }
    1433                     break;
    1434             }
    1435
    1436             switch (node.value) {
    1437                 // const_1 + const_2 -> (const_1 + const_2)
    1438                 // a + a -> 2*a
    1439                 // a + (-b) = a - b
    1440                 case "op_add":
    1441                     n0 = node.children[0];
    1442                     n1 = node.children[1];
    1443                     if (
    1444                         n0.type == "node_const" &&
    1445                         n1.type == "node_const" &&
    1446                         n0.value == n1.value
    1447                     ) {
    1448                         n0.value += n1.value;
    1449                         return n0;
    1450                     }
    1451
    1452                     if (
    1453                         n0.type == "node_var" &&
    1454                         n1.type == "node_var" &&
    1455                         n0.value == n1.value
    1456                     ) {
    1457                         node.children[0] = this.createNode("node_const", 2.0);
    1458                         node.value = "op_mul";
    1459                         return node;
    1460                     }
    1461
    1462                     if (n0.type == "node_op" && n0.value == "op_neg") {
    1463                         node.value = "op_sub";
    1464                         node.children[0] = n1;
    1465                         node.children[1] = n0.children[0];
    1466                         this.mayNotBeSimplified = true;
    1467                         return node;
    1468                     }
    1469
    1470                     if (n1.type == "node_op" && n1.value == "op_neg") {
    1471                         node.value = "op_sub";
    1472                         node.children[1] = n1.children[0];
    1473                         this.mayNotBeSimplified = true;
    1474                         return node;
    1475                     }
    1476
    1477                     // const * a + const * a -> const * a
    1478                     if (
    1479                         n0.type == "node_op" &&
    1480                         n0.value == "op_mul" &&
    1481                         n1.type == "node_op" &&
    1482                         n1.value == "op_mul"
    1483                     ) {
    1484                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1485                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1486                         if (n0.children[1].hash === n1.children[1].hash) {
    1487                             node.value = "op_mul";
    1488                             node.children = [
    1489                                 this.createNode(
    1490                                     "node_op",
    1491                                     "op_add",
    1492                                     n0.children[0],
    1493                                     n1.children[0]
    1494                                 ),
    1495                                 n0.children[1]
    1496                             ];
    1497                             this.mayNotBeSimplified = true;
    1498                             return node;
    1499                         }
    1500                     }
    1501                     // const * a + a -> (const + 1) * a
    1502                     if (n0.type == "node_op" && n0.value == "op_mul") {
    1503                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1504                         n1.hash = this.parser.compile(n1);
    1505                         if (n0.children[1].hash === n1.hash) {
    1506                             node.value = "op_mul";
    1507                             node.children = [
    1508                                 this.createNode(
    1509                                     "node_op",
    1510                                     "op_add",
    1511                                     n0.children[0],
    1512                                     this.createNode("node_const", 1.0)
    1513                                 ),
    1514                                 n1
    1515                             ];
    1516                             this.mayNotBeSimplified = true;
    1517                             return node;
    1518                         }
    1519                     }
    1520                     // a + const*a -> (const + 1) * a
    1521                     if (n1.type == "node_op" && n1.value == "op_mul") {
    1522                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1523                         n0.hash = this.parser.compile(n0);
    1524                         if (n1.children[1].hash === n0.hash) {
    1525                             node.value = "op_mul";
    1526                             node.children = [
    1527                                 this.createNode(
    1528                                     "node_op",
    1529                                     "op_add",
    1530                                     this.createNode("node_const", 1.0),
    1531                                     n1.children[0]
    1532                                 ),
    1533                                 n0
    1534                             ];
    1535                             this.mayNotBeSimplified = true;
    1536                             return node;
    1537                         }
    1538                     }
    1539
    1540                     break;
    1541
    1542                 // a - (-b) = a + b
    1543                 case "op_sub":
    1544                     n0 = node.children[0];
    1545                     n1 = node.children[1];
    1546                     if (n1.type == "node_op" && n1.value == "op_neg") {
    1547                         node.value = "op_add";
    1548                         node.children[1] = n1.children[0];
    1549                         this.mayNotBeSimplified = true;
    1550                         return node;
    1551                     }
    1552                     break;
    1553
    1554                 case "op_execfun":
    1555                     return this.simplifyElementary(node);
    1556             }
    1557
    1558             return node;
    1559         },
    1560
    1561         simplifyElementary: function (node) {
    1562             var fun = node.children[0].value,
    1563                 arg = node.children[1];
    1564
    1565             // Catch errors of the form sin()
    1566             if (arg.length == 0) {
    1567                 return node;
    1568             }
    1569
    1570             switch (fun) {
    1571                 // sin(0) -> 0
    1572                 // sin(PI) -> 0
    1573                 // sin (int * PI) -> 0
    1574                 // sin (PI * int) -> 0
    1575                 // Same for tan()
    1576                 case "sin":
    1577                 case "tan":
    1578                     if (arg[0].type == "node_const" && arg[0].value === 0) {
    1579                         node.type = "node_const";
    1580                         node.value = 0.0;
    1581                         return node;
    1582                     }
    1583                     if (arg[0].type == "node_var" && arg[0].value == "PI") {
    1584                         node.type = "node_const";
    1585                         node.value = 0.0;
    1586                         return node;
    1587                     }
    1588                     if (
    1589                         arg[0].type == "node_op" &&
    1590                         arg[0].value == "op_mul" &&
    1591                         arg[0].children[0].type == "node_const" &&
    1592                         arg[0].children[0].value % 1 === 0 &&
    1593                         arg[0].children[1].type == "node_var" &&
    1594                         arg[0].children[1].value == "PI"
    1595                     ) {
    1596                         node.type = "node_const";
    1597                         node.value = 0.0;
    1598                         return node;
    1599                     }
    1600                     break;
    1601
    1602                 // cos(0) -> 1.0
    1603                 // cos(PI) -> -1.0
    1604                 // cos(int * PI) -> +/- 1.0
    1605                 // cos(PI * int) -> +/- 1.0
    1606                 case "cos":
    1607                     if (arg[0].type == "node_const" && arg[0].value === 0) {
    1608                         node.type = "node_const";
    1609                         node.value = 1.0;
    1610                         return node;
    1611                     }
    1612                     if (arg[0].type == "node_var" && arg[0].value == "PI") {
    1613                         node.type = "node_op";
    1614                         node.value = "op_neg";
    1615                         node.children = [this.createNode("node_const", 1.0)];
    1616                         return node;
    1617                     }
    1618                     /*
    1619                     if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' &&
    1620                         ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 &&
    1621                          arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') ||
    1622                          (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 &&
    1623                           arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) {
    1624                         node.type = 'node_const';
    1625                         node.value = 1.0;
    1626                         return node;
    1627                     }
    1628                     */
    1629                     break;
    1630
    1631                 // exp(0) -> 1
    1632                 case "exp":
    1633                     if (arg[0].type == "node_const" && arg[0].value === 0) {
    1634                         node.type = "node_const";
    1635                         node.value = 1.0;
    1636                         return node;
    1637                     }
    1638                     break;
    1639
    1640                 // pow(a, 0) -> 1
    1641                 case "pow":
    1642                     if (arg[1].type == "node_const" && arg[1].value === 0) {
    1643                         node.type = "node_const";
    1644                         node.value = 1.0;
    1645                         return node;
    1646                     }
    1647                     break;
    1648             }
    1649
    1650             return node;
    1651         }
    1652     }
    1653 );
    1654
    1655 export default JXG.CA;
    1656