Add expand, collect and simplify functions

Add the basic rules for `expand`, `collect` and `simplify`.

And to be honest, I am quite proud of the fact that the core code looks
_exactly_ like this (this is actual code):

```c++
    return rewrite((x+y)*z,     x*z+y*z,
                   x*(y+z),     x*y+x*z,
                   (x-y)*z,     x*z-y*z,
                   x*(y-z),     x*y-x*z);
```

A lot of template metaprogramming to turn that into transparent
building of _compile-time constants_ representing the RPL objects for
the equations, and passing that to the `rewrite` function that was
implemented earlier.

What is really neet about this is that the generated equations:

* Are automatically _shared_ if used multiple times, because they are
  represented by the same template. So the equations used in `collect`
  and those used in `expand` (which are the same in reverse) share the
  same memory.

* Automatically go into QSPI, not consuming precious flash space,
  because they are represented as initialized data arrays, and I
  requested that all C++ .rodata go in QSPI.

I intentionally did not support the historical names
(`expan` and `colct`). Our level of compatibility with the HP48 is
never going to be that good anyway.

The code as is works with `clang`, but runs into unsatisfied symbols
with g++ 10.3 due to what I believe is a g++ bug (that's what living
on the edge of template metaprogramming gives you):

```
/opt/homebrew/Cellar/arm-none-eabi-gcc/10.3-2021.07/gcc/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: build/release/equation.o: in function `equation::collect() const':
equation.cc:(.text._ZNK8equation7collectEv+0xd0): undefined reference to `eq<(unsigned char)10, (unsigned char)1, (unsigned char)120, (unsigned char)10, (unsigned char)1, (unsigned char)122, (unsigned char)57, (unsigned char)10, (unsigned char)1, (unsigned char)121, (unsigned char)10, (unsigned char)1, (unsigned char)122, (unsigned char)57, (unsigned char)55>::object_data'
```

Fixes: #231

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2023-07-29 20:39:51 +02:00
parent 7451edfccc
commit 9a47650ca9
9 changed files with 139 additions and 8 deletions

View file

