use num_traits::Float;
use rand_core::{RngCore, SeedableRng};
use rand_xoshiro::Xoshiro256PlusPlus;
pub(crate) struct Rng(Xoshiro256PlusPlus);
impl Rng {
pub fn new(seed: u64) -> Self {
Self(Xoshiro256PlusPlus::seed_from_u64(seed))
}
pub fn for_replica(seed: u64, index: u64) -> Self {
let mixed = seed
.wrapping_add(index.wrapping_mul(0x9E37_79B9_7F4A_7C15))
.rotate_left(31)
.wrapping_mul(0xBF58_476D_1CE4_E5B9);
Self::new(mixed)
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
#[inline]
pub fn uniform(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 * (1.0 / (1u64 << 53) as f64)
}
#[inline]
pub fn uniform_symmetric<T: Float>(&mut self, half_width: T) -> T {
let u = T::from(self.uniform() * 2.0 - 1.0).unwrap();
u * half_width
}
#[inline]
pub fn below(&mut self, n: usize) -> usize {
((self.next_u64() as u128 * n as u128) >> 64) as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_for_same_seed() {
let mut a = Rng::new(42);
let mut b = Rng::new(42);
for _ in 0..100 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn replica_streams_differ() {
let mut a = Rng::for_replica(42, 0);
let mut b = Rng::for_replica(42, 1);
assert_ne!(a.next_u64(), b.next_u64());
}
#[test]
fn uniform_in_range() {
let mut rng = Rng::new(7);
for _ in 0..1000 {
let u = rng.uniform();
assert!((0.0..1.0).contains(&u));
}
for _ in 0..1000 {
let i = rng.below(13);
assert!(i < 13);
}
}
}