"""R1 — deterministic seed core, and cross-language parity with the Rust mirror.

The parity vectors below are asserted identically in
hh/src/sor/mod.rs::tests::parity_vector. If either language changes the
algorithm, one of the two test suites fails — that is the reproducibility
contract for `--sor-seed`.
"""

from cmd_chat.sor.config import Domain, SorRng, Stream, bringup, select_path


def test_splitmix64_known_answer():
    # Canonical SplitMix64 from initial state 0 (published first outputs).
    s = Stream(0)
    assert s.next_u64() == 0xE220A8397B1DCDAF
    assert s.next_u64() == 0x6E789E6AA1B965F4
    assert s.next_u64() == 0x06C45D188009454F


def test_same_seed_identical_bringup():
    # R1 acceptance check, primitive form.
    assert bringup(0xDEADBEEF, 8, 3, 5) == bringup(0xDEADBEEF, 8, 3, 5)


def test_different_seed_diverges():
    assert bringup(1, 12, 3, 4) != bringup(2, 12, 3, 4)


def test_domains_decorrelated():
    rng = SorRng(42)
    assert rng.stream(Domain.PATH).next_u64() != rng.stream(Domain.CHURN).next_u64()


def test_select_path_distinct_in_range():
    path = select_path(SorRng(7), 6, 3)
    assert len(path) == 3
    assert all(0 <= h < 6 for h in path)
    assert len(set(path)) == len(path)


def test_next_below_in_range():
    for seed in (0, 1, 42, 0xFFFFFFFF):
        s = SorRng(seed).stream(Domain.CHURN)
        for n in (1, 2, 3, 5, 8, 1000, 999983):
            for _ in range(32):
                assert 0 <= s.next_below(n) < n


def test_select_path_degenerate_hops_ge_pool():
    # hops >= pool returns a full deterministic permutation, no panic.
    path = select_path(SorRng(3), 4, 10)
    assert sorted(path) == [0, 1, 2, 3]


def test_parity_vector_matches_rust():
    # These EXACT values are baked into hh/src/sor/mod.rs::tests::parity_vector.
    assert select_path(SorRng(42), 8, 3) == [5, 0, 6]
    assert bringup(123456789, 5, 3, 3) == [[4, 1, 3], [3, 2, 0], [3, 0, 4]]
