urng 0.10.0

Universal Random Number Generator
Documentation
use crate::wide::{impl_methods, wide_rotate_left};
use crate::{Rng32, SplitMix32};
use ::wide::{u32x4, u32x8, u32x16};

macro_rules! impl_variants {
    ($name:ident, $scrambler:tt, $size:expr) => {
        ::paste::paste! {
            #[doc = concat!(stringify!($name), " (xoshiro128) producing ", stringify!($size), " values per call via `wide` SIMD vectors.")]
            #[doc = ""]
            #[doc = "Portable-SIMD counterpart of the scalar xoshiro128 generators in `crate::rng32`."]
            #[doc = "A 128-bit state (four 32-bit words) per lane; each `nextu` call returns an array of"]
            #[doc = "`u32`, one per lane."]
            #[doc = ""]
            #[doc = "# Example"]
            #[doc = "```"]
            #[doc = concat!("use urng::wide::", stringify!($name), "x", stringify!($size), ";")]
            #[doc = ""]
            #[doc = concat!("let mut rng = ", stringify!($name), "x", stringify!($size), "::new(1);")]
            #[doc = concat!("let v = rng.nextu();")]
            #[doc = concat!("assert_eq!(v.len(), ", stringify!($size), ");")]
            #[doc = "```"]
            #[allow(dead_code)]
            #[repr(C, align(64))]
            pub struct [<$name x $size>] {
                s0: [<u32x $size>],
                s1: [<u32x $size>],
                s2: [<u32x $size>],
                s3: [<u32x $size>],
            }

            #[allow(dead_code)]
            impl [<$name x $size>] {
                #[doc = "Creates a new generator, seeding the four state words of every lane from `seed`."]
                pub fn new(seed: u32) -> Self {
                    let mut seedgen = SplitMix32::new(seed);
                    Self {
                        s0: [<u32x $size>]::from([0u32; $size].map(|_| seedgen.nextu())),
                        s1: [<u32x $size>]::from([0u32; $size].map(|_| seedgen.nextu())),
                        s2: [<u32x $size>]::from([0u32; $size].map(|_| seedgen.nextu())),
                        s3: [<u32x $size>]::from([0u32; $size].map(|_| seedgen.nextu())),
                    }
                }

                #[doc = "Generates the next block of `u32` values, one per SIMD lane."]
                #[doc = ""]
                #[doc = "Applies the xoshiro128 state update and the selected (`++` or `**`) scrambler."]
                #[inline(always)]
                pub fn nextu(&mut self) -> [u32; $size] {
                    let res = impl_variants!(@scramble $scrambler, self.s0, self.s1, self.s3);
                    let t = self.s1 << 9;

                    self.s2 ^= self.s0;
                    self.s3 ^= self.s1;
                    self.s1 ^= self.s2;
                    self.s0 ^= self.s3;
                    self.s2 ^= t;
                    self.s3 = wide_rotate_left!(32 self.s3, 11);

                    bytemuck::cast(res)
                }

                impl_methods!($size, 32);
            }
        }
    };
    (@scramble pp, $s0:expr, $s1:expr, $s3:expr) => {
        wide_rotate_left!(32 ($s0 + $s3), 7) + $s0
    };
    (@scramble ss, $s0:expr, $s1:expr, $s3:expr) => {
        wide_rotate_left!(32 ($s1 * 5u32), 7) * 9u32
    };
    ($name:ident, $scrambler:tt; $($size:expr),+ $(,)*) => {
        $(impl_variants!($name, $scrambler, $size);)+
    };
}

impl_variants!(Xoshiro128Pp, pp; 4, 8, 16);
impl_variants!(Xoshiro128Ss, ss; 4, 8, 16);

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

    crate::safe_test!(Xoshiro128Ppx4);
    crate::safe_test!(Xoshiro128Ppx8);
    crate::safe_test!(Xoshiro128Ppx16);
    crate::safe_test!(Xoshiro128Ssx4);
    crate::safe_test!(Xoshiro128Ssx8);
    crate::safe_test!(Xoshiro128Ssx16);
}