g_math 0.7.0

Deterministic fixed-point arithmetic, every format a type: correctly rounded transcendentals, decimal and symbolic domains, linear algebra, Lie groups, manifolds, ODE solvers, certified intervals. Pure Rust, zero-float
Documentation
# Canonical routing layer (FASC)

The user-friendly entry point: `gmath("…")` builds a lazy expression,
`evaluate::<Q64_64>(…)` runs it in a format through a domain-routing stack
evaluator and rounds the result once.

## What it is

The canonical layer (`g_math::canonical`) is where a value is sent to the numeric
domain that holds it best, and where a chain of operations stays at the wide
compute tier until the final result is materialized. It trades a small amount of
dispatch overhead for two things the direct [imperative layer](README_IMPERATIVE.md)
does not do on its own: **cross-domain routing** and **chain persistence**. Reach
for it when domains are mixed (decimal money + integers + symbolic fractions),
when the input is user- or config-supplied, or when you are composing
transcendentals and want precision preserved across the whole chain.

## Usage

```rust
use g_math::canonical::{gmath, evaluate};
use g_math::fixed_point::Q64_64;

// Each literal is classified and routed: "0.1" → decimal, "3" → binary, "1/3" → symbolic.
// The format is named once; the tree's type follows it.
let expr = (gmath("100") + gmath("50")) / gmath("3");
let value = evaluate::<Q64_64>(&expr).unwrap();
println!("{value}");

// A chain of transcendentals is rounded once from its exact value.
let y = evaluate::<Q64_64>(&gmath("1.5").exp().sin()).unwrap();

// Runtime strings return a Result instead of panicking.
use g_math::canonical::gmath_parse;
let parsed = gmath_parse::<Q64_64>("3.14159265358979323846").unwrap();

// Feed a result back into a new expression without reparsing.
use g_math::canonical::LazyExpr;
let year0 = evaluate::<Q64_64>(&gmath("1000")).unwrap();
let year1 = evaluate(&(LazyExpr::from(year0) * gmath("1.05"))).unwrap();
```

## How it works

**Four numeric domains.** Values route to the representation that is exact for
them: binary fixed-point (integers, powers of two), decimal fixed-point (decimal
literals like `0.1`), balanced ternary (powers of three), and
symbolic rational (everything else, as an exact numerator/denominator pair). See
[the ternary guide](README_TERNARY.md) and [the precision guide](README_PRECISION.md)
for per-domain detail.

**FASC: Fixed Allocation Stack Computation.** `gmath()` builds a `LazyExpr` tree
(one small heap node per operation); `evaluate()` walks it on a thread-local
stack evaluator with fixed-size workspaces (one per format the thread uses), so
evaluation itself allocates nothing on the heap, including the 1000-bit sure
path, whose numbers live on the stack.

**One rounding per tree.** Every transcendental and every accumulating
operation computes one tier *wider* than the storage format; between chained
operations the intermediate stays at the wide tier (`sin(exp(x))` never narrows
mid-chain). The result is then rounded once from the exact value of the whole
tree: when the wide-tier result lies too close to a rounding boundary for its
tracked error bound to decide, the tree is evaluated again at 128 to 4160 bits
until it is decided (0.7.0; 0.6.7 rounded the wide-tier result, which
misrounded 10 to 30 of 300 random chains per format).

**UGOD: tiered graceful overflow.** Arithmetic is attempted at the current tier;
on overflow it promotes to a wider tier and retries. The top of the ladder is the
symbolic rational domain, so overflow degrades into a wider or exact
representation instead of wrapping.

**Fractal-topology router + shadow system.** Cross-domain arithmetic (e.g. decimal
+ binary) is dispatched by classifying each operand through shadow denominator
factoring (strip factors of 2 → binary-exact, 2 and 5 → decimal-exact, 3 →
ternary-exact) and coercing both sides to the optimal shared domain via a small
const lookup table. A `CompactShadow` (0–32 bytes, stack-only) can ride alongside
an approximated value carrying its exact rational identity or a constant reference
(π, e, √2, φ, ln 2, ln 10, γ); the router reads shadows to classify domain
exactness without reparsing. Only when no shared domain exists does arithmetic
fall back to exact rational. Ternary is a routing destination for
cross-domain **add/sub** of 3-adic values (denominator 3ᵏ), where routed
arithmetic is exact by construction; mul/div deliberately keep their
previous routes because products can leave the tier's exactness range,
and the router must never trade correctness for speed (the design
reasoning lives in `docs/design/TERNARY_ROUTING_COLUMN.md`). If a routed
value cannot fit ternary storage in a narrow format, dispatch silently
falls back to the previous route.

**Mode routing.** `set_gmath_mode("compute:output")` forces the compute and output
domains independently (`auto`, `binary`, `decimal`, `symbolic`, `ternary`);
`reset_gmath_mode()` restores automatic routing.

## Public API

See **[PUBLIC_API.md → Canonical](../PUBLIC_API.md#canonical-g_mathcanonical)** for
the full symbol list, and [docs.rs](https://docs.rs/g_math) for live signatures.
`LazyExpr` supports `+`, `-`, `*`, `/`, unary `-`, and the 17
[transcendental methods](README_TRANSCENDENTALS.md). `LazyMatrixExpr` is the
matrix analog of chain persistence; `DomainMatrix` holds per-element
domain-tagged values for mixed-domain matrices.

## Behaviour & limits

- **Correctness is path-independent**: for compound results (same engines,
  one shared downscale) AND for direct storage-tier arithmetic: since the
  0.5.0 rounding unification every domain uses one rule on every path
  (binary nearest-ties-+∞, decimal exact-then-banker's, ternary nearest),
  gated permanently by `tests/rounding_unification.rs` with constructed
  exact-tie inputs. The pre-unification divergence (~half of inexact
  products, 1 ULP) is preserved as history in
  [the rounding census]design/ROUNDING_CENSUS.md.
- Routing adds dispatch overhead per operation relative to the imperative path;
  for known-domain hot loops use the [imperative layer]README_IMPERATIVE.md.
- Literals may be decimals (`"0.1"`), integers, fractions (`"1/3"`), repeating
  decimals (`"0.333..."`), hex/binary/ternary (`"0x1F"` is 31, `"0b101"` is 5,
  `"0t10"`), or named constants (`"pi"`, `"e"`, `"sqrt2"`, `"phi"`). A literal
  beyond the format's storage is kept as an exact rational or fails with
  `TierOverflow`; it never wraps.

The routing and determinism contract is in **[CONTRACT.md](../CONTRACT.md)**.

## Disclaimer

This software is provided **"as is"**, without warranty of any kind, express or
implied. Use of this software is entirely at your own risk. In no event shall the
author or contributors be held liable for any damages arising from the use or
inability to use this software.

---

Built by **Niels Erik Toren** · [support & donations](../README.md#author--support).