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