From 2fb3b8106440c88a0e8730367ccad999e7437bea Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 6 Oct 2021 14:40:17 +0300 Subject: [PATCH 01/34] try to determine equivalence between 2 way inequalities with variables --- .../2-way-inequalities-with-variables.ts | 49 +++++++++++++++++++ .../3-way-inequalities-with-variables.ts | 14 ++++++ .../latex-equal/symbolic/equiv-symbolic.ts | 14 ------ src/symbolic/index.ts | 25 ++++++++++ 4 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts create mode 100644 src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts new file mode 100644 index 0000000..68070da --- /dev/null +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -0,0 +1,49 @@ +export default { + mode: "symbolic", + tests: [ + { + // 2-way inequality with variable(s) + target: "2x<4", + eq: [ + "4>2x", + "x+x < 2*2", + "x<2", + ], + }, + { + target: "2x≤4", + eq: [ + "4≥2x", + "x+x ≤ 2*2", + "x≤2", + ], + ne: ["x<2", "3x≤4"] + }, + { + target: "2x<4", + eq: [ + "4>2x", + "x+x < 2*2", + "x<2", + ], + }, + { + target: "2x+y≤4+y", + eq: [ + "4≥2x", + "x+x ≤ 2*2", + "4x≤8", + ], + ne: ["x<2", "3x≤4", "x<3"] + }, + { + target: "2x+3y≤4+y", + eq: [ + "4+y≥2x+3y", + "x+x +3y≤ 2*2 +y", + "4x+6y≤8+2y", + ], + ne: ["x<2", "3x≤4", "x<3"] + }, + ], +}; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts new file mode 100644 index 0000000..7a1a9ef --- /dev/null +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -0,0 +1,14 @@ +export default { + mode: "symbolic", + tests: [ + { + // 3-way inequality with variable(s) + target: "1<2x ≤ 3", + eq: [ + "3 ≥ 2x>1", + "x/x2x", "x+x < 2*2", "x<2"], - // }, - { - // 3-way inequality with variable(s) - target: "1<2x ≤ 3", - eq: [ - "3 ≥ 2x>1", - "x/x { if (equality) { return true; } + // if both expressions are equations if (as.fn === "equal" && bs.fn === "equal") { equality = compareEquations(as, bs); + + if (equality) { + return true; + } + } + + // if both expressions are inequalities + if ( + (as.fn === "larger" && bs.fn === "larger") || + (as.fn === "largerEq" && bs.fn === "largerEq") + ) { + // at this point this is an ideea, it must be tested + // solving a 2 way inequality is the same as solving an equation, we can treat the greater sign as an equal sign + // the diffrence is that the solution should be plotet on a number line + // since we have the same signs and the same direction, the interval will always be the same, starting with the solution + console.log(as.fn, "asfn") + as.fn = "equal" + bs.fn ="equal" + as.op= "=" + bs.op= "=" + // equality = compareEquations(as, bs); + console.log(as.toTex()) + equality = compareEquations(as, bs); + } return equality; From 449d2cde835798b92687236f1f137da9270cd8db Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 6 Oct 2021 15:28:19 +0300 Subject: [PATCH 02/34] add test, I found a problem in current aproach --- .../2-way-inequalities-with-variables.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 68070da..dba9430 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -27,6 +27,15 @@ export default { "x<2", ], }, + { + target: "2x<4", + eq: [ + "4>2x", + "x+x < 2*2", + ], + // this is a problem, when multiplying both parts with a negative number, direction should be changed, expressions should not be equivalent anymore + ne: ["x*(-1)<2*(-1)"] + }, { target: "2x+y≤4+y", eq: [ @@ -43,7 +52,18 @@ export default { "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y", ], - ne: ["x<2", "3x≤4", "x<3"] + ne: ["x+y<2", "3x+2y≤4", "x+4y≤3"] + }, + { + target: "5.5x + 8y≥100", + eq: [ + "11x+16y≥200", + "5.5x + 8y+10≥100+10", + "5.5x + 8y+z≥100+z", + "(5.5x + 8y)*2≥100*2", + "5.5x + 8y -100≥0", + "(5.5x + 8y)≥100", + ], }, ], }; From eb1f75066ad983d592f12df5df881b3ef1e27dcd Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 6 Oct 2021 17:22:54 +0300 Subject: [PATCH 03/34] add tests, try stuff --- .../2-way-inequalities-with-variables.ts | 1 + src/symbolic/compare-equations.ts | 37 +++++++++++++++---- src/symbolic/index.ts | 4 +- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index dba9430..c07621b 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -28,6 +28,7 @@ export default { ], }, { + only:true, target: "2x<4", eq: [ "4>2x", diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 3582f5f..4298c42 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -41,10 +41,8 @@ export const setXToOne = (equation: any, unknownName: string) => { result = equation.transform(function (node, path, parent) { if (node.isSymbolNode && node.name === unknownName) { - return new m.ConstantNode(1); } else { - return node; } }); @@ -57,7 +55,7 @@ export const solveLinearEquation = (coefficients: number[]) => { let result: number; // TO DO: solve quadratic equation - if (coefficients.length === 3 && coefficients[0] === 0 ) { + if (coefficients.length === 3 && coefficients[0] === 0) { coefficients = coefficients.splice(1, 2); } @@ -89,9 +87,24 @@ export const equationsHaveTheSameUnknowns = ( ); }; +export const constructInequality = ( inequality:MathNode, unknownName:String, solution: Number) => { + let result: MathNode; + + result = inequality.transform(function (node, path, parent) { + if (node.isSymbolNode && node.name === unknownName) { + return new m.ConstantNode(solution); + } else { + return node; + } + }); + + return result; +} + export const compareEquations = ( firstEquation: MathNode, - secondEquation: MathNode + secondEquation: MathNode, + isInequality: boolean ) => { let noFunctionOrArray: boolean = true; let firstSymbolNode: boolean = false; @@ -157,9 +170,19 @@ export const compareEquations = ( firstEquationCoefficients = getCoefficients(firstExpression); secondEquationCoefficients = getCoefficients(secondExpression); - equivalence = - solveLinearEquation(firstEquationCoefficients) === - solveLinearEquation(secondEquationCoefficients); + const solutionForFirstEquation = solveLinearEquation( + firstEquationCoefficients + ); + const solutionForSecondEquation = solveLinearEquation( + secondEquationCoefficients + ); + + equivalence = solutionForFirstEquation === solutionForSecondEquation; + + if (equivalence && isInequality) { + let firstInequality = constructInequality(firstEquation, firstEquationUnknownsName[0], solutionForFirstEquation); + console.log(firstInequality.toTex()) + } } // if both equations are linear in two variabled then we give value "1" for both "x". Doing this we get a linear equation in one variable "y". Then we solve "y" for both. If y has the same value then equations are equivalent diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index c515385..7b1e644 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -152,7 +152,7 @@ export const isMathEqual = (a: any, b: any) => { // if both expressions are equations if (as.fn === "equal" && bs.fn === "equal") { - equality = compareEquations(as, bs); + equality = compareEquations(as, bs, false); if (equality) { return true; @@ -175,7 +175,7 @@ export const isMathEqual = (a: any, b: any) => { bs.op= "=" // equality = compareEquations(as, bs); console.log(as.toTex()) - equality = compareEquations(as, bs); + equality = compareEquations(as, bs, true); } From fca375e628f3cc117b2a6b02ba16276cb48d8d76 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Thu, 7 Oct 2021 12:52:42 +0300 Subject: [PATCH 04/34] check if direction of 2-way inequality in one variable should be changed --- .DS_Store | Bin 8196 -> 6148 bytes .../2-way-inequalities-with-variables.ts | 12 ++++++- .../symbolic/7119-mixed-numbers.ts | 8 +++++ src/symbolic/compare-equations.ts | 32 +++++++++++------- src/symbolic/index.ts | 6 ++-- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.DS_Store b/.DS_Store index 0fa7973059715c0649ee32857426cb918f4a7309..72104221a983c2433f6416ba18dc211cc00e7dfc 100644 GIT binary patch delta 105 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5r;6q~50$SANeU^g?Pz-AtS8;qO(3n?=# rX6N7#WCkh$0s(Fy;R;f{vG6kX{BRh&dpm88*lB%wYxqrfd+* delta 177 zcmZoMXmOBWU|?W$DortDU;r^WfEYvza8E20o2aMAD7-OXH}hr%jz7$c**Q2SHn1=X zZ{}gS!N|_cki(G4kTm%so3OCne=q>*V_<-ZJFwgkhO*0oi}G^v^U{H`jGG^_sxfVB yc*-Qm3|7t{!40HcK{jp{2x", @@ -37,6 +37,16 @@ export default { // this is a problem, when multiplying both parts with a negative number, direction should be changed, expressions should not be equivalent anymore ne: ["x*(-1)<2*(-1)"] }, + { + // only:true, + target: "2x>4", + eq: [ + "4<2x", + "x+x > 2*2", + ], + // this is a problem, when multiplying both parts with a negative number, direction should be changed, expressions should not be equivalent anymore + ne: ["x*(-1)>2*(-1)"] + }, { target: "2x+y≤4+y", eq: [ diff --git a/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts b/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts index c4fa96f..ccdbcb2 100644 --- a/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts +++ b/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts @@ -26,5 +26,13 @@ export default { "\\frac{10}{2} - 0.5", ], }, + { + // only:true, + target: "4\\frac{1}{2} + b + ca", + eq: [ + "\\frac{10}{2} + ac + b- 0.5" + ], + // ne: ["\\frac{10}{2} + ac + b + 0.5"], + }, ], }; diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 4298c42..51e74ea 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -87,19 +87,20 @@ export const equationsHaveTheSameUnknowns = ( ); }; -export const constructInequality = ( inequality:MathNode, unknownName:String, solution: Number) => { - let result: MathNode; +// export const constructInequality = ( inequality:MathNode, unknownName:String, solution: Number) => { +// let result: MathNode; - result = inequality.transform(function (node, path, parent) { - if (node.isSymbolNode && node.name === unknownName) { - return new m.ConstantNode(solution); - } else { - return node; - } - }); +// result = inequality.transform(function (node, path, parent) { +// if (node.isSymbolNode && node.name === unknownName) { +// return new m.ConstantNode(solution); +// } else { +// return node; +// } +// }); - return result; -} + +// return result; +// } export const compareEquations = ( firstEquation: MathNode, @@ -180,8 +181,13 @@ export const compareEquations = ( equivalence = solutionForFirstEquation === solutionForSecondEquation; if (equivalence && isInequality) { - let firstInequality = constructInequality(firstEquation, firstEquationUnknownsName[0], solutionForFirstEquation); - console.log(firstInequality.toTex()) + // check if direction should be changed + if (firstEquationCoefficients[0]> 0 && firstEquationCoefficients[1] <0 && secondEquationCoefficients[0]<0 && secondEquationCoefficients[1]>0 ){ + equivalence= false; + } + if (firstEquationCoefficients[0]< 0 && firstEquationCoefficients[1] >0 && secondEquationCoefficients[0]>0 && secondEquationCoefficients[1]<0 ){ + equivalence= false; + } } } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 7b1e644..a29a36a 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -166,15 +166,13 @@ export const isMathEqual = (a: any, b: any) => { ) { // at this point this is an ideea, it must be tested // solving a 2 way inequality is the same as solving an equation, we can treat the greater sign as an equal sign - // the diffrence is that the solution should be plotet on a number line + // the difference is that the solution should be plotted on a number line (or interval) // since we have the same signs and the same direction, the interval will always be the same, starting with the solution - console.log(as.fn, "asfn") as.fn = "equal" bs.fn ="equal" as.op= "=" bs.op= "=" - // equality = compareEquations(as, bs); - console.log(as.toTex()) + equality = compareEquations(as, bs, true); } From d8581e388123e99e836ae7ebe74693c3133f2ef5 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Thu, 7 Oct 2021 13:28:24 +0300 Subject: [PATCH 05/34] refactor --- .../2-way-inequalities-with-variables.ts | 102 ++++++------------ src/symbolic/compare-equations.ts | 33 +++--- src/symbolic/index.ts | 21 ++-- 3 files changed, 59 insertions(+), 97 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 11c9e3d..0279ead 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -1,80 +1,48 @@ export default { mode: "symbolic", tests: [ + // 2-way inequalities in one variable { - // 2-way inequality with variable(s) target: "2x<4", - eq: [ - "4>2x", - "x+x < 2*2", - "x<2", - ], + eq: ["4>2x", "x+x < 2*2", "x<2"], + ne: ["x*(-1)<2*(-1)"], }, { target: "2x≤4", + eq: ["4≥2x", "x+x ≤ 2*2", "x≤2"], + ne: ["x<2", "3x≤4", "x*(-1)≤2*(-1)"], + }, + { + target: "2x>4", + eq: ["4<2x", "x+x > 2*2"], + ne: ["x*(-1)>2*(-1)"], + }, + { + target: "2x≥4", + eq: ["4≤2x", "x+x ≥ 2*2"], + ne: ["x*(-1)≥2*(-1)"], + }, + // 2-way inequalities in two variables + { + target: "2x+y≤4+y", + eq: ["4≥2x", "x+x ≤ 2*2", "4x≤8"], + ne: ["x<2", "3x≤4", "x<3"], + }, + { + target: "2x+3y≤4+y", + eq: ["4+y≥2x+3y", "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y"], + ne: ["x+y<2", "3x+2y≤4", "x+4y≤3"], + }, + { + target: "5.5x + 8y≥100", eq: [ - "4≥2x", - "x+x ≤ 2*2", - "x≤2", + "11x+16y≥200", + "5.5x + 8y+10≥100+10", + "5.5x + 8y+z≥100+z", + "(5.5x + 8y)*2≥100*2", + "5.5x + 8y -100≥0", + "(5.5x + 8y)≥100", ], - ne: ["x<2", "3x≤4"] }, - { - target: "2x<4", - eq: [ - "4>2x", - "x+x < 2*2", - "x<2", - ], - }, - { - // only:true, - target: "2x<4", - eq: [ - "4>2x", - "x+x < 2*2", - ], - // this is a problem, when multiplying both parts with a negative number, direction should be changed, expressions should not be equivalent anymore - ne: ["x*(-1)<2*(-1)"] - }, - { - // only:true, - target: "2x>4", - eq: [ - "4<2x", - "x+x > 2*2", - ], - // this is a problem, when multiplying both parts with a negative number, direction should be changed, expressions should not be equivalent anymore - ne: ["x*(-1)>2*(-1)"] - }, - { - target: "2x+y≤4+y", - eq: [ - "4≥2x", - "x+x ≤ 2*2", - "4x≤8", - ], - ne: ["x<2", "3x≤4", "x<3"] - }, - { - target: "2x+3y≤4+y", - eq: [ - "4+y≥2x+3y", - "x+x +3y≤ 2*2 +y", - "4x+6y≤8+2y", - ], - ne: ["x+y<2", "3x+2y≤4", "x+4y≤3"] - }, - { - target: "5.5x + 8y≥100", - eq: [ - "11x+16y≥200", - "5.5x + 8y+10≥100+10", - "5.5x + 8y+z≥100+z", - "(5.5x + 8y)*2≥100*2", - "5.5x + 8y -100≥0", - "(5.5x + 8y)≥100", - ], - }, ], }; diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 51e74ea..0f91d17 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -87,21 +87,6 @@ export const equationsHaveTheSameUnknowns = ( ); }; -// export const constructInequality = ( inequality:MathNode, unknownName:String, solution: Number) => { -// let result: MathNode; - -// result = inequality.transform(function (node, path, parent) { -// if (node.isSymbolNode && node.name === unknownName) { -// return new m.ConstantNode(solution); -// } else { -// return node; -// } -// }); - - -// return result; -// } - export const compareEquations = ( firstEquation: MathNode, secondEquation: MathNode, @@ -182,11 +167,21 @@ export const compareEquations = ( if (equivalence && isInequality) { // check if direction should be changed - if (firstEquationCoefficients[0]> 0 && firstEquationCoefficients[1] <0 && secondEquationCoefficients[0]<0 && secondEquationCoefficients[1]>0 ){ - equivalence= false; + if ( + m.isPositive(firstEquationCoefficients[0]) && + m.isNegative(firstEquationCoefficients[1]) && + m.isNegative(secondEquationCoefficients[0]) && + m.isPositive(secondEquationCoefficients[1]) + ) { + equivalence = false; } - if (firstEquationCoefficients[0]< 0 && firstEquationCoefficients[1] >0 && secondEquationCoefficients[0]>0 && secondEquationCoefficients[1]<0 ){ - equivalence= false; + if ( + m.isNegative(firstEquationCoefficients[0]) && + m.isPositive(firstEquationCoefficients[1]) && + m.isPositive(secondEquationCoefficients[0]) && + m.isNegative(secondEquationCoefficients[1]) + ) { + equivalence = false; } } } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index a29a36a..3cb7f59 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -159,22 +159,21 @@ export const isMathEqual = (a: any, b: any) => { } } - // if both expressions are inequalities + // at this point this is an ideea, it must be tested + // if both expressions are inequalities treat greater sign as equal sign if ( (as.fn === "larger" && bs.fn === "larger") || (as.fn === "largerEq" && bs.fn === "largerEq") ) { - // at this point this is an ideea, it must be tested // solving a 2 way inequality is the same as solving an equation, we can treat the greater sign as an equal sign - // the difference is that the solution should be plotted on a number line (or interval) - // since we have the same signs and the same direction, the interval will always be the same, starting with the solution - as.fn = "equal" - bs.fn ="equal" - as.op= "=" - bs.op= "=" - - equality = compareEquations(as, bs, true); - + // the difference is that solution should be plotted on a number line (or interval) + // if we have the same signs and the same direction, the interval will always be the same, starting with the solution + as.fn = "equal"; + bs.fn = "equal"; + as.op = "="; + bs.op = "="; + + equality = compareEquations(as, bs, true); } return equality; From 635640f74165c16efa5472ed2117a4d4aefe8e1f Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Thu, 7 Oct 2021 14:59:36 +0300 Subject: [PATCH 06/34] check equivalence between 2 way inequalities in 2 variables, add tests --- .../2-way-inequalities-with-variables.ts | 24 ++++++++++++++----- .../latex-equal/symbolic/equiv-symbolic.ts | 2 -- src/symbolic/compare-equations.ts | 20 ++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 0279ead..4a21f60 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -10,28 +10,31 @@ export default { { target: "2x≤4", eq: ["4≥2x", "x+x ≤ 2*2", "x≤2"], - ne: ["x<2", "3x≤4", "x*(-1)≤2*(-1)"], + ne: ["x<2", "3x≤4", "x*(-1)≤2*(-1)", "2x≥4", "2x=4"], }, { target: "2x>4", eq: ["4<2x", "x+x > 2*2"], - ne: ["x*(-1)>2*(-1)"], + ne: ["x*(-1)>2*(-1)", "2x<4", "2x=4"], }, { target: "2x≥4", eq: ["4≤2x", "x+x ≥ 2*2"], ne: ["x*(-1)≥2*(-1)"], }, - // 2-way inequalities in two variables { target: "2x+y≤4+y", - eq: ["4≥2x", "x+x ≤ 2*2", "4x≤8"], - ne: ["x<2", "3x≤4", "x<3"], + eq: ["4≥2x", "x+x ≤ 2*2", "4x≤8", "2x+y-y-4≤0"], + ne: ["x<2", "3x≤4", "x<3", "-2x-y≤-4-y"], }, + // 2-way inequalities in two variables { target: "2x+3y≤4+y", eq: ["4+y≥2x+3y", "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y"], - ne: ["x+y<2", "3x+2y≤4", "x+4y≤3"], + ne: [ + "x+y<2", "3x+2y≤4", "x+4y≤3", + "-2x-3y≤-4-y" + ], }, { target: "5.5x + 8y≥100", @@ -44,5 +47,14 @@ export default { "(5.5x + 8y)≥100", ], }, + { + target: "2x + 3y≥7", + eq: [ + "x + 1.5y≥3.5", + "3x + 6y≥14-x", + "-2x-3y≤-7" + ], + ne: ["-2x-3y≥-7", "3x + 6y≤14-x","x + 1.5y≤3.5","x + 1.5y=3.5"] + }, ], }; diff --git a/src/fixtures/latex-equal/symbolic/equiv-symbolic.ts b/src/fixtures/latex-equal/symbolic/equiv-symbolic.ts index eb78360..b1d78ad 100644 --- a/src/fixtures/latex-equal/symbolic/equiv-symbolic.ts +++ b/src/fixtures/latex-equal/symbolic/equiv-symbolic.ts @@ -45,14 +45,12 @@ export default { { // equation with no variable target: "2+2=4", - // they should not be equal ne: ["1=1", "0.5=1/2", "9x=3(3x)"], }, { // equation with no variable target: "4=4", eq: ["2+2=2+2"], - // they should not be equal ne: ["1=1", "0.5=1/2", "9x=3(3x)"], }, // expressions (no equal or inequality sign) diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 0f91d17..f0dae10 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -206,6 +206,26 @@ export const compareEquations = ( // if y has the same value, for the same x then the expressions should be equivalent equivalence = yFromFirstExpression === yFromSecondExpression; } + + if (equivalence && isInequality) { + // check if direction should be changed + if ( + m.isPositive(firstEquationCoefficients[0]) && + m.isNegative(firstEquationCoefficients[1]) && + m.isNegative(secondEquationCoefficients[0]) && + m.isPositive(secondEquationCoefficients[1]) + ) { + equivalence = false; + } + if ( + m.isNegative(firstEquationCoefficients[0]) && + m.isPositive(firstEquationCoefficients[1]) && + m.isPositive(secondEquationCoefficients[0]) && + m.isNegative(secondEquationCoefficients[1]) + ) { + equivalence = false; + } + } } return equivalence; From 818b71a403644cf5731319e06a7def719714b5ba Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Fri, 8 Oct 2021 15:22:39 +0300 Subject: [PATCH 07/34] try solution for 3 way inequalities equivalence --- .../3-way-inequalities-with-variables.ts | 3 +- .../symbolic/7119-mixed-numbers.ts | 2 +- src/symbolic/compare-compound-inequations.ts | 44 +++++++++++++++++ src/symbolic/index.ts | 48 ++++++++++++++++++- 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/symbolic/compare-compound-inequations.ts diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 7a1a9ef..b4211fe 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,12 +2,13 @@ export default { mode: "symbolic", tests: [ { + only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: [ "3 ≥ 2x>1", "x/x { + if (signName === 'larger'){ + return ">" + } + + return "≥" + } + +const breakInequalities = (compoundInequality: any) => { + let firstInequality: MathNode; + let secondInequality: MathNode; + + if(compoundInequality.conditionals?.length === 2 && compoundInequality.params?.length === 3 ){ + firstInequality = new m.OperatorNode(operation(compoundInequality.conditionals[0]),compoundInequality.conditionals[0],[compoundInequality.params[0],compoundInequality.params[1]] ) + } + + console.log(firstInequality, "first ine") + }; + +export const compareCompoundInequations = (firstInequation:any, secondInequation:any ) => { +let equality: boolean = false; + +//@ts-ignore +const result = firstInequation.conditionals.every((relation: string) => +//@ts-ignore +secondInequation.conditionals.includes(relation) +); + +if (!result) { +equality = false; +} else { +const firstInequalities = breakInequalities(firstInequation); +const secondInequalities = breakInequalities(secondInequation); +} + +console.log(result, "result"); + +return equality; +} \ No newline at end of file diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 3cb7f59..65ac964 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -3,6 +3,7 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; import { sort } from "../node-sort"; import { compareEquations } from "./compare-equations"; +import { compareCompoundInequations } from "./compare-compound-inequations"; const m: any = mathjs; const log = logger("mv:symbolic"); @@ -130,6 +131,25 @@ const normalize = (a: string | MathNode | any) => { return r; }; +const operation = (signName:string) => { + if (signName === 'larger'){ + return ">" + } + + return "≥" +} + +const breakInequalities = (compoundInequality: any) => { + let firstInequality: MathNode; + let secondInequality: MathNode; + + if(compoundInequality.conditionals?.length === 2 && compoundInequality.params?.length === 3 ){ + firstInequality = new m.OperatorNode(operation(compoundInequality.conditionals[0]),compoundInequality.conditionals[0],[compoundInequality.params[0],compoundInequality.params[1]] ) + } + + console.log(firstInequality, "first ine") +}; + export const isMathEqual = (a: any, b: any) => { let as: MathNode; let bs: MathNode; @@ -159,7 +179,6 @@ export const isMathEqual = (a: any, b: any) => { } } - // at this point this is an ideea, it must be tested // if both expressions are inequalities treat greater sign as equal sign if ( (as.fn === "larger" && bs.fn === "larger") || @@ -176,5 +195,32 @@ export const isMathEqual = (a: any, b: any) => { equality = compareEquations(as, bs, true); } + // at this point this is an ideea, it must be tested + // check for compound inequalities + + if ( + //@ts-ignore + as?.conditionals.length === bs?.conditionals.length && + //@ts-ignore + as?.conditionals.length === 2 + ) { + + return compareCompoundInequations(as,bs) + //@ts-ignore + const result = as.conditionals.every((relation: string) => + //@ts-ignore + bs.conditionals.includes(relation) + ); + + if (!result) { + equality = false; + } else { + const firstInequalities = breakInequalities(as); + const secondInequalities = breakInequalities(bs); + } + + console.log(result, "result"); + } + return equality; }; From c33909a065f24a3d50a91654e8641674703e93d4 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Fri, 8 Oct 2021 16:37:08 +0300 Subject: [PATCH 08/34] try solution for 3 way inequalities equivalence --- .../3-way-inequalities-with-variables.ts | 2 +- src/symbolic/index.ts | 32 +------ src/symbolic/utils.ts | 88 +++++++++++++++++++ 3 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 src/symbolic/utils.ts diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index b4211fe..a2e668f 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,7 +2,7 @@ export default { mode: "symbolic", tests: [ { - only:true, + only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: [ diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 65ac964..a25c996 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -139,17 +139,6 @@ const operation = (signName:string) => { return "≥" } -const breakInequalities = (compoundInequality: any) => { - let firstInequality: MathNode; - let secondInequality: MathNode; - - if(compoundInequality.conditionals?.length === 2 && compoundInequality.params?.length === 3 ){ - firstInequality = new m.OperatorNode(operation(compoundInequality.conditionals[0]),compoundInequality.conditionals[0],[compoundInequality.params[0],compoundInequality.params[1]] ) - } - - console.log(firstInequality, "first ine") -}; - export const isMathEqual = (a: any, b: any) => { let as: MathNode; let bs: MathNode; @@ -200,26 +189,13 @@ export const isMathEqual = (a: any, b: any) => { if ( //@ts-ignore - as?.conditionals.length === bs?.conditionals.length && + as?.conditionals?.length === bs?.conditionals?.length && //@ts-ignore - as?.conditionals.length === 2 + as?.conditionals?.length === 2 ) { - return compareCompoundInequations(as,bs) - //@ts-ignore - const result = as.conditionals.every((relation: string) => - //@ts-ignore - bs.conditionals.includes(relation) - ); - - if (!result) { - equality = false; - } else { - const firstInequalities = breakInequalities(as); - const secondInequalities = breakInequalities(bs); - } - - console.log(result, "result"); + equality= compareCompoundInequations(as,bs) + } return equality; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts new file mode 100644 index 0000000..cfef02d --- /dev/null +++ b/src/symbolic/utils.ts @@ -0,0 +1,88 @@ +import { mathjs } from "../mathjs"; +import { MathNode } from "mathjs"; +import { isMathEqual, simplify } from "."; + +const m: any = mathjs; + +// check if equation is valid and find out the number of unknowns and their name +export const getUnknowns = (equation: MathNode) => { + let variableNames: string[] = []; + + equation.traverse(function (node, path, parent) { + if ( + node.isSymbolNode && + node?.name.length === 1 && + !variableNames.includes(node.name) + ) { + variableNames.push(node.name); + } + }); + + variableNames.sort(); + + return variableNames; +}; + +export const getCoefficients = (equation: MathNode) => { + let result: number[] = []; + + try { + const rationalizedEquation = m.rationalize(equation, {}, true); + result = rationalizedEquation.coefficients; + } catch (e) {} + + result = result.length === 0 ? [1, 0] : result; + + return result; +}; + +export const setXToOne = (equation: any, unknownName: string) => { + let result: MathNode; + + result = equation.transform(function (node, path, parent) { + if (node.isSymbolNode && node.name === unknownName) { + return new m.ConstantNode(1); + } else { + return node; + } + }); + + return result; +}; + +// solve x +export const solveLinearEquation = (coefficients: number[]) => { + let result: number; + + // TO DO: solve quadratic equation + if (coefficients.length === 3 && coefficients[0] === 0) { + coefficients = coefficients.splice(1, 2); + } + + if (coefficients.length === 2) { + if (coefficients[0] === 0 && coefficients[1] === 0) { + result = Infinity; + } else if (coefficients[0] === 0) { + result = 0; + } else { + // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity + result = m.divide(coefficients[0], -1 * coefficients[1]); + } + } + + return result; +}; + +export const equationsHaveTheSameUnknowns = ( + firstEquationUnknowns: string[], + secondEquationUnknowns: string[] +) => { + return ( + Array.isArray(firstEquationUnknowns) && + Array.isArray(secondEquationUnknowns) && + firstEquationUnknowns.length === secondEquationUnknowns.length && + firstEquationUnknowns.every( + (unknonwn, index) => unknonwn === secondEquationUnknowns[index] + ) + ); +}; From 5560bf02035fb22356c692d2ec9495729713d142 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 11 Oct 2021 11:46:40 +0300 Subject: [PATCH 09/34] refactor code in order to provide the possibility to reuse functions from compare-equations --- src/__tests__/compare-equations.spec.ts | 2 +- .../3-way-inequalities-with-variables.ts | 2 +- src/symbolic/compare-equations.ts | 136 ++---------------- src/symbolic/index.ts | 2 - src/symbolic/utils.ts | 36 ++++- 5 files changed, 52 insertions(+), 126 deletions(-) diff --git a/src/__tests__/compare-equations.spec.ts b/src/__tests__/compare-equations.spec.ts index 848029a..d6a3e71 100644 --- a/src/__tests__/compare-equations.spec.ts +++ b/src/__tests__/compare-equations.spec.ts @@ -5,7 +5,7 @@ import { getCoefficients, setXToOne, solveLinearEquation, -} from "../symbolic/compare-equations"; +} from "../symbolic/utils"; const lta = new LatexToAst(); const atm = new AstToMathJs(); diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index a2e668f..0386fb3 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,7 +2,7 @@ export default { mode: "symbolic", tests: [ { - only:true, + // only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: [ diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index f0dae10..c4cc575 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -1,137 +1,31 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { isMathEqual, simplify } from "."; +import { isMathEqual } from "."; +import { + getUnknowns, + equationsHaveTheSameUnknowns, + getCoefficients, + solveLinearEquation, + setXToOne, + transformEqualityInExpression, + equationsCanBeCompared, +} from "./utils"; const m: any = mathjs; -// check if equation is valid and find out the number of unknowns and their name -export const getUnknowns = (equation: MathNode) => { - let variableNames: string[] = []; - - equation.traverse(function (node, path, parent) { - if ( - node.isSymbolNode && - node?.name.length === 1 && - !variableNames.includes(node.name) - ) { - variableNames.push(node.name); - } - }); - - variableNames.sort(); - - return variableNames; -}; - -export const getCoefficients = (equation: MathNode) => { - let result: number[] = []; - - try { - const rationalizedEquation = m.rationalize(equation, {}, true); - result = rationalizedEquation.coefficients; - } catch (e) {} - - result = result.length === 0 ? [1, 0] : result; - - return result; -}; - -export const setXToOne = (equation: any, unknownName: string) => { - let result: MathNode; - - result = equation.transform(function (node, path, parent) { - if (node.isSymbolNode && node.name === unknownName) { - return new m.ConstantNode(1); - } else { - return node; - } - }); - - return result; -}; - -// solve x -export const solveLinearEquation = (coefficients: number[]) => { - let result: number; - - // TO DO: solve quadratic equation - if (coefficients.length === 3 && coefficients[0] === 0) { - coefficients = coefficients.splice(1, 2); - } - - if (coefficients.length === 2) { - if (coefficients[0] === 0 && coefficients[1] === 0) { - result = Infinity; - } else if (coefficients[0] === 0) { - result = 0; - } else { - // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity - result = m.divide(coefficients[0], -1 * coefficients[1]); - } - } - - return result; -}; - -export const equationsHaveTheSameUnknowns = ( - firstEquationUnknowns: string[], - secondEquationUnknowns: string[] -) => { - return ( - Array.isArray(firstEquationUnknowns) && - Array.isArray(secondEquationUnknowns) && - firstEquationUnknowns.length === secondEquationUnknowns.length && - firstEquationUnknowns.every( - (unknonwn, index) => unknonwn === secondEquationUnknowns[index] - ) - ); -}; - export const compareEquations = ( firstEquation: MathNode, secondEquation: MathNode, isInequality: boolean ) => { - let noFunctionOrArray: boolean = true; - let firstSymbolNode: boolean = false; - let symbolNode: boolean = false; let equivalence: boolean = false; - firstEquation.traverse(function (node, path, parent) { - noFunctionOrArray = - noFunctionOrArray || node.isFunctionNode || node.isArrayNode; - firstSymbolNode = firstSymbolNode || node.isSymbolNode; - - return node; - }); - - secondEquation.traverse(function (node, path, parent) { - if (node.isFunctionNode || node.isArrayNode) { - noFunctionOrArray = false; - } - - if (node.isSymbolNode && firstSymbolNode) symbolNode = true; - - return node; - }); - - // move the terms of the equations to the left hand side - if (noFunctionOrArray && symbolNode) { - let firstExpression = new m.OperatorNode( - "-", - "subtract", - firstEquation.args - ); - let secondExpression = new m.OperatorNode( - "-", - "subtract", - secondEquation.args - ); - - // remove added/subtracted numbers/variables from both sides of the equation - firstExpression = simplify(firstExpression); - secondExpression = simplify(secondExpression); + if (equationsCanBeCompared(firstEquation,secondEquation)) { + let firstExpression = transformEqualityInExpression(firstEquation); + console.log (firstExpression, "firstexpression") + let secondExpression = transformEqualityInExpression(secondEquation); + console.log (secondEquation, "secondexpression") if (isMathEqual(firstExpression, secondExpression)) { return true; } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index a25c996..0ce1c0c 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -193,9 +193,7 @@ export const isMathEqual = (a: any, b: any) => { //@ts-ignore as?.conditionals?.length === 2 ) { - equality= compareCompoundInequations(as,bs) - } return equality; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index cfef02d..5e15755 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -1,9 +1,43 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { isMathEqual, simplify } from "."; +import { simplify } from "."; const m: any = mathjs; +export const equationsCanBeCompared = (firstEquation: MathNode, secondEquation: MathNode):boolean => { + let noFunctionOrArray: boolean = true; + let firstSymbolNode: boolean = false; + let symbolNode: boolean = false; + + firstEquation.traverse(function (node, path, parent) { + noFunctionOrArray = + noFunctionOrArray || node.isFunctionNode || node.isArrayNode; + firstSymbolNode = firstSymbolNode || node.isSymbolNode; + + return node; + }); + + secondEquation.traverse(function (node, path, parent) { + if (node.isFunctionNode || node.isArrayNode) { + noFunctionOrArray = false; + } + + if (node.isSymbolNode && firstSymbolNode) symbolNode = true; + + return node; + }); + + return noFunctionOrArray && symbolNode; +} + +// move the terms of the equations to the left hand side +export const transformEqualityInExpression = (equality: MathNode) => { + const expression = new m.OperatorNode("-", "subtract", equality.args); + + // remove added/subtracted numbers/variables from both sides of the equation + return simplify(expression); +}; + // check if equation is valid and find out the number of unknowns and their name export const getUnknowns = (equation: MathNode) => { let variableNames: string[] = []; From d1036a194361cb205607dd38568aef3af0b9c5d9 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 11 Oct 2021 15:04:24 +0300 Subject: [PATCH 10/34] break compound inequalities --- .../3-way-inequalities-with-variables.ts | 2 +- src/symbolic/compare-compound-inequations.ts | 92 ++++++++++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 0386fb3..a2e668f 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,7 +2,7 @@ export default { mode: "symbolic", tests: [ { - // only:true, + only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: [ diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 061ea88..0e42e94 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,44 +1,72 @@ import { mathjs } from "../mathjs"; -import { MathNode } from "mathjs"; +import { MathNode } from "mathjs"; const m: any = mathjs; -const operation = (signName:string) => { - if (signName === 'larger'){ - return ">" - } - - return "≥" +export type InequalitiesPairs = { + leftHandInequality: MathNode; + rightHandInequality: MathNode; +}; + +const operation = (signName: string) => { + if (signName === "larger") { + return ">"; } -const breakInequalities = (compoundInequality: any) => { - let firstInequality: MathNode; - let secondInequality: MathNode; - - if(compoundInequality.conditionals?.length === 2 && compoundInequality.params?.length === 3 ){ - firstInequality = new m.OperatorNode(operation(compoundInequality.conditionals[0]),compoundInequality.conditionals[0],[compoundInequality.params[0],compoundInequality.params[1]] ) - } - - console.log(firstInequality, "first ine") + console.log(signName, "signName"); + return "≥"; +}; + +const breakInequality = (compoundInequality: any) => { + let inequalities: InequalitiesPairs; + + inequalities= { + leftHandInequality: new m.OperatorNode( + operation(compoundInequality.conditionals[0]), + compoundInequality.conditionals[0], + [compoundInequality.params[0], compoundInequality.params[1]] + ), + rightHandInequality: new m.OperatorNode( + operation(compoundInequality.conditionals[1]), + compoundInequality.conditionals[0], + [compoundInequality.params[1], compoundInequality.params[2]] + ), }; -export const compareCompoundInequations = (firstInequation:any, secondInequation:any ) => { -let equality: boolean = false; + console.log(compoundInequality.conditionals, "conditionals"); + + console.log( + inequalities.leftHandInequality, + inequalities.rightHandInequality, + "first inequality" + ); -//@ts-ignore -const result = firstInequation.conditionals.every((relation: string) => -//@ts-ignore -secondInequation.conditionals.includes(relation) -); + return inequalities; +}; -if (!result) { -equality = false; -} else { -const firstInequalities = breakInequalities(firstInequation); -const secondInequalities = breakInequalities(secondInequation); -} +export const compareCompoundInequations = ( + firstInequation: any, + secondInequation: any +) => { + let equality: boolean = false; + + //@ts-ignore + const result = firstInequation.conditionals.every((relation: string) => + //@ts-ignore + secondInequation.conditionals.includes(relation) + ); + + if (!result) { + equality = false; + } else if ( + firstInequation.conditionals?.length === 2 && + firstInequation.params?.length === 3 + ) { + const firstInequalities = breakInequality(firstInequation); + const secondInequalities = breakInequality(secondInequation); + } -console.log(result, "result"); + console.log(result, "result"); -return equality; -} \ No newline at end of file + return equality; +}; From 4ae5f6eec00f0d275bb52a1c0958f36fdb76bc52 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 11 Oct 2021 15:24:25 +0300 Subject: [PATCH 11/34] refactor break compound inequalities --- .../3-way-inequalities-with-variables.ts | 4 ++- src/symbolic/compare-compound-inequations.ts | 31 ++++++------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index a2e668f..45a8c6c 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -8,8 +8,10 @@ export default { eq: [ "3 ≥ 2x>1", "x/x { return "≥"; }; -const breakInequality = (compoundInequality: any) => { - let inequalities: InequalitiesPairs; - - inequalities= { +const breakInequality = (compoundInequality: any): InequalitiesPairs => { + return { leftHandInequality: new m.OperatorNode( operation(compoundInequality.conditionals[0]), compoundInequality.conditionals[0], @@ -31,17 +29,7 @@ const breakInequality = (compoundInequality: any) => { compoundInequality.conditionals[0], [compoundInequality.params[1], compoundInequality.params[2]] ), - }; - - console.log(compoundInequality.conditionals, "conditionals"); - - console.log( - inequalities.leftHandInequality, - inequalities.rightHandInequality, - "first inequality" - ); - - return inequalities; + } }; export const compareCompoundInequations = ( @@ -50,20 +38,19 @@ export const compareCompoundInequations = ( ) => { let equality: boolean = false; - //@ts-ignore const result = firstInequation.conditionals.every((relation: string) => - //@ts-ignore - secondInequation.conditionals.includes(relation) + secondInequation.conditionals.includes(relation) && firstInequation.conditionals?.length === 2 && + firstInequation.params?.length === 3 ); if (!result) { equality = false; - } else if ( - firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 - ) { + } else { const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); + + console.log(firstInequalities, "firstInequalities") + console.log(secondInequalities, "secondInequalities") } console.log(result, "result"); From 2f96664f4f6a238dff6018336606eef2871bcbf8 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 11 Oct 2021 17:02:14 +0300 Subject: [PATCH 12/34] find range solution for compound inequalities --- .../3-way-inequalities-with-variables.ts | 2 +- src/symbolic/compare-compound-inequations.ts | 66 ++++++++++++++----- src/symbolic/compare-equations.ts | 4 +- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 45a8c6c..f31f32c 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,7 +2,7 @@ export default { mode: "symbolic", tests: [ { - only:true, + //only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: [ diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index bd15511..360909a 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,6 @@ import { mathjs } from "../mathjs"; -import { MathNode } from "mathjs"; +import { MathNode, number } from "mathjs"; +import { equationsCanBeCompared, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression } from "./utils"; const m: any = mathjs; @@ -8,16 +9,14 @@ export type InequalitiesPairs = { rightHandInequality: MathNode; }; -const operation = (signName: string) => { - if (signName === "larger") { - return ">"; - } - - console.log(signName, "signName"); - return "≥"; +export type xRange = { + inferiorLimit: number; + superiorLimit: number; }; -const breakInequality = (compoundInequality: any): InequalitiesPairs => { +const operation = (signName: string) => (signName === "larger" ? ">" : "≥"); + +const breakInequality = (compoundInequality: any): InequalitiesPairs => { return { leftHandInequality: new m.OperatorNode( operation(compoundInequality.conditionals[0]), @@ -29,7 +28,29 @@ const breakInequality = (compoundInequality: any): InequalitiesPairs => { compoundInequality.conditionals[0], [compoundInequality.params[1], compoundInequality.params[2]] ), + }; +}; + +const findX = (inequality): number => { + let x: number; + // TO DO: sanity checks + let expression = transformEqualityInExpression(inequality); + // TO DO: must chek if we have the same unknowns + + let equationUnknownsName = getUnknowns(expression); + let equationCoefficients: number[]; + + if (equationUnknownsName.length === 1) { + equationCoefficients = getCoefficients(expression); } + + x = solveLinearEquation( + equationCoefficients + ); + + console.log(x, "x") + + return x; }; export const compareCompoundInequations = ( @@ -38,22 +59,33 @@ export const compareCompoundInequations = ( ) => { let equality: boolean = false; - const result = firstInequation.conditionals.every((relation: string) => - secondInequation.conditionals.includes(relation) && firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 + const result = firstInequation.conditionals.every( + (relation: string) => + secondInequation.conditionals.includes(relation) && + firstInequation.conditionals?.length === 2 && + firstInequation.params?.length === 3 && equationsCanBeCompared (firstInequation,secondInequation) ); if (!result) { - equality = false; + return false; } else { const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); - console.log(firstInequalities, "firstInequalities") - console.log(secondInequalities, "secondInequalities") - } + console.log(firstInequalities, "firstInequalities"); + console.log(secondInequalities, "secondInequalities"); + + let firstInequalitiesSolution: xRange = { + inferiorLimit: findX(firstInequalities.rightHandInequality), + superiorLimit: findX(firstInequalities.leftHandInequality), + }; + let secondInequalitiesSolution: xRange = { + inferiorLimit: findX(secondInequalities.rightHandInequality), + superiorLimit: findX(secondInequalities.leftHandInequality), + }; - console.log(result, "result"); + equality = firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit + } return equality; }; diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index c4cc575..9d83637 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -22,10 +22,8 @@ export const compareEquations = ( if (equationsCanBeCompared(firstEquation,secondEquation)) { let firstExpression = transformEqualityInExpression(firstEquation); - console.log (firstExpression, "firstexpression") - let secondExpression = transformEqualityInExpression(secondEquation); - console.log (secondEquation, "secondexpression") + if (isMathEqual(firstExpression, secondExpression)) { return true; } From 15e622e755cd0c48f91c0b21a28019d819826ea4 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 12 Oct 2021 12:48:22 +0300 Subject: [PATCH 13/34] compound-inequations-equivalence: add tests, add sanity checks for coefficients, check if we have same unknowns in both inequation before compare --- .../3-way-inequalities-with-variables.ts | 11 ++----- .../symbolic/7119-mixed-numbers.ts | 3 +- src/symbolic/compare-compound-inequations.ts | 31 +++++++++++++------ src/symbolic/index.ts | 4 +++ src/symbolic/utils.ts | 8 ++++- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index f31f32c..b82c3b8 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,16 +2,11 @@ export default { mode: "symbolic", tests: [ { - //only:true, + // only:true, // 3-way inequality with variable(s) target: "1<2x ≤ 3", - eq: [ - "3 ≥ 2x>1", - "x/x1", "x/x { }; }; -const findX = (inequality): number => { - let x: number; +const findX = (inequality: MathNode): number => { // TO DO: sanity checks let expression = transformEqualityInExpression(inequality); // TO DO: must chek if we have the same unknowns @@ -44,13 +43,9 @@ const findX = (inequality): number => { equationCoefficients = getCoefficients(expression); } - x = solveLinearEquation( + return solveLinearEquation( equationCoefficients ); - - console.log(x, "x") - - return x; }; export const compareCompoundInequations = ( @@ -69,11 +64,27 @@ export const compareCompoundInequations = ( if (!result) { return false; } else { + const firstInequalityUnknownsName = getUnknowns(firstInequation); + const secondInequalityUnknownsName = getUnknowns(secondInequation); + + console.log(firstInequalityUnknownsName, secondInequalityUnknownsName) + + if ( + !equationsHaveTheSameUnknowns( + firstInequalityUnknownsName, + secondInequalityUnknownsName + ) + ) { + return false; + } + + + const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); - console.log(firstInequalities, "firstInequalities"); - console.log(secondInequalities, "secondInequalities"); + // console.log(firstInequalities, "firstInequalities"); + // console.log(secondInequalities, "secondInequalities"); let firstInequalitiesSolution: xRange = { inferiorLimit: findX(firstInequalities.rightHandInequality), diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 0ce1c0c..2cad6de 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -184,6 +184,10 @@ export const isMathEqual = (a: any, b: any) => { equality = compareEquations(as, bs, true); } + if (equality) { + return true; + } + // at this point this is an ideea, it must be tested // check for compound inequalities diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 5e15755..829f496 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -59,6 +59,7 @@ export const getUnknowns = (equation: MathNode) => { export const getCoefficients = (equation: MathNode) => { let result: number[] = []; + // coefficients will be determined if equation has only one unknown try { const rationalizedEquation = m.rationalize(equation, {}, true); @@ -84,11 +85,16 @@ export const setXToOne = (equation: any, unknownName: string) => { return result; }; + // TO DO: solve quadratic equation + // solve x export const solveLinearEquation = (coefficients: number[]) => { let result: number; - // TO DO: solve quadratic equation + if (!coefficients) { + return undefined; + } + if (coefficients.length === 3 && coefficients[0] === 0) { coefficients = coefficients.splice(1, 2); } From 71672beb920b896461c496d2f2ef8df8dbacd0f4 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 12 Oct 2021 15:26:14 +0300 Subject: [PATCH 14/34] add tests, another sollution must be found for rationalization/get coefficients --- .../3-way-inequalities-with-variables.ts | 23 +++++++++++++++++-- src/symbolic/compare-compound-inequations.ts | 8 +++++-- src/symbolic/index.ts | 22 ++++++------------ src/symbolic/utils.ts | 3 ++- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index b82c3b8..dac50fc 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -2,11 +2,30 @@ export default { mode: "symbolic", tests: [ { - // only:true, - // 3-way inequality with variable(s) target: "1<2x ≤ 3", eq: ["3 ≥ 2x>1", "x/x0"], + ne: ["0 < 4x ≤ 22", "66 ≥ 6x>1"], + }, + { + only:true, + // 3-way inequality with variable(s) + target: "-4 < 2x - 8 ≤ 10", + eq: [ + //"-2 < x - 4 ≤ 5", + "-4/4 < (2x - 8)/4 ≤ 10/4", + "-1 < x/2 - 2 ≤ 5/2", + //" 5 ≥ -4 + x > -1 - 1" + ], + }, ], }; diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index c2f4b2e..62b7912 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -34,6 +34,7 @@ const breakInequality = (compoundInequality: any): InequalitiesPairs => { const findX = (inequality: MathNode): number => { // TO DO: sanity checks let expression = transformEqualityInExpression(inequality); + let result: number; // TO DO: must chek if we have the same unknowns let equationUnknownsName = getUnknowns(expression); @@ -43,9 +44,12 @@ const findX = (inequality: MathNode): number => { equationCoefficients = getCoefficients(expression); } - return solveLinearEquation( + result = solveLinearEquation( equationCoefficients ); + + console.log(result, "x") + return result }; export const compareCompoundInequations = ( @@ -68,7 +72,7 @@ export const compareCompoundInequations = ( const secondInequalityUnknownsName = getUnknowns(secondInequation); console.log(firstInequalityUnknownsName, secondInequalityUnknownsName) - + if ( !equationsHaveTheSameUnknowns( firstInequalityUnknownsName, diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 2cad6de..7ee161b 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -111,13 +111,13 @@ const normalize = (a: string | MathNode | any) => { log("[normalize] input: ", a.toString(), "output: ", r.toString()); // check for infinity - if ( - r.toString() === "Infinity" || - +r.toString() >= positiveInfinity || - +r.toString() <= negativeInfinity - ) { - r = new m.SymbolNode("Infinity"); - } + // if ( + // r.toString() === "Infinity" || + // +r.toString() >= positiveInfinity || + // +r.toString() <= negativeInfinity + // ) { + // r = new m.SymbolNode("Infinity"); + // } if (r.value) { r.value = new m.Fraction(Math.round(r.value * 10000) / 10000); @@ -131,14 +131,6 @@ const normalize = (a: string | MathNode | any) => { return r; }; -const operation = (signName:string) => { - if (signName === 'larger'){ - return ">" - } - - return "≥" -} - export const isMathEqual = (a: any, b: any) => { let as: MathNode; let bs: MathNode; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 829f496..6d6fd30 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -61,6 +61,7 @@ export const getCoefficients = (equation: MathNode) => { let result: number[] = []; // coefficients will be determined if equation has only one unknown + console.log(equation.toTex(), "equation in get coefficients") try { const rationalizedEquation = m.rationalize(equation, {}, true); result = rationalizedEquation.coefficients; @@ -86,7 +87,7 @@ export const setXToOne = (equation: any, unknownName: string) => { }; // TO DO: solve quadratic equation - + // solve x export const solveLinearEquation = (coefficients: number[]) => { let result: number; From a9f3515fa7a683807d9d9bc2a59e9be45b420fb6 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 13 Oct 2021 14:26:20 +0300 Subject: [PATCH 15/34] fix: getCoefficients for expressions with unknown isolated in fraction --- .../3-way-inequalities-with-variables.ts | 4 +-- src/symbolic/index.ts | 14 ++++---- src/symbolic/utils.ts | 33 ++++++++++++++----- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index dac50fc..b33516d 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -17,12 +17,12 @@ export default { ne: ["0 < 4x ≤ 22", "66 ≥ 6x>1"], }, { - only:true, + // only:true, // 3-way inequality with variable(s) target: "-4 < 2x - 8 ≤ 10", eq: [ //"-2 < x - 4 ≤ 5", - "-4/4 < (2x - 8)/4 ≤ 10/4", + // "-4/4 < (2x - 8)/4 ≤ 10/4", "-1 < x/2 - 2 ≤ 5/2", //" 5 ≥ -4 + x > -1 - 1" ], diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 7ee161b..cff9117 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -111,13 +111,13 @@ const normalize = (a: string | MathNode | any) => { log("[normalize] input: ", a.toString(), "output: ", r.toString()); // check for infinity - // if ( - // r.toString() === "Infinity" || - // +r.toString() >= positiveInfinity || - // +r.toString() <= negativeInfinity - // ) { - // r = new m.SymbolNode("Infinity"); - // } + if ( + r.toString() === "Infinity" || + +r.toString() >= positiveInfinity || + +r.toString() <= negativeInfinity + ) { + r = new m.SymbolNode("Infinity"); + } if (r.value) { r.value = new m.Fraction(Math.round(r.value * 10000) / 10000); diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 6d6fd30..a8977b4 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -1,10 +1,14 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { simplify } from "."; +import {simplify as customSimplify} from "./" +const { simplify } = mathjs; const m: any = mathjs; -export const equationsCanBeCompared = (firstEquation: MathNode, secondEquation: MathNode):boolean => { +export const equationsCanBeCompared = ( + firstEquation: MathNode, + secondEquation: MathNode +): boolean => { let noFunctionOrArray: boolean = true; let firstSymbolNode: boolean = false; let symbolNode: boolean = false; @@ -25,17 +29,18 @@ export const equationsCanBeCompared = (firstEquation: MathNode, secondEquation: if (node.isSymbolNode && firstSymbolNode) symbolNode = true; return node; - }); + }); return noFunctionOrArray && symbolNode; -} +}; // move the terms of the equations to the left hand side export const transformEqualityInExpression = (equality: MathNode) => { const expression = new m.OperatorNode("-", "subtract", equality.args); // remove added/subtracted numbers/variables from both sides of the equation - return simplify(expression); + // @ts-ignore + return customSimplify(expression); }; // check if equation is valid and find out the number of unknowns and their name @@ -58,14 +63,26 @@ export const getUnknowns = (equation: MathNode) => { }; export const getCoefficients = (equation: MathNode) => { + // add sanity check: rationalize is possible if we have only one unknown let result: number[] = []; // coefficients will be determined if equation has only one unknown - console.log(equation.toTex(), "equation in get coefficients") + console.log(equation.toTex(), "equation in get coefficients"); try { const rationalizedEquation = m.rationalize(equation, {}, true); + console.log(rationalizedEquation, "rationalizes"); result = rationalizedEquation.coefficients; - } catch (e) {} + } catch (e) { + equation = simplify(equation, [ + { l: "(n1-n2)/n3", r: "n1/n3-n2/n3" }, + { l: "(n1+n2)/n3", r: "n1/n3+n2/n3" }, + { l: "(n1-n2)*n3/n4", r: "(n1*n3)/n4-(n2*n3)/n4" }, + ]); + try { + const rat = m.rationalize(equation, {}, true); + result = rat.coefficients; + } catch(e) {} + } result = result.length === 0 ? [1, 0] : result; @@ -86,7 +103,7 @@ export const setXToOne = (equation: any, unknownName: string) => { return result; }; - // TO DO: solve quadratic equation +// TO DO: solve quadratic equation // solve x export const solveLinearEquation = (coefficients: number[]) => { From 1dad1b33b7e6e91eaf124eddd8d7e44903470b7f Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Fri, 15 Oct 2021 16:05:39 +0300 Subject: [PATCH 16/34] fix: getCoefficients for expressions with unknown isolated in fractions, add custom simplify rules before rationalize --- .../2-way-inequalities-with-variables.ts | 25 +++++++++++++++++++ .../3-way-inequalities-with-variables.ts | 7 +++--- .../symbolic/7119-mixed-numbers.ts | 3 +-- src/symbolic/compare-compound-inequations.ts | 7 ------ src/symbolic/compare-equations.ts | 2 +- src/symbolic/index.ts | 18 ++++++++++--- src/symbolic/utils.ts | 6 +++-- 7 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 4a21f60..9fdd49e 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -56,5 +56,30 @@ export default { ], ne: ["-2x-3y≥-7", "3x + 6y≤14-x","x + 1.5y≤3.5","x + 1.5y=3.5"] }, + { + target: "\\frac{2x+4}{10}>4", + eq: ["\\frac{x+2}{5}>4", "\\frac{x}{5}+\\frac{2}{5}>4"], + ne: ["\\frac{x+3}{5}>4", "\\frac{2x}{5}+\\frac{2}{5}>4"] + }, + { + // only:true, + target: "\\frac{(2x-3)+9(4)}{4}≥\\frac{9+4x}{3}", + eq: ["\\frac{2x-3+36}{4}≥\\frac{9+4x}{3}", "\\frac{2x+33}{4}≥\\frac{9+4x}{3}"], + ne: [ + "\\frac{2x+3+36}{4}≥\\frac{9+4x}{3}", + "\\frac{2x-33}{4}≥\\frac{9+4x}{3}" + ], + }, + { + + target: "\\frac{5x-2}{3}-\\frac{7x-3}{5}>\\frac{x}{4}", + eq: ["\\frac{5(5x-2)-3(7x-3)}{15}>\\frac{x}{4}"], + ne: ["\\frac{5.1(5x-2)-3(7x-3)}{15}>\\frac{x}{4}"], + }, + { + target: "\\frac{x}{4}+1>\\frac{1}{2}", + eq: ["\\frac{x*4}{4}+1*4>\\frac{1*4}{2}", "x+4>2","x>-2"], + ne: ["x>-1","x≥-2"] + }, ], }; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index b33516d..96ddb1a 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -17,14 +17,13 @@ export default { ne: ["0 < 4x ≤ 22", "66 ≥ 6x>1"], }, { - // only:true, // 3-way inequality with variable(s) target: "-4 < 2x - 8 ≤ 10", eq: [ - //"-2 < x - 4 ≤ 5", - // "-4/4 < (2x - 8)/4 ≤ 10/4", + "-2 < x - 4 ≤ 5", + "-4/4 < (2x - 8)/4 ≤ 10/4", "-1 < x/2 - 2 ≤ 5/2", - //" 5 ≥ -4 + x > -1 - 1" + " 5 ≥ -4 + x > -1 - 1" ], }, ], diff --git a/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts b/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts index 07db83d..bb0a50b 100644 --- a/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts +++ b/src/fixtures/latex-equal/symbolic/7119-mixed-numbers.ts @@ -27,12 +27,11 @@ export default { ], }, { - // only:true, target: "4\\frac{1}{2} + b + ca", eq: [ "\\frac{10}{2} + ac + b- 0.5" ], - // ne: ["\\frac{10}{2} + ac + b + 0.5"], + ne: ["\\frac{10}{2} + ac + b + 0.5"], }, ], }; diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 62b7912..6e715d3 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -48,7 +48,6 @@ const findX = (inequality: MathNode): number => { equationCoefficients ); - console.log(result, "x") return result }; @@ -71,8 +70,6 @@ export const compareCompoundInequations = ( const firstInequalityUnknownsName = getUnknowns(firstInequation); const secondInequalityUnknownsName = getUnknowns(secondInequation); - console.log(firstInequalityUnknownsName, secondInequalityUnknownsName) - if ( !equationsHaveTheSameUnknowns( firstInequalityUnknownsName, @@ -82,13 +79,9 @@ export const compareCompoundInequations = ( return false; } - - const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); - // console.log(firstInequalities, "firstInequalities"); - // console.log(secondInequalities, "secondInequalities"); let firstInequalitiesSolution: xRange = { inferiorLimit: findX(firstInequalities.rightHandInequality), diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 9d83637..a8fab1b 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { isMathEqual } from "."; +import { isMathEqual, simplify } from "."; import { getUnknowns, equationsHaveTheSameUnknowns, diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index cff9117..074bd42 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -140,10 +140,14 @@ export const isMathEqual = (a: any, b: any) => { bs = b.conditionals ? normalize(b) : sort(normalize(b)); - log("[isMathEqual]", as.toString(), "==?", bs.toString()); + console.log("[isMathEqual]", as.toString(), "==?", bs.toString()); + const isSortingEnough = sort(a).equals(sort(b)); + console.log(isSortingEnough, "isSortingEnough") const isTexEnough = as.toTex().trim() === bs.toTex().trim(); + console.log(isTexEnough, "isTexEnough") +console.log(as.equals(bs), "equality") let equality = isTexEnough || as.equals(bs) || isSortingEnough; @@ -157,6 +161,8 @@ export const isMathEqual = (a: any, b: any) => { if (equality) { return true; + } else { + return false } } @@ -174,11 +180,15 @@ export const isMathEqual = (a: any, b: any) => { bs.op = "="; equality = compareEquations(as, bs, true); - } - if (equality) { - return true; + if (equality) { + return true; + } else { + return false + } } + + // at this point this is an ideea, it must be tested // check for compound inequalities diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index a8977b4..bd8013f 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -67,17 +67,19 @@ export const getCoefficients = (equation: MathNode) => { let result: number[] = []; // coefficients will be determined if equation has only one unknown - console.log(equation.toTex(), "equation in get coefficients"); + console.log(equation.toString(), "equation in get coefficients") try { const rationalizedEquation = m.rationalize(equation, {}, true); - console.log(rationalizedEquation, "rationalizes"); result = rationalizedEquation.coefficients; } catch (e) { equation = simplify(equation, [ { l: "(n1-n2)/n3", r: "n1/n3-n2/n3" }, { l: "(n1+n2)/n3", r: "n1/n3+n2/n3" }, { l: "(n1-n2)*n3/n4", r: "(n1*n3)/n4-(n2*n3)/n4" }, + { l: "(n1+n2)*n3/n4", r: "(n1*n3)/n4+(n2*n3)/n4" }, ]); + + console.log(equation.toString(), "after simplify") try { const rat = m.rationalize(equation, {}, true); result = rat.coefficients; From df4767466877ce4f3c7c096de0d19c8d9ee39e45 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 18 Oct 2021 09:53:48 +0300 Subject: [PATCH 17/34] add tests --- .../2-way-inequalities-with-variables.ts | 7 +++- .../3-way-inequalities-with-variables.ts | 41 ++++++++++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 9fdd49e..4945799 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -62,7 +62,6 @@ export default { ne: ["\\frac{x+3}{5}>4", "\\frac{2x}{5}+\\frac{2}{5}>4"] }, { - // only:true, target: "\\frac{(2x-3)+9(4)}{4}≥\\frac{9+4x}{3}", eq: ["\\frac{2x-3+36}{4}≥\\frac{9+4x}{3}", "\\frac{2x+33}{4}≥\\frac{9+4x}{3}"], ne: [ @@ -71,7 +70,6 @@ export default { ], }, { - target: "\\frac{5x-2}{3}-\\frac{7x-3}{5}>\\frac{x}{4}", eq: ["\\frac{5(5x-2)-3(7x-3)}{15}>\\frac{x}{4}"], ne: ["\\frac{5.1(5x-2)-3(7x-3)}{15}>\\frac{x}{4}"], @@ -81,5 +79,10 @@ export default { eq: ["\\frac{x*4}{4}+1*4>\\frac{1*4}{2}", "x+4>2","x>-2"], ne: ["x>-1","x≥-2"] }, + { + target: "\\frac{-3}{4}m-\\frac{1}{8} ≤ - \\frac{1}{4}", + eq: ["8*(-\\frac{3}{4}m-\\frac{1}{8}) ≤ - \\frac{1}{4} * 8", "8(-\\frac{3}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", "-6m-1≤-2", "-6m-1+1≤-2+1", "m ≥ \\frac{1}{6}"], + ne: ["8(-\\frac{5}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", "8(-\\frac{3}{4}m)-8(\\frac{2}{8})≤ 8(-\\frac{1}{4})"] + }, ], }; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 96ddb1a..b02a0ea 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -8,8 +8,12 @@ export default { }, { target: "-5 < 3x + 7 ≤ 22", - eq: ["-5/2< 3x/2 +3.5 ≤ 11", "-10 < 6x + 14 ≤ 44", "-5*2 < (3x+7)*2 ≤ 88/2"], - ne: ["-5*2 < (3x+7)*0 ≤ 88/2", "-5 < 3z + 7 ≤ 22",], + eq: [ + "-5/2< 3x/2 +3.5 ≤ 11", + "-10 < 6x + 14 ≤ 44", + "-5*2 < (3x+7)*2 ≤ 88/2", + ], + ne: ["-5*2 < (3x+7)*0 ≤ 88/2", "-5 < 3z + 7 ≤ 22"], }, { target: "0 < 3x ≤ 33", @@ -17,13 +21,38 @@ export default { ne: ["0 < 4x ≤ 22", "66 ≥ 6x>1"], }, { - // 3-way inequality with variable(s) target: "-4 < 2x - 8 ≤ 10", eq: [ - "-2 < x - 4 ≤ 5", - "-4/4 < (2x - 8)/4 ≤ 10/4", + "-2 < x - 4 ≤ 5", + "-4/4 < (2x - 8)/4 ≤ 10/4", "-1 < x/2 - 2 ≤ 5/2", - " 5 ≥ -4 + x > -1 - 1" + " 5 ≥ -4 + x > -1 - 1", + ], + }, + { + only: true, + target: "-3 < 2x +5 < 17", + eq: [ + "-3-5 < 2x +5-5 < 17-5", + "-8 < 2x < 12", + "\\frac{-8}{2}<\\frac{2x}{2}<\\frac{12}{2}", + ], + }, + { + only: true, + target: "-5 ≤ 3 - 2z ≤ 5", + eq: [ + "-8 ≤ -2z ≤ 2", + // not validating + //"4 ≥ z ≥ -1" + ], + }, + { + only: true, + target: "57.06 ≤ 24.74 + 1.54b ≤ 171.02", + eq: [ + // not validating + // "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", ], }, ], From b907eccaa451423e58427c37cd9a85659e2fb67c Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 18 Oct 2021 10:40:21 +0300 Subject: [PATCH 18/34] increase error acceptance in difference cutoff for comound inequalities manipulation --- src/difference.ts | 4 +- .../2-way-inequalities-with-variables.ts | 45 ++++++++++--------- .../3-way-inequalities-with-variables.ts | 9 ++-- src/symbolic/utils.ts | 2 + 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/difference.ts b/src/difference.ts index 04035ae..bb82f0d 100644 --- a/src/difference.ts +++ b/src/difference.ts @@ -6,8 +6,8 @@ const log = debug("difference"); export const differenceIsTooGreat = (a: RawAst, b: RawAst) => { const smallest = Math.min(a.toString().length, b.toString().length); const biggest = Math.max(a.toString().length, b.toString().length); - const errorAcceptance = 6; - const limit = (1 / smallest) * 100 + 10 + errorAcceptance; + const errorAcceptance = 27; + const limit = (1 / smallest) * 100 + errorAcceptance; const diff = biggest - smallest; log("a:", a.toString(), "b:", b.toString(), "limit:", limit, "diff:", diff); diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 4945799..4cb176a 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -1,7 +1,7 @@ export default { mode: "symbolic", tests: [ - // 2-way inequalities in one variable + // 2-way inequalities in one variable { target: "2x<4", eq: ["4>2x", "x+x < 2*2", "x<2"], @@ -25,16 +25,13 @@ export default { { target: "2x+y≤4+y", eq: ["4≥2x", "x+x ≤ 2*2", "4x≤8", "2x+y-y-4≤0"], - ne: ["x<2", "3x≤4", "x<3", "-2x-y≤-4-y"], + ne: ["x<2", "3x≤4", "x<3", "-2x-y≤-4-y"], }, // 2-way inequalities in two variables { target: "2x+3y≤4+y", eq: ["4+y≥2x+3y", "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y"], - ne: [ - "x+y<2", "3x+2y≤4", "x+4y≤3", - "-2x-3y≤-4-y" - ], + ne: ["x+y<2", "3x+2y≤4", "x+4y≤3", "-2x-3y≤-4-y"], }, { target: "5.5x + 8y≥100", @@ -49,24 +46,23 @@ export default { }, { target: "2x + 3y≥7", - eq: [ - "x + 1.5y≥3.5", - "3x + 6y≥14-x", - "-2x-3y≤-7" - ], - ne: ["-2x-3y≥-7", "3x + 6y≤14-x","x + 1.5y≤3.5","x + 1.5y=3.5"] + eq: ["x + 1.5y≥3.5", "3x + 6y≥14-x", "-2x-3y≤-7"], + ne: ["-2x-3y≥-7", "3x + 6y≤14-x", "x + 1.5y≤3.5", "x + 1.5y=3.5"], }, { target: "\\frac{2x+4}{10}>4", eq: ["\\frac{x+2}{5}>4", "\\frac{x}{5}+\\frac{2}{5}>4"], - ne: ["\\frac{x+3}{5}>4", "\\frac{2x}{5}+\\frac{2}{5}>4"] + ne: ["\\frac{x+3}{5}>4", "\\frac{2x}{5}+\\frac{2}{5}>4"], }, { target: "\\frac{(2x-3)+9(4)}{4}≥\\frac{9+4x}{3}", - eq: ["\\frac{2x-3+36}{4}≥\\frac{9+4x}{3}", "\\frac{2x+33}{4}≥\\frac{9+4x}{3}"], + eq: [ + "\\frac{2x-3+36}{4}≥\\frac{9+4x}{3}", + "\\frac{2x+33}{4}≥\\frac{9+4x}{3}", + ], ne: [ - "\\frac{2x+3+36}{4}≥\\frac{9+4x}{3}", - "\\frac{2x-33}{4}≥\\frac{9+4x}{3}" + "\\frac{2x+3+36}{4}≥\\frac{9+4x}{3}", + "\\frac{2x-33}{4}≥\\frac{9+4x}{3}", ], }, { @@ -76,13 +72,22 @@ export default { }, { target: "\\frac{x}{4}+1>\\frac{1}{2}", - eq: ["\\frac{x*4}{4}+1*4>\\frac{1*4}{2}", "x+4>2","x>-2"], - ne: ["x>-1","x≥-2"] + eq: ["\\frac{x*4}{4}+1*4>\\frac{1*4}{2}", "x+4>2", "x>-2"], + ne: ["x>-1", "x≥-2"], }, { target: "\\frac{-3}{4}m-\\frac{1}{8} ≤ - \\frac{1}{4}", - eq: ["8*(-\\frac{3}{4}m-\\frac{1}{8}) ≤ - \\frac{1}{4} * 8", "8(-\\frac{3}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", "-6m-1≤-2", "-6m-1+1≤-2+1", "m ≥ \\frac{1}{6}"], - ne: ["8(-\\frac{5}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", "8(-\\frac{3}{4}m)-8(\\frac{2}{8})≤ 8(-\\frac{1}{4})"] + eq: [ + "8*(-\\frac{3}{4}m-\\frac{1}{8}) ≤ - \\frac{1}{4} * 8", + "8(-\\frac{3}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", + "-6m-1≤-2", + "-6m-1+1≤-2+1", + "m ≥ \\frac{1}{6}", + ], + ne: [ + "8(-\\frac{5}{4}m)-8(\\frac{1}{8})≤ 8(-\\frac{1}{4})", + "8(-\\frac{3}{4}m)-8(\\frac{2}{8})≤ 8(-\\frac{1}{4})", + ], }, ], }; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index b02a0ea..d715486 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -30,7 +30,6 @@ export default { ], }, { - only: true, target: "-3 < 2x +5 < 17", eq: [ "-3-5 < 2x +5-5 < 17-5", @@ -39,20 +38,20 @@ export default { ], }, { - only: true, + //only: true, target: "-5 ≤ 3 - 2z ≤ 5", eq: [ "-8 ≤ -2z ≤ 2", // not validating - //"4 ≥ z ≥ -1" + "4 ≥ z ≥ -1" ], }, { only: true, target: "57.06 ≤ 24.74 + 1.54b ≤ 171.02", eq: [ - // not validating - // "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", + // increase error acceptance in difference for this: + "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", ], }, ], diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index bd8013f..deb4c23 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -67,6 +67,7 @@ export const getCoefficients = (equation: MathNode) => { let result: number[] = []; // coefficients will be determined if equation has only one unknown + console.log(equation.toString(), "equation in get coefficients") try { const rationalizedEquation = m.rationalize(equation, {}, true); @@ -87,6 +88,7 @@ export const getCoefficients = (equation: MathNode) => { } result = result.length === 0 ? [1, 0] : result; +console.log(result, "result") return result; }; From 8d0156b6ed6fa8747c67ae78cac9bf4114b3c446 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 18 Oct 2021 12:03:40 +0300 Subject: [PATCH 19/34] fix: round solution for equations --- .../symbolic/2-way-inequalities-with-variables.ts | 1 + .../symbolic/3-way-inequalities-with-variables.ts | 7 +++---- src/symbolic/utils.ts | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 4cb176a..f2bf832 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -76,6 +76,7 @@ export default { ne: ["x>-1", "x≥-2"], }, { + // only:true, target: "\\frac{-3}{4}m-\\frac{1}{8} ≤ - \\frac{1}{4}", eq: [ "8*(-\\frac{3}{4}m-\\frac{1}{8}) ≤ - \\frac{1}{4} * 8", diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index d715486..988fe25 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -43,15 +43,14 @@ export default { eq: [ "-8 ≤ -2z ≤ 2", // not validating - "4 ≥ z ≥ -1" + "4 ≥ z ≥ -1" ], }, { - only: true, target: "57.06 ≤ 24.74 + 1.54b ≤ 171.02", eq: [ - // increase error acceptance in difference for this: - "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", + "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", + "32.32 ≤1.54b ≤146.28 " ], }, ], diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index deb4c23..a391390 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -88,6 +88,8 @@ export const getCoefficients = (equation: MathNode) => { } result = result.length === 0 ? [1, 0] : result; + + console.log(result, "result") return result; @@ -128,7 +130,8 @@ export const solveLinearEquation = (coefficients: number[]) => { result = 0; } else { // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity - result = m.divide(coefficients[0], -1 * coefficients[1]); + // Math.round(coefficient * 100000) / 100000) + result = Math.round(m.divide(coefficients[0], -1 * coefficients[1]) * 10000) / 10000; } } From 599e2e9be2153d7e4a38614a8b82a0ec57d7ca2a Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 18 Oct 2021 14:56:30 +0300 Subject: [PATCH 20/34] fix: make sure that inferior and superior limit of inequalities set results are always in the correct order --- .../2-way-inequalities-with-variables.ts | 1 - .../3-way-inequalities-with-variables.ts | 6 ++-- src/symbolic/compare-compound-inequations.ts | 30 +++++++++++++++---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index f2bf832..4cb176a 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -76,7 +76,6 @@ export default { ne: ["x>-1", "x≥-2"], }, { - // only:true, target: "\\frac{-3}{4}m-\\frac{1}{8} ≤ - \\frac{1}{4}", eq: [ "8*(-\\frac{3}{4}m-\\frac{1}{8}) ≤ - \\frac{1}{4} * 8", diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 988fe25..372c726 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -38,11 +38,11 @@ export default { ], }, { - //only: true, + // only: true, target: "-5 ≤ 3 - 2z ≤ 5", eq: [ - "-8 ≤ -2z ≤ 2", - // not validating + "-8 ≤ -2z ≤ 2", + // not validating "4 ≥ z ≥ -1" ], }, diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 6e715d3..4ba26ee 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,5 @@ import { mathjs } from "../mathjs"; -import { MathNode, number } from "mathjs"; +import math, { MathNode, number } from "mathjs"; import { equationsCanBeCompared, equationsHaveTheSameUnknowns, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression } from "./utils"; const m: any = mathjs; @@ -51,6 +51,19 @@ const findX = (inequality: MathNode): number => { return result }; + +export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):number => { + const xFirstInequality = findX(expressionsPair.rightHandInequality); + const xSecondInequality = findX(expressionsPair.leftHandInequality); + + if (limitType === "inferior") { + return Math.min(xFirstInequality,xSecondInequality) + } else { + return Math.max(xFirstInequality,xSecondInequality) + } +} + + export const compareCompoundInequations = ( firstInequation: any, secondInequation: any @@ -79,18 +92,25 @@ export const compareCompoundInequations = ( return false; } + console.log(firstInequation.toString(), "first inequation") + console.log(secondInequation.toString(), "second inequation") + const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); + console.log(secondInequalities.leftHandInequality, secondInequalities.rightHandInequality, "second inequalities") + let firstInequalitiesSolution: xRange = { - inferiorLimit: findX(firstInequalities.rightHandInequality), - superiorLimit: findX(firstInequalities.leftHandInequality), + inferiorLimit: getLimit(firstInequalities, "inferior"), + superiorLimit:getLimit(firstInequalities, "superior"), }; + console.log(firstInequalitiesSolution) let secondInequalitiesSolution: xRange = { - inferiorLimit: findX(secondInequalities.rightHandInequality), - superiorLimit: findX(secondInequalities.leftHandInequality), + inferiorLimit:getLimit(secondInequalities, "inferior"), + superiorLimit: getLimit(secondInequalities, "superior"), }; + console.log(secondInequalitiesSolution) equality = firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit } From ddf2d6659d1db67f31e6346593b12673750e340d Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 18 Oct 2021 15:57:58 +0300 Subject: [PATCH 21/34] update readme --- README.md | 37 +++++++++++++++++--- src/symbolic/compare-compound-inequations.ts | 9 +---- src/symbolic/index.ts | 16 +++------ src/symbolic/utils.ts | 8 +---- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index adbb2f8..460025d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ - an api - a large set of test data +- aims to determine if two mathematical expressions are equal, either having the same form or not - have a public website where you can add some math and check if it works - also the link should be shareable @@ -29,7 +30,6 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts ## Next Steps -* Check that we have a test case for any outstanding jira tickets relating to math-validation. * Check that api we expose will support what is needed. * Do triage on the test failures, add a note to failing test so we can build a picture of the work needed @@ -52,6 +52,18 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts * more advanced literal validation (todo) * block input that is clearly too large/unrelated (eg: a user can type in gobbledy-gook - we should just abort if we see that) +## Capabilities + +- comparing linear equations in one variable +- linear equations in two variables +- 2-way inequalities with one or two unknown variables +- compound inequalities in one variable +- trigonometric identities or functions +- inverse trigonometric functions +- it can also handle degrees, radians and gradians +- recognises similar notation for logarithms and based logarithms + + ### things that'd be great (but we may have to park until we have more time) * a faster latex parser @@ -61,18 +73,33 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts There are 2 modes - literal and symbolic -Literal: needs to more advanced than the legacy literal implementation which was essentially a string check. +Literal: is at its most basic form very similar to a string validation + +Literal will ignore spaces and parentheses as long as they do not change the meaning of operations + +For example “a+7 +b” will not validate against “7 + a+b” but will validate against “ ((a) + (7))+b ”. +It will accept commas for decimal marks. For example “1,000” will be equivalent with 1000. +Validation will ignore leading zeros: “0.1” will validate against “.1” + +Literal Validation offers two configuration options that can be used to validate some variety of forms for an expression: + +Ignore trailing zeros option; allows the evaluation to accept zeros to the right of the decimal place “4.5” will validate against “4.50000000000000” +Ignore order option; makes validation indifferent to the variables order, as long as it does not change operations meaning. In this case “a+7 +b*c” will validate against “7 + a+bc”, but not against “ac+7+b” + + +Symbolic: attempts to decide if expressions are mathematically equivalent or not + +The second type of validation, and the most relevant in mathematics, is symbolic validation. By default, it offers all configurations presented for literal validation and is more relevant in mathematics + +In order to check equivalence between 2 expressions, we have to reduce both expressions to the simplest one. Then distribute all coefficients, combine any like terms on each side of the expression, and arrange them in the same order. -Symbolic: ### Notes * `@babel/runtime` is a devDependency if you ever need to link this repo to another package for testing ## TODO -* strip logs on compile * set up api that is compatible w/ ui component options -* start going through the tests, build up literal + symbolic a bit att the start * derivatives kind of work and kind of not - how to use? ### CI diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 4ba26ee..4b714ea 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -92,25 +92,18 @@ export const compareCompoundInequations = ( return false; } - console.log(firstInequation.toString(), "first inequation") - console.log(secondInequation.toString(), "second inequation") - const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); - console.log(secondInequalities.leftHandInequality, secondInequalities.rightHandInequality, "second inequalities") - - let firstInequalitiesSolution: xRange = { inferiorLimit: getLimit(firstInequalities, "inferior"), superiorLimit:getLimit(firstInequalities, "superior"), }; - console.log(firstInequalitiesSolution) + let secondInequalitiesSolution: xRange = { inferiorLimit:getLimit(secondInequalities, "inferior"), superiorLimit: getLimit(secondInequalities, "superior"), }; - console.log(secondInequalitiesSolution) equality = firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 074bd42..7cb3e49 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -140,14 +140,10 @@ export const isMathEqual = (a: any, b: any) => { bs = b.conditionals ? normalize(b) : sort(normalize(b)); - console.log("[isMathEqual]", as.toString(), "==?", bs.toString()); - + log("[isMathEqual]", as.toString(), "==?", bs.toString()); const isSortingEnough = sort(a).equals(sort(b)); - console.log(isSortingEnough, "isSortingEnough") const isTexEnough = as.toTex().trim() === bs.toTex().trim(); - console.log(isTexEnough, "isTexEnough") -console.log(as.equals(bs), "equality") let equality = isTexEnough || as.equals(bs) || isSortingEnough; @@ -162,7 +158,7 @@ console.log(as.equals(bs), "equality") if (equality) { return true; } else { - return false + return false; } } @@ -184,22 +180,18 @@ console.log(as.equals(bs), "equality") if (equality) { return true; } else { - return false + return false; } } - - - // at this point this is an ideea, it must be tested // check for compound inequalities - if ( //@ts-ignore as?.conditionals?.length === bs?.conditionals?.length && //@ts-ignore as?.conditionals?.length === 2 ) { - equality= compareCompoundInequations(as,bs) + equality = compareCompoundInequations(as, bs); } return equality; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index a391390..d88f400 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -67,8 +67,6 @@ export const getCoefficients = (equation: MathNode) => { let result: number[] = []; // coefficients will be determined if equation has only one unknown - - console.log(equation.toString(), "equation in get coefficients") try { const rationalizedEquation = m.rationalize(equation, {}, true); result = rationalizedEquation.coefficients; @@ -80,7 +78,6 @@ export const getCoefficients = (equation: MathNode) => { { l: "(n1+n2)*n3/n4", r: "(n1*n3)/n4+(n2*n3)/n4" }, ]); - console.log(equation.toString(), "after simplify") try { const rat = m.rationalize(equation, {}, true); result = rat.coefficients; @@ -89,9 +86,6 @@ export const getCoefficients = (equation: MathNode) => { result = result.length === 0 ? [1, 0] : result; - -console.log(result, "result") - return result; }; @@ -150,4 +144,4 @@ export const equationsHaveTheSameUnknowns = ( (unknonwn, index) => unknonwn === secondEquationUnknowns[index] ) ); -}; +}; \ No newline at end of file From d4db4d444d29982820cefc88bf63c03a38cb351c Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 19 Oct 2021 11:19:46 +0300 Subject: [PATCH 22/34] clean-up --- src/difference.ts | 4 +- .../2-way-inequalities-with-variables.ts | 44 +++++++++---------- .../3-way-inequalities-with-variables.ts | 2 - src/symbolic/compare-compound-inequations.ts | 8 +--- src/symbolic/compare-equations.ts | 5 ++- src/symbolic/index.ts | 12 +---- src/symbolic/utils.ts | 2 - 7 files changed, 31 insertions(+), 46 deletions(-) diff --git a/src/difference.ts b/src/difference.ts index bb82f0d..1c9ee42 100644 --- a/src/difference.ts +++ b/src/difference.ts @@ -6,8 +6,8 @@ const log = debug("difference"); export const differenceIsTooGreat = (a: RawAst, b: RawAst) => { const smallest = Math.min(a.toString().length, b.toString().length); const biggest = Math.max(a.toString().length, b.toString().length); - const errorAcceptance = 27; - const limit = (1 / smallest) * 100 + errorAcceptance; + const errorAcceptance = 17; + const limit = (1 / smallest) * 100 + 10 + errorAcceptance; const diff = biggest - smallest; log("a:", a.toString(), "b:", b.toString(), "limit:", limit, "diff:", diff); diff --git a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts index 4cb176a..0824ab5 100644 --- a/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/2-way-inequalities-with-variables.ts @@ -27,28 +27,6 @@ export default { eq: ["4≥2x", "x+x ≤ 2*2", "4x≤8", "2x+y-y-4≤0"], ne: ["x<2", "3x≤4", "x<3", "-2x-y≤-4-y"], }, - // 2-way inequalities in two variables - { - target: "2x+3y≤4+y", - eq: ["4+y≥2x+3y", "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y"], - ne: ["x+y<2", "3x+2y≤4", "x+4y≤3", "-2x-3y≤-4-y"], - }, - { - target: "5.5x + 8y≥100", - eq: [ - "11x+16y≥200", - "5.5x + 8y+10≥100+10", - "5.5x + 8y+z≥100+z", - "(5.5x + 8y)*2≥100*2", - "5.5x + 8y -100≥0", - "(5.5x + 8y)≥100", - ], - }, - { - target: "2x + 3y≥7", - eq: ["x + 1.5y≥3.5", "3x + 6y≥14-x", "-2x-3y≤-7"], - ne: ["-2x-3y≥-7", "3x + 6y≤14-x", "x + 1.5y≤3.5", "x + 1.5y=3.5"], - }, { target: "\\frac{2x+4}{10}>4", eq: ["\\frac{x+2}{5}>4", "\\frac{x}{5}+\\frac{2}{5}>4"], @@ -89,5 +67,27 @@ export default { "8(-\\frac{3}{4}m)-8(\\frac{2}{8})≤ 8(-\\frac{1}{4})", ], }, + // 2-way inequalities in two variables + { + target: "2x+3y≤4+y", + eq: ["4+y≥2x+3y", "x+x +3y≤ 2*2 +y", "4x+6y≤8+2y"], + ne: ["x+y<2", "3x+2y≤4", "x+4y≤3", "-2x-3y≤-4-y"], + }, + { + target: "5.5x + 8y≥100", + eq: [ + "11x+16y≥200", + "5.5x + 8y+10≥100+10", + "5.5x + 8y+z≥100+z", + "(5.5x + 8y)*2≥100*2", + "5.5x + 8y -100≥0", + "(5.5x + 8y)≥100", + ], + }, + { + target: "2x + 3y≥7", + eq: ["x + 1.5y≥3.5", "3x + 6y≥14-x", "-2x-3y≤-7"], + ne: ["-2x-3y≥-7", "3x + 6y≤14-x", "x + 1.5y≤3.5", "x + 1.5y=3.5"], + }, ], }; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 372c726..9ff674a 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -38,11 +38,9 @@ export default { ], }, { - // only: true, target: "-5 ≤ 3 - 2z ≤ 5", eq: [ "-8 ≤ -2z ≤ 2", - // not validating "4 ≥ z ≥ -1" ], }, diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 4b714ea..29fe280 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,5 @@ import { mathjs } from "../mathjs"; -import math, { MathNode, number } from "mathjs"; +import { MathNode } from "mathjs"; import { equationsCanBeCompared, equationsHaveTheSameUnknowns, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression } from "./utils"; const m: any = mathjs; @@ -32,10 +32,8 @@ const breakInequality = (compoundInequality: any): InequalitiesPairs => { }; const findX = (inequality: MathNode): number => { - // TO DO: sanity checks let expression = transformEqualityInExpression(inequality); let result: number; - // TO DO: must chek if we have the same unknowns let equationUnknownsName = getUnknowns(expression); let equationCoefficients: number[]; @@ -51,7 +49,6 @@ const findX = (inequality: MathNode): number => { return result }; - export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):number => { const xFirstInequality = findX(expressionsPair.rightHandInequality); const xSecondInequality = findX(expressionsPair.leftHandInequality); @@ -63,7 +60,6 @@ export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):n } } - export const compareCompoundInequations = ( firstInequation: any, secondInequation: any @@ -74,7 +70,7 @@ export const compareCompoundInequations = ( (relation: string) => secondInequation.conditionals.includes(relation) && firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 && equationsCanBeCompared (firstInequation,secondInequation) + firstInequation.params?.length === 3 && equationsCanBeCompared(firstInequation,secondInequation) ); if (!result) { diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index a8fab1b..bc91e13 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { isMathEqual, simplify } from "."; +import { isMathEqual } from "."; import { getUnknowns, equationsHaveTheSameUnknowns, @@ -24,7 +24,7 @@ export const compareEquations = ( let firstExpression = transformEqualityInExpression(firstEquation); let secondExpression = transformEqualityInExpression(secondEquation); - if (isMathEqual(firstExpression, secondExpression)) { + if (firstExpression.equals(secondExpression)) { return true; } @@ -57,6 +57,7 @@ export const compareEquations = ( equivalence = solutionForFirstEquation === solutionForSecondEquation; + // 2-way inequality if (equivalence && isInequality) { // check if direction should be changed if ( diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 7cb3e49..a686087 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -155,11 +155,7 @@ export const isMathEqual = (a: any, b: any) => { if (as.fn === "equal" && bs.fn === "equal") { equality = compareEquations(as, bs, false); - if (equality) { - return true; - } else { - return false; - } + return equality; } // if both expressions are inequalities treat greater sign as equal sign @@ -177,11 +173,7 @@ export const isMathEqual = (a: any, b: any) => { equality = compareEquations(as, bs, true); - if (equality) { - return true; - } else { - return false; - } + return equality; } // check for compound inequalities diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index d88f400..4b3fed0 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -39,7 +39,6 @@ export const transformEqualityInExpression = (equality: MathNode) => { const expression = new m.OperatorNode("-", "subtract", equality.args); // remove added/subtracted numbers/variables from both sides of the equation - // @ts-ignore return customSimplify(expression); }; @@ -124,7 +123,6 @@ export const solveLinearEquation = (coefficients: number[]) => { result = 0; } else { // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity - // Math.round(coefficient * 100000) / 100000) result = Math.round(m.divide(coefficients[0], -1 * coefficients[1]) * 10000) / 10000; } } From 128a483ea088f50518680c0826a6ecd020d29cdd Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 19 Oct 2021 15:21:17 +0300 Subject: [PATCH 23/34] add low level tests for utils --- ...ompare-equations.spec.ts => utils.spec.ts} | 138 ++++++++++++++++++ src/symbolic/compare-compound-inequations.ts | 22 +-- src/symbolic/compare-equations.ts | 5 +- src/symbolic/utils.ts | 21 ++- 4 files changed, 162 insertions(+), 24 deletions(-) rename src/__tests__/{compare-equations.spec.ts => utils.spec.ts} (54%) diff --git a/src/__tests__/compare-equations.spec.ts b/src/__tests__/utils.spec.ts similarity index 54% rename from src/__tests__/compare-equations.spec.ts rename to src/__tests__/utils.spec.ts index d6a3e71..3336dfa 100644 --- a/src/__tests__/compare-equations.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -1,15 +1,75 @@ import { AstToMathJs } from "../conversion/ast-to-mathjs"; import { LatexToAst } from "../conversion/latex-to-ast"; +import { simplify } from "../symbolic"; + import { getUnknowns, getCoefficients, setXToOne, solveLinearEquation, + expressionsCanBeCompared, + transformEqualityInExpression, + findX, } from "../symbolic/utils"; const lta = new LatexToAst(); const atm = new AstToMathJs(); +describe("expressionsCanBeCompared", () => { + it('equations: "x = x" and "2=2" - should return false: equations can not be compared because second equation does not have an unknown', () => { + const firstEquation = atm.convert(lta.convert("x=x")); + const secondEquation = atm.convert(lta.convert("2=2")); + const result = expressionsCanBeCompared(firstEquation, secondEquation); + + expect(result).toEqual(false); + }); + + it('equations: "x = x" and "\\log x=2" - should return false: equations can not be compared because second equation contains a function', () => { + const firstEquation = atm.convert(lta.convert("x=x")); + const secondEquation = atm.convert(lta.convert("\\log x=2")); + const result = expressionsCanBeCompared(firstEquation, secondEquation); + + expect(result).toEqual(false); + }); + + it('equations: "5z = 0" and "2y+3=m" - should return true: both equations have unknowns and does not contain functions', () => { + const firstEquation = atm.convert(lta.convert("x=x")); + const secondEquation = atm.convert(lta.convert("2y+3=m")); + const result = expressionsCanBeCompared(firstEquation, secondEquation); + + expect(result).toEqual(true); + }); + + it('equations: "x" and "y" - should return true: both expressions have variables and does not contain functions', () => { + const firstEquation = atm.convert(lta.convert("x")); + const secondEquation = atm.convert(lta.convert("y")); + const result = expressionsCanBeCompared(firstEquation, secondEquation); + + expect(result).toEqual(true); + }); +}); + +describe("transformEqualityInExpression", () => { + it.each` + equation | transformedExpression + ${"x+5= 2x+3"} | ${"2-x"} + ${"5-2(3-m)= 4m+10"} | ${"-5-4m-2(3-m)"} + ${"a=2b+3"} | ${"a-2b-3"} + `( + "$equation => $transformedExpression", + ({ equation, transformedExpression }) => { + const equationToTransform = atm.convert(lta.convert(equation)); + const expression = simplify( + atm.convert(lta.convert(transformedExpression)) + ); + + const result = transformEqualityInExpression(equationToTransform); + + expect(result.equals(expression)).toEqual(true); + } + ); +}); + describe("getUnknowns", () => { it.each` expression | unknowns @@ -169,3 +229,81 @@ describe("solveLinearEquation", () => { expect(result).toEqual(undefined); }); }); + +describe("findX", () => { + it.each` + equation | unknownValue + ${"x = x"} | ${Infinity} + ${"x + 5 - 3 + x = 6 + x - 2"} | ${2} + ${"2x = x"} | ${0} + ${"x = x + 2"} | ${undefined} + ${"3x - 2x = x + 7 + 9"} | ${undefined} + ${"2x^2 = 2x"} | ${0} + ${"y^2+5y - 1"} | ${0} + ${"y^2+5y + 1"} | ${0} + `("$equation => $unknownValue", ({ equation, unknownValue }) => { + const nodeEquation = atm.convert(lta.convert(equation)); + const result = findX(nodeEquation); + + expect(result).toEqual(unknownValue); + }); + }); + + describe("solveLinearEquation", () => { + it('equation: "x = x" - has infinite solutions', () => { + const coefficients = [0, 0]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(Infinity); + }); + + it('equation: "x + 5 - 3 + x = 6 + x - 2" - solution should be 2', () => { + const coefficients = [-2, 1]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(2); + }); + + it('equation: "2x = x" - solution should be 0', () => { + const coefficients = [0, 1]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(0); + }); + + it('equation: "x = x + 2" - if equation has no solution it will return - Infinity', () => { + const coefficients = [2, 0]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(-Infinity); + }); + + it('equation: "3x - 2x = x + 7 + 9" - if equation has no solution it will return - Infinity', () => { + const coefficients = [16, 0]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(-Infinity); + }); + + it('equation: "2x^2 = 2x" - has no solution', () => { + const coefficients = [0, 4, 2]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(-2); + }); + + it('equation: "y^2+5y - 1" - if equation is quadratic, result is undefined', () => { + const coefficients = [-1, 5, 1]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(undefined); + }); + + it('equation: "y^2+5y + 1" - if equation is quadratic, result is undefined', () => { + const coefficients = [1, 5, 1]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(undefined); + }); + }); + diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 29fe280..ebb2ea2 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { equationsCanBeCompared, equationsHaveTheSameUnknowns, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression } from "./utils"; +import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression, findX } from "./utils"; const m: any = mathjs; @@ -31,24 +31,6 @@ const breakInequality = (compoundInequality: any): InequalitiesPairs => { }; }; -const findX = (inequality: MathNode): number => { - let expression = transformEqualityInExpression(inequality); - let result: number; - - let equationUnknownsName = getUnknowns(expression); - let equationCoefficients: number[]; - - if (equationUnknownsName.length === 1) { - equationCoefficients = getCoefficients(expression); - } - - result = solveLinearEquation( - equationCoefficients - ); - - return result -}; - export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):number => { const xFirstInequality = findX(expressionsPair.rightHandInequality); const xSecondInequality = findX(expressionsPair.leftHandInequality); @@ -70,7 +52,7 @@ export const compareCompoundInequations = ( (relation: string) => secondInequation.conditionals.includes(relation) && firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 && equationsCanBeCompared(firstInequation,secondInequation) + firstInequation.params?.length === 3 && expressionsCanBeCompared(firstInequation,secondInequation) ); if (!result) { diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index bc91e13..d59262e 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -1,6 +1,5 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { isMathEqual } from "."; import { getUnknowns, equationsHaveTheSameUnknowns, @@ -8,7 +7,7 @@ import { solveLinearEquation, setXToOne, transformEqualityInExpression, - equationsCanBeCompared, + expressionsCanBeCompared, } from "./utils"; const m: any = mathjs; @@ -20,7 +19,7 @@ export const compareEquations = ( ) => { let equivalence: boolean = false; - if (equationsCanBeCompared(firstEquation,secondEquation)) { + if (expressionsCanBeCompared(firstEquation,secondEquation)) { let firstExpression = transformEqualityInExpression(firstEquation); let secondExpression = transformEqualityInExpression(secondEquation); diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 4b3fed0..1c00adb 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -5,7 +5,7 @@ const { simplify } = mathjs; const m: any = mathjs; -export const equationsCanBeCompared = ( +export const expressionsCanBeCompared = ( firstEquation: MathNode, secondEquation: MathNode ): boolean => { @@ -142,4 +142,23 @@ export const equationsHaveTheSameUnknowns = ( (unknonwn, index) => unknonwn === secondEquationUnknowns[index] ) ); +}; + +//solve unknown for linear equation/inequality in one variable +export const findX = (inequality: MathNode): number => { + let expression = transformEqualityInExpression(inequality); + let result: number; + + let equationUnknownsName = getUnknowns(expression); + let equationCoefficients: number[]; + + if (equationUnknownsName.length === 1) { + equationCoefficients = getCoefficients(expression); + } + + result = solveLinearEquation( + equationCoefficients + ); + + return result }; \ No newline at end of file From 9de55fb593dcb012020beba9a92556e157aa2e72 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 19 Oct 2021 15:36:11 +0300 Subject: [PATCH 24/34] add low level tests for utils --- src/__tests__/utils.spec.ts | 102 +++++++++--------------------------- 1 file changed, 26 insertions(+), 76 deletions(-) diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 3336dfa..e161442 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -93,6 +93,7 @@ describe("getCoefficients", () => { it.each` expression | coefficients ${"x+0"} | ${[0, 1]} + ${"2x^2 = 2x"} | ${[1, 0]} ${"x +1"} | ${[1, 1]} ${"((x^2 + x) / x) - 1"} | ${[0, 0, 1]} ${"1+2"} | ${[1, 0]} @@ -208,13 +209,20 @@ describe("solveLinearEquation", () => { expect(result).toEqual(-Infinity); }); - it('equation: "2x^2 = 2x" - has no solution', () => { + it('equation: "2y^2+4y" - solution is -2', () => { const coefficients = [0, 4, 2]; const result = solveLinearEquation(coefficients); expect(result).toEqual(-2); }); + it('equation: "2x^2 = 2x" - solution is 1', () => { + const coefficients = [1, 0]; + const result = solveLinearEquation(coefficients); + + expect(result).toEqual(1); + }); + it('equation: "y^2+5y - 1" - if equation is quadratic, result is undefined', () => { const coefficients = [-1, 5, 1]; const result = solveLinearEquation(coefficients); @@ -231,79 +239,21 @@ describe("solveLinearEquation", () => { }); describe("findX", () => { - it.each` - equation | unknownValue - ${"x = x"} | ${Infinity} - ${"x + 5 - 3 + x = 6 + x - 2"} | ${2} - ${"2x = x"} | ${0} - ${"x = x + 2"} | ${undefined} - ${"3x - 2x = x + 7 + 9"} | ${undefined} - ${"2x^2 = 2x"} | ${0} - ${"y^2+5y - 1"} | ${0} - ${"y^2+5y + 1"} | ${0} - `("$equation => $unknownValue", ({ equation, unknownValue }) => { - const nodeEquation = atm.convert(lta.convert(equation)); - const result = findX(nodeEquation); - - expect(result).toEqual(unknownValue); - }); - }); - - describe("solveLinearEquation", () => { - it('equation: "x = x" - has infinite solutions', () => { - const coefficients = [0, 0]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(Infinity); - }); - - it('equation: "x + 5 - 3 + x = 6 + x - 2" - solution should be 2', () => { - const coefficients = [-2, 1]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(2); - }); - - it('equation: "2x = x" - solution should be 0', () => { - const coefficients = [0, 1]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(0); - }); - - it('equation: "x = x + 2" - if equation has no solution it will return - Infinity', () => { - const coefficients = [2, 0]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(-Infinity); - }); - - it('equation: "3x - 2x = x + 7 + 9" - if equation has no solution it will return - Infinity', () => { - const coefficients = [16, 0]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(-Infinity); - }); - - it('equation: "2x^2 = 2x" - has no solution', () => { - const coefficients = [0, 4, 2]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(-2); - }); - - it('equation: "y^2+5y - 1" - if equation is quadratic, result is undefined', () => { - const coefficients = [-1, 5, 1]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(undefined); - }); - - it('equation: "y^2+5y + 1" - if equation is quadratic, result is undefined', () => { - const coefficients = [1, 5, 1]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(undefined); - }); + it.each` + equation | unknownValue + ${"x = x"} | ${Infinity} + ${"x + 5 - 3 + x = 6 + x - 2"} | ${2} + ${"2x = x"} | ${0} + ${"x = x + 2"} | ${undefined} + ${"3x - 2x = x + 7 + 9"} | ${undefined} + ${"2x^2 = 2x"} | ${1} + ${"y^2+5y - 1 = 0"} | ${undefined} + ${"y^2+5y + 1 =0 "} | ${undefined} + ${"2y^2+4y = 0"} | ${-2} + `("$equation => $unknownValue", ({ equation, unknownValue }) => { + const nodeEquation = atm.convert(lta.convert(equation)); + const result = findX(nodeEquation); + + expect(result).toEqual(unknownValue); }); - +}); From ca776c4334d971b2828f012c1d54f7cbff24be05 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 20 Oct 2021 12:43:59 +0300 Subject: [PATCH 25/34] update readme --- README.md | 20 ++++++++-------- src/__tests__/utils.spec.ts | 8 ------- src/symbolic/compare-compound-inequations.ts | 4 ++-- src/symbolic/compare-equations.ts | 25 ++++---------------- src/symbolic/utils.ts | 14 ++--------- 5 files changed, 18 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 460025d..e93d35f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ yarn demo Then go to `http://localhost:$PORT/demo` + ## tests ```shell @@ -28,6 +29,7 @@ There is one test that runs fixture data located here: `src/fixtures/latex-equal yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts --reporters default ``` + ## Next Steps * Check that api we expose will support what is needed. @@ -52,6 +54,7 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts * more advanced literal validation (todo) * block input that is clearly too large/unrelated (eg: a user can type in gobbledy-gook - we should just abort if we see that) + ## Capabilities - comparing linear equations in one variable @@ -69,28 +72,25 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts * a faster latex parser * faster math evaluation + ## modes There are 2 modes - literal and symbolic -Literal: is at its most basic form very similar to a string validation +Literal: is at its most basic a tuned version of a string validation -Literal will ignore spaces and parentheses as long as they do not change the meaning of operations - -For example “a+7 +b” will not validate against “7 + a+b” but will validate against “ ((a) + (7))+b ”. -It will accept commas for decimal marks. For example “1,000” will be equivalent with 1000. -Validation will ignore leading zeros: “0.1” will validate against “.1” +By default - ignores spaces and parentheses as long as they do not change the meaning of operations (ex. “a+7 +b” will validate against “ ((a) + (7))+b ”) + - ignores leading zeros: “0.1” will validate against “.1” + - accepts commas for decimal marks. For example “1,000” will be equivalent with 1000 Literal Validation offers two configuration options that can be used to validate some variety of forms for an expression: Ignore trailing zeros option; allows the evaluation to accept zeros to the right of the decimal place “4.5” will validate against “4.50000000000000” -Ignore order option; makes validation indifferent to the variables order, as long as it does not change operations meaning. In this case “a+7 +b*c” will validate against “7 + a+bc”, but not against “ac+7+b” - +Ignore order option; makes validation indifferent to the variables order, as long as it does not change operations meaning. In this case “a+7 +b*c” will validate against “7 + a+bc”, but not against “ac+7+b”; without it “a+7 +b” will not validate against “7 + a+b” Symbolic: attempts to decide if expressions are mathematically equivalent or not -The second type of validation, and the most relevant in mathematics, is symbolic validation. By default, it offers all configurations presented for literal validation and is more relevant in mathematics - +By default, it offers all configurations presented for literal validation, exceeding it by quite a lot In order to check equivalence between 2 expressions, we have to reduce both expressions to the simplest one. Then distribute all coefficients, combine any like terms on each side of the expression, and arrange them in the same order. diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index e161442..3233281 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -216,13 +216,6 @@ describe("solveLinearEquation", () => { expect(result).toEqual(-2); }); - it('equation: "2x^2 = 2x" - solution is 1', () => { - const coefficients = [1, 0]; - const result = solveLinearEquation(coefficients); - - expect(result).toEqual(1); - }); - it('equation: "y^2+5y - 1" - if equation is quadratic, result is undefined', () => { const coefficients = [-1, 5, 1]; const result = solveLinearEquation(coefficients); @@ -246,7 +239,6 @@ describe("findX", () => { ${"2x = x"} | ${0} ${"x = x + 2"} | ${undefined} ${"3x - 2x = x + 7 + 9"} | ${undefined} - ${"2x^2 = 2x"} | ${1} ${"y^2+5y - 1 = 0"} | ${undefined} ${"y^2+5y + 1 =0 "} | ${undefined} ${"2y^2+4y = 0"} | ${-2} diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index ebb2ea2..1eec3a1 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getCoefficients, getUnknowns, solveLinearEquation, transformEqualityInExpression, findX } from "./utils"; +import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getUnknowns, findX } from "./utils"; const m: any = mathjs; @@ -65,7 +65,7 @@ export const compareCompoundInequations = ( !equationsHaveTheSameUnknowns( firstInequalityUnknownsName, secondInequalityUnknownsName - ) + ) && firstInequalityUnknownsName?.length === 1 ) { return false; } diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index d59262e..ba89ec5 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -19,7 +19,7 @@ export const compareEquations = ( ) => { let equivalence: boolean = false; - if (expressionsCanBeCompared(firstEquation,secondEquation)) { + if (expressionsCanBeCompared(firstEquation, secondEquation)) { let firstExpression = transformEqualityInExpression(firstEquation); let secondExpression = transformEqualityInExpression(secondEquation); @@ -56,26 +56,6 @@ export const compareEquations = ( equivalence = solutionForFirstEquation === solutionForSecondEquation; - // 2-way inequality - if (equivalence && isInequality) { - // check if direction should be changed - if ( - m.isPositive(firstEquationCoefficients[0]) && - m.isNegative(firstEquationCoefficients[1]) && - m.isNegative(secondEquationCoefficients[0]) && - m.isPositive(secondEquationCoefficients[1]) - ) { - equivalence = false; - } - if ( - m.isNegative(firstEquationCoefficients[0]) && - m.isPositive(firstEquationCoefficients[1]) && - m.isPositive(secondEquationCoefficients[0]) && - m.isNegative(secondEquationCoefficients[1]) - ) { - equivalence = false; - } - } } // if both equations are linear in two variabled then we give value "1" for both "x". Doing this we get a linear equation in one variable "y". Then we solve "y" for both. If y has the same value then equations are equivalent @@ -99,6 +79,9 @@ export const compareEquations = ( equivalence = yFromFirstExpression === yFromSecondExpression; } + // determine equivalence between 2-way inequalities with 1 or 2 variables: + // we treat 2-way inequalities the same way as linear equations in 1 or 2 variables; we find out the solutions that solve the inequality then compare them + // we have one distinct case, when multiplying both parts of an inequality with a negative number, the sign must change direction if (equivalence && isInequality) { // check if direction should be changed if ( diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 1c00adb..9aabc7a 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -147,18 +147,8 @@ export const equationsHaveTheSameUnknowns = ( //solve unknown for linear equation/inequality in one variable export const findX = (inequality: MathNode): number => { let expression = transformEqualityInExpression(inequality); - let result: number; - - let equationUnknownsName = getUnknowns(expression); - let equationCoefficients: number[]; - if (equationUnknownsName.length === 1) { - equationCoefficients = getCoefficients(expression); - } - - result = solveLinearEquation( - equationCoefficients + return solveLinearEquation( + getCoefficients(expression) ); - - return result }; \ No newline at end of file From 80598fc2c81140f20637b51f39c7d1377426d56a Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 20 Oct 2021 14:16:01 +0300 Subject: [PATCH 26/34] refactor --- src/__tests__/utils.spec.ts | 19 ------------- src/symbolic/compare-compound-inequations.ts | 15 ++++++---- src/symbolic/compare-equations.ts | 28 ++++++++----------- src/symbolic/index.ts | 10 ++----- src/symbolic/utils.ts | 29 ++++++++------------ 5 files changed, 35 insertions(+), 66 deletions(-) diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 3233281..a0ca62c 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -230,22 +230,3 @@ describe("solveLinearEquation", () => { expect(result).toEqual(undefined); }); }); - -describe("findX", () => { - it.each` - equation | unknownValue - ${"x = x"} | ${Infinity} - ${"x + 5 - 3 + x = 6 + x - 2"} | ${2} - ${"2x = x"} | ${0} - ${"x = x + 2"} | ${undefined} - ${"3x - 2x = x + 7 + 9"} | ${undefined} - ${"y^2+5y - 1 = 0"} | ${undefined} - ${"y^2+5y + 1 =0 "} | ${undefined} - ${"2y^2+4y = 0"} | ${-2} - `("$equation => $unknownValue", ({ equation, unknownValue }) => { - const nodeEquation = atm.convert(lta.convert(equation)); - const result = findX(nodeEquation); - - expect(result).toEqual(unknownValue); - }); -}); diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 1eec3a1..c576602 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; -import { MathNode } from "mathjs"; -import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getUnknowns, findX } from "./utils"; +import { expression, MathNode } from "mathjs"; +import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getUnknowns, transformEqualityInExpression, getCoefficients, solveLinearEquation } from "./utils"; const m: any = mathjs; @@ -32,8 +32,11 @@ const breakInequality = (compoundInequality: any): InequalitiesPairs => { }; export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):number => { - const xFirstInequality = findX(expressionsPair.rightHandInequality); - const xSecondInequality = findX(expressionsPair.leftHandInequality); + const expressionR = transformEqualityInExpression(expressionsPair.rightHandInequality); + const expressionL = transformEqualityInExpression(expressionsPair.leftHandInequality); + + const xFirstInequality = solveLinearEquation(getCoefficients(expressionR)); + const xSecondInequality =solveLinearEquation(getCoefficients(expressionL)); if (limitType === "inferior") { return Math.min(xFirstInequality,xSecondInequality) @@ -57,7 +60,7 @@ export const compareCompoundInequations = ( if (!result) { return false; - } else { + } const firstInequalityUnknownsName = getUnknowns(firstInequation); const secondInequalityUnknownsName = getUnknowns(secondInequation); @@ -84,7 +87,7 @@ export const compareCompoundInequations = ( }; equality = firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit - } + return equality; }; diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index ba89ec5..9d99f13 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -55,7 +55,6 @@ export const compareEquations = ( ); equivalence = solutionForFirstEquation === solutionForSecondEquation; - } // if both equations are linear in two variabled then we give value "1" for both "x". Doing this we get a linear equation in one variable "y". Then we solve "y" for both. If y has the same value then equations are equivalent @@ -84,22 +83,17 @@ export const compareEquations = ( // we have one distinct case, when multiplying both parts of an inequality with a negative number, the sign must change direction if (equivalence && isInequality) { // check if direction should be changed - if ( - m.isPositive(firstEquationCoefficients[0]) && - m.isNegative(firstEquationCoefficients[1]) && - m.isNegative(secondEquationCoefficients[0]) && - m.isPositive(secondEquationCoefficients[1]) - ) { - equivalence = false; - } - if ( - m.isNegative(firstEquationCoefficients[0]) && - m.isPositive(firstEquationCoefficients[1]) && - m.isPositive(secondEquationCoefficients[0]) && - m.isNegative(secondEquationCoefficients[1]) - ) { - equivalence = false; - } + let signShouldBeChanged = + (m.isPositive(firstEquationCoefficients[0]) && + m.isNegative(firstEquationCoefficients[1]) && + m.isNegative(secondEquationCoefficients[0]) && + m.isPositive(secondEquationCoefficients[1])) || + (m.isNegative(firstEquationCoefficients[0]) && + m.isPositive(firstEquationCoefficients[1]) && + m.isPositive(secondEquationCoefficients[0]) && + m.isNegative(secondEquationCoefficients[1])); + + return !signShouldBeChanged; } } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index a686087..68ecbcc 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -153,9 +153,7 @@ export const isMathEqual = (a: any, b: any) => { // if both expressions are equations if (as.fn === "equal" && bs.fn === "equal") { - equality = compareEquations(as, bs, false); - - return equality; + return compareEquations(as, bs, false); } // if both expressions are inequalities treat greater sign as equal sign @@ -171,9 +169,7 @@ export const isMathEqual = (a: any, b: any) => { as.op = "="; bs.op = "="; - equality = compareEquations(as, bs, true); - - return equality; + return compareEquations(as, bs, true); } // check for compound inequalities @@ -183,7 +179,7 @@ export const isMathEqual = (a: any, b: any) => { //@ts-ignore as?.conditionals?.length === 2 ) { - equality = compareCompoundInequations(as, bs); + return compareCompoundInequations(as, bs); } return equality; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 9aabc7a..24ec5c3 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -1,6 +1,6 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; -import {simplify as customSimplify} from "./" +import { simplify as customSimplify } from "./"; const { simplify } = mathjs; const m: any = mathjs; @@ -80,7 +80,7 @@ export const getCoefficients = (equation: MathNode) => { try { const rat = m.rationalize(equation, {}, true); result = rat.coefficients; - } catch(e) {} + } catch (e) {} } result = result.length === 0 ? [1, 0] : result; @@ -118,13 +118,17 @@ export const solveLinearEquation = (coefficients: number[]) => { if (coefficients.length === 2) { if (coefficients[0] === 0 && coefficients[1] === 0) { - result = Infinity; - } else if (coefficients[0] === 0) { - result = 0; - } else { - // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity - result = Math.round(m.divide(coefficients[0], -1 * coefficients[1]) * 10000) / 10000; + return Infinity; + } + + if (coefficients[0] === 0) { + return 0; } + + // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity + result = + Math.round(m.divide(coefficients[0], -1 * coefficients[1]) * 10000) / + 10000; } return result; @@ -143,12 +147,3 @@ export const equationsHaveTheSameUnknowns = ( ) ); }; - -//solve unknown for linear equation/inequality in one variable -export const findX = (inequality: MathNode): number => { - let expression = transformEqualityInExpression(inequality); - - return solveLinearEquation( - getCoefficients(expression) - ); -}; \ No newline at end of file From 4a43a66f9afa64d52880784e504c99723606b3bc Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 20 Oct 2021 14:22:45 +0300 Subject: [PATCH 27/34] refactor --- src/__tests__/utils.spec.ts | 3 +-- src/symbolic/compare-compound-inequations.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index a0ca62c..758897e 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -8,8 +8,7 @@ import { setXToOne, solveLinearEquation, expressionsCanBeCompared, - transformEqualityInExpression, - findX, + transformEqualityInExpression } from "../symbolic/utils"; const lta = new LatexToAst(); diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index c576602..de107b2 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,5 @@ import { mathjs } from "../mathjs"; -import { expression, MathNode } from "mathjs"; +import { MathNode } from "mathjs"; import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getUnknowns, transformEqualityInExpression, getCoefficients, solveLinearEquation } from "./utils"; const m: any = mathjs; From 784fbdb17adb63f6e5a471ed53b2b2f43dece658 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 20 Oct 2021 14:42:58 +0300 Subject: [PATCH 28/34] add comments --- src/symbolic/compare-compound-inequations.ts | 5 +---- src/symbolic/utils.ts | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index de107b2..1322301 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -86,8 +86,5 @@ export const compareCompoundInequations = ( superiorLimit: getLimit(secondInequalities, "superior"), }; - equality = firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit - - - return equality; + return firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit }; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 24ec5c3..fa13ad6 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -62,7 +62,6 @@ export const getUnknowns = (equation: MathNode) => { }; export const getCoefficients = (equation: MathNode) => { - // add sanity check: rationalize is possible if we have only one unknown let result: number[] = []; // coefficients will be determined if equation has only one unknown @@ -70,6 +69,8 @@ export const getCoefficients = (equation: MathNode) => { const rationalizedEquation = m.rationalize(equation, {}, true); result = rationalizedEquation.coefficients; } catch (e) { + // rationalize may fail if unknown is isolated in a fraction + // we give it another try to rationalize after applying a new round of simplify to separate the unknown equation = simplify(equation, [ { l: "(n1-n2)/n3", r: "n1/n3-n2/n3" }, { l: "(n1+n2)/n3", r: "n1/n3+n2/n3" }, From 2be847aa83aff8fea8069b4c7a02480d49754d15 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Wed, 20 Oct 2021 15:11:02 +0300 Subject: [PATCH 29/34] refactor --- src/symbolic/compare-compound-inequations.ts | 123 +++++++++++-------- 1 file changed, 71 insertions(+), 52 deletions(-) diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 1322301..add0715 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,6 +1,13 @@ import { mathjs } from "../mathjs"; -import { MathNode } from "mathjs"; -import { expressionsCanBeCompared, equationsHaveTheSameUnknowns, getUnknowns, transformEqualityInExpression, getCoefficients, solveLinearEquation } from "./utils"; +import { MathNode } from "mathjs"; +import { + expressionsCanBeCompared, + equationsHaveTheSameUnknowns, + getUnknowns, + transformEqualityInExpression, + getCoefficients, + solveLinearEquation, +} from "./utils"; const m: any = mathjs; @@ -16,34 +23,39 @@ export type xRange = { const operation = (signName: string) => (signName === "larger" ? ">" : "≥"); -const breakInequality = (compoundInequality: any): InequalitiesPairs => { - return { - leftHandInequality: new m.OperatorNode( - operation(compoundInequality.conditionals[0]), - compoundInequality.conditionals[0], - [compoundInequality.params[0], compoundInequality.params[1]] - ), - rightHandInequality: new m.OperatorNode( - operation(compoundInequality.conditionals[1]), - compoundInequality.conditionals[0], - [compoundInequality.params[1], compoundInequality.params[2]] - ), - }; -}; - -export const getLimit = (expressionsPair :InequalitiesPairs, limitType:string):number => { - const expressionR = transformEqualityInExpression(expressionsPair.rightHandInequality); - const expressionL = transformEqualityInExpression(expressionsPair.leftHandInequality); +const breakInequality = (compoundInequality: any): InequalitiesPairs => ({ + leftHandInequality: new m.OperatorNode( + operation(compoundInequality.conditionals[0]), + compoundInequality.conditionals[0], + [compoundInequality.params[0], compoundInequality.params[1]] + ), + rightHandInequality: new m.OperatorNode( + operation(compoundInequality.conditionals[1]), + compoundInequality.conditionals[0], + [compoundInequality.params[1], compoundInequality.params[2]] + ), +}); + +export const getLimit = ( + expressionsPair: InequalitiesPairs, + limitType: string +): number => { + const expressionR = transformEqualityInExpression( + expressionsPair.rightHandInequality + ); + const expressionL = transformEqualityInExpression( + expressionsPair.leftHandInequality + ); const xFirstInequality = solveLinearEquation(getCoefficients(expressionR)); - const xSecondInequality =solveLinearEquation(getCoefficients(expressionL)); + const xSecondInequality = solveLinearEquation(getCoefficients(expressionL)); if (limitType === "inferior") { - return Math.min(xFirstInequality,xSecondInequality) + return Math.min(xFirstInequality, xSecondInequality); } else { - return Math.max(xFirstInequality,xSecondInequality) + return Math.max(xFirstInequality, xSecondInequality); } -} +}; export const compareCompoundInequations = ( firstInequation: any, @@ -55,36 +67,43 @@ export const compareCompoundInequations = ( (relation: string) => secondInequation.conditionals.includes(relation) && firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 && expressionsCanBeCompared(firstInequation,secondInequation) + firstInequation.params?.length === 3 && + expressionsCanBeCompared(firstInequation, secondInequation) ); if (!result) { return false; - } - const firstInequalityUnknownsName = getUnknowns(firstInequation); - const secondInequalityUnknownsName = getUnknowns(secondInequation); - - if ( - !equationsHaveTheSameUnknowns( - firstInequalityUnknownsName, - secondInequalityUnknownsName - ) && firstInequalityUnknownsName?.length === 1 - ) { - return false; - } - - const firstInequalities = breakInequality(firstInequation); - const secondInequalities = breakInequality(secondInequation); - - let firstInequalitiesSolution: xRange = { - inferiorLimit: getLimit(firstInequalities, "inferior"), - superiorLimit:getLimit(firstInequalities, "superior"), - }; - - let secondInequalitiesSolution: xRange = { - inferiorLimit:getLimit(secondInequalities, "inferior"), - superiorLimit: getLimit(secondInequalities, "superior"), - }; - - return firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && firstInequalitiesSolution.superiorLimit === secondInequalitiesSolution.superiorLimit + } + const firstInequalityUnknownsName = getUnknowns(firstInequation); + const secondInequalityUnknownsName = getUnknowns(secondInequation); + + if ( + !equationsHaveTheSameUnknowns( + firstInequalityUnknownsName, + secondInequalityUnknownsName + ) && + firstInequalityUnknownsName?.length === 1 + ) { + return false; + } + + const firstInequalities = breakInequality(firstInequation); + const secondInequalities = breakInequality(secondInequation); + + let firstInequalitiesSolution: xRange = { + inferiorLimit: getLimit(firstInequalities, "inferior"), + superiorLimit: getLimit(firstInequalities, "superior"), + }; + + let secondInequalitiesSolution: xRange = { + inferiorLimit: getLimit(secondInequalities, "inferior"), + superiorLimit: getLimit(secondInequalities, "superior"), + }; + + return ( + firstInequalitiesSolution.inferiorLimit === + secondInequalitiesSolution.inferiorLimit && + firstInequalitiesSolution.superiorLimit === + secondInequalitiesSolution.superiorLimit + ); }; From 64109a0025304c9501a2099727361e07ad26affc Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Thu, 21 Oct 2021 14:42:07 +0300 Subject: [PATCH 30/34] add functions comments and remove unused code --- src/symbolic/compare-compound-inequations.ts | 12 ++++++++---- src/symbolic/index.ts | 4 +--- src/symbolic/utils.ts | 20 ++++++-------------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index add0715..206c3a0 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -23,6 +23,7 @@ export type xRange = { const operation = (signName: string) => (signName === "larger" ? ">" : "≥"); +// break 3-way inequality in two 2-way inequalities; input a > b > c, output a > b and b > c const breakInequality = (compoundInequality: any): InequalitiesPairs => ({ leftHandInequality: new m.OperatorNode( operation(compoundInequality.conditionals[0]), @@ -52,17 +53,16 @@ export const getLimit = ( if (limitType === "inferior") { return Math.min(xFirstInequality, xSecondInequality); - } else { - return Math.max(xFirstInequality, xSecondInequality); } + + return Math.max(xFirstInequality, xSecondInequality); + }; export const compareCompoundInequations = ( firstInequation: any, secondInequation: any ) => { - let equality: boolean = false; - const result = firstInequation.conditionals.every( (relation: string) => secondInequation.conditionals.includes(relation) && @@ -74,6 +74,7 @@ export const compareCompoundInequations = ( if (!result) { return false; } + const firstInequalityUnknownsName = getUnknowns(firstInequation); const secondInequalityUnknownsName = getUnknowns(secondInequation); @@ -90,6 +91,8 @@ export const compareCompoundInequations = ( const firstInequalities = breakInequality(firstInequation); const secondInequalities = breakInequality(secondInequation); + // find out interval for inequality solution; + // it does not matter whether we have an open, or half-open/half-close interval, the signs are already compared and at this point they match let firstInequalitiesSolution: xRange = { inferiorLimit: getLimit(firstInequalities, "inferior"), superiorLimit: getLimit(firstInequalities, "superior"), @@ -100,6 +103,7 @@ export const compareCompoundInequations = ( superiorLimit: getLimit(secondInequalities, "superior"), }; + // if interval limits are the same for both inequalities then we can say that inequalities are equivalent return ( firstInequalitiesSolution.inferiorLimit === secondInequalitiesSolution.inferiorLimit && diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 68ecbcc..f435dfd 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -75,8 +75,6 @@ const normalize = (a: string | MathNode | any) => { containsArrayNode = true; node.items = node.items.map((item) => simplify(item)); } - - return node; }); if (r.fn === "equal") { @@ -172,7 +170,7 @@ export const isMathEqual = (a: any, b: any) => { return compareEquations(as, bs, true); } - // check for compound inequalities + // check for compound inequalities/3-way inequalities if ( //@ts-ignore as?.conditionals?.length === bs?.conditionals?.length && diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index fa13ad6..96e342c 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -5,6 +5,7 @@ const { simplify } = mathjs; const m: any = mathjs; +// expressions can be compared if we have at least one symbol node and has no function node or array export const expressionsCanBeCompared = ( firstEquation: MathNode, secondEquation: MathNode @@ -17,8 +18,6 @@ export const expressionsCanBeCompared = ( noFunctionOrArray = noFunctionOrArray || node.isFunctionNode || node.isArrayNode; firstSymbolNode = firstSymbolNode || node.isSymbolNode; - - return node; }); secondEquation.traverse(function (node, path, parent) { @@ -27,8 +26,6 @@ export const expressionsCanBeCompared = ( } if (node.isSymbolNode && firstSymbolNode) symbolNode = true; - - return node; }); return noFunctionOrArray && symbolNode; @@ -89,19 +86,14 @@ export const getCoefficients = (equation: MathNode) => { return result; }; -export const setXToOne = (equation: any, unknownName: string) => { - let result: MathNode; - - result = equation.transform(function (node, path, parent) { +export const setXToOne = (equation: any, unknownName: string) => + equation.transform(function (node, path, parent) { if (node.isSymbolNode && node.name === unknownName) { return new m.ConstantNode(1); - } else { - return node; } - }); - return result; -}; + return node; + }); // TO DO: solve quadratic equation @@ -125,7 +117,7 @@ export const solveLinearEquation = (coefficients: number[]) => { if (coefficients[0] === 0) { return 0; } - + // equation with no solution : if coefficient for x is 0 => division by zero => result == -Infinity result = Math.round(m.divide(coefficients[0], -1 * coefficients[1]) * 10000) / From c4d71f42ade1d9c0f005b25ec9c9cfb9716a16f5 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Mon, 25 Oct 2021 17:20:40 +0300 Subject: [PATCH 31/34] refactor, add tests --- .../compare-compound-inequations.spec.ts | 29 +++++ src/__tests__/utils.spec.ts | 28 ++--- .../3-way-inequalities-with-variables.ts | 17 ++- .../linear-equations-in-two-variables.ts | 4 +- src/symbolic/compare-compound-inequations.ts | 103 +++++++++--------- src/symbolic/compare-equations.ts | 20 ++-- src/symbolic/index.ts | 27 ++++- src/symbolic/utils.ts | 41 ++++--- 8 files changed, 162 insertions(+), 107 deletions(-) create mode 100644 src/__tests__/compare-compound-inequations.spec.ts diff --git a/src/__tests__/compare-compound-inequations.spec.ts b/src/__tests__/compare-compound-inequations.spec.ts new file mode 100644 index 0000000..5abde99 --- /dev/null +++ b/src/__tests__/compare-compound-inequations.spec.ts @@ -0,0 +1,29 @@ +import { AstToMathJs } from "../conversion/ast-to-mathjs"; +import { LatexToAst } from "../conversion/latex-to-ast"; + +import { splitInequality } from "../symbolic/compare-compound-inequations"; + +const lta = new LatexToAst(); +const atm = new AstToMathJs(); + +describe("splitInequality", () => { + it.each` + compoundInequality | leftPart | rightPart + ${"20>x>7"} | ${"20>x"} | ${"x>7"} + ${" 5 ≥ -4 + x > -1 - 1"} | ${" 5 ≥ -4 + x"} | ${"-4 + x > -1 - 1"} + ${"x/x 20"} | ${"2<4x"} | ${"4x>20"} + `( + "$compoundInequality => $leftPart, $rightPart", + ({ compoundInequality, leftPart, rightPart }) => { + const equation = atm.convert(lta.convert(compoundInequality)); + const broken = splitInequality(equation); + + const leftPartExpression = atm.convert(lta.convert(leftPart)); + const rightPartExpression = atm.convert(lta.convert(rightPart)); + + expect(broken.left).toEqual(leftPartExpression); + expect(broken.right).toEqual(rightPartExpression); + } + ); +}); diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 758897e..4c5d2ef 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -3,7 +3,7 @@ import { LatexToAst } from "../conversion/latex-to-ast"; import { simplify } from "../symbolic"; import { - getUnknowns, + getVariables, getCoefficients, setXToOne, solveLinearEquation, @@ -15,7 +15,7 @@ const lta = new LatexToAst(); const atm = new AstToMathJs(); describe("expressionsCanBeCompared", () => { - it('equations: "x = x" and "2=2" - should return false: equations can not be compared because second equation does not have an unknown', () => { + it('equations: "x = x" and "2=2" - should return false: equations can not be compared because second equation does not have a variable', () => { const firstEquation = atm.convert(lta.convert("x=x")); const secondEquation = atm.convert(lta.convert("2=2")); const result = expressionsCanBeCompared(firstEquation, secondEquation); @@ -31,7 +31,7 @@ describe("expressionsCanBeCompared", () => { expect(result).toEqual(false); }); - it('equations: "5z = 0" and "2y+3=m" - should return true: both equations have unknowns and does not contain functions', () => { + it('equations: "5z = 0" and "2y+3=m" - should return true: both equations have variables and does not contain functions', () => { const firstEquation = atm.convert(lta.convert("x=x")); const secondEquation = atm.convert(lta.convert("2y+3=m")); const result = expressionsCanBeCompared(firstEquation, secondEquation); @@ -69,9 +69,9 @@ describe("transformEqualityInExpression", () => { ); }); -describe("getUnknowns", () => { +describe("getVariables", () => { it.each` - expression | unknowns + expression | variables ${"x"} | ${["x"]} ${"x +1"} | ${["x"]} ${"((x^2 + x) / x) - 1"} | ${["x"]} @@ -80,11 +80,11 @@ describe("getUnknowns", () => { ${"((y^2 + z) / x) - 1"} | ${["x", "y", "z"]} ${"109h"} | ${["h"]} ${"m+n+10"} | ${["m", "n"]} - `("$expression => $unknowns", ({ expression, unknowns }) => { + `("$expression => $variables", ({ expression, variables }) => { const equation = atm.convert(lta.convert(expression)); - const unknownsName = getUnknowns(equation); + const variablesName = getVariables(equation); - expect(unknownsName).toEqual(unknowns); + expect(variablesName).toEqual(variables); }); }); @@ -95,16 +95,16 @@ describe("getCoefficients", () => { ${"2x^2 = 2x"} | ${[1, 0]} ${"x +1"} | ${[1, 1]} ${"((x^2 + x) / x) - 1"} | ${[0, 0, 1]} - ${"1+2"} | ${[1, 0]} - ${"a +1+c"} | ${[1, 0]} + ${"1+2"} | ${[]} + ${"a +1+c"} | ${[]} ${"y^2+5y - 1"} | ${[-1, 5, 1]} ${"2y^2+4y"} | ${[0, 4, 2]} ${"109h"} | ${[0, 109]} - ${"m+n+10"} | ${[1, 0]} + ${"m+n+10"} | ${[]} ${"x-x"} | ${[0, 0]} ${"x + 5 - 3 + x - 6 - x + 2"} | ${[-2, 1]} ${"2x-x"} | ${[0, 1]} - ${"x - x - 2"} | ${[1, 0]} + ${"x - x - 2"} | ${[]} `("$expression => $coefficients", ({ expression, coefficients }) => { const equation = atm.convert(lta.convert(expression)); const coefficientsList = getCoefficients(equation); @@ -121,11 +121,11 @@ describe("getCoefficients", () => { expect(coefficientsList).toEqual([0, 0]); }); - it('equation: "1 = -2" - if equation has no coefficient for x it will return coefficients [1, 0]', () => { + it('equation: "1 = -2" - if equation has no coefficient for x but can be rationalized it will return an empty array', () => { const equation = atm.convert(lta.convert("1+2")); const coefficientsList = getCoefficients(equation); - expect(coefficientsList).toEqual([1, 0]); + expect(coefficientsList).toEqual([]); }); it('equation: "m + n = - 2" - if equation has more than one variable, will return coefficients [1, 0]', () => { diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 9ff674a..10478ab 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -39,16 +39,21 @@ export default { }, { target: "-5 ≤ 3 - 2z ≤ 5", - eq: [ - "-8 ≤ -2z ≤ 2", - "4 ≥ z ≥ -1" - ], + eq: ["-8 ≤ -2z ≤ 2", "4 ≥ z ≥ -1"], }, { target: "57.06 ≤ 24.74 + 1.54b ≤ 171.02", eq: [ - "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", - "32.32 ≤1.54b ≤146.28 " + "57.06 - 24.74 ≤ 24.74 - 24.74 + 1.54b ≤ 171.02- 24.74", + "32.32 ≤1.54b ≤146.28 ", + ], + }, + { + target: "1 < 2x < 10", + eq: ["2 < 4x < 20"], + ne: [ + "2 = 4x = 20", + "1 <2x >10" ], }, ], diff --git a/src/fixtures/latex-equal/symbolic/linear-equations-in-two-variables.ts b/src/fixtures/latex-equal/symbolic/linear-equations-in-two-variables.ts index ba69be0..be13230 100644 --- a/src/fixtures/latex-equal/symbolic/linear-equations-in-two-variables.ts +++ b/src/fixtures/latex-equal/symbolic/linear-equations-in-two-variables.ts @@ -15,7 +15,7 @@ export default { ], }, { - // linear equations with 2 unknowns that are not x or y + // linear equations with 2 variables that are not x or y target: "a=\\frac{1}{2}b +5", eq: [ "-2a =-b -10", @@ -24,7 +24,7 @@ export default { ], }, { - // linear equations with 2 unknowns that are not x or y + // linear equations with 2 variables that are not x or y target: "a=\\frac{1}{2}b +5", eq: [ "-2a =-b -10", diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index 206c3a0..f53d74e 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -2,8 +2,8 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; import { expressionsCanBeCompared, - equationsHaveTheSameUnknowns, - getUnknowns, + equationsHaveTheSameVariables, + getVariables, transformEqualityInExpression, getCoefficients, solveLinearEquation, @@ -11,42 +11,54 @@ import { const m: any = mathjs; -export type InequalitiesPairs = { - leftHandInequality: MathNode; - rightHandInequality: MathNode; +export type NodePair = { + left: MathNode; + right: MathNode; }; -export type xRange = { - inferiorLimit: number; - superiorLimit: number; +export type Range = { + min: number; + max: number; }; -const operation = (signName: string) => (signName === "larger" ? ">" : "≥"); +const operation = (signName: string) => { + switch (signName) { + case "smaller": + return "<"; + case "smallerEq": + return "<="; + case "larger": + return ">"; + case "largerEq": + return ">="; + case "equal": + return "=="; + default: + return "!="; + } +}; -// break 3-way inequality in two 2-way inequalities; input a > b > c, output a > b and b > c -const breakInequality = (compoundInequality: any): InequalitiesPairs => ({ - leftHandInequality: new m.OperatorNode( +// splitInequality takes in a RelationalNode with 2 conditionals and 3 params and returns 2 operatorNodes each containing a 2-way inequality +export const splitInequality = (compoundInequality: any): NodePair => ({ + left: new m.OperatorNode( operation(compoundInequality.conditionals[0]), compoundInequality.conditionals[0], [compoundInequality.params[0], compoundInequality.params[1]] ), - rightHandInequality: new m.OperatorNode( + right: new m.OperatorNode( operation(compoundInequality.conditionals[1]), - compoundInequality.conditionals[0], + compoundInequality.conditionals[1], [compoundInequality.params[1], compoundInequality.params[2]] ), }); +// get interval inferior/superior limits export const getLimit = ( - expressionsPair: InequalitiesPairs, + expressionsPair: NodePair, limitType: string ): number => { - const expressionR = transformEqualityInExpression( - expressionsPair.rightHandInequality - ); - const expressionL = transformEqualityInExpression( - expressionsPair.leftHandInequality - ); + const expressionR = transformEqualityInExpression(expressionsPair.right); + const expressionL = transformEqualityInExpression(expressionsPair.left); const xFirstInequality = solveLinearEquation(getCoefficients(expressionR)); const xSecondInequality = solveLinearEquation(getCoefficients(expressionL)); @@ -55,59 +67,50 @@ export const getLimit = ( return Math.min(xFirstInequality, xSecondInequality); } - return Math.max(xFirstInequality, xSecondInequality); - + return Math.max(xFirstInequality, xSecondInequality); }; export const compareCompoundInequations = ( firstInequation: any, secondInequation: any ) => { - const result = firstInequation.conditionals.every( - (relation: string) => - secondInequation.conditionals.includes(relation) && - firstInequation.conditionals?.length === 2 && - firstInequation.params?.length === 3 && - expressionsCanBeCompared(firstInequation, secondInequation) - ); - if (!result) { + + if (!expressionsCanBeCompared(firstInequation, secondInequation)) { return false; } - const firstInequalityUnknownsName = getUnknowns(firstInequation); - const secondInequalityUnknownsName = getUnknowns(secondInequation); + const firstInequalityVariablesName = getVariables(firstInequation); + const secondInequalityVariablesName = getVariables(secondInequation); if ( - !equationsHaveTheSameUnknowns( - firstInequalityUnknownsName, - secondInequalityUnknownsName + !equationsHaveTheSameVariables( + firstInequalityVariablesName, + secondInequalityVariablesName ) && - firstInequalityUnknownsName?.length === 1 + firstInequalityVariablesName?.length === 1 ) { return false; } - const firstInequalities = breakInequality(firstInequation); - const secondInequalities = breakInequality(secondInequation); + const firstInequalities = splitInequality(firstInequation); + const secondInequalities = splitInequality(secondInequation); - // find out interval for inequality solution; + // find out interval for inequality solution; // it does not matter whether we have an open, or half-open/half-close interval, the signs are already compared and at this point they match - let firstInequalitiesSolution: xRange = { - inferiorLimit: getLimit(firstInequalities, "inferior"), - superiorLimit: getLimit(firstInequalities, "superior"), + let firstInequalitiesSolution: Range = { + min: getLimit(firstInequalities, "inferior"), + max: getLimit(firstInequalities, "superior"), }; - let secondInequalitiesSolution: xRange = { - inferiorLimit: getLimit(secondInequalities, "inferior"), - superiorLimit: getLimit(secondInequalities, "superior"), + let secondInequalitiesSolution: Range = { + min: getLimit(secondInequalities, "inferior"), + max: getLimit(secondInequalities, "superior"), }; // if interval limits are the same for both inequalities then we can say that inequalities are equivalent return ( - firstInequalitiesSolution.inferiorLimit === - secondInequalitiesSolution.inferiorLimit && - firstInequalitiesSolution.superiorLimit === - secondInequalitiesSolution.superiorLimit + firstInequalitiesSolution.min === secondInequalitiesSolution.min && + firstInequalitiesSolution.max === secondInequalitiesSolution.max ); }; diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index 9d99f13..ea36ece 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -1,8 +1,8 @@ import { mathjs } from "../mathjs"; import { MathNode } from "mathjs"; import { - getUnknowns, - equationsHaveTheSameUnknowns, + getVariables, + equationsHaveTheSameVariables, getCoefficients, solveLinearEquation, setXToOne, @@ -27,13 +27,13 @@ export const compareEquations = ( return true; } - let firstEquationUnknownsName = getUnknowns(firstExpression); - let secondEquationUnknownsName = getUnknowns(secondExpression); + let firstEquationVariablesName = getVariables(firstExpression); + let secondEquationVariablesName = getVariables(secondExpression); if ( - !equationsHaveTheSameUnknowns( - firstEquationUnknownsName, - secondEquationUnknownsName + !equationsHaveTheSameVariables( + firstEquationVariablesName, + secondEquationVariablesName ) ) { return false; @@ -43,7 +43,7 @@ export const compareEquations = ( let secondEquationCoefficients: number[]; // if both equations are linear in one variable then we solve "x" for both. If x has the same value then equations are equivalent - if (firstEquationUnknownsName.length === 1) { + if (firstEquationVariablesName.length === 1) { firstEquationCoefficients = getCoefficients(firstExpression); secondEquationCoefficients = getCoefficients(secondExpression); @@ -58,8 +58,8 @@ export const compareEquations = ( } // if both equations are linear in two variabled then we give value "1" for both "x". Doing this we get a linear equation in one variable "y". Then we solve "y" for both. If y has the same value then equations are equivalent - if (firstEquationUnknownsName.length === 2) { - let x = firstEquationUnknownsName[0]; + if (firstEquationVariablesName.length === 2) { + let x = firstEquationVariablesName[0]; // solve expression for x=1 let expraNoX = setXToOne(firstExpression, x); diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index f435dfd..0a0c140 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -151,7 +151,7 @@ export const isMathEqual = (a: any, b: any) => { // if both expressions are equations if (as.fn === "equal" && bs.fn === "equal") { - return compareEquations(as, bs, false); + return compareEquations(as, bs, false); } // if both expressions are inequalities treat greater sign as equal sign @@ -175,9 +175,30 @@ export const isMathEqual = (a: any, b: any) => { //@ts-ignore as?.conditionals?.length === bs?.conditionals?.length && //@ts-ignore - as?.conditionals?.length === 2 + as?.conditionals?.length === 2 && as?.conditionals?.toString() === bs?.conditionals?.toString() ) { - return compareCompoundInequations(as, bs); + const params = [ + "smaller", + "smallerEq", + "larger", + "largerEq", + "equal", + "unequal", + ]; + + const paramsIncludedA = params.some((param) => + //@ts-ignore + as.conditionals.includes(param) + ); + + const paramsIncludedB = params.some((param) => + //@ts-ignore + bs.conditionals.includes(param) + ); + + if (paramsIncludedA && paramsIncludedB) { + return compareCompoundInequations(as, bs); + } } return equality; diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 96e342c..1f439bc 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -39,8 +39,8 @@ export const transformEqualityInExpression = (equality: MathNode) => { return customSimplify(expression); }; -// check if equation is valid and find out the number of unknowns and their name -export const getUnknowns = (equation: MathNode) => { +// check if equation is valid and find out the number of variables and their name +export const getVariables = (equation: MathNode) => { let variableNames: string[] = []; equation.traverse(function (node, path, parent) { @@ -59,15 +59,14 @@ export const getUnknowns = (equation: MathNode) => { }; export const getCoefficients = (equation: MathNode) => { - let result: number[] = []; - // coefficients will be determined if equation has only one unknown + // coefficients will be determined if equation has only one variable try { const rationalizedEquation = m.rationalize(equation, {}, true); - result = rationalizedEquation.coefficients; + return rationalizedEquation.coefficients; } catch (e) { - // rationalize may fail if unknown is isolated in a fraction - // we give it another try to rationalize after applying a new round of simplify to separate the unknown + // rationalize may fail if variable is isolated in a fraction + // we give it another try to rationalize after applying a new round of simplify to separate the variable equation = simplify(equation, [ { l: "(n1-n2)/n3", r: "n1/n3-n2/n3" }, { l: "(n1+n2)/n3", r: "n1/n3+n2/n3" }, @@ -76,19 +75,17 @@ export const getCoefficients = (equation: MathNode) => { ]); try { - const rat = m.rationalize(equation, {}, true); - result = rat.coefficients; + const rationalizedEquation = m.rationalize(equation, {}, true); + return rationalizedEquation.coefficients; } catch (e) {} } - result = result.length === 0 ? [1, 0] : result; - - return result; + return [1, 0]; }; -export const setXToOne = (equation: any, unknownName: string) => +export const setXToOne = (equation: any, variableName: string) => equation.transform(function (node, path, parent) { - if (node.isSymbolNode && node.name === unknownName) { + if (node.isSymbolNode && node.name === variableName) { return new m.ConstantNode(1); } @@ -127,16 +124,16 @@ export const solveLinearEquation = (coefficients: number[]) => { return result; }; -export const equationsHaveTheSameUnknowns = ( - firstEquationUnknowns: string[], - secondEquationUnknowns: string[] +export const equationsHaveTheSameVariables = ( + firstEquationVariables: string[], + secondEquationVariables: string[] ) => { return ( - Array.isArray(firstEquationUnknowns) && - Array.isArray(secondEquationUnknowns) && - firstEquationUnknowns.length === secondEquationUnknowns.length && - firstEquationUnknowns.every( - (unknonwn, index) => unknonwn === secondEquationUnknowns[index] + Array.isArray(firstEquationVariables) && + Array.isArray(secondEquationVariables) && + firstEquationVariables.length === secondEquationVariables.length && + firstEquationVariables.every( + (variable, index) => variable === secondEquationVariables[index] ) ); }; From 76419a494cbefcf5445ba404c1fbe5fff2189192 Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 26 Oct 2021 15:41:15 +0300 Subject: [PATCH 32/34] add tests for supported operators as conditionals, fix latex-to-ast conversion for unequal token --- .../compare-compound-inequations.spec.ts | 5 +++++ src/conversion/latex-to-ast.ts | 13 +++++++++++-- .../3-way-inequalities-with-variables.ts | 17 +++++++++++++++++ src/symbolic/compare-compound-inequations.ts | 4 ++-- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/__tests__/compare-compound-inequations.spec.ts b/src/__tests__/compare-compound-inequations.spec.ts index 5abde99..2cd2ece 100644 --- a/src/__tests__/compare-compound-inequations.spec.ts +++ b/src/__tests__/compare-compound-inequations.spec.ts @@ -13,6 +13,11 @@ describe("splitInequality", () => { ${" 5 ≥ -4 + x > -1 - 1"} | ${" 5 ≥ -4 + x"} | ${"-4 + x > -1 - 1"} ${"x/x 20"} | ${"2<4x"} | ${"4x>20"} + ${ "-3 < 2x+5 < 17"} | ${"-3<2x+5"} | ${"2x+5 < 17"} + ${ "-3 = 6x = -3"} | ${"-3 = 6x"} | ${"6x = -3"} + ${ "a≥b≥c "} | ${"a≥b"} | ${"b≥c"} + ${ "a+2≥b-10≥c-100 "} | ${"a+2≥b-10"} | ${"b-10≥c-100"} + ${ "a≠b≠c "} | ${"a≠b"} | ${"b≠c"} `( "$compoundInequality => $leftPart, $rightPart", ({ compoundInequality, leftPart, rightPart }) => { diff --git a/src/conversion/latex-to-ast.ts b/src/conversion/latex-to-ast.ts index 2cc0266..5ad05d0 100644 --- a/src/conversion/latex-to-ast.ts +++ b/src/conversion/latex-to-ast.ts @@ -298,10 +298,11 @@ export const latex_rules = [ ["\\\\lnot(?![a-zA-Z])", "NOT"], ["=", "="], + ["≠", "NE"], ["\\\\neq(?![a-zA-Z])", "NE"], ["\\\\ne(?![a-zA-Z])", "NE"], ["\\\\not\\s*=", "NE"], - ["≠", "NE"], + ["\\\\leq(?![a-zA-Z])", "LE"], ["\\\\le(?![a-zA-Z])", "LE"], ["\\\\geq(?![a-zA-Z])", "GE"], @@ -689,7 +690,11 @@ export class LatexToAst { var lhs = this.expression(params); let relationalToken = (token) => - token === "<" || token === "LE" || token === ">" || token === "GE"; + token === "<" || + token === "LE" || + token === ">" || + token === "GE" || + token === "NE"; while ( this.token.token_type === "=" || @@ -733,6 +738,10 @@ export class LatexToAst { case "GE": case "ge": return "largerEq"; + case "NE": + return "unequal"; + case "ne": + return "unequal"; } }; diff --git a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts index 10478ab..77425fa 100644 --- a/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts +++ b/src/fixtures/latex-equal/symbolic/3-way-inequalities-with-variables.ts @@ -56,5 +56,22 @@ export default { "1 <2x >10" ], }, + { + target: "a≠b≠c", + ne: ["c≠d≠e"] + }, + { + target: "a=b=c", + ne: ["c=d=e"] + }, + { + target: "-99 ≤ 9-12r ≤ 45", + eq: ["-108≤ -12r ≤ 36", "-108/-12≤ -12r/-12 ≤ 36/-12","9 ≥ r ≥ -3", "-3≤ r≤ 9", ], + ne: [ + "-108≤ -12r < 36", + "-108≤ -12r > 36", + "-107/-12≤ -12r/-12 ≤ 36/-12" + ], + }, ], }; diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index f53d74e..f92c437 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,5 @@ import { mathjs } from "../mathjs"; -import { MathNode } from "mathjs"; +import { MathNode, unequal } from "mathjs"; import { expressionsCanBeCompared, equationsHaveTheSameVariables, @@ -33,7 +33,7 @@ const operation = (signName: string) => { return ">="; case "equal": return "=="; - default: + case "unequal": return "!="; } }; From 109ff0b941b646099af19398e81fc358614f868e Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 26 Oct 2021 15:57:51 +0300 Subject: [PATCH 33/34] update readme file --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e93d35f..3a104d3 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,17 @@ yarn jest src/__tests__/latex-equal.spec.ts -t src/fixtures/latex-equal/7119.ts ## Capabilities -- comparing linear equations in one variable +It can determine mathematical equivalence between: + +- linear equations in one variable - linear equations in two variables -- 2-way inequalities with one or two unknown variables +- 2-way inequalities in one or two variables - compound inequalities in one variable -- trigonometric identities or functions +- trigonometric identities and functions - inverse trigonometric functions -- it can also handle degrees, radians and gradians -- recognises similar notation for logarithms and based logarithms +- similar notation for logarithms and based logarithms + +It can also handle degrees, radians and gradians ### things that'd be great (but we may have to park until we have more time) From 77514d20b7e542b1675bedf93ebe162500e4fefc Mon Sep 17 00:00:00 2001 From: Carla Costea Date: Tue, 26 Oct 2021 16:49:33 +0300 Subject: [PATCH 34/34] refactor code and format --- .../compare-compound-inequations.spec.ts | 32 +++++++++---------- src/__tests__/utils.spec.ts | 4 +-- src/conversion/latex-to-ast.ts | 2 -- src/symbolic/compare-compound-inequations.ts | 3 +- src/symbolic/compare-equations.ts | 7 ++-- src/symbolic/index.ts | 4 ++- src/symbolic/utils.ts | 11 ++----- 7 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/__tests__/compare-compound-inequations.spec.ts b/src/__tests__/compare-compound-inequations.spec.ts index 2cd2ece..4c551e4 100644 --- a/src/__tests__/compare-compound-inequations.spec.ts +++ b/src/__tests__/compare-compound-inequations.spec.ts @@ -8,27 +8,27 @@ const atm = new AstToMathJs(); describe("splitInequality", () => { it.each` - compoundInequality | leftPart | rightPart - ${"20>x>7"} | ${"20>x"} | ${"x>7"} - ${" 5 ≥ -4 + x > -1 - 1"} | ${" 5 ≥ -4 + x"} | ${"-4 + x > -1 - 1"} - ${"x/x 20"} | ${"2<4x"} | ${"4x>20"} - ${ "-3 < 2x+5 < 17"} | ${"-3<2x+5"} | ${"2x+5 < 17"} - ${ "-3 = 6x = -3"} | ${"-3 = 6x"} | ${"6x = -3"} - ${ "a≥b≥c "} | ${"a≥b"} | ${"b≥c"} - ${ "a+2≥b-10≥c-100 "} | ${"a+2≥b-10"} | ${"b-10≥c-100"} - ${ "a≠b≠c "} | ${"a≠b"} | ${"b≠c"} + compoundInequality | leftPart | rightPart + ${"20>x>7"} | ${"20>x"} | ${"x>7"} + ${" 5 ≥ -4 + x > -1 - 1"} | ${" 5 ≥ -4 + x"} | ${"-4 + x > -1 - 1"} + ${"x/x 20"} | ${"2<4x"} | ${"4x>20"} + ${"-3 < 2x+5 < 17"} | ${"-3<2x+5"} | ${"2x+5 < 17"} + ${"-3 = 6x = -3"} | ${"-3 = 6x"} | ${"6x = -3"} + ${"a≥b≥c "} | ${"a≥b"} | ${"b≥c"} + ${"a+2≥b-10≥c-100 "} | ${"a+2≥b-10"} | ${"b-10≥c-100"} + ${"a≠b≠c "} | ${"a≠b"} | ${"b≠c"} `( "$compoundInequality => $leftPart, $rightPart", ({ compoundInequality, leftPart, rightPart }) => { - const equation = atm.convert(lta.convert(compoundInequality)); - const broken = splitInequality(equation); + const inequality = atm.convert(lta.convert(compoundInequality)); + const broken = splitInequality(inequality); - const leftPartExpression = atm.convert(lta.convert(leftPart)); - const rightPartExpression = atm.convert(lta.convert(rightPart)); + const leftSide = atm.convert(lta.convert(leftPart)); + const rightSide = atm.convert(lta.convert(rightPart)); - expect(broken.left).toEqual(leftPartExpression); - expect(broken.right).toEqual(rightPartExpression); + expect(broken.left).toEqual(leftSide); + expect(broken.right).toEqual(rightSide); } ); }); diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 4c5d2ef..e067b58 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -8,7 +8,7 @@ import { setXToOne, solveLinearEquation, expressionsCanBeCompared, - transformEqualityInExpression + transformEqualityInExpression, } from "../symbolic/utils"; const lta = new LatexToAst(); @@ -92,7 +92,7 @@ describe("getCoefficients", () => { it.each` expression | coefficients ${"x+0"} | ${[0, 1]} - ${"2x^2 = 2x"} | ${[1, 0]} + ${"2x^2 = 2x"} | ${[1, 0]} ${"x +1"} | ${[1, 1]} ${"((x^2 + x) / x) - 1"} | ${[0, 0, 1]} ${"1+2"} | ${[]} diff --git a/src/conversion/latex-to-ast.ts b/src/conversion/latex-to-ast.ts index 5ad05d0..50702a0 100644 --- a/src/conversion/latex-to-ast.ts +++ b/src/conversion/latex-to-ast.ts @@ -302,7 +302,6 @@ export const latex_rules = [ ["\\\\neq(?![a-zA-Z])", "NE"], ["\\\\ne(?![a-zA-Z])", "NE"], ["\\\\not\\s*=", "NE"], - ["\\\\leq(?![a-zA-Z])", "LE"], ["\\\\le(?![a-zA-Z])", "LE"], ["\\\\geq(?![a-zA-Z])", "GE"], @@ -739,7 +738,6 @@ export class LatexToAst { case "ge": return "largerEq"; case "NE": - return "unequal"; case "ne": return "unequal"; } diff --git a/src/symbolic/compare-compound-inequations.ts b/src/symbolic/compare-compound-inequations.ts index f92c437..0a9ea2b 100644 --- a/src/symbolic/compare-compound-inequations.ts +++ b/src/symbolic/compare-compound-inequations.ts @@ -1,5 +1,5 @@ import { mathjs } from "../mathjs"; -import { MathNode, unequal } from "mathjs"; +import { MathNode } from "mathjs"; import { expressionsCanBeCompared, equationsHaveTheSameVariables, @@ -75,7 +75,6 @@ export const compareCompoundInequations = ( secondInequation: any ) => { - if (!expressionsCanBeCompared(firstInequation, secondInequation)) { return false; } diff --git a/src/symbolic/compare-equations.ts b/src/symbolic/compare-equations.ts index ea36ece..447a325 100644 --- a/src/symbolic/compare-equations.ts +++ b/src/symbolic/compare-equations.ts @@ -83,7 +83,7 @@ export const compareEquations = ( // we have one distinct case, when multiplying both parts of an inequality with a negative number, the sign must change direction if (equivalence && isInequality) { // check if direction should be changed - let signShouldBeChanged = + return !( (m.isPositive(firstEquationCoefficients[0]) && m.isNegative(firstEquationCoefficients[1]) && m.isNegative(secondEquationCoefficients[0]) && @@ -91,9 +91,8 @@ export const compareEquations = ( (m.isNegative(firstEquationCoefficients[0]) && m.isPositive(firstEquationCoefficients[1]) && m.isPositive(secondEquationCoefficients[0]) && - m.isNegative(secondEquationCoefficients[1])); - - return !signShouldBeChanged; + m.isNegative(secondEquationCoefficients[1])) + ); } } diff --git a/src/symbolic/index.ts b/src/symbolic/index.ts index 0a0c140..96ff068 100644 --- a/src/symbolic/index.ts +++ b/src/symbolic/index.ts @@ -175,7 +175,9 @@ export const isMathEqual = (a: any, b: any) => { //@ts-ignore as?.conditionals?.length === bs?.conditionals?.length && //@ts-ignore - as?.conditionals?.length === 2 && as?.conditionals?.toString() === bs?.conditionals?.toString() + as?.conditionals?.length === 2 && + //@ts-ignore + as?.conditionals?.toString() === bs?.conditionals?.toString() ) { const params = [ "smaller", diff --git a/src/symbolic/utils.ts b/src/symbolic/utils.ts index 1f439bc..d63eb0f 100644 --- a/src/symbolic/utils.ts +++ b/src/symbolic/utils.ts @@ -32,12 +32,9 @@ export const expressionsCanBeCompared = ( }; // move the terms of the equations to the left hand side -export const transformEqualityInExpression = (equality: MathNode) => { - const expression = new m.OperatorNode("-", "subtract", equality.args); - +export const transformEqualityInExpression = (equality: MathNode) => // remove added/subtracted numbers/variables from both sides of the equation - return customSimplify(expression); -}; + customSimplify(new m.OperatorNode("-", "subtract", equality.args)); // check if equation is valid and find out the number of variables and their name export const getVariables = (equation: MathNode) => { @@ -53,9 +50,7 @@ export const getVariables = (equation: MathNode) => { } }); - variableNames.sort(); - - return variableNames; + return variableNames.sort(); }; export const getCoefficients = (equation: MathNode) => {