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