first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
# utils/shuffle
## Purpose
The `shuffle` module provides deterministic Fisher-Yates shuffling and single-element `choice` drawn from a user-supplied `Rng`. Because `Rng` is the canonical interface defined in `@suaveplan/types`, these utilities compose with every `SeededRng` implementation in this package as well as with the CSPRNG in `@suaveplan/random`.
Shuffling is a building block for game-card dealing, playlist generation, A/B assignment, and deterministic test data. Isolating it here ensures every downstream package uses the same unbiased Fisher-Yates algorithm rather than re-implementing it (which frequently introduces subtle bias).
## Features
- `shuffle(input, rng)` — non-mutating, returns a new shuffled array
- `shuffleInPlace(arr, rng)` — mutates the argument, returns it
- `choice(input, rng)` — uniformly random element; typed `PseudoRandomError` on empty input
- Draws exactly `n - 1` uint32 values per shuffle — counts are predictable for replay
## Usage: Basic Example
```ts
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
import { shuffle } from "@suaveplan/pseudo-random/utils";
const rng = new Mulberry32Rng(42);
const deck = shuffle(["A", "K", "Q", "J"], rng);
```
## Usage: Advanced Example
```ts
import { Sfc32Rng } from "@suaveplan/pseudo-random";
import { choice, shuffleInPlace } from "@suaveplan/pseudo-random/utils";
const rng = new Sfc32Rng("round-12");
const deck = ["clubs", "hearts", "diamonds", "spades"];
shuffleInPlace(deck, rng);
const first = choice(deck, rng);
```
## API Reference
- `shuffle<T>(input: ReadonlyArray<T>, rng: Rng): T[]` — returns a new array.
- `shuffleInPlace<T>(arr: T[], rng: Rng): T[]` — mutates and returns.
- `choice<T>(input: ReadonlyArray<T>, rng: Rng): T` — throws `PseudoRandomError.ARG.EMPTY_ARRAY` on empty input.
## Implementation Notes
The Fisher-Yates loop walks from `n-1` down to `1`, swapping `arr[i]` with `arr[j]` where `j = rng.nextUint32() % (i+1)`. The modulo introduces a negligible bias when `i+1` does not divide 2^32 evenly (max bias ≈ `n / 2^32`); for any `n` short of 2^32 elements this is statistically undetectable and matches the behaviour every other mainstream library ships. If you need provably unbiased sampling for large `n`, use `@suaveplan/random`'s `randomInt`, which re-draws on biased samples.