use num::rational::Ratio;
use num::{Bounded, Integer};
use rand;
use rand::distributions::range::SampleRange;
use rand::Rng;
use std::fmt::Display;
pub trait Choose<T> {
fn choose(&self) -> Option<&T>;
fn choose_index(&self) -> Option<usize>;
fn choose_enumerate(&self) -> Option<(usize, &T)>;
}
impl<T> Choose<T> for Vec<T> {
fn choose(&self) -> Option<&T> {
rand::thread_rng().choose(self)
}
fn choose_index(&self) -> Option<usize> {
if !self.is_empty() {
Some(rand_int(0, self.len() - 1))
} else {
None
}
}
fn choose_enumerate(&self) -> Option<(usize, &T)> {
let i = self.choose_index();
match i {
Some(i) => Some((i, &self[i])),
None => None,
}
}
}
pub fn rand_int<T>(x: T, y: T) -> T
where
T: Integer + SampleRange,
{
rand::thread_rng().gen_range(x, y + T::one())
}
pub fn rand_ratio<T>(x: T, y: T, d: T) -> Ratio<T>
where
T: Clone + Copy + Integer + SampleRange,
{
Ratio::new(rand_int(x * d, y * d), d)
}
pub fn dice<T>(x: T, y: T) -> bool
where
T: Copy + Display + Integer + SampleRange,
{
debug_assert!(x <= y, format!("Assert failed: dice({}, {})", x, y));
rand_int(T::one(), y) <= x
}
pub fn chance<T>(x: Ratio<T>) -> bool
where
T: Bounded + Clone + Copy + Integer + SampleRange,
{
let max = T::max_value() - T::one();
Ratio::new(rand_int(T::min_value(), max), max) <= x
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::math::between;
#[test]
fn test_rand_int() {
for _ in 1..100 {
let (a, b) = (rand_int(0, 1000), rand_int(0, 1000));
assert!(between(rand_int(a.min(b), a.max(b)), a, b))
}
}
#[test]
fn test_dice() {
for _ in 1..100 {
assert!(!dice(0, rand_int(1, 100)));
}
}
}