Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
# Capability: seeded-prng
## ADDED Requirements
### Package identity
- REQ-PRNG-001: Package name `@suaveplan/pseudo-random`, `packages/universal/pseudo-random`.
- REQ-PRNG-002: Runtime deps: `@suaveplan/error`, `@suaveplan/types`, `tslib`, `zod`. Zero crypto deps, zero browser deps.
- REQ-PRNG-003: `"sideEffects": false`.
### SeededRng interface
- REQ-PRNG-010: Exports `SeededRng` interface extending `Rng` from `@suaveplan/types`: `{ readonly seed: number; readonly position: number; skip(n: number): this; reset(): this; at(offset: number): SeededRng; fork(id: number | string): SeededRng; checkpoint(): RngCheckpoint; restore(checkpoint: RngCheckpoint): this }`.
- REQ-PRNG-011: Exports `RngCheckpoint = { seed: number; position: number; state: unknown }` — serialisable to JSON.
### Seed normalisation
- REQ-PRNG-020: Exports `normalizeSeed(seed: string | number): number`.
- REQ-PRNG-021: String seeds: splitmix64 mixing over UTF-16 char codes. Produces stable 32-bit integer.
- REQ-PRNG-022: Numeric seeds: single splitmix64 round to avalanche input bias. Rejects NaN, Infinity, non-safe-integers with `PseudoRandomError("PRNG.SEED.INVALID")`.
- REQ-PRNG-023: Zero input produces a non-zero seed (avoids degenerate PRNG state).
### Mulberry32Rng
- REQ-PRNG-030: Exports `Mulberry32Rng` implementing `SeededRng`. 32-bit state.
- REQ-PRNG-031: `nextUint32()` uses the standard Mulberry32 algorithm: `a = (a + 0x6d2b79f5) >>> 0; ...`.
- REQ-PRNG-032: Byte-identical output to the inline implementation in `@suaveplan/testing/context`.
### Sfc32Rng
- REQ-PRNG-040: Exports `Sfc32Rng` implementing `SeededRng`. 128-bit state (4×uint32).
- REQ-PRNG-041: State initialised with 12 warmup rounds from `seedToSFC32State`.
- REQ-PRNG-042: Byte-identical output to `BulletproofPRNG` (SFC32 mode) in the bingo app for same seed.
### Xoshiro256Rng
- REQ-PRNG-050: Exports `Xoshiro256Rng` implementing `SeededRng`. 256-bit state (4×uint64 via BigInt or two uint32 pairs).
- REQ-PRNG-051: Uses xoshiro256++ algorithm.
- REQ-PRNG-052: `skip(2**128)` and `skip(2**192)` use the algebraic jump polynomials (O(log n)); other `skip(n)` uses linear burn.
### CounterRng
- REQ-PRNG-060: Exports `CounterRng` implementing `SeededRng`. Counter-based via splitmix64 hash.
- REQ-PRNG-061: `at(offset)` is O(1) — directly sets the counter.
- REQ-PRNG-062: `nextUint32()` = `splitmix64(seed XOR counter++)` truncated to 32 bits.
### Shared SeededRng behaviour
- REQ-PRNG-070: `fork(id)` derives child seed via `normalizeSeed(parentSeed XOR hash(id))`. Children are statistically independent.
- REQ-PRNG-071: `checkpoint()` returns a plain object that `JSON.stringify` round-trips without loss.
- REQ-PRNG-072: `restore(checkpoint)` resets the instance to the checkpointed state and returns `this`.
- REQ-PRNG-073: `at(offset)` returns a **new instance** at the given offset (does not mutate the original).
### Utilities
- REQ-PRNG-080: Exports `shuffle<T>(array: T[], rng?: Rng): T[]` — Fisher-Yates, returns new array.
- REQ-PRNG-081: Exports `shuffleInPlace<T>(array: T[], rng?: Rng): T[]` — Fisher-Yates, mutates.
- REQ-PRNG-082: Exports `sample<T>(array: T[], n: number, rng?: Rng): T[]` — n distinct elements without replacement.
- REQ-PRNG-083: Exports `weightedChoice<T>(items: T[], weights: number[], rng?: Rng): T`.
### Error surface
- REQ-PRNG-090: Exports `PseudoRandomError extends SuaveplanError<PseudoRandomCode>`.
- REQ-PRNG-091: Codes: `PRNG.SEED.INVALID`, `PRNG.OFFSET.OUT_OF_RANGE`, `PRNG.SAMPLE.INSUFFICIENT_ITEMS`, `PRNG.CHECKPOINT.INVALID`.
### Quality
- REQ-PRNG-100: 100% statement + branch coverage.
- REQ-PRNG-101: fast-check: determinism, fork independence (10k samples, correlation < 0.01), checkpoint round-trip.
- REQ-PRNG-102: mitata benchmarks: `nextUint32` throughput for each algorithm.
- REQ-PRNG-103: `Sfc32Rng` output verified against known vectors from bingo test suite.