1 |
|
2 | var assert = require('assert');
|
3 | var approx = require('../../tools/approx');
|
4 | var math = require('../../index');
|
5 | var ArgumentsError = require('../../lib/error/ArgumentsError');
|
6 | var parse = math.expression.parse;
|
7 | var ConditionalNode = math.expression.node.ConditionalNode;
|
8 | var OperatorNode = math.expression.node.OperatorNode;
|
9 | var RangeNode = math.expression.node.RangeNode;
|
10 | var Complex = math.type.Complex;
|
11 | var Matrix = math.type.Matrix;
|
12 | var Range = math.type.Range;
|
13 | var Unit = math.type.Unit;
|
14 | var ResultSet = math.type.ResultSet;
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | function parseAndEval(expr, scope) {
|
23 | return parse(expr).eval(scope);
|
24 | }
|
25 |
|
26 | describe('parse', function() {
|
27 |
|
28 | it('should parse a single expression', function() {
|
29 | approx.equal(parse('2 + 6 / 3').compile().eval(), 4);
|
30 | });
|
31 |
|
32 | it('should parse an empty expression', function() {
|
33 | assert.strictEqual(parse('').compile().eval(), undefined);
|
34 | assert.strictEqual(parse('\n').compile().eval(), undefined);
|
35 | assert.strictEqual(parse('\n\n').compile().eval(), undefined);
|
36 | assert.strictEqual(parse('\n \n').compile().eval(), undefined);
|
37 | assert.strictEqual(parse('#foo\n').compile().eval(), undefined);
|
38 | assert.strictEqual(parse('#foo\n#bar\n').compile().eval(), undefined);
|
39 | });
|
40 |
|
41 | it('should parse an array with expressions', function() {
|
42 | var scope = {};
|
43 | assert.deepEqual(parse(['a=3', 'b=4', 'a*b']).map(function (node) {
|
44 | return node.compile().eval(scope);
|
45 | }), [3, 4, 12]);
|
46 | });
|
47 |
|
48 | it('should parse a matrix with expressions', function() {
|
49 | var scope = {};
|
50 | assert.deepEqual(parse(math.matrix(['a=3', 'b=4', 'a*b'])).map(function (node) {
|
51 | return node.compile().eval(scope);
|
52 | }), math.matrix([3, 4, 12]));
|
53 | });
|
54 |
|
55 | it('should parse an array with an empty expression', function() {
|
56 | assert.deepEqual(parse(['']).map(function (node) {
|
57 | return node.compile().eval();
|
58 | }), [undefined]);
|
59 | });
|
60 |
|
61 | it('should parse an array with an empty expression', function() {
|
62 | assert.deepEqual(parse(math.matrix([''])).map(function (node) {
|
63 | return node.compile().eval();
|
64 | }), math.matrix([undefined]));
|
65 | });
|
66 |
|
67 | it('should parse unicode and other special characters', function() {
|
68 |
|
69 | var scope = {};
|
70 |
|
71 | math.eval('$ab$c = 2', scope);
|
72 | assert.strictEqual(scope['$ab$c'], 2);
|
73 |
|
74 | math.eval('\u00E9 = 2', scope);
|
75 | assert.strictEqual(scope['\u00E9'], 2);
|
76 |
|
77 | math.eval('\u03A6 = 3', scope);
|
78 | assert.strictEqual(scope['\u03A6'], 3);
|
79 |
|
80 | math.eval('\u03A9 = 4', scope);
|
81 | assert.strictEqual(scope['\u03A9'], 4);
|
82 |
|
83 | math.eval('\u2126 = 4', scope);
|
84 | assert.strictEqual(scope['\u2126'], 4);
|
85 |
|
86 | math.eval('k\u00F6ln = 5', scope);
|
87 | assert.strictEqual(scope['k\u00F6ln'], 5);
|
88 |
|
89 |
|
90 | math.eval('\uD835\uDD38 = 1', scope);
|
91 | assert.strictEqual(scope['\uD835\uDD38'], 1);
|
92 |
|
93 |
|
94 | assert.throws(function () {
|
95 | math.eval('\uD835\uDCA3 = 1', scope);
|
96 | })
|
97 |
|
98 | });
|
99 |
|
100 | describe('multiline', function () {
|
101 |
|
102 | it('should parse multiline expressions', function() {
|
103 | assert.deepEqual(parse('a=3\nb=4\na*b').compile().eval(), new ResultSet([3, 4, 12]));
|
104 | assert.deepEqual(parse('b = 43; b * 4').compile().eval(), new ResultSet([172]));
|
105 | });
|
106 |
|
107 | it('should skip empty lines in multiline expressions', function() {
|
108 | assert.deepEqual(parse('\n;\n2 * 4\n').compile().eval(), new ResultSet([8]));
|
109 | });
|
110 |
|
111 | it('should spread operators over multiple lines', function() {
|
112 | assert.deepEqual(parse('2+\n3').compile().eval(), 5);
|
113 | assert.deepEqual(parse('2+\n\n3').compile().eval(), 5);
|
114 | assert.deepEqual(parse('2*\n3').compile().eval(), 6);
|
115 | assert.deepEqual(parse('2^\n3').compile().eval(), 8);
|
116 | assert.deepEqual(parse('2==\n3').compile().eval(), false);
|
117 | assert.deepEqual(parse('2*-\n3').compile().eval(), -6);
|
118 | });
|
119 |
|
120 | it('should parse multiple function assignments', function() {
|
121 | var scope = {};
|
122 | parse('f(x)=x*2;g(x)=x*3').compile().eval(scope);
|
123 | assert.equal(scope.f(2), 4);
|
124 | assert.equal(scope.g(2), 6);
|
125 |
|
126 | var scope2 = {};
|
127 | parse('a=2;f(x)=x^a;').compile().eval(scope2);
|
128 | assert.equal(scope2.a, 2);
|
129 | assert.equal(scope2.f(3), 9);
|
130 | });
|
131 |
|
132 | it ('should correctly scope a function variable if also used outside the function', function () {
|
133 | var scope = {};
|
134 | var res = parse('x=2;f(x)=x^2;x').compile().eval(scope);
|
135 |
|
136 | assert.deepEqual(res, {entries: [2]});
|
137 | assert.equal(scope.x, 2);
|
138 | assert.equal(scope.f(3), 9);
|
139 | });
|
140 |
|
141 | it('should spread a function over multiple lines', function() {
|
142 | assert.deepEqual(parse('add(\n4\n,\n2\n)').compile().eval(), 6);
|
143 | });
|
144 |
|
145 | it('should spread contents of parameters over multiple lines', function() {
|
146 | assert.deepEqual(parse('(\n4\n+\n2\n)').compile().eval(), 6);
|
147 | });
|
148 |
|
149 | it('should spread a function assignment over multiple lines', function() {
|
150 | assert.deepEqual(typeof parse('f(\nx\n,\ny\n)=\nx+\ny').compile().eval(), 'function');
|
151 | });
|
152 |
|
153 | it('should spread a variable assignment over multiple lines', function() {
|
154 | assert.deepEqual(parse('x=\n2').compile().eval(), 2);
|
155 | });
|
156 |
|
157 | it('should spread a matrix over multiple lines', function() {
|
158 | assert.deepEqual(parse('[\n1\n,\n2\n]').compile().eval(), math.matrix([1, 2]));
|
159 | });
|
160 |
|
161 | it('should spread a range over multiple lines', function() {
|
162 | assert.deepEqual(parse('2:\n4').compile().eval(), math.matrix([2,3,4]));
|
163 | assert.deepEqual(parse('2:\n2:\n6').compile().eval(), math.matrix([2,4,6]));
|
164 | });
|
165 |
|
166 | it('should spread an index over multiple lines', function() {
|
167 | assert.deepEqual(parse('a[\n1\n,\n1\n]').compile().eval({a: [[1,2],[3,4]]}), 1);
|
168 |
|
169 | var scope = {a: [[1,2],[3,4]]};
|
170 | assert.deepEqual(parse('a[\n1\n,\n1\n]=\n100').compile().eval(scope), 100);
|
171 | assert.deepEqual(scope, {a: [[100,2],[3,4]]})
|
172 | });
|
173 |
|
174 | });
|
175 |
|
176 | it('should throw an error when scope contains a reserved keyword', function() {
|
177 | var scope = {
|
178 | end: 2
|
179 | };
|
180 | assert.throws(function () {
|
181 | parse('2+3').compile().eval(scope);
|
182 | }, /Scope contains an illegal symbol/);
|
183 | });
|
184 |
|
185 | it('should give informative syntax errors', function() {
|
186 | assert.throws(function () {parse('2 +');}, /Unexpected end of expression \(char 4\)/);
|
187 | assert.throws(function () {parse('2 + 3 + *');}, /Value expected \(char 9\)/);
|
188 | });
|
189 |
|
190 | it('should throw an error if called with wrong number of arguments', function() {
|
191 | assert.throws(function () {parse();}, ArgumentsError);
|
192 | assert.throws(function () {parse(1,2,3);}, ArgumentsError);
|
193 | assert.throws(function () {parse([1, 2]);}, TypeError);
|
194 | });
|
195 |
|
196 | it('should throw an error if called with a wrong type of argument', function() {
|
197 | assert.throws(function () {parse(23);}, TypeError);
|
198 | assert.throws(function () {parse(math.unit('5cm'));}, TypeError);
|
199 | assert.throws(function () {parse(new Complex(2,3));}, TypeError);
|
200 | assert.throws(function () {parse(true);}, TypeError);
|
201 | });
|
202 |
|
203 | it('should throw an error in case of unsupported characters', function() {
|
204 | assert.throws(function () {parse('2\u00A1');}, /Syntax error in part "\u00A1"/);
|
205 | });
|
206 |
|
207 | describe('comments', function () {
|
208 |
|
209 | it('should skip comments', function() {
|
210 | assert.equal(parseAndEval('2 + 3 # - 4'), 5);
|
211 | });
|
212 |
|
213 | it('should skip comments in a ResultSet', function() {
|
214 | assert.deepEqual(parseAndEval('2 + 3 # - 4\n6-2'), new ResultSet([5, 4]));
|
215 | });
|
216 |
|
217 | it('should fill in the property comment of a Node', function() {
|
218 | assert.equal(parse('2 + 3').comment, '');
|
219 |
|
220 | assert.equal(parse('2 + 3 # hello').comment, '# hello');
|
221 | assert.equal(parse(' # hi').comment, '# hi');
|
222 |
|
223 | var blockNode = parse('2 # foo\n3 # bar');
|
224 | assert.equal(blockNode.blocks.length, 2);
|
225 | assert.equal(blockNode.blocks[0].node.comment, '# foo');
|
226 | assert.equal(blockNode.blocks[1].node.comment, '# bar');
|
227 | });
|
228 |
|
229 | });
|
230 |
|
231 | describe('number', function () {
|
232 |
|
233 | it('should parse valid numbers', function() {
|
234 | assert.equal(parseAndEval('0'), 0);
|
235 | assert.equal(parseAndEval('3'), 3);
|
236 | assert.equal(parseAndEval('3.2'), 3.2);
|
237 | assert.equal(parseAndEval('3.'), 3);
|
238 | assert.equal(parseAndEval('3. '), 3);
|
239 | assert.equal(parseAndEval('3.\t'), 3);
|
240 | assert.equal(parseAndEval('003.2'), 3.2);
|
241 | assert.equal(parseAndEval('003.200'), 3.2);
|
242 | assert.equal(parseAndEval('.2'), 0.2);
|
243 | assert.equal(parseAndEval('3e2'), 300);
|
244 | assert.equal(parseAndEval('300e2'), 30000);
|
245 | assert.equal(parseAndEval('300e+2'), 30000);
|
246 | assert.equal(parseAndEval('300e-2'), 3);
|
247 | assert.equal(parseAndEval('300E-2'), 3);
|
248 | assert.equal(parseAndEval('3.2e2'), 320);
|
249 | });
|
250 |
|
251 | it('should parse a number followed by e', function() {
|
252 | approx.equal(parseAndEval('2e'), 2 * Math.E);
|
253 | });
|
254 |
|
255 | it('should throw an error with invalid numbers', function() {
|
256 | assert.throws(function () {parseAndEval('.'); }, /Value expected/);
|
257 | assert.throws(function () {parseAndEval('3.2.2'); }, SyntaxError);
|
258 | assert.throws(function () {parseAndEval('3.2e2.2'); }, SyntaxError);
|
259 |
|
260 | assert.throws(function () {parseAndEval('3e0.5'); }, /Digit expected, got "."/);
|
261 | assert.throws(function () {parseAndEval('3e.5'); }, /Digit expected, got "."/);
|
262 | assert.throws(function () {parseAndEval('-3e0.5'); }, /Digit expected, got "."/);
|
263 | assert.throws(function () {parseAndEval('-3e.5'); }, /Digit expected, got "."/);
|
264 | assert.throws(function () {parseAndEval('3e-0.5'); }, /Digit expected, got "."/);
|
265 | assert.throws(function () {parseAndEval('3e-.5'); }, /Digit expected, got "."/);
|
266 | assert.throws(function () {parseAndEval('-3e-0.5'); }, /Digit expected, got "."/);
|
267 | assert.throws(function () {parseAndEval('-3e-.5'); }, /Digit expected, got "."/);
|
268 |
|
269 | assert.throws(function () {parseAndEval('2e+a'); }, /Digit expected, got "a"/);
|
270 | });
|
271 |
|
272 | });
|
273 |
|
274 | describe('bignumber', function () {
|
275 |
|
276 | it('should parse bignumbers', function() {
|
277 | assert.deepEqual(parseAndEval('bignumber(0.1)'), math.bignumber(0.1));
|
278 | assert.deepEqual(parseAndEval('bignumber("1.2e500")'), math.bignumber('1.2e500'));
|
279 | });
|
280 |
|
281 | it('should output bignumbers if default number type is bignumber', function() {
|
282 | var bigmath = math.create({
|
283 | number: 'BigNumber'
|
284 | });
|
285 |
|
286 | assert.deepEqual(bigmath.parse('0.1').compile().eval(), bigmath.bignumber(0.1));
|
287 | assert.deepEqual(bigmath.parse('1.2e5000').compile().eval(), bigmath.bignumber('1.2e5000'));
|
288 | });
|
289 |
|
290 | });
|
291 |
|
292 | describe('fraction', function () {
|
293 |
|
294 | it('should output fractions if default number type is fraction', function() {
|
295 | var fmath = math.create({
|
296 | number: 'Fraction'
|
297 | });
|
298 |
|
299 | assert(fmath.parse('0.1').compile().eval() instanceof math.type.Fraction);
|
300 | assert.equal(fmath.parse('1/3').compile().eval().toString(), '0.(3)');
|
301 | assert.equal(fmath.parse('0.1+0.2').compile().eval().toString(), '0.3');
|
302 | });
|
303 |
|
304 | });
|
305 |
|
306 | describe('string', function () {
|
307 |
|
308 | it('should parse a string', function() {
|
309 | assert.deepEqual(parseAndEval('"hello"'), "hello");
|
310 | assert.deepEqual(parseAndEval(' "hi" '), "hi");
|
311 | });
|
312 |
|
313 | it('should parse a with escaped characters', function() {
|
314 | assert.deepEqual(parseAndEval('"line end\\nnext"'), 'line end\nnext');
|
315 | assert.deepEqual(parseAndEval('"line end\\n"'), 'line end\n');
|
316 | assert.deepEqual(parseAndEval('"tab\\tnext"'), 'tab\tnext');
|
317 | assert.deepEqual(parseAndEval('"tab\\t"'), 'tab\t');
|
318 | assert.deepEqual(parseAndEval('"escaped backslash\\\\next"'), 'escaped backslash\\next');
|
319 | assert.deepEqual(parseAndEval('"escaped backslash\\\\"'), 'escaped backslash\\');
|
320 | });
|
321 |
|
322 | it('should throw an error with invalid strings', function() {
|
323 | assert.throws(function () {parseAndEval('"hi'); }, SyntaxError);
|
324 | assert.throws(function () {parseAndEval(' hi" '); }, Error);
|
325 | });
|
326 |
|
327 | it('should get a string subset', function() {
|
328 | var scope = {};
|
329 | assert.deepEqual(parseAndEval('c="hello"', scope), "hello");
|
330 | assert.deepEqual(parseAndEval('c[2:4]', scope), "ell");
|
331 | assert.deepEqual(parseAndEval('c[5:-1:1]', scope), "olleh");
|
332 | assert.deepEqual(parseAndEval('c[end-2:-1:1]', scope), "leh");
|
333 | assert.deepEqual(parseAndEval('"hello"[2:4]', scope), "ell");
|
334 | });
|
335 |
|
336 | it('should set a string subset', function() {
|
337 | var scope = {};
|
338 | assert.deepEqual(parseAndEval('c="hello"', scope), "hello");
|
339 | assert.deepEqual(parseAndEval('c[1] = "H"', scope), "H");
|
340 | assert.deepEqual(scope.c, "Hello");
|
341 | assert.deepEqual(parseAndEval('c', scope), "Hello");
|
342 | assert.deepEqual(parseAndEval('c[6:11] = " world"', scope), " world");
|
343 | assert.deepEqual(scope.c, "Hello world");
|
344 | assert.deepEqual(parseAndEval('c[end] = "D"', scope), "D");
|
345 | assert.deepEqual(scope.c, "Hello worlD");
|
346 | });
|
347 |
|
348 | it('should set a string subset on an object', function() {
|
349 | var scope = { a: {} };
|
350 | assert.deepEqual(parseAndEval('a.c="hello"', scope), "hello");
|
351 | assert.deepEqual(parseAndEval('a.c[1] = "H"', scope), "H");
|
352 | assert.deepEqual(scope.a, {c: "Hello"});
|
353 | assert.deepEqual(parseAndEval('a.c', scope), "Hello");
|
354 | assert.deepEqual(parseAndEval('a.c[6:11] = " world"', scope), " world");
|
355 | assert.deepEqual(scope.a, {c: "Hello world"});
|
356 | assert.deepEqual(parseAndEval('a.c', scope), "Hello world");
|
357 | assert.deepEqual(scope.a, {c: "Hello world"});
|
358 | assert.deepEqual(parseAndEval('a.c[end] = "D"', scope), "D");
|
359 | assert.deepEqual(scope.a, {c: "Hello worlD"});
|
360 | });
|
361 |
|
362 | });
|
363 |
|
364 | describe('unit', function () {
|
365 |
|
366 | it('should parse units', function() {
|
367 | assert.deepEqual(parseAndEval('5cm'), new Unit(5, 'cm'));
|
368 | assert.ok(parseAndEval('5cm') instanceof Unit);
|
369 | });
|
370 |
|
371 | it('should parse constants', function() {
|
372 | assert.equal(parseAndEval('pi'), Math.PI);
|
373 | });
|
374 |
|
375 | it('should parse physical constants', function() {
|
376 | var expected = new Unit(299792458, 'm/s');
|
377 | expected.fixPrefix = true;
|
378 | assert.deepEqual(parseAndEval('speedOfLight'), expected);
|
379 | });
|
380 |
|
381 | it('should correctly parse negative temperatures', function () {
|
382 | approx.deepEqual(parseAndEval('-6 celsius'), new Unit(-6, 'celsius'));
|
383 | approx.deepEqual(parseAndEval('--6 celsius'), new Unit(6, 'celsius'));
|
384 | approx.deepEqual(parseAndEval('-6 celsius to fahrenheit'),
|
385 | new Unit(21.2, 'fahrenheit').to('fahrenheit'));
|
386 | });
|
387 |
|
388 | it('should convert units', function() {
|
389 | var scope = {};
|
390 | approx.deepEqual(parseAndEval('(5.08 cm * 1000) to inch', scope),
|
391 | math.unit(2000, 'inch').to('inch'));
|
392 | approx.deepEqual(parseAndEval('a = (5.08 cm * 1000) to mm', scope),
|
393 | math.unit(50800, 'mm').to('mm'));
|
394 | approx.deepEqual(parseAndEval('a to inch', scope),
|
395 | math.unit(2000, 'inch').to('inch'));
|
396 |
|
397 | approx.deepEqual(parseAndEval('10 celsius to fahrenheit'),
|
398 | math.unit(50, 'fahrenheit').to('fahrenheit'));
|
399 | approx.deepEqual(parseAndEval('20 celsius to fahrenheit'),
|
400 | math.unit(68, 'fahrenheit').to('fahrenheit'));
|
401 | approx.deepEqual(parseAndEval('50 fahrenheit to celsius'),
|
402 | math.unit(10, 'celsius').to('celsius'));
|
403 | });
|
404 |
|
405 | it('should create units and aliases', function() {
|
406 | var myMath = math.create()
|
407 | myMath.eval('createUnit("knot", {definition: "0.514444444 m/s", aliases: ["knots", "kt", "kts"]})');
|
408 | assert.equal(myMath.eval('5 knot').toString(), '5 knot');
|
409 | assert.equal(myMath.eval('5 knots').toString(), '5 knots');
|
410 | assert.equal(myMath.eval('5 kt').toString(), '5 kt');
|
411 | });
|
412 |
|
413 | it('should evaluate operator "to" with correct precedence ', function () {
|
414 | approx.deepEqual(parseAndEval('5.08 cm * 1000 to inch'),
|
415 | new Unit(2000, 'inch').to('inch'));
|
416 | });
|
417 |
|
418 | it('should evaluate operator "in" (alias of "to") ', function () {
|
419 | approx.deepEqual(parseAndEval('5.08 cm in inch'),
|
420 | new Unit(2, 'inch').to('inch'));
|
421 | });
|
422 |
|
423 | it('should evaluate unit "in" (should not conflict with operator "in")', function () {
|
424 | approx.deepEqual(parseAndEval('2 in'), new Unit(2, 'in'));
|
425 | approx.deepEqual(parseAndEval('5.08 cm in in'), new Unit(2, 'in').to('in'));
|
426 | approx.deepEqual(parseAndEval('5 in in in'), new Unit(5, 'in').to('in'));
|
427 | approx.deepEqual(parseAndEval('2 in to meter'), new Unit(2, 'inch').to('meter'));
|
428 | approx.deepEqual(parseAndEval('2 in in meter'), new Unit(2, 'inch').to('meter'));
|
429 | approx.deepEqual(parseAndEval('a in inch', {a: new Unit(5.08, 'cm')}), new Unit(2, 'inch').to('inch'));
|
430 | approx.deepEqual(parseAndEval('(2+3) in'), new Unit(5, 'in'));
|
431 | approx.deepEqual(parseAndEval('a in', {a: 5}), new Unit(5, 'in'));
|
432 | approx.deepEqual(parseAndEval('0.5in + 1.5in to cm'), new Unit(5.08, 'cm').to('cm'));
|
433 | });
|
434 | });
|
435 |
|
436 | describe('complex', function () {
|
437 |
|
438 | it('should parse complex values', function () {
|
439 | assert.deepEqual(parseAndEval('i'), new Complex(0,1));
|
440 | assert.deepEqual(parseAndEval('2+3i'), new Complex(2,3));
|
441 | assert.deepEqual(parseAndEval('2+3*i'), new Complex(2,3));
|
442 | assert.deepEqual(parseAndEval('1/2i'), new Complex(0, 0.5));
|
443 | });
|
444 |
|
445 | });
|
446 |
|
447 | describe('matrix', function () {
|
448 |
|
449 | it('should parse a matrix', function() {
|
450 | assert.ok(parseAndEval('[1,2;3,4]') instanceof Matrix);
|
451 |
|
452 | var m = parseAndEval('[1,2,3;4,5,6]');
|
453 | assert.deepEqual(m.size(), [2,3]);
|
454 | assert.deepEqual(m, math.matrix([[1,2,3],[4,5,6]]));
|
455 |
|
456 | var b = parseAndEval('[5, 6; 1, 1]');
|
457 | assert.deepEqual(b.size(), [2,2]);
|
458 | assert.deepEqual(b, math.matrix([[5,6],[1,1]]));
|
459 |
|
460 |
|
461 | assert.deepEqual(parseAndEval('[ ]'), math.matrix([]));
|
462 | assert.deepEqual(parseAndEval('[1,2,3]'), math.matrix([1,2,3]));
|
463 | assert.deepEqual(parseAndEval('[1;2;3]'), math.matrix([[1],[2],[3]]));
|
464 | assert.deepEqual(parseAndEval('[[1,2],[3,4]]'), math.matrix([[1,2],[3,4]]));
|
465 | assert.deepEqual(parseAndEval('[[[1],[2]],[[3],[4]]]'), math.matrix([[[1],[2]],[[3],[4]]]));
|
466 | });
|
467 |
|
468 | it('should parse an empty matrix', function() {
|
469 | assert.deepEqual(parseAndEval('[]'), math.matrix([]));
|
470 | });
|
471 |
|
472 | it('should get a matrix subset', function() {
|
473 | var scope = {
|
474 | a: math.matrix([
|
475 | [1,2,3],
|
476 | [4,5,6],
|
477 | [7,8,9]
|
478 | ])
|
479 | };
|
480 | assert.deepEqual(parseAndEval('a[2, :]', scope), math.matrix([[4,5,6]]));
|
481 | assert.deepEqual(parseAndEval('a[2, :2]', scope), math.matrix([[4,5]]));
|
482 | assert.deepEqual(parseAndEval('a[2, :end-1]', scope), math.matrix([[4,5]]));
|
483 | assert.deepEqual(parseAndEval('a[2, 2:]', scope), math.matrix([[5,6]]));
|
484 | assert.deepEqual(parseAndEval('a[2, 2:3]', scope), math.matrix([[5,6]]));
|
485 | assert.deepEqual(parseAndEval('a[2, 1:2:3]', scope), math.matrix([[4,6]]));
|
486 | assert.deepEqual(parseAndEval('a[:, 2]', scope), math.matrix([[2],[5],[8]]));
|
487 | assert.deepEqual(parseAndEval('a[:2, 2]', scope), math.matrix([[2],[5]]));
|
488 | assert.deepEqual(parseAndEval('a[:end-1, 2]', scope), math.matrix([[2],[5]]));
|
489 | assert.deepEqual(parseAndEval('a[2:, 2]', scope), math.matrix([[5],[8]]));
|
490 | assert.deepEqual(parseAndEval('a[2:3, 2]', scope), math.matrix([[5],[8]]));
|
491 | assert.deepEqual(parseAndEval('a[1:2:3, 2]', scope), math.matrix([[2],[8]]));
|
492 | });
|
493 |
|
494 | it('should get a matrix subset of a matrix subset', function() {
|
495 | var scope = {
|
496 | a: math.matrix([
|
497 | [1,2,3],
|
498 | [4,5,6],
|
499 | [7,8,9]
|
500 | ])
|
501 | };
|
502 | assert.deepEqual(parseAndEval('a[2, :][1,1]', scope), 4);
|
503 | });
|
504 |
|
505 | it('should get BigNumber value from an array', function() {
|
506 | var res = parseAndEval('arr[1]', {arr: [math.bignumber(2)]});
|
507 | assert.deepEqual(res, math.bignumber(2));
|
508 | });
|
509 |
|
510 | it('should parse matrix resizings', function() {
|
511 | var scope = {};
|
512 | assert.deepEqual(parseAndEval('a = []', scope), math.matrix([]));
|
513 | assert.deepEqual(parseAndEval('a[1:3,1] = [1;2;3]', scope), math.matrix([[1],[2],[3]]));
|
514 | assert.deepEqual(parseAndEval('a[:,2] = [4;5;6]', scope), math.matrix([[4],[5],[6]]));
|
515 | assert.deepEqual(scope.a, math.matrix([[1,4],[2,5],[3,6]]));
|
516 |
|
517 | assert.deepEqual(parseAndEval('a = []', scope), math.matrix([]));
|
518 | assert.strictEqual(parseAndEval('a[1,3] = 3', scope), 3);
|
519 | assert.deepEqual(scope.a, math.matrix([[0,0,3]]));
|
520 | assert.deepEqual(parseAndEval('a[2,:] = [[4,5,6]]', scope), math.matrix([[4,5,6]]));
|
521 | assert.deepEqual(scope.a, math.matrix([[0,0,3],[4,5,6]]));
|
522 |
|
523 | assert.deepEqual(parseAndEval('a = []', scope), math.matrix([]));
|
524 | assert.strictEqual(parseAndEval('a[3,1] = 3', scope), 3);
|
525 | assert.deepEqual(scope.a, math.matrix([[0],[0],[3]]));
|
526 | assert.deepEqual(parseAndEval('a[:,2] = [4;5;6]', scope), math.matrix([[4],[5],[6]]));
|
527 | assert.deepEqual(scope.a, math.matrix([[0,4],[0,5],[3,6]]));
|
528 |
|
529 | assert.deepEqual(parseAndEval('a = []', scope), math.matrix([]));
|
530 | assert.deepEqual(parseAndEval('a[1,1:3] = [[1,2,3]]', scope), math.matrix([[1,2,3]]));
|
531 | assert.deepEqual(scope.a, math.matrix([[1,2,3]]));
|
532 | assert.deepEqual(parseAndEval('a[2,:] = [[4,5,6]]', scope), math.matrix([[4,5,6]]));
|
533 | assert.deepEqual(scope.a, math.matrix([[1,2,3],[4,5,6]]));
|
534 | });
|
535 |
|
536 | it('should get/set the matrix correctly', function() {
|
537 | var scope = {};
|
538 | parseAndEval('a=[1,2;3,4]', scope);
|
539 | parseAndEval('a[1,1] = 100', scope);
|
540 | assert.deepEqual(scope.a.size(), [2,2]);
|
541 | assert.deepEqual(scope.a, math.matrix([[100,2],[3,4]]));
|
542 | parseAndEval('a[2:3,2:3] = [10,11;12,13]', scope);
|
543 | assert.deepEqual(scope.a.size(), [3,3]);
|
544 | assert.deepEqual(scope.a, math.matrix([[100, 2, 0],[3,10,11],[0,12,13]]));
|
545 | var a = scope.a;
|
546 |
|
547 | assert.deepEqual(a.subset(math.index(new Range(0,3), new Range(0,2))), math.matrix([[100,2],[3,10],[0,12]]));
|
548 | assert.deepEqual(parseAndEval('a[1:3,1:2]', scope), math.matrix([[100,2],[3,10],[0,12]]));
|
549 |
|
550 | scope.b = [[1,2],[3,4]];
|
551 | assert.deepEqual(parseAndEval('b[1,:]', scope), [[1, 2]]);
|
552 | });
|
553 |
|
554 | it('should get/set the matrix correctly for 3d matrices', function() {
|
555 | var scope = {};
|
556 | assert.deepEqual(parseAndEval('f=[1,2;3,4]', scope), math.matrix([[1,2],[3,4]]));
|
557 | assert.deepEqual(parseAndEval('size(f)', scope), math.matrix([2,2]));
|
558 |
|
559 | parseAndEval('f[:,:,2]=[5,6;7,8]', scope);
|
560 | assert.deepEqual(scope.f, math.matrix([
|
561 | [
|
562 | [1,5],
|
563 | [2,6]
|
564 | ],
|
565 | [
|
566 | [3,7],
|
567 | [4,8]
|
568 | ]
|
569 | ]));
|
570 |
|
571 | assert.deepEqual(parseAndEval('size(f)', scope), math.matrix([2,2,2]));
|
572 | assert.deepEqual(parseAndEval('f[:,:,1]', scope), math.matrix([[[1],[2]],[[3],[4]]]));
|
573 | assert.deepEqual(parseAndEval('f[:,:,2]', scope), math.matrix([[[5],[6]],[[7],[8]]]));
|
574 | assert.deepEqual(parseAndEval('f[:,2,:]', scope), math.matrix([[[2,6]],[[4,8]]]));
|
575 | assert.deepEqual(parseAndEval('f[2,:,:]', scope), math.matrix([[[3,7],[4,8]]]));
|
576 |
|
577 | parseAndEval('a=diag([1,2,3,4])', scope);
|
578 | assert.deepEqual(parseAndEval('a[3:end, 3:end]', scope), math.matrix([[3,0],[0,4]]));
|
579 | parseAndEval('a[3:end, 2:end]=9*ones(2,3)', scope);
|
580 | assert.deepEqual(scope.a, math.matrix([
|
581 | [1,0,0,0],
|
582 | [0,2,0,0],
|
583 | [0,9,9,9],
|
584 | [0,9,9,9]
|
585 | ]));
|
586 | assert.deepEqual(parseAndEval('a[2:end-1, 2:end-1]', scope), math.matrix([[2,0],[9,9]]));
|
587 | });
|
588 |
|
589 | it('should merge nested matrices', function() {
|
590 | var scope = {};
|
591 | parseAndEval('a=[1,2;3,4]', scope);
|
592 |
|
593 | });
|
594 |
|
595 | it('should parse matrix concatenations', function() {
|
596 | var scope = {};
|
597 | parseAndEval('a=[1,2;3,4]', scope);
|
598 | parseAndEval('b=[5,6;7,8]', scope);
|
599 | assert.deepEqual(parseAndEval('c=concat(a,b)', scope), math.matrix([[1,2,5,6],[3,4,7,8]]));
|
600 | assert.deepEqual(parseAndEval('c=concat(a,b,1)', scope), math.matrix([[1,2],[3,4],[5,6],[7,8]]));
|
601 | assert.deepEqual(parseAndEval('c=concat(concat(a,b), concat(b,a), 1)', scope), math.matrix([[1,2,5,6],[3,4,7,8],[5,6,1,2],[7,8,3,4]]));
|
602 | assert.deepEqual(parseAndEval('c=concat([[1,2]], [[3,4]], 1)', scope), math.matrix([[1,2],[3,4]]));
|
603 | assert.deepEqual(parseAndEval('c=concat([[1,2]], [[3,4]], 2)', scope), math.matrix([[1,2,3,4]]));
|
604 | assert.deepEqual(parseAndEval('c=concat([[1]], [2;3], 1)', scope), math.matrix([[1],[2],[3]]));
|
605 | assert.deepEqual(parseAndEval('d=1:3', scope), math.matrix([1,2,3]));
|
606 | assert.deepEqual(parseAndEval('concat(d,d)', scope), math.matrix([1,2,3,1,2,3]));
|
607 | assert.deepEqual(parseAndEval('e=1+d', scope), math.matrix([2,3,4]));
|
608 | assert.deepEqual(parseAndEval('size(e)', scope), math.matrix([3]));
|
609 | assert.deepEqual(parseAndEval('concat(e,e)', scope), math.matrix([2,3,4,2,3,4]));
|
610 | assert.deepEqual(parseAndEval('[[],[]]', scope), math.matrix([[],[]]));
|
611 | assert.deepEqual(parseAndEval('[[],[]]', scope).size(), [2, 0]);
|
612 | assert.deepEqual(parseAndEval('size([[],[]])', scope), math.matrix([2, 0]));
|
613 | });
|
614 |
|
615 | it('should disable arrays as range in a matrix index', function () {
|
616 | var scope = {
|
617 | a: [[1,2,3],[4,5,6]]
|
618 | };
|
619 |
|
620 | assert.throws(function () {
|
621 | parseAndEval('a[2, 2+3i]', scope);
|
622 | }, /TypeError: Dimension must be an Array, Matrix, number, string, or Range/);
|
623 | });
|
624 |
|
625 | it('should throw an error for invalid matrix', function() {
|
626 | assert.throws(function () {parseAndEval('[1, 2');}, /End of matrix ] expected/);
|
627 | assert.throws(function () {parseAndEval('[1; 2');}, /End of matrix ] expected/);
|
628 | });
|
629 |
|
630 | it('should throw an error when matrix rows mismatch', function() {
|
631 | assert.throws(function () {parseAndEval('[1, 2; 1, 2, 3]');}, /Column dimensions mismatch/);
|
632 | });
|
633 |
|
634 | it('should throw an error for invalid matrix subsets', function() {
|
635 | var scope = {a: [1,2,3]};
|
636 | assert.throws(function () {parseAndEval('a[1', scope);}, /Parenthesis ] expected/);
|
637 | });
|
638 |
|
639 | it('should throw an error for invalid matrix concatenations', function() {
|
640 | var scope = {};
|
641 | assert.throws(function () {parseAndEval('c=concat(a, [1,2,3])', scope);});
|
642 | });
|
643 | });
|
644 |
|
645 | describe('objects', function () {
|
646 |
|
647 | it('should get an object property', function () {
|
648 | assert.deepEqual(parseAndEval('obj["foo"]', {obj: {foo: 2}}), 2);
|
649 | });
|
650 |
|
651 | it('should get a nested object property', function () {
|
652 | assert.deepEqual(parseAndEval('obj["foo"]["bar"]', {obj: {foo: {bar: 2}}}), 2);
|
653 | });
|
654 |
|
655 | it('should get a nested matrix subset from an object property', function () {
|
656 | assert.deepEqual(parseAndEval('obj.foo[2]', {obj: {foo: [1,2,3]}}), 2);
|
657 | assert.deepEqual(parseAndEval('obj.foo[end]', {obj: {foo: [1,2,3]}}), 3);
|
658 | assert.deepEqual(parseAndEval('obj.foo[2][3]', {obj: {foo: ['hello', 'world']}}), 'r');
|
659 | assert.deepEqual(parseAndEval('obj.foo[2][end]', {obj: {foo: ['hello', 'world']}}), 'd');
|
660 | assert.deepEqual(parseAndEval('obj.foo[1].bar', {obj: {foo: [{bar:4}]}}), 4);
|
661 | });
|
662 |
|
663 | it('should set an object property', function () {
|
664 | var scope = {obj: {a:3}};
|
665 | var res = parseAndEval('obj["b"] = 2', scope);
|
666 | assert.strictEqual(res, 2);
|
667 | assert.deepEqual(scope, {obj: {a: 3, b: 2}});
|
668 | });
|
669 |
|
670 | it('should set a nested object property', function () {
|
671 | var scope = {obj: {foo: {}}};
|
672 | var res = parseAndEval('obj["foo"]["bar"] = 2', scope);
|
673 | assert.strictEqual(res, 2);
|
674 | assert.deepEqual(scope, {obj: {foo: {bar: 2}}});
|
675 | });
|
676 |
|
677 | it('should throw an error when trying to apply a matrix index as object property', function () {
|
678 | var scope = {a: {}};
|
679 | assert.throws(function () {
|
680 | parseAndEval('a[2] = 6', scope);
|
681 | }, /Cannot apply a numeric index as object property/);
|
682 | });
|
683 |
|
684 | it('should set a nested matrix subset from an object property (1)', function () {
|
685 | var scope = {obj: {foo: [1,2,3]}};
|
686 | assert.deepEqual(parseAndEval('obj.foo[2] = 6', scope), 6);
|
687 | assert.deepEqual(scope, {obj: {foo: [1,6,3]}});
|
688 |
|
689 | assert.deepEqual(parseAndEval('obj.foo[end] = 8', scope), 8);
|
690 | assert.deepEqual(scope, {obj: {foo: [1,6,8]}});
|
691 | });
|
692 |
|
693 | it('should set a nested matrix subset from an object property (2)', function () {
|
694 | var scope = {obj: {foo: [{bar:4}]}};
|
695 | assert.deepEqual(parseAndEval('obj.foo[1].bar = 6', scope), 6);
|
696 | assert.deepEqual(scope, {obj: {foo: [{bar: 6}]}});
|
697 | });
|
698 |
|
699 | it('should set a nested matrix subset from an object property (3)', function () {
|
700 | var scope = {obj: {foo: [{bar:{}}]}};
|
701 | assert.deepEqual(parseAndEval('obj.foo[1].bar.baz = 6', scope), 6);
|
702 | assert.deepEqual(scope, {obj: {foo: [{bar: {baz:6}}]}});
|
703 | });
|
704 |
|
705 | it('should set a nested matrix subset from an object property (4)', function () {
|
706 | var scope = {obj: {foo: ['hello', 'world']}};
|
707 | assert.deepEqual(parseAndEval('obj.foo[1][end] = "a"', scope), 'a');
|
708 | assert.deepEqual(scope, {obj: {foo: ['hella', 'world']}});
|
709 | assert.deepEqual(parseAndEval('obj.foo[end][end] = "!"', scope), '!');
|
710 | assert.deepEqual(scope, {obj: {foo: ['hella', 'worl!']}});
|
711 | });
|
712 |
|
713 |
|
714 |
|
715 | it('should get an object property with dot notation', function () {
|
716 | assert.deepEqual(parseAndEval('obj.foo', {obj: {foo: 2}}), 2);
|
717 | });
|
718 |
|
719 | it('should get an object property from an object inside parentheses', function () {
|
720 | assert.deepEqual(parseAndEval('(obj).foo', {obj: {foo: 2}}), 2);
|
721 | });
|
722 |
|
723 | it('should get a nested object property with dot notation', function () {
|
724 | assert.deepEqual(parseAndEval('obj.foo.bar', {obj: {foo: {bar: 2}}}), 2);
|
725 | });
|
726 |
|
727 | it('should invoke a function in an object', function () {
|
728 | var scope = {
|
729 | obj: {
|
730 | fn: function (x) {
|
731 | return x * x;
|
732 | }
|
733 | }
|
734 | };
|
735 | assert.deepEqual(parseAndEval('obj.fn(2)', scope), 4);
|
736 | assert.deepEqual(parseAndEval('obj["fn"](2)', scope), 4);
|
737 | });
|
738 |
|
739 | it('should invoke a function returned by a function', function () {
|
740 | var scope = {
|
741 | theAnswer: function () {
|
742 | return function () {
|
743 | return 42;
|
744 | };
|
745 | },
|
746 | partialAdd: function (a) {
|
747 | return function (b) {
|
748 | return a + b;
|
749 | };
|
750 | }
|
751 | };
|
752 | assert.deepEqual(parseAndEval('theAnswer()()', scope), 42);
|
753 | assert.deepEqual(parseAndEval('partialAdd(2)(3)', scope), 5);
|
754 | });
|
755 |
|
756 | it('should invoke a function on an object with the right context', function () {
|
757 | approx.equal(parseAndEval('(2.54 cm).toNumeric("inch")'), 1);
|
758 | assert.deepEqual(parseAndEval('bignumber(2).plus(3)'), math.bignumber(5));
|
759 | assert.deepEqual(parseAndEval('bignumber(2)["plus"](3)'), math.bignumber(5));
|
760 | });
|
761 |
|
762 | it('should invoke native methods on a number', function () {
|
763 | assert.strictEqual(parseAndEval('(3).toString()'), '3');
|
764 | assert.strictEqual(parseAndEval('(3.2).toFixed()'), '3');
|
765 | });
|
766 |
|
767 | it('should get nested object property with mixed dot- and index-notation', function () {
|
768 | assert.deepEqual(parseAndEval('obj.foo["bar"].baz', {obj: {foo: {bar: {baz: 2}}}}), 2);
|
769 | assert.deepEqual(parseAndEval('obj["foo"].bar["baz"]', {obj: {foo: {bar: {baz: 2}}}}), 2);
|
770 | });
|
771 |
|
772 | it('should set an object property with dot notation', function () {
|
773 | var scope = {obj: {}};
|
774 | parseAndEval('obj.foo = 2', scope);
|
775 | assert.deepEqual(scope, {obj: {foo: 2}});
|
776 | });
|
777 |
|
778 | it('should set a nested object property with dot notation', function () {
|
779 | var scope = {obj: {foo: {}}};
|
780 | parseAndEval('obj.foo.bar = 2', scope);
|
781 | assert.deepEqual(scope, {obj: {foo: {bar: 2}}});
|
782 | });
|
783 |
|
784 | it('should throw an error in case of invalid property with dot notation', function () {
|
785 | assert.throws(function () {parseAndEval('obj. +foo')}, /SyntaxError: Property name expected after dot \(char 6\)/);
|
786 | assert.throws(function () {parseAndEval('obj.["foo"]')}, /SyntaxError: Property name expected after dot \(char 5\)/);
|
787 | });
|
788 |
|
789 | it('should create an empty object', function () {
|
790 | assert.deepEqual(parseAndEval('{}'), {});
|
791 | });
|
792 |
|
793 | it('should create an object with quoted keys', function () {
|
794 | assert.deepEqual(parseAndEval('{"a":2+3,"b":"foo"}'), {a: 5, b: 'foo'});
|
795 | });
|
796 |
|
797 | it('should create an object with unquoted keys', function () {
|
798 | assert.deepEqual(parseAndEval('{a:2+3,b:"foo"}'), {a: 5, b: 'foo'});
|
799 | });
|
800 |
|
801 | it('should create an object with child object', function () {
|
802 | assert.deepEqual(parseAndEval('{a:{b:2}}'), {a:{b:2}})
|
803 | });
|
804 |
|
805 | it('should get a property from a just created object', function () {
|
806 | assert.deepEqual(parseAndEval('{foo:2}["foo"]'), 2);
|
807 | });
|
808 |
|
809 | it('should parse an object containing a function assignment', function () {
|
810 | var obj = parseAndEval('{f: f(x)=x^2}');
|
811 | assert.deepEqual(Object.keys(obj), ['f']);
|
812 | assert.equal(obj.f(2), 4);
|
813 | });
|
814 |
|
815 | it('should not parse a function assignment in an accessor node', function () {
|
816 | assert.throws(function () {
|
817 | var scope = {}
|
818 | var obj = parseAndEval('a["b"](x)=x^2', scope);
|
819 | }, /SyntaxError: Invalid left hand side of assignment operator =/)
|
820 | });
|
821 |
|
822 | it('should parse an object containing a variable assignment', function () {
|
823 | var scope = {};
|
824 | assert.deepEqual(parseAndEval('{f: a=42}', scope), {f: 42});
|
825 | assert.strictEqual(scope.a, 42);
|
826 | });
|
827 |
|
828 | it('should throw an exception in case of invalid object key', function () {
|
829 | assert.throws(function () {parseAndEval('{a b: 2}')}, /SyntaxError: Colon : expected after object key \(char 4\)/);
|
830 | assert.throws(function () {parseAndEval('{a: }')}, /SyntaxError: Value expected \(char 5\)/);
|
831 | });
|
832 |
|
833 | });
|
834 |
|
835 | describe('boolean', function () {
|
836 |
|
837 | it('should parse boolean values', function () {
|
838 | assert.equal(parseAndEval('true'), true);
|
839 | assert.equal(parseAndEval('false'), false);
|
840 | });
|
841 |
|
842 | });
|
843 |
|
844 |
|
845 | describe('constants', function () {
|
846 |
|
847 | it('should parse constants', function() {
|
848 | assert.deepEqual(parseAndEval('i'), new Complex(0, 1));
|
849 | approx.equal(parseAndEval('pi'), Math.PI);
|
850 | approx.equal(parseAndEval('e'), Math.E);
|
851 | });
|
852 |
|
853 | });
|
854 |
|
855 | describe('variables', function () {
|
856 |
|
857 | it('should parse valid variable assignments', function() {
|
858 | var scope = {};
|
859 | assert.equal(parseAndEval('a = 0.75', scope), 0.75);
|
860 | assert.equal(parseAndEval('a + 2', scope), 2.75);
|
861 | assert.equal(parseAndEval('a = 2', scope), 2);
|
862 | assert.equal(parseAndEval('a + 2', scope), 4);
|
863 | approx.equal(parseAndEval('pi * 2', scope), 6.283185307179586);
|
864 | });
|
865 |
|
866 | it('should throw an error on undefined symbol', function() {
|
867 | assert.throws(function() {parseAndEval('qqq + 2'); });
|
868 | });
|
869 |
|
870 | it('should throw an error on invalid assignments', function() {
|
871 |
|
872 | assert.throws(function () {parseAndEval('sin + 2 = 3');}, SyntaxError);
|
873 | });
|
874 |
|
875 | it('should parse nested assignments', function() {
|
876 | var scope = {};
|
877 | assert.equal(parseAndEval('c = d = (e = 4.5)', scope), 4.5);
|
878 | assert.equal(scope.c, 4.5);
|
879 | assert.equal(scope.d, 4.5);
|
880 | assert.equal(scope.e, 4.5);
|
881 | assert.deepEqual(parseAndEval('a = [1,2,f=3]', scope), math.matrix([1,2,3]));
|
882 | assert.equal(scope.f, 3);
|
883 | assert.equal(parseAndEval('2 + (g = 3 + 4)', scope), 9);
|
884 | assert.equal(scope.g, 7);
|
885 | });
|
886 |
|
887 | it('should parse variable assignment inside a function call', function() {
|
888 | var scope = {};
|
889 | assert.deepEqual(parseAndEval('sqrt(x=4)', scope), 2);
|
890 | assert.deepEqual(scope, { x:4 });
|
891 | });
|
892 |
|
893 | it('should parse variable assignment inside an accessor', function () {
|
894 | var scope = {A: [10,20,30]};
|
895 | assert.deepEqual(parseAndEval('A[x=2]', scope), 20);
|
896 | assert.deepEqual(scope, { A:[10,20,30], x:2 });
|
897 | });
|
898 |
|
899 | });
|
900 |
|
901 |
|
902 | describe('functions', function () {
|
903 |
|
904 | it('should parse functions', function() {
|
905 | assert.equal(parseAndEval('sqrt(4)'), 2);
|
906 | assert.equal(parseAndEval('sqrt(6+3)'), 3);
|
907 | assert.equal(parseAndEval('atan2(2,2)'), 0.7853981633974483);
|
908 | assert.deepEqual(parseAndEval('sqrt(-4)'), new Complex(0, 2));
|
909 | assert.equal(parseAndEval('abs(-4.2)'), 4.2);
|
910 | assert.equal(parseAndEval('add(2, 3)'), 5);
|
911 | approx.deepEqual(parseAndEval('1+exp(pi*i)'), new Complex(0, 0));
|
912 | assert.equal(parseAndEval('unequal(2, 3)'), true);
|
913 | });
|
914 |
|
915 | it('should get a subset of a matrix returned by a function', function() {
|
916 | var scope = {
|
917 | test: function () {
|
918 | return [1,2,3,4];
|
919 | }
|
920 | };
|
921 | assert.equal(parseAndEval('test()[2]', scope), 2);
|
922 | });
|
923 |
|
924 | it('should parse functions without parameters', function() {
|
925 | assert.equal(parseAndEval('r()', {r: function() {return 2;}}), 2);
|
926 | });
|
927 |
|
928 | it('should parse function assignments', function() {
|
929 | var scope = {};
|
930 | parseAndEval('x=100', scope);
|
931 | assert.equal(parseAndEval('f(x) = x^2', scope).syntax, 'f(x)');
|
932 | assert.equal(parseAndEval('f(3)', scope), 9);
|
933 | assert.equal(scope.f(3), 9);
|
934 | assert.equal(scope.x, 100);
|
935 | assert.equal(parseAndEval('g(x, y) = x^y', scope).syntax, 'g(x, y)');
|
936 | assert.equal(parseAndEval('g(4,5)', scope), 1024);
|
937 | assert.equal(scope.g(4,5), 1024);
|
938 | });
|
939 |
|
940 | it ('should correctly evaluate variables in assigned functions', function () {
|
941 | var scope = {};
|
942 | assert.equal(parseAndEval('a = 3', scope), 3);
|
943 | assert.equal(parseAndEval('f(x) = a * x', scope).syntax, 'f(x)');
|
944 | assert.equal(parseAndEval('f(2)', scope), 6);
|
945 | assert.equal(parseAndEval('a = 5', scope), 5);
|
946 | assert.equal(parseAndEval('f(2)', scope), 10);
|
947 | assert.equal(parseAndEval('g(x) = x^q', scope).syntax, 'g(x)');
|
948 | assert.equal(parseAndEval('q = 4/2', scope), 2);
|
949 | assert.equal(parseAndEval('g(3)', scope), 9);
|
950 | });
|
951 |
|
952 | it('should throw an error for undefined variables in an assigned function', function() {
|
953 | var scope = {};
|
954 | assert.equal(parseAndEval('g(x) = x^q', scope).syntax, 'g(x)');
|
955 | assert.throws(function () {
|
956 | parseAndEval('g(3)', scope);
|
957 | }, function (err) {
|
958 | return (err instanceof Error) && (err.toString() == 'Error: Undefined symbol q');
|
959 | });
|
960 | });
|
961 |
|
962 | it('should throw an error on invalid left hand side of a function assignment', function() {
|
963 | assert.throws(function () {
|
964 | var scope = {};
|
965 | parseAndEval('g(x, 2) = x^2', scope);
|
966 | }, SyntaxError);
|
967 |
|
968 | assert.throws(function () {
|
969 | var scope = {};
|
970 | parseAndEval('2(x, 2) = x^2', scope);
|
971 | }, SyntaxError);
|
972 | });
|
973 | });
|
974 |
|
975 | describe ('parentheses', function () {
|
976 | it('should parse parentheses overriding the default precedence', function () {
|
977 | approx.equal(parseAndEval('2 - (2 - 2)'), 2);
|
978 | approx.equal(parseAndEval('2 - ((2 - 2) - 2)'), 4);
|
979 | approx.equal(parseAndEval('3 * (2 + 3)'), 15);
|
980 | approx.equal(parseAndEval('(2 + 3) * 3'), 15);
|
981 | });
|
982 |
|
983 | it('should throw an error in case of unclosed parentheses', function () {
|
984 | assert.throws(function () {parseAndEval('3 * (1 + 2');}, /Parenthesis \) expected/);
|
985 | });
|
986 | });
|
987 |
|
988 | describe ('operators', function () {
|
989 |
|
990 | it('should parse operations', function() {
|
991 | approx.equal(parseAndEval('(2+3)/4'), 1.25);
|
992 | approx.equal(parseAndEval('2+3/4'), 2.75);
|
993 | assert.equal(parse('0 + 2').toString(), '0 + 2');
|
994 | });
|
995 |
|
996 | it('should parse add +', function() {
|
997 | assert.equal(parseAndEval('2 + 3'), 5);
|
998 | assert.equal(parseAndEval('2 + 3 + 4'), 9);
|
999 | assert.equal(parseAndEval('2.+3'), 5);
|
1000 | });
|
1001 |
|
1002 | it('should parse divide /', function() {
|
1003 | assert.equal(parseAndEval('4 / 2'), 2);
|
1004 | assert.equal(parseAndEval('8 / 2 / 2'), 2);
|
1005 | });
|
1006 |
|
1007 | it('should parse dotDivide ./', function() {
|
1008 | assert.equal(parseAndEval('4./2'), 2);
|
1009 | assert.deepEqual(parseAndEval('4./[2,4]'), math.matrix([2,1]));
|
1010 | assert.equal(parseAndEval('4 ./ 2'), 2);
|
1011 | assert.equal(parseAndEval('8 ./ 2 / 2'), 2);
|
1012 |
|
1013 | assert.deepEqual(parseAndEval('[1,2,3] ./ [1,2,3]'), math.matrix([1,1,1]));
|
1014 | });
|
1015 |
|
1016 | it('should parse dotMultiply .*', function() {
|
1017 | approx.deepEqual(parseAndEval('2.*3'), 6);
|
1018 | approx.deepEqual(parseAndEval('2e3.*3'), 6e3);
|
1019 | approx.deepEqual(parseAndEval('2 .* 3'), 6);
|
1020 | approx.deepEqual(parseAndEval('4 .* 2'), 8);
|
1021 | approx.deepEqual(parseAndEval('8 .* 2 .* 2'), 32);
|
1022 | assert.deepEqual(parseAndEval('a=3; a.*4'), new ResultSet([12]));
|
1023 |
|
1024 | assert.deepEqual(parseAndEval('[1,2,3] .* [1,2,3]'), math.matrix([1,4,9]));
|
1025 | });
|
1026 |
|
1027 | it('should parse dotPower .^', function() {
|
1028 | approx.deepEqual(parseAndEval('2.^3'), 8);
|
1029 | approx.deepEqual(parseAndEval('2 .^ 3'), 8);
|
1030 | approx.deepEqual(parseAndEval('-2.^2'), -4);
|
1031 | approx.deepEqual(parseAndEval('2.^3.^4'), 2.41785163922926e+24);
|
1032 |
|
1033 | assert.deepEqual(parseAndEval('[2,3] .^ [2,3]'), math.matrix([4,27]));
|
1034 | });
|
1035 |
|
1036 | it('should parse equal ==', function() {
|
1037 | assert.strictEqual(parseAndEval('2 == 3'), false);
|
1038 | assert.strictEqual(parseAndEval('2 == 2'), true);
|
1039 | assert.deepEqual(parseAndEval('[2,3] == [2,4]'), math.matrix([true, false]));
|
1040 | });
|
1041 |
|
1042 | it('should parse larger >', function() {
|
1043 | assert.equal(parseAndEval('2 > 3'), false);
|
1044 | assert.equal(parseAndEval('2 > 2'), false);
|
1045 | assert.equal(parseAndEval('2 > 1'), true);
|
1046 | });
|
1047 |
|
1048 | it('should parse largerEq >=', function() {
|
1049 | assert.equal(parseAndEval('2 >= 3'), false);
|
1050 | assert.equal(parseAndEval('2 >= 2'), true);
|
1051 | assert.equal(parseAndEval('2 >= 1'), true);
|
1052 | });
|
1053 |
|
1054 | it('should parse mod %', function() {
|
1055 | approx.equal(parseAndEval('8 % 3'), 2);
|
1056 | });
|
1057 |
|
1058 | it('should parse operator mod', function() {
|
1059 | approx.equal(parseAndEval('8 mod 3'), 2);
|
1060 | });
|
1061 |
|
1062 | it('should parse multiply *', function() {
|
1063 | approx.equal(parseAndEval('4 * 2'), 8);
|
1064 | approx.equal(parseAndEval('8 * 2 * 2'), 32);
|
1065 | });
|
1066 |
|
1067 | it('should parse implicit multiplication', function() {
|
1068 | assert.equal(parseAndEval('4a', {a:2}), 8);
|
1069 | assert.equal(parseAndEval('4 a', {a:2}), 8);
|
1070 | assert.equal(parseAndEval('a b', {a: 2, b: 4}), 8);
|
1071 | assert.equal(parseAndEval('2a b', {a: 2, b: 4}), 16);
|
1072 | assert.equal(parseAndEval('2a * b', {a: 2, b: 4}), 16);
|
1073 | assert.equal(parseAndEval('2a / b', {a: 2, b: 4}), 1);
|
1074 | assert.equal(parseAndEval('a b c', {a: 2, b: 4, c: 6}), 48);
|
1075 | assert.equal(parseAndEval('a b*c', {a: 2, b: 4, c: 6}), 48);
|
1076 | assert.equal(parseAndEval('a*b c', {a: 2, b: 4, c: 6}), 48);
|
1077 | assert.equal(parseAndEval('a/b c', {a: 4, b: 2, c: 6}), 12);
|
1078 |
|
1079 | assert.equal(parseAndEval('1/2a', {a:2}), 1);
|
1080 | assert.equal(parseAndEval('8/2a/2', {a:2}), 4);
|
1081 | assert.equal(parseAndEval('8/2a*2', {a:2}), 16);
|
1082 | assert.equal(parseAndEval('4*2a', {a:2}), 16);
|
1083 | assert.equal(parseAndEval('3!10'), 60);
|
1084 |
|
1085 | assert.equal(parseAndEval('(2+3)a', {a:2}), 10);
|
1086 | assert.equal(parseAndEval('(2+3)2'), 10);
|
1087 | assert.equal(parseAndEval('(2)(3)+4'), 10);
|
1088 | assert.equal(parseAndEval('2(3+4)'), 14);
|
1089 | assert.equal(parseAndEval('(2+3)-2'), 3);
|
1090 | assert.equal(parseAndEval('a(2+3)', {a: function() {return 42;}}), 42);
|
1091 | assert.equal(parseAndEval('a.b(2+3)', {a: {b: function() {return 42;}}}), 42);
|
1092 | assert.equal(parseAndEval('(2+3)(4+5)'), 45);
|
1093 | assert.equal(parseAndEval('(2+3)(4+5)(3-1)'), 90);
|
1094 |
|
1095 | assert.equal(parseAndEval('(2a)^3', {a:2}), 64);
|
1096 | assert.equal(parseAndEval('2a^3', {a:2}), 16);
|
1097 | assert.equal(parseAndEval('2(a)^3', {a:2}), 16);
|
1098 | assert.equal(parseAndEval('(2)a^3', {a:2}), 16);
|
1099 | assert.equal(parseAndEval('2^3a', {a:2}), 16);
|
1100 | assert.equal(parseAndEval('2^3(a)', {a:2}), 16);
|
1101 | assert.equal(parseAndEval('2^(3)(a)', {a:2}), 16);
|
1102 | assert.equal(parseAndEval('sqrt(2a)', {a:2}), 2);
|
1103 |
|
1104 | assert.deepEqual(parseAndEval('[2, 3] 2'), math.matrix([4, 6]));
|
1105 | assert.deepEqual(parseAndEval('[2, 3] a', {a:2}), math.matrix([4, 6]));
|
1106 | assert.deepEqual(parseAndEval('A [2,2]', {A: [[1,2], [3,4]]}), 4);
|
1107 | assert.deepEqual(parseAndEval('(A) [2,2]', {A: [[1,2], [3,4]]}), 4);
|
1108 |
|
1109 | assert.deepEqual(parseAndEval('[1,2;3,4] [2,2]'), 4);
|
1110 | assert.deepEqual(parseAndEval('([1,2;3,4])[2,2]'), 4);
|
1111 | assert.throws(function () {parseAndEval('2[1,2,3]')}, /Unexpected operator/);
|
1112 | });
|
1113 |
|
1114 | it('should tell the OperatorNode about implicit multiplications', function() {
|
1115 | assert.equal(parse('2 + 3').implicit, false);
|
1116 | assert.equal(parse('4 * a').implicit, false);
|
1117 |
|
1118 | assert.equal(parse('4a').implicit, true);
|
1119 | assert.equal(parse('4 a').implicit, true);
|
1120 | assert.equal(parse('a b').implicit, true);
|
1121 | assert.equal(parse('2a b').implicit, true);
|
1122 | assert.equal(parse('a b c').implicit, true);
|
1123 |
|
1124 | assert.equal(parse('(2+3)a').implicit, true);
|
1125 | assert.equal(parse('(2+3)2').implicit, true);
|
1126 | assert.equal(parse('2(3+4)').implicit, true);
|
1127 | });
|
1128 |
|
1129 | it('should correctly order consecutive multiplications and implicit multiplications', function() {
|
1130 | var node = parse('9km*3km');
|
1131 | assert.equal(node.toString({parenthesis: 'all'}), '((9 km) * 3) km');
|
1132 | });
|
1133 |
|
1134 | it('should throw an error when having an implicit multiplication between two numbers', function() {
|
1135 | assert.throws(function () { math.parse('2 3'); }, /Unexpected part "3"/);
|
1136 | assert.throws(function () { math.parse('2 * 3 4'); }, /Unexpected part "4"/);
|
1137 | assert.throws(function () { math.parse('2 * 3 4 * 5'); }, /Unexpected part "4"/);
|
1138 | assert.throws(function () { math.parse('2 / 3 4 5'); }, /Unexpected part "4"/);
|
1139 | assert.throws(function () { math.parse('2 + 3 4'); }, /Unexpected part "4"/);
|
1140 | assert.throws(function () { math.parse('-2 2'); }, /Unexpected part "2"/);
|
1141 | assert.throws(function () { math.parse('+3 3'); }, /Unexpected part "3"/);
|
1142 | assert.throws(function () { math.parse('2^3 4'); }, /Unexpected part "4"/);
|
1143 | });
|
1144 |
|
1145 | it('should parse pow ^', function() {
|
1146 | approx.equal(parseAndEval('2^3'), 8);
|
1147 | approx.equal(parseAndEval('-2^2'), -4);
|
1148 | approx.equal(parseAndEval('2^3^4'), 2.41785163922926e+24);
|
1149 | });
|
1150 |
|
1151 | it('should parse smaller <', function() {
|
1152 | assert.strictEqual(parseAndEval('2 < 3'), true);
|
1153 | assert.strictEqual(parseAndEval('2 < 2'), false);
|
1154 | assert.strictEqual(parseAndEval('2 < 1'), false);
|
1155 | });
|
1156 |
|
1157 | it('should parse smallerEq <=', function() {
|
1158 | assert.strictEqual(parseAndEval('2 <= 3'), true);
|
1159 | assert.strictEqual(parseAndEval('2 <= 2'), true);
|
1160 | assert.strictEqual(parseAndEval('2 <= 1'), false);
|
1161 | });
|
1162 |
|
1163 | it('should parse bitwise and &', function() {
|
1164 | assert.strictEqual(parseAndEval('2 & 6'), 2);
|
1165 | assert.strictEqual(parseAndEval('5 & 3'), 1);
|
1166 | assert.strictEqual(parseAndEval('true & true'), 1);
|
1167 | assert.strictEqual(parseAndEval('true & false'), 0);
|
1168 | assert.strictEqual(parseAndEval('false & true'), 0);
|
1169 | assert.strictEqual(parseAndEval('false & false'), 0);
|
1170 | });
|
1171 |
|
1172 | it('should parse bitwise xor ^|', function() {
|
1173 | assert.strictEqual(parseAndEval('2 ^| 6'), 4);
|
1174 | assert.strictEqual(parseAndEval('5 ^| 3'), 6);
|
1175 | assert.strictEqual(parseAndEval('true ^| true'), 0);
|
1176 | assert.strictEqual(parseAndEval('true ^| false'), 1);
|
1177 | assert.strictEqual(parseAndEval('false ^| true'), 1);
|
1178 | assert.strictEqual(parseAndEval('false ^| false'), 0);
|
1179 | });
|
1180 |
|
1181 | it('should parse bitwise or |', function() {
|
1182 | assert.strictEqual(parseAndEval('2 | 6'), 6);
|
1183 | assert.strictEqual(parseAndEval('5 | 3'), 7);
|
1184 | assert.strictEqual(parseAndEval('true | true'), 1);
|
1185 | assert.strictEqual(parseAndEval('true | false'), 1);
|
1186 | assert.strictEqual(parseAndEval('false | true'), 1);
|
1187 | assert.strictEqual(parseAndEval('false | false'), 0);
|
1188 | });
|
1189 |
|
1190 | it('should parse bitwise left shift <<', function() {
|
1191 | assert.strictEqual(parseAndEval('23 << 1'), 46);
|
1192 | });
|
1193 |
|
1194 | it('should parse bitwise right arithmetic shift >>', function() {
|
1195 | assert.strictEqual(parseAndEval('32 >> 4'), 2);
|
1196 | assert.strictEqual(parseAndEval('-12 >> 2'), -3);
|
1197 | });
|
1198 |
|
1199 | it('should parse bitwise right logical shift >>>', function() {
|
1200 | assert.strictEqual(parseAndEval('32 >>> 4'), 2);
|
1201 | assert.strictEqual(parseAndEval('-12 >>> 2'), 1073741821);
|
1202 | });
|
1203 |
|
1204 | it('should parse logical and', function() {
|
1205 | assert.strictEqual(parseAndEval('2 and 6'), true);
|
1206 | assert.strictEqual(parseAndEval('2 and 0'), false);
|
1207 | assert.strictEqual(parseAndEval('true and true'), true);
|
1208 | assert.strictEqual(parseAndEval('true and false'), false);
|
1209 | assert.strictEqual(parseAndEval('false and true'), false);
|
1210 | assert.strictEqual(parseAndEval('false and false'), false);
|
1211 | });
|
1212 |
|
1213 | it('should parse logical xor', function() {
|
1214 | assert.strictEqual(parseAndEval('2 xor 6'), false);
|
1215 | assert.strictEqual(parseAndEval('2 xor 0'), true);
|
1216 | assert.strictEqual(parseAndEval('true xor true'), false);
|
1217 | assert.strictEqual(parseAndEval('true xor false'), true);
|
1218 | assert.strictEqual(parseAndEval('false xor true'), true);
|
1219 | assert.strictEqual(parseAndEval('false xor false'), false);
|
1220 | });
|
1221 |
|
1222 | it('should parse logical or', function() {
|
1223 | assert.strictEqual(parseAndEval('2 or 6'), true);
|
1224 | assert.strictEqual(parseAndEval('2 or 0'), true);
|
1225 | assert.strictEqual(parseAndEval('true or true'), true);
|
1226 | assert.strictEqual(parseAndEval('true or false'), true);
|
1227 | assert.strictEqual(parseAndEval('false or true'), true);
|
1228 | assert.strictEqual(parseAndEval('false or false'), false);
|
1229 | });
|
1230 |
|
1231 | it('should parse logical not', function() {
|
1232 | assert.strictEqual(parseAndEval('not 2'), false);
|
1233 | assert.strictEqual(parseAndEval('not not 2'), true);
|
1234 | assert.strictEqual(parseAndEval('not not not 2'), false);
|
1235 | assert.strictEqual(parseAndEval('not true'), false);
|
1236 |
|
1237 | assert.strictEqual(parseAndEval('4*not 2'), 0);
|
1238 | assert.strictEqual(parseAndEval('4 * not 2'), 0);
|
1239 | assert.strictEqual(parseAndEval('4-not 2'), 4);
|
1240 | assert.strictEqual(parseAndEval('4 - not 2'), 4);
|
1241 | assert.strictEqual(parseAndEval('4+not 2'), 4);
|
1242 | assert.strictEqual(parseAndEval('4 + not 2'), 4);
|
1243 |
|
1244 | assert.strictEqual(parseAndEval('10+not not 3'), 11);
|
1245 | });
|
1246 |
|
1247 | it('should parse minus -', function() {
|
1248 | assert.equal(parseAndEval('4 - 2'), 2);
|
1249 | assert.equal(parseAndEval('8 - 2 - 2'), 4);
|
1250 | });
|
1251 |
|
1252 | it('should parse unary minus -', function() {
|
1253 | assert.equal(parseAndEval('-2'), -2);
|
1254 | assert.equal(parseAndEval('--2'), 2);
|
1255 | assert.equal(parseAndEval('---2'), -2);
|
1256 |
|
1257 | assert.equal(parseAndEval('4*-2'), -8);
|
1258 | assert.equal(parseAndEval('4 * -2'), -8);
|
1259 | assert.equal(parseAndEval('4+-2'), 2);
|
1260 | assert.equal(parseAndEval('4 + -2'), 2);
|
1261 | assert.equal(parseAndEval('4--2'), 6);
|
1262 | assert.equal(parseAndEval('4 - -2'), 6);
|
1263 |
|
1264 | assert.equal(parseAndEval('5-3'), 2);
|
1265 | assert.equal(parseAndEval('5--3'), 8);
|
1266 | assert.equal(parseAndEval('5---3'), 2);
|
1267 | assert.equal(parseAndEval('5+---3'), 2);
|
1268 | assert.equal(parseAndEval('5----3'), 8);
|
1269 | assert.equal(parseAndEval('5+--(2+1)'), 8);
|
1270 | });
|
1271 |
|
1272 | it('should parse unary +', function() {
|
1273 | assert.equal(parseAndEval('+2'), 2);
|
1274 | assert.equal(parseAndEval('++2'), 2);
|
1275 | assert.equal(parseAndEval('+++2'), 2);
|
1276 | assert.equal(parseAndEval('+true'), 1);
|
1277 |
|
1278 | assert.equal(parseAndEval('4*+2'), 8);
|
1279 | assert.equal(parseAndEval('4 * +2'), 8);
|
1280 | assert.equal(parseAndEval('4-+2'), 2);
|
1281 | assert.equal(parseAndEval('4 - +2'), 2);
|
1282 | assert.equal(parseAndEval('4++2'), 6);
|
1283 | assert.equal(parseAndEval('4 + +2'), 6);
|
1284 |
|
1285 | assert.equal(parseAndEval('5+3'), 8);
|
1286 | assert.equal(parseAndEval('5++3'), 8);
|
1287 | });
|
1288 |
|
1289 | it('should parse unary ~', function() {
|
1290 | assert.equal(parseAndEval('~2'), -3);
|
1291 | assert.equal(parseAndEval('~~2'), 2);
|
1292 | assert.equal(parseAndEval('~~~2'), -3);
|
1293 | assert.equal(parseAndEval('~true'), -2);
|
1294 |
|
1295 | assert.equal(parseAndEval('4*~2'), -12);
|
1296 | assert.equal(parseAndEval('4 * ~2'), -12);
|
1297 | assert.equal(parseAndEval('4-~2'), 7);
|
1298 | assert.equal(parseAndEval('4 - ~2'), 7);
|
1299 | assert.equal(parseAndEval('4+~2'), 1);
|
1300 | assert.equal(parseAndEval('4 + ~2'), 1);
|
1301 |
|
1302 | assert.equal(parseAndEval('10+~~3'), 13);
|
1303 | });
|
1304 |
|
1305 | it('should parse unary plus and minus +, -', function() {
|
1306 | assert.equal(parseAndEval('-+2'), -2);
|
1307 | assert.equal(parseAndEval('-+-2'), 2);
|
1308 | assert.equal(parseAndEval('+-+-2'), 2);
|
1309 | assert.equal(parseAndEval('+-2'), -2);
|
1310 | assert.equal(parseAndEval('+-+2'), -2);
|
1311 | assert.equal(parseAndEval('-+-+2'), 2);
|
1312 | });
|
1313 |
|
1314 | it('should parse unary plus and bitwise not +, ~', function() {
|
1315 | assert.equal(parseAndEval('~+2'), -3);
|
1316 | assert.equal(parseAndEval('~+~2'), 2);
|
1317 | assert.equal(parseAndEval('+~+~2'), 2);
|
1318 | assert.equal(parseAndEval('+~2'), -3);
|
1319 | assert.equal(parseAndEval('+~+2'), -3);
|
1320 | assert.equal(parseAndEval('~+~+2'), 2);
|
1321 | });
|
1322 |
|
1323 | it('should parse unary minus and bitwise not -, ~', function() {
|
1324 | assert.equal(parseAndEval('~-2'), 1);
|
1325 | assert.equal(parseAndEval('~-~2'), -4);
|
1326 | assert.equal(parseAndEval('-~-~2'), 4);
|
1327 | assert.equal(parseAndEval('-~2'), 3);
|
1328 | assert.equal(parseAndEval('-~-2'), -1);
|
1329 | assert.equal(parseAndEval('~-~-2'), 0);
|
1330 | });
|
1331 |
|
1332 | it('should parse unary plus + and logical not', function() {
|
1333 | assert.equal(parseAndEval('not+2'), false);
|
1334 | assert.equal(parseAndEval('not + not 2'), true);
|
1335 | assert.equal(parseAndEval('+not+not 2'), 1);
|
1336 | assert.equal(parseAndEval('+ not 2'), 0);
|
1337 | assert.equal(parseAndEval('+ not +2'), 0);
|
1338 | assert.equal(parseAndEval('not + not +2'), true);
|
1339 | });
|
1340 |
|
1341 | it('should parse bitwise not ~ and logical not', function() {
|
1342 | assert.equal(parseAndEval('~not 2'), -1);
|
1343 | assert.equal(parseAndEval('~not~2'), -1);
|
1344 | assert.equal(parseAndEval('not~not~2'), false);
|
1345 | assert.equal(parseAndEval('not~2'), false);
|
1346 | assert.equal(parseAndEval('not~not 2'), false);
|
1347 | assert.equal(parseAndEval('~not~not 2'), -1);
|
1348 | });
|
1349 |
|
1350 | it('should parse unary minus and logical not', function() {
|
1351 | assert.equal(parseAndEval('not-2'), false);
|
1352 | assert.equal(parseAndEval('not-not 2'), true);
|
1353 | assert.equal(parseAndEval('-not-not 2'), -1);
|
1354 | assert.equal(parseAndEval('-not 2'), -0);
|
1355 | assert.equal(parseAndEval('-not-2'), -0);
|
1356 | assert.equal(parseAndEval('not-not-2'), true);
|
1357 | });
|
1358 |
|
1359 | it('should parse unequal !=', function() {
|
1360 | assert.strictEqual(parseAndEval('2 != 3'), true);
|
1361 | assert.strictEqual(parseAndEval('2 != 2'), false);
|
1362 | assert.deepEqual(parseAndEval('[2,3] != [2,4]'), math.matrix([false, true]));
|
1363 | });
|
1364 |
|
1365 | it('should parse conditional expression a ? b : c', function() {
|
1366 | assert.equal(parseAndEval('2 ? true : false'), true);
|
1367 | assert.equal(parseAndEval('0 ? true : false'), false);
|
1368 | assert.equal(parseAndEval('false ? true : false'), false);
|
1369 |
|
1370 | assert.equal(parseAndEval('2 > 0 ? 1 : 2 < 0 ? -1 : 0'), 1);
|
1371 | assert.equal(parseAndEval('(2 > 0 ? 1 : 2 < 0) ? -1 : 0'), -1);
|
1372 | assert.equal(parseAndEval('-2 > 0 ? 1 : -2 < 0 ? -1 : 0'), -1);
|
1373 | assert.equal(parseAndEval('0 > 0 ? 1 : 0 < 0 ? -1 : 0'), 0);
|
1374 | });
|
1375 |
|
1376 | it('should lazily evaluate conditional expression a ? b : c', function() {
|
1377 | var scope = {};
|
1378 | math.parse('true ? (a = 2) : (b = 2)').compile().eval(scope);
|
1379 | assert.deepEqual(scope, {a: 2});
|
1380 | });
|
1381 |
|
1382 | it('should throw an error when false part of conditional expression is missing', function() {
|
1383 | assert.throws(function() {parseAndEval('2 ? true');}, /False part of conditional expression expected/);
|
1384 | });
|
1385 |
|
1386 | it('should parse : (range)', function() {
|
1387 | assert.ok(parseAndEval('2:5') instanceof Matrix);
|
1388 | assert.deepEqual(parseAndEval('2:5'), math.matrix([2,3,4,5]));
|
1389 | assert.deepEqual(parseAndEval('10:-2:0'), math.matrix([10,8,6,4,2,0]));
|
1390 | assert.deepEqual(parseAndEval('2:4.0'), math.matrix([2,3,4]));
|
1391 | assert.deepEqual(parseAndEval('2:4.5'), math.matrix([2,3,4]));
|
1392 | assert.deepEqual(parseAndEval('2:4.1'), math.matrix([2,3,4]));
|
1393 | assert.deepEqual(parseAndEval('2:3.9'), math.matrix([2,3]));
|
1394 | assert.deepEqual(parseAndEval('2:3.5'), math.matrix([2,3]));
|
1395 | assert.deepEqual(parseAndEval('3:-1:0.5'), math.matrix([3,2,1]));
|
1396 | assert.deepEqual(parseAndEval('3:-1:0.5'), math.matrix([3,2,1]));
|
1397 | assert.deepEqual(parseAndEval('3:-1:0.1'), math.matrix([3,2,1]));
|
1398 | assert.deepEqual(parseAndEval('3:-1:-0.1'), math.matrix([3,2,1,0]));
|
1399 | });
|
1400 |
|
1401 | it('should parse to', function() {
|
1402 | approx.deepEqual(parseAndEval('2.54 cm to inch'), math.unit(1, 'inch').to('inch'));
|
1403 | approx.deepEqual(parseAndEval('2.54 cm + 2 inch to foot'), math.unit(0.25, 'foot').to('foot'));
|
1404 | });
|
1405 |
|
1406 | it('should parse in', function() {
|
1407 | approx.deepEqual(parseAndEval('2.54 cm in inch'), math.unit(1, 'inch').to('inch'));
|
1408 | });
|
1409 |
|
1410 | it('should parse factorial !', function() {
|
1411 | assert.deepEqual(parseAndEval('5!'), 120);
|
1412 | assert.deepEqual(parseAndEval('[1,2,3,4]!'), math.matrix([1,2,6,24]));
|
1413 | assert.deepEqual(parseAndEval('4!+2'), 26);
|
1414 | assert.deepEqual(parseAndEval('4!-2'), 22);
|
1415 | assert.deepEqual(parseAndEval('4!*2'), 48);
|
1416 | assert.deepEqual(parseAndEval('3!!'), 720);
|
1417 | assert.deepEqual(parseAndEval('[1,2;3,1]!\'!'), math.matrix([[1, 720], [2, 1]]));
|
1418 | assert.deepEqual(parseAndEval('[4,5]![2]'), 120);
|
1419 | });
|
1420 |
|
1421 | it('should parse transpose \'', function() {
|
1422 | assert.deepEqual(parseAndEval('23\''), 23);
|
1423 | assert.deepEqual(parseAndEval('[1,2,3;4,5,6]\''), math.matrix([[1,4],[2,5],[3,6]]));
|
1424 | assert.ok(parseAndEval('[1,2,3;4,5,6]\'') instanceof Matrix);
|
1425 | assert.deepEqual(parseAndEval('[1:5]'), math.matrix([[1,2,3,4,5]]));
|
1426 | assert.deepEqual(parseAndEval('[1:5]\''), math.matrix([[1],[2],[3],[4],[5]]));
|
1427 | assert.deepEqual(parseAndEval('size([1:5])'), math.matrix([1, 5]));
|
1428 | assert.deepEqual(parseAndEval('[1,2;3,4]\''), math.matrix([[1,3],[2,4]]));
|
1429 | });
|
1430 |
|
1431 | describe('operator precedence', function() {
|
1432 | it('should respect precedence of plus and minus', function () {
|
1433 | assert.equal(parseAndEval('4-2+3'), 5);
|
1434 | assert.equal(parseAndEval('4-(2+3)'), -1);
|
1435 | assert.equal(parseAndEval('4-2-3'), -1);
|
1436 | assert.equal(parseAndEval('4-(2-3)'), 5);
|
1437 | });
|
1438 |
|
1439 | it('should respect precedence of plus/minus and multiply/divide', function () {
|
1440 | assert.equal(parseAndEval('2+3*4'), 14);
|
1441 | assert.equal(parseAndEval('2*3+4'), 10);
|
1442 | });
|
1443 |
|
1444 | it('should respect precedence of plus/minus and pow', function () {
|
1445 | assert.equal(parseAndEval('2+3^2'), 11);
|
1446 | assert.equal(parseAndEval('3^2+2'), 11);
|
1447 | assert.equal(parseAndEval('8-2^2'), 4);
|
1448 | assert.equal(parseAndEval('4^2-2'), 14);
|
1449 | });
|
1450 |
|
1451 | it('should respect precedence of multiply/divide and pow', function () {
|
1452 | assert.equal(parseAndEval('2*3^2'), 18);
|
1453 | assert.equal(parseAndEval('3^2*2'), 18);
|
1454 | assert.equal(parseAndEval('8/2^2'), 2);
|
1455 | assert.equal(parseAndEval('4^2/2'), 8);
|
1456 | });
|
1457 |
|
1458 | it('should respect precedence of pow', function () {
|
1459 | assert.equal(parseAndEval('2^3'), 8);
|
1460 | assert.equal(parseAndEval('2^3^4'), Math.pow(2, Math.pow(3, 4)));
|
1461 | assert.equal(parseAndEval('1.5^1.5^1.5'), parseAndEval('1.5^(1.5^1.5)'));
|
1462 | assert.equal(parseAndEval('1.5^1.5^1.5^1.5'), parseAndEval('1.5^(1.5^(1.5^1.5))'));
|
1463 | });
|
1464 |
|
1465 | it('should respect precedence of unary operations and pow', function () {
|
1466 | assert.equal(parseAndEval('-3^2'), -9);
|
1467 | assert.equal(parseAndEval('(-3)^2'), 9);
|
1468 | assert.equal(parseAndEval('2^-2'), 0.25);
|
1469 | assert.equal(parseAndEval('2^(-2)'), 0.25);
|
1470 |
|
1471 | assert.equal(parseAndEval('+3^2'), 9);
|
1472 | assert.equal(parseAndEval('(+3)^2'), 9);
|
1473 | assert.equal(parseAndEval('2^(+2)'), 4);
|
1474 |
|
1475 | assert.equal(parseAndEval('~3^2'), -10);
|
1476 | assert.equal(parseAndEval('(~3)^2'), 16);
|
1477 | assert.equal(parseAndEval('2^~2'), 0.125);
|
1478 | assert.equal(parseAndEval('2^(~2)'), 0.125);
|
1479 |
|
1480 | assert.equal(parseAndEval('not 3^2'), false);
|
1481 | assert.equal(parseAndEval('(not 3)^2'), 0);
|
1482 | assert.equal(parseAndEval('2^not 2'), 1);
|
1483 | assert.equal(parseAndEval('2^(not 2)'), 1);
|
1484 | });
|
1485 |
|
1486 | it('should respect precedence of factorial and pow', function () {
|
1487 | assert.equal(parseAndEval('2^3!'), 64);
|
1488 | assert.equal(parseAndEval('2^(3!)'), 64);
|
1489 | assert.equal(parseAndEval('3!^2'), 36);
|
1490 | });
|
1491 |
|
1492 | it('should respect precedence of factorial and unary operations', function () {
|
1493 | assert.equal(parseAndEval('-4!'), -24);
|
1494 | assert.equal(parseAndEval('-(4!)'), -24);
|
1495 |
|
1496 | assert.equal(parseAndEval('3!+2'), 8);
|
1497 | assert.equal(parseAndEval('(3!)+2'), 8);
|
1498 | assert.equal(parseAndEval('+4!'), 24);
|
1499 |
|
1500 | assert.equal(parseAndEval('~4!+1'), -24);
|
1501 | assert.equal(parseAndEval('~(4!)+1'), -24);
|
1502 |
|
1503 | assert.equal(parseAndEval('not 4!'), false);
|
1504 | assert.equal(parseAndEval('not not 4!'), true);
|
1505 | assert.equal(parseAndEval('not(4!)'), false);
|
1506 | assert.equal(parseAndEval('(not 4!)'), false);
|
1507 | assert.equal(parseAndEval('(not 4)!'), 1);
|
1508 | });
|
1509 |
|
1510 | it('should respect precedence of transpose', function () {
|
1511 | var node = math.parse('a + b\'');
|
1512 | assert(node instanceof OperatorNode);
|
1513 | assert.equal(node.op, '+');
|
1514 | assert.equal(node.args[0].toString(), 'a');
|
1515 | assert.equal(node.args[1].toString(), 'b\'');
|
1516 | });
|
1517 |
|
1518 | it('should respect precedence of transpose (2)', function () {
|
1519 | var node = math.parse('a ^ b\'');
|
1520 | assert(node instanceof OperatorNode);
|
1521 | assert.equal(node.op, '^');
|
1522 | assert.equal(node.args[0].toString(), 'a');
|
1523 | assert.equal(node.args[1].toString(), 'b\'');
|
1524 | });
|
1525 |
|
1526 | it('should respect precedence of conditional operator and other operators', function () {
|
1527 | assert.equal(parseAndEval('2 > 3 ? true : false'), false);
|
1528 | assert.equal(parseAndEval('2 == 3 ? true : false'), false);
|
1529 | assert.equal(parseAndEval('3 ? 2 + 4 : 2 - 1'), 6);
|
1530 | assert.deepEqual(parseAndEval('3 ? true : false; 22'), new ResultSet([22]));
|
1531 | assert.deepEqual(parseAndEval('3 ? 5cm to m : 5cm in mm'), new Unit(5, 'cm').to('m'));
|
1532 | assert.deepEqual(parseAndEval('2 == 4-2 ? [1,2] : false'), math.matrix([1,2]));
|
1533 | assert.deepEqual(parseAndEval('false ? 1:2:6'), math.matrix([2,3,4,5,6]));
|
1534 | });
|
1535 |
|
1536 | it('should respect precedence between left/right shift and relational operators', function () {
|
1537 | assert.strictEqual(parseAndEval('32 >> 4 == 2'), true);
|
1538 | assert.strictEqual(parseAndEval('2 == 32 >> 4'), true);
|
1539 | assert.strictEqual(parseAndEval('2 << 2 == 8'), true);
|
1540 | assert.strictEqual(parseAndEval('8 == 2 << 2'), true);
|
1541 | });
|
1542 |
|
1543 | it('should respect precedence between relational operators and bitwise and', function () {
|
1544 | assert.strictEqual(parseAndEval('2 == 3 & 1'), 0);
|
1545 | assert.strictEqual(parseAndEval('3 & 1 == 2'), 0);
|
1546 | assert.strictEqual(parseAndEval('2 == (3 & 1)'), false);
|
1547 | });
|
1548 |
|
1549 | it('should respect precedence between bitwise or | and logical and', function () {
|
1550 | assert.strictEqual(parseAndEval('2 | 2 and 4'), true);
|
1551 | assert.strictEqual(parseAndEval('4 and 2 | 2'), true);
|
1552 | });
|
1553 |
|
1554 | it('should respect precedence between bitwise xor ^| and bitwise or |', function () {
|
1555 | assert.strictEqual(parseAndEval('4 ^| 6 | 2'), 2);
|
1556 | assert.strictEqual(parseAndEval('2 | 4 ^| 6'), 2);
|
1557 | assert.strictEqual(parseAndEval('(2 | 4) ^| 6'), 0);
|
1558 | });
|
1559 |
|
1560 | it('should respect precedence between bitwise and & and bitwise or |', function () {
|
1561 | assert.strictEqual(parseAndEval('4 & 3 | 12'), 12);
|
1562 | assert.strictEqual(parseAndEval('12 | 4 & 3'), 12);
|
1563 | assert.strictEqual(parseAndEval('(12 | 4) & 3'), 0);
|
1564 | });
|
1565 |
|
1566 | it('should respect precedence between logical and and or', function () {
|
1567 | assert.strictEqual(parseAndEval('false and true or true'), true);
|
1568 | assert.strictEqual(parseAndEval('false and (true or true)'), false);
|
1569 | assert.strictEqual(parseAndEval('true or true and false'), true);
|
1570 | assert.strictEqual(parseAndEval('(true or true) and false'), false);
|
1571 | });
|
1572 |
|
1573 | it('should respect precedence of conditional operator and logical or', function () {
|
1574 | var node = math.parse('1 or 0 ? 2 or 3 : 0 or 0');
|
1575 | assert(node instanceof ConditionalNode);
|
1576 | assert.equal(node.condition.toString(), '1 or 0');
|
1577 | assert.equal(node.trueExpr.toString(), '2 or 3');
|
1578 | assert.equal(node.falseExpr.toString(), '0 or 0');
|
1579 | assert.strictEqual(node.compile().eval(), true);
|
1580 | });
|
1581 |
|
1582 | it('should respect precedence of conditional operator and relational operators', function () {
|
1583 | var node = math.parse('a == b ? a > b : a < b');
|
1584 | assert(node instanceof ConditionalNode);
|
1585 | assert.equal(node.condition.toString(), 'a == b');
|
1586 | assert.equal(node.trueExpr.toString(), 'a > b');
|
1587 | assert.equal(node.falseExpr.toString(), 'a < b');
|
1588 | });
|
1589 |
|
1590 | it('should respect precedence of conditional operator and range operator', function () {
|
1591 | var node = math.parse('a ? b : c : d');
|
1592 | assert(node instanceof ConditionalNode);
|
1593 | assert.equal(node.condition.toString(), 'a');
|
1594 | assert.equal(node.trueExpr.toString(), 'b');
|
1595 | assert.equal(node.falseExpr.toString(), 'c:d');
|
1596 | });
|
1597 |
|
1598 | it('should respect precedence of conditional operator and range operator (2)', function () {
|
1599 | var node = math.parse('a ? (b : c) : (d : e)');
|
1600 | assert(node instanceof ConditionalNode);
|
1601 | assert.equal(node.condition.toString(), 'a');
|
1602 | assert.equal(node.trueExpr.toString(), '(b:c)');
|
1603 | assert.equal(node.falseExpr.toString(), '(d:e)');
|
1604 | });
|
1605 |
|
1606 | it('should respect precedence of conditional operator and range operator (2)', function () {
|
1607 | var node = math.parse('a ? (b ? c : d) : (e ? f : g)');
|
1608 | assert(node instanceof ConditionalNode);
|
1609 | assert.equal(node.condition.toString(), 'a');
|
1610 | assert.equal(node.trueExpr.toString(), '(b ? c : d)');
|
1611 | assert.equal(node.falseExpr.toString(), '(e ? f : g)');
|
1612 | });
|
1613 |
|
1614 | it('should respect precedence of range operator and relational operators', function () {
|
1615 | var node = math.parse('a:b == c:d');
|
1616 | assert(node instanceof OperatorNode);
|
1617 | assert.equal(node.args[0].toString(), 'a:b');
|
1618 | assert.equal(node.args[1].toString(), 'c:d');
|
1619 | });
|
1620 |
|
1621 | it('should respect precedence of range operator and operator plus and minus', function () {
|
1622 | var node = math.parse('a + b : c - d');
|
1623 | assert(node instanceof RangeNode);
|
1624 | assert.equal(node.start.toString(), 'a + b');
|
1625 | assert.equal(node.end.toString(), 'c - d');
|
1626 | });
|
1627 |
|
1628 | it('should respect precedence of "to" operator and relational operators', function () {
|
1629 | var node = math.parse('a == b to c');
|
1630 | assert(node instanceof OperatorNode);
|
1631 | assert.equal(node.args[0].toString(), 'a');
|
1632 | assert.equal(node.args[1].toString(), 'b to c');
|
1633 | });
|
1634 |
|
1635 | it('should respect precedence of "to" operator and relational operators (2)', function () {
|
1636 | var node = math.parse('a to b == c');
|
1637 | assert(node instanceof OperatorNode);
|
1638 | assert.equal(node.args[0].toString(), 'a to b');
|
1639 | assert.equal(node.args[1].toString(), 'c');
|
1640 | });
|
1641 |
|
1642 |
|
1643 |
|
1644 | });
|
1645 | });
|
1646 |
|
1647 | describe('functions', function () {
|
1648 | it('should evaluate function "mod"', function () {
|
1649 | approx.equal(parseAndEval('mod(8, 3)'), 2);
|
1650 |
|
1651 | });
|
1652 |
|
1653 | it('should evaluate function "to" ', function () {
|
1654 | approx.deepEqual(parseAndEval('to(5.08 cm * 1000, inch)'),
|
1655 | math.unit(2000, 'inch').to('inch'));
|
1656 | });
|
1657 |
|
1658 | it('should evaluate function "sort" with a custom sort function', function () {
|
1659 | var scope = {};
|
1660 | parseAndEval('sortByLength(a, b) = size(a)[1] - size(b)[1]', scope);
|
1661 | assert.deepEqual(parseAndEval('sort(["Langdon", "Tom", "Sara"], sortByLength)', scope),
|
1662 | math.matrix(["Tom", "Sara", "Langdon"]));
|
1663 | });
|
1664 |
|
1665 | });
|
1666 |
|
1667 | describe('bignumber', function () {
|
1668 | var bigmath = math.create({
|
1669 | number: 'BigNumber'
|
1670 | });
|
1671 | var BigNumber = bigmath.type.BigNumber;
|
1672 |
|
1673 | it('should parse numbers as bignumber', function() {
|
1674 | assert.deepEqual(bigmath.bignumber('2.3'), new BigNumber('2.3'));
|
1675 | assert.deepEqual(bigmath.eval('2.3'), new BigNumber('2.3'));
|
1676 | assert.deepEqual(bigmath.eval('2.3e+500'), new BigNumber('2.3e+500'));
|
1677 | });
|
1678 |
|
1679 | it('should evaluate functions supporting bignumbers', function() {
|
1680 | assert.deepEqual(bigmath.eval('0.1 + 0.2'), new BigNumber('0.3'));
|
1681 | });
|
1682 |
|
1683 | it('should evaluate functions supporting bignumbers', function() {
|
1684 | assert.deepEqual(bigmath.eval('add(0.1, 0.2)'), new BigNumber('0.3'));
|
1685 | });
|
1686 |
|
1687 | it('should work with mixed numbers and bignumbers', function() {
|
1688 | approx.equal(bigmath.eval('pi + 1'), 4.141592653589793);
|
1689 | });
|
1690 |
|
1691 | it('should evaluate functions not supporting bignumbers', function() {
|
1692 | approx.equal(bigmath.eval('sin(0.1)'), 0.09983341664682815);
|
1693 | });
|
1694 |
|
1695 | it('should create a range from bignumbers', function() {
|
1696 | assert.deepEqual(bigmath.eval('4:6'),
|
1697 | bigmath.matrix([new BigNumber(4), new BigNumber(5), new BigNumber(6)]));
|
1698 | assert.deepEqual(bigmath.eval('0:2:4'),
|
1699 | bigmath.matrix([new BigNumber(0), new BigNumber(2), new BigNumber(4)]));
|
1700 | });
|
1701 |
|
1702 | it('should create a matrix with bignumbers', function() {
|
1703 | assert.deepEqual(bigmath.eval('[0.1, 0.2]'),
|
1704 | bigmath.matrix([new BigNumber(0.1), new BigNumber(0.2)]));
|
1705 | });
|
1706 |
|
1707 | it('should get an element from a matrix with bignumbers', function() {
|
1708 | var scope = {};
|
1709 | assert.deepEqual(bigmath.eval('a=[0.1, 0.2]', scope),
|
1710 | bigmath.matrix([new BigNumber(0.1), new BigNumber(0.2)]));
|
1711 |
|
1712 | assert.deepEqual(bigmath.eval('a[1]', scope), new BigNumber(0.1));
|
1713 | assert.deepEqual(bigmath.eval('a[:]', scope),
|
1714 | bigmath.matrix([new BigNumber(0.1), new BigNumber(0.2)]));
|
1715 | assert.deepEqual(bigmath.eval('a[1:2]', scope),
|
1716 | bigmath.matrix([new BigNumber(0.1), new BigNumber(0.2)]));
|
1717 | });
|
1718 |
|
1719 | it('should replace elements in a matrix with bignumbers', function() {
|
1720 | var scope = {};
|
1721 | assert.deepEqual(bigmath.eval('a=[0.1, 0.2]', scope),
|
1722 | bigmath.matrix([new BigNumber(0.1), new BigNumber(0.2)]));
|
1723 |
|
1724 | bigmath.eval('a[1] = 0.3', scope);
|
1725 | assert.deepEqual(scope.a, bigmath.matrix([new BigNumber(0.3), new BigNumber(0.2)]));
|
1726 | bigmath.eval('a[:] = [0.5, 0.6]', scope);
|
1727 | assert.deepEqual(scope.a, bigmath.matrix([new BigNumber(0.5), new BigNumber(0.6)]));
|
1728 | bigmath.eval('a[1:2] = [0.7, 0.8]', scope);
|
1729 | assert.deepEqual(scope.a, bigmath.matrix([new BigNumber(0.7), new BigNumber(0.8)]));
|
1730 | });
|
1731 |
|
1732 | it('should work with complex numbers (downgrades bignumbers to number)', function() {
|
1733 | assert.deepEqual(bigmath.eval('3i'), new Complex(0, 3));
|
1734 | assert.deepEqual(bigmath.eval('2 + 3i'), new Complex(2, 3));
|
1735 | assert.deepEqual(bigmath.eval('2 * i'), new Complex(0, 2));
|
1736 | });
|
1737 |
|
1738 | it('should work with units', function() {
|
1739 | assert.deepEqual(bigmath.eval('2 cm'), new Unit(new BigNumber(2), 'cm'));
|
1740 | });
|
1741 | });
|
1742 |
|
1743 | describe('scope', function () {
|
1744 |
|
1745 | it('should use a given scope for assignments', function() {
|
1746 | var scope = {
|
1747 | a: 3,
|
1748 | b: 4
|
1749 | };
|
1750 | assert.deepEqual(parse('a*b').compile().eval(scope), 12);
|
1751 | assert.deepEqual(parse('c=5').compile().eval(scope), 5);
|
1752 | assert.deepEqual(parse('f(x) = x^a').compile().eval(scope).syntax, 'f(x)');
|
1753 |
|
1754 |
|
1755 | assert.deepEqual(Object.keys(scope).length, 4);
|
1756 | assert.deepEqual(scope.a, 3);
|
1757 | assert.deepEqual(scope.b, 4);
|
1758 | assert.deepEqual(scope.c, 5);
|
1759 | assert.deepEqual(typeof scope.f, 'function');
|
1760 |
|
1761 | assert.equal(scope.f(3), 27);
|
1762 | scope.a = 2;
|
1763 | assert.equal(scope.f(3), 9);
|
1764 | scope.hello = function (name) {
|
1765 | return 'hello, ' + name + '!';
|
1766 | };
|
1767 | assert.deepEqual(parse('hello("jos")').compile().eval(scope), 'hello, jos!');
|
1768 | });
|
1769 |
|
1770 | it('should parse undefined symbols, defining symbols, and removing symbols', function() {
|
1771 | var scope = {};
|
1772 | var n = parse('q');
|
1773 | assert.throws(function () { n.compile().eval(scope); });
|
1774 | parse('q=33').compile().eval(scope);
|
1775 | assert.equal(n.compile().eval(scope), 33);
|
1776 | delete scope.q;
|
1777 | assert.throws(function () { n.compile().eval(scope); });
|
1778 |
|
1779 | n = parse('qq[1,1]=33');
|
1780 | assert.throws(function () { n.compile().eval(scope); });
|
1781 | parse('qq=[1,2;3,4]').compile().eval(scope);
|
1782 | n.compile().eval(scope);
|
1783 | assert.deepEqual(scope.qq, math.matrix([[33,2],[3,4]]));
|
1784 | parse('qq=[4]').compile().eval(scope);
|
1785 | n.compile().eval(scope);
|
1786 | assert.deepEqual(scope.qq, math.matrix([[33]]));
|
1787 | delete scope.qq;
|
1788 | assert.throws(function () { n.compile().eval(scope); });
|
1789 | });
|
1790 |
|
1791 | it('should evaluate a symbol with value null or undefined', function () {
|
1792 | assert.equal(parse('a').compile().eval({a: null}), null);
|
1793 | assert.equal(parse('a').compile().eval({a: undefined}), undefined);
|
1794 | });
|
1795 |
|
1796 | });
|
1797 |
|
1798 | describe('errors', function () {
|
1799 |
|
1800 | it('should return IndexErrors with one based indices', function () {
|
1801 |
|
1802 | assert.throws(function () {math.subset([1,2,3], math.index(4));}, /Index out of range \(4 > 2\)/);
|
1803 | assert.throws(function () {math.subset([1,2,3], math.index(-2));}, /Index out of range \(-2 < 0\)/);
|
1804 |
|
1805 |
|
1806 | assert.throws(function () {math.eval('A[4]', {A:[1,2,3]});}, /Index out of range \(4 > 3\)/);
|
1807 | assert.throws(function () {math.eval('A[-2]', {A: [1,2,3]});}, /IndexError: Index out of range \(-2 < 1\)/);
|
1808 | });
|
1809 |
|
1810 | it('should return DimensionErrors with one based indices (subset)', function () {
|
1811 |
|
1812 |
|
1813 |
|
1814 | assert.throws(function () {math.subset([1,2,3], math.index(1,1));}, /DimensionError: Dimension mismatch \(2 != 1\)/);
|
1815 |
|
1816 |
|
1817 | assert.throws(function () {math.eval('A[1,1]', {A: [1,2,3]});}, /DimensionError: Dimension mismatch \(2 != 1\)/);
|
1818 | });
|
1819 |
|
1820 | it('should return DimensionErrors with one based indices (concat)', function () {
|
1821 |
|
1822 |
|
1823 |
|
1824 | assert.throws(function () {math.concat([1,2], [[3,4]]);}, /DimensionError: Dimension mismatch \(1 != 2\)/);
|
1825 | assert.throws(function () {math.concat([[1,2]], [[3,4]], 2);}, /IndexError: Index out of range \(2 > 1\)/);
|
1826 | assert.throws(function () {math.concat([[1,2]], [[3,4]], -1);}, /IndexError: Index out of range \(-1 < 0\)/);
|
1827 |
|
1828 |
|
1829 | assert.throws(function () {math.eval('concat([1,2], [[3,4]])');}, /DimensionError: Dimension mismatch \(1 != 2\)/);
|
1830 | assert.throws(function () {math.eval('concat([[1,2]], [[3,4]], 3)');}, /IndexError: Index out of range \(3 > 2\)/);
|
1831 | assert.throws(function () {math.eval('concat([[1,2]], [[3,4]], 0)');}, /IndexError: Index out of range \(0 < 1\)/);
|
1832 | });
|
1833 |
|
1834 | it('should return DimensionErrors with one based indices (max)', function () {
|
1835 |
|
1836 |
|
1837 |
|
1838 |
|
1839 |
|
1840 |
|
1841 | assert.deepEqual(math.eval('max([[1,2], [3,4]])'), 4);
|
1842 | assert.deepEqual(math.eval('max([[1,2], [3,4]], 1)'), math.matrix([3, 4]));
|
1843 | assert.deepEqual(math.eval('max([[1,2], [3,4]], 2)'), math.matrix([2, 4]));
|
1844 | assert.throws(function () {math.eval('max([[1,2], [3,4]], 3)');}, /IndexError: Index out of range \(3 > 2\)/);
|
1845 | assert.throws(function () {math.eval('max([[1,2], [3,4]], 0)');}, /IndexError: Index out of range \(0 < 1\)/);
|
1846 | });
|
1847 |
|
1848 | it('should return DimensionErrors with one based indices (min)', function () {
|
1849 |
|
1850 |
|
1851 |
|
1852 |
|
1853 |
|
1854 |
|
1855 | assert.deepEqual(math.eval('min([[1,2], [3,4]])'), 1);
|
1856 | assert.deepEqual(math.eval('min([[1,2], [3,4]], 1)'), math.matrix([1, 2]));
|
1857 | assert.deepEqual(math.eval('min([[1,2], [3,4]], 2)'), math.matrix([1, 3]));
|
1858 | assert.throws(function () {math.eval('min([[1,2], [3,4]], 3)');}, /IndexError: Index out of range \(3 > 2\)/);
|
1859 | assert.throws(function () {math.eval('min([[1,2], [3,4]], 0)');}, /IndexError: Index out of range \(0 < 1\)/);
|
1860 | });
|
1861 |
|
1862 | it('should return DimensionErrors with one based indices (mean)', function () {
|
1863 |
|
1864 |
|
1865 |
|
1866 |
|
1867 |
|
1868 |
|
1869 | assert.deepEqual(math.eval('mean([[1,2], [3,4]])'), 2.5);
|
1870 | assert.deepEqual(math.eval('mean([[1,2], [3,4]], 1)'), math.matrix([2, 3]));
|
1871 | assert.deepEqual(math.eval('mean([[1,2], [3,4]], 2)'), math.matrix([1.5, 3.5]));
|
1872 | assert.throws(function () {math.eval('mean([[1,2], [3,4]], 3)');}, /IndexError: Index out of range \(3 > 2\)/);
|
1873 | assert.throws(function () {math.eval('mean([[1,2], [3,4]], 0)');}, /IndexError: Index out of range \(0 < 1\)/);
|
1874 | });
|
1875 |
|
1876 | });
|
1877 |
|
1878 | describe('node tree', function () {
|
1879 |
|
1880 |
|
1881 |
|
1882 | it('should correctly stringify a node tree', function() {
|
1883 | assert.equal(parse('0').toString(), '0');
|
1884 | assert.equal(parse('"hello"').toString(), '"hello"');
|
1885 | assert.equal(parse('[1, 2 + 3i, 4]').toString(), '[1, 2 + 3 i, 4]');
|
1886 | assert.equal(parse('1/2a').toString(), '1 / 2 a');
|
1887 | });
|
1888 |
|
1889 | it('should correctly stringify an index with dot notation', function() {
|
1890 | assert.equal(parse('A[2]').toString(), 'A[2]');
|
1891 | assert.equal(parse('a["b"]').toString(), 'a["b"]');
|
1892 | assert.equal(parse('a.b').toString(), 'a.b');
|
1893 | });
|
1894 |
|
1895 | describe('custom nodes', function () {
|
1896 |
|
1897 | function CustomNode (args) {
|
1898 | this.args = args;
|
1899 | }
|
1900 | CustomNode.prototype = new math.expression.node.Node();
|
1901 | CustomNode.prototype.toString = function () {
|
1902 | return 'CustomNode';
|
1903 | };
|
1904 | CustomNode.prototype._compile = function (defs) {
|
1905 | var strArgs = [];
|
1906 | this.args.forEach(function (arg) {
|
1907 | strArgs.push(arg.toString());
|
1908 | });
|
1909 | return '"CustomNode(' + strArgs.join(', ') + ')"';
|
1910 | };
|
1911 | CustomNode.prototype.forEach = function (callback) {
|
1912 |
|
1913 | };
|
1914 |
|
1915 | var options = {
|
1916 | nodes: {
|
1917 | custom: CustomNode
|
1918 | }
|
1919 | };
|
1920 |
|
1921 | it('should parse custom nodes', function() {
|
1922 | var node = parse('custom(x, (2+x), sin(x))', options);
|
1923 | assert.equal(node.compile().eval(), 'CustomNode(x, (2 + x), sin(x))');
|
1924 | });
|
1925 |
|
1926 | it('should parse custom nodes without parameters', function() {
|
1927 | var node = parse('custom()', options);
|
1928 | assert.equal(node.compile().eval(), 'CustomNode()');
|
1929 | assert.equal(node.filter(function (node) {return node instanceof CustomNode;}).length, 1);
|
1930 |
|
1931 | var node2 = parse('custom', options);
|
1932 | assert.equal(node2.compile().eval(), 'CustomNode()');
|
1933 | assert.equal(node2.filter(function (node) {return node instanceof CustomNode;}).length, 1);
|
1934 | });
|
1935 |
|
1936 | it('should throw an error on syntax errors in using custom nodes', function() {
|
1937 | assert.throws(function () {parse('custom(x', options);}, /Parenthesis \) expected/);
|
1938 | assert.throws(function () {parse('custom(x, ', options);}, /Unexpected end of expression/);
|
1939 | });
|
1940 | });
|
1941 |
|
1942 | });
|
1943 |
|
1944 | describe ('expose test functions', function () {
|
1945 | it('should expose isAlpha', function() {
|
1946 | assert.ok('should expose isAlpha', typeof math.expression.parse.isAlpha === 'function')
|
1947 | });
|
1948 |
|
1949 | it('should expose isValidLatinOrGreek', function() {
|
1950 | assert.ok('should expose isAlpha', typeof math.expression.parse.isValidLatinOrGreek === 'function')
|
1951 | });
|
1952 |
|
1953 | it('should expose isValidMathSymbol', function() {
|
1954 | assert.ok('should expose isAlpha', typeof math.expression.parse.isValidMathSymbol === 'function')
|
1955 | });
|
1956 |
|
1957 | it('should expose isWhitespace', function() {
|
1958 | assert.ok('should expose isAlpha', typeof math.expression.parse.isWhitespace === 'function')
|
1959 | });
|
1960 |
|
1961 | it('should expose isDecimalMark', function() {
|
1962 | assert.ok('should expose isAlpha', typeof math.expression.parse.isDecimalMark === 'function')
|
1963 | });
|
1964 |
|
1965 | it('should expose isDigitDot', function() {
|
1966 | assert.ok('should expose isAlpha', typeof math.expression.parse.isDigitDot === 'function')
|
1967 | });
|
1968 |
|
1969 | it('should expose isDigit', function() {
|
1970 | assert.ok('should expose isAlpha', typeof math.expression.parse.isDigit === 'function')
|
1971 | });
|
1972 |
|
1973 | it('should allow overriding isAlpha', function() {
|
1974 | var originalIsAlpha = math.expression.parse.isAlpha;
|
1975 |
|
1976 |
|
1977 | math.expression.parse.isAlpha = function (c, cPrev, cNext) {
|
1978 | return /^[a-zA-Z_$]$/.test(c)
|
1979 | };
|
1980 |
|
1981 | var node = math.expression.parse('$foo');
|
1982 | var result = node.eval({$foo: 42});
|
1983 | assert.equal(result, 42);
|
1984 |
|
1985 |
|
1986 | math.expression.parse.isAlpha = originalIsAlpha
|
1987 | });
|
1988 |
|
1989 | });
|
1990 |
|
1991 | it ('Should not allow crashing math by placing a clone function in the config', function () {
|
1992 | var mathClone = math.create();
|
1993 |
|
1994 | try {
|
1995 | mathClone.eval('f(x)=1;config({clone:f})')
|
1996 | }
|
1997 | catch (err) {}
|
1998 |
|
1999 | assert.equal(mathClone.eval('2'), 2);
|
2000 | });
|
2001 |
|
2002 | });
|