Skip to content

Commit 3e4350d

Browse files
authored
Merge pull request #44 from pie-framework/beta
feat: linear-equation-equivalence
2 parents 73b69a3 + 6311ece commit 3e4350d

12 files changed

Lines changed: 516 additions & 64 deletions

.DS_Store

0 Bytes
Binary file not shown.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
"@semantic-release/github": "^7.2.3",
3737
"@semantic-release/npm": "^7.1.3",
3838
"@semantic-release/release-notes-generator": "^9.0.3",
39-
"prettier": "^2.3.2",
4039
"@types/fs-extra": "^9.0.1",
4140
"@types/glob": "^7.1.3",
4241
"@types/jest": "^26.0.14",
@@ -53,6 +52,7 @@
5352
"latex-utensils": "^2.0.3",
5453
"latex.js": "^0.12.4",
5554
"minimist": "^1.2.5",
55+
"prettier": "^2.3.2",
5656
"rollup": "^2.50.2",
5757
"rollup-plugin-cleanup": "^3.2.1",
5858
"semantic-release": "^17.4.7",
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { AstToMathJs } from "../conversion/ast-to-mathjs";
2+
import { LatexToAst } from "../conversion/latex-to-ast";
3+
import {
4+
getUnknowns,
5+
getCoefficients,
6+
setXToOne,
7+
solveLinearEquation,
8+
} from "../symbolic/compare-equations";
9+
10+
const lta = new LatexToAst();
11+
const atm = new AstToMathJs();
12+
13+
describe("getUnknowns", () => {
14+
it.each`
15+
expression | unknowns
16+
${"x"} | ${["x"]}
17+
${"x +1"} | ${["x"]}
18+
${"((x^2 + x) / x) - 1"} | ${["x"]}
19+
${"1+2"} | ${[]}
20+
${"a +1+c"} | ${["a", "c"]}
21+
${"((y^2 + z) / x) - 1"} | ${["x", "y", "z"]}
22+
${"109h"} | ${["h"]}
23+
${"m+n+10"} | ${["m", "n"]}
24+
`("$expression => $unknowns", ({ expression, unknowns }) => {
25+
const equation = atm.convert(lta.convert(expression));
26+
const unknownsName = getUnknowns(equation);
27+
28+
expect(unknownsName).toEqual(unknowns);
29+
});
30+
});
31+
32+
describe("getCoefficients", () => {
33+
it.each`
34+
expression | coefficients
35+
${"x+0"} | ${[0, 1]}
36+
${"x +1"} | ${[1, 1]}
37+
${"((x^2 + x) / x) - 1"} | ${[0, 0, 1]}
38+
${"1+2"} | ${[1, 0]}
39+
${"a +1+c"} | ${[1, 0]}
40+
${"y^2+5y - 1"} | ${[-1, 5, 1]}
41+
${"2y^2+4y"} | ${[0, 4, 2]}
42+
${"109h"} | ${[0, 109]}
43+
${"m+n+10"} | ${[1, 0]}
44+
${"x-x"} | ${[0, 0]}
45+
${"x + 5 - 3 + x - 6 - x + 2"} | ${[-2, 1]}
46+
${"2x-x"} | ${[0, 1]}
47+
${"x - x - 2"} | ${[1, 0]}
48+
`("$expression => $coefficients", ({ expression, coefficients }) => {
49+
const equation = atm.convert(lta.convert(expression));
50+
const coefficientsList = getCoefficients(equation);
51+
52+
expect(coefficientsList).toEqual(coefficients);
53+
});
54+
});
55+
56+
describe("getCoefficients", () => {
57+
it('equation: "x = x" - has coefficients [0, 0]', () => {
58+
const equation = atm.convert(lta.convert("x-x"));
59+
const coefficientsList = getCoefficients(equation);
60+
61+
expect(coefficientsList).toEqual([0, 0]);
62+
});
63+
64+
it('equation: "1 = -2" - if equation has no coefficient for x it will return coefficients [1, 0]', () => {
65+
const equation = atm.convert(lta.convert("1+2"));
66+
const coefficientsList = getCoefficients(equation);
67+
68+
expect(coefficientsList).toEqual([1, 0]);
69+
});
70+
71+
it('equation: "m + n = - 2" - if equation has more than one variable, will return coefficients [1, 0]', () => {
72+
const equation = atm.convert(lta.convert("m+n = - 2"));
73+
const coefficientsList = getCoefficients(equation);
74+
75+
expect(coefficientsList).toEqual([1, 0]);
76+
});
77+
});
78+
79+
describe("setXToOne", () => {
80+
it.each`
81+
expression | expressionNoX
82+
${"x+0"} | ${"1+0"}
83+
${"x +1"} | ${"1+1"}
84+
${"((x^2 + x) / x) - 1"} | ${"((1^2 + 1) / 1) - 1"}
85+
${"1+2"} | ${"1+2"}
86+
${"a +1+c"} | ${"a+1+c"}
87+
${"y^2+5x - 1"} | ${"y^2+5*1 - 1"}
88+
${"109h"} | ${"109h"}
89+
${"m+n+10"} | ${"m+n+10"}
90+
`("$expression => $expressionNoX", ({ expression, expressionNoX }) => {
91+
const equation = atm.convert(lta.convert(expression));
92+
const equationNoX = setXToOne(equation, "x");
93+
const result = atm.convert(lta.convert(expressionNoX));
94+
95+
expect(equationNoX.toTex()).toEqual(result.toTex());
96+
});
97+
});
98+
99+
describe("solveLinearEquation", () => {
100+
it.each`
101+
coefficients | xValue
102+
${[0, 1]} | ${0}
103+
${[1, 1]} | ${-1}
104+
${[0, 0, 1]} | ${0}
105+
${[]} | ${undefined}
106+
${[-1, 5, 1]} | ${undefined}
107+
${[0, 109]} | ${0}
108+
`("$coefficients => $xValue", ({ coefficients, xValue }) => {
109+
const result = solveLinearEquation(coefficients);
110+
111+
expect(result).toEqual(xValue);
112+
});
113+
});
114+
115+
describe("solveLinearEquation", () => {
116+
it('equation: "x = x" - has infinite solutions', () => {
117+
const coefficients = [0, 0];
118+
const result = solveLinearEquation(coefficients);
119+
120+
expect(result).toEqual(Infinity);
121+
});
122+
123+
it('equation: "x + 5 - 3 + x = 6 + x - 2" - solution should be 2', () => {
124+
const coefficients = [-2, 1];
125+
const result = solveLinearEquation(coefficients);
126+
127+
expect(result).toEqual(2);
128+
});
129+
130+
it('equation: "2x = x" - solution should be 0', () => {
131+
const coefficients = [0, 1];
132+
const result = solveLinearEquation(coefficients);
133+
134+
expect(result).toEqual(0);
135+
});
136+
137+
it('equation: "x = x + 2" - if equation has no solution it will return - Infinity', () => {
138+
const coefficients = [2, 0];
139+
const result = solveLinearEquation(coefficients);
140+
141+
expect(result).toEqual(-Infinity);
142+
});
143+
144+
it('equation: "3x - 2x = x + 7 + 9" - if equation has no solution it will return - Infinity', () => {
145+
const coefficients = [16, 0];
146+
const result = solveLinearEquation(coefficients);
147+
148+
expect(result).toEqual(-Infinity);
149+
});
150+
151+
it('equation: "2x^2 = 2x" - has no solution', () => {
152+
const coefficients = [0, 4, 2];
153+
const result = solveLinearEquation(coefficients);
154+
155+
expect(result).toEqual(-2);
156+
});
157+
158+
it('equation: "y^2+5y - 1" - if equation is quadratic, result is undefined', () => {
159+
const coefficients = [-1, 5, 1];
160+
const result = solveLinearEquation(coefficients);
161+
162+
expect(result).toEqual(undefined);
163+
});
164+
165+
it('equation: "y^2+5y + 1" - if equation is quadratic, result is undefined', () => {
166+
const coefficients = [1, 5, 1];
167+
const result = solveLinearEquation(coefficients);
168+
169+
expect(result).toEqual(undefined);
170+
});
171+
});

