[M0-CORE-06] Deterministic PRNG: xorshift128+ + splitmix64 seeding, per-substream derivation, period proof - #6
Merged
Merged
Conversation
…ubstreams, period proof laige::Prng (header-only): xorshift128+ (reference-transcribed and verified), splitmix64 seeding (zero state unreachable), documented substream derivation (Prng(seed + id*K), id 0 = master, composes), next_u64 / next_range (Lemire unbiased) / next_float01 (exact 24-bit dyadic). Public seedState/stepState for determinism verification and M1 save/replay. Determinism scope: cross-platform bit-exact (pure unsigned integer arithmetic + one exact power-of-two scale, ADR 0002). Tests (ctest -R prng, 18 cases): golden KATs on seed 0x1234567890ABCDEF (32 draws + FNV-1a-of-4096 replay hash; intentionally algorithm- sensitive), 10^4-step reference transcription check, zero-state seeding spot check, next_range KATs + unbiasedness, next_float01 dyadic exactness, substream derivation KATs + composition + overlap sanity, and the committed period proof: the state map's characteristic polynomial over GF(2) (reconstructed via Berlekamp-Massey on a probe orbit, as the reciprocal of the BM connection polynomial) is checked to annihilate the map on all 128 basis states, irreducible (Ruffini), and primitive (full factorization of 2^128-1, Miller-Rabin, portable 128-bit multiply) => every nonzero state has period exactly 2^128 - 1; plus an empirical short-cycle screen of the output stream. GF(2)/BM/MR machinery is self-contained and portable (no __int128/builtins). Verified locally 2026-09-11: 12/12 ctest on GCC 16.2.1 (static, shared, ASan+UBSan, TSan halt_on_error=1) and Clang 22.1.8; KAT constants identical across the two compilers (CI hookup M1-DET-04); zero warnings under the NFR-8.10 policy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements roadmap step M0-CORE-06 (PRD §10.3 "seeded, per-substream" randomness, AGENTS ARCH-010):
laige::Prngin a new header-onlysrc/laige-core/include/laige/prng.h, with the committed period proof in the test suite.What
xorshift128plus.c): state(part1, part2), steppart1 = o1; t = o0 ^ (o0 << 23); part2 = t ^ o1 ^ (t >> 18) ^ (o1 >> 5); out = part2 + o1(unsigned wrap); the all-zero state is excluded and unreachable. The algorithm is the determinism contract — the golden vectors intentionally fail if it changes (ARCH-010, ADR 0002).seed + K(K = 0x9E3779B97F4A7C15=kSplitmix64Increment; named mix constants). splitmix64 is a bijection with two inputs differing by nonzero K, so no 64-bit seed reaches the zero state.deriveSubstream(seed, id) == Prng(seed + id * K)(id 0 = master stream; composes:derive(derive(s, i), j) == derive(s, i + j)).substream(id)derives from the seed, not from the current position.next_u64(),next_range(min, max)(Lemire unbiased reduction;min >= maxis a debug assert / documented UB),next_float01()(next_u64() >> 40 * 2^-24— exactlyk * 2^-24, 24-bit resolution,[0, 1), bit-exact),seed(),substream(id),deriveSubstream(seed, id), and public staticsseedState/stepStatefor determinism verification and M1 save/replay (a saved stream is(seed, part1, part2)). Value type (copy = shared stream position, documented), single-owner, not thread-safe (CONC-001), no allocation, NFR-8.10 policy.2^-24: cross-platform bit-exact. Golden KATs on seed0x1234567890ABCDEF: 32 draws + FNV-1a-of-first-4096 replay hash0xB64E76173859B6D8.PrngPeriodsuite reconstructs the state map's characteristic polynomial over GF(2) from a 512-bit probe orbit via Berlekamp-Massey (the BM connection polynomial is the reciprocal of the characteristic polynomial — the recurrence relatesp[t]to lower indices), checksP(A) = 0on all 128 basis states, then verifiesPirreducible (Ruffini:x^(2^128) ≡ x (mod P)andgcd(x^(2^d) − x, P) = 1ford ∈ {1,2,4,8,16,32,64}) and primitive (P | x^(2^128−1) − 1with noq-th root for the nine prime factors of2^128 − 1 = 3·5·17·257·641·65537·6700417·274177·67280421310721; primality by Miller-Rabin, factorization by portable 128-bit multiply — no__int128/builtins, MSVC-compatible). A primitive characteristic polynomial of an invertible linear map over GF(2) is exactly maximal period for every nonzero state.docs/api/prng.md(API contracts, Performance per DOC-004, misuse warnings, save/replay).Verification (local, 2026-09-11)
ctest -R prng: 18 GTest cases green —PrngGolden(32-draw KAT, FNV-1a replay hash, 10^4-step reference transcription check, zero-state seeding spot check over 100k seeds),PrngRange(5 KATs incl. the power-of-two fast path, span-1 identity, 110k in-bounds draws over 10 spans, unbiasedness: 2^18 draws over 8-way and 3-way spans within ~10 sigma),PrngFloat01(8-draw KAT, 2^16 draws: dyadic exactness,[0, 1)bounds, mean 0.5 ± 14 sigma),PrngSubstreams(8 id KATs, id-0 = master, derivation composition, same-id determinism, 2^16-draw cross-substream overlap check),PrngPeriod(the characteristic-polynomial proof above + empirical screen: no duplicate in 4M draws, no period-q window pattern for the eight small prime factors of 2^128 − 1).buildstatic,build-shared,build-asanASan+UBSan fatal,build-tsanTSanhalt_on_error=1) and Clang 22.1.8 (build-clang) — the KAT constants are identical across the two compilers (local two-compiler run; CI hookup lands in M1-DET-04), zero warnings under the NFR-8.10 policy (-Wall -Werror -fno-exceptions -fno-rtti), incl. the real-tree include-lint.The CI lane will additionally cover the P0 Windows job (MSVC compile of the new header and the portable GF(2) test machinery) and macOS.