pub struct SplitMix64 {
state: u64,
}
impl SplitMix64 {
pub fn new(seed: u64) -> Self {
Self { state: seed }
}
pub fn from_coords(seed: u64, x: i32, y: i32, salt: u64) -> Self {
let mix = mix_u64(
seed ^ salt
^ ((x as i64 as u64).rotate_left(13))
^ ((y as i64 as u64)
.rotate_left(31)
.wrapping_mul(0x9E37_79B9_7F4A_7C15)),
);
Self::new(mix)
}
pub fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
mix_u64(self.state)
}
pub fn next_f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / ((1u64 << 53) as f64)
}
pub fn range_f64(&mut self, lo: f64, hi: f64) -> f64 {
lo + (hi - lo) * self.next_f64()
}
pub fn range_usize(&mut self, max: usize) -> usize {
if max == 0 {
return 0;
}
(self.next_u64() % (max as u64)) as usize
}
pub fn bool_with_prob(&mut self, p: f64) -> bool {
self.next_f64() < p
}
}
pub const fn mix_u64(seed: u64) -> u64 {
let mut z = seed;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn same_seed_same_sequence() {
let mut a = SplitMix64::new(42);
let mut b = SplitMix64::new(42);
for _ in 0..16 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_coords_different_states() {
let a = SplitMix64::from_coords(7, 1, 1, 0).state;
let b = SplitMix64::from_coords(7, 1, 2, 0).state;
assert_ne!(a, b);
}
#[test]
fn salt_separates_call_sites() {
let a = SplitMix64::from_coords(7, 1, 1, 0).state;
let b = SplitMix64::from_coords(7, 1, 1, 1).state;
assert_ne!(a, b);
}
#[test]
fn range_f64_within_bounds() {
let mut r = SplitMix64::new(1234);
for _ in 0..1000 {
let v = r.range_f64(-2.0, 5.0);
assert!((-2.0..5.0).contains(&v), "{}", v);
}
}
#[test]
fn range_usize_within_bounds() {
let mut r = SplitMix64::new(1234);
for _ in 0..1000 {
let v = r.range_usize(10);
assert!(v < 10);
}
}
}