parity probe: cross-tier corpus + fix python remainder + asm modulo

New: wasm/tests/parity-cross-tier.mjs runs the parity-corpus.mjs (216
test cases tagged by whitepaper section / R7RS concept) against three
tiers — native python (reference), c-wasm, asm-wasm — and fails on any
unknown divergence. Known gaps live in KNOWN_DIVERGE so the table stays
green while the bignum / call/cc / etc. work proceeds.

Wired into `make wasm-test` so a regression against any spec claim gets
caught before merge.

Bugs caught and fixed:
  - python remainder: was `signed_a % signed_b * sign(a)`, which double-
    applied the sign of a (python's % floors) — gave -3 for (-17, 5)
    instead of the R7RS-correct -2. Now uses abs() on both sides.
  - asm-wasm modulo: was i32.rem_s (truncated, remainder semantics)
    where R7RS modulo wants sign of divisor. Added the "if rem and
    divisor disagree on sign, add divisor" branch.

Cross-tier numbers after fix:
  216 passing
    3 known diverge: expt-2-100, expt-3-50, big-arith — all asm-wasm
      (no bignums on the asm tier yet; whitepaper §2.1 claim still open)
    0 fail

REPL layout: body is now the scroll container, prompt-bar is
position:fixed at the viewport bottom so it doesn't get pushed off
screen by a long transcript. Empty space above the prompt on a fresh
session reads like a terminal.

All other tests still pass: 20 unit, 8 integration, 11 functional.
This commit is contained in:
russell@unturf.com 2026-06-14 15:21:19 -04:00
parent c50a9da7e8
commit 7ea65dba21
No known key found for this signature in database
17 changed files with 357 additions and 38 deletions

View file

@ -3438,7 +3438,11 @@ def make_global_env():
return n
d(S('/'), _div)
d(S('quotient'), lambda a, _: (lambda x, y: -(abs(int(x)) // abs(int(y))) if (x < 0) != (y < 0) else abs(int(x)) // abs(int(y)))(_num(a[0]), _num(a[1])))
d(S('remainder'), lambda a, _: int(_num(a[0])) % int(_num(a[1])) * (1 if _num(a[0]) >= 0 else -1))
# R7RS: remainder has the sign of the dividend; uses truncated division.
# The previous impl did `signed_a % signed_b * sign(a)` which double-counted
# the sign of a (Python's % floors, so `-17 % 5 == 3`) and gave -3 for
# (-17, 5) instead of the correct -2. Use abs() on both sides, then re-sign.
d(S('remainder'), lambda a, _: (abs(int(_num(a[0]))) % abs(int(_num(a[1])))) * (1 if _num(a[0]) >= 0 else -1))
d(S('modulo'), lambda a, _: int(_num(a[0])) % int(_num(a[1])))
d(S('expt'), lambda a, _: _num(a[0]) ** _num(a[1]))
d(S('abs'), lambda a, _: abs(_num(a[0])))