@khanacademy/kas 0.3.7 → 0.3.8

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.
@@ -1,379 +0,0 @@
1
- import _ from "underscore";
2
-
3
- import * as KAS from "../index";
4
-
5
- expect.extend({
6
- toHaveGCD(
7
- input: [string, string],
8
- expected: string,
9
- ): jest.CustomMatcherResult {
10
- const [a, b] = input;
11
- const actualRep = KAS.parse(a).expr.findGCD(KAS.parse(b).expr).repr();
12
- const expectedRep = KAS.parse(expected).expr.repr();
13
-
14
- return actualRep === expectedRep
15
- ? {pass: true, message: () => ""}
16
- : {
17
- pass: false,
18
- message: () => `(${a}).findGCD(${b}) = ${expected}`,
19
- };
20
- },
21
- toBeSimplified(input: string, options?: any): jest.CustomMatcherResult {
22
- const actual = KAS.parse(input, options).expr.isSimplified();
23
-
24
- if (this.isNot) {
25
- return actual
26
- ? {pass: true, message: () => ""}
27
- : {
28
- pass: false,
29
- message: () => `${input} is simplified`,
30
- };
31
- }
32
- return actual
33
- ? {pass: true, message: () => ""}
34
- : {
35
- pass: false,
36
- message: () => `${input} is NOT simplified`,
37
- };
38
- },
39
- toHaveConsts(
40
- input: string,
41
- expected: ReadonlyArray<string>,
42
- options?: any,
43
- ): jest.CustomMatcherResult {
44
- const actual = KAS.parse(input, options).expr.getConsts();
45
-
46
- if (this.isNot) {
47
- expect(actual).not.toEqual(expected);
48
- } else {
49
- expect(actual).toEqual(expected);
50
- }
51
-
52
- return {pass: !this.isNot, message: () => ""};
53
- },
54
- toBeExpr(input: string, reference: string): jest.CustomMatcherResult {
55
- const actual = KAS.parse(input)
56
- .expr.asExpr()
57
- .simplify()
58
- .normalize()
59
- .print();
60
- const expected = KAS.parse(reference).expr.normalize().print();
61
-
62
- return actual === expected
63
- ? {pass: true, message: () => ""}
64
- : {
65
- pass: false,
66
- message: () => `${input} as an expression is ${reference}`,
67
- };
68
- },
69
- });
70
-
71
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
72
- declare global {
73
- // eslint-disable-next-line @typescript-eslint/no-namespace
74
- namespace jest {
75
- interface Matchers<R> {
76
- toHaveGCD(expected: string): R;
77
- toBeSimplified(options?: any): R;
78
- toHaveConsts(expected: ReadonlyArray<string>, options?: any): R;
79
- toBeExpr(reference: string): R;
80
- }
81
- }
82
- }
83
-
84
- describe("KAS", () => {
85
- it("should parse", () => {
86
- const {expr} = KAS.parse("3x \\frac{42}{42} sin^2y");
87
-
88
- const result = expr.print();
89
-
90
- expect(result).toEqual("3*x*42/42*sin(y)^(2)");
91
- });
92
-
93
- it("should evaluate expressions", () => {
94
- const {expr} = KAS.parse("(x^2+y^2)^.5");
95
-
96
- const result = expr.eval({x: 3, y: 4});
97
-
98
- expect(result).toEqual(5);
99
- });
100
-
101
- it("should compare expressions", () => {
102
- const expr1 = KAS.parse("(1-x)(-1-6x)").expr;
103
- const expr2 = KAS.parse("(6x+1)(x-1)").expr;
104
-
105
- const {equal} = KAS.compare(expr1, expr2);
106
-
107
- expect(equal).toBeTrue();
108
- });
109
-
110
- it("should compare equations", () => {
111
- const eq1 = KAS.parse("2w+50/w=25").expr;
112
- const eq2 = KAS.parse("w(12.5-w)=25").expr;
113
-
114
- const {equal} = KAS.compare(eq1, eq2);
115
-
116
- expect(equal).toBeTrue();
117
- });
118
-
119
- it("can collect like terms", () => {
120
- const {expr} = KAS.parse("1+1+x+x+x+y");
121
-
122
- const result = expr.collect().print();
123
-
124
- expect(result).toEqual("2+3*x+y");
125
- });
126
-
127
- it("can simplify exp/log expressions", () => {
128
- const {expr} = KAS.parse("b^(2*y*log_b x)");
129
-
130
- const result = expr.collect().print();
131
-
132
- expect(result).toEqual("x^(2*y)");
133
- });
134
-
135
- it("can expand expressions", () => {
136
- const {expr} = KAS.parse("ab(c+d)e^f");
137
-
138
- const expansion = expr.expand().print();
139
-
140
- expect(expansion).toEqual("a*b*e^(f)*c+a*b*e^(f)*d");
141
- });
142
-
143
- it("can expand and factor expressions", () => {
144
- const {expr} = KAS.parse("ab(c+d)e^f");
145
-
146
- const factored = expr.expand().factor().print();
147
-
148
- expect(factored).toEqual("a*b*e^(f)*(c+d)");
149
- });
150
-
151
- it("can simplify complex expressions", () => {
152
- const {expr} = KAS.parse("((nx^5)^5)/(n^-2x^2)^-3");
153
-
154
- const simplified = expr.simplify().print();
155
-
156
- expect(simplified).toEqual("n^(-1)*x^(31)");
157
- });
158
-
159
- it("can simplify if more complex expressions", () => {
160
- const {expr} = KAS.parse(
161
- "(15np-25mp)/(15p^2-5p)+(20mp+10p^2)/(15p^2-5p)",
162
- );
163
-
164
- const simplified = expr.simplify().print();
165
-
166
- expect(simplified).toEqual("(-1+3*p)^(-1)*(3*n+-1*m+2*p)");
167
- });
168
-
169
- test("equation to expression", () => {
170
- const forms = [
171
- "y=2x-5",
172
- "-2x+5=-y",
173
- "2x-5<>y",
174
- "2x-y<>5",
175
- "(y+5)/2=x",
176
- "(y+5)/x=2",
177
- "1/2(y+5)=x",
178
- ".5(y+5)=x",
179
- "y-3=2(x-4)",
180
- "2y=4x-10",
181
- "yz=2xz-5z",
182
- ];
183
-
184
- _.each(forms, (form) => {
185
- if (form.includes("<>")) {
186
- expect(form).toBeExpr("-y+2x-5");
187
- } else {
188
- expect(form).toBeExpr("y-2x+5");
189
- }
190
- });
191
-
192
- const forms2 = ["1/3p-3=114", "1/3p=117", "p=351", "p-351=0"];
193
-
194
- _.each(forms2, function (form) {
195
- expect(form).toBeExpr("p-351");
196
- });
197
- });
198
-
199
- describe("findGCD", () => {
200
- test("findGCD on ints", () => {
201
- expect(["40", "30"]).toHaveGCD("10");
202
- expect(["14", "21"]).toHaveGCD("7");
203
- expect(["13", "26"]).toHaveGCD("13");
204
- });
205
-
206
- test("findGCD on rationals", () => {
207
- expect(["40/3", "55/6"]).toHaveGCD("5/6");
208
- expect(["3/4", "1/2"]).toHaveGCD("1/4");
209
- expect(["3/7", "12/22"]).toHaveGCD("3/77");
210
- expect(["3/10", "4/15"]).toHaveGCD("1/30");
211
- expect(["2/3", "23/6"]).toHaveGCD("1/6");
212
- expect(["2/3", "22/6"]).toHaveGCD("1/3");
213
-
214
- expect(["2/3", "2"]).toHaveGCD("2/3");
215
- expect(["2/3", "3"]).toHaveGCD("1/3");
216
- expect(["1/2", "4"]).toHaveGCD("1/2");
217
- expect(["4", "3/4"]).toHaveGCD("1/4");
218
- expect(["3", "3/4"]).toHaveGCD("3/4");
219
- });
220
-
221
- test("findGCD on floats", () => {
222
- // Feel free to change this when we do something other
223
- // than a naive "return 1" for floats
224
- expect(["1.23", "1.42"]).toHaveGCD("1");
225
- expect(["1", String(Math.PI)]).toHaveGCD("1");
226
- });
227
- });
228
-
229
- describe("isSimplified", () => {
230
- test("isSimplified (addition/subtraction)", () => {
231
- expect("a+b+c").toBeSimplified();
232
- expect("a-b-c").toBeSimplified();
233
- expect("a+b+c+c").not.toBeSimplified();
234
- expect("a-b-c-d-d+d").not.toBeSimplified();
235
- expect("x").toBeSimplified();
236
- expect("x+0").not.toBeSimplified();
237
- });
238
-
239
- test("isSimplified (multiplication/division/negation)", () => {
240
- expect("1/2").toBeSimplified();
241
- expect("2/1").not.toBeSimplified();
242
- expect("(2x)/(5x)").not.toBeSimplified();
243
- expect("-x").toBeSimplified();
244
- expect("-1*x").toBeSimplified();
245
- expect("--x").not.toBeSimplified();
246
- expect("x/1").not.toBeSimplified();
247
- expect("x/y").toBeSimplified();
248
- expect("xy/z").toBeSimplified();
249
- expect("-3x").toBeSimplified();
250
- expect("-x*3").toBeSimplified();
251
- expect("-x*3*y").toBeSimplified();
252
- expect("(x+1)/(2(x+1))").not.toBeSimplified();
253
- });
254
-
255
- test("isSimplified (exponentiation)", () => {
256
- expect("x^-1").toBeSimplified();
257
- expect("1/x").toBeSimplified();
258
- expect("1/x^-1").not.toBeSimplified();
259
- });
260
-
261
- test("isSimplified (logarithms)", () => {
262
- expect("ln(x)").toBeSimplified();
263
- expect("ln(x+y)").toBeSimplified();
264
-
265
- // Will only expand logarithms if leads to a simpler expression
266
- // TODO(alex): Combine all simplify(assert, ) and isSimplified(assert, ) tests!
267
- expect("ln(x/y)").toBeSimplified();
268
- expect("ln(x/y)+ln(y)").not.toBeSimplified();
269
- });
270
-
271
- test("isSimplified (equations)", () => {
272
- expect("x=10").toBeSimplified();
273
- expect("x=-10").toBeSimplified();
274
- expect("x=10y").toBeSimplified();
275
- expect("x=-10y").toBeSimplified();
276
- expect("x=10+y").toBeSimplified();
277
- expect("x=-10+y").toBeSimplified();
278
-
279
- expect("f(2x) = 10").toBeSimplified({functions: ["f"]});
280
- expect("f(x+x) = 10").not.toBeSimplified({functions: ["f"]});
281
- });
282
-
283
- test("isSimplified (equalities)", () => {
284
- expect("y=x").toBeSimplified();
285
- expect("y=x^2").toBeSimplified();
286
- expect("y=x*x").not.toBeSimplified();
287
-
288
- expect("y=x^2+1").toBeSimplified();
289
- expect("xy=x^2+1").toBeSimplified();
290
- expect("y+1=x^2+1").not.toBeSimplified();
291
-
292
- expect("xy=x^2").not.toBeSimplified();
293
- expect("x^2y=x^3").not.toBeSimplified();
294
- expect("alnx=blnx+clnx").not.toBeSimplified();
295
-
296
- expect("xy=0").toBeSimplified();
297
- expect("2xy=0").not.toBeSimplified();
298
- expect("xy/z=0").toBeSimplified();
299
- });
300
-
301
- test("isSimplified (inequalities)", () => {
302
- expect("y<x").toBeSimplified();
303
- expect("y<x^2").toBeSimplified();
304
- expect("y<x*x").not.toBeSimplified();
305
-
306
- expect("y<x^2+1").toBeSimplified();
307
- expect("xy<x^2+1").toBeSimplified();
308
- expect("y+1<x^2+1").not.toBeSimplified();
309
-
310
- expect("xy<x^2"); // x might be negativ.toBeSimplified();
311
- expect("x^2y<x^3").not.toBeSimplified();
312
- expect("alnx<blnx+clnx"); // lnx might be negativ.toBeSimplified();
313
-
314
- expect("xy<0").toBeSimplified();
315
- expect("2xy<0").not.toBeSimplified();
316
- expect("xy/z<0").toBeSimplified();
317
- });
318
-
319
- test("isSimplified (rational expressions)", () => {
320
- expect("3/4 x").toBeSimplified();
321
- expect("3/(4x)").toBeSimplified();
322
- expect("3/4 1/x").toBeSimplified();
323
-
324
- expect("(x+1)/(x+2)").toBeSimplified();
325
- expect("(x+1)/(2x+2)").not.toBeSimplified();
326
- expect("(2x+2)/(x+1)").not.toBeSimplified();
327
-
328
- expect("xy+2y=x+1").toBeSimplified();
329
- expect("y=(x+1)/(x+2)").toBeSimplified();
330
- expect("y/(x+1)=1/(x+2)").toBeSimplified();
331
-
332
- // TODO(alex): Combine all isSimplified(assert, ) and simplify(assert, ) tests!
333
- expect("y=(x+1)/(2x+2)").not.toBeSimplified();
334
- expect("y=1/2").toBeSimplified();
335
-
336
- expect("y=(2x+2)/(x+1)").not.toBeSimplified();
337
- expect("y=2").toBeSimplified();
338
-
339
- // same denominators (adding_and_subtracting_rational_expressions_1.5)
340
- expect(
341
- "(15np-25mp)/(15p^2-5p)+(20mp+10p^2)/(15p^2-5p)",
342
- ).not.toBeSimplified();
343
- expect("(3n-m+2p)/(3p-1)").toBeSimplified();
344
-
345
- expect("5yx/(2x^2+4yx)-(2x^2-6yx)/(2x^2+4yx)").not.toBeSimplified();
346
- expect("(11y-2x)/(2x+4y)").toBeSimplified();
347
-
348
- expect("(25m^2-20pm)/(5pm+5m)-20m^2/(5pm+5m)").not.toBeSimplified();
349
- expect("(m-4p)/(p+1)").toBeSimplified();
350
-
351
- // pathological examples
352
- expect(" (a + b)/(2c+2d)").toBeSimplified();
353
- expect(" (3a+3b)/(2c+2d)").toBeSimplified();
354
- expect(" (2a+2b)/( c+ d)").toBeSimplified();
355
- expect(" (2a+2b)/(3c+3d)").toBeSimplified();
356
- expect(" (2a+2b)/(4c+4d)").not.toBeSimplified();
357
- expect(" (4a+4b)/(2c+2d)").not.toBeSimplified();
358
- expect("y=(a + b)/(2c+2d)").toBeSimplified();
359
- expect("y=(3a+3b)/(2c+2d)").toBeSimplified();
360
- expect("y=(2a+2b)/( c+ d)").toBeSimplified();
361
- expect("y=(2a+2b)/(3c+3d)").toBeSimplified();
362
- expect("y=(2a+2b)/(4c+4d)").not.toBeSimplified();
363
- expect("y=(4a+4b)/(2c+2d)").not.toBeSimplified();
364
- });
365
-
366
- test("getConsts", () => {
367
- expect("4").toHaveConsts([]);
368
- expect("4x").toHaveConsts([]);
369
- expect("4pi").toHaveConsts(["pi"]);
370
- expect("4e").toHaveConsts(["e"]);
371
- expect("4pi+e").toHaveConsts(["e", "pi"]);
372
- expect("4pi+pi").toHaveConsts(["pi"]);
373
- expect("4cos(pi)").toHaveConsts(["pi"]);
374
- expect("4cos(xpi)").toHaveConsts(["pi"]);
375
- expect("(4x)/(2pi)").toHaveConsts(["pi"]);
376
- expect("4sec(x)").toHaveConsts([]);
377
- });
378
- });
379
- });