smallrand 1.1.0

Random number generation with absolutely minimal dependencies and no unsafe code.
Documentation
use crate::{EntropySource, Rng};

/// An xoshiro256++ 1.0 (see <https://prng.di.unimi.it>) random generator.
/// This is an efficient PRNG with good random properties, but not cryptographically secure:
/// An attacker will be able to calculate the internal state by observing
/// a number of samples, and thus predict future output.
#[allow(clippy::module_name_repetitions)]
pub struct Xoshiro256pp {
    state: [u64; 4],
}

impl Xoshiro256pp {
    /// Creates a new xoshiro256++ random generator with a seed from an [EntropySource].
    ///
    /// # Arguments
    ///
    /// * `entropy_source`: The entropy source to get the seed from
    ///
    /// returns: [Xoshiro256pp]
    pub fn from_entropy<T>(entropy_source: &mut T) -> Self
    where
        T: EntropySource,
    {
        Self {
            state: core::array::from_fn(|_| entropy_source.seed::<u64>()),
        }
    }

    /// Creates a new xoshiro256++ random generator with a specified seed.
    ///
    /// Warning: You need to provide values for all four elements of the array.
    /// Providing a value for one and leaving the
    /// others as zeros will generate poor random output.
    /// If you have only one random u64 value to use as seed, then please
    /// initialize using the [SplitMix](crate::SplitMix) (see example below).
    ///
    /// # Arguments
    ///
    /// * `seed`: The seed to use
    ///
    /// returns: [Xoshiro256pp]
    ///
    /// # Examples
    /// ```
    /// use smallrand::Rng;
    /// let mut rng = smallrand::Xoshiro256pp::from_seed([0x12345678, 0x34567812, 0x56781234, 0x78123456]);
    /// let random_value : u32 = rng.random();
    /// ```
    ///
    /// ```
    /// use smallrand::Rng;
    /// let mut rng = smallrand::Xoshiro256pp::from_entropy(&mut smallrand::SplitMix::new(0x12345678));
    /// let random_value : u32 = rng.random();
    /// ```
    #[must_use]
    pub fn from_seed(seed: [u64; 4]) -> Self {
        Self { state: seed }
    }

    // This is "next" from the C reference implementation
    #[inline]
    fn next_random(&mut self) -> u64 {
        let result = (self.state[0].wrapping_add(self.state[3]))
            .rotate_left(23)
            .wrapping_add(self.state[0]);

        let t = self.state[1] << 17;

        self.state[2] ^= self.state[0];
        self.state[3] ^= self.state[1];
        self.state[1] ^= self.state[2];
        self.state[0] ^= self.state[3];

        self.state[2] ^= t;

        self.state[3] = self.state[3].rotate_left(45);

        result
    }
}

impl Rng for Xoshiro256pp {
    #[allow(clippy::cast_possible_truncation)]
    #[inline]
    fn random_u32(&mut self) -> u32 {
        self.random_u64() as u32
    }

    #[inline]
    fn random_u64(&mut self) -> u64 {
        self.next_random()
    }
}

#[cfg(test)]
mod tests {
    use super::Xoshiro256pp;
    use crate::entropy::SplitMix;
    use crate::rng::Rng;

    struct DummyEntropy;

    impl crate::EntropySource for DummyEntropy {
        fn fill(&mut self, destination: &mut [u8]) {
            for (inx, element) in destination.iter_mut().enumerate() {
                *element = (inx + 42) as u8;
            }
        }
    }

    fn xoshiro() -> Xoshiro256pp {
        Xoshiro256pp::from_entropy(&mut DummyEntropy {})
    }

    #[test]
    fn test_xoshiro_output() {
        // These test vectors match the values generated by the `rand` crate:
        let mut rng = Xoshiro256pp {
            state: [1, 2, 3, 4],
        };
        assert_eq!(
            vec![
                41943041,
                58720359,
                3588806011781223,
                3591011842654386,
                9228616714210784205,
                9973669472204895162,
                14011001112246962877,
                12406186145184390807,
                15849039046786891736,
                10450023813501588000,
            ],
            rng.iter().take(10).collect::<Vec<u64>>()
        );
    }

