use crate::library::pcg::pcg_seek;
pub struct Prng {
seed: u64,
inc: u64,
position: u64,
}
impl Prng {
pub fn new(seed: u64) -> Self {
Self {
seed,
inc: 1, position: 0,
}
}
pub fn with_stream(seed: u64, stream: u64) -> Self {
Self {
seed,
inc: 2u64.wrapping_mul(stream).wrapping_add(1),
position: 0,
}
}
pub fn next_u64(&mut self) -> u64 {
let v = pcg_seek(self.seed, self.inc, self.position);
self.position = self.position.wrapping_add(1);
v
}
pub fn next_bounded(&mut self, n: u64) -> u64 {
if n == 0 {
return 0;
}
let threshold = ((u64::MAX - n) + 1) % n;
loop {
let candidate = self.next_u64();
if candidate >= threshold {
return candidate % n;
}
}
}
pub fn shuffle<T>(&mut self, slice: &mut [T]) {
let n = slice.len();
if n < 2 {
return;
}
for i in (1..n).rev() {
let j = self.next_bounded((i as u64) + 1) as usize;
slice.swap(i, j);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn determinism() {
let mut a = Prng::new(42);
let mut b = Prng::new(42);
for _ in 0..100 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_seeds_diverge() {
let mut a = Prng::new(1);
let mut b = Prng::new(2);
let mut differ = false;
for _ in 0..10 {
if a.next_u64() != b.next_u64() {
differ = true;
break;
}
}
assert!(differ);
}
#[test]
fn different_streams_diverge() {
let mut a = Prng::with_stream(42, 0);
let mut b = Prng::with_stream(42, 1);
let mut differ = false;
for _ in 0..10 {
if a.next_u64() != b.next_u64() {
differ = true;
break;
}
}
assert!(differ);
}
#[test]
fn bounded_within_range() {
let mut rng = Prng::new(7);
for _ in 0..1000 {
let v = rng.next_bounded(10);
assert!(v < 10);
}
}
#[test]
fn shuffle_preserves_elements() {
let mut rng = Prng::new(123);
let mut data: Vec<u64> = (0..50).collect();
rng.shuffle(&mut data);
let mut sorted = data.clone();
sorted.sort();
assert_eq!(sorted, (0..50).collect::<Vec<_>>());
}
#[test]
fn shuffle_deterministic_for_seed() {
let mut rng1 = Prng::new(42);
let mut rng2 = Prng::new(42);
let mut data1: Vec<u64> = (0..20).collect();
let mut data2: Vec<u64> = (0..20).collect();
rng1.shuffle(&mut data1);
rng2.shuffle(&mut data2);
assert_eq!(data1, data2);
}
}