@khanacademy/kas 0.3.6 → 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,286 +0,0 @@
1
- import _ from "underscore";
2
-
3
- import * as KAS from "../index";
4
-
5
- expect.extend({
6
- toRenderTex(
7
- input: string,
8
- expected: string,
9
- options?: any,
10
- ): jest.CustomMatcherResult {
11
- const actual = KAS.parse(input, options).expr.tex();
12
-
13
- if (actual !== expected) {
14
- return {
15
- pass: false,
16
- message: () => `${input} renders as ${expected}`,
17
- };
18
- }
19
-
20
- return {pass: !this.isNot, message: () => ""};
21
- },
22
- toRenderTexOpt(
23
- input: string,
24
- expected: string,
25
- ...optlist: ReadonlyArray<any>
26
- ) {
27
- const options = {
28
- display: false,
29
- dynamic: false,
30
- times: false,
31
- } as const;
32
- _.each(optlist, function (opt) {
33
- options[opt] = true;
34
- });
35
-
36
- const actual = KAS.parse(input).expr.asTex(options);
37
-
38
- if (actual !== expected) {
39
- return {
40
- pass: false,
41
- message: () => `${input} renders with options as ${expected}`,
42
- };
43
- }
44
-
45
- return {pass: true, message: () => ""};
46
- },
47
- });
48
-
49
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
50
- declare global {
51
- // eslint-disable-next-line @typescript-eslint/no-namespace
52
- namespace jest {
53
- interface Matchers<R> {
54
- toRenderTex(expected: string, options?: any): R;
55
- toRenderTexOpt(expected: string, ...optlist: ReadonlyArray<any>): R;
56
- }
57
- }
58
- }
59
-
60
- describe("rendering", () => {
61
- test("positive and negative primitives", () => {
62
- expect("0").toRenderTex("0");
63
- expect("-1").toRenderTex("-1");
64
- expect("--1").toRenderTex("--1");
65
- expect("-2").toRenderTex("-2");
66
- expect("--2").toRenderTex("--2");
67
- expect("x").toRenderTex("x");
68
- expect("theta").toRenderTex("\\theta");
69
- expect("1/2").toRenderTex("\\frac{1}{2}");
70
- expect("-1/2").toRenderTex("-\\frac{1}{2}");
71
- expect("1/-2").toRenderTex("-\\frac{1}{2}");
72
- expect("-1/-2").toRenderTex("--\\frac{1}{2}");
73
- });
74
-
75
- test("addition", () => {
76
- expect("1-2").toRenderTex("1-2");
77
- expect("a+b").toRenderTex("a+b");
78
- expect("a-b").toRenderTex("a-b");
79
- expect("a-1b").toRenderTex("a-1b");
80
- expect("a+-b").toRenderTex("a+-b");
81
- expect("a+-1b").toRenderTex("a+-1b");
82
- });
83
-
84
- test("multiplication", () => {
85
- expect("ab").toRenderTex("ab");
86
- expect("a*b").toRenderTex("ab");
87
- expect("a/b").toRenderTex("\\frac{a}{b}");
88
- expect("a/bc/d").toRenderTex("\\frac{ac}{bd}");
89
-
90
- expect("1/(x+y)").toRenderTex("\\frac{1}{x+y}");
91
- expect("2/(x+y)").toRenderTex("\\frac{2}{x+y}");
92
- expect("(z+2)/(x+y)").toRenderTex("\\frac{z+2}{x+y}");
93
- expect("(z+2)/4").toRenderTex("\\frac{z+2}{4}");
94
- });
95
-
96
- test("rational expressions", () => {
97
- expect("x+1/2").toRenderTex("x+\\frac{1}{2}");
98
- expect("x-1/2").toRenderTex("x-\\frac{1}{2}");
99
-
100
- expect("1/2x").toRenderTex("\\frac{1}{2}x");
101
- expect("1/2x/y").toRenderTex("\\frac{1}{2}\\frac{x}{y}");
102
- expect("5*1/2x/y").toRenderTex("\\frac{1}{2}\\frac{5x}{y}");
103
- expect("1/2*4*x/y").toRenderTex("\\frac{1}{2}\\frac{4x}{y}");
104
- expect("-1/2x").toRenderTex("-\\frac{1}{2}x");
105
- expect("a-1/2x").toRenderTex("a-\\frac{1}{2}x");
106
-
107
- expect("1/(2x)").toRenderTex("\\frac{1}{2x}");
108
- expect("8/(7p^4)").toRenderTex("\\frac{8}{7p^{4}}");
109
-
110
- expect("x/2").toRenderTex("\\frac{x}{2}");
111
- expect("1x/2").toRenderTex("\\frac{1x}{2}");
112
- expect("-x/2").toRenderTex("-\\frac{x}{2}");
113
- expect("x/-2").toRenderTex("-\\frac{x}{2}");
114
- expect("x/-2/-3").toRenderTex("--\\frac{x}{2 \\cdot 3}");
115
- expect("--x/2/3").toRenderTex("--\\frac{x}{2 \\cdot 3}");
116
- expect("a-x/2").toRenderTex("a-\\frac{x}{2}");
117
-
118
- expect("1*-2").toRenderTex("1 \\cdot -2");
119
- expect("1*-2*3").toRenderTex("1 \\cdot -2 \\cdot 3");
120
- expect("1*-2*3/4").toRenderTex("1 \\cdot -2 \\cdot \\frac{3}{4}");
121
- expect("1*-2*3/4/5").toRenderTex(
122
- "1 \\cdot -2 \\cdot \\frac{3}{4} \\cdot \\frac{1}{5}",
123
- );
124
- expect("1/2*1/2").toRenderTex("\\frac{1}{2} \\cdot \\frac{1}{2}");
125
- });
126
-
127
- test("exponentiation", () => {
128
- expect("x^y").toRenderTex("x^{y}");
129
- expect("xy^z").toRenderTex("xy^{z}");
130
- expect("(xy)^z").toRenderTex("(xy)^{z}");
131
- expect("(x+y)^z").toRenderTex("(x+y)^{z}");
132
- expect("x^(yz)").toRenderTex("x^{yz}");
133
- expect("x^-(yz)").toRenderTex("x^{-yz}");
134
- expect("x^(y+z)").toRenderTex("x^{y+z}");
135
- expect("x^-(y+z)").toRenderTex("x^{-(y+z)}");
136
- expect("(x^y)^z").toRenderTex("(x^{y})^{z}");
137
- expect("pir^2").toRenderTex("\\pi r^{2}");
138
- });
139
-
140
- test("square root", () => {
141
- expect("sqrt(x)").toRenderTex("\\sqrt{x}");
142
- expect("sqrt(x)y").toRenderTex("\\sqrt{x}y");
143
- expect("1/sqrt(x)").toRenderTex("\\frac{1}{\\sqrt{x}}");
144
- expect("1/sqrt(x)y").toRenderTex("\\frac{y}{\\sqrt{x}}");
145
-
146
- expect("sqrt(2)/2").toRenderTex("\\frac{\\sqrt{2}}{2}");
147
- expect("sqrt(2)^2").toRenderTex("(\\sqrt{2})^{2}");
148
- });
149
-
150
- test("nth root", () => {
151
- // This is an unfortunate case, but only nth degree roots with integer
152
- // n's get nicely printed as tex.
153
- expect("sqrt[z]{x}").toRenderTex("x^{\\frac{1}{z}}");
154
-
155
- expect("sqrt[3]{x}").toRenderTex("\\sqrt[3]{x}");
156
- expect("sqrt[3]{x}z").toRenderTex("\\sqrt[3]{x}z");
157
- expect("1/sqrt[4]{x}").toRenderTex("\\frac{1}{\\sqrt[4]{x}}");
158
- expect("1/sqrt[4]{x}y").toRenderTex("\\frac{y}{\\sqrt[4]{x}}");
159
-
160
- expect("sqrt[9]{2}/2").toRenderTex("\\frac{\\sqrt[9]{2}}{2}");
161
- expect("sqrt[9]{2}^2").toRenderTex("(\\sqrt[9]{2})^{2}");
162
- });
163
-
164
- test("absolute value", () => {
165
- expect("|x|").toRenderTex("\\left|x\\right|");
166
- expect("|x|y").toRenderTex("\\left|x\\right|y");
167
- });
168
-
169
- test("logarithms", () => {
170
- expect("lnx").toRenderTex("\\ln(x)");
171
- expect("logx").toRenderTex("\\log_{10}(x)");
172
- expect("lnx^y").toRenderTex("\\ln(x^{y})");
173
- expect("logx^y").toRenderTex("\\log_{10}(x^{y})");
174
- expect("(lnx)^y").toRenderTex("[\\ln(x)]^{y}");
175
- expect("(logx)^y").toRenderTex("[\\log_{10}(x)]^{y}");
176
- });
177
-
178
- test("trig functions", () => {
179
- expect("sinx").toRenderTex("\\sin(x)");
180
-
181
- expect("arcsin x").toRenderTex("\\arcsin(x)");
182
- expect("sin^-1 x").toRenderTex("\\arcsin(x)");
183
-
184
- expect("(sinx)^2").toRenderTex("\\sin^{2}(x)");
185
- expect("sin^2 x").toRenderTex("\\sin^{2}(x)");
186
-
187
- expect("1/(sinx)^2").toRenderTex("\\frac{1}{\\sin^{2}(x)}");
188
- expect("1/sin^2x").toRenderTex("\\frac{1}{\\sin^{2}(x)}");
189
-
190
- expect("sin^2 x + cos^2 x = 1").toRenderTex(
191
- "\\sin^{2}(x)+\\cos^{2}(x) = 1",
192
- );
193
- });
194
-
195
- test("hyperbolic functions", () => {
196
- expect("sinhx").toRenderTex("\\sinh(x)");
197
- expect("sinh^2 x").toRenderTex("\\sinh^{2}(x)");
198
- });
199
-
200
- test("multiplication with numbers", () => {
201
- expect("4*10").toRenderTex("4 \\cdot 10");
202
- expect("10^5").toRenderTex("10^{5}");
203
- expect("4*10^5").toRenderTex("4 \\cdot 10^{5}");
204
- expect("10^5x").toRenderTex("10^{5}x");
205
- expect("4*10^5x").toRenderTex("4 \\cdot 10^{5}x");
206
- expect("x*(10+4)^5").toRenderTex("x(10+4)^{5}");
207
-
208
- expect("-1*2").toRenderTex("-1 \\cdot 2");
209
- expect("1*-2").toRenderTex("1 \\cdot -2");
210
- expect("-1*-2").toRenderTex("-1 \\cdot -2");
211
- expect("-1*2*3").toRenderTex("-1 \\cdot 2 \\cdot 3");
212
- });
213
-
214
- test("inverses and division", () => {
215
- expect("x^-1").toRenderTex("x^{-1}");
216
- expect("2x^-1").toRenderTex("2x^{-1}");
217
- expect("1/x").toRenderTex("\\frac{1}{x}");
218
- expect("-1/x").toRenderTex("\\frac{-1}{x}");
219
- expect("2/x").toRenderTex("\\frac{2}{x}");
220
- expect("1/x^2").toRenderTex("\\frac{1}{x^{2}}");
221
- expect("2/x^2").toRenderTex("\\frac{2}{x^{2}}");
222
- expect("1/1/x").toRenderTex("\\frac{1}{x}");
223
- expect("1/(1/x)").toRenderTex("\\frac{1}{\\frac{1}{x}}");
224
- expect("1/x/x").toRenderTex("\\frac{1}{xx}");
225
- expect("1/(x/x)").toRenderTex("\\frac{1}{\\frac{x}{x}}");
226
- expect("-1/1/x").toRenderTex("\\frac{-1}{x}");
227
- expect("-1/(1/x)").toRenderTex("\\frac{-1}{\\frac{1}{x}}");
228
- expect("-1/x/x").toRenderTex("\\frac{-1}{xx}");
229
- expect("-1/(x/x)").toRenderTex("\\frac{-1}{\\frac{x}{x}}");
230
- });
231
-
232
- test("distributive property", () => {
233
- expect("ab+c").toRenderTex("ab+c");
234
- expect("ab+ac").toRenderTex("ab+ac");
235
- expect("a(b+c)").toRenderTex("a(b+c)");
236
- });
237
-
238
- test("numerical exponents", () => {
239
- expect("9^4").toRenderTex("9^{4}");
240
- expect("-9^4").toRenderTex("-9^{4}");
241
- expect("1-9^4").toRenderTex("1-9^{4}");
242
- });
243
-
244
- test("negating a Mul", () => {
245
- expect("-3x").toRenderTex("-3x");
246
- expect("--3x").toRenderTex("--3x");
247
- expect("-x*3").toRenderTex("-3x");
248
- expect("--x*3").toRenderTex("--3x");
249
- });
250
-
251
- test("equations", () => {
252
- expect("y=x").toRenderTex("y = x");
253
- expect("y<x").toRenderTex("y < x");
254
- expect("y>x").toRenderTex("y > x");
255
- expect("y<>x").toRenderTex("y \\ne x");
256
- expect("y=/=x").toRenderTex("y \\ne x");
257
- expect("y<=x").toRenderTex("y \\le x");
258
- expect("y>=x").toRenderTex("y \\ge x");
259
- });
260
-
261
- test("function variables", () => {
262
- expect("f(x)").toRenderTex("fx");
263
- expect("f(x)").toRenderTex("f(x)", {functions: ["f"]});
264
- expect("f(sin x)").toRenderTex("f(\\sin(x))", {functions: ["f"]});
265
- expect("sin f(x)").toRenderTex("\\sin(f(x))", {functions: ["f"]});
266
- });
267
-
268
- test("options", () => {
269
- expect("x").toRenderTexOpt("x");
270
- expect("x").toRenderTexOpt("\\displaystyle x", "display");
271
- expect("a(b+c(d+e))").toRenderTexOpt("a(b+c(d+e))");
272
- expect("a(b+c(d+e))").toRenderTexOpt(
273
- "a\\left(b+c\\left(d+e\\right)\\right)",
274
- "dynamic",
275
- );
276
- expect("2*2").toRenderTexOpt("2 \\cdot 2");
277
- expect("2*2").toRenderTexOpt("2 \\times 2", "times");
278
-
279
- expect("1*2(3+4)").toRenderTexOpt(
280
- "\\displaystyle 1 \\times 2\\left(3+4\\right)",
281
- "display",
282
- "dynamic",
283
- "times",
284
- );
285
- });
286
- });
@@ -1,348 +0,0 @@
1
- import _ from "underscore";
2
-
3
- import * as KAS from "../index";
4
-
5
- expect.extend({
6
- toFactorAs(input: string, reference: string): jest.CustomMatcherResult {
7
- const actual = KAS.parse(input).expr.factor().normalize().repr();
8
- const expected = KAS.parse(reference).expr.normalize().repr();
9
-
10
- return actual === expected
11
- ? {pass: true, message: () => ""}
12
- : {pass: false, message: () => `${input} factors as ${reference}`};
13
- },
14
- toExpandAs(input: string, reference: string): jest.CustomMatcherResult {
15
- const actual = KAS.parse(input).expr.expand().normalize().print();
16
- const expected = KAS.parse(reference).expr.normalize().print();
17
-
18
- return actual === expected
19
- ? {pass: true, message: () => ""}
20
- : {pass: false, message: () => `${input} expands as ${reference}`};
21
- },
22
- toExpandAsRepr(input: string, reference: string): jest.CustomMatcherResult {
23
- const actual = KAS.parse(input).expr.expand().repr();
24
-
25
- return actual === reference
26
- ? {pass: true, message: () => ""}
27
- : {pass: false, message: () => `${input} expands as ${reference}`};
28
- },
29
- toExpandAsTex(input: string, reference: string): jest.CustomMatcherResult {
30
- const actual = KAS.parse(input).expr.expand().tex();
31
-
32
- return actual === reference
33
- ? {pass: true, message: () => ""}
34
- : {pass: false, message: () => `${input} expands as ${reference}`};
35
- },
36
- toCollectAs(input: string, reference: string): jest.CustomMatcherResult {
37
- const actual = KAS.parse(input).expr.collect().normalize().print();
38
- const expected = KAS.parse(reference)
39
- .expr.collect()
40
- .normalize()
41
- .print();
42
-
43
- return actual === expected
44
- ? {pass: true, message: () => ""}
45
- : {pass: false, message: () => `${input} collects as ${reference}`};
46
- },
47
- toCollectAsRepr(
48
- input: string,
49
- reference: string,
50
- ): jest.CustomMatcherResult {
51
- const actual = KAS.parse(input).expr.collect().repr();
52
-
53
- return actual === reference
54
- ? {pass: true, message: () => ""}
55
- : {pass: false, message: () => `${input} collects as ${reference}`};
56
- },
57
- toCollectAsTex(input: string, reference: string): jest.CustomMatcherResult {
58
- const actual = KAS.parse(input).expr.collect().tex();
59
-
60
- return actual === reference
61
- ? {pass: true, message: () => ""}
62
- : {pass: false, message: () => `${input} collects as ${reference}`};
63
- },
64
- toSimplifyAs(input: string, reference: string): jest.CustomMatcherResult {
65
- const actual = KAS.parse(input).expr.simplify().normalize().print();
66
- const expected = KAS.parse(reference).expr.normalize().print();
67
-
68
- return actual === expected
69
- ? {pass: true, message: () => ""}
70
- : {
71
- pass: false,
72
- message: () => `${input} simplifies as ${reference}`,
73
- };
74
- },
75
- });
76
-
77
- // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
78
- declare global {
79
- // eslint-disable-next-line @typescript-eslint/no-namespace
80
- namespace jest {
81
- interface Matchers<R> {
82
- toFactorAs(reference: string): R;
83
- toExpandAs(reference: string): R;
84
- toExpandAsRepr(reference: string): R;
85
- toExpandAsTex(reference: string): R;
86
- toCollectAs(reference: string): R;
87
- toCollectAsRepr(reference: string): R;
88
- toCollectAsTex(reference: string): R;
89
- toSimplifyAs(reference: string): R;
90
- }
91
- }
92
- }
93
-
94
- describe("transforming", () => {
95
- test("factoring Adds", () => {
96
- expect("2+2").toFactorAs("2(1+1)");
97
- expect("-2-2").toFactorAs("-2(1+1)");
98
- expect("2x+2").toFactorAs("2(x+1)");
99
- expect("x^3+x^2").toFactorAs("x^2(x+1)");
100
- expect("2x+xy").toFactorAs("x(2+y)");
101
- expect("2xy+xy^2").toFactorAs("xy(2+y)");
102
- expect("2+2/3").toFactorAs("2/3(3+1)"); // a little questionable, but 2/3 is what
103
- // wolframalpha returns for the gcd, so
104
- // we pull it out
105
- expect("2x+1.1").toFactorAs("2x+1.1");
106
- });
107
-
108
- test("factoring Muls", () => {
109
- expect("(2x+2)/(x+1)").toFactorAs("2 (x+1)/(x+1)");
110
- expect("(x+1)/(2x+2)").toFactorAs("1/2 (x+1)/(x+1)");
111
- });
112
-
113
- test("factoring Pows", () => {
114
- expect("x^y+x^(2y)").toFactorAs("x^y(1+x^y)");
115
- expect("x^y+x^z").toFactorAs("x^y+x^z");
116
- });
117
-
118
- test("distribute over multiplication", () => {
119
- expect("a(b+c)").toExpandAs("ab+ac");
120
- expect("a(b+c)").toExpandAsTex("ab+ac");
121
- expect("a(b+c)").toExpandAsRepr(
122
- "Add(Mul(Var(a),Var(b)),Mul(Var(a),Var(c)))",
123
- );
124
-
125
- expect("a(b-c)").toExpandAs("ab-ac");
126
- expect("a(b-c)").toExpandAsTex("ab-ac");
127
- expect("a(b-c)").toExpandAsRepr(
128
- "Add(Mul(Var(a),Var(b)),Mul(Var(a),-1,Var(c)))",
129
- );
130
-
131
- expect("a(b+c)d").toExpandAs("abd+acd");
132
- expect("a(b+c)d").toExpandAsRepr(
133
- "Add(Mul(Var(a),Var(d),Var(b)),Mul(Var(a),Var(d),Var(c)))",
134
- );
135
-
136
- expect("(a+b)(c+d)").toExpandAs("ac+ad+bc+bd");
137
- expect("(a+b)(c+d)ef").toExpandAs("acef+adef+bcef+bdef");
138
- expect("(a+b)c^d").toExpandAs("ac^d+bc^d");
139
- expect("ab(c+d)e^f").toExpandAs("abce^f+abde^f");
140
-
141
- expect("(a+b(c+d))e").toExpandAs("ae+bce+bde");
142
- expect("(a+b(c+d))e").toExpandAsRepr(
143
- "Add(Mul(Const(e),Var(a)),Mul(Const(e),Var(b),Var(c)),Mul(Const(e),Var(b),Var(d)))",
144
- );
145
- });
146
-
147
- test("distribute over rational expressions", () => {
148
- expect("(a+b)/(c+d)").toExpandAs("(a+b)/(c+d)");
149
- expect("(a+b)/(c+d)*a").toExpandAs("(aa+ab)/(c+d)");
150
- expect("(a+b)/(c+d)*1/e").toExpandAs("(a+b)/(ce+de)");
151
- expect("(a+b)/(c+d)*a/e").toExpandAs("(aa+ab)/(ce+de)");
152
- });
153
-
154
- test("expand exponentiation", () => {
155
- expect("(ab)^2").toExpandAs("a^2 b^2");
156
- expect("2*(ab)^2").toExpandAs("2 a^2 b^2");
157
- expect("(a+b)^2").toExpandAs("a^2+2ab+b^2");
158
-
159
- expect("(ab)^-2").toExpandAs("a^-2 b^-2");
160
- expect("2*(ab)^-2").toExpandAs("2 a^-2 b^-2");
161
- expect("(a+b)^-2").toExpandAs("(a^2+2ab+b^2)^-1");
162
- });
163
-
164
- test("expand absolute value", () => {
165
- expect("|a+b|").toExpandAs("|a+b|");
166
- expect("|ab|").toExpandAs("|a|*|b|");
167
- });
168
-
169
- test("expand logarithms", () => {
170
- expect("ln(xy)").toExpandAs("lnx+lny");
171
- expect("log_b(x)").toExpandAs("lnx/lnb");
172
- expect("log_b(xy)").toExpandAs("lnx/lnb+lny/lnb");
173
- expect("ln(xy/z)").toExpandAs("lnx+lny-lnz");
174
-
175
- expect("ln(x^y)").toExpandAs("ylnx");
176
- expect("log_b(x^y)").toExpandAs("ylnx/lnb");
177
- expect("ln(x^y^z)").toExpandAs("y^zlnx");
178
-
179
- expect("ln(x^y/z)").toExpandAs("ylnx-lnz");
180
-
181
- // ln((xy)^z) -> ln(x^z*y^z) -> z*ln(x)+z*ln(y)
182
- expect("ln((xy)^z)").toExpandAs("zlnx+zlny");
183
-
184
- expect("log_b(x)log_x(y)").toExpandAs("ln(x)/ln(b)*ln(y)/ln(x)");
185
- expect("log_b(x)log_x(y)log_y(z)").toExpandAs(
186
- "ln(x)/ln(b)*ln(y)/ln(x)*ln(z)/ln(y)",
187
- );
188
- });
189
-
190
- test("expand trig functions", () => {
191
- expect("sin(x)").toExpandAs("sin(x)");
192
- expect("cos(x)").toExpandAs("cos(x)");
193
- expect("tan(x)").toExpandAs("sin(x)/cos(x)");
194
- expect("csc(x)").toExpandAs("1/sin(x)");
195
- expect("sec(x)").toExpandAs("1/cos(x)");
196
- expect("cot(x)").toExpandAs("cos(x)/sin(x)");
197
- });
198
-
199
- test("expand hyperbolic functions", () => {
200
- expect("sinh(x)").toExpandAs("sinh(x)");
201
- expect("cosh(x)").toExpandAs("cosh(x)");
202
- expect("tanh(x)").toExpandAs("sinh(x)/cosh(x)");
203
- expect("csch(x)").toExpandAs("1/sinh(x)");
204
- expect("sech(x)").toExpandAs("1/cosh(x)");
205
- expect("coth(x)").toExpandAs("cosh(x)/sinh(x)");
206
- });
207
-
208
- test("collect over addition", () => {
209
- expect("").toCollectAs("0");
210
- expect("0").toCollectAs("0");
211
- expect("1+3").toCollectAs("4");
212
- expect("x+3").toCollectAs("3+x");
213
- expect("x+3x").toCollectAs("4x");
214
- expect("x+3x").toCollectAsRepr("Mul(4,Var(x))");
215
- expect("a+a+a").toCollectAs("3a");
216
- expect("a+a+a").toCollectAsRepr("Mul(3,Var(a))");
217
- expect("a+b+b+c").toCollectAs("a+2b+c");
218
- expect("a+b+b+c").toCollectAsRepr("Add(Var(a),Mul(2,Var(b)),Var(c))");
219
- expect("4x^2-x^2+8x+7-5x-4").toCollectAs("3+3x+3x^2");
220
- });
221
-
222
- test("collect over multiplication", () => {
223
- expect("5*7").toCollectAs("35");
224
- expect("5*7x+20x").toCollectAs("55x");
225
- expect("3x*xy+2yx^2").toCollectAs("5x^2y");
226
- expect("4/6").toCollectAs("2/3");
227
- expect("1/1").toCollectAs("1");
228
- expect("1/2+1/3").toCollectAs("5/6");
229
- expect("1/2+1/3+1").toCollectAs("11/6");
230
- expect("1.2+1/2").toCollectAs("1.7");
231
- expect("1/2-1/2").toCollectAs("0");
232
- expect("1/2-.5").toCollectAs("0");
233
- });
234
-
235
- test("collect over exponentiation", () => {
236
- expect("x^0").toCollectAs("1");
237
- expect("x^1").toCollectAs("x");
238
- expect("x^(log_x y)").toCollectAs("y");
239
- expect("(x^y)^z").toCollectAs("x^(yz)");
240
- expect("0^0").toCollectAs("1");
241
- expect("4^1.5").toCollectAs("8");
242
- expect("(2/3)^2").toCollectAs("4/9");
243
- expect("(2/3)^-2").toCollectAs("9/4");
244
- });
245
-
246
- test("collect over roots", () => {
247
- expect("sqrt(2)^2").toCollectAs("2");
248
- expect("sqrt[3]{3}^3").toCollectAs("3");
249
- expect("(2^(1/3))^3").toCollectAs("2");
250
- });
251
-
252
- test("collect over absolute value", () => {
253
- expect("|x|").toCollectAs("|x|");
254
- expect("|2|").toCollectAs("2");
255
- expect("|0|").toCollectAs("0");
256
- expect("|-2|").toCollectAs("2");
257
- expect("|pi|").toCollectAs("pi");
258
- expect("|2^x|").toCollectAs("2^x");
259
- expect("|x^2|").toCollectAs("x^2");
260
- expect("|-2pix^2y^3|").toCollectAs("2pix^2*|y^3|");
261
- });
262
-
263
- test("collect over logarithms", () => {
264
- expect("log(1)").toCollectAs("0");
265
- expect("log_x(x)").toCollectAs("1");
266
- expect("log_b(b^x)").toCollectAs("x");
267
-
268
- expect("b^(2*y*log_b x)").toCollectAs("x^(2y)");
269
- expect("b^(log_b a) b^(-log_b c)").toCollectAs("a/c");
270
-
271
- expect("ln(x)/ln(b)").toCollectAs("log_b(x)");
272
- expect("ln(x)/ln(b)*ln(y)/ln(x)").toCollectAs("log_b(y)");
273
- expect("ln(x)/ln(b)*ln(y)/ln(x)*ln(z)/ln(y)").toCollectAs("log_b(z)");
274
- });
275
-
276
- test("collect trig functions", () => {
277
- expect("sin(x)cos(x)").toCollectAs("sin(x)cos(x)");
278
- expect("sin(x)/cos(x)").toCollectAs("tan(x)");
279
-
280
- expect("sin^2(x)/cos^2(x)").toCollectAs("tan^2(x)");
281
- expect("cos^2(x)/sin^2(x)").toCollectAs("cot^2(x)");
282
-
283
- expect("sin^-2(x)/cos^-2(x)").toCollectAs("cot^2(x)");
284
- expect("cos^-2(x)/sin^-2(x)").toCollectAs("tan^2(x)");
285
-
286
- expect("sin^--2(x)/cos^--2(x)").toCollectAs("tan^2(x)");
287
- expect("cos^--2(x)/sin^--2(x)").toCollectAs("cot^2(x)");
288
-
289
- expect("sin^2(x)/cos(x)").toCollectAs("sin^2(x)/cos(x)");
290
- expect("sin(x)/cos^2(x)").toCollectAs("sin(x)/cos^2(x)");
291
-
292
- expect("sin(-x)").toCollectAs("-sin(x)");
293
- expect("cos(-x)").toCollectAs("cos(x)");
294
- expect("tan(-x)").toCollectAs("-tan(x)");
295
- expect("csc(-x)").toCollectAs("-csc(x)");
296
- expect("sec(-x)").toCollectAs("sec(x)");
297
- expect("cot(-x)").toCollectAs("-cot(x)");
298
-
299
- expect("sin(--x)").toCollectAs("sin(x)");
300
- expect("arcsin(-x)").toCollectAs("arcsin(-x)");
301
-
302
- expect("sin(-x)cos(-x)").toCollectAs("-sin(x)cos(x)");
303
- expect("sin(-x)/cos(-x)").toCollectAs("-tan(x)");
304
- });
305
-
306
- test("collect then output tex", () => {
307
- // user-friendly tex representation is not guaranteed after collect(assert, )
308
- expect("-x").toCollectAs("-1x");
309
- expect("-x").toCollectAsTex("-1x");
310
- expect("a-b").toCollectAs("a+-1b");
311
- expect("a-b").toCollectAsTex("a+-1b");
312
- expect("a/b").toCollectAs("ab^-1");
313
- expect("a/b").toCollectAsTex("ab^{-1}");
314
- });
315
-
316
- test("collect over an equation", () => {
317
- // collect does not try to collect across both sides of an equation
318
- expect("y+1-1=x*x").toCollectAs("y=x^(2)");
319
- expect("1+y=1+x^2").toCollectAs("1+y=1+x^(2)");
320
- });
321
-
322
- test("simplify", () => {
323
- expect("(a+b)^2").toSimplifyAs("(a+b)^2");
324
- expect("(a+b)(a+b)").toSimplifyAs("(a+b)^2");
325
-
326
- // (ab)^2 ->[factor]-> a^2 * b^2 ->[collect]-> a^2 * b^2
327
- // (no change during collect, therefore factoring is rolled back)
328
- expect("(ab)^2").toSimplifyAs("(ab)^2");
329
-
330
- // (3x)^2 ->[factor]-> 3^2 * x^2 ->[collect]-> 9x^2
331
- // (changed during collect, therefore factoring persists)
332
- expect("(3x)^2").toSimplifyAs("9x^2");
333
-
334
- expect("(2sqrt(2))^4").toSimplifyAs("64");
335
- expect("(3sqrt[3]{3})^9").toSimplifyAs("531441");
336
-
337
- // from "Simplifying expressions with exponents"
338
- expect("((nx^5)^5)").toSimplifyAs("n^5 x^25");
339
- expect("((nx^5)^5)/2").toSimplifyAs("1/2 n^5 x^25");
340
- expect("((nx^5)^5)/(n^-2x^2)^-3").toSimplifyAs("n^-1 x^31");
341
-
342
- expect("1/(xya)+1/(xyb)").toSimplifyAs("1/(xya)+1/(xyb)");
343
-
344
- // Simplify rationals correctly
345
- expect("2*(x+1/3)").toSimplifyAs("2*x+2/3");
346
- expect("-1*(1/3+x)").toSimplifyAs("-1*x+-1/3");
347
- });
348
- });