"""The random streams every simulation uses, specified so a browser can replay them.

Each (seed, chunk) pair gets its own xoshiro128** stream, seeded through
splitmix32 from seed XOR chunk*0x9E3779B1. Only 32-bit integer arithmetic is
used, and `below(n)` draws by rejection (no modulo bias), so site_src/sims.js
reproduces every draw bit for bit. Change nothing here without changing
sims.js the same way: published chunks depend on it.
"""

from __future__ import annotations

M = 0xFFFFFFFF
TWO32 = 4294967296


class Stream:
    __slots__ = ("a", "b", "c", "d")

    def __init__(self, seed: int, chunk: int):
        sm = (seed ^ ((chunk * 0x9E3779B1) & M)) & M
        st = []
        for _ in range(4):
            sm = (sm + 0x9E3779B9) & M
            z = sm
            z = ((z ^ (z >> 16)) * 0x85EBCA6B) & M
            z = ((z ^ (z >> 13)) * 0xC2B2AE35) & M
            z ^= z >> 16
            st.append(z)
        if not any(st):
            st[0] = 1
        self.a, self.b, self.c, self.d = st

    def below(self, n: int) -> int:
        """A uniform integer in [0, n)."""
        lim = TWO32 - TWO32 % n
        s0, s1, s2, s3 = self.a, self.b, self.c, self.d
        while True:
            x = (s1 * 5) & M
            r = ((((x << 7) | (x >> 25)) & M) * 9) & M
            t = (s1 << 9) & M
            s2 ^= s0; s3 ^= s1; s1 ^= s2; s0 ^= s3; s2 ^= t
            s3 = ((s3 << 11) | (s3 >> 21)) & M
            if r < lim:
                break
        self.a, self.b, self.c, self.d = s0, s1, s2, s3
        return r % n


def page_seed(slug: str) -> int:
    import hashlib
    return int(hashlib.sha256(slug.encode()).hexdigest()[:8], 16)
