///```rust
///use doe::*;
///let lcg = LCG::new();
///for _ in 0..10000{
///   let data = lcg.random_in_range(1..=20).to_string().push_back("\n");
///   fs::fs::append_data_to_file("doe.txt", data).unwrap();
///}
/// ```
#[allow(warnings)]
pub mod rand {
    /// A Linear Congruential Generator (LCG) is a simple type of random number generator
    /// that produces a sequence of pseudo-random numbers based on a linear recurrence relation.
    #[derive(Debug, Clone, Default)]
    pub struct LCG {
        seed: u128,
        a: u128,
        c: u128,
        m: u128,
    }

    impl LCG {
        /// Creates a new LCG instance with the specified initial seed is timestamp as nanoseconds.
        ///
        /// ```rust
        ///use doe::*;
        ///let lcg = LCG::new();
        ///for _ in 0..10000{
        ///    let data = lcg.random_in_range(1..=20).to_string().push_back("\n");
        ///    fs::fs::append_data_to_file("doe.txt", data).unwrap();
        ///}
        /// ```
        /// # Returns
        ///
        /// A new `LCG` instance with the specified seed and default constants.
        pub fn new() -> LCG {
            let seed = std::time::SystemTime::now()
                .duration_since(std::time::SystemTime::UNIX_EPOCH)
                .unwrap_or_default()
                .as_nanos();
            Self::init(seed, 1664525, 1013904223, 2u128.pow(32))
        }
        ///random_in_range
        /// ```rust
        ///use doe::*;  
        ///let lcg = LCG::new();
        ///for _ in 0..10000{
        ///    lcg.random_in_range(-30..30).dprintln();  
        ///}
        /// ```
        ///
        pub fn random_in_range(&self, range: impl std::ops::RangeBounds<i128>) -> i128 {
            let min = match range.start_bound() {
                std::ops::Bound::Included(v) => *v,
                std::ops::Bound::Excluded(v) => *v,
                std::ops::Bound::Unbounded => 0,
            };

            let max = match range.end_bound() {
                std::ops::Bound::Included(v) => *v + 1,
                std::ops::Bound::Excluded(v) => *v,
                std::ops::Bound::Unbounded => i128::MAX,
            };
            let random = Self::new().random();
            let sub = (max - min) as f64;
            f64::floor(random * sub) as i128 + min
        }
        ///random_in_range_f64
        ///```rust
        ///use doe::*;  
        ///let lcg = LCG::new();
        ///for _ in 0..10000{
        ///    lcg.random_in_range_f64(-30.0..30.0).dprintln();  
        ///}
        /// ```
        pub fn random_in_range_f64(&self, range: impl std::ops::RangeBounds<f64>) -> f64 {
            let min = match range.start_bound() {
                std::ops::Bound::Included(v) => *v,
                std::ops::Bound::Excluded(v) => *v,
                std::ops::Bound::Unbounded => 0.0,
            };

            let max = match range.end_bound() {
                std::ops::Bound::Included(v) => *v,
                std::ops::Bound::Excluded(v) => *v,
                std::ops::Bound::Unbounded => f64::MAX,
            };

            let random = Self::new().random();
            let sub = max - min;
            (random * sub) + min
        }

        /// Creates a new LCG instance with the specified initial seed value.
        ///
        /// # Arguments
        ///
        /// * `seed` - The initial seed value for the LCG.
        ///
        /// # Returns
        ///
        /// A new `LCG` instance with the specified seed and default constants.
        pub fn new_with_seed(seed: u128) -> LCG {
            Self::init(seed, 1664525, 1013904223, 2u128.pow(32))
        }

        /// Creates a new LCG instance with the specified initial seed value and constants.
        ///
        /// # Arguments
        ///
        /// * `seed` - The initial seed value for the LCG.
        /// * `a` - The multiplier constant for the LCG.
        /// * `c` - The increment constant for the LCG.
        /// * `m` - The modulus constant for the LCG.
        ///
        /// # Returns
        ///
        /// A new `LCG` instance with the specified seed and constants.
        pub fn init(seed: u128, a: u128, c: u128, m: u128) -> LCG {
            LCG { seed, a, c, m }
        }

        /// Generates the next random number in the sequence.
        ///
        /// # Returns
        ///
        /// The next pseudo-random number in the sequence as a `f64` value between 0 and 1.
        pub fn random(&mut self) -> f64 {
            self.seed = (self.a * self.seed + self.c) % self.m;
            self.seed as f64 / self.m as f64
        }
        pub fn random_f32(&mut self) -> f32 {
            self.seed = (self.a * self.seed + self.c) % self.m;
            self.seed as f32 / self.m as f32
        }
    }
}
