#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);
}
