      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 <http://www.gnu.org/licenses/>
     25     and <http://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 /* depends:
     33  jxg
     34  parser/geonext
     35  base/constants
     36  base/text
     37  math/math
     38  math/geometry
     39  math/statistics
     40  utils/type
     41  utils/uuid
     42  */
     43
     44 /**
     45  * @fileoverview Here, the computer algebra algorithms are implemented.
     46  */
     47
     48 define([
     49     'jxg', 'base/constants', 'base/text', 'math/math', 'math/geometry', 'math/statistics', 'utils/type', 'utils/env'
     50 ], function (JXG, Const, Text, Mat, Geometry, Statistics, Type, Env) {
     51
     52     "use strict";
     53
     54     /**
     55      * A JessieCode object provides an interface to the parser and stores all variables and objects used within a JessieCode script.
     56      * The optional argument <tt>code</tt> is interpreted after initializing. To evaluate more code after initializing a JessieCode instance
     57      * please use {@link JXG.JessieCode#parse}. For code snippets like single expressions use {@link JXG.JessieCode#snippet}.
     58      * @constructor
     59      * @param {String} [code] Code to parse.
     60      * @param {Boolean} [geonext=false] Geonext compatibility mode.
     61      */
     62     JXG.CA = function (node, createNode, parser) {
     63         this.node = node;
     64         this.createNode = createNode;
     65         this.parser = parser;
     66     };
     67
     68     JXG.extend(JXG.CA.prototype, /** @lends JXG.CA.prototype */ {
     69         findMapNode: function (mapname, node) {
     70             var i, len, ret;
     71
     72             //console.log("FINDMAP", node);
     73             if (node.value === 'op_assign' && node.children[0].value === mapname) {
     74                 return node.children[1];
     75             } else if (node.children) {
     76                 len = node.children.length;
     77                 for (i = 0; i < len; ++i) {
     78                     ret = this.findMapNode(mapname, node.children[i]);
     79                     if (ret !== null) {
     80                         return ret;
     81                     }
     82                 }
     83             }
     84             return null;
     85         },
     86
     87         /**
     88          * Declare all subnodes as math nodes,
     89          * i.e recursively set node.isMath = true;
     90          */
     91         setMath: function (node) {
     92             var i, len;
     93
     94             if ((node.type == 'node_op' && (
     95                 node.value == 'op_add' || node.value == 'op_sub' ||
     96                 node.value == 'op_mul' || node.value == 'op_div' ||
     97                 node.value == 'op_neg' || node.value == 'op_execfun' ||
     98                 node.value == 'op_exp')) ||
     99                 node.type == 'node_var' || node.type == 'node_const') {
    100
    101                 node.isMath = true;
    102             }
    103             if (node.children) {
    104                 len = node.children.length;
    105                 for (i = 0; i < len; ++i) {
    106                     this.setMath(node.children[i]);
    107                 }
    108             }
    109         },
    110
    111         deriveElementary: function (node, varname) {
    112             var fun = node.children[0].value,
    113                 arg = node.children[1],
    114                 newNode;
    115
    116
    117             switch (fun) {
    118                 case 'abs':
    119                     // x / sqrt(x * x)
    120                     newNode = this.createNode('node_op', 'op_div',
    121                         arg[0],
    122                         this.createNode('node_op', 'op_execfun',
    123                             this.createNode('node_var', 'sqrt'),
    124                             [this.createNode('node_op', 'op_mul',
    125                                 Type.deepCopy(arg[0]),
    126                                 Type.deepCopy(arg[0])
    127                             )]
    128                         )
    129                     );
    130                     break;
    131
    132                 case 'sqrt':
    133                     newNode = this.createNode('node_op', 'op_div',
    134                         this.createNode('node_const', 1.0),
    135                         this.createNode('node_op', 'op_mul',
    136                             this.createNode('node_const', 2.0),
    137                             this.createNode(node.type, node.value,
    138                                 Type.deepCopy(node.children[0]),
    139                                 Type.deepCopy(node.children[1])
    140                             )
    141                         )
    142                     );
    143                     break;
    144
    145                 case 'sin':
    146                     newNode = this.createNode('node_op', 'op_execfun',
    147                         this.createNode('node_var', 'cos'),
    148                         Type.deepCopy(arg)
    149                     );
    150                     break;
    151
    152                 case 'cos':
    153                     newNode = this.createNode('node_op', 'op_neg',
    154                         this.createNode('node_op', 'op_execfun',
    155                             this.createNode('node_var', 'sin'),
    156                             Type.deepCopy(arg)
    157                         )
    158                     );
    159                     break;
    160
    161                 case 'tan':
    162                     newNode = this.createNode('node_op', 'op_div',
    163                         this.createNode('node_const', 1.0),
    164                         this.createNode('node_op', 'op_exp',
    165                             this.createNode('node_op', 'op_execfun',
    166                                 this.createNode('node_var', 'cos'),
    167                                 Type.deepCopy(arg)
    168                             ),
    169                             this.createNode('node_const', 2)
    170                         )
    171                     );
    172                     break;
    173
    174                 case 'cot':
    175                     newNode = this.createNode('node_op', 'op_neg',
    176                         this.createNode('node_op', 'op_div',
    177                             this.createNode('node_const', 1.0),
    178                             this.createNode('node_op', 'op_exp',
    179                                 this.createNode('node_op', 'op_execfun',
    180                                     this.createNode('node_var', 'sin'),
    181                                     Type.deepCopy(arg)
    182                                 ),
    183                                 this.createNode('node_const', 2)
    184                             )
    185                         )
    186                     );
    187                     break;
    188
    189                 case 'exp':
    190                     newNode = this.createNode(node.type, node.value,
    191                         Type.deepCopy(node.children[0]),
    192                         Type.deepCopy(node.children[1])
    193                     );
    194                     break;
    195
    196                 case 'pow':
    197                     // (f^g)' = f^g*(f'g/f + g' log(f))
    198                     newNode = this.createNode('node_op', 'op_mul',
    199                         this.createNode('node_op', 'op_execfun',
    200                             Type.deepCopy(node.children[0]),
    201                             Type.deepCopy(node.children[1])
    202                         ),
    203                         this.createNode('node_op', 'op_add',
    204                             this.createNode('node_op', 'op_mul',
    205                                 this.derivative(node.children[1][0], varname),
    206                                 this.createNode('node_op', 'op_div',
    207                                     Type.deepCopy(node.children[1][1]),
    208                                     Type.deepCopy(node.children[1][0])
    209                                 )
    210                             ),
    211                             this.createNode('node_op', 'op_mul',
    212                                 this.derivative(node.children[1][1], varname),
    213                                 this.createNode('node_op', 'op_execfun',
    214                                     this.createNode('node_var', 'log'),
    215                                     [Type.deepCopy(node.children[1][0])]
    216                                 )
    217                             )
    218                         )
    219                     );
    220                     break;
    221
    222                 case 'log':
    223                 case 'ln':
    224                     newNode = this.createNode('node_op', 'op_div',
    225                         this.createNode('node_const', 1.0),
    226                         // Attention: single variable mode
    227                         Type.deepCopy(arg[0])
    228                     );
    229                     break;
    230
    231                 case 'log2':
    232                 case 'lb':
    233                 case 'ld':
    234                     newNode = this.createNode('node_op', 'op_mul',
    235                         this.createNode('node_op', 'op_div',
    236                             this.createNode('node_const', 1.0),
    237                             // Attention: single variable mode
    238                             Type.deepCopy(arg[0])
    239                         ),
    240                         this.createNode('node_const', 1.4426950408889634)  // 1/log(2)
    241                     );
    242                     break;
    243
    244                 case 'log10':
    245                 case 'lg':
    246                     newNode = this.createNode('node_op', 'op_mul',
    247                         this.createNode('node_op', 'op_div',
    248                             this.createNode('node_const', 1.0),
    249                             // Attention: single variable mode
    250                             Type.deepCopy(arg[0])
    251                         ),
    252                         this.createNode('node_const', 0.43429448190325176)  // 1/log(10)
    253                     );
    254                     break;
    255
    256                 case 'asin':
    257                     newNode = this.createNode('node_op', 'op_div',
    258                         this.createNode('node_const', 1.0),
    259                         this.createNode('node_op', 'op_execfun',
    260                             this.createNode('node_var', 'sqrt'),
    261                             [
    262                                 this.createNode('node_op', 'op_sub',
    263                                     this.createNode('node_const', 1.0),
    264                                     this.createNode('node_op', 'op_mul',
    265                                         Type.deepCopy(arg[0]),
    266                                         Type.deepCopy(arg[0])
    267                                     )
    268                                 )
    269                             ]
    270                         )
    271                     );
    272                     break;
    273
    274                 case 'acos':
    275                     newNode = this.createNode('node_op', 'op_neg',
    276                         this.createNode('node_op', 'op_div',
    277                             this.createNode('node_const', 1.0),
    278                             this.createNode('node_op', 'op_execfun',
    279                                 this.createNode('node_var', 'sqrt'),
    280                                 [
    281                                     this.createNode('node_op', 'op_sub',
    282                                         this.createNode('node_const', 1.0),
    283                                         this.createNode('node_op', 'op_mul',
    284                                             Type.deepCopy(arg[0]),
    285                                             Type.deepCopy(arg[0])
    286                                         )
    287                                     )
    288                                 ]
    289                             )
    290                         )
    291                     );
    292                     break;
    293
    294                 //case 'atan2':
    295
    296                 case 'atan':
    297                     newNode = this.createNode('node_op', 'op_div',
    298                         this.createNode('node_const', 1.0),
    299                         this.createNode('node_op', 'op_add',
    300                             this.createNode('node_const', 1.0),
    301                             this.createNode('node_op', 'op_mul',
    302                                 Type.deepCopy(arg[0]),
    303                                 Type.deepCopy(arg[0])
    304                             )
    305                         )
    306                     );
    307                     break;
    308
    309                 case 'acot':
    310                     newNode = this.createNode('node_op', 'op_neg',
    311                         this.createNode('node_op', 'op_div',
    312                             this.createNode('node_const', 1.0),
    313                             this.createNode('node_op', 'op_add',
    314                                 this.createNode('node_const', 1.0),
    315                                 this.createNode('node_op', 'op_mul',
    316                                     Type.deepCopy(arg[0]),
    317                                     Type.deepCopy(arg[0])
    318                                 )
    319                             )
    320                         )
    321                     );
    322                     break;
    323
    324                 case 'sinh':
    325                     newNode = this.createNode('node_op', 'op_execfun',
    326                         this.createNode('node_var', 'cosh'),
    327                         [Type.deepCopy(arg[0])]
    328                     );
    329                     break;
    330
    331                 case 'cosh':
    332                     newNode = this.createNode('node_op', 'op_execfun',
    333                         this.createNode('node_var', 'sinh'),
    334                         [Type.deepCopy(arg[0])]
    335                     );
    336                     break;
    337
    338                 case 'tanh':
    339                     newNode = this.createNode('node_op', 'op_sub',
    340                         this.createNode('node_const', 1.0),
    341                         this.createNode('node_op', 'op_exp',
    342                             this.createNode('node_op', 'op_execfun',
    343                                 this.createNode('node_var', 'tanh'),
    344                                 [Type.deepCopy(arg[0])]
    345                             ),
    346                             this.createNode('node_const', 2.0)
    347                         )
    348                     );
    349                     break;
    350
    351                 case 'asinh':
    352                     newNode = this.createNode('node_op', 'op_div',
    353                         this.createNode('node_const', 1.0),
    354                         this.createNode('node_op', 'op_execfun',
    355                             this.createNode('node_var', 'sqrt'),
    356                             [
    357                                 this.createNode('node_op', 'op_add',
    358                                     this.createNode('node_op', 'op_mul',
    359                                         Type.deepCopy(arg[0]),
    360                                         Type.deepCopy(arg[0])
    361                                     ),
    362                                     this.createNode('node_const', 1.0)
    363                                 )
    364                             ]
    365                         )
    366                     );
    367                     break;
    368
    369                 case 'acosh':
    370                     newNode = this.createNode('node_op', 'op_div',
    371                         this.createNode('node_const', 1.0),
    372                         this.createNode('node_op', 'op_execfun',
    373                             this.createNode('node_var', 'sqrt'),
    374                             [
    375                                 this.createNode('node_op', 'op_sub',
    376                                     this.createNode('node_op', 'op_mul',
    377                                         Type.deepCopy(arg[0]),
    378                                         Type.deepCopy(arg[0])
    379                                     ),
    380                                     this.createNode('node_const', 1.0)
    381                                 )
    382                             ]
    383                         )
    384                     );
    385                     break;
    386
    387                 case 'atanh':
    388                     newNode = this.createNode('node_op', 'op_div',
    389                         this.createNode('node_const', 1.0),
    390                         this.createNode('node_op', 'op_sub',
    391                             this.createNode('node_const', 1.0),
    392                             this.createNode('node_op', 'op_mul',
    393                                 Type.deepCopy(arg[0]),
    394                                 Type.deepCopy(arg[0])
    395                             )
    396                         )
    397                     );
    398                     break;
    399
    400                 default:
    401                     newNode = this.createNode('node_const', 0.0);
    402                     console.log('Derivative of "' + fun + '" not yet implemented');
    403                     throw new Error('Error(' + this.line + '): ');
    404                 //  this._error('Derivative of "' + fun + '" not yet implemented');
    405
    406             }
    407
    408             return newNode;
    409         },
    410
    411         derivative: function (node, varname) {
    412             var newNode;
    413
    414             switch (node.type) {
    415                 case 'node_op':
    416                     switch (node.value) {
    417                         /*
    418                         case 'op_map':
    419                             if (true) {
    420                                 newNode = this.createNode('node_op', 'op_map',
    421                                         Type.deepCopy(node.children[0]),
    422                                         this.derivative(node.children[1], varname)
    423                                     );
    424                             } else {
    425                                 newNode = this.derivative(node.children[1], varname);
    426                             }
    427                             break;
    428                         */
    429                         case 'op_execfun':
    430                             // f'(g(x))g'(x)
    431                             if (node.children[0].value == 'pow') {
    432                                 newNode = this.deriveElementary(node, varname);
    433                             } else {
    434                                 if (node.children[1].length === 0) {
    435                                     newNode = this.createNode('node_const', 0.0);
    436                                 } else {
    437                                     newNode = this.createNode('node_op', 'op_mul',
    438                                         this.deriveElementary(node, varname),
    439                                         // Warning: single variable mode
    440                                         this.derivative(node.children[1][0], varname)
    441                                     );
    442                                 }
    443                             }
    444                             break;
    445
    446                         case 'op_div':
    447                             // (f'g − g'f )/(g*g)
    448                             newNode = this.createNode('node_op', 'op_div',
    449                                 this.createNode('node_op', 'op_sub',
    450                                     this.createNode('node_op', 'op_mul',
    451                                         this.derivative(node.children[0], varname),
    452                                         Type.deepCopy(node.children[1])
    453                                     ),
    454                                     this.createNode('node_op', 'op_mul',
    455                                         Type.deepCopy(node.children[0]),
    456                                         this.derivative(node.children[1], varname)
    457                                     )
    458                                 ),
    459                                 this.createNode('node_op', 'op_mul',
    460                                     Type.deepCopy(node.children[1]),
    461                                     Type.deepCopy(node.children[1])
    462                                 )
    463                             );
    464                             break;
    465
    466                         case 'op_mul':
    467                             // fg' + f'g
    468                             newNode = this.createNode('node_op', 'op_add',
    469                                 this.createNode('node_op', 'op_mul',
    470                                     Type.deepCopy(node.children[0]),
    471                                     this.derivative(node.children[1], varname)),
    472                                 this.createNode('node_op', 'op_mul',
    473                                     this.derivative(node.children[0], varname),
    474                                     Type.deepCopy(node.children[1]))
    475                             );
    476                             break;
    477
    478                         case 'op_neg':
    479                             newNode = this.createNode('node_op', 'op_neg',
    480                                 this.derivative(node.children[0], varname)
    481                             );
    482                             break;
    483
    484                         case 'op_add':
    485                         case 'op_sub':
    486                             newNode = this.createNode('node_op', node.value,
    487                                 this.derivative(node.children[0], varname),
    488                                 this.derivative(node.children[1], varname)
    489                             );
    490                             break;
    491
    492                         case 'op_exp':
    493                             // (f^g)' = f^g*(f'g/f + g' log(f))
    494                             newNode = this.createNode('node_op', 'op_mul',
    495                                 Type.deepCopy(node),
    496                                 this.createNode('node_op', 'op_add',
    497                                     this.createNode('node_op', 'op_mul',
    498                                         this.derivative(node.children[0], varname),
    499                                         this.createNode('node_op', 'op_div',
    500                                             Type.deepCopy(node.children[1]),
    501                                             Type.deepCopy(node.children[0])
    502                                         )
    503                                     ),
    504                                     this.createNode('node_op', 'op_mul',
    505                                         this.derivative(node.children[1], varname),
    506                                         this.createNode('node_op', 'op_execfun',
    507                                             this.createNode('node_var', 'log'),
    508                                             [Type.deepCopy(node.children[0])]
    509                                         )
    510                                     )
    511                                 )
    512                             );
    513                             break;
    514                     }
    515                     break;
    516
    517                 case 'node_var':
    518                     //console.log('node_var', node);
    519                     if (node.value === varname) {
    520                         newNode = this.createNode('node_const', 1.0);
    521                     } else {
    522                         newNode = this.createNode('node_const', 0.0);
    523                     }
    524                     break;
    525
    526                 case 'node_const':
    527                     newNode = this.createNode('node_const', 0.0);
    528                     break;
    529
    530                 case 'node_const_bool':
    531                     break;
    532
    533                 case 'node_str':
    534                     break;
    535
    536             }
    537
    538             return newNode;
    539         },
    540
    541         /**
    542          * f = map (x) -> x*sin(x);
    543          * Usages:
    544          * h = D(f, x);
    545          * h = map (x) -> D(f, x);
    546          *
    547          */
    548         expandDerivatives: function (node, parent, ast) {
    549             var len, i, j, mapNode, codeNode, ret, node2, newNode,
    550                 mapName, varname, vArray, order;
    551
    552             ret = 0;
    553             if (!node) {
    554                 return ret;
    555             }
    556
    557             this.line = node.line;
    558             this.col = node.col;
    559
    560             // First we have to go down in the tree.
    561             // This ensures that in cases like D(D(f,x),x) the inner D is expanded first.
    562             len = node.children.length;
    563             for (i = 0; i < len; ++i) {
    564                 if (node.children[i] && node.children[i].type) {
    565                     node.children[i] = this.expandDerivatives(node.children[i], node, ast);
    566                 } else if (Type.isArray(node.children[i])) {
    567                     for (j = 0; j < node.children[i].length; ++j) {
    568                         if (node.children[i][j] && node.children[i][j].type) {
    569                             node.children[i][j] = this.expandDerivatives(node.children[i][j], node, ast);
    570                         }
    571                     }
    572                 }
    573             }
    574
    575             switch (node.type) {
    576                 case 'node_op':
    577                     switch (node.value) {
    578                         case 'op_execfun':
    579                             if (node.children[0] && node.children[0].value === 'D') {
    580                                 if (node.children[1][0].type == 'node_var') {
    581                                     /*
    582                                      * Derive map, that is compute D(f,x)
    583                                      * where e.g. f = map (x) -> x^2
    584                                      *
    585                                      * First step: find node where the map is defined
    586                                      */
    587                                     mapName = node.children[1][0].value;
    588                                     mapNode = this.findMapNode(mapName, ast);
    589                                     vArray = mapNode.children[0];
    590
    591                                     // Variable name for differentiation
    592                                     if (node.children[1].length >= 2) {
    593                                         varname = node.children[1][1].value;
    594                                     } else {
    595                                         varname = mapNode.children[0][0]; // Usually it's 'x'
    596                                     }
    597                                     codeNode = mapNode.children[1];
    598                                 } else {
    599                                     /*
    600                                      * Derive expression, e.g.
    601                                      *     D(2*x, x)
    602                                      */
    603                                     codeNode = node.children[1][0];
    604                                     vArray = ['x'];
    605
    606                                     // Variable name for differentiation and order
    607                                     if (node.children[1].length >= 2) {
    608                                         varname = node.children[1][1].value;
    609                                     } else {
    610                                         varname = 'x';
    611                                     }
    612                                 }
    613
    614                                 // Differentiation order
    615                                 if (node.children[1].length >= 3) {
    616                                     order = node.children[1][2].value;
    617                                 } else {
    618                                     order = 1;
    619                                 }
    620
    621                                 // Create node which contains the derivative
    622                                 newNode = codeNode;
    623                                 //newNode = this.removeTrivialNodes(newNode);
    624                                 if (order >= 1) {
    625                                     while (order >= 1) {
    626                                         newNode = this.derivative(newNode, varname);
    627                                         newNode = this.removeTrivialNodes(newNode);
    628                                         order--;
    629                                     }
    630                                 }
    631
    632                                 // Replace the node containing e.g. D(f,x) by the derivative.
    633                                 if (parent.type == 'node_op' && parent.value == 'op_assign') {
    634                                     // If D is an assignment it has to be replaced by a map
    635                                     // h = D(f, x)
    636                                     node2 = this.createNode('node_op', 'op_map',
    637                                         vArray,
    638                                         newNode
    639                                     );
    640                                 } else {
    641                                     node2 = newNode;
    642                                 }
    643
    644                                 this.setMath(node2);
    645                                 node.type = node2.type;
    646                                 node.value = node2.value;
    647                                 node.children[0] = node2.children[0];
    648                                 node.children[1] = node2.children[1];
    649                             }
    650                     }
    651                     break;
    652
    653                 case 'node_var':
    654                 case 'node_const':
    655                 case 'node_const_bool':
    656                 case 'node_str':
    657                     break;
    658             }
    659
    660             return node;
    661         },
    662
    663         removeTrivialNodes: function (node) {
    664             var i, len, n0, n1, swap;
    665
    666             // In case of 'op_execfun' the children[1] node is an array.
    667             if (Type.isArray(node)) {
    668                 len = node.length;
    669                 for (i = 0; i < len; ++i) {
    670                     node[i] = this.removeTrivialNodes(node[i]);
    671                 }
    672             }
    673             if (node.type != 'node_op' || !node.children) {
    674                 return node;
    675             }
    676
    677             len = node.children.length;
    678             for (i = 0; i < len; ++i) {
    679                 this.mayNotBeSimplified = false;
    680                 do {
    681                     node.children[i] = this.removeTrivialNodes(node.children[i]);
    682                 } while (this.mayNotBeSimplified);
    683
    684             }
    685
    686             switch (node.value) {
    687                 // Allow maps of the form
    688                 //  map (x) -> x;
    689                 case 'op_map':
    690                     n0 = node.children[0];
    691                     n1 = node.children[1];
    692                     if (n1.type == 'node_var') {
    693                         for (i = 0; i < n0.length; ++i) {
    694                             // Allow maps of the form map(x) -> x
    695                             if (n0[i] == n1.value) {
    696                                 n1.isMath = true;
    697                                 break;
    698                             }
    699                         }
    700                     }
    701                     break;
    702
    703                 // a + 0 -> a
    704                 // 0 + a -> a
    705                 case 'op_add':
    706                     n0 = node.children[0];
    707                     n1 = node.children[1];
    708                     if (n0.type == 'node_const' && n0.value === 0.0) {
    709                         return n1;
    710                     }
    711                     if (n1.type == 'node_const' && n1.value === 0.0) {
    712                         return n0;
    713                     }
    714
    715                     // const + const -> const
    716                     if (n0.type == 'node_const' && n1.type == 'node_const') {
    717                         n0.value += n1.value;
    718                         return n0;
    719                     }
    720                     break;
    721
    722                 // 1 * a = a
    723                 // a * 1 = a
    724                 // a * 0 = 0
    725                 // 0 * a = 0
    726                 // - * - = +
    727                 // Order children
    728                 case 'op_mul':
    729                     n0 = node.children[0];
    730                     n1 = node.children[1];
    731                     if (n0.type == 'node_const' && n0.value == 1.0) {
    732                         return n1;
    733                     }
    734                     if (n1.type == 'node_const' && n1.value == 1.0) {
    735                         return n0;
    736                     }
    737                     if (n0.type == 'node_const' && n0.value === 0.0) {
    738                         return n0;
    739                     }
    740                     if (n1.type == 'node_const' && n1.value === 0.0) {
    741                         return n1;
    742                     }
    743                     if (n1.type == 'node_const' && n1.value === 0.0) {
    744                         return n1;
    745                     }
    746
    747                     // (-a) * (-b) -> a*b
    748                     if (n0.type == 'node_op' && n0.value == 'op_neg' &&
    749                         n1.type == 'node_op' && n1.value == 'op_neg') {
    750                         node.children = [n0.children[0], n1.children[0]];
    751                         this.mayNotBeSimplified = true;
    752                         return node;
    753                     }
    754                     // (-a) * b -> -(a*b)
    755                     if (n0.value == 'op_neg' && n1.value != 'op_neg') {
    756                         node.type = 'node_op';
    757                         node.value = 'op_neg';
    758                         node.children = [this.createNode('node_op', 'op_mul', n0.children[0], n1)];
    759                         this.mayNotBeSimplified = true;
    760                         return node;
    761                     }
    762                     // a * (-b) -> -(a*b)
    763                     if (n0.value != 'op_neg' && n1.value == 'op_neg') {
    764                         node.type = 'node_op';
    765                         node.value = 'op_neg';
    766                         node.children = [this.createNode('node_op', 'op_mul', n0, n1.children[0])];
    767                         this.mayNotBeSimplified = true;
    768                         return node;
    769                     }
    770                     // (1 / a) * b -> a / b
    771                     if (n0.value == 'op_div' &&
    772                         n0.children[0].type == 'node_const' && n0.children[0].value == 1.0) {
    773                         node.type = 'node_op';
    774                         node.value = 'op_div';
    775                         node.children = [n1, n0.children[1]];
    776                         this.mayNotBeSimplified = true;
    777                         return node;
    778                     }
    779                     // a * (1 / b) -> a / b
    780                     if (n1.value == 'op_div' &&
    781                         n1.children[0].type == 'node_const' && n1.children[0].value == 1.0) {
    782                         node.type = 'node_op';
    783                         node.value = 'op_div';
    784                         node.children = [n0, n1.children[1]];
    785                         this.mayNotBeSimplified = true;
    786                         return node;
    787                     }
    788
    789                     // Order children
    790                     // a * const -> const * a
    791                     if (n0.type != 'node_const' && n1.type == 'node_const') {
    792                         node.children = [n1, n0];
    793                         this.mayNotBeSimplified = true;
    794                         return node;
    795                     }
    796                     // a + (-const) -> -const * a
    797                     if (n0.type != 'node_const' && n1.type == 'node_op' &&
    798                         n1.value == 'op_neg' && n1.children[0].type == 'node_const') {
    799                         node.children = [n1, n0];
    800                         this.mayNotBeSimplified = true;
    801                         return node;
    802                     }
    803
    804                     // a * var -> var * a
    805                     // a * fun -> fun * a
    806                     if (n0.type == 'node_op' && n0.value != 'op_execfun' &&
    807                         (n1.type == 'node_var' || (n1.type == 'node_op' && n1.value == 'op_execfun'))) {
    808                         node.children = [n1, n0];
    809                         this.mayNotBeSimplified = true;
    810                         return node;
    811                     }
    812
    813                     // a + (-var) -> -var * a
    814                     if (n0.type != 'node_op' && n1.type == 'node_op' &&
    815                         n1.value == 'op_neg' && n1.children[0].type == 'node_var') {
    816                         node.children = [n1, n0];
    817                         this.mayNotBeSimplified = true;
    818                         return node;
    819                     }
    820                     // a * (const * b) -> const * (a*b)
    821                     // a * (const / b) -> const * (a/b)
    822                     if (n0.type != 'node_const' && n1.type == 'node_op' &&
    823                         (n1.value == 'op_mul' || n1.value == 'op_div') &&
    824                         n1.children[0].type == 'node_const') {
    825                         swap = n1.children[0];
    826                         n1.children[0] = n0;
    827                         node.children = [swap, n1];
    828                         this.mayNotBeSimplified = true;
    829                         return node;
    830                     }
    831
    832                     // (const * a) * b -> const * (a * b)
    833                     if (n1.type != 'node_const' && n0.type == 'node_op' &&
    834                         n0.value == 'op_mul' &&
    835                         n0.children[0].type == 'node_const') {
    836                         node.children = [
    837                             n0.children[0],
    838                             this.createNode('node_op', 'op_mul', n0.children[1], n1)
    839                         ];
    840                         this.mayNotBeSimplified = true;
    841                         return node;
    842                     }
    843
    844                     // const * const -> const
    845                     if (n0.type == 'node_const' && n1.type == 'node_const') {
    846                         n0.value *= n1.value;
    847                         return n0;
    848                     }
    849
    850                     // const * (const * a) -> const * a
    851                     // const * (const / a) -> const / a
    852                     if (n0.type == 'node_const' && n1.type == 'node_op' &&
    853                         (n1.value == 'op_mul' || n1.value == 'op_div') &&
    854                         n1.children[0].type == 'node_const') {
    855                         n1.children[0].value *= n0.value;
    856                         return n1;
    857                     }
    858
    859                     // a * a-> a^2
    860                     n0.hash = this.parser.compile(n0);
    861                     n1.hash = this.parser.compile(n1);
    862                     if (n0.hash === n1.hash) {
    863                         node.value = 'op_exp';
    864                         node.children[1] = this.createNode('node_const', 2.0);
    865                         return node;
    866                     }
    867
    868                     if (n0.type == 'node_const' && n1.type == 'node_op' &&
    869                         (n1.value == 'op_mul' || n1.value == 'op_div') &&
    870                         n1.children[0].type == 'node_const') {
    871                         n1.children[0].value *= n0.value;
    872                         return n1;
    873                     }
    874
    875                     // a * a^b -> a^(b+1)
    876                     if (n1.type == 'node_op' && n1.value == 'op_exp') {
    877                         if (!n0.hash) {
    878                             n0.hash = this.parser.compile(n0);
    879                         }
    880                         if (!n1.children[0].hash) {
    881                             n1.children[0].hash = this.parser.compile(n1.children[0]);
    882                         }
    883                         if (n0.hash === n1.children[0].hash) {
    884                             n1.children[1] = this.createNode('node_op', 'op_add',
    885                                 n1.children[1],
    886                                 this.createNode('node_const', 1.0)
    887                             );
    888                             this.mayNotBeSimplified = true;
    889                             return n1;
    890                         }
    891                     }
    892
    893                     // a^b * a^c -> a^(b+c)
    894                     if (n0.type == 'node_op' && n0.value == 'op_exp' &&
    895                         n1.type == 'node_op' && n1.value == 'op_exp') {
    896                         n0.children[0].hash = this.parser.compile(n0.children[0]);
    897                         n1.children[0].hash = this.parser.compile(n1.children[0]);
    898                         if (n0.children[0].hash === n1.children[0].hash) {
    899                             n0.children[1] = this.createNode('node_op', 'op_add',
    900                                 n0.children[1],
    901                                 n1.children[1]
    902                             );
    903                             this.mayNotBeSimplified = true;
    904                             return n0;
    905                         }
    906                     }
    907
    908                     break;
    909
    910                 // 0 - a -> -a
    911                 // a - 0 -> a
    912                 // a - a -> 0
    913                 case 'op_sub':
    914                     n0 = node.children[0];
    915                     n1 = node.children[1];
    916                     if (n0.type == 'node_const' && n0.value === 0.0) {
    917                         node.value = 'op_neg';
    918                         node.children[0] = n1;
    919                         return node;
    920                     }
    921                     if (n1.type == 'node_const' && n1.value === 0.0) {
    922                         return n0;
    923                     }
    924                     if (n0.type == 'node_const' && n1.type == 'node_const' &&
    925                         n0.value == n1.value) {
    926                         return this.createNode('node_const', 0.0);
    927                     }
    928                     if (n0.type == 'node_var' && n1.type == 'node_var' &&
    929                         n0.value == n1.value) {
    930                         return this.createNode('node_const', 0.0);
    931                     }
    932
    933                     // const - const -> const
    934                     if (n0.type == 'node_const' && n1.type == 'node_const') {
    935                         n0.value -= n1.value;
    936                         return n0;
    937                     }
    938
    939                     // const * a - const * a -> const * a
    940                     if (n0.type == 'node_op' && n0.value == 'op_mul' &&
    941                         n1.type == 'node_op' && n1.value == 'op_mul') {
    942
    943                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    944                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    945                         if (n0.children[1].hash === n1.children[1].hash) {
    946
    947                             node.value = 'op_mul';
    948                             node.children = [
    949                                 this.createNode('node_op', 'op_sub',
    950                                     n0.children[0],
    951                                     n1.children[0]),
    952                                 n0.children[1]
    953                             ];
    954                             this.mayNotBeSimplified = true;
    955                             return node;
    956                         }
    957                     }
    958                     // const * a - a -> (const - 1) * a
    959                     if (n0.type == 'node_op' && n0.value == 'op_mul') {
    960
    961                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    962                         n1.hash = this.parser.compile(n1);
    963                         if (n0.children[1].hash === n1.hash) {
    964
    965                             node.value = 'op_mul';
    966                             node.children = [
    967                                 this.createNode('node_op', 'op_sub',
    968                                     n0.children[0],
    969                                     this.createNode('node_const', 1.0)),
    970                                 n1
    971                             ];
    972                             this.mayNotBeSimplified = true;
    973                             return node;
    974                         }
    975                     }
    976                     // a - const*a -> (const - 1) * a
    977                     if (n1.type == 'node_op' && n1.value == 'op_mul') {
    978
    979                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    980                         n0.hash = this.parser.compile(n0);
    981                         if (n1.children[1].hash === n0.hash) {
    982
    983                             node.value = 'op_mul';
    984                             node.children = [
    985                                 this.createNode('node_op', 'op_sub',
    986                                     this.createNode('node_const', 1.0),
    987                                     n1.children[0]),
    988                                 n0
    989                             ];
    990                             this.mayNotBeSimplified = true;
    991                             return node;
    992                         }
    993                     }
    994
    995                     break;
    996
    997                 // -0 -> 0
    998                 // -(-b) = b
    999                 case 'op_neg':
    1000                     n0 = node.children[0];
    1001                     if (n0.type == 'node_const' && n0.value === 0.0) {
    1002                         return n0;
    1003                     }
    1004                     if (n0.type == 'node_op' && n0.value == 'op_neg') {
    1005                         return n0.children[0];
    1006                     }
    1007                     break;
    1008
    1009                 // a / a -> 1, a != 0
    1010                 // 0 / a -> 0, a != 0
    1011                 // a / 0 -> Infinity, a != 0
    1012                 // 0 / 0 -> NaN, a == 0
    1013                 case 'op_div':
    1014                     n0 = node.children[0];
    1015                     n1 = node.children[1];
    1016                     if (n0.type == 'node_const' && n1.type == 'node_const' &&
    1017                         n0.value == n1.value && n0.value !== 0) {
    1018                         n0.value = 1.0;
    1019                         return n0;
    1020                     }
    1021                     if (n0.type == 'node_const' && n0.value === 0 &&
    1022                         n1.type == 'node_const' && n1.value !== 0) {
    1023                         n0.value = 0.0;
    1024                         return n0;
    1025                     }
    1026
    1027                     // Risky: 0 / (something != 0) -> 0.0
    1028                     if (n0.type == 'node_const' && n0.value === 0 &&
    1029                         (n1.type == 'node_op' || n1.type == 'node_var')) {
    1030                         node.type = 'node_const';
    1031                         node.value = 0.0;
    1032                         return node;
    1033                     }
    1034
    1035                     if (n0.type == 'node_var' && n1.type == 'node_var' &&
    1036                         n0.value == n1.value) {
    1037                         return this.createNode('node_const', 1.0);
    1038                     }
    1039                     if (n0.type == 'node_const' && n0.value !== 0 &&
    1040                         n1.type == 'node_const' && n1.value === 0) {
    1041                         if (n0.value > 0.0) {
    1042                             n0.value = Infinity;
    1043                         } else {
    1044                             n0.value = -Infinity; // Do we ever need this?
    1045                         }
    1046                         return n0;
    1047                     }
    1048
    1049                     // (-a) / (-b) -> a/b
    1050                     if (n0.type == 'node_op' && n0.value == 'op_neg' &&
    1051                         n1.type == 'node_op' && n1.value == 'op_neg') {
    1052                         node.children = [n0.children[0], n1.children[0]];
    1053                         this.mayNotBeSimplified = true;
    1054                         return node;
    1055                     }
    1056                     // (-a) / b -> -(a/b)
    1057                     if (n0.value == 'op_neg' && n1.value != 'op_neg') {
    1058                         node.type = 'node_op';
    1059                         node.value = 'op_neg';
    1060                         node.children = [this.createNode('node_op', 'op_div', n0.children[0], n1)];
    1061                         this.mayNotBeSimplified = true;
    1062                         return node;
    1063                     }
    1064                     // a / (-b) -> -(a/b)
    1065                     if (n0.value != 'op_neg' && n1.value == 'op_neg') {
    1066                         node.type = 'node_op';
    1067                         node.value = 'op_neg';
    1068                         node.children = [this.createNode('node_op', 'op_div', n0, n1.children[0])];
    1069                         this.mayNotBeSimplified = true;
    1070                         return node;
    1071                     }
    1072
    1073                     // a^b / a -> a^(b-1)
    1074                     if (n0.type == 'node_op' && n0.value == 'op_exp') {
    1075                         if (!n1.hash) {
    1076                             n1.hash = this.parser.compile(n1);
    1077                         }
    1078                         if (!n0.children[0].hash) {
    1079                             n0.children[0].hash = this.parser.compile(n0.children[0]);
    1080                         }
    1081                         if (n1.hash === n0.children[0].hash) {
    1082                             n0.children[1] = this.createNode('node_op', 'op_sub',
    1083                                 n0.children[1],
    1084                                 this.createNode('node_const', 1.0)
    1085                             );
    1086                             this.mayNotBeSimplified = true;
    1087                             return n0;
    1088                         }
    1089                     }
    1090
    1091                     // (const * a) / b -> const * (a / b)
    1092                     if (n1.type != 'node_const' && n0.type == 'node_op' &&
    1093                         n0.value == 'op_mul' &&
    1094                         n0.children[0].type == 'node_const') {
    1095                         node.value = 'op_mul';
    1096                         node.children = [
    1097                             n0.children[0],
    1098                             this.createNode('node_op', 'op_div', n0.children[1], n1)
    1099                         ];
    1100                         this.mayNotBeSimplified = true;
    1101                         return node;
    1102                     }
    1103
    1104                     // a^b / a^c -> a^(b-c)
    1105                     if (n0.type == 'node_op' && n0.value == 'op_exp' &&
    1106                         n1.type == 'node_op' && n1.value == 'op_exp') {
    1107                         n0.children[0].hash = this.parser.compile(n0.children[0]);
    1108                         n1.children[0].hash = this.parser.compile(n1.children[0]);
    1109                         if (n0.children[0].hash === n1.children[0].hash) {
    1110                             n0.children[1] = this.createNode('node_op', 'op_sub',
    1111                                 n0.children[1],
    1112                                 n1.children[1]
    1113                             );
    1114                             this.mayNotBeSimplified = true;
    1115                             return n0;
    1116                         }
    1117                     }
    1118
    1119
    1120                     break;
    1121
    1122                 // a^0 = 1
    1123                 // a^1 -> a
    1124                 // 1^a -> 1
    1125                 // 0^a -> 0: a const != 0
    1126                 case 'op_exp':
    1127                     n0 = node.children[0];
    1128                     n1 = node.children[1];
    1129                     if (n1.type == 'node_const' && n1.value === 0.0) {
    1130                         n1.value = 1.0;
    1131                         return n1;
    1132                     }
    1133                     if (n1.type == 'node_const' && n1.value == 1.0) {
    1134                         return n0;
    1135                     }
    1136                     if (n0.type == 'node_const' && n0.value == 1.0) {
    1137                         return n0;
    1138                     }
    1139                     if (n0.type == 'node_const' && n0.value === 0.0 &&
    1140                         n1.type == 'node_const' && n1.value !== 0.0) {
    1141                         return n0;
    1142                     }
    1143
    1144                     // (a^b)^c -> a^(b*c)
    1145                     if (n0.type == 'node_op' && n0.value == 'op_exp') {
    1146                         node.children = [
    1147                             n0.children[0],
    1148                             this.createNode('node_op', 'op_mul',
    1149                                 n0.children[1],
    1150                                 n1)
    1151                         ];
    1152                         return node;
    1153                     }
    1154                     break;
    1155             }
    1156
    1157             switch (node.value) {
    1158                 // const_1 + const_2 -> (const_1 + const_2)
    1159                 // a + a -> 2*a
    1160                 // a + (-b) = a - b
    1161                 case 'op_add':
    1162                     n0 = node.children[0];
    1163                     n1 = node.children[1];
    1164                     if (n0.type == 'node_const' && n1.type == 'node_const' &&
    1165                         n0.value == n1.value) {
    1166                         n0.value += n1.value;
    1167                         return n0;
    1168                     }
    1169
    1170                     if (n0.type == 'node_var' && n1.type == 'node_var' &&
    1171                         n0.value == n1.value) {
    1172                         node.children[0] = this.createNode('node_const', 2.0);
    1173                         node.value = 'op_mul';
    1174                         return node;
    1175                     }
    1176
    1177                     if (n0.type == 'node_op' && n0.value == 'op_neg') {
    1178                         node.value = 'op_sub';
    1179                         node.children[0] = n1;
    1180                         node.children[1] = n0.children[0];
    1181                         this.mayNotBeSimplified = true;
    1182                         return node;
    1183                     }
    1184
    1185                     if (n1.type == 'node_op' && n1.value == 'op_neg') {
    1186                         node.value = 'op_sub';
    1187                         node.children[1] = n1.children[0];
    1188                         this.mayNotBeSimplified = true;
    1189                         return node;
    1190                     }
    1191
    1192                     // const * a + const * a -> const * a
    1193                     if (n0.type == 'node_op' && n0.value == 'op_mul' &&
    1194                         n1.type == 'node_op' && n1.value == 'op_mul') {
    1195
    1196                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1197                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1198                         if (n0.children[1].hash === n1.children[1].hash) {
    1199
    1200                             node.value = 'op_mul';
    1201                             node.children = [
    1202                                 this.createNode('node_op', 'op_add',
    1203                                     n0.children[0],
    1204                                     n1.children[0]),
    1205                                 n0.children[1]
    1206                             ];
    1207                             this.mayNotBeSimplified = true;
    1208                             return node;
    1209                         }
    1210                     }
    1211                     // const * a + a -> (const + 1) * a
    1212                     if (n0.type == 'node_op' && n0.value == 'op_mul') {
    1213
    1214                         n0.children[1].hash = this.parser.compile(n0.children[1]);
    1215                         n1.hash = this.parser.compile(n1);
    1216                         if (n0.children[1].hash === n1.hash) {
    1217
    1218                             node.value = 'op_mul';
    1219                             node.children = [
    1220                                 this.createNode('node_op', 'op_add',
    1221                                     n0.children[0],
    1222                                     this.createNode('node_const', 1.0)),
    1223                                 n1
    1224                             ];
    1225                             this.mayNotBeSimplified = true;
    1226                             return node;
    1227                         }
    1228                     }
    1229                     // a + const*a -> (const + 1) * a
    1230                     if (n1.type == 'node_op' && n1.value == 'op_mul') {
    1231
    1232                         n1.children[1].hash = this.parser.compile(n1.children[1]);
    1233                         n0.hash = this.parser.compile(n0);
    1234                         if (n1.children[1].hash === n0.hash) {
    1235
    1236                             node.value = 'op_mul';
    1237                             node.children = [
    1238                                 this.createNode('node_op', 'op_add',
    1239                                     this.createNode('node_const', 1.0),
    1240                                     n1.children[0]),
    1241                                 n0
    1242                             ];
    1243                             this.mayNotBeSimplified = true;
    1244                             return node;
    1245                         }
    1246                     }
    1247
    1248                     break;
    1249
    1250                 // a - (-b) = a + b
    1251                 case 'op_sub':
    1252                     n0 = node.children[0];
    1253                     n1 = node.children[1];
    1254                     if (n1.type == 'node_op' && n1.value == 'op_neg') {
    1255                         node.value = 'op_add';
    1256                         node.children[1] = n1.children[0];
    1257                         this.mayNotBeSimplified = true;
    1258                         return node;
    1259                     }
    1260                     break;
    1261
    1262                 case 'op_execfun':
    1263                     return this.simplifyElementary(node);
    1264             }
    1265
    1266             return node;
    1267         },
    1268
    1269         simplifyElementary: function (node) {
    1270             var fun = node.children[0].value,
    1271                 arg = node.children[1];
    1272
    1273             // Catch errors of the form sin()
    1274             if (arg.length == 0) {
    1275                 return node;
    1276             }
    1277
    1278             switch (fun) {
    1279                 // sin(0) -> 0
    1280                 // sin(PI) -> 0
    1281                 // sin (int * PI) -> 0
    1282                 // sin (PI * int) -> 0
    1283                 // Same for tan()
    1284                 case 'sin':
    1285                 case 'tan':
    1286                     if (arg[0].type == 'node_const' && arg[0].value === 0) {
    1287                         node.type = 'node_const';
    1288                         node.value = 0.0;
    1289                         return node;
    1290                     }
    1291                     if (arg[0].type == 'node_var' && arg[0].value == 'PI') {
    1292                         node.type = 'node_const';
    1293                         node.value = 0.0;
    1294                         return node;
    1295                     }
    1296                     if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' &&
    1297                         arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 &&
    1298                         arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') {
    1299                         node.type = 'node_const';
    1300                         node.value = 0.0;
    1301                         return node;
    1302                     }
    1303                     break;
    1304
    1305                 // cos(0) -> 1.0
    1306                 // cos(PI) -> -1.0
    1307                 // cos(int * PI) -> +/- 1.0
    1308                 // cos(PI * int) -> +/- 1.0
    1309                 case 'cos':
    1310                     if (arg[0].type == 'node_const' && arg[0].value === 0) {
    1311                         node.type = 'node_const';
    1312                         node.value = 1.0;
    1313                         return node;
    1314                     }
    1315                     if (arg[0].type == 'node_var' && arg[0].value == 'PI') {
    1316                         node.type = 'node_op';
    1317                         node.value = 'op_neg';
    1318                         node.children = [this.createNode('node_const', 1.0)];
    1319                         return node;
    1320                     }
    1321                     /*
    1322                     if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' &&
    1323                         ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 &&
    1324                          arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') ||
    1325                          (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 &&
    1326                           arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) {
    1327                         node.type = 'node_const';
    1328                         node.value = 1.0;
    1329                         return node;
    1330                     }
    1331                     */
    1332                     break;
    1333
    1334                 // exp(0) -> 1
    1335                 case 'exp':
    1336                     if (arg[0].type == 'node_const' && arg[0].value === 0) {
    1337                         node.type = 'node_const';
    1338                         node.value = 1.0;
    1339                         return node;
    1340                     }
    1341                     break;
    1342
    1343                 // pow(a, 0) -> 1
    1344                 case 'pow':
    1345                     if (arg[1].type == 'node_const' && arg[1].value === 0) {
    1346                         node.type = 'node_const';
    1347                         node.value = 1.0;
    1348                         return node;
    1349                     }
    1350                     break;
    1351
    1352             }
    1353
    1354             return node;
    1355         }
    1356
    1357     });
    1358
    1359     return JXG.CA;
    1360 });
    1361