@ -7,7 +7,7 @@ extern const unsigned char EditorFont_sparse_font_data[];
const unsigned char EditorFont_sparse_font_data[74100] FONT_QSPI = const unsigned char EditorFont_sparse_font_data[74100] FONT_QSPI =
{ {
0x81, 0x02, 0xEF, 0xC2, 0x04, 0x36, 0x00, 0x01, 0x00, 0x2C, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x84, 0x02, 0xEF, 0xC2, 0x04, 0x36, 0x00, 0x01, 0x00, 0x2C, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01,
0x00, 0x2C, 0x01, 0x01, 0x08, 0x00, 0x20, 0x5F, 0x00, 0x2C, 0x01, 0x01, 0x08, 0x00, 0x02, 0x0B, 0x00, 0x2C, 0x01, 0x01, 0x08, 0x00, 0x20, 0x5F, 0x00, 0x2C, 0x01, 0x01, 0x08, 0x00, 0x02, 0x0B,
0x05, 0x21, 0x09, 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0xF7, 0x05, 0x21, 0x09, 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0xF7,
0x1E, 0x00, 0x00, 0xB8, 0xFF, 0xFF, 0xFF, 0x0E, 0x03, 0x0B, 0x0B, 0x0F, 0x11, 0x8F, 0x7F, 0xFC, 0x1E, 0x00, 0x00, 0xB8, 0xFF, 0xFF, 0xFF, 0x0E, 0x03, 0x0B, 0x0B, 0x0F, 0x11, 0x8F, 0x7F, 0xFC,

View file

@ -7,7 +7,7 @@ extern const unsigned char HelpFont_sparse_font_data[];
const unsigned char HelpFont_sparse_font_data[15113] FONT_QSPI = const unsigned char HelpFont_sparse_font_data[15113] FONT_QSPI =
{ {
0x81, 0x02, 0x85, 0x76, 0x14, 0x00, 0x01, 0x00, 0x11, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x84, 0x02, 0x85, 0x76, 0x14, 0x00, 0x01, 0x00, 0x11, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00,
0x11, 0x01, 0x01, 0x03, 0x00, 0x20, 0x5F, 0x00, 0x11, 0x01, 0x01, 0x03, 0x00, 0x01, 0x04, 0x02, 0x11, 0x01, 0x01, 0x03, 0x00, 0x20, 0x5F, 0x00, 0x11, 0x01, 0x01, 0x03, 0x00, 0x01, 0x04, 0x02,
0x0D, 0x04, 0xFF, 0xFF, 0xF3, 0x03, 0x01, 0x04, 0x04, 0x05, 0x06, 0xFF, 0xFF, 0x0F, 0x01, 0x04, 0x0D, 0x04, 0xFF, 0xFF, 0xF3, 0x03, 0x01, 0x04, 0x04, 0x05, 0x06, 0xFF, 0xFF, 0x0F, 0x01, 0x04,
0x08, 0x0D, 0x0A, 0x12, 0x12, 0x14, 0x7F, 0x7F, 0x24, 0x24, 0x24, 0xFE, 0xFE, 0x28, 0x48, 0x48, 0x08, 0x0D, 0x0A, 0x12, 0x12, 0x14, 0x7F, 0x7F, 0x24, 0x24, 0x24, 0xFE, 0xFE, 0x28, 0x48, 0x48,

View file

@ -7,7 +7,7 @@ extern const unsigned char StackFont_sparse_font_data[];
const unsigned char StackFont_sparse_font_data[36629] FONT_QSPI = const unsigned char StackFont_sparse_font_data[36629] FONT_QSPI =
{ {
0x81, 0x02, 0x90, 0x9E, 0x02, 0x24, 0x00, 0x01, 0x00, 0x1C, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x84, 0x02, 0x90, 0x9E, 0x02, 0x24, 0x00, 0x01, 0x00, 0x1C, 0x01, 0x01, 0x00, 0x00, 0x0D, 0x01,
0x00, 0x1C, 0x01, 0x01, 0x06, 0x00, 0x20, 0x5F, 0x00, 0x1C, 0x01, 0x01, 0x06, 0x00, 0x02, 0x05, 0x00, 0x1C, 0x01, 0x01, 0x06, 0x00, 0x20, 0x5F, 0x00, 0x1C, 0x01, 0x01, 0x06, 0x00, 0x02, 0x05,
0x03, 0x17, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xC0, 0xFF, 0x1F, 0x02, 0x05, 0x08, 0x0A, 0x03, 0x17, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xC0, 0xFF, 0x1F, 0x02, 0x05, 0x08, 0x0A,
0x0C, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x01, 0x05, 0x0E, 0x17, 0x10, 0x0C, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0x01, 0x05, 0x0E, 0x17, 0x10,

View file

@ -701,3 +701,54 @@ COMMAND_BODY(Rewrite)
return OK; return OK;
} }
static eq_symbol<'x'> x;
static eq_symbol<'y'> y;
static eq_symbol<'z'> z;
static eq_integer<0> zero;
static eq_integer<1> one;
static eq_integer<2> two;
equation_p equation::expand() const
// ----------------------------------------------------------------------------
// Run various rewrites to expand equation
// ----------------------------------------------------------------------------
{
return rewrite((x+y)*z, x*z+y*z,
x*(y+z), x*y+x*z,
(x-y)*z, x*z-y*z,
x*(y-z), x*y-x*z);
}
equation_p equation::collect() const
// ----------------------------------------------------------------------------
// Run various rewrites to collect terms / factor equation
// ----------------------------------------------------------------------------
{
return rewrite(x*z+y*z, (x+y)*z,
x*y+x*z, x*(y+z),
x*z-y*z, (x-y)*z,
x*y-x*z, x*(y-z));
}
equation_p equation::simplify() const
// ----------------------------------------------------------------------------
// Run various rewrites to simplify equation
// ----------------------------------------------------------------------------
{
return rewrite(x + zero, x,
zero + x, x,
x - zero, x,
zero - x, x,
x * zero, zero,
zero * x, zero,
x * one, x,
one * x, x,
x / one, x,
x / x, one,
one / x, inv(x),
x * x * x, cubed(x),
x * x, sq(x));
}

View file

@ -88,11 +88,34 @@ struct equation : program
} }
equation_p rewrite(equation_r from, equation_r to) const; equation_p rewrite(equation_r from, equation_r to) const;
equation_p rewrite(equation_p from, equation_p to) const
{
return rewrite(equation_g(from), equation_g(to));
}
static equation_p rewrite(equation_r eq, equation_r from, equation_r to) static equation_p rewrite(equation_r eq, equation_r from, equation_r to)
{ {
return eq->rewrite(from, to); return eq->rewrite(from, to);
} }
template<typename from_eq, typename to_eq>
equation_p rewrite(from_eq from, to_eq to) const
{
return rewrite(from.as_equation(), to.as_equation());
}
template <typename from_eq, typename to_eq, typename ...args>
equation_p rewrite(from_eq from, to_eq to, args... rest) const
{
if (equation_p eq = rewrite(from, to))
return eq->rewrite(rest...);
return nullptr;
}
equation_p expand() const;
equation_p collect() const;
equation_p simplify() const;
protected: protected:
static symbol_g render(uint depth, int &precedence, bool edit); static symbol_g render(uint depth, int &precedence, bool edit);
static symbol_g parentheses(symbol_g what); static symbol_g parentheses(symbol_g what);

View file

@ -514,3 +514,51 @@ INSERT_BODY(fact)
// We need to pass "x!' because ui.edit() strips the x // We need to pass "x!' because ui.edit() strips the x
return ui.edit(utf8("x!"), 2, ui.POSTFIX); return ui.edit(utf8("x!"), 2, ui.POSTFIX);
} }
FUNCTION_BODY(Expand)
// ----------------------------------------------------------------------------
// Expand equations
// ----------------------------------------------------------------------------
{
if (!x.Safe())
return nullptr;
if (equation_p eq = x->as<equation>())
return algebraic_p(eq->expand());
if (x->is_algebraic())
return x;
rt.type_error();
return nullptr;
}
FUNCTION_BODY(Collect)
// ----------------------------------------------------------------------------
// Collect equations
// ----------------------------------------------------------------------------
{
if (!x.Safe())
return nullptr;
if (equation_p eq = x->as<equation>())
return algebraic_p(eq->collect());
if (x->is_algebraic())
return x;
rt.type_error();
return nullptr;
}
FUNCTION_BODY(Simplify)
// ----------------------------------------------------------------------------
// Simplify equations
// ----------------------------------------------------------------------------
{
if (!x.Safe())
return nullptr;
if (equation_p eq = x->as<equation>())
return algebraic_p(eq->simplify());
if (x->is_algebraic())
return x;
rt.type_error();
return nullptr;
}

