Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
import type { Rng } from "@suaveplan/types";
import { pwgenError } from "../errors/codes.js";
/**
* Bounded retry budget for the rejection loop. A correctly-implemented
* `Rng.nextUint32` produces a uniformly-distributed uint32 every call; the
* worst-case rejection probability is `(2^32 mod max) / 2^32 < max/2^32`,
* which is < 0.0000019 for `max = 7776`. 1024 attempts give a worst-case
* success probability of `1 - (max/2^32)^1024`, indistinguishable from 1.
* Hitting this cap therefore proves the supplied `Rng` is broken.
*/
const REJECTION_RETRY_BUDGET = 1024;
/** Exclusive upper bound of the uint32 space. */
const TWO_POW_32 = 0x1_0000_0000;
/**
* Return a uniformly-distributed integer in the half-open interval
* `[0, max)` using Lemire-style rejection sampling.
*
* Reference: Daniel Lemire, _Fast Random Integer Generation in an
* Interval_, ACM Trans. Model. Comput. Simul. 2019,
* https://arxiv.org/abs/1805.10941 §3 ("Nearly Divisionless").
*
* The naïve `Math.floor(rng() * max)` is biased for non-power-of-two
* `max`. This implementation draws a uint32 and rejects values above the
* threshold `2^32 - (2^32 mod max)`, re-drawing on rejection. The bias
* disappears at the cost of an expected `< 2` draws per call.
*
* @throws PwgenError(PWGEN.RNG_FAILED) if `rng.nextUint32` returns
* non-uint32 values for {@link REJECTION_RETRY_BUDGET} consecutive
* attempts.
*/
export function randomInt(rng: Rng, max: number): number {
if (!Number.isInteger(max) || max < 1 || max >= TWO_POW_32) {
throw pwgenError(
"PWGEN.RNG_FAILED",
`randomInt requires max in [1, 2^32); received ${max}`
);
}
const threshold = TWO_POW_32 - (TWO_POW_32 % max);
for (let attempt = 0; attempt < REJECTION_RETRY_BUDGET; attempt++) {
const draw = rng.nextUint32() >>> 0;
if (draw < threshold) return draw % max;
}
throw pwgenError(
"PWGEN.RNG_FAILED",
`Rng failed to produce an in-range uint32 after ${REJECTION_RETRY_BUDGET} attempts`
);
}