jsxgraph 1.4.0 → 1.4.3

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