UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

127 lines (122 loc) 7.07 kB
'use strict'; 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;