Status. v1.0.0 held plain numbers. v2.0.0 ships the general expression tree: a canonical polynomial-in-atoms engine (STL-free, fixed arena + compacting GC) that keeps fractions,
k√m, rational multiples of π, their sums/products/integer powers, nested radicals (√(1+√2)), conjugate division (1/(1+√2)→√2−1) and symbolic variables exact — with a decimal fallback for transcendental functions / overflow / arena limits. Results render in 2D (stacked fractions, radicals with a vinculum, exponents) with the level-1 decimal beneath.Remaining, portable: exact special values (
sin(π/6)), general simplification beyond the polynomial form, and tuning the on-device arena size on real hardware. The native-build route (real Poincaré) stays the escape hatch.
The stack holds expression terms, not just numbers — a bit like lambda calculus: operators build a term, and a reduction step normalizes it on demand rather than computing eagerly. Results are then presented like Poincaré: real 2D math layout (stacked fractions, radicals, raised exponents) with the exact form and its decimal approximation shown together, exactly the feel of the standard NumWorks app.
2 ENTER 8 √ × level 1: 2·√8 ── exact, unreduced term
reduce level 1: 4·√2 ── normalized
→Num level 1: 5.656854
An external app cannot call Poincaré (EADK sandbox). Two routes to the vision:
| Route A — native fork | Route B — self-contained engine | |
|---|---|---|
| Where | RPL mode inside Epsilon (the epsilon fork) |
still a portable .nwa |
| Math | reuse Poincaré Expression + simplification + Layout |
build a small term engine + 2D renderer ourselves |
| Fidelity | full (real Poincaré look & CAS) | good, bounded |
| Cost | fork maintenance, upstream sync, bigger build | more app code, but stays portable |
Recommendation: prototype Route B, Phase 1 first — it extends the
existing host-tested pure core and keeps the app a single .nwa. Keep Route A
as the escape hatch if you later want true Poincaré fidelity.
Replace the flat Value on the stack with an expression tree:
- Leaves:
Integer,Rational,Constant(π, e, i),Symbol(future). - Nodes: n-ary
Add, n-aryMul,Pow,Neg,Func(sin, ln, …). - N-ary
Add/Mulwith sorted, canonicalized operands so equal terms collapse (√2 + √2 → 2·√2).
The stack becomes a stack of Expr (kept small: arena/pool allocation, no heap
churn — important on the device).
Operators are constructors: × just builds Mul(a, b). A reduce() pass
normalizes to a canonical form:
- rational arithmetic (reuse v1's overflow-checked
Value), - constant folding, like-term collection,
√extraction of square factors, power rules (xᵃ·xᵇ → xᵃ⁺ᵇ), sign normalization.
Two outputs from any term: exact (the normalized tree) and approximate
(numeric eval to double). Reduction runs on ENTER / on demand — building a
term then reducing it mirrors build → β-reduce → normal form.
The biggest new piece in Route B. A layout tree of boxes with a measure (width, height, baseline) then draw pass over the EADK framebuffer:
HBox/VBox,FractionLayout(numerator over bar over denominator),RadicalLayout(√ with vinculum),SuperscriptLayout(raised exponent).
Reuses the two EADK font sizes; measurement drives right-alignment on the stack.
Each stack level renders its exact 2D layout; a secondary line (or a
per-level toggle) shows the decimal approximation, like the standard app's
exact/approx duality. →Num forces the approximate view.
- Term engine + reducer for rationals,
√, π, basic simplification — pure C++, unit-tested on host (extends the currentmake testharness). 1D text output first (4·√2). - 2D layout renderer: fractions, powers, radicals over the framebuffer.
- Exact / approximate dual presentation +
→Num. - (optional) evaluate a Route A native-fork port if fidelity or scope demands the real Poincaré engine.
- Binary size — v1 is already ~500 KB; a layout engine adds more. Budget it;
consider dropping
_printf_floatvia a hand-rolled number formatter. - Mini-CAS scope creep — explicitly not a general algebra system. Bound it
to: exact rationals,
√, π/e, like-term collection, power/product rules. - Device memory — pool-allocate terms; cap expression depth/size and report when a level exceeds it rather than failing silently.
The pure, host-tested core (value, stack, input_field, rpn) is the
foundation: Value becomes the numeric leaf of the term model, Stack becomes
a stack of Expr, and the make test harness extends to cover reduction rules.