From 9a47650ca9c31677932c9acd902a95d378a34ac6 Mon Sep 17 00:00:00 2001 From: Christophe de Dinechin Date: Sat, 29 Jul 2023 20:39:51 +0200 Subject: [PATCH] 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 --- fonts/EditorFont.cc | 2 +- fonts/HelpFont.cc | 2 +- fonts/StackFont.cc | 2 +- src/equation.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/equation.h | 23 ++++++++++++++++++++ src/functions.cc | 48 ++++++++++++++++++++++++++++++++++++++++++ src/functions.h | 4 ++++ src/ids.tbl | 5 ++++- src/menu.cc | 10 +++++---- 9 files changed, 139 insertions(+), 8 deletions(-) diff --git a/fonts/EditorFont.cc b/fonts/EditorFont.cc index 14499332..f194cdea 100644 --- a/fonts/EditorFont.cc +++ b/fonts/EditorFont.cc @@ -7,7 +7,7 @@ extern const unsigned char EditorFont_sparse_font_data[]; 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, 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, diff --git a/fonts/HelpFont.cc b/fonts/HelpFont.cc index 726a4c1c..dbeeaa7e 100644 --- a/fonts/HelpFont.cc +++ b/fonts/HelpFont.cc @@ -7,7 +7,7 @@ extern const unsigned char HelpFont_sparse_font_data[]; 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, 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, diff --git a/fonts/StackFont.cc b/fonts/StackFont.cc index 03ec536e..2782a07d 100644 --- a/fonts/StackFont.cc +++ b/fonts/StackFont.cc @@ -7,7 +7,7 @@ extern const unsigned char StackFont_sparse_font_data[]; 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, 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, diff --git a/src/equation.cc b/src/equation.cc index 9bcabb6b..d3c2cb8f 100644 --- a/src/equation.cc +++ b/src/equation.cc @@ -701,3 +701,54 @@ COMMAND_BODY(Rewrite) 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)); +} diff --git a/src/equation.h b/src/equation.h index 3b5b8c70..9006e4b2 100644 --- a/src/equation.h +++ b/src/equation.h @@ -88,11 +88,34 @@ struct equation : program } 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) { return eq->rewrite(from, to); } + template + equation_p rewrite(from_eq from, to_eq to) const + { + return rewrite(from.as_equation(), to.as_equation()); + } + + template + 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: static symbol_g render(uint depth, int &precedence, bool edit); static symbol_g parentheses(symbol_g what); diff --git a/src/functions.cc b/src/functions.cc index 1de330b7..45ceccc6 100644 --- a/src/functions.cc +++ b/src/functions.cc @@ -514,3 +514,51 @@ INSERT_BODY(fact) // We need to pass "x!' because ui.edit() strips the x return ui.edit(utf8("x!"), 2, ui.POSTFIX); } + + +FUNCTION_BODY(Expand) +// ---------------------------------------------------------------------------- +// Expand equations +// ---------------------------------------------------------------------------- +{ + if (!x.Safe()) + return nullptr; + if (equation_p eq = x->as()) + 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()) + 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()) + return algebraic_p(eq->simplify()); + if (x->is_algebraic()) + return x; + rt.type_error(); + return nullptr; +} diff --git a/src/functions.h b/src/functions.h index e4e6ab3f..42224cc5 100644 --- a/src/functions.h +++ b/src/functions.h @@ -194,4 +194,8 @@ FUNCTION(im); FUNCTION(arg); FUNCTION(conj); +FUNCTION(Expand); +FUNCTION(Collect); +FUNCTION(Simplify); + #endif // FUNCTIONS_H diff --git a/src/ids.tbl b/src/ids.tbl index 41185083..0d33e270 100644 --- a/src/ids.tbl +++ b/src/ids.tbl @@ -236,7 +236,7 @@ CMD(re) CMD(im) CMD(arg) CMD(conj) -NAMED(det, "Determinant") +NAMED(det, "Determinant") // Last algebraic @@ -265,6 +265,9 @@ NAMED(ToList, "→List") CMD(Get) CMD(Rewrite) +CMD(Expand) +CMD(Collect) +CMD(Simplify) NAMED(RealToComplex, "ℝ→ℂ") ALIAS(RealToComplex, "R→C") diff --git a/src/menu.cc b/src/menu.cc index a95ea8a2..5701eaba 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -536,12 +536,13 @@ MENU(SymbolicMenu, // ---------------------------------------------------------------------------- // Symbolic operations // ---------------------------------------------------------------------------- - ID_Rewrite, - "Collect", ID_Unimplemented, - "Expand", ID_Unimplemented, + ID_Rewrite, + ID_Collect, + ID_Expand, + ID_Simplify, + "Isolate", ID_Unimplemented, "Apply", ID_Unimplemented, - "Taylor", ID_Unimplemented, "Ex/Co", ID_Unimplemented, "→Q", ID_Unimplemented, @@ -554,6 +555,7 @@ MENU(SymbolicMenu, "∑", ID_Unimplemented, "∏", ID_Unimplemented, "∆", ID_Unimplemented, + "Taylor", ID_Unimplemented, "Show", ID_Unimplemented, "Quote", ID_Unimplemented,