use core::convert::Infallible;
use rand_core::{Rng, SeedableRng, TryRng, utils};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[allow(missing_copy_implementations)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Sfc32 {
a: u32,
b: u32,
c: u32,
weyl: u32,
}
const BARREL_SHIFT: u32 = 21;
const RSHIFT: u32 = 9;
const LSHIFT: u32 = 3;
const WEYL_INC: u32 = 1;
impl TryRng for Sfc32 {
type Error = Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let old_b = self.b;
let old_c = self.c;
let old_weyl = self.weyl;
let result = self.a.wrapping_add(old_b).wrapping_add(old_weyl);
self.a = old_b ^ (old_b >> RSHIFT);
self.b = old_c.wrapping_add(old_c << LSHIFT);
self.c = result.wrapping_add(old_c.rotate_left(BARREL_SHIFT));
self.weyl = old_weyl.wrapping_add(WEYL_INC);
Ok(result)
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
utils::next_u64_via_u32(self)
}
#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
utils::fill_bytes_via_next_word(dest, || self.try_next_u32())
}
}
const SEED_MIXING_STEPS: u32 = 15;
impl SeedableRng for Sfc32 {
type Seed = [u8; 12];
fn from_seed(seed: [u8; 12]) -> Sfc32 {
let s: [_; 3] = utils::read_words(&seed);
let mut rng = Sfc32 {
a: s[0],
b: s[1],
c: s[2],
weyl: WEYL_INC,
};
for _ in 0..SEED_MIXING_STEPS {
rng.next_u32();
}
rng
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn u64_seed() {
let reference_rng = Sfc32 {
a: 0x09BC85E1,
b: 0x5A96CB07,
c: 0xB53C149C,
weyl: 0x10,
};
let test_rng = Sfc32::seed_from_u64(1);
assert_eq!(test_rng, reference_rng)
}
#[test]
fn reference() {
let mut rng = Sfc32::from_seed([0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]);
#[rustfmt::skip]
let expected: [u32; 16] = [
0x03B80BB8, 0xA87DBC7E, 0x1787178C, 0x4C7B7234,
0xC65DADE2, 0x2C692349, 0xF52C2153, 0xDF098072,
0x9D49B03C, 0x9562381A, 0xC9B41738, 0x64B75E54,
0x36CE9B32, 0xF106947E, 0x0AFC726B, 0x549BBC87,
];
for &e in &expected {
assert_eq!(rng.next_u32(), e);
}
}
}