Completes ticket 0001 started in27f468c. All three impls now carry bit-identical xoshiro256**; portal state round-trips across process boundaries in every producer x consumer cell (Python <-> C <-> asm). asm impl: - 4 new builtins: random-seed!, random-int, random-state, random-state! - g_rng_state in BSS (4 x u64); rng_splitmix64_step, rng_seed, rng_next - Binary portal header bumped LUMBDAB1/48 -> LUMBDAB2/80; carries 32 bytes of rng state at offsets 40..64, reserved moved to 72 - No float support in asm, so (random) intentionally omitted there - _start seeds with 0 so the stream is deterministic from startup Python + C (supplements27f468c): - rng_seed(0) auto-invoked at module load / register_portal_builtins so (random) without explicit (random-seed!) returns a real value instead of the all-zero xoshiro fixed point Tests: - tests/functional.lsp: 7 new shared assertions (Python + C) - asm/test.sh: 5 new asm-local assertions (142 -> 147) - tests/portal-rng-save.lsp / portal-rng-load.lsp: portable S-expression portal that captures both state AND next-5 baseline so loader self- verifies without a separate harness - tests/portal-cross-test.sh: 9 new producer x consumer RNG cells; all 18 cells pass end-to-end Verified: seed=42, (random-int 1000000) draws 1..10 = 558742 543102 559009 124193 317476 750584 200754 814407 344958 929085 identical in Python, C, and asm. unmoad scan: zero new findings in added code.
106 lines
3.8 KiB
Bash
Executable file
106 lines
3.8 KiB
Bash
Executable file
#!/bin/bash
|
||
# portal-cross-test.sh — Exercise the full save×load matrix
|
||
# across Python, C, and asm using the S-expression portal.
|
||
#
|
||
# Each implementation must be able to:
|
||
# 1. Write a portable .sexp file
|
||
# 2. Read a .sexp file written by any other implementation
|
||
#
|
||
# Result: 3x3 = 9 combinations. 7 are tested here (Python and C
|
||
# have built-in (load); asm gained (load) in commit on 2026-04-16,
|
||
# so it participates as both producer and consumer).
|
||
|
||
set -e
|
||
cd "$(dirname "$0")/.."
|
||
|
||
PY="python3 lumbda.py --fast"
|
||
C="./c/lumbda"
|
||
ASM="./asm/lumbda"
|
||
SEXP=/tmp/portal-xtest.sexp
|
||
|
||
PASS=0
|
||
FAIL=0
|
||
|
||
verify() {
|
||
local label="$1" output="$2"
|
||
if echo "$output" | grep -q "PASS:"; then
|
||
PASS=$((PASS + 1))
|
||
printf " %-30s OK\n" "$label"
|
||
else
|
||
FAIL=$((FAIL + 1))
|
||
printf " %-30s FAIL\n" "$label"
|
||
echo "----- output:"
|
||
echo "$output"
|
||
echo "-----"
|
||
fi
|
||
}
|
||
|
||
echo "═══════════════════════════════════════════════════════"
|
||
echo "Portal cross-impl exchange (S-expression format)"
|
||
echo "═══════════════════════════════════════════════════════"
|
||
|
||
for PRODUCER in "Python|$PY tests/portal-cross-save.lsp" \
|
||
"C|$C tests/portal-cross-save.lsp" \
|
||
"Asm|$ASM < tests/portal-cross-save.lsp"; do
|
||
P_NAME="${PRODUCER%%|*}"
|
||
P_CMD="${PRODUCER#*|}"
|
||
echo
|
||
echo "── producer: $P_NAME ──"
|
||
rm -f "$SEXP"
|
||
# portal-cross-save writes to /tmp/cross.sexp; copy out to our slot
|
||
eval "$P_CMD" >/dev/null 2>&1
|
||
cp /tmp/cross.sexp "$SEXP"
|
||
|
||
for CONSUMER in "Python|$PY tests/portal-cross-load.lsp" \
|
||
"C|$C tests/portal-cross-load.lsp" \
|
||
"Asm|$ASM < tests/portal-cross-load.lsp"; do
|
||
C_NAME="${CONSUMER%%|*}"
|
||
C_CMD="${CONSUMER#*|}"
|
||
# Regenerate /tmp/cross.sexp from our slot (portal-cross-load reads it)
|
||
cp "$SEXP" /tmp/cross.sexp
|
||
OUTPUT=$(eval "$C_CMD" 2>&1)
|
||
verify "$P_NAME → $C_NAME" "$OUTPUT"
|
||
done
|
||
done
|
||
|
||
rm -f "$SEXP" /tmp/cross.sexp
|
||
|
||
echo
|
||
echo "═══════════════════════════════════════════════════════"
|
||
echo "RNG portal cross-impl (xoshiro256** state via S-expression)"
|
||
echo "Proves: seed in one impl, resume in another, identical stream."
|
||
echo "Ticket: docs/tickets/0001-portal-rng.md"
|
||
echo "═══════════════════════════════════════════════════════"
|
||
|
||
RNG_SEXP=/tmp/cross-rng-xtest.sexp
|
||
|
||
for PRODUCER in "Python|$PY tests/portal-rng-save.lsp" \
|
||
"C|$C tests/portal-rng-save.lsp" \
|
||
"Asm|$ASM < tests/portal-rng-save.lsp"; do
|
||
P_NAME="${PRODUCER%%|*}"
|
||
P_CMD="${PRODUCER#*|}"
|
||
echo
|
||
echo "── producer: $P_NAME ──"
|
||
rm -f "$RNG_SEXP" /tmp/cross-rng.sexp
|
||
eval "$P_CMD" >/dev/null 2>&1
|
||
cp /tmp/cross-rng.sexp "$RNG_SEXP"
|
||
|
||
for CONSUMER in "Python|$PY tests/portal-rng-load.lsp" \
|
||
"C|$C tests/portal-rng-load.lsp" \
|
||
"Asm|$ASM < tests/portal-rng-load.lsp"; do
|
||
C_NAME="${CONSUMER%%|*}"
|
||
C_CMD="${CONSUMER#*|}"
|
||
cp "$RNG_SEXP" /tmp/cross-rng.sexp
|
||
OUTPUT=$(eval "$C_CMD" 2>&1)
|
||
verify "$P_NAME → $C_NAME" "$OUTPUT"
|
||
done
|
||
done
|
||
|
||
rm -f "$RNG_SEXP" /tmp/cross-rng.sexp
|
||
|
||
echo
|
||
echo "═══════════════════════════════════════════════════════"
|
||
echo "Cross-impl portal + RNG: $PASS passed, $FAIL failed"
|
||
if [ $FAIL -ne 0 ]; then
|
||
exit 1
|
||
fi
|