#!/bin/bash
# portal-rng-cross-test.sh — ticket 0001 deliverable (goal 4):
#
#   "seed k, draw N values, save, clear, resume in any impl, draw M more —
#    full stream matches a single-process Python baseline."
#
# This script proves that every (producer, consumer) pair across
# Python / C / asm reproduces the exact same N+M stream that a single
# Python process produces with no save/resume at all. That is the
# stronger claim than portal-cross-test.sh's producer-side self-
# consistency: here the truth is computed independently, once, upfront,
# by a Python run that never touches the portal. Every cell compares
# against the same independent baseline.
#
# Matrix: 3 producers × 3 consumers = 9 cells. K=42, N=5, M=5.

set -e
cd "$(dirname "$0")/.."

PY="python3 lumbda.py --fast"
C="./c/lumbda"
ASM="./asm/lumbda"

K=42          # seed
N=5           # draws before save
M=5           # draws after resume
RANGE=1000000 # random-int upper bound

# ─── Compute the independent single-process Python baseline (N+M draws) ───
BASELINE_LSP=$(mktemp --suffix=.lsp)
trap 'rm -f "$BASELINE_LSP" /tmp/rng-xstream.sexp' EXIT INT TERM

cat > "$BASELINE_LSP" <<EOF
(random-seed! $K)
;; for-each returns a non-printing value across Python/C/asm, so no
;; trailing token leaks into stdout. Iteration count is carried by the
;; length of the dummy list below (shell-expanded once).
(for-each (lambda (_) (display (random-int $RANGE)) (newline))
          '($(seq 1 $((N + M)) | tr '\n' ' ')))
EOF

# shellcheck disable=SC2086
BASELINE=$($PY "$BASELINE_LSP")
BASELINE_COUNT=$(printf "%s\n" "$BASELINE" | wc -l)

if [ "$BASELINE_COUNT" -ne $((N + M)) ]; then
    echo "FAIL: baseline produced $BASELINE_COUNT draws, expected $((N + M))"
    exit 1
fi

# Split baseline into first-N and next-M.
BASELINE_N=$(printf "%s\n" "$BASELINE" | head -n $N)
BASELINE_M=$(printf "%s\n" "$BASELINE" | tail -n $M)

echo "═══════════════════════════════════════════════════════"
echo "RNG portal cross-impl round-trip — independent Python baseline"
echo "Ticket: docs/tickets/0001-portal-rng.md (goal 4)"
echo "  seed=$K, save after N=$N draws, resume for M=$M draws"
echo "═══════════════════════════════════════════════════════"
echo
echo "single-process Python baseline (N+M=$((N + M)) draws):"
printf "%s\n" "$BASELINE" | sed 's/^/  /'
echo

# ─── Producer script (generated): seed, draw N, emit them to stdout, save state ───
PROD_LSP=$(mktemp --suffix=.lsp)
cat > "$PROD_LSP" <<EOF
(random-seed! $K)
(for-each (lambda (_) (display (random-int $RANGE)) (newline))
          '($(seq 1 $N | tr '\n' ' ')))
(define snapshot (random-state))
(define port (open-output-file "/tmp/rng-xstream.sexp"))
(display "(random-state! '" port) (write snapshot port) (display ")\n" port)
(close-port port)
EOF

# ─── Consumer script (generated): load state, draw M, emit them to stdout ───
CONS_LSP=$(mktemp --suffix=.lsp)
cat > "$CONS_LSP" <<EOF
(load "/tmp/rng-xstream.sexp")
(for-each (lambda (_) (display (random-int $RANGE)) (newline))
          '($(seq 1 $M | tr '\n' ' ')))
EOF

# shellcheck disable=SC2064
trap "rm -f '$BASELINE_LSP' '$PROD_LSP' '$CONS_LSP' /tmp/rng-xstream.sexp" EXIT INT TERM

PASS=0
FAIL=0

run_producer() {
    local impl="$1"
    case "$impl" in
        Python) $PY "$PROD_LSP" ;;
        C)      $C "$PROD_LSP"  ;;
        Asm)    $ASM < "$PROD_LSP" ;;
    esac
}

run_consumer() {
    local impl="$1"
    case "$impl" in
        Python) $PY "$CONS_LSP" ;;
        C)      $C "$CONS_LSP"  ;;
        Asm)    $ASM < "$CONS_LSP" ;;
    esac
}

verify_stream() {
    local label="$1" expected="$2" actual="$3"
    if [ "$expected" = "$actual" ]; then
        PASS=$((PASS + 1))
        printf "  %-22s OK\n" "$label"
    else
        FAIL=$((FAIL + 1))
        printf "  %-22s FAIL\n" "$label"
        echo "    expected:"
        printf "%s\n" "$expected" | sed 's/^/      /'
        echo "    actual:"
        printf "%s\n" "$actual" | sed 's/^/      /'
    fi
}

for P_NAME in Python C Asm; do
    echo "── producer: $P_NAME ──"
    rm -f /tmp/rng-xstream.sexp
    PROD_OUT=$(run_producer "$P_NAME")

    # Producer's first N must equal the Python baseline's first N.
    verify_stream "$P_NAME: first $N draws" "$BASELINE_N" "$PROD_OUT"

    for C_NAME in Python C Asm; do
        CONS_OUT=$(run_consumer "$C_NAME")
        # Consumer's M draws after resume must equal the Python baseline's last M.
        verify_stream "$P_NAME -> $C_NAME (next $M)" "$BASELINE_M" "$CONS_OUT"
    done
done

echo
echo "═══════════════════════════════════════════════════════"
echo "Cross-impl RNG round-trip vs Python baseline: $PASS passed, $FAIL failed"
echo "═══════════════════════════════════════════════════════"

if [ $FAIL -ne 0 ]; then
    exit 1
fi
