mirror of
https://github.com/c3d/DB48X-on-DM42.git
synced 2024-09-28 03:20:53 +02:00
algebra: Symbolic integration and Primitive
command
Implement the `Primitive` command, parsing, rendering and graphical rendering. Implement pattern recognition for the primitive patterns described in section E-2 of the HP50G Advanced Reference Manual. This also fixes a vertical alignment issue rendering expression, which was showing where `X^4` and `dX` would not align properly. Fixes: #1212 Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
parent
b93b5863ab
commit
ed41ec646c
7 changed files with 449 additions and 78 deletions
|
@ -106,7 +106,7 @@ ERROR(purge_active_directory, "Cannot purge active directory")
|
|||
ERROR(invalid_bitmap_file, "Invalid bitmap file")
|
||||
ERROR(cannot_isolate, "Unable to isolate")
|
||||
ERROR(unknown_derivative, "Unknown derivative")
|
||||
|
||||
ERROR(unknown_primitive, "Unknown primitive")
|
||||
|
||||
// Filesystem errors
|
||||
#ifndef FRROR
|
||||
|
|
|
@ -212,13 +212,13 @@ symbol_p expression::render(uint depth, int &precedence, bool editing)
|
|||
symbol_g ltxt = render(depth, lprec, editing);
|
||||
int prec = obj->precedence();
|
||||
id oid = obj->type();
|
||||
if (oid == ID_Derivative)
|
||||
if (oid == ID_Derivative || oid == ID_Primitive)
|
||||
{
|
||||
symbol_g arg = parentheses(ltxt);
|
||||
precedence = precedence::FUNCTION;
|
||||
return op + rtxt + arg;
|
||||
}
|
||||
else if (prec != precedence::FUNCTION)
|
||||
else if (prec != precedence::FUNCTION)
|
||||
{
|
||||
if (op->is_alpha())
|
||||
{
|
||||
|
@ -595,10 +595,12 @@ size_t expression::required_memory(id type, id op,
|
|||
//
|
||||
// Names of wildcards have a special role based on the initial letter:
|
||||
// - a, b, c: Constant values (numbers), i.e. is_real returns true
|
||||
// c must not be zero
|
||||
// c must not be zero when matching, is numbered when generating
|
||||
// - d, e, f: Expressions (non-constant), i.e. is_real returns false
|
||||
// - i, j : Positive integer values,i.e. type is ID_integer
|
||||
// - k, l, m: Non-zero positive integer values,i.e. type is ID_integer
|
||||
// - k : Non-zero positive integer values,i.e. type is ID_integer
|
||||
// - l : Linear function of independent variable,
|
||||
// sets 'a' and 'b'in output for a*x+b
|
||||
// - n, o : An expression containing the independent variable
|
||||
// - p, q, r: An expression not containing the independent variable
|
||||
// r must not be zero
|
||||
|
@ -724,7 +726,7 @@ static inline bool must_be_integer(char first)
|
|||
// Convention for naming integers in rewrite rules
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
return must_be(first, 'i', 'm');
|
||||
return must_be(first, 'i', 'k');
|
||||
}
|
||||
|
||||
|
||||
|
@ -733,7 +735,7 @@ static inline bool must_be_nonzero(char first)
|
|||
// Convention for naming non-zero integers in rewrite rules
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
return must_be(first, "klmcr");
|
||||
return must_be(first, "kcr");
|
||||
}
|
||||
|
||||
|
||||
|
@ -782,6 +784,15 @@ static inline bool must_not_contain_the_independent_variable(char first)
|
|||
}
|
||||
|
||||
|
||||
static inline bool must_be_linear_function_of_independent_variable(char first)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Matches a linear function of the independent variable
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
return first == 'l';
|
||||
}
|
||||
|
||||
|
||||
static inline char some_index(char first)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Convention for naming a constant when generating non-principal solutions
|
||||
|
@ -793,6 +804,7 @@ static inline char some_index(char first)
|
|||
case '#': return 'i';
|
||||
case '+': return 'n';
|
||||
case '-': return 's';
|
||||
case 'c': return 'C';
|
||||
case '@': return '@'; // Pi
|
||||
case '!': return '!'; // i
|
||||
case '=': return '='; // Current independent variable
|
||||
|
@ -919,6 +931,40 @@ static size_t check_match(size_t eq, size_t eqsz,
|
|||
if (must_be_nonzero(cat) && ftop->is_zero(false))
|
||||
return 0;
|
||||
}
|
||||
else if (must_be_linear_function_of_independent_variable(cat))
|
||||
{
|
||||
if (!expression::contains_independent_variable)
|
||||
return 0;
|
||||
bool isok = false;
|
||||
algebraic_g a, b;
|
||||
if (expression::independent)
|
||||
{
|
||||
if (symbol_g ivar = *expression::independent)
|
||||
{
|
||||
if (symbol_p s = ftop->as_quoted<symbol>())
|
||||
{
|
||||
isok = s->is_same_as(ivar);
|
||||
if (isok)
|
||||
{
|
||||
a = integer::make(1);
|
||||
b = integer::make(0);
|
||||
}
|
||||
}
|
||||
expression_p x = expression::as_expression(ftop);
|
||||
if (x && x->is_linear(ivar, a, b))
|
||||
isok = true;
|
||||
}
|
||||
}
|
||||
if (!isok)
|
||||
return 0;
|
||||
symbol_g an = symbol::make("A");
|
||||
symbol_g bn = symbol::make("B");
|
||||
if (!an || !bn ||
|
||||
!rt.push(+an) || !rt.push(+a) ||
|
||||
!rt.push(+bn) || !rt.push(+b) ||
|
||||
!rt.locals(4))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if things must be sorted
|
||||
if (must_be_sorted(wcat))
|
||||
|
@ -1056,6 +1102,7 @@ static algebraic_p build_expr(expression_p eqin,
|
|||
found = expression::independent
|
||||
? *expression::independent
|
||||
: nullptr;
|
||||
nvars++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1063,6 +1110,7 @@ static algebraic_p build_expr(expression_p eqin,
|
|||
snprintf(buf, sizeof(buf),
|
||||
"%c%u", c, ++expression::constant_index);
|
||||
found = symbol::make(buf);
|
||||
nvars++;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1831,8 +1879,8 @@ grob_p expression::suscript(grapher &g,
|
|||
pixsize gw = xw + yw;
|
||||
|
||||
coord voff = (1 - dir) * xh / 2;
|
||||
coord xt = -vx - xh / 2;
|
||||
coord yt = -vy + xt + voff - yh / 2;
|
||||
coord xt = vy - xh / 2;
|
||||
coord yt = vx - yh / 2 + xt + voff;
|
||||
coord xb = xt + xh - 1;
|
||||
coord yb = yt + yh - 1;
|
||||
coord t = xt < yt ? xt : yt;
|
||||
|
@ -1851,11 +1899,10 @@ grob_p expression::suscript(grapher &g,
|
|||
rs.copy(xs, 0, xt - t);
|
||||
rs.copy(ys, xw, yt - t);
|
||||
if (alignleft)
|
||||
g.voffset = xt - t + coord(xh)/2 - coord(gh)/2;
|
||||
g.voffset = xt - t + coord(xh)/2 - coord(gh)/2 + vx;
|
||||
else
|
||||
g.voffset = yt - t + coord(yh)/2 - coord(gh)/2 + vy;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2232,11 +2279,17 @@ grob_p expression::graph(grapher &g, uint depth, int &precedence)
|
|||
oid != ID_comb && oid != ID_perm)
|
||||
{
|
||||
if (lprec < prec)
|
||||
{
|
||||
lg = parentheses(g, lg);
|
||||
if (oid != ID_pow)
|
||||
if (rprec < prec ||
|
||||
(rprec == prec && (oid == ID_sub || oid == ID_div)))
|
||||
rg = parentheses(g, rg);
|
||||
lv = g.voffset;
|
||||
}
|
||||
if (oid != ID_pow &&
|
||||
(rprec < prec ||
|
||||
(rprec == prec && (oid == ID_sub || oid == ID_div))))
|
||||
{
|
||||
rg = parentheses(g, rg);
|
||||
rv = g.voffset;
|
||||
}
|
||||
}
|
||||
precedence = prec;
|
||||
switch (oid)
|
||||
|
@ -2264,6 +2317,17 @@ grob_p expression::graph(grapher &g, uint depth, int &precedence)
|
|||
rv = g.voffset;
|
||||
lg = prefix(g, rv, rg, lv, lg);
|
||||
return lg;
|
||||
case ID_Primitive:
|
||||
rg = prefix(g, 0, "d", rv, rg);
|
||||
rv = g.voffset;
|
||||
rg = prefix(g, lv, lg, rv, rg);
|
||||
rv = g.voffset;
|
||||
if (rg)
|
||||
{
|
||||
lg = integral(g, rg->height());
|
||||
rg = suscript(g, 0, lg, rv, rg, false);
|
||||
}
|
||||
return rg;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
@ -2476,6 +2540,110 @@ object::result expression::variable_command(command_fn callback)
|
|||
}
|
||||
|
||||
|
||||
bool expression::is_linear(symbol_r sym, algebraic_g &a, algebraic_g &b) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Check if the expression is linear in the current independent variable
|
||||
// ----------------------------------------------------------------------------
|
||||
// Note that this returns false if we only have constants
|
||||
{
|
||||
if (!depends_on(sym))
|
||||
return false;
|
||||
|
||||
object_p op = outermost_operator();
|
||||
if (!op)
|
||||
return false;
|
||||
|
||||
id type = op->type();
|
||||
|
||||
if (type == ID_symbol)
|
||||
{
|
||||
bool ok = symbol_p(op)->is_same_as(sym);
|
||||
if (ok)
|
||||
{
|
||||
a = integer::make(1);
|
||||
b = integer::make(0);
|
||||
}
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
if (type == ID_add || type == ID_sub || type == ID_mul || type == ID_div)
|
||||
{
|
||||
expression_g l, r;
|
||||
if (split(type, l, r))
|
||||
{
|
||||
bool lcst = !l->depends_on(sym);
|
||||
bool rcst = !r->depends_on(sym);
|
||||
if (lcst)
|
||||
{
|
||||
if (rcst)
|
||||
return false;
|
||||
if (r->is_linear(sym, a, b))
|
||||
{
|
||||
algebraic_g t = l->evaluate();
|
||||
switch(type)
|
||||
{
|
||||
case ID_add: b = t + b; break;
|
||||
case ID_sub: b = t - b; break;
|
||||
case ID_mul: b = t * b; a = t * a; break;
|
||||
default:
|
||||
case ID_div: return false;
|
||||
}
|
||||
return a && b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (rcst)
|
||||
{
|
||||
if (l->is_linear(sym, a, b))
|
||||
{
|
||||
algebraic_g t = r->evaluate();
|
||||
switch(type)
|
||||
{
|
||||
case ID_add: b = b + t; break;
|
||||
case ID_sub: b = b - t; break;
|
||||
case ID_mul: b = b * t; a = a * t; break;
|
||||
case ID_div: b = b / t; a = a / t; break;
|
||||
default: return false;
|
||||
}
|
||||
return a && b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (l->is_linear(sym, a, b))
|
||||
{
|
||||
algebraic_g ra, rb;
|
||||
if (r->is_linear(sym, ra, rb))
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ID_add: a = a + ra; b = b + rb; break;
|
||||
case ID_sub: a = a - ra; b = b - rb; break;
|
||||
case ID_mul:
|
||||
case ID_div:
|
||||
default: return false;
|
||||
}
|
||||
return a && b;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool expression::depends_on(symbol_r sym) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Return true if the symbol is referenced in expression
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
return sym->found_in(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
|
@ -2839,21 +3007,29 @@ bool expression::split_equation(expression_g &left, expression_g &right) const
|
|||
// Split an expression between left and right parts
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size_t depth = rt.depth();
|
||||
bool result = false;
|
||||
return split(ID_TestEQ, left, right);
|
||||
}
|
||||
|
||||
for (object_p obj : *this)
|
||||
if (!rt.push(obj))
|
||||
goto error;
|
||||
|
||||
if (rt.depth() > depth)
|
||||
bool expression::split(id type, expression_g &left, expression_g &right) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Split around a binary operator
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
stack_depth_restore sdr;
|
||||
bool result = false;
|
||||
|
||||
if (!expand_without_size())
|
||||
return false;
|
||||
|
||||
if (rt.depth() > sdr.depth)
|
||||
{
|
||||
if (object_p outer = rt.top())
|
||||
{
|
||||
if (outer->type() == ID_TestEQ)
|
||||
if (outer->type() == type)
|
||||
{
|
||||
size_t eq = 1;
|
||||
size_t len = rt.depth() - depth - eq;
|
||||
size_t len = rt.depth() - sdr.depth - eq;
|
||||
if (object_g r = grab_arguments(eq, len))
|
||||
{
|
||||
if (object_g l = grab_arguments(eq, len))
|
||||
|
@ -2872,10 +3048,6 @@ bool expression::split_equation(expression_g &left, expression_g &right) const
|
|||
}
|
||||
}
|
||||
|
||||
error:
|
||||
size_t ndepth = rt.depth();
|
||||
if (ndepth > depth)
|
||||
rt.drop(ndepth - depth);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -3740,19 +3912,22 @@ expression_p expression::derivative(symbol_r sym) const
|
|||
expression_g eq = this;
|
||||
eq = expression::make(ID_Derivative, algebraic_g(eq), algebraic_g(sym));
|
||||
expression_g result = eq->rewrites(
|
||||
P|indep, zero, // Expression not containing the variable
|
||||
P>>indep, zero, // Expression not containing the variable
|
||||
|
||||
(S(X))|indep, S(X), // Special handling for this form
|
||||
(S(X))>>indep, S(X), // Special handling for this form
|
||||
|
||||
indep|indep, one,
|
||||
(X + Y)|indep, (X|indep)+(Y|indep),
|
||||
(X - Y)|indep, (X|indep)-(Y|indep),
|
||||
(X * Y)|indep, X*(Y|indep) + (X|indep)*Y,
|
||||
(X / Y)|indep, ((X|indep)*Y-X*(Y|indep))/sq(Y),
|
||||
(X ^ A)|indep, A*(X^(A-one)) * (X|indep),
|
||||
indep>>indep, one,
|
||||
(X + Y)>>indep, (X>>indep)+(Y>>indep),
|
||||
(X - Y)>>indep, (X>>indep)-(Y>>indep),
|
||||
(X * Y)>>indep, X*(Y>>indep) + (X>>indep)*Y,
|
||||
(X / Y)>>indep, ((X>>indep)*Y-X*(Y>>indep))/sq(Y),
|
||||
(X ^ A)>>indep, A*(X^(A-one)) * (X>>indep),
|
||||
|
||||
A+B, A+B,
|
||||
A-B, A-B,
|
||||
A*B, A*B,
|
||||
A/B, A/B,
|
||||
A^B, A^B,
|
||||
zero*X, zero,
|
||||
X*zero, zero,
|
||||
zero+X, X,
|
||||
|
@ -3762,40 +3937,40 @@ expression_p expression::derivative(symbol_r sym) const
|
|||
X^zero, one,
|
||||
X^one, X,
|
||||
|
||||
(X ^ E)|indep, (X^E)*((E|indep)*log(X) + ((X|indep)*E)/X),
|
||||
(X ^ E)>>indep, (X^E)*((E>>indep)*log(X) + ((X>>indep)*E)/X),
|
||||
|
||||
(-X)|indep, -(X|indep),
|
||||
inv(X)|indep, -(X|indep) / sq(X),
|
||||
abs(X)|indep, (X|indep)*sign(X),
|
||||
sign(X)|indep, zero,
|
||||
(-X)>>indep, -(X>>indep),
|
||||
inv(X)>>indep, -(X>>indep) / sq(X),
|
||||
abs(X)>>indep, (X>>indep)*sign(X),
|
||||
sign(X)>>indep, zero,
|
||||
|
||||
sin(X)|indep, (X|indep)*cos(X),
|
||||
cos(X)|indep, -(X|indep)*sin(X),
|
||||
tan(X)|indep, (X|indep)/sq(cos(x)),
|
||||
sinh(X)|indep, (X|indep)*cosh(X),
|
||||
cosh(X)|indep, (X|indep)*sinh(X),
|
||||
tanh(X)|indep, (X|indep)/sq(cosh(X)),
|
||||
sin(X)>>indep, (X>>indep)*cos(X),
|
||||
cos(X)>>indep, -(X>>indep)*sin(X),
|
||||
tan(X)>>indep, (X>>indep)/sq(cos(x)),
|
||||
sinh(X)>>indep, (X>>indep)*cosh(X),
|
||||
cosh(X)>>indep, (X>>indep)*sinh(X),
|
||||
tanh(X)>>indep, (X>>indep)/sq(cosh(X)),
|
||||
|
||||
asin(X)|indep, (X|indep)/sqrt(one-sq(X)),
|
||||
acos(X)|indep, -(X|indep)/sqrt(one-sq(X)),
|
||||
atan(X)|indep, (X|indep)/(one+sq(X)),
|
||||
asinh(X)|indep, (X|indep)/sqrt(one+sq(X)),
|
||||
acosh(X)|indep, (X|indep)/sqrt(sq(X)-one),
|
||||
atanh(X)|indep, (X|indep)/(one-sq(X)),
|
||||
asin(X)>>indep, (X>>indep)/sqrt(one-sq(X)),
|
||||
acos(X)>>indep, -(X>>indep)/sqrt(one-sq(X)),
|
||||
atan(X)>>indep, (X>>indep)/(one+sq(X)),
|
||||
asinh(X)>>indep, (X>>indep)/sqrt(one+sq(X)),
|
||||
acosh(X)>>indep, (X>>indep)/sqrt(sq(X)-one),
|
||||
atanh(X)>>indep, (X>>indep)/(one-sq(X)),
|
||||
|
||||
log(X)|indep, (X|indep)/X,
|
||||
exp(X)|indep, (X|indep)*exp(X),
|
||||
log2(X)|indep, (X|indep)/(log(two)*X),
|
||||
exp2(X)|indep, log(two)*(X|indep)*exp2(X),
|
||||
log10(X)|indep, (X|indep)/(log(ten)*X),
|
||||
exp10(X)|indep, log(ten)*(X|indep)*exp10(X),
|
||||
log1p(X)|indep, (X|indep)/(X+one),
|
||||
expm1(X)|indep, (X|indep)*exp(X),
|
||||
log(X)>>indep, (X>>indep)/X,
|
||||
exp(X)>>indep, (X>>indep)*exp(X),
|
||||
log2(X)>>indep, (X>>indep)/(log(two)*X),
|
||||
exp2(X)>>indep, log(two)*(X>>indep)*exp2(X),
|
||||
log10(X)>>indep, (X>>indep)/(log(ten)*X),
|
||||
exp10(X)>>indep, log(ten)*(X>>indep)*exp10(X),
|
||||
log1p(X)>>indep, (X>>indep)/(X+one),
|
||||
expm1(X)>>indep, (X>>indep)*exp(X),
|
||||
|
||||
sq(X)|indep, two*X*(X|indep),
|
||||
sqrt(X)|indep, (X|indep)/(two * sqrt(X)),
|
||||
cubed(X)|indep, three*sq(X)*(X|indep),
|
||||
cbrt(X)|indep, (X|indep)/(three*(sq(cbrt(X))))
|
||||
sq(X)>>indep, two*X*(X>>indep),
|
||||
sqrt(X)>>indep, (X>>indep)/(two * sqrt(X)),
|
||||
cubed(X)>>indep, three*sq(X)*(X>>indep),
|
||||
cbrt(X)>>indep, (X>>indep)/(three*(sq(cbrt(X))))
|
||||
);
|
||||
|
||||
if (+result == +eq)
|
||||
|
@ -3871,3 +4046,177 @@ INSERT_BODY(Derivative)
|
|||
int key = ui.evaluating;
|
||||
return ui.insert_softkey(key, "", "", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
// Primitive
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
expression_p expression::primitive(symbol_r sym) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Compute the primitive of the
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
save<symbol_g *> sindep(independent, (symbol_g *) &sym);
|
||||
save<object_g *> sindval(independent_value, nullptr);
|
||||
save<uint> sconstant(constant_index, 0);
|
||||
expression_g eq = this;
|
||||
eq = expression::make(ID_Primitive, algebraic_g(eq), algebraic_g(sym));
|
||||
expression_g result = eq->rewrites(
|
||||
P<<indep, P*indep,
|
||||
|
||||
indep<<indep, sq(indep)/two,
|
||||
(X + Y)<<indep, (X<<indep)+(Y<<indep),
|
||||
(X - Y)<<indep, (X<<indep)-(Y<<indep),
|
||||
(P * X)<<indep, P*(X<<indep),
|
||||
(X * P)<<indep, P*(X<<indep),
|
||||
(X / P)<<indep, (X<<indep)/P,
|
||||
(indep ^ mone)<<indep, log(indep),
|
||||
(indep ^ P)<<indep, (indep^(P+one)) / (P+one),
|
||||
|
||||
A+B, A+B,
|
||||
A-B, A-B,
|
||||
A*B, A*B,
|
||||
A/B, A/B,
|
||||
A^B, A^B,
|
||||
X/A/B, X/(A*B),
|
||||
P*N*Q, (P*Q)*N,
|
||||
P*(N*Q), (P*Q)*N,
|
||||
P*N/Q, (P/Q)*N,
|
||||
P*(N/Q), (P/Q)*N,
|
||||
zero*X, zero,
|
||||
X*zero, zero,
|
||||
zero+X, X,
|
||||
X+zero, X,
|
||||
X*one, X,
|
||||
X/one, X,
|
||||
one*X, X,
|
||||
one/X, inv(X),
|
||||
X^mone, inv(X),
|
||||
A/X, A*inv(X),
|
||||
X^zero, one,
|
||||
X^one, X,
|
||||
inv(X^A), X^-A,
|
||||
(X^A)^B, X^(A*B),
|
||||
sq(X), X^two,
|
||||
cubed(X), X^three,
|
||||
sqrt(X), X^(one/two),
|
||||
cbrt(X), X^(one/three),
|
||||
|
||||
// Patterns below in the order of section E-2 of HP50G ARM
|
||||
acos(L)<<indep, (L*acos(L)-sqrt(one-sq(L)))/A,
|
||||
exp10(L)<<indep, exp10(L)/(A*log(ten)),
|
||||
asin(L)<<indep, (L*asin(L)+sqrt(one-sq(L)))/A,
|
||||
atan(L)<<indep, (L*atan(L)-log(one+sq(L))/two)/A,
|
||||
cos(L)<<indep, sin(L)/A,
|
||||
inv(cos(L)*sin(L))<<indep, log(tan(L))/A,
|
||||
cosh(L)<<indep, sinh(L)/A,
|
||||
inv(cosh(L)*sinh(L))<<indep, log(tan(L))/A,
|
||||
inv(sinh(L)*cosh(L))<<indep, log(tan(L))/A,
|
||||
inv(cosh(L)^two)<<indep, tanh(L)/A,
|
||||
exp(L)<<indep, exp(L)/A,
|
||||
expm1(L)<<indep, exp(L)/A,
|
||||
log(L)<<indep, (L*log(L)-L)/A,
|
||||
log10(L)<<indep, (L*log10(L)-L/log(ten))/A,
|
||||
log2(L)<<indep, (L*log2(L)-L/log(two))/A,
|
||||
sign(L)<<indep, abs(L)/A,
|
||||
sin(L)<<indep, -cos(L)/A,
|
||||
inv(sin(L)*cos(L))<<indep, log(tan(L))/A,
|
||||
inv(sin(L)*tan(L))<<indep, -inv(sin(L))/A,
|
||||
inv(sin(L)^two)<<indep, -inv(tan(L))/A,
|
||||
(tan(L)^two)<<indep, (tan(L)-L)/A,
|
||||
tan(L)<<indep, -log(cos(L))/A,
|
||||
(tan(L)/cos(L))<<indep, inv(cos(L))/A,
|
||||
inv(tan(L))<<indep, log(sin(L))/A,
|
||||
inv(tan(L)*sin(L))<<indep, -inv(sin(L))/A,
|
||||
tanh(L)<<indep, log(cosh(L))/A,
|
||||
(tanh(L)/cosh(L))<<indep, inv(cosh(L))/A,
|
||||
inv(tanh(L))<<indep, log(sinh(L))/A,
|
||||
inv(tanh(L)*sinh(L))<<indep, -inv(sinh(L))/A,
|
||||
(L^zero)<<indep, L/A,
|
||||
inv(L)<<indep, log(L)/A,
|
||||
inv(one-(L^two))<<indep, atanh(L)/A,
|
||||
inv(one+(L^two))<<indep, atan(L)/A,
|
||||
inv(((L^two)-one)^(one/two))<<indep, acosh(L)/A,
|
||||
inv((one-(L^two))^(one/two))<<indep, asin(L)/A,
|
||||
inv((one+(L^two))^(one/two))<<indep, asinh(L)/A,
|
||||
inv(((L^two)+one)^(one/two))<<indep, asinh(L)/A
|
||||
);
|
||||
|
||||
if (+result == +eq)
|
||||
{
|
||||
rt.unknown_primitive_error();
|
||||
return nullptr;
|
||||
}
|
||||
if (result && Settings.AutoSimplify())
|
||||
result = result->simplify();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
COMMAND_BODY(Primitive)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Compute the primitive of an expression
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
return expression::variable_command(&expression::primitive);
|
||||
}
|
||||
|
||||
|
||||
PARSE_BODY(Primitive)
|
||||
// ----------------------------------------------------------------------------
|
||||
// The syntax for primitive is something like ∫X(sin X)
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
utf8 source = p.source;
|
||||
size_t max = p.length;
|
||||
size_t parsed = 0;
|
||||
|
||||
// First character must be a constant marker
|
||||
unicode cp = utf8_codepoint(source);
|
||||
if (cp != L'∫')
|
||||
return SKIP;
|
||||
parsed = utf8_next(source, parsed, max);
|
||||
|
||||
// If it's not in the form ∫X, then just return the Integrate object
|
||||
if (!p.precedence || parsed >= max ||
|
||||
!is_valid_as_name_initial(utf8_codepoint(source + parsed)))
|
||||
{
|
||||
p.length = parsed;
|
||||
p.out = object::static_object(ID_Integrate);
|
||||
return p.out ? OK : ERROR;
|
||||
}
|
||||
|
||||
// Parse the name
|
||||
parser namep(p, source + parsed, LOWEST);
|
||||
result nres = symbol::do_parse(namep);
|
||||
if (nres != OK)
|
||||
return nres;
|
||||
algebraic_g name = symbol_p(+namep.out);
|
||||
ASSERT(name->type() == ID_symbol);
|
||||
|
||||
// Parse the expression
|
||||
parser exprp(p, source + parsed + namep.length, LOWEST);
|
||||
result eres = expression::list_parse(ID_expression, exprp, '(', ')');
|
||||
if (eres != OK)
|
||||
return eres;
|
||||
algebraic_g expr = expression_p(+exprp.out);
|
||||
|
||||
expression_g res = expression::make(ID_Primitive, expr, name);
|
||||
p.out = +res;
|
||||
p.length = parsed + namep.length + exprp.length;
|
||||
return p.out ? OK : ERROR;
|
||||
}
|
||||
|
||||
|
||||
INSERT_BODY(Primitive)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Insert the primitive symbol
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
int key = ui.evaluating;
|
||||
return ui.insert_softkey(key, "", "", false);
|
||||
}
|
||||
|
|
|
@ -243,7 +243,10 @@ struct expression : program
|
|||
expression_p simplify() const;
|
||||
expression_p as_difference_for_solve() const; // Transform A=B into A-B
|
||||
bool split_equation(expression_g &left, expression_g &right) const;
|
||||
bool split(id ty, expression_g &left, expression_g &right) const;
|
||||
object_p outermost_operator() const;
|
||||
bool is_linear(symbol_r sym, algebraic_g &a, algebraic_g &b) const;
|
||||
bool depends_on(symbol_r sym) const;
|
||||
size_t render(renderer &r, bool quoted = false) const
|
||||
{
|
||||
return render(this, r, quoted);
|
||||
|
@ -255,6 +258,7 @@ struct expression : program
|
|||
algebraic_g &exponent);
|
||||
expression_p isolate(symbol_r sym) const;
|
||||
expression_p derivative(symbol_r sym) const;
|
||||
expression_p primitive(symbol_r sym) const;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
|
@ -511,9 +515,19 @@ struct eq
|
|||
hb(object::ID_Derivative)>(); }
|
||||
template<byte ...y>
|
||||
eq<args..., y..., lb(object::ID_Derivative), hb(object::ID_Derivative)>
|
||||
operator|(eq<y...>) { return eq<args..., y...,
|
||||
lb(object::ID_Derivative),
|
||||
hb(object::ID_Derivative)>(); }
|
||||
operator>>(eq<y...>) { return eq<args..., y...,
|
||||
lb(object::ID_Derivative),
|
||||
hb(object::ID_Derivative)>(); }
|
||||
template<byte ...y>
|
||||
eq<args..., y..., lb(object::ID_Primitive), hb(object::ID_Primitive)>
|
||||
prim(eq<y...>) { return eq<args..., y...,
|
||||
lb(object::ID_Primitive),
|
||||
hb(object::ID_Primitive)>(); }
|
||||
template<byte ...y>
|
||||
eq<args..., y..., lb(object::ID_Primitive), hb(object::ID_Primitive)>
|
||||
operator<<(eq<y...>) { return eq<args..., y...,
|
||||
lb(object::ID_Primitive),
|
||||
hb(object::ID_Primitive)>(); }
|
||||
|
||||
template<byte ...y>
|
||||
eq<leb(object::ID_funcall),
|
||||
|
@ -654,6 +668,10 @@ COMMAND_DECLARE_SPECIAL(Derivative, algebraic, 2,
|
|||
PREC_DECL(SYMBOL);
|
||||
INSERT_DECL(Derivative);
|
||||
PARSE_DECL(Derivative););
|
||||
COMMAND_DECLARE_SPECIAL(Primitive, algebraic, 2,
|
||||
PREC_DECL(MULTIPLICATIVE);
|
||||
INSERT_DECL(Primitive);
|
||||
PARSE_DECL(Primitive););
|
||||
COMMAND_DECLARE_SPECIAL(Where, arithmetic, 2, PREC_DECL(WHERE); );
|
||||
NFUNCTION(Subst, 2, static bool can_be_symbolic(uint) { return true; } );
|
||||
|
||||
|
|
|
@ -508,11 +508,12 @@ CMD(Collect)
|
|||
CMD(FoldConstants)
|
||||
CMD(ReorderTerms)
|
||||
CMD(Simplify)
|
||||
NAMED(ToPolynomial, "→Polynomial") ALIAS(ToPolynomial, "→Poly")
|
||||
NAMED(FromPolynomial, "Polynomial→") ALIAS(FromPolynomial, "Poly→")
|
||||
NAMED(ToPolynomial, "→Polynomial") ALIAS(ToPolynomial, "→Poly")
|
||||
NAMED(FromPolynomial, "Polynomial→") ALIAS(FromPolynomial, "Poly→")
|
||||
CMD(Apply)
|
||||
CMD(Isolate) ALIAS(Isolate, "Isol")
|
||||
CMD(Isolate) ALIAS(Isolate, "Isol")
|
||||
OP(Derivative, "∂")
|
||||
OP(Primitive, "∫") ALIAS(Primitive, "Risch")
|
||||
|
||||
// Complex numbers
|
||||
NAMED(RealToRectangular, "ℝ→ℂ")
|
||||
|
|
14
src/menu.cc
14
src/menu.cc
|
@ -812,7 +812,7 @@ MENU(AlgebraMenu,
|
|||
"|", ID_Where,
|
||||
|
||||
"∂", ID_Derivative,
|
||||
"∫", ID_Integrate,
|
||||
"∫", ID_Primitive,
|
||||
"∑", ID_Sum,
|
||||
"∏", ID_Product,
|
||||
"∆", ID_Unimplemented,
|
||||
|
@ -829,8 +829,8 @@ MENU(ArithmeticMenu,
|
|||
// ----------------------------------------------------------------------------
|
||||
// Arithmetic menu
|
||||
// ----------------------------------------------------------------------------
|
||||
"∂", ID_Unimplemented,
|
||||
"∫", ID_Integrate,
|
||||
"∂", ID_Derivative,
|
||||
"∫", ID_Primitive,
|
||||
"∑", ID_Sum,
|
||||
"∏", ID_Product,
|
||||
"∆", ID_Unimplemented,
|
||||
|
@ -838,7 +838,7 @@ MENU(ArithmeticMenu,
|
|||
|
||||
"Show", ID_Unimplemented,
|
||||
"Quote", ID_Unimplemented,
|
||||
"|", ID_Unimplemented,
|
||||
"|", ID_Where,
|
||||
"Rules", ID_Unimplemented,
|
||||
"Symb", ID_SymbolicMenu);
|
||||
|
||||
|
@ -1192,10 +1192,10 @@ MENU(IntegrationMenu,
|
|||
// ----------------------------------------------------------------------------
|
||||
// Symbolic and numerical integration
|
||||
// ----------------------------------------------------------------------------
|
||||
"∫", ID_Integrate,
|
||||
"∫", ID_Primitive,
|
||||
"Num ∫", ID_Integrate,
|
||||
"Symb ∫", ID_Unimplemented,
|
||||
"Prim", ID_Unimplemented,
|
||||
"Symb ∫", ID_Primitive,
|
||||
"Prim", ID_Primitive,
|
||||
"Eq", ID_Equation,
|
||||
"Indep", ID_Unimplemented,
|
||||
|
||||
|
|
|
@ -303,6 +303,9 @@ retry:
|
|||
case L'∂':
|
||||
r = Derivative::do_parse(p);
|
||||
break;
|
||||
case L'∫':
|
||||
r = Primitive::do_parse(p);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Symbols and commands
|
||||
|
|
|
@ -1049,7 +1049,7 @@ polynomial::iterator polynomial::ranking(size_t *var) const
|
|||
|
||||
polynomial::iterator polynomial::ranking(size_t var) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Locate the highest-ranking term for given vairable in the polynomial
|
||||
// Locate the highest-ranking term for given variable in the polynomial
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size_t vars = variables();
|
||||
|
|
Loading…
Reference in a new issue