"""Shared helpers for the reduced-round SHA-256 experiments (Axis C of WP34). Nothing here modifies the reference implementation `wp34.sha256`; it is imported as ground truth. """ from __future__ import annotations import hashlib, os, pathlib, resource, struct, sys, time ROOT = pathlib.Path(__file__).resolve().parents[2] SRC = ROOT / "src" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) from wp34 import sha256 as S # noqa: E402 from wp34 import provenance # noqa: E402 MASK = 0xFFFFFFFF def peak_rss_mb(children: bool = False) -> float: """Peak resident set size in MiB (macOS reports bytes, Linux kilobytes).""" who = resource.RUSAGE_CHILDREN if children else resource.RUSAGE_SELF v = resource.getrusage(who).ru_maxrss if sys.platform == "darwin": return round(v / 2**20, 1) return round(v / 2**10, 1) def random_block(rng) -> bytes: return bytes(rng.getrandbits(8) for _ in range(64)) def state_to_int(state) -> int: v = 0 for w in state: v = (v << 32) | (w & MASK) return v def int_to_bits(v: int, n: int) -> list[int]: """Bit list, index 0 = most significant bit of the n-bit integer (so index 0 = bit 31 of word a).""" return [(v >> (n - 1 - i)) & 1 for i in range(n)] def random_oracle(salt: bytes, msg: bytes) -> int: """256-bit 'random oracle' negative control: SHA-256 (hashlib) over a secret salt and the input.""" return int.from_bytes(hashlib.sha256(salt + msg).digest(), "big") def hamming(v: int) -> int: return bin(v).count("1") def words_hex(words) -> str: return "".join("%08x" % (w & MASK) for w in words) def block_hex(block: bytes) -> str: return block.hex() def bootstrap_ci(values, rng, n_boot: int = 2000, alpha: float = 0.05): """Bootstrap percentile CI of the mean (floats are fine: statistics only).""" import statistics vals = list(values) if len(vals) < 2: return [None, None] means = [] n = len(vals) for _ in range(n_boot): means.append(sum(vals[rng.randrange(n)] for _ in range(n)) / n) means.sort() lo = means[int(alpha / 2 * n_boot)] hi = means[min(n_boot - 1, int((1 - alpha / 2) * n_boot))] return [lo, hi] def mean_sd(values): vals = list(values) if not vals: return None, None m = sum(vals) / len(vals) if len(vals) < 2: return m, 0.0 var = sum((x - m) ** 2 for x in vals) / (len(vals) - 1) return m, var ** 0.5