Numeric Tower: BigInt, Rational, Complex

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