use std::sync::OnceLock;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
mod fill;
#[allow(clippy::module_inception)]
mod simd_rng;
#[cfg(test)]
mod tests;
mod xoshiro;
pub use simd_rng::SimdRng;
pub use xoshiro::Xoshiro128PP8;
pub use xoshiro::Xoshiro256PP4;
use xoshiro::splitmix64_mix;
use xoshiro::splitmix64_next;
const SEED_GAMMA: u64 = 0x9e37_79b9_7f4a_7c15;
#[inline]
fn global_seed_counter() -> &'static AtomicU64 {
static SEED_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
SEED_COUNTER.get_or_init(|| AtomicU64::new(initial_seed()))
}
#[inline(always)]
fn next_global_seed() -> u64 {
let base = global_seed_counter().fetch_add(SEED_GAMMA, Ordering::Relaxed);
let mut seed = base;
splitmix64_next(&mut seed)
}
#[inline]
fn initial_seed() -> u64 {
let t = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let t_lo = t as u64;
let pid = std::process::id() as u64;
let x = 0u64;
let stack_addr = (&x as *const u64 as usize) as u64;
let text_addr = (initial_seed as fn() -> u64 as usize) as u64;
let mut seed =
t_lo ^ pid.rotate_left(11) ^ stack_addr.rotate_left(37) ^ text_addr.rotate_left(53);
splitmix64_next(&mut seed)
}
#[inline]
pub fn rng() -> SimdRng {
SimdRng::new()
}
#[inline]
pub fn derive_seed(state: &mut u64) -> u64 {
splitmix64_next(state)
}
pub trait SeedExt: Clone + Send + Sync + 'static {
fn rng(&self) -> SimdRng;
#[doc(hidden)]
fn derive(&self) -> Self;
fn rng_ext<R: SimdRngExt>(&self) -> R;
fn reseed(&self, _seed: u64) {}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Unseeded;
#[derive(Debug)]
pub struct Deterministic {
state: AtomicU64,
}
impl Deterministic {
#[inline]
pub const fn new(seed: u64) -> Self {
Self {
state: AtomicU64::new(seed),
}
}
#[inline(always)]
fn next_u64(&self) -> u64 {
let new_state = self
.state
.fetch_add(0x9e37_79b9_7f4a_7c15, Ordering::Relaxed)
.wrapping_add(0x9e37_79b9_7f4a_7c15);
splitmix64_mix(new_state)
}
#[inline]
pub fn current(&self) -> u64 {
self.state.load(Ordering::Relaxed)
}
#[inline]
pub fn reset(&self, seed: u64) {
self.state.store(seed, Ordering::Relaxed);
}
}
impl Clone for Deterministic {
fn clone(&self) -> Self {
Self::new(self.current())
}
}
impl SeedExt for Unseeded {
#[inline(always)]
fn rng(&self) -> SimdRng {
SimdRng::new()
}
#[inline(always)]
fn derive(&self) -> Self {
Unseeded
}
#[inline(always)]
fn rng_ext<R: SimdRngExt>(&self) -> R {
R::new()
}
}
impl SeedExt for Deterministic {
#[inline(always)]
fn rng(&self) -> SimdRng {
SimdRng::from_seed(self.next_u64())
}
#[inline(always)]
fn derive(&self) -> Self {
Deterministic::new(self.next_u64())
}
#[inline(always)]
fn rng_ext<R: SimdRngExt>(&self) -> R {
R::from_seed(self.next_u64())
}
#[inline(always)]
fn reseed(&self, seed: u64) {
self.reset(seed);
}
}
pub trait SimdRngExt: rand::RngCore + Sized + Send + 'static {
const HAS_PAIR_ILP: bool = false;
fn new() -> Self;
fn from_seed(seed: u64) -> Self;
fn next_i32x8(&mut self) -> wide::i32x8;
#[inline(always)]
fn next_i32x8_pair(&mut self) -> (wide::i32x8, wide::i32x8) {
(self.next_i32x8(), self.next_i32x8())
}
fn next_i32(&mut self) -> i32;
fn next_f64(&mut self) -> f64;
fn next_f32(&mut self) -> f32;
fn fill_uniform_f64(&mut self, out: &mut [f64]);
fn fill_uniform_f32(&mut self, out: &mut [f32]);
}
impl SimdRngExt for SimdRng {
#[inline(always)]
fn new() -> Self {
Self::new()
}
#[inline(always)]
fn from_seed(seed: u64) -> Self {
Self::from_seed(seed)
}
#[inline(always)]
fn next_i32x8(&mut self) -> wide::i32x8 {
self.next_i32x8()
}
#[inline(always)]
fn next_i32(&mut self) -> i32 {
self.next_i32()
}
#[inline(always)]
fn next_f64(&mut self) -> f64 {
self.next_f64()
}
#[inline(always)]
fn next_f32(&mut self) -> f32 {
self.next_f32()
}
#[inline(always)]
fn fill_uniform_f64(&mut self, out: &mut [f64]) {
self.fill_uniform_f64(out);
}
#[inline(always)]
fn fill_uniform_f32(&mut self, out: &mut [f32]) {
self.fill_uniform_f32(out);
}
}