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