use core::convert::Infallible;
use rand::rand_core::{SeedableRng, TryRng};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct Xoshiro256PlusPlus {
state: [u64; 4],
}
#[inline]
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)
}
impl Xoshiro256PlusPlus {
pub fn from_seed(seed: [u8; 32]) -> Self {
let mut sm = u64::from_le_bytes([
seed[0], seed[1], seed[2], seed[3], seed[4], seed[5],
seed[6], seed[7],
]) ^ u64::from_le_bytes([
seed[8], seed[9], seed[10], seed[11], seed[12], seed[13],
seed[14], seed[15],
]) ^ u64::from_le_bytes([
seed[16], seed[17], seed[18], seed[19], seed[20], seed[21],
seed[22], seed[23],
]) ^ u64::from_le_bytes([
seed[24], seed[25], seed[26], seed[27], seed[28], seed[29],
seed[30], seed[31],
]);
if sm == 0 {
sm = 0xBAD1_DEAA_5BD1_CAFE;
}
let s0 = splitmix64(&mut sm);
let s1 = splitmix64(&mut sm);
let s2 = splitmix64(&mut sm);
let s3 = splitmix64(&mut sm);
Self {
state: [s0, s1, s2, s3],
}
}
pub fn from_u64_seed(mut seed: u64) -> Self {
if seed == 0 {
seed = 0xBAD1_DEAA_5BD1_CAFE;
}
let s0 = splitmix64(&mut seed);
let s1 = splitmix64(&mut seed);
let s2 = splitmix64(&mut seed);
let s3 = splitmix64(&mut seed);
Self {
state: [s0, s1, s2, s3],
}
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
let res = self.state[0]
.wrapping_add(self.state[3])
.rotate_left(23)
.wrapping_add(self.state[0]);
let t = self.state[1] << 17;
self.state[2] ^= self.state[0];
self.state[3] ^= self.state[1];
self.state[1] ^= self.state[2];
self.state[0] ^= self.state[3];
self.state[2] ^= t;
self.state[3] = self.state[3].rotate_left(45);
res
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut i = 0;
while i + 8 <= dest.len() {
let bytes = self.next_u64().to_le_bytes();
dest[i..i + 8].copy_from_slice(&bytes);
i += 8;
}
if i < dest.len() {
let bytes = self.next_u64().to_le_bytes();
let remaining = dest.len() - i;
dest[i..].copy_from_slice(&bytes[..remaining]);
}
}
pub fn jump(&mut self) {
const JUMP: [u64; 4] = [
0x180E_C6D3_3CFD_0ABA,
0xD5A6_1266_F0C9_392C,
0xA958_2618_E03F_C9AA,
0x39AB_DC45_29B1_661C,
];
let mut s0 = 0u64;
let mut s1 = 0u64;
let mut s2 = 0u64;
let mut s3 = 0u64;
for &jump in &JUMP {
for b in 0..64 {
if (jump & (1u64 << b)) != 0 {
s0 ^= self.state[0];
s1 ^= self.state[1];
s2 ^= self.state[2];
s3 ^= self.state[3];
}
let _ = self.next_u64();
}
}
self.state = [s0, s1, s2, s3];
}
}
impl TryRng for Xoshiro256PlusPlus {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.next_u32())
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
Ok(self.next_u64())
}
fn try_fill_bytes(
&mut self,
dest: &mut [u8],
) -> Result<(), Self::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl SeedableRng for Xoshiro256PlusPlus {
type Seed = [u8; 32];
fn from_seed(seed: Self::Seed) -> Self {
Self::from_seed(seed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn produces_nonzero_from_zero_seed() {
let mut rng = Xoshiro256PlusPlus::from_seed([0u8; 32]);
assert_ne!(rng.next_u64(), 0);
}
#[test]
fn produces_nonzero_from_one_seed() {
let mut rng = Xoshiro256PlusPlus::from_seed([1u8; 32]);
assert_ne!(rng.next_u64(), 0);
}
#[test]
fn deterministic_for_same_seed() {
let seed = [42u8; 32];
let mut a = Xoshiro256PlusPlus::from_seed(seed);
let mut b = Xoshiro256PlusPlus::from_seed(seed);
for _ in 0..16 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn jump_diverges_from_baseline() {
let seed = [123u8; 32];
let mut a = Xoshiro256PlusPlus::from_seed(seed);
let mut b = Xoshiro256PlusPlus::from_seed(seed);
a.jump();
assert_ne!(a.next_u64(), b.next_u64());
}
#[test]
fn fill_bytes_handles_unaligned_lengths() {
let mut rng = Xoshiro256PlusPlus::from_seed([7u8; 32]);
let mut buf = [0u8; 17]; rng.fill_bytes(&mut buf);
assert!(buf.iter().any(|&b| b != 0));
}
#[test]
fn next_u32_uses_high_bits() {
let rng = Xoshiro256PlusPlus::from_seed([99u8; 32]);
let high = (rng.clone().next_u64() >> 32) as u32;
let mut copy = rng;
assert_eq!(copy.next_u32(), high);
}
}
#[cfg(test)]
mod additional_xoshiro_tests {
use super::*;
#[test]
fn test_xoshiro_from_u64_seed() {
let mut rng = Xoshiro256PlusPlus::from_u64_seed(123);
let mut rng2 = Xoshiro256PlusPlus::from_u64_seed(123);
assert_eq!(rng.next_u64(), rng2.next_u64());
}
}
#[cfg(test)]
mod xoshiro_coverage {
use super::*;
#[cfg(feature = "alloc")]
use alloc::format;
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std::format;
#[test]
fn test_xoshiro_fill_bytes_large() {
let mut rng = Xoshiro256PlusPlus::from_u64_seed(42);
let mut buf = [0u8; 100];
rng.fill_bytes(&mut buf);
assert!(buf.iter().any(|&x| x != 0));
}
#[test]
fn test_xoshiro_next_u32() {
let mut rng = Xoshiro256PlusPlus::from_u64_seed(42);
let _ = rng.next_u32();
}
#[test]
fn test_xoshiro_debug_display() {
let rng = Xoshiro256PlusPlus::from_u64_seed(42);
let s = format!("{:?}", rng);
assert!(s.contains("Xoshiro256PlusPlus"));
}
}
#[cfg(test)]
mod xoshiro_final_coverage {
use super::*;
#[test]
fn test_xoshiro_try_next() {
let mut rng = Xoshiro256PlusPlus::from_u64_seed(42);
assert!(rng.try_next_u32().is_ok());
assert!(rng.try_next_u64().is_ok());
}
#[test]
fn test_xoshiro_seedable_rng() {
let seed = [1u8; 32];
let mut rng =
<Xoshiro256PlusPlus as SeedableRng>::from_seed(seed);
assert_ne!(rng.next_u64(), 0);
}
}
#[cfg(test)]
mod xoshiro_zero_test {
use super::*;
#[test]
fn test_xoshiro_all_zero_seed() {
let seed = [0u8; 32];
let mut rng = Xoshiro256PlusPlus::from_seed(seed);
assert_ne!(rng.next_u64(), 0);
}
}
#[cfg(test)]
mod xoshiro_edge_cases {
use super::*;
#[test]
fn test_xoshiro_from_u64_seed_zero() {
let mut rng = Xoshiro256PlusPlus::from_u64_seed(0);
assert_ne!(rng.next_u64(), 0);
}
}