c-tier bignum — arbitrary-precision integers unblock secp256k1 widths

Adds tagged bignum support alongside the existing 48-bit fixnum on the C
tier. Tag 6 = bignum, heap struct sign-magnitude with u64 little-endian
limbs. Reader emits bignums for any literal past the fixnum range; +, -,
*, quotient, remainder, modulo, expt, =, <, >, abs, odd?, even?,
integer?, exact?, number->string, string->number all promote fixnum →
bignum on overflow & demote back when results fit. Boehm GC owns every
allocation. Schoolbook O(n²) mul + shift-subtract divmod is sufficient
at our 4-limb / 256-bit scale.

Before: (expt 2 48) = 0, (expt 2 256) = 0, secp256k1-p = -4294968273.
After: all three return their exact arbitrary-precision values, matching
Python tier byte-for-byte.

Validated:
- c/test.c — 85/85 pass (+2 new bignum unit tests).
- tests/functional.lsp — 205/205 pass on both C & Python tiers.
- tests/bignum-cross-tier.lsp — 33/33 pass byte-identical on both tiers
  (diff produces no output).
- ecdsa/runs/lumbda-sweep-003/c-tier-bignum-probe.lsp — all four
  assertions now match the Python oracle.
- ecdsa Phase B byte-identity sweep inside QEMU guest:
  n+1=9  p=251           sha256 c668bbe3... — matches Python oracle.
  n+1=18 p=131071        sha256 8a031f96... — matches Python oracle.
  n+1=33 p=2³²-5         sha256 0bc56905... — matches Python oracle.
  Previously the n+1=33 C tier emitted sha256 b024d6d9... (26,078 fewer
  Toffolis due to silent fixnum wrap). Bignums close that gate.

secp256k1 production-width emit (n+1=257) is now structurally unblocked
on C tier; downstream agent (#55) drives that next-step on the ecdsa
side. Asm tier inherits in a follow-up port.
This commit is contained in:
russell@unturf.com 2026-06-06 20:23:37 -04:00
parent cf68c0da15
commit 2f342c3be2
No known key found for this signature in database
11 changed files with 918 additions and 29 deletions

View file

@ -480,7 +480,7 @@ static bool is_literal(SyntaxTransformer *st, const char *name) {
static bool sr_match(SyntaxTransformer *st, Value pat, Value form, Env *bindings) {
if (IS_NIL(pat)) return IS_NIL(form);
if (pat == VAL_TRUE || pat == VAL_FALSE) return pat == form;
if (IS_INT(pat) || IS_DOUBLE(pat)) return values_equal(pat, form);
if (IS_INT(pat) || IS_DOUBLE(pat) || IS_BIGNUM(pat)) return values_equal(pat, form);
if (IS_SYM(pat)) {
const char *pname = sym_name(pat);
@ -573,7 +573,7 @@ static bool sr_match(SyntaxTransformer *st, Value pat, Value form, Env *bindings
static Value sr_expand(SyntaxTransformer *st, Value tmpl, Env *bindings) {
if (IS_NIL(tmpl) || tmpl == VAL_TRUE || tmpl == VAL_FALSE) return tmpl;
if (IS_INT(tmpl) || IS_DOUBLE(tmpl)) return tmpl;
if (IS_INT(tmpl) || IS_DOUBLE(tmpl) || IS_BIGNUM(tmpl)) return tmpl;
if (IS_STRING(tmpl)) return tmpl;
if (IS_SYM(tmpl)) {
@ -669,7 +669,7 @@ Value leval(Value expr, Env *env) {
/* Self-evaluating */
if (IS_NIL(expr) || IS_VOID(expr) || IS_TRUE(expr) || IS_FALSE(expr) ||
IS_EOF(expr) || IS_CHAR(expr)) return expr;
if (IS_INT(expr) || IS_DOUBLE(expr) || IS_RATIONAL(expr)) return expr;
if (IS_INT(expr) || IS_DOUBLE(expr) || IS_RATIONAL(expr) || IS_BIGNUM(expr)) return expr;
if (IS_STRING(expr)) return expr;
if (IS_VECTOR(expr)) return expr;
if (IS_BUILTIN(expr)) return expr;