#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SplitMix64 {
state: u64,
}
impl Default for SplitMix64 {
fn default() -> Self {
SplitMix64::new(0)
}
}
impl SplitMix64 {
pub const fn new(seed: u64) -> Self {
SplitMix64 { state: seed }
}
pub 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 fn next_f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / ((1u64 << 53) as f64)
}
pub fn next_bool(&mut self) -> bool {
self.next_u64().is_multiple_of(2)
}
pub fn below(&mut self, bound: usize) -> usize {
if bound == 0 {
0
} else {
(self.next_u64() % bound as u64) as usize
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deterministic() {
let mut a = SplitMix64::new(12345);
let mut b = SplitMix64::new(12345);
for _ in 0..1000 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn test_distinct_seeds_diverge() {
let mut a = SplitMix64::new(1);
let mut b = SplitMix64::new(2);
assert_ne!(a.next_u64(), b.next_u64());
}
#[test]
fn test_next_f64_in_range() {
let mut r = SplitMix64::new(42);
for _ in 0..10_000 {
let x = r.next_f64();
assert!((0.0..1.0).contains(&x));
}
}
#[test]
fn test_below_bound() {
let mut r = SplitMix64::new(7);
for _ in 0..10_000 {
assert!(r.below(10) < 10);
}
assert_eq!(r.below(0), 0);
}
}