UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

133 lines (130 loc) 7.45 kB
import math from '../index.js'; import { getCrossMultiplication } from '../kemuEquation/Special-CrossMultiplication.js'; import { areExpressionEqual } from '../newServices/expressionEqualsAndNormalization.js'; import { myNodeToString } from '../newServices/nodeServices/myNodeToString.js'; import { findAllOperationsThatCanBeRemoved } from '../newServices/nodeServices/termRemovalOperations.js'; import { getValidStepEqCache } from './equationCache.js'; import { mistakeSearches } from './mistakes/regexPemdasMistakes.js'; import { getAddRemoveTermTypeBasedOnOp } from '../types/changeType/changeAndMistakeUtils.js'; import { ChangeTypes } from '../types/changeType/ChangeTypes.js'; import { filterUniqueValues } from '../util/arrayUtils.js'; import { cleanString } from '../util/cleanString.js'; import { RGIntOrDecimal } from '../util/regex.js'; import { parseText } from '../newServices/nodeServices/parseText.js'; const { SIMPLIFY_ARITHMETIC__ADD, SIMPLIFY_ARITHMETIC__SUBTRACT } = ChangeTypes; const expressionEquals = (exp0, exp1) => areExpressionEqual(exp0, exp1, getValidStepEqCache()); function _normalizeExpressionForCountingSubtractions(expr) { return expr.replace(/\+-/g, "-").replace(/-\+/g, "-").replace(/--/g, "+").replace(/^-/, ""); } function _correctSimplifyAdditionToSubtraction(steps) { steps.forEach((step) => { if (step.changeType === SIMPLIFY_ARITHMETIC__ADD && step.from.includes("-")) { const fromRaw = _normalizeExpressionForCountingSubtractions(step.from); const toRaw = _normalizeExpressionForCountingSubtractions(step.to); const fromMinusCount = [...fromRaw.matchAll(/-/g)].length; const toMinusCount = [...toRaw.matchAll(/-/g)].length; if (toMinusCount < fromMinusCount) { step.changeType = SIMPLIFY_ARITHMETIC__SUBTRACT; } } }); const newAvailableChangeTypes = steps.filter((step) => !step?.isMistake).map((step) => step?.changeType); steps.forEach((step) => { step.availableChangeTypes = newAvailableChangeTypes; }); } function _convertRawStepsToProcessedStep(steps) { const processedStepsSet = /* @__PURE__ */ new Set(); return steps.filter((step) => step.to).flatMap((step) => step.to.map((toStr) => ({ ...step, availableChangeTypes: steps.filter((step2) => !step2?.isMistake).map((step2) => step2?.changeType), // TODO remove availableChangeTypes from here? We add it again in _addAllAvailableChangeTypesToEachStep to: cleanString(toStr), from: cleanString(step.from) }))).filter((step) => !processedStepsSet.has(step.to) && processedStepsSet.add(step.to)).filter((step) => step.to !== step.from); } function _addAllAvailableChangeTypesToEachStep(steps) { const availableChangeTypes = steps.filter((step) => !step?.isMistake).map((step) => step?.changeType); steps.forEach((step) => { step.availableChangeTypes = availableChangeTypes; }); } function _addAllPossibleCorrectTosToEachStep(steps) { const allPossibleCorrectTos = steps.filter((step) => !step.isMistake).map((step) => step.to); steps.forEach((step) => { step.allPossibleCorrectTos = allPossibleCorrectTos; }); } function findAllNextStepOptions(userStep_, neededForEquations, { getMistakes = true, isDebugMode = false } = {}) { let userStep = typeof userStep_ === "string" ? myNodeToString(parseText(userStep_)) : myNodeToString(userStep_); userStep = userStep.replaceAll(/\b0[a-z]\b/gmi, "0").replaceAll(/\b0\*[a-z]\b/gmi, "0"); const potentialSteps = []; math.simplifyExpression({ expressionAsText: userStep, isDebugMode, getMistakes, getAllNextStepPossibilities: true, onStepCb: (step) => potentialSteps.push(step) }); const processedSteps = filterUniqueValues(_convertRawStepsToProcessedStep(potentialSteps), (itemA, itemB) => expressionEquals(itemA.to, itemB.to)); if (neededForEquations?.otherSide && neededForEquations?.history) { const removedDepth = neededForEquations.history.filter((step) => step.removeNumberOp).map((step) => step.removeNumberOp).length; const addedDepth = neededForEquations.history.filter((step) => step.addedNumOp).map((step) => step.addedNumOp).length; if (neededForEquations.history.length <= 1 && removedDepth === 0 && addedDepth === 0) { const crossMultiplied = getCrossMultiplication(userStep, neededForEquations.otherSide); if (crossMultiplied) { processedSteps.push({ from: userStep, to: myNodeToString(crossMultiplied.crossRight), changeType: "EQ_CROSS_MULTIPLY", isMistake: false, availableChangeTypes: ["EQ_CROSS_MULTIPLY"] }); processedSteps.push({ from: userStep, to: myNodeToString(crossMultiplied.crossLeft), changeType: "EQ_CROSS_MULTIPLY", isMistake: false, availableChangeTypes: ["EQ_CROSS_MULTIPLY"] }); } } if (neededForEquations.history.length <= 2 && removedDepth === 0 && addedDepth === 0) { const hasSingleNegative = (str) => (str.match(/-/gmi)?.length || 0) === 1; const hasVariable = (str) => (str.match(/[a-z]/gmi)?.length || 0) === 1; const hasNumber = (str) => (str.match(RGIntOrDecimal)?.length || 0) === 1; const thisSideIsVariableAndNegative = hasSingleNegative(userStep) && hasVariable(userStep); const thisSideIsNumberAndNegative = hasSingleNegative(userStep) && hasNumber(userStep); const otherSideIsVariableAndNegative = hasSingleNegative(neededForEquations.otherSide) && hasVariable(neededForEquations.otherSide); const otherSideIsNumberAndNegative = hasSingleNegative(neededForEquations.otherSide) && hasNumber(neededForEquations.otherSide); if (thisSideIsVariableAndNegative && otherSideIsNumberAndNegative || otherSideIsVariableAndNegative && thisSideIsNumberAndNegative) { processedSteps.push({ from: userStep, to: `(${userStep}) * -1`, changeType: "EQ_MULTIPLY_BOTH_SIDES_BY_NEGATIVE_ONE", isMistake: false, availableChangeTypes: ["EQ_MULTIPLY_BOTH_SIDES_BY_NEGATIVE_ONE"] }); } } if (removedDepth < 2 && addedDepth < 1) { processedSteps.push(...findAllOperationsThatCanBeRemoved(userStep, neededForEquations.history)); } if (removedDepth < 1 && addedDepth < 2) { let makeAddedFn = function(expression0, otherSide, history) { const steps = findAllOperationsThatCanBeRemoved(otherSide, history, { isAdded: true }); const removedNumOps = steps.map((step) => step.removeNumberOp).filter((removedNumOp) => removedNumOp !== void 0); const newExpressions = removedNumOps.map((removedNumOp) => { return { from: expression0, to: `(${expression0}) ${removedNumOp.op} ${removedNumOp.number}`, removeNumberOp: void 0, addedNumOp: removedNumOp, changeType: getAddRemoveTermTypeBasedOnOp(removedNumOp.op, "add"), availableChangeTypes: ["EQ_ADD_TERM"], isMistake: false }; }); return newExpressions; }; processedSteps.push(...makeAddedFn(userStep, neededForEquations.otherSide, neededForEquations.history)); } } if (!neededForEquations?.otherSide) { const manualMistakes = mistakeSearches(userStep); processedSteps.push(...manualMistakes); } _addAllAvailableChangeTypesToEachStep(processedSteps); _addAllPossibleCorrectTosToEachStep(processedSteps); _correctSimplifyAdditionToSubtraction(processedSteps); return processedSteps; } export { findAllNextStepOptions };