@khanacademy/kas 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ import _ from "underscore";
2
+
3
+ import * as KAS from "../index.js";
4
+
5
+ expect.extend({
6
+ toHaveNorm(input: string, reference: string) {
7
+ var actual = KAS.parse(input).expr.normalize().print();
8
+ var expected = KAS.parse(reference).expr.normalize().print();
9
+
10
+ return {
11
+ pass: actual === expected,
12
+ message: () => `${input} is the same as ${reference}`,
13
+ };
14
+ },
15
+ toHaveStripNorm(input: string, reference: string) {
16
+ var actual = KAS.parse(input).expr.strip().normalize().print();
17
+ var expected = KAS.parse(reference).expr.strip().normalize().print();
18
+
19
+ return {
20
+ pass: actual === expected,
21
+ message: () => `${input} is the same as ${reference}`,
22
+ };
23
+ },
24
+ });
25
+
26
+ describe("checking form", () => {
27
+ test("normalize", () => {
28
+ expect("ab").toHaveNorm("ba");
29
+ expect("(ab)c").toHaveNorm("(cb)a");
30
+
31
+ var forms = [
32
+ "(6x+1)(x-1)",
33
+ "(1+6x)(x-1)",
34
+ "(6x+1)(-1+x)",
35
+ "(1+6x)(-1+x)",
36
+ "(x-1)(6x+1)",
37
+ "(x-1)(1+6x)",
38
+ "(-1+x)(6x+1)",
39
+ "(-1+x)(1+6x)"
40
+ ];
41
+
42
+ _.each(forms, function(form) {
43
+ expect(forms[0]).toHaveNorm(form);
44
+ });
45
+ });
46
+
47
+ test("strip then normalize", () => {
48
+ expect("ab").toHaveStripNorm("ba");
49
+ expect("(ab)c").toHaveStripNorm("(cb)a");
50
+
51
+ var forms = [
52
+ "(6x+1)(x-1)",
53
+ "(1+6x)(x-1)",
54
+ "(6x+1)(-1+x)",
55
+ "(1+6x)(-1+x)",
56
+ "(-6x-1)(-x+1)",
57
+ "(-1-6x)(-x+1)",
58
+ "(-6x-1)(1-x)",
59
+ "(-1-6x)(1-x)",
60
+ "(x-1)(6x+1)",
61
+ "(x-1)(1+6x)",
62
+ "(-1+x)(6x+1)",
63
+ "(-1+x)(1+6x)",
64
+ "(-x+1)(-6x-1)",
65
+ "(-x+1)(-1-6x)",
66
+ "(1-x)(-6x-1)",
67
+ "(1-x)(-1-6x)",
68
+ "-(6x+1)(1-x)",
69
+ "-(-6x-1)(x-1)"
70
+ ];
71
+
72
+ _.each(forms, function(form) {
73
+ expect(forms[0]).toHaveStripNorm(form);
74
+ });
75
+ });
76
+ });
@@ -0,0 +1,322 @@
1
+ import _ from "underscore";
2
+
3
+ import * as KAS from "../index.js";
4
+
5
+ expect.extend({
6
+ toEqualExpr(input: string, expected: string) {
7
+ const inputExpr = KAS.parse(input, {functions: ["f", "g", "h"]}).expr;
8
+ const expectedExpr = KAS.parse(expected, {
9
+ functions: ["f", "g", "h"],
10
+ }).expr;
11
+
12
+ const actual = KAS.compare(inputExpr, expectedExpr, {form: false});
13
+
14
+ if (this.isNot) {
15
+ return actual.equal
16
+ ? {pass: true}
17
+ : {
18
+ pass: false,
19
+ message: () => `${input} is NOT the same as ${expected}`,
20
+ };
21
+ }
22
+ return actual.equal
23
+ ? {pass: true}
24
+ : {
25
+ pass: false,
26
+ message: () => `${input} is the same as ${expected}`,
27
+ };
28
+ },
29
+ toEqualExprAndForm(input: string, expected: string) {
30
+ const inputExpr = KAS.parse(input, {functions: ["f", "g", "h"]}).expr;
31
+ const expectedExpr = KAS.parse(expected, {
32
+ functions: ["f", "g", "h"],
33
+ }).expr;
34
+
35
+ const actual = KAS.compare(inputExpr, expectedExpr, {form: true});
36
+
37
+ if (this.isNot) {
38
+ return actual.equal
39
+ ? {pass: true}
40
+ : {
41
+ pass: false,
42
+ message: () => `${input} is NOT the same as ${expected}`,
43
+ };
44
+ }
45
+ return actual.equal
46
+ ? {pass: true}
47
+ : {
48
+ pass: false,
49
+ message: () => `${input} is the same as ${expected}`,
50
+ };
51
+ },
52
+ });
53
+
54
+ describe("comparing", () => {
55
+ test("evaluate only", () => {
56
+ expect("2+2").toEqualExpr("4");
57
+ expect("a(b+c)").toEqualExpr("ab+ac");
58
+ expect("a/b").toEqualExpr("a*b^-1");
59
+
60
+ expect("1.2^2").toEqualExpr("1.44");
61
+ expect("1.3^2").toEqualExpr("1.69");
62
+ expect("1.4^2").toEqualExpr("1.96");
63
+ expect("1.5^2").toEqualExpr("2.25");
64
+
65
+ expect("1.2345^2").toEqualExpr("1.52399025");
66
+ expect("1.2345*1.2345").toEqualExpr("1.52399025");
67
+ expect("(1+.2345)^2").toEqualExpr("1.52399025");
68
+ expect("(-5)^(1/3)").toEqualExpr("-1.709975946");
69
+ expect("(-5)^(2/6)").toEqualExpr("-1.709975946");
70
+ expect("(-5)^(4/3)").toEqualExpr("8.549879733");
71
+ expect("(-5)^(-1/3)").toEqualExpr("-0.584803547");
72
+
73
+ expect("(-5)^(1/5)").toEqualExpr("-1.379729661");
74
+ expect("(-5)^(0.2)").toEqualExpr("-1.379729661");
75
+
76
+ expect("x^(1/5)").toEqualExpr("x^(0.2)");
77
+ expect("x^(8/5)").toEqualExpr("x^(1.6)");
78
+
79
+ expect("(1-x)(-1-6x)").toEqualExpr("(6x+1)(x-1)");
80
+ expect("y=x").not.toEqualExpr("x");
81
+ expect("x").not.toEqualExpr("y=x");
82
+ expect("y=x").toEqualExpr("y=x");
83
+ expect("y=x").toEqualExpr("x=y");
84
+ expect("y=x").toEqualExpr("-y=-x");
85
+ expect("y=x").toEqualExpr("-x=-y");
86
+ expect("y=x").not.toEqualExpr("y=-x");
87
+ expect("y=x").not.toEqualExpr("-y=x");
88
+ expect("y=x").not.toEqualExpr("y=/=x");
89
+ expect("y<x").toEqualExpr("x>y");
90
+ expect("y<=x").toEqualExpr("x>=y");
91
+ expect("y>x").toEqualExpr("x<y");
92
+ expect("y>x").not.toEqualExpr("x>y");
93
+ expect("y>=x").toEqualExpr("x<=y");
94
+ expect("a+b<c-d").toEqualExpr("a+b-c+d<0");
95
+
96
+ expect("y=mx+b").toEqualExpr("-b-mx=-y");
97
+ expect("y=mx+b").toEqualExpr("y-b=mx");
98
+
99
+ // all of these normalize to the same expression, set to zero
100
+ const forms = [
101
+ "y=2x-5",
102
+ "2x-5=y",
103
+ "2x-y=5",
104
+ "(y+5)/2=x",
105
+ "(y+5)/x=2",
106
+ "1/2(y+5)=x",
107
+ ".5(y+5)=x",
108
+ "y-3=2(x-4)",
109
+ "2y=4x-10",
110
+ "yz=2xz-5z",
111
+ ];
112
+
113
+ _.each(forms, (form) => {
114
+ expect(forms[0]).toEqualExpr(form);
115
+ });
116
+
117
+ expect("3y=2x-15").toEqualExpr("3/2(y+5)=x");
118
+
119
+ const forms2 = ["1/3p-3=114", "1/3p=117", "p=351", "p-351=0"];
120
+
121
+ _.each(forms2, (form) => {
122
+ expect(forms2[0]).toEqualExpr(form);
123
+ });
124
+
125
+ expect("x").toEqualExpr("xy/y");
126
+ expect("e^x").toEqualExpr("e^x");
127
+ expect("e^x").not.toEqualExpr("e^x + 1");
128
+
129
+ const forms3 = [
130
+ "x+x+x+6=12",
131
+ "x+2x+6=12",
132
+ "3x+6=12",
133
+ "x+2x=6",
134
+ "2x=6-x",
135
+ "3x=6",
136
+ "x=2",
137
+ ];
138
+
139
+ _.each(forms3, function (form) {
140
+ expect(forms3[0]).toEqualExpr(form);
141
+ });
142
+
143
+ expect("100/55.6=t").toEqualExpr("t=100/55.6");
144
+ expect("100/1.6^2=t").toEqualExpr("t=100/1.6^2");
145
+ expect("7/3x+x=15").toEqualExpr("(2+1/3)x+x=15");
146
+ expect("7/3x+x=15").not.toEqualExpr("(1+1/3)x+x=15");
147
+
148
+ // Symmetric equations
149
+ expect("x^2+y^2=r^2").toEqualExpr("r^2=x^2+y^2");
150
+ expect("23^1.5=110.304").toEqualExpr("110.304=23^1.5");
151
+
152
+ // TODO(alex): make sure that I have both positive and negative
153
+ // test cases for all functionality
154
+ expect("6.12*10^-2").toEqualExpr("6.12*10^-2");
155
+ expect("6.12*10^-2").not.toEqualExpr("6.12*10^-6");
156
+
157
+ expect("3^-x").toEqualExpr("(1/3)^x");
158
+ expect("(1/3)^-x").toEqualExpr("3^x");
159
+ expect("(3)^-x").not.toEqualExpr("3^x");
160
+
161
+ expect("5.6=x+0.4+5.2").toEqualExpr("5.6=x+0.4+5.2");
162
+
163
+ // Reciprocal trig functions
164
+ expect("csc x").toEqualExpr("1/sin x");
165
+ expect("sec x").toEqualExpr("1/cos x");
166
+ expect("cot x").toEqualExpr("1/tan x");
167
+ expect("arccsc x").toEqualExpr("arcsin (1/x)");
168
+ expect("arcsec x").toEqualExpr("arccos (1/x)");
169
+ expect("arccot x").toEqualExpr("arctan (1/x)");
170
+
171
+ // Reciprocal hyperbolic trig functions
172
+ expect("csch x").toEqualExpr("1/sinh x");
173
+ expect("sech x").toEqualExpr("1/cosh x");
174
+ expect("coth x").toEqualExpr("1/tanh x");
175
+
176
+ // Make sure trig functions that are the same for all integer values
177
+ // are not the same
178
+ expect("-2sin(pi x) + 4").not.toEqualExpr("4");
179
+ expect("2sin(pi x) + 4").not.toEqualExpr("-2sin(pi x) + 4");
180
+ expect("sin(pi x)").not.toEqualExpr("0");
181
+ expect("0").not.toEqualExpr("sin(pi x)");
182
+ expect("cos(pi x)").not.toEqualExpr("cos(2 pi x)");
183
+ expect("sin(pi x)").not.toEqualExpr("sin(500pi x)");
184
+ expect("sin(500pi x)").not.toEqualExpr("sin(pi x)");
185
+
186
+ // Check that floating point error isn't killing us
187
+ // TODO(jack): These don't seem to test much; make better tests
188
+ expect("0").toEqualExpr("sin(7pi)");
189
+ expect("sin(7pi)").toEqualExpr("0");
190
+ expect("0").toEqualExpr("sin(500pi)");
191
+ expect("sin(500pi)").toEqualExpr("0");
192
+
193
+ // Handle denominators the same way regardless of a fraction's format
194
+ expect("x=1.2^2").toEqualExpr("x=1.44");
195
+ expect("x=1.2^2").toEqualExpr("x=36/25");
196
+ expect("x=1.44").toEqualExpr("x=36/25");
197
+ expect("x=1.44").not.toEqualExpr("x=35/25");
198
+
199
+ expect("x=1.2^(2y)").toEqualExpr("x=1.44^y");
200
+ expect("x=1.2^(2y)").toEqualExpr("x=(36/25)^y");
201
+ expect("x=1.44^y").toEqualExpr("x=(36/25)^y");
202
+
203
+ expect("x=1.3^2").toEqualExpr("x=1.69");
204
+ expect("x=1.4^2").toEqualExpr("x=1.96");
205
+ expect("x=1.5^2").toEqualExpr("x=2.25");
206
+ expect("x=1.5^2").toEqualExpr("x=2.25");
207
+
208
+ expect("x=1.2345^2").toEqualExpr("x=1.52399025");
209
+ expect("x=1.2345*1.2345").toEqualExpr("x=1.52399025");
210
+ expect("x=(1+.2345)^2").toEqualExpr("x=1.52399025");
211
+ expect("x=(1+.2345)^2").not.toEqualExpr("x=1.52399022");
212
+
213
+ // Varying small and large comparisons
214
+ expect("1.1234567891235 * 10^200").toEqualExpr(
215
+ "1.1234567891234 * 10^200",
216
+ );
217
+ expect("1.1234567891235 * 10^200").not.toEqualExpr("0.10");
218
+ expect("0.10").not.toEqualExpr("1.1234567891235 * 10^200");
219
+ expect("0.50").not.toEqualExpr("0.51");
220
+ expect("0.51").not.toEqualExpr("0.50");
221
+
222
+ expect("1.00").toEqualExpr("1.00");
223
+ expect("0.9").not.toEqualExpr("1.1");
224
+ expect("1.1").not.toEqualExpr("0.9");
225
+
226
+ // Real-world examples of equivalent equations that are now accepted
227
+ expect("12(1-r)^2+58(1-r)=10").toEqualExpr("(1-r)(70-12r)=10");
228
+ expect("720m+480(m-5)=42000").toEqualExpr("720m+480m-2400=42000");
229
+ expect("2w+50/w=25").toEqualExpr("w(12.5-w)=25");
230
+ expect("(n*(8+4n))/2>19206").toEqualExpr("6n+2n(n-1)>19206");
231
+
232
+ // Correctly handle exponents with negative bases and variables in exponent
233
+ expect("( 2)^n").not.toEqualExpr("( 2)^(n-1)");
234
+ expect("( 2)^n").toEqualExpr("( 2)^(n)");
235
+ expect("( 2)^n").not.toEqualExpr("( 2)^(n+1)");
236
+
237
+ expect("(-2)^n").not.toEqualExpr("(-2)^(n-1)");
238
+ expect("(-2)^n").toEqualExpr("(-2)^(n)");
239
+ expect("(-2)^n").not.toEqualExpr("(-2)^(n+1)");
240
+
241
+ expect("(-2)^(a)").not.toEqualExpr("(-2)^(ab)");
242
+ expect("(-2)^(a)").not.toEqualExpr("(-2)^(a^b)");
243
+
244
+ // This is incorrect, but accurately captures the current behavior
245
+ // See comment in `Expr.compare()` for more details
246
+ expect("(-2)^(n+0.1)").toEqualExpr("(-2)^(n+1.1)");
247
+ });
248
+
249
+ test("simplify can't yet handle these", () => {
250
+ expect("sin(x + 2pi)").toEqualExpr("sin(x)");
251
+ expect("y = sin(x + 2pi)").toEqualExpr("y = sin(x)");
252
+ expect("sin^2(x)+cos^2(x)").toEqualExpr("x/x");
253
+ expect("y = sin^2(x)+cos^2(x)").toEqualExpr("y = x/x");
254
+ });
255
+
256
+ test("partially evaluating functions", () => {
257
+ expect("f(x)").toEqualExpr("f(x)");
258
+ expect("f(x)").not.toEqualExpr("g(x)");
259
+ expect("f(g(x))").toEqualExpr("f(g(x))");
260
+ expect("sin(f(3x-x))/cos(f(x+x))").toEqualExpr("tan(f(2x))");
261
+ expect("f(x) = sin(x + 2pi)").toEqualExpr("f(x) = sin(x)");
262
+ // NOTE(kevinb): This test is flaky, because Expr.prototype.compare
263
+ // is non-deterministic. When comparing expressions this normally
264
+ // wouldn't be an issue because we could just evaluate the different
265
+ // variables at different point and then check that the difference
266
+ // between the expressions is always less than some very small number.
267
+ // In this case though, we convert equations to expressions, e.g.
268
+ // `f(x) = 1` is converted to `1-f(x)`, but because `f` is a function,
269
+ // we can't evaluate it. One possible solution to this would be
270
+ // to isolate `f(x)` in each expression and then check that the
271
+ // expressions that they're equal to are equal. In this case that
272
+ // would be `sin^2(x)+cos^2(x)` and `1`.
273
+ // TODO(TP-11651): Update compare to isolate functions on wide side
274
+ // before comparing expressions on the other side.
275
+ // expect("f(x) = sin^2(x)+cos^2(x)").toEqualExpr("f(x) = 1");
276
+ expect("f(x) = ln|x|+c").toEqualExpr("f(x)-ln|x|-c = 0");
277
+ });
278
+
279
+ test("evaluating and comparing form", () => {
280
+ expect("ab").toEqualExprAndForm("ba");
281
+ expect("(ab)c").toEqualExprAndForm("(cb)a");
282
+
283
+ const forms = [
284
+ "(6x+1)(x-1)",
285
+ "(1+6x)(x-1)",
286
+ "(6x+1)(-1+x)",
287
+ "(1+6x)(-1+x)",
288
+ "(-6x-1)(-x+1)",
289
+ "(-1-6x)(-x+1)",
290
+ "(-6x-1)(1-x)",
291
+ "(-1-6x)(1-x)",
292
+ "(x-1)(6x+1)",
293
+ "(x-1)(1+6x)",
294
+ "(-1+x)(6x+1)",
295
+ "(-1+x)(1+6x)",
296
+ "(-x+1)(-6x-1)",
297
+ "(-x+1)(-1-6x)",
298
+ "(1-x)(-6x-1)",
299
+ "(1-x)(-1-6x)",
300
+ "-(6x+1)(1-x)",
301
+ "-(-6x-1)(x-1)",
302
+ ];
303
+
304
+ _.each(forms, function (form) {
305
+ expect(forms[0]).toEqualExprAndForm(form);
306
+ });
307
+
308
+ expect("(6x+1)(x+1)").not.toEqualExprAndForm("(6x+1)(x-1)");
309
+ expect("a-b-c").not.toEqualExprAndForm("c+b+a");
310
+
311
+ expect("(6x+1)(x+1)").not.toEqualExprAndForm("(6x+1)(x-1)");
312
+ expect("a-b-c").not.toEqualExprAndForm("c+b+a");
313
+ expect("mx+b").toEqualExprAndForm("b+mx");
314
+
315
+ expect("y=mx+b").toEqualExprAndForm("-b-mx=-y");
316
+ expect("y=mx+b").not.toEqualExprAndForm("y-b=mx");
317
+
318
+ expect("y-3=2(x-4)").not.toEqualExprAndForm("y=2x-5");
319
+ expect("y-3=2(x-4)").not.toEqualExprAndForm("2x-y=5");
320
+ expect("y=2x-5").not.toEqualExprAndForm("2x-y=5");
321
+ });
322
+ });
@@ -0,0 +1,97 @@
1
+ import _ from "underscore";
2
+
3
+ import * as KAS from "../index.js";
4
+
5
+ expect.extend({
6
+ toCompileAs(
7
+ input: string,
8
+ expected: number,
9
+ vars?: {[string]: string | number | (number) => number} = {},
10
+ ) {
11
+ const functions = Object.keys(vars).filter(k => typeof vars[k] === 'function');
12
+ const func = KAS.parse(input, {functions}).expr.compile();
13
+
14
+ const actual = func(vars);
15
+
16
+ return Math.abs(actual - expected) < 1e-9
17
+ ? {pass: true}
18
+ : {
19
+ pass: false,
20
+ message: () =>
21
+ `${input} should evaluate to ${expected}, was ${actual}; by func: ${func.toString()}`,
22
+ };
23
+ },
24
+ });
25
+
26
+ describe("compilation", () => {
27
+ test("empty", () => {
28
+ expect("").toCompileAs(0);
29
+ });
30
+
31
+ test("simple expressions", () => {
32
+ expect("1+2+3+4").toCompileAs(10);
33
+ expect("1+2-3+4").toCompileAs(4);
34
+ expect("1*2*3*4").toCompileAs(24);
35
+ expect("1*2/3*4").toCompileAs(2 + 2 / 3);
36
+ expect("4^3^2^1").toCompileAs(262144);
37
+ expect("-1").toCompileAs(-1);
38
+ expect("--1").toCompileAs(1);
39
+ expect("---1").toCompileAs(-1);
40
+ expect("2^-2").toCompileAs(0.25);
41
+ expect("8^(1/3)").toCompileAs(2);
42
+ expect("0^0").toCompileAs(1);
43
+ expect(".25*4").toCompileAs(1);
44
+ expect("ln e").toCompileAs(1);
45
+ expect("log 10").toCompileAs(1);
46
+ expect("log_2 2").toCompileAs(1);
47
+ });
48
+
49
+ test("variable expressions", () => {
50
+ expect("x").toCompileAs(3, {x: 3});
51
+ expect("x^2").toCompileAs(9, {x: 3});
52
+ expect("(x^2+y^2)^.5").toCompileAs(5, {x: 3, y: 4});
53
+ expect("log x_0").toCompileAs(1, {x_0: 10});
54
+ expect("log x_0 + log x_1").toCompileAs(3, {x_0: 10, x_1: 100});
55
+ expect("log x_42").toCompileAs(1, {x_42: 10});
56
+ expect("x_a + x_bc").toCompileAs(7, {x_a: 1, x_b: 2, c: 3});
57
+ });
58
+
59
+ test("function expressions", () => {
60
+ expect("f(2)").toCompileAs(
61
+ 4,
62
+ {
63
+ f: function (x) {
64
+ return 2 * x;
65
+ },
66
+ },
67
+ );
68
+ expect("f(4+8)").toCompileAs(
69
+ 48,
70
+ {
71
+ f: function (x) {
72
+ return 4 * x;
73
+ },
74
+ },
75
+ );
76
+ expect("f(x-1)-f(x)").toCompileAs(
77
+ -7,
78
+ {
79
+ f: function (x) {
80
+ return Math.pow(x, 3);
81
+ },
82
+ x: 2,
83
+ },
84
+ );
85
+ });
86
+
87
+ test("trig expressions", () => {
88
+ expect("-2sin(pi x) + 4").toCompileAs(4, {x: 32});
89
+ expect("sin(pi x)").toCompileAs(0, {x: 6});
90
+ expect("sin(x)").toCompileAs(1, {x: Math.PI / 2});
91
+ expect("sin(pi x)").toCompileAs(-1, {x: 3 / 2});
92
+ expect("cos(x)").toCompileAs(1, {x: 0});
93
+ expect("cos x").toCompileAs(-1, {x: Math.PI});
94
+ expect("tan(x)").toCompileAs(0, {x: 0});
95
+ expect("tan x").toCompileAs(1, {x: Math.PI / 4});
96
+ });
97
+ });
@@ -0,0 +1,73 @@
1
+ import _ from "underscore";
2
+
3
+ import * as KAS from "../index.js";
4
+
5
+ expect.extend({
6
+ toEvaluateAs(
7
+ input: string,
8
+ expected: number,
9
+ vars?: {[string]: string | number} = {},
10
+ functions?: $ReadOnlyArray<string>,
11
+ ) {
12
+ const actual = KAS.parse(input, {functions: functions}).expr.eval(
13
+ vars,
14
+ {functions: functions},
15
+ );
16
+
17
+ if (actual !== expected) {
18
+ return {
19
+ pass: actual !== expected,
20
+ message: () => `${input} evaluates as ${expected}`,
21
+ };
22
+ }
23
+
24
+ return {pass: !this.isNot};
25
+ },
26
+ });
27
+
28
+ describe("evaluating", () => {
29
+ test("empty", () => {
30
+ expect("").toEvaluateAs(0);
31
+ });
32
+
33
+ test("simple expressions", () => {
34
+ expect("1+2+3+4").toEvaluateAs(10);
35
+ expect("1+2-3+4").toEvaluateAs(4);
36
+ expect("1*2*3*4").toEvaluateAs(24);
37
+ expect("1*2/3*4").toEvaluateAs(2 + 2 / 3);
38
+ expect("4^3^2^1").toEvaluateAs(262144);
39
+ expect("-1").toEvaluateAs(-1);
40
+ expect("--1").toEvaluateAs(1);
41
+ expect("---1").toEvaluateAs(-1);
42
+ expect("2^-2").toEvaluateAs(0.25);
43
+ expect("8^(1/3)").toEvaluateAs(2);
44
+ expect("0^0").toEvaluateAs(1);
45
+ expect(".25*4").toEvaluateAs(1);
46
+ expect("ln e").toEvaluateAs(1);
47
+ expect("log 10").toEvaluateAs(1);
48
+ expect("log_2 2").toEvaluateAs(1);
49
+ });
50
+
51
+ test("hyperbolic expressions", () => {
52
+ expect("cosh(0.2)").toEvaluateAs(1.020066755619076);
53
+ expect("coth(0.2)").toEvaluateAs(5.066489563439473);
54
+ expect("csch(3 * 2)").toEvaluateAs(0.00495753481347936);
55
+ });
56
+
57
+ test("variable expressions", () => {
58
+ expect("x").toEvaluateAs(3, {x: 3});
59
+ expect("x^2").toEvaluateAs(9, {x: 3});
60
+ expect("(x^2+y^2)^.5").toEvaluateAs(5, {x: 3, y: 4});
61
+ expect("log x_0").toEvaluateAs(1, {x_0: 10});
62
+ expect("log x_0 + log x_1").toEvaluateAs(3, {x_0: 10, x_1: 100});
63
+ expect("log x_42").toEvaluateAs(1, {x_42: 10});
64
+ expect("x_a + x_bc").toEvaluateAs(7, {x_a: 1, x_b: 2, c: 3});
65
+ });
66
+
67
+ test("function expressions", () => {
68
+ expect("f(2)").toEvaluateAs(4, {f: "2x"}, ["f"]);
69
+ expect("f(4+8)").toEvaluateAs(48, {f: "4x"}, ["f"]);
70
+ expect("f(x-1)-f(x)").toEvaluateAs(-7, {f: "x^3", x: 2}, ["f"]);
71
+ expect("g(1)").toEvaluateAs(-1, {f: "x", g: "-f(x)"}, ["f", "g"]);
72
+ });
73
+ });