use crate::{WallSwitchError, WallSwitchResult};
use std::hash::{BuildHasher, Hasher, RandomState};
pub trait RandomExt {
fn shuffle(&mut self);
}
impl<T> RandomExt for [T] {
fn shuffle(&mut self) {
let n: usize = self.len();
if n < 2 {
return;
}
for i in 0..(n - 1) {
let j = (rand() as usize) % (n - i) + i;
self.swap(i, j);
}
}
}
pub fn rand() -> u64 {
RandomState::new().build_hasher().finish()
}
pub trait RandomCast {
fn from_u64(val: u64) -> Self;
}
macro_rules! impl_random_cast {
($($t:ty),*) => {
$(
impl RandomCast for $t {
#[inline]
fn from_u64(val: u64) -> Self {
val as $t
}
}
)*
};
}
impl_random_cast!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
);
pub fn get_random_integer<T, R>(min: T, max: T) -> R
where
T: TryInto<u64>,
R: RandomCast,
{
let min_val = min.try_into().ok().unwrap_or(0);
let max_val = max.try_into().ok().unwrap_or(0);
let raw_val = if min_val >= max_val {
min_val
} else {
min_val + rand() % (max_val - min_val + 1)
};
R::from_u64(raw_val)
}
pub fn get_random_integer_safe<R>(min: u64, max: u64) -> WallSwitchResult<R>
where
R: RandomCast,
{
if min > max {
Err(WallSwitchError::MinMax { min, max })
} else {
let raw_val = min + rand() % (max - min + 1);
Ok(R::from_u64(raw_val))
}
}
#[cfg(test)]
mod tests_random {
use super::*;
#[test]
fn test_shuffle_and_rand() {
let mut data: Vec<usize> = (0..100).collect();
let original = data.clone();
data.shuffle();
assert_eq!(data.len(), 100);
assert_ne!(data, original);
}
#[test]
fn test_automatic_inference_casts() {
let val_usize: usize = get_random_integer(10, 20);
assert!((10..=20).contains(&val_usize));
let val_u32: u32 = get_random_integer(100, 200);
assert!((100..=200).contains(&val_u32));
let val_f64: f64 = get_random_integer(0, 359);
assert!((0.0..=359.0).contains(&val_f64));
let val_i32: i32 = get_random_integer(1, 5);
assert!((1..=5).contains(&val_i32));
}
#[test]
fn test_safe_random_bounds() {
let valid: Result<u32, _> = get_random_integer_safe(50, 100);
assert!(valid.is_ok());
let val = valid.unwrap();
assert!((50..=100).contains(&val));
let invalid: Result<u32, _> = get_random_integer_safe(100, 50);
assert!(invalid.is_err());
}
}