use ferray_core::FerrayError;
mod mt19937;
mod pcg64;
mod pcg64dxsm;
mod philox;
mod seed_sequence;
mod sfc64;
mod xoshiro256;
pub(crate) const 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 use mt19937::MT19937;
pub use pcg64::Pcg64;
pub use pcg64dxsm::Pcg64Dxsm;
pub use philox::Philox;
pub use seed_sequence::SeedSequence;
pub use sfc64::Sfc64;
pub use xoshiro256::Xoshiro256StarStar;
pub trait BitGenerator: Send {
fn next_u64(&mut self) -> u64;
fn seed_from_u64(seed: u64) -> Self
where
Self: Sized;
fn jump(&mut self) -> Option<()>;
fn stream(seed: u64, stream_id: u64) -> Option<Self>
where
Self: Sized;
fn next_f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 * (1.0 / (1u64 << 53) as f64)
}
fn next_f32(&mut self) -> f32 {
(self.next_u64() >> 40) as f32 * (1.0 / (1u64 << 24) as f32)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut i = 0;
while i + 8 <= dest.len() {
let val = self.next_u64();
dest[i..i + 8].copy_from_slice(&val.to_le_bytes());
i += 8;
}
if i < dest.len() {
let val = self.next_u64();
let bytes = val.to_le_bytes();
for (j, byte) in dest[i..].iter_mut().enumerate() {
*byte = bytes[j];
}
}
}
fn state_bytes(&self) -> Result<Vec<u8>, FerrayError> {
Err(FerrayError::invalid_value(
"this BitGenerator does not implement state_bytes",
))
}
fn set_state_bytes(&mut self, bytes: &[u8]) -> Result<(), FerrayError> {
let _ = bytes;
Err(FerrayError::invalid_value(
"this BitGenerator does not implement set_state_bytes",
))
}
fn next_u64_bounded(&mut self, bound: u64) -> u64 {
if bound == 0 {
return 0;
}
let mut x = self.next_u64();
let mut m = (x as u128) * (bound as u128);
let mut l = m as u64;
if l < bound {
let threshold = bound.wrapping_neg() % bound;
while l < threshold {
x = self.next_u64();
m = (x as u128) * (bound as u128);
l = m as u64;
}
}
(m >> 64) as u64
}
}
#[cfg(test)]
mod state_tests {
use super::*;
fn roundtrip<B: BitGenerator + Sized>(make: impl Fn() -> B, expected_size: usize) {
let mut a = make();
for _ in 0..7 {
a.next_u64();
}
let snapshot = a.state_bytes().unwrap();
assert_eq!(
snapshot.len(),
expected_size,
"{} expected state size {expected_size}, got {}",
std::any::type_name::<B>(),
snapshot.len()
);
let mut from_a: Vec<u64> = Vec::with_capacity(64);
for _ in 0..64 {
from_a.push(a.next_u64());
}
let mut b = make();
b.set_state_bytes(&snapshot).unwrap();
let mut from_b: Vec<u64> = Vec::with_capacity(64);
for _ in 0..64 {
from_b.push(b.next_u64());
}
assert_eq!(
from_a,
from_b,
"round-trip diverged for {}",
std::any::type_name::<B>()
);
}
#[test]
fn roundtrip_xoshiro256() {
roundtrip(|| Xoshiro256StarStar::seed_from_u64(42), 32);
}
#[test]
fn roundtrip_sfc64() {
roundtrip(|| Sfc64::seed_from_u64(0xc0ffee), 32);
}
#[test]
fn roundtrip_pcg64() {
roundtrip(|| Pcg64::seed_from_u64(7), 32);
}
#[test]
fn roundtrip_pcg64dxsm() {
roundtrip(|| Pcg64Dxsm::seed_from_u64(7), 32);
}
#[test]
fn roundtrip_philox() {
roundtrip(|| Philox::seed_from_u64(123), 44);
}
#[test]
fn roundtrip_mt19937() {
roundtrip(|| MT19937::seed_from_u64(2026), 312 * 8 + 8);
}
#[test]
fn xoshiro_rejects_all_zero_state() {
let mut g = Xoshiro256StarStar::seed_from_u64(0);
let zeros = vec![0u8; 32];
assert!(g.set_state_bytes(&zeros).is_err());
}
#[test]
fn pcg64_rejects_even_inc() {
let mut g = Pcg64::seed_from_u64(0);
let mut bad = vec![0u8; 32];
bad[16] = 2; assert!(g.set_state_bytes(&bad).is_err());
}
#[test]
fn philox_rejects_oversize_buf_idx() {
let mut g = Philox::seed_from_u64(0);
let mut bad = vec![0u8; 44];
bad[40] = 5; assert!(g.set_state_bytes(&bad).is_err());
}
#[test]
fn wrong_length_returns_error() {
let mut g = Xoshiro256StarStar::seed_from_u64(0);
assert!(g.set_state_bytes(&[0u8; 16]).is_err());
}
}