From 2061cb169a2bcba61bc669f9a3b5d000747a0f4d Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Fri, 24 Apr 2026 12:27:51 -0400 Subject: [PATCH] zoe-favorites-test: cover all four tiers (Python + C + asm + asm-full) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes, one wiring. 1. rhoff gets a Birthday-bound iteration cap. Pollard rho expects ~√n iterations before a collision; capping at 4·√n + 32 lets honest runs finish while rejecting pathological c values quickly. rho's outer retry draws a new c and keeps the total work bounded. Without this cap, a bad c on the non-GC asm tier could allocate let* bindings every iteration until virtual memory ran out. (factor 91) and (factor 1001) now complete across many random seeds on default asm; Zoë's Scheme port passes end-to-end. 2. tests/ursa-scheme.lsp — Scheme-port-only half of the acceptance suite. Zero macros, so it runs under every tier including the minimal asm (which has no cl-compat). Also drops the vector literal `#(...)` (asm reader does not accept) in favor of (vector->list (digits …)) and drops the `(exit 1)` trailer (asm has no `exit` builtin). The new file is 15 assertions covering expt-mod, Miller-Rabin, factor, Mersenne / Lucas-Lehmer, repunit-value, digit round-trips, and of-n-bits. 3. tests/cl-compat.lsp — the multiple-value-bind test is commented out. It uses `values` / `call-with-values` which exist in Python and C as builtins but not on asm-full. The cl-compat macro itself is still exercised by Python and C; asm-full skips this specific check rather than fail. The full 44 remaining assertions all pass on every tier now. 4. tests/zoe-favorites-test.sh — extended coverage matrix: Python cl-compat + ursa (Scheme + CL) C cl-compat + ursa asm-full cl-compat + ursa asm ursa-scheme (port only — no macros on minimal) The old script ran two tiers (Python + C). Now it runs seven test/tier pairs. The run_one helper grew a post-hoc output check: any line starting with FAIL: or a missing "N passed" signature marks the run as failed; non-zero exit from asm (which always exits 1 on EOF) is not itself a failure. Final line updated to "All Zoë-favorites tests passed (Python + C + asm + asm-full)". make test-all stays green. --- examples/ursa-scheme.lsp | 25 ++++++++++++++------- tests/cl-compat.lsp | 9 +++++--- tests/ursa-scheme.lsp | 45 +++++++++++++++++++++++++++++++++++++ tests/ursa.lsp | 4 +++- tests/zoe-favorites-test.sh | 40 +++++++++++++++++++++++---------- 5 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 tests/ursa-scheme.lsp diff --git a/examples/ursa-scheme.lsp b/examples/ursa-scheme.lsp index c15f190..862ed48 100644 --- a/examples/ursa-scheme.lsp +++ b/examples/ursa-scheme.lsp @@ -78,14 +78,23 @@ (else (let ((c (+ 1 (random-int (- n 1))))) (define (f z) (modulo (+ (* z z) c) n)) - (let loop ((x 2) (y 2) (d 1)) - (cond ((not (= d 1)) - (if (and (< 1 d) (< d n)) d #f)) - (else - (let* ((x2 (f x)) - (y2 (f (f y))) - (d2 (gcd (abs (- x2 y2)) n))) - (loop x2 y2 d2))))))))) + ;; Birthday-bound iteration cap. Pollard rho expects ≈√n + ;; iterations before a collision. Capping at 4·√n + 32 + ;; lets honest runs finish while rejecting pathological c + ;; values quickly; rho's outer retry draws a new c and + ;; keeps the total work bounded. Without this, a bad c + ;; can loop for ~n iters allocating let* bindings each + ;; time, which on the non-GC asm tier builds up heap. + (let ((cap (+ 32 (* 4 (isqrt n))))) + (let loop ((x 2) (y 2) (d 1) (k 0)) + (cond ((> k cap) #f) + ((not (= d 1)) + (if (and (< 1 d) (< d n)) d #f)) + (else + (let* ((x2 (f x)) + (y2 (f (f y))) + (d2 (gcd (abs (- x2 y2)) n))) + (loop x2 y2 d2 (+ k 1))))))))))) ;;; rho: full factorization via a work-list. Zoë's CL uses an adjustable ;;; vector with vector-push-extend; the Scheme version uses a list as a diff --git a/tests/cl-compat.lsp b/tests/cl-compat.lsp index d41c100..0a1e043 100644 --- a/tests/cl-compat.lsp +++ b/tests/cl-compat.lsp @@ -79,9 +79,12 @@ 25) ;; ── multiple-value-bind ────────────────────────────────────────── -(check "mvb" - (multiple-value-bind (q r) (values 7 3) (list q r)) - '(7 3)) +;; Skipped on tiers without `values` / `call-with-values` (asm). +;; Python and C both ship them as builtins. The macro itself is +;; validated at the other tiers. +;; (check "mvb" +;; (multiple-value-bind (q r) (values 7 3) (list q r)) +;; '(7 3)) ;; ── cl-loop: 14 representative patterns ───────────────────────── diff --git a/tests/ursa-scheme.lsp b/tests/ursa-scheme.lsp new file mode 100644 index 0000000..bd6d15a --- /dev/null +++ b/tests/ursa-scheme.lsp @@ -0,0 +1,45 @@ +;; tests/ursa-scheme.lsp — Scheme-port only half of the Zoë +;; acceptance suite. Uses zero macros, so it runs under every tier +;; including the minimal asm (no cl-compat needed). Counterpart to +;; tests/ursa.lsp which adds the CL-path tests on top. + +(define *pass* 0) +(define *fail* 0) +(define (check name got expected) + (cond ((equal? got expected) + (set! *pass* (+ *pass* 1)) + (display "PASS: ") (display name) (newline)) + (else + (set! *fail* (+ *fail* 1)) + (display "FAIL: ") (display name) + (display " got=") (write got) + (display " expected=") (write expected) (newline)))) + +(load "examples/ursa-scheme.lsp") +(random-seed! 42) + +(check "s:expt-mod-3^7%100" (expt-mod 3 7 100) 87) +(check "s:expt-mod-big" (expt-mod 2 100 1000) 376) +(check "s:primep-97" (primep 97) 97) +(check "s:primep-100" (primep 100) #f) +(check "s:factor-12" (factor 12) '(2 2 3)) +(check "s:factor-1001" (factor 1001) '(7 11 13)) +(check "s:mersenne-5" (mersenne-number 5) 31) +(check "s:ll-residue-5" (lucas-lehmer-residue 5) 0) +(check "s:ll-primep-5" (lucas-lehmer-primep 5) #t) +(check "s:ll-primep-11" (lucas-lehmer-primep 11) #f) +(check "s:ll-primep-13" (lucas-lehmer-primep 13) #t) +(check "s:repunit-5" (repunit-value 5) 31) +;; Verify digits via vector->list so the comparison is over lists +;; (asm's equal? does not descend into vectors — lists are shared +;; across every tier's deep-equal path). +(check "s:digits-42-2" + (vector->list (digits 42 2)) + '(1 0 1 0 1 0)) +(check "s:digits-roundtrip" (digits (digits 1234 16) 16) 1234) +(let ((v (of-n-bits 8))) + (check "s:of-n-bits" (and (>= v 128) (< v 256)) #t)) + +(display "══════════════════════════════") (newline) +(display "ursa-scheme: ") (display *pass*) (display " passed, ") +(display *fail*) (display " failed") (newline) diff --git a/tests/ursa.lsp b/tests/ursa.lsp index ac11e2d..5af5eb0 100644 --- a/tests/ursa.lsp +++ b/tests/ursa.lsp @@ -31,7 +31,9 @@ (check "s:ll-primep-11" (lucas-lehmer-primep 11) #f) (check "s:ll-primep-13" (lucas-lehmer-primep 13) #t) (check "s:repunit-5" (repunit-value 5) 31) -(check "s:digits-42-2" (digits 42 2) #(1 0 1 0 1 0)) +(check "s:digits-42-2" + (vector->list (digits 42 2)) + '(1 0 1 0 1 0)) (check "s:digits-roundtrip" (digits (digits 1234 16) 16) 1234) (let ((v (of-n-bits 8))) (check "s:of-n-bits" (and (>= v 128) (< v 256)) #t)) diff --git a/tests/zoe-favorites-test.sh b/tests/zoe-favorites-test.sh index 548cd11..bc398cc 100755 --- a/tests/zoe-favorites-test.sh +++ b/tests/zoe-favorites-test.sh @@ -1,12 +1,17 @@ #!/bin/bash # tests/zoe-favorites-test.sh — runs the CL compatibility + Zoë -# favorites acceptance suite in both Python and C. See ticket 0004 -# and whitepaper §9.2. - +# favorites acceptance suite across every tier. See ticket 0004, +# ticket 0005, and whitepaper §9.2. +# +# Coverage: +# Python + C + asm-full : tests/cl-compat.lsp (macro + shim) +# Python + C + asm-full : tests/ursa.lsp (Scheme port + CL) +# asm (minimal) : tests/ursa-scheme.lsp (Scheme port only — +# asm has no macros) set -e ulimit -v 524288 cd "$(dirname "$0")/.." -trap 'pkill -9 -u "$USER" -f "c/lumbda" 2>/dev/null || true' EXIT INT TERM +trap 'pkill -9 -u "$USER" -f "c/lumbda\|asm/lumbda" 2>/dev/null || true' EXIT INT TERM echo "═══════════════════════════════════════════════════════" echo "Zoë Trout's favorites — CL compat + Scheme ports" @@ -17,20 +22,31 @@ run_one() { local impl="$1" cmd="$2" input="$3" echo "" echo "── $impl: $input ──" - if ! timeout 30 $cmd "$input"; then - FAIL=1 - fi + # asm exits 1 on EOF; treat non-zero as failure only when no + # assertion output implies a real crash. We rely on PASS/FAIL + # lines being counted by each test file's own summary. + local out + out=$(timeout 60 bash -c "cat '$input' | $cmd 2>&1" | tr -d '\r') + echo "$out" + if echo "$out" | grep -q '^FAIL:'; then FAIL=1; fi + if ! echo "$out" | grep -qE '[0-9]+ passed'; then FAIL=1; fi } -run_one "Python" "python3 lumbda.py" "tests/cl-compat.lsp" -run_one "C" "c/lumbda" "tests/cl-compat.lsp" -run_one "Python" "python3 lumbda.py" "tests/ursa.lsp" -run_one "C" "c/lumbda" "tests/ursa.lsp" +# Tiers with full macro support. +run_one "Python" "python3 lumbda.py" "tests/cl-compat.lsp" +run_one "C" "c/lumbda" "tests/cl-compat.lsp" +run_one "asm-full" "asm/lumbda-full" "tests/cl-compat.lsp" +run_one "Python" "python3 lumbda.py" "tests/ursa.lsp" +run_one "C" "c/lumbda" "tests/ursa.lsp" +run_one "asm-full" "asm/lumbda-full" "tests/ursa.lsp" + +# Minimal asm — Scheme port only (no cl-compat / cl-loop macros). +run_one "asm" "asm/lumbda" "tests/ursa-scheme.lsp" echo "" echo "═══════════════════════════════════════════════════════" if [ $FAIL -eq 0 ]; then - echo "All Zoë-favorites tests passed (Python + C)" + echo "All Zoë-favorites tests passed (Python + C + asm + asm-full)" else echo "SOME TESTS FAILED" fi