UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

110 lines (102 loc) 3.93 kB
const MAX_ITERATIONS = 20; class EquationSolverCore { constructor() { this.solveFunctionsMap = {}; } registerSolveFunction(pattern, solveFunction) { this.solveFunctionsMap[pattern] = solveFunction; } _applyCommonRules(equation) { // Transformations common to each equation types. // These rules are applied BEFORE rules delivered by solve providers. const poolOfRules = [ { l: 'EQ(a, fx)', r: 'EQ(fx, a)', id: 'swap_sides' }, ]; equation.applyRules(poolOfRules); } _solveInternal(equation) { equation._logStep(); equation._logStep('solving'); // Apply most generic rules before any other steps delivered by // solve providers. this._applyCommonRules(equation); // Build equation pattern. // Example: 'x^2 + 2x = -1' gives 'W(x)=C' const scheme = equation.getScheme(); // Search for solve function for given pattern. const solveFunction = this.solveFunctionsMap[scheme]; if (solveFunction == null) throw new Error(`no solver for equation: '${scheme}'`) // Run solve function. solveFunction(equation); } solveEquation(equation) { // TODO: Better way to detect did any changes applied. // TODO: Optimize it. let equationHashBefore = equation.toString(); let equationHashAfter = null; // TODO: Better logs. const _solve = () => { let iterIdx = 0; let haveHope = true; while (!equation.isSolved() && haveHope && (iterIdx < MAX_ITERATIONS)) { this._solveInternal(equation); equationHashAfter = equation.toString(); // eslint-disable-next-line eqeqeq if (equationHashBefore == equationHashAfter) { // Nothing changed after equation step. // We stuck without hope to solve it in next steps. haveHope = false; } // Go to next iteration if needed. equationHashBefore = equationHashAfter; iterIdx++; } }; // 1. First try. _solve(); if (!equation.isSolved()) { // 2. Second try. Simplify left side and try one more time. // We don't apply simplify at the begining to avoid alling into // too complicated patterns, which we can't match easily e.g. after // multiplication (x-2)(x+2). equation.simplifyLeft(); _solve(); if (!equation.isSolved()) { // 3. Third try. Simplify right and try one more time. equation.simplifyRight(); _solve(); } } } forkEquation(parentEquation, arrayOfChildEquations) { let arrayOfSolutions = []; // Name child equation basing on parent id: parent.1, parent.2, etc. const prefixForId = parentEquation.id ? `${parentEquation.id}.` : ''; arrayOfChildEquations.forEach((childEquation, childIdx) => { // Possible improvement: Better way to pass step callback to // child equations. childEquation.onStepCb = parentEquation.onStepCb; childEquation.id = prefixForId + (childIdx + 1); }); // Possible improvement: Better step logging. parentEquation._logStep('fork', true, { arrayOfChildEquations }); arrayOfChildEquations.forEach((childEquation, _childIdx) => { // Solve child equation. this.solveEquation(childEquation); // Join solutions from child nodes. if (childEquation.isSolved()) { // Success, child equation is solved. // Collect another solution for the parent equation. arrayOfSolutions = arrayOfSolutions.concat(childEquation.getSolutions()); } }); // Apply solutions collected from forked equations if found. parentEquation._logStep('join', true, { arrayOfChildEquations }); if (arrayOfSolutions.length > 0) parentEquation.applySolution(arrayOfSolutions); } } export { EquationSolverCore as default };