mathsteps-experimental-fork
Version:
Step by step math solutions. Experimental Fork
131 lines (128 loc) • 6.5 kB
JavaScript
import math from '../index.js';
import { myNodeToString } from './nodeServices/myNodeToString.js';
import { parseText } from './nodeServices/parseText.js';
import { removeUnnecessaryParentheses } from './nodeServices/removeUnnessaryParenthesis.js';
import { removeImplicitMultiplicationFromNode } from './treeUtil.js';
import { kemuFlatten, kemuNormalizeConstantNodes } from '../simplifyExpression/kemuSimplifyCommonServices.js';
import { convertNumParVarDivNumToNumVarDivNum, convertAll1xToX, convertAllSimpleFractionsToDecimals, normalizeNegativesAndFractions, makePlusMinusMinusAndReturnString } from '../simplifyExpression/mscNormalizeNodeFunctions.js';
import { areArraysEqualUnordered } from '../util/arrayUtils.js';
import { cleanString } from '../util/cleanString.js';
import { RGFraction, makeExtendedRegExp } from '../util/regex.js';
import _kemuSortArgs from '../simplifyExpression/kemuSortArgs.js';
const getNodeStepsToSolveExpression = (userStep) => math.simplifyExpression({
expressionAsText: cleanString(userStep),
isDebugMode: false,
expressionCtx: void 0,
getMistakes: false,
getAllNextStepPossibilities: false,
onStepCb: (step) => {
}
});
function normalizeNodeForEquality(node_) {
let node = typeof node_ === "string" ? parseText(node_) : node_;
node = convertNumParVarDivNumToNumVarDivNum(node);
node = removeUnnecessaryParentheses(node);
node = convertAll1xToX(node);
node = kemuFlatten(node);
node = removeImplicitMultiplicationFromNode(node);
node = convertAllSimpleFractionsToDecimals(node);
node = normalizeNegativesAndFractions(node);
node = kemuNormalizeConstantNodes(node);
node = convertAll1xToX(node);
node = _kemuSortArgs(node, true, true);
return node;
}
function normalizeStringExpressionForEquality(stringExpression) {
stringExpression = cleanString(stringExpression);
stringExpression = stringExpression.replace(/\((\d+(\.\d+)?)\)/g, "$1");
stringExpression = stringExpression.replace(/\(-(\d+(\.\d+)?)\)/g, "-$1");
stringExpression = makePlusMinusMinusAndReturnString(`0+${stringExpression}`);
stringExpression = stringExpression.replaceAll(/\b0[a-z]\b/gi, "0");
stringExpression = stringExpression.replaceAll(/\b1([a-z])\b/gi, "$1");
stringExpression = stringExpression.replace(/(\d+)\*([a-z])/gi, "$1$2");
const numberRG = "\\b\\d+(?:\\.\\d+)?[a-z]?\\b";
const regexExp = makeExtendedRegExp(String.raw`\b(${numberRG})-\((${numberRG})/(${numberRG})\)`, "gi");
stringExpression = stringExpression.replace(regexExp, "$1+(-$2/$3)");
stringExpression = cleanString(stringExpression);
return stringExpression;
}
function areEquationsEqual(eq0, eq1, equalityCache = null) {
const sides0 = eq0.split("=").map((side) => cleanString(side));
const sides1 = eq1.split("=").map((side) => cleanString(side));
return areExpressionEqual(sides0[0], sides1[0], equalityCache) && areExpressionEqual(sides0[1], sides1[1], equalityCache);
}
function areExpressionEqual(exp0, exp1, equalityCache = null) {
if (!exp0 || !exp1)
return false;
if (exp0 === exp1)
return true;
if (!equalityCache) {
return areExpressionEqualCore(exp0, exp1);
} else {
const strExp0 = typeof exp0 === "string" ? cleanString(exp0) : myNodeToString(exp0);
const strExp1 = typeof exp1 === "string" ? cleanString(exp1) : myNodeToString(exp1);
return equalityCache.getCachedEquality(strExp0, strExp1, areExpressionEqualCore);
}
}
function areExpressionEqualCore(exp0, exp1) {
if (!exp0 || !exp1)
return false;
if (exp0 === exp1)
return true;
const strExp0 = typeof exp0 === "string" ? cleanString(exp0) : myNodeToString(exp0);
const strExp1 = typeof exp1 === "string" ? cleanString(exp1) : myNodeToString(exp1);
if (strExp0 === strExp1)
return true;
const normalizedExprString0 = normalizeStringExpressionForEquality(strExp0);
const normalizedExprString1 = normalizeStringExpressionForEquality(strExp1);
if (normalizedExprString0 === normalizedExprString1)
return true;
if (canDoEqualityBasedOnEventualAnswer(normalizedExprString0, normalizedExprString1)) {
const answer1 = myNodeToString(getNodeStepsToSolveExpression(normalizedExprString0));
const answer2 = myNodeToString(getNodeStepsToSolveExpression(normalizedExprString1));
if (answer1 === answer2)
return true;
const normalizedAnswer1 = normalizeStringExpressionForEquality(answer1);
const normalizedAnswer2 = normalizeStringExpressionForEquality(answer2);
if (normalizedAnswer1 === normalizedAnswer2)
return true;
const normalizedAnswerNode0 = normalizeNodeForEquality(parseText(normalizedAnswer1));
const normalizedAnswerNode1 = normalizeNodeForEquality(parseText(normalizedAnswer2));
return normalizedAnswerNode0.equals(normalizedAnswerNode1);
} else {
const normalizedNode0 = normalizeNodeForEquality(parseText(normalizedExprString0));
const normalizedNode1 = normalizeNodeForEquality(parseText(normalizedExprString1));
return normalizedNode0.equals(normalizedNode1);
}
}
function canDoEqualityBasedOnEventualAnswer(strExp0, strExp1) {
const countMult0 = (strExp0.match(/\*/g) || []).length;
const countMult1 = (strExp1.match(/\*/g) || []).length;
if (countMult0 !== countMult1)
return false;
const countDiv0 = (strExp0.match(/\//g) || []).length;
const countDiv1 = (strExp1.match(/\//g) || []).length;
if (countDiv0 !== countDiv1)
return false;
if (strExp0.includes("/")) {
const fractions0 = strExp0.match(RGFraction) || [];
const fractions1 = strExp1.match(RGFraction) || [];
if (!areArraysEqualUnordered(fractions0, fractions1, { isEqualFn: (a, b) => a === b }))
return false;
}
const operators = ["+", "-", "*", "/"];
operators.forEach((op) => {
strExp0 = strExp0.replaceAll(op, ` ${op} `);
strExp1 = strExp1.replaceAll(op, ` ${op} `);
});
strExp0 = strExp0.replace(/\s+/g, " ");
strExp1 = strExp1.replace(/\s+/g, " ");
strExp0 = strExp0.replaceAll("(", "").replaceAll(")", "");
strExp1 = strExp1.replaceAll("(", "").replaceAll(")", "");
strExp0 = strExp0.replaceAll("+", " ").replaceAll("-", " ");
strExp1 = strExp1.replaceAll("+", " ").replaceAll("-", " ");
const sortedStrExp0 = strExp0.split(/\s+/).sort().join("$");
const sortedStrExp1 = strExp1.split(/\s+/).sort().join("$");
return sortedStrExp0 === sortedStrExp1;
}
export { areEquationsEqual, areExpressionEqual, getNodeStepsToSolveExpression, normalizeNodeForEquality, normalizeStringExpressionForEquality };