use rand::RngCore;
#[derive(Clone, Debug)]
pub(crate) struct SimRng {
state: u64,
}
impl SimRng {
pub(crate) fn seed(seed: u64) -> Self {
Self { state: seed }
}
}
impl RngCore for SimRng {
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)
}
fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut i = 0;
while i < dest.len() {
let bytes = self.next_u64().to_le_bytes();
let n = (dest.len() - i).min(8);
dest[i..i + n].copy_from_slice(&bytes[..n]);
i += n;
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.fill_bytes(dest);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_and_seed_sensitive() {
let a: Vec<u64> = (0..4)
.scan(SimRng::seed(1), |r, _| Some(r.next_u64()))
.collect();
let b: Vec<u64> = (0..4)
.scan(SimRng::seed(1), |r, _| Some(r.next_u64()))
.collect();
let c: Vec<u64> = (0..4)
.scan(SimRng::seed(2), |r, _| Some(r.next_u64()))
.collect();
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn first_value_matches_the_reference_splitmix64() {
assert_eq!(SimRng::seed(0).next_u64(), 0xE220_A839_7B1D_CDAF);
}
}