diff --git a/lib/formula_evaluator.dart b/lib/formula_evaluator.dart index f985479..91f7914 100644 --- a/lib/formula_evaluator.dart +++ b/lib/formula_evaluator.dart @@ -117,9 +117,7 @@ class FormulaEvaluator { } catch (e, stack) { // SPECIAL CASE: If the error message starts with signalMagicString, treat it as a signal message and return it instead of throwing an exception // SEE signal() function in the generated d4rt code above for how this is used - print("#######################"); if (e.toString().contains(signalMagicString)) { - print("***********************"); final signalMessage = e.toString().split(signalMagicString).last.trim(); return signalMessage; } @@ -295,6 +293,52 @@ class FormulaEvaluator { } } +Number formulaSolver(Formula formula, + String variableToSolve, + Map fixedInputValues, { + Number hint = 0, + Number step = 10, + Number maxDelta = 0.01, + int maxTries = 100, + }) { + + if( variableToSolve == formula.output.name ){ + return FormulaEvaluator().evaluate(formula, fixedInputValues); + } + + if (!formula.inputVarNames().contains(variableToSolve) ){ + throw ArgumentError( + 'Variable "$variableToSolve" is not an input or output variable of the formula "${formula + .name}".', + ); + } + + + final modifiedInputValues = Map.from(fixedInputValues); + var evaluator = FormulaEvaluator(); + Number f(Number x) { + modifiedInputValues[variableToSolve] = x; + final result = evaluator.evaluate(formula, modifiedInputValues); + if (result is Number) { + return result; + } else { + throw FormulaEvaluationException( + 'Expected formula evaluation to return a number, but got: $result ${result.runtimeType}', + ); + } + } + + var fixedFormulaOutput = fixedInputValues[formula.output.name]; + + return functionSolver( + (Number x) => f(x) - fixedFormulaOutput, + hint: hint, + step: step, + maxDelta: maxDelta, + maxTries: maxTries, + ); +} + class NoSolutionException implements Exception { final String message; diff --git a/test/formula_solver_test.dart b/test/formula_solver_test.dart index 2714a5f..b0407ae 100644 --- a/test/formula_solver_test.dart +++ b/test/formula_solver_test.dart @@ -5,53 +5,75 @@ import 'dart:math' as Math; void main() { - test("Solve x^2", () { - Number f(Number x) => x*x; - var root = functionSolver(f, hint: 10, step: 1); - expect(root, closeTo(0, 0.1)); + + group("Formulas", (){ + + test("Solve x^2 formula", () { + final formula = Formula( + name: 'Test x^2', + input: [ + VariableSpec(name: 'x', unit: 'scalar'), + ], + output: VariableSpec(name: 'y', unit: 'scalar'), + d4rtCode: 'y = x*x;', + ); + + var solution = formulaSolver(formula, "x", {"y": 25}, maxDelta: 1e-10); + expect( solution, closeTo(5, 1e-10)); + }); + }); + group('Native functions', () { + test("Solve x^2", () { + Number f(Number x) => x * x; + var root = functionSolver(f, hint: 10, step: 1); + expect(root, closeTo(0, 0.1)); + }); - test("Solve (x-1000)^2", () { - Number f(Number x) => (x-1000)*(x-1000); - var root = functionSolver(f, hint: 10, step: 1, maxTries: 1000); - expect(root, closeTo(1000, 0.1)); - }); - test("Solve x^2 + 1", () { - Number f(Number x) => x*x+1; + test("Solve (x-1000)^2", () { + Number f(Number x) => (x - 1000) * (x - 1000); + var root = functionSolver(f, hint: 10, step: 1, maxTries: 1000); + expect(root, closeTo(1000, 0.1)); + }); - expect(()=> functionSolver(f, hint: 10, step: 1), throwsA(isA())); - }); + test("Solve x^2 + 1", () { + Number f(Number x) => x * x + 1; - test("Solve (x-2)(x-10", () { - Number f(Number x) => (x-2)*(x-10); + expect(() => functionSolver(f, hint: 10, step: 1), + throwsA(isA())); + }); - expect( functionSolver(f, hint: 10, step: 1), closeTo(10, 0.1)); - }); + test("Solve (x-2)(x-10", () { + Number f(Number x) => (x - 2) * (x - 10); - test('Solve sqrt(x) = 2 => x = 4', () { - Number f(Number x) => Math.sqrt(x) - 2; - var root = functionSolver(f, hint: 5, step: 1); - expect(root, closeTo(4, 0.1)); - }); + expect(functionSolver(f, hint: 10, step: 1), closeTo(10, 0.1)); + }); - test('Solve sin(x) = 0 near pi (hint 3)', () { - Number f(Number x) => Math.sin(x); - var root = functionSolver(f, hint: 3, step: 1); - expect(root, closeTo(Math.pi, 0.01)); - }); + test('Solve sqrt(x) = 2 => x = 4', () { + Number f(Number x) => Math.sqrt(x) - 2; + var root = functionSolver(f, hint: 5, step: 1); + expect(root, closeTo(4, 0.1)); + }); - test('Solve tan(x) = 1 => x = pi/4', () { - Number f(Number x) => Math.tan(x) - 1; - var root = functionSolver(f, hint: 0, step: 1); - expect(root, closeTo(Math.pi / 4, 0.01)); - }); + test('Solve sin(x) = 0 near pi (hint 3)', () { + Number f(Number x) => Math.sin(x); + var root = functionSolver(f, hint: 3, step: 1); + expect(root, closeTo(Math.pi, 0.01)); + }); - test('Solve exp(x) = 2 => x = ln(2)', () { - Number f(Number x) => Math.exp(x) - 2; - var root = functionSolver(f, hint: 1, step: 1); - expect(root, closeTo(Math.log(2), 0.01)); + test('Solve tan(x) = 1 => x = pi/4', () { + Number f(Number x) => Math.tan(x) - 1; + var root = functionSolver(f, hint: 0, step: 1); + expect(root, closeTo(Math.pi / 4, 0.01)); + }); + + test('Solve exp(x) = 2 => x = ln(2)', () { + Number f(Number x) => Math.exp(x) - 2; + var root = functionSolver(f, hint: 1, step: 1); + expect(root, closeTo(Math.log(2), 0.01)); + }); }); }