#include "rng.h" uint64_t rng_seed_from_string(const char *seed) { uint64_t hash = 0xcbf29ce484222325ULL; /* FNV offset basis */ for (const unsigned char *p = (const unsigned char *)seed; *p; p++) { hash ^= (uint64_t)*p; hash *= 0x100000001b3ULL; /* FNV prime */ } return hash; } void rng_init(RngState *rng, uint64_t seed) { /* splitmix64 requires a nonzero-friendly state; any seed works, but * avoid the degenerate all-zero stream for the empty string. */ rng->state = seed ? seed : 0x9e3779b97f4a7c15ULL; } uint64_t rng_next_u64(RngState *rng) { uint64_t z = (rng->state += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } uint32_t rng_next_bounded(RngState *rng, uint32_t bound) { /* Rejection sampling against the largest multiple of `bound` that fits * in 32 bits, so every outcome in [0, bound) is equally likely. `limit` * must stay 64-bit: when `bound` evenly divides 2^32 (e.g. bound == 2, * used for every reversed-card coin flip), the true limit is 2^32 * itself, which truncates to 0 in a uint32_t and turns the loop below * into an infinite one. */ uint64_t limit = 0x100000000ULL - (0x100000000ULL % bound); uint64_t value; do { value = rng_next_u64(rng) & 0xffffffffULL; } while (value >= limit); return (uint32_t)(value % bound); }