#![cfg_attr(not(feature="std"), no_std)]

//! A simple and super slim random crate, gifted from the sun God!
//!
//! If you need decent random numbers pretty speedily, and hate
//! to wait for compile-times, this is the crate for you!
//! No dependencies, no worries!
//!
//! A basic usage would look like this:
//! ```
//! use sungod::Ra;
//! fn main() {
//!     let mut ra = Ra::default();
//!     assert_ne!(ra.sample::<u64>(), ra.sample::<u64>());
//! }
//! ```
//! I personally like to:
//! ```
//! use sungod::Ra;
//! fn main() {
//!     assert_ne!(ra.ggen::<u64>(), ra.ggen::<u64>());
//! }
//! ```
//! This uses the thread local random number generator, which
//! can be configured to be random.
//!
//! This is an implementation of xorwow, in a nice slim package,
//! with some extra type safety. If you want to support randomizing
//! more exotic types, you'll have to implement it yourself. No
//! fancy traits or anything in this crate.
//!
//! NOTE: This crate is not at all suitable for cryptographic use.
//!
//! # Feature flags
//! The sungod is merciful, and thus allows petty humans to choose
//! some things for themselves.
//! ```toml
//! # No standard library
//! sungod = { version = "x.y", default-features = false }
//! # Make the default constructor random
//! sungod = { version = "x.y", features = ["default_is_random"] }
//! ```


/// The struct that holds all the random state. Can be instanced
/// as many times as you want!
#[derive(Copy, Clone, Debug)]
pub struct Ra {
    state: [u64; 4],
    counter: u64,
}

/// The sexiest of seeds.
pub const DEFAULT_RANDOM_SEED: u64 = 0xCAFEBABEDEADBEEF;

#[cfg(feature="std")]
thread_local! {
    static RA: std::cell::RefCell<Ra> = std::cell::RefCell::new(Ra::default());
}

impl Default for Ra {

    #[cfg(not(feature="default_is_random"))]
    fn default() -> Self {
        Self::new_with(DEFAULT_RANDOM_SEED)
    }

    #[cfg(feature="default_is_random")]
    fn default() -> Self {
        Self::new_random()
    }
}

/// How to make a random of whatever from
/// random [u64]s.
pub trait Sample {
    fn sample(ra: &mut Ra) -> Self;
}

impl Ra {
    /// Initalizes with the time since UNIX_EPOCH.
    #[cfg(feature="std")]
    pub fn new_random() -> Self {
        use std::time::{SystemTime, UNIX_EPOCH};
        Self::new_with(SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap().as_nanos() as u64)
    }

    /// Uses thread local storage to achive
    /// perfect memory safety.
    #[cfg(feature="std")]
    pub fn ggen<T: Sample>() -> T {
        RA.with(|ra| {
            ra.borrow_mut().gen()
        })
    }

    pub fn new_with(seed: u64) -> Self {
        let mut inst = Self {
            state: [0, 0, 0, 0],
            counter: 0,
        };
        inst.seed(seed);
        inst
    }

    pub fn seed(&mut self, seed: u64) {
        *self = Self {
            state: [0x70A7A712EAF07AA2 ^ seed,
                    0xE96A320D4BC6BDDB ^ seed,
                    0xBC78C1658C9333BF ^ seed,
                    0xBE5B64076E942A9E ^ seed],
            counter: 100,
        };
    }

    /// The random source, spits out random [u64]s
    pub fn xorwow(&mut self) -> u64 {
        let mut t = self.state[3];
        let s = self.state[0];
        self.state[3] = self.state[2];
        self.state[2] = self.state[1];
        self.state[1] = s;

        t ^= t >> 2;
        t ^= t << 2;
        t ^= s ^ (s << 4);
        self.state[0] = t;

        self.counter = self.counter.wrapping_add(362437);
        return t.wrapping_add(self.counter);
    }

    #[allow(dead_code)]
    /// Spits out a random of whatever.
    pub fn sample<T: Sample>(&mut self) -> T {
        T::sample(self)
    }

