minstd 0.9.4

MINSTD minimal standard random number generator
Documentation
/*
MINSTD Multiplicative congruential generator
implementation by Radim Kolar <hsn@sendmail.cz> 2024
https://gitlab.com/hsn10/minstd

This is free and unencumbered software released into the public domain.
SPDX-License-Identifier: Unlicense OR CC0-1.0

For more information, please refer to <http://unlicense.org/>
*/

//! # MINSTD
//!
//! Lehmer "minimum standard" random number generator.
//!
//! This crate have _no dependencies_ and can be used without
//! standard library by activating feature _no-std_.
//!
//! This is free and unencumbered software released into the public domain.

#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![forbid(rustdoc::broken_intra_doc_links)]
#![allow(unused_parens)]
#![allow(non_snake_case)]

// disable std library if we are not running tests.
#![cfg_attr(not(test), no_std)]

/**
  MINSTD0 mcg16807 / 1988 version

  Initial generator version. It uses _multiplier 16807_
  which was first suggested by Lewis, Goodman and Miller in 1969.

  First published in 1988 by Park and Miller as
  "minimum standard". Multiplier _A_ got changed in 1990.

  This initial version is significantly weaker but is way more widely used.
  It _should not be used_ in new code unless you need to verify experiments
  which used this earlier variant.
*/
pub struct MINSTD0 (i32);
/**
  MINSTD

  Updated version of "minimum standard" with _multiplier 48271_
  and sugested version to use because it have _way stronger_
  [spectral test](https://en.wikipedia.org/wiki/Spectral_test) than
  earlier 1988 version.
*/
pub struct MINSTD  (i32);

/** 
   modulus M = 2^31 - 1

   Mersenne prime M31
 */
pub const M: i32 = 2147483647;
/** 
  initial seed.

  Initial seed for MCG generator family is 1.
  Seed can not be zero or multiple of M.
  While it technically can be negative this
  crate rejects negative seeds.
*/
pub const SEED: i32 = 1;

/** minimum generated value */
pub const MIN: i32 = 1;

/** maximum generated value */
pub const MAX: i32 = M - 1;

/**
  clamps seed into valid range for generator.

  Valid seeds are integers from 1 to M-1 inclusive.

  Its recommended to always use this function unless
  you are sure that your supplied seed is in correct
  range. Generator will panic if started with
  invalid seed.
*/
pub fn clamp_seed(seed: i32) -> i32 {
   seed.abs().max(1).min(M-1)
}

/**
  Validate if supplied integer is in valid
  range of seeds for generator.

  Valid seeds are from \[1,M-1]
*/
pub fn validate_seed(seed: i32) -> bool {
      if seed <= 0 || seed % M == 0 {
         false
      } else {
         true
      }
}

impl MINSTD {
   /** multiplier A = 48271 */
   pub const A: i32 = 48271;
   /** Create new MINSTD generator with default seed */
   pub fn new() -> Self {
      Self(SEED)
   }
   /**
       Create seeded random generator

       Seed must be valid otherwise function will panic.
       See See also: [validate_seed](fn@validate_seed) and [clamp_seed](fn@clamp_seed).
    */
   pub fn seed(seed: i32) -> Self {
      if !validate_seed(seed) {
         panic!("Supplied seed value is invalid. Use correct value [1,M-1] or clamp_seed() it into valid range.");
      } else {
         Self(seed)
      }
   }
   /** generate next random number */
   pub fn next(& mut self) -> i32 {
      let next64: i64 = ( self.0 as i64 * Self::A as i64) % M as i64;
      let next32: i32 = next64 as i32;
      self.0 = next32;
      next32
   }
}
   
impl MINSTD0 {
   /** multiplier A = 16807 */
   pub const A:i32 = 16807;
   /** create new random generator with default seed */
   pub fn new() -> Self {
      Self(SEED)
   }
   /**
     create seeded generator

     Seed needs to be in \[1, M-1] range inclusive otherwise function will panic.
     See also: [validate_seed](fn@validate_seed) and [clamp_seed](fn@clamp_seed).
   */
   pub fn seed(seed: i32) -> Self {
      if !validate_seed(seed) {
         panic!("Supplied seed value is invalid. Use correct value [1,M-1] or clamp_seed() it into valid range.");
      } else {
         Self(seed)
      }
   }
   /** generate next random integer */
   pub fn next(& mut self) -> i32 {
      let next64: i64 = ( self.0 as i64 * Self::A as i64) % M as i64;
      let next32: i32 = next64 as i32;
      self.0 = next32;
      next32
   }
}


#[cfg(test)]
mod minstd_seq_test;

#[cfg(test)]
mod minstd0_seq_test;

#[cfg(test)]
#[path="validate_test.rs"]
mod validate;

#[cfg(test)]
#[path="clamp_test.rs"]
mod clamp;

#[cfg(test)]
#[path="period_test.rs"]
mod period;