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