"""Per-track steady-state summary (docs/rules/db-sql-schema.md section 5). Both engines must produce identical values: SQLite calls this function during load (task 05); the PostgreSQL materialized view (task 06) implements the same three steps in SQL. Plain sequential float arithmetic keeps results within the 1e-9 checksum tolerance across implementations. """ from __future__ import annotations import math TAIL_START_CYCLE = 501 # steady-state proxy window: cycles 501..N def summarize_track(cofs: list[float]) -> tuple[float, float, int]: """Return (cof_ss_mean, cof_ss_std, run_in_cycles) for cofs[i] = cof at cycle i+1.""" n = len(cofs) tail = cofs[TAIL_START_CYCLE - 1:] m = _mean(tail) s = _stddev_pop(tail, m) run_in = TAIL_START_CYCLE - 1 # fallback when no cycle enters the 2-sigma band band = 2.0 * s for c in range(1, n + 1): if abs(cofs[c - 1] - m) < band: run_in = c break ss = cofs[run_in:] # cycles strictly greater than run_in ss_mean = _mean(ss) ss_std = _stddev_pop(ss, ss_mean) return ss_mean, ss_std, run_in def _mean(values: list[float]) -> float: total = 0.0 for v in values: total += v return total / len(values) def _stddev_pop(values: list[float], mean: float) -> float: acc = 0.0 for v in values: d = v - mean acc += d * d return math.sqrt(acc / len(values))