devela 0.28.0

A development substrate of coherence.
Documentation
// devela::num::prob::rand::splitmix
//
//! Defines [`SplitMix64`].
//

use crate::{
    ConstInit, Infallible, InfallibleResult, RandQualities, RandSeedable, RandTry, Slice, slice,
};

#[doc = crate::_tags!(rand)]
/// SplitMix64 pseudo-random number generator.
#[doc = crate::_doc_meta!{location("num/prob/rand")}]
///
/// A small 64-bit-state generator useful for seed expansion,
/// simple streams, and diffusing weak seed material.
///
/// This is not a cryptographic generator.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SplitMix64 {
    state: u64,
}
/// Creates [`SplitMix64::INIT`] as the default value.
impl Default for SplitMix64 {
    fn default() -> Self {
        Self::INIT
    }
}
/// Provides a fixed default initialized value.
impl ConstInit for SplitMix64 {
    const INIT: Self = Self::new(Self::DEFAULT_SEED);
}
impl SplitMix64 {
    #[doc(hidden)]
    /// Fixed seed used by [`Default`] and [`ConstInit`].
    pub const DEFAULT_SEED: u64 = 0xDEFA_0017_DEFA_0017;

    /// Weyl increment derived from the 64-bit fractional golden ratio.
    pub const GOLDEN_GAMMA: u64 = 0x9E37_79B9_7F4A_7C15;

    /// First SplitMix64 avalanche multiplier.
    pub const MIX_A: u64 = 0xBF58_476D_1CE4_E5B9;

    /// Second SplitMix64 avalanche multiplier.
    pub const MIX_B: u64 = 0x94D0_49BB_1331_11EB;

    /// Creates a new generator from a 64-bit state.
    #[must_use]
    #[inline(always)]
    pub const fn new(seed: u64) -> Self {
        Self { state: seed }
    }

    /// Applies the SplitMix64 output mixer.
    #[must_use]
    #[inline(always)]
    pub const fn mix64(mut x: u64) -> u64 {
        x = (x ^ (x >> 30)).wrapping_mul(Self::MIX_A);
        x = (x ^ (x >> 27)).wrapping_mul(Self::MIX_B);
        x ^ (x >> 31)
    }

    /// Advances the state and returns the next value.
    #[must_use]
    #[inline(always)]
    pub const fn next_u64(&mut self) -> u64 {
        self.state = self.state.wrapping_add(Self::GOLDEN_GAMMA);
        Self::mix64(self.state)
    }

    /// Fills the buffer with generated bytes.
    pub const fn fill_bytes(&mut self, buffer: &mut [u8]) {
        let mut i = 0;
        while i < buffer.len() {
            let r = self.next_u64();
            let bytes = r.to_le_bytes();
            let remaining = buffer.len() - i;
            if remaining >= 8 {
                // buffer[i..i + 8].copy_from_slice(&bytes);
                Slice::copy(slice!(mut buffer, i, ..i + 8), &bytes);
                i += 8;
            } else {
                // buffer[i..].copy_from_slice(&bytes[..remaining]);
                Slice::copy(slice!(mut buffer, i, ..), slice!(&bytes, ..remaining));
                break;
            }
        }
    }
}

#[rustfmt::skip]
impl RandTry for SplitMix64 {
    type Error = Infallible;
    const RAND_OUTPUT_BITS: u32 = 64;
    const RAND_STATE_BITS: u32 = 64;
    const RAND_QUALITIES: RandQualities = RandQualities::PRNG;
    #[inline(always)]
    fn rand_try_next_u64(&mut self) -> InfallibleResult<u64> {
        Ok(self.next_u64())
    }
    #[inline(always)]
    fn rand_try_fill_bytes(&mut self, buffer: &mut [u8]) -> InfallibleResult<()> {
        self.fill_bytes(buffer);
        Ok(())
    }
}
impl RandSeedable for SplitMix64 {
    type RandSeed = [u8; 8];
    #[inline(always)]
    fn rand_from_seed(seed: Self::RandSeed) -> Self {
        Self::new(u64::from_le_bytes(seed))
    }
}
crate::__impl_dep_rand_core!(SplitMix64);