use devela::convert::{u64_from_u16_le, u64_from_u32_le, u64_from_u8_le};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct XorShift64(u64);
impl Default for XorShift64 {
fn default() -> Self {
Self::new_unchecked(Self::DEFAULT_SEED)
}
}
impl XorShift64 {
const DEFAULT_SEED: u64 = 0xDEFA0017_DEFA0017;
#[cold]
#[inline]
const fn cold_path_result() -> Option<Self> {
None
}
#[cold]
#[inline]
#[allow(dead_code)]
const fn cold_path_default() -> Self {
Self::new_unchecked(Self::DEFAULT_SEED)
}
}
impl XorShift64 {
#[inline]
#[must_use]
pub const fn new(seed: u64) -> Option<Self> {
if seed == 0 {
Self::cold_path_result()
} else {
Some(Self(seed))
}
}
#[inline]
#[must_use]
pub const fn new_unchecked(seed: u64) -> Self {
debug_assert![seed != 0, "Seed must be non-zero"];
Self(seed)
}
#[inline(always)]
#[must_use]
pub const fn current_u16(&self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
#[inline]
#[must_use]
pub const fn next_new(&self) -> Self {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
Self(x)
}
}
impl XorShift64 {
#[inline]
pub const fn new1_u64(seed: u64) -> Option<Self> {
Self::new(seed)
}
#[inline]
pub const fn new2_u32(seeds: [u32; 2]) -> Option<Self> {
Self::new(u64_from_u32_le(seeds))
}
#[inline]
pub const fn new4_u16(seeds: [u16; 4]) -> Option<Self> {
Self::new(u64_from_u16_le(seeds))
}
#[inline]
pub const fn new8_u8(seeds: [u8; 8]) -> Option<Self> {
Self::new(u64_from_u8_le(seeds))
}
}
#[cfg(feature = "rand_core")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "rand_core")))]
mod impl_rand {
use super::XorShift64;
use rand_core::{Error, RngCore, SeedableRng};
impl RngCore for XorShift64 {
fn next_u32(&mut self) -> u32 {
(self.next_u64() & 0xFFFF_FFFF) as u32
}
fn next_u64(&mut self) -> u64 {
self.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut i = 0;
while i < dest.len() {
let random_u64 = self.next_u64();
let bytes = random_u64.to_le_bytes();
let remaining = dest.len() - i;
if remaining >= 8 {
dest[i..i + 8].copy_from_slice(&bytes);
i += 8;
} else {
dest[i..].copy_from_slice(&bytes[..remaining]);
break;
}
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl SeedableRng for XorShift64 {
type Seed = [u8; 8];
fn from_seed(seed: Self::Seed) -> Self {
if seed == [0; 8] {
Self::cold_path_default()
} else {
Self::new_unchecked(u64::from_le_bytes(seed))
}
}
}
}