Skip to content

Commit c56673e

Browse files
authored
Merge pull request #47 from pie-framework/feat/equivalence-between-quadratic-equations
feat/equivalence-between-quadratic-equations
2 parents c495c52 + b0c9171 commit c56673e

11 files changed

Lines changed: 401 additions & 57 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ It can determine mathematical equivalence between:
6161

6262
- linear equations in one variable
6363
- linear equations in two variables
64+
- quadratic equations
6465
- 2-way inequalities in one or two variables
6566
- compound inequalities in one variable
6667
- trigonometric identities and functions

src/__tests__/utils.spec.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getCoefficients,
88
setXToOne,
99
solveLinearEquation,
10+
solveQuadraticEquation,
1011
expressionsCanBeCompared,
1112
transformEqualityInExpression,
1213
} from "../symbolic/utils";
@@ -107,7 +108,7 @@ describe("getCoefficients", () => {
107108
${"x - x - 2"} | ${[]}
108109
`("$expression => $coefficients", ({ expression, coefficients }) => {
109110
const equation = atm.convert(lta.convert(expression));
110-
const coefficientsList = getCoefficients(equation);
111+
const coefficientsList = getCoefficients(equation, false);
111112

112113
expect(coefficientsList).toEqual(coefficients);
113114
});
@@ -116,21 +117,21 @@ describe("getCoefficients", () => {
116117
describe("getCoefficients", () => {
117118
it('equation: "x = x" - has coefficients [0, 0]', () => {
118119
const equation = atm.convert(lta.convert("x-x"));
119-
const coefficientsList = getCoefficients(equation);
120+
const coefficientsList = getCoefficients(equation, false);
120121

121122
expect(coefficientsList).toEqual([0, 0]);
122123
});
123124

124125
it('equation: "1 = -2" - if equation has no coefficient for x but can be rationalized it will return an empty array', () => {
125126
const equation = atm.convert(lta.convert("1+2"));
126-
const coefficientsList = getCoefficients(equation);
127+
const coefficientsList = getCoefficients(equation, false);
127128

128129
expect(coefficientsList).toEqual([]);
129130
});
130131

131132
it('equation: "m + n = - 2" - if equation has more than one variable, will return coefficients [1, 0]', () => {
132133
const equation = atm.convert(lta.convert("m+n = - 2"));
133-
const coefficientsList = getCoefficients(equation);
134+
const coefficientsList = getCoefficients(equation, false);
134135

135136
expect(coefficientsList).toEqual([1, 0]);
136137
});
@@ -172,6 +173,26 @@ describe("solveLinearEquation", () => {
172173
});
173174
});
174175

176+
describe("solveQuadraticEquation", () => {
177+
it.each`
178+
coefficients | roots
179+
${[14, -9, 1]} | ${[{ re: 7, im: 0 }, { re: 2, im: 0 }]}
180+
${[-3, 2, 8]} | ${[{ re: 0.5, im: 0 }, { re: -0.75, im: 0 }]}
181+
${[8, 6, 2]} | ${[{ re: -1.5, im: 1.3228756555323 }, { re: -1.5, im: -1.3228756555323 }]}
182+
${[6, 5, 1]} | ${[{ re: -2, im: 0 }, { re: -3, im: 0 }]}
183+
${[3, -7, 2]} | ${[{ re: 3, im: 0 }, { re: 1/2, im: 0 }]}
184+
${[1, 3, 2]} | ${[{ re: -0.5, im: 0 }, { re: -1, im: 0 }]}
185+
${[-3, 2, 1]} | ${[{ re: 1, im: 0 }, { re: -3, im: 0 }]}
186+
${[-9, 0, 1]} | ${[{ re: 3, im: 0 }, { re: -3, im: 0 }]}
187+
${[-8, -5, 3]} | ${[{ re: 2.6666666666667, im: 0 }, { re: -1, im: 0 }]}
188+
${[17, -5, 4]} | ${[{ re: 0.625, im: 1.9645292056877 }, { re: 0.625, im: -1.9645292056877}]}
189+
`("$coefficients => $roots", ({ coefficients, roots }) => {
190+
const result = solveQuadraticEquation(coefficients);
191+
192+
expect(result).toEqual(roots);
193+
});
194+
});
195+
175196
describe("solveLinearEquation", () => {
176197
it('equation: "x = x" - has infinite solutions', () => {
177198
const coefficients = [0, 0];

src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,23 @@ export default {
5050
},
5151
{
5252
target: "1 < 2x < 10",
53-
eq: ["2 < 4x < 20"],
53+
eq: ["2 < 4x < 20"],
5454
ne: [
55-
"2 = 4x = 20",
55+
"2 = 4x = 20",
5656
"1 <2x >10"
5757
],
5858
},
5959
{
6060
target: "a≠b≠c",
61-
ne: ["c≠d≠e"]
61+
ne: ["c≠d≠e"]
6262
},
6363
{
6464
target: "a=b=c",
65-
ne: ["c=d=e"]
65+
ne: ["c=d=e"]
6666
},
6767
{
6868
target: "-99 ≤ 9-12r ≤ 45",
69-
eq: ["-108≤ -12r ≤ 36", "-108/-12≤ -12r/-12 ≤ 36/-12","9 ≥ r ≥ -3", "-3≤ r≤ 9", ],
69+
eq: ["-108≤ -12r ≤ 36", "-108/-12≤ -12r/-12 ≤ 36/-12", "9 ≥ r ≥ -3", "-3≤ r≤ 9",],
7070
ne: [
7171
"-108≤ -12r < 36",
7272
"-108≤ -12r > 36",

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,5 +419,26 @@ export default {
419419
"60x-900=y^2-14y+49",
420420
],
421421
},
422+
{
423+
target: "\\frac{6}{(x+1)^2}+\\frac{1}{x+1}",
424+
eq: [
425+
"\\frac{6}{(x+1)^2}+\\frac{5}{5(x+1)}",
426+
"\\frac{6}{(x+1)(x+1)}+\\frac{5}{5(x+1)}",
427+
"\\frac{6}{x(x+1)+1(x+1)}+\\frac{5}{5(x+1)}",
428+
"\\frac{6}{x^2+x+1(x+1)}+\\frac{20}{20(x+1)}",
429+
],
430+
},
431+
{
432+
target: "\\frac{2}{(x+5)}+\\frac{1}{x+2}+10",
433+
eq: [
434+
"\\frac{4}{2(x+5)}+\\frac{3}{3(x+2)}+10",
435+
"\\frac{4}{2(x+5)}+\\frac{3}{3(x+2)}+5+5",
436+
"\\frac{10}{5(x+5)}+\\frac{1}{1(x+4-2)}+20-10",
437+
],
438+
ne: [
439+
"\\frac{1}{(x+5)}+\\frac{3}{3(x+2)}+10",
440+
"\\frac{2}{(x+4)}+\\frac{1}{x+2}+10",
441+
],
442+
},
422443
],
423444
};

src/fixtures/latex-equal/symbolic/math-series.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export default {
99
{
1010
target: "a_{i}=1/2i[2s+(i-1)d]",
1111
eq: ["a_{i}=0.5i(2s+di-d)"],
12-
ne: ["a_{i-1}=0.5i(2s+di-d)"],
12+
ne: ["a_{i-1}=0.5i(2s+di-d)", "0.5i(2s+di-d)=a_{i-1}", "0.5i(2s+di-d)=a"],
1313
},
1414
{
1515
target: "a_{n}=(-1/(2i))^n",
Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,130 @@
11
export default {
22
mode: "symbolic",
3-
43
tests: [
54
{
6-
// // quadratic equation
7-
//only: true,
8-
// target: "x^2-7x+10=0",
9-
// eq: ["2x^2-14x+20=0"],
5+
target: "x^2-7x+10=0",
6+
eq: ["2x^2-14x+20=0"],
7+
},
8+
{
9+
target: "5x^2+20x+32=0",
10+
eq: ["10x^2+40x+64=0", "15x^2+60x+96=0"],
11+
},
12+
{
13+
target: "x^2 + 3x +2 = 5x +10",
14+
eq: [
15+
"(x+2)(x-4)=0",
16+
"x^2-4x+2x-8=0"
17+
],
18+
ne: ["(x+1)(x-4)=0", "(x)(x-4)=0", "x^2-4x+2x-8"],
19+
},
20+
{
21+
target: "x^2 = 5x - 6",
22+
eq: ["x^2 - 5x + 6=0", "(x-3)(x-2)=0"],
23+
ne: ["(x-3)(x-1)=0", "(x)(x-3)=0", "x^2-5x - 6 = 0"],
24+
},
25+
{
26+
target: "6x^2-3x-2x +1 =0",
27+
eq: ["3x(2x-1)-1(2x-1)=0", "(3x-1)(2x-1)=0"],
28+
ne: ["3x(2x)-1(2x-1)=0", "(3x-1)(2x)=0"],
29+
},
30+
{
31+
target: "6x^2+4x+2=0",
32+
eq: [
33+
"3x^2+2x+1=0",
34+
"x^2+\\frac{2x}{3}+\\frac{1}{3}=0",
35+
"x^2+\\frac{2x}{3}+\\frac{2}{6}=0",
36+
],
37+
ne: [
38+
"x^2+\\frac{2x}{3}+\\frac{2}{4}=0",
39+
"x^2+\\frac{2x}{3}+\\frac{2}{5}=0",
40+
"x^2+\\frac{2x}{3}+\\frac{2}{3}=0",
41+
],
42+
},
43+
{
44+
target: "3x^2-18x+14=0",
45+
eq: [
46+
"3(x^2-6x)+14=0",
47+
"3(x^2-6x +3^2-3^2)=-14",
48+
"3(x^2-6x+3^2)-3(9)+14=0",
49+
"3(x-3)^2-13=0",
50+
],
51+
},
52+
{
53+
target: "(t-5)^2-9=0",
54+
eq: ["(t-5)^2=9"],
55+
ne: ["(t-5)^3=9", "(x-5)^2=9", "(t-4)^2=9"],
56+
},
57+
{
58+
target: "\\frac{6}{(x+1)^2}+\\frac{2}{2x}+2=0",
59+
eq: [
60+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}+2=0",
61+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}=-2",
62+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}+6-4=0",
63+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}+6=+4",
64+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}+6=+4",
65+
"\\frac{6}{(x+1)(x+1)}+\\frac{2}{2x}+4=+6-4",
66+
"\\frac{6}{(x+1)(x+1)}+\\frac{3}{3x}+2=0",
67+
"\\frac{6}{x(x+1)+1(x+1)}+\\frac{5}{5(x)}+2=0",
68+
],
69+
},
70+
{
71+
target: "\\frac{6}{(x+1)^2}+\\frac{1}{x+1}=1",
72+
eq: [
73+
"\\frac{6}{(x+1)^2}+\\frac{5}{5(x+1)}=1",
74+
"\\frac{6}{(x+1)(x+1)}+\\frac{5}{5(x+1)}=1",
75+
"\\frac{6}{x(x+1)+1(x+1)}+\\frac{5}{5(x+1)}=1",
76+
"\\frac{6}{x^2+x+1(x+1)}+\\frac{5}{5(x+1)}=1",
77+
],
78+
},
79+
{
80+
// test square props
81+
target: "(u+1)^2=0",
82+
eq: [
83+
// expand square
84+
"(u+1)(u+1)=0",
85+
// distributive property
86+
"u(u+1)+1(u+1)=0",
87+
"u^2+u+1(u+1)=0",
88+
],
89+
},
90+
{
91+
// test square props
92+
target: "(z-1)^2=0",
93+
eq: [
94+
// expand square
95+
"(z-1)(z-1)=0",
96+
// distributive property
97+
"z(z-1)-1(z-1)=0",
98+
"z*z+z*(-1)-1(z-1)=0",
99+
"z*z+z*(-1)-1z-1*(-1)=0",
100+
"z^2-2z+1=0",
101+
],
102+
},
103+
{
104+
// they both have complex solutions but they should not be equal
105+
target: "4x^2-5x+17=0",
106+
ne: ["2x^2+6x+8=0", "x^2+4x+5=0"],
107+
},
108+
{
109+
target: "x^2+4x+5=0",
110+
eq: ["2x^2+8x+10=0"],
111+
},
112+
{
113+
// positive real roots
114+
target: "x^2+6x+5=0",
115+
eq: ["3x^2+18x+15=0", "x^2+6x+4=-1", "x^2+6x=-5"],
116+
ne:["4x^2+18x+15=0", "3x^2+17x+15=0", "3x^2+18x+16=0", "x^3+6x+5=0"]
117+
},
118+
{
119+
// one real root - zero
120+
target: "x^2-2x+1=0",
121+
eq: ["2x^2-4x+2=0"],
122+
},
123+
{
124+
// two complex roots
125+
target: "x^2-3x+10=0",
126+
eq: ["2x^2-6x+20=0"],
127+
ne:["x^2-4x+10=0", "x^2-3x+10=1"]
10128
},
11129
],
12130
};

src/mathjs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { all, create } from "mathjs";
22
export const mathjs = create(all, { number: "Fraction" });
33
export const { parse } = mathjs;
4+
45
// @ts-ignore
56
export const replacer = mathjs.replacer;

src/symbolic/compare-compound-inequations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ export const getLimit = (
6060
const expressionR = transformEqualityInExpression(expressionsPair.right);
6161
const expressionL = transformEqualityInExpression(expressionsPair.left);
6262

63-
const xFirstInequality = solveLinearEquation(getCoefficients(expressionR));
64-
const xSecondInequality = solveLinearEquation(getCoefficients(expressionL));
63+
const xFirstInequality = solveLinearEquation(getCoefficients(expressionR, true));
64+
const xSecondInequality = solveLinearEquation(getCoefficients(expressionL, true));
6565

6666
if (limitType === "inferior") {
6767
return Math.min(xFirstInequality, xSecondInequality);

0 commit comments

Comments
 (0)