Library

random

Pseudo-random number generator.

1 file lib/random/random.vader 9 exports
dice :: new_rng(42)
roll :: dice.range_i32(1, 7)   // from 1 to 6, the same at every run
deck :: [1, 2, 3, 4, 5]
new_rng().shuffle(deck)        // another order at every run

Algorithm : xoroshiro128++ (Blackman & Vigna, 2021). u64 output, 2^128 period, passes the BigCrush statistical battery. Fast on every modern CPU and trivially portable to WASM (no SIMD intrinsics, no platform primitives). Pure Vader: its one host call is std/time.monotonic(), read for the seed when new_rng is given none.

NOT suitable for cryptography: the output is reversible from any 256 consecutive u64s. For a cryptographic PRNG plug std/crypto (post-MVP).

Types

Rng :: struct

Mutable RNG state. Two 64-bit words ; the seeder guarantees at least one of them is non-zero (a fully-zero state would lock xoroshiro to emitting zeros forever).

Functions

new_rng :: fn(seed: u64 = u64(i64(monotonic()))) -> Rng

Build an Rng from a 64-bit seed: the same seed gives the same draws, and without one the seed is read from the clock, so the draws change from one run to the next. The seed is fanned out via splitmix64 to populate both state words ; a seed == 0 input therefore still produces a usable non-zero state.

Two calls close together without a seed may read the same clock value (its resolution is bounded) ; pass distinct seeds when two streams of one process must differ.

Parameters

seedu64 = u64(i64(monotonic()))any 64-bit value, including 0; read from the clock when left out.

Returns

Example

replayed :: new_rng(42)   // the same draws at every run
fresh :: new_rng()        // other draws at every run
next_u64 :: fn(self: Rng) -> u64

Draw the next u64. The full 64 bits are uniformly distributed.

Parameters

selfRng the generator, whose state advances.

Returns

u64

Example

new_rng(42).next_u64()   // 1700210143001418247
next_u32 :: fn(self: Rng) -> u32

Draw the next u32 by taking the upper 32 bits of next_u64: the upper bits of xoroshiro128++ pass BigCrush, the lower bits are slightly weaker on certain tests.

Parameters

selfRng the generator, whose state advances.

Returns

u32

Example

new_rng(42).next_u32()   // 395861021
next_bool :: fn(self: Rng) -> bool

Uniform random boolean (50/50).

Parameters

selfRng the generator, whose state advances.

Returns

bool

Example

new_rng(42).next_bool()   // false
range_i32 :: fn(self: Rng, lo: i32, hi: i32) -> i32

Uniform i32 in the half-open range [lo, hi). A degenerate range (hi <= lo) is empty and returns lo: without this guard the modulus would divide by zero.

Implementation uses rejection sampling on the top 32 bits to avoid modulo bias when (hi - lo) doesn't divide 2^32.

Parameters

selfRng the generator, whose state advances.
loi32 the smallest value drawn.
hii32 the bound above the largest value drawn.

Returns

i32

Example

rng :: new_rng(42)
rng.range_i32(1, 7)   // 6: a die roll
rng.range_i32(5, 5)   // 5: an empty range gives `lo`
range_u32 :: fn(self: Rng, lo: u32, hi: u32) -> u32

Uniform u32 in the half-open range [lo, hi). A degenerate range (hi <= lo) is empty and returns lo: without this guard the modulus would divide by zero.

Parameters

selfRng the generator, whose state advances.
lou32 the smallest value drawn.
hiu32 the bound above the largest value drawn.

Returns

u32

Example

new_rng(42).range_u32(10, 20)   // 11
unit_f64 :: fn(self: Rng) -> f64

Uniform f64 in [0.0, 1.0). Uses 52 bits of mantissa: one bit less than the f64 maximum so the largest possible numerator (2^52 - 1) divided by 2^52 is the immediate predecessor of 1.0 rather than rounding up to exactly 1.0 (which (2^53 - 1) / 2^53 does on IEEE 754 round-to-nearest).

Parameters

selfRng the generator, whose state advances.

Returns

f64

Example

new_rng(42).unit_f64()   // 0.09216857653620125
shuffle :: fn<T>(self: Rng, arr: T[]) -> void

In-place Fisher-Yates shuffle. Visits each element once in O(n) ; O(1) extra memory (one i32 swap helper). Returns nothing: the input array is permuted directly.

Parameters

selfRng the generator, whose state advances.
arrT[] the array to permute.

Example

cards :: [1, 2, 3, 4, 5]
new_rng(42).shuffle(cards)
cards   // [5, 1, 3, 4, 2]
Written by vader doc. Icons: Codicons, CC BY 4.0. Fonts: Atkinson Hyperlegible, JetBrains Mono, OFL.