View file

@ -194,4 +194,8 @@ FUNCTION(im);
FUNCTION(arg); FUNCTION(arg);
FUNCTION(conj); FUNCTION(conj);
FUNCTION(Expand);
FUNCTION(Collect);
FUNCTION(Simplify);
#endif // FUNCTIONS_H #endif // FUNCTIONS_H

View file

@ -236,7 +236,7 @@ CMD(re)
CMD(im) CMD(im)
CMD(arg) CMD(arg)
CMD(conj) CMD(conj)
NAMED(det, "Determinant") NAMED(det, "Determinant") // Last algebraic
@ -265,6 +265,9 @@ NAMED(ToList, "→List")
CMD(Get) CMD(Get)
CMD(Rewrite) CMD(Rewrite)
CMD(Expand)
CMD(Collect)
CMD(Simplify)
NAMED(RealToComplex, "ℝ→ℂ") NAMED(RealToComplex, "ℝ→ℂ")
ALIAS(RealToComplex, "R→C") ALIAS(RealToComplex, "R→C")

View file

@ -537,11 +537,12 @@ MENU(SymbolicMenu,
// Symbolic operations // Symbolic operations
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ID_Rewrite, ID_Rewrite,
"Collect", ID_Unimplemented, ID_Collect,
"Expand", ID_Unimplemented, ID_Expand,
ID_Simplify,
"Isolate", ID_Unimplemented, "Isolate", ID_Unimplemented,
"Apply", ID_Unimplemented, "Apply", ID_Unimplemented,
"Taylor", ID_Unimplemented,
"Ex/Co", ID_Unimplemented, "Ex/Co", ID_Unimplemented,
"→Q", ID_Unimplemented, "→Q", ID_Unimplemented,
@ -554,6 +555,7 @@ MENU(SymbolicMenu,
"", ID_Unimplemented, "", ID_Unimplemented,
"", ID_Unimplemented, "", ID_Unimplemented,
"", ID_Unimplemented, "", ID_Unimplemented,
"Taylor", ID_Unimplemented,
"Show", ID_Unimplemented, "Show", ID_Unimplemented,
"Quote", ID_Unimplemented, "Quote", ID_Unimplemented,