urng 0.12.0

Universal Random Number Generator
Documentation
use wrapn::Wrap;

use crate::{_internal::impl_seed, SplitMix64, rng::Rng64};

// --- TwistedGFSR ---

/// A Twisted Generalized Feedback Shift Register (TGFSR) generator.
///
/// # Examples
///
/// ```
/// use urng::*;
///
/// let mut rng = TwistedGFSR::new(0);
/// let _ = rng.nextu();
/// ```
#[repr(C, align(64))]
pub struct TwistedGFSR {
    seed: [u32; N_GFSR],
    index: Wrap<usize>,
}

const N_GFSR: usize = 25;
const M_GFSR: usize = 7;
const MAG01: [u32; 2] = [0x0, 0x8ebf_d028];

impl TwistedGFSR {
    /// Creates a new `TwistedGFSR` instance.
    pub fn new(seed: u64) -> Self {
        let mut seedgen = SplitMix64::new(seed);
        let mut seeds = [0u32; N_GFSR];
        for s in seeds.iter_mut() {
            *s = seedgen.nextu() as u32;
        }
        Self {
            seed: seeds,
            index: N_GFSR.into(),
        }
    }

    fn twist(&mut self) {
        for k in 0..(N_GFSR - M_GFSR) {
            self.seed[k] =
                self.seed[k + M_GFSR] ^ (self.seed[k] >> 1) ^ MAG01[(self.seed[k] & 1) as usize];
        }
        for k in (N_GFSR - M_GFSR)..N_GFSR {
            self.seed[k] = self.seed[k + M_GFSR - N_GFSR]
                ^ (self.seed[k] >> 1)
                ^ MAG01[(self.seed[k] & 1) as usize];
        }
        self.index = 0.into();
    }
}

impl_seed!(TwistedGFSR, 64);

impl Rng64 for TwistedGFSR {
    #[inline]
    fn nextu(&mut self) -> u64 {
        if self.index >= N_GFSR {
            self.twist();
        }
        let mut y = self.seed[self.index.value()];
        y ^= (y << 7) & 0x2b5b_2500;
        y ^= (y << 15) & 0xdb8b_0000;
        y ^= y >> 16;
        self.index += 1;
        u64::from(y)
    }

    #[inline(always)]
    fn nextf(&mut self) -> f64 {
        self.nextu() as f64 * (1.0 / (u32::MAX as f64 + 1.0))
    }

    #[inline(always)]
    fn randi(&mut self, min: i64, max: i64) -> i64 {
        let range = (max as i128 - min as i128 + 1) as u128;
        ((self.nextu() as u128 * range) >> 32) as i64 + min
    }

    #[inline(always)]
    fn randf(&mut self, min: f64, max: f64) -> f64 {
        min + self.nextf() * (max - min)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    crate::safe_test!(TwistedGFSR);
}