use std::time::{SystemTime, UNIX_EPOCH};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
const SIGN_MASK: u128 = 1u128 << 127;
fn now_ns() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH).unwrap()
.as_nanos()
}
fn thread_id() -> u64 {
let tid = std::thread::current().id();
let mut hasher = DefaultHasher::new();
tid.hash(&mut hasher);
hasher.finish()
}
fn mixer(h: &mut u64, x: u64) {
*h ^= x.wrapping_mul(0x9E37_79B9_7F4A_7C15);
*h = (*h).rotate_left(27).wrapping_mul(0x94D0_49BB_1331_11EB);
}
fn prng(state: &mut u64) -> u64 {
let mut x = *state;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
*state = x;
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
fn generator_u128(thread_id: u64, ts_ns: u128) -> u128 {
let mut h: u64 = 0x9E37_79B9_7F4A_7C15;
mixer(&mut h, thread_id);
mixer(&mut h, (ts_ns >> 64) as u64);
mixer(&mut h, ts_ns as u64);
let mut state = if h == 0 { 1 } else { h };
let mut bytes = [0u8; 16];
bytes[..8].copy_from_slice(&prng(&mut state).to_be_bytes());
bytes[8..].copy_from_slice(&prng(&mut state).to_be_bytes());
u128::from_be_bytes(bytes)
}
pub fn integer_in(min: i128, max: i128) -> i128{
assert!(min <= max, "min must be <= max");
if min == max {
return min;
}
let seed = generator_u128(thread_id(), now_ns());
if min == i128::MIN && max == i128::MAX {
return seed as i128;
}
let start = (min as u128) ^ SIGN_MASK;
let end = (max as u128) ^ SIGN_MASK;
let width = (end - start) + 1;
let r = start + (seed % width);
(r ^ SIGN_MASK) as i128
}
pub fn decimal_in(start: f64, end: f64) -> f64{
assert!(start <= end, "start must be <= end");
if start == end {
return start;
}
let seed = generator_u128(thread_id(), now_ns());
let mant: u64 = (seed >> (128 - 53)) as u64;
let unit: f64 = (mant as f64) * (1.0 / ((1u64 << 53) as f64));
start + (end - start) * unit
}
pub fn choose<T>(slice: &[T]) -> Option<&T>{
if slice.is_empty() {
return None;
}
let idx = integer_in(0, (slice.len() - 1) as i128) as usize;
slice.get(idx)
}
pub fn choose_iter<I>(iter: I) -> Option<I::Item>
where I: IntoIterator,
{
let v: Vec<I::Item> = iter.into_iter().collect();
if v.is_empty() {
return None;
}
let idx = integer_in(0, (v.len() - 1) as i128) as usize;
v.into_iter().nth(idx)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn integer_in_respects_range_small_negative_to_positive() {
for _ in 0..1_000 {
let x = integer_in(-10, 10);
assert!((-10..=10).contains(&x));
}
}
#[test]
fn integer_in_respects_range_negative_only() {
for _ in 0..1_000 {
let x = integer_in(-50, -1);
assert!((-50..=-1).contains(&x));
}
}
#[test]
fn integer_in_respects_range_positive_only() {
for _ in 0..1_000 {
let x = integer_in(0, 50);
assert!((0..=50).contains(&x));
}
}
#[test]
fn integer_in_returns_exact_when_bounds_equal() {
assert_eq!(integer_in(42, 42), 42);
assert_eq!(integer_in(-7, -7), -7);
}
#[test]
fn decimal_in_respects_range() {
for _ in 0..1_000 {
let x = decimal_in(-0.1, 0.1);
assert!(x >= -0.1 - f64::EPSILON && x <= 0.1 + f64::EPSILON);
}
}
#[test]
fn decimal_in_returns_exact_when_bounds_equal() {
let x = decimal_in(3.14, 3.14);
assert_eq!(x, 3.14);
}
#[test]
fn choose_empty_returns_none() {
let s: [i32; 0] = [];
assert!(choose(&s).is_none());
}
#[test]
fn choose_returns_one_of_elements() {
let s = [10, 20, 30, 40];
for _ in 0..100 {
let v = choose(&s).unwrap();
assert!(s.contains(v));
}
}
#[test]
fn choose_iter_empty_returns_none() {
let v: Vec<i32> = vec![];
assert!(choose_iter(v).is_none());
}
#[test]
fn choose_iter_returns_from_iterable() {
let v = vec!["a", "b", "c"];
for _ in 0..100 {
let picked = choose_iter(v.clone()).unwrap();
assert!(v.contains(&picked));
}
}
}