//! Here are functions used to generate random numbers or 3x1 vector with random elements.
//!
//! The function [`get_rng`] could be integrated into [`gen_randn`] and [`gen_randn_vec3`].
//! However, consider that in certain cases we may need to create an RNG in `main()` or
//! certain upper level functions, then pass it to down-stream functions, I decided to make
//! [`get_rng`] a public function and you have to follow the usage examples given in
//! [`gen_randn`] and [`gen_randn_vec3`] to generate random numbers or vectors.
//!
use nalgebra::Vector3;
use rand::{
    SeedableRng,
    rngs::{StdRng, ThreadRng},
};
use rand_distr::{Distribution, Normal};

#[derive(Debug)]
pub enum SensorRng {
    Seedable(StdRng),
    NoSeed(ThreadRng),
}

/// Get an RNG based on an optional seed.
///
/// # Example
/// See the example given in [`gen_randn`] and [`gen_randn_vec3`].
///
pub fn get_rng(seed: Option<u64>) -> SensorRng {
    match seed {
        Some(s) => SensorRng::Seedable(StdRng::seed_from_u64(s)),
        None => SensorRng::NoSeed(rand::rng()),
    }
}

/// # Example
/// ```
/// use navitools::{get_rng, gen_randn};
///
/// // Create an RNG with the seed `42`.
/// let mut rng = get_rng(Some(42));
/// let ans = vec![
///     0.06942791836196335,
///     0.13293812199412544,
///     0.2625763573739537,
///     -0.22530087839099155,
///     -0.6642248458355339,
/// ];
///
/// for i in 0..ans.len() {
///     let randn = gen_randn(&mut rng, 0.0, 1.0);
///     assert!(randn == ans[i]);
/// }
///
/// // Create an RNG with no seed.
/// let mut rng = get_rng(None);
/// let randn = gen_randn(&mut rng, 0.0, 1.0);
/// println!("The random number is {}.", randn);
/// ```
pub fn gen_randn(rng: &mut SensorRng, mean: f64, std_dev: f64) -> f64 {
    let normal = Normal::new(mean, std_dev).unwrap();
    match rng {
        SensorRng::Seedable(r) => normal.sample(r),
        SensorRng::NoSeed(r) => normal.sample(r),
    }
}

/// # Example
/// ```
/// use nalgebra::Vector3;
/// use navitools::{get_rng, gen_randn_vec3};
///
/// // Create an RNG with the seed `42`.
/// let mut rng = get_rng(Some(42));
/// let randn_vec3 = gen_randn_vec3(&mut rng, 0.0, 1.0);
/// let ans = Vector3::new(0.06942791836196335, 0.13293812199412544, 0.2625763573739537);
/// assert!(randn_vec3 == ans);
/// ```
///
pub fn gen_randn_vec3(rng: &mut SensorRng, mean: f64, std_dev: f64) -> Vector3<f64> {
    let normal = Normal::new(mean, std_dev).unwrap();
    match rng {
        SensorRng::Seedable(r) => Vector3::from_iterator((0..3).map(|_| normal.sample(r))),
        SensorRng::NoSeed(r) => Vector3::from_iterator((0..3).map(|_| normal.sample(r))),
    }
}
