AxlRand — deterministic pseudo-random numbers

Mirrors GLib’s GRand: a seedable, reproducible PRNG (xoshiro256** seeded through SplitMix64) independent of any firmware entropy source. It is the deterministic complement to AxlRng — cryptographic random bytes — reach for AxlRand when you want repeatable streams (test fixtures, sampling, retry-backoff jitter, procedural graphics, shuffling) and for axl_rng_bytes when you need cryptographic material.

A given seed produces a byte-identical stream on x86-64 and AArch64. The output is defined as a sequence of 64-bit words and every call is pinned to it: axl_rand_uint32 returns the high 32 bits of the next word, axl_rand_double maps the top 53 bits into [0, 1), and axl_rand_bytes emits successive words little-endian. int_range is unbiased (rejection-sampled) and double_range rounds its scaled product before adding the offset so no fused multiply-add can perturb the low bit.

A process-global stream (axl_random_*, mirroring g_random_*) is lazily seeded from a non-reproducible source; call axl_random_set_seed first for a deterministic global sequence.

AXL_AUTOPTR(AxlRand) r = axl_rand_new_seeded(0x1234);
uint32_t a = axl_rand_uint32(r);
int      d = axl_rand_int_range(r, 1, 7);   // dice roll, [1,7)
double   t = axl_rand_double(r);            // [0.0, 1.0)

API Reference

Deterministic pseudo-random number generator.

Mirrors GLib’s GRand. A seedable, reproducible PRNG that is independent of any firmware entropy source — the complement to axl-rng.h (axl_rng_bytes, which draws from the hardware EFI_RNG_PROTOCOL and has no deterministic mode). Reach for AxlRand when you want repeatable streams: test fixtures, sampling, retry-backoff jitter, procedural graphics (noise, particles), shuffling.

The generator is xoshiro256** seeded through SplitMix64. A given seed produces the identical stream on every architecture — the output is defined as a sequence of 64-bit words w0, w1, w2, …, and every generator call is pinned to that sequence:

  • axl_rand_uint64 returns the next word and advances one step.

  • axl_rand_uint32 returns the high 32 bits of the next word (one step). It is a narrowing of the 64-bit stream, not a separate generator: uint32 and uint64 draw from the same sequence, one word per call.

  • axl_rand_double maps the next word’s top 53 bits into [0,1).

  • axl_rand_boolean returns bit 63 of the next word.

  • axl_rand_bytes emits successive words in little-endian byte order (low byte first), truncating the final word.

All of these are bit-identical across x86-64 and AArch64 (pure 64-bit integer math, plus the standard 53-bit double construction). axl_rand_double_range is likewise bit-identical: the implementation rounds the scaled product to double before adding the offset, so no fused multiply-add can perturb the low bit. The generator is fast and statistically strong but is NOT cryptographically secure — use axl_rng_bytes for nonces, keys, and tokens.

// Reproducible stream:
AXL_AUTOPTR(AxlRand) r = axl_rand_new_seeded(0x1234);
uint32_t a = axl_rand_uint32(r);
int      d = axl_rand_int_range(r, 1, 7);   // dice roll, [1,7)
double   t = axl_rand_double(r);            // [0.0, 1.0)

// One-off without managing a handle (process-global stream):
if (axl_random_boolean())
    axl_printf("heads\n");

Typedefs

typedef struct AxlRand AxlRand

Opaque PRNG state (xoshiro256**).

Functions

AxlRand *axl_rand_new(void)

Create a generator seeded from a non-reproducible source.

Seeds from axl_rng_bytes() when the firmware entropy protocol is available, otherwise from a monotonic clock reading. Two calls therefore yield different streams. For a repeatable stream use axl_rand_new_seeded(). Never returns NULL (aborts on OOM, per the library’s allocation contract).

Returns:

a new generator; free with axl_rand_free().

AxlRand *axl_rand_new_seeded(uint64_t seed)

Create a generator with an explicit 64-bit seed.

Deterministic: the same seed always produces the same stream. Every seed value is valid (a zero seed is handled — the SplitMix64 expansion guarantees a non-degenerate xoshiro state).

Parameters:
  • seed – seed value

Returns:

a new generator; free with axl_rand_free().

