Building an Equation Solver with the Parser and Transformer
ID: 1461
using uCalcSoftware;
var uc = new uCalc();
static void EqSolveCb(uCalc.Callback cb) { // Callback based on the Bisection Method
var expr = cb.ArgExpr(1); // ByExpr: Unevaluated Expression object (lazy evaluation)
var a = cb.Arg(2); // Argument 2: Range Minimum
var b = cb.Arg(3); // Argument 3: Range Maximum
var variable = cb.ArgItem(4); // ByHandle: The variable Item object
// Helper to update the variable in the uCalc engine and evaluate the expression
double EvaluateAt(double val) {
variable.Value(val); // Push the new test value to the variable
return expr.Evaluate(); // Evaluate the pre-parsed expression
}
// Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
if (EvaluateAt(b) < EvaluateAt(a)) (a, b) = (b, a);
var midpoint = 0.0;
var fMidpoint = 0.0;
// Bisection loop
for (int i = 0; i <= 100; i++) {
midpoint = (a + b) / 2;
fMidpoint = EvaluateAt(midpoint);
if (Math.Abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0
// Narrow the bounds (compact logic!)
if (fMidpoint < 0) a = midpoint; else b = midpoint;
}
if (Math.Abs(fMidpoint) > 1e-5) cb.Error.Raise("No solution found in the given range.");
cb.Return(Math.Round(midpoint, 7)); // Return the final solved value
}
// 1. Define variables that might be used by the end-user
uc.DefineVariable("x");
uc.DefineVariable("MyVar");
// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
var t = uc.ExpressionTransformer;
t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
"EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})");
// 3. Define the custom function signature
uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb);
// --- Demo Executions ---
System.Collections.Generic.List eqList = new() {
"EqSolve(x + 5 = 125)", // Using default range [-10000,10000]
"EqSolve(x^2 + 5 = 105)", // Picks one result from the default range
"EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root
"EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root
"EqSolve(x^2 + 1000 = 5)", // No existing solution
"EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x'
};
foreach(var eq in eqList) {
Console.WriteLine(uc.ExpressionTransformer.Transform(eq)); // Displays transformed expression
Console.WriteLine($"Result: {uc.EvalStr(eq)}"); // Returns result
}
EqSolve(x + 5 - (125), -10000, 10000, x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000, 10000, x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100, x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0, x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000, 10000, x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000, 10000, MyVar)
Result: 8 using uCalcSoftware; var uc = new uCalc(); static void EqSolveCb(uCalc.Callback cb) { // Callback based on the Bisection Method var expr = cb.ArgExpr(1); // ByExpr: Unevaluated Expression object (lazy evaluation) var a = cb.Arg(2); // Argument 2: Range Minimum var b = cb.Arg(3); // Argument 3: Range Maximum var variable = cb.ArgItem(4); // ByHandle: The variable Item object // Helper to update the variable in the uCalc engine and evaluate the expression double EvaluateAt(double val) { variable.Value(val); // Push the new test value to the variable return expr.Evaluate(); // Evaluate the pre-parsed expression } // Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary if (EvaluateAt(b) < EvaluateAt(a)) (a, b) = (b, a); var midpoint = 0.0; var fMidpoint = 0.0; // Bisection loop for (int i = 0; i <= 100; i++) { midpoint = (a + b) / 2; fMidpoint = EvaluateAt(midpoint); if (Math.Abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0 // Narrow the bounds (compact logic!) if (fMidpoint < 0) a = midpoint; else b = midpoint; } if (Math.Abs(fMidpoint) > 1e-5) cb.Error.Raise("No solution found in the given range."); cb.Return(Math.Round(midpoint, 7)); // Return the final solved value } // 1. Define variables that might be used by the end-user uc.DefineVariable("x"); uc.DefineVariable("MyVar"); // 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser var t = uc.ExpressionTransformer; t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])", "EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})"); // 3. Define the custom function signature uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb); // --- Demo Executions --- System.Collections.Generic.List<string> eqList = new() { "EqSolve(x + 5 = 125)", // Using default range [-10000,10000] "EqSolve(x^2 + 5 = 105)", // Picks one result from the default range "EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root "EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root "EqSolve(x^2 + 1000 = 5)", // No existing solution "EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x' }; foreach(var eq in eqList) { Console.WriteLine(uc.ExpressionTransformer.Transform(eq)); // Displays transformed expression Console.WriteLine($"Result: {uc.EvalStr(eq)}"); // Returns result }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
void ucalc_call EqSolveCb(uCalcBase::Callback cb) { // Callback based on the Bisection Method
auto expr = cb.ArgExpr(1); // ByExpr: Unevaluated Expression object (lazy evaluation)
auto a = cb.Arg(2); // Argument 2: Range Minimum
auto b = cb.Arg(3); // Argument 3: Range Maximum
auto variable = cb.ArgItem(4); // ByHandle: The variable Item object
// Helper to update the variable in the uCalc engine and evaluate the expression
auto EvaluateAt = [&](double val) -> double {
variable.Value(val);
return expr.Evaluate();
};
// Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
if (EvaluateAt(b) < EvaluateAt(a)) swap(a, b);
auto midpoint = 0.0;
auto fMidpoint = 0.0;
// Bisection loop
for (int i = 0; i <= 100; i++) {
midpoint = (a + b) / 2;
fMidpoint = EvaluateAt(midpoint);
if (abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0
// Narrow the bounds (compact logic!)
if (fMidpoint < 0) a = midpoint; else b = midpoint;
}
if (abs(fMidpoint) > 1e-5) cb.Error().Raise("No solution found in the given range.");
cb.Return(round(midpoint * 10000000.0) / 10000000.0); // Return the final solved value
}
int main() {
uCalc uc;
// 1. Define variables that might be used by the end-user
uc.DefineVariable("x");
uc.DefineVariable("MyVar");
// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
auto t = uc.ExpressionTransformer();
t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
"EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})");
// 3. Define the custom function signature
uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb);
// --- Demo Executions ---
vector eqList = {
"EqSolve(x + 5 = 125)", // Using default range [-10000,10000]
"EqSolve(x^2 + 5 = 105)", // Picks one result from the default range
"EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root
"EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root
"EqSolve(x^2 + 1000 = 5)", // No existing solution
"EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x'
};
for(auto eq : eqList) {
cout << uc.ExpressionTransformer().Transform(eq) << endl; // Displays transformed expression
cout << "Result: " << uc.EvalStr(eq) << endl; // Returns result
}
}
EqSolve(x + 5 - (125), -10000, 10000, x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000, 10000, x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100, x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0, x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000, 10000, x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000, 10000, MyVar)
Result: 8 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; void ucalc_call EqSolveCb(uCalcBase::Callback cb) { // Callback based on the Bisection Method auto expr = cb.ArgExpr(1); // ByExpr: Unevaluated Expression object (lazy evaluation) auto a = cb.Arg(2); // Argument 2: Range Minimum auto b = cb.Arg(3); // Argument 3: Range Maximum auto variable = cb.ArgItem(4); // ByHandle: The variable Item object // Helper to update the variable in the uCalc engine and evaluate the expression auto EvaluateAt = [&](double val) -> double { variable.Value(val); return expr.Evaluate(); }; // Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary if (EvaluateAt(b) < EvaluateAt(a)) swap(a, b); auto midpoint = 0.0; auto fMidpoint = 0.0; // Bisection loop for (int i = 0; i <= 100; i++) { midpoint = (a + b) / 2; fMidpoint = EvaluateAt(midpoint); if (abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0 // Narrow the bounds (compact logic!) if (fMidpoint < 0) a = midpoint; else b = midpoint; } if (abs(fMidpoint) > 1e-5) cb.Error().Raise("No solution found in the given range."); cb.Return(round(midpoint * 10000000.0) / 10000000.0); // Return the final solved value } int main() { uCalc uc; // 1. Define variables that might be used by the end-user uc.DefineVariable("x"); uc.DefineVariable("MyVar"); // 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser auto t = uc.ExpressionTransformer(); t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])", "EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})"); // 3. Define the custom function signature uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb); // --- Demo Executions --- vector<string> eqList = { "EqSolve(x + 5 = 125)", // Using default range [-10000,10000] "EqSolve(x^2 + 5 = 105)", // Picks one result from the default range "EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root "EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root "EqSolve(x^2 + 1000 = 5)", // No existing solution "EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x' }; for(auto eq : eqList) { cout << uc.ExpressionTransformer().Transform(eq) << endl; // Displays transformed expression cout << "Result: " << uc.EvalStr(eq) << endl; // Returns result } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub EqSolveCb(ByVal cb As uCalc.Callback)REM // Callback based on the Bisection Method
Dim expr = cb.ArgExpr(1) '// ByExpr: Unevaluated Expression object (lazy evaluation)
Dim a = cb.Arg(2) '// Argument 2: Range Minimum
Dim b = cb.Arg(3) '// Argument 3: Range Maximum
Dim variable = cb.ArgItem(4) '// ByHandle: The variable Item object
'// Helper to update the variable in the uCalc engine and evaluate the expression
Dim EvaluateAt = Function (val as Double) As Double
variable.Value(val) '// Push the new test value to the variable
return expr.Evaluate() '// Evaluate the pre-parsed expression
End Function
'// Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
If EvaluateAt(b) < EvaluateAt(a) Then Dim temp = a : a = b : b = temp
Dim midpoint = 0.0
Dim fMidpoint = 0.0
'// Bisection loop
For i As Integer = 0 To 100
midpoint = (a + b) / 2
fMidpoint = EvaluateAt(midpoint)
If Math.Abs(fMidpoint) < 1e-7 Then Exit For REM // Stop if close enough to 0
REM// Narrow the bounds (compact logic!)
If fMidpoint < 0 Then a = midpoint Else b = midpoint
Next
If Math.Abs(fMidpoint) > 1e-5 Then cb.Error.Raise("No solution found in the given range.")
cb.Return(Math.Round(midpoint, 7)) '// Return the final solved value
End Sub
Public Sub Main()
Dim uc As New uCalc()
'// 1. Define variables that might be used by the end-user
uc.DefineVariable("x")
uc.DefineVariable("MyVar")
'// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
Dim t = uc.ExpressionTransformer
t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
"EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})")
'// 3. Define the custom function signature
uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", AddressOf EqSolveCb)
'// --- Demo Executions ---
Dim eqList As New List(Of String) From {
"EqSolve(x + 5 = 125)", '// Using default range [-10000,10000]
"EqSolve(x^2 + 5 = 105)", '// Picks one result from the default range
"EqSolve(x^2 + 5 = 105, 0, 100)", '// Restricts to positive root
"EqSolve(x^2 + 5 = 105, -100, 0)", '// Restricts to negative root
"EqSolve(x^2 + 1000 = 5)", '// No existing solution
"EqSolve(40 + MyVar * 6 = 88, for MyVar)" '// Uses custom variable 'MyVar' instead of 'x'
}
For Each eq In eqList
Console.WriteLine(uc.ExpressionTransformer.Transform(eq)) '// Displays transformed expression
Console.WriteLine($"Result: {uc.EvalStr(eq)}") '// Returns result
Next
End Sub
End Module
EqSolve(x + 5 - (125), -10000, 10000, x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000, 10000, x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100, x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0, x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000, 10000, x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000, 10000, MyVar)
Result: 8 Imports System Imports uCalcSoftware Public Module Program Public Sub EqSolveCb(ByVal cb As uCalc.Callback)REM // Callback based on the Bisection Method Dim expr = cb.ArgExpr(1) '// ByExpr: Unevaluated Expression object (lazy evaluation) Dim a = cb.Arg(2) '// Argument 2: Range Minimum Dim b = cb.Arg(3) '// Argument 3: Range Maximum Dim variable = cb.ArgItem(4) '// ByHandle: The variable Item object '// Helper to update the variable in the uCalc engine and evaluate the expression Dim EvaluateAt = Function (val as Double) As Double variable.Value(val) '// Push the new test value to the variable return expr.Evaluate() '// Evaluate the pre-parsed expression End Function '// Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary If EvaluateAt(b) < EvaluateAt(a) Then Dim temp = a : a = b : b = temp Dim midpoint = 0.0 Dim fMidpoint = 0.0 '// Bisection loop For i As Integer = 0 To 100 midpoint = (a + b) / 2 fMidpoint = EvaluateAt(midpoint) If Math.Abs(fMidpoint) < 1e-7 Then Exit For REM // Stop if close enough to 0 REM// Narrow the bounds (compact logic!) If fMidpoint < 0 Then a = midpoint Else b = midpoint Next If Math.Abs(fMidpoint) > 1e-5 Then cb.Error.Raise("No solution found in the given range.") cb.Return(Math.Round(midpoint, 7)) '// Return the final solved value End Sub Public Sub Main() Dim uc As New uCalc() '// 1. Define variables that might be used by the end-user uc.DefineVariable("x") uc.DefineVariable("MyVar") '// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser Dim t = uc.ExpressionTransformer t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])", "EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})") '// 3. Define the custom function signature uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", AddressOf EqSolveCb) '// --- Demo Executions --- Dim eqList As New List(Of String) From { "EqSolve(x + 5 = 125)", '// Using default range [-10000,10000] "EqSolve(x^2 + 5 = 105)", '// Picks one result from the default range "EqSolve(x^2 + 5 = 105, 0, 100)", '// Restricts to positive root "EqSolve(x^2 + 5 = 105, -100, 0)", '// Restricts to negative root "EqSolve(x^2 + 1000 = 5)", '// No existing solution "EqSolve(40 + MyVar * 6 = 88, for MyVar)" '// Uses custom variable 'MyVar' instead of 'x' } For Each eq In eqList Console.WriteLine(uc.ExpressionTransformer.Transform(eq)) '// Displays transformed expression Console.WriteLine($"Result: {uc.EvalStr(eq)}") '// Returns result Next End Sub End Module