mathsteps-experimental-fork
Version:
Step by step math solutions. Experimental Fork
127 lines (122 loc) • 7.07 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const myNodeToString = require('../../../newServices/nodeServices/myNodeToString.js');
const nodeHelpers = require('../../../newServices/nodeServices/nodeHelpers.js');
const parseText = require('../../../newServices/nodeServices/parseText.js');
const removeUnnessaryParenthesis = require('../../../newServices/nodeServices/removeUnnessaryParenthesis.js');
const stepEvaluationHelpers = require('../../stepEvaluationHelpers.js');
const changeAndMistakeUtils = require('../../../types/changeType/changeAndMistakeUtils.js');
function applyOpFixBasedOnTermRemovals(removed1, removed2, removedOp) {
const is1Negative = removed1.includes("-");
const is2Negative = removed2.includes("-");
const isNeitherNegative = !is1Negative && !is2Negative;
const isBothNegative = is1Negative && is2Negative;
const isOnly1Negative = is1Negative && !is2Negative || !is1Negative && is2Negative;
let opPerformed = null;
if (removedOp === "*" || removedOp === "/" || isNeitherNegative)
opPerformed = removedOp;
else if (removedOp === "+" && isBothNegative)
opPerformed = "+";
else if (removedOp === "+" && isOnly1Negative)
opPerformed = "-";
else
throw new Error(`Unexpected state. Operator: ${removedOp}`);
return opPerformed;
}
function _parseExpression(expr) {
const ast = parseText.parseText(expr);
const flattenedTerms = nodeHelpers.flattenAndIndexTrackAST(ast).sort((a, b) => a.index - b.index);
const combinedTerms = nodeHelpers.combineNumberVarTimesTerms(flattenedTerms);
const normalizedTerms = nodeHelpers.combineMakeMinusNegativeTerms(combinedTerms);
return nodeHelpers.makeCountTerms(normalizedTerms);
}
function earlyIsValidOperation(fromTerms, toTerms) {
const fromHasDivisionOp = fromTerms.some((term) => term.type === "operator" && term.value === "/");
const toHasDivisionOp = toTerms.some((term) => term.type === "operator" && term.value === "/");
const fromHasMultOp = fromTerms.some((term) => term.type === "operator" && term.value === "*");
const toHasMultOp = toTerms.some((term) => term.type === "operator" && term.value === "*");
if (!fromHasDivisionOp && toHasDivisionOp)
return false;
if (!fromHasMultOp && toHasMultOp)
return false;
const fromNoOps = fromTerms.filter((term) => term.type === "term");
const toNoOps = toTerms.filter((term) => term.type === "term");
if (fromNoOps.length !== toNoOps.length + 1)
return false;
return true;
}
function isValidOperation(useOperators, removedNoOpTerms, addedNoOpTerms, removedOperators) {
const hasDivisionOp = useOperators.some((op) => op.term.value === "/");
const hasVariables = removedNoOpTerms.some((term) => /[a-zA-Z]/.test(term.term.value));
const hasDivisionOrVariables = hasDivisionOp || hasVariables;
if (addedNoOpTerms.length > 1 || removedOperators.length > 1 && !hasDivisionOrVariables || removedOperators.length > 2 && hasDivisionOrVariables || hasVariables && removedNoOpTerms.length > 3 || !hasVariables && removedNoOpTerms.length > 2 || removedNoOpTerms.length === 0) {
return false;
} else {
return true;
}
}
function findAttemptedOperationUseCore(from, to) {
const fromStr = typeof from === "string" ? from : myNodeToString.myNodeToString(from);
const toStr = typeof to === "string" ? to : myNodeToString.myNodeToString(to);
const strLengthDiff = Math.abs(fromStr.length - toStr.length);
if (strLengthDiff > 7)
return null;
const fromTerms = _parseExpression(fromStr);
const toTerms = _parseExpression(toStr);
const fromStrRemovedUnnecessaryParen = myNodeToString.myNodeToString(removeUnnessaryParenthesis.removeUnnecessaryParentheses(fromStr));
const toStrRemovedUnnecessaryParen = myNodeToString.myNodeToString(removeUnnessaryParenthesis.removeUnnecessaryParentheses(toStr));
const fromParenthesisCount = fromStrRemovedUnnecessaryParen.match(/\(/g)?.length || 0;
const toParenthesisCount = toStrRemovedUnnecessaryParen.match(/\(/g)?.length || 0;
if (fromParenthesisCount !== toParenthesisCount)
return null;
if (!earlyIsValidOperation(fromTerms, toTerms))
return null;
const { removed, added, opFound = null } = nodeHelpers.getAddedAndRemovedTerms(fromTerms, toTerms);
const removedNoOpTerms = removed.filter((item) => item.term.type === "term").sort((a, b) => a.term.index - b.term.index) || [];
const addedNoOpTerms = added?.filter((item) => item.term.type === "term").sort((a, b) => a.term.index - b.term.index) || [];
const removedOperators = removed?.filter((item) => item.term.type === "operator").sort((a, b) => a.term.index - b.term.index) || [];
const useOperators = [opFound, ...removedOperators].filter((op) => op);
if (!isValidOperation(useOperators, removedNoOpTerms, addedNoOpTerms, removedOperators))
return null;
const removedTerm1 = removedNoOpTerms[0].term;
const removedTerm2 = removedNoOpTerms[1]?.term ?? removedTerm1;
const resultTerm = addedNoOpTerms[0].term;
const useOp = useOperators[0].term;
const termOperationFromSystem = stepEvaluationHelpers.getAnswerFromStep(`${removedTerm1.value} ${useOp.value} ${removedTerm2.value}`);
let fullAttemptedOpResult = toTerms.map((term) => {
return term.type === "term" && term.value === resultTerm.value && resultTerm.index === term.index ? { ...term, value: termOperationFromSystem } : term;
}).map((term) => term.value).join("");
fullAttemptedOpResult = fullAttemptedOpResult.replace(/^--/g, "-");
return {
opResult: resultTerm.value,
removedTerms: [removedTerm1.value, removedTerm2.value],
fullAttemptedOpResult,
// Fix operators we use based on num + - num specifically for getting the changeType.
changeType: changeAndMistakeUtils.getSimplifyChangeTypeByOp(applyOpFixBasedOnTermRemovals(removedTerm1.value, removedTerm2.value, useOp.value))
};
}
function findAttemptedOperationUse({ from, to, expressionEquals }) {
const fromStr = typeof from === "string" ? from : myNodeToString.myNodeToString(from);
const toStr = typeof to === "string" ? to : myNodeToString.myNodeToString(to);
const findWhatOpRes = findAttemptedOperationUseCore(fromStr, toStr);
if (!findWhatOpRes)
return null;
const { changeType, fullAttemptedOpResult } = findWhatOpRes;
const isMistake = !expressionEquals(fullAttemptedOpResult, toStr);
const fromReachesSameAsAttempted = expressionEquals(stepEvaluationHelpers.getAnswerFromStep(fromStr), stepEvaluationHelpers.getAnswerFromStep(fullAttemptedOpResult));
return {
removedTerms: findWhatOpRes.removedTerms,
from: fromStr,
to: toStr,
changeType,
attemptedToGetTo: fromReachesSameAsAttempted ? fullAttemptedOpResult : void 0,
/* mistakenChangeType: 'UNKNOWN', */
isMistake,
// mTo: [],
// availableChangeTypes: allPossibleNextStep.map(step => step.changeType),
attemptedChangeType: changeType
// allPossibleCorrectTos,
};
}
exports.findAttemptedOperationUse = findAttemptedOperationUse;
exports.findAttemptedOperationUseCore = findAttemptedOperationUseCore;