use sha2::{Digest, Sha256};
use crate::{Error, Result};
const F64_UNIT: f64 = 1.0 / 9_007_199_254_740_992.0;
const NONZERO_STATE: [u64; 4] = [
0x9E37_79B9_7F4A_7C15,
0xBF58_476D_1CE4_E5B9,
0x94D0_49BB_1331_11EB,
0x2545_F491_4F6C_DD1D,
];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Rng {
state: [u64; 4],
}
impl Rng {
#[must_use]
pub fn from_seed(seed: &str) -> Self {
let digest: [u8; 32] = Sha256::digest(seed.as_bytes()).into();
Self::from_bytes(&digest)
}
pub fn from_entropy() -> Result<Self> {
let mut bytes = [0u8; 32];
getrandom::fill(&mut bytes).map_err(Error::Entropy)?;
Ok(Self::from_bytes(&bytes))
}
#[must_use]
pub fn from_state(state: [u64; 4]) -> Self {
if state == [0; 4] {
Self {
state: NONZERO_STATE,
}
} else {
Self { state }
}
}
fn from_bytes(bytes: &[u8; 32]) -> Self {
let mut state = [0u64; 4];
let (chunks, _) = bytes.as_chunks::<8>();
for (word, chunk) in state.iter_mut().zip(chunks) {
*word = u64::from_le_bytes(*chunk);
}
Self::from_state(state)
}
pub fn next_u64(&mut self) -> u64 {
let s = &mut self.state;
let result = s[1].wrapping_mul(5).rotate_left(7).wrapping_mul(9);
let t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = s[3].rotate_left(45);
result
}
pub fn next_f64(&mut self) -> f64 {
let bits = self.next_u64() >> 11;
let high = u32::try_from(bits >> 32).map_or(0.0, f64::from);
let low = u32::try_from(bits & 0xFFFF_FFFF).map_or(0.0, f64::from);
(high * 4_294_967_296.0 + low) * F64_UNIT
}
}