use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::SystemTime;
static GLOBAL_COUNTER: AtomicU64 = AtomicU64::new(0x853c_49e6_748f_ea9b);
#[derive(Clone, Copy, Debug)]
pub(crate) struct Xoshiro256PlusPlus {
s: [u64; 4],
}
impl Xoshiro256PlusPlus {
pub fn from_seed(mut seed: u64) -> Self {
fn splitmix64(state: &mut u64) -> u64 {
*state = state.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut z = *state;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
if seed == 0 {
seed = 0x9e37_79b9_7f4a_7c15;
}
let mut sm = seed;
let s0 = splitmix64(&mut sm);
let s1 = splitmix64(&mut sm);
let s2 = splitmix64(&mut sm);
let s3 = splitmix64(&mut sm);
Self {
s: [s0, s1, s2, s3],
}
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let result = (self.s[0].wrapping_add(self.s[3]))
.rotate_left(23)
.wrapping_add(self.s[0]);
let t = self.s[1] << 17;
self.s[2] ^= self.s[0];
self.s[3] ^= self.s[1];
self.s[1] ^= self.s[2];
self.s[0] ^= self.s[3];
self.s[2] ^= t;
self.s[3] = self.s[3].rotate_left(45);
result
}
#[inline]
pub fn next_f64(&mut self) -> f64 {
let val = self.next_u64() >> 11;
(val as f64) * (1.0 / ((1u64 << 53) as f64))
}
}
fn generate_entropy() -> u64 {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0x1234_5678_9abc_def0);
let counter = GLOBAL_COUNTER.fetch_add(0x9e37_79b9_7f4a_7c15, Ordering::Relaxed);
let ptr_entropy = &GLOBAL_COUNTER as *const _ as usize as u64;
let pid = std::process::id() as u64;
now ^ counter.rotate_left(17) ^ ptr_entropy.rotate_left(31) ^ pid.rotate_left(43)
}
thread_local! {
static RNG: Cell<Xoshiro256PlusPlus> = Cell::new(Xoshiro256PlusPlus::from_seed(generate_entropy()));
}
#[inline]
pub fn random() -> f64 {
RNG.with(|rng| {
let mut state = rng.get();
let val = state.next_f64();
rng.set(state);
val
})
}
pub fn seed_random(seed: u64) {
RNG.with(|rng| {
rng.set(Xoshiro256PlusPlus::from_seed(seed));
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_range() {
for _ in 0..10_000 {
let val = random();
assert!(val >= 0.0);
assert!(val < 1.0);
}
}
#[test]
fn test_reproducible_seeding() {
seed_random(42);
let a1 = random();
let a2 = random();
seed_random(42);
let b1 = random();
let b2 = random();
assert_eq!(a1, b1);
assert_eq!(a2, b2);
}
}