    #[test]
    fn test_xoshiro_from_seed() {
        // These test vectors match the values generated by the `rand` crate:
        let mut rng = Xoshiro256pp::from_entropy(&mut SplitMix::new(0u64));
        assert_eq!(
            vec![
                5987356902031041503,
                7051070477665621255,
                6633766593972829180,
                211316841551650330,
                9136120204379184874,
                379361710973160858,
                15813423377499357806,
                15596884590815070553,
                5439680534584881407,
                1369371744833522710,
            ],
            rng.iter().take(10).collect::<Vec<u64>>()
        );
    }

    #[test]
    fn test_xoshiro_random() {
        // These test vectors match the values generated by the `rand` crate:
        let mut rng = Xoshiro256pp::from_entropy(&mut SplitMix::new(0u64));
        assert_eq!(rng.random::<u64>(), 5987356902031041503)
    }

    #[test]
    fn test_xoshiro_range() {
        let mut rng = Xoshiro256pp::from_entropy(&mut SplitMix::new(0u64));
        assert_eq!(rng.range(11_u8..42), 15)
    }

    #[test]
    fn xoshiro_generate_bools() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![true, true, false, true, true, true],
            rng.iter().take(6).collect::<Vec<_>>()
        );
    }

    #[test]
    fn xoshiro_generate_u8() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![93, 199, 18, 93, 255, 159],
            rng.iter().take(6).collect::<Vec<u8>>()
        );
    }

    #[test]
    fn xoshiro_generate_u16() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![23389, 17863, 786, 12381, 18687, 18079],
            rng.iter().take(6).collect::<Vec<u16>>()
        );
    }

    #[test]
    fn xoshiro_generate_u32() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![1599691613, 1187268039, 3807576850, 1187065949, 2131446015, 3237824159],
            rng.iter().take(6).collect::<Vec<u32>>()
        );
    }

    #[test]
    fn xoshiro_generate_u128() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![
                116106803150699428516699394013734913479,
                216263115653590202844377321789695537245,
                328425740128965645684326511152092497567,
                254561204173543679954141383040234184739,
                40455170329874112030313783831106647436,
                309825739868400302084046781679576737863
            ],
            rng.iter().take(6).collect::<Vec<u128>>()
        );
    }

    #[test]
    fn xoshiro_generate_usize() {
        let mut rng = xoshiro();
        assert_eq!(
            vec![
                6294162410816756573,
                4666366678484141511,
                11723646991005319954,
                3577826909737791581,
                17803995047399213311,
                17636447047143736991
            ],
            rng.iter().take(6).collect::<Vec<usize>>()
        );
    }

    #[test]
    fn xoshiro_fill_u32() {
        let mut rng = xoshiro();
        let mut data = [0_u32; 4];
        rng.fill(&mut data);
        assert_eq!(&vec![1599691613, 1187268039, 3807576850, 1187065949], &data);
    }

    #[test]
    fn xoshiro_fill_u8() {
        let mut rng = xoshiro();
        let mut data = [0_u8; 4];
        rng.fill_u8(&mut data);
        assert_eq!(&vec![93, 91, 89, 95], &data);
    }

    #[test]
    fn xoshiro_bounded_range_f64() {
        let mut rng = xoshiro();
        let mut min = 42_f64;
        let mut max = 4_f64;
        for _ in 0..100 * 256 {
            let value: f64 = rng.range(4.0..42.0);
            assert!(value >= 4.0);
            assert!(value <= 42.0);
            min = min.min(value);
            max = max.max(value);
        }
        assert!(min < 4.01);
        assert!(max >= 41.99);
    }

    #[test]
    fn xoshiro_bounded_range_f32() {
        let mut rng = xoshiro();
        let mut min = 42_f32;
        let mut max = 4_f32;
        for _ in 0..100 * 256 {
            let value: f32 = rng.range(4.0..42.0);
            assert!(value >= 4.0);
            assert!(value <= 42.0);
            min = min.min(value);
            max = max.max(value);
        }
        assert!(min < 4.01);
        assert!(max >= 41.99);
    }
}