clr_random 0.1.0

.NET CLR Random implementation in Rust
Documentation
#![no_std]
#![doc = include_str!("../README.md")]

mod seed;
pub use rand_core;
use rand_core::{RngCore, SeedableRng};
pub use seed::Seed;

/// A pseudo-random number generator (PRNG) that is compatible with .NET's `System.Random`.
///
/// This structure implements the same algorithm as the Common Language Runtime (CLR)
/// to produce a deterministic sequence of random numbers from a given seed. It is
/// useful when you need to reproduce random sequences that were originally generated
/// in a .NET environment.
///
/// [`CLRRandom`] implements [`RngCore`], allowing it to be used with other
/// crates in the `rand` ecosystem.
pub struct CLRRandom {
    inext: usize,
    inextp: usize,
    seed_array: [i32; 56],
}

impl CLRRandom {
    const MBIG: i32 = i32::MAX;
    const MSEED: i32 = 161803398;

    /// Initializes the random number generator with a seed.
    fn initialize(seed: i32) -> Self {
        let mut rng = CLRRandom {
            inext: 0,
            inextp: 0,
            seed_array: [0; 56],
        };

        let subtraction = if seed == i32::MIN {
            i32::MAX
        } else {
            seed.abs()
        };
        let mut mj = Self::MSEED.wrapping_sub(subtraction);
        rng.seed_array[55] = mj;
        let mut mk = 1;

        for i in 1..55 {
            let ii = (21 * i) % 55;
            rng.seed_array[ii] = mk;
            mk = mj.wrapping_sub(mk);
            if mk < 0 {
                mk = mk.wrapping_add(Self::MBIG);
            }
            mj = rng.seed_array[ii];
        }

        for _k in 1..5 {
            for i in 1..56 {
                rng.seed_array[i] =
                    rng.seed_array[i].wrapping_sub(rng.seed_array[1 + ((i + 30) % 55)]);

                if rng.seed_array[i] < 0 {
                    rng.seed_array[i] = rng.seed_array[i].wrapping_add(Self::MBIG);
                }
            }
        }

        rng.inextp = 21;
        rng
    }

    /// Returns a random integer from the internal buffer.
    fn internal_sample(&mut self) -> i32 {
        let mut loc_inext = self.inext;
        let mut loc_inextp = self.inextp;

        loc_inext = loc_inext.wrapping_add(1);
        if loc_inext >= 56 {
            loc_inext = 1;
        }

        loc_inextp = loc_inextp.wrapping_add(1);
        if loc_inextp >= 56 {
            loc_inextp = 1;
        }

        let mut ret_val = self.seed_array[loc_inext].wrapping_sub(self.seed_array[loc_inextp]);

        if (ret_val) == Self::MBIG {
            ret_val = ret_val.wrapping_sub(1);
        }
        if ret_val < 0 {
            ret_val = ret_val.wrapping_add(Self::MBIG);
        }

        self.seed_array[loc_inext] = ret_val;

        self.inext = loc_inext;
        self.inextp = loc_inextp;

        ret_val
    }

    /// Returns a random float between 0.0 and 1.0.
    fn sample(&mut self) -> f64 {
        self.internal_sample() as f64 * (1.0 / Self::MBIG as f64)
    }

    /// Returns a non-negative random 32-bit integer.
    ///
    /// # Returns
    ///
    /// A 32-bit signed integer that is greater than or equal to 0 and less than [`i32::MAX`].
    pub fn next_i32(&mut self) -> i32 {
        self.internal_sample()
    }

    /// Returns a non-negative random 64-bit integer.
    ///
    /// # Returns
    ///
    /// A 64-bit signed integer that is greater than or equal to 0 and less than [`i64::MAX`].
    pub fn next_i64(&mut self) -> i64 {
        let high = self.internal_sample() as i64;
        let low = (self.internal_sample() as u32) as i64;

        high << 32 | low
    }

    /// Returns a random floating-point number that is greater than or equal to 0.0, and less than 1.0.
    ///
    /// # Returns
    ///
    /// A 64-bit floating-point number that is greater than or equal to 0.0, and less than 1.0.
    pub fn next_f64(&mut self) -> f64 {
        let res = self.sample();
        assert!(res < 1.0 && res >= 0.0);
        res
    }
}

impl RngCore for CLRRandom {
    /// Generates a random `u32` value.
    fn next_u32(&mut self) -> u32 {
        self.internal_sample() as u32
    }

    /// Generates a random `u64` value.
    fn next_u64(&mut self) -> u64 {
        let high = self.internal_sample() as u64;
        let low = self.internal_sample() as u64;

        high << 32 | low
    }

    /// Fills a slice with random bytes.
    fn fill_bytes(&mut self, dst: &mut [u8]) {
        for b in dst.iter_mut() {
            *b = (self.internal_sample() % 256) as u8;
        }
    }
}

impl SeedableRng for CLRRandom {
    type Seed = Seed;

    /// Creates a new `CLRRandom` instance from a seed.
    ///
    /// This allows the generator to be predictably initialized for reproducible results.
    ///
    /// # Example
    /// ```
    /// use clr_random::{CLRRandom, Seed};
    /// use rand_core::{RngCore, SeedableRng};
    ///
    /// let seed: Seed = 0.into();
    /// let mut rng = CLRRandom::from_seed(seed);
    /// assert_eq!(rng.next_i32(), 1559595546);
    /// ```
    fn from_seed(seed: Self::Seed) -> Self {
        Self::initialize(seed.seed)
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple() {
        let mut rand = CLRRandom::from_seed(0.into());
        assert_eq!(rand.next_i32(), 1559595546);
        assert_eq!(rand.next_i32(), 1755192844);
    }
    #[test]
    fn simple_float() {
        let mut rand = CLRRandom::from_seed(42.into());
        assert_eq!(rand.next_f64(), 0.6681064659115423);
        assert_eq!(rand.next_f64(), 0.14090729837348093);
    }

    #[test]
    fn complex() {
        let mut rand = CLRRandom::from_seed(1919810.into());
        assert_eq!(rand.next_i32(), 147482110);
        assert_eq!(rand.next_i32(), 1747108798);
        assert_eq!(rand.next_i32(), 1937076328);
        let mut array = [0; 10];
        rand.fill_bytes(&mut array);
        assert_eq!(array, [255, 178, 141, 186, 113, 4, 147, 197, 124, 251]);
        assert_eq!(rand.next_i32(), 1584818534);
        assert_eq!(rand.next_i32(), 2082382094);
    }
}