UNPKG

83.6 kBJavaScriptView Raw
1// test parse
2var assert = require('assert');
3var approx = require('../../tools/approx');
4var math = require('../../index');
5var ArgumentsError = require('../../lib/error/ArgumentsError');
6var parse = math.expression.parse;
7var ConditionalNode = math.expression.node.ConditionalNode;
8var OperatorNode = math.expression.node.OperatorNode;
9var RangeNode = math.expression.node.RangeNode;
10var Complex = math.type.Complex;
11var Matrix = math.type.Matrix;
12var Range = math.type.Range;
13var Unit = math.type.Unit;
14var ResultSet = math.type.ResultSet;
15
16/**
17 * Helper function to parse an expression and immediately evaluate its results
18 * @param {String} expr
19 * @param {Object} [scope]
20 * @return {*} result
21 */
22function parseAndEval(expr, scope) {
23 return parse(expr).eval(scope);
24}
25
26describe('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 // http://unicode-table.com/en
69 var scope = {};
70
71 math.eval('$ab$c = 2', scope); // dollar sign
72 assert.strictEqual(scope['$ab$c'], 2);
73
74 math.eval('\u00E9 = 2', scope); // Latin Small Letter E with Acute
75 assert.strictEqual(scope['\u00E9'], 2);
76
77 math.eval('\u03A6 = 3', scope); // Greek Capital Letter Phi
78 assert.strictEqual(scope['\u03A6'], 3);
79
80 math.eval('\u03A9 = 4', scope); // Greek Capital Letter Omega
81 assert.strictEqual(scope['\u03A9'], 4);
82
83 math.eval('\u2126 = 4', scope); // Letter-like character Ohm
84 assert.strictEqual(scope['\u2126'], 4);
85
86 math.eval('k\u00F6ln = 5', scope); // Combination of latin and unicode
87 assert.strictEqual(scope['k\u00F6ln'], 5);
88
89 // test unicode characters in the astral plane (surrogate pairs
90 math.eval('\uD835\uDD38 = 1', scope); // double struck capital A
91 assert.strictEqual(scope['\uD835\uDD38'], 1);
92
93 // should not allow the "holes"
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); // x should be x=2, not x of the function
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 // from 1 to n dimensions
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 // note: after getting subset, uninitialized elements are replaced by elements with an undefined value
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 // TODO: test whether 1-based IndexErrors are thrown
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 //assert.throws(function () {parseAndEval('sin(2) = 0.75')}, SyntaxError); // TODO: should this throw an exception?
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); // for testing scoping of the function variables
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); // test whether the decimal mark isn't confused
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); // -(2^2)
1031 approx.deepEqual(parseAndEval('2.^3.^4'), 2.41785163922926e+24); // 2^(3^4)
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); // no implicit multiplication, just a unary minus
1090 assert.equal(parseAndEval('a(2+3)', {a: function() {return 42;}}), 42); // function call
1091 assert.equal(parseAndEval('a.b(2+3)', {a: {b: function() {return 42;}}}), 42); // function call
1092 assert.equal(parseAndEval('(2+3)(4+5)'), 45); // implicit multiplication
1093 assert.equal(parseAndEval('(2+3)(4+5)(3-1)'), 90); // implicit multiplication
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); // index
1107 assert.deepEqual(parseAndEval('(A) [2,2]', {A: [[1,2], [3,4]]}), 4); // index
1108
1109 assert.deepEqual(parseAndEval('[1,2;3,4] [2,2]'), 4); // index
1110 assert.deepEqual(parseAndEval('([1,2;3,4])[2,2]'), 4); // index
1111 assert.throws(function () {parseAndEval('2[1,2,3]')}, /Unexpected operator/);// index
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); // -(2^2)
1148 approx.equal(parseAndEval('2^3^4'), 2.41785163922926e+24); // 2^(3^4)
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); // index [2]
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 // TODO: extensively test operator precedence
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 // functions throw a zero-based error
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 // evaluation via parser throws one-based error
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 // TODO: it would be more clear when all errors where DimensionErrors
1812
1813 // functions throw a zero-based error
1814 assert.throws(function () {math.subset([1,2,3], math.index(1,1));}, /DimensionError: Dimension mismatch \(2 != 1\)/);
1815
1816 // evaluation via parser throws one-based error
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 // TODO: it would be more clear when all errors where DimensionErrors
1822
1823 // functions throw a zero-based error
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 // evaluation via parser throws one-based error
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 // TODO: it would be more clear when all errors where DimensionErrors
1836
1837 // functions throw a zero-based error
1838 // TODO
1839
1840 // evaluation via parser throws one-based error
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 // TODO: it would be more clear when all errors where DimensionErrors
1850
1851 // functions throw a zero-based error
1852 // TODO
1853
1854 // evaluation via parser throws one-based error
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 // TODO: it would be more clear when all errors where DimensionErrors
1864
1865 // functions throw a zero-based error
1866 // TODO
1867
1868 // evaluation via parser throws one-based error
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 // TODO: test parsing into a node tree
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 // define a custom node
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 // we don't have childs
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 // override isAlpha with one accepting $ characters too
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 // restore original isAlpha
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});