@khanacademy/kas 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +6 -0
- package/dist/es/index.js +387 -933
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +385 -931
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/{checking-form_test.js → checking-form.test.ts} +17 -4
- package/src/__tests__/{comparing_test.js → comparing.test.ts} +21 -10
- package/src/__tests__/{compilation_test.js → compilation.test.ts} +18 -5
- package/src/__tests__/{evaluating_test.js → evaluating.test.ts} +22 -6
- package/src/__tests__/{index_test.js → index.test.ts} +28 -16
- package/src/__tests__/{parsing_test.js → parsing.test.ts} +27 -9
- package/src/__tests__/{rendering_test.js → rendering.test.ts} +21 -8
- package/src/__tests__/{transforming_test.js → transforming.test.ts} +37 -18
- package/src/__tests__/{units_test.js → units.test.ts} +37 -16
- package/src/index.js +2 -2
- package/src/nodes.js +1182 -763
- package/src/parser-generator.js +166 -142
- package/tsconfig.json +9 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js.flow +0 -2
package/dist/es/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import _ from 'underscore';
|
|
|
3
3
|
// this is a @generated file
|
|
4
4
|
|
|
5
5
|
/* parser generated by jison 0.4.15 */
|
|
6
|
-
|
|
7
6
|
/*
|
|
8
7
|
Returns a Parser object of the following structure:
|
|
9
8
|
|
|
@@ -78,17 +77,15 @@ import _ from 'underscore';
|
|
|
78
77
|
*/
|
|
79
78
|
var parser$1 = function () {
|
|
80
79
|
var o = function o(k, v, _o, l) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
$V4 = [6, 11, 13, 17];
|
|
91
|
-
|
|
80
|
+
for (_o = _o || {}, l = k.length; l--; _o[k[l]] = v) {
|
|
81
|
+
}
|
|
82
|
+
return _o;
|
|
83
|
+
},
|
|
84
|
+
$V0 = [1, 11],
|
|
85
|
+
$V1 = [1, 9],
|
|
86
|
+
$V2 = [8, 17],
|
|
87
|
+
$V3 = [6, 11],
|
|
88
|
+
$V4 = [6, 11, 13, 17];
|
|
92
89
|
var parser = {
|
|
93
90
|
trace: function trace() {},
|
|
94
91
|
yy: {},
|
|
@@ -128,16 +125,10 @@ var parser$1 = function () {
|
|
|
128
125
|
20: "NEG"
|
|
129
126
|
},
|
|
130
127
|
productions_: [0, [3, 3], [3, 2], [4, 3], [4, 1], [5, 3], [5, 1], [10, 3], [10, 2], [10, 1], [12, 3], [12, 1], [14, 1], [7, 1], [7, 1], [16, 1], [9, 2], [9, 1]],
|
|
131
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate
|
|
132
|
-
/* action[1] */
|
|
133
|
-
, $$
|
|
134
|
-
/* vstack */
|
|
135
|
-
, _$
|
|
136
|
-
/* lstack */
|
|
137
|
-
) {
|
|
128
|
+
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
|
138
129
|
/* this == yyval */
|
|
139
|
-
var $0 = $$.length - 1;
|
|
140
130
|
|
|
131
|
+
var $0 = $$.length - 1;
|
|
141
132
|
switch (yystate) {
|
|
142
133
|
case 1:
|
|
143
134
|
return {
|
|
@@ -145,17 +136,14 @@ var parser$1 = function () {
|
|
|
145
136
|
magnitude: $$[$0 - 2],
|
|
146
137
|
unit: $$[$0 - 1]
|
|
147
138
|
};
|
|
148
|
-
|
|
149
139
|
case 2:
|
|
150
140
|
return {
|
|
151
141
|
type: "unitStandalone",
|
|
152
142
|
unit: $$[$0 - 1]
|
|
153
143
|
};
|
|
154
|
-
|
|
155
144
|
case 3:
|
|
156
145
|
this.$ = $$[$0 - 2] + "e" + $$[$0];
|
|
157
146
|
break;
|
|
158
|
-
|
|
159
147
|
case 4:
|
|
160
148
|
case 13:
|
|
161
149
|
case 14:
|
|
@@ -163,51 +151,42 @@ var parser$1 = function () {
|
|
|
163
151
|
case 17:
|
|
164
152
|
this.$ = $$[$0];
|
|
165
153
|
break;
|
|
166
|
-
|
|
167
154
|
case 5:
|
|
168
155
|
this.$ = {
|
|
169
156
|
num: $$[$0 - 2],
|
|
170
157
|
denom: $$[$0]
|
|
171
158
|
};
|
|
172
159
|
break;
|
|
173
|
-
|
|
174
160
|
case 6:
|
|
175
161
|
this.$ = {
|
|
176
162
|
num: $$[$0],
|
|
177
163
|
denom: null
|
|
178
164
|
};
|
|
179
165
|
break;
|
|
180
|
-
|
|
181
166
|
case 7:
|
|
182
167
|
this.$ = [$$[$0 - 2]].concat($$[$0]);
|
|
183
168
|
break;
|
|
184
|
-
|
|
185
169
|
case 8:
|
|
186
170
|
this.$ = [$$[$0 - 1]].concat($$[$0]);
|
|
187
171
|
break;
|
|
188
|
-
|
|
189
172
|
case 9:
|
|
190
173
|
this.$ = [$$[$0]];
|
|
191
174
|
break;
|
|
192
|
-
|
|
193
175
|
case 10:
|
|
194
176
|
this.$ = {
|
|
195
177
|
name: $$[$0 - 2],
|
|
196
178
|
pow: $$[$0]
|
|
197
179
|
};
|
|
198
180
|
break;
|
|
199
|
-
|
|
200
181
|
case 11:
|
|
201
182
|
this.$ = {
|
|
202
183
|
name: $$[$0],
|
|
203
184
|
pow: 1
|
|
204
185
|
};
|
|
205
186
|
break;
|
|
206
|
-
|
|
207
187
|
case 12:
|
|
208
188
|
this.$ = yytext;
|
|
209
189
|
break;
|
|
210
|
-
|
|
211
190
|
case 16:
|
|
212
191
|
this.$ = "-" + $$[$0];
|
|
213
192
|
break;
|
|
@@ -300,96 +279,80 @@ var parser$1 = function () {
|
|
|
300
279
|
},
|
|
301
280
|
parse: function parse(input) {
|
|
302
281
|
var self = this,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
282
|
+
stack = [0],
|
|
283
|
+
vstack = [null],
|
|
284
|
+
lstack = [],
|
|
285
|
+
table = this.table,
|
|
286
|
+
yytext = '',
|
|
287
|
+
yylineno = 0,
|
|
288
|
+
yyleng = 0,
|
|
289
|
+
TERROR = 2,
|
|
290
|
+
EOF = 1;
|
|
312
291
|
var args = lstack.slice.call(arguments, 1);
|
|
313
292
|
var lexer = Object.create(this.lexer);
|
|
314
293
|
var sharedState = {
|
|
315
294
|
yy: {}
|
|
316
295
|
};
|
|
317
|
-
|
|
318
296
|
for (var k in this.yy) {
|
|
319
297
|
if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
|
|
320
298
|
sharedState.yy[k] = this.yy[k];
|
|
321
299
|
}
|
|
322
300
|
}
|
|
323
|
-
|
|
324
301
|
lexer.setInput(input, sharedState.yy);
|
|
325
302
|
sharedState.yy.lexer = lexer;
|
|
326
303
|
sharedState.yy.parser = this;
|
|
327
|
-
|
|
328
304
|
if (typeof lexer.yylloc == 'undefined') {
|
|
329
305
|
lexer.yylloc = {};
|
|
330
306
|
}
|
|
331
|
-
|
|
332
307
|
var yyloc = lexer.yylloc;
|
|
333
308
|
lstack.push(yyloc);
|
|
334
309
|
var ranges = lexer.options && lexer.options.ranges;
|
|
335
|
-
|
|
336
310
|
if (typeof sharedState.yy.parseError === 'function') {
|
|
337
311
|
this.parseError = sharedState.yy.parseError;
|
|
338
312
|
} else {
|
|
339
313
|
this.parseError = Object.getPrototypeOf(this).parseError;
|
|
340
314
|
}
|
|
341
|
-
|
|
342
|
-
|
|
315
|
+
//_token_stack:
|
|
343
316
|
function lex() {
|
|
344
317
|
var token;
|
|
345
318
|
token = lexer.lex() || EOF;
|
|
346
|
-
|
|
347
319
|
if (typeof token !== 'number') {
|
|
348
320
|
token = self.symbols_[token] || token;
|
|
349
321
|
}
|
|
350
|
-
|
|
351
322
|
return token;
|
|
352
323
|
}
|
|
353
|
-
|
|
354
324
|
var symbol,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
325
|
+
state,
|
|
326
|
+
action,
|
|
327
|
+
r,
|
|
328
|
+
yyval = {},
|
|
329
|
+
p,
|
|
330
|
+
len,
|
|
331
|
+
newState,
|
|
332
|
+
expected;
|
|
364
333
|
while (true) {
|
|
365
334
|
state = stack[stack.length - 1];
|
|
366
|
-
|
|
367
335
|
if (this.defaultActions[state]) {
|
|
368
336
|
action = this.defaultActions[state];
|
|
369
337
|
} else {
|
|
370
338
|
if (symbol === null || typeof symbol == 'undefined') {
|
|
371
339
|
symbol = lex();
|
|
372
340
|
}
|
|
373
|
-
|
|
374
341
|
action = table[state] && table[state][symbol];
|
|
375
342
|
}
|
|
376
|
-
|
|
377
343
|
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
|
378
344
|
var errStr = '';
|
|
379
345
|
expected = [];
|
|
380
|
-
|
|
381
346
|
for (p in table[state]) {
|
|
382
347
|
if (this.terminals_[p] && p > TERROR) {
|
|
383
348
|
expected.push('\'' + this.terminals_[p] + '\'');
|
|
384
349
|
}
|
|
385
350
|
}
|
|
386
|
-
|
|
387
351
|
if (lexer.showPosition) {
|
|
388
352
|
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
|
|
389
353
|
} else {
|
|
390
354
|
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
|
|
391
355
|
}
|
|
392
|
-
|
|
393
356
|
this.parseError(errStr, {
|
|
394
357
|
text: lexer.match,
|
|
395
358
|
token: this.terminals_[symbol] || symbol,
|
|
@@ -398,11 +361,9 @@ var parser$1 = function () {
|
|
|
398
361
|
expected: expected
|
|
399
362
|
});
|
|
400
363
|
}
|
|
401
|
-
|
|
402
364
|
if (action[0] instanceof Array && action.length > 1) {
|
|
403
365
|
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
|
|
404
366
|
}
|
|
405
|
-
|
|
406
367
|
switch (action[0]) {
|
|
407
368
|
case 1:
|
|
408
369
|
stack.push(symbol);
|
|
@@ -410,16 +371,13 @@ var parser$1 = function () {
|
|
|
410
371
|
lstack.push(lexer.yylloc);
|
|
411
372
|
stack.push(action[1]);
|
|
412
373
|
symbol = null;
|
|
413
|
-
|
|
414
374
|
{
|
|
415
375
|
yyleng = lexer.yyleng;
|
|
416
376
|
yytext = lexer.yytext;
|
|
417
377
|
yylineno = lexer.yylineno;
|
|
418
378
|
yyloc = lexer.yylloc;
|
|
419
379
|
}
|
|
420
|
-
|
|
421
380
|
break;
|
|
422
|
-
|
|
423
381
|
case 2:
|
|
424
382
|
len = this.productions_[action[1]][1];
|
|
425
383
|
yyval.$ = vstack[vstack.length - len];
|
|
@@ -429,40 +387,32 @@ var parser$1 = function () {
|
|
|
429
387
|
first_column: lstack[lstack.length - (len || 1)].first_column,
|
|
430
388
|
last_column: lstack[lstack.length - 1].last_column
|
|
431
389
|
};
|
|
432
|
-
|
|
433
390
|
if (ranges) {
|
|
434
391
|
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
|
|
435
392
|
}
|
|
436
|
-
|
|
437
393
|
r = this.performAction.apply(yyval, [yytext, yyleng, yylineno, sharedState.yy, action[1], vstack, lstack].concat(args));
|
|
438
|
-
|
|
439
394
|
if (typeof r !== 'undefined') {
|
|
440
395
|
return r;
|
|
441
396
|
}
|
|
442
|
-
|
|
443
397
|
if (len) {
|
|
444
398
|
stack = stack.slice(0, -1 * len * 2);
|
|
445
399
|
vstack = vstack.slice(0, -1 * len);
|
|
446
400
|
lstack = lstack.slice(0, -1 * len);
|
|
447
401
|
}
|
|
448
|
-
|
|
449
402
|
stack.push(this.productions_[action[1]][0]);
|
|
450
403
|
vstack.push(yyval.$);
|
|
451
404
|
lstack.push(yyval._$);
|
|
452
405
|
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
|
|
453
406
|
stack.push(newState);
|
|
454
407
|
break;
|
|
455
|
-
|
|
456
408
|
case 3:
|
|
457
409
|
return true;
|
|
458
410
|
}
|
|
459
411
|
}
|
|
460
|
-
|
|
461
412
|
return true;
|
|
462
413
|
}
|
|
463
414
|
};
|
|
464
415
|
/* generated by jison-lex 0.3.4 */
|
|
465
|
-
|
|
466
416
|
var lexer = function () {
|
|
467
417
|
var lexer = {
|
|
468
418
|
EOF: 1,
|
|
@@ -487,11 +437,9 @@ var parser$1 = function () {
|
|
|
487
437
|
last_line: 1,
|
|
488
438
|
last_column: 0
|
|
489
439
|
};
|
|
490
|
-
|
|
491
440
|
if (this.options.ranges) {
|
|
492
441
|
this.yylloc.range = [0, 0];
|
|
493
442
|
}
|
|
494
|
-
|
|
495
443
|
this.offset = 0;
|
|
496
444
|
return this;
|
|
497
445
|
},
|
|
@@ -504,18 +452,15 @@ var parser$1 = function () {
|
|
|
504
452
|
this.match += ch;
|
|
505
453
|
this.matched += ch;
|
|
506
454
|
var lines = ch.match(/(?:\r\n?|\n).*/g);
|
|
507
|
-
|
|
508
455
|
if (lines) {
|
|
509
456
|
this.yylineno++;
|
|
510
457
|
this.yylloc.last_line++;
|
|
511
458
|
} else {
|
|
512
459
|
this.yylloc.last_column++;
|
|
513
460
|
}
|
|
514
|
-
|
|
515
461
|
if (this.options.ranges) {
|
|
516
462
|
this.yylloc.range[1]++;
|
|
517
463
|
}
|
|
518
|
-
|
|
519
464
|
this._input = this._input.slice(1);
|
|
520
465
|
return ch;
|
|
521
466
|
},
|
|
@@ -524,17 +469,15 @@ var parser$1 = function () {
|
|
|
524
469
|
var len = ch.length;
|
|
525
470
|
var lines = ch.split(/(?:\r\n?|\n)/g);
|
|
526
471
|
this._input = ch + this._input;
|
|
527
|
-
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
528
|
-
|
|
472
|
+
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
473
|
+
//this.yyleng -= len;
|
|
529
474
|
this.offset -= len;
|
|
530
475
|
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
|
|
531
476
|
this.match = this.match.substr(0, this.match.length - 1);
|
|
532
477
|
this.matched = this.matched.substr(0, this.matched.length - 1);
|
|
533
|
-
|
|
534
478
|
if (lines.length - 1) {
|
|
535
479
|
this.yylineno -= lines.length - 1;
|
|
536
480
|
}
|
|
537
|
-
|
|
538
481
|
var r = this.yylloc.range;
|
|
539
482
|
this.yylloc = {
|
|
540
483
|
first_line: this.yylloc.first_line,
|
|
@@ -542,11 +485,9 @@ var parser$1 = function () {
|
|
|
542
485
|
first_column: this.yylloc.first_column,
|
|
543
486
|
last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
|
|
544
487
|
};
|
|
545
|
-
|
|
546
488
|
if (this.options.ranges) {
|
|
547
489
|
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
|
|
548
490
|
}
|
|
549
|
-
|
|
550
491
|
this.yyleng = this.yytext.length;
|
|
551
492
|
return this;
|
|
552
493
|
},
|
|
@@ -566,7 +507,6 @@ var parser$1 = function () {
|
|
|
566
507
|
line: this.yylineno
|
|
567
508
|
});
|
|
568
509
|
}
|
|
569
|
-
|
|
570
510
|
return this;
|
|
571
511
|
},
|
|
572
512
|
// retain first n characters of the match
|
|
@@ -581,11 +521,9 @@ var parser$1 = function () {
|
|
|
581
521
|
// displays upcoming input, i.e. for error messages
|
|
582
522
|
upcomingInput: function upcomingInput() {
|
|
583
523
|
var next = this.match;
|
|
584
|
-
|
|
585
524
|
if (next.length < 20) {
|
|
586
525
|
next += this._input.substr(0, 20 - next.length);
|
|
587
526
|
}
|
|
588
|
-
|
|
589
527
|
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
|
|
590
528
|
},
|
|
591
529
|
// displays the character position where the lexing error occurred, i.e. for error messages
|
|
@@ -597,7 +535,6 @@ var parser$1 = function () {
|
|
|
597
535
|
// test the lexed token: return FALSE when not a match, otherwise return token
|
|
598
536
|
test_match: function test_match(match, indexed_rule) {
|
|
599
537
|
var token, lines, backup;
|
|
600
|
-
|
|
601
538
|
if (this.options.backtrack_lexer) {
|
|
602
539
|
// save context
|
|
603
540
|
backup = {
|
|
@@ -620,18 +557,14 @@ var parser$1 = function () {
|
|
|
620
557
|
conditionStack: this.conditionStack.slice(0),
|
|
621
558
|
done: this.done
|
|
622
559
|
};
|
|
623
|
-
|
|
624
560
|
if (this.options.ranges) {
|
|
625
561
|
backup.yylloc.range = this.yylloc.range.slice(0);
|
|
626
562
|
}
|
|
627
563
|
}
|
|
628
|
-
|
|
629
564
|
lines = match[0].match(/(?:\r\n?|\n).*/g);
|
|
630
|
-
|
|
631
565
|
if (lines) {
|
|
632
566
|
this.yylineno += lines.length;
|
|
633
567
|
}
|
|
634
|
-
|
|
635
568
|
this.yylloc = {
|
|
636
569
|
first_line: this.yylloc.last_line,
|
|
637
570
|
last_line: this.yylineno + 1,
|
|
@@ -642,21 +575,17 @@ var parser$1 = function () {
|
|
|
642
575
|
this.match += match[0];
|
|
643
576
|
this.matches = match;
|
|
644
577
|
this.yyleng = this.yytext.length;
|
|
645
|
-
|
|
646
578
|
if (this.options.ranges) {
|
|
647
579
|
this.yylloc.range = [this.offset, this.offset += this.yyleng];
|
|
648
580
|
}
|
|
649
|
-
|
|
650
581
|
this._more = false;
|
|
651
582
|
this._backtrack = false;
|
|
652
583
|
this._input = this._input.slice(match[0].length);
|
|
653
584
|
this.matched += match[0];
|
|
654
585
|
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
|
|
655
|
-
|
|
656
586
|
if (this.done && this._input) {
|
|
657
587
|
this.done = false;
|
|
658
588
|
}
|
|
659
|
-
|
|
660
589
|
if (token) {
|
|
661
590
|
return token;
|
|
662
591
|
} else if (this._backtrack) {
|
|
@@ -664,7 +593,6 @@ var parser$1 = function () {
|
|
|
664
593
|
for (var k in backup) {
|
|
665
594
|
this[k] = backup[k];
|
|
666
595
|
}
|
|
667
|
-
|
|
668
596
|
return false; // rule action called reject() implying the next rule should be tested instead.
|
|
669
597
|
}
|
|
670
598
|
|
|
@@ -675,30 +603,22 @@ var parser$1 = function () {
|
|
|
675
603
|
if (this.done) {
|
|
676
604
|
return this.EOF;
|
|
677
605
|
}
|
|
678
|
-
|
|
679
606
|
if (!this._input) {
|
|
680
607
|
this.done = true;
|
|
681
608
|
}
|
|
682
|
-
|
|
683
609
|
var token, match, tempMatch, index;
|
|
684
|
-
|
|
685
610
|
if (!this._more) {
|
|
686
611
|
this.yytext = '';
|
|
687
612
|
this.match = '';
|
|
688
613
|
}
|
|
689
|
-
|
|
690
614
|
var rules = this._currentRules();
|
|
691
|
-
|
|
692
615
|
for (var i = 0; i < rules.length; i++) {
|
|
693
616
|
tempMatch = this._input.match(this.rules[rules[i]]);
|
|
694
|
-
|
|
695
617
|
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
|
696
618
|
match = tempMatch;
|
|
697
619
|
index = i;
|
|
698
|
-
|
|
699
620
|
if (this.options.backtrack_lexer) {
|
|
700
621
|
token = this.test_match(tempMatch, rules[i]);
|
|
701
|
-
|
|
702
622
|
if (token !== false) {
|
|
703
623
|
return token;
|
|
704
624
|
} else if (this._backtrack) {
|
|
@@ -713,18 +633,14 @@ var parser$1 = function () {
|
|
|
713
633
|
}
|
|
714
634
|
}
|
|
715
635
|
}
|
|
716
|
-
|
|
717
636
|
if (match) {
|
|
718
637
|
token = this.test_match(match, rules[index]);
|
|
719
|
-
|
|
720
638
|
if (token !== false) {
|
|
721
639
|
return token;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
|
|
640
|
+
}
|
|
641
|
+
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
725
642
|
return false;
|
|
726
643
|
}
|
|
727
|
-
|
|
728
644
|
if (this._input === "") {
|
|
729
645
|
return this.EOF;
|
|
730
646
|
} else {
|
|
@@ -738,7 +654,6 @@ var parser$1 = function () {
|
|
|
738
654
|
// return next match that has a token
|
|
739
655
|
lex: function lex() {
|
|
740
656
|
var r = this.next();
|
|
741
|
-
|
|
742
657
|
if (r) {
|
|
743
658
|
return r;
|
|
744
659
|
} else {
|
|
@@ -752,7 +667,6 @@ var parser$1 = function () {
|
|
|
752
667
|
// pop the previously active lexer condition state off the condition stack
|
|
753
668
|
popState: function popState() {
|
|
754
669
|
var n = this.conditionStack.length - 1;
|
|
755
|
-
|
|
756
670
|
if (n > 0) {
|
|
757
671
|
return this.conditionStack.pop();
|
|
758
672
|
} else {
|
|
@@ -770,7 +684,6 @@ var parser$1 = function () {
|
|
|
770
684
|
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
|
771
685
|
topState: function topState(n) {
|
|
772
686
|
n = this.conditionStack.length - 1 - Math.abs(n || 0);
|
|
773
|
-
|
|
774
687
|
if (n >= 0) {
|
|
775
688
|
return this.conditionStack[n];
|
|
776
689
|
} else {
|
|
@@ -787,48 +700,34 @@ var parser$1 = function () {
|
|
|
787
700
|
},
|
|
788
701
|
options: {},
|
|
789
702
|
performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
|
|
790
|
-
|
|
791
703
|
switch ($avoiding_name_collisions) {
|
|
792
704
|
case 0:
|
|
793
705
|
return 11;
|
|
794
|
-
|
|
795
706
|
case 1:
|
|
796
707
|
return '(';
|
|
797
|
-
|
|
798
708
|
case 2:
|
|
799
709
|
return ')';
|
|
800
|
-
|
|
801
710
|
case 3:
|
|
802
711
|
return 8;
|
|
803
|
-
|
|
804
712
|
case 4:
|
|
805
713
|
return 15;
|
|
806
|
-
|
|
807
714
|
case 5:
|
|
808
715
|
return 13;
|
|
809
|
-
|
|
810
716
|
case 6:
|
|
811
717
|
return 18;
|
|
812
|
-
|
|
813
718
|
case 7:
|
|
814
719
|
return 19;
|
|
815
|
-
|
|
816
720
|
case 8:
|
|
817
721
|
return 20;
|
|
818
|
-
|
|
819
722
|
case 9:
|
|
820
723
|
return 17;
|
|
821
|
-
|
|
822
724
|
case 10:
|
|
823
725
|
return 17;
|
|
824
|
-
|
|
825
726
|
case 11:
|
|
826
727
|
return 17;
|
|
827
|
-
|
|
828
728
|
case 12:
|
|
829
729
|
/* skip whitespace */
|
|
830
730
|
break;
|
|
831
|
-
|
|
832
731
|
case 13:
|
|
833
732
|
return 6;
|
|
834
733
|
}
|
|
@@ -843,23 +742,19 @@ var parser$1 = function () {
|
|
|
843
742
|
};
|
|
844
743
|
return lexer;
|
|
845
744
|
}();
|
|
846
|
-
|
|
847
745
|
parser.lexer = lexer;
|
|
848
|
-
|
|
849
746
|
function Parser() {
|
|
850
747
|
this.yy = {};
|
|
851
748
|
}
|
|
852
|
-
|
|
853
749
|
Parser.prototype = parser;
|
|
854
750
|
parser.Parser = Parser;
|
|
855
751
|
return new Parser();
|
|
856
752
|
}();
|
|
857
|
-
|
|
858
753
|
var unitParser = parser$1;
|
|
859
754
|
|
|
860
755
|
// This is a @generated file
|
|
861
|
-
/* parser generated by jison 0.4.15 */
|
|
862
756
|
|
|
757
|
+
/* parser generated by jison 0.4.15 */
|
|
863
758
|
/*
|
|
864
759
|
Returns a Parser object of the following structure:
|
|
865
760
|
|
|
@@ -932,40 +827,37 @@ var unitParser = parser$1;
|
|
|
932
827
|
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
|
|
933
828
|
}
|
|
934
829
|
*/
|
|
935
|
-
|
|
936
830
|
var parser = function () {
|
|
937
831
|
var o = function o(k, v, _o, l) {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
$Vo = [5, 6, 8, 10, 12, 14, 16, 18, 19, 21, 22, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 42, 43, 44, 45, 46];
|
|
968
|
-
|
|
832
|
+
for (_o = _o || {}, l = k.length; l--; _o[k[l]] = v) {
|
|
833
|
+
}
|
|
834
|
+
return _o;
|
|
835
|
+
},
|
|
836
|
+
$V0 = [1, 7],
|
|
837
|
+
$V1 = [1, 17],
|
|
838
|
+
$V2 = [1, 13],
|
|
839
|
+
$V3 = [1, 14],
|
|
840
|
+
$V4 = [1, 15],
|
|
841
|
+
$V5 = [1, 32],
|
|
842
|
+
$V6 = [1, 22],
|
|
843
|
+
$V7 = [1, 23],
|
|
844
|
+
$V8 = [1, 24],
|
|
845
|
+
$V9 = [1, 25],
|
|
846
|
+
$Va = [1, 26],
|
|
847
|
+
$Vb = [1, 33],
|
|
848
|
+
$Vc = [1, 27],
|
|
849
|
+
$Vd = [1, 28],
|
|
850
|
+
$Ve = [1, 29],
|
|
851
|
+
$Vf = [1, 30],
|
|
852
|
+
$Vg = [1, 20],
|
|
853
|
+
$Vh = [1, 36],
|
|
854
|
+
$Vi = [1, 37],
|
|
855
|
+
$Vj = [5, 6, 8, 10, 33, 35, 41, 43, 45],
|
|
856
|
+
$Vk = [1, 39],
|
|
857
|
+
$Vl = [1, 40],
|
|
858
|
+
$Vm = [5, 6, 8, 10, 12, 14, 16, 19, 21, 22, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 42, 43, 44, 45, 46],
|
|
859
|
+
$Vn = [10, 16, 19, 21, 22, 28, 29, 30, 31, 32, 34, 37, 39, 42, 43, 44, 46],
|
|
860
|
+
$Vo = [5, 6, 8, 10, 12, 14, 16, 18, 19, 21, 22, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, 42, 43, 44, 45, 46];
|
|
969
861
|
var parser = {
|
|
970
862
|
trace: function trace() {},
|
|
971
863
|
yy: {},
|
|
@@ -1051,26 +943,17 @@ var parser = function () {
|
|
|
1051
943
|
46: "FRAC"
|
|
1052
944
|
},
|
|
1053
945
|
productions_: [0, [3, 4], [3, 2], [3, 1], [4, 1], [7, 3], [7, 3], [7, 1], [9, 2], [9, 3], [9, 3], [9, 1], [13, 2], [13, 1], [15, 1], [17, 1], [17, 3], [17, 1], [20, 1], [20, 1], [20, 3], [11, 2], [11, 2], [11, 1], [25, 3], [25, 1], [27, 1], [24, 3], [24, 1], [24, 1], [24, 1], [24, 1], [24, 3], [24, 3], [36, 1], [38, 4], [38, 4], [38, 7], [38, 4], [38, 3], [38, 3], [38, 4], [26, 1], [26, 1], [26, 7]],
|
|
1054
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate
|
|
1055
|
-
/* action[1] */
|
|
1056
|
-
, $$
|
|
1057
|
-
/* vstack */
|
|
1058
|
-
, _$
|
|
1059
|
-
/* lstack */
|
|
1060
|
-
) {
|
|
946
|
+
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
|
|
1061
947
|
/* this == yyval */
|
|
1062
|
-
var $0 = $$.length - 1;
|
|
1063
948
|
|
|
949
|
+
var $0 = $$.length - 1;
|
|
1064
950
|
switch (yystate) {
|
|
1065
951
|
case 1:
|
|
1066
952
|
return new yy.Eq($$[$0 - 3], $$[$0 - 2], $$[$0 - 1]);
|
|
1067
|
-
|
|
1068
953
|
case 2:
|
|
1069
954
|
return $$[$0 - 1];
|
|
1070
|
-
|
|
1071
955
|
case 3:
|
|
1072
956
|
return new yy.Add([]);
|
|
1073
|
-
|
|
1074
957
|
case 4:
|
|
1075
958
|
case 7:
|
|
1076
959
|
case 11:
|
|
@@ -1083,112 +966,86 @@ var parser = function () {
|
|
|
1083
966
|
case 43:
|
|
1084
967
|
this.$ = $$[$0];
|
|
1085
968
|
break;
|
|
1086
|
-
|
|
1087
969
|
case 5:
|
|
1088
970
|
this.$ = yy.Add.createOrAppend($$[$0 - 2], $$[$0]);
|
|
1089
971
|
break;
|
|
1090
|
-
|
|
1091
972
|
case 6:
|
|
1092
973
|
this.$ = yy.Add.createOrAppend($$[$0 - 2], yy.Mul.handleNegative($$[$0], "subtract"));
|
|
1093
974
|
break;
|
|
1094
|
-
|
|
1095
975
|
case 8:
|
|
1096
976
|
this.$ = yy.Mul.fold(yy.Mul.createOrAppend($$[$0 - 1], $$[$0]));
|
|
1097
977
|
break;
|
|
1098
|
-
|
|
1099
978
|
case 9:
|
|
1100
979
|
this.$ = yy.Mul.fold(yy.Mul.createOrAppend($$[$0 - 2], $$[$0]));
|
|
1101
980
|
break;
|
|
1102
|
-
|
|
1103
981
|
case 10:
|
|
1104
982
|
this.$ = yy.Mul.fold(yy.Mul.handleDivide($$[$0 - 2], $$[$0]));
|
|
1105
983
|
break;
|
|
1106
|
-
|
|
1107
984
|
case 12:
|
|
1108
985
|
this.$ = yy.Mul.handleNegative($$[$0]);
|
|
1109
986
|
break;
|
|
1110
|
-
|
|
1111
987
|
case 14:
|
|
1112
988
|
case 17:
|
|
1113
989
|
this.$ = [yytext];
|
|
1114
990
|
break;
|
|
1115
|
-
|
|
1116
991
|
case 16:
|
|
1117
992
|
this.$ = $$[$0 - 2].concat($$[$0]);
|
|
1118
993
|
break;
|
|
1119
|
-
|
|
1120
994
|
case 18:
|
|
1121
995
|
this.$ = yy.Log.natural();
|
|
1122
996
|
break;
|
|
1123
|
-
|
|
1124
997
|
case 19:
|
|
1125
998
|
this.$ = yy.Log.common();
|
|
1126
999
|
break;
|
|
1127
|
-
|
|
1128
1000
|
case 21:
|
|
1129
1001
|
this.$ = yy.Trig.create($$[$0 - 1], $$[$0]);
|
|
1130
1002
|
break;
|
|
1131
|
-
|
|
1132
1003
|
case 22:
|
|
1133
1004
|
this.$ = yy.Log.create($$[$0 - 1], $$[$0]);
|
|
1134
1005
|
break;
|
|
1135
|
-
|
|
1136
1006
|
case 24:
|
|
1137
1007
|
this.$ = new yy.Pow($$[$0 - 2], $$[$0]);
|
|
1138
1008
|
break;
|
|
1139
|
-
|
|
1140
1009
|
case 26:
|
|
1141
1010
|
case 34:
|
|
1142
1011
|
this.$ = yytext;
|
|
1143
1012
|
break;
|
|
1144
|
-
|
|
1145
1013
|
case 27:
|
|
1146
1014
|
this.$ = new yy.Var($$[$0 - 2], $$[$0]);
|
|
1147
1015
|
break;
|
|
1148
|
-
|
|
1149
1016
|
case 28:
|
|
1150
1017
|
this.$ = new yy.Var($$[$0]);
|
|
1151
1018
|
break;
|
|
1152
|
-
|
|
1153
1019
|
case 29:
|
|
1154
1020
|
this.$ = new yy.Const(yytext.toLowerCase());
|
|
1155
1021
|
break;
|
|
1156
|
-
|
|
1157
1022
|
case 30:
|
|
1158
1023
|
this.$ = yy.Int.create(Number(yytext));
|
|
1159
1024
|
break;
|
|
1160
|
-
|
|
1161
1025
|
case 31:
|
|
1162
1026
|
this.$ = yy.Float.create(Number(yytext));
|
|
1163
1027
|
break;
|
|
1164
|
-
|
|
1165
1028
|
case 32:
|
|
1166
1029
|
this.$ = $$[$0 - 1].completeParse();
|
|
1167
1030
|
break;
|
|
1168
|
-
|
|
1169
1031
|
case 33:
|
|
1170
1032
|
this.$ = $$[$0 - 1].completeParse().addHint('parens');
|
|
1171
1033
|
break;
|
|
1172
|
-
|
|
1173
1034
|
case 35:
|
|
1174
1035
|
case 36:
|
|
1175
1036
|
this.$ = yy.Pow.sqrt($$[$0 - 1]);
|
|
1176
1037
|
break;
|
|
1177
|
-
|
|
1178
1038
|
case 37:
|
|
1179
1039
|
this.$ = new yy.Pow.nthroot($$[$0 - 1], $$[$0 - 4]);
|
|
1180
1040
|
break;
|
|
1181
|
-
|
|
1182
1041
|
case 38:
|
|
1183
1042
|
case 39:
|
|
1184
1043
|
case 40:
|
|
1185
1044
|
this.$ = new yy.Abs($$[$0 - 1]);
|
|
1186
1045
|
break;
|
|
1187
|
-
|
|
1188
1046
|
case 41:
|
|
1189
1047
|
this.$ = new yy.Func($$[$0 - 3], $$[$0 - 1]);
|
|
1190
1048
|
break;
|
|
1191
|
-
|
|
1192
1049
|
case 44:
|
|
1193
1050
|
this.$ = yy.Mul.handleDivide($$[$0 - 4], $$[$0 - 1]);
|
|
1194
1051
|
break;
|
|
@@ -2101,96 +1958,80 @@ var parser = function () {
|
|
|
2101
1958
|
},
|
|
2102
1959
|
parse: function parse(input) {
|
|
2103
1960
|
var self = this,
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
1961
|
+
stack = [0],
|
|
1962
|
+
vstack = [null],
|
|
1963
|
+
lstack = [],
|
|
1964
|
+
table = this.table,
|
|
1965
|
+
yytext = '',
|
|
1966
|
+
yylineno = 0,
|
|
1967
|
+
yyleng = 0,
|
|
1968
|
+
TERROR = 2,
|
|
1969
|
+
EOF = 1;
|
|
2113
1970
|
var args = lstack.slice.call(arguments, 1);
|
|
2114
1971
|
var lexer = Object.create(this.lexer);
|
|
2115
1972
|
var sharedState = {
|
|
2116
1973
|
yy: {}
|
|
2117
1974
|
};
|
|
2118
|
-
|
|
2119
1975
|
for (var k in this.yy) {
|
|
2120
1976
|
if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
|
|
2121
1977
|
sharedState.yy[k] = this.yy[k];
|
|
2122
1978
|
}
|
|
2123
1979
|
}
|
|
2124
|
-
|
|
2125
1980
|
lexer.setInput(input, sharedState.yy);
|
|
2126
1981
|
sharedState.yy.lexer = lexer;
|
|
2127
1982
|
sharedState.yy.parser = this;
|
|
2128
|
-
|
|
2129
1983
|
if (typeof lexer.yylloc == 'undefined') {
|
|
2130
1984
|
lexer.yylloc = {};
|
|
2131
1985
|
}
|
|
2132
|
-
|
|
2133
1986
|
var yyloc = lexer.yylloc;
|
|
2134
1987
|
lstack.push(yyloc);
|
|
2135
1988
|
var ranges = lexer.options && lexer.options.ranges;
|
|
2136
|
-
|
|
2137
1989
|
if (typeof sharedState.yy.parseError === 'function') {
|
|
2138
1990
|
this.parseError = sharedState.yy.parseError;
|
|
2139
1991
|
} else {
|
|
2140
1992
|
this.parseError = Object.getPrototypeOf(this).parseError;
|
|
2141
1993
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
1994
|
+
//_token_stack:
|
|
2144
1995
|
function lex() {
|
|
2145
1996
|
var token;
|
|
2146
1997
|
token = lexer.lex() || EOF;
|
|
2147
|
-
|
|
2148
1998
|
if (typeof token !== 'number') {
|
|
2149
1999
|
token = self.symbols_[token] || token;
|
|
2150
2000
|
}
|
|
2151
|
-
|
|
2152
2001
|
return token;
|
|
2153
2002
|
}
|
|
2154
|
-
|
|
2155
2003
|
var symbol,
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2004
|
+
state,
|
|
2005
|
+
action,
|
|
2006
|
+
r,
|
|
2007
|
+
yyval = {},
|
|
2008
|
+
p,
|
|
2009
|
+
len,
|
|
2010
|
+
newState,
|
|
2011
|
+
expected;
|
|
2165
2012
|
while (true) {
|
|
2166
2013
|
state = stack[stack.length - 1];
|
|
2167
|
-
|
|
2168
2014
|
if (this.defaultActions[state]) {
|
|
2169
2015
|
action = this.defaultActions[state];
|
|
2170
2016
|
} else {
|
|
2171
2017
|
if (symbol === null || typeof symbol == 'undefined') {
|
|
2172
2018
|
symbol = lex();
|
|
2173
2019
|
}
|
|
2174
|
-
|
|
2175
2020
|
action = table[state] && table[state][symbol];
|
|
2176
2021
|
}
|
|
2177
|
-
|
|
2178
2022
|
if (typeof action === 'undefined' || !action.length || !action[0]) {
|
|
2179
2023
|
var errStr = '';
|
|
2180
2024
|
expected = [];
|
|
2181
|
-
|
|
2182
2025
|
for (p in table[state]) {
|
|
2183
2026
|
if (this.terminals_[p] && p > TERROR) {
|
|
2184
2027
|
expected.push('\'' + this.terminals_[p] + '\'');
|
|
2185
2028
|
}
|
|
2186
2029
|
}
|
|
2187
|
-
|
|
2188
2030
|
if (lexer.showPosition) {
|
|
2189
2031
|
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
|
|
2190
2032
|
} else {
|
|
2191
2033
|
errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
|
|
2192
2034
|
}
|
|
2193
|
-
|
|
2194
2035
|
this.parseError(errStr, {
|
|
2195
2036
|
text: lexer.match,
|
|
2196
2037
|
token: this.terminals_[symbol] || symbol,
|
|
@@ -2199,11 +2040,9 @@ var parser = function () {
|
|
|
2199
2040
|
expected: expected
|
|
2200
2041
|
});
|
|
2201
2042
|
}
|
|
2202
|
-
|
|
2203
2043
|
if (action[0] instanceof Array && action.length > 1) {
|
|
2204
2044
|
throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
|
|
2205
2045
|
}
|
|
2206
|
-
|
|
2207
2046
|
switch (action[0]) {
|
|
2208
2047
|
case 1:
|
|
2209
2048
|
stack.push(symbol);
|
|
@@ -2211,16 +2050,13 @@ var parser = function () {
|
|
|
2211
2050
|
lstack.push(lexer.yylloc);
|
|
2212
2051
|
stack.push(action[1]);
|
|
2213
2052
|
symbol = null;
|
|
2214
|
-
|
|
2215
2053
|
{
|
|
2216
2054
|
yyleng = lexer.yyleng;
|
|
2217
2055
|
yytext = lexer.yytext;
|
|
2218
2056
|
yylineno = lexer.yylineno;
|
|
2219
2057
|
yyloc = lexer.yylloc;
|
|
2220
2058
|
}
|
|
2221
|
-
|
|
2222
2059
|
break;
|
|
2223
|
-
|
|
2224
2060
|
case 2:
|
|
2225
2061
|
len = this.productions_[action[1]][1];
|
|
2226
2062
|
yyval.$ = vstack[vstack.length - len];
|
|
@@ -2230,40 +2066,32 @@ var parser = function () {
|
|
|
2230
2066
|
first_column: lstack[lstack.length - (len || 1)].first_column,
|
|
2231
2067
|
last_column: lstack[lstack.length - 1].last_column
|
|
2232
2068
|
};
|
|
2233
|
-
|
|
2234
2069
|
if (ranges) {
|
|
2235
2070
|
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
|
|
2236
2071
|
}
|
|
2237
|
-
|
|
2238
2072
|
r = this.performAction.apply(yyval, [yytext, yyleng, yylineno, sharedState.yy, action[1], vstack, lstack].concat(args));
|
|
2239
|
-
|
|
2240
2073
|
if (typeof r !== 'undefined') {
|
|
2241
2074
|
return r;
|
|
2242
2075
|
}
|
|
2243
|
-
|
|
2244
2076
|
if (len) {
|
|
2245
2077
|
stack = stack.slice(0, -1 * len * 2);
|
|
2246
2078
|
vstack = vstack.slice(0, -1 * len);
|
|
2247
2079
|
lstack = lstack.slice(0, -1 * len);
|
|
2248
2080
|
}
|
|
2249
|
-
|
|
2250
2081
|
stack.push(this.productions_[action[1]][0]);
|
|
2251
2082
|
vstack.push(yyval.$);
|
|
2252
2083
|
lstack.push(yyval._$);
|
|
2253
2084
|
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
|
|
2254
2085
|
stack.push(newState);
|
|
2255
2086
|
break;
|
|
2256
|
-
|
|
2257
2087
|
case 3:
|
|
2258
2088
|
return true;
|
|
2259
2089
|
}
|
|
2260
2090
|
}
|
|
2261
|
-
|
|
2262
2091
|
return true;
|
|
2263
2092
|
}
|
|
2264
2093
|
};
|
|
2265
2094
|
/* generated by jison-lex 0.3.4 */
|
|
2266
|
-
|
|
2267
2095
|
var lexer = function () {
|
|
2268
2096
|
var lexer = {
|
|
2269
2097
|
EOF: 1,
|
|
@@ -2288,11 +2116,9 @@ var parser = function () {
|
|
|
2288
2116
|
last_line: 1,
|
|
2289
2117
|
last_column: 0
|
|
2290
2118
|
};
|
|
2291
|
-
|
|
2292
2119
|
if (this.options.ranges) {
|
|
2293
2120
|
this.yylloc.range = [0, 0];
|
|
2294
2121
|
}
|
|
2295
|
-
|
|
2296
2122
|
this.offset = 0;
|
|
2297
2123
|
return this;
|
|
2298
2124
|
},
|
|
@@ -2305,18 +2131,15 @@ var parser = function () {
|
|
|
2305
2131
|
this.match += ch;
|
|
2306
2132
|
this.matched += ch;
|
|
2307
2133
|
var lines = ch.match(/(?:\r\n?|\n).*/g);
|
|
2308
|
-
|
|
2309
2134
|
if (lines) {
|
|
2310
2135
|
this.yylineno++;
|
|
2311
2136
|
this.yylloc.last_line++;
|
|
2312
2137
|
} else {
|
|
2313
2138
|
this.yylloc.last_column++;
|
|
2314
2139
|
}
|
|
2315
|
-
|
|
2316
2140
|
if (this.options.ranges) {
|
|
2317
2141
|
this.yylloc.range[1]++;
|
|
2318
2142
|
}
|
|
2319
|
-
|
|
2320
2143
|
this._input = this._input.slice(1);
|
|
2321
2144
|
return ch;
|
|
2322
2145
|
},
|
|
@@ -2325,17 +2148,15 @@ var parser = function () {
|
|
|
2325
2148
|
var len = ch.length;
|
|
2326
2149
|
var lines = ch.split(/(?:\r\n?|\n)/g);
|
|
2327
2150
|
this._input = ch + this._input;
|
|
2328
|
-
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
2329
|
-
|
|
2151
|
+
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
2152
|
+
//this.yyleng -= len;
|
|
2330
2153
|
this.offset -= len;
|
|
2331
2154
|
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
|
|
2332
2155
|
this.match = this.match.substr(0, this.match.length - 1);
|
|
2333
2156
|
this.matched = this.matched.substr(0, this.matched.length - 1);
|
|
2334
|
-
|
|
2335
2157
|
if (lines.length - 1) {
|
|
2336
2158
|
this.yylineno -= lines.length - 1;
|
|
2337
2159
|
}
|
|
2338
|
-
|
|
2339
2160
|
var r = this.yylloc.range;
|
|
2340
2161
|
this.yylloc = {
|
|
2341
2162
|
first_line: this.yylloc.first_line,
|
|
@@ -2343,11 +2164,9 @@ var parser = function () {
|
|
|
2343
2164
|
first_column: this.yylloc.first_column,
|
|
2344
2165
|
last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
|
|
2345
2166
|
};
|
|
2346
|
-
|
|
2347
2167
|
if (this.options.ranges) {
|
|
2348
2168
|
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
|
|
2349
2169
|
}
|
|
2350
|
-
|
|
2351
2170
|
this.yyleng = this.yytext.length;
|
|
2352
2171
|
return this;
|
|
2353
2172
|
},
|
|
@@ -2367,7 +2186,6 @@ var parser = function () {
|
|
|
2367
2186
|
line: this.yylineno
|
|
2368
2187
|
});
|
|
2369
2188
|
}
|
|
2370
|
-
|
|
2371
2189
|
return this;
|
|
2372
2190
|
},
|
|
2373
2191
|
// retain first n characters of the match
|
|
@@ -2382,11 +2200,9 @@ var parser = function () {
|
|
|
2382
2200
|
// displays upcoming input, i.e. for error messages
|
|
2383
2201
|
upcomingInput: function upcomingInput() {
|
|
2384
2202
|
var next = this.match;
|
|
2385
|
-
|
|
2386
2203
|
if (next.length < 20) {
|
|
2387
2204
|
next += this._input.substr(0, 20 - next.length);
|
|
2388
2205
|
}
|
|
2389
|
-
|
|
2390
2206
|
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
|
|
2391
2207
|
},
|
|
2392
2208
|
// displays the character position where the lexing error occurred, i.e. for error messages
|
|
@@ -2398,7 +2214,6 @@ var parser = function () {
|
|
|
2398
2214
|
// test the lexed token: return FALSE when not a match, otherwise return token
|
|
2399
2215
|
test_match: function test_match(match, indexed_rule) {
|
|
2400
2216
|
var token, lines, backup;
|
|
2401
|
-
|
|
2402
2217
|
if (this.options.backtrack_lexer) {
|
|
2403
2218
|
// save context
|
|
2404
2219
|
backup = {
|
|
@@ -2421,18 +2236,14 @@ var parser = function () {
|
|
|
2421
2236
|
conditionStack: this.conditionStack.slice(0),
|
|
2422
2237
|
done: this.done
|
|
2423
2238
|
};
|
|
2424
|
-
|
|
2425
2239
|
if (this.options.ranges) {
|
|
2426
2240
|
backup.yylloc.range = this.yylloc.range.slice(0);
|
|
2427
2241
|
}
|
|
2428
2242
|
}
|
|
2429
|
-
|
|
2430
2243
|
lines = match[0].match(/(?:\r\n?|\n).*/g);
|
|
2431
|
-
|
|
2432
2244
|
if (lines) {
|
|
2433
2245
|
this.yylineno += lines.length;
|
|
2434
2246
|
}
|
|
2435
|
-
|
|
2436
2247
|
this.yylloc = {
|
|
2437
2248
|
first_line: this.yylloc.last_line,
|
|
2438
2249
|
last_line: this.yylineno + 1,
|
|
@@ -2443,21 +2254,17 @@ var parser = function () {
|
|
|
2443
2254
|
this.match += match[0];
|
|
2444
2255
|
this.matches = match;
|
|
2445
2256
|
this.yyleng = this.yytext.length;
|
|
2446
|
-
|
|
2447
2257
|
if (this.options.ranges) {
|
|
2448
2258
|
this.yylloc.range = [this.offset, this.offset += this.yyleng];
|
|
2449
2259
|
}
|
|
2450
|
-
|
|
2451
2260
|
this._more = false;
|
|
2452
2261
|
this._backtrack = false;
|
|
2453
2262
|
this._input = this._input.slice(match[0].length);
|
|
2454
2263
|
this.matched += match[0];
|
|
2455
2264
|
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
|
|
2456
|
-
|
|
2457
2265
|
if (this.done && this._input) {
|
|
2458
2266
|
this.done = false;
|
|
2459
2267
|
}
|
|
2460
|
-
|
|
2461
2268
|
if (token) {
|
|
2462
2269
|
return token;
|
|
2463
2270
|
} else if (this._backtrack) {
|
|
@@ -2465,7 +2272,6 @@ var parser = function () {
|
|
|
2465
2272
|
for (var k in backup) {
|
|
2466
2273
|
this[k] = backup[k];
|
|
2467
2274
|
}
|
|
2468
|
-
|
|
2469
2275
|
return false; // rule action called reject() implying the next rule should be tested instead.
|
|
2470
2276
|
}
|
|
2471
2277
|
|
|
@@ -2476,30 +2282,22 @@ var parser = function () {
|
|
|
2476
2282
|
if (this.done) {
|
|
2477
2283
|
return this.EOF;
|
|
2478
2284
|
}
|
|
2479
|
-
|
|
2480
2285
|
if (!this._input) {
|
|
2481
2286
|
this.done = true;
|
|
2482
2287
|
}
|
|
2483
|
-
|
|
2484
2288
|
var token, match, tempMatch, index;
|
|
2485
|
-
|
|
2486
2289
|
if (!this._more) {
|
|
2487
2290
|
this.yytext = '';
|
|
2488
2291
|
this.match = '';
|
|
2489
2292
|
}
|
|
2490
|
-
|
|
2491
2293
|
var rules = this._currentRules();
|
|
2492
|
-
|
|
2493
2294
|
for (var i = 0; i < rules.length; i++) {
|
|
2494
2295
|
tempMatch = this._input.match(this.rules[rules[i]]);
|
|
2495
|
-
|
|
2496
2296
|
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
|
|
2497
2297
|
match = tempMatch;
|
|
2498
2298
|
index = i;
|
|
2499
|
-
|
|
2500
2299
|
if (this.options.backtrack_lexer) {
|
|
2501
2300
|
token = this.test_match(tempMatch, rules[i]);
|
|
2502
|
-
|
|
2503
2301
|
if (token !== false) {
|
|
2504
2302
|
return token;
|
|
2505
2303
|
} else if (this._backtrack) {
|
|
@@ -2514,18 +2312,14 @@ var parser = function () {
|
|
|
2514
2312
|
}
|
|
2515
2313
|
}
|
|
2516
2314
|
}
|
|
2517
|
-
|
|
2518
2315
|
if (match) {
|
|
2519
2316
|
token = this.test_match(match, rules[index]);
|
|
2520
|
-
|
|
2521
2317
|
if (token !== false) {
|
|
2522
2318
|
return token;
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2319
|
+
}
|
|
2320
|
+
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
2526
2321
|
return false;
|
|
2527
2322
|
}
|
|
2528
|
-
|
|
2529
2323
|
if (this._input === "") {
|
|
2530
2324
|
return this.EOF;
|
|
2531
2325
|
} else {
|
|
@@ -2539,7 +2333,6 @@ var parser = function () {
|
|
|
2539
2333
|
// return next match that has a token
|
|
2540
2334
|
lex: function lex() {
|
|
2541
2335
|
var r = this.next();
|
|
2542
|
-
|
|
2543
2336
|
if (r) {
|
|
2544
2337
|
return r;
|
|
2545
2338
|
} else {
|
|
@@ -2553,7 +2346,6 @@ var parser = function () {
|
|
|
2553
2346
|
// pop the previously active lexer condition state off the condition stack
|
|
2554
2347
|
popState: function popState() {
|
|
2555
2348
|
var n = this.conditionStack.length - 1;
|
|
2556
|
-
|
|
2557
2349
|
if (n > 0) {
|
|
2558
2350
|
return this.conditionStack.pop();
|
|
2559
2351
|
} else {
|
|
@@ -2571,7 +2363,6 @@ var parser = function () {
|
|
|
2571
2363
|
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
|
2572
2364
|
topState: function topState(n) {
|
|
2573
2365
|
n = this.conditionStack.length - 1 - Math.abs(n || 0);
|
|
2574
|
-
|
|
2575
2366
|
if (n >= 0) {
|
|
2576
2367
|
return this.conditionStack[n];
|
|
2577
2368
|
} else {
|
|
@@ -2590,297 +2381,212 @@ var parser = function () {
|
|
|
2590
2381
|
"flex": true
|
|
2591
2382
|
},
|
|
2592
2383
|
performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
|
|
2593
|
-
|
|
2594
2384
|
switch ($avoiding_name_collisions) {
|
|
2595
2385
|
case 0:
|
|
2596
2386
|
/* skip whitespace */
|
|
2597
2387
|
break;
|
|
2598
|
-
|
|
2599
2388
|
case 1:
|
|
2600
2389
|
/* skip \space */
|
|
2601
2390
|
break;
|
|
2602
|
-
|
|
2603
2391
|
case 2:
|
|
2604
2392
|
/* skip '\ ' */
|
|
2605
2393
|
break;
|
|
2606
|
-
|
|
2607
2394
|
case 3:
|
|
2608
2395
|
return "INT";
|
|
2609
|
-
|
|
2610
2396
|
case 4:
|
|
2611
2397
|
return "FLOAT";
|
|
2612
|
-
|
|
2613
2398
|
case 5:
|
|
2614
2399
|
return "^";
|
|
2615
|
-
|
|
2616
2400
|
case 6:
|
|
2617
2401
|
return "*";
|
|
2618
|
-
|
|
2619
2402
|
case 7:
|
|
2620
2403
|
return "*";
|
|
2621
|
-
|
|
2622
2404
|
case 8:
|
|
2623
2405
|
return "*";
|
|
2624
|
-
|
|
2625
2406
|
case 9:
|
|
2626
2407
|
return "*";
|
|
2627
|
-
|
|
2628
2408
|
case 10:
|
|
2629
2409
|
return "/";
|
|
2630
|
-
|
|
2631
2410
|
case 11:
|
|
2632
2411
|
return "/";
|
|
2633
|
-
|
|
2634
2412
|
case 12:
|
|
2635
2413
|
return "-";
|
|
2636
|
-
|
|
2637
2414
|
case 13:
|
|
2638
2415
|
return "-";
|
|
2639
|
-
|
|
2640
2416
|
case 14:
|
|
2641
2417
|
return "+";
|
|
2642
|
-
|
|
2643
2418
|
case 15:
|
|
2644
2419
|
return "^";
|
|
2645
|
-
|
|
2646
2420
|
case 16:
|
|
2647
2421
|
return "(";
|
|
2648
|
-
|
|
2649
2422
|
case 17:
|
|
2650
2423
|
return ")";
|
|
2651
|
-
|
|
2652
2424
|
case 18:
|
|
2653
2425
|
return "(";
|
|
2654
|
-
|
|
2655
2426
|
case 19:
|
|
2656
2427
|
return ")";
|
|
2657
|
-
|
|
2658
2428
|
case 20:
|
|
2659
2429
|
return "[";
|
|
2660
|
-
|
|
2661
2430
|
case 21:
|
|
2662
2431
|
return "]";
|
|
2663
|
-
|
|
2664
2432
|
case 22:
|
|
2665
2433
|
return "{";
|
|
2666
|
-
|
|
2667
2434
|
case 23:
|
|
2668
2435
|
return "}";
|
|
2669
|
-
|
|
2670
2436
|
case 24:
|
|
2671
2437
|
return "{";
|
|
2672
|
-
|
|
2673
2438
|
case 25:
|
|
2674
2439
|
return "}";
|
|
2675
|
-
|
|
2676
2440
|
case 26:
|
|
2677
2441
|
return "_";
|
|
2678
|
-
|
|
2679
2442
|
case 27:
|
|
2680
2443
|
return "|";
|
|
2681
|
-
|
|
2682
2444
|
case 28:
|
|
2683
2445
|
return "LEFT|";
|
|
2684
|
-
|
|
2685
2446
|
case 29:
|
|
2686
2447
|
return "RIGHT|";
|
|
2687
|
-
|
|
2688
2448
|
case 30:
|
|
2689
2449
|
return "!";
|
|
2690
|
-
|
|
2691
2450
|
case 31:
|
|
2692
2451
|
return "SIGN";
|
|
2693
|
-
|
|
2694
2452
|
case 32:
|
|
2695
2453
|
yy_.yytext = "<=";
|
|
2696
2454
|
return "SIGN";
|
|
2697
|
-
|
|
2698
2455
|
case 33:
|
|
2699
2456
|
yy_.yytext = ">=";
|
|
2700
2457
|
return "SIGN";
|
|
2701
|
-
|
|
2702
2458
|
case 34:
|
|
2703
2459
|
yy_.yytext = "<=";
|
|
2704
2460
|
return "SIGN";
|
|
2705
|
-
|
|
2706
2461
|
case 35:
|
|
2707
2462
|
yy_.yytext = ">=";
|
|
2708
2463
|
return "SIGN";
|
|
2709
|
-
|
|
2710
2464
|
case 36:
|
|
2711
2465
|
yy_.yytext = "<>";
|
|
2712
2466
|
return "SIGN";
|
|
2713
|
-
|
|
2714
2467
|
case 37:
|
|
2715
2468
|
yy_.yytext = "<>";
|
|
2716
2469
|
return "SIGN";
|
|
2717
|
-
|
|
2718
2470
|
case 38:
|
|
2719
2471
|
yy_.yytext = "<>";
|
|
2720
2472
|
return "SIGN";
|
|
2721
|
-
|
|
2722
2473
|
case 39:
|
|
2723
2474
|
yy_.yytext = "<>";
|
|
2724
2475
|
return "SIGN";
|
|
2725
|
-
|
|
2726
2476
|
case 40:
|
|
2727
2477
|
yy_.yytext = "<=";
|
|
2728
2478
|
return "SIGN";
|
|
2729
|
-
|
|
2730
2479
|
case 41:
|
|
2731
2480
|
yy_.yytext = ">=";
|
|
2732
2481
|
return "SIGN";
|
|
2733
|
-
|
|
2734
2482
|
case 42:
|
|
2735
2483
|
return "FRAC";
|
|
2736
|
-
|
|
2737
2484
|
case 43:
|
|
2738
2485
|
return "FRAC";
|
|
2739
|
-
|
|
2740
2486
|
case 44:
|
|
2741
2487
|
return "sqrt";
|
|
2742
|
-
|
|
2743
2488
|
case 45:
|
|
2744
2489
|
return "abs";
|
|
2745
|
-
|
|
2746
2490
|
case 46:
|
|
2747
2491
|
return "ln";
|
|
2748
|
-
|
|
2749
2492
|
case 47:
|
|
2750
2493
|
return "log";
|
|
2751
|
-
|
|
2752
2494
|
case 48:
|
|
2753
2495
|
return "TRIG";
|
|
2754
|
-
|
|
2755
2496
|
case 49:
|
|
2756
2497
|
return "TRIG";
|
|
2757
|
-
|
|
2758
2498
|
case 50:
|
|
2759
2499
|
return "TRIG";
|
|
2760
|
-
|
|
2761
2500
|
case 51:
|
|
2762
2501
|
return "TRIG";
|
|
2763
|
-
|
|
2764
2502
|
case 52:
|
|
2765
2503
|
yy_.yytext = "sin";
|
|
2766
2504
|
return "TRIG";
|
|
2767
|
-
|
|
2768
2505
|
case 53:
|
|
2769
2506
|
yy_.yytext = "cos";
|
|
2770
2507
|
return "TRIG";
|
|
2771
|
-
|
|
2772
2508
|
case 54:
|
|
2773
2509
|
yy_.yytext = "tan";
|
|
2774
2510
|
return "TRIG";
|
|
2775
|
-
|
|
2776
2511
|
case 55:
|
|
2777
2512
|
yy_.yytext = "csc";
|
|
2778
2513
|
return "TRIG";
|
|
2779
|
-
|
|
2780
2514
|
case 56:
|
|
2781
2515
|
yy_.yytext = "sec";
|
|
2782
2516
|
return "TRIG";
|
|
2783
|
-
|
|
2784
2517
|
case 57:
|
|
2785
2518
|
yy_.yytext = "cot";
|
|
2786
2519
|
return "TRIG";
|
|
2787
|
-
|
|
2788
2520
|
case 58:
|
|
2789
2521
|
yy_.yytext = "arcsin";
|
|
2790
2522
|
return "TRIG";
|
|
2791
|
-
|
|
2792
2523
|
case 59:
|
|
2793
2524
|
yy_.yytext = "arccos";
|
|
2794
2525
|
return "TRIG";
|
|
2795
|
-
|
|
2796
2526
|
case 60:
|
|
2797
2527
|
yy_.yytext = "arctan";
|
|
2798
2528
|
return "TRIG";
|
|
2799
|
-
|
|
2800
2529
|
case 61:
|
|
2801
2530
|
yy_.yytext = "arccsc";
|
|
2802
2531
|
return "TRIG";
|
|
2803
|
-
|
|
2804
2532
|
case 62:
|
|
2805
2533
|
yy_.yytext = "arcsec";
|
|
2806
2534
|
return "TRIG";
|
|
2807
|
-
|
|
2808
2535
|
case 63:
|
|
2809
2536
|
yy_.yytext = "arccot";
|
|
2810
2537
|
return "TRIG";
|
|
2811
|
-
|
|
2812
2538
|
case 64:
|
|
2813
2539
|
return "TRIGINV";
|
|
2814
|
-
|
|
2815
2540
|
case 65:
|
|
2816
2541
|
return "TRIGINV";
|
|
2817
|
-
|
|
2818
2542
|
case 66:
|
|
2819
2543
|
yy_.yytext = "sinh";
|
|
2820
2544
|
return "TRIG";
|
|
2821
|
-
|
|
2822
2545
|
case 67:
|
|
2823
2546
|
yy_.yytext = "cosh";
|
|
2824
2547
|
return "TRIG";
|
|
2825
|
-
|
|
2826
2548
|
case 68:
|
|
2827
2549
|
yy_.yytext = "tanh";
|
|
2828
2550
|
return "TRIG";
|
|
2829
|
-
|
|
2830
2551
|
case 69:
|
|
2831
2552
|
yy_.yytext = "csch";
|
|
2832
2553
|
return "TRIG";
|
|
2833
|
-
|
|
2834
2554
|
case 70:
|
|
2835
2555
|
yy_.yytext = "sech";
|
|
2836
2556
|
return "TRIG";
|
|
2837
|
-
|
|
2838
2557
|
case 71:
|
|
2839
2558
|
yy_.yytext = "tanh";
|
|
2840
2559
|
return "TRIG";
|
|
2841
|
-
|
|
2842
2560
|
case 72:
|
|
2843
2561
|
return "CONST";
|
|
2844
|
-
|
|
2845
2562
|
case 73:
|
|
2846
2563
|
yy_.yytext = "pi";
|
|
2847
2564
|
return "CONST";
|
|
2848
|
-
|
|
2849
2565
|
case 74:
|
|
2850
2566
|
yy_.yytext = "pi";
|
|
2851
2567
|
return "CONST";
|
|
2852
|
-
|
|
2853
2568
|
case 75:
|
|
2854
2569
|
return "VAR";
|
|
2855
|
-
|
|
2856
2570
|
case 76:
|
|
2857
2571
|
yy_.yytext = "theta";
|
|
2858
2572
|
return "VAR";
|
|
2859
|
-
|
|
2860
2573
|
case 77:
|
|
2861
2574
|
yy_.yytext = "theta";
|
|
2862
2575
|
return "VAR";
|
|
2863
|
-
|
|
2864
2576
|
case 78:
|
|
2865
2577
|
return "VAR";
|
|
2866
|
-
|
|
2867
2578
|
case 79:
|
|
2868
2579
|
yy_.yytext = "phi";
|
|
2869
2580
|
return "VAR";
|
|
2870
|
-
|
|
2871
2581
|
case 80:
|
|
2872
2582
|
yy_.yytext = "phi";
|
|
2873
2583
|
return "VAR";
|
|
2874
|
-
|
|
2875
2584
|
case 81:
|
|
2876
2585
|
return yy.symbolLexer(yy_.yytext);
|
|
2877
|
-
|
|
2878
2586
|
case 82:
|
|
2879
2587
|
return "EOF";
|
|
2880
|
-
|
|
2881
2588
|
case 83:
|
|
2882
2589
|
return "INVALID";
|
|
2883
|
-
|
|
2884
2590
|
case 84:
|
|
2885
2591
|
console.log(yy_.yytext);
|
|
2886
2592
|
break;
|
|
@@ -2896,19 +2602,17 @@ var parser = function () {
|
|
|
2896
2602
|
};
|
|
2897
2603
|
return lexer;
|
|
2898
2604
|
}();
|
|
2899
|
-
|
|
2900
2605
|
parser.lexer = lexer;
|
|
2901
|
-
|
|
2902
2606
|
function Parser() {
|
|
2903
2607
|
this.yy = {};
|
|
2904
2608
|
}
|
|
2905
|
-
|
|
2906
2609
|
Parser.prototype = parser;
|
|
2907
2610
|
parser.Parser = Parser;
|
|
2908
2611
|
return new Parser();
|
|
2909
2612
|
}();
|
|
2910
2613
|
|
|
2911
2614
|
/* eslint-disable prettier/prettier */
|
|
2615
|
+
|
|
2912
2616
|
/* The node hierarcy is as follows:
|
|
2913
2617
|
|
|
2914
2618
|
(Expr)
|
|
@@ -2939,40 +2643,38 @@ var parser = function () {
|
|
|
2939
2643
|
*/
|
|
2940
2644
|
|
|
2941
2645
|
/* non user-facing functions */
|
|
2942
|
-
// assert that all abstract methods have been overridden
|
|
2943
2646
|
|
|
2647
|
+
// assert that all abstract methods have been overridden
|
|
2944
2648
|
var abstract = function abstract() {
|
|
2945
2649
|
// Try to give people a bit of information when this happens
|
|
2946
|
-
throw new Error("Abstract method - must override for expr: " +
|
|
2650
|
+
throw new Error("Abstract method - must override for expr: " +
|
|
2651
|
+
// eslint-disable-next-line @babel/no-invalid-this
|
|
2947
2652
|
this.print());
|
|
2948
|
-
};
|
|
2949
|
-
|
|
2653
|
+
};
|
|
2950
2654
|
|
|
2655
|
+
// throw an error that is meant to be caught by the test suite (not user facing)
|
|
2951
2656
|
var error = function error(message) {
|
|
2952
2657
|
throw new Error(message);
|
|
2953
|
-
};
|
|
2954
|
-
|
|
2658
|
+
};
|
|
2955
2659
|
|
|
2660
|
+
// reliably detect NaN
|
|
2956
2661
|
var isNaN = function isNaN(object) {
|
|
2957
2662
|
return object !== object;
|
|
2958
|
-
};
|
|
2959
|
-
// not that inclusivity means much, probabilistically, on floats
|
|
2960
|
-
|
|
2663
|
+
};
|
|
2961
2664
|
|
|
2665
|
+
// return a random float between min (inclusive) and max (exclusive),
|
|
2666
|
+
// not that inclusivity means much, probabilistically, on floats
|
|
2962
2667
|
var randomFloat = function randomFloat(min, max) {
|
|
2963
2668
|
var extent = max - min;
|
|
2964
2669
|
return Math.random() * extent + min;
|
|
2965
2670
|
};
|
|
2966
|
-
/* constants */
|
|
2967
|
-
|
|
2968
2671
|
|
|
2672
|
+
/* constants */
|
|
2969
2673
|
var ITERATIONS = 12;
|
|
2970
2674
|
var TOLERANCE = 9; // decimal places
|
|
2971
2675
|
|
|
2972
2676
|
/* abstract base expression node */
|
|
2973
|
-
|
|
2974
2677
|
function Expr() {}
|
|
2975
|
-
|
|
2976
2678
|
_.extend(Expr.prototype, {
|
|
2977
2679
|
// this node's immediate constructor
|
|
2978
2680
|
func: abstract,
|
|
@@ -2987,11 +2689,9 @@ _.extend(Expr.prototype, {
|
|
|
2987
2689
|
// an abstraction for chainable, bottom-up recursion
|
|
2988
2690
|
recurse: function recurse(method) {
|
|
2989
2691
|
var passed = Array.prototype.slice.call(arguments, 1);
|
|
2990
|
-
|
|
2991
2692
|
var args = _.map(this.args(), function (arg) {
|
|
2992
2693
|
return _.isString(arg) ? arg : arg[method].apply(arg, passed);
|
|
2993
2694
|
});
|
|
2994
|
-
|
|
2995
2695
|
return this.construct(args);
|
|
2996
2696
|
},
|
|
2997
2697
|
// evaluate numerically with given variable mapping
|
|
@@ -2999,7 +2699,6 @@ _.extend(Expr.prototype, {
|
|
|
2999
2699
|
codegen: abstract,
|
|
3000
2700
|
compile: function compile() {
|
|
3001
2701
|
var code = this.codegen();
|
|
3002
|
-
|
|
3003
2702
|
try {
|
|
3004
2703
|
return new Function("vars", "return " + code + ";");
|
|
3005
2704
|
} catch (e) {
|
|
@@ -3015,28 +2714,22 @@ _.extend(Expr.prototype, {
|
|
|
3015
2714
|
// returns a TeX string, modified by the given options
|
|
3016
2715
|
asTex: function asTex(options) {
|
|
3017
2716
|
options = options || {};
|
|
3018
|
-
|
|
3019
2717
|
_.defaults(options, {
|
|
3020
2718
|
display: true,
|
|
3021
2719
|
dynamic: true,
|
|
3022
2720
|
times: false
|
|
3023
2721
|
});
|
|
3024
|
-
|
|
3025
2722
|
var tex = this.tex();
|
|
3026
|
-
|
|
3027
2723
|
if (options.display) {
|
|
3028
2724
|
tex = "\\displaystyle " + tex;
|
|
3029
2725
|
}
|
|
3030
|
-
|
|
3031
2726
|
if (options.dynamic) {
|
|
3032
2727
|
tex = tex.replace(/\(/g, "\\left(");
|
|
3033
2728
|
tex = tex.replace(/\)/g, "\\right)");
|
|
3034
2729
|
}
|
|
3035
|
-
|
|
3036
2730
|
if (options.times) {
|
|
3037
2731
|
tex = tex.replace(/\\cdot/g, "\\times");
|
|
3038
2732
|
}
|
|
3039
|
-
|
|
3040
2733
|
return tex;
|
|
3041
2734
|
},
|
|
3042
2735
|
// returns the name of this expression's constructor as a string
|
|
@@ -3082,26 +2775,28 @@ _.extend(Expr.prototype, {
|
|
|
3082
2775
|
simplify: function simplify(options) {
|
|
3083
2776
|
options = _.extend({
|
|
3084
2777
|
once: false
|
|
3085
|
-
}, options);
|
|
2778
|
+
}, options);
|
|
3086
2779
|
|
|
2780
|
+
// Attempt to factor and collect
|
|
3087
2781
|
var step1 = this.factor(options);
|
|
3088
|
-
var step2 = step1.collect(options);
|
|
2782
|
+
var step2 = step1.collect(options);
|
|
3089
2783
|
|
|
2784
|
+
// Rollback if collect didn't do anything
|
|
3090
2785
|
if (step1.equals(step2)) {
|
|
3091
2786
|
step2 = this.collect(options);
|
|
3092
|
-
}
|
|
3093
|
-
|
|
2787
|
+
}
|
|
3094
2788
|
|
|
2789
|
+
// Attempt to expand and collect
|
|
3095
2790
|
var step3 = step2.expand(options);
|
|
3096
|
-
var step4 = step3.collect(options);
|
|
2791
|
+
var step4 = step3.collect(options);
|
|
3097
2792
|
|
|
2793
|
+
// Rollback if collect didn't do anything
|
|
3098
2794
|
if (step3.equals(step4)) {
|
|
3099
2795
|
step4 = step2.collect(options);
|
|
3100
|
-
}
|
|
3101
|
-
|
|
2796
|
+
}
|
|
3102
2797
|
|
|
2798
|
+
// One round of simplification complete
|
|
3103
2799
|
var simplified = step4;
|
|
3104
|
-
|
|
3105
2800
|
if (options.once || this.equals(simplified)) {
|
|
3106
2801
|
return simplified;
|
|
3107
2802
|
} else {
|
|
@@ -3137,7 +2832,6 @@ _.extend(Expr.prototype, {
|
|
|
3137
2832
|
if (this instanceof func) {
|
|
3138
2833
|
return true;
|
|
3139
2834
|
}
|
|
3140
|
-
|
|
3141
2835
|
return _.any(this.exprArgs(), function (arg) {
|
|
3142
2836
|
return arg.has(func);
|
|
3143
2837
|
});
|
|
@@ -3165,17 +2859,16 @@ _.extend(Expr.prototype, {
|
|
|
3165
2859
|
// check that the variables in both expressions are the same
|
|
3166
2860
|
sameVars: function sameVars(other) {
|
|
3167
2861
|
var vars1 = this.getVars();
|
|
3168
|
-
var vars2 = other.getVars();
|
|
3169
|
-
// this lets you multiply equations by other variables
|
|
2862
|
+
var vars2 = other.getVars();
|
|
3170
2863
|
|
|
2864
|
+
// the other Expr can have more variables than this one
|
|
2865
|
+
// this lets you multiply equations by other variables
|
|
3171
2866
|
var same = function same(array1, array2) {
|
|
3172
2867
|
return !_.difference(array1, array2).length;
|
|
3173
2868
|
};
|
|
3174
|
-
|
|
3175
2869
|
var lower = function lower(array) {
|
|
3176
2870
|
return _.uniq(_.invoke(array, "toLowerCase")).sort();
|
|
3177
2871
|
};
|
|
3178
|
-
|
|
3179
2872
|
var equal = same(vars1, vars2);
|
|
3180
2873
|
var equalIgnoringCase = same(lower(vars1), lower(vars2));
|
|
3181
2874
|
return {
|
|
@@ -3191,16 +2884,11 @@ _.extend(Expr.prototype, {
|
|
|
3191
2884
|
if (other instanceof Eq) {
|
|
3192
2885
|
return false;
|
|
3193
2886
|
}
|
|
2887
|
+
var varList = _.union(this.getVars( /* excludeFunc */true), other.getVars( /* excludeFunc */true));
|
|
3194
2888
|
|
|
3195
|
-
|
|
3196
|
-
/* excludeFunc */
|
|
3197
|
-
true), other.getVars(
|
|
3198
|
-
/* excludeFunc */
|
|
3199
|
-
true)); // If the numbers are large we would like to do a relative comparison
|
|
2889
|
+
// If the numbers are large we would like to do a relative comparison
|
|
3200
2890
|
// rather than an absolute one, but if they're small enough then an
|
|
3201
2891
|
// absolute comparison makes more sense
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
2892
|
var getDelta = function getDelta(num1, num2) {
|
|
3205
2893
|
if (Math.abs(num1) < 1 || Math.abs(num2) < 1) {
|
|
3206
2894
|
return Math.abs(num1 - num2);
|
|
@@ -3208,30 +2896,28 @@ _.extend(Expr.prototype, {
|
|
|
3208
2896
|
return Math.abs(1 - num1 / num2);
|
|
3209
2897
|
}
|
|
3210
2898
|
};
|
|
3211
|
-
|
|
3212
2899
|
var equalNumbers = function equalNumbers(num1, num2) {
|
|
3213
2900
|
var delta = getDelta(num1, num2);
|
|
3214
|
-
return num1 === num2 ||
|
|
3215
|
-
|
|
3216
|
-
isNaN(num1) && isNaN(num2) || delta < Math.pow(10, -TOLERANCE);
|
|
3217
|
-
}; // if no variables, only need to evaluate once
|
|
3218
|
-
|
|
2901
|
+
return num1 === num2 /* needed if either is +/- Infinity */ || isNaN(num1) && isNaN(num2) || delta < Math.pow(10, -TOLERANCE);
|
|
2902
|
+
};
|
|
3219
2903
|
|
|
2904
|
+
// if no variables, only need to evaluate once
|
|
3220
2905
|
if (!varList.length && !this.has(Unit) && !other.has(Unit)) {
|
|
3221
2906
|
return equalNumbers(this.eval(), other.eval());
|
|
3222
|
-
}
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
// collect here to avoid sometimes dividing by zero, and sometimes not
|
|
3223
2910
|
// it is better to be deterministic, e.g. x/x -> 1
|
|
3224
2911
|
// TODO(alex): may want to keep track of assumptions as they're made
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
2912
|
var expr1 = this.collect();
|
|
3228
2913
|
var expr2 = other.collect();
|
|
3229
2914
|
var unitList1 = this.getUnits();
|
|
3230
2915
|
var unitList2 = other.getUnits();
|
|
3231
|
-
|
|
3232
2916
|
if (!_.isEqual(unitList1, unitList2)) {
|
|
3233
2917
|
return false;
|
|
3234
|
-
}
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
// Compare at a set number (currently 12) of points to determine
|
|
3235
2921
|
// equality.
|
|
3236
2922
|
//
|
|
3237
2923
|
// `range` (and `vars`) is the only variable that varies through the
|
|
@@ -3240,12 +2926,13 @@ _.extend(Expr.prototype, {
|
|
|
3240
2926
|
//
|
|
3241
2927
|
// Note that because there are 12 iterations and three ranges, each
|
|
3242
2928
|
// range is checked four times.
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
2929
|
for (var i = 0; i < ITERATIONS; i++) {
|
|
3246
|
-
var vars = {};
|
|
2930
|
+
var vars = {};
|
|
3247
2931
|
|
|
3248
|
-
|
|
2932
|
+
// One third total iterations each with range 10, 100, and 1000
|
|
2933
|
+
var range = Math.pow(10, 1 + Math.floor(3 * i / ITERATIONS));
|
|
2934
|
+
|
|
2935
|
+
// Half of the iterations should only use integer values.
|
|
3249
2936
|
// This is because expressions like (-2)^x are common but result
|
|
3250
2937
|
// in NaN when evaluated in JS with non-integer values of x.
|
|
3251
2938
|
// Without this, (-2)^x and (-2)^(x+1) both end up always being NaN
|
|
@@ -3259,15 +2946,11 @@ _.extend(Expr.prototype, {
|
|
|
3259
2946
|
// Integrate a library for handling complex numbers.
|
|
3260
2947
|
//
|
|
3261
2948
|
// TODO(alex): Add support for complex numbers, then remove this.
|
|
3262
|
-
|
|
3263
2949
|
var useFloats = i % 2 === 0;
|
|
3264
|
-
|
|
3265
2950
|
_.each(varList, function (v) {
|
|
3266
2951
|
vars[v] = useFloats ? randomFloat(-range, range) : _.random(-range, range);
|
|
3267
2952
|
});
|
|
3268
|
-
|
|
3269
2953
|
var equal;
|
|
3270
|
-
|
|
3271
2954
|
if (expr1.has(Func) || expr2.has(Func) || expr1.has(Unit) || expr2.has(Unit)) {
|
|
3272
2955
|
var result1 = expr1.partialEval(vars);
|
|
3273
2956
|
var result2 = expr2.partialEval(vars);
|
|
@@ -3277,12 +2960,10 @@ _.extend(Expr.prototype, {
|
|
|
3277
2960
|
var result2 = expr2.eval(vars);
|
|
3278
2961
|
equal = equalNumbers(result1, result2);
|
|
3279
2962
|
}
|
|
3280
|
-
|
|
3281
2963
|
if (!equal) {
|
|
3282
2964
|
return false;
|
|
3283
2965
|
}
|
|
3284
2966
|
}
|
|
3285
|
-
|
|
3286
2967
|
return true;
|
|
3287
2968
|
},
|
|
3288
2969
|
// evaluate as much of the expression as possible
|
|
@@ -3333,7 +3014,6 @@ _.extend(Expr.prototype, {
|
|
|
3333
3014
|
if (!hint) {
|
|
3334
3015
|
return this;
|
|
3335
3016
|
}
|
|
3336
|
-
|
|
3337
3017
|
var expr = this.construct(this.args());
|
|
3338
3018
|
expr.hints = _.clone(this.hints);
|
|
3339
3019
|
expr.hints[hint] = true;
|
|
@@ -3355,13 +3035,10 @@ _.extend(Expr.prototype, {
|
|
|
3355
3035
|
return new Mul(Num.Neg, this);
|
|
3356
3036
|
}
|
|
3357
3037
|
});
|
|
3358
|
-
/* abstract sequence node */
|
|
3359
|
-
|
|
3360
3038
|
|
|
3039
|
+
/* abstract sequence node */
|
|
3361
3040
|
function Seq() {}
|
|
3362
|
-
|
|
3363
3041
|
Seq.prototype = new Expr();
|
|
3364
|
-
|
|
3365
3042
|
_.extend(Seq.prototype, {
|
|
3366
3043
|
args: function args() {
|
|
3367
3044
|
return this.terms;
|
|
@@ -3370,7 +3047,6 @@ _.extend(Seq.prototype, {
|
|
|
3370
3047
|
var terms = _.sortBy(_.invoke(this.terms, "normalize"), function (term) {
|
|
3371
3048
|
return term.print();
|
|
3372
3049
|
});
|
|
3373
|
-
|
|
3374
3050
|
return new this.func(terms);
|
|
3375
3051
|
},
|
|
3376
3052
|
expand: function expand() {
|
|
@@ -3381,11 +3057,11 @@ _.extend(Seq.prototype, {
|
|
|
3381
3057
|
partition: function partition() {
|
|
3382
3058
|
var terms = _.groupBy(this.terms, function (term) {
|
|
3383
3059
|
return term instanceof Num;
|
|
3384
|
-
});
|
|
3060
|
+
});
|
|
3061
|
+
|
|
3062
|
+
// XXX using a boolean as a key just converts it to a string. I don't
|
|
3385
3063
|
// think this code was written with that in mind. Probably doesn't
|
|
3386
3064
|
// matter except for readability.
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
3065
|
var numbers = terms[true] || [];
|
|
3390
3066
|
var others = terms[false] || [];
|
|
3391
3067
|
return [new this.func(numbers), new this.func(others)];
|
|
@@ -3394,29 +3070,23 @@ _.extend(Seq.prototype, {
|
|
|
3394
3070
|
// this is a shallow flattening and will return a non-Seq if terms.length <= 1
|
|
3395
3071
|
flatten: function flatten() {
|
|
3396
3072
|
var type = this;
|
|
3397
|
-
|
|
3398
3073
|
var terms = _.reject(this.terms, function (term) {
|
|
3399
3074
|
return term.equals(type.identity);
|
|
3400
3075
|
});
|
|
3401
|
-
|
|
3402
3076
|
if (terms.length === 0) {
|
|
3403
3077
|
return type.identity;
|
|
3404
3078
|
}
|
|
3405
|
-
|
|
3406
3079
|
if (terms.length === 1) {
|
|
3407
3080
|
return terms[0];
|
|
3408
3081
|
}
|
|
3409
|
-
|
|
3410
3082
|
var grouped = _.groupBy(terms, function (term) {
|
|
3411
3083
|
return term instanceof type.func;
|
|
3412
|
-
});
|
|
3413
|
-
|
|
3084
|
+
});
|
|
3414
3085
|
|
|
3086
|
+
// same contains the children which are Seqs of the same type as this Seq
|
|
3415
3087
|
var same = grouped[true] || [];
|
|
3416
3088
|
var others = grouped[false] || [];
|
|
3417
|
-
var flattened = others.concat(_.flatten(_.pluck(same, "terms"),
|
|
3418
|
-
/* shallow: */
|
|
3419
|
-
true));
|
|
3089
|
+
var flattened = others.concat(_.flatten(_.pluck(same, "terms"), /* shallow: */true));
|
|
3420
3090
|
return new type.func(flattened);
|
|
3421
3091
|
},
|
|
3422
3092
|
// the identity associated with the sequence
|
|
@@ -3425,7 +3095,6 @@ _.extend(Seq.prototype, {
|
|
|
3425
3095
|
reduce: abstract,
|
|
3426
3096
|
isPositive: function isPositive() {
|
|
3427
3097
|
var terms = _.invoke(this.terms, "collect");
|
|
3428
|
-
|
|
3429
3098
|
return _.all(_.invoke(terms, "isPositive"));
|
|
3430
3099
|
},
|
|
3431
3100
|
// return a new Seq with a given term replaced by a different term
|
|
@@ -3433,21 +3102,17 @@ _.extend(Seq.prototype, {
|
|
|
3433
3102
|
// if no new term is provided, the old one is simply removed
|
|
3434
3103
|
replace: function replace(oldTerm, newTerm) {
|
|
3435
3104
|
var index;
|
|
3436
|
-
|
|
3437
3105
|
if (oldTerm instanceof Expr) {
|
|
3438
3106
|
index = _.indexOf(this.terms, oldTerm);
|
|
3439
3107
|
} else {
|
|
3440
3108
|
index = oldTerm;
|
|
3441
3109
|
}
|
|
3442
|
-
|
|
3443
3110
|
var newTerms = [];
|
|
3444
|
-
|
|
3445
3111
|
if (_.isArray(newTerm)) {
|
|
3446
3112
|
newTerms = newTerm;
|
|
3447
3113
|
} else if (newTerm) {
|
|
3448
3114
|
newTerms = [newTerm];
|
|
3449
3115
|
}
|
|
3450
|
-
|
|
3451
3116
|
var terms = this.terms.slice(0, index).concat(newTerms).concat(this.terms.slice(index + 1));
|
|
3452
3117
|
return new this.func(terms);
|
|
3453
3118
|
},
|
|
@@ -3460,9 +3125,8 @@ _.extend(Seq.prototype, {
|
|
|
3460
3125
|
return new Mul(_.invoke(this.terms, "getDenominator")).flatten();
|
|
3461
3126
|
}
|
|
3462
3127
|
});
|
|
3463
|
-
/* sequence of additive terms */
|
|
3464
|
-
|
|
3465
3128
|
|
|
3129
|
+
/* sequence of additive terms */
|
|
3466
3130
|
function Add() {
|
|
3467
3131
|
if (arguments.length === 1) {
|
|
3468
3132
|
this.terms = arguments[0];
|
|
@@ -3471,7 +3135,6 @@ function Add() {
|
|
|
3471
3135
|
}
|
|
3472
3136
|
}
|
|
3473
3137
|
Add.prototype = new Seq();
|
|
3474
|
-
|
|
3475
3138
|
_.extend(Add.prototype, {
|
|
3476
3139
|
func: Add,
|
|
3477
3140
|
eval: function _eval(vars, options) {
|
|
@@ -3489,7 +3152,6 @@ _.extend(Add.prototype, {
|
|
|
3489
3152
|
},
|
|
3490
3153
|
tex: function tex() {
|
|
3491
3154
|
var tex = "";
|
|
3492
|
-
|
|
3493
3155
|
_.each(this.terms, function (term) {
|
|
3494
3156
|
if (!tex || term.isSubtract()) {
|
|
3495
3157
|
tex += term.tex();
|
|
@@ -3497,15 +3159,13 @@ _.extend(Add.prototype, {
|
|
|
3497
3159
|
tex += "+" + term.tex();
|
|
3498
3160
|
}
|
|
3499
3161
|
});
|
|
3500
|
-
|
|
3501
3162
|
return tex;
|
|
3502
3163
|
},
|
|
3503
3164
|
collect: function collect(options) {
|
|
3504
|
-
var terms = _.invoke(this.terms, "collect", options);
|
|
3505
|
-
|
|
3165
|
+
var terms = _.invoke(this.terms, "collect", options);
|
|
3506
3166
|
|
|
3167
|
+
// [Expr expr, Num coefficient]
|
|
3507
3168
|
var pairs = [];
|
|
3508
|
-
|
|
3509
3169
|
_.each(terms, function (term) {
|
|
3510
3170
|
if (term instanceof Mul) {
|
|
3511
3171
|
var muls = term.partition();
|
|
@@ -3515,21 +3175,21 @@ _.extend(Add.prototype, {
|
|
|
3515
3175
|
} else {
|
|
3516
3176
|
pairs.push([term, Num.One]);
|
|
3517
3177
|
}
|
|
3518
|
-
});
|
|
3519
|
-
|
|
3178
|
+
});
|
|
3520
3179
|
|
|
3180
|
+
// { (Expr expr).print(): [[Expr expr, Num coefficient]] }
|
|
3521
3181
|
var grouped = _.groupBy(pairs, function (pair) {
|
|
3522
3182
|
return pair[0].normalize().print();
|
|
3523
3183
|
});
|
|
3524
|
-
|
|
3525
3184
|
var collected = _.compact(_.map(grouped, function (pairs) {
|
|
3526
3185
|
var expr = pairs[0][0];
|
|
3527
3186
|
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
3528
3187
|
var coefficient = sum.reduce(options);
|
|
3529
3188
|
return new Mul(coefficient, expr).collect(options);
|
|
3530
|
-
}));
|
|
3531
|
-
// e.g. x*sin^2(y) + x*cos^2(y) -> x
|
|
3189
|
+
}));
|
|
3532
3190
|
|
|
3191
|
+
// TODO(alex): use the Pythagorean identity here
|
|
3192
|
+
// e.g. x*sin^2(y) + x*cos^2(y) -> x
|
|
3533
3193
|
|
|
3534
3194
|
return new Add(collected).flatten();
|
|
3535
3195
|
},
|
|
@@ -3539,33 +3199,25 @@ _.extend(Add.prototype, {
|
|
|
3539
3199
|
options = _.extend({
|
|
3540
3200
|
keepNegative: false
|
|
3541
3201
|
}, options);
|
|
3542
|
-
|
|
3543
3202
|
var terms = _.invoke(this.terms, "collect");
|
|
3544
|
-
|
|
3545
3203
|
var factors;
|
|
3546
|
-
|
|
3547
3204
|
if (terms[0] instanceof Mul) {
|
|
3548
3205
|
factors = terms[0].terms;
|
|
3549
3206
|
} else {
|
|
3550
3207
|
factors = [terms[0]];
|
|
3551
3208
|
}
|
|
3552
|
-
|
|
3553
3209
|
_.each(_.rest(this.terms), function (term) {
|
|
3554
3210
|
factors = _.map(factors, function (factor) {
|
|
3555
3211
|
return term.findGCD(factor);
|
|
3556
3212
|
});
|
|
3557
3213
|
});
|
|
3558
|
-
|
|
3559
3214
|
if (!options.keepNegative && this.isNegative()) {
|
|
3560
3215
|
factors.push(Num.Neg);
|
|
3561
3216
|
}
|
|
3562
|
-
|
|
3563
3217
|
factors = new Mul(factors).flatten().collect();
|
|
3564
|
-
|
|
3565
3218
|
var remainder = _.map(terms, function (term) {
|
|
3566
3219
|
return Mul.handleDivide(term, factors).simplify();
|
|
3567
3220
|
});
|
|
3568
|
-
|
|
3569
3221
|
remainder = new Add(remainder).flatten();
|
|
3570
3222
|
return Mul.createOrAppend(factors, remainder).flatten();
|
|
3571
3223
|
},
|
|
@@ -3579,16 +3231,14 @@ _.extend(Add.prototype, {
|
|
|
3579
3231
|
},
|
|
3580
3232
|
isNegative: function isNegative() {
|
|
3581
3233
|
var terms = _.invoke(this.terms, "collect");
|
|
3582
|
-
|
|
3583
3234
|
return _.all(_.invoke(terms, "isNegative"));
|
|
3584
3235
|
},
|
|
3585
3236
|
negate: function negate() {
|
|
3586
3237
|
return new Add(_.invoke(this.terms, "negate"));
|
|
3587
3238
|
}
|
|
3588
3239
|
});
|
|
3589
|
-
/* sequence of multiplicative terms */
|
|
3590
|
-
|
|
3591
3240
|
|
|
3241
|
+
/* sequence of multiplicative terms */
|
|
3592
3242
|
function Mul() {
|
|
3593
3243
|
if (arguments.length === 1) {
|
|
3594
3244
|
this.terms = arguments[0];
|
|
@@ -3597,7 +3247,6 @@ function Mul() {
|
|
|
3597
3247
|
}
|
|
3598
3248
|
}
|
|
3599
3249
|
Mul.prototype = new Seq();
|
|
3600
|
-
|
|
3601
3250
|
_.extend(Mul.prototype, {
|
|
3602
3251
|
func: Mul,
|
|
3603
3252
|
eval: function _eval(vars, options) {
|
|
@@ -3619,7 +3268,6 @@ _.extend(Mul.prototype, {
|
|
|
3619
3268
|
var tmUnits = _(this.terms).chain().map(function (term) {
|
|
3620
3269
|
return term.getUnits();
|
|
3621
3270
|
}).flatten().value();
|
|
3622
|
-
|
|
3623
3271
|
tmUnits.sort((a, b) => a.unit.localeCompare(b.unit));
|
|
3624
3272
|
return tmUnits;
|
|
3625
3273
|
},
|
|
@@ -3627,7 +3275,6 @@ _.extend(Mul.prototype, {
|
|
|
3627
3275
|
// so we follow convention: first any negatives, then any numbers, then everything else
|
|
3628
3276
|
tex: function tex() {
|
|
3629
3277
|
var cdot = " \\cdot ";
|
|
3630
|
-
|
|
3631
3278
|
var terms = _.groupBy(this.terms, function (term) {
|
|
3632
3279
|
if (term.isDivide()) {
|
|
3633
3280
|
return "inverse";
|
|
@@ -3637,18 +3284,17 @@ _.extend(Mul.prototype, {
|
|
|
3637
3284
|
return "other";
|
|
3638
3285
|
}
|
|
3639
3286
|
});
|
|
3640
|
-
|
|
3641
3287
|
var inverses = terms.inverse || [];
|
|
3642
3288
|
var numbers = terms.number || [];
|
|
3643
3289
|
var others = terms.other || [];
|
|
3644
3290
|
var negatives = "";
|
|
3645
|
-
var numerator;
|
|
3291
|
+
var numerator;
|
|
3292
|
+
|
|
3293
|
+
// check all the numbers to see if there is a rational we can extract,
|
|
3646
3294
|
// since we would like 1/2x/y to come out as \frac{1}{2}\frac{x}{y},
|
|
3647
3295
|
// and not \frac{1x}{2y}.
|
|
3648
|
-
|
|
3649
3296
|
for (var i = 0; i < numbers.length; i++) {
|
|
3650
3297
|
var isRational = numbers[i] instanceof Rational && !(numbers[i] instanceof Int);
|
|
3651
|
-
|
|
3652
3298
|
if (isRational && others.length > 0 && inverses.length > 0) {
|
|
3653
3299
|
var withThisRemoved = numbers.slice();
|
|
3654
3300
|
withThisRemoved.splice(i, 1);
|
|
@@ -3656,11 +3302,9 @@ _.extend(Mul.prototype, {
|
|
|
3656
3302
|
return numbers[i].tex() + new Mul(newTerms).tex();
|
|
3657
3303
|
}
|
|
3658
3304
|
}
|
|
3659
|
-
|
|
3660
3305
|
numbers = _.compact(_.map(numbers, function (term) {
|
|
3661
3306
|
var hasDenom = term instanceof Rational && !(term instanceof Int);
|
|
3662
3307
|
var shouldPushDown = !term.hints.fraction || inverses.length > 0;
|
|
3663
|
-
|
|
3664
3308
|
if (hasDenom && shouldPushDown) {
|
|
3665
3309
|
// e.g. 3x/4 -> 3/4*x (internally) -> 3x/4 (rendered)
|
|
3666
3310
|
inverses.push(new Pow(new Int(term.d), Num.Div));
|
|
@@ -3671,13 +3315,11 @@ _.extend(Mul.prototype, {
|
|
|
3671
3315
|
return term;
|
|
3672
3316
|
}
|
|
3673
3317
|
}));
|
|
3674
|
-
|
|
3675
3318
|
if (numbers.length === 0 && others.length === 1) {
|
|
3676
3319
|
// e.g. (x+y)/z -> \frac{x+y}{z}
|
|
3677
3320
|
numerator = others[0].tex();
|
|
3678
3321
|
} else {
|
|
3679
3322
|
var tex = "";
|
|
3680
|
-
|
|
3681
3323
|
_.each(numbers, function (term) {
|
|
3682
3324
|
if (term.hints.subtract && term.hints.entered) {
|
|
3683
3325
|
negatives += "-";
|
|
@@ -3691,7 +3333,6 @@ _.extend(Mul.prototype, {
|
|
|
3691
3333
|
tex += (tex ? cdot : "") + term.tex();
|
|
3692
3334
|
}
|
|
3693
3335
|
});
|
|
3694
|
-
|
|
3695
3336
|
_.each(others, function (term) {
|
|
3696
3337
|
if (term.needsExplicitMul()) {
|
|
3697
3338
|
// e.g. 2*2^3 -> 2(dot)2^3
|
|
@@ -3704,10 +3345,8 @@ _.extend(Mul.prototype, {
|
|
|
3704
3345
|
tex += term.tex();
|
|
3705
3346
|
}
|
|
3706
3347
|
});
|
|
3707
|
-
|
|
3708
3348
|
numerator = tex ? tex : "1";
|
|
3709
3349
|
}
|
|
3710
|
-
|
|
3711
3350
|
if (!inverses.length) {
|
|
3712
3351
|
return negatives + numerator;
|
|
3713
3352
|
} else {
|
|
@@ -3719,7 +3358,6 @@ _.extend(Mul.prototype, {
|
|
|
3719
3358
|
var terms = _.map(this.terms, function (term) {
|
|
3720
3359
|
return term instanceof Num ? term.abs() : term.strip();
|
|
3721
3360
|
});
|
|
3722
|
-
|
|
3723
3361
|
return new Mul(terms).flatten();
|
|
3724
3362
|
},
|
|
3725
3363
|
// expand numerator and denominator separately
|
|
@@ -3727,36 +3365,27 @@ _.extend(Mul.prototype, {
|
|
|
3727
3365
|
var isAdd = function isAdd(term) {
|
|
3728
3366
|
return term instanceof Add;
|
|
3729
3367
|
};
|
|
3730
|
-
|
|
3731
3368
|
var isInverse = function isInverse(term) {
|
|
3732
3369
|
return term instanceof Pow && term.exp.isNegative();
|
|
3733
3370
|
};
|
|
3734
|
-
|
|
3735
3371
|
var isInverseAdd = function isInverseAdd(term) {
|
|
3736
3372
|
return isInverse(term) && isAdd(term.base);
|
|
3737
3373
|
};
|
|
3738
|
-
|
|
3739
3374
|
var mul = this.recurse("expand").flatten();
|
|
3740
|
-
|
|
3741
3375
|
var hasAdd = _.any(mul.terms, isAdd);
|
|
3742
|
-
|
|
3743
3376
|
var hasInverseAdd = _.any(mul.terms, isInverseAdd);
|
|
3744
|
-
|
|
3745
3377
|
if (!(hasAdd || hasInverseAdd)) {
|
|
3746
3378
|
return mul;
|
|
3747
3379
|
}
|
|
3748
|
-
|
|
3749
3380
|
var terms = _.groupBy(mul.terms, isInverse);
|
|
3750
|
-
|
|
3751
3381
|
var normals = terms[false] || [];
|
|
3752
3382
|
var inverses = terms[true] || [];
|
|
3753
|
-
|
|
3754
3383
|
if (hasAdd) {
|
|
3755
3384
|
var grouped = _.groupBy(normals, isAdd);
|
|
3756
|
-
|
|
3757
3385
|
var adds = grouped[true] || [];
|
|
3758
|
-
var others = grouped[false] || [];
|
|
3386
|
+
var others = grouped[false] || [];
|
|
3759
3387
|
|
|
3388
|
+
// loop over each additive sequence
|
|
3760
3389
|
var expanded = _.reduce(adds, function (expanded, add) {
|
|
3761
3390
|
// loop over each expanded array of terms
|
|
3762
3391
|
return _.reduce(expanded, function (temp, array) {
|
|
@@ -3765,38 +3394,34 @@ _.extend(Mul.prototype, {
|
|
|
3765
3394
|
return array.concat(term);
|
|
3766
3395
|
}));
|
|
3767
3396
|
}, []);
|
|
3768
|
-
}, [[]]);
|
|
3769
|
-
|
|
3397
|
+
}, [[]]);
|
|
3770
3398
|
|
|
3399
|
+
// join each fully expanded array of factors with remaining multiplicative factors
|
|
3771
3400
|
var muls = _.map(expanded, function (array) {
|
|
3772
3401
|
return new Mul(others.concat(array)).flatten();
|
|
3773
3402
|
});
|
|
3774
|
-
|
|
3775
3403
|
normals = [new Add(muls)];
|
|
3776
3404
|
}
|
|
3777
|
-
|
|
3778
3405
|
if (hasInverseAdd) {
|
|
3779
3406
|
var denominator = new Mul(_.invoke(inverses, "getDenominator")).flatten();
|
|
3780
3407
|
inverses = [new Pow(denominator.expand(), Num.Div)];
|
|
3781
3408
|
}
|
|
3782
|
-
|
|
3783
3409
|
return new Mul(normals.concat(inverses)).flatten();
|
|
3784
3410
|
},
|
|
3785
3411
|
factor: function factor(options) {
|
|
3786
3412
|
var factored = this.recurse("factor", options).flatten();
|
|
3787
|
-
|
|
3788
3413
|
if (!(factored instanceof Mul)) {
|
|
3789
3414
|
return factored;
|
|
3790
|
-
}
|
|
3791
|
-
|
|
3415
|
+
}
|
|
3792
3416
|
|
|
3417
|
+
// Combine any factored out Rationals into one, but don't collect
|
|
3793
3418
|
var grouped = _.groupBy(factored.terms, function (term) {
|
|
3794
3419
|
return term instanceof Rational;
|
|
3795
|
-
});
|
|
3420
|
+
});
|
|
3421
|
+
|
|
3422
|
+
// Could also accomplish this by passing a new option
|
|
3796
3423
|
// e.g. return memo.mul(term, {autocollect: false});
|
|
3797
3424
|
// TODO(alex): Decide whether this is a good use of options or not
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
3425
|
var rational = _.reduce(grouped[true], function (memo, term) {
|
|
3801
3426
|
return {
|
|
3802
3427
|
n: memo.n * term.n,
|
|
@@ -3806,61 +3431,58 @@ _.extend(Mul.prototype, {
|
|
|
3806
3431
|
n: 1,
|
|
3807
3432
|
d: 1
|
|
3808
3433
|
});
|
|
3809
|
-
|
|
3810
3434
|
if (rational.d === 1) {
|
|
3811
3435
|
rational = new Int(rational.n);
|
|
3812
3436
|
} else {
|
|
3813
3437
|
rational = new Rational(rational.n, rational.d);
|
|
3814
3438
|
}
|
|
3815
|
-
|
|
3816
3439
|
return new Mul((grouped[false] || []).concat(rational)).flatten();
|
|
3817
3440
|
},
|
|
3818
3441
|
collect: function collect(options) {
|
|
3819
3442
|
var partitioned = this.recurse("collect", options).partition();
|
|
3820
|
-
var number = partitioned[0].reduce(options);
|
|
3443
|
+
var number = partitioned[0].reduce(options);
|
|
3821
3444
|
|
|
3445
|
+
// e.g. 0*x -> 0
|
|
3822
3446
|
if (number.eval() === 0) {
|
|
3823
3447
|
return Num.Zero;
|
|
3824
3448
|
}
|
|
3449
|
+
var others = partitioned[1].flatten();
|
|
3825
3450
|
|
|
3826
|
-
|
|
3451
|
+
// e.g. 2*2 -> 4
|
|
3827
3452
|
// e.g. 2*2*x -> 4*x
|
|
3828
|
-
|
|
3829
3453
|
if (!(others instanceof Mul)) {
|
|
3830
3454
|
return new Mul(number, others).flatten();
|
|
3831
3455
|
}
|
|
3456
|
+
others = others.terms;
|
|
3832
3457
|
|
|
3833
|
-
|
|
3834
|
-
|
|
3458
|
+
// [Expr base, Expr exp]
|
|
3835
3459
|
var pairs = [];
|
|
3836
|
-
|
|
3837
3460
|
_.each(others, function (term) {
|
|
3838
3461
|
if (term instanceof Pow) {
|
|
3839
3462
|
pairs.push([term.base, term.exp]);
|
|
3840
3463
|
} else {
|
|
3841
3464
|
pairs.push([term, Num.One]);
|
|
3842
3465
|
}
|
|
3843
|
-
});
|
|
3844
|
-
|
|
3466
|
+
});
|
|
3845
3467
|
|
|
3468
|
+
// {(Expr base).print(): [[Expr base, Expr exp]]}
|
|
3846
3469
|
var grouped = _.groupBy(pairs, function (pair) {
|
|
3847
3470
|
return pair[0].normalize().print();
|
|
3848
|
-
});
|
|
3849
|
-
|
|
3471
|
+
});
|
|
3850
3472
|
|
|
3473
|
+
// [[Expr base, Expr exp]]
|
|
3851
3474
|
var summed = _.compact(_.map(grouped, function (pairs) {
|
|
3852
3475
|
var base = pairs[0][0];
|
|
3853
3476
|
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
3854
3477
|
var exp = sum.collect(options);
|
|
3855
|
-
|
|
3856
3478
|
if (exp instanceof Num && exp.eval() === 0) {
|
|
3857
3479
|
return null;
|
|
3858
3480
|
} else {
|
|
3859
3481
|
return [base, exp];
|
|
3860
3482
|
}
|
|
3861
|
-
}));
|
|
3862
|
-
|
|
3483
|
+
}));
|
|
3863
3484
|
|
|
3485
|
+
// XXX `pairs` is shadowed four or five times in this function
|
|
3864
3486
|
var pairs = _.groupBy(summed, function (pair) {
|
|
3865
3487
|
if (pair[0] instanceof Trig && pair[0].isBasic()) {
|
|
3866
3488
|
return "trig";
|
|
@@ -3870,32 +3492,28 @@ _.extend(Mul.prototype, {
|
|
|
3870
3492
|
return "expr";
|
|
3871
3493
|
}
|
|
3872
3494
|
});
|
|
3873
|
-
|
|
3874
3495
|
var trigs = pairs.trig || [];
|
|
3875
3496
|
var logs = pairs.log || [];
|
|
3876
3497
|
var exprs = pairs.expr || [];
|
|
3877
|
-
|
|
3878
3498
|
if (trigs.length > 1) {
|
|
3879
3499
|
// combine sines and cosines into other trig functions
|
|
3500
|
+
|
|
3880
3501
|
// {Trig.arg.print(): [[Trig base, Expr exp]]}
|
|
3881
3502
|
var byArg = _.groupBy(trigs, function (pair) {
|
|
3882
3503
|
return pair[0].arg.normalize().print();
|
|
3883
3504
|
});
|
|
3884
|
-
|
|
3885
3505
|
trigs = [];
|
|
3886
|
-
|
|
3887
3506
|
_.each(byArg, function (pairs) {
|
|
3888
|
-
var arg = pairs[0][0].arg;
|
|
3507
|
+
var arg = pairs[0][0].arg;
|
|
3889
3508
|
|
|
3509
|
+
// {Trig.type: Expr exp}
|
|
3890
3510
|
var funcs = {
|
|
3891
3511
|
sin: Num.Zero,
|
|
3892
3512
|
cos: Num.Zero
|
|
3893
3513
|
};
|
|
3894
|
-
|
|
3895
3514
|
_.each(pairs, function (pair) {
|
|
3896
3515
|
funcs[pair[0].type] = pair[1];
|
|
3897
3516
|
});
|
|
3898
|
-
|
|
3899
3517
|
if (Mul.handleNegative(funcs.sin).collect(options).equals(funcs.cos)) {
|
|
3900
3518
|
// e.g. sin^x(y)/cos^x(y) -> tan^x(y)
|
|
3901
3519
|
if (funcs.cos.isNegative()) {
|
|
@@ -3907,25 +3525,24 @@ _.extend(Mul.prototype, {
|
|
|
3907
3525
|
cot: funcs.cos
|
|
3908
3526
|
};
|
|
3909
3527
|
}
|
|
3910
|
-
}
|
|
3911
|
-
// TODO(alex): transform 1/sin and 1/cos into csc and sec
|
|
3528
|
+
}
|
|
3912
3529
|
|
|
3530
|
+
// TODO(alex): combine even if exponents not a perfect match
|
|
3531
|
+
// TODO(alex): transform 1/sin and 1/cos into csc and sec
|
|
3913
3532
|
|
|
3914
3533
|
_.each(funcs, function (exp, type) {
|
|
3915
3534
|
trigs.push([new Trig(type, arg), exp]);
|
|
3916
3535
|
});
|
|
3917
3536
|
});
|
|
3918
3537
|
}
|
|
3919
|
-
|
|
3920
3538
|
if (logs.length > 1) {
|
|
3921
3539
|
// combine logs with the same base
|
|
3540
|
+
|
|
3922
3541
|
// {Log.base.print(): [[Log base, Expr exp]]}
|
|
3923
3542
|
var byBase = _.groupBy(logs, function (pair) {
|
|
3924
3543
|
return pair[0].base.normalize().print();
|
|
3925
3544
|
});
|
|
3926
|
-
|
|
3927
3545
|
logs = [];
|
|
3928
|
-
|
|
3929
3546
|
_.each(byBase, function (pairs) {
|
|
3930
3547
|
// only combine two logs of the same base, otherwise commutative
|
|
3931
3548
|
// differences result in different equally valid output
|
|
@@ -3941,16 +3558,15 @@ _.extend(Mul.prototype, {
|
|
|
3941
3558
|
} else {
|
|
3942
3559
|
logs = logs.concat(pairs);
|
|
3943
3560
|
}
|
|
3944
|
-
});
|
|
3561
|
+
});
|
|
3945
3562
|
|
|
3563
|
+
// TODO(alex): combine if all inverses are the same e.g. ln(y)*ln(z)/ln(x)/ln(x)
|
|
3946
3564
|
}
|
|
3947
3565
|
|
|
3948
3566
|
pairs = trigs.concat(logs).concat(exprs);
|
|
3949
|
-
|
|
3950
3567
|
var collected = _.map(pairs, function (pair) {
|
|
3951
3568
|
return new Pow(pair[0], pair[1]).collect(options);
|
|
3952
3569
|
});
|
|
3953
|
-
|
|
3954
3570
|
return new Mul([number].concat(collected)).flatten();
|
|
3955
3571
|
},
|
|
3956
3572
|
isSubtract: function isSubtract() {
|
|
@@ -3963,11 +3579,9 @@ _.extend(Mul.prototype, {
|
|
|
3963
3579
|
factorIn: function factorIn(hint) {
|
|
3964
3580
|
var partitioned = this.partition();
|
|
3965
3581
|
var numbers = partitioned[0].terms;
|
|
3966
|
-
|
|
3967
3582
|
var fold = numbers.length && _.all(numbers, function (num) {
|
|
3968
3583
|
return num.n > 0;
|
|
3969
3584
|
});
|
|
3970
|
-
|
|
3971
3585
|
if (fold) {
|
|
3972
3586
|
// e.g. - x*2*3 -> x*-2*3
|
|
3973
3587
|
var num = numbers[0].negate();
|
|
@@ -3983,7 +3597,6 @@ _.extend(Mul.prototype, {
|
|
|
3983
3597
|
// TODO(alex): make more general or rename to be more specific
|
|
3984
3598
|
factorOut: function factorOut() {
|
|
3985
3599
|
var factored = false;
|
|
3986
|
-
|
|
3987
3600
|
var terms = _.compact(_.map(this.terms, function (term, i, list) {
|
|
3988
3601
|
if (!factored && term instanceof Num && term.hints.divide) {
|
|
3989
3602
|
factored = true;
|
|
@@ -3992,7 +3605,6 @@ _.extend(Mul.prototype, {
|
|
|
3992
3605
|
return term;
|
|
3993
3606
|
}
|
|
3994
3607
|
}));
|
|
3995
|
-
|
|
3996
3608
|
if (terms.length === 1) {
|
|
3997
3609
|
return terms[0];
|
|
3998
3610
|
} else {
|
|
@@ -4015,7 +3627,6 @@ _.extend(Mul.prototype, {
|
|
|
4015
3627
|
return this;
|
|
4016
3628
|
} else {
|
|
4017
3629
|
var terms = _.invoke(this.collect().terms, "asPositiveFactor");
|
|
4018
|
-
|
|
4019
3630
|
return new Mul(terms).flatten();
|
|
4020
3631
|
}
|
|
4021
3632
|
},
|
|
@@ -4029,18 +3640,16 @@ _.extend(Mul.prototype, {
|
|
|
4029
3640
|
var isNum = function isNum(expr) {
|
|
4030
3641
|
return expr instanceof Num;
|
|
4031
3642
|
};
|
|
4032
|
-
|
|
4033
3643
|
if (_.any(this.terms, isNum)) {
|
|
4034
3644
|
var num = _.find(this.terms, isNum);
|
|
4035
|
-
|
|
4036
3645
|
return this.replace(num, num.negate());
|
|
4037
3646
|
} else {
|
|
4038
3647
|
return new Mul([Num.Neg].concat(this.terms));
|
|
4039
3648
|
}
|
|
4040
3649
|
}
|
|
4041
|
-
});
|
|
4042
|
-
|
|
3650
|
+
});
|
|
4043
3651
|
|
|
3652
|
+
// static methods for the sequence types
|
|
4044
3653
|
_.each([Add, Mul], function (type) {
|
|
4045
3654
|
_.extend(type, {
|
|
4046
3655
|
// create a new sequence unless left is already one (returns a copy)
|
|
@@ -4053,7 +3662,6 @@ _.each([Add, Mul], function (type) {
|
|
|
4053
3662
|
}
|
|
4054
3663
|
});
|
|
4055
3664
|
});
|
|
4056
|
-
|
|
4057
3665
|
_.extend(Mul, {
|
|
4058
3666
|
// negative signs should be folded into numbers whenever possible
|
|
4059
3667
|
// never fold into a Num that's already negative or a Mul that has a negative Num
|
|
@@ -4062,8 +3670,8 @@ _.extend(Mul, {
|
|
|
4062
3670
|
handleNegative: function handleNegative(expr, hint) {
|
|
4063
3671
|
if (expr instanceof Num && expr.n > 0) {
|
|
4064
3672
|
// e.g. - 2 -> -2
|
|
4065
|
-
var negated = expr.negate();
|
|
4066
|
-
|
|
3673
|
+
var negated = expr.negate();
|
|
3674
|
+
// TODO(alex): rework hint system so that this isn't necessary
|
|
4067
3675
|
negated.hints = expr.hints;
|
|
4068
3676
|
return negated.addHint(hint);
|
|
4069
3677
|
} else if (expr instanceof Mul) {
|
|
@@ -4084,35 +3692,30 @@ _.extend(Mul, {
|
|
|
4084
3692
|
var rest = new Mul(_.rest(right.terms)).flatten();
|
|
4085
3693
|
return Mul.handleDivide(first, rest);
|
|
4086
3694
|
}
|
|
4087
|
-
|
|
4088
3695
|
var isInt = function isInt(expr) {
|
|
4089
3696
|
return expr instanceof Int;
|
|
4090
3697
|
};
|
|
4091
|
-
|
|
4092
3698
|
var isRational = function isRational(expr) {
|
|
4093
3699
|
return expr instanceof Rational;
|
|
4094
|
-
};
|
|
4095
|
-
// e.g. 3x / 4 -> 3/4 * x (will still render as 3x/4)
|
|
4096
|
-
|
|
3700
|
+
};
|
|
4097
3701
|
|
|
3702
|
+
// for simplification purposes, fold Ints into Rationals if possible
|
|
3703
|
+
// e.g. 3x / 4 -> 3/4 * x (will still render as 3x/4)
|
|
4098
3704
|
if (isInt(right) && left instanceof Mul && _.any(left.terms, isInt)) {
|
|
4099
3705
|
// search from the right
|
|
4100
3706
|
var reversed = left.terms.slice().reverse();
|
|
4101
|
-
|
|
4102
3707
|
var num = _.find(reversed, isRational);
|
|
4103
|
-
|
|
4104
3708
|
if (!isInt(num)) {
|
|
4105
3709
|
return new Mul(left.terms.concat([new Rational(1, right.n).addHint("fraction")]));
|
|
4106
3710
|
}
|
|
4107
|
-
|
|
4108
3711
|
var rational = new Rational(num.n, right.n);
|
|
4109
|
-
rational.hints = num.hints;
|
|
4110
|
-
// 6/8 to be considered a fraction, not just a division
|
|
3712
|
+
rational.hints = num.hints;
|
|
4111
3713
|
|
|
3714
|
+
// in the case of something like 1/3 * 6/8, we want the
|
|
3715
|
+
// 6/8 to be considered a fraction, not just a division
|
|
4112
3716
|
if (num === reversed[0]) {
|
|
4113
3717
|
rational = rational.addHint("fraction");
|
|
4114
3718
|
}
|
|
4115
|
-
|
|
4116
3719
|
if (num.n < 0 && right.n < 0) {
|
|
4117
3720
|
rational.d = -rational.d;
|
|
4118
3721
|
return left.replace(num, [Num.Neg, rational]);
|
|
@@ -4120,7 +3723,6 @@ _.extend(Mul, {
|
|
|
4120
3723
|
return left.replace(num, rational);
|
|
4121
3724
|
}
|
|
4122
3725
|
}
|
|
4123
|
-
|
|
4124
3726
|
var divide = function divide(a, b) {
|
|
4125
3727
|
if (b instanceof Int) {
|
|
4126
3728
|
if (a instanceof Int) {
|
|
@@ -4137,7 +3739,6 @@ _.extend(Mul, {
|
|
|
4137
3739
|
// e.g. x / 3 -> x*1/3
|
|
4138
3740
|
// e.g. x / -3 -> x*-1/3
|
|
4139
3741
|
var inverse = new Rational(1, b.eval());
|
|
4140
|
-
|
|
4141
3742
|
if (b.eval() < 0) {
|
|
4142
3743
|
return [a, inverse.addHint("negate")];
|
|
4143
3744
|
} else {
|
|
@@ -4146,14 +3747,12 @@ _.extend(Mul, {
|
|
|
4146
3747
|
}
|
|
4147
3748
|
} else {
|
|
4148
3749
|
var pow;
|
|
4149
|
-
|
|
4150
3750
|
if (b instanceof Trig && b.exp) {
|
|
4151
3751
|
// e.g. sin^2(x) -> sin(x)^2
|
|
4152
3752
|
var exp = b.exp;
|
|
4153
3753
|
b.exp = undefined;
|
|
4154
3754
|
b = new Pow(b, exp);
|
|
4155
3755
|
}
|
|
4156
|
-
|
|
4157
3756
|
if (b instanceof Pow) {
|
|
4158
3757
|
// e.g. (x^2) ^ -1 -> x^-2
|
|
4159
3758
|
// e.g. (x^y) ^ -1 -> x^(-1*y)
|
|
@@ -4163,7 +3762,6 @@ _.extend(Mul, {
|
|
|
4163
3762
|
// e.g. x ^ -1 -> x^-1
|
|
4164
3763
|
pow = new Pow(b, Num.Div);
|
|
4165
3764
|
}
|
|
4166
|
-
|
|
4167
3765
|
if (a instanceof Int && a.n === 1) {
|
|
4168
3766
|
// e.g. 1 / x -> x^-1
|
|
4169
3767
|
return [pow];
|
|
@@ -4173,7 +3771,6 @@ _.extend(Mul, {
|
|
|
4173
3771
|
}
|
|
4174
3772
|
}
|
|
4175
3773
|
};
|
|
4176
|
-
|
|
4177
3774
|
if (left instanceof Mul) {
|
|
4178
3775
|
var divided = divide(_.last(left.terms), right);
|
|
4179
3776
|
return new Mul(_.initial(left.terms).concat(divided));
|
|
@@ -4188,6 +3785,7 @@ _.extend(Mul, {
|
|
|
4188
3785
|
// e.g. -2*x -> -2*x simplified
|
|
4189
3786
|
// e.g. -x*2 -> -1*x*2 not simplified -> x*-2 simplified
|
|
4190
3787
|
// e.g. -1*x*2 -> -1*x*2 not simplified
|
|
3788
|
+
|
|
4191
3789
|
// also fold multiplicative terms into open Trig and Log nodes
|
|
4192
3790
|
// e.g. (sin x)*x -> sin(x)*x
|
|
4193
3791
|
// e.g. sin(x)*x -> sin(x)*x
|
|
@@ -4199,23 +3797,18 @@ _.extend(Mul, {
|
|
|
4199
3797
|
var trigLog = _.find(_.initial(expr.terms), function (term) {
|
|
4200
3798
|
return (term instanceof Trig || term instanceof Log) && term.hints.open;
|
|
4201
3799
|
});
|
|
4202
|
-
|
|
4203
3800
|
var index = _.indexOf(expr.terms, trigLog);
|
|
4204
|
-
|
|
4205
3801
|
if (trigLog) {
|
|
4206
3802
|
var last = _.last(expr.terms);
|
|
4207
|
-
|
|
4208
3803
|
if (trigLog.hints.parens || last.hints.parens || last.has(Trig) || last.has(Log)) {
|
|
4209
3804
|
trigLog.hints.open = false;
|
|
4210
3805
|
} else {
|
|
4211
3806
|
var newTrigLog;
|
|
4212
|
-
|
|
4213
3807
|
if (trigLog instanceof Trig) {
|
|
4214
3808
|
newTrigLog = Trig.create([trigLog.type, trigLog.exp], Mul.createOrAppend(trigLog.arg, last).fold());
|
|
4215
3809
|
} else {
|
|
4216
3810
|
newTrigLog = Log.create(trigLog.base, Mul.createOrAppend(trigLog.power, last).fold());
|
|
4217
3811
|
}
|
|
4218
|
-
|
|
4219
3812
|
if (index === 0) {
|
|
4220
3813
|
return newTrigLog;
|
|
4221
3814
|
} else {
|
|
@@ -4223,47 +3816,39 @@ _.extend(Mul, {
|
|
|
4223
3816
|
}
|
|
4224
3817
|
}
|
|
4225
3818
|
}
|
|
4226
|
-
|
|
4227
3819
|
var partitioned = expr.partition();
|
|
4228
3820
|
var numbers = partitioned[0].terms;
|
|
4229
|
-
|
|
4230
3821
|
var pos = function pos(num) {
|
|
4231
3822
|
return num.n > 0;
|
|
4232
3823
|
};
|
|
4233
|
-
|
|
4234
3824
|
var neg = function neg(num) {
|
|
4235
3825
|
return num.n === -1 && num.hints.negate;
|
|
4236
3826
|
};
|
|
4237
|
-
|
|
4238
3827
|
var posOrNeg = function posOrNeg(num) {
|
|
4239
3828
|
return pos(num) || neg(num);
|
|
4240
3829
|
};
|
|
4241
|
-
|
|
4242
3830
|
if (numbers.length > 1 && _.some(numbers, neg) && _.some(numbers, pos) && _.every(numbers, posOrNeg)) {
|
|
4243
3831
|
var firstNeg = _.indexOf(expr.terms, _.find(expr.terms, neg));
|
|
3832
|
+
var firstNum = _.indexOf(expr.terms, _.find(expr.terms, pos));
|
|
4244
3833
|
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
3834
|
+
// e.g. -x*2 -> x*-2
|
|
4248
3835
|
if (firstNeg < firstNum) {
|
|
4249
3836
|
return expr.replace(firstNum, expr.terms[firstNum].negate()).remove(firstNeg);
|
|
4250
3837
|
}
|
|
4251
3838
|
}
|
|
4252
|
-
}
|
|
4253
|
-
|
|
3839
|
+
}
|
|
4254
3840
|
|
|
3841
|
+
// in all other cases, make no change
|
|
4255
3842
|
return expr;
|
|
4256
3843
|
}
|
|
4257
3844
|
});
|
|
4258
|
-
/* exponentiation */
|
|
4259
|
-
|
|
4260
3845
|
|
|
3846
|
+
/* exponentiation */
|
|
4261
3847
|
function Pow(base, exp) {
|
|
4262
3848
|
this.base = base;
|
|
4263
3849
|
this.exp = exp;
|
|
4264
3850
|
}
|
|
4265
3851
|
Pow.prototype = new Expr();
|
|
4266
|
-
|
|
4267
3852
|
_.extend(Pow.prototype, {
|
|
4268
3853
|
func: Pow,
|
|
4269
3854
|
args: function args() {
|
|
@@ -4271,7 +3856,9 @@ _.extend(Pow.prototype, {
|
|
|
4271
3856
|
},
|
|
4272
3857
|
eval: function _eval(vars, options) {
|
|
4273
3858
|
var evaledBase = this.base.eval(vars, options);
|
|
4274
|
-
var evaledExp = this.exp.eval(vars, options);
|
|
3859
|
+
var evaledExp = this.exp.eval(vars, options);
|
|
3860
|
+
|
|
3861
|
+
// Math.pow unequivocally returns NaN when provided with both a
|
|
4275
3862
|
// negative base and a fractional exponent. However, in some cases, we
|
|
4276
3863
|
// know that our exponent is actually valid for use with negative
|
|
4277
3864
|
// bases (e.g., (-5)^(1/3)).
|
|
@@ -4280,10 +3867,10 @@ _.extend(Pow.prototype, {
|
|
|
4280
3867
|
// limited subset (by requiring that the exponent is rational with an
|
|
4281
3868
|
// odd denominator), but it's still useful.
|
|
4282
3869
|
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
|
|
4283
|
-
|
|
4284
3870
|
if (evaledBase < 0) {
|
|
4285
|
-
var simplifiedExp = this.exp.simplify();
|
|
3871
|
+
var simplifiedExp = this.exp.simplify();
|
|
4286
3872
|
|
|
3873
|
+
// If Float, convert to a Rational to enable the logic below
|
|
4287
3874
|
if (simplifiedExp instanceof Float) {
|
|
4288
3875
|
var num = simplifiedExp.n;
|
|
4289
3876
|
var decimals = (num - num.toFixed()).toString().length - 2;
|
|
@@ -4291,10 +3878,8 @@ _.extend(Pow.prototype, {
|
|
|
4291
3878
|
var rationalExp = new Rational(num * denominator, denominator);
|
|
4292
3879
|
simplifiedExp = rationalExp.simplify();
|
|
4293
3880
|
}
|
|
4294
|
-
|
|
4295
3881
|
if (simplifiedExp instanceof Rational) {
|
|
4296
3882
|
var oddDenominator = Math.abs(simplifiedExp.d) % 2 === 1;
|
|
4297
|
-
|
|
4298
3883
|
if (oddDenominator) {
|
|
4299
3884
|
var oddNumerator = Math.abs(simplifiedExp.n) % 2 === 1;
|
|
4300
3885
|
var sign = oddNumerator ? -1 : 1;
|
|
@@ -4302,7 +3887,6 @@ _.extend(Pow.prototype, {
|
|
|
4302
3887
|
}
|
|
4303
3888
|
}
|
|
4304
3889
|
}
|
|
4305
|
-
|
|
4306
3890
|
return Math.pow(evaledBase, evaledExp);
|
|
4307
3891
|
},
|
|
4308
3892
|
getUnits: function getUnits() {
|
|
@@ -4318,11 +3902,9 @@ _.extend(Pow.prototype, {
|
|
|
4318
3902
|
},
|
|
4319
3903
|
print: function print() {
|
|
4320
3904
|
var base = this.base.print();
|
|
4321
|
-
|
|
4322
3905
|
if (this.base instanceof Seq || this.base instanceof Pow) {
|
|
4323
3906
|
base = "(" + base + ")";
|
|
4324
3907
|
}
|
|
4325
|
-
|
|
4326
3908
|
return base + "^(" + this.exp.print() + ")";
|
|
4327
3909
|
},
|
|
4328
3910
|
tex: function tex() {
|
|
@@ -4333,7 +3915,6 @@ _.extend(Pow.prototype, {
|
|
|
4333
3915
|
if (this.exp.n !== 1) {
|
|
4334
3916
|
error("Node marked with hint 'root' does not have exponent " + "of form 1/x.");
|
|
4335
3917
|
}
|
|
4336
|
-
|
|
4337
3918
|
if (this.exp.d === 2) {
|
|
4338
3919
|
// e.g. x ^ 1/2 w/hint -> sqrt{x}
|
|
4339
3920
|
return "\\sqrt{" + this.base.tex() + "}";
|
|
@@ -4350,7 +3931,6 @@ _.extend(Pow.prototype, {
|
|
|
4350
3931
|
} else {
|
|
4351
3932
|
// e.g. x ^ y -> x^y
|
|
4352
3933
|
var base = this.base.tex();
|
|
4353
|
-
|
|
4354
3934
|
if (this.base instanceof Seq || this.base instanceof Pow || this.base instanceof Num && !this.base.isSimple()) {
|
|
4355
3935
|
// e.g. a+b ^ c -> (a+b)^c
|
|
4356
3936
|
base = "(" + base + ")";
|
|
@@ -4358,7 +3938,6 @@ _.extend(Pow.prototype, {
|
|
|
4358
3938
|
// e.g. ln(x) ^ 2 -> [ln(x)]^2
|
|
4359
3939
|
base = "[" + base + "]";
|
|
4360
3940
|
}
|
|
4361
|
-
|
|
4362
3941
|
return base + "^{" + this.exp.tex() + "}";
|
|
4363
3942
|
}
|
|
4364
3943
|
},
|
|
@@ -4367,55 +3946,53 @@ _.extend(Pow.prototype, {
|
|
|
4367
3946
|
},
|
|
4368
3947
|
expand: function expand() {
|
|
4369
3948
|
var pow = this.recurse("expand");
|
|
4370
|
-
|
|
4371
3949
|
if (pow.base instanceof Mul) {
|
|
4372
3950
|
// e.g. (ab)^c -> a^c*b^c
|
|
3951
|
+
|
|
4373
3952
|
var terms = _.map(pow.base.terms, function (term) {
|
|
4374
3953
|
return new Pow(term, pow.exp);
|
|
4375
3954
|
});
|
|
4376
|
-
|
|
4377
3955
|
return new Mul(terms).expand();
|
|
4378
3956
|
} else if (pow.base instanceof Add && pow.exp instanceof Int && pow.exp.abs().eval() > 1) {
|
|
4379
3957
|
// e.g. (a+b)^2 -> a*a+a*b+a*b+b*b
|
|
4380
3958
|
// e.g. (a+b)^-2 -> (a*a+a*b+a*b+b*b)^-1
|
|
3959
|
+
|
|
4381
3960
|
var positive = pow.exp.eval() > 0;
|
|
4382
3961
|
var n = pow.exp.abs().eval();
|
|
4383
|
-
|
|
4384
3962
|
var signed = function signed(mul) {
|
|
4385
3963
|
return positive ? mul : new Pow(mul, Num.Div);
|
|
4386
|
-
};
|
|
4387
|
-
|
|
3964
|
+
};
|
|
4388
3965
|
|
|
3966
|
+
// compute and cache powers of 2 up to n
|
|
4389
3967
|
var cache = {
|
|
4390
3968
|
1: pow.base
|
|
4391
3969
|
};
|
|
4392
|
-
|
|
4393
3970
|
for (var i = 2; i <= n; i *= 2) {
|
|
4394
3971
|
var mul = new Mul(cache[i / 2], cache[i / 2]);
|
|
4395
3972
|
cache[i] = mul.expand().collect();
|
|
4396
|
-
}
|
|
4397
|
-
|
|
3973
|
+
}
|
|
4398
3974
|
|
|
3975
|
+
// if n is a power of 2, you're done!
|
|
4399
3976
|
if (_.has(cache, n)) {
|
|
4400
3977
|
return signed(cache[n]);
|
|
4401
|
-
}
|
|
4402
|
-
|
|
3978
|
+
}
|
|
4403
3979
|
|
|
3980
|
+
// otherwise decompose n into powers of 2 ...
|
|
4404
3981
|
var indices = _.map(n.toString(2).split(""), function (str, i, list) {
|
|
4405
3982
|
return Number(str) * Math.pow(2, list.length - i - 1);
|
|
4406
3983
|
});
|
|
3984
|
+
indices = _.without(indices, 0);
|
|
4407
3985
|
|
|
4408
|
-
|
|
4409
|
-
|
|
3986
|
+
// ... then combine
|
|
4410
3987
|
var mul = new Mul(_.pick(cache, indices)).expand().collect();
|
|
4411
3988
|
return signed(mul);
|
|
4412
3989
|
} else if (pow.exp instanceof Add) {
|
|
4413
3990
|
// DEFINITELY want behind super-simplify() flag
|
|
4414
3991
|
// e.g. x^(a+b) -> x^a*x^b
|
|
3992
|
+
|
|
4415
3993
|
var terms = _.map(pow.exp.terms, function (term) {
|
|
4416
3994
|
return new Pow(pow.base, term).expand();
|
|
4417
3995
|
});
|
|
4418
|
-
|
|
4419
3996
|
return new Mul(terms).expand();
|
|
4420
3997
|
} else {
|
|
4421
3998
|
return pow;
|
|
@@ -4423,7 +4000,6 @@ _.extend(Pow.prototype, {
|
|
|
4423
4000
|
},
|
|
4424
4001
|
factor: function factor() {
|
|
4425
4002
|
var pow = this.recurse("factor");
|
|
4426
|
-
|
|
4427
4003
|
if (pow.base instanceof Mul) {
|
|
4428
4004
|
var terms = _.map(pow.base.terms, function (term) {
|
|
4429
4005
|
if (term instanceof Int && pow.exp.equals(Num.Div)) {
|
|
@@ -4434,7 +4010,6 @@ _.extend(Pow.prototype, {
|
|
|
4434
4010
|
return new Pow(term, pow.exp);
|
|
4435
4011
|
}
|
|
4436
4012
|
});
|
|
4437
|
-
|
|
4438
4013
|
return new Mul(terms);
|
|
4439
4014
|
} else {
|
|
4440
4015
|
return pow;
|
|
@@ -4449,13 +4024,10 @@ _.extend(Pow.prototype, {
|
|
|
4449
4024
|
var exp = Mul.createOrAppend(this.base.exp, this.exp);
|
|
4450
4025
|
return new Pow(base, exp).collect(options);
|
|
4451
4026
|
}
|
|
4452
|
-
|
|
4453
4027
|
var pow = this.recurse("collect", options);
|
|
4454
|
-
|
|
4455
4028
|
var isSimilarLog = function isSimilarLog(term) {
|
|
4456
4029
|
return term instanceof Log && term.base.equals(pow.base);
|
|
4457
4030
|
};
|
|
4458
|
-
|
|
4459
4031
|
if (pow.exp instanceof Num && pow.exp.eval() === 0) {
|
|
4460
4032
|
// e.g. x^0 -> 1
|
|
4461
4033
|
return Num.One;
|
|
@@ -4468,7 +4040,6 @@ _.extend(Pow.prototype, {
|
|
|
4468
4040
|
} else if (pow.exp instanceof Mul && _.any(pow.exp.terms, isSimilarLog)) {
|
|
4469
4041
|
// e.g. b^(2*y*log_b(x)) -> x^(2*y)
|
|
4470
4042
|
var log = _.find(pow.exp.terms, isSimilarLog);
|
|
4471
|
-
|
|
4472
4043
|
var base = log.power;
|
|
4473
4044
|
var exp = pow.exp.remove(log).flatten();
|
|
4474
4045
|
return new Pow(base, exp).collect(options);
|
|
@@ -4478,6 +4049,7 @@ _.extend(Pow.prototype, {
|
|
|
4478
4049
|
if (options && options.preciseFloats) {
|
|
4479
4050
|
// Avoid creating an imprecise float
|
|
4480
4051
|
// e.g. 23^1.5 -> 12167^0.5, not ~110.304
|
|
4052
|
+
|
|
4481
4053
|
// If you take the root as specified by the denominator and
|
|
4482
4054
|
// end up with more digits after the decimal point,
|
|
4483
4055
|
// the result is imprecise. This works for rationals as well
|
|
@@ -4488,16 +4060,15 @@ _.extend(Pow.prototype, {
|
|
|
4488
4060
|
var decimalsInBase = pow.base.getDecimalPlaces();
|
|
4489
4061
|
var root = new Pow(pow.base, new Rational(1, exp.d));
|
|
4490
4062
|
var decimalsInRoot = root.collect().getDecimalPlaces();
|
|
4491
|
-
|
|
4492
4063
|
if (decimalsInRoot > decimalsInBase) {
|
|
4493
4064
|
// Collecting over this denominator would result in an
|
|
4494
4065
|
// imprecise float, so avoid doing so.
|
|
4495
4066
|
var newBase = new Pow(pow.base, new Int(exp.n)).collect();
|
|
4496
4067
|
return new Pow(newBase, new Rational(1, exp.d));
|
|
4497
4068
|
}
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4069
|
+
}
|
|
4500
4070
|
|
|
4071
|
+
// e.g. 4^1.5 -> 8
|
|
4501
4072
|
return pow.base.raiseToThe(pow.exp, options);
|
|
4502
4073
|
} else {
|
|
4503
4074
|
return pow;
|
|
@@ -4508,7 +4079,6 @@ _.extend(Pow.prototype, {
|
|
|
4508
4079
|
var isDiv = function isDiv(arg) {
|
|
4509
4080
|
return arg instanceof Num && arg.hints.divide;
|
|
4510
4081
|
};
|
|
4511
|
-
|
|
4512
4082
|
return isDiv(this.exp) || this.exp instanceof Mul && _.any(this.exp.terms, isDiv);
|
|
4513
4083
|
},
|
|
4514
4084
|
// assuming this Pow represents user-entered division, returns the denominator
|
|
@@ -4550,16 +4120,15 @@ _.extend(Pow.prototype, {
|
|
|
4550
4120
|
},
|
|
4551
4121
|
findGCD: function findGCD(factor) {
|
|
4552
4122
|
var base, exp;
|
|
4553
|
-
|
|
4554
4123
|
if (factor instanceof Pow) {
|
|
4555
4124
|
base = factor.base;
|
|
4556
4125
|
exp = factor.exp;
|
|
4557
4126
|
} else {
|
|
4558
4127
|
base = factor;
|
|
4559
4128
|
exp = Num.One;
|
|
4560
|
-
}
|
|
4561
|
-
|
|
4129
|
+
}
|
|
4562
4130
|
|
|
4131
|
+
// GCD is only relevant if same base
|
|
4563
4132
|
if (this.base.equals(base)) {
|
|
4564
4133
|
if (this.exp.equals(exp)) {
|
|
4565
4134
|
// exact match
|
|
@@ -4574,10 +4143,8 @@ _.extend(Pow.prototype, {
|
|
|
4574
4143
|
// e.g. GCD(x^2, x^y) -> 1
|
|
4575
4144
|
return Num.One;
|
|
4576
4145
|
}
|
|
4577
|
-
|
|
4578
4146
|
var expA = this.exp.asMul().partition();
|
|
4579
4147
|
var expB = exp.asMul().partition();
|
|
4580
|
-
|
|
4581
4148
|
if (expA[1].equals(expB[1])) {
|
|
4582
4149
|
// exponents match except for coefficient
|
|
4583
4150
|
// e.g. GCD(x^3y, x^y) -> x^y
|
|
@@ -4586,14 +4153,12 @@ _.extend(Pow.prototype, {
|
|
|
4586
4153
|
return new Pow(base, mul).collect();
|
|
4587
4154
|
}
|
|
4588
4155
|
}
|
|
4589
|
-
|
|
4590
4156
|
return Num.One;
|
|
4591
4157
|
},
|
|
4592
4158
|
isPositive: function isPositive() {
|
|
4593
4159
|
if (this.base.isPositive()) {
|
|
4594
4160
|
return true;
|
|
4595
4161
|
}
|
|
4596
|
-
|
|
4597
4162
|
var exp = this.exp.simplify();
|
|
4598
4163
|
return exp instanceof Int && exp.eval() % 2 === 0;
|
|
4599
4164
|
},
|
|
@@ -4602,10 +4167,8 @@ _.extend(Pow.prototype, {
|
|
|
4602
4167
|
return this;
|
|
4603
4168
|
} else {
|
|
4604
4169
|
var exp = this.exp.simplify();
|
|
4605
|
-
|
|
4606
4170
|
if (exp instanceof Int) {
|
|
4607
4171
|
var n = exp.eval();
|
|
4608
|
-
|
|
4609
4172
|
if (n > 2) {
|
|
4610
4173
|
// e.g. x^3 -> x^2
|
|
4611
4174
|
return new Pow(this.base, new Int(n - 1));
|
|
@@ -4614,32 +4177,29 @@ _.extend(Pow.prototype, {
|
|
|
4614
4177
|
return new Pow(this.base, new Int(n + 1));
|
|
4615
4178
|
}
|
|
4616
4179
|
}
|
|
4617
|
-
|
|
4618
4180
|
return Num.One;
|
|
4619
4181
|
}
|
|
4620
4182
|
}
|
|
4621
4183
|
});
|
|
4622
|
-
|
|
4623
4184
|
_.extend(Pow, {
|
|
4624
4185
|
sqrt: function sqrt(arg) {
|
|
4625
4186
|
return new Pow(arg, Num.Sqrt);
|
|
4626
4187
|
},
|
|
4627
4188
|
nthroot: function nthroot(radicand, degree) {
|
|
4628
|
-
var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
|
|
4629
|
-
// this "root" hint is lost between here and when tex() is called.
|
|
4189
|
+
var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
|
|
4630
4190
|
|
|
4191
|
+
// FIXME(johnsullivan): If oneOverDegree ends up being a pow object,
|
|
4192
|
+
// this "root" hint is lost between here and when tex() is called.
|
|
4631
4193
|
return new Pow(radicand, exp.addHint("root"));
|
|
4632
4194
|
}
|
|
4633
4195
|
});
|
|
4634
|
-
/* logarithm */
|
|
4635
|
-
|
|
4636
4196
|
|
|
4197
|
+
/* logarithm */
|
|
4637
4198
|
function Log(base, power) {
|
|
4638
4199
|
this.base = base;
|
|
4639
4200
|
this.power = power;
|
|
4640
4201
|
}
|
|
4641
4202
|
Log.prototype = new Expr();
|
|
4642
|
-
|
|
4643
4203
|
_.extend(Log.prototype, {
|
|
4644
4204
|
func: Log,
|
|
4645
4205
|
args: function args() {
|
|
@@ -4653,7 +4213,6 @@ _.extend(Log.prototype, {
|
|
|
4653
4213
|
},
|
|
4654
4214
|
print: function print() {
|
|
4655
4215
|
var power = "(" + this.power.print() + ")";
|
|
4656
|
-
|
|
4657
4216
|
if (this.isNatural()) {
|
|
4658
4217
|
return "ln" + power;
|
|
4659
4218
|
} else {
|
|
@@ -4662,7 +4221,6 @@ _.extend(Log.prototype, {
|
|
|
4662
4221
|
},
|
|
4663
4222
|
tex: function tex() {
|
|
4664
4223
|
var power = "(" + this.power.tex() + ")";
|
|
4665
|
-
|
|
4666
4224
|
if (this.isNatural()) {
|
|
4667
4225
|
return "\\ln" + power;
|
|
4668
4226
|
} else {
|
|
@@ -4671,7 +4229,6 @@ _.extend(Log.prototype, {
|
|
|
4671
4229
|
},
|
|
4672
4230
|
collect: function collect(options) {
|
|
4673
4231
|
var log = this.recurse("collect", options);
|
|
4674
|
-
|
|
4675
4232
|
if (log.power instanceof Num && log.power.eval() === 1) {
|
|
4676
4233
|
// e.g. ln(1) -> 0
|
|
4677
4234
|
return Num.Zero;
|
|
@@ -4687,21 +4244,22 @@ _.extend(Log.prototype, {
|
|
|
4687
4244
|
},
|
|
4688
4245
|
expand: function expand() {
|
|
4689
4246
|
var log = this.recurse("expand");
|
|
4690
|
-
|
|
4691
4247
|
if (log.power instanceof Mul) {
|
|
4692
4248
|
// might want behind super-simplify() flag
|
|
4693
4249
|
// e.g. ln(xy) -> ln(x) + ln(y)
|
|
4250
|
+
|
|
4694
4251
|
var terms = _.map(log.power.terms, function (term) {
|
|
4695
4252
|
// need to expand again in case new log powers are Pows
|
|
4696
4253
|
return new Log(log.base, term).expand();
|
|
4697
4254
|
});
|
|
4698
|
-
|
|
4699
4255
|
return new Add(terms);
|
|
4700
4256
|
} else if (log.power instanceof Pow) {
|
|
4701
4257
|
// e.g. ln(x^y) -> y*ln(x)
|
|
4258
|
+
|
|
4702
4259
|
return new Mul(log.power.exp, new Log(log.base, log.power.base).expand()).flatten();
|
|
4703
4260
|
} else if (!log.isNatural()) {
|
|
4704
4261
|
// e.g. log_b(x) -> ln(x)/ln(b)
|
|
4262
|
+
|
|
4705
4263
|
return Mul.handleDivide(new Log(Const.e, log.power), new Log(Const.e, log.base));
|
|
4706
4264
|
} else {
|
|
4707
4265
|
return log;
|
|
@@ -4712,7 +4270,6 @@ _.extend(Log.prototype, {
|
|
|
4712
4270
|
}),
|
|
4713
4271
|
isPositive: function isPositive() {
|
|
4714
4272
|
var log = this.collect();
|
|
4715
|
-
|
|
4716
4273
|
if (log.base instanceof Num && log.power instanceof Num) {
|
|
4717
4274
|
return this.eval() > 0;
|
|
4718
4275
|
} else {
|
|
@@ -4726,7 +4283,6 @@ _.extend(Log.prototype, {
|
|
|
4726
4283
|
return this.base.equals(Const.e);
|
|
4727
4284
|
}
|
|
4728
4285
|
});
|
|
4729
|
-
|
|
4730
4286
|
_.extend(Log, {
|
|
4731
4287
|
natural: function natural() {
|
|
4732
4288
|
return Const.e;
|
|
@@ -4736,23 +4292,19 @@ _.extend(Log, {
|
|
|
4736
4292
|
},
|
|
4737
4293
|
create: function create(base, power) {
|
|
4738
4294
|
var log = new Log(base, power);
|
|
4739
|
-
|
|
4740
4295
|
if (!power.hints.parens) {
|
|
4741
4296
|
log = log.addHint("open");
|
|
4742
4297
|
}
|
|
4743
|
-
|
|
4744
4298
|
return log;
|
|
4745
4299
|
}
|
|
4746
4300
|
});
|
|
4747
|
-
/* trigonometric functions */
|
|
4748
|
-
|
|
4749
4301
|
|
|
4302
|
+
/* trigonometric functions */
|
|
4750
4303
|
function Trig(type, arg) {
|
|
4751
4304
|
this.type = type;
|
|
4752
4305
|
this.arg = arg;
|
|
4753
4306
|
}
|
|
4754
4307
|
Trig.prototype = new Expr();
|
|
4755
|
-
|
|
4756
4308
|
_.extend(Trig.prototype, {
|
|
4757
4309
|
func: Trig,
|
|
4758
4310
|
args: function args() {
|
|
@@ -4938,7 +4490,6 @@ _.extend(Trig.prototype, {
|
|
|
4938
4490
|
},
|
|
4939
4491
|
codegen: function codegen() {
|
|
4940
4492
|
var func = this.functions[this.type].codegen;
|
|
4941
|
-
|
|
4942
4493
|
if (typeof func === "function") {
|
|
4943
4494
|
return func(this.arg.codegen());
|
|
4944
4495
|
} else if (typeof func === "string") {
|
|
@@ -4960,7 +4511,6 @@ _.extend(Trig.prototype, {
|
|
|
4960
4511
|
}),
|
|
4961
4512
|
isPositive: function isPositive() {
|
|
4962
4513
|
var trig = this.collect();
|
|
4963
|
-
|
|
4964
4514
|
if (trig.arg instanceof Num) {
|
|
4965
4515
|
return this.eval() > 0;
|
|
4966
4516
|
} else {
|
|
@@ -4982,7 +4532,6 @@ _.extend(Trig.prototype, {
|
|
|
4982
4532
|
},
|
|
4983
4533
|
expand: function expand() {
|
|
4984
4534
|
var trig = this.recurse("expand");
|
|
4985
|
-
|
|
4986
4535
|
if (!trig.isInverse()) {
|
|
4987
4536
|
// e.g. tan(x) -> sin(x)/cos(x)
|
|
4988
4537
|
var expand = trig.functions[trig.type].expand;
|
|
@@ -4993,16 +4542,13 @@ _.extend(Trig.prototype, {
|
|
|
4993
4542
|
},
|
|
4994
4543
|
collect: function collect(options) {
|
|
4995
4544
|
var trig = this.recurse("collect", options);
|
|
4996
|
-
|
|
4997
4545
|
if (!trig.isInverse() && trig.arg.isNegative()) {
|
|
4998
4546
|
var arg;
|
|
4999
|
-
|
|
5000
4547
|
if (trig.arg instanceof Num) {
|
|
5001
4548
|
arg = trig.arg.abs();
|
|
5002
4549
|
} else {
|
|
5003
4550
|
arg = Mul.handleDivide(trig.arg, Num.Neg).collect(options);
|
|
5004
4551
|
}
|
|
5005
|
-
|
|
5006
4552
|
if (trig.isEven()) {
|
|
5007
4553
|
// e.g. cos(-x) -> cos(x)
|
|
5008
4554
|
return new Trig(trig.type, arg);
|
|
@@ -5015,28 +4561,22 @@ _.extend(Trig.prototype, {
|
|
|
5015
4561
|
}
|
|
5016
4562
|
}
|
|
5017
4563
|
});
|
|
5018
|
-
|
|
5019
4564
|
_.extend(Trig, {
|
|
5020
4565
|
create: function create(pair, arg) {
|
|
5021
4566
|
var type = pair[0];
|
|
5022
4567
|
var exp = pair[1];
|
|
5023
|
-
|
|
5024
4568
|
if (exp && exp.equals(Num.Neg)) {
|
|
5025
4569
|
// e.g. sin^-1(x) -> arcsin(x)
|
|
5026
4570
|
type = "arc" + type;
|
|
5027
4571
|
exp = undefined;
|
|
5028
4572
|
}
|
|
5029
|
-
|
|
5030
4573
|
var trig = new Trig(type, arg);
|
|
5031
|
-
|
|
5032
4574
|
if (!arg.hints.parens) {
|
|
5033
4575
|
trig = trig.addHint("open");
|
|
5034
4576
|
}
|
|
5035
|
-
|
|
5036
4577
|
if (exp) {
|
|
5037
4578
|
trig.exp = exp;
|
|
5038
4579
|
}
|
|
5039
|
-
|
|
5040
4580
|
return trig;
|
|
5041
4581
|
},
|
|
5042
4582
|
sin: function sin(arg) {
|
|
@@ -5052,12 +4592,10 @@ _.extend(Trig, {
|
|
|
5052
4592
|
return new Trig("cosh", arg);
|
|
5053
4593
|
}
|
|
5054
4594
|
});
|
|
5055
|
-
|
|
5056
4595
|
function Abs(arg) {
|
|
5057
4596
|
this.arg = arg;
|
|
5058
4597
|
}
|
|
5059
4598
|
Abs.prototype = new Expr();
|
|
5060
|
-
|
|
5061
4599
|
_.extend(Abs.prototype, {
|
|
5062
4600
|
func: Abs,
|
|
5063
4601
|
args: function args() {
|
|
@@ -5077,7 +4615,6 @@ _.extend(Abs.prototype, {
|
|
|
5077
4615
|
},
|
|
5078
4616
|
collect: function collect(options) {
|
|
5079
4617
|
var abs = this.recurse("collect", options);
|
|
5080
|
-
|
|
5081
4618
|
if (abs.arg.isPositive()) {
|
|
5082
4619
|
// e.g. |2^x| -> 2^x
|
|
5083
4620
|
return abs.arg;
|
|
@@ -5095,13 +4632,10 @@ _.extend(Abs.prototype, {
|
|
|
5095
4632
|
return "other";
|
|
5096
4633
|
}
|
|
5097
4634
|
});
|
|
5098
|
-
|
|
5099
4635
|
var positives = terms.positive.concat(_.invoke(terms.number, "abs"));
|
|
5100
|
-
|
|
5101
4636
|
if (terms.other.length) {
|
|
5102
4637
|
positives.push(new Abs(new Mul(terms.other).flatten()));
|
|
5103
4638
|
}
|
|
5104
|
-
|
|
5105
4639
|
return new Mul(positives).flatten();
|
|
5106
4640
|
} else {
|
|
5107
4641
|
return abs;
|
|
@@ -5110,13 +4644,11 @@ _.extend(Abs.prototype, {
|
|
|
5110
4644
|
// this should definitely be behind a super-simplify flag
|
|
5111
4645
|
expand: function expand() {
|
|
5112
4646
|
var abs = this.recurse("expand");
|
|
5113
|
-
|
|
5114
4647
|
if (abs.arg instanceof Mul) {
|
|
5115
4648
|
// e.g. |xyz| -> |x|*|y|*|z|
|
|
5116
4649
|
var terms = _.map(abs.arg.terms, function (term) {
|
|
5117
4650
|
return new Abs(term);
|
|
5118
4651
|
});
|
|
5119
|
-
|
|
5120
4652
|
return new Mul(terms);
|
|
5121
4653
|
} else {
|
|
5122
4654
|
return abs;
|
|
@@ -5126,16 +4658,14 @@ _.extend(Abs.prototype, {
|
|
|
5126
4658
|
return true;
|
|
5127
4659
|
}
|
|
5128
4660
|
});
|
|
5129
|
-
/* equation */
|
|
5130
|
-
|
|
5131
4661
|
|
|
4662
|
+
/* equation */
|
|
5132
4663
|
function Eq(left, type, right) {
|
|
5133
4664
|
this.left = left;
|
|
5134
4665
|
this.type = type;
|
|
5135
4666
|
this.right = right;
|
|
5136
4667
|
}
|
|
5137
4668
|
Eq.prototype = new Expr();
|
|
5138
|
-
|
|
5139
4669
|
_.extend(Eq.prototype, {
|
|
5140
4670
|
func: Eq,
|
|
5141
4671
|
args: function args() {
|
|
@@ -5160,7 +4690,6 @@ _.extend(Eq.prototype, {
|
|
|
5160
4690
|
},
|
|
5161
4691
|
normalize: function normalize() {
|
|
5162
4692
|
var eq = this.recurse("normalize");
|
|
5163
|
-
|
|
5164
4693
|
if (_.contains([">", ">="], eq.type)) {
|
|
5165
4694
|
// inequalities should have the smaller side on the left
|
|
5166
4695
|
return new Eq(eq.right, eq.type.replace(">", "<"), eq.left);
|
|
@@ -5175,40 +4704,39 @@ _.extend(Eq.prototype, {
|
|
|
5175
4704
|
asExpr: function asExpr(unfactored) {
|
|
5176
4705
|
var isZero = function isZero(expr) {
|
|
5177
4706
|
return expr instanceof Num && expr.isSimple() && expr.eval() === 0;
|
|
5178
|
-
};
|
|
5179
|
-
|
|
4707
|
+
};
|
|
5180
4708
|
|
|
4709
|
+
// first convert to a sequence of additive terms
|
|
5181
4710
|
var terms = [];
|
|
5182
|
-
|
|
5183
4711
|
if (this.left instanceof Add) {
|
|
5184
4712
|
terms = _.clone(this.left.terms);
|
|
5185
4713
|
} else if (!isZero(this.left)) {
|
|
5186
4714
|
terms = [this.left];
|
|
5187
4715
|
}
|
|
5188
|
-
|
|
5189
4716
|
if (this.right instanceof Add) {
|
|
5190
4717
|
terms = terms.concat(this.right.negate().terms);
|
|
5191
4718
|
} else if (!isZero(this.right)) {
|
|
5192
4719
|
terms.push(this.right.negate());
|
|
5193
4720
|
}
|
|
4721
|
+
var isInequality = !this.isEquality();
|
|
5194
4722
|
|
|
5195
|
-
|
|
4723
|
+
// Collect over each term individually to transform simple expressions
|
|
5196
4724
|
// into numbers that might have denominators, taking into account
|
|
5197
4725
|
// float precision. We have to be very careful to not introduce any
|
|
5198
4726
|
// irrational floats before asExpr() returns, because by definition
|
|
5199
4727
|
// they do not have exact denominators...
|
|
5200
|
-
|
|
5201
4728
|
terms = _.invoke(terms, "collect", {
|
|
5202
4729
|
preciseFloats: true
|
|
5203
|
-
});
|
|
4730
|
+
});
|
|
5204
4731
|
|
|
4732
|
+
// ...and we multiply through by every denominator.
|
|
5205
4733
|
for (var i = 0; i < terms.length; i++) {
|
|
5206
|
-
var denominator = terms[i].getDenominator();
|
|
4734
|
+
var denominator = terms[i].getDenominator();
|
|
5207
4735
|
|
|
4736
|
+
// Can't multiply inequalities by non 100% positive factors
|
|
5208
4737
|
if (isInequality && !denominator.isPositive()) {
|
|
5209
4738
|
denominator = denominator.asPositiveFactor();
|
|
5210
4739
|
}
|
|
5211
|
-
|
|
5212
4740
|
if (!denominator.equals(Num.One)) {
|
|
5213
4741
|
terms = _.map(terms, function (term) {
|
|
5214
4742
|
return Mul.createOrAppend(term, denominator).simplify({
|
|
@@ -5218,7 +4746,6 @@ _.extend(Eq.prototype, {
|
|
|
5218
4746
|
});
|
|
5219
4747
|
}
|
|
5220
4748
|
}
|
|
5221
|
-
|
|
5222
4749
|
var add = new Add(terms).flatten();
|
|
5223
4750
|
return unfactored ? add : this.divideThrough(add);
|
|
5224
4751
|
},
|
|
@@ -5234,58 +4761,48 @@ _.extend(Eq.prototype, {
|
|
|
5234
4761
|
var factored = simplified.factor({
|
|
5235
4762
|
keepNegative: isInequality
|
|
5236
4763
|
});
|
|
5237
|
-
|
|
5238
4764
|
if (!(factored instanceof Mul)) {
|
|
5239
4765
|
return expr;
|
|
5240
4766
|
}
|
|
5241
|
-
|
|
5242
4767
|
var terms = factored.terms;
|
|
5243
|
-
|
|
5244
4768
|
var isAdd = function isAdd(term) {
|
|
5245
4769
|
return term instanceof Add;
|
|
5246
4770
|
};
|
|
5247
|
-
|
|
5248
4771
|
var hasVar = function hasVar(term) {
|
|
5249
4772
|
return !!term.getVars().length;
|
|
5250
4773
|
};
|
|
5251
|
-
|
|
5252
4774
|
var isOne = function isOne(term) {
|
|
5253
4775
|
return term.equals(Num.One);
|
|
5254
4776
|
};
|
|
5255
|
-
|
|
5256
4777
|
var grouped = _.groupBy(terms, isAdd);
|
|
5257
|
-
|
|
5258
4778
|
var adds = grouped[true] || [];
|
|
5259
4779
|
var others = grouped[false] || [];
|
|
5260
|
-
|
|
5261
4780
|
if (adds.length && this.isEquality()) {
|
|
5262
4781
|
// keep only Adds
|
|
5263
4782
|
// e.g. 2xy(z+1)(=0) -> z+1(=0)
|
|
5264
4783
|
return new Mul(adds).flatten();
|
|
5265
4784
|
}
|
|
5266
|
-
|
|
5267
4785
|
var denominator = others;
|
|
5268
|
-
|
|
5269
4786
|
if (!adds.length) {
|
|
5270
4787
|
// if no Adds, keep all variable terms to preserve meaning
|
|
5271
4788
|
// e.g. 42xyz(=0) -> xyz(=0)
|
|
5272
4789
|
denominator = _.reject(denominator, hasVar);
|
|
5273
4790
|
}
|
|
5274
|
-
|
|
5275
4791
|
if (isInequality) {
|
|
5276
4792
|
// can't divide inequalities by non 100% positive factors
|
|
5277
4793
|
// e.g. 42x^2y(z+1)(=0) -> y(z+1)(=0)
|
|
5278
4794
|
denominator = _.invoke(denominator, "asPositiveFactor");
|
|
5279
|
-
}
|
|
5280
|
-
|
|
4795
|
+
}
|
|
5281
4796
|
|
|
4797
|
+
// don't need to divide by one
|
|
5282
4798
|
denominator = _.reject(denominator, isOne);
|
|
5283
4799
|
denominator = _.map(denominator, function (term) {
|
|
5284
4800
|
return new Pow(term, Num.Div);
|
|
5285
4801
|
});
|
|
5286
|
-
var dividedResult = new Mul(terms.concat(denominator)).collect();
|
|
5287
|
-
// rollback the factoring and discard all intermediate steps.
|
|
4802
|
+
var dividedResult = new Mul(terms.concat(denominator)).collect();
|
|
5288
4803
|
|
|
4804
|
+
// If the end result is the same as the original factoring,
|
|
4805
|
+
// rollback the factoring and discard all intermediate steps.
|
|
5289
4806
|
if (dividedResult.equals(factored)) {
|
|
5290
4807
|
return simplified;
|
|
5291
4808
|
} else {
|
|
@@ -5300,23 +4817,16 @@ _.extend(Eq.prototype, {
|
|
|
5300
4817
|
if (!(other instanceof Eq)) {
|
|
5301
4818
|
return false;
|
|
5302
4819
|
}
|
|
5303
|
-
|
|
5304
4820
|
var eq1 = this.normalize();
|
|
5305
4821
|
var eq2 = other.normalize();
|
|
5306
|
-
|
|
5307
4822
|
if (eq1.type !== eq2.type) {
|
|
5308
4823
|
return false;
|
|
5309
|
-
}
|
|
5310
|
-
// e.g x+2x=6 -> 3x=6 -> 3x-6(=0) -> x-2(=0)
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
var expr1 = eq1.divideThrough(eq1.asExpr(
|
|
5314
|
-
/* unfactored */
|
|
5315
|
-
true).collect());
|
|
5316
|
-
var expr2 = eq2.divideThrough(eq2.asExpr(
|
|
5317
|
-
/* unfactored */
|
|
5318
|
-
true).collect());
|
|
4824
|
+
}
|
|
5319
4825
|
|
|
4826
|
+
// need to collect to properly factor out common factors
|
|
4827
|
+
// e.g x+2x=6 -> 3x=6 -> 3x-6(=0) -> x-2(=0)
|
|
4828
|
+
var expr1 = eq1.divideThrough(eq1.asExpr( /* unfactored */true).collect());
|
|
4829
|
+
var expr2 = eq2.divideThrough(eq2.asExpr( /* unfactored */true).collect());
|
|
5320
4830
|
if (eq1.isEquality()) {
|
|
5321
4831
|
// equals and not-equals can be subtracted either way
|
|
5322
4832
|
return expr1.compare(expr2) || expr1.compare(Mul.handleNegative(expr2));
|
|
@@ -5329,7 +4839,6 @@ _.extend(Eq.prototype, {
|
|
|
5329
4839
|
var eq1 = this.normalize();
|
|
5330
4840
|
var eq2 = other.normalize();
|
|
5331
4841
|
var same = eq1.left.sameForm(eq2.left) && eq1.right.sameForm(eq2.right);
|
|
5332
|
-
|
|
5333
4842
|
if (eq1.isEquality()) {
|
|
5334
4843
|
// equals and not-equals can be commutative with respect to the sign
|
|
5335
4844
|
return same || eq1.left.sameForm(eq2.right) && eq1.right.sameForm(eq2.left);
|
|
@@ -5340,29 +4849,22 @@ _.extend(Eq.prototype, {
|
|
|
5340
4849
|
// we don't want to override collect because it would turn y=x into y-x(=0)
|
|
5341
4850
|
// instead, we ask if the equation was in that form, would it be simplified?
|
|
5342
4851
|
isSimplified: function isSimplified() {
|
|
5343
|
-
var expr = this.asExpr(
|
|
5344
|
-
/* unfactored */
|
|
5345
|
-
true);
|
|
4852
|
+
var expr = this.asExpr( /* unfactored */true);
|
|
5346
4853
|
var simplified = this.divideThrough(expr).simplify();
|
|
5347
4854
|
return expr.equals(simplified) && this.left.isSimplified() && this.right.isSimplified();
|
|
5348
4855
|
}
|
|
5349
4856
|
});
|
|
5350
|
-
|
|
5351
4857
|
_.extend(Eq.prototype, {
|
|
5352
4858
|
// Assumptions: Expression is of the form a+bx, and we solve for x
|
|
5353
4859
|
solveLinearEquationForVariable: function solveLinearEquationForVariable(variable) {
|
|
5354
4860
|
var expr = this.asExpr();
|
|
5355
|
-
|
|
5356
4861
|
if (!expr.is(Add) || expr.terms.length !== 2) {
|
|
5357
4862
|
throw new Error("Can only handle linear equations of the form " + "a + bx (= 0)");
|
|
5358
4863
|
}
|
|
5359
|
-
|
|
5360
4864
|
var hasVar = function hasVar(term) {
|
|
5361
4865
|
return term.has(Var) && _.contains(term.getVars(), variable.symbol);
|
|
5362
4866
|
};
|
|
5363
|
-
|
|
5364
4867
|
var a, b;
|
|
5365
|
-
|
|
5366
4868
|
if (hasVar(expr.terms[0])) {
|
|
5367
4869
|
a = Mul.handleNegative(expr.terms[1]);
|
|
5368
4870
|
b = Mul.handleDivide(expr.terms[0], variable);
|
|
@@ -5370,17 +4872,13 @@ _.extend(Eq.prototype, {
|
|
|
5370
4872
|
a = Mul.handleNegative(expr.terms[0]);
|
|
5371
4873
|
b = Mul.handleDivide(expr.terms[1], variable);
|
|
5372
4874
|
}
|
|
5373
|
-
|
|
5374
4875
|
return Mul.handleDivide(a, b).simplify();
|
|
5375
4876
|
}
|
|
5376
4877
|
});
|
|
5377
|
-
/* abstract symbol node */
|
|
5378
|
-
|
|
5379
4878
|
|
|
4879
|
+
/* abstract symbol node */
|
|
5380
4880
|
function Symbol() {}
|
|
5381
|
-
|
|
5382
4881
|
Symbol.prototype = new Expr();
|
|
5383
|
-
|
|
5384
4882
|
_.extend(Symbol.prototype, {
|
|
5385
4883
|
needsExplicitMul: function needsExplicitMul() {
|
|
5386
4884
|
return false;
|
|
@@ -5393,15 +4891,13 @@ _.extend(Symbol.prototype, {
|
|
|
5393
4891
|
}
|
|
5394
4892
|
}
|
|
5395
4893
|
});
|
|
5396
|
-
/* function variable */
|
|
5397
|
-
|
|
5398
4894
|
|
|
4895
|
+
/* function variable */
|
|
5399
4896
|
function Func(symbol, arg) {
|
|
5400
4897
|
this.symbol = symbol;
|
|
5401
4898
|
this.arg = arg;
|
|
5402
4899
|
}
|
|
5403
4900
|
Func.prototype = new Symbol();
|
|
5404
|
-
|
|
5405
4901
|
_.extend(Func.prototype, {
|
|
5406
4902
|
func: Func,
|
|
5407
4903
|
args: function args() {
|
|
@@ -5416,22 +4912,18 @@ _.extend(Func.prototype, {
|
|
|
5416
4912
|
eval: function _eval(vars, options) {
|
|
5417
4913
|
var arg = this.arg;
|
|
5418
4914
|
var func = vars[this.symbol];
|
|
5419
|
-
|
|
5420
4915
|
var newVars = _.extend(_.clone(vars), {
|
|
5421
4916
|
x: arg.eval(vars, options)
|
|
5422
4917
|
});
|
|
5423
|
-
|
|
5424
4918
|
var parsedFunc = parse(func, options);
|
|
5425
|
-
|
|
5426
4919
|
if (parsedFunc.parsed) {
|
|
5427
4920
|
return parsedFunc.expr.eval(newVars, options);
|
|
5428
|
-
}
|
|
5429
|
-
|
|
5430
|
-
|
|
4921
|
+
}
|
|
4922
|
+
// If parsedFunc isn't actually parsed, return its error
|
|
5431
4923
|
return parsedFunc;
|
|
5432
4924
|
},
|
|
5433
4925
|
codegen: function codegen() {
|
|
5434
|
-
return 'vars["' + this.symbol + '"](' + this.arg.codegen() +
|
|
4926
|
+
return 'vars["' + this.symbol + '"](' + this.arg.codegen() + ")";
|
|
5435
4927
|
},
|
|
5436
4928
|
getUnits: function getUnits() {
|
|
5437
4929
|
return this.arg.getUnits();
|
|
@@ -5447,15 +4939,13 @@ _.extend(Func.prototype, {
|
|
|
5447
4939
|
return this.arg.getConsts();
|
|
5448
4940
|
}
|
|
5449
4941
|
});
|
|
5450
|
-
/* variable */
|
|
5451
|
-
|
|
5452
4942
|
|
|
4943
|
+
/* variable */
|
|
5453
4944
|
function Var(symbol, subscript) {
|
|
5454
4945
|
this.symbol = symbol;
|
|
5455
4946
|
this.subscript = subscript;
|
|
5456
4947
|
}
|
|
5457
4948
|
Var.prototype = new Symbol();
|
|
5458
|
-
|
|
5459
4949
|
_.extend(Var.prototype, {
|
|
5460
4950
|
func: Var,
|
|
5461
4951
|
args: function args() {
|
|
@@ -5469,18 +4959,15 @@ _.extend(Var.prototype, {
|
|
|
5469
4959
|
},
|
|
5470
4960
|
print: function print() {
|
|
5471
4961
|
var sub = "";
|
|
5472
|
-
|
|
5473
4962
|
if (this.subscript) {
|
|
5474
4963
|
sub = "_(" + this.subscript.print() + ")";
|
|
5475
4964
|
}
|
|
5476
|
-
|
|
5477
4965
|
return this.symbol + sub;
|
|
5478
4966
|
},
|
|
5479
4967
|
// Provide a way to easily evalate expressions with the common case,
|
|
5480
4968
|
// subscripts that consist of a single number or symbol e.g. x_a or x_42
|
|
5481
4969
|
prettyPrint: function prettyPrint() {
|
|
5482
4970
|
var sub = this.subscript;
|
|
5483
|
-
|
|
5484
4971
|
if (sub && (sub instanceof Num || sub instanceof Symbol)) {
|
|
5485
4972
|
return this.symbol + "_" + sub.print();
|
|
5486
4973
|
} else {
|
|
@@ -5489,11 +4976,9 @@ _.extend(Var.prototype, {
|
|
|
5489
4976
|
},
|
|
5490
4977
|
tex: function tex() {
|
|
5491
4978
|
var sub = "";
|
|
5492
|
-
|
|
5493
4979
|
if (this.subscript) {
|
|
5494
4980
|
sub = "_{" + this.subscript.tex() + "}";
|
|
5495
4981
|
}
|
|
5496
|
-
|
|
5497
4982
|
var prefix = this.symbol.length > 1 ? "\\" : "";
|
|
5498
4983
|
return prefix + this.symbol + sub;
|
|
5499
4984
|
},
|
|
@@ -5513,14 +4998,12 @@ _.extend(Var.prototype, {
|
|
|
5513
4998
|
return false;
|
|
5514
4999
|
}
|
|
5515
5000
|
});
|
|
5516
|
-
/* constant */
|
|
5517
|
-
|
|
5518
5001
|
|
|
5002
|
+
/* constant */
|
|
5519
5003
|
function Const(symbol) {
|
|
5520
5004
|
this.symbol = symbol;
|
|
5521
5005
|
}
|
|
5522
5006
|
Const.prototype = new Symbol();
|
|
5523
|
-
|
|
5524
5007
|
_.extend(Const.prototype, {
|
|
5525
5008
|
func: Const,
|
|
5526
5009
|
args: function args() {
|
|
@@ -5567,15 +5050,12 @@ _.extend(Const.prototype, {
|
|
|
5567
5050
|
return [this.print()];
|
|
5568
5051
|
}
|
|
5569
5052
|
});
|
|
5570
|
-
|
|
5571
5053
|
Const.e = new Const("e");
|
|
5572
5054
|
Const.pi = new Const("pi");
|
|
5573
|
-
/* abstract number node */
|
|
5574
5055
|
|
|
5056
|
+
/* abstract number node */
|
|
5575
5057
|
function Num() {}
|
|
5576
|
-
|
|
5577
5058
|
Num.prototype = new Expr();
|
|
5578
|
-
|
|
5579
5059
|
_.extend(Num.prototype, {
|
|
5580
5060
|
repr: function repr() {
|
|
5581
5061
|
return this.print();
|
|
@@ -5627,10 +5107,11 @@ _.extend(Num.prototype, {
|
|
|
5627
5107
|
// Based on http://stackoverflow.com/a/10454560/2571482
|
|
5628
5108
|
getDecimalPlaces: function getDecimalPlaces() {
|
|
5629
5109
|
var match = ("" + this.n).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
|
5630
|
-
|
|
5631
5110
|
if (match) {
|
|
5632
|
-
return Math.max(0,
|
|
5633
|
-
|
|
5111
|
+
return Math.max(0,
|
|
5112
|
+
// Number of digits right of decimal point
|
|
5113
|
+
(match[1] ? match[1].length : 0) - (
|
|
5114
|
+
// Adjust for scientific notation
|
|
5634
5115
|
match[2] ? +match[2] : 0));
|
|
5635
5116
|
} else {
|
|
5636
5117
|
return 0;
|
|
@@ -5638,23 +5119,19 @@ _.extend(Num.prototype, {
|
|
|
5638
5119
|
},
|
|
5639
5120
|
asRational: abstract
|
|
5640
5121
|
});
|
|
5641
|
-
/* rational number (n: numerator, d: denominator) */
|
|
5642
|
-
|
|
5643
5122
|
|
|
5123
|
+
/* rational number (n: numerator, d: denominator) */
|
|
5644
5124
|
function Rational(numerator, denominator) {
|
|
5645
5125
|
var n = numerator;
|
|
5646
5126
|
var d = denominator;
|
|
5647
|
-
|
|
5648
5127
|
if (d < 0) {
|
|
5649
5128
|
n = -n;
|
|
5650
5129
|
d = -d;
|
|
5651
5130
|
}
|
|
5652
|
-
|
|
5653
5131
|
this.n = n;
|
|
5654
5132
|
this.d = d;
|
|
5655
5133
|
}
|
|
5656
5134
|
Rational.prototype = new Num();
|
|
5657
|
-
|
|
5658
5135
|
_.extend(Rational.prototype, {
|
|
5659
5136
|
func: Rational,
|
|
5660
5137
|
args: function args() {
|
|
@@ -5688,7 +5165,6 @@ _.extend(Rational.prototype, {
|
|
|
5688
5165
|
var gcd = Num.findGCD(this.n, this.d);
|
|
5689
5166
|
var n = this.n / gcd;
|
|
5690
5167
|
var d = this.d / gcd;
|
|
5691
|
-
|
|
5692
5168
|
if (d === 1) {
|
|
5693
5169
|
return new Int(n);
|
|
5694
5170
|
} else {
|
|
@@ -5708,8 +5184,8 @@ _.extend(Rational.prototype, {
|
|
|
5708
5184
|
// For more background, see
|
|
5709
5185
|
// http://math.stackexchange.com/questions/151081/gcd-of-rationals
|
|
5710
5186
|
var numerator = Num.findGCD(this.n * factor.d, factor.n * this.d);
|
|
5711
|
-
var denominator = this.d * factor.d;
|
|
5712
|
-
|
|
5187
|
+
var denominator = this.d * factor.d;
|
|
5188
|
+
// Create the rational, then call .collect() to simplify it
|
|
5713
5189
|
return new Rational(numerator, denominator).collect();
|
|
5714
5190
|
} else if (factor instanceof Int) {
|
|
5715
5191
|
return new Rational(Num.findGCD(this.n, factor.n), this.d);
|
|
@@ -5724,7 +5200,6 @@ _.extend(Rational.prototype, {
|
|
|
5724
5200
|
var abs = exp.abs().eval();
|
|
5725
5201
|
var n = Math.pow(this.n, abs);
|
|
5726
5202
|
var d = Math.pow(this.d, abs);
|
|
5727
|
-
|
|
5728
5203
|
if (positive) {
|
|
5729
5204
|
return new Rational(n, d).collect();
|
|
5730
5205
|
} else {
|
|
@@ -5744,14 +5219,12 @@ _.extend(Rational.prototype, {
|
|
|
5744
5219
|
return this;
|
|
5745
5220
|
}
|
|
5746
5221
|
});
|
|
5747
|
-
/* integer (n: numerator/number) */
|
|
5748
|
-
|
|
5749
5222
|
|
|
5223
|
+
/* integer (n: numerator/number) */
|
|
5750
5224
|
function Int(number) {
|
|
5751
5225
|
this.n = number;
|
|
5752
5226
|
}
|
|
5753
5227
|
Int.prototype = new Rational(0, 1);
|
|
5754
|
-
|
|
5755
5228
|
_.extend(Int.prototype, {
|
|
5756
5229
|
func: Int,
|
|
5757
5230
|
args: function args() {
|
|
@@ -5780,20 +5253,17 @@ _.extend(Int.prototype, {
|
|
|
5780
5253
|
}
|
|
5781
5254
|
}
|
|
5782
5255
|
});
|
|
5783
|
-
|
|
5784
5256
|
_.extend(Int, {
|
|
5785
5257
|
create: function create(n) {
|
|
5786
5258
|
return new Int(n).addHint("entered");
|
|
5787
5259
|
}
|
|
5788
5260
|
});
|
|
5789
|
-
/* float (n: number) */
|
|
5790
|
-
|
|
5791
5261
|
|
|
5262
|
+
/* float (n: number) */
|
|
5792
5263
|
function Float(number) {
|
|
5793
5264
|
this.n = number;
|
|
5794
5265
|
}
|
|
5795
5266
|
Float.prototype = new Num();
|
|
5796
|
-
|
|
5797
5267
|
_.extend(Float.prototype, {
|
|
5798
5268
|
func: Float,
|
|
5799
5269
|
args: function args() {
|
|
@@ -5853,7 +5323,6 @@ _.extend(Float.prototype, {
|
|
|
5853
5323
|
// only to be used on non-repeating decimals (e.g. user-provided)
|
|
5854
5324
|
asRational: function asRational() {
|
|
5855
5325
|
var parts = this.n.toString().split(".");
|
|
5856
|
-
|
|
5857
5326
|
if (parts.length === 1) {
|
|
5858
5327
|
return new Rational(this.n, 1);
|
|
5859
5328
|
} else {
|
|
@@ -5869,7 +5338,6 @@ _.extend(Float.prototype, {
|
|
|
5869
5338
|
return true;
|
|
5870
5339
|
}
|
|
5871
5340
|
});
|
|
5872
|
-
|
|
5873
5341
|
_.extend(Float, {
|
|
5874
5342
|
create: function create(n) {
|
|
5875
5343
|
return new Float(n).addHint("entered");
|
|
@@ -5879,9 +5347,9 @@ _.extend(Float, {
|
|
|
5879
5347
|
toDecimalPlaces: function toDecimalPlaces(n, places) {
|
|
5880
5348
|
return new Float(+n.toFixed(Math.min(places, 20))).collect();
|
|
5881
5349
|
}
|
|
5882
|
-
});
|
|
5883
|
-
|
|
5350
|
+
});
|
|
5884
5351
|
|
|
5352
|
+
// static methods and fields that are best defined on Num
|
|
5885
5353
|
_.extend(Num, {
|
|
5886
5354
|
negativeOne: function negativeOne(hint) {
|
|
5887
5355
|
if (hint === "subtract") {
|
|
@@ -5896,20 +5364,19 @@ _.extend(Num, {
|
|
|
5896
5364
|
findGCD: function findGCD(a, b) {
|
|
5897
5365
|
var mod;
|
|
5898
5366
|
a = Math.abs(a);
|
|
5899
|
-
b = Math.abs(b);
|
|
5367
|
+
b = Math.abs(b);
|
|
5368
|
+
|
|
5369
|
+
// Euclid's method doesn't handle non-integers very well. For now
|
|
5900
5370
|
// we just say we can't pull out a common factor. It might be
|
|
5901
5371
|
// reasonable to do better than this in the future.
|
|
5902
|
-
|
|
5903
5372
|
if (a !== Math.floor(a) || b !== Math.floor(b)) {
|
|
5904
5373
|
return 1;
|
|
5905
5374
|
}
|
|
5906
|
-
|
|
5907
5375
|
while (b) {
|
|
5908
5376
|
mod = a % b;
|
|
5909
5377
|
a = b;
|
|
5910
5378
|
b = mod;
|
|
5911
5379
|
}
|
|
5912
|
-
|
|
5913
5380
|
return a;
|
|
5914
5381
|
},
|
|
5915
5382
|
min: function min() {
|
|
@@ -5923,25 +5390,24 @@ _.extend(Num, {
|
|
|
5923
5390
|
});
|
|
5924
5391
|
}
|
|
5925
5392
|
});
|
|
5926
|
-
|
|
5927
5393
|
Num.Neg = new Int(-1).addHint("negate");
|
|
5928
5394
|
Num.Sub = new Int(-1).addHint("subtract");
|
|
5929
5395
|
Num.Div = new Int(-1).addHint("divide");
|
|
5930
5396
|
Num.Sqrt = new Rational(1, 2).addHint("root");
|
|
5931
5397
|
Num.Zero = new Int(0);
|
|
5932
5398
|
Num.One = new Int(1);
|
|
5933
|
-
Num.Ten = new Int(10);
|
|
5399
|
+
Num.Ten = new Int(10);
|
|
5934
5400
|
|
|
5401
|
+
// set identities here
|
|
5935
5402
|
Add.prototype.identity = Num.Zero;
|
|
5936
5403
|
Mul.prototype.identity = Num.One;
|
|
5937
|
-
|
|
5938
5404
|
var parseError = function parseError(str, hash) {
|
|
5939
5405
|
// return int location of parsing error
|
|
5940
5406
|
throw new Error(hash.loc.first_column);
|
|
5941
|
-
};
|
|
5942
|
-
// see http://zaach.github.io/jison/docs/#sharing-scope
|
|
5943
|
-
|
|
5407
|
+
};
|
|
5944
5408
|
|
|
5409
|
+
// expose concrete nodes to parser scope
|
|
5410
|
+
// see http://zaach.github.io/jison/docs/#sharing-scope
|
|
5945
5411
|
parser.yy = {
|
|
5946
5412
|
Add: Add,
|
|
5947
5413
|
Mul: Mul,
|
|
@@ -5974,16 +5440,15 @@ var parse = function parse(input, options) {
|
|
|
5974
5440
|
parser.yy.functions = _.without(options.functions, "i");
|
|
5975
5441
|
} else {
|
|
5976
5442
|
parser.yy.functions = [];
|
|
5977
|
-
}
|
|
5443
|
+
}
|
|
5444
|
+
|
|
5445
|
+
// If ',' is the decimal dividor in your country, replace any ','s
|
|
5978
5446
|
// with '.'s.
|
|
5979
5447
|
// This isn't perfect, since the output will all still have '.'s.
|
|
5980
5448
|
// TODO(jack): Fix the output to have ','s in this case
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
5449
|
if (options && options.decimal_separator) {
|
|
5984
5450
|
input = input.split(options.decimal_separator).join(".");
|
|
5985
5451
|
}
|
|
5986
|
-
|
|
5987
5452
|
var expr = parser.parse(input).completeParse();
|
|
5988
5453
|
return {
|
|
5989
5454
|
parsed: true,
|
|
@@ -5996,34 +5461,35 @@ var parse = function parse(input, options) {
|
|
|
5996
5461
|
};
|
|
5997
5462
|
}
|
|
5998
5463
|
};
|
|
5999
|
-
/* unit */
|
|
6000
5464
|
|
|
5465
|
+
/* unit */
|
|
6001
5466
|
function Unit(symbol) {
|
|
6002
5467
|
this.symbol = symbol;
|
|
6003
5468
|
}
|
|
6004
|
-
Unit.prototype = new Symbol();
|
|
5469
|
+
Unit.prototype = new Symbol();
|
|
5470
|
+
|
|
5471
|
+
// If possible, replace unit prefixes with a multiplication.
|
|
6005
5472
|
//
|
|
6006
5473
|
// "g" -> Unit("g")
|
|
6007
5474
|
// "kg" -> 1000 * Unit("g")
|
|
6008
|
-
|
|
6009
5475
|
var unprefixify = function unprefixify(symbol) {
|
|
6010
5476
|
if (_(baseUnits).has(symbol) || _(derivedUnits).has(symbol)) {
|
|
6011
5477
|
return new Unit(symbol);
|
|
6012
|
-
}
|
|
6013
|
-
|
|
5478
|
+
}
|
|
6014
5479
|
|
|
5480
|
+
// check for prefix
|
|
6015
5481
|
var prefix = _(_(siPrefixes).keys()).find(function (testPrefix) {
|
|
6016
5482
|
return new RegExp("^" + testPrefix).test(symbol);
|
|
6017
5483
|
});
|
|
6018
|
-
|
|
6019
5484
|
if (prefix) {
|
|
6020
|
-
var base = symbol.replace(new RegExp("^" + prefix), "");
|
|
5485
|
+
var base = symbol.replace(new RegExp("^" + prefix), "");
|
|
5486
|
+
|
|
5487
|
+
// It's okay to be here if either:
|
|
6021
5488
|
// * `base` is a base unit (the seven units listed in baseUnits)
|
|
6022
5489
|
// * `base` is a derived unit which allows prefixes
|
|
6023
5490
|
//
|
|
6024
5491
|
// Otherwise, we're trying to parse a unit label which is not
|
|
6025
5492
|
// allowed (mwk, mBTU, etc).
|
|
6026
|
-
|
|
6027
5493
|
if (_(baseUnits).has(base) || derivedUnits[base] && derivedUnits[base].prefixes === hasPrefixes) {
|
|
6028
5494
|
return new Mul(siPrefixes[prefix], new Unit(base));
|
|
6029
5495
|
} else {
|
|
@@ -6033,10 +5499,11 @@ var unprefixify = function unprefixify(symbol) {
|
|
|
6033
5499
|
return new Unit(symbol);
|
|
6034
5500
|
}
|
|
6035
5501
|
};
|
|
6036
|
-
|
|
6037
5502
|
var unitParse = function unitParse(input) {
|
|
6038
5503
|
try {
|
|
6039
|
-
var parseResult = unitParser.parse(input);
|
|
5504
|
+
var parseResult = unitParser.parse(input);
|
|
5505
|
+
|
|
5506
|
+
// parseResult looks like:
|
|
6040
5507
|
// {
|
|
6041
5508
|
// magnitude: "5",
|
|
6042
5509
|
// unit: {
|
|
@@ -6052,17 +5519,13 @@ var unitParse = function unitParse(input) {
|
|
|
6052
5519
|
// denom is optionally null
|
|
6053
5520
|
|
|
6054
5521
|
var unitArray = [];
|
|
6055
|
-
|
|
6056
5522
|
_(parseResult.unit.num).each(function (unitSpec) {
|
|
6057
5523
|
unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(unitSpec.pow)));
|
|
6058
5524
|
});
|
|
6059
|
-
|
|
6060
5525
|
_(parseResult.unit.denom).each(function (unitSpec) {
|
|
6061
5526
|
unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(-1 * unitSpec.pow)));
|
|
6062
5527
|
});
|
|
6063
|
-
|
|
6064
5528
|
var unit = new Mul(unitArray).flatten();
|
|
6065
|
-
|
|
6066
5529
|
if (parseResult.type === "unitMagnitude") {
|
|
6067
5530
|
// in the first case we have a magnitude coefficient as well as the
|
|
6068
5531
|
// unit itself.
|
|
@@ -6090,7 +5553,6 @@ var unitParse = function unitParse(input) {
|
|
|
6090
5553
|
};
|
|
6091
5554
|
}
|
|
6092
5555
|
};
|
|
6093
|
-
|
|
6094
5556
|
_.extend(Unit.prototype, {
|
|
6095
5557
|
func: Unit,
|
|
6096
5558
|
args: function args() {
|
|
@@ -6135,7 +5597,6 @@ _.extend(Unit.prototype, {
|
|
|
6135
5597
|
}
|
|
6136
5598
|
}
|
|
6137
5599
|
});
|
|
6138
|
-
|
|
6139
5600
|
var baseUnits = {
|
|
6140
5601
|
m: new Unit("m"),
|
|
6141
5602
|
// Note: kg is the SI base unit but we use g for consistency
|
|
@@ -6165,24 +5626,21 @@ var siPrefixes = {
|
|
|
6165
5626
|
E: new Pow(new Int(10), new Int(18)),
|
|
6166
5627
|
// http://en.wikipedia.org/wiki/Metric_prefix#.22Hella.22_prefix_proposal
|
|
6167
5628
|
hella: new Pow(new Int(10), new Int(27))
|
|
6168
|
-
};
|
|
5629
|
+
};
|
|
6169
5630
|
|
|
5631
|
+
// Use these two values to mark a unit as either SI-prefixable or not.
|
|
6170
5632
|
var hasPrefixes = {};
|
|
6171
5633
|
var hasntPrefixes = {};
|
|
6172
|
-
|
|
6173
5634
|
var makeAlias = function makeAlias(str, prefixes) {
|
|
6174
5635
|
var splits = str.split("|");
|
|
6175
5636
|
var coefficientStr = splits[0].trim();
|
|
6176
5637
|
var unitsStr = splits[1].trim();
|
|
6177
5638
|
var coefficient = Num.One;
|
|
6178
|
-
|
|
6179
5639
|
if (coefficientStr !== "") {
|
|
6180
5640
|
coefficient = parse(coefficientStr).expr;
|
|
6181
5641
|
}
|
|
6182
|
-
|
|
6183
5642
|
var numdenomStr = unitsStr.split("/");
|
|
6184
5643
|
var numdenom = [coefficient];
|
|
6185
|
-
|
|
6186
5644
|
if (numdenomStr[0]) {
|
|
6187
5645
|
numdenomStr[0].split(" ").filter(function (x) {
|
|
6188
5646
|
return x !== "";
|
|
@@ -6190,7 +5648,6 @@ var makeAlias = function makeAlias(str, prefixes) {
|
|
|
6190
5648
|
numdenom.push(new Unit(x));
|
|
6191
5649
|
});
|
|
6192
5650
|
}
|
|
6193
|
-
|
|
6194
5651
|
if (numdenomStr[1]) {
|
|
6195
5652
|
numdenomStr[1].split(" ").filter(function (x) {
|
|
6196
5653
|
return x !== "";
|
|
@@ -6198,12 +5655,13 @@ var makeAlias = function makeAlias(str, prefixes) {
|
|
|
6198
5655
|
numdenom.push(new Pow(new Unit(x), Num.Div));
|
|
6199
5656
|
});
|
|
6200
5657
|
}
|
|
6201
|
-
|
|
6202
5658
|
return {
|
|
6203
5659
|
conversion: new Mul(numdenom),
|
|
6204
5660
|
prefixes: prefixes
|
|
6205
5661
|
};
|
|
6206
|
-
};
|
|
5662
|
+
};
|
|
5663
|
+
|
|
5664
|
+
// This is a mapping of derived units (or different names for a unit) to their
|
|
6207
5665
|
// definitions. For example, an inch is defined as 0.0254 m.
|
|
6208
5666
|
//
|
|
6209
5667
|
// Definitions don't need to be in terms of base units. For example, tsp is
|
|
@@ -6225,148 +5683,147 @@ var makeAlias = function makeAlias(str, prefixes) {
|
|
|
6225
5683
|
//
|
|
6226
5684
|
// Where possible, these units are taken from "The International System of
|
|
6227
5685
|
// Units (SI)" 8th edition (2006).
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
5686
|
var derivedUnits = {
|
|
6231
5687
|
// mass
|
|
6232
5688
|
// The atomic mass unit / dalton.
|
|
6233
5689
|
Da: makeAlias("1.6605388628 x 10^-24 | g", hasPrefixes),
|
|
6234
5690
|
u: makeAlias("| Da", hasntPrefixes),
|
|
6235
5691
|
// length
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
5692
|
+
meter: makeAlias("| m", hasntPrefixes),
|
|
5693
|
+
meters: makeAlias("| m", hasntPrefixes),
|
|
5694
|
+
in: makeAlias("254 / 10000 | m", hasntPrefixes),
|
|
5695
|
+
ft: makeAlias("3048 / 10000 | m", hasntPrefixes),
|
|
5696
|
+
yd: makeAlias("9144 / 10000 | m", hasntPrefixes),
|
|
5697
|
+
mi: makeAlias("1609344 / 1000 | m", hasntPrefixes),
|
|
5698
|
+
ly: makeAlias("9.4607 x 10^15 | m", hasntPrefixes),
|
|
5699
|
+
nmi: makeAlias("1852 | m", hasntPrefixes),
|
|
5700
|
+
Å: makeAlias("10^-10 | m", hasntPrefixes),
|
|
5701
|
+
pc: makeAlias("3.0857 x 10^16 | m", hasntPrefixes),
|
|
6246
5702
|
// time
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
5703
|
+
min: makeAlias("60 | s", hasntPrefixes),
|
|
5704
|
+
hr: makeAlias("3600 | s", hasntPrefixes),
|
|
5705
|
+
sec: makeAlias("| s", hasntPrefixes),
|
|
6250
5706
|
// TODO(joel) make day work
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
5707
|
+
day: makeAlias("86400 | s", hasntPrefixes),
|
|
5708
|
+
wk: makeAlias("604800 | s", hasntPrefixes),
|
|
5709
|
+
fortnight: makeAlias("14 | day", hasntPrefixes),
|
|
5710
|
+
shake: makeAlias("10^-8 | s", hasntPrefixes),
|
|
5711
|
+
olympiad: makeAlias("126200000 | s", hasntPrefixes),
|
|
6256
5712
|
// temperature
|
|
6257
5713
|
"°C": makeAlias("1 | K", hasntPrefixes),
|
|
6258
5714
|
"°F": makeAlias("5/9 | K", hasntPrefixes),
|
|
6259
5715
|
"°R": makeAlias("5/9 | K", hasntPrefixes),
|
|
6260
5716
|
// electric charge
|
|
6261
|
-
|
|
5717
|
+
e: makeAlias("1.6021765314 x 10^-19 | C", hasntPrefixes),
|
|
6262
5718
|
// speed
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
5719
|
+
c: makeAlias("299792458 | m / s", hasntPrefixes),
|
|
5720
|
+
kn: makeAlias("514/1000 | m / s", hasntPrefixes),
|
|
5721
|
+
kt: makeAlias("| kn", hasntPrefixes),
|
|
5722
|
+
knot: makeAlias("| kn", hasntPrefixes),
|
|
6267
5723
|
// energy
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
5724
|
+
J: makeAlias("| N m", hasPrefixes),
|
|
5725
|
+
BTU: makeAlias("1060 | J", hasntPrefixes),
|
|
5726
|
+
cal: makeAlias("4184 / 1000 | J", hasPrefixes),
|
|
5727
|
+
eV: makeAlias("1.602176514 x 10^-19 | J", hasPrefixes),
|
|
5728
|
+
erg: makeAlias("10^−7 | J", hasPrefixes),
|
|
6273
5729
|
// power
|
|
6274
|
-
|
|
5730
|
+
W: makeAlias("| J / s", hasPrefixes),
|
|
6275
5731
|
"H-e": makeAlias("80 | W", hasntPrefixes),
|
|
6276
5732
|
// force
|
|
6277
|
-
|
|
5733
|
+
N: makeAlias("1000 | g m / s s", hasPrefixes),
|
|
6278
5734
|
// "lb": makeAlias("4448 / 1000 | N", hasntPrefixes),
|
|
6279
5735
|
// 4.4482216152605
|
|
6280
|
-
|
|
6281
|
-
|
|
5736
|
+
lb: makeAlias("4448221615 / 1000000000 | N", hasntPrefixes),
|
|
5737
|
+
dyn: makeAlias("10^-5 | N", hasntPrefixes),
|
|
6282
5738
|
// pressure
|
|
6283
|
-
|
|
6284
|
-
|
|
5739
|
+
Pa: makeAlias("1 | N / m m m", hasPrefixes),
|
|
5740
|
+
bar: makeAlias("10^5 | Pa", hasPrefixes),
|
|
6285
5741
|
"㏔": makeAlias("1/1000 | bar", hasntPrefixes),
|
|
6286
5742
|
"㍴": makeAlias("| bar", hasntPrefixes),
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
5743
|
+
atm: makeAlias("101325 | Pa", hasntPrefixes),
|
|
5744
|
+
Torr: makeAlias("1/760 | atm", hasntPrefixes),
|
|
5745
|
+
mmHg: makeAlias("| Torr", hasntPrefixes),
|
|
6290
5746
|
// area
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
5747
|
+
ha: makeAlias("10^4 | m m", hasntPrefixes),
|
|
5748
|
+
b: makeAlias("10^−28 | m m", hasPrefixes),
|
|
5749
|
+
barn: makeAlias("| b", hasPrefixes),
|
|
5750
|
+
acre: makeAlias("4046.87 | m m", hasntPrefixes),
|
|
5751
|
+
skilodge: makeAlias("10^-31 | m m", hasntPrefixes),
|
|
5752
|
+
outhouse: makeAlias("10^-34 | m m", hasntPrefixes),
|
|
5753
|
+
shed: makeAlias("10^-52 | m m", hasntPrefixes),
|
|
6298
5754
|
// volume
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
5755
|
+
L: makeAlias("1/1000 | m m m", hasPrefixes),
|
|
5756
|
+
gal: makeAlias("3785/1000 | L", hasPrefixes),
|
|
5757
|
+
cup: makeAlias("1/16 | gal", hasntPrefixes),
|
|
5758
|
+
qt: makeAlias("1/4 | gal", hasntPrefixes),
|
|
5759
|
+
quart: makeAlias("| qt", hasntPrefixes),
|
|
5760
|
+
p: makeAlias("1/8 | gal", hasntPrefixes),
|
|
5761
|
+
pt: makeAlias("| p", hasntPrefixes),
|
|
5762
|
+
pint: makeAlias("| p", hasntPrefixes),
|
|
6307
5763
|
"fl oz": makeAlias("1/8 | cup", hasntPrefixes),
|
|
6308
5764
|
"fl. oz.": makeAlias("1/8 | cup", hasntPrefixes),
|
|
6309
|
-
|
|
6310
|
-
|
|
5765
|
+
tbsp: makeAlias("1/16 | cup", hasntPrefixes),
|
|
5766
|
+
tsp: makeAlias("1/3 | tbsp", hasntPrefixes),
|
|
6311
5767
|
// rotational
|
|
6312
5768
|
// "rad":
|
|
6313
|
-
|
|
6314
|
-
|
|
5769
|
+
rev: makeAlias("2 pi | rad", hasntPrefixes),
|
|
5770
|
+
deg: makeAlias("180 pi | rad", hasntPrefixes),
|
|
6315
5771
|
"°": makeAlias("| deg", hasntPrefixes),
|
|
6316
|
-
|
|
6317
|
-
|
|
5772
|
+
arcminute: makeAlias("1/60 | deg", hasntPrefixes),
|
|
5773
|
+
arcsec: makeAlias("1/3600 | deg", hasntPrefixes),
|
|
6318
5774
|
// dimensionless
|
|
6319
5775
|
// "B": makeAlias("10 | dB", hasntPrefixes), // XXX danger - logarithmic
|
|
6320
5776
|
// "dB"
|
|
6321
5777
|
// "nP"
|
|
6322
|
-
|
|
6323
|
-
|
|
5778
|
+
Hu: makeAlias("1000 | dB", hasPrefixes),
|
|
5779
|
+
dozen: makeAlias("12 |", hasntPrefixes),
|
|
6324
5780
|
// XXX
|
|
6325
|
-
|
|
5781
|
+
mol: makeAlias("6.0221412927 x 10^23 |", hasPrefixes),
|
|
6326
5782
|
"%": makeAlias("1/100 |", hasntPrefixes),
|
|
6327
|
-
|
|
6328
|
-
|
|
5783
|
+
percent: makeAlias("| %", hasntPrefixes),
|
|
5784
|
+
ppm: makeAlias("1/1000000 |", hasntPrefixes),
|
|
6329
5785
|
// electric / magnetic
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
5786
|
+
V: makeAlias("1000 | g m m / s s C", hasPrefixes),
|
|
5787
|
+
C: makeAlias("| A s", hasPrefixes),
|
|
5788
|
+
ampere: makeAlias("| A", hasntPrefixes),
|
|
5789
|
+
Ω: makeAlias("| V / A", hasPrefixes),
|
|
5790
|
+
ohm: makeAlias("| Ω", hasntPrefixes),
|
|
5791
|
+
F: makeAlias("| C / V", hasPrefixes),
|
|
5792
|
+
H: makeAlias("| ohm s", hasPrefixes),
|
|
5793
|
+
T: makeAlias("1000 | g / C s", hasPrefixes),
|
|
5794
|
+
Wb: makeAlias("1000 | g m m / C s", hasPrefixes),
|
|
6339
5795
|
// photometry
|
|
6340
5796
|
// TODO not sure this is right
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
5797
|
+
lm: makeAlias("pi x 10^4 | cd / m m", hasntPrefixes),
|
|
5798
|
+
lx: makeAlias("| lm / m m", hasntPrefixes),
|
|
5799
|
+
nit: makeAlias("| cd / m m", hasntPrefixes),
|
|
5800
|
+
sb: makeAlias("10^4 | cd / m m", hasntPrefixes),
|
|
5801
|
+
stilb: makeAlias("1 | sb", hasntPrefixes),
|
|
5802
|
+
apostilb: makeAlias("1 / pi x 10^(-4) | sb", hasntPrefixes),
|
|
5803
|
+
blondel: makeAlias("| apostilb", hasntPrefixes),
|
|
5804
|
+
asb: makeAlias("| apostilb", hasntPrefixes),
|
|
5805
|
+
la: makeAlias("| lm", hasntPrefixes),
|
|
5806
|
+
Lb: makeAlias("| lm", hasntPrefixes),
|
|
5807
|
+
sk: makeAlias("10^-7 | lm", hasntPrefixes),
|
|
5808
|
+
skot: makeAlias("| sk", hasntPrefixes),
|
|
5809
|
+
bril: makeAlias("10^-11 | lm", hasntPrefixes),
|
|
6354
5810
|
// other
|
|
6355
|
-
|
|
5811
|
+
Hz: makeAlias("| / s", hasPrefixes)
|
|
6356
5812
|
};
|
|
6357
5813
|
var Zero = Num.Zero;
|
|
6358
5814
|
var One = Num.One;
|
|
6359
5815
|
|
|
6360
5816
|
/* eslint-disable */
|
|
6361
|
-
// TODO(alex): be able to pass a random() function to compare()
|
|
6362
5817
|
|
|
5818
|
+
// Assumes that both expressions have already been parsed
|
|
5819
|
+
// TODO(alex): be able to pass a random() function to compare()
|
|
6363
5820
|
var compare = function compare(expr1, expr2, options) {
|
|
6364
5821
|
var defaults = {
|
|
6365
5822
|
form: false,
|
|
6366
5823
|
// Check that the two expressions have the same form
|
|
6367
5824
|
simplify: false // Check that the second expression is simplified
|
|
6368
|
-
|
|
6369
5825
|
};
|
|
5826
|
+
|
|
6370
5827
|
/* Options that could be added in the future:
|
|
6371
5828
|
* - Allow ratios: e.g. 3/1 and 3 should both be accepted for something
|
|
6372
5829
|
* like slope
|
|
@@ -6378,53 +5835,50 @@ var compare = function compare(expr1, expr2, options) {
|
|
|
6378
5835
|
options = _.extend(defaults, options);
|
|
6379
5836
|
} else {
|
|
6380
5837
|
options = defaults;
|
|
6381
|
-
}
|
|
6382
|
-
// Variable check
|
|
5838
|
+
}
|
|
6383
5839
|
|
|
5840
|
+
// TODO(CP-1614): Figure out how to make these messages translatable
|
|
6384
5841
|
|
|
5842
|
+
// Variable check
|
|
6385
5843
|
var vars = expr1.sameVars(expr2);
|
|
6386
|
-
|
|
6387
5844
|
if (!vars.equal) {
|
|
6388
5845
|
var message;
|
|
6389
|
-
|
|
6390
5846
|
if (vars.equalIgnoringCase) {
|
|
6391
5847
|
message = "Check your variables; one or more are using " + "the wrong case (upper or lower).";
|
|
6392
5848
|
} else {
|
|
6393
5849
|
message = "Check your variables; you may have used the wrong " + "letter for one or more of them.";
|
|
6394
5850
|
}
|
|
6395
|
-
|
|
6396
5851
|
return {
|
|
6397
5852
|
equal: false,
|
|
6398
5853
|
wrongVariableCase: vars.equalIgnoringCase,
|
|
6399
5854
|
wrongVariableNames: !vars.equalIgnoringCase,
|
|
6400
5855
|
message: message
|
|
6401
5856
|
};
|
|
6402
|
-
}
|
|
6403
|
-
|
|
5857
|
+
}
|
|
6404
5858
|
|
|
5859
|
+
// Semantic check
|
|
6405
5860
|
if (!expr1.compare(expr2)) {
|
|
6406
5861
|
return {
|
|
6407
5862
|
equal: false,
|
|
6408
5863
|
message: null
|
|
6409
5864
|
};
|
|
6410
|
-
}
|
|
6411
|
-
|
|
5865
|
+
}
|
|
6412
5866
|
|
|
5867
|
+
// Syntactic check
|
|
6413
5868
|
if (options.form && !expr1.sameForm(expr2)) {
|
|
6414
5869
|
return {
|
|
6415
5870
|
equal: false,
|
|
6416
5871
|
message: "Your answer is not in the correct form."
|
|
6417
5872
|
};
|
|
6418
|
-
}
|
|
6419
|
-
|
|
5873
|
+
}
|
|
6420
5874
|
|
|
5875
|
+
// Syntactic check
|
|
6421
5876
|
if (options.simplify && !expr1.isSimplified()) {
|
|
6422
5877
|
return {
|
|
6423
5878
|
equal: false,
|
|
6424
5879
|
message: "Your answer is not fully expanded and simplified."
|
|
6425
5880
|
};
|
|
6426
5881
|
}
|
|
6427
|
-
|
|
6428
5882
|
return {
|
|
6429
5883
|
equal: true,
|
|
6430
5884
|
message: null
|