use core::cell::Cell;
thread_local! {
static STATE: Cell<u64> = Cell::new(fresh());
}
fn fresh() -> u64 {
let local = 0u8;
let addr = &local as *const u8 as u64;
let time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_nanos() as u64);
(addr ^ time.rotate_left(32)) | 1
}
fn mix(mut z: u64) -> u64 {
z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn next() -> u64 {
STATE
.try_with(|s| {
let mut x = s.get();
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
s.set(x);
x
})
.unwrap_or_else(|_| fresh())
}
pub(crate) fn random_below(n: usize) -> usize {
((u128::from(next()) * n as u128) >> 64) as usize
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RngSeed(u64);
impl RngSeed {
pub fn new() -> Self {
Self(mix(next()))
}
pub fn from_bytes(bytes: &[u8]) -> Self {
let hash = bytes.iter().fold(0xCBF2_9CE4_8422_2325u64, |h, &b| {
(h ^ u64::from(b)).wrapping_mul(0x0000_0100_0000_01B3)
});
Self(mix(hash))
}
}
impl Default for RngSeed {
fn default() -> Self {
Self::new()
}
}
pub(crate) struct Reseeded(Option<u64>);
impl Reseeded {
pub(crate) fn new(seed: RngSeed, thread: u64) -> Self {
let state = mix(seed.0 ^ mix(thread)) | 1;
Self(STATE.try_with(|s| s.replace(state)).ok())
}
}
impl Drop for Reseeded {
fn drop(&mut self) {
if let Some(previous) = self.0 {
let _ = STATE.try_with(|s| s.set(previous));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_seed_draws_the_same_numbers_each_time_and_is_undone() {
let before: Vec<u64> = {
let _seeded = Reseeded::new(RngSeed::from_bytes(b"x"), 3);
(0..8).map(|_| next()).collect()
};
let again: Vec<u64> = {
let _seeded = Reseeded::new(RngSeed::from_bytes(b"x"), 3);
(0..8).map(|_| next()).collect()
};
assert_eq!(before, again);
let other_thread: Vec<u64> = {
let _seeded = Reseeded::new(RngSeed::from_bytes(b"x"), 4);
(0..8).map(|_| next()).collect()
};
assert_ne!(before, other_thread);
let other_seed: Vec<u64> = {
let _seeded = Reseeded::new(RngSeed::from_bytes(b"y"), 3);
(0..8).map(|_| next()).collect()
};
assert_ne!(before, other_seed);
let after: Vec<u64> = (0..8).map(|_| next()).collect();
assert_ne!(before, after);
}
#[test]
fn numbers_stay_below_the_bound() {
for n in [1, 2, 3, 7, 64, 1000] {
assert!((0..1000).all(|_| random_below(n) < n));
}
assert_ne!(RngSeed::new(), RngSeed::new());
}
}