AxlRand *axl_rand_copy(const AxlRand *r)

Duplicate a generator, including its current position.

The copy continues the identical stream from the point of the copy, independently of the original. Useful for branching a reproducible sequence. NULL-safe: returns NULL when r is NULL.

Parameters:
  • r – generator to copy

Returns:

a new generator, or NULL if r is NULL.

void axl_rand_set_seed(AxlRand *r, uint64_t seed)

Re-seed an existing generator in place.

Resets the stream as if the generator had just been created with axl_rand_new_seeded(seed). No-op when r is NULL.

Parameters:
  • r – generator

  • seed – new seed value

void axl_rand_free(AxlRand *r)

Free a generator. NULL-safe.

Parameters:
  • r – generator (may be NULL)

uint32_t axl_rand_uint32(AxlRand *r)

Next 32-bit value, uniform over the full range.

Parameters:
  • r – generator

Returns:

a value in [0, 2^32).

uint64_t axl_rand_uint64(AxlRand *r)

Next 64-bit value, uniform over the full range.

The generator’s native word — no narrowing. Prefer this over two axl_rand_uint32() calls when 64 bits are needed.

Parameters:
  • r – generator

Returns:

a value in [0, 2^64).

int32_t axl_rand_int_range(AxlRand *r, int32_t begin, int32_t end)

Uniform integer in the half-open range [begin, end).

Rejection-sampled, so the result is unbiased across the whole range (no modulo skew). The span is computed in 64-bit arithmetic, so every begin < end pair is valid — including the full [INT32_MIN, INT32_MAX) range whose width exceeds INT32_MAX. A degenerate range (begin >= end) returns begin without drawing from the stream. Note there is no 64-bit range variant — for an unbiased 64-bit range, layer rejection sampling over axl_rand_uint64().

Parameters:
  • r – generator

  • begin – inclusive lower bound

  • end – exclusive upper bound

Returns:

a value v with begin <= v < end, or begin if the range is empty.

double axl_rand_double(AxlRand *r)

Uniform double in the half-open range [0.0, 1.0).

53 bits of resolution (one per representable double in the range).

Parameters:
  • r – generator

Returns:

a value in [0.0, 1.0).

double axl_rand_double_range(AxlRand *r, double begin, double end)

Uniform double in the half-open range [begin, end).

A degenerate range (begin >= end) returns begin.

Parameters:
  • r – generator

  • begin – inclusive lower bound

  • end – exclusive upper bound

Returns:

a value v with begin <= v < end, or begin if the range is empty.

bool axl_rand_boolean(AxlRand *r)

Fair coin flip.

Parameters:
  • r – generator

Returns:

true or false, each with probability 0.5.

void axl_rand_bytes(AxlRand *r, void *out, size_t len)

Fill a buffer with len pseudo-random bytes.

Bytes are drawn from successive 64-bit stream words emitted little-endian (see the file header). Deterministic for a given seed and byte-identical across architectures — unlike axl_rng_bytes(), which draws hardware entropy and returns a status. This call cannot fail (returns void). len == 0 is a no-op. Do NOT use for cryptographic material.

Parameters:
  • r – generator

  • out – destination buffer

  • len – number of bytes to write

void axl_random_set_seed(uint64_t seed)

Seed the process-global generator for a reproducible stream.

May be called at any time; resets the global stream to seed seed regardless of whether it had already been lazily seeded or used. Call this before the first axl_random_* use in a test to get a deterministic global sequence.

Parameters:
  • seed – seed value

uint32_t axl_random_uint32(void)

Next 32-bit value from the global generator.

See also

axl_rand_uint32.

int32_t axl_random_int_range(int32_t begin, int32_t end)

Uniform integer in [begin, end) from the global generator.

See also

axl_rand_int_range.

Parameters:
  • begin – inclusive lower bound

  • end – exclusive upper bound

double axl_random_double(void)

Uniform double in [0.0, 1.0) from the global generator.

See also

axl_rand_double.

double axl_random_double_range(double begin, double end)

Uniform double in [begin, end) from the global generator.

Parameters:
  • begin – inclusive lower bound

  • end – exclusive upper bound

bool axl_random_boolean(void)

Fair coin flip from the global generator.

See also

axl_rand_boolean.