#[derive(Debug, Clone)]
pub(crate) struct SplitMix64 {
state: u64,
}
impl SplitMix64 {
pub(crate) fn new(seed: u64) -> Self {
Self { state: seed }
}
pub(crate) fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub(crate) fn next_open_unit(&mut self) -> f64 {
for _ in 0..4 {
let bits = self.next_u64() >> 11;
if bits != 0 {
return (bits as f64) * (1.0 / ((1_u64 << 53) as f64));
}
}
1.0 / ((1_u64 << 53) as f64)
}
pub(crate) fn next_below(&mut self, n: u64) -> u64 {
debug_assert!(n >= 1, "next_below requires n >= 1");
let zone = u64::MAX - (u64::MAX % n);
loop {
let v = self.next_u64();
if v < zone {
return v % n;
}
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn same_seed_produces_same_sequence() {
let mut a = SplitMix64::new(0xCAFE_F00D);
let mut b = SplitMix64::new(0xCAFE_F00D);
for _ in 0..1024 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_seeds_diverge() {
let mut a = SplitMix64::new(1);
let mut b = SplitMix64::new(2);
let mut equal = 0;
for _ in 0..1024 {
if a.next_u64() == b.next_u64() {
equal += 1;
}
}
assert!(equal < 4, "too much agreement: {equal}/1024");
}
#[test]
fn open_unit_is_in_unit_interval_exclusive_zero() {
let mut rng = SplitMix64::new(7);
for _ in 0..10_000 {
let u = rng.next_open_unit();
assert!(u > 0.0 && u <= 1.0, "u = {u}");
}
}
#[test]
fn open_unit_never_returns_zero_from_zero_seed() {
let mut rng = SplitMix64::new(0);
for _ in 0..10_000 {
assert!(rng.next_open_unit() > 0.0);
}
}
#[test]
fn next_below_stays_in_range() {
let mut rng = SplitMix64::new(42);
for _ in 0..10_000 {
let v = rng.next_below(7);
assert!(v < 7);
}
}
#[test]
fn next_below_one_always_returns_zero() {
let mut rng = SplitMix64::new(0);
for _ in 0..1_000 {
assert_eq!(rng.next_below(1), 0);
}
}
}