pub struct DetRng {
state: u64,
}
impl DetRng {
pub fn new(seed: u64) -> Self {
Self { state: seed }
}
pub fn derive(master_seed: u64, domain: &str) -> Self {
let mut hash: u64 = 14695981039346656037; for byte in master_seed.to_le_bytes() {
hash ^= byte as u64;
hash = hash.wrapping_mul(1099511628211);
}
for byte in domain.as_bytes() {
hash ^= *byte as u64;
hash = hash.wrapping_mul(1099511628211);
}
Self { state: hash }
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
self.state = self
.state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.state
}
#[inline]
pub fn next_f64(&mut self) -> f64 {
let val = self.next_u64();
(val >> 11) as f64 / (1u64 << 53) as f64
}
#[inline]
pub fn next_u64_below(&mut self, max: u64) -> u64 {
if max == 0 {
return 0;
}
self.next_u64() % max
}
#[inline]
pub fn next_u64_range(&mut self, min: u64, max: u64) -> u64 {
if min >= max {
return min;
}
let range = max - min + 1;
min + self.next_u64_below(range)
}
#[inline]
pub fn chance(&mut self, probability: f64) -> bool {
self.next_f64() < probability
}
pub fn shuffle<T>(&mut self, slice: &mut [T]) {
let len = slice.len();
if len < 2 {
return;
}
for i in (1..len).rev() {
let j = self.next_u64_below(i as u64 + 1) as usize;
slice.swap(i, j);
}
}
pub fn state(&self) -> u64 {
self.state
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deterministic_same_seed() {
let mut rng1 = DetRng::new(42);
let mut rng2 = DetRng::new(42);
let seq1: Vec<u64> = (0..100).map(|_| rng1.next_u64()).collect();
let seq2: Vec<u64> = (0..100).map(|_| rng2.next_u64()).collect();
assert_eq!(seq1, seq2, "Same seed must produce identical sequences");
}
#[test]
fn test_different_seeds_differ() {
let mut rng1 = DetRng::new(42);
let mut rng2 = DetRng::new(99);
let seq1: Vec<u64> = (0..10).map(|_| rng1.next_u64()).collect();
let seq2: Vec<u64> = (0..10).map(|_| rng2.next_u64()).collect();
assert_ne!(seq1, seq2);
}
#[test]
fn test_derive_produces_different_streams() {
let mut net = DetRng::derive(42, "network");
let mut disk = DetRng::derive(42, "storage");
let a = net.next_u64();
let b = disk.next_u64();
assert_ne!(a, b);
}
#[test]
fn test_derive_is_deterministic() {
let mut r1 = DetRng::derive(42, "network");
let mut r2 = DetRng::derive(42, "network");
let seq1: Vec<u64> = (0..50).map(|_| r1.next_u64()).collect();
let seq2: Vec<u64> = (0..50).map(|_| r2.next_u64()).collect();
assert_eq!(seq1, seq2);
}
#[test]
fn test_f64_in_range() {
let mut rng = DetRng::new(42);
for _ in 0..10_000 {
let v = rng.next_f64();
assert!((0.0..1.0).contains(&v), "f64 out of [0,1): {v}");
}
}
#[test]
fn test_range_in_bounds() {
let mut rng = DetRng::new(42);
for _ in 0..10_000 {
let v = rng.next_u64_range(10, 50);
assert!((10..=50).contains(&v), "Range out of [10,50]: {v}");
}
}
#[test]
fn test_chance_probability() {
let mut rng = DetRng::new(42);
let total = 10_000;
let hits: usize = (0..total).filter(|_| rng.chance(0.5)).count();
let rate = hits as f64 / total as f64;
assert!(
rate > 0.45 && rate < 0.55,
"50% chance should be ~50%, got {:.1}%",
rate * 100.0
);
}
#[test]
fn test_shuffle_deterministic() {
let mut rng1 = DetRng::new(42);
let mut rng2 = DetRng::new(42);
let mut a: Vec<i32> = (0..20).collect();
let mut b: Vec<i32> = (0..20).collect();
rng1.shuffle(&mut a);
rng2.shuffle(&mut b);
assert_eq!(a, b, "Same seed shuffle must match");
}
#[test]
fn test_shuffle_actually_shuffles() {
let mut rng = DetRng::new(42);
let original: Vec<i32> = (0..20).collect();
let mut shuffled = original.clone();
rng.shuffle(&mut shuffled);
assert_ne!(original, shuffled, "Shuffle should reorder elements");
}
}