devela 0.27.0

A development layer of coherence.
Documentation
// devela::num::prob::rand::xyza8
//
//!

use crate::{ConstInit, Own, Rand};

#[doc = crate::_tags!(rand)]
/// A simple 8-bit <abbr title="Pseudo-Random Number Generator">PRNG</abbr>
/// with 32-bit of state, based on the *XorShift* algorithm.
#[doc = crate::_doc_location!("num/prob/rand")]
///
/// It has a 0.8% chance of falling into a poor quality short chain,
/// a some degree of care is required to seed it. However, the quality of the
/// random numbers is excellent for such a small state (32 bits), and it passes
/// almost all of the die hard tests.
///
/// Its longest cycle is 4_261_412_736.
// (== u32::MAX + u16::MAX * 512 + 639).
#[doc = crate::_doc!(vendor: "8bit_rng")]
#[must_use]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Xyza8a {
    x: u8,
    y: u8,
    z: u8,
    a: u8,
}

/// Creates a new PRNG initialized with the default fixed seed.
impl Default for Xyza8a {
    fn default() -> Self {
        Self::INIT
    }
}
/// Creates a new PRNG initialized with the default fixed seed.
impl ConstInit for Xyza8a {
    const INIT: Self = Self::new(Self::DEFAULT_SEED);
}

// private associated items
impl Xyza8a {
    #[doc(hidden)]
    pub const DEFAULT_SEED: [u8; 4] = [0xDE, 0xFA, 0x00, 0x17];
}

impl Xyza8a {
    /// Returns a seeded `Xyza8a` generator from the given 4 × 8-bit seeds.
    pub const fn new(seeds: [u8; 4]) -> Self {
        Self { x: seeds[0], y: seeds[1], z: seeds[2], a: seeds[3] }
    }

    #[must_use]
    /// Returns the PRNG's inner state as a raw snapshot.
    pub const fn inner_state(self) -> [u8; 4] {
        [self.x, self.y, self.z, self.a]
    }
    /// Restores the PRNG from the given state.
    pub const fn from_state(state: [u8; 4]) -> Self {
        Self { x: state[0], y: state[1], z: state[2], a: state[3] }
    }

    #[must_use]
    /// Returns the current random `u8`.
    pub const fn current_u8(&self) -> u8 {
        self.a
    }
    /// Advances the state and returns the next random `u8`.
    pub const fn next_u8(&mut self) -> u8 {
        let t = self.x ^ (self.x << 4);
        self.x = self.y;
        self.y = self.z;
        self.z = self.a;
        self.a = self.z ^ t ^ (self.z >> 1) ^ (t << 1);
        self.a
    }

    /// Returns a copy of the next new random state.
    pub const fn peek_next_state(&self) -> Self {
        let mut new = *self;

        let t = new.x ^ (new.x << 4);
        new.x = new.y;
        new.y = new.z;
        new.z = new.a;
        new.a = new.z ^ t ^ (new.z >> 1) ^ (t << 1);
        new
    }

    /// Returns both the next random state and the `u8` value.
    pub const fn own_next_u8(self) -> Own<Self, u8> {
        let s = self.peek_next_state();
        let v = s.current_u8();
        Own::new(s, v)
    }
}

/// # Extra constructors
impl Xyza8a {
    /// Returns a seeded `Xyza8a` generator from the given 32-bit seed.
    ///
    /// The seeds will be split in little endian order.
    pub const fn new1_u32(seed: u32) -> Self {
        Self::new(seed.to_le_bytes())
    }

    /// Returns a seeded `Xyza8a` generator from the given 2 × 16-bit seeds.
    ///
    /// The seeds will be split in little endian order.
    pub const fn new2_u16(seeds: [u16; 2]) -> Self {
        let [x, y] = seeds[0].to_le_bytes();
        let [z, a] = seeds[1].to_le_bytes();
        Self::new([x, y, z, a])
    }

    /// Returns a seeded `Xyza8b` generator from the given 4 × 8-bit seeds.
    /// This is an alias of [`new`][Self#method.new].
    pub const fn new4_u8(seeds: [u8; 4]) -> Self {
        Self::new(seeds)
    }
}

// -----------------------------------------------------------------------------

#[doc = crate::_tags!(rand)]
/// A simple 8-bit <abbr title="Pseudo-Random Number Generator">PRNG</abbr>
/// with 32-bit of state, based on the *XorShift* algorithm.
#[doc = crate::_doc_location!("num/prob/rand")]
///
/// It has an almost optimal cycle so no real care is required
/// for seeding except avoiding all zeros, but it fails many of the die hard
/// random number tests.
///
/// Its longest cycle is 4,294,967,294.
#[doc = crate::_doc!(vendor: "8bit_rng")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Xyza8b {
    x: u8,
    y: u8,
    z: u8,
    a: u8,
}

impl Default for Xyza8b {
    fn default() -> Self {
        Self::INIT
    }
}
impl ConstInit for Xyza8b {
    const INIT: Self = Self::new(Self::DEFAULT_SEED);
}

// private associated items
impl Xyza8b {
    #[doc(hidden)]
    pub const DEFAULT_SEED: [u8; 4] = [0xDE, 0xFA, 0x00, 0x17];
}

impl Xyza8b {
    /// Returns a seeded `Xyza8b` generator from the given 4 × 8-bit seeds.
    /// This is the fastest constructor.
    pub const fn new(seeds: [u8; 4]) -> Self {
        Self { x: seeds[0], y: seeds[1], z: seeds[2], a: seeds[3] }
    }

