UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

91 lines (88 loc) 4.58 kB
import { isConstantNode, isOperatorNode } from '../../config.js'; import { myNodeToString } from './myNodeToString.js'; import { parseText } from './parseText.js'; import { getReverseOp, getAddRemoveTermTypeBasedOnOp } from '../../types/changeType/changeAndMistakeUtils.js'; import { filterUniqueValues } from '../../util/arrayUtils.js'; import { cleanString } from '../../util/cleanString.js'; function isNegative(node) { if (isOperatorNode(node) && node.isUnary()) { return true; } if (isOperatorNode(node) && node.op === "*") { return node.args.some( (arg) => isConstantNode(arg) && Number.parseFloat(arg.value.toString()) < 0 ); } if (isConstantNode(node)) { return Number.parseFloat(node.value.toString()) < 0; } return false; } function generateExpressions(node, options = { combinedTermLimit: 2 }) { const countTermsFn = (node2) => isOperatorNode(node2) && node2.args ? node2.args.reduce((acc, arg) => acc + countTermsFn(arg), 0) : 1; let dfsNodeId = 0; function core(node2) { let expressions = []; const operations = ["+", "-", "*", "/", "+-"]; if (isOperatorNode(node2) && operations.includes(node2.op) && node2.args.length >= 2) { const secondOp = node2.op; let firstOp = node2.op; if (node2.args.length === 2 && firstOp === "-") { firstOp = "+"; } expressions.push({ op: firstOp, node: node2.args[0], term: myNodeToString(node2.args[0]), dfsNodeId: dfsNodeId++ }); expressions.push({ op: secondOp, node: node2.args[1], term: myNodeToString(node2.args[1]), dfsNodeId: dfsNodeId++ }); } if (isOperatorNode(node2) && node2.args) { for (const arg of node2.args) expressions = expressions.concat(core(arg)); } return expressions; } const filterResult = filterUniqueValues(core(node), (a, b) => a.term === b.term && a.op === b.op); if (filterResult.length === 0) { if (isConstantNode(node)) { return isNegative(node) ? [{ op: "+-", term: myNodeToString(node), node, dfsNodeId: 0 }] : [{ op: "+", term: myNodeToString(node), node, dfsNodeId: 0 }]; } } return filterResult.filter((item) => countTermsFn(item.node) <= options.combinedTermLimit); } function findAllOperationsThatCanBeRemoved(fromNode_, history, options = {}) { const unFlattenNodeFn = (node) => parseText(myNodeToString(node)); const getNumberOpFn = (step) => options.isAdded ? step?.addedNumOp || step?.removeNumberOp : step?.removeNumberOp || step?.addedNumOp; const fromNodeNode = typeof fromNode_ === "string" ? parseText(fromNode_) : unFlattenNodeFn(fromNode_); const fromNodeString = typeof fromNode_ === "string" ? cleanString(fromNode_) : myNodeToString(fromNodeNode); const previousRemovedNumOps = history.filter((step) => getNumberOpFn(step)).map((step) => getNumberOpFn(step)); const flippedPreviousReverseNumOps = previousRemovedNumOps.map((numOp) => ({ number: numOp.number, op: getReverseOp(numOp.op) })); let termOps = generateExpressions(fromNodeNode); termOps = filterUniqueValues(termOps, (a, b) => a.term === b.term && a.op === b.op); termOps = termOps.filter((term) => !flippedPreviousReverseNumOps.some((prev) => prev.number === term.term && prev.op === term.op)); termOps = termOps.filter((term) => term.term !== "0"); termOps = filterUniqueValues(termOps, (a, b) => a.term === b.term && a.op === b.op); const newTermStrings = termOps.map((termOp) => { const fn = (str) => ["+", "-", "*", "/", "^", "(", ")"].some((op) => str.includes(op)); const tempFrom = fn(fromNodeString) ? `(${fromNodeString})` : fromNodeString; const tempTerm = fn(termOp.term) ? `(${termOp.term})` : termOp.term; return `${tempFrom} ${getReverseOp(termOp.op)} ${tempTerm}`; }); return termOps.map((term, index) => ({ removeNumberOp: { number: term.term, op: term.op, dfsNodeId: term.dfsNodeId }, newExpression: newTermStrings[index] })).filter((term) => term.removeNumberOp.number !== void 0 && term.removeNumberOp.op !== void 0 && term.newExpression !== void 0).map((term) => ({ // map to the correct format from: fromNodeString, to: term.newExpression, changeType: getAddRemoveTermTypeBasedOnOp(getReverseOp(term.removeNumberOp.op), options.isAdded ? "add" : "remove"), isMistake: false, removeNumberOp: { ...term.removeNumberOp, op: getReverseOp(term.removeNumberOp.op), number: term.removeNumberOp.number }, availableChangeTypes: ["EQ_REMOVE_TERM"] })); } export { findAllOperationsThatCanBeRemoved };