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          *
702          */
703         expandDerivatives: function (node, parent, ast) {
704             var len, i, j, mapNode, codeNode,
705                 ret, node2, newNode, mapName,
706                 varname, vArray, order;
707 
708             ret = 0;
709             if (!node) {
710                 return ret;
711             }
712 
713             this.line = node.line;
714             this.col = node.col;
715 
716             // First we have to go down in the tree.
717             // This ensures that in cases like D(D(f,x),x) the inner D is expanded first.
718             len = node.children.length;
719             for (i = 0; i < len; ++i) {
720                 if (node.children[i] && node.children[i].type) {
721                     node.children[i] = this.expandDerivatives(node.children[i], node, ast);
722                 } else if (Type.isArray(node.children[i])) {
723                     for (j = 0; j < node.children[i].length; ++j) {
724                         if (node.children[i][j] && node.children[i][j].type) {
725                             node.children[i][j] = this.expandDerivatives(
726                                 node.children[i][j],
727                                 node,
728                                 ast
729                             );
730                         }
731                     }
732                 }
733             }
734 
735             switch (node.type) {
736                 case "node_op":
737                     switch (node.value) {
738                         case "op_execfun":
739                             if (node.children[0] && node.children[0].value === "D") {
740                                 if (node.children[1][0].type == "node_var") {
741                                     /*
742                                      * Derive map, that is compute D(f,x)
743                                      * where e.g. f = map (x) -> x^2
744                                      *
745                                      * First step: find node where the map is defined
746                                      */
747                                     mapName = node.children[1][0].value;
748                                     mapNode = this.findMapNode(mapName, ast);
749                                     vArray = mapNode.children[0];
750 
751                                     // Variable name for differentiation
752                                     if (node.children[1].length >= 2) {
753                                         varname = node.children[1][1].value;
754                                     } else {
755                                         varname = mapNode.children[0][0]; // Usually it's 'x'
756                                     }
757                                     codeNode = mapNode.children[1];
758                                 } else {
759                                     /*
760                                      * Derive expression, e.g.
761                                      *     D(2*x, x)
762                                      */
763                                     codeNode = node.children[1][0];
764                                     vArray = ["x"];
765 
766                                     // Variable name for differentiation and order
767                                     if (node.children[1].length >= 2) {
768                                         varname = node.children[1][1].value;
769                                     } else {
770                                         varname = "x";
771                                     }
772                                 }
773 
774                                 // Differentiation order
775                                 if (node.children[1].length >= 3) {
776                                     order = node.children[1][2].value;
777                                 } else {
778                                     order = 1;
779                                 }
780 
781                                 // Create node which contains the derivative
782                                 newNode = codeNode;
783                                 //newNode = this.removeTrivialNodes(newNode);
784                                 if (order >= 1) {
785                                     while (order >= 1) {
786                                         newNode = this.derivative(newNode, varname);
787                                         newNode = this.removeTrivialNodes(newNode);
788                                         order--;
789                                     }
790                                 }
791 
792                                 // Replace the node containing e.g. D(f,x) by the derivative.
793                                 if (parent.type == "node_op" && parent.value == "op_assign") {
794                                     // If D is an assignment it has to be replaced by a map
795                                     // h = D(f, x)
796                                     node2 = this.createNode(
797                                         "node_op",
798                                         "op_map",
799                                         vArray,
800                                         newNode
801                                     );
802                                 } else {
803                                     node2 = newNode;
804                                 }
805 
806                                 this.setMath(node2);
807                                 node.type = node2.type;
808                                 node.value = node2.value;
809                                 if (node2.children.length > 0) {
810                                     node.children[0] = node2.children[0];
811                                 }
812                                 if (node2.children.length > 1) {
813                                     node.children[1] = node2.children[1];
814                                 }
815                             }
816                     }
817                     break;
818 
819                 case "node_var":
820                 case "node_const":
821                 case "node_const_bool":
822                 case "node_str":
823                     break;
824             }
825 
826             return node;
827         },
828 
829         removeTrivialNodes: function (node) {
830             var i, len, n0, n1, swap;
831 
832             // In case of 'op_execfun' the children[1] node is an array.
833             if (Type.isArray(node)) {
834                 len = node.length;
835                 for (i = 0; i < len; ++i) {
836                     node[i] = this.removeTrivialNodes(node[i]);
837                 }
838             }
839             if (node.type != "node_op" || !node.children) {
840                 return node;
841             }
842 
843             len = node.children.length;
844             for (i = 0; i < len; ++i) {
845                 this.mayNotBeSimplified = false;
846                 do {
847                     node.children[i] = this.removeTrivialNodes(node.children[i]);
848                 } while (this.mayNotBeSimplified);
849             }
850 
851             switch (node.value) {
852                 // Allow maps of the form
853                 //  map (x) -> x;
854                 case "op_map":
855                     n0 = node.children[0];
856                     n1 = node.children[1];
857                     if (n1.type == "node_var") {
858                         for (i = 0; i < n0.length; ++i) {
859                             // Allow maps of the form map(x) -> x
860                             if (n0[i] == n1.value) {
861                                 n1.isMath = true;
862                                 break;
863                             }
864                         }
865                     }
866                     break;
867 
868                 // a + 0 -> a
869                 // 0 + a -> a
870                 case "op_add":
871                     n0 = node.children[0];
872                     n1 = node.children[1];
873                     if (n0.type == "node_const" && n0.value === 0.0) {
874                         return n1;
875                     }
876                     if (n1.type == "node_const" && n1.value === 0.0) {
877                         return n0;
878                     }
879 
880                     // const + const -> const
881                     if (n0.type == "node_const" && n1.type == "node_const") {
882                         n0.value += n1.value;
883                         return n0;
884                     }
885                     break;
886 
887                 // 1 * a = a
888                 // a * 1 = a
889                 // a * 0 = 0
890                 // 0 * a = 0
891                 // - * - = +
892                 // Order children
893                 case "op_mul":
894                     n0 = node.children[0];
895                     n1 = node.children[1];
896                     if (n0.type == "node_const" && n0.value == 1.0) {
897                         return n1;
898                     }
899                     if (n1.type == "node_const" && n1.value == 1.0) {
900                         return n0;
901                     }
902                     if (n0.type == "node_const" && n0.value === 0.0) {
903                         return n0;
904                     }
905                     if (n1.type == "node_const" && n1.value === 0.0) {
906                         return n1;
907                     }
908                     if (n1.type == "node_const" && n1.value === 0.0) {
909                         return n1;
910                     }
911 
912                     // (-a) * (-b) -> a*b
913                     if (
914                         n0.type == "node_op" &&
915                         n0.value == "op_neg" &&
916                         n1.type == "node_op" &&
917                         n1.value == "op_neg"
918                     ) {
919                         node.children = [n0.children[0], n1.children[0]];
920                         this.mayNotBeSimplified = true;
921                         return node;
922                     }
923                     // (-a) * b -> -(a*b)
924                     if (n0.value == "op_neg" && n1.value != "op_neg") {
925                         node.type = "node_op";
926                         node.value = "op_neg";
927                         node.children = [
928                             this.createNode("node_op", "op_mul", n0.children[0], n1)
929                         ];
930                         this.mayNotBeSimplified = true;
931                         return node;
932                     }
933                     // a * (-b) -> -(a*b)
934                     if (n0.value != "op_neg" && n1.value == "op_neg") {
935                         node.type = "node_op";
936                         node.value = "op_neg";
937                         node.children = [
938                             this.createNode("node_op", "op_mul", n0, n1.children[0])
939                         ];
940                         this.mayNotBeSimplified = true;
941                         return node;
942                     }
943                     // (1 / a) * b -> a / b
944                     if (
945                         n0.value == "op_div" &&
946                         n0.children[0].type == "node_const" &&
947                         n0.children[0].value == 1.0
948                     ) {
949                         node.type = "node_op";
950                         node.value = "op_div";
951                         node.children = [n1, n0.children[1]];
952                         this.mayNotBeSimplified = true;
953                         return node;
954                     }
955                     // a * (1 / b) -> a / b
956                     if (
957                         n1.value == "op_div" &&
958                         n1.children[0].type == "node_const" &&
959                         n1.children[0].value == 1.0
960                     ) {
961                         node.type = "node_op";
962                         node.value = "op_div";
963                         node.children = [n0, n1.children[1]];
964                         this.mayNotBeSimplified = true;
965                         return node;
966                     }
967 
968                     // Order children
969                     // a * const -> const * a
970                     if (n0.type != "node_const" && n1.type == "node_const") {
971                         node.children = [n1, n0];
972                         this.mayNotBeSimplified = true;
973                         return node;
974                     }
975                     // a + (-const) -> -const + a
976                     if (
977                         n0.type != "node_const" &&
978                         n1.type == "node_op" &&
979                         n1.value == "op_neg" &&
980                         n1.children[0].type == "node_const"
981                     ) {
982                         node.children = [n1, n0];
983                         this.mayNotBeSimplified = true;
984                         return node;
985                     }
986 
987                     // a * var -> var * a
988                     // a * fun -> fun * a
989                     if (
990                         n0.type == "node_op" &&
991                         n0.value != "op_execfun" &&
992                         (n1.type == "node_var" ||
993                             (n1.type == "node_op" && n1.value == "op_execfun"))
994                     ) {
995                         node.children = [n1, n0];
996                         this.mayNotBeSimplified = true;
997                         return node;
998                     }
999 
1000                     // a + (-var) -> -var  + a
1001                     if (
1002                         n0.type != "node_op" &&
1003                         n1.type == "node_op" &&
1004                         n1.value == "op_neg" &&
1005                         n1.children[0].type == "node_var"
1006                     ) {
1007                         node.children = [n1, n0];
1008                         this.mayNotBeSimplified = true;
1009                         return node;
1010                     }
1011                     // a * (const * b) -> const * (a*b)
1012                     // a * (const / b) -> const * (a/b)
1013                     if (
1014                         n0.type != "node_const" &&
1015                         n1.type == "node_op" &&
1016                         (n1.value == "op_mul" || n1.value == "op_div") &&
1017                         n1.children[0].type == "node_const"
1018                     ) {
1019                         swap = n1.children[0];
1020                         n1.children[0] = n0;
1021                         node.children = [swap, n1];
1022                         this.mayNotBeSimplified = true;
1023                         return node;
1024                     }
1025 
1026                     // (const * a) * b -> const * (a * b)
1027                     if (
1028                         n1.type != "node_const" &&
1029                         n0.type == "node_op" &&
1030                         n0.value == "op_mul" &&
1031                         n0.children[0].type == "node_const"
1032                     ) {
1033                         node.children = [
1034                             n0.children[0],
1035                             this.createNode("node_op", "op_mul", n0.children[1], n1)
1036                         ];
1037                         this.mayNotBeSimplified = true;
1038                         return node;
1039                     }
1040 
1041                     // const * const -> const
1042                     if (n0.type == "node_const" && n1.type == "node_const") {
1043                         n0.value *= n1.value;
1044                         return n0;
1045                     }
1046 
1047                     // const * (const * a) -> const * a
1048                     // const * (const / a) -> const / a
1049                     if (
1050                         n0.type == "node_const" &&
1051                         n1.type == "node_op" &&
1052                         (n1.value == "op_mul" || n1.value == "op_div") &&
1053                         n1.children[0].type == "node_const"
1054                     ) {
1055                         n1.children[0].value *= n0.value;
1056                         return n1;
1057                     }
1058 
1059                     // a * a-> a^2
1060                     n0.hash = this.parser.compile(n0);
1061                     n1.hash = this.parser.compile(n1);
1062                     if (n0.hash === n1.hash) {
1063                         node.value = "op_exp";
1064                         node.children[1] = this.createNode("node_const", 2.0);
1065                         return node;
1066                     }
1067 
1068                     if (
1069                         n0.type == "node_const" &&
1070                         n1.type == "node_op" &&
1071                         (n1.value == "op_mul" || n1.value == "op_div") &&
1072                         n1.children[0].type == "node_const"
1073                     ) {
1074                         n1.children[0].value *= n0.value;
1075                         return n1;
1076                     }
1077 
1078                     // a * a^b -> a^(b+1)
1079                     if (n1.type == "node_op" && n1.value == "op_exp") {
1080                         if (!n0.hash) {
1081                             n0.hash = this.parser.compile(n0);
1082                         }
1083                         if (!n1.children[0].hash) {
1084                             n1.children[0].hash = this.parser.compile(n1.children[0]);
1085                         }
1086                         if (n0.hash === n1.children[0].hash) {
1087                             n1.children[1] = this.createNode(
1088                                 "node_op",
1089                                 "op_add",
1090                                 n1.children[1],
1091                                 this.createNode("node_const", 1.0)
1092                             );
1093                             this.mayNotBeSimplified = true;
1094                             return n1;
1095                         }
1096                     }
1097 
1098                     // a^b * a^c -> a^(b+c)
1099                     if (
1100                         n0.type == "node_op" &&
1101                         n0.value == "op_exp" &&
1102                         n1.type == "node_op" &&
1103                         n1.value == "op_exp"
1104                     ) {
1105                         n0.children[0].hash = this.parser.compile(n0.children[0]);
1106                         n1.children[0].hash = this.parser.compile(n1.children[0]);
1107                         if (n0.children[0].hash === n1.children[0].hash) {
1108                             n0.children[1] = this.createNode(
1109                                 "node_op",
1110                                 "op_add",
1111                                 n0.children[1],
1112                                 n1.children[1]
1113                             );
1114                             this.mayNotBeSimplified = true;
1115                             return n0;
1116                         }
1117                     }
1118 
1119                     break;
1120 
1121                 // 0 - a -> -a
1122                 // a - 0 -> a
1123                 // a - a -> 0
1124                 case "op_sub":
1125                     n0 = node.children[0];
1126                     n1 = node.children[1];
1127                     if (n0.type == "node_const" && n0.value === 0.0) {
1128                         node.value = "op_neg";
1129                         node.children[0] = n1;
1130                         return node;
1131                     }
1132                     if (n1.type == "node_const" && n1.value === 0.0) {
1133                         return n0;
1134                     }
1135                     if (
1136                         n0.type == "node_const" &&
1137                         n1.type == "node_const" &&
1138                         n0.value == n1.value
1139                     ) {
1140                         return this.createNode("node_const", 0.0);
1141                     }
1142                     if (
1143                         n0.type == "node_var" &&
1144                         n1.type == "node_var" &&
1145                         n0.value == n1.value
1146                     ) {
1147                         return this.createNode("node_const", 0.0);
1148                     }
1149 
1150                     // const - const -> const
1151                     if (n0.type == "node_const" && n1.type == "node_const") {
1152                         n0.value -= n1.value;
1153                         return n0;
1154                     }
1155 
1156                     // const * a - const * a -> const * a
1157                     if (
1158                         n0.type == "node_op" &&
1159                         n0.value == "op_mul" &&
1160                         n1.type == "node_op" &&
1161                         n1.value == "op_mul"
1162                     ) {
1163                         n0.children[1].hash = this.parser.compile(n0.children[1]);
1164                         n1.children[1].hash = this.parser.compile(n1.children[1]);
1165                         if (n0.children[1].hash === n1.children[1].hash) {
1166                             node.value = "op_mul";
1167                             node.children = [
1168                                 this.createNode(
1169                                     "node_op",
1170                                     "op_sub",
1171                                     n0.children[0],
1172                                     n1.children[0]
1173                                 ),
1174                                 n0.children[1]
1175                             ];
1176                             this.mayNotBeSimplified = true;
1177                             return node;
1178                         }
1179                     }
1180                     // const * a - a -> (const - 1) * a
1181                     if (n0.type == "node_op" && n0.value == "op_mul") {
1182                         n0.children[1].hash = this.parser.compile(n0.children[1]);
1183                         n1.hash = this.parser.compile(n1);
1184                         if (n0.children[1].hash === n1.hash) {
1185                             node.value = "op_mul";
1186                             node.children = [
1187                                 this.createNode(
1188                                     "node_op",
1189                                     "op_sub",
1190                                     n0.children[0],
1191                                     this.createNode("node_const", 1.0)
1192                                 ),
1193                                 n1
1194                             ];
1195                             this.mayNotBeSimplified = true;
1196                             return node;
1197                         }
1198                     }
1199                     // a - const*a -> (const - 1) * a
1200                     if (n1.type == "node_op" && n1.value == "op_mul") {
1201                         n1.children[1].hash = this.parser.compile(n1.children[1]);
1202                         n0.hash = this.parser.compile(n0);
1203                         if (n1.children[1].hash === n0.hash) {
1204                             node.value = "op_mul";
1205                             node.children = [
1206                                 this.createNode(
1207                                     "node_op",
1208                                     "op_sub",
1209                                     this.createNode("node_const", 1.0),
1210                                     n1.children[0]
1211                                 ),
1212                                 n0
1213                             ];
1214                             this.mayNotBeSimplified = true;
1215                             return node;
1216                         }
1217                     }
1218 
1219                     break;
1220 
1221                 // -0 -> 0
1222                 // -(-b) = b
1223                 case "op_neg":
1224                     n0 = node.children[0];
1225                     if (n0.type == "node_const" && n0.value === 0.0) {
1226                         return n0;
1227                     }
1228                     if (n0.type == "node_op" && n0.value == "op_neg") {
1229                         return n0.children[0];
1230                     }
1231                     break;
1232 
1233                 // a / a -> 1, a != 0
1234                 // 0 / a -> 0, a != 0
1235                 // a / 0 -> Infinity, a != 0
1236                 // 0 / 0 -> NaN, a == 0
1237                 case "op_div":
1238                     n0 = node.children[0];
1239                     n1 = node.children[1];
1240                     if (
1241                         n0.type == "node_const" &&
1242                         n1.type == "node_const" &&
1243                         n0.value == n1.value &&
1244                         n0.value !== 0
1245                     ) {
1246                         n0.value = 1.0;
1247                         return n0;
1248                     }
1249                     if (
1250                         n0.type == "node_const" &&
1251                         n0.value === 0 &&
1252                         n1.type == "node_const" &&
1253                         n1.value !== 0
1254                     ) {
1255                         n0.value = 0.0;
1256                         return n0;
1257                     }
1258 
1259                     // Risky: 0 / (something != 0) -> 0.0
1260                     if (
1261                         n0.type == "node_const" &&
1262                         n0.value === 0 &&
1263                         (n1.type == "node_op" || n1.type == "node_var")
1264                     ) {
1265                         node.type = "node_const";
1266                         node.value = 0.0;
1267                         return node;
1268                     }
1269 
1270                     if (
1271                         n0.type == "node_var" &&
1272                         n1.type == "node_var" &&
1273                         n0.value == n1.value
1274                     ) {
1275                         return this.createNode("node_const", 1.0);
1276                     }
1277                     if (
1278                         n0.type == "node_const" &&
1279                         n0.value !== 0 &&
1280                         n1.type == "node_const" &&
1281                         n1.value === 0
1282                     ) {
1283                         if (n0.value > 0.0) {
1284                             n0.value = Infinity;
1285                         } else {
1286                             n0.value = -Infinity; // Do we ever need this?
1287                         }
1288                         return n0;
1289                     }
1290 
1291                     // (-a) / (-b) -> a/b
1292                     if (
1293                         n0.type == "node_op" &&
1294                         n0.value == "op_neg" &&
1295                         n1.type == "node_op" &&
1296                         n1.value == "op_neg"
1297                     ) {
1298                         node.children = [n0.children[0], n1.children[0]];
1299                         this.mayNotBeSimplified = true;
1300                         return node;
1301                     }
1302                     // (-a) / b -> -(a/b)
1303                     if (n0.value == "op_neg" && n1.value != "op_neg") {
1304                         node.type = "node_op";
1305                         node.value = "op_neg";
1306                         node.children = [
1307                             this.createNode("node_op", "op_div", n0.children[0], n1)
1308                         ];
1309                         this.mayNotBeSimplified = true;
1310                         return node;
1311                     }
1312                     // a / (-b) -> -(a/b)
1313                     if (n0.value != "op_neg" && n1.value == "op_neg") {
1314                         node.type = "node_op";
1315                         node.value = "op_neg";
1316                         node.children = [
1317                             this.createNode("node_op", "op_div", n0, n1.children[0])
1318                         ];
1319                         this.mayNotBeSimplified = true;
1320                         return node;
1321                     }
1322 
1323                     // a^b / a -> a^(b-1)
1324                     if (n0.type == "node_op" && n0.value == "op_exp") {
1325                         if (!n1.hash) {
1326                             n1.hash = this.parser.compile(n1);
1327                         }
1328                         if (!n0.children[0].hash) {
1329                             n0.children[0].hash = this.parser.compile(n0.children[0]);
1330                         }
1331                         if (n1.hash === n0.children[0].hash) {
1332                             n0.children[1] = this.createNode(
1333                                 "node_op",
1334                                 "op_sub",
1335                                 n0.children[1],
1336                                 this.createNode("node_const", 1.0)
1337                             );
1338                             this.mayNotBeSimplified = true;
1339                             return n0;
1340                         }
1341                     }
1342 
1343                     // (const * a) / b -> const * (a / b)
1344                     if (
1345                         n1.type != "node_const" &&
1346                         n0.type == "node_op" &&
1347                         n0.value == "op_mul" &&
1348                         n0.children[0].type == "node_const"
1349                     ) {
1350                         node.value = "op_mul";
1351                         node.children = [
1352                             n0.children[0],
1353                             this.createNode("node_op", "op_div", n0.children[1], n1)
1354                         ];
1355                         this.mayNotBeSimplified = true;
1356                         return node;
1357                     }
1358 
1359                     // a^b / a^c -> a^(b-c)
1360                     if (
1361                         n0.type == "node_op" &&
1362                         n0.value == "op_exp" &&
1363                         n1.type == "node_op" &&
1364                         n1.value == "op_exp"
1365                     ) {
1366                         n0.children[0].hash = this.parser.compile(n0.children[0]);
1367                         n1.children[0].hash = this.parser.compile(n1.children[0]);
1368                         if (n0.children[0].hash === n1.children[0].hash) {
1369                             n0.children[1] = this.createNode(
1370                                 "node_op",
1371                                 "op_sub",
1372                                 n0.children[1],
1373                                 n1.children[1]
1374                             );
1375                             this.mayNotBeSimplified = true;
1376                             return n0;
1377                         }
1378                     }
1379 
1380                     break;
1381 
1382                 // a^0 = 1
1383                 // a^1 -> a
1384                 // 1^a -> 1
1385                 // 0^a -> 0: a const != 0
1386                 case "op_exp":
1387                     n0 = node.children[0];
1388                     n1 = node.children[1];
1389                     if (n1.type == "node_const" && n1.value === 0.0) {
1390                         n1.value = 1.0;
1391                         return n1;
1392                     }
1393                     if (n1.type == "node_const" && n1.value == 1.0) {
1394                         return n0;
1395                     }
1396                     if (n0.type == "node_const" && n0.value == 1.0) {
1397                         return n0;
1398                     }
1399                     if (
1400                         n0.type == "node_const" &&
1401                         n0.value === 0.0 &&
1402                         n1.type == "node_const" &&
1403                         n1.value !== 0.0
1404                     ) {
1405                         return n0;
1406                     }
1407 
1408                     // (a^b)^c -> a^(b*c)
1409                     if (n0.type == "node_op" && n0.value == "op_exp") {
1410                         node.children = [
1411                             n0.children[0],
1412                             this.createNode("node_op", "op_mul", n0.children[1], n1)
1413                         ];
1414                         return node;
1415                     }
1416                     break;
1417             }
1418 
1419             switch (node.value) {
1420                 // const_1 + const_2 -> (const_1 + const_2)
1421                 // a + a -> 2*a
1422                 // a + (-b) = a - b
1423                 case "op_add":
1424                     n0 = node.children[0];
1425                     n1 = node.children[1];
1426                     if (
1427                         n0.type == "node_const" &&
1428                         n1.type == "node_const" &&
1429                         n0.value == n1.value
1430                     ) {
1431                         n0.value += n1.value;
1432                         return n0;
1433                     }
1434 
1435                     if (
1436                         n0.type == "node_var" &&
1437                         n1.type == "node_var" &&
1438                         n0.value == n1.value
1439                     ) {
1440                         node.children[0] = this.createNode("node_const", 2.0);
1441                         node.value = "op_mul";
1442                         return node;
1443                     }
1444 
1445                     if (n0.type == "node_op" && n0.value == "op_neg") {
1446                         node.value = "op_sub";
1447                         node.children[0] = n1;
1448                         node.children[1] = n0.children[0];
1449                         this.mayNotBeSimplified = true;
1450                         return node;
1451                     }
1452 
1453                     if (n1.type == "node_op" && n1.value == "op_neg") {
1454                         node.value = "op_sub";
1455                         node.children[1] = n1.children[0];
1456                         this.mayNotBeSimplified = true;
1457                         return node;
1458                     }
1459 
1460                     // const * a + const * a -> const * a
1461                     if (
1462                         n0.type == "node_op" &&
1463                         n0.value == "op_mul" &&
1464                         n1.type == "node_op" &&
1465                         n1.value == "op_mul"
1466                     ) {
1467                         n0.children[1].hash = this.parser.compile(n0.children[1]);
1468                         n1.children[1].hash = this.parser.compile(n1.children[1]);
1469                         if (n0.children[1].hash === n1.children[1].hash) {
1470                             node.value = "op_mul";
1471                             node.children = [
1472                                 this.createNode(
1473                                     "node_op",
1474                                     "op_add",
1475                                     n0.children[0],
1476                                     n1.children[0]
1477                                 ),
1478                                 n0.children[1]
1479                             ];
1480                             this.mayNotBeSimplified = true;
1481                             return node;
1482                         }
1483                     }
1484                     // const * a + a -> (const + 1) * a
1485                     if (n0.type == "node_op" && n0.value == "op_mul") {
1486                         n0.children[1].hash = this.parser.compile(n0.children[1]);
1487                         n1.hash = this.parser.compile(n1);
1488                         if (n0.children[1].hash === n1.hash) {
1489                             node.value = "op_mul";
1490                             node.children = [
1491                                 this.createNode(
1492                                     "node_op",
1493                                     "op_add",
1494                                     n0.children[0],
1495                                     this.createNode("node_const", 1.0)
1496                                 ),
1497                                 n1
1498                             ];
1499                             this.mayNotBeSimplified = true;
1500                             return node;
1501                         }
1502                     }
1503                     // a + const*a -> (const + 1) * a
1504                     if (n1.type == "node_op" && n1.value == "op_mul") {
1505                         n1.children[1].hash = this.parser.compile(n1.children[1]);
1506                         n0.hash = this.parser.compile(n0);
1507                         if (n1.children[1].hash === n0.hash) {
1508                             node.value = "op_mul";
1509                             node.children = [
1510                                 this.createNode(
1511                                     "node_op",
1512                                     "op_add",
1513                                     this.createNode("node_const", 1.0),
1514                                     n1.children[0]
1515                                 ),
1516                                 n0
1517                             ];
1518                             this.mayNotBeSimplified = true;
1519                             return node;
1520                         }
1521                     }
1522 
1523                     break;
1524 
1525                 // a - (-b) = a + b
1526                 case "op_sub":
1527                     n0 = node.children[0];
1528                     n1 = node.children[1];
1529                     if (n1.type == "node_op" && n1.value == "op_neg") {
1530                         node.value = "op_add";
1531                         node.children[1] = n1.children[0];
1532                         this.mayNotBeSimplified = true;
1533                         return node;
1534                     }
1535                     break;
1536 
1537                 case "op_execfun":
1538                     return this.simplifyElementary(node);
1539             }
1540 
1541             return node;
1542         },
1543 
1544         simplifyElementary: function (node) {
1545             var fun = node.children[0].value,
1546                 arg = node.children[1];
1547 
1548             // Catch errors of the form sin()
1549             if (arg.length == 0) {
1550                 return node;
1551             }
1552 
1553             switch (fun) {
1554                 // sin(0) -> 0
1555                 // sin(PI) -> 0
1556                 // sin (int * PI) -> 0
1557                 // sin (PI * int) -> 0
1558                 // Same for tan()
1559                 case "sin":
1560                 case "tan":
1561                     if (arg[0].type == "node_const" && arg[0].value === 0) {
1562                         node.type = "node_const";
1563                         node.value = 0.0;
1564                         return node;
1565                     }
1566                     if (arg[0].type == "node_var" && arg[0].value == "PI") {
1567                         node.type = "node_const";
1568                         node.value = 0.0;
1569                         return node;
1570                     }
1571                     if (
1572                         arg[0].type == "node_op" &&
1573                         arg[0].value == "op_mul" &&
1574                         arg[0].children[0].type == "node_const" &&
1575                         arg[0].children[0].value % 1 === 0 &&
1576                         arg[0].children[1].type == "node_var" &&
1577                         arg[0].children[1].value == "PI"
1578                     ) {
1579                         node.type = "node_const";
1580                         node.value = 0.0;
1581                         return node;
1582                     }
1583                     break;
1584 
1585                 // cos(0) -> 1.0
1586                 // cos(PI) -> -1.0
1587                 // cos(int * PI) -> +/- 1.0
1588                 // cos(PI * int) -> +/- 1.0
1589                 case "cos":
1590                     if (arg[0].type == "node_const" && arg[0].value === 0) {
1591                         node.type = "node_const";
1592                         node.value = 1.0;
1593                         return node;
1594                     }
1595                     if (arg[0].type == "node_var" && arg[0].value == "PI") {
1596                         node.type = "node_op";
1597                         node.value = "op_neg";
1598                         node.children = [this.createNode("node_const", 1.0)];
1599                         return node;
1600                     }
1601                     /*
1602                     if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' &&
1603                         ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 &&
1604                          arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') ||
1605                          (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 &&
1606                           arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) {
1607                         node.type = 'node_const';
1608                         node.value = 1.0;
1609                         return node;
1610                     }
1611                     */
1612                     break;
1613 
1614                 // exp(0) -> 1
1615                 case "exp":
1616                     if (arg[0].type == "node_const" && arg[0].value === 0) {
1617                         node.type = "node_const";
1618                         node.value = 1.0;
1619                         return node;
1620                     }
1621                     break;
1622 
1623                 // pow(a, 0) -> 1
1624                 case "pow":
1625                     if (arg[1].type == "node_const" && arg[1].value === 0) {
1626                         node.type = "node_const";
1627                         node.value = 1.0;
1628                         return node;
1629                     }
1630                     break;
1631             }
1632 
1633             return node;
1634         }
1635     }
1636 );
1637 
1638 export default JXG.CA;
1639