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);
}
}