#[derive(Clone, Debug)]
pub struct Rng {
state: [u64; 4],
}
impl Rng {
pub fn new(seed: u64) -> Rng {
let mut expander = seed;
let mut state = [0u64; 4];
for slot in state.iter_mut() {
*slot = split_mix_64(&mut expander);
}
Rng { state }
}
pub fn next_u64(&mut self) -> u64 {
let [s0, s1, s2, s3] = self.state;
let result = s1.wrapping_mul(5).rotate_left(7).wrapping_mul(9);
let shifted = s1 << 17;
let n2 = s2 ^ s0;
let n3 = s3 ^ s1;
self.state = [s0 ^ n3, s1 ^ n2, n2 ^ shifted, n3.rotate_left(45)];
result
}
pub fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}
pub fn below(&mut self, bound: u64) -> u64 {
if bound == 0 {
return 0;
}
let product = u128::from(self.next_u64()).wrapping_mul(u128::from(bound));
(product >> 64) as u64
}
pub fn chance(&mut self, numerator: u64, denominator: u64) -> bool {
denominator != 0 && self.below(denominator) < numerator
}
pub fn fill(&mut self, output: &mut [u8]) {
for chunk in output.chunks_mut(8) {
let word = self.next_u64().to_le_bytes();
for (slot, byte) in chunk.iter_mut().zip(word.iter()) {
*slot = *byte;
}
}
}
pub fn pick<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> {
items.get(self.below(items.len() as u64) as usize)
}
}
fn split_mix_64(state: &mut u64) -> u64 {
*state = state.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut value = *state;
value = (value ^ (value >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
value = (value ^ (value >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
value ^ (value >> 31)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_same_seed_replays_the_same_stream() {
let first: Vec<u64> = (0..64)
.scan(Rng::new(1782), |rng, _| Some(rng.next_u64()))
.collect();
let second: Vec<u64> = (0..64)
.scan(Rng::new(1782), |rng, _| Some(rng.next_u64()))
.collect();
assert_eq!(first, second);
}
#[test]
fn adjacent_seeds_produce_unrelated_streams() {
let a = Rng::new(1).next_u64();
let b = Rng::new(2).next_u64();
assert_ne!(a, b);
assert!(
a ^ b != 1,
"streams differ by more than the seed difference"
);
}
#[test]
fn bounded_selection_stays_in_range() {
let mut rng = Rng::new(7);
assert_eq!(rng.below(0), 0);
assert_eq!(rng.below(1), 0);
for _ in 0..10_000 {
assert!(rng.below(97) < 97);
}
}
#[cfg_attr(miri, ignore)]
#[test]
fn output_covers_its_range() {
let mut rng = Rng::new(0xdead_beef);
let mut buckets = [0u32; 16];
for _ in 0..160_000 {
let index = rng.below(16) as usize;
buckets[index] += 1;
}
for count in buckets {
assert!((7_000..13_000).contains(&count), "uneven bucket {count}");
}
}
}