    #[must_use]
    /// Returns the PRNG's inner state as a raw snapshot.
    pub const fn inner_state(self) -> [u8; 4] {
        [self.x, self.y, self.z, self.a]
    }
    /// Restores the PRNG from the given state.
    pub const fn from_state(state: [u8; 4]) -> Self {
        Self { x: state[0], y: state[1], z: state[2], a: state[3] }
    }

    /// Returns the current random `u8`.
    pub const fn current_u8(&self) -> u8 {
        self.a
    }

    /// Returns the next random `u8`.
    pub const fn next_u8(&mut self) -> u8 {
        let t = self.x ^ (self.x >> 1);
        self.x = self.y;
        self.y = self.z;
        self.z = self.a;
        self.a = self.z ^ t ^ (self.z >> 3) ^ (t << 1);
        self.a
    }

    /// Returns a copy of the next new random state.
    pub const fn peek_next_state(&self) -> Self {
        let mut new = *self;

        let t = new.x ^ (new.x >> 1);
        new.x = new.y;
        new.y = new.z;
        new.z = new.a;
        new.a = new.z ^ t ^ (new.z >> 3) ^ (t << 1);
        new
    }

    /// Returns both the next random state and the `u8` value.
    pub const fn own_next_u8(self) -> Own<Self, u8> {
        let s = self.peek_next_state();
        let v = s.current_u8();
        Own::new(s, v)
    }
}

/// # Extra constructors
impl Xyza8b {
    /// Returns a seeded `Xyza8a` generator from the given 32-bit seed.
    ///
    /// The seeds will be split in little endian order.
    pub const fn new1_u32(seed: u32) -> Self {
        Self::new(seed.to_le_bytes())
    }

    /// Returns a seeded `Xyza8a` generator from the given 2 × 16-bit seeds.
    ///
    /// The seeds will be split in little endian order.
    pub const fn new2_u16(seeds: [u16; 2]) -> Self {
        let [x, y] = seeds[0].to_le_bytes();
        let [z, b] = seeds[1].to_le_bytes();
        Self::new([x, y, z, b])
    }

    /// Returns a seeded `Xyza8b` generator from the given 4 × 8-bit seeds.
    /// This is an alias of [`new`][Self#method.new].
    pub const fn new4_u8(seeds: [u8; 4]) -> Self {
        Self::new(seeds)
    }
}

impl Rand for Xyza8a {
    const RAND_OUTPUT_BITS: u32 = 8;
    const RAND_STATE_BITS: u32 = 32;
    /// Returns the next 4 × random `u8` combined as a single `u32`.
    fn rand_next_u32(&mut self) -> u32 {
        u32::from_le_bytes([self.next_u8(), self.next_u8(), self.next_u8(), self.next_u8()])
    }
    /// Returns the next 8 × random `u8` combined as a single `u64`.
    fn rand_next_u64(&mut self) -> u64 {
        u64::from_le_bytes([
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
        ])
    }
    fn rand_fill_bytes(&mut self, dest: &mut [u8]) {
        for byte in dest {
            *byte = self.next_u8();
        }
    }
}
impl Rand for Xyza8b {
    const RAND_OUTPUT_BITS: u32 = 8;
    const RAND_STATE_BITS: u32 = 32;
    /// Returns the next 4 × random `u8` combined as a single `u32`.
    fn rand_next_u32(&mut self) -> u32 {
        u32::from_le_bytes([self.next_u8(), self.next_u8(), self.next_u8(), self.next_u8()])
    }
    /// Returns the next 8 × random `u8` combined as a single `u64`.
    fn rand_next_u64(&mut self) -> u64 {
        u64::from_le_bytes([
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
            self.next_u8(),
        ])
    }
    fn rand_fill_bytes(&mut self, dest: &mut [u8]) {
        for byte in dest {
            *byte = self.next_u8();
        }
    }
}
#[cfg(feature = "dep_rand_core")]
#[cfg_attr(nightly_doc, doc(cfg(feature = "dep_rand_core")))]
mod impl_rand {
    use super::{Rand, Xyza8a, Xyza8b};
    use crate::_dep::rand_core::{SeedableRng, TryRng};

    impl TryRng for Xyza8a {
        type Error = crate::Infallible;

        /// Returns the next 4 × random `u8` combined as a single `u32`.
        fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
            Ok(self.rand_next_u32())
        }
        /// Returns the next 8 × random `u8` combined as a single `u64`.
        fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
            Ok(self.rand_next_u64())
        }
        fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
            self.rand_fill_bytes(dst);
            Ok(())
        }
    }
    impl SeedableRng for Xyza8a {
        type Seed = [u8; 4];
        fn from_seed(seeds: Self::Seed) -> Self {
            Self::new(seeds)
        }
    }
    impl TryRng for Xyza8b {
        type Error = crate::Infallible;

        /// Returns the next 4 × random `u8` combined as a single `u32`.
        fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
            Ok(self.rand_next_u32())
        }
        /// Returns the next 8 × random `u8` combined as a single `u64`.
        fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
            Ok(self.rand_next_u64())
        }
        fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
            self.rand_fill_bytes(dst);
            Ok(())
        }
    }
    impl SeedableRng for Xyza8b {
        type Seed = [u8; 4];
        fn from_seed(seeds: Self::Seed) -> Self {
            Self::new(seeds)
        }
    }
}