expressions: Accept algebraic forms for integrate and root

Acceptable syntaxes:

- `‘integrate(1;2;sin(X);X)’` or `‘∫(1;2;sin(X);X)’`

- `‘root(sin(X)-0.5;X;1)’`

Fixes: #1172

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2024-09-11 00:58:57 +02:00
parent 8330601303
commit b8d2be1425
7 changed files with 91 additions and 63 deletions

View file

@ -1235,7 +1235,7 @@ static algebraic_p sum_product(object::id op,
if (!expr->is_program())
{
rt.type_error();
rt.invalid_equation_error();
return nullptr;
}

View file

@ -369,6 +369,8 @@ NAMED(comb, "Combinations")
NAMED(perm, "Permutations")
NAMED(Sum, "Σ")
NAMED(Product, "∏")
CMD(Root)
NAMED(Integrate, "∫")
NAMED(cbrt, "∛") ALIAS(cbrt, "CubeRoot")
OP(hypot, "⊿") ALIAS(hypot, "Hypothenuse")
@ -583,10 +585,6 @@ CMD(TypeName)
CMD(Version)
CMD(ScreenCapture)
// High-level applications
CMD(Root)
NAMED(Integrate, "∫")
// Special arithmetic
NAMED(Div2, "QuoRem") ALIAS(Div2, "IDiv2")
ALIAS(Div2, "QuotientRemainder")

View file

@ -45,24 +45,29 @@ RECORDER(integrate, 16, "Numerical integration");
RECORDER(integrate_error, 16, "Numerical integrationsol");
COMMAND_BODY(Integrate)
NFUNCTION_BODY(Integrate)
// ----------------------------------------------------------------------------
// Numerical integration
// ----------------------------------------------------------------------------
{
object_g variable = rt.stack(0);
object_g eqobj = rt.stack(1);
object_g high = rt.stack(2);
object_g low = rt.stack(3);
if (!eqobj || !variable || !high || !low)
return ERROR;
if (arity != 4)
{
rt.internal_error();
return nullptr;
}
algebraic_g &variable = args[0];
algebraic_g &eqobj = args[1];
algebraic_g &high = args[2];
algebraic_g &low = args[3];
record(integrate,
"Integrating %t for variable %t in range %t-%t",
+eqobj,
+variable,
+low,
+high);
if (!eqobj->is_program())
{
rt.invalid_equation_error();
return nullptr;
}
record(integrate, "Integrating %t for variable %t in range %t-%t",
+eqobj, +variable, +low, +high);
// Check that we have a variable name on stack level 1 and
// a proram or equation on level 2
@ -70,30 +75,22 @@ COMMAND_BODY(Integrate)
id eqty = eqobj->type();
if (eqty == ID_equation)
{
eqobj = equation_p(+eqobj)->value();
if (!eqobj)
return ERROR;
eqobj = algebraic_p(equation_p(+eqobj)->value());
if (!eqobj || !eqobj->is_algebraic())
return nullptr;
eqty = eqobj->type();
}
if (eqty != ID_program && eqty != ID_expression)
name = nullptr;
if (!name || !low->is_algebraic() || !high->is_algebraic())
if ((eqty != ID_program && eqty != ID_expression) ||
!name || !low->is_algebraic() || !high->is_algebraic())
{
rt.type_error();
return ERROR;
return nullptr;
}
// Drop input parameters
rt.drop(4);
// Actual integration
program_g eq = program_p(+eqobj);
algebraic_g intg =
integrate(eq, name, algebraic_p(+low), algebraic_p(+high));
if (intg&& rt.push(+intg))
return OK;
return ERROR;
algebraic_g i = integrate(eq, name, algebraic_p(+low), algebraic_p(+high));
return i;
}

View file

@ -30,7 +30,7 @@
// ****************************************************************************
#include "algebraic.h"
#include "command.h"
#include "functions.h"
#include "symbol.h"
algebraic_p integrate(program_g eq,
@ -38,6 +38,11 @@ algebraic_p integrate(program_g eq,
algebraic_g low,
algebraic_g high);
COMMAND_DECLARE(Integrate,4);
NFUNCTION(Integrate, 4,
static bool can_be_symbolic(uint a)
{
return a == 0 || a == 1;
}
);
#endif // INTEGRATE_H

View file

@ -49,22 +49,24 @@ RECORDER(solve, 16, "Numerical solver");
RECORDER(solve_error, 16, "Numerical solver errors");
COMMAND_BODY(Root)
NFUNCTION_BODY(Root)
// ----------------------------------------------------------------------------
// Numerical solver
// ----------------------------------------------------------------------------
{
object_g eqobj = rt.stack(2);
object_g variable = rt.stack(1);
object_g guess = rt.stack(0);
if (arity != 3)
{
rt.internal_error();
return nullptr;
}
algebraic_g &eqobj = args[2];
algebraic_g &variable = args[1];
algebraic_g &guess = args[0];
if (!eqobj || !variable || !guess)
return ERROR;
return nullptr;
record(solve,
"Solving %t for variable %t with guess %t",
+eqobj,
+variable,
+guess);
record(solve, "Solving %t for variable %t with guess %t",
+eqobj, +variable, +guess);
// Check that we have a variable name on stack level 1 and
// a proram or equation on level 2
@ -72,40 +74,35 @@ COMMAND_BODY(Root)
id eqty = eqobj->type();
if (eqty == ID_equation)
{
eqobj = equation_p(+eqobj)->value();
if (!eqobj)
return ERROR;
eqobj = algebraic_p(equation_p(+eqobj)->value());
if (!eqobj || !eqobj->is_algebraic())
return nullptr;
eqty = eqobj->type();
}
if (eqty != ID_program && eqty != ID_expression)
name = nullptr;
if (!name)
if ((eqty != ID_program && eqty != ID_expression) || !name)
{
rt.type_error();
return ERROR;
return nullptr;
}
// Drop input parameters
rt.drop(3);
if (!eqobj->is_program())
{
rt.invalid_equation_error();
return ERROR;
return nullptr;
}
// Actual solving
program_g eq = program_p(+eqobj);
if (algebraic_g x = solve(eq, +name, guess))
if (algebraic_g x = solve(eq, +name, +guess))
{
size_t nlen = 0;
gcutf8 ntxt = name->value(&nlen);
object_g top = tag::make(ntxt, nlen, +x);
if (rt.push(top))
return rt.error() ? ERROR : OK;
if (top && !rt.error())
return algebraic_p(+top);
}
return ERROR;
return nullptr;
}

View file

@ -30,13 +30,18 @@
// ****************************************************************************
#include "algebraic.h"
#include "command.h"
#include "functions.h"
#include "menu.h"
#include "symbol.h"
algebraic_p solve(program_g eq, algebraic_g name, object_g guess);
COMMAND_DECLARE(Root,3);
NFUNCTION(Root,3,
static bool can_be_symbolic(uint a)
{
return a == 1 || a == 2;
}
);
COMMAND_DECLARE(StEq, 1);
COMMAND_DECLARE(RcEq, 0);

View file

@ -5687,6 +5687,11 @@ void tests::solver_testing()
step("Solver with expression")
.test(CLEAR, "'X+3' 'X' 0 ROOT", ENTER)
.noerror().expect("X:-3.");
step("Solver with arithmetic syntax")
.test(CLEAR, "'ROOT(X+3;X;0)'", ENTER)
.expect("'Root(X+3;X;0)'")
.test(RUNSTOP)
.expect("X:-3.");
step("Solver with equation")
.test(CLEAR, "'sq(x)=3' 'X' 0 ROOT", ENTER)
.noerror().expect("X:1.73205080757");
@ -5949,6 +5954,27 @@ void tests::numerical_integration_testing()
.test("'sq(Z)+Z'", ENTER).expect("'Z²+Z'")
.test(F, ALPHA, Z, ENTER).expect("'Z'")
.test(SHIFT, KEY8, F2).expect("8.83333333333", 350);
step("Integrate with symbols")
.test(CLEAR, "A B '1/X' 'X' INTEGRATE", ENTER)
.expect("'∫(A;B;1÷X;X)'")
.test(DOWN)
.editor("'∫(A;B;1÷X;X)'")
.test(ENTER)
.expect("'∫(A;B;1÷X;X)'");
step("Integrate with one symbol")
.test(CLEAR, "1 B '1/X' 'X' INTEGRATE", ENTER)
.expect("'∫(1;B;1÷X;X)'")
.test(DOWN)
.editor("'∫(1;B;1÷X;X)'")
.test(ENTER)
.expect("'∫(1;B;1÷X;X)'");
step("Integrate with second symbol")
.test(CLEAR, "A 1 '1/X' 'X' INTEGRATE", ENTER)
.expect("'∫(A;1;1÷X;X)'")
.test(DOWN)
.editor("'∫(A;1;1÷X;X)'")
.test(ENTER)
.expect("'∫(A;1;1÷X;X)'");
}