gMath

Ternary Routing Column: Design Decisions (0.4.34)

Why the fractal router's ternary column is scoped the way it is. Companion to BALANCED_TERNARY_CONTRACT.md (which proves the domain this column routes into). Decisions ratified by the owner 2026-08-14.

Context

Since v0.4.0 the router's classifier has computed TERNARY_BIT (shadow denominator stripped of 3s), but no routing-table column consumed it: ternary-exact values fell through to Binary (where they are inexact) or to the symbolic rational fallback. The 0.4.33 contract suite plus the post-0.4.33 gap-closing (tiers 1–6 adversarial coverage, two sign-extension fixes, checked storage narrowing) made the domain formally defended; this column makes it reachable.

Target class: values exact in ternary but not in binary/decimal: denominator 3^k, k ≥ 1 (1/3, 2/3, 100/3…). Domain preference order becomes Binary > Decimal > Ternary > Symbolic, ranked by cost: integers keep routing Binary; only the 3-adic class moves. The win is replacing rational-pair arithmetic (two multiplies + add + gcd normalization per op on num/den pairs up to I512, not BigInt, which is feature-gated behind infinite-precision and absent from default builds) with single integer adds on scaled raws.

Decision 1: the column covers add/sub (+neg) only, not mul/div

This is a correctness argument, not caution.

For operands representable at scale 3^F (denominators dividing 3^F):

Mul routing can become viable later via (a) a dispatch-time shadow-exponent guard (route to ternary only when a+b ≤ F, checked against the actual shadows, falling back otherwise), or (b) an explicit owner re-scoping of the fail-safe invariant. Option (a) is the natural follow-up; neither blocks this release. This scoping also decouples the column from the pending mul/div rounding decision (toward-zero vs the theorem's tie-free nearest): that decision can now be taken on its own schedule.

Decision 2: coercion failure falls back silently to the previous route

Gap-closing proved (and pinned by test) that narrow profiles cannot hold Tier-2+ ternary raws in FASC storage: realtime caps at TQ10.10/i32, compact at TQ20.20/i64. Coercing a large 3-adic value into ternary on those profiles is a legitimate TierOverflow.

If the router propagated that error, routing would introduce failures where the pre-column path succeeded: the other way to violate the fail-safe invariant. So: on coercion failure, dispatch proceeds exactly as if the column did not exist (rational fallback).

Implementation note: this costs nothing structural. The routing seam (try_route_coerce) already returns Option; None is the rational fallback. A failed convert_to_ternary maps to None.

Scope honesty: what the column does NOT change

Found during implementation

Measured cost (embedded, release, full evaluate pipeline)

0t2 + 1/3 (routed to ternary): 334 ns/eval; 0t2 + 1/7 (rational fallback): 355 ns/eval: ~1.06×. Honest reading: at single-expression scale the win is modest because literal parsing dominates both paths. The column's value is architectural: the 3-adic exactness class finally reaches the domain that represents it, results stay in fixed-point form instead of rational pairs, and the seam is in place for the guarded mul extension.

Validation