Numeric Tower: BigInt, Rational, Complex
For new readers
A "numeric tower" is a hierarchy of number types (plain integer → arbitrary-precision integer → exact fraction → complex number) that a language promotes between automatically, so ordinary arithmetic never silently loses precision or overflows — the key point below is that this happens invisibly, without you writing any type annotation to opt in. The demo further down is runnable live in your browser. If you're new to why this matters in practice, the Paradigms Guide's numeric-tower section gives the shorter version first.
Ordinary Int arithmetic stays on the fast path until it can't: factorial(30) overflows a 64-bit integer partway through and the runtime silently promotes to an exact, arbitrary-precision BigInt — no annotation, no wraparound, no lost digits. 10 / 3 doesn't divide evenly, so instead of quietly rounding to a float it promotes to an exact Rational and prints 10/3. sqrt of a negative number produces a Complex value. The last two lines call into self_hosting/lib/math.patlang (hypot, is_prime, mean), a small PatLang-authored library built entirely on top of a handful of Host:: primitives (sqrt/floor/abs/numeric_kind) — the hybrid design that keeps the primitive surface small while still growing a real standard library. This card runs on the exact same in-browser WASM playground engine as the demos above (no rustc needed to try it live); the compiled path's arithmetic is exact because the emitted Rust program carries its own hand-rolled limb-based BigInt/Rational/Complex implementation rather than depending on a crate (compiled programs are single-file rustc invocations with no Cargo.toml), and a cross-implementation test in the Rust test suite verifies that hand-rolled bignum arithmetic is byte-identical, operation for operation, to the interpreter's own num-bigint-crate-based arithmetic — two independent implementations kept honest against each other.
PatLang source
# Stage 39 -- math library, built entirely in terms of the Host:: primitives
# added in rust-runtime/src/ir/hosts.rs (sqrt/pow/floor/ceil/round/trunc/abs/
# numeric_kind), so this file is plain PatLang, not new host functions. Include
# it via `include "lib/math.patlang"` (path relative to the including file);
# it is pulled in as a normal whole-chunk inclusion, not symbol-level shaking
# (that finer-grained mechanism is Stage 37 Part C, explicitly out of scope
# for this stage).
make a function called hypot takes a, b returns h
return sqrt(a * a + b * b)
end
# Iterative (not recursive) so it stays O(1) stack regardless of n -- and,
# per the doc, a good smoke test for bignum promotion via repeated
# multiplication on a large n.
make a function called factorial takes n returns result
let acc = 1
let i = 2
while i <= n do
let acc = acc * i
let i = i + 1
end
return acc
end
make a function called gcd takes a, b returns g
let a = abs(a)
let b = abs(b)
while b != 0 do
let t = b
let b = a % b
let a = t
end
return a
end
make a function called lcm takes a, b returns l
if a == 0 then
if b == 0 then
return 0
end
end
let g = gcd(a, b)
return abs(a * b) / g
end
make a function called clamp takes x, lo, hi returns v
if x < lo then
return lo
end
if x > hi then
return hi
end
return x
end
# sign(x): -1/0/1 for real x, via comparisons rather than re-deriving sign
# logic against float bit patterns; complex inputs are not orderable, so
# numeric_kind is used to reject them with a clear error rather than a
# nonsensical comparison result.
make a function called sign takes x returns s
if numeric_kind(x) == "complex" then
print("sign: complex input is not supported")
return 0
end
if x > 0 then
return 1
end
if x < 0 then
return -1
end
return 0
end
# is_prime(n): trial division up to sqrt(n) -- exercises bignum via % for
# large n per the doc.
make a function called is_prime takes n returns result
if n < 2 then
return false
end
if n == 2 then
return true
end
if n % 2 == 0 then
return false
end
let limit = floor(sqrt(n))
let i = 3
while i <= limit do
if n % i == 0 then
return false
end
let i = i + 2
end
return true
end
make a function called sum takes xs returns total
let total = 0
let i = 0
while i < xs.length do
let total = total + xs[i]
let i = i + 1
end
return total
end
make a function called mean takes xs returns m
if xs.length == 0 then
return 0
end
return sum(xs) / xs.length
end
# Numeric tower demo (Stages 36-39): a single narrative walking small-int
# arithmetic up through every rung of the tower -- fast-path Int, silent
# BigInt promotion on overflow, exact Rational on inexact division, Complex
# from sqrt of a negative number -- then closing with a couple of
# self_hosting/lib/math.patlang library calls to show the hybrid
# primitives+library design (sqrt/floor/abs/numeric_kind are Host::
# primitives; hypot/is_prime/mean are plain PatLang built on top of them).
#
# Concatenate after lib/math.patlang when compiling (matches the
# pos_demo.patlang/report_demo.patlang "concatenate after lib/X" convention
# used by self_hosting/tools/build_portfolio.patlang, and required because
# the self-hosted parser has no `include` support of its own -- unlike the
# Rust CLI's preprocess.rs, which is why math_compare_demo.patlang/
# math_lib_demo.patlang can get away with an `include "../lib/math.patlang"`
# line when run standalone via `pat --ir-run`/`--compare`). To run this file
# standalone the same way, prepend self_hosting/lib/math.patlang's contents
# yourself (e.g. `cat lib/math.patlang examples/numeric_tower_demo.patlang`).
# --- 1. Ordinary small-integer arithmetic: fast Int path, no promotion ---
let a = 6
let b = 7
print("6 * 7 = " + (a * b))
# --- 2. Overflow silently promotes to exact BigInt ---
# 20! is far beyond i64::MAX (~9.2e18); the tower detects the i64 overflow
# inside `*` and promotes both operands to BigInt automatically -- no
# annotation, no error, just an exact (not approximated) answer.
print("factorial(20) = " + factorial(20))
print("factorial(30) = " + factorial(30))
# --- 3. Inexact integer division produces an exact Rational, not a float ---
# 10 / 3 does not divide evenly, so instead of silently losing precision to
# a float, the tower promotes to an exact Rational and prints it as a
# reduced fraction "a/b".
print("10 / 3 = " + (10 / 3))
print("22 / 7 = " + (22 / 7))
# Division that DOES divide evenly stays a plain Int, for contrast:
print("12 / 4 = " + (12 / 4))
# --- 4. sqrt of a negative number produces a Complex value ---
print("sqrt(16) = " + sqrt(16))
print("sqrt(-4) = " + sqrt(-4))
print("numeric_kind(sqrt(-4)) = " + numeric_kind(sqrt(-4)))
# --- 5. Hybrid design in action: math.patlang library calls built on top of
# the Host:: primitives ---
print("hypot(3, 4) = " + hypot(3, 4))
print("is_prime(999983) = " + is_prime(999983))
print("mean([1, 2, 3, 4, 5]) = " + mean([1, 2, 3, 4, 5]))
(not run yet)
Native run on the build machine:
6 * 7 = 42 factorial(20) = 2432902008176640000 factorial(30) = 265252859812191058636308480000000 10 / 3 = 10/3 22 / 7 = 22/7 12 / 4 = 3 sqrt(16) = 4 sqrt(-4) = 0+2i numeric_kind(sqrt(-4)) = complex hypot(3, 4) = 5 is_prime(999983) = true mean([1, 2, 3, 4, 5]) = 3