src/fixtures/latex-equal/symbolic/equation-with-no-variables.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default {
55
{
66
target: "4=4",
77
eq: ["4=4", "2+2=4"],
8-
// ne: ["1", "3=3", "9=8", "a=b", "0=0", "\\true=\\true"],
8+
ne: ["1", "3=3", "9=8", "a=b", "0=0", "\\true=\\true"],
99
},
1010
{
1111
target: "4=4=2+2",

src/fixtures/latex-equal/symbolic/equiv-symbolic.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export default {
99
{
1010
// equation with variable
1111
target: "y= \\frac{1}{2}x +5",
12-
1312
eq: [
1413
// flip sides of equation
1514
"\\frac{1}{2}x +5 = y",
@@ -21,19 +20,19 @@ export default {
2120
"y = 0.5x+5 ",
2221
// Use division sign rather than fraction form
2322
"y =x÷2 +5",
24-
// // Multiply both sides of the equation by a non-zero number
25-
// "-2y =-x -10",
23+
// Multiply both sides of the equation by a non-zero number
24+
"-2y =-x -10",
2625
// Add a number to both sides of the equation
2726
"y-5 =\\frac{x}{2}",
2827
"-5 =\\frac{1}{2}x-y",
2928
// Subtract one of the variables from both sides of the equation
3029
"0=x/2-y+5",
31-
// // Add a new variable to both sides of the equation
32-
// "z=x/2-y+5 +z",
33-
// // Divide both sides of the equation by y
34-
// "1=\\frac{x}{2y}+\\frac{5}{y}",
35-
// // Combine several of the above
36-
// "x-2y+z=z-10",
30+
// Add a new variable to both sides of the equation
31+
"z=x/2-y+5 +z",
32+
// Divide both sides of the equation by y TO DO- equation is transformed in a quadratic eequation
33+
"1=\\frac{x}{2y}+\\frac{5}{y}",
34+
// Combine several of the above
35+
"x-2y+z=z-10",
3736
],
3837
ne: [
3938
"\\frac{1}{2}x +5 = x",
@@ -61,17 +60,16 @@ export default {
6160
// target: "2x<4",
6261
// eq: ["4>2x", "x+x < 2*2", "x<2"],
6362
// },
64-
// {
65-
// // 3-way inequality with variable(s)
66-
// target: "1<2x ≤ 3",
67-
// eq: ["3 ≥ 2x>1", "x/x<x+x≤1.5*2", "1/2<x ≤1.5"], //
68-
// },
69-
// {
70-
// // inequality with no variable(s)
71-
// target: "1<2",
72-
// eq: ["0<1", "x+1>x"],
73-
// },
74-
// expression (no equal or inequality sign)
63+
{
64+
// 3-way inequality with variable(s)
65+
target: "1<2x ≤ 3",
66+
eq: [
67+
"3 ≥ 2x>1",
68+
"x/x<x+x≤1.5*2",
69+
// "1/2<x ≤1.5"
70+
],
71+
},
72+
// expressions (no equal or inequality sign)
7573
{
7674
target: "\\frac{1}{3}*(8c)-5",
7775
eq: [
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
export default {
2+
mode: "symbolic",
3+
tests: [
4+
{
5+
target: "x=9",
6+
eq: ["30-2x=12", "10+10-3x+x+10=12", "\\frac{1}{x}=\\frac{1}{9}"],
7+
ne: ["31-2x=12"],
8+
},
9+
{
10+
target: "a=9",
11+
eq: ["30-2a=12"],
12+
ne: ["31-2x=12", "b=9"],
13+
},
14+
{
15+
// infinite solutions equations
16+
target: "a=a",
17+
eq: ["2a=2a"],
18+
ne: ["12=12", "a=0"],
19+
},
20+
{
21+
// infinite solutions equations
22+
target: "5c+5c=5c+5c",
23+
eq: ["10c=10c", "c=c"],
24+
ne: ["12=12"],
25+
},
26+
{
27+
// if equations have no solutions they will not be equivalent
28+
target: "x=x+2",
29+
ne: ["2x=2x+4"],
30+
},
31+
{
32+
target: "((x^2 + x) / x) - 1=0",
33+
eq: ["3x=0", "109x=0"],
34+
},
35+
{
36+
target: "q=9",
37+
eq: ["15-q-6=0"],
38+
ne: ["31-2q=12", "qa=9", "q=q"],
39+
},
40+
{
41+
target: "2x+3=8",
42+
eq: [
43+
"4x+6=16",
44+
"2x=5",
45+
"x=2.5",
46+
"x=\\frac{5}{2}",
47+
"2.500000=x",
48+
"2x+3-8=0",
49+
"2x+3-8-m=0-m",
50+
],
51+
ne: ["5x+6=16", "2x+4-8=0", "x+3=4"],
52+
},
53+
{
54+
target: "4x+2=6x-4",
55+
eq: ["-2x+6=0"],
56+
},
57+
{
58+
target: "y^2+5y - 1=0",
59+
ne: ["y^2+5y +1=0"],
60+
},
61+
],
62+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
export default {
2+
mode: "symbolic",
3+
tests: [
4+
{
5+
target: "y= \\frac{1}{2}x +5",
6+
eq: [
7+
// Multiply both sides of the equation by a non-zero number
8+
"-2y =-x -10",
9+
// Divide both sides of the equation by y
10+
"1=\\frac{x}{2y}+\\frac{5}{y}",
11+
// Combine several of the above
12+
"x-2y+z=z-10",
13+
// Multiply both sides of the equation by a variable
14+
"y^2=\\frac{xy}{2}+5y",
15+
],
16+
},
17+
{
18+
// linear equations with 2 unknowns that are not x or y
19+
target: "a=\\frac{1}{2}b +5",
20+
eq: [
21+
"-2a =-b -10",
22+
"1=\\frac{b}{2a}+\\frac{5}{a}",
23+
"b-2a+c=c-10",
24+
],
25+
},
26+
{
27+
// linear equations with 2 unknowns that are not x or y
28+
target: "a=\\frac{1}{2}b +5",
29+
eq: [
30+
"-2a =-b -10",
31+
"1=\\frac{b}{2a}+\\frac{5}{a}",
32+
"b-2a+c=c-10",
33+
],
34+
},
35+
{
36+
// Multiply both sides of the equation by a variable
37+
target: "x+2y=7",
38+
eq: ["x*x+2yx-7x=0"],
39+
},
40+
{
41+
// Multiply both sides of the equation by a variable
42+
target: "x+2y=7",
43+
eq: ["x^2+2yx-7x=0"],
44+
},
45+
{
46+
// Divide both sides of the equation by a variable
47+
target: "x+2y=7",
48+
eq: ["\\frac{x}{y}+2-7/y=0", "\\frac{x}{x}+\\frac{2y}{x}=\\frac{7}{x}"],
49+
},
50+
],
51+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default {
2+
mode: "symbolic",
3+
4+
tests: [
5+
{
6+
// // quadratic equation
7+
//only: true,
8+
// target: "x^2-7x+10=0",
9+
// eq: ["2x^2-14x+20=0"],
10+
},
11+
],
12+
};

src/latex-equal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ export const latexEqual = (a: Latex, b: Latex, opts: Opts) => {
4040
if (opts.mode === "literal") {
4141
return isLiteralEqual(amo, bmo, opts.literal);
4242
} else {
43-
return isSymbolicEqual(amo, bmo, opts.symbolic);
43+
return isSymbolicEqual(amo, bmo);
4444
}
4545
};

0 commit comments

Comments
 (0)