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