pub struct Xorshift64 {
state: u64,
}
impl Xorshift64 {
pub fn new(seed: u64) -> Self {
Self { state: seed.max(1) }
}
pub fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.state = x;
x.wrapping_mul(0x2545F4914F6CDD1D)
}
pub fn next_f32(&mut self) -> f32 {
((self.next_u64() >> 40) as f32) / ((1u32 << 24) as f32)
}
pub fn next_signed_unit(&mut self) -> f32 {
self.next_f32() * 2.0 - 1.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xorshift_zero_seed_is_handled() {
let mut rng = Xorshift64::new(0);
let a = rng.next_u64();
let b = rng.next_u64();
assert_ne!(a, 0);
assert_ne!(a, b);
}
#[test]
fn next_f32_stays_in_unit_interval() {
let mut rng = Xorshift64::new(0xCAFEF00D);
for _ in 0..10_000 {
let v = rng.next_f32();
assert!((0.0..1.0).contains(&v), "next_f32 escaped [0, 1): {v}");
}
}
#[test]
fn next_signed_unit_stays_in_signed_unit_interval() {
let mut rng = Xorshift64::new(0xDEADBEEF);
for _ in 0..10_000 {
let v = rng.next_signed_unit();
assert!(
(-1.0..1.0).contains(&v),
"next_signed_unit escaped [-1, 1): {v}"
);
}
}
}