use crate::C64;
#[inline]
pub(crate) 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)
}
pub fn fnv1a64(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01B3);
}
h
}
#[derive(Clone, Debug)]
pub struct Rng {
s: [u64; 4],
}
impl Rng {
pub fn new(seed: u64) -> Rng {
let mut st = seed;
let s = [splitmix64(&mut st), splitmix64(&mut st), splitmix64(&mut st), splitmix64(&mut st)];
Rng { s }
}
pub fn state(&self) -> [u64; 4] {
self.s
}
pub fn from_state(s: [u64; 4]) -> crate::Result<Rng> {
crate::ensure(s != [0; 4], || "the all-zero state is not a state of xoshiro256**".into())?;
Ok(Rng { s })
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let s = &mut self.s;
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
}
#[inline]
pub fn uniform(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 * (1.0 / 9_007_199_254_740_992.0)
}
#[inline]
pub fn below(&mut self, n: usize) -> usize {
debug_assert!(n >= 1, "Rng::below(0): the range 0..0 is empty");
((self.uniform() * n as f64) as usize).min(n.saturating_sub(1))
}
#[inline]
pub fn phase(&mut self) -> f64 {
self.uniform() * 2.0 * std::f64::consts::PI - std::f64::consts::PI
}
#[inline]
pub fn bit(&mut self) -> u8 {
(self.next_u64() >> 63) as u8
}
#[inline]
pub fn gaussian(&mut self) -> f64 {
let u1 = self.uniform();
let u2 = self.uniform();
let r = (-2.0 * (1.0 - u1).ln()).sqrt();
r * (2.0 * std::f64::consts::PI * u2).cos()
}
#[inline]
pub fn complex_gaussian(&mut self) -> C64 {
let u1 = self.uniform();
let u2 = self.uniform();
let r = (-2.0 * (1.0 - u1).ln()).sqrt() * std::f64::consts::FRAC_1_SQRT_2;
let (s, c) = (2.0 * std::f64::consts::PI * u2).sin_cos();
C64::new(r * c, r * s)
}
pub fn gamma(&mut self, shape: f64) -> f64 {
if !(shape > 0.0 && shape.is_finite()) {
return f64::NAN;
}
if shape < 1.0 {
let u = self.uniform();
return self.gamma(shape + 1.0) * (1.0 - u).powf(1.0 / shape);
}
let d = shape - 1.0 / 3.0;
let c = 1.0 / (9.0 * d).sqrt();
loop {
let x = self.gaussian();
let v = 1.0 + c * x;
if v <= 0.0 {
continue;
}
let v = v * v * v;
let u = 1.0 - self.uniform();
if u < 1.0 - 0.0331 * x * x * x * x || u.ln() < 0.5 * x * x + d * (1.0 - v + v.ln()) {
return d * v;
}
}
}
pub fn shuffle<T>(&mut self, v: &mut [T]) {
for i in (1..v.len()).rev() {
v.swap(i, self.below(i + 1));
}
}
pub fn complex_noise(&mut self, n: usize, noise_var: f64) -> Vec<C64> {
let s = noise_var.max(0.0).sqrt();
(0..n).map(|_| self.complex_gaussian() * s).collect()
}
}
pub type SimRng = Rng;
pub fn batch_rng(master_seed: u64, batch: u64) -> SimRng {
stream(master_seed, "", 0, batch)
}
pub fn member(seed: u64, index: u64) -> Rng {
Rng::new(seed ^ index.wrapping_mul(0x9E37_79B9_7F4A_7C15))
}
pub fn stream(base_seed: u64, experiment: &str, condition: u64, trial: u64) -> Rng {
let mut st = base_seed ^ fnv1a64(experiment.as_bytes());
let a = splitmix64(&mut st);
let mut st = a ^ condition;
let b = splitmix64(&mut st);
let mut st = b ^ trial;
let c = splitmix64(&mut st);
Rng::new(c)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "is empty")]
fn below_zero_is_a_broken_invariant_in_debug_builds() {
let _ = Rng::new(1).below(0);
}
#[test]
fn a_saved_state_resumes_the_draws() {
let mut a = Rng::new(42);
a.next_u64();
let mut b = Rng::from_state(a.state()).unwrap();
assert_eq!((0..5).map(|_| a.next_u64()).collect::<Vec<_>>(), (0..5).map(|_| b.next_u64()).collect::<Vec<_>>());
assert!(Rng::from_state([0; 4]).is_err());
}
#[test]
fn reference_values() {
assert_eq!(Rng::new(0).next_u64(), 0x99EC_5F36_CB75_F2B4);
assert_eq!(fnv1a64(b"a"), 0xAF63_DC4C_8601_EC8C);
}
#[test]
fn a_phase_is_the_uniform_draw_scaled_to_minus_pi_pi() {
let (mut a, mut b) = (Rng::new(3), Rng::new(3));
for _ in 0..1000 {
let pi = std::f64::consts::PI;
assert_eq!(a.phase().to_bits(), (b.uniform() * 2.0 * pi - pi).to_bits());
}
}
#[test]
fn moments() {
let mut r = Rng::new(7);
let n = 200_000;
let (mut m, mut v, mut b) = (0.0, 0.0, 0u64);
for _ in 0..n {
let z = r.complex_gaussian();
m += z.re;
v += z.norm_sqr();
b += r.bit() as u64;
}
assert!((m / n as f64).abs() < 0.01);
assert!((v / n as f64 - 1.0).abs() < 0.01);
assert!((b as f64 / n as f64 - 0.5).abs() < 0.01);
}
}