#[cfg(feature = "float")]
const INV_2POW24: f32 = f32::from_bits(0x3380_0000);
#[cfg(feature = "float")]
const INV_2POW53: f64 = f64::from_bits(0x3ca0_0000_0000_0000);
pub trait CoreRNG: Sized {
fn new(seed: u64) -> Self;
#[cfg(feature = "osseed")]
fn seeded() -> Result<Self, getrandom::Error> {
let seed = getrandom::u64()?;
Ok(Self::new(seed))
}
#[doc(hidden)]
fn next(&mut self) -> u64;
#[allow(clippy::cast_possible_truncation)]
#[doc(hidden)]
fn next_lim_u64(&mut self, max_out: u64) -> u64 {
let cutoff = max_out.wrapping_neg().checked_rem(max_out).unwrap_or(0);
let mut full;
let mut low;
loop {
full = (u128::from(self.next())).wrapping_mul(u128::from(max_out));
low = full as u64;
if low >= cutoff {
break;
}
}
(full >> 64) as u64
}
#[allow(clippy::cast_possible_truncation)]
#[doc(hidden)]
fn next_lim_u32(&mut self, max_out: u32) -> u32 {
let cutoff = max_out.wrapping_neg().checked_rem(max_out).unwrap_or(0);
let mut full;
let mut low;
loop {
full = (self.next()).wrapping_mul(u64::from(max_out));
low = full as u32;
if low >= cutoff {
break;
}
}
(full >> 32) as u32
}
#[inline]
fn reseed(&mut self, seed: u64) {
*self = Self::new(seed);
}
#[inline]
fn generate_int<T: PrimitiveInteger>(&mut self) -> T {
T::truncate_from_u64(self.next())
}
fn generate_int_lim<T: PrimitiveInteger>(&mut self, max_out: T) -> T {
if max_out < T::TWO {
return T::ZERO;
}
T::truncate_from_u64(self.next_lim_u64(T::cast_to_u64(max_out)))
}
fn generate_int_range<T: PrimitiveInteger>(&mut self, min_out: T, max_out: T) -> T {
let delta = max_out.saturating_sub(&min_out);
if delta < T::TWO {
return min_out;
}
min_out + self.generate_int_lim::<T>(delta)
}
#[inline]
fn generate_u128(&mut self) -> u128 {
let upper = u128::from(self.next());
let lower = u128::from(self.next());
(upper << 64) | lower
}
#[inline]
#[allow(clippy::cast_possible_wrap)]
fn generate_i128(&mut self) -> i128 {
let upper = u128::from(self.next());
let lower = u128::from(self.next());
((upper << 64) | lower) as i128
}
#[inline]
fn generate_bool(&mut self) -> bool {
(self.next() & 1) != 0
}
fn fill_bytes(&mut self, destination: &mut [u8]) {
for block in destination.chunks_mut(8) {
block.copy_from_slice(&self.next().to_le_bytes()[0..block.len()]);
}
}
fn fill<T: PrimitiveInteger>(&mut self, destination: &mut [T]) {
for element in destination {
*element = self.generate_int::<T>();
}
}
fn choice<'a, T>(&mut self, selection: &'a [T]) -> &'a T {
let index = self.generate_int_lim(selection.len());
&selection[index]
}
}
pub trait SeekableRNG: CoreRNG {
fn move_state_forwards(&mut self, delta: u64);
fn move_state_backwards(&mut self, delta: u64);
}
#[cfg(feature = "float")]
pub trait FloatRNG: CoreRNG {
#[inline]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_precision_loss)]
fn generate_f32(&mut self) -> f32 {
((self.next() as u32) >> 8) as f32 * INV_2POW24
}
#[inline]
#[allow(clippy::cast_precision_loss)]
fn generate_f64(&mut self) -> f64 {
(self.next() >> 11) as f64 * INV_2POW53
}
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn generate_any_f32(&mut self) -> f32 {
f32::from_bits(self.next() as u32)
}
#[inline]
fn generate_any_f64(&mut self) -> f64 {
f64::from_bits(self.next())
}
#[inline]
fn generate_weighted_bool(&mut self, chance: f32) -> bool {
self.generate_f32() < chance
}
}
pub trait PrimitiveInteger:
Sized + core::cmp::PartialOrd + core::ops::Add<Output = Self> + core::ops::Sub<Output = Self>
{
const N_U64: usize;
const SIZE_BYTES: usize;
const ONE: Self;
const ZERO: Self;
const TWO: Self;
fn truncate_from_u64(value: u64) -> Self;
fn truncate_from_u128(value: u128) -> Self;
fn cast_to_u64(value: Self) -> u64;
#[must_use]
fn saturating_sub(self, rhs: &Self) -> Self;
}
macro_rules! impl_primitive_int {
($($t:ty),*) => {
$(
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_lossless)]
#[allow(clippy::cast_possible_wrap)]
impl PrimitiveInteger for $t {
const SIZE_BYTES: usize = core::mem::size_of::<$t>();
const N_U64: usize = (u64::BITS / <$t>::BITS) as usize;
const ONE: Self = 1;
const ZERO: Self = 0;
const TWO: Self = 2;
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn truncate_from_u128(value: u128) -> Self {
value as $t
}
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn truncate_from_u64(value: u64) -> Self {
value as $t
}
#[inline]
fn cast_to_u64(value: $t) -> u64 {
value as u64
}
#[inline]
fn saturating_sub(self, rhs: &Self) -> Self {
<$t>::saturating_sub(self, *rhs)
}
}
)*
};
}
impl_primitive_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
#[cfg(test)]
mod tests {
use super::*;
use crate::wyrand::WyRand;
#[test]
fn test_float_distribuition() {
assert!((u64::MAX >> 11) as f64 * INV_2POW53 < 1.0);
assert!((u32::MAX >> 8) as f32 * INV_2POW24 < 1.0);
}
#[test]
fn test_lim_generation() {
let mut prng = WyRand::new(0xDEADBEEF);
assert_eq!(0, prng.next_lim_u64(0));
}
}