minimo 0.5.42

terminal ui library combining alot of things from here and there and making it slightly easier to play with
Documentation
use std::cell::Cell;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Clone, Copy)]
struct Xorshift128Plus {
    state: [u64; 2],
}

impl Xorshift128Plus {
    fn new(seed: u64) -> Self {
        let mut state = [0; 2];
        state[0] = seed;
        state[1] = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
        Xorshift128Plus { state }
    }

    fn next(&mut self) -> u64 {
        let s1 = self.state[0];
        let s0 = self.state[1];
        self.state[0] = s0;
        let s1 = s1 ^ (s1 << 23);
        let t = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5);
        self.state[1] = t;
        t.wrapping_add(s0)
    }
}

thread_local! {
    static RNG: Cell<Xorshift128Plus> = Cell::new(Xorshift128Plus::new(initial_seed()));
}

fn initial_seed() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards")
        .as_nanos() as u64
}

pub fn set_seed(seed: u64) {
    RNG.with(|rng| rng.set(Xorshift128Plus::new(seed)));
}

fn next_u64() -> u64 {
    RNG.with(|rng| {
        let mut r = rng.get();
        let value = r.next();
        rng.set(r);
        value
    })
}

pub fn random_bool() -> bool {
    (next_u64() & 1) == 1
}

pub fn random_u64_range(min: u64, max: u64) -> u64 {
    assert!(min < max, "min must be less than max");
    min + next_u64() % (max - min)
}

pub fn random_i64_range(min: i64, max: i64) -> i64 {
    assert!(min < max, "min must be less than max");
    min + (next_u64() % (max - min) as u64) as i64
}

pub fn random_u32_range(min: u32, max: u32) -> u32 {
    assert!(min < max, "min must be less than max");
    min + (next_u64() % (max - min) as u64) as u32
}

pub fn random_i32_range(min: i32, max: i32) -> i32 {
    assert!(min < max, "min must be less than max");
    min + (next_u64() % (max - min) as u64) as i32
}

pub fn random_f64_range(min: f64, max: f64) -> f64 {
    assert!(min < max, "min must be less than max");
    min + (next_u64() as f64 / u64::MAX as f64) * (max - min)
}

pub fn random_number(min: impl Into<f64>, max: impl Into<f64>) -> f64 {
    random_f64_range(min.into(), max.into())
}

pub fn random_int(min: impl Into<i64>, max: impl Into<i64>) -> i64 {
    random_i64_range(min.into(), max.into())
}

pub fn random_char() -> char {
    std::char::from_u32(random_u32_range(0, 128)).unwrap_or(' ')
}

pub fn random_byte() -> u8 {
    next_u64() as u8
}

pub fn random_ascii_string(length: usize) -> String {
    (0..length)
        .map(|_| (random_u32_range(33, 127) as u8) as char)
        .collect()
}

pub fn random_alphanumeric_string(length: usize) -> String {
    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    (0..length)
        .map(|_| CHARSET[random_u64_range(0, CHARSET.len() as u64) as usize] as char)
        .collect()
}

pub fn shuffle<T>(slice: &mut [T]) {
    for i in (1..slice.len()).rev() {
        let j = random_u64_range(0, (i + 1) as u64) as usize;
        slice.swap(i, j);
    }
}

pub fn random_choice<T>(slice: &[T]) -> Option<&T> {
    if slice.is_empty() {
        None
    } else {
        Some(&slice[random_u64_range(0, slice.len() as u64) as usize])
    }
}

pub fn random_sample<T: Clone>(slice: &[T], k: usize) -> Vec<T> {
    let mut indices: Vec<usize> = (0..slice.len()).collect();
    shuffle(&mut indices);
    indices.truncate(k.min(slice.len()));
    indices.into_iter().map(|i| slice[i].clone()).collect()
}

pub fn random_weighted_choice<T>(choices: &[(T, f64)]) -> Option<&T> {
    if choices.is_empty() {
        return None;
    }

    let total_weight: f64 = choices.iter().map(|(_, weight)| weight).sum();
    let mut random = random_number(0.0, total_weight);

    for (item, weight) in choices {
        random -= weight;
        if random <= 0.0 {
            return Some(item);
        }
    }

    Some(&choices.last().unwrap().0)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    #[test]
    fn test_random_number() {
        let value = random_number(0.0, 1.0);
        assert!(value >= 0.0 && value < 1.0, "Generated value {} is out of range", value);
        let value_2 = random_number(0, 1);
        assert_ne!(value, value_2, "Numers are not really random");
    }

    #[test]
    fn test_random_u64() {
        let mut set = HashSet::new();
        for _ in 0..10000 {
            set.insert(random_u64_range(0, 1000));
        }
        assert!(set.len() > 900, "Expected at least 900 unique values, got {}", set.len());
    }

    #[test]
    fn test_random_i64() {
        let mut positives = 0;
        let mut negatives = 0;
        for _ in 0..1000 {
            match random_i64_range(-1000, 1000).signum() {
                1 => positives += 1,
                -1 => negatives += 1,
                _ => {}
            }
        }
        assert!(positives > 400 && negatives > 400, "Expected roughly equal positive and negative numbers");
    }

    #[test]
    fn test_random_f64() {
        for _ in 0..1000 {
            let value = random_number(0.0, 1.0);
            assert!(value >= 0.0 && value < 1.0, "Generated value {} is out of range", value);
        }
    }

    #[test]
    fn test_random_bool() {
        let mut trues = 0;
        for _ in 0..1000 {
            if random_bool() {
                trues += 1;
            }
        }
        assert!(trues > 400 && trues < 600, "Expected roughly 500 true values, got {}", trues);
    }

    #[test]
    fn test_random_range() {
        for _ in 0..1000 {
            let value = random_i32_range(-10, 10);
            assert!(value >= -10 && value < 10, "Generated value {} is out of range", value);
        }
    }

    #[test]
    fn test_random_char() {
        let mut set = HashSet::new();
        for _ in 0..1000 {
            set.insert(random_char());
        }
        assert!(set.len() > 50, "Expected at least 50 unique characters, got {}", set.len());
    }

    #[test]
    fn test_random_ascii_string() {
        let s = random_ascii_string(100);
        assert_eq!(s.len(), 100, "Expected string length of 100, got {}", s.len());
        assert!(s.chars().all(|c| c.is_ascii() && !c.is_ascii_control()), "Expected only printable ASCII characters");
    }

    #[test]
    fn test_shuffle() {
        let mut v: Vec<i32> = (0..100).collect();
        let original = v.clone();
        shuffle(&mut v);
        assert_ne!(v, original, "Expected shuffled vector to be different from original");
        assert_eq!(v.len(), original.len(), "Expected shuffled vector to have the same length as original");
        assert_eq!(v.iter().sum::<i32>(), original.iter().sum::<i32>(), "Expected sum of elements to remain the same after shuffling");
    }

    #[test]
    fn test_random_weighted_choice() {
        let choices = vec![(1, 0.1), (2, 0.2), (3, 0.7)];
        let mut counts = [0, 0, 0];
        for _ in 0..100000 {
            let choice = random_weighted_choice(&choices).unwrap();
            counts[choice - 1] += 1;
        }
        println!("Counts: {:?}", counts);
        assert!(counts[2] > counts[1] && counts[1] > counts[0], 
                "Expected counts[2] > counts[1] > counts[0], got {:?}", counts);
    }
}