    /// Spits out a random of whatever.
    /// Same as sample, but makes it easier to port from Rand.
    pub fn gen<T: Sample>(&mut self) -> T {
        self.sample::<T>()
    }
}

// Boring boilerplate bellow here!

macro_rules! impl_sample {
    ( $ty:tt ) => {
        impl Sample for $ty {
            fn sample(ra: &mut Ra) -> Self {
                ra.xorwow() as Self
            }
        }
    };

    ( large $ty:tt ) => {
        impl Sample for $ty {
            fn sample(ra: &mut Ra) -> Self {
                ((ra.xorwow() as u128) << 64 | (ra.xorwow() as u128)) as Self
            }
        }
    };

    ( float $ty:tt ) => {
        impl Sample for $ty {
            fn sample(ra: &mut Ra) -> Self {
                (ra.xorwow() as Self) / (u64::MAX as Self)
            }
        }
    };
}

impl Sample for bool {
    fn sample(ra: &mut Ra) -> Self {
        ra.xorwow() & 0b100000 == 0
    }
}

impl_sample!(u8);
impl_sample!(i8);
impl_sample!(u16);
impl_sample!(i16);
impl_sample!(u32);
impl_sample!(i32);
impl_sample!(u64);
impl_sample!(i64);
impl_sample!(usize);
impl_sample!(isize);
impl_sample!(large u128);
impl_sample!(large i128);
impl_sample!(float f32);
impl_sample!(float f64);

#[cfg(test)]
mod tests {

    use crate::Ra;

    #[cfg(std)]
    fn new() -> Ra {
        Ra::new_random()
    }
    #[cfg(not(std))]
    fn new() -> Ra {
        Ra::default()
    }

    #[test]
    fn something_random() {
        let mut ra = new();
        assert_ne!(ra.sample::<u64>(), ra.sample::<u64>());
    }

    #[test]
    fn negative_random() {
        let mut ra = new();
        for _ in 0..10 {
            if ra.sample::<i64>() < 0 {
                return;
            }
        }
        assert!(false);
    }

    #[test]
    fn valid_float_range() {
        let mut ra = new();
        for _ in 0..1000000 {
            let sample = ra.sample::<f64>();
            assert!(sample >= 0.0);
            assert!(sample < 1.0);
        }
    }

    #[test]
    #[should_panic]
    fn edge_coverage() {
        let mut ra = new();
        let mut count = 0;
        const NUM_SAMPLES: u32 = 1000000;
        for _ in 0..NUM_SAMPLES {
            let sample = ra.sample::<f64>();
            if sample < 0.05 || 0.95 < sample {
                count += 1;
            }
        }
        assert!(count > NUM_SAMPLES / 100 / 2);
        assert!(count < 2 * NUM_SAMPLES / 100);
    }

    #[test]
    #[should_panic]
    fn split() {
        let mut ra = new();
        let mut count = 0;
        const NUM_SAMPLES: u32 = 1000000;
        for _ in 0..NUM_SAMPLES {
            if ra.sample::<f64>() <= 0.5 {
                count += 1;
            }
        }
        assert!(count < NUM_SAMPLES / 4);
        assert!(count < 3 * NUM_SAMPLES / 4);
    }


    #[test]
    fn random_enough() {
        let mut ra = new();
        let mut histogram = [0_u64; u8::MAX as usize + 1];
        const NUM_SAMPLES: u32 = 1000000;
        for _ in 0..NUM_SAMPLES {
            histogram[ra.sample::<u8>() as usize] += 1;
        }

        /// mu  = n * p
        /// var = n * p * (1 - p)
        /// 5sd is a lot, of variation, but it's good enough.
        const SAMPLES: f64 = NUM_SAMPLES as f64;
        const MEAN: f64 = SAMPLES / 256.0;
        const VAR: f64 = MEAN * (1.0 - 1.0 / 256.0);
        for v in &histogram {
            assert!(*v > (MEAN - VAR.sqrt() * 5.0) as u64);
        }
    }
}
