@khanacademy/kas 0.5.1 → 1.0.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/dist/index.js +399 -1421
- package/dist/index.js.map +1 -1
- package/package.json +13 -6
- package/dist/es/index.js +0 -5954
- package/dist/es/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,98 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-utils';
|
|
2
|
+
import _extends from '@babel/runtime/helpers/extends';
|
|
3
|
+
import _ from 'underscore';
|
|
2
4
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var perseusUtils = require('@khanacademy/perseus-utils');
|
|
6
|
-
var _ = require('underscore');
|
|
7
|
-
|
|
8
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
-
|
|
10
|
-
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
|
|
11
|
-
|
|
12
|
-
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
13
5
|
const libName = "@khanacademy/kas";
|
|
14
|
-
const libVersion = "0.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// this is a @generated file
|
|
18
|
-
|
|
19
|
-
/* parser generated by jison 0.4.15 */
|
|
20
|
-
/*
|
|
21
|
-
Returns a Parser object of the following structure:
|
|
22
|
-
|
|
23
|
-
Parser: {
|
|
24
|
-
yy: {}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
Parser.prototype: {
|
|
28
|
-
yy: {},
|
|
29
|
-
trace: function(),
|
|
30
|
-
symbols_: {associative list: name ==> number},
|
|
31
|
-
terminals_: {associative list: number ==> name},
|
|
32
|
-
productions_: [...],
|
|
33
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
|
|
34
|
-
table: [...],
|
|
35
|
-
defaultActions: {...},
|
|
36
|
-
parseError: function(str, hash),
|
|
37
|
-
parse: function(input),
|
|
38
|
-
|
|
39
|
-
lexer: {
|
|
40
|
-
EOF: 1,
|
|
41
|
-
parseError: function(str, hash),
|
|
42
|
-
setInput: function(input),
|
|
43
|
-
input: function(),
|
|
44
|
-
unput: function(str),
|
|
45
|
-
more: function(),
|
|
46
|
-
less: function(n),
|
|
47
|
-
pastInput: function(),
|
|
48
|
-
upcomingInput: function(),
|
|
49
|
-
showPosition: function(),
|
|
50
|
-
test_match: function(regex_match_array, rule_index),
|
|
51
|
-
next: function(),
|
|
52
|
-
lex: function(),
|
|
53
|
-
begin: function(condition),
|
|
54
|
-
popState: function(),
|
|
55
|
-
_currentRules: function(),
|
|
56
|
-
topState: function(),
|
|
57
|
-
pushState: function(condition),
|
|
58
|
-
|
|
59
|
-
options: {
|
|
60
|
-
ranges: boolean (optional: true ==> token location info will include a .range[] member)
|
|
61
|
-
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
|
|
62
|
-
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
|
|
66
|
-
rules: [...],
|
|
67
|
-
conditions: {associative list: name ==> set},
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
token location info (@$, _$, etc.): {
|
|
73
|
-
first_line: n,
|
|
74
|
-
last_line: n,
|
|
75
|
-
first_column: n,
|
|
76
|
-
last_column: n,
|
|
77
|
-
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
|
|
78
|
-
}
|
|
79
|
-
|
|
6
|
+
const libVersion = "1.0.0";
|
|
7
|
+
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
80
8
|
|
|
81
|
-
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
|
|
82
|
-
text: (matched text)
|
|
83
|
-
token: (the produced terminal token, if any)
|
|
84
|
-
line: (yylineno)
|
|
85
|
-
}
|
|
86
|
-
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
|
|
87
|
-
loc: (yylloc)
|
|
88
|
-
expected: (string describing the set of expected tokens)
|
|
89
|
-
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
|
|
90
|
-
}
|
|
91
|
-
*/
|
|
92
9
|
var parser$1 = function () {
|
|
93
|
-
var o = function (k, v,
|
|
94
|
-
for (
|
|
95
|
-
return
|
|
10
|
+
var o = function o(k, v, _o, l) {
|
|
11
|
+
for (_o = _o || {}, l = k.length; l--; _o[k[l]] = v);
|
|
12
|
+
return _o;
|
|
96
13
|
},
|
|
97
14
|
$V0 = [1, 11],
|
|
98
15
|
$V1 = [1, 9],
|
|
@@ -138,9 +55,7 @@ var parser$1 = function () {
|
|
|
138
55
|
20: "NEG"
|
|
139
56
|
},
|
|
140
57
|
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]],
|
|
141
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate
|
|
142
|
-
/* this == yyval */
|
|
143
|
-
|
|
58
|
+
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
|
|
144
59
|
var $0 = $$.length - 1;
|
|
145
60
|
switch (yystate) {
|
|
146
61
|
case 1:
|
|
@@ -325,7 +240,6 @@ var parser$1 = function () {
|
|
|
325
240
|
} else {
|
|
326
241
|
this.parseError = Object.getPrototypeOf(this).parseError;
|
|
327
242
|
}
|
|
328
|
-
//_token_stack:
|
|
329
243
|
function lex() {
|
|
330
244
|
var token;
|
|
331
245
|
token = lexer.lex() || EOF;
|
|
@@ -425,7 +339,6 @@ var parser$1 = function () {
|
|
|
425
339
|
return true;
|
|
426
340
|
}
|
|
427
341
|
};
|
|
428
|
-
/* generated by jison-lex 0.3.4 */
|
|
429
342
|
var lexer = function () {
|
|
430
343
|
var lexer = {
|
|
431
344
|
EOF: 1,
|
|
@@ -436,7 +349,6 @@ var parser$1 = function () {
|
|
|
436
349
|
throw new Error(str);
|
|
437
350
|
}
|
|
438
351
|
},
|
|
439
|
-
// resets the lexer, sets new input
|
|
440
352
|
setInput: function (input, yy) {
|
|
441
353
|
this.yy = yy || this.yy || {};
|
|
442
354
|
this._input = input;
|
|
@@ -456,7 +368,6 @@ var parser$1 = function () {
|
|
|
456
368
|
this.offset = 0;
|
|
457
369
|
return this;
|
|
458
370
|
},
|
|
459
|
-
// consumes and returns one char from the input
|
|
460
371
|
input: function () {
|
|
461
372
|
var ch = this._input[0];
|
|
462
373
|
this.yytext += ch;
|
|
@@ -477,13 +388,11 @@ var parser$1 = function () {
|
|
|
477
388
|
this._input = this._input.slice(1);
|
|
478
389
|
return ch;
|
|
479
390
|
},
|
|
480
|
-
// unshifts one char (or a string) into the input
|
|
481
391
|
unput: function (ch) {
|
|
482
392
|
var len = ch.length;
|
|
483
393
|
var lines = ch.split(/(?:\r\n?|\n)/g);
|
|
484
394
|
this._input = ch + this._input;
|
|
485
395
|
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
486
|
-
//this.yyleng -= len;
|
|
487
396
|
this.offset -= len;
|
|
488
397
|
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
|
|
489
398
|
this.match = this.match.substr(0, this.match.length - 1);
|
|
@@ -504,12 +413,10 @@ var parser$1 = function () {
|
|
|
504
413
|
this.yyleng = this.yytext.length;
|
|
505
414
|
return this;
|
|
506
415
|
},
|
|
507
|
-
// When called from action, caches matched text and appends it on next action
|
|
508
416
|
more: function () {
|
|
509
417
|
this._more = true;
|
|
510
418
|
return this;
|
|
511
419
|
},
|
|
512
|
-
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
|
513
420
|
reject: function () {
|
|
514
421
|
if (this.options.backtrack_lexer) {
|
|
515
422
|
this._backtrack = true;
|
|
@@ -522,16 +429,13 @@ var parser$1 = function () {
|
|
|
522
429
|
}
|
|
523
430
|
return this;
|
|
524
431
|
},
|
|
525
|
-
// retain first n characters of the match
|
|
526
432
|
less: function (n) {
|
|
527
433
|
this.unput(this.match.slice(n));
|
|
528
434
|
},
|
|
529
|
-
// displays already matched input, i.e. for error messages
|
|
530
435
|
pastInput: function () {
|
|
531
436
|
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
|
532
437
|
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
|
|
533
438
|
},
|
|
534
|
-
// displays upcoming input, i.e. for error messages
|
|
535
439
|
upcomingInput: function () {
|
|
536
440
|
var next = this.match;
|
|
537
441
|
if (next.length < 20) {
|
|
@@ -539,17 +443,14 @@ var parser$1 = function () {
|
|
|
539
443
|
}
|
|
540
444
|
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
|
|
541
445
|
},
|
|
542
|
-
// displays the character position where the lexing error occurred, i.e. for error messages
|
|
543
446
|
showPosition: function () {
|
|
544
447
|
var pre = this.pastInput();
|
|
545
448
|
var c = new Array(pre.length + 1).join("-");
|
|
546
449
|
return pre + this.upcomingInput() + "\n" + c + "^";
|
|
547
450
|
},
|
|
548
|
-
// test the lexed token: return FALSE when not a match, otherwise return token
|
|
549
451
|
test_match: function (match, indexed_rule) {
|
|
550
452
|
var token, lines, backup;
|
|
551
453
|
if (this.options.backtrack_lexer) {
|
|
552
|
-
// save context
|
|
553
454
|
backup = {
|
|
554
455
|
yylineno: this.yylineno,
|
|
555
456
|
yylloc: {
|
|
@@ -602,15 +503,13 @@ var parser$1 = function () {
|
|
|
602
503
|
if (token) {
|
|
603
504
|
return token;
|
|
604
505
|
} else if (this._backtrack) {
|
|
605
|
-
// recover context
|
|
606
506
|
for (var k in backup) {
|
|
607
507
|
this[k] = backup[k];
|
|
608
508
|
}
|
|
609
|
-
return false;
|
|
509
|
+
return false;
|
|
610
510
|
}
|
|
611
511
|
return false;
|
|
612
512
|
},
|
|
613
|
-
// return next match in input
|
|
614
513
|
next: function () {
|
|
615
514
|
if (this.done) {
|
|
616
515
|
return this.EOF;
|
|
@@ -635,9 +534,8 @@ var parser$1 = function () {
|
|
|
635
534
|
return token;
|
|
636
535
|
} else if (this._backtrack) {
|
|
637
536
|
match = false;
|
|
638
|
-
continue;
|
|
537
|
+
continue;
|
|
639
538
|
} else {
|
|
640
|
-
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
641
539
|
return false;
|
|
642
540
|
}
|
|
643
541
|
} else if (!this.options.flex) {
|
|
@@ -650,7 +548,6 @@ var parser$1 = function () {
|
|
|
650
548
|
if (token !== false) {
|
|
651
549
|
return token;
|
|
652
550
|
}
|
|
653
|
-
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
654
551
|
return false;
|
|
655
552
|
}
|
|
656
553
|
if (this._input === "") {
|
|
@@ -663,7 +560,6 @@ var parser$1 = function () {
|
|
|
663
560
|
});
|
|
664
561
|
}
|
|
665
562
|
},
|
|
666
|
-
// return next match that has a token
|
|
667
563
|
lex: function lex() {
|
|
668
564
|
var r = this.next();
|
|
669
565
|
if (r) {
|
|
@@ -672,11 +568,9 @@ var parser$1 = function () {
|
|
|
672
568
|
return this.lex();
|
|
673
569
|
}
|
|
674
570
|
},
|
|
675
|
-
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
|
676
571
|
begin: function begin(condition) {
|
|
677
572
|
this.conditionStack.push(condition);
|
|
678
573
|
},
|
|
679
|
-
// pop the previously active lexer condition state off the condition stack
|
|
680
574
|
popState: function popState() {
|
|
681
575
|
var n = this.conditionStack.length - 1;
|
|
682
576
|
if (n > 0) {
|
|
@@ -685,7 +579,6 @@ var parser$1 = function () {
|
|
|
685
579
|
return this.conditionStack[0];
|
|
686
580
|
}
|
|
687
581
|
},
|
|
688
|
-
// produce the lexer rule set which is active for the currently active lexer condition state
|
|
689
582
|
_currentRules: function _currentRules() {
|
|
690
583
|
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
|
691
584
|
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
|
|
@@ -693,7 +586,6 @@ var parser$1 = function () {
|
|
|
693
586
|
return this.conditions["INITIAL"].rules;
|
|
694
587
|
}
|
|
695
588
|
},
|
|
696
|
-
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
|
697
589
|
topState: function topState(n) {
|
|
698
590
|
n = this.conditionStack.length - 1 - Math.abs(n || 0);
|
|
699
591
|
if (n >= 0) {
|
|
@@ -702,11 +594,9 @@ var parser$1 = function () {
|
|
|
702
594
|
return "INITIAL";
|
|
703
595
|
}
|
|
704
596
|
},
|
|
705
|
-
// alias for begin(condition)
|
|
706
597
|
pushState: function pushState(condition) {
|
|
707
598
|
this.begin(condition);
|
|
708
599
|
},
|
|
709
|
-
// return the number of states currently on the stack
|
|
710
600
|
stateStackSize: function stateStackSize() {
|
|
711
601
|
return this.conditionStack.length;
|
|
712
602
|
},
|
|
@@ -738,7 +628,6 @@ var parser$1 = function () {
|
|
|
738
628
|
case 11:
|
|
739
629
|
return 17;
|
|
740
630
|
case 12:
|
|
741
|
-
/* skip whitespace */
|
|
742
631
|
break;
|
|
743
632
|
case 13:
|
|
744
633
|
return 6;
|
|
@@ -764,85 +653,10 @@ var parser$1 = function () {
|
|
|
764
653
|
}();
|
|
765
654
|
const unitParser = parser$1;
|
|
766
655
|
|
|
767
|
-
// This is a @generated file
|
|
768
|
-
|
|
769
|
-
/* parser generated by jison 0.4.15 */
|
|
770
|
-
/*
|
|
771
|
-
Returns a Parser object of the following structure:
|
|
772
|
-
|
|
773
|
-
Parser: {
|
|
774
|
-
yy: {}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
Parser.prototype: {
|
|
778
|
-
yy: {},
|
|
779
|
-
trace: function(),
|
|
780
|
-
symbols_: {associative list: name ==> number},
|
|
781
|
-
terminals_: {associative list: number ==> name},
|
|
782
|
-
productions_: [...],
|
|
783
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
|
|
784
|
-
table: [...],
|
|
785
|
-
defaultActions: {...},
|
|
786
|
-
parseError: function(str, hash),
|
|
787
|
-
parse: function(input),
|
|
788
|
-
|
|
789
|
-
lexer: {
|
|
790
|
-
EOF: 1,
|
|
791
|
-
parseError: function(str, hash),
|
|
792
|
-
setInput: function(input),
|
|
793
|
-
input: function(),
|
|
794
|
-
unput: function(str),
|
|
795
|
-
more: function(),
|
|
796
|
-
less: function(n),
|
|
797
|
-
pastInput: function(),
|
|
798
|
-
upcomingInput: function(),
|
|
799
|
-
showPosition: function(),
|
|
800
|
-
test_match: function(regex_match_array, rule_index),
|
|
801
|
-
next: function(),
|
|
802
|
-
lex: function(),
|
|
803
|
-
begin: function(condition),
|
|
804
|
-
popState: function(),
|
|
805
|
-
_currentRules: function(),
|
|
806
|
-
topState: function(),
|
|
807
|
-
pushState: function(condition),
|
|
808
|
-
|
|
809
|
-
options: {
|
|
810
|
-
ranges: boolean (optional: true ==> token location info will include a .range[] member)
|
|
811
|
-
flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
|
|
812
|
-
backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
|
|
813
|
-
},
|
|
814
|
-
|
|
815
|
-
performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
|
|
816
|
-
rules: [...],
|
|
817
|
-
conditions: {associative list: name ==> set},
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
token location info (@$, _$, etc.): {
|
|
823
|
-
first_line: n,
|
|
824
|
-
last_line: n,
|
|
825
|
-
first_column: n,
|
|
826
|
-
last_column: n,
|
|
827
|
-
range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
the parseError function receives a 'hash' object with these members for lexer and parser errors: {
|
|
832
|
-
text: (matched text)
|
|
833
|
-
token: (the produced terminal token, if any)
|
|
834
|
-
line: (yylineno)
|
|
835
|
-
}
|
|
836
|
-
while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
|
|
837
|
-
loc: (yylloc)
|
|
838
|
-
expected: (string describing the set of expected tokens)
|
|
839
|
-
recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
|
|
840
|
-
}
|
|
841
|
-
*/
|
|
842
656
|
var parser = function () {
|
|
843
|
-
var o = function (k, v,
|
|
844
|
-
for (
|
|
845
|
-
return
|
|
657
|
+
var o = function o(k, v, _o, l) {
|
|
658
|
+
for (_o = _o || {}, l = k.length; l--; _o[k[l]] = v);
|
|
659
|
+
return _o;
|
|
846
660
|
},
|
|
847
661
|
$V0 = [1, 7],
|
|
848
662
|
$V1 = [1, 17],
|
|
@@ -954,9 +768,7 @@ var parser = function () {
|
|
|
954
768
|
46: "FRAC"
|
|
955
769
|
},
|
|
956
770
|
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]],
|
|
957
|
-
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate
|
|
958
|
-
/* this == yyval */
|
|
959
|
-
|
|
771
|
+
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
|
|
960
772
|
var $0 = $$.length - 1;
|
|
961
773
|
switch (yystate) {
|
|
962
774
|
case 1:
|
|
@@ -2002,7 +1814,6 @@ var parser = function () {
|
|
|
2002
1814
|
} else {
|
|
2003
1815
|
this.parseError = Object.getPrototypeOf(this).parseError;
|
|
2004
1816
|
}
|
|
2005
|
-
//_token_stack:
|
|
2006
1817
|
function lex() {
|
|
2007
1818
|
var token;
|
|
2008
1819
|
token = lexer.lex() || EOF;
|
|
@@ -2102,7 +1913,6 @@ var parser = function () {
|
|
|
2102
1913
|
return true;
|
|
2103
1914
|
}
|
|
2104
1915
|
};
|
|
2105
|
-
/* generated by jison-lex 0.3.4 */
|
|
2106
1916
|
var lexer = function () {
|
|
2107
1917
|
var lexer = {
|
|
2108
1918
|
EOF: 1,
|
|
@@ -2113,7 +1923,6 @@ var parser = function () {
|
|
|
2113
1923
|
throw new Error(str);
|
|
2114
1924
|
}
|
|
2115
1925
|
},
|
|
2116
|
-
// resets the lexer, sets new input
|
|
2117
1926
|
setInput: function (input, yy) {
|
|
2118
1927
|
this.yy = yy || this.yy || {};
|
|
2119
1928
|
this._input = input;
|
|
@@ -2133,7 +1942,6 @@ var parser = function () {
|
|
|
2133
1942
|
this.offset = 0;
|
|
2134
1943
|
return this;
|
|
2135
1944
|
},
|
|
2136
|
-
// consumes and returns one char from the input
|
|
2137
1945
|
input: function () {
|
|
2138
1946
|
var ch = this._input[0];
|
|
2139
1947
|
this.yytext += ch;
|
|
@@ -2154,13 +1962,11 @@ var parser = function () {
|
|
|
2154
1962
|
this._input = this._input.slice(1);
|
|
2155
1963
|
return ch;
|
|
2156
1964
|
},
|
|
2157
|
-
// unshifts one char (or a string) into the input
|
|
2158
1965
|
unput: function (ch) {
|
|
2159
1966
|
var len = ch.length;
|
|
2160
1967
|
var lines = ch.split(/(?:\r\n?|\n)/g);
|
|
2161
1968
|
this._input = ch + this._input;
|
|
2162
1969
|
this.yytext = this.yytext.substr(0, this.yytext.length - len);
|
|
2163
|
-
//this.yyleng -= len;
|
|
2164
1970
|
this.offset -= len;
|
|
2165
1971
|
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
|
|
2166
1972
|
this.match = this.match.substr(0, this.match.length - 1);
|
|
@@ -2181,12 +1987,10 @@ var parser = function () {
|
|
|
2181
1987
|
this.yyleng = this.yytext.length;
|
|
2182
1988
|
return this;
|
|
2183
1989
|
},
|
|
2184
|
-
// When called from action, caches matched text and appends it on next action
|
|
2185
1990
|
more: function () {
|
|
2186
1991
|
this._more = true;
|
|
2187
1992
|
return this;
|
|
2188
1993
|
},
|
|
2189
|
-
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
|
|
2190
1994
|
reject: function () {
|
|
2191
1995
|
if (this.options.backtrack_lexer) {
|
|
2192
1996
|
this._backtrack = true;
|
|
@@ -2199,16 +2003,13 @@ var parser = function () {
|
|
|
2199
2003
|
}
|
|
2200
2004
|
return this;
|
|
2201
2005
|
},
|
|
2202
|
-
// retain first n characters of the match
|
|
2203
2006
|
less: function (n) {
|
|
2204
2007
|
this.unput(this.match.slice(n));
|
|
2205
2008
|
},
|
|
2206
|
-
// displays already matched input, i.e. for error messages
|
|
2207
2009
|
pastInput: function () {
|
|
2208
2010
|
var past = this.matched.substr(0, this.matched.length - this.match.length);
|
|
2209
2011
|
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
|
|
2210
2012
|
},
|
|
2211
|
-
// displays upcoming input, i.e. for error messages
|
|
2212
2013
|
upcomingInput: function () {
|
|
2213
2014
|
var next = this.match;
|
|
2214
2015
|
if (next.length < 20) {
|
|
@@ -2216,17 +2017,14 @@ var parser = function () {
|
|
|
2216
2017
|
}
|
|
2217
2018
|
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
|
|
2218
2019
|
},
|
|
2219
|
-
// displays the character position where the lexing error occurred, i.e. for error messages
|
|
2220
2020
|
showPosition: function () {
|
|
2221
2021
|
var pre = this.pastInput();
|
|
2222
2022
|
var c = new Array(pre.length + 1).join("-");
|
|
2223
2023
|
return pre + this.upcomingInput() + "\n" + c + "^";
|
|
2224
2024
|
},
|
|
2225
|
-
// test the lexed token: return FALSE when not a match, otherwise return token
|
|
2226
2025
|
test_match: function (match, indexed_rule) {
|
|
2227
2026
|
var token, lines, backup;
|
|
2228
2027
|
if (this.options.backtrack_lexer) {
|
|
2229
|
-
// save context
|
|
2230
2028
|
backup = {
|
|
2231
2029
|
yylineno: this.yylineno,
|
|
2232
2030
|
yylloc: {
|
|
@@ -2279,15 +2077,13 @@ var parser = function () {
|
|
|
2279
2077
|
if (token) {
|
|
2280
2078
|
return token;
|
|
2281
2079
|
} else if (this._backtrack) {
|
|
2282
|
-
// recover context
|
|
2283
2080
|
for (var k in backup) {
|
|
2284
2081
|
this[k] = backup[k];
|
|
2285
2082
|
}
|
|
2286
|
-
return false;
|
|
2083
|
+
return false;
|
|
2287
2084
|
}
|
|
2288
2085
|
return false;
|
|
2289
2086
|
},
|
|
2290
|
-
// return next match in input
|
|
2291
2087
|
next: function () {
|
|
2292
2088
|
if (this.done) {
|
|
2293
2089
|
return this.EOF;
|
|
@@ -2312,9 +2108,8 @@ var parser = function () {
|
|
|
2312
2108
|
return token;
|
|
2313
2109
|
} else if (this._backtrack) {
|
|
2314
2110
|
match = false;
|
|
2315
|
-
continue;
|
|
2111
|
+
continue;
|
|
2316
2112
|
} else {
|
|
2317
|
-
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
2318
2113
|
return false;
|
|
2319
2114
|
}
|
|
2320
2115
|
} else if (!this.options.flex) {
|
|
@@ -2327,7 +2122,6 @@ var parser = function () {
|
|
|
2327
2122
|
if (token !== false) {
|
|
2328
2123
|
return token;
|
|
2329
2124
|
}
|
|
2330
|
-
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
|
|
2331
2125
|
return false;
|
|
2332
2126
|
}
|
|
2333
2127
|
if (this._input === "") {
|
|
@@ -2340,7 +2134,6 @@ var parser = function () {
|
|
|
2340
2134
|
});
|
|
2341
2135
|
}
|
|
2342
2136
|
},
|
|
2343
|
-
// return next match that has a token
|
|
2344
2137
|
lex: function lex() {
|
|
2345
2138
|
var r = this.next();
|
|
2346
2139
|
if (r) {
|
|
@@ -2349,11 +2142,9 @@ var parser = function () {
|
|
|
2349
2142
|
return this.lex();
|
|
2350
2143
|
}
|
|
2351
2144
|
},
|
|
2352
|
-
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
|
2353
2145
|
begin: function begin(condition) {
|
|
2354
2146
|
this.conditionStack.push(condition);
|
|
2355
2147
|
},
|
|
2356
|
-
// pop the previously active lexer condition state off the condition stack
|
|
2357
2148
|
popState: function popState() {
|
|
2358
2149
|
var n = this.conditionStack.length - 1;
|
|
2359
2150
|
if (n > 0) {
|
|
@@ -2362,7 +2153,6 @@ var parser = function () {
|
|
|
2362
2153
|
return this.conditionStack[0];
|
|
2363
2154
|
}
|
|
2364
2155
|
},
|
|
2365
|
-
// produce the lexer rule set which is active for the currently active lexer condition state
|
|
2366
2156
|
_currentRules: function _currentRules() {
|
|
2367
2157
|
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
|
|
2368
2158
|
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
|
|
@@ -2370,7 +2160,6 @@ var parser = function () {
|
|
|
2370
2160
|
return this.conditions["INITIAL"].rules;
|
|
2371
2161
|
}
|
|
2372
2162
|
},
|
|
2373
|
-
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
|
|
2374
2163
|
topState: function topState(n) {
|
|
2375
2164
|
n = this.conditionStack.length - 1 - Math.abs(n || 0);
|
|
2376
2165
|
if (n >= 0) {
|
|
@@ -2379,11 +2168,9 @@ var parser = function () {
|
|
|
2379
2168
|
return "INITIAL";
|
|
2380
2169
|
}
|
|
2381
2170
|
},
|
|
2382
|
-
// alias for begin(condition)
|
|
2383
2171
|
pushState: function pushState(condition) {
|
|
2384
2172
|
this.begin(condition);
|
|
2385
2173
|
},
|
|
2386
|
-
// return the number of states currently on the stack
|
|
2387
2174
|
stateStackSize: function stateStackSize() {
|
|
2388
2175
|
return this.conditionStack.length;
|
|
2389
2176
|
},
|
|
@@ -2393,13 +2180,10 @@ var parser = function () {
|
|
|
2393
2180
|
performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
|
|
2394
2181
|
switch ($avoiding_name_collisions) {
|
|
2395
2182
|
case 0:
|
|
2396
|
-
/* skip whitespace */
|
|
2397
2183
|
break;
|
|
2398
2184
|
case 1:
|
|
2399
|
-
/* skip \space */
|
|
2400
2185
|
break;
|
|
2401
2186
|
case 2:
|
|
2402
|
-
/* skip '\ ' */
|
|
2403
2187
|
break;
|
|
2404
2188
|
case 3:
|
|
2405
2189
|
return "INT";
|
|
@@ -2621,62 +2405,20 @@ var parser = function () {
|
|
|
2621
2405
|
return new Parser();
|
|
2622
2406
|
}();
|
|
2623
2407
|
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
/* The node hierarcy is as follows:
|
|
2627
|
-
|
|
2628
|
-
(Expr)
|
|
2629
|
-
(Seq) 2+ children
|
|
2630
|
-
Add
|
|
2631
|
-
Mul
|
|
2632
|
-
Pow 2 children
|
|
2633
|
-
Log 2 children
|
|
2634
|
-
Eq 2 children
|
|
2635
|
-
Trig 1 child
|
|
2636
|
-
Abs 1 child
|
|
2637
|
-
(Sym)
|
|
2638
|
-
Func 1 child e.g. f(x)
|
|
2639
|
-
Var leaf node e.g. x, x_n
|
|
2640
|
-
Const leaf node e.g. pi, e, <i>
|
|
2641
|
-
Unit leaf node e.g. kg
|
|
2642
|
-
(Num) leaf node
|
|
2643
|
-
Rational e.g. 2/3
|
|
2644
|
-
Int
|
|
2645
|
-
Float
|
|
2646
|
-
|
|
2647
|
-
(abstract, not meant to be instantiated)
|
|
2648
|
-
|
|
2649
|
-
== Key design concepts ==
|
|
2650
|
-
Functional: All methods return new nodes - nodes are never mutated.
|
|
2651
|
-
Ignore commutativity: Commutative inputs should be parsed equivalently.
|
|
2652
|
-
Exploit commutativity: Output should take advantage of ordering.
|
|
2653
|
-
*/
|
|
2654
|
-
|
|
2655
|
-
/* non user-facing functions */
|
|
2656
|
-
|
|
2657
|
-
// reliably detect NaN
|
|
2658
|
-
const isNaN = function (object) {
|
|
2408
|
+
var _class5, _class12;
|
|
2409
|
+
const isNaN = function isNaN(object) {
|
|
2659
2410
|
return object !== object;
|
|
2660
2411
|
};
|
|
2661
|
-
|
|
2662
|
-
// return a random float between min (inclusive) and max (exclusive),
|
|
2663
|
-
// not that inclusivity means much, probabilistically, on floats
|
|
2664
|
-
const randomFloat = function (min, max) {
|
|
2412
|
+
const randomFloat = function randomFloat(min, max) {
|
|
2665
2413
|
var extent = max - min;
|
|
2666
2414
|
return Math.random() * extent + min;
|
|
2667
2415
|
};
|
|
2668
|
-
|
|
2669
|
-
/* constants */
|
|
2670
2416
|
var ITERATIONS = 12;
|
|
2671
|
-
var TOLERANCE = 9;
|
|
2672
|
-
|
|
2673
|
-
// NOTE(kevinb): _.partition exists in a more recent version of underscore.
|
|
2674
|
-
// To avoid having to update underscore I've added a hacky version of this
|
|
2675
|
-
// method here.
|
|
2417
|
+
var TOLERANCE = 9;
|
|
2676
2418
|
function partition(list, iteratee) {
|
|
2677
2419
|
const a = [];
|
|
2678
2420
|
const b = [];
|
|
2679
|
-
|
|
2421
|
+
_.forEach(list, (elem, key, ctx) => {
|
|
2680
2422
|
if (iteratee(elem, key, ctx)) {
|
|
2681
2423
|
a.push(elem);
|
|
2682
2424
|
} else {
|
|
@@ -2688,7 +2430,7 @@ function partition(list, iteratee) {
|
|
|
2688
2430
|
function isExpr(arg) {
|
|
2689
2431
|
return arg instanceof Expr;
|
|
2690
2432
|
}
|
|
2691
|
-
const isAdd = function (term) {
|
|
2433
|
+
const isAdd = function isAdd(term) {
|
|
2692
2434
|
return term instanceof Add;
|
|
2693
2435
|
};
|
|
2694
2436
|
function isRational(arg) {
|
|
@@ -2701,23 +2443,13 @@ function getFactors(expr) {
|
|
|
2701
2443
|
return [expr];
|
|
2702
2444
|
}
|
|
2703
2445
|
}
|
|
2704
|
-
/* abstract base expression node */
|
|
2705
2446
|
class Expr {
|
|
2706
|
-
hints;
|
|
2707
2447
|
constructor() {
|
|
2448
|
+
this.hints = void 0;
|
|
2708
2449
|
this.hints = {
|
|
2709
2450
|
parens: false
|
|
2710
2451
|
};
|
|
2711
2452
|
}
|
|
2712
|
-
|
|
2713
|
-
// this node's immediate constructor
|
|
2714
|
-
// The `new (...args: any[]): any;` part of the type is a
|
|
2715
|
-
// "construct" signature. It indicates that `func` is a class.
|
|
2716
|
-
// See https://www.typescriptlang.org/docs/handbook/2/functions.html#construct-signatures.
|
|
2717
|
-
|
|
2718
|
-
// an array of the arguments to this node's immediate constructor
|
|
2719
|
-
|
|
2720
|
-
// make a new node with the given arguments
|
|
2721
2453
|
construct(args) {
|
|
2722
2454
|
const func = this.func;
|
|
2723
2455
|
const instance = new func(...args);
|
|
@@ -2726,59 +2458,29 @@ class Expr {
|
|
|
2726
2458
|
}
|
|
2727
2459
|
return instance;
|
|
2728
2460
|
}
|
|
2729
|
-
|
|
2730
|
-
// an abstraction for chainable, bottom-up recursion
|
|
2731
|
-
// NOTE(kevinb): This method is highly dynamic. It's possible that it
|
|
2732
|
-
// could be made more type-safe using overload signatures.
|
|
2733
|
-
recurse(method) {
|
|
2734
|
-
for (var _len = arguments.length, passed = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
2735
|
-
passed[_key - 1] = arguments[_key];
|
|
2736
|
-
}
|
|
2461
|
+
recurse(method, ...passed) {
|
|
2737
2462
|
var args = this.args().map(function (arg) {
|
|
2738
|
-
return
|
|
2463
|
+
return _.isString(arg) || _.isNumber(arg) ? arg : arg == null ? void 0 : arg[method].apply(arg, passed);
|
|
2739
2464
|
});
|
|
2740
2465
|
return this.construct(args);
|
|
2741
2466
|
}
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
// NOTE(kevin): This could made into an abstract method but
|
|
2745
|
-
// Eq doesn't implement it. This indicates that we probably
|
|
2746
|
-
// need to introduce another class in our hierarchy.
|
|
2747
|
-
eval() {
|
|
2748
|
-
throw new Error("Abstract method - must override for expr: " +
|
|
2749
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
2750
|
-
this.print());
|
|
2467
|
+
eval(vars = {}, options) {
|
|
2468
|
+
throw new Error("Abstract method - must override for expr: " + this.print());
|
|
2751
2469
|
}
|
|
2752
|
-
|
|
2753
|
-
// NOTE(kevin): This could made into an abstract method but
|
|
2754
|
-
// Eq doesn't implement it. This indicates that we probably
|
|
2755
|
-
// need to introduce another class in our hierarchy.
|
|
2756
2470
|
codegen() {
|
|
2757
|
-
throw new Error("Abstract method - must override for expr: " +
|
|
2758
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
2759
|
-
this.print());
|
|
2471
|
+
throw new Error("Abstract method - must override for expr: " + this.print());
|
|
2760
2472
|
}
|
|
2761
2473
|
compile() {
|
|
2762
2474
|
var code = this.codegen();
|
|
2763
2475
|
try {
|
|
2764
|
-
// @ts-expect-error: TypeScript doesn't want to unify
|
|
2765
|
-
// `Function` with the `compile`'s return type.
|
|
2766
2476
|
return new Function("vars", "return " + code + ";");
|
|
2767
|
-
} catch {
|
|
2477
|
+
} catch (_unused) {
|
|
2768
2478
|
throw new Error("Function did not compile: " + code);
|
|
2769
2479
|
}
|
|
2770
2480
|
}
|
|
2771
|
-
|
|
2772
|
-
// returns a string unambiguously representing the expression
|
|
2773
|
-
// should be valid as input
|
|
2774
|
-
// e.g. this.equals(parse(this.print())) === true
|
|
2775
|
-
|
|
2776
|
-
// returns a TeX string representing the expression
|
|
2777
|
-
|
|
2778
|
-
// returns a TeX string, modified by the given options
|
|
2779
2481
|
asTex(options) {
|
|
2780
2482
|
options = options || {};
|
|
2781
|
-
|
|
2483
|
+
_.defaults(options, {
|
|
2782
2484
|
display: true,
|
|
2783
2485
|
dynamic: true,
|
|
2784
2486
|
times: false
|
|
@@ -2796,76 +2498,46 @@ class Expr {
|
|
|
2796
2498
|
}
|
|
2797
2499
|
return tex;
|
|
2798
2500
|
}
|
|
2799
|
-
|
|
2800
|
-
// returns the name of this expression's constructor as a string
|
|
2801
|
-
// only used for testing and debugging
|
|
2802
2501
|
name() {
|
|
2803
2502
|
return this.func.name;
|
|
2804
2503
|
}
|
|
2805
|
-
|
|
2806
|
-
// returns a string representing current node structure
|
|
2807
2504
|
repr() {
|
|
2808
2505
|
return this.name() + "(" + this.args().map(function (arg) {
|
|
2809
|
-
return
|
|
2506
|
+
return _.isString(arg) || _.isNumber(arg) ? arg : arg == null ? void 0 : arg.repr();
|
|
2810
2507
|
}).join(",") + ")";
|
|
2811
2508
|
}
|
|
2812
|
-
|
|
2813
|
-
// removes all negative signs
|
|
2814
2509
|
strip() {
|
|
2815
2510
|
return this.recurse("strip");
|
|
2816
2511
|
}
|
|
2817
|
-
|
|
2818
|
-
// canonically reorders all commutative elements
|
|
2819
2512
|
normalize() {
|
|
2820
2513
|
return this.recurse("normalize");
|
|
2821
2514
|
}
|
|
2822
|
-
|
|
2823
|
-
// expands the expression
|
|
2824
2515
|
expand() {
|
|
2825
2516
|
return this.recurse("expand");
|
|
2826
2517
|
}
|
|
2827
|
-
|
|
2828
|
-
// naively factors out like terms
|
|
2829
2518
|
factor(options) {
|
|
2830
2519
|
return this.recurse("factor", options);
|
|
2831
2520
|
}
|
|
2832
|
-
|
|
2833
|
-
// collect all like terms
|
|
2834
2521
|
collect(options) {
|
|
2835
2522
|
return this.recurse("collect", options);
|
|
2836
2523
|
}
|
|
2837
|
-
|
|
2838
|
-
// strict syntactic equality check
|
|
2839
2524
|
equals(other) {
|
|
2840
2525
|
return this.normalize().print() === other.normalize().print();
|
|
2841
2526
|
}
|
|
2842
|
-
|
|
2843
|
-
// expand and collect until the expression no longer changes
|
|
2844
2527
|
simplify(options) {
|
|
2845
|
-
options = {
|
|
2846
|
-
once: false
|
|
2847
|
-
|
|
2848
|
-
};
|
|
2849
|
-
|
|
2850
|
-
// Attempt to factor and collect
|
|
2528
|
+
options = _extends({
|
|
2529
|
+
once: false
|
|
2530
|
+
}, options);
|
|
2851
2531
|
var step1 = this.factor(options);
|
|
2852
2532
|
var step2 = step1.collect(options);
|
|
2853
|
-
|
|
2854
|
-
// Rollback if collect didn't do anything
|
|
2855
2533
|
if (step1.equals(step2)) {
|
|
2856
2534
|
step2 = this.collect(options);
|
|
2857
2535
|
}
|
|
2858
|
-
|
|
2859
|
-
// Attempt to expand and collect
|
|
2860
2536
|
var step3 = step2.expand();
|
|
2861
2537
|
var step4 = step3.collect(options);
|
|
2862
|
-
|
|
2863
|
-
// Rollback if collect didn't do anything
|
|
2864
2538
|
if (step3.equals(step4)) {
|
|
2865
2539
|
step4 = step2.collect(options);
|
|
2866
2540
|
}
|
|
2867
|
-
|
|
2868
|
-
// One round of simplification complete
|
|
2869
2541
|
var simplified = step4;
|
|
2870
2542
|
if (options.once || this.equals(simplified)) {
|
|
2871
2543
|
return simplified;
|
|
@@ -2873,52 +2545,35 @@ class Expr {
|
|
|
2873
2545
|
return simplified.simplify(options);
|
|
2874
2546
|
}
|
|
2875
2547
|
}
|
|
2876
|
-
|
|
2877
|
-
// check whether this expression is simplified
|
|
2878
2548
|
isSimplified() {
|
|
2879
2549
|
return this.equals(this.simplify());
|
|
2880
2550
|
}
|
|
2881
|
-
|
|
2882
|
-
// return the child nodes of this node
|
|
2883
2551
|
exprArgs() {
|
|
2884
|
-
// @ts-expect-error: Type 'string | number | Expr | undefined' is not assignable to type 'string | Expr'.
|
|
2885
2552
|
return this.args().filter(isExpr);
|
|
2886
2553
|
}
|
|
2887
|
-
|
|
2888
|
-
// return the variables (function and non) within the expression
|
|
2889
2554
|
getVars(excludeFunc) {
|
|
2890
|
-
return
|
|
2555
|
+
return _.uniq(_.flatten(_.invoke(this.exprArgs(), "getVars", excludeFunc))).sort();
|
|
2891
2556
|
}
|
|
2892
2557
|
getConsts() {
|
|
2893
|
-
return
|
|
2558
|
+
return _.uniq(_.flatten(_.invoke(this.exprArgs(), "getConsts"))).sort();
|
|
2894
2559
|
}
|
|
2895
2560
|
getUnits() {
|
|
2896
|
-
return
|
|
2561
|
+
return _.flatten(_.invoke(this.exprArgs(), "getUnits"));
|
|
2897
2562
|
}
|
|
2898
|
-
|
|
2899
|
-
// check whether this expression node is of a particular type
|
|
2900
2563
|
is(func) {
|
|
2901
2564
|
return this instanceof func;
|
|
2902
2565
|
}
|
|
2903
|
-
|
|
2904
|
-
// check whether this expression has a particular node type
|
|
2905
2566
|
has(func) {
|
|
2906
2567
|
if (this instanceof func) {
|
|
2907
2568
|
return true;
|
|
2908
2569
|
}
|
|
2909
|
-
return
|
|
2570
|
+
return _.any(this.exprArgs(), function (arg) {
|
|
2910
2571
|
return arg.has(func);
|
|
2911
2572
|
});
|
|
2912
2573
|
}
|
|
2913
|
-
|
|
2914
|
-
// raise this expression to a given exponent
|
|
2915
|
-
// most useful for eventually implementing i^3 = -i, etc.
|
|
2916
2574
|
raiseToThe(exp, options) {
|
|
2917
2575
|
return new Pow(this, exp);
|
|
2918
2576
|
}
|
|
2919
|
-
|
|
2920
|
-
// does this expression have a specific rendering hint?
|
|
2921
|
-
// rendering hints are picked up while parsing, but are lost during transformations
|
|
2922
2577
|
isSubtract() {
|
|
2923
2578
|
return false;
|
|
2924
2579
|
}
|
|
@@ -2928,24 +2583,17 @@ class Expr {
|
|
|
2928
2583
|
isRoot() {
|
|
2929
2584
|
return false;
|
|
2930
2585
|
}
|
|
2931
|
-
|
|
2932
|
-
// whether this node needs an explicit multiplication sign if following a Num
|
|
2933
2586
|
needsExplicitMul() {
|
|
2934
2587
|
return this.exprArgs()[0].needsExplicitMul();
|
|
2935
2588
|
}
|
|
2936
|
-
|
|
2937
|
-
// check that the variables in both expressions are the same
|
|
2938
2589
|
sameVars(other) {
|
|
2939
2590
|
var vars1 = this.getVars();
|
|
2940
2591
|
var vars2 = other.getVars();
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
// this lets you multiply equations by other variables
|
|
2944
|
-
var same = function (array1, array2) {
|
|
2945
|
-
return !___default["default"].difference(array1, array2).length;
|
|
2592
|
+
var same = function same(array1, array2) {
|
|
2593
|
+
return !_.difference(array1, array2).length;
|
|
2946
2594
|
};
|
|
2947
|
-
var lower = function (array) {
|
|
2948
|
-
return
|
|
2595
|
+
var lower = function lower(array) {
|
|
2596
|
+
return _.uniq(_.invoke(array, "toLowerCase")).sort();
|
|
2949
2597
|
};
|
|
2950
2598
|
var equal = same(vars1, vars2);
|
|
2951
2599
|
var equalIgnoringCase = same(lower(vars1), lower(vars2));
|
|
@@ -2954,85 +2602,39 @@ class Expr {
|
|
|
2954
2602
|
equalIgnoringCase: equalIgnoringCase
|
|
2955
2603
|
};
|
|
2956
2604
|
}
|
|
2957
|
-
|
|
2958
|
-
// semantic equality check, call after sameVars() to avoid potential false positives
|
|
2959
|
-
// plug in random numbers for the variables in both expressions
|
|
2960
|
-
// if they both consistently evaluate the same, then they're the same
|
|
2961
2605
|
compare(other) {
|
|
2962
|
-
// equation comparisons are handled by Eq.compare()
|
|
2963
2606
|
if (other instanceof Eq) {
|
|
2964
2607
|
return false;
|
|
2965
2608
|
}
|
|
2966
|
-
var varList =
|
|
2967
|
-
|
|
2968
|
-
// If the numbers are large we would like to do a relative comparison
|
|
2969
|
-
// rather than an absolute one, but if they're small enough then an
|
|
2970
|
-
// absolute comparison makes more sense
|
|
2971
|
-
var getDelta = function (num1, num2) {
|
|
2609
|
+
var varList = _.union(this.getVars(true), other.getVars(true));
|
|
2610
|
+
var getDelta = function getDelta(num1, num2) {
|
|
2972
2611
|
if (Math.abs(num1) < 1 || Math.abs(num2) < 1) {
|
|
2973
2612
|
return Math.abs(num1 - num2);
|
|
2974
2613
|
} else {
|
|
2975
2614
|
return Math.abs(1 - num1 / num2);
|
|
2976
2615
|
}
|
|
2977
2616
|
};
|
|
2978
|
-
var equalNumbers = function (num1, num2) {
|
|
2617
|
+
var equalNumbers = function equalNumbers(num1, num2) {
|
|
2979
2618
|
var delta = getDelta(num1, num2);
|
|
2980
|
-
return num1 === num2
|
|
2619
|
+
return num1 === num2 || isNaN(num1) && isNaN(num2) || delta < Math.pow(10, -9);
|
|
2981
2620
|
};
|
|
2982
|
-
|
|
2983
|
-
// If no variables, only need to evaluate once.
|
|
2984
|
-
// note(matthew) Seems to be an optimization for simple cases like `2+2=4`
|
|
2985
|
-
// where there are no variables / functions.
|
|
2986
|
-
// Ran into issues with it in LEMS-2777 and found that tests pass
|
|
2987
|
-
// with this removed, but keeping a modified version out of caution.
|
|
2988
|
-
const varAndFuncList = ___default["default"].union(this.getVars(/* excludeFunc */false), other.getVars(/* excludeFunc */false));
|
|
2621
|
+
const varAndFuncList = _.union(this.getVars(false), other.getVars(false));
|
|
2989
2622
|
if (!varAndFuncList.length && !this.has(Unit) && !other.has(Unit)) {
|
|
2990
2623
|
return equalNumbers(this.eval(), other.eval());
|
|
2991
2624
|
}
|
|
2992
|
-
|
|
2993
|
-
// collect here to avoid sometimes dividing by zero, and sometimes not
|
|
2994
|
-
// it is better to be deterministic, e.g. x/x -> 1
|
|
2995
|
-
// TODO(alex): may want to keep track of assumptions as they're made
|
|
2996
2625
|
var expr1 = this.collect();
|
|
2997
2626
|
var expr2 = other.collect();
|
|
2998
2627
|
var unitList1 = this.getUnits();
|
|
2999
2628
|
var unitList2 = other.getUnits();
|
|
3000
|
-
if (!
|
|
2629
|
+
if (!_.isEqual(unitList1, unitList2)) {
|
|
3001
2630
|
return false;
|
|
3002
2631
|
}
|
|
3003
|
-
|
|
3004
|
-
// Compare at a set number (currently 12) of points to determine
|
|
3005
|
-
// equality.
|
|
3006
|
-
//
|
|
3007
|
-
// `range` (and `vars`) is the only variable that varies through the
|
|
3008
|
-
// iterations. For each of range = 10, 100, and 1000, each random
|
|
3009
|
-
// variable is picked from (-range, range).
|
|
3010
|
-
//
|
|
3011
|
-
// Note that because there are 12 iterations and three ranges, each
|
|
3012
|
-
// range is checked four times.
|
|
3013
2632
|
for (var i = 0; i < ITERATIONS; i++) {
|
|
3014
2633
|
var vars = {};
|
|
3015
|
-
|
|
3016
|
-
// One third total iterations each with range 10, 100, and 1000
|
|
3017
2634
|
var range = Math.pow(10, 1 + Math.floor(3 * i / ITERATIONS));
|
|
3018
|
-
|
|
3019
|
-
// Half of the iterations should only use integer values.
|
|
3020
|
-
// This is because expressions like (-2)^x are common but result
|
|
3021
|
-
// in NaN when evaluated in JS with non-integer values of x.
|
|
3022
|
-
// Without this, (-2)^x and (-2)^(x+1) both end up always being NaN
|
|
3023
|
-
// and thus equivalent. With this, the most common failure case is
|
|
3024
|
-
// avoided. However, less common cases such as (-2)^(x+0.1) and
|
|
3025
|
-
// (-2)^(x+1.1) will still both evaluate to NaN and result in a
|
|
3026
|
-
// false positive.
|
|
3027
|
-
//
|
|
3028
|
-
// Note that the above is only true in vanilla JS Number-land,
|
|
3029
|
-
// which has no concept of complex numbers. The solution is simple:
|
|
3030
|
-
// Integrate a library for handling complex numbers.
|
|
3031
|
-
//
|
|
3032
|
-
// TODO(alex): Add support for complex numbers, then remove this.
|
|
3033
2635
|
var useFloats = i % 2 === 0;
|
|
3034
|
-
|
|
3035
|
-
vars[v] = useFloats ? randomFloat(-range, range) :
|
|
2636
|
+
_.each(varList, function (v) {
|
|
2637
|
+
vars[v] = useFloats ? randomFloat(-range, range) : _.random(-range, range);
|
|
3036
2638
|
});
|
|
3037
2639
|
let equal;
|
|
3038
2640
|
if (expr1.has(Func) || expr2.has(Func) || expr1.has(Unit) || expr2.has(Unit)) {
|
|
@@ -3050,8 +2652,6 @@ class Expr {
|
|
|
3050
2652
|
}
|
|
3051
2653
|
return true;
|
|
3052
2654
|
}
|
|
3053
|
-
|
|
3054
|
-
// evaluate as much of the expression as possible
|
|
3055
2655
|
partialEval(vars) {
|
|
3056
2656
|
if (this instanceof Unit) {
|
|
3057
2657
|
return this;
|
|
@@ -3063,86 +2663,50 @@ class Expr {
|
|
|
3063
2663
|
return this.recurse("partialEval", vars);
|
|
3064
2664
|
}
|
|
3065
2665
|
}
|
|
3066
|
-
|
|
3067
|
-
// check that the structure of both expressions is the same
|
|
3068
|
-
// all negative signs are stripped and the expressions are converted to
|
|
3069
|
-
// a canonical commutative form
|
|
3070
|
-
// should only be done after compare() returns true to avoid false positives
|
|
3071
2666
|
sameForm(other) {
|
|
3072
2667
|
return this.strip().equals(other.strip());
|
|
3073
2668
|
}
|
|
3074
|
-
|
|
3075
|
-
// returns the GCD of this expression and the given factor
|
|
3076
2669
|
findGCD(factor) {
|
|
3077
2670
|
return this.equals(factor) ? factor : NumOne;
|
|
3078
2671
|
}
|
|
3079
|
-
|
|
3080
|
-
// return this expression's denominator
|
|
3081
2672
|
getDenominator() {
|
|
3082
2673
|
return NumOne;
|
|
3083
2674
|
}
|
|
3084
|
-
|
|
3085
|
-
// return this expression as a Mul
|
|
3086
2675
|
asMul() {
|
|
3087
2676
|
return new Mul(NumOne, this);
|
|
3088
2677
|
}
|
|
3089
|
-
|
|
3090
|
-
// TODO(alex): rename to isDefinitePositive or similar?
|
|
3091
|
-
// return whether this expression is 100% positive
|
|
3092
2678
|
isPositive() {
|
|
3093
|
-
throw new Error("Abstract method - must override for expr: " +
|
|
3094
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
3095
|
-
this.print());
|
|
2679
|
+
throw new Error("Abstract method - must override for expr: " + this.print());
|
|
3096
2680
|
}
|
|
3097
|
-
|
|
3098
|
-
// TODO(alex): rename to hasNegativeSign or similar?
|
|
3099
|
-
// return whether this expression has a negative sign
|
|
3100
2681
|
isNegative() {
|
|
3101
2682
|
return false;
|
|
3102
2683
|
}
|
|
3103
|
-
|
|
3104
|
-
// return a factor of this expression that is 100% positive
|
|
3105
2684
|
asPositiveFactor() {
|
|
3106
2685
|
return this.isPositive() ? this : NumOne;
|
|
3107
2686
|
}
|
|
3108
|
-
|
|
3109
|
-
// return a copy of the expression with a new hint set (preserves hints)
|
|
3110
2687
|
addHint(hint) {
|
|
3111
2688
|
if (!hint) {
|
|
3112
2689
|
return this;
|
|
3113
2690
|
}
|
|
3114
2691
|
var expr = this.construct(this.args());
|
|
3115
|
-
expr.hints =
|
|
2692
|
+
expr.hints = _.clone(this.hints);
|
|
3116
2693
|
expr.hints[hint] = true;
|
|
3117
2694
|
return expr;
|
|
3118
2695
|
}
|
|
3119
|
-
|
|
3120
|
-
// complete parse by performing a few necessary transformations
|
|
3121
2696
|
completeParse() {
|
|
3122
2697
|
return this.recurse("completeParse");
|
|
3123
2698
|
}
|
|
3124
2699
|
abs() {
|
|
3125
|
-
throw new Error("Abstract method - must override for expr: " +
|
|
3126
|
-
// eslint-disable-next-line @babel/no-invalid-this
|
|
3127
|
-
this.print());
|
|
2700
|
+
throw new Error("Abstract method - must override for expr: " + this.print());
|
|
3128
2701
|
}
|
|
3129
2702
|
negate() {
|
|
3130
2703
|
return new Mul(NumNeg, this);
|
|
3131
2704
|
}
|
|
3132
2705
|
}
|
|
3133
|
-
|
|
3134
|
-
/* abstract sequence node */
|
|
3135
2706
|
class Seq extends Expr {
|
|
3136
|
-
|
|
3137
|
-
// TODO(kevinb): Try enforcing this at the type-level using [T, T, T[]]
|
|
3138
|
-
terms;
|
|
3139
|
-
|
|
3140
|
-
// TODO(kevinb): Update this use `...args: Expr[]`
|
|
3141
|
-
constructor() {
|
|
2707
|
+
constructor(...args) {
|
|
3142
2708
|
super();
|
|
3143
|
-
|
|
3144
|
-
args[_key2] = arguments[_key2];
|
|
3145
|
-
}
|
|
2709
|
+
this.terms = void 0;
|
|
3146
2710
|
if (args.length === 1) {
|
|
3147
2711
|
this.terms = args[0];
|
|
3148
2712
|
} else {
|
|
@@ -3153,7 +2717,7 @@ class Seq extends Expr {
|
|
|
3153
2717
|
return this.terms;
|
|
3154
2718
|
}
|
|
3155
2719
|
normalize() {
|
|
3156
|
-
var terms =
|
|
2720
|
+
var terms = _.sortBy(_.invoke(this.terms, "normalize"), term => {
|
|
3157
2721
|
return term.print();
|
|
3158
2722
|
});
|
|
3159
2723
|
return new this.func(terms);
|
|
@@ -3161,54 +2725,35 @@ class Seq extends Expr {
|
|
|
3161
2725
|
expand() {
|
|
3162
2726
|
return this.recurse("expand").flatten();
|
|
3163
2727
|
}
|
|
3164
|
-
|
|
3165
|
-
// partition the sequence into its numeric and non-numeric parts
|
|
3166
|
-
// makes no guarantees about the validity of either part!
|
|
3167
2728
|
partition() {
|
|
3168
2729
|
var [numbers, others] = partition(this.terms, term => {
|
|
3169
2730
|
return term instanceof Num;
|
|
3170
2731
|
});
|
|
3171
2732
|
return [new this.func(numbers), new this.func(others)];
|
|
3172
2733
|
}
|
|
3173
|
-
|
|
3174
|
-
// ensure that sequences have 2+ terms and no nested sequences of the same type
|
|
3175
|
-
// this is a shallow flattening and will return a non-Seq if terms.length <= 1
|
|
3176
2734
|
flatten() {
|
|
3177
2735
|
var type = this;
|
|
3178
|
-
var terms =
|
|
3179
|
-
// @ts-expect-error: `identity` is defined on Add and Mul but doesn't
|
|
3180
|
-
// exist on Seq itself.
|
|
2736
|
+
var terms = _.reject(this.terms, term => {
|
|
3181
2737
|
return term.equals(type.identity);
|
|
3182
2738
|
});
|
|
3183
2739
|
if (terms.length === 0) {
|
|
3184
|
-
// @ts-expect-error: `identity` is defined on Add and Mul but doesn't
|
|
3185
|
-
// exist on Seq itself.
|
|
3186
2740
|
return type.identity;
|
|
3187
2741
|
}
|
|
3188
2742
|
if (terms.length === 1) {
|
|
3189
2743
|
return terms[0];
|
|
3190
2744
|
}
|
|
3191
|
-
|
|
3192
|
-
// same contains the children which are Seqs of the same type as this Seq
|
|
3193
2745
|
const [same, others] = partition(terms, term => {
|
|
3194
2746
|
return term instanceof type.func;
|
|
3195
2747
|
});
|
|
3196
|
-
var flattened = others.concat(
|
|
2748
|
+
var flattened = others.concat(_.flatten(_.pluck(same, "terms"), true));
|
|
3197
2749
|
return new type.func(flattened);
|
|
3198
2750
|
}
|
|
3199
|
-
|
|
3200
|
-
// reduce a numeric sequence to a Num
|
|
3201
|
-
|
|
3202
2751
|
isPositive() {
|
|
3203
|
-
var terms =
|
|
3204
|
-
return
|
|
2752
|
+
var terms = _.invoke(this.terms, "collect");
|
|
2753
|
+
return _.all(_.invoke(terms, "isPositive"));
|
|
3205
2754
|
}
|
|
3206
|
-
|
|
3207
|
-
// return a new Seq with a given term replaced by a different term
|
|
3208
|
-
// (or array of terms). given term can be passed directly, or by index
|
|
3209
|
-
// if no new term is provided, the old one is simply removed
|
|
3210
2755
|
replace(oldTerm, newTerm) {
|
|
3211
|
-
const index = oldTerm instanceof Expr ?
|
|
2756
|
+
const index = oldTerm instanceof Expr ? _.indexOf(this.terms, oldTerm) : oldTerm;
|
|
3212
2757
|
var newTerms = [];
|
|
3213
2758
|
if (Array.isArray(newTerm)) {
|
|
3214
2759
|
newTerms = newTerm;
|
|
@@ -3218,39 +2763,35 @@ class Seq extends Expr {
|
|
|
3218
2763
|
var terms = this.terms.slice(0, index).concat(newTerms).concat(this.terms.slice(index + 1));
|
|
3219
2764
|
return new this.func(terms);
|
|
3220
2765
|
}
|
|
3221
|
-
|
|
3222
|
-
// syntactic sugar for replace()
|
|
3223
2766
|
remove(term) {
|
|
3224
2767
|
return this.replace(term);
|
|
3225
2768
|
}
|
|
3226
2769
|
getDenominator() {
|
|
3227
|
-
|
|
3228
|
-
return new Mul(___default["default"].invoke(this.terms, "getDenominator")).flatten();
|
|
2770
|
+
return new Mul(_.invoke(this.terms, "getDenominator")).flatten();
|
|
3229
2771
|
}
|
|
3230
2772
|
}
|
|
3231
|
-
|
|
3232
|
-
/* sequence of additive terms */
|
|
3233
2773
|
class Add extends Seq {
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
2774
|
+
constructor(...args) {
|
|
2775
|
+
super(...args);
|
|
2776
|
+
this.identity = NumZero;
|
|
2777
|
+
this.func = Add;
|
|
2778
|
+
}
|
|
2779
|
+
eval(vars = {}, options) {
|
|
2780
|
+
return _.reduce(this.terms, (memo, term) => {
|
|
3240
2781
|
return memo + term.eval(vars, options);
|
|
3241
2782
|
}, 0);
|
|
3242
2783
|
}
|
|
3243
2784
|
codegen() {
|
|
3244
|
-
return
|
|
2785
|
+
return _.map(this.terms, term => {
|
|
3245
2786
|
return "(" + term.codegen() + ")";
|
|
3246
2787
|
}).join(" + ") || "0";
|
|
3247
2788
|
}
|
|
3248
2789
|
print() {
|
|
3249
|
-
return
|
|
2790
|
+
return _.invoke(this.terms, "print").join("+");
|
|
3250
2791
|
}
|
|
3251
2792
|
tex() {
|
|
3252
2793
|
let tex = "";
|
|
3253
|
-
|
|
2794
|
+
_.each(this.terms, term => {
|
|
3254
2795
|
if (!tex || term.isSubtract()) {
|
|
3255
2796
|
tex += term.tex();
|
|
3256
2797
|
} else {
|
|
@@ -3260,9 +2801,9 @@ class Add extends Seq {
|
|
|
3260
2801
|
return tex;
|
|
3261
2802
|
}
|
|
3262
2803
|
collect(options) {
|
|
3263
|
-
var terms =
|
|
2804
|
+
var terms = _.invoke(this.terms, "collect", options);
|
|
3264
2805
|
var pairs = [];
|
|
3265
|
-
|
|
2806
|
+
_.each(terms, term => {
|
|
3266
2807
|
if (term instanceof Mul) {
|
|
3267
2808
|
var muls = term.partition();
|
|
3268
2809
|
pairs.push([muls[1].flatten(), muls[0].reduce(options)]);
|
|
@@ -3272,30 +2813,20 @@ class Add extends Seq {
|
|
|
3272
2813
|
pairs.push([term, NumOne]);
|
|
3273
2814
|
}
|
|
3274
2815
|
});
|
|
3275
|
-
|
|
3276
|
-
// { (Expr expr).print(): [[Expr expr, Num coefficient]] }
|
|
3277
|
-
var grouped = ___default["default"].groupBy(pairs, pair => {
|
|
2816
|
+
var grouped = _.groupBy(pairs, pair => {
|
|
3278
2817
|
return pair[0].normalize().print();
|
|
3279
2818
|
});
|
|
3280
|
-
var collected =
|
|
2819
|
+
var collected = _.compact(_.map(grouped, pairs => {
|
|
3281
2820
|
var expr = pairs[0][0];
|
|
3282
|
-
var sum = new Add(
|
|
2821
|
+
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
3283
2822
|
var coefficient = sum.reduce(options);
|
|
3284
2823
|
return new Mul(coefficient, expr).collect(options);
|
|
3285
2824
|
}));
|
|
3286
|
-
|
|
3287
|
-
// TODO(alex): use the Pythagorean identity here
|
|
3288
|
-
// e.g. x*sin^2(y) + x*cos^2(y) -> x
|
|
3289
|
-
|
|
3290
2825
|
return new Add(collected).flatten();
|
|
3291
2826
|
}
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
factor() {
|
|
3296
|
-
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
|
|
3297
|
-
keepNegative: false
|
|
3298
|
-
};
|
|
2827
|
+
factor(options = {
|
|
2828
|
+
keepNegative: false
|
|
2829
|
+
}) {
|
|
3299
2830
|
const terms = this.terms.map(term => term.collect());
|
|
3300
2831
|
let factors;
|
|
3301
2832
|
if (terms[0] instanceof Mul) {
|
|
@@ -3303,8 +2834,8 @@ class Add extends Seq {
|
|
|
3303
2834
|
} else {
|
|
3304
2835
|
factors = [terms[0]];
|
|
3305
2836
|
}
|
|
3306
|
-
|
|
3307
|
-
factors =
|
|
2837
|
+
_.each(_.rest(this.terms), term => {
|
|
2838
|
+
factors = _.map(factors, factor => {
|
|
3308
2839
|
return term.findGCD(factor);
|
|
3309
2840
|
});
|
|
3310
2841
|
});
|
|
@@ -3317,9 +2848,7 @@ class Add extends Seq {
|
|
|
3317
2848
|
return Mul.createOrAppend(left, right).flatten();
|
|
3318
2849
|
}
|
|
3319
2850
|
reduce(options) {
|
|
3320
|
-
return
|
|
3321
|
-
// @ts-expect-error: Type 'Expr' is not assignable to type 'Num'.
|
|
3322
|
-
(memo, term) => {
|
|
2851
|
+
return _.reduce(this.terms, (memo, term) => {
|
|
3323
2852
|
return memo.add(term, options);
|
|
3324
2853
|
}, this.identity);
|
|
3325
2854
|
}
|
|
@@ -3327,14 +2856,12 @@ class Add extends Seq {
|
|
|
3327
2856
|
return false;
|
|
3328
2857
|
}
|
|
3329
2858
|
isNegative() {
|
|
3330
|
-
var terms =
|
|
3331
|
-
return
|
|
2859
|
+
var terms = _.invoke(this.terms, "collect");
|
|
2860
|
+
return _.all(_.invoke(terms, "isNegative"));
|
|
3332
2861
|
}
|
|
3333
2862
|
negate() {
|
|
3334
|
-
return new Add(
|
|
2863
|
+
return new Add(_.invoke(this.terms, "negate"));
|
|
3335
2864
|
}
|
|
3336
|
-
|
|
3337
|
-
// create a new sequence unless left is already one (returns a copy)
|
|
3338
2865
|
static createOrAppend(left, right) {
|
|
3339
2866
|
if (left instanceof Add) {
|
|
3340
2867
|
return new Add(left.terms.concat(right));
|
|
@@ -3343,41 +2870,37 @@ class Add extends Seq {
|
|
|
3343
2870
|
}
|
|
3344
2871
|
}
|
|
3345
2872
|
}
|
|
3346
|
-
|
|
3347
|
-
/* sequence of multiplicative terms */
|
|
3348
2873
|
class Mul extends Seq {
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
2874
|
+
constructor(...args) {
|
|
2875
|
+
super(...args);
|
|
2876
|
+
this.identity = NumOne;
|
|
2877
|
+
this.func = Mul;
|
|
2878
|
+
}
|
|
2879
|
+
eval(vars = {}, options) {
|
|
2880
|
+
return _.reduce(this.terms, (memo, term) => {
|
|
3355
2881
|
return memo * term.eval(vars, options);
|
|
3356
2882
|
}, 1);
|
|
3357
2883
|
}
|
|
3358
2884
|
codegen() {
|
|
3359
|
-
return
|
|
2885
|
+
return _.map(this.terms, term => {
|
|
3360
2886
|
return "(" + term.codegen() + ")";
|
|
3361
2887
|
}).join(" * ") || "0";
|
|
3362
2888
|
}
|
|
3363
2889
|
print() {
|
|
3364
|
-
return
|
|
2890
|
+
return _.map(this.terms, term => {
|
|
3365
2891
|
return term instanceof Add ? "(" + term.print() + ")" : term.print();
|
|
3366
2892
|
}).join("*");
|
|
3367
2893
|
}
|
|
3368
2894
|
getUnits() {
|
|
3369
|
-
var tmUnits =
|
|
2895
|
+
var tmUnits = _(this.terms).chain().map(term => {
|
|
3370
2896
|
return term.getUnits();
|
|
3371
2897
|
}).flatten().value();
|
|
3372
2898
|
tmUnits.sort((a, b) => a.unit.localeCompare(b.unit));
|
|
3373
2899
|
return tmUnits;
|
|
3374
2900
|
}
|
|
3375
|
-
|
|
3376
|
-
// since we don't care about commutativity, we can render a Mul any way we choose
|
|
3377
|
-
// so we follow convention: first any negatives, then any numbers, then everything else
|
|
3378
2901
|
tex() {
|
|
3379
2902
|
var cdot = " \\cdot ";
|
|
3380
|
-
var terms =
|
|
2903
|
+
var terms = _.groupBy(this.terms, term => {
|
|
3381
2904
|
if (term.isDivide()) {
|
|
3382
2905
|
return "inverse";
|
|
3383
2906
|
} else if (term instanceof Num) {
|
|
@@ -3391,10 +2914,6 @@ class Mul extends Seq {
|
|
|
3391
2914
|
var others = terms.other || [];
|
|
3392
2915
|
var negatives = "";
|
|
3393
2916
|
var numerator;
|
|
3394
|
-
|
|
3395
|
-
// check all the numbers to see if there is a rational we can extract,
|
|
3396
|
-
// since we would like 1/2x/y to come out as \frac{1}{2}\frac{x}{y},
|
|
3397
|
-
// and not \frac{1x}{2y}.
|
|
3398
2917
|
for (var i = 0; i < numbers.length; i++) {
|
|
3399
2918
|
var isRational = numbers[i] instanceof Rational && !(numbers[i] instanceof Int);
|
|
3400
2919
|
if (isRational && others.length > 0 && inverses.length > 0) {
|
|
@@ -3404,45 +2923,37 @@ class Mul extends Seq {
|
|
|
3404
2923
|
return numbers[i].tex() + new Mul(newTerms).tex();
|
|
3405
2924
|
}
|
|
3406
2925
|
}
|
|
3407
|
-
numbers =
|
|
2926
|
+
numbers = _.compact(_.map(numbers, term => {
|
|
3408
2927
|
var shouldPushDown = !term.hints.fraction || inverses.length > 0;
|
|
3409
2928
|
if (term instanceof Rational && !(term instanceof Int) && shouldPushDown) {
|
|
3410
|
-
// e.g. 3x/4 -> 3/4*x (internally) -> 3x/4 (rendered)
|
|
3411
2929
|
inverses.push(new Pow(new Int(term.d), NumDiv));
|
|
3412
2930
|
var number = new Int(term.n);
|
|
3413
2931
|
number.hints = term.hints;
|
|
3414
|
-
return
|
|
2932
|
+
return _.any(term.hints) ? number : null;
|
|
3415
2933
|
} else {
|
|
3416
2934
|
return term;
|
|
3417
2935
|
}
|
|
3418
2936
|
}));
|
|
3419
2937
|
if (numbers.length === 0 && others.length === 1) {
|
|
3420
|
-
// e.g. (x+y)/z -> \frac{x+y}{z}
|
|
3421
2938
|
numerator = others[0].tex();
|
|
3422
2939
|
} else {
|
|
3423
2940
|
var tex = "";
|
|
3424
|
-
|
|
2941
|
+
_.each(numbers, term => {
|
|
3425
2942
|
if (term.hints.subtract && term.hints.entered) {
|
|
3426
2943
|
negatives += "-";
|
|
3427
2944
|
tex += (tex ? cdot : "") + term.abs().tex();
|
|
3428
2945
|
} else if (term instanceof Int && term.n === -1 && (term.hints.negate || term.hints.subtract)) {
|
|
3429
|
-
// e.g. -1*-1 -> --1
|
|
3430
|
-
// e.g. -1*x -> -x
|
|
3431
2946
|
negatives += "-";
|
|
3432
2947
|
} else {
|
|
3433
|
-
// e.g. 2*3 -> 2(dot)3
|
|
3434
2948
|
tex += (tex ? cdot : "") + term.tex();
|
|
3435
2949
|
}
|
|
3436
2950
|
});
|
|
3437
|
-
|
|
2951
|
+
_.each(others, term => {
|
|
3438
2952
|
if (term.needsExplicitMul()) {
|
|
3439
|
-
// e.g. 2*2^3 -> 2(dot)2^3
|
|
3440
2953
|
tex += (tex ? cdot : "") + term.tex();
|
|
3441
2954
|
} else if (term instanceof Add) {
|
|
3442
|
-
// e.g. (a+b)*c -> (a+b)c
|
|
3443
2955
|
tex += "(" + term.tex() + ")";
|
|
3444
2956
|
} else {
|
|
3445
|
-
// e.g. a*b*c -> abc
|
|
3446
2957
|
tex += term.tex();
|
|
3447
2958
|
}
|
|
3448
2959
|
});
|
|
@@ -3451,53 +2962,45 @@ class Mul extends Seq {
|
|
|
3451
2962
|
if (!inverses.length) {
|
|
3452
2963
|
return negatives + numerator;
|
|
3453
2964
|
} else {
|
|
3454
|
-
var denominator = new Mul(
|
|
2965
|
+
var denominator = new Mul(_.invoke(inverses, "asDivide")).flatten().tex();
|
|
3455
2966
|
return negatives + "\\frac{" + numerator + "}{" + denominator + "}";
|
|
3456
2967
|
}
|
|
3457
2968
|
}
|
|
3458
2969
|
strip() {
|
|
3459
|
-
var terms =
|
|
2970
|
+
var terms = _.map(this.terms, term => {
|
|
3460
2971
|
return term instanceof Num ? term.abs() : term.strip();
|
|
3461
2972
|
});
|
|
3462
2973
|
return new Mul(terms).flatten();
|
|
3463
2974
|
}
|
|
3464
|
-
|
|
3465
|
-
// expand numerator and denominator separately
|
|
3466
2975
|
expand() {
|
|
3467
|
-
const isInverse = function (term) {
|
|
2976
|
+
const isInverse = function isInverse(term) {
|
|
3468
2977
|
return term instanceof Pow && term.exp.isNegative();
|
|
3469
2978
|
};
|
|
3470
|
-
const isInverseAdd = function (term) {
|
|
2979
|
+
const isInverseAdd = function isInverseAdd(term) {
|
|
3471
2980
|
return isInverse(term) && isAdd(term.base);
|
|
3472
2981
|
};
|
|
3473
2982
|
const mul = this.recurse("expand").flatten();
|
|
3474
2983
|
const factors = getFactors(mul);
|
|
3475
|
-
const hasAdd =
|
|
3476
|
-
const hasInverseAdd =
|
|
2984
|
+
const hasAdd = _.any(factors, isAdd);
|
|
2985
|
+
const hasInverseAdd = _.any(factors, isInverseAdd);
|
|
3477
2986
|
if (!(hasAdd || hasInverseAdd)) {
|
|
3478
2987
|
return mul;
|
|
3479
2988
|
}
|
|
3480
2989
|
let [inverses, normals] = partition(factors, isInverse);
|
|
3481
2990
|
if (hasAdd) {
|
|
3482
2991
|
const [adds, others] = partition(normals, isAdd);
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
// loop over each expanded array of terms
|
|
3487
|
-
return ___default["default"].reduce(expanded, function (temp, array) {
|
|
3488
|
-
// loop over each additive sequence's terms
|
|
3489
|
-
return temp.concat(___default["default"].map(add.terms, term => array.concat(term)));
|
|
2992
|
+
const expanded = _.reduce(adds, function (expanded, add) {
|
|
2993
|
+
return _.reduce(expanded, function (temp, array) {
|
|
2994
|
+
return temp.concat(_.map(add.terms, term => array.concat(term)));
|
|
3490
2995
|
}, []);
|
|
3491
2996
|
}, [[]]);
|
|
3492
|
-
|
|
3493
|
-
// join each fully expanded array of factors with remaining multiplicative factors
|
|
3494
|
-
const muls = ___default["default"].map(expanded, function (array) {
|
|
2997
|
+
const muls = _.map(expanded, function (array) {
|
|
3495
2998
|
return new Mul(others.concat(array)).flatten();
|
|
3496
2999
|
});
|
|
3497
3000
|
normals = [new Add(muls)];
|
|
3498
3001
|
}
|
|
3499
3002
|
if (hasInverseAdd) {
|
|
3500
|
-
const denominator = new Mul(
|
|
3003
|
+
const denominator = new Mul(_.invoke(inverses, "getDenominator")).flatten();
|
|
3501
3004
|
inverses = [new Pow(denominator.expand(), NumDiv)];
|
|
3502
3005
|
}
|
|
3503
3006
|
return new Mul(normals.concat(inverses)).flatten();
|
|
@@ -3507,16 +3010,10 @@ class Mul extends Seq {
|
|
|
3507
3010
|
if (!(factored instanceof Mul)) {
|
|
3508
3011
|
return factored;
|
|
3509
3012
|
}
|
|
3510
|
-
|
|
3511
|
-
// Combine any factored out Rationals into one, but don't collect
|
|
3512
3013
|
var [rationals, others] = partition(factored.terms, term => {
|
|
3513
3014
|
return term instanceof Rational;
|
|
3514
3015
|
});
|
|
3515
|
-
|
|
3516
|
-
// Could also accomplish this by passing a new option
|
|
3517
|
-
// e.g. return memo.mul(term, {autocollect: false});
|
|
3518
|
-
// TODO(alex): Decide whether this is a good use of options or not
|
|
3519
|
-
const ratObj = ___default["default"].reduce(rationals, (memo, term) => {
|
|
3016
|
+
const ratObj = _.reduce(rationals, (memo, term) => {
|
|
3520
3017
|
return {
|
|
3521
3018
|
n: memo.n * term.n,
|
|
3522
3019
|
d: memo.d * term.d
|
|
@@ -3531,35 +3028,28 @@ class Mul extends Seq {
|
|
|
3531
3028
|
collect(options) {
|
|
3532
3029
|
var partitioned = this.recurse("collect", options).partition();
|
|
3533
3030
|
var number = partitioned[0].reduce(options);
|
|
3534
|
-
|
|
3535
|
-
// e.g. 0*x -> 0
|
|
3536
3031
|
if (number.eval() === 0) {
|
|
3537
3032
|
return NumZero;
|
|
3538
3033
|
}
|
|
3539
3034
|
const other = partitioned[1].flatten();
|
|
3540
|
-
|
|
3541
|
-
// e.g. 2*2 -> 4
|
|
3542
|
-
// e.g. 2*2*x -> 4*x
|
|
3543
3035
|
if (!(other instanceof Mul)) {
|
|
3544
3036
|
return new Mul(number, other).flatten();
|
|
3545
3037
|
}
|
|
3546
3038
|
const others = other.terms;
|
|
3547
3039
|
var pairs = [];
|
|
3548
|
-
|
|
3040
|
+
_.each(others, term => {
|
|
3549
3041
|
if (term instanceof Pow) {
|
|
3550
3042
|
pairs.push([term.base, term.exp]);
|
|
3551
3043
|
} else {
|
|
3552
3044
|
pairs.push([term, NumOne]);
|
|
3553
3045
|
}
|
|
3554
3046
|
});
|
|
3555
|
-
|
|
3556
|
-
// {(Expr base).print(): [[Expr base, Expr exp]]}
|
|
3557
|
-
var grouped = ___default["default"].groupBy(pairs, pair => {
|
|
3047
|
+
var grouped = _.groupBy(pairs, pair => {
|
|
3558
3048
|
return pair[0].normalize().print();
|
|
3559
3049
|
});
|
|
3560
|
-
var summed =
|
|
3050
|
+
var summed = _.compact(_.map(grouped, pairs => {
|
|
3561
3051
|
var base = pairs[0][0];
|
|
3562
|
-
var sum = new Add(
|
|
3052
|
+
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
3563
3053
|
var exp = sum.collect(options);
|
|
3564
3054
|
if (exp instanceof Num && exp.eval() === 0) {
|
|
3565
3055
|
return null;
|
|
@@ -3567,9 +3057,7 @@ class Mul extends Seq {
|
|
|
3567
3057
|
return [base, exp];
|
|
3568
3058
|
}
|
|
3569
3059
|
}));
|
|
3570
|
-
|
|
3571
|
-
// XXX `pairs` is shadowed four or five times in this function
|
|
3572
|
-
const groupedPairs = ___default["default"].groupBy(summed, pair => {
|
|
3060
|
+
const groupedPairs = _.groupBy(summed, pair => {
|
|
3573
3061
|
if (pair[0] instanceof Trig && pair[0].isBasic()) {
|
|
3574
3062
|
return "trig";
|
|
3575
3063
|
} else if (pair[0] instanceof Log) {
|
|
@@ -3582,26 +3070,20 @@ class Mul extends Seq {
|
|
|
3582
3070
|
let logs = groupedPairs.log || [];
|
|
3583
3071
|
const exprs = groupedPairs.expr || [];
|
|
3584
3072
|
if (trigs.length > 1) {
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
// {Trig.arg.print(): [[Trig base, Expr exp]]}
|
|
3588
|
-
var byArg = ___default["default"].groupBy(trigs, pair => {
|
|
3073
|
+
var byArg = _.groupBy(trigs, pair => {
|
|
3589
3074
|
return pair[0].arg.normalize().print();
|
|
3590
3075
|
});
|
|
3591
3076
|
trigs = [];
|
|
3592
|
-
|
|
3077
|
+
_.each(byArg, pairs => {
|
|
3593
3078
|
const arg = pairs[0][0].arg;
|
|
3594
|
-
|
|
3595
|
-
// {Trig.type: Expr exp}
|
|
3596
3079
|
let funcs = {
|
|
3597
3080
|
sin: NumZero,
|
|
3598
3081
|
cos: NumZero
|
|
3599
3082
|
};
|
|
3600
|
-
|
|
3083
|
+
_.each(pairs, pair => {
|
|
3601
3084
|
funcs[pair[0].type] = pair[1];
|
|
3602
3085
|
});
|
|
3603
3086
|
if (Mul.handleNegative(funcs.sin).collect(options).equals(funcs.cos)) {
|
|
3604
|
-
// e.g. sin^x(y)/cos^x(y) -> tan^x(y)
|
|
3605
3087
|
if (funcs.cos.isNegative()) {
|
|
3606
3088
|
funcs = {
|
|
3607
3089
|
tan: funcs.sin
|
|
@@ -3612,30 +3094,18 @@ class Mul extends Seq {
|
|
|
3612
3094
|
};
|
|
3613
3095
|
}
|
|
3614
3096
|
}
|
|
3615
|
-
|
|
3616
|
-
// TODO(alex): combine even if exponents not a perfect match
|
|
3617
|
-
// TODO(alex): transform 1/sin and 1/cos into csc and sec
|
|
3618
|
-
|
|
3619
|
-
___default["default"].each(funcs, (exp, type) => {
|
|
3097
|
+
_.each(funcs, (exp, type) => {
|
|
3620
3098
|
trigs.push([new Trig(type, arg), exp]);
|
|
3621
3099
|
});
|
|
3622
3100
|
});
|
|
3623
3101
|
}
|
|
3624
3102
|
if (logs.length > 1) {
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
// {Log.base.print(): [[Log base, Expr exp]]}
|
|
3628
|
-
var byBase = ___default["default"].groupBy(logs, pair => {
|
|
3103
|
+
var byBase = _.groupBy(logs, pair => {
|
|
3629
3104
|
return pair[0].base.normalize().print();
|
|
3630
3105
|
});
|
|
3631
3106
|
logs = [];
|
|
3632
|
-
|
|
3633
|
-
// only combine two logs of the same base, otherwise commutative
|
|
3634
|
-
// differences result in different equally valid output
|
|
3635
|
-
// e.g. ln(x)/ln(z)*ln(y) -> log_z(x)*ln(y)
|
|
3636
|
-
// e.g. ln(x)*ln(y)/ln(z) -> ln(x)*log_z(y)
|
|
3107
|
+
_.each(byBase, pairs => {
|
|
3637
3108
|
if (pairs.length === 2 && Mul.handleNegative(pairs[0][1]).collect(options).equals(pairs[1][1])) {
|
|
3638
|
-
// e.g. ln(x)^y/ln(b)^y -> log_b(x)^y
|
|
3639
3109
|
if (pairs[0][1].isNegative()) {
|
|
3640
3110
|
logs.push([new Log(pairs[0][0].power, pairs[1][0].power), pairs[1][1]]);
|
|
3641
3111
|
} else {
|
|
@@ -3645,45 +3115,32 @@ class Mul extends Seq {
|
|
|
3645
3115
|
logs = logs.concat(pairs);
|
|
3646
3116
|
}
|
|
3647
3117
|
});
|
|
3648
|
-
|
|
3649
|
-
// TODO(alex): combine if all inverses are the same e.g. ln(y)*ln(z)/ln(x)/ln(x)
|
|
3650
3118
|
}
|
|
3651
|
-
var collected =
|
|
3119
|
+
var collected = _.map([...trigs, ...logs, ...exprs], pair => {
|
|
3652
3120
|
return new Pow(pair[0], pair[1]).collect(options);
|
|
3653
3121
|
});
|
|
3654
3122
|
return new Mul([number].concat(collected)).flatten();
|
|
3655
3123
|
}
|
|
3656
3124
|
isSubtract() {
|
|
3657
|
-
return
|
|
3125
|
+
return _.any(this.terms, term => {
|
|
3658
3126
|
return term instanceof Num && Boolean(term.hints.subtract);
|
|
3659
3127
|
});
|
|
3660
3128
|
}
|
|
3661
|
-
|
|
3662
|
-
// factor a single -1 in to the Mul
|
|
3663
|
-
// combine with a Num if all Nums are positive, else add as a term
|
|
3664
3129
|
factorIn(hint) {
|
|
3665
3130
|
var partitioned = this.partition();
|
|
3666
|
-
// `partition` splits the terms into two Seqs - one containing
|
|
3667
|
-
// only Nums and the all non-Num nodes.
|
|
3668
3131
|
var numbers = partitioned[0].terms;
|
|
3669
|
-
var fold = numbers.length &&
|
|
3132
|
+
var fold = numbers.length && _.all(numbers, num => num.n > 0);
|
|
3670
3133
|
if (fold) {
|
|
3671
|
-
// e.g. - x*2*3 -> x*-2*3
|
|
3672
3134
|
var num = numbers[0].negate();
|
|
3673
3135
|
num.hints = numbers[0].hints;
|
|
3674
3136
|
return this.replace(numbers[0], num.addHint(hint));
|
|
3675
3137
|
} else {
|
|
3676
|
-
// e.g. - x*y -> -1*x*y
|
|
3677
|
-
// e.g. - x*-2 -> -1*x*-2
|
|
3678
3138
|
return new Mul([Num.negativeOne(hint)].concat(this.terms));
|
|
3679
3139
|
}
|
|
3680
3140
|
}
|
|
3681
|
-
|
|
3682
|
-
// factor out a single hinted -1 (assume it is the division hint)
|
|
3683
|
-
// TODO(alex): make more general or rename to be more specific
|
|
3684
3141
|
factorOut() {
|
|
3685
3142
|
var factored = false;
|
|
3686
|
-
var terms =
|
|
3143
|
+
var terms = _.compact(_.map(this.terms, term => {
|
|
3687
3144
|
if (!factored && term instanceof Num && term.hints.divide) {
|
|
3688
3145
|
factored = true;
|
|
3689
3146
|
return term.n !== -1 ? term.negate() : null;
|
|
@@ -3698,14 +3155,12 @@ class Mul extends Seq {
|
|
|
3698
3155
|
}
|
|
3699
3156
|
}
|
|
3700
3157
|
reduce(options) {
|
|
3701
|
-
return
|
|
3702
|
-
// @ts-expect-error: Type 'Expr' is not assignable to type 'Num'.
|
|
3703
|
-
(memo, term) => {
|
|
3158
|
+
return _.reduce(this.terms, (memo, term) => {
|
|
3704
3159
|
return memo.mul(term, options);
|
|
3705
3160
|
}, this.identity);
|
|
3706
3161
|
}
|
|
3707
3162
|
findGCD(factor) {
|
|
3708
|
-
return new Mul(
|
|
3163
|
+
return new Mul(_.invoke(this.terms, "findGCD", factor)).flatten();
|
|
3709
3164
|
}
|
|
3710
3165
|
asMul() {
|
|
3711
3166
|
return this;
|
|
@@ -3720,7 +3175,7 @@ class Mul extends Seq {
|
|
|
3720
3175
|
}
|
|
3721
3176
|
isNegative() {
|
|
3722
3177
|
const terms = getFactors(this.collect()).map(factor => factor.isNegative());
|
|
3723
|
-
return
|
|
3178
|
+
return _.any(terms);
|
|
3724
3179
|
}
|
|
3725
3180
|
fold() {
|
|
3726
3181
|
return Mul.fold(this);
|
|
@@ -3729,42 +3184,28 @@ class Mul extends Seq {
|
|
|
3729
3184
|
var isNum = expr => {
|
|
3730
3185
|
return expr instanceof Num;
|
|
3731
3186
|
};
|
|
3732
|
-
const num =
|
|
3187
|
+
const num = _.find(this.terms, isNum);
|
|
3733
3188
|
if (num) {
|
|
3734
3189
|
return this.replace(num, num.negate());
|
|
3735
3190
|
} else {
|
|
3736
3191
|
return new Mul([NumNeg].concat(this.terms));
|
|
3737
3192
|
}
|
|
3738
3193
|
}
|
|
3739
|
-
|
|
3740
|
-
// negative signs should be folded into numbers whenever possible
|
|
3741
|
-
// never fold into a Num that's already negative or a Mul that has a negative Num
|
|
3742
|
-
// an optional hint is kept track of to properly render user input
|
|
3743
|
-
// an empty hint means negation
|
|
3744
3194
|
static handleNegative(expr, hint) {
|
|
3745
3195
|
if (expr instanceof Num && expr.n > 0) {
|
|
3746
|
-
// e.g. - 2 -> -2
|
|
3747
3196
|
var negated = expr.negate();
|
|
3748
|
-
// TODO(alex): rework hint system so that this isn't necessary
|
|
3749
3197
|
negated.hints = expr.hints;
|
|
3750
3198
|
return negated.addHint(hint);
|
|
3751
3199
|
} else if (expr instanceof Mul) {
|
|
3752
|
-
// e.g. - x*2*3 -> x*-2*3
|
|
3753
|
-
// e.g. - x*y -> -1*x*y
|
|
3754
|
-
// e.g. - x*-2 -> -1*x*-2
|
|
3755
3200
|
return expr.factorIn(hint);
|
|
3756
3201
|
} else {
|
|
3757
|
-
// e.g. - x -> -1*x
|
|
3758
3202
|
return new Mul(Num.negativeOne(hint), expr);
|
|
3759
3203
|
}
|
|
3760
3204
|
}
|
|
3761
|
-
|
|
3762
|
-
// division can create either a Rational or a Mul
|
|
3763
3205
|
static handleDivide(left, right) {
|
|
3764
|
-
// dividing by a Mul is the same as repeated division by its terms
|
|
3765
3206
|
if (right instanceof Mul) {
|
|
3766
3207
|
var first = Mul.handleDivide(left, right.terms[0]);
|
|
3767
|
-
var rest = new Mul(
|
|
3208
|
+
var rest = new Mul(_.rest(right.terms)).flatten();
|
|
3768
3209
|
return Mul.handleDivide(first, rest);
|
|
3769
3210
|
}
|
|
3770
3211
|
var isInt = expr => {
|
|
@@ -3773,11 +3214,7 @@ class Mul extends Seq {
|
|
|
3773
3214
|
var isRational = expr => {
|
|
3774
3215
|
return expr instanceof Rational;
|
|
3775
3216
|
};
|
|
3776
|
-
|
|
3777
|
-
// for simplification purposes, fold Ints into Rationals if possible
|
|
3778
|
-
// e.g. 3x / 4 -> 3/4 * x (will still render as 3x/4)
|
|
3779
|
-
if (isInt(right) && left instanceof Mul && ___default["default"].any(left.terms, isInt)) {
|
|
3780
|
-
// search from the right
|
|
3217
|
+
if (isInt(right) && left instanceof Mul && _.any(left.terms, isInt)) {
|
|
3781
3218
|
var reversed = left.terms.slice().reverse();
|
|
3782
3219
|
var num = reversed.find(isRational);
|
|
3783
3220
|
if (!isInt(num)) {
|
|
@@ -3785,9 +3222,6 @@ class Mul extends Seq {
|
|
|
3785
3222
|
}
|
|
3786
3223
|
var rational = new Rational(num.n, right.n);
|
|
3787
3224
|
rational.hints = num.hints;
|
|
3788
|
-
|
|
3789
|
-
// in the case of something like 1/3 * 6/8, we want the
|
|
3790
|
-
// 6/8 to be considered a fraction, not just a division
|
|
3791
3225
|
if (num === reversed[0]) {
|
|
3792
3226
|
rational = rational.addHint("fraction");
|
|
3793
3227
|
}
|
|
@@ -3802,17 +3236,11 @@ class Mul extends Seq {
|
|
|
3802
3236
|
if (b instanceof Int) {
|
|
3803
3237
|
if (a instanceof Int) {
|
|
3804
3238
|
if (a.n < 0 && b.n < 0) {
|
|
3805
|
-
// e.g. -2 / -3 -> -1*-2/3
|
|
3806
3239
|
return [NumNeg, new Rational(a.n, -b.n).addHint("fraction")];
|
|
3807
3240
|
} else {
|
|
3808
|
-
// e.g. 2 / 3 -> 2/3
|
|
3809
|
-
// e.g. -2 / 3 -> -2/3
|
|
3810
|
-
// e.g. 2 / -3 -> -2/3
|
|
3811
3241
|
return [new Rational(a.n, b.n).addHint("fraction")];
|
|
3812
3242
|
}
|
|
3813
3243
|
} else {
|
|
3814
|
-
// e.g. x / 3 -> x*1/3
|
|
3815
|
-
// e.g. x / -3 -> x*-1/3
|
|
3816
3244
|
var inverse = new Rational(1, b.eval());
|
|
3817
3245
|
if (b.eval() < 0) {
|
|
3818
3246
|
return [a, inverse.addHint("negate")];
|
|
@@ -3823,66 +3251,42 @@ class Mul extends Seq {
|
|
|
3823
3251
|
} else {
|
|
3824
3252
|
var pow;
|
|
3825
3253
|
if (b instanceof Trig && b.exp) {
|
|
3826
|
-
// e.g. sin^2(x) -> sin(x)^2
|
|
3827
3254
|
var exp = b.exp;
|
|
3828
3255
|
b.exp = undefined;
|
|
3829
3256
|
b = new Pow(b, exp);
|
|
3830
3257
|
}
|
|
3831
3258
|
if (b instanceof Pow) {
|
|
3832
|
-
// e.g. (x^2) ^ -1 -> x^-2
|
|
3833
|
-
// e.g. (x^y) ^ -1 -> x^(-1*y)
|
|
3834
|
-
// e.g. (x^(yz)) ^ -1 -> x^(-1*y*z)
|
|
3835
3259
|
pow = new Pow(b.base, Mul.handleNegative(b.exp, "divide"));
|
|
3836
3260
|
} else {
|
|
3837
|
-
// e.g. x ^ -1 -> x^-1
|
|
3838
3261
|
pow = new Pow(b, NumDiv);
|
|
3839
3262
|
}
|
|
3840
3263
|
if (a instanceof Int && a.n === 1) {
|
|
3841
|
-
// e.g. 1 / x -> x^-1
|
|
3842
3264
|
return [pow];
|
|
3843
3265
|
} else {
|
|
3844
|
-
// e.g. 2 / x -> 2*x^-1
|
|
3845
3266
|
return [a, pow];
|
|
3846
3267
|
}
|
|
3847
3268
|
}
|
|
3848
3269
|
};
|
|
3849
3270
|
if (left instanceof Mul) {
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
var divided = divide(___default["default"].last(left.terms), right);
|
|
3853
|
-
return new Mul(___default["default"].initial(left.terms).concat(divided));
|
|
3271
|
+
var divided = divide(_.last(left.terms), right);
|
|
3272
|
+
return new Mul(_.initial(left.terms).concat(divided));
|
|
3854
3273
|
} else {
|
|
3855
3274
|
var divided = divide(left, right);
|
|
3856
3275
|
return new Mul(divided).flatten();
|
|
3857
3276
|
}
|
|
3858
3277
|
}
|
|
3859
|
-
|
|
3860
|
-
// fold negative signs into numbers if possible
|
|
3861
|
-
// negative signs are not the same as multiplying by negative one!
|
|
3862
|
-
// e.g. -x -> -1*x simplified
|
|
3863
|
-
// e.g. -2*x -> -2*x simplified
|
|
3864
|
-
// e.g. -x*2 -> -1*x*2 not simplified -> x*-2 simplified
|
|
3865
|
-
// e.g. -1*x*2 -> -1*x*2 not simplified
|
|
3866
|
-
|
|
3867
|
-
// also fold multiplicative terms into open Trig and Log nodes
|
|
3868
|
-
// e.g. (sin x)*x -> sin(x)*x
|
|
3869
|
-
// e.g. sin(x)*x -> sin(x)*x
|
|
3870
|
-
// e.g. sin(x)*(x) -> sin(x)*x
|
|
3871
|
-
// e.g. sin(x)*sin(y) -> sin(x)*sin(y)
|
|
3872
3278
|
static fold(expr) {
|
|
3873
3279
|
if (expr instanceof Mul) {
|
|
3874
|
-
|
|
3875
|
-
var trigLog = ___default["default"].find(___default["default"].initial(expr.terms), term => {
|
|
3280
|
+
var trigLog = _.find(_.initial(expr.terms), term => {
|
|
3876
3281
|
return (term instanceof Trig || term instanceof Log) && Boolean(term.hints.open);
|
|
3877
3282
|
});
|
|
3878
3283
|
if (trigLog) {
|
|
3879
|
-
|
|
3880
|
-
const last = ___default["default"].last(expr.terms);
|
|
3284
|
+
const last = _.last(expr.terms);
|
|
3881
3285
|
if (trigLog.hints.parens || last.hints.parens || last.has(Trig) || last.has(Log)) {
|
|
3882
3286
|
trigLog.hints.open = false;
|
|
3883
3287
|
} else {
|
|
3884
3288
|
const newTrigLog = trigLog instanceof Trig ? Trig.create([trigLog.type, trigLog.exp], Mul.createOrAppend(trigLog.arg, last).fold()) : Log.create(trigLog.base, Mul.createOrAppend(trigLog.power, last).fold());
|
|
3885
|
-
const index =
|
|
3289
|
+
const index = _.indexOf(expr.terms, trigLog);
|
|
3886
3290
|
if (index === 0) {
|
|
3887
3291
|
return newTrigLog;
|
|
3888
3292
|
} else {
|
|
@@ -3898,32 +3302,21 @@ class Mul extends Seq {
|
|
|
3898
3302
|
var neg = num => {
|
|
3899
3303
|
return num.n === -1 && Boolean(num.hints.negate);
|
|
3900
3304
|
};
|
|
3901
|
-
var posOrNeg = function (num) {
|
|
3305
|
+
var posOrNeg = function posOrNeg(num) {
|
|
3902
3306
|
return pos(num) || neg(num);
|
|
3903
3307
|
};
|
|
3904
|
-
|
|
3905
|
-
// @ts-expect-error: Type 'Expr' is not assignable to type 'Num'.
|
|
3906
3308
|
const posNum = numbers.find(pos);
|
|
3907
|
-
// @ts-expect-error: Type 'Expr' is not assignable to type 'Num'.
|
|
3908
3309
|
const negNum = numbers.find(neg);
|
|
3909
|
-
if (numbers.length > 1 && negNum && posNum &&
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
var firstNeg = ___default["default"].indexOf(expr.terms, negNum);
|
|
3913
|
-
var firstNum = ___default["default"].indexOf(expr.terms, posNum);
|
|
3914
|
-
|
|
3915
|
-
// e.g. -x*2 -> x*-2
|
|
3310
|
+
if (numbers.length > 1 && negNum && posNum && _.every(numbers, posOrNeg)) {
|
|
3311
|
+
var firstNeg = _.indexOf(expr.terms, negNum);
|
|
3312
|
+
var firstNum = _.indexOf(expr.terms, posNum);
|
|
3916
3313
|
if (firstNeg < firstNum) {
|
|
3917
3314
|
return expr.replace(firstNum, expr.terms[firstNum].negate()).remove(firstNeg);
|
|
3918
3315
|
}
|
|
3919
3316
|
}
|
|
3920
3317
|
}
|
|
3921
|
-
|
|
3922
|
-
// in all other cases, make no change
|
|
3923
3318
|
return expr;
|
|
3924
3319
|
}
|
|
3925
|
-
|
|
3926
|
-
// create a new sequence unless left is already one (returns a copy)
|
|
3927
3320
|
static createOrAppend(left, right) {
|
|
3928
3321
|
if (left instanceof Mul) {
|
|
3929
3322
|
return new Mul(left.terms.concat(right));
|
|
@@ -3932,39 +3325,23 @@ class Mul extends Seq {
|
|
|
3932
3325
|
}
|
|
3933
3326
|
}
|
|
3934
3327
|
}
|
|
3935
|
-
|
|
3936
|
-
/* exponentiation */
|
|
3937
3328
|
class Pow extends Expr {
|
|
3938
|
-
base;
|
|
3939
|
-
exp;
|
|
3940
3329
|
constructor(base, exp) {
|
|
3941
3330
|
super();
|
|
3331
|
+
this.base = void 0;
|
|
3332
|
+
this.exp = void 0;
|
|
3333
|
+
this.func = Pow;
|
|
3942
3334
|
this.base = base;
|
|
3943
3335
|
this.exp = exp;
|
|
3944
3336
|
}
|
|
3945
|
-
func = Pow;
|
|
3946
3337
|
args() {
|
|
3947
3338
|
return [this.base, this.exp];
|
|
3948
3339
|
}
|
|
3949
|
-
eval() {
|
|
3950
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
3951
|
-
let options = arguments.length > 1 ? arguments[1] : undefined;
|
|
3340
|
+
eval(vars = {}, options) {
|
|
3952
3341
|
var evaledBase = this.base.eval(vars, options);
|
|
3953
3342
|
var evaledExp = this.exp.eval(vars, options);
|
|
3954
|
-
|
|
3955
|
-
// Math.pow unequivocally returns NaN when provided with both a
|
|
3956
|
-
// negative base and a fractional exponent. However, in some cases, we
|
|
3957
|
-
// know that our exponent is actually valid for use with negative
|
|
3958
|
-
// bases (e.g., (-5)^(1/3)).
|
|
3959
|
-
//
|
|
3960
|
-
// Here, we explicitly check for such cases. We really only handle a
|
|
3961
|
-
// limited subset (by requiring that the exponent is rational with an
|
|
3962
|
-
// odd denominator), but it's still useful.
|
|
3963
|
-
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
|
|
3964
3343
|
if (evaledBase < 0) {
|
|
3965
3344
|
var simplifiedExp = this.exp.simplify();
|
|
3966
|
-
|
|
3967
|
-
// If Float, convert to a Rational to enable the logic below
|
|
3968
3345
|
if (simplifiedExp instanceof Float) {
|
|
3969
3346
|
var num = simplifiedExp.n;
|
|
3970
3347
|
var decimals = (num - +num.toFixed()).toString().length - 2;
|
|
@@ -3987,7 +3364,6 @@ class Pow extends Expr {
|
|
|
3987
3364
|
return this.base.getUnits().map(unit => {
|
|
3988
3365
|
return {
|
|
3989
3366
|
unit: unit.unit,
|
|
3990
|
-
// Exponents in units should always be integers
|
|
3991
3367
|
pow: unit.pow * this.exp.n
|
|
3992
3368
|
};
|
|
3993
3369
|
});
|
|
@@ -4004,31 +3380,24 @@ class Pow extends Expr {
|
|
|
4004
3380
|
}
|
|
4005
3381
|
tex() {
|
|
4006
3382
|
if (this.isDivide()) {
|
|
4007
|
-
// e.g. x ^ -1 w/hint -> 1/x
|
|
4008
3383
|
return "\\frac{1}{" + this.asDivide().tex() + "}";
|
|
4009
3384
|
} else if (this.isRoot() && isRational(this.exp)) {
|
|
4010
3385
|
if (this.exp.n !== 1) {
|
|
4011
3386
|
throw new Error("Node marked with hint 'root' does not have exponent " + "of form 1/x.");
|
|
4012
3387
|
}
|
|
4013
3388
|
if (this.exp.d === 2) {
|
|
4014
|
-
// e.g. x ^ 1/2 w/hint -> sqrt{x}
|
|
4015
3389
|
return "\\sqrt{" + this.base.tex() + "}";
|
|
4016
3390
|
} else {
|
|
4017
|
-
// e.g. x ^ 1/y w/hint -> sqrt[y]{x}
|
|
4018
3391
|
return "\\sqrt[" + this.exp.d + "]{" + this.base.tex() + "}";
|
|
4019
3392
|
}
|
|
4020
3393
|
} else if (this.base instanceof Trig && !this.base.isInverse() && this.exp instanceof Num && this.exp.isSimple() && this.exp.eval() >= 0) {
|
|
4021
|
-
// e.g sin(x) ^ 2 -> sin^2(x)
|
|
4022
3394
|
var split = this.base.texSplit();
|
|
4023
3395
|
return split[0] + "^{" + this.exp.tex() + "}" + split[1];
|
|
4024
3396
|
} else {
|
|
4025
|
-
// e.g. x ^ y -> x^y
|
|
4026
3397
|
var base = this.base.tex();
|
|
4027
3398
|
if (this.base instanceof Seq || this.base instanceof Pow || this.base instanceof Num && !this.base.isSimple()) {
|
|
4028
|
-
// e.g. a+b ^ c -> (a+b)^c
|
|
4029
3399
|
base = "(" + base + ")";
|
|
4030
3400
|
} else if (this.base instanceof Trig || this.base instanceof Log) {
|
|
4031
|
-
// e.g. ln(x) ^ 2 -> [ln(x)]^2
|
|
4032
3401
|
base = "[" + base + "]";
|
|
4033
3402
|
}
|
|
4034
3403
|
return base + "^{" + this.exp.tex() + "}";
|
|
@@ -4040,43 +3409,30 @@ class Pow extends Expr {
|
|
|
4040
3409
|
expand() {
|
|
4041
3410
|
var pow = this.recurse("expand");
|
|
4042
3411
|
if (pow.base instanceof Mul) {
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
var terms = ___default["default"].map(pow.base.terms, term => {
|
|
3412
|
+
var terms = _.map(pow.base.terms, term => {
|
|
4046
3413
|
return new Pow(term, pow.exp);
|
|
4047
3414
|
});
|
|
4048
3415
|
return new Mul(terms).expand();
|
|
4049
3416
|
} else if (pow.base instanceof Add && pow.exp instanceof Int && pow.exp.abs().eval() > 1) {
|
|
4050
|
-
// e.g. (a+b)^2 -> a*a+a*b+a*b+b*b
|
|
4051
|
-
// e.g. (a+b)^-2 -> (a*a+a*b+a*b+b*b)^-1
|
|
4052
|
-
|
|
4053
3417
|
var positive = pow.exp.eval() > 0;
|
|
4054
3418
|
var n = pow.exp.abs().eval();
|
|
4055
|
-
var signed = function (mul) {
|
|
3419
|
+
var signed = function signed(mul) {
|
|
4056
3420
|
return positive ? mul : new Pow(mul, NumDiv);
|
|
4057
3421
|
};
|
|
4058
|
-
|
|
4059
|
-
// compute and cache powers of 2 up to n
|
|
4060
3422
|
const cache = {
|
|
4061
3423
|
1: pow.base
|
|
4062
3424
|
};
|
|
4063
3425
|
for (var i = 2; i <= n; i *= 2) {
|
|
4064
|
-
const
|
|
4065
|
-
cache[i] =
|
|
3426
|
+
const _mul = new Mul(cache[i / 2], cache[i / 2]);
|
|
3427
|
+
cache[i] = _mul.expand().collect();
|
|
4066
3428
|
}
|
|
4067
|
-
|
|
4068
|
-
// if n is a power of 2, you're done!
|
|
4069
3429
|
if (n in cache) {
|
|
4070
3430
|
return signed(cache[n]);
|
|
4071
3431
|
}
|
|
4072
|
-
|
|
4073
|
-
// otherwise decompose n into powers of 2 ...
|
|
4074
|
-
let indices = ___default["default"].map(n.toString(2).split(""), function (str, i, list) {
|
|
3432
|
+
let indices = _.map(n.toString(2).split(""), function (str, i, list) {
|
|
4075
3433
|
return Number(str) * Math.pow(2, list.length - i - 1);
|
|
4076
3434
|
});
|
|
4077
|
-
indices =
|
|
4078
|
-
|
|
4079
|
-
// ... then combine
|
|
3435
|
+
indices = _.without(indices, 0);
|
|
4080
3436
|
const factors = [];
|
|
4081
3437
|
for (const index of indices) {
|
|
4082
3438
|
if (index in cache) {
|
|
@@ -4086,13 +3442,10 @@ class Pow extends Expr {
|
|
|
4086
3442
|
const mul = new Mul(factors).expand().collect();
|
|
4087
3443
|
return signed(mul);
|
|
4088
3444
|
} else if (pow.exp instanceof Add) {
|
|
4089
|
-
|
|
4090
|
-
// e.g. x^(a+b) -> x^a*x^b
|
|
4091
|
-
|
|
4092
|
-
const terms = ___default["default"].map(pow.exp.terms, term => {
|
|
3445
|
+
const _terms = _.map(pow.exp.terms, term => {
|
|
4093
3446
|
return new Pow(pow.base, term).expand();
|
|
4094
3447
|
});
|
|
4095
|
-
return new Mul(
|
|
3448
|
+
return new Mul(_terms).expand();
|
|
4096
3449
|
} else {
|
|
4097
3450
|
return pow;
|
|
4098
3451
|
}
|
|
@@ -4100,10 +3453,8 @@ class Pow extends Expr {
|
|
|
4100
3453
|
factor() {
|
|
4101
3454
|
var pow = this.recurse("factor");
|
|
4102
3455
|
if (pow.base instanceof Mul) {
|
|
4103
|
-
var terms =
|
|
3456
|
+
var terms = _.map(pow.base.terms, term => {
|
|
4104
3457
|
if (term instanceof Int && pow.exp.equals(NumDiv)) {
|
|
4105
|
-
// Anything that can be a Rational should be a Rational
|
|
4106
|
-
// e.g. 2^(-1) -> 1/2
|
|
4107
3458
|
return new Rational(1, term.n);
|
|
4108
3459
|
} else {
|
|
4109
3460
|
return new Pow(term, pow.exp);
|
|
@@ -4116,86 +3467,54 @@ class Pow extends Expr {
|
|
|
4116
3467
|
}
|
|
4117
3468
|
collect(options) {
|
|
4118
3469
|
if (this.base instanceof Pow) {
|
|
4119
|
-
// collect this first to avoid having to deal with float precision
|
|
4120
|
-
// e.g. sqrt(2)^2 -> 2, not 2.0000000000000004
|
|
4121
|
-
// e.g. (x^y)^z -> x^(yz)
|
|
4122
3470
|
const base = this.base.base;
|
|
4123
3471
|
const exp = Mul.createOrAppend(this.base.exp, this.exp);
|
|
4124
3472
|
return new Pow(base, exp).collect(options);
|
|
4125
3473
|
}
|
|
4126
3474
|
const pow = this.recurse("collect", options);
|
|
4127
|
-
const isSimilarLog = function (term) {
|
|
3475
|
+
const isSimilarLog = function isSimilarLog(term) {
|
|
4128
3476
|
return term instanceof Log && term.base.equals(pow.base);
|
|
4129
3477
|
};
|
|
4130
3478
|
if (pow.exp instanceof Num && pow.exp.eval() === 0) {
|
|
4131
|
-
// e.g. x^0 -> 1
|
|
4132
3479
|
return NumOne;
|
|
4133
3480
|
} else if (pow.exp instanceof Num && pow.exp.eval() === 1) {
|
|
4134
|
-
// e.g. x^1 -> x
|
|
4135
3481
|
return pow.base;
|
|
4136
3482
|
} else if (isSimilarLog(pow.exp)) {
|
|
4137
|
-
// e.g. b^(log_b(x)) -> x
|
|
4138
3483
|
return pow.exp.power;
|
|
4139
|
-
} else if (pow.exp instanceof Mul &&
|
|
4140
|
-
// e.g. b^(2*y*log_b(x)) -> x^(2*y)
|
|
4141
|
-
// `log` will always be defined here because of the
|
|
4142
|
-
// `_.any(pow.exp.terms, isSimilarLog)` check above.
|
|
3484
|
+
} else if (pow.exp instanceof Mul && _.any(pow.exp.terms, isSimilarLog)) {
|
|
4143
3485
|
const log = pow.exp.terms.find(isSimilarLog);
|
|
4144
3486
|
const base = log.power;
|
|
4145
3487
|
const exp = pow.exp.remove(log).flatten();
|
|
4146
3488
|
return new Pow(base, exp).collect(options);
|
|
4147
3489
|
} else if (pow.base instanceof Num && pow.exp instanceof Num) {
|
|
4148
|
-
// TODO(alex): Consider encapsualting this logic (and similar logic
|
|
4149
|
-
// elsewhere) into a separate Decimal class for user-entered floats
|
|
4150
3490
|
if (options && options.preciseFloats) {
|
|
4151
|
-
// Avoid creating an imprecise float
|
|
4152
|
-
// e.g. 23^1.5 -> 12167^0.5, not ~110.304
|
|
4153
|
-
|
|
4154
|
-
// If you take the root as specified by the denominator and
|
|
4155
|
-
// end up with more digits after the decimal point,
|
|
4156
|
-
// the result is imprecise. This works for rationals as well
|
|
4157
|
-
// as floats, but ideally rationals should be pre-processed
|
|
4158
|
-
// e.g. (1/27)^(1/3) -> 1/3 to avoid most cases.
|
|
4159
|
-
// TODO(alex): Catch such cases and avoid converting to floats.
|
|
4160
3491
|
const exp = pow.exp.asRational();
|
|
4161
3492
|
const decimalsInBase = pow.base.getDecimalPlaces();
|
|
4162
3493
|
const root = new Pow(pow.base, new Rational(1, exp.d));
|
|
4163
|
-
const decimalsInRoot = root.collect()
|
|
4164
|
-
// @ts-expect-error: we assume that `root.collect()` returns
|
|
4165
|
-
// a Num here but tbh I'm not sure how this code isn't causing
|
|
4166
|
-
// an infinite loop.
|
|
4167
|
-
.getDecimalPlaces();
|
|
3494
|
+
const decimalsInRoot = root.collect().getDecimalPlaces();
|
|
4168
3495
|
if (decimalsInRoot > decimalsInBase) {
|
|
4169
|
-
// Collecting over this denominator would result in an
|
|
4170
|
-
// imprecise float, so avoid doing so.
|
|
4171
3496
|
const newBase = new Pow(pow.base, new Int(exp.n)).collect();
|
|
4172
3497
|
return new Pow(newBase, new Rational(1, exp.d));
|
|
4173
3498
|
}
|
|
4174
3499
|
}
|
|
4175
|
-
|
|
4176
|
-
// e.g. 4^1.5 -> 8
|
|
4177
3500
|
return pow.base.raiseToThe(pow.exp, options);
|
|
4178
3501
|
} else {
|
|
4179
3502
|
return pow;
|
|
4180
3503
|
}
|
|
4181
3504
|
}
|
|
4182
|
-
|
|
4183
|
-
// checks whether this Pow represents user-entered division
|
|
4184
3505
|
isDivide() {
|
|
4185
|
-
var isDiv = function (arg) {
|
|
3506
|
+
var isDiv = function isDiv(arg) {
|
|
4186
3507
|
return arg instanceof Num && Boolean(arg.hints.divide);
|
|
4187
3508
|
};
|
|
4188
|
-
return isDiv(this.exp) || this.exp instanceof Mul &&
|
|
3509
|
+
return isDiv(this.exp) || this.exp instanceof Mul && _.any(this.exp.terms, isDiv);
|
|
4189
3510
|
}
|
|
4190
|
-
|
|
4191
|
-
// assuming this Pow represents user-entered division, returns the denominator
|
|
4192
3511
|
asDivide() {
|
|
4193
3512
|
if (this.exp instanceof Num) {
|
|
4194
3513
|
if (this.exp.eval() === -1) {
|
|
4195
3514
|
return this.base;
|
|
4196
3515
|
} else {
|
|
4197
3516
|
var negated = this.exp.negate();
|
|
4198
|
-
negated.hints =
|
|
3517
|
+
negated.hints = _.clone(this.exp.hints);
|
|
4199
3518
|
negated.hints.divide = false;
|
|
4200
3519
|
return new Pow(this.base, negated);
|
|
4201
3520
|
}
|
|
@@ -4211,9 +3530,6 @@ class Pow extends Expr {
|
|
|
4211
3530
|
isSquaredTrig() {
|
|
4212
3531
|
return this.base instanceof Trig && !this.base.isInverse() && this.exp instanceof Num && this.exp.eval() === 2;
|
|
4213
3532
|
}
|
|
4214
|
-
|
|
4215
|
-
// extract whatever denominator makes sense, ignoring hints
|
|
4216
|
-
// if negative exponent, will recursively include the base's denominator as well
|
|
4217
3533
|
getDenominator() {
|
|
4218
3534
|
if (this.exp instanceof Num && this.exp.eval() === -1) {
|
|
4219
3535
|
return Mul.createOrAppend(this.base, this.base.getDenominator()).flatten();
|
|
@@ -4228,27 +3544,17 @@ class Pow extends Expr {
|
|
|
4228
3544
|
}
|
|
4229
3545
|
findGCD(factor) {
|
|
4230
3546
|
const [base, exp] = factor instanceof Pow ? [factor.base, factor.exp] : [factor, NumOne];
|
|
4231
|
-
|
|
4232
|
-
// GCD is only relevant if same base
|
|
4233
3547
|
if (this.base.equals(base)) {
|
|
4234
3548
|
if (this.exp.equals(exp)) {
|
|
4235
|
-
// exact match
|
|
4236
|
-
// e.g. GCD(x^y^z, x^y^z) -> x^y^z
|
|
4237
3549
|
return this;
|
|
4238
3550
|
} else if (this.exp instanceof Num && exp instanceof Num) {
|
|
4239
|
-
// two numerical exponents
|
|
4240
|
-
// e.g. GCD(x^3, x^2) -> x^2
|
|
4241
3551
|
return new Pow(this.base, Num.min(this.exp, exp)).collect();
|
|
4242
3552
|
} else if (this.exp instanceof Num || exp instanceof Num) {
|
|
4243
|
-
// one numerical exponent
|
|
4244
|
-
// e.g. GCD(x^2, x^y) -> 1
|
|
4245
3553
|
return NumOne;
|
|
4246
3554
|
}
|
|
4247
3555
|
var expA = this.exp.asMul().partition();
|
|
4248
3556
|
var expB = exp.asMul().partition();
|
|
4249
3557
|
if (expA[1].equals(expB[1])) {
|
|
4250
|
-
// exponents match except for coefficient
|
|
4251
|
-
// e.g. GCD(x^3y, x^y) -> x^y
|
|
4252
3558
|
var coefficient = Num.min(expA[0].reduce(), expB[0].reduce());
|
|
4253
3559
|
var mul = new Mul(coefficient, expA[1].flatten()).flatten();
|
|
4254
3560
|
return new Pow(base, mul).collect();
|
|
@@ -4271,10 +3577,8 @@ class Pow extends Expr {
|
|
|
4271
3577
|
if (exp instanceof Int) {
|
|
4272
3578
|
var n = exp.eval();
|
|
4273
3579
|
if (n > 2) {
|
|
4274
|
-
// e.g. x^3 -> x^2
|
|
4275
3580
|
return new Pow(this.base, new Int(n - 1));
|
|
4276
3581
|
} else if (n < -2) {
|
|
4277
|
-
// e.g. x^-3 -> x^-2
|
|
4278
3582
|
return new Pow(this.base, new Int(n + 1));
|
|
4279
3583
|
}
|
|
4280
3584
|
}
|
|
@@ -4284,40 +3588,28 @@ class Pow extends Expr {
|
|
|
4284
3588
|
static sqrt(arg) {
|
|
4285
3589
|
return new Pow(arg, NumSqrt);
|
|
4286
3590
|
}
|
|
4287
|
-
|
|
4288
|
-
// NOTE(kevinb): nthroot is used as a constructor so we need to
|
|
4289
|
-
// define it as a static property instead of a static method.
|
|
4290
|
-
// TODO(kevinb): update parser-generator.ts to call nthrooth
|
|
4291
|
-
// without using `new`.
|
|
4292
|
-
static nthroot = function (radicand, degree) {
|
|
4293
|
-
var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
|
|
4294
|
-
|
|
4295
|
-
// FIXME(johnsullivan): If oneOverDegree ends up being a pow object,
|
|
4296
|
-
// this "root" hint is lost between here and when tex() is called.
|
|
4297
|
-
return new Pow(radicand, exp.addHint("root"));
|
|
4298
|
-
};
|
|
4299
3591
|
}
|
|
4300
|
-
|
|
4301
|
-
|
|
3592
|
+
_class5 = Pow;
|
|
3593
|
+
Pow.nthroot = function (radicand, degree) {
|
|
3594
|
+
var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
|
|
3595
|
+
return new _class5(radicand, exp.addHint("root"));
|
|
3596
|
+
};
|
|
4302
3597
|
class Log extends Expr {
|
|
4303
|
-
base;
|
|
4304
|
-
power;
|
|
4305
3598
|
constructor(base, power) {
|
|
4306
3599
|
super();
|
|
3600
|
+
this.base = void 0;
|
|
3601
|
+
this.power = void 0;
|
|
3602
|
+
this.func = Log;
|
|
4307
3603
|
this.base = base;
|
|
4308
3604
|
this.power = power;
|
|
4309
|
-
this.hints = {
|
|
4310
|
-
...this.hints,
|
|
3605
|
+
this.hints = _extends({}, this.hints, {
|
|
4311
3606
|
open: false
|
|
4312
|
-
};
|
|
3607
|
+
});
|
|
4313
3608
|
}
|
|
4314
|
-
func = Log;
|
|
4315
3609
|
args() {
|
|
4316
3610
|
return [this.base, this.power];
|
|
4317
3611
|
}
|
|
4318
|
-
eval() {
|
|
4319
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
4320
|
-
let options = arguments.length > 1 ? arguments[1] : undefined;
|
|
3612
|
+
eval(vars = {}, options) {
|
|
4321
3613
|
return Math.log(this.power.eval(vars, options)) / Math.log(this.base.eval(vars, options));
|
|
4322
3614
|
}
|
|
4323
3615
|
codegen() {
|
|
@@ -4342,13 +3634,10 @@ class Log extends Expr {
|
|
|
4342
3634
|
collect(options) {
|
|
4343
3635
|
var log = this.recurse("collect", options);
|
|
4344
3636
|
if (log.power instanceof Num && log.power.eval() === 1) {
|
|
4345
|
-
// e.g. ln(1) -> 0
|
|
4346
3637
|
return NumZero;
|
|
4347
3638
|
} else if (log.base.equals(log.power)) {
|
|
4348
|
-
// e.g. log_b(b) -> 1
|
|
4349
3639
|
return NumOne;
|
|
4350
3640
|
} else if (log.power instanceof Pow && log.power.base.equals(log.base)) {
|
|
4351
|
-
// e.g. log_b(b^x) -> x
|
|
4352
3641
|
return log.power.exp;
|
|
4353
3642
|
} else {
|
|
4354
3643
|
return log;
|
|
@@ -4357,21 +3646,13 @@ class Log extends Expr {
|
|
|
4357
3646
|
expand() {
|
|
4358
3647
|
var log = this.recurse("expand");
|
|
4359
3648
|
if (log.power instanceof Mul) {
|
|
4360
|
-
|
|
4361
|
-
// e.g. ln(xy) -> ln(x) + ln(y)
|
|
4362
|
-
|
|
4363
|
-
var terms = ___default["default"].map(log.power.terms, term => {
|
|
4364
|
-
// need to expand again in case new log powers are Pows
|
|
3649
|
+
var terms = _.map(log.power.terms, term => {
|
|
4365
3650
|
return new Log(log.base, term).expand();
|
|
4366
3651
|
});
|
|
4367
3652
|
return new Add(terms);
|
|
4368
3653
|
} else if (log.power instanceof Pow) {
|
|
4369
|
-
// e.g. ln(x^y) -> y*ln(x)
|
|
4370
|
-
|
|
4371
3654
|
return new Mul(log.power.exp, new Log(log.base, log.power.base).expand()).flatten();
|
|
4372
3655
|
} else if (!log.isNatural()) {
|
|
4373
|
-
// e.g. log_b(x) -> ln(x)/ln(b)
|
|
4374
|
-
|
|
4375
3656
|
return Mul.handleDivide(new Log(Const.e, log.power), new Log(Const.e, log.base));
|
|
4376
3657
|
} else {
|
|
4377
3658
|
return log;
|
|
@@ -4405,178 +3686,172 @@ class Log extends Expr {
|
|
|
4405
3686
|
return log;
|
|
4406
3687
|
}
|
|
4407
3688
|
}
|
|
4408
|
-
/* trigonometric functions */
|
|
4409
3689
|
class Trig extends Expr {
|
|
4410
|
-
type
|
|
4411
|
-
arg;
|
|
4412
|
-
exp;
|
|
4413
|
-
constructor(type, arg) {
|
|
3690
|
+
constructor(type, _arg) {
|
|
4414
3691
|
super();
|
|
4415
|
-
this.type =
|
|
4416
|
-
this.arg =
|
|
4417
|
-
this.
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
}
|
|
4426
|
-
|
|
4427
|
-
// TODO(kevinb): Use union type for the function names.
|
|
4428
|
-
functions = {
|
|
4429
|
-
sin: {
|
|
4430
|
-
eval: Math.sin,
|
|
4431
|
-
codegen: "Math.sin((",
|
|
4432
|
-
tex: "\\sin",
|
|
4433
|
-
expand: () => this
|
|
4434
|
-
},
|
|
4435
|
-
cos: {
|
|
4436
|
-
eval: Math.cos,
|
|
4437
|
-
codegen: "Math.cos((",
|
|
4438
|
-
tex: "\\cos",
|
|
4439
|
-
expand: () => this
|
|
4440
|
-
},
|
|
4441
|
-
tan: {
|
|
4442
|
-
eval: Math.tan,
|
|
4443
|
-
codegen: "Math.tan((",
|
|
4444
|
-
tex: "\\tan",
|
|
4445
|
-
expand: () => Mul.handleDivide(Trig.sin(this.arg), Trig.cos(this.arg))
|
|
4446
|
-
},
|
|
4447
|
-
csc: {
|
|
4448
|
-
eval: arg => {
|
|
4449
|
-
return 1 / Math.sin(arg);
|
|
4450
|
-
},
|
|
4451
|
-
codegen: "(1/Math.sin(",
|
|
4452
|
-
tex: "\\csc",
|
|
4453
|
-
expand: () => Mul.handleDivide(NumOne, Trig.sin(this.arg))
|
|
4454
|
-
},
|
|
4455
|
-
sec: {
|
|
4456
|
-
eval: arg => {
|
|
4457
|
-
return 1 / Math.cos(arg);
|
|
3692
|
+
this.type = void 0;
|
|
3693
|
+
this.arg = void 0;
|
|
3694
|
+
this.exp = void 0;
|
|
3695
|
+
this.func = Trig;
|
|
3696
|
+
this.functions = {
|
|
3697
|
+
sin: {
|
|
3698
|
+
eval: Math.sin,
|
|
3699
|
+
codegen: "Math.sin((",
|
|
3700
|
+
tex: "\\sin",
|
|
3701
|
+
expand: () => this
|
|
4458
3702
|
},
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
eval: arg => {
|
|
4465
|
-
return 1 / Math.tan(arg);
|
|
3703
|
+
cos: {
|
|
3704
|
+
eval: Math.cos,
|
|
3705
|
+
codegen: "Math.cos((",
|
|
3706
|
+
tex: "\\cos",
|
|
3707
|
+
expand: () => this
|
|
4466
3708
|
},
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
eval: Math.asin,
|
|
4473
|
-
codegen: "Math.asin((",
|
|
4474
|
-
tex: "\\arcsin"
|
|
4475
|
-
},
|
|
4476
|
-
arccos: {
|
|
4477
|
-
eval: Math.acos,
|
|
4478
|
-
codegen: "Math.acos((",
|
|
4479
|
-
tex: "\\arccos"
|
|
4480
|
-
},
|
|
4481
|
-
arctan: {
|
|
4482
|
-
eval: Math.atan,
|
|
4483
|
-
codegen: "Math.atan((",
|
|
4484
|
-
tex: "\\arctan"
|
|
4485
|
-
},
|
|
4486
|
-
arccsc: {
|
|
4487
|
-
eval: arg => {
|
|
4488
|
-
return Math.asin(1 / arg);
|
|
3709
|
+
tan: {
|
|
3710
|
+
eval: Math.tan,
|
|
3711
|
+
codegen: "Math.tan((",
|
|
3712
|
+
tex: "\\tan",
|
|
3713
|
+
expand: () => Mul.handleDivide(Trig.sin(this.arg), Trig.cos(this.arg))
|
|
4489
3714
|
},
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
3715
|
+
csc: {
|
|
3716
|
+
eval: arg => {
|
|
3717
|
+
return 1 / Math.sin(arg);
|
|
3718
|
+
},
|
|
3719
|
+
codegen: "(1/Math.sin(",
|
|
3720
|
+
tex: "\\csc",
|
|
3721
|
+
expand: () => Mul.handleDivide(NumOne, Trig.sin(this.arg))
|
|
4496
3722
|
},
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
3723
|
+
sec: {
|
|
3724
|
+
eval: arg => {
|
|
3725
|
+
return 1 / Math.cos(arg);
|
|
3726
|
+
},
|
|
3727
|
+
codegen: "(1/Math.cos(",
|
|
3728
|
+
tex: "\\sec",
|
|
3729
|
+
expand: () => Mul.handleDivide(NumOne, Trig.cos(this.arg))
|
|
4503
3730
|
},
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
3731
|
+
cot: {
|
|
3732
|
+
eval: arg => {
|
|
3733
|
+
return 1 / Math.tan(arg);
|
|
3734
|
+
},
|
|
3735
|
+
codegen: "(1/Math.tan(",
|
|
3736
|
+
tex: "\\cot",
|
|
3737
|
+
expand: () => Mul.handleDivide(Trig.cos(this.arg), Trig.sin(this.arg))
|
|
4510
3738
|
},
|
|
4511
|
-
|
|
4512
|
-
|
|
3739
|
+
arcsin: {
|
|
3740
|
+
eval: Math.asin,
|
|
3741
|
+
codegen: "Math.asin((",
|
|
3742
|
+
tex: "\\arcsin"
|
|
4513
3743
|
},
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
eval: arg => {
|
|
4519
|
-
return (Math.exp(arg) + Math.exp(-arg)) / 2;
|
|
3744
|
+
arccos: {
|
|
3745
|
+
eval: Math.acos,
|
|
3746
|
+
codegen: "Math.acos((",
|
|
3747
|
+
tex: "\\arccos"
|
|
4520
3748
|
},
|
|
4521
|
-
|
|
4522
|
-
|
|
3749
|
+
arctan: {
|
|
3750
|
+
eval: Math.atan,
|
|
3751
|
+
codegen: "Math.atan((",
|
|
3752
|
+
tex: "\\arctan"
|
|
4523
3753
|
},
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
3754
|
+
arccsc: {
|
|
3755
|
+
eval: arg => {
|
|
3756
|
+
return Math.asin(1 / arg);
|
|
3757
|
+
},
|
|
3758
|
+
codegen: "Math.asin(1/(",
|
|
3759
|
+
tex: "\\operatorname{arccsc}"
|
|
4530
3760
|
},
|
|
4531
|
-
|
|
4532
|
-
|
|
3761
|
+
arcsec: {
|
|
3762
|
+
eval: arg => {
|
|
3763
|
+
return Math.acos(1 / arg);
|
|
3764
|
+
},
|
|
3765
|
+
codegen: "Math.acos(1/(",
|
|
3766
|
+
tex: "\\operatorname{arcsec}"
|
|
4533
3767
|
},
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
3768
|
+
arccot: {
|
|
3769
|
+
eval: arg => {
|
|
3770
|
+
return Math.atan(1 / arg);
|
|
3771
|
+
},
|
|
3772
|
+
codegen: "Math.atan(1/(",
|
|
3773
|
+
tex: "\\operatorname{arccot}"
|
|
4540
3774
|
},
|
|
4541
|
-
|
|
4542
|
-
|
|
3775
|
+
sinh: {
|
|
3776
|
+
eval: arg => {
|
|
3777
|
+
return (Math.exp(arg) - Math.exp(-arg)) / 2;
|
|
3778
|
+
},
|
|
3779
|
+
codegen: argStr => {
|
|
3780
|
+
return "((Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))) / 2)";
|
|
3781
|
+
},
|
|
3782
|
+
tex: "\\sinh",
|
|
3783
|
+
expand: () => this
|
|
4543
3784
|
},
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
3785
|
+
cosh: {
|
|
3786
|
+
eval: arg => {
|
|
3787
|
+
return (Math.exp(arg) + Math.exp(-arg)) / 2;
|
|
3788
|
+
},
|
|
3789
|
+
codegen: argStr => {
|
|
3790
|
+
return "((Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))) / 2)";
|
|
3791
|
+
},
|
|
3792
|
+
tex: "\\cosh",
|
|
3793
|
+
expand: () => this
|
|
4550
3794
|
},
|
|
4551
|
-
|
|
4552
|
-
|
|
3795
|
+
tanh: {
|
|
3796
|
+
eval: arg => {
|
|
3797
|
+
return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
|
|
3798
|
+
},
|
|
3799
|
+
codegen: argStr => {
|
|
3800
|
+
return "(" + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + ")";
|
|
3801
|
+
},
|
|
3802
|
+
tex: "\\tanh",
|
|
3803
|
+
expand: () => Mul.handleDivide(Trig.sinh(this.arg), Trig.cosh(this.arg))
|
|
4553
3804
|
},
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
3805
|
+
csch: {
|
|
3806
|
+
eval: arg => {
|
|
3807
|
+
return 2 / (Math.exp(arg) - Math.exp(-arg));
|
|
3808
|
+
},
|
|
3809
|
+
codegen: argStr => {
|
|
3810
|
+
return "(2 / (Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))))";
|
|
3811
|
+
},
|
|
3812
|
+
tex: "\\csch",
|
|
3813
|
+
expand: () => Mul.handleDivide(NumOne, Trig.sinh(this.arg))
|
|
4560
3814
|
},
|
|
4561
|
-
|
|
4562
|
-
|
|
3815
|
+
sech: {
|
|
3816
|
+
eval: arg => {
|
|
3817
|
+
return 2 / (Math.exp(arg) + Math.exp(-arg));
|
|
3818
|
+
},
|
|
3819
|
+
codegen: argStr => {
|
|
3820
|
+
return "(2 / (Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))))";
|
|
3821
|
+
},
|
|
3822
|
+
tex: "\\sech",
|
|
3823
|
+
expand: () => Mul.handleDivide(NumOne, Trig.cosh(this.arg))
|
|
4563
3824
|
},
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
3825
|
+
coth: {
|
|
3826
|
+
eval: arg => {
|
|
3827
|
+
return (Math.exp(arg) + Math.exp(-arg)) / (Math.exp(arg) - Math.exp(-arg));
|
|
3828
|
+
},
|
|
3829
|
+
codegen: argStr => {
|
|
3830
|
+
return "(" + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + ")";
|
|
3831
|
+
},
|
|
3832
|
+
tex: "\\coth",
|
|
3833
|
+
expand: () => Mul.handleDivide(Trig.cosh(this.arg), Trig.sinh(this.arg))
|
|
3834
|
+
}
|
|
3835
|
+
};
|
|
3836
|
+
this.type = type;
|
|
3837
|
+
this.arg = _arg;
|
|
3838
|
+
this.hints = _extends({}, this.hints, {
|
|
3839
|
+
open: false
|
|
3840
|
+
});
|
|
3841
|
+
}
|
|
3842
|
+
args() {
|
|
3843
|
+
return [this.type, this.arg];
|
|
3844
|
+
}
|
|
4568
3845
|
isEven() {
|
|
4569
|
-
return
|
|
3846
|
+
return _.contains(["cos", "sec"], this.type);
|
|
4570
3847
|
}
|
|
4571
3848
|
isInverse() {
|
|
4572
3849
|
return this.type.indexOf("arc") === 0;
|
|
4573
3850
|
}
|
|
4574
3851
|
isBasic() {
|
|
4575
|
-
return
|
|
3852
|
+
return _.contains(["sin", "cos"], this.type);
|
|
4576
3853
|
}
|
|
4577
|
-
eval() {
|
|
4578
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
4579
|
-
let options = arguments.length > 1 ? arguments[1] : undefined;
|
|
3854
|
+
eval(vars = {}, options) {
|
|
4580
3855
|
var func = this.functions[this.type].eval;
|
|
4581
3856
|
var arg = this.arg.eval(vars, options);
|
|
4582
3857
|
return func(arg);
|
|
@@ -4621,18 +3896,14 @@ class Trig extends Expr {
|
|
|
4621
3896
|
return this;
|
|
4622
3897
|
}
|
|
4623
3898
|
}
|
|
4624
|
-
|
|
4625
|
-
// TODO(alex): does every new node type need to redefine these?
|
|
4626
3899
|
needsExplicitMul() {
|
|
4627
3900
|
return false;
|
|
4628
3901
|
}
|
|
4629
3902
|
expand() {
|
|
4630
3903
|
var trig = this.recurse("expand");
|
|
4631
3904
|
if (!trig.isInverse()) {
|
|
4632
|
-
// e.g. tan(x) -> sin(x)/cos(x)
|
|
4633
|
-
// NOTE(kevinb): All non-inverse trig functions have an expand property.
|
|
4634
3905
|
var expand = trig.functions[trig.type].expand;
|
|
4635
|
-
return
|
|
3906
|
+
return _.bind(expand, trig)();
|
|
4636
3907
|
} else {
|
|
4637
3908
|
return trig;
|
|
4638
3909
|
}
|
|
@@ -4642,10 +3913,8 @@ class Trig extends Expr {
|
|
|
4642
3913
|
if (!trig.isInverse() && trig.arg.isNegative()) {
|
|
4643
3914
|
const arg = trig.arg instanceof Num ? trig.arg.abs() : Mul.handleDivide(trig.arg, NumNeg).collect(options);
|
|
4644
3915
|
if (trig.isEven()) {
|
|
4645
|
-
// e.g. cos(-x) -> cos(x)
|
|
4646
3916
|
return new Trig(trig.type, arg);
|
|
4647
3917
|
} else {
|
|
4648
|
-
// e.g. sin(-x) -> -sin(x)
|
|
4649
3918
|
return new Mul(NumNeg, new Trig(trig.type, arg));
|
|
4650
3919
|
}
|
|
4651
3920
|
} else {
|
|
@@ -4656,7 +3925,6 @@ class Trig extends Expr {
|
|
|
4656
3925
|
var type = pair[0];
|
|
4657
3926
|
var exp = pair[1];
|
|
4658
3927
|
if (exp && exp.equals(NumNeg)) {
|
|
4659
|
-
// e.g. sin^-1(x) -> arcsin(x)
|
|
4660
3928
|
type = "arc" + type;
|
|
4661
3929
|
exp = undefined;
|
|
4662
3930
|
}
|
|
@@ -4683,18 +3951,16 @@ class Trig extends Expr {
|
|
|
4683
3951
|
}
|
|
4684
3952
|
}
|
|
4685
3953
|
class Abs extends Expr {
|
|
4686
|
-
arg;
|
|
4687
3954
|
constructor(arg) {
|
|
4688
3955
|
super();
|
|
3956
|
+
this.arg = void 0;
|
|
3957
|
+
this.func = Abs;
|
|
4689
3958
|
this.arg = arg;
|
|
4690
3959
|
}
|
|
4691
|
-
func = Abs;
|
|
4692
3960
|
args() {
|
|
4693
3961
|
return [this.arg];
|
|
4694
3962
|
}
|
|
4695
|
-
eval() {
|
|
4696
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
4697
|
-
let options = arguments.length > 1 ? arguments[1] : undefined;
|
|
3963
|
+
eval(vars = {}, options) {
|
|
4698
3964
|
return Math.abs(this.arg.eval(vars, options));
|
|
4699
3965
|
}
|
|
4700
3966
|
codegen() {
|
|
@@ -4709,14 +3975,11 @@ class Abs extends Expr {
|
|
|
4709
3975
|
collect(options) {
|
|
4710
3976
|
var abs = this.recurse("collect", options);
|
|
4711
3977
|
if (abs.arg.isPositive()) {
|
|
4712
|
-
// e.g. |2^x| -> 2^x
|
|
4713
3978
|
return abs.arg;
|
|
4714
3979
|
} else if (abs.arg instanceof Num) {
|
|
4715
|
-
// e.g. |-2| -> 2
|
|
4716
3980
|
return abs.arg.abs();
|
|
4717
3981
|
} else if (abs.arg instanceof Mul) {
|
|
4718
|
-
|
|
4719
|
-
var terms = ___default["default"].groupBy(abs.arg.terms, term => {
|
|
3982
|
+
var terms = _.groupBy(abs.arg.terms, term => {
|
|
4720
3983
|
if (term.isPositive()) {
|
|
4721
3984
|
return "positive";
|
|
4722
3985
|
} else if (term instanceof Num) {
|
|
@@ -4725,7 +3988,7 @@ class Abs extends Expr {
|
|
|
4725
3988
|
return "other";
|
|
4726
3989
|
}
|
|
4727
3990
|
});
|
|
4728
|
-
var positives = terms.positive.concat(
|
|
3991
|
+
var positives = terms.positive.concat(_.invoke(terms.number, "abs"));
|
|
4729
3992
|
if (terms.other.length) {
|
|
4730
3993
|
positives.push(new Abs(new Mul(terms.other).flatten()));
|
|
4731
3994
|
}
|
|
@@ -4734,13 +3997,10 @@ class Abs extends Expr {
|
|
|
4734
3997
|
return abs;
|
|
4735
3998
|
}
|
|
4736
3999
|
}
|
|
4737
|
-
|
|
4738
|
-
// this should definitely be behind a super-simplify flag
|
|
4739
4000
|
expand() {
|
|
4740
4001
|
var abs = this.recurse("expand");
|
|
4741
4002
|
if (abs.arg instanceof Mul) {
|
|
4742
|
-
|
|
4743
|
-
var terms = ___default["default"].map(abs.arg.terms, term => {
|
|
4003
|
+
var terms = _.map(abs.arg.terms, term => {
|
|
4744
4004
|
return new Abs(term);
|
|
4745
4005
|
});
|
|
4746
4006
|
return new Mul(terms);
|
|
@@ -4752,19 +4012,25 @@ class Abs extends Expr {
|
|
|
4752
4012
|
return true;
|
|
4753
4013
|
}
|
|
4754
4014
|
}
|
|
4755
|
-
|
|
4756
|
-
/* equation */
|
|
4757
4015
|
class Eq extends Expr {
|
|
4758
|
-
left;
|
|
4759
|
-
type; // TODO(kevinb): use an enum for this
|
|
4760
|
-
right;
|
|
4761
4016
|
constructor(left, type, right) {
|
|
4762
4017
|
super();
|
|
4018
|
+
this.left = void 0;
|
|
4019
|
+
this.type = void 0;
|
|
4020
|
+
this.right = void 0;
|
|
4021
|
+
this.func = Eq;
|
|
4022
|
+
this.signs = {
|
|
4023
|
+
"=": " = ",
|
|
4024
|
+
"<": " < ",
|
|
4025
|
+
">": " > ",
|
|
4026
|
+
"<>": " \\ne ",
|
|
4027
|
+
"<=": " \\le ",
|
|
4028
|
+
">=": " \\ge "
|
|
4029
|
+
};
|
|
4763
4030
|
this.left = left;
|
|
4764
4031
|
this.type = type;
|
|
4765
4032
|
this.right = right;
|
|
4766
4033
|
}
|
|
4767
|
-
func = Eq;
|
|
4768
4034
|
args() {
|
|
4769
4035
|
return [this.left, this.type, this.right];
|
|
4770
4036
|
}
|
|
@@ -4774,41 +4040,24 @@ class Eq extends Expr {
|
|
|
4774
4040
|
print() {
|
|
4775
4041
|
return this.left.print() + this.type + this.right.print();
|
|
4776
4042
|
}
|
|
4777
|
-
signs = {
|
|
4778
|
-
"=": " = ",
|
|
4779
|
-
"<": " < ",
|
|
4780
|
-
">": " > ",
|
|
4781
|
-
"<>": " \\ne ",
|
|
4782
|
-
"<=": " \\le ",
|
|
4783
|
-
">=": " \\ge "
|
|
4784
|
-
};
|
|
4785
4043
|
tex() {
|
|
4786
4044
|
return this.left.tex() + this.signs[this.type] + this.right.tex();
|
|
4787
4045
|
}
|
|
4788
4046
|
normalize() {
|
|
4789
4047
|
var eq = this.recurse("normalize");
|
|
4790
|
-
if (
|
|
4791
|
-
// inequalities should have the smaller side on the left
|
|
4048
|
+
if (_.contains([">", ">="], eq.type)) {
|
|
4792
4049
|
return new Eq(eq.right, eq.type.replace(">", "<"), eq.left);
|
|
4793
4050
|
} else {
|
|
4794
4051
|
return eq;
|
|
4795
4052
|
}
|
|
4796
4053
|
}
|
|
4797
|
-
|
|
4798
|
-
// convert this equation to an expression set to zero
|
|
4799
|
-
// the expression is normalized to a canonical form
|
|
4800
|
-
// e.g. y/2=x/4 -> y/2-x/4(=0) -> 2y-x(=0)
|
|
4801
|
-
// unless unfactored is specified, will then divide through
|
|
4802
|
-
asExpr() {
|
|
4803
|
-
let unfactored = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
4054
|
+
asExpr(unfactored = false) {
|
|
4804
4055
|
var isZero = expr => {
|
|
4805
4056
|
return expr instanceof Num && expr.isSimple() && expr.eval() === 0;
|
|
4806
4057
|
};
|
|
4807
|
-
|
|
4808
|
-
// first convert to a sequence of additive terms
|
|
4809
4058
|
let terms = [];
|
|
4810
4059
|
if (this.left instanceof Add) {
|
|
4811
|
-
terms =
|
|
4060
|
+
terms = _.clone(this.left.terms);
|
|
4812
4061
|
} else if (!isZero(this.left)) {
|
|
4813
4062
|
terms = [this.left];
|
|
4814
4063
|
}
|
|
@@ -4818,26 +4067,16 @@ class Eq extends Expr {
|
|
|
4818
4067
|
terms.push(this.right.negate());
|
|
4819
4068
|
}
|
|
4820
4069
|
var isInequality = !this.isEquality();
|
|
4821
|
-
|
|
4822
|
-
// Collect over each term individually to transform simple expressions
|
|
4823
|
-
// into numbers that might have denominators, taking into account
|
|
4824
|
-
// float precision. We have to be very careful to not introduce any
|
|
4825
|
-
// irrational floats before asExpr() returns, because by definition
|
|
4826
|
-
// they do not have exact denominators...
|
|
4827
|
-
terms = ___default["default"].invoke(terms, "collect", {
|
|
4070
|
+
terms = _.invoke(terms, "collect", {
|
|
4828
4071
|
preciseFloats: true
|
|
4829
4072
|
});
|
|
4830
|
-
|
|
4831
|
-
// ...and we multiply through by every denominator.
|
|
4832
4073
|
for (var i = 0; i < terms.length; i++) {
|
|
4833
4074
|
var denominator = terms[i].getDenominator();
|
|
4834
|
-
|
|
4835
|
-
// Can't multiply inequalities by non 100% positive factors
|
|
4836
4075
|
if (isInequality && !denominator.isPositive()) {
|
|
4837
4076
|
denominator = denominator.asPositiveFactor();
|
|
4838
4077
|
}
|
|
4839
4078
|
if (!denominator.equals(NumOne)) {
|
|
4840
|
-
terms =
|
|
4079
|
+
terms = _.map(terms, term => {
|
|
4841
4080
|
return Mul.createOrAppend(term, denominator).simplify({
|
|
4842
4081
|
once: true,
|
|
4843
4082
|
preciseFloats: true
|
|
@@ -4848,11 +4087,6 @@ class Eq extends Expr {
|
|
|
4848
4087
|
var add = new Add(terms).flatten();
|
|
4849
4088
|
return unfactored ? add : this.divideThrough(add);
|
|
4850
4089
|
}
|
|
4851
|
-
|
|
4852
|
-
// divide through by every common factor in the expression
|
|
4853
|
-
// e.g. 2y-4x(=0) -> y-2x(=0)
|
|
4854
|
-
// TODO(alex): Make it an option to only divide by variables/expressions
|
|
4855
|
-
// guaranteed to be nonzero
|
|
4856
4090
|
divideThrough(expr) {
|
|
4857
4091
|
const isInequality = !this.isEquality();
|
|
4858
4092
|
const simplified = expr.simplify({
|
|
@@ -4869,31 +4103,20 @@ class Eq extends Expr {
|
|
|
4869
4103
|
const isOne = term => term.equals(NumOne);
|
|
4870
4104
|
const [adds, others] = partition(terms, isAdd);
|
|
4871
4105
|
if (adds.length && this.isEquality()) {
|
|
4872
|
-
// keep only Adds
|
|
4873
|
-
// e.g. 2xy(z+1)(=0) -> z+1(=0)
|
|
4874
4106
|
return new Mul(adds).flatten();
|
|
4875
4107
|
}
|
|
4876
4108
|
let denominator = others;
|
|
4877
4109
|
if (!adds.length) {
|
|
4878
|
-
|
|
4879
|
-
// e.g. 42xyz(=0) -> xyz(=0)
|
|
4880
|
-
denominator = ___default["default"].reject(denominator, hasVar);
|
|
4110
|
+
denominator = _.reject(denominator, hasVar);
|
|
4881
4111
|
}
|
|
4882
4112
|
if (isInequality) {
|
|
4883
|
-
|
|
4884
|
-
// e.g. 42x^2y(z+1)(=0) -> y(z+1)(=0)
|
|
4885
|
-
denominator = ___default["default"].invoke(denominator, "asPositiveFactor");
|
|
4113
|
+
denominator = _.invoke(denominator, "asPositiveFactor");
|
|
4886
4114
|
}
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
denominator = ___default["default"].reject(denominator, isOne);
|
|
4890
|
-
denominator = ___default["default"].map(denominator, term => {
|
|
4115
|
+
denominator = _.reject(denominator, isOne);
|
|
4116
|
+
denominator = _.map(denominator, term => {
|
|
4891
4117
|
return new Pow(term, NumDiv);
|
|
4892
4118
|
});
|
|
4893
4119
|
const dividedResult = new Mul(terms.concat(denominator)).collect();
|
|
4894
|
-
|
|
4895
|
-
// If the end result is the same as the original factoring,
|
|
4896
|
-
// rollback the factoring and discard all intermediate steps.
|
|
4897
4120
|
if (dividedResult.equals(factored)) {
|
|
4898
4121
|
return simplified;
|
|
4899
4122
|
} else {
|
|
@@ -4901,10 +4124,9 @@ class Eq extends Expr {
|
|
|
4901
4124
|
}
|
|
4902
4125
|
}
|
|
4903
4126
|
isEquality() {
|
|
4904
|
-
return
|
|
4127
|
+
return _.contains(["=", "<>"], this.type);
|
|
4905
4128
|
}
|
|
4906
4129
|
compare(other) {
|
|
4907
|
-
// expression comparisons are handled by Expr.compare()
|
|
4908
4130
|
if (!(other instanceof Eq)) {
|
|
4909
4131
|
return false;
|
|
4910
4132
|
}
|
|
@@ -4913,48 +4135,36 @@ class Eq extends Expr {
|
|
|
4913
4135
|
if (eq1.type !== eq2.type) {
|
|
4914
4136
|
return false;
|
|
4915
4137
|
}
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
// e.g x+2x=6 -> 3x=6 -> 3x-6(=0) -> x-2(=0)
|
|
4919
|
-
var expr1 = eq1.divideThrough(eq1.asExpr(/* unfactored */true).collect());
|
|
4920
|
-
var expr2 = eq2.divideThrough(eq2.asExpr(/* unfactored */true).collect());
|
|
4138
|
+
var expr1 = eq1.divideThrough(eq1.asExpr(true).collect());
|
|
4139
|
+
var expr2 = eq2.divideThrough(eq2.asExpr(true).collect());
|
|
4921
4140
|
if (eq1.isEquality()) {
|
|
4922
|
-
// equals and not-equals can be subtracted either way
|
|
4923
4141
|
return expr1.compare(expr2) || expr1.compare(Mul.handleNegative(expr2));
|
|
4924
4142
|
} else {
|
|
4925
4143
|
return expr1.compare(expr2);
|
|
4926
4144
|
}
|
|
4927
4145
|
}
|
|
4928
|
-
|
|
4929
|
-
// should only be done after compare() returns true to avoid false positives
|
|
4930
4146
|
sameForm(other) {
|
|
4931
4147
|
var eq1 = this.normalize();
|
|
4932
4148
|
var eq2 = other.normalize();
|
|
4933
4149
|
var same = eq1.left.sameForm(eq2.left) && eq1.right.sameForm(eq2.right);
|
|
4934
4150
|
if (eq1.isEquality()) {
|
|
4935
|
-
// equals and not-equals can be commutative with respect to the sign
|
|
4936
4151
|
return same || eq1.left.sameForm(eq2.right) && eq1.right.sameForm(eq2.left);
|
|
4937
4152
|
} else {
|
|
4938
4153
|
return same;
|
|
4939
4154
|
}
|
|
4940
4155
|
}
|
|
4941
|
-
|
|
4942
|
-
// we don't want to override collect because it would turn y=x into y-x(=0)
|
|
4943
|
-
// instead, we ask if the equation was in that form, would it be simplified?
|
|
4944
4156
|
isSimplified() {
|
|
4945
|
-
var expr = this.asExpr(
|
|
4157
|
+
var expr = this.asExpr(true);
|
|
4946
4158
|
var simplified = this.divideThrough(expr).simplify();
|
|
4947
4159
|
return expr.equals(simplified) && this.left.isSimplified() && this.right.isSimplified();
|
|
4948
4160
|
}
|
|
4949
|
-
|
|
4950
|
-
// Assumptions: Expression is of the form a+bx, and we solve for x
|
|
4951
4161
|
solveLinearEquationForVariable(variable) {
|
|
4952
4162
|
var expr = this.asExpr();
|
|
4953
4163
|
if (!(expr instanceof Add) || expr.terms.length !== 2) {
|
|
4954
4164
|
throw new Error("Can only handle linear equations of the form " + "a + bx (= 0)");
|
|
4955
4165
|
}
|
|
4956
4166
|
var hasVar = term => {
|
|
4957
|
-
return term.has(Var) &&
|
|
4167
|
+
return term.has(Var) && _.contains(term.getVars(), variable.symbol);
|
|
4958
4168
|
};
|
|
4959
4169
|
const termHasVar = hasVar(expr.terms[0]);
|
|
4960
4170
|
const a = termHasVar ? Mul.handleNegative(expr.terms[1]) : Mul.handleNegative(expr.terms[0]);
|
|
@@ -4962,8 +4172,6 @@ class Eq extends Expr {
|
|
|
4962
4172
|
return Mul.handleDivide(a, b).simplify();
|
|
4963
4173
|
}
|
|
4964
4174
|
}
|
|
4965
|
-
|
|
4966
|
-
/* abstract symbol node */
|
|
4967
4175
|
class Sym extends Expr {
|
|
4968
4176
|
needsExplicitMul() {
|
|
4969
4177
|
return false;
|
|
@@ -4976,17 +4184,15 @@ class Sym extends Expr {
|
|
|
4976
4184
|
}
|
|
4977
4185
|
}
|
|
4978
4186
|
}
|
|
4979
|
-
|
|
4980
|
-
/* function variable */
|
|
4981
4187
|
class Func extends Sym {
|
|
4982
|
-
symbol;
|
|
4983
|
-
arg;
|
|
4984
4188
|
constructor(symbol, arg) {
|
|
4985
4189
|
super();
|
|
4190
|
+
this.symbol = void 0;
|
|
4191
|
+
this.arg = void 0;
|
|
4192
|
+
this.func = Func;
|
|
4986
4193
|
this.symbol = symbol;
|
|
4987
4194
|
this.arg = arg;
|
|
4988
4195
|
}
|
|
4989
|
-
func = Func;
|
|
4990
4196
|
args() {
|
|
4991
4197
|
return [this.symbol, this.arg];
|
|
4992
4198
|
}
|
|
@@ -4996,19 +4202,16 @@ class Func extends Sym {
|
|
|
4996
4202
|
tex() {
|
|
4997
4203
|
return this.symbol + "(" + this.arg.tex() + ")";
|
|
4998
4204
|
}
|
|
4999
|
-
eval() {
|
|
5000
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
5001
|
-
let options = arguments.length > 1 ? arguments[1] : undefined;
|
|
4205
|
+
eval(vars = {}, options) {
|
|
5002
4206
|
var arg = this.arg;
|
|
5003
4207
|
var func = vars[this.symbol];
|
|
5004
|
-
var newVars =
|
|
4208
|
+
var newVars = _.extend(_.clone(vars), {
|
|
5005
4209
|
x: arg.eval(vars, options)
|
|
5006
4210
|
});
|
|
5007
4211
|
var parsedFunc = parse(func, options);
|
|
5008
4212
|
if (parsedFunc.parsed) {
|
|
5009
4213
|
return parsedFunc.expr.eval(newVars, options);
|
|
5010
4214
|
}
|
|
5011
|
-
// If parsedFunc isn't actually parsed, return its error
|
|
5012
4215
|
return parsedFunc;
|
|
5013
4216
|
}
|
|
5014
4217
|
codegen() {
|
|
@@ -5021,24 +4224,22 @@ class Func extends Sym {
|
|
|
5021
4224
|
if (excludeFunc) {
|
|
5022
4225
|
return this.arg.getVars();
|
|
5023
4226
|
} else {
|
|
5024
|
-
return
|
|
4227
|
+
return _.union(this.arg.getVars(), [this.symbol]).sort();
|
|
5025
4228
|
}
|
|
5026
4229
|
}
|
|
5027
4230
|
getConsts() {
|
|
5028
4231
|
return this.arg.getConsts();
|
|
5029
4232
|
}
|
|
5030
4233
|
}
|
|
5031
|
-
|
|
5032
|
-
/* variable */
|
|
5033
4234
|
class Var extends Sym {
|
|
5034
|
-
symbol;
|
|
5035
|
-
subscript;
|
|
5036
4235
|
constructor(symbol, subscript) {
|
|
5037
4236
|
super();
|
|
4237
|
+
this.symbol = void 0;
|
|
4238
|
+
this.subscript = void 0;
|
|
4239
|
+
this.func = Var;
|
|
5038
4240
|
this.symbol = symbol;
|
|
5039
4241
|
this.subscript = subscript;
|
|
5040
4242
|
}
|
|
5041
|
-
func = Var;
|
|
5042
4243
|
args() {
|
|
5043
4244
|
return [this.symbol, this.subscript];
|
|
5044
4245
|
}
|
|
@@ -5055,9 +4256,6 @@ class Var extends Sym {
|
|
|
5055
4256
|
}
|
|
5056
4257
|
return this.symbol + sub;
|
|
5057
4258
|
}
|
|
5058
|
-
|
|
5059
|
-
// Provide a way to easily evalate expressions with the common case,
|
|
5060
|
-
// subscripts that consist of a single number or symbol e.g. x_a or x_42
|
|
5061
4259
|
prettyPrint() {
|
|
5062
4260
|
var sub = this.subscript;
|
|
5063
4261
|
if (sub && (sub instanceof Num || sub instanceof Sym)) {
|
|
@@ -5077,12 +4275,7 @@ class Var extends Sym {
|
|
|
5077
4275
|
repr() {
|
|
5078
4276
|
return "Var(" + this.print() + ")";
|
|
5079
4277
|
}
|
|
5080
|
-
eval() {
|
|
5081
|
-
let vars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
5082
|
-
// @ts-expect-error: values is Vars are strings, but here
|
|
5083
|
-
// we expect them to be numbers. We should probably store
|
|
5084
|
-
// Var and Func entries separately in the Vars type so that
|
|
5085
|
-
// they can be typed correctly.
|
|
4278
|
+
eval(vars = {}, options) {
|
|
5086
4279
|
return vars[this.prettyPrint()];
|
|
5087
4280
|
}
|
|
5088
4281
|
codegen() {
|
|
@@ -5095,28 +4288,25 @@ class Var extends Sym {
|
|
|
5095
4288
|
return false;
|
|
5096
4289
|
}
|
|
5097
4290
|
}
|
|
5098
|
-
|
|
5099
|
-
/* constant */
|
|
5100
4291
|
class Const extends Sym {
|
|
5101
|
-
symbol;
|
|
5102
4292
|
constructor(symbol) {
|
|
5103
4293
|
super();
|
|
4294
|
+
this.symbol = void 0;
|
|
4295
|
+
this.func = Const;
|
|
5104
4296
|
this.symbol = symbol;
|
|
5105
4297
|
}
|
|
5106
|
-
func = Const;
|
|
5107
4298
|
args() {
|
|
5108
4299
|
return [this.symbol];
|
|
5109
4300
|
}
|
|
5110
4301
|
recurse() {
|
|
5111
4302
|
return this;
|
|
5112
4303
|
}
|
|
5113
|
-
eval() {
|
|
4304
|
+
eval(vars = {}, options) {
|
|
5114
4305
|
if (this.symbol === "pi") {
|
|
5115
4306
|
return Math.PI;
|
|
5116
4307
|
} else if (this.symbol === "e") {
|
|
5117
4308
|
return Math.E;
|
|
5118
4309
|
} else {
|
|
5119
|
-
// @ts-expect-error: should we throw an error here?
|
|
5120
4310
|
return undefined;
|
|
5121
4311
|
}
|
|
5122
4312
|
}
|
|
@@ -5126,7 +4316,6 @@ class Const extends Sym {
|
|
|
5126
4316
|
} else if (this.symbol === "e") {
|
|
5127
4317
|
return "Math.E";
|
|
5128
4318
|
} else {
|
|
5129
|
-
// @ts-expect-error: should we throw an error here?
|
|
5130
4319
|
return undefined;
|
|
5131
4320
|
}
|
|
5132
4321
|
}
|
|
@@ -5139,7 +4328,6 @@ class Const extends Sym {
|
|
|
5139
4328
|
} else if (this.symbol === "e") {
|
|
5140
4329
|
return "e";
|
|
5141
4330
|
} else {
|
|
5142
|
-
// @ts-expect-error: should we return this.symbol here?
|
|
5143
4331
|
return undefined;
|
|
5144
4332
|
}
|
|
5145
4333
|
}
|
|
@@ -5156,25 +4344,22 @@ class Const extends Sym {
|
|
|
5156
4344
|
getConsts() {
|
|
5157
4345
|
return [this.print()];
|
|
5158
4346
|
}
|
|
5159
|
-
static e = new Const("e");
|
|
5160
|
-
static pi = new Const("pi");
|
|
5161
4347
|
}
|
|
5162
|
-
|
|
5163
|
-
|
|
4348
|
+
_class12 = Const;
|
|
4349
|
+
Const.e = new _class12("e");
|
|
4350
|
+
Const.pi = new _class12("pi");
|
|
5164
4351
|
class Num extends Expr {
|
|
5165
|
-
n = 0;
|
|
5166
4352
|
constructor() {
|
|
5167
4353
|
super();
|
|
5168
|
-
|
|
5169
|
-
this.hints = {
|
|
5170
|
-
...this.hints,
|
|
4354
|
+
this.n = 0;
|
|
4355
|
+
this.hints = _extends({}, this.hints, {
|
|
5171
4356
|
negate: false,
|
|
5172
4357
|
subtract: false,
|
|
5173
4358
|
divide: false,
|
|
5174
4359
|
root: false,
|
|
5175
4360
|
fraction: false,
|
|
5176
4361
|
entered: false
|
|
5177
|
-
};
|
|
4362
|
+
});
|
|
5178
4363
|
}
|
|
5179
4364
|
repr() {
|
|
5180
4365
|
return this.print();
|
|
@@ -5188,17 +4373,9 @@ class Num extends Expr {
|
|
|
5188
4373
|
codegen() {
|
|
5189
4374
|
return this.print();
|
|
5190
4375
|
}
|
|
5191
|
-
|
|
5192
|
-
// takes another Num and returns a new Num
|
|
5193
|
-
|
|
5194
|
-
// returns this Num's additive inverse
|
|
5195
|
-
|
|
5196
4376
|
isSubtract() {
|
|
5197
4377
|
return Boolean(this.hints.subtract);
|
|
5198
4378
|
}
|
|
5199
|
-
|
|
5200
|
-
// return the absolute value of the number
|
|
5201
|
-
|
|
5202
4379
|
needsExplicitMul() {
|
|
5203
4380
|
return true;
|
|
5204
4381
|
}
|
|
@@ -5211,19 +4388,10 @@ class Num extends Expr {
|
|
|
5211
4388
|
asPositiveFactor() {
|
|
5212
4389
|
return this.isPositive() ? this : this.abs();
|
|
5213
4390
|
}
|
|
5214
|
-
|
|
5215
|
-
// whether a number is considered simple (one term)
|
|
5216
|
-
// e.g. for reals, ints and floats are simple
|
|
5217
|
-
|
|
5218
|
-
// Based on http://stackoverflow.com/a/10454560/2571482
|
|
5219
4391
|
getDecimalPlaces() {
|
|
5220
4392
|
var match = ("" + this.n).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
|
5221
4393
|
if (match) {
|
|
5222
|
-
return Math.max(0,
|
|
5223
|
-
// Number of digits right of decimal point
|
|
5224
|
-
(match[1] ? match[1].length : 0) - (
|
|
5225
|
-
// Adjust for scientific notation
|
|
5226
|
-
match[2] ? +match[2] : 0));
|
|
4394
|
+
return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
|
|
5227
4395
|
} else {
|
|
5228
4396
|
return 0;
|
|
5229
4397
|
}
|
|
@@ -5237,16 +4405,10 @@ class Num extends Expr {
|
|
|
5237
4405
|
return NumNeg;
|
|
5238
4406
|
}
|
|
5239
4407
|
}
|
|
5240
|
-
|
|
5241
|
-
// find the greatest common denominator
|
|
5242
4408
|
static findGCD(a, b) {
|
|
5243
4409
|
var mod;
|
|
5244
4410
|
a = Math.abs(a);
|
|
5245
4411
|
b = Math.abs(b);
|
|
5246
|
-
|
|
5247
|
-
// Euclid's method doesn't handle non-integers very well. For now
|
|
5248
|
-
// we just say we can't pull out a common factor. It might be
|
|
5249
|
-
// reasonable to do better than this in the future.
|
|
5250
4412
|
if (a !== Math.floor(a) || b !== Math.floor(b)) {
|
|
5251
4413
|
return 1;
|
|
5252
4414
|
}
|
|
@@ -5257,26 +4419,19 @@ class Num extends Expr {
|
|
|
5257
4419
|
}
|
|
5258
4420
|
return a;
|
|
5259
4421
|
}
|
|
5260
|
-
static min() {
|
|
5261
|
-
|
|
5262
|
-
args[_key3] = arguments[_key3];
|
|
5263
|
-
}
|
|
5264
|
-
return ___default["default"].min(args, num => num.eval());
|
|
4422
|
+
static min(...args) {
|
|
4423
|
+
return _.min(args, num => num.eval());
|
|
5265
4424
|
}
|
|
5266
|
-
static max() {
|
|
5267
|
-
|
|
5268
|
-
args[_key4] = arguments[_key4];
|
|
5269
|
-
}
|
|
5270
|
-
return ___default["default"].max(args, num => num.eval());
|
|
4425
|
+
static max(...args) {
|
|
4426
|
+
return _.max(args, num => num.eval());
|
|
5271
4427
|
}
|
|
5272
4428
|
}
|
|
5273
|
-
|
|
5274
|
-
/* rational number (n: numerator, d: denominator) */
|
|
5275
4429
|
class Rational extends Num {
|
|
5276
|
-
n;
|
|
5277
|
-
d;
|
|
5278
4430
|
constructor(numerator, denominator) {
|
|
5279
4431
|
super();
|
|
4432
|
+
this.n = void 0;
|
|
4433
|
+
this.d = void 0;
|
|
4434
|
+
this.func = Rational;
|
|
5280
4435
|
var n = numerator;
|
|
5281
4436
|
var d = denominator;
|
|
5282
4437
|
if (d < 0) {
|
|
@@ -5286,7 +4441,6 @@ class Rational extends Num {
|
|
|
5286
4441
|
this.n = n;
|
|
5287
4442
|
this.d = d;
|
|
5288
4443
|
}
|
|
5289
|
-
func = Rational;
|
|
5290
4444
|
args() {
|
|
5291
4445
|
return [this.n, this.d];
|
|
5292
4446
|
}
|
|
@@ -5331,14 +4485,9 @@ class Rational extends Num {
|
|
|
5331
4485
|
return new Rational(Math.abs(this.n), this.d);
|
|
5332
4486
|
}
|
|
5333
4487
|
findGCD(factor) {
|
|
5334
|
-
// Attempt to factor out common numerators and denominators to return
|
|
5335
|
-
// a Rational instead of a Float
|
|
5336
4488
|
if (factor instanceof Rational) {
|
|
5337
|
-
// For more background, see
|
|
5338
|
-
// http://math.stackexchange.com/questions/151081/gcd-of-rationals
|
|
5339
4489
|
var numerator = Num.findGCD(this.n * factor.d, factor.n * this.d);
|
|
5340
4490
|
var denominator = this.d * factor.d;
|
|
5341
|
-
// Create the rational, then call .collect() to simplify it
|
|
5342
4491
|
return new Rational(numerator, denominator).collect();
|
|
5343
4492
|
} else if (factor instanceof Int) {
|
|
5344
4493
|
return new Rational(Num.findGCD(this.n, factor.n), this.d);
|
|
@@ -5346,8 +4495,6 @@ class Rational extends Num {
|
|
|
5346
4495
|
return factor.findGCD(this);
|
|
5347
4496
|
}
|
|
5348
4497
|
}
|
|
5349
|
-
|
|
5350
|
-
// for now, assuming that exp is a Num
|
|
5351
4498
|
raiseToThe(exp) {
|
|
5352
4499
|
if (exp instanceof Int) {
|
|
5353
4500
|
var positive = exp.eval() > 0;
|
|
@@ -5373,13 +4520,11 @@ class Rational extends Num {
|
|
|
5373
4520
|
return this;
|
|
5374
4521
|
}
|
|
5375
4522
|
}
|
|
5376
|
-
|
|
5377
|
-
/* integer (n: numerator/number) */
|
|
5378
4523
|
class Int extends Rational {
|
|
5379
4524
|
constructor(number) {
|
|
5380
4525
|
super(number, 1);
|
|
4526
|
+
this.func = Int;
|
|
5381
4527
|
}
|
|
5382
|
-
func = Int;
|
|
5383
4528
|
args() {
|
|
5384
4529
|
return [this.n];
|
|
5385
4530
|
}
|
|
@@ -5409,24 +4554,19 @@ class Int extends Rational {
|
|
|
5409
4554
|
return new Int(n).addHint("entered");
|
|
5410
4555
|
}
|
|
5411
4556
|
}
|
|
5412
|
-
|
|
5413
|
-
/* float (n: number) */
|
|
5414
4557
|
class Float extends Num {
|
|
5415
|
-
n;
|
|
5416
4558
|
constructor(number) {
|
|
5417
4559
|
super();
|
|
4560
|
+
this.n = void 0;
|
|
4561
|
+
this.func = Float;
|
|
5418
4562
|
this.n = number;
|
|
5419
4563
|
}
|
|
5420
|
-
func = Float;
|
|
5421
4564
|
args() {
|
|
5422
4565
|
return [this.n];
|
|
5423
4566
|
}
|
|
5424
4567
|
eval() {
|
|
5425
4568
|
return this.n;
|
|
5426
4569
|
}
|
|
5427
|
-
|
|
5428
|
-
// TODO(alex): when we internationalize number parsing/display
|
|
5429
|
-
// we should make sure to use the appropriate decimal mark here
|
|
5430
4570
|
print() {
|
|
5431
4571
|
return this.n.toString();
|
|
5432
4572
|
}
|
|
@@ -5448,8 +4588,6 @@ class Float extends Num {
|
|
|
5448
4588
|
}
|
|
5449
4589
|
}
|
|
5450
4590
|
collect(options) {
|
|
5451
|
-
// We used to simplify Floats to Ints here whenever possible, but no
|
|
5452
|
-
// longer do so in order to preserve significant figures.
|
|
5453
4591
|
return this;
|
|
5454
4592
|
}
|
|
5455
4593
|
negate() {
|
|
@@ -5465,8 +4603,6 @@ class Float extends Num {
|
|
|
5465
4603
|
return factor.findGCD(this);
|
|
5466
4604
|
}
|
|
5467
4605
|
}
|
|
5468
|
-
|
|
5469
|
-
// for now, assuming that exp is a Num
|
|
5470
4606
|
raiseToThe(exp, options) {
|
|
5471
4607
|
if (options && options.preciseFloats && exp instanceof Int && exp.n > 1) {
|
|
5472
4608
|
return Float.toDecimalPlaces(new Pow(this, exp).eval(), this.getDecimalPlaces() * exp.n);
|
|
@@ -5474,8 +4610,6 @@ class Float extends Num {
|
|
|
5474
4610
|
return new Float(new Pow(this, exp).eval()).collect();
|
|
5475
4611
|
}
|
|
5476
4612
|
}
|
|
5477
|
-
|
|
5478
|
-
// only to be used on non-repeating decimals (e.g. user-provided)
|
|
5479
4613
|
asRational() {
|
|
5480
4614
|
var parts = this.n.toString().split(".");
|
|
5481
4615
|
if (parts.length === 1) {
|
|
@@ -5495,9 +4629,6 @@ class Float extends Num {
|
|
|
5495
4629
|
static create(n) {
|
|
5496
4630
|
return new Float(n).addHint("entered");
|
|
5497
4631
|
}
|
|
5498
|
-
|
|
5499
|
-
// Account for floating point imprecision by explicitly controlling the
|
|
5500
|
-
// number of decimal places in common operations (e.g. +, *, ^)
|
|
5501
4632
|
static toDecimalPlaces(n, places) {
|
|
5502
4633
|
return new Float(+n.toFixed(Math.min(places, 20))).collect();
|
|
5503
4634
|
}
|
|
@@ -5509,13 +4640,9 @@ const NumSqrt = new Rational(1, 2).addHint("root");
|
|
|
5509
4640
|
const NumZero = new Int(0);
|
|
5510
4641
|
const NumOne = new Int(1);
|
|
5511
4642
|
const NumTen = new Int(10);
|
|
5512
|
-
var parseError = function (str, hash) {
|
|
5513
|
-
// return int location of parsing error
|
|
4643
|
+
var parseError = function parseError(str, hash) {
|
|
5514
4644
|
throw new Error(hash.loc.first_column);
|
|
5515
4645
|
};
|
|
5516
|
-
|
|
5517
|
-
// expose concrete nodes to parser scope
|
|
5518
|
-
// see http://zaach.github.io/jison/docs/#sharing-scope
|
|
5519
4646
|
parser.yy = {
|
|
5520
4647
|
Add: Add,
|
|
5521
4648
|
Mul: Mul,
|
|
@@ -5532,28 +4659,22 @@ parser.yy = {
|
|
|
5532
4659
|
parseError: parseError,
|
|
5533
4660
|
constants: ["e"],
|
|
5534
4661
|
symbolLexer: function (symbol) {
|
|
5535
|
-
if (
|
|
4662
|
+
if (_.contains(parser.yy.constants, symbol)) {
|
|
5536
4663
|
return "CONST";
|
|
5537
|
-
} else if (
|
|
4664
|
+
} else if (_.contains(parser.yy.functions, symbol)) {
|
|
5538
4665
|
return "FUNC";
|
|
5539
4666
|
} else {
|
|
5540
4667
|
return "VAR";
|
|
5541
4668
|
}
|
|
5542
4669
|
}
|
|
5543
4670
|
};
|
|
5544
|
-
const parse = function (input, options) {
|
|
4671
|
+
const parse = function parse(input, options) {
|
|
5545
4672
|
try {
|
|
5546
4673
|
if (options && options.functions) {
|
|
5547
|
-
|
|
5548
|
-
parser.yy.functions = ___default["default"].without(options.functions, "i");
|
|
4674
|
+
parser.yy.functions = _.without(options.functions, "i");
|
|
5549
4675
|
} else {
|
|
5550
4676
|
parser.yy.functions = [];
|
|
5551
4677
|
}
|
|
5552
|
-
|
|
5553
|
-
// If ',' is the decimal dividor in your country, replace any ','s
|
|
5554
|
-
// with '.'s.
|
|
5555
|
-
// This isn't perfect, since the output will all still have '.'s.
|
|
5556
|
-
// TODO(jack): Fix the output to have ','s in this case
|
|
5557
4678
|
if (options && options.decimal_separator) {
|
|
5558
4679
|
input = input.split(options.decimal_separator).join(".");
|
|
5559
4680
|
}
|
|
@@ -5569,29 +4690,20 @@ const parse = function (input, options) {
|
|
|
5569
4690
|
};
|
|
5570
4691
|
}
|
|
5571
4692
|
};
|
|
5572
|
-
|
|
5573
|
-
/* unit */
|
|
5574
4693
|
class Unit extends Sym {
|
|
5575
|
-
symbol;
|
|
5576
4694
|
constructor(symbol) {
|
|
5577
4695
|
super();
|
|
4696
|
+
this.symbol = void 0;
|
|
4697
|
+
this.func = Unit;
|
|
5578
4698
|
this.symbol = symbol;
|
|
5579
4699
|
}
|
|
5580
|
-
func = Unit;
|
|
5581
4700
|
args() {
|
|
5582
4701
|
return [this.symbol];
|
|
5583
4702
|
}
|
|
5584
4703
|
recurse() {
|
|
5585
4704
|
return this;
|
|
5586
4705
|
}
|
|
5587
|
-
eval() {
|
|
5588
|
-
// This is called when comparing units. A unit doesn't affect the
|
|
5589
|
-
// numerical value of its coefficient, so this needs to be 1.
|
|
5590
|
-
//
|
|
5591
|
-
// On the other hand, things must not evaluate to the same thing if
|
|
5592
|
-
// they don't have the same type. I believe that's also true - form is
|
|
5593
|
-
// checked before numerical equivalence. I do not know where, though.
|
|
5594
|
-
// However, there are a couple tests checking this.
|
|
4706
|
+
eval(vars = {}, options) {
|
|
5595
4707
|
return 1;
|
|
5596
4708
|
}
|
|
5597
4709
|
getUnits() {
|
|
@@ -5609,42 +4721,26 @@ class Unit extends Sym {
|
|
|
5609
4721
|
tex() {
|
|
5610
4722
|
return this.symbol;
|
|
5611
4723
|
}
|
|
5612
|
-
|
|
5613
|
-
// Simplify units by replacing prefixes with multiplication
|
|
5614
4724
|
collect(options) {
|
|
5615
|
-
if (
|
|
4725
|
+
if (_(baseUnits).has(this.symbol)) {
|
|
5616
4726
|
return this;
|
|
5617
|
-
} else if (
|
|
4727
|
+
} else if (_(derivedUnits).has(this.symbol)) {
|
|
5618
4728
|
return derivedUnits[this.symbol].conversion;
|
|
5619
4729
|
} else {
|
|
5620
4730
|
throw new Error("could not understand unit: " + this.symbol);
|
|
5621
4731
|
}
|
|
5622
4732
|
}
|
|
5623
4733
|
}
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
//
|
|
5627
|
-
// "g" -> Unit("g")
|
|
5628
|
-
// "kg" -> 1000 * Unit("g")
|
|
5629
|
-
var unprefixify = function (symbol) {
|
|
5630
|
-
if (___default["default"](baseUnits).has(symbol) || ___default["default"](derivedUnits).has(symbol)) {
|
|
4734
|
+
var unprefixify = function unprefixify(symbol) {
|
|
4735
|
+
if (_(baseUnits).has(symbol) || _(derivedUnits).has(symbol)) {
|
|
5631
4736
|
return new Unit(symbol);
|
|
5632
4737
|
}
|
|
5633
|
-
|
|
5634
|
-
// check for prefix
|
|
5635
|
-
var prefix = ___default["default"](___default["default"](siPrefixes).keys()).find(testPrefix => {
|
|
4738
|
+
var prefix = _(_(siPrefixes).keys()).find(testPrefix => {
|
|
5636
4739
|
return new RegExp("^" + testPrefix).test(symbol);
|
|
5637
4740
|
});
|
|
5638
4741
|
if (prefix) {
|
|
5639
4742
|
var base = symbol.replace(new RegExp("^" + prefix), "");
|
|
5640
|
-
|
|
5641
|
-
// It's okay to be here if either:
|
|
5642
|
-
// * `base` is a base unit (the seven units listed in baseUnits)
|
|
5643
|
-
// * `base` is a derived unit which allows prefixes
|
|
5644
|
-
//
|
|
5645
|
-
// Otherwise, we're trying to parse a unit label which is not
|
|
5646
|
-
// allowed (mwk, mBTU, etc).
|
|
5647
|
-
if (___default["default"](baseUnits).has(base) || derivedUnits[base] && derivedUnits[base].prefixes === hasPrefixes) {
|
|
4743
|
+
if (_(baseUnits).has(base) || derivedUnits[base] && derivedUnits[base].prefixes === hasPrefixes) {
|
|
5648
4744
|
return new Mul(siPrefixes[prefix], new Unit(base));
|
|
5649
4745
|
} else {
|
|
5650
4746
|
throw new Error(base + " does not allow prefixes");
|
|
@@ -5653,36 +4749,18 @@ var unprefixify = function (symbol) {
|
|
|
5653
4749
|
return new Unit(symbol);
|
|
5654
4750
|
}
|
|
5655
4751
|
};
|
|
5656
|
-
const unitParse = function (input) {
|
|
4752
|
+
const unitParse = function unitParse(input) {
|
|
5657
4753
|
try {
|
|
5658
4754
|
var parseResult = unitParser.parse(input);
|
|
5659
|
-
|
|
5660
|
-
// parseResult looks like:
|
|
5661
|
-
// {
|
|
5662
|
-
// magnitude: "5",
|
|
5663
|
-
// unit: {
|
|
5664
|
-
// num: [
|
|
5665
|
-
// { name: "s", pow: 2 }
|
|
5666
|
-
// ],
|
|
5667
|
-
// denom: [
|
|
5668
|
-
// { name: "kg", pow: 1 }
|
|
5669
|
-
// ]
|
|
5670
|
-
// }
|
|
5671
|
-
// }
|
|
5672
|
-
//
|
|
5673
|
-
// denom is optionally null
|
|
5674
|
-
|
|
5675
4755
|
const unitArray = [];
|
|
5676
|
-
|
|
4756
|
+
_(parseResult.unit.num).each(unitSpec => {
|
|
5677
4757
|
unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(unitSpec.pow)));
|
|
5678
4758
|
});
|
|
5679
|
-
|
|
4759
|
+
_(parseResult.unit.denom).each(unitSpec => {
|
|
5680
4760
|
unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(-1 * unitSpec.pow)));
|
|
5681
4761
|
});
|
|
5682
4762
|
var unit = new Mul(unitArray).flatten();
|
|
5683
4763
|
if (parseResult.type === "unitMagnitude") {
|
|
5684
|
-
// in the first case we have a magnitude coefficient as well as the
|
|
5685
|
-
// unit itself.
|
|
5686
4764
|
var coefArray = [new Float(+parseResult.magnitude), ...unitArray];
|
|
5687
4765
|
var expr = new Mul(coefArray);
|
|
5688
4766
|
return {
|
|
@@ -5693,7 +4771,6 @@ const unitParse = function (input) {
|
|
|
5693
4771
|
type: parseResult.type
|
|
5694
4772
|
};
|
|
5695
4773
|
} else {
|
|
5696
|
-
// in the second case it's just the unit with no magnitude.
|
|
5697
4774
|
return {
|
|
5698
4775
|
parsed: true,
|
|
5699
4776
|
unit: unit,
|
|
@@ -5709,7 +4786,6 @@ const unitParse = function (input) {
|
|
|
5709
4786
|
};
|
|
5710
4787
|
var baseUnits = {
|
|
5711
4788
|
m: new Unit("m"),
|
|
5712
|
-
// Note: kg is the SI base unit but we use g for consistency
|
|
5713
4789
|
g: new Unit("g"),
|
|
5714
4790
|
s: new Unit("s"),
|
|
5715
4791
|
A: new Unit("A"),
|
|
@@ -5734,14 +4810,11 @@ var siPrefixes = {
|
|
|
5734
4810
|
T: new Pow(new Int(10), new Int(12)),
|
|
5735
4811
|
P: new Pow(new Int(10), new Int(15)),
|
|
5736
4812
|
E: new Pow(new Int(10), new Int(18)),
|
|
5737
|
-
// http://en.wikipedia.org/wiki/Metric_prefix#.22Hella.22_prefix_proposal
|
|
5738
4813
|
hella: new Pow(new Int(10), new Int(27))
|
|
5739
4814
|
};
|
|
5740
|
-
|
|
5741
|
-
// Use these two values to mark a unit as either SI-prefixable or not.
|
|
5742
4815
|
const hasPrefixes = "hasPrefixes";
|
|
5743
4816
|
const hasntPrefixes = "hasntPrefixes";
|
|
5744
|
-
const makeAlias = function (str, prefixes) {
|
|
4817
|
+
const makeAlias = function makeAlias(str, prefixes) {
|
|
5745
4818
|
var splits = str.split("|");
|
|
5746
4819
|
var coefficientStr = splits[0].trim();
|
|
5747
4820
|
var unitsStr = splits[1].trim();
|
|
@@ -5762,35 +4835,9 @@ const makeAlias = function (str, prefixes) {
|
|
|
5762
4835
|
prefixes: prefixes
|
|
5763
4836
|
};
|
|
5764
4837
|
};
|
|
5765
|
-
|
|
5766
|
-
// This is a mapping of derived units (or different names for a unit) to their
|
|
5767
|
-
// definitions. For example, an inch is defined as 0.0254 m.
|
|
5768
|
-
//
|
|
5769
|
-
// Definitions don't need to be in terms of base units. For example, tsp is
|
|
5770
|
-
// defined in terms of tbsp (which is defined in terms of cup -> gal -> L ->
|
|
5771
|
-
// m^3). However, units must get simpler. I.e. there's no loop checking.
|
|
5772
|
-
//
|
|
5773
|
-
// makeAlias takes two parameters:
|
|
5774
|
-
// * a string specifying the simplification to perform
|
|
5775
|
-
// - a required pipe separates the constant factor from the base units
|
|
5776
|
-
// - the constant factor is parsed by KAS
|
|
5777
|
-
// - the base units are in a simple format which disallows exponents and
|
|
5778
|
-
// requires multiplicands to be space-separated ("m m" rather than "m^2)
|
|
5779
|
-
// with an optional "/" separating numerator and denominator
|
|
5780
|
-
// - prefixes are not allowed to be used in the converted to units
|
|
5781
|
-
// (note that this restriction, the format of the string, and the choice to
|
|
5782
|
-
// use a string in the first place are made out of laziness to minimize
|
|
5783
|
-
// both typing and parsing)
|
|
5784
|
-
// * a boolean specifying whether or not it's acceptable to use SI units
|
|
5785
|
-
//
|
|
5786
|
-
// Where possible, these units are taken from "The International System of
|
|
5787
|
-
// Units (SI)" 8th edition (2006).
|
|
5788
4838
|
var derivedUnits = {
|
|
5789
|
-
// mass
|
|
5790
|
-
// The atomic mass unit / dalton.
|
|
5791
4839
|
Da: makeAlias("1.6605388628 x 10^-24 | g", hasPrefixes),
|
|
5792
4840
|
u: makeAlias("| Da", hasntPrefixes),
|
|
5793
|
-
// length
|
|
5794
4841
|
meter: makeAlias("| m", hasntPrefixes),
|
|
5795
4842
|
meters: makeAlias("| m", hasntPrefixes),
|
|
5796
4843
|
in: makeAlias("254 / 10000 | m", hasntPrefixes),
|
|
@@ -5801,43 +4848,32 @@ var derivedUnits = {
|
|
|
5801
4848
|
nmi: makeAlias("1852 | m", hasntPrefixes),
|
|
5802
4849
|
Å: makeAlias("10^-10 | m", hasntPrefixes),
|
|
5803
4850
|
pc: makeAlias("3.0857 x 10^16 | m", hasntPrefixes),
|
|
5804
|
-
// time
|
|
5805
4851
|
min: makeAlias("60 | s", hasntPrefixes),
|
|
5806
4852
|
hr: makeAlias("3600 | s", hasntPrefixes),
|
|
5807
4853
|
sec: makeAlias("| s", hasntPrefixes),
|
|
5808
|
-
// TODO(joel) make day work
|
|
5809
4854
|
day: makeAlias("86400 | s", hasntPrefixes),
|
|
5810
4855
|
wk: makeAlias("604800 | s", hasntPrefixes),
|
|
5811
4856
|
fortnight: makeAlias("14 | day", hasntPrefixes),
|
|
5812
4857
|
shake: makeAlias("10^-8 | s", hasntPrefixes),
|
|
5813
4858
|
olympiad: makeAlias("126200000 | s", hasntPrefixes),
|
|
5814
|
-
// temperature
|
|
5815
4859
|
"°C": makeAlias("1 | K", hasntPrefixes),
|
|
5816
4860
|
"°F": makeAlias("5/9 | K", hasntPrefixes),
|
|
5817
4861
|
"°R": makeAlias("5/9 | K", hasntPrefixes),
|
|
5818
|
-
// electric charge
|
|
5819
4862
|
e: makeAlias("1.6021765314 x 10^-19 | C", hasntPrefixes),
|
|
5820
|
-
// speed
|
|
5821
4863
|
c: makeAlias("299792458 | m / s", hasntPrefixes),
|
|
5822
4864
|
kn: makeAlias("514/1000 | m / s", hasntPrefixes),
|
|
5823
4865
|
kt: makeAlias("| kn", hasntPrefixes),
|
|
5824
4866
|
knot: makeAlias("| kn", hasntPrefixes),
|
|
5825
|
-
// energy
|
|
5826
4867
|
J: makeAlias("| N m", hasPrefixes),
|
|
5827
4868
|
BTU: makeAlias("1060 | J", hasntPrefixes),
|
|
5828
4869
|
cal: makeAlias("4184 / 1000 | J", hasPrefixes),
|
|
5829
4870
|
eV: makeAlias("1.602176514 x 10^-19 | J", hasPrefixes),
|
|
5830
4871
|
erg: makeAlias("10^−7 | J", hasPrefixes),
|
|
5831
|
-
// power
|
|
5832
4872
|
W: makeAlias("| J / s", hasPrefixes),
|
|
5833
4873
|
"H-e": makeAlias("80 | W", hasntPrefixes),
|
|
5834
|
-
// force
|
|
5835
4874
|
N: makeAlias("1000 | g m / s s", hasPrefixes),
|
|
5836
|
-
// "lb": makeAlias("4448 / 1000 | N", hasntPrefixes),
|
|
5837
|
-
// 4.4482216152605
|
|
5838
4875
|
lb: makeAlias("4448221615 / 1000000000 | N", hasntPrefixes),
|
|
5839
4876
|
dyn: makeAlias("10^-5 | N", hasntPrefixes),
|
|
5840
|
-
// pressure
|
|
5841
4877
|
Pa: makeAlias("1 | N / m m m", hasPrefixes),
|
|
5842
4878
|
bar: makeAlias("10^5 | Pa", hasPrefixes),
|
|
5843
4879
|
"㏔": makeAlias("1/1000 | bar", hasntPrefixes),
|
|
@@ -5845,7 +4881,6 @@ var derivedUnits = {
|
|
|
5845
4881
|
atm: makeAlias("101325 | Pa", hasntPrefixes),
|
|
5846
4882
|
Torr: makeAlias("1/760 | atm", hasntPrefixes),
|
|
5847
4883
|
mmHg: makeAlias("| Torr", hasntPrefixes),
|
|
5848
|
-
// area
|
|
5849
4884
|
ha: makeAlias("10^4 | m m", hasntPrefixes),
|
|
5850
4885
|
b: makeAlias("10^−28 | m m", hasPrefixes),
|
|
5851
4886
|
barn: makeAlias("| b", hasPrefixes),
|
|
@@ -5853,7 +4888,6 @@ var derivedUnits = {
|
|
|
5853
4888
|
skilodge: makeAlias("10^-31 | m m", hasntPrefixes),
|
|
5854
4889
|
outhouse: makeAlias("10^-34 | m m", hasntPrefixes),
|
|
5855
4890
|
shed: makeAlias("10^-52 | m m", hasntPrefixes),
|
|
5856
|
-
// volume
|
|
5857
4891
|
L: makeAlias("1/1000 | m m m", hasPrefixes),
|
|
5858
4892
|
gal: makeAlias("3785/1000 | L", hasPrefixes),
|
|
5859
4893
|
cup: makeAlias("1/16 | gal", hasntPrefixes),
|
|
@@ -5866,25 +4900,17 @@ var derivedUnits = {
|
|
|
5866
4900
|
"fl. oz.": makeAlias("1/8 | cup", hasntPrefixes),
|
|
5867
4901
|
tbsp: makeAlias("1/16 | cup", hasntPrefixes),
|
|
5868
4902
|
tsp: makeAlias("1/3 | tbsp", hasntPrefixes),
|
|
5869
|
-
// rotational
|
|
5870
|
-
// "rad":
|
|
5871
4903
|
rev: makeAlias("2 pi | rad", hasntPrefixes),
|
|
5872
4904
|
deg: makeAlias("180 pi | rad", hasntPrefixes),
|
|
5873
4905
|
"°": makeAlias("| deg", hasntPrefixes),
|
|
5874
4906
|
arcminute: makeAlias("1/60 | deg", hasntPrefixes),
|
|
5875
4907
|
arcsec: makeAlias("1/3600 | deg", hasntPrefixes),
|
|
5876
|
-
// dimensionless
|
|
5877
|
-
// "B": makeAlias("10 | dB", hasntPrefixes), // XXX danger - logarithmic
|
|
5878
|
-
// "dB"
|
|
5879
|
-
// "nP"
|
|
5880
4908
|
Hu: makeAlias("1000 | dB", hasPrefixes),
|
|
5881
4909
|
dozen: makeAlias("12 |", hasntPrefixes),
|
|
5882
|
-
// XXX
|
|
5883
4910
|
mol: makeAlias("6.0221412927 x 10^23 |", hasPrefixes),
|
|
5884
4911
|
"%": makeAlias("1/100 |", hasntPrefixes),
|
|
5885
4912
|
percent: makeAlias("| %", hasntPrefixes),
|
|
5886
4913
|
ppm: makeAlias("1/1000000 |", hasntPrefixes),
|
|
5887
|
-
// electric / magnetic
|
|
5888
4914
|
V: makeAlias("1000 | g m m / s s C", hasPrefixes),
|
|
5889
4915
|
C: makeAlias("| A s", hasPrefixes),
|
|
5890
4916
|
ampere: makeAlias("| A", hasntPrefixes),
|
|
@@ -5894,8 +4920,6 @@ var derivedUnits = {
|
|
|
5894
4920
|
H: makeAlias("| ohm s", hasPrefixes),
|
|
5895
4921
|
T: makeAlias("1000 | g / C s", hasPrefixes),
|
|
5896
4922
|
Wb: makeAlias("1000 | g m m / C s", hasPrefixes),
|
|
5897
|
-
// photometry
|
|
5898
|
-
// TODO not sure this is right
|
|
5899
4923
|
lm: makeAlias("pi x 10^4 | cd / m m", hasntPrefixes),
|
|
5900
4924
|
lx: makeAlias("| lm / m m", hasntPrefixes),
|
|
5901
4925
|
nit: makeAlias("| cd / m m", hasntPrefixes),
|
|
@@ -5909,38 +4933,17 @@ var derivedUnits = {
|
|
|
5909
4933
|
sk: makeAlias("10^-7 | lm", hasntPrefixes),
|
|
5910
4934
|
skot: makeAlias("| sk", hasntPrefixes),
|
|
5911
4935
|
bril: makeAlias("10^-11 | lm", hasntPrefixes),
|
|
5912
|
-
// other
|
|
5913
4936
|
Hz: makeAlias("| / s", hasPrefixes)
|
|
5914
4937
|
};
|
|
5915
4938
|
const Zero = NumZero;
|
|
5916
4939
|
const One = NumOne;
|
|
5917
4940
|
|
|
5918
|
-
|
|
5919
|
-
* Compares two expressions for equality.
|
|
5920
|
-
*
|
|
5921
|
-
* Assumes that both expressions have already been parsed.
|
|
5922
|
-
*/
|
|
5923
|
-
// TODO(alex): be able to pass a random() function to compare()
|
|
5924
|
-
const compare = function (expr1, expr2) {
|
|
5925
|
-
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
4941
|
+
const compare = function compare(expr1, expr2, options = {}) {
|
|
5926
4942
|
const defaults = {
|
|
5927
4943
|
form: false,
|
|
5928
4944
|
simplify: false
|
|
5929
4945
|
};
|
|
5930
|
-
|
|
5931
|
-
/* Options that could be added in the future:
|
|
5932
|
-
* - Allow ratios: e.g. 3/1 and 3 should both be accepted for something
|
|
5933
|
-
* like slope
|
|
5934
|
-
* - Allow student to choose their own variable names
|
|
5935
|
-
*/
|
|
5936
|
-
const optionsWithDefaults = {
|
|
5937
|
-
...defaults,
|
|
5938
|
-
...options
|
|
5939
|
-
};
|
|
5940
|
-
|
|
5941
|
-
// TODO(CP-1614): Figure out how to make these messages translatable
|
|
5942
|
-
|
|
5943
|
-
// Variable check
|
|
4946
|
+
const optionsWithDefaults = _extends({}, defaults, options);
|
|
5944
4947
|
const vars = expr1.sameVars(expr2);
|
|
5945
4948
|
if (!vars.equal) {
|
|
5946
4949
|
let message;
|
|
@@ -5956,24 +4959,18 @@ const compare = function (expr1, expr2) {
|
|
|
5956
4959
|
message: message
|
|
5957
4960
|
};
|
|
5958
4961
|
}
|
|
5959
|
-
|
|
5960
|
-
// Semantic check
|
|
5961
4962
|
if (!expr1.compare(expr2)) {
|
|
5962
4963
|
return {
|
|
5963
4964
|
equal: false,
|
|
5964
4965
|
message: null
|
|
5965
4966
|
};
|
|
5966
4967
|
}
|
|
5967
|
-
|
|
5968
|
-
// Syntactic check
|
|
5969
4968
|
if (optionsWithDefaults.form && !expr1.sameForm(expr2)) {
|
|
5970
4969
|
return {
|
|
5971
4970
|
equal: false,
|
|
5972
4971
|
message: "Your answer is not in the correct form."
|
|
5973
4972
|
};
|
|
5974
4973
|
}
|
|
5975
|
-
|
|
5976
|
-
// Syntactic check
|
|
5977
4974
|
if (optionsWithDefaults.simplify && !expr1.isSimplified()) {
|
|
5978
4975
|
return {
|
|
5979
4976
|
equal: false,
|
|
@@ -5986,24 +4983,5 @@ const compare = function (expr1, expr2) {
|
|
|
5986
4983
|
};
|
|
5987
4984
|
};
|
|
5988
4985
|
|
|
5989
|
-
|
|
5990
|
-
exports.Add = Add;
|
|
5991
|
-
exports.Const = Const;
|
|
5992
|
-
exports.Eq = Eq;
|
|
5993
|
-
exports.Float = Float;
|
|
5994
|
-
exports.Func = Func;
|
|
5995
|
-
exports.Int = Int;
|
|
5996
|
-
exports.Log = Log;
|
|
5997
|
-
exports.Mul = Mul;
|
|
5998
|
-
exports.One = One;
|
|
5999
|
-
exports.Pow = Pow;
|
|
6000
|
-
exports.Rational = Rational;
|
|
6001
|
-
exports.Trig = Trig;
|
|
6002
|
-
exports.Unit = Unit;
|
|
6003
|
-
exports.Var = Var;
|
|
6004
|
-
exports.Zero = Zero;
|
|
6005
|
-
exports.compare = compare;
|
|
6006
|
-
exports.libVersion = libVersion;
|
|
6007
|
-
exports.parse = parse;
|
|
6008
|
-
exports.unitParse = unitParse;
|
|
4986
|
+
export { Abs, Add, Const, Eq, Float, Func, Int, Log, Mul, One, Pow, Rational, Trig, Unit, Var, Zero, compare, libVersion, parse, unitParse };
|
|
6009
4987
|
//# sourceMappingURL=index.js.map
|