#!/usr/bin/env bash
# Erlang-on-SX ring benchmark.
#
# Spawns N processes in a ring, passes a token N hops (one full round),
# and reports wall-clock time + throughput. Aspirational target from
# the plan is 1M processes; current sync-scheduler architecture caps out
# orders of magnitude lower — this script measures honestly across a
# range of N so the result/scaling is recorded.
#
# Usage:
#   bash lib/erlang/bench_ring.sh                # default ladder
#   bash lib/erlang/bench_ring.sh 100 1000 5000  # custom Ns

set -uo pipefail
cd "$(git rev-parse --show-toplevel)"

SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
if [ ! -x "$SX_SERVER" ]; then
  SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
fi
if [ ! -x "$SX_SERVER" ]; then
  echo "ERROR: sx_server.exe not found." >&2
  exit 1
fi

if [ "$#" -gt 0 ]; then
  NS=("$@")
else
  NS=(10 100 500 1000)
fi

TMPFILE=$(mktemp)
trap "rm -f $TMPFILE" EXIT

# One-line Erlang program. Replaces __N__ with the size for each run.
PROGRAM='Me = self(), N = __N__, Spawner = fun () -> receive {setup, Next} -> Loop = fun () -> receive {token, 0, Parent} -> Parent ! done; {token, K, Parent} -> Next ! {token, K-1, Parent}, Loop() end end, Loop() end end, BuildRing = fun (K, Acc) -> if K =:= 0 -> Acc; true -> BuildRing(K-1, [spawn(Spawner) | Acc]) end end, Pids = BuildRing(N, []), Wire = fun (Ps) -> case Ps of [P, Q | _] -> P ! {setup, Q}, Wire(tl(Ps)); [Last] -> Last ! {setup, hd(Pids)} end end, Wire(Pids), hd(Pids) ! {token, N, Me}, receive done -> done end'

run_n() {
  local n="$1"
  local prog="${PROGRAM//__N__/$n}"
  cat > "$TMPFILE" <<EPOCHS
(epoch 1)
(load "lib/erlang/tokenizer.sx")
(load "lib/erlang/parser.sx")
(load "lib/erlang/parser-core.sx")
(load "lib/erlang/parser-expr.sx")
(load "lib/erlang/parser-module.sx")
(load "lib/erlang/transpile.sx")
(load "lib/erlang/runtime.sx")
(epoch 2)
(eval "(erlang-eval-ast \"${prog//\"/\\\"}\")")
EPOCHS

  local start_s start_ns end_s end_ns elapsed_ms
  start_s=$(date +%s)
  start_ns=$(date +%N)
  out=$(timeout 300 "$SX_SERVER" < "$TMPFILE" 2>&1)
  end_s=$(date +%s)
  end_ns=$(date +%N)

  local ok="false"
  if echo "$out" | grep -q ':name "done"'; then ok="true"; fi

  # ms = (end_s - start_s)*1000 + (end_ns - start_ns)/1e6
  elapsed_ms=$(awk -v s1="$start_s" -v n1="$start_ns" -v s2="$end_s" -v n2="$end_ns" \
    'BEGIN { printf "%d", (s2 - s1) * 1000 + (n2 - n1) / 1000000 }')

  if [ "$ok" = "true" ]; then
    local hops_per_s
    hops_per_s=$(awk -v n="$n" -v ms="$elapsed_ms" \
      'BEGIN { if (ms == 0) ms = 1; printf "%.0f", n * 1000 / ms }')
    printf "  N=%-8s  hops=%-8s  %sms  (%s hops/s)\n" "$n" "$n" "$elapsed_ms" "$hops_per_s"
  else
    printf "  N=%-8s  FAILED  %sms\n" "$n" "$elapsed_ms"
  fi
}

echo "Ring benchmark — sx_server.exe (synchronous scheduler)"
echo
for n in "${NS[@]}"; do
  run_n "$n"
done
echo
echo "Note: 1M-process target from the plan is aspirational; the synchronous"
echo "scheduler with shift-based suspension and dict-based env copies is not"
echo "engineered for that scale. Numbers above are honest baselines."
