# gMath Roadmap
Current version: **0.4.34**
This document tracks planned work and known gaps. Items are grouped by priority, not by timeline. Nothing here is a promise; this is a working list for a solo-maintained project.
---
## Delivered
### v0.3.0: Five profiles
| realtime | Q16.16 (i32) | Q32.32 (i64) | 4 | 4 | validated vs mpmath |
| compact | Q32.32 (i64) | Q64.64 (i128) | 8 | 9 | validated vs mpmath |
| embedded | Q64.64 (i128) | Q128.128 (I256) | 16 | 19 | validated vs mpmath |
| balanced | Q128.128 (I256) | Q256.256 (I512) | 32 | 38 | validated vs mpmath |
| scientific | Q256.256 (I512) | Q512.512 (I1024) | 64 | 77 | validated vs mpmath |
All profiles use true tier N+1 computation. 18 transcendentals, 4 domains (binary, decimal, ternary, symbolic), FASC zero-allocation stack computation, UGOD tiered overflow delegation, BinaryCompute chain persistence.
### v0.3.89: TQ1.9 standalone module
Dedicated inference module with AVX2 SIMD, rayon row-parallel dispatch, batch matvec. `TQ19Matrix`, `tq19_dot`, `trit_dot`, packed trit operations, `TRIT_DECODE_TABLE`. 38 tests.
### v0.3.90: Configurable FRAC_BITS, native transcendental dispatch, inference feature gate
- `GMATH_FRAC_BITS` env var for realtime profile (e.g., Q8.24 via `GMATH_FRAC_BITS=24`)
- Native transcendental dispatch: Q16.16/Q32.32 use hardware i128 instead of software I256
- Fixed `ln_q64_64_native` algorithm bug (additive -> multiplicative decomposition)
- `FixedPoint::sincos_wide(i64)` for wide-range RoPE position encoding
- TQ1.9 gated behind `inference` feature (replaces `parallel`)
- Decimal-to-binary rounding fix (round-to-nearest instead of truncation)
### v0.4.0: Decimal transcendentals, fractal router, direct engine calls, gmath! macro
**Decimal transcendentals (native engines):**
- 5 native engines: exp (4-stage table decomposition), ln (atanh), sqrt (Newton-Raphson), sin/cos (Cody-Waite + Machin pi), atan (half-angle)
- DecimalCompute StackValue variant + full FASC dispatch
- DecimalFixed imperative type: 18 transcendental methods wired to native engines (eliminated binary round-trip)
- 14 ULP validation tests + 24 FASC integration tests + ~9500 mpmath reference points
**Fractal topology router:**
- Shadow-based operand classifier: factors CompactShadow denominator to determine domain exactness
- Compile-time routing table (5.25 KB .rodata, 21 ops x 16 x 16 classes): O(1) lookup
- Cross-domain coercion in arithmetic: `gmath("0.1") + gmath("255")` routes to Decimal (was Symbolic fallback)
- Tree walker: `route_expression(&LazyExpr) -> OperandClass`, O(N) bottom-up
**Decimal 4-stage exp tables** (replaced in 0.6.4: the table entries lost
accuracy at large arguments and wrapped on realtime past e^22):
- `exp(x) = exp(k) * exp(d1/10) * exp(d2/100) * exp(d3/1000) * exp(r)`: 71 cached entries
- Cached-table decimal exp path; narrows the binary/decimal gap
**Direct binary engine calls (FASC bypass):**
- FixedPoint: exp/ln/sqrt/sin/cos/atan use `direct_unary` pattern: upscale -> engine -> downscale
- Avoids the FASC pipeline overhead per call (no LazyExpr, no TLS, no StackValue boxing)
**gmath!() compile-time macro:**
- `g_math_macros/` proc-macro crate: zero external deps, `--features macros`
- Pre-parses decimal/integer literals at compile time, emits direct StackValue construction
- Falls back to runtime `gmath()` for fractions, constants, hex
**FRAC_BITS root cause fix:**
- BinaryTier1 mul/div hardcoded `>> 16`: wrong when `GMATH_FRAC_BITS != 16`
- Fixed: uses `frac_config::FRAC_BITS` for q16_16 profile, hardcoded 16 for all others
**Matrix chain persistence, fused ops (also in v0.4.0):**
- `LazyMatrixExpr`: 14-variant enum, operator overloading, recursive evaluator at ComputeMatrix tier
- `DomainMatrix`: StackValue-tagged matrix, 4 domains + cross-domain routing
- Fused transcendental paths: `evaluate_sincos()`, identity short-circuits (`exp(ln(x))` -> x)
- Fused compute-tier ops: `sqrt_sum_sq`, `euclidean_distance`, `softmax`, `rms_norm_factor`, `silu`
**Tensor decompositions (for inference weight/KV-cache compression):**
- `truncated_svd(a, k)`: keep top-k singular values, O(mk + nk) memory vs O(m² + n²)
- `truncated_svd_auto(a, threshold)`: automatic rank selection via singular value threshold
- `tucker_decompose(t, ranks)`: HOSVD: mode-n unfolding → SVD per mode → core tensor
- `cp_decompose(t, rank, max_iter, tol)`: Alternating Least Squares for rank-R canonical polyadic
**Composed transcendental direct bypass:**
- All *infallible* FixedPoint transcendentals bypass FASC entirely (the fallible `try_*` variants still route through FASC: see 0.5.0)
- Composed functions (tan, sinh, asin, etc.) use direct compute-tier arithmetic
- `pow(x, y) = direct_exp(direct_ln(x) * y)`: zero FASC overhead
**Test count: 1375+ across all 5 profiles, 0 failures, 0 warnings.**
### v0.4.1: Fused `sinhcosh` hyperbolic pair
Independently-rounded `sinh(θ)` and `cosh(θ)` fail to cancel in expressions like `r = cosh(θ)·p + (sinh(θ)/θ)·v`, producing closure drift on hyperbolic-geometry round-trips at low-precision profiles. Fix: expose a fused pair sharing one `(exp(x), exp(-x))` evaluation so the rounding bias is correlated.
**Delivered:**
- `FixedPoint::sinhcosh` / `try_sinhcosh`: imperative binary, mirrors `sincos` / `try_sincos` shape
- `DecimalFixed::sinhcosh`: imperative decimal, native compute-tier path (no binary round-trip)
- `decimal_sinhcosh(ComputeStorage)` kernel in `decimal_fixed::transcendental::exp`
- `sinhcosh_at_compute_tier(ComputeStorage)` in `stack_evaluator::compute`
- `StackEvaluator::evaluate_sinhcosh`: internal, decimal/binary dispatch
- `canonical::evaluate_sinhcosh(&LazyExpr)`: FASC public surface, parity with `evaluate_sincos`
**Benefits:**
- 2 `exp` evaluations instead of 4 (separate `sinh` + `cosh` each call `exp` twice)
- **Correlated rounding**: sinh and cosh derive from the same `(ep, en)` compute-tier pair, so systematic bias cancels in expressions like `cosh(θ)·p + (sinh(θ)/θ)·v`. Per-function ULP is unchanged (still 1 ULP at final downscale), but the *joint* error is tighter.
**Validation:** mpmath-backed tests in `tests/sinhcosh_validation.rs` on realtime (Q16.16) + compact (Q32.32) profiles. Binary identity `cosh²−sinh² = 1` holds to ≤8 ULP at Q16.16 storage, ≤64 ULP at Q32.32 (both dominated by cosh² amplification of input-representation ULP).
**Deferred for future:** Dedicated mpmath-backed `sinhcosh` validation passes on embedded (Q64.64), balanced (Q128.128), and scientific (Q256.256) profiles. The underlying compute-tier kernels are profile-agnostic and already exercised by the existing per-profile `sinh` + `cosh` validation suites; the fused pair's correctness inherits from those. Run dedicated passes if consumer demand emerges for those profiles.
### v0.4.2: Internal adoption of `try_sinhcosh` in `HyperbolicSpace`
gMath now consumes its own fused primitive internally. `HyperbolicSpace::exp_map` and `HyperbolicSpace::parallel_transport` (in `src/fixed_point/imperative/manifold.rs`) previously called `try_cosh` and `try_sinh` as two independent transcendentals on the same `theta`. Both sites feed the outputs into expressions where correlated rounding matters:
- `exp_map`: `r = cosh(θ)·p + (sinh(θ)/θ)·v`
- `parallel_transport`: `correction = sinh(θ)·p + (cosh(θ)-1)·u`
Swapping to `try_sinhcosh` shares one `(exp(θ), exp(-θ))` pair at compute tier, so the two rounding errors cancel in the downstream sum instead of accumulating. This directly addresses the closure drift hyperbolic-geometry round-trips can show at low-precision profiles.
**API-compatible:** no surface change; both methods retain their `Result<FixedVector, OverflowDetected>` signatures. Downstream consumers get the tighter bound transparently via `cargo update`.
**Regression-tested:** `tests/l1c_l1d_l3a_validation.rs` (24 tests) and `tests/sinhcosh_validation.rs` (8 tests) pass cleanly on realtime and compact profiles.
### v0.4.24: Decimal arithmetic correctness + contract validation
Built a contract-validation harness (`tests/decimal_contract_validation.rs`)
grading gMath's decimal domain against mootable/decimal-scaled's independent
mpmath corpus (pinned), under gMath's own declared rounding rule. It surfaced (
and this release root-causes) three bug classes:
- **Composed decimal transcendentals** (tan, asin, acos, sinh, cosh, tanh,
asinh, acosh, atanh) composed at storage tier with raw i128 arithmetic,
overflowing for large operands and panicking. Rewritten to compose at the
compute tier with a single downscale; now correctly rounded.
- **Parser sign drop**: `gmath_parse` lost the sign of negative decimals with a
zero integer part (`-0.5` parsed as `+0.5`), via `integer_part < 0` being
false for `-0`. Fixed at the source.
- **Decimal mul/div overflow**: `divmod_d256_by_i128` overflowed its u128
remainder for divisors > 2^64, and `banker_round_decimal_i128` mis-rounded odd
divisors and exact scale-0 results. Both fixed; mul/div rewritten UGOD-shaped
(narrow i128 tier → widen to a 256-bit intermediate on overflow → saturate,
never panic), banker's rounding preserved.
Both user-reachable decimal paths (imperative `DecimalFixed` and canonical
`gmath()`/FASC) grade **0 LSB** across all 15 transcendentals, atan2, and
add/sub/mul/div over decimal-scaled's d18+d38 tiers and all scales. Audited
every tier×op across decimal/binary/ternary: the bug class was contained to the
imperative `DecimalFixed` path (the tiered arithmetic already widened correctly).
CI (`.github/workflows/decimal-precision.yml`) gates the harness plus
multi-profile lib tests on every push.
### v0.4.25: Trit-plane weight formats + fused attention hardening
`PlanarTQ19` (per-digit ternary planes, density-chosen encodings) and
`HybridTQ19` (12-bit packed low part + sparse high corrections): lossless
re-encodings of `TQ19Matrix` with bit-identical matvec at 25–29% fewer weight
bytes. `fused::softmax_mix` numerator/exp-sum accumulate with overflow
detection. CI: `fused-tq19-precision` workflow gates fused ops and TQ1.9
exactness across all five profiles plus a Q22.10 floor job.
### v0.4.26–v0.4.27: U1 fused kernels + try_* domain-error contract
`fused::{euclidean_distance_squared, dot, mobius_denominator_sq}` (U1 consumer
asks: squared-space scoring and Möbius-ratio kernels without the sqrt
round-trip). `try_ln`/`try_sqrt` again return `DomainError` for out-of-domain
inputs: the v0.4.0 direct-engine switch had bypassed the FASC domain checks
and misreported them as `TierOverflow`.
### v0.4.28: round_to_storage overflow-panic
The shared fused/linalg downscale silently wrapped results exceeding the
storage tier (a squared distance of 520,000 returned as −4288 on Q16.16). Now
panics like every other infallible downscale. Fused test suite made
multi-profile compliant (representable tolerances, narrow-profile-safe data).
### v0.4.29: Saturating exp downscale + ceiling guards
`downscale_q64_to_q32` wrapped oversized Q64.64 results (including the exp
overflow sentinel) via a plain cast: `fused::silu(x)` for x ≲ −30 returned the
input unsquashed, `tanh(25)` returned −1. Now saturates; every exp consumer
whose follow-up add could wrap on a saturated exp is guarded (silu → 0,
tanh → 1, cosh → loud overflow, imperative + FASC paths). Property test pins
exp monotonicity over the full storage range.
### v0.4.30: RowScaledTQ19 (TQ1.9-R)
Per-row quantization scales (`s_rel` in unsigned Q32.32 relative to the global
step): adapts the step to each row's max, ~20× matvec output error reduction
on BF16-trained MoE weight distributions at unchanged 2 bytes/weight. Gated to
q16_16/q32_32. Review hardening: loud range check on the scale multiply,
independent i128-oracle test.
### v0.4.31: Wide-output matvec_q2f family
`matvec_q2f`/`_par`/`_batch_par` on all four TQ1.9 forms plus `tq19_dot_q2f`:
the exact row accumulator at 2·FRAC_BITS fractional precision with exactly one
rounding, for consumers whose signal sits below the storage rounding floor
(fine-grained-MoE expert outputs). Narrowing contract pinned by property
tests: `q2f / (1 << FRAC_BITS)` reproduces the narrow matvec bit-for-bit for
the three unscaled forms; `RowScaledTQ19` applies the scale to the wide dot
(±1 storage LSB vs the narrow path for non-unit scales, exact at unit scale).
---
### v0.4.32: Public compute-tier transcendentals
`g_math::compute_tier` (feature `inference`): the tier-N+1 engines exposed
directly over raw `ComputeStorage` at 2·FRAC_BITS precision: `exp`, `ln`,
`sqrt`, `sinhcosh` plus `sigmoid`/`softplus`/`ln1p` stable compositions,
with `from_fixed`/`to_fixed`/`try_to_fixed` conversions. Same engines as
every other path (path independence pinned by test); format-compatible
with the wide-output `matvec_q2f` accumulators. exp saturates at
`ceiling()` and never wraps; domain violations panic; storage conversions
fail loud. mpmath-gated at q16_16/q32_32 with measured tolerances:
storage level exact (0 LSB) for all functions at both profiles. Closes
the consumer ask that had downstream re-deriving integer-only wide-tier
exp/sigmoid/softplus/ln1p.
---
### v0.4.33: Balanced ternary contract + validation; inv_sqrt family
**DELIVERED 2026-08-11.** The balanced-ternary domain is now formally
defended rather than merely supported (contract + oracle + exhaustive +
theorem suites + `ternary-domain` CI), two latent ternary defects found by
the suite are fixed (`-0.x` sign loss in `from_str`; silent wrap in
`ternary_to_storage` on narrow profiles; now loud `TierOverflow`), and the
consumer-requested reciprocal-norm family shipped additively:
`FixedPoint::inv_sqrt`/`try_inv_sqrt` and `fused::inv_sqrt_sum_sq`
(compute-tier throughout, one rounding; one inv_sqrt + N multiplies
replaces N per-component divisions in normalization). Original scope
below for reference:
- `docs/design/BALANCED_TERNARY_CONTRACT.md`: representation invariants,
canonicalization, and the **tie-free rounding theorem**: for values with a
finite balanced-ternary representation, truncating trits is round-to-nearest
with error strictly below ulp/2 (discarded tail ≤ ½·(1−3⁻ᵐ)·ulp), so
ternary-closed add/sub/mul need no tie-breaking logic and satisfy
`round(−x) = −round(x)` by construction. The contract also pins the
boundary cases where ties *do* reappear (conversion into ternary: binary ½
is `0.111…` repeating, and division) and specifies the rule used there.
Sibling invariant: ranges are symmetric `[−(3ⁿ−1)/2, +(3ⁿ−1)/2]`, so
negation can never overflow (no two's-complement MIN edge case).
- Reference trit-vector oracle (test-only, deliberately simple `Vec<i8>`).
- Exhaustive small-width add/sub/mul vs the oracle; boundary families
(±3ⁿ, ±3ⁿ±1, long runs of ±1 trits, alternating patterns).
- Theorem tests: tie-free truncation, negation symmetry/no-overflow, the
conversion-tie case pinned as documented behavior.
- UGOD promotion at 3ⁿ tier boundaries; canonical↔imperative path
equivalence; ternary↔binary/decimal coercion.
- CI job (no table rebuild needed: cheap on every push).
### v0.4.34: Ternary routing column (Add/Sub)
**DELIVERED 2026-08-14.** `DomainChoice::Ternary` + table column for
cross-domain Add/Sub of 3-adic operands (exact by construction), silent
fallback when coercion overflows narrow storage, classifier now reads
Symbolic denominators directly, and the unchecked-cast wrap in
`convert_to_ternary` fixed. Mul/Div deliberately excluded: products can
leave the tier's exactness range where ternary truncates but symbolic is
exact, and the class mask cannot see denominator exponents; a
dispatch-time shadow-exponent guard is the future path. Reasoning:
`docs/design/TERNARY_ROUTING_COLUMN.md`. Measured end-to-end: ~1.06×
vs the rational fallback (parse-dominated); the value is architectural.
### v0.6.0: Certified interval arithmetic, definiteness verdict, exact predicates
**IMPLEMENTED 2026-08-29 on main; released as 0.6.0 on 2026-08-30, with the three substrate fixes it found released separately as 0.5.1.**
`g_math::fixed_point::Interval` (module `imperative::interval`): an enclosure
`[lo, hi]` sound by construction, from exact compute-tier products and
directed narrowing (floor for the lower endpoint, ceil for the upper) at the
single narrowing point per compound operation. Operations: `+ - * /`, `Neg`,
`sqrt` (certified a posteriori by `k^2 <= n < (k+1)^2` in exact integers, so
the certificate is independent of the engine), `dot` and `quadratic_form`
(exact accumulation, one narrowing per stage), and the certainty predicates.
Endpoint arithmetic never wraps: storage overflow is a typed `TierOverflow`.
No transcendental is provided until its bound is proven per engine. Imperative
tier, binary domain, not a FASC domain and not UGOD-tiered (reasoning in
`docs/design/CERTIFIED_INTERVALS.md`). Measured at Q64.64 on a 23-dimensional
quadratic form before implementation: 5 to 64 ulp wide on values of order 10,
zero enclosure failures, unmoved by ill-conditioning, 106 to 113 percent of
the scalar path; 12 to 24 times tighter than narrowing every elementary
operation. Gate: `tests/interval_enclosure.rs`, all profiles, in the
narrow-profile CI matrix. Delivers the "Interval arithmetic: certified
enclosures" item below for the certifiable subset; the transcendental half of
that item stays open.
Same day, the decimal twin: `DecimalInterval<D>` over `DecimalFixed<D>`, one
profile-independent code path with `10^D` in place of `2^F`, `D256`
intermediates, the same sqrt certificate, typed errors where the scalar
saturates. Gate: `tests/decimal_interval_enclosure.rs`. Building it found two
silent-wrong-value bugs in the wide integer types, both fixed and gated by
`tests/wide_integer_sign_semantics.rs`: `D256`/`D512` subtraction never
propagated a borrow (the UGOD decimal tier 5 and 6 arms), and `Ord` on `I1024`,
`I2048`, `D256`, `D512` ordered two negatives backwards (the scientific
profile's compute tier among them).
Then `predicates::pd_verdict`: the Cholesky factorisation in certified
interval arithmetic, returning `PositiveDefinite` (proven, all pivots
certainly positive, no BigInt), `NotPositiveDefinite { pivot }` (proven, a
pivot certainly at or below zero) or `Inconclusive { pivot, straddle }`
(the caller decides with the enclosure in hand). Last-pivot width measured
at `2.6e-17` for n = 23 and `1.2e-15` for n = 50 on Q64.64. Gate:
`tests/pd_verdict_validation.rs`, every profile.
And `predicates::{orient2d, orient3d, incircle, insphere}` returning a
`Sign` trichotomy: exact integer determinants on a per-profile accumulator
derived from the storage width, no arbitrary precision, every multiply
asserting its width budget. The circle predicates are not compiled on the
scientific profile (2053 / 2566 bits against 2048); the recorded route for a
future consumer there is the existing arbitrary-precision type behind its
gate, not a wider hand-rolled type. Gate:
`tests/exact_predicates_validation.rs`, exact i128 oracle, every profile.
Together with `pd_verdict` this delivers the "Exact geometric predicates"
item below except segment intersection and containment, which have no
consumer yet.
### v0.6.1: Fused single-narrowing quadratic form
**IMPLEMENTED 2026-08-30 on main, additive.** Requested by the
geometric-validation consumer's width decomposition: the 1 to 5 ulp width
of the certified `d^2` was, to the digit, `sum_i |v_i|` (one narrowing per
row of `M v`) plus the final pair, so the lever was the narrowing count, not
the profile. `fused::quadratic_form` / `try_quadratic_form` compute
`v^T M v` from exact triple products at `3 * FRAC_BITS` on the orient3d
accumulator of the profile (i128 / I256 / I512 / I1024 / I2048) and round
ONCE to nearest, ties toward +infinity: the correctly rounded scalar.
`Interval::quadratic_form` narrows the same exact value to floor and ceil:
width at most 1 ulp, 0 when representable (was 3.2 ulp mean at n = 7, 5 to
64 ulp at n = 23). Off-diagonal terms are paired without assuming symmetry.
Measured on the embedded profile (release, min of 3): the fused scalar at
85 to 89 percent of the two-stage scalar, the certified form at 86 to 90
percent of it and 107 to 122 percent of the 0.6.0 two-stage interval. The
accumulator trait moved to a private shared module; `I2048::checked_add`
added. Gates: the enclosure gate asserts `[floor, ceil]` and nearest
exactly on random forms and constructed ties; the reference gate carries
quadratic-form references computed on values with exact rationals and
mpmath at 700 digits; all five profiles in CI.
### v0.6.2: Iterative decompositions converge or fail loudly
**Patch release 2026-09-16, no API change.** A consumer report (SVD wrong at
realtime Q22.10 since 0.5.0) traced to defects older than the rounding change
it bisected to, on every profile, in all three iterative decompositions:
`svd_decompose` returned the unconverged diagonal as `Ok` on exactly
rank-deficient input and rotated U the wrong way in interior zero-diagonal
deflation; `schur_decompose` stopped its bulge chase one step short, never
reduced 2×2 blocks, had no exceptional shifts, and returned `Ok` on budget
exhaustion; `eigen_symmetric` squared off-diagonal entries at storage
precision in its convergence test (squares beyond range wrapped and passed at
sweep zero) and returned `Ok` on stagnation. Fix: rotation coefficients,
Householder factors and shifts at the compute tier with one narrowing per
transformed entry; the SVD's bidiagonal carried at the compute tier through its
whole iteration; the existing tight relative bound floored at four quanta;
absolute zero-diagonal tests with explicit zeros; a stagnation fallback bounded
by the looser sqrt(quantum) test (on realtime following `GMATH_FRAC_BITS`);
`Err(PrecisionLimit)` and `Err(TierOverflow)` in place of silent results and
panics. Design measured first on an exact-integer replica of the SVD,
bit-exact against 0.6.1. Gates: `tests/decomposition_convergence_validation.rs`
with 37 fixed cases and a seeded random corpus of 64 cases per decomposition
drawn from the same failure classes (mpmath references cross-checked against
exact characteristic polynomials), every profile plus realtime at
`GMATH_FRAC_BITS=10` on every push, and a fresh seed weekly
(`linalg-decompositions-fresh`). Calibration of the random corpus on other
seeds (52,736 decompositions over six configurations, every one converged)
found three more defects before release, all fixed: the SVD's bidiagonal
chase rounded to storage lost sub-quantum bulges and stalled on small-valued
matrices; the realtime stagnation bound was fixed at the Q16.16 shift and too
tight at Q22.10; and both stagnation fallbacks fired during normal convergence
(the largest entry holding, or a count of iterations), deflating at the loose
bound where the tight one was reachable. Schur now repeats its exceptional
shifts every 10 iterations.
Follow-up, measured and not in 0.6.2 (done in 0.6.4, which carries H at the
compute tier): `schur_decompose` still applies its Francis steps to H at
storage precision. On embedded, a random 10×10 with
entries `k/64` stops improving above the tight bound and deflates at the
looser sqrt(quantum) bound after 64 iterations (backward error 2.8e7 ulp);
divided by a further 64 or 4096 it takes 210 to 220 iterations, and
multiplied by 64 it converges at the tight bound in 20. This is inside the
documented contract and never an error, but carrying H at the compute tier
through the Francis phase, as the SVD now carries its bidiagonal, is the
likely way to lift it (a hypothesis, not yet measured).
### v0.6.3: Exact float boundary
**Patch release 2026-09-19.** A downstream report (realtime at
`GMATH_FRAC_BITS=10`) showed `to_f64` printing a truncated decimal string and
parsing it: 1024 raw values mapped to 1000 f64 values, and every
`from_f64(to_f64(x))` round trip lost a step toward zero. Confirmed on every
profile (the cut was worth up to 1.0 raw step at Q22.10, 6.6 at Q16.16, 4.3 on
compact). `to_f64` / `to_f32` now assemble the IEEE bits from the raw integer:
exact whenever the raw value fits the significand, nearest-even otherwise, f32
subnormal, zero or infinite at the wide profiles' extremes; 16 to 99 times
faster. Checking the report also found `from_f64` / `from_f32` wrapping
out-of-range values silently on every profile; they now panic, and the new
`try_from_f64` / `try_from_f32` return `TierOverflow` (NaN: `InvalidInput`).
In-range `from_f64` / `from_f32` stay bit-identical to 0.6.2 (compared on
200,000 inputs per profile), and `x.to_string().parse::<f64>()` reproduces the
0.6.2 `to_f64` exactly for replays. Gate `tests/float_boundary_validation.rs`
(exact-rational references, `scripts/generate_float_boundary_refs.py`), CI
`float-boundary` on every profile plus Q22.10.
### v0.6.5: Defect fixes ahead of the width-as-type work
**Release 2026-10-05.** Two downstream notes were checked claim by claim
against 0.6.4 and a survey of the profile wiring was made for the next major
piece of work (precision as a type instead of a build setting). The checks
found defects in the library itself, fixed here with no change to any
in-range result: the TQ1.9 and packed-trit kernels narrowed with wrapping
casts (and the AVX2 trit kernel mishandled `i32::MIN`); `rms_norm_factor`
returned `Ok(0)` for a negative radicand; the serialization tag did not record
the realtime fractional split; two profile features enabled at once were
resolved silently; the top of the decimal UGOD ladder truncated oversized
products and quotients; `Currency` was documented public but not exported.
The error conditions of the fused softmax and RMS-norm operations are now
documented per function. Gate `tests/defects_065_validation.rs`.
The requests in the two notes that held up under checking shipped in the same
release, verified bit-identical to the code they replace downstream (except
`rms_norm` and `rotate_pairs`, which round once where the downstream loops
rounded two or three times, and are gated against mpmath instead): a
tie rule for decimal rounding and a one-rounding `mul_div`; a layout guarantee
for `FixedPoint` with zero-copy slice views; a faster realtime dot and
softmax mix with unchanged results; five-trit row-scaled matrices
(`RowScaledTQ5`), bit-pattern decoders and quantisers; `dot_many`,
`rms_norm`, `rotate_pairs`. Declined: importing a kernel that is not
correctly rounded, and a second (truncating) rule for decimal literals.
### v0.6.6: Row-scaled quantisers on wide exponent gaps
**Release 2026-10-05.** A downstream report against 0.6.5: the row-scaled
quantisers (`tq19::quantize`) compared and divided values by shifting a
mantissa by the difference of two binary exponents in 128 bits. bfloat16
rows that hold a value near 2^-124 beside ordinary weights exceed that; the
result was a panic, or wrong codes in a release build. The comparison now
uses the exponents alone from a gap of 11 bits and never shifts further.
The exact-rational reference set gained a 32-row wide-gap matrix; nothing
else changed.
### v0.6.7: Gate functions at the wide tier, caller buffers
**Release 2026-10-05.** Additions requested downstream, none changing an
existing result. `g_math::wide` gains sigmoid, softplus and silu at Q64.64
(measured 3, 9 and 79 units; the nearest value after one narrowing to 40 or
20 bits on every tested input), a correctly rounded square root at any
target precision, full-width `mul_div`, and exact decimal ratios. `fused`
gains `sigmoid_mul` and `entropy` (0 units from mpmath on every build) and
buffer-writing forms of `dot_many` and the flat softmax mixes. The five
ternary matrix types gain `matvec_batch_par_into`. Held for the
width-as-type line, where a second fixed-point format exists to carry them:
recurrence arithmetic on a 64-bit state with 40 fractional bits.
### v0.6.4: Wide tier, exact literal parser, compute-tier state, loud operators
**Release 2026-09-25.** A float-free downstream consumer (realtime Q22.10) reported four places it had
to route around the library. All four confirmed. `rms_norm_factor` added its
epsilon at the storage tier, where transformer epsilons (`1e-5`, `1e-6`) are
zero at Q22.10: new `rms_norm_factor_eps_wide` takes a Q64.64 epsilon and adds
it at the compute tier. `from_str` had no exponent notation and panicked on
`"1e-06"`: decimal literals now convert exactly with integer arithmetic,
bit-identical to the old path on every literal it handled, and
`try_from_str` never panics. The Q64.64 `exp`/`ln` engines were reachable only
through the hidden `domains` path with a "~10-13 digits" note: the new
`g_math::wide` module exposes them with sin/cos, π constants and a measured
contract, the same results on every profile. `sincos_wide_q64` removes the
caller-side Q32.32 truncation. Found on the way and fixed in the same cycle:
the canonical layer narrowed with unchecked casts in four places (decimal
literals into i32/i64 decimal storage, decimal UGOD results back to storage on
every profile, decimal-to-binary coercion, hex/binary literals), and rounded
toward zero in two (literals past 38 digits, decimal compute results to
binary). At Q22.10 `gmath("214748.3648")` was -214748.3648 and
`214748.3647 + 1` was -214747.3649. Hex/binary literals now denote their
integer.
The whole suite then ran at other realtime splits (the owner: `GMATH_FRAC_BITS`
is a choice, 20.12 as much as 22.10). Library defects found that way, all
fixed: fraction bits hardcoded to 16 (rational conversion, curvature
differences), silent narrowing (`from_int`, `make_compute_int`,
`compute_divide`, symbolic/decimal to compute tier, compute-tier sums), RK4
and Dormand-Prince stages rounded to storage (Dormand-Prince also missed its
b*7 term), constants and counts forced into the storage range (Rodrigues
series, vector lengths, n!), a Rodrigues threshold of zero at 8 bits. On the
owner's decision, intermediates rounded to storage were then reworked to the
compute tier (norms, dot, QR, Minkowski product, tensor means, the shared
Householder update) and `matrix_exp` / `matrix_log` / `matrix_sqrt` were made
accurate on the wide profiles (21 units on Q64.64 and about 2^31 on Q128.128
and wider before). The suite gates realtime 8 to 24 fraction bits in full
(`realtime-splits` CI) and runs correctness gates at 2 to 6 and 26 to 30; a new
mpmath gate (`one_rounding_validation`) holds the reworked operations within
one unit of the correctly rounded result.
Then, on the owner's direction that every computation of several steps carries
its state at the compute tier and that operators never wrap: QR, LU, Cholesky
(with their solves), Jacobi, the SVD and Schur (with thresholds at the compute
scale, `2^-(3F/2)`), the ODE integrators, geodesics and transports, curvature,
the manifold and Lie-group maps, fiber bundles, projective maps and the tensor
decompositions now carry their state at the compute tier and round once. Six
mpmath gates hold them within one unit on every profile and split, except the
SE(3) log round trip near pi (3 units: the exp result it starts from is
rounded) and CP-ALS reconstruction (2: four independently rounded outputs).
Before: up to 114 units in an LU solve, past `2^30` units in SVD vectors and
Schur eigenvalues on the wide profiles, 1536 in parallel transport, 10^25 in
Riemann curvature on scientific. The same work found wrong results: the
sectional curvature contraction was identically zero, `StiefelManifold`'s
distance was the square root of the distance, the Grassmannian log paired the
wrong angles, SO(3)/SE(3) exp failed for small angles on realtime,
`FixedPoint::atan2` panicked on every realtime split. The `FixedPoint` and
`DecimalFixed` operators, `to_int`, `from_int` and the compute-tier helpers now
panic instead of wrapping or saturating, with `try_` twins (owner decision:
panic plus `try_` twins; UGOD stays in the canonical layer, since a
fixed-width type cannot promote). Checked `*` costs 0.24 to 0.44 ns on
realtime and compact, 15% on balanced; `+ - /` are unchanged.
The matrix functions then moved to a Q64.64 path on realtime (they ran at the
compute tier's `2F` bits: `expm` of a norm-7 matrix was 8 units off at 8
fraction bits) and are correctly rounded on every profile and split, and
`DecimalFixed` gained `try_` twins for its 18 transcendentals (never panic,
bit-identical where the infallible form returns; the sweep behind that found
six more panics on extreme inputs, fixed).
Follow-ups, measured and not done: `sin_q64`/`cos_q64` lose accuracy linearly
in `|x|` (0.34 units of `2^-64` per radian) because range reduction uses the
single truncated `PI_HALF_Q64`; a two-part Cody-Waite constant would remove
it, but changes results the realtime and compact compute tiers depend on. On
realtime, `rms_norm_factor_eps_wide` is limited by the compute tier's
`2 x FRAC_BITS` bits (at Q22.10, 2.4% high on an all-zero input with `1e-5`).
---
## Next: 0.5.0: Correctness audit + remaining composed transcendental bypass
**Status: staged on `main`, not yet released.** Items 0 through 2c below are
delivered, swept green on all five profiles, and CI-green; item 3 is deferred
past the cut. What remains for the release itself: version bump, CHANGELOG
dating, and the consumer notice for the two bit-level changes (binary
mul/div rounding, ternary raw scales). Until the tag lands, the published
crate is 0.4.34 and `Cargo.toml` still reads 0.4.34 by design.
### 0. Narrow-profile integer-literal parse fallback: DELIVERED 2026-08-14
**Fixed on main** (post-0.4.34): oversized integer literals now fall back
to the Symbolic domain in `parse_integer` (all three narrow-profile arms);
regression test `oversized_integer_literal_falls_back_to_symbolic` runs on
every profile, and the router/domain integration suites now run on
realtime+compact in CI (ternary-domain workflow) so this class cannot hide
again. Original finding:
On realtime (Q16.16), any integer literal beyond the binary storage range
(`32768`+) fails PARSE with `Overflow` instead of falling back to the
symbolic domain, so `evaluate(gmath("1000000") * gmath("0.001"))` errors
on realtime while succeeding everywhere else. UGOD's contract says the
symbolic ladder top never fails; the parse path should route oversized
integers to Symbolic. Pre-existing at least since 0.4.32 (bisected);
invisible because no CI job runs the router integration suite on realtime
(`large_integer_times_decimal` fails there today). Fix parse routing, make
the test profile-aware, and add a realtime router-integration CI job.
### 0b. Unsigned widening-multiply call-site audit: DELIVERED 2026-08-23
Every call site of the unsigned family (`mul_to_i512/i1024/i2048`)
enumerated and classified (appendix in `docs/design/ROUNDING_CENSUS.md`):
- **Sign-wrapped (correct)**: linalg dot/product helpers, decimal compute
engine, compute_multiply, sincos/atan via the sign-safe
`multiply_i1024_q512_512`, the 0.4.34-era ternary fixes.
- **Positive-by-construction (now debug_assert-ENFORCED)**: exp table
chains + Taylor, ln Taylor remainder, sqrt Newton: every such site
carries a non-negativity assert, so every debug/test run polices the
invariant instead of trusting comments.
- **De-shadowed**: exp and sqrt each had a private UNSIGNED
`multiply_i1024_q512_512` shadowing the sign-safe pub(crate) one:
renamed `*_nonneg` with asserts; the shadowing hazard is gone.
- **Latent bugs fixed in dead code**: the three pow `y·ln_x` sites were
genuinely sign-broken but `pow_tier_n_plus_1.rs` has ZERO external
callers (FASC/imperative compose pow as exp(y·ln x) through the safe
path): sign-wrapped anyway. RESOLVED 2026-08-23: module REMOVED per
owner decision (with its direct-engine tests and reference data);
composed pow keeps its 0-ULP gates.
- **Modular-truncating wide `Mul` fallbacks** (I512/I1024 schoolbook mod
2^N) are sign-correct by modular arithmetic; exercised with negatives
by the 0.4.34 tier-5 lattice tests.
- New gate: `tests/negative_operand_battery.rs`: odd/even transcendental
symmetries bit-exact, negative-intermediate chains (exp∘ln on x<1,
sinh/cosh of −x), negative FASC compute products; all profiles.
### 0c. Uniform rounding policy: DELIVERED on main 2026-08-23
**Implemented exactly as analyzed below**; permanent gate
`tests/rounding_unification.rs`; per-site evidence and implementation
corrections in `docs/design/ROUNDING_CENSUS.md`; CONTRACT.md §3 collapsed
to one row per domain. Original analysis retained:
**Owner-directed 2026-08-14** ("having different rounding makes it a more
difficult library to work with"). Today rounding differs not just per
domain but per PATH within a domain (CONTRACT.md §3): binary multiply is
banker's imperatively but ties-up canonically (1 ULP apart on exact ties,
measured); DecimalFixed is banker's while the canonical decimal divide
truncates; ternary truncates toward zero despite nearest being provably
tie-free.
**Analysis of the three options:**
- *No rounding (exactness preservation)* is not an alternative; it is
deferral. It is superior wherever a result stays representable (the
canonical decimal multiply already grows decimal places instead of
rounding, and tier N+1 defers all compound rounding to one downscale),
but every domain eventually hits its scale cap and needs a rule. So the
policy is: **exact when representable, round once otherwise**, with
the remaining question being which tie rule fires on that one rounding.
- *One global rule* is simplest to state but fights both convention and
the codebase: banker's everywhere would rewrite the wide-tier downscale
that every 0-ULP validation is pinned to (huge blast radius, zero
consumer benefit: compound paths never tie); ties-up everywhere would
break the banker's behavior financial consumers expect of DecimalFixed.
- *One rule per domain, uniform across paths* matches convention AND
minimizes result churn. **Recommended:**
| Binary | nearest, ties toward +INF | the wide-tier downscale (the only rounding compound results ever see) is already ties-up everywhere; aligning the imperative mul kernel is a one-branch change | `multiply_binary_i128` (+ AVX2 twin): banker's -> ties-up; closes the measured 1-ULP path divergence |
| Decimal | banker's (half-even) | the accounting convention, and what DecimalFixed already does on mul AND div | canonical decimal divide: truncation -> banker's; dp-cap rounding aligned |
| Ternary | nearest (tie-free) | 3^m is odd: nearest can never tie (contract theorem); no rule needed at all | mul/div/div3: toward-zero (<1 ulp) -> nearest (<0.5 ulp); resolves the 0.4.33-flagged decision; div3 becomes a true trit shift |
End state a consumer can memorize in one sentence: *binary rounds to
nearest ties-up, decimal rounds banker's, ternary rounds to nearest
(which cannot tie)*: identical on every path within each domain.
All three alignments change results only on exact ties or by <1 ulp on
currently-truncated ops: breaking-precision class, so they ship together
in 0.5.0 with a consumer notice (consumers freezing hashed outputs should
pin versions across the boundary). Add tie-case and cross-path
equivalence tests per domain to the path-independence suite.
### 0d. "Realtime cosine plateau": SOLVED 2026-08-23 (was a materialization bug)
Investigation cleared the kernel entirely: the q16_16 sin/cos engines are
correct (cos(0.1) = 0.99524 at the kernel, imperative path bit-perfect).
The real defect: DecimalCompute results materialized at a fixed
`DECIMAL_STORAGE_MAX_DP - 2` in THREE places (Display, to_decimal_string,
to_rational): imperceptible slack on wide profiles, but HALF of
realtime's four decimal digits, so 0.9952 rendered as "1.00" and every
display-round-tripping test saw a "plateau". Fixed with adaptive
materialization: try full MAX_DP, step down only when the magnitude
genuinely needs fewer decimals (checked downscale, never a wrap). Wide
profiles gained their 2 withheld digits back as a side effect. The
0d-era test-tolerance workarounds were REVERTED: original strict
tolerances pass again on realtime. The related FASC exp(20) realtime
TierOverflow was resolved under item 1: it is correct-and-loud (e^20
exceeds every realtime representation), not a missing fallback.
### 1. UGOD multi-tier promotion verification: DELIVERED 2026-08-23
**Verdict:** the original concern (promoting to symbolic after a single
tier) was NOT the defect: mid-ladder promotion (binary tiers 1→4) was
always correct (multiply/divide chain tiers explicitly; add/sub's
single-step promotion is provably sufficient since a sum grows by at
most one bit). The TOP of every ladder was broken instead, and wrapped
SILENTLY rather than falling back:
- Binary Tier-4/5 mul truncated wide products unchecked (balanced
`1e20 × 1e20` → 1.318e38 wrap garbage); Tier-4/5/6 add/sub used bare
wrapping operators (embedded `9e18 + 9e18` → 0.0). Now checked, with
4→5→6 promotion arms.
- `binary_to_storage` narrowed promoted raws with bare casts; now
fits-checked, and the FASC binary arms fall back to the exact
rational path on `TierOverflow`: the true ladder top: exact or loud.
- Divide mislabeled quotient overflow as `DivisionByZero` (zero check
now decided once at ladder entry).
- Symbolic ladder: try_multiply's "promotion" retried at the same i128
width (never reached Massive/I256); `divide_mixed_tiers` didn't
escalate although a quotient can need a wider tier than either
operand (9e18 ÷ 1e-9 = 9e27). Both now climb.
- FASC symbolic/ternary→binary coercion shifted i128 before any range
check: symbolic 1e20 coerced on embedded wrapped mod 2^64 into a
PLAUSIBLE WRONG value. Now checked nearest-ties-+∞ at tier N+1 width.
- Two adjacent finds: narrow-profile fractional literals beyond the dp
cap silently parsed to truncated values (realtime `"0.000000001"` →
exactly 0) (now Symbolic-fallback like item 0; and the scientific
formatter displayed integer parts mod 2^128 (i128 squeeze)) now
digit-at-a-time at I256 width.
Gate: `tests/ugod_promotion_validation.rs` (all 5 profiles green; in
ternary-domain CI on realtime+compact). The 0d-flagged realtime FASC
exp(20) TierOverflow is RESOLVED as correct-and-loud: e^20 ≈ 4.85e8
exceeds every realtime representation (Q16.16 max ≈ 3.3e4), so a loud
TierOverflow is the contract, not a missing fallback.
### 2. Complete FixedPoint direct-engine bypass for composed transcendentals: DELIVERED 2026-08-23
The fallible `try_*` variants (tan/atan/asin/acos/sinh/cosh/tanh/asinh/
acosh/atanh) no longer route through FASC (`try_apply_unary(LazyExpr)`);
each is now a direct compute-tier composition mirroring its infallible
twin; same engines, same formula, same single downscale. Error contract
preserved: `DomainError` on domain violations (0.4.27), `TierOverflow`
on storage overflow, `asin(±1) = ±π/2` boundary shortcut, tanh ceiling
saturation to exactly 1. Gate: `tests/try_direct_bypass_validation.rs`:
bit-identity with the infallible twins on in-domain inputs (which are
0-ULP gated), typed domain errors, loud overflow.
### 2b. Ternary tier resize: DELIVERED 2026-08-23 (owner-approved)
Every balanced-ternary tier now fills its storage word: TQ10.10 (i32),
TQ20.20 (i64), TQ40.40 (i128), TQ80.80 (I256), TQ160.160 (I512),
TQ320.320 (I1024): +25% trits per tier, same words, same speed, clean
2× ladder. BREAKING for persisted ternary raws (all scale factors
changed; tie pins now 0.5 → 29,525 / −0.5 → −29,524). The FASC
binary→ternary coercion's tier-3 arm moved to I256 intermediates (the
larger 3^40 scale overflowed the i128 path). TQ1.9 unaffected. Full
capacity math: docs/design/BALANCED_TERNARY_CONTRACT.md §1b. Resolves
the 0.4.33-flagged "tier resize" owner decision: batched with the 0c
rounding change so consumers absorb ONE bit-compatibility boundary.
### 2c. Dead pow engine removed: DELIVERED 2026-08-23 (owner-approved)
`pow_tier_n_plus_1.rs` deleted with its direct-engine tests and
reference data: zero production callers since the composed
exp(y·ln x) path shipped; its latent sign bugs (0b audit) are moot.
Composed pow keeps its 0-ULP mpmath gates.
### 3. Stack evaluator `profile_dispatch!` macro
**Priority:** MEDIUM: reduces maintenance burden, prevents cfg copy-paste bugs
**Effort:** ~750 lines, 2 sessions
Extract macro to replace the 5-way `#[cfg(table_format)]` blocks across all stack evaluator submodules. Each profile-conditional function currently has 5 copy-paste arms. A macro reduces this to 1 invocation per function and makes future profile additions mechanical.
---
## Requested by consumers (gHyper/gFile): U2: cross-profile rounding contract
Different profile bit-widths necessarily round to different values (a Q32.32
result cannot hold Q64.64 digits): that divergence is inherent and out of
scope. What IS in scope is stating and CI-testing the contract BETWEEN
profiles:
1. **Widening is exact**: every narrower-profile value embeds losslessly in
any wider profile (pure bit-shift; test it).
2. **Narrowing is a single correct rounding**: because transcendentals are
computed at the compute tier and rounded once to storage, a narrow
profile's result should equal round_narrow(exact): i.e. no
double-rounding artifacts (round_32(round_64(x)) != round_32(x) edge
cases). State it, and CI-gate it with cross-profile fixture comparisons.
3. **Serialization semantics for foreign profiles**: `profile_tag()` exists;
document whether a reader encountering a foreign-profile raw value must
reject or may convert (widening ok, narrowing rounds, and say which
rounding mode).
Context: gFile's `.htt` format pins Q64.64 and byte-determinism is CI-gated
per platform; this contract is what would make any future cross-profile
tooling (e.g. compact-profile analytics over embedded-written data) sound
rather than accidental.
## Future: High Priority
### Open defect: `exp` on the compact profile is one unit off for large results
**Found 2026-10-05, not fixed.** On the compact profile (Q32.32) `exp` and
`try_exp` return a value one storage unit from the nearest on about 1 input
in 100 when the result is large: 18 of 1,500 random arguments with `|x|` up
to 21, against mpmath. For `|x| < 2` it is the nearest value on 1,500 of
1,500. Realtime is not affected (Q22.10 and Q16.16: 1,500 of 1,500 nearest,
large arguments included).
Cause: the compact compute tier is Q64.64, 32 bits above storage, and the
Q64.64 engine's error is relative to the result (about 5 units of `2^-64`
times `e^x`). A result near `2^29` leaves no margin. The canonical evaluator
calls the same engine through the same rounding, so it is expected to behave
the same; that path was not measured. The existing gates use small
arguments, which is why they pass.
To do, as one change that moves results (so its own release, not folded into
an additive one):
- Measure every profile at the top of its range, and the functions built on
`exp` (`sinh`, `cosh`, `tanh`, `pow`), on both the imperative and the
canonical path.
- Compute large results with enough headroom (the next wider engine, or a
split of the argument that keeps the error absolute).
- Extend the reference gates so every transcendental is tested across its
whole output range on every profile and split, not only near zero.
### Complete the imperative (non-routed) layer for every domain
**Priority:** HIGH: the largest ergonomics gap in the public surface
The imperative layer (`FixedPoint`, `FixedVector`, `FixedMatrix`, and everything
built on them: decompositions, matrix functions, manifolds, Lie groups, ODE,
tensors) is the direct, no-routing, `Copy`-semantics path: one call is one engine
invocation, no lazy tree, no fractal-router dispatch. Today it exists **only for
the binary domain**. `DecimalFixed` covers decimal *scalars* with native
transcendentals, but there is no `DecimalVector`/`DecimalMatrix`, and balanced
ternary and symbolic rational have no imperative vector/matrix surface at all.
The goal is parity: give each domain gMath already supports (decimal, balanced
ternary, symbolic rational) the same direct imperative surface binary has, so a
consumer who knows their domain can skip the router overhead in *any* domain, not
just binary. Concretely:
- `DecimalVector` / `DecimalMatrix` over `DecimalFixed<N>`, with compute-tier dot
accumulation mirroring `FixedVector`/`FixedMatrix`.
- An imperative surface for balanced ternary vectors/matrices (beyond the current
TQ1.9 inference path).
- Symbolic rational vectors/matrices for exact linear algebra.
- Where a domain lacks native transcendentals (ternary, symbolic), document the
binary-compute bridge explicitly rather than silently routing.
This is a multi-session track; sequence decimal first (highest consumer demand),
then ternary, then symbolic. Relates to *Symbolic rational transcendentals* and
*Imperative geometry methods* below.
### Batch/vectorized API
SIMD-friendly array processing for FixedPoint operations beyond TQ1.9. Bulk exp, sqrt, and arithmetic over vectors would accelerate softmax, RMSNorm, and embedding decode. Compelling with Q32.32 (8x i32 per AVX2 register) and Q16.16/Q8.24 (16x i16).
### Ternary test coverage
Balanced ternary arithmetic lacks a dedicated validation suite. The domain works but needs stress-testing against reference values. Low effort, fills a known quality gap. **Now scheduled: see "Planned: 0.4.33" above.**
### Interval arithmetic: certified enclosures
A first-class interval type ([lo, hi] guaranteed to enclose the true value) built
on the existing tier-N+1 machinery. The downscale step already rounds once;
adding round-toward-−∞ and round-toward-+∞ modes beside the current
round-to-nearest is a branch on the discarded bits, not new math. Arithmetic
shift is already floor for two's complement, so floor is computed today and ceil
is the same shift plus a nonzero-remainder test. That directed (outward) rounding
is what makes an enclosure sound. The payoff is the error-transparency story for
validation and anomaly consumers: a score of d² ∈ [lo, hi] separates "certainly
outside" from "within our own numerical noise".
What may carry the word certified is decided by an algebraic boundary, not by
convenience. Add, subtract, multiply and divide are sound by the standard
endpoint argument. sqrt and inv_sqrt certify a posteriori in exact integers,
since isqrt(n) is the unique k with k² ≤ n < (k+1)² and both inequalities are
checkable at the compute tier: no error analysis is required and the certificate
survives a change of algorithm. The transcendentals do **not** belong in v1.
Their accuracy is validated against mpmath at chosen test points, which is
evidence about those points rather than a proof of a bound over the domain, and
an interval widened by a measured error encloses nothing while looking exactly
like one that does. Each transcendental joins only once its bound is proven, one
engine at a time.
Measured 2026-08-29 on a 23-dimensional quadratic form at Q64.64: enclosures came
out 5 to 64 ulp wide on values of order 10, a relative width near 1e-19, with no
enclosure failures and no widening under deliberate ill-conditioning. Cost was 6
to 13 percent over the scalar path, because endpoint work only doubles on stages
whose inputs are already intervals; where inputs are exact and the accumulator is
wide, an interval costs one extra directed narrowing. The dependency problem is
bounded by the width it starts from: its sign-dependent cases need an interval
that straddles a critical point, which a 1-ulp interval cannot, and its
subtractive cases only double a width that is already negligible. The same
computation with a narrowing after every elementary operation, which is the
classical formulation, is 12 to 24 times wider. Rounding once per compound
operation is therefore what makes the enclosure tight, and affine arithmetic is
not needed for a monotonic-first v1. Reference: Moore/Kearfott; IEEE 1788-2015,
whose decorations and reverse operations are an explicit non-goal.
### Exact geometric predicates
Certified orient2d/orient3d, incircle/insphere, segment intersection, and
containment, built on the exact rational (BigInt a/b) and integer fixed-point
domains. Each predicate is the sign of a determinant polynomial in the inputs,
and only the sign matters, so exact arithmetic makes the verdict provably
correct, including the exact-zero degenerate cases (collinear/cocircular) that
floating point cannot decide reliably. Each predicate has a fixed degree, so the
accumulator width is bounded and computable in advance: 2W+2 bits for orient2d,
3W+3 for orient3d, 4W+5 for incircle and 5W+6 for insphere, where W is the
storage width. Checked against the integer types already in the library, 19 of
20 predicate-and-profile combinations fit, the exceptions being the circle
predicates at Q256.256. No arbitrary-precision backend is required, and the
accumulator type should be derived at compile time from the storage width rather
than hand-written per profile. The received view that exact predicates need big
integers is conditioned on floating-point inputs, whose exponent range makes the
product width unbounded; integer inputs remove that premise. The standard
filter→fallback pattern is therefore an optimisation here rather than a
requirement, since the exact path is a handful of wide multiplies with no
allocation, and it should not be imported without measurement. The predicate
returns a trichotomy, never a bool: the exact-zero case is the whole reason the
work is worth doing. The same sign-of-determinant primitive certifies matrix
positive-definiteness/rank (a real need for any SPD/Cholesky consumer: replaces
blind diagonal regularization). Enables a topological "shape over a point cloud"
layer (Delaunay / alpha-complex / persistent homology: integer reduction, zero
float) in low dimension. Provenance: Delone (1934, "Sur la sphère vide") +
Voronoy; Shewchuk adaptive predicates; CGAL exact-computation paradigm.
### Imperative geometry methods: UGOD + FASC integration
Upstream `square()`, `reciprocal()`, `powi()`, `manhattan_distance()`, `mul_vector()` etc. as first-class UGOD-dispatched, FASC-computed methods. ~800 lines.
### Symbolic rational transcendentals
The third compute domain after Binary and Decimal. Transcendental chains where ALL intermediates are exact rational numbers (num/den BigInt pairs). Unbounded precision, no ULP concept, just exact arithmetic until final materialization. Ultra-precision mode for scientific computing and formal verification.
### n-D Clifford algebra with Vahlen matrices (L4B): 0.5.0
Cl(n,0,1) Clifford algebra with Vahlen matrix representation. Multivector arithmetic, geometric product, inner/outer products, grade extraction, and Vahlen matrix Mobius transformations. Fills a gap I'm not aware of an existing zero-float Rust crate addressing. Targeted for v0.5.0 release.
### Custom FRAC_BITS for non-realtime profiles
Extending `GMATH_FRAC_BITS` to compact (i64), embedded (i128), and higher profiles. Requires verifying that `COMPUTE_FRAC_BITS = 2 * FRAC_BITS` does not exceed the native transcendental tier's capacity. Deferred until demand.
### Public API stabilization
Pre-1.0 audit of exports, feature gating, StackValue extraction methods. Five API tiers documented (FASC, Imperative, Fused, Geometric, TQ1.9) but export hygiene not yet audited.
### Additional decimal operations
decimal-scaled's corpus covers several functions gMath doesn't expose in the
decimal domain yet, all composable from the existing ln/exp/sqrt compute-tier
engines: `powf` (xʸ: financially core, already present on binary `FixedPoint`),
`rem` (modulo), and the `log2` / `log10` / `exp2` / `log(base)` family (trivial
ln/exp compositions). `cbrt` and `hypot` are lower priority. Implement the
subset that serves real use cases (finance: `powf`/`rem`; ML: `log2`), not for
corpus parity.
### Decimal sin/cos 3-stage tables
Precomputed sin/cos tables for the decimal engine. Deferred: analysis showed Taylor computation is a small fraction of FASC pipeline time, so tables give little gain through the full pipeline. Revisit when decimal sin/cos becomes a bottleneck (currently not: binary sin/cos handles most inference).
---
## Non-goals
- **Floating-point interop beyond convenience**: `to_f64()`/`from_f64()` exist for user convenience. Internal float usage is architecturally forbidden.
- **Dynamic precision selection**: Profiles are compile-time. Runtime tier selection within a profile is handled by UGOD, but the base storage tier is fixed at build time.
- **GPU compute**: The library targets CPU determinism. GPU offload would compromise the cross-platform bit-identical guarantee.