use std::num::NonZero;
use std::panic::Location;
use crate::Rng;
fn raw_bytes<const N: usize>(rng: &mut Rng) -> [u8; N] {
let mut buf = [0u8; N];
rng.fill(&mut buf);
buf
}
#[track_caller]
pub fn gen_choice<T: Clone + std::fmt::Debug + 'static>(rng: &mut Rng, choices: &[T]) -> T {
assert!(!choices.is_empty(), "gen_choice: empty slice");
let loc = Location::caller();
let idx = usize::from_le_bytes(raw_bytes(rng)) % choices.len();
let v = choices[idx].clone();
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_bytes<const N: usize>(rng: &mut Rng) -> [u8; N] {
let loc = Location::caller();
let bytes = raw_bytes::<N>(rng);
rng.record_generated(&bytes, loc);
bytes
}
#[track_caller]
pub fn gen_bytes_vec(rng: &mut Rng, len: usize) -> Vec<u8> {
let loc = Location::caller();
let mut bytes = vec![0u8; len];
rng.fill(&mut bytes);
rng.record_generated(&bytes, loc);
bytes
}
#[track_caller]
pub fn gen_bool(rng: &mut Rng) -> bool {
let loc = Location::caller();
let v = raw_bytes::<1>(rng)[0] & 1 != 0;
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_u8(rng: &mut Rng) -> u8 {
let loc = Location::caller();
let v = raw_bytes::<1>(rng)[0];
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_u16(rng: &mut Rng) -> u16 {
let loc = Location::caller();
let v = u16::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_u32(rng: &mut Rng) -> u32 {
let loc = Location::caller();
let v = u32::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_u64(rng: &mut Rng) -> u64 {
let loc = Location::caller();
let v = u64::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_u128(rng: &mut Rng) -> u128 {
let loc = Location::caller();
let v = u128::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_usize(rng: &mut Rng) -> usize {
let loc = Location::caller();
let v = usize::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_i8(rng: &mut Rng) -> i8 {
let loc = Location::caller();
let v = raw_bytes::<1>(rng)[0] as i8;
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_i16(rng: &mut Rng) -> i16 {
let loc = Location::caller();
let v = i16::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_i32(rng: &mut Rng) -> i32 {
let loc = Location::caller();
let v = i32::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_i64(rng: &mut Rng) -> i64 {
let loc = Location::caller();
let v = i64::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_i128(rng: &mut Rng) -> i128 {
let loc = Location::caller();
let v = i128::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_isize(rng: &mut Rng) -> isize {
let loc = Location::caller();
let v = isize::from_le_bytes(raw_bytes(rng));
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_non_zero_u8(rng: &mut Rng) -> NonZero<u8> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(raw_bytes::<1>(rng)[0]) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_u8: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_u16(rng: &mut Rng) -> NonZero<u16> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(u16::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_u16: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_u32(rng: &mut Rng) -> NonZero<u32> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(u32::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_u32: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_u64(rng: &mut Rng) -> NonZero<u64> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(u64::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_u64: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_u128(rng: &mut Rng) -> NonZero<u128> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(u128::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_u128: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_usize(rng: &mut Rng) -> NonZero<usize> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(usize::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_usize: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_i8(rng: &mut Rng) -> NonZero<i8> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(raw_bytes::<1>(rng)[0] as i8) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_i8: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_i16(rng: &mut Rng) -> NonZero<i16> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(i16::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_i16: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_i32(rng: &mut Rng) -> NonZero<i32> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(i32::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_i32: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_i64(rng: &mut Rng) -> NonZero<i64> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(i64::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_i64: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_i128(rng: &mut Rng) -> NonZero<i128> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(i128::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_i128: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_non_zero_isize(rng: &mut Rng) -> NonZero<isize> {
let loc = Location::caller();
for _ in 0..64 {
if let Some(nz) = NonZero::new(isize::from_le_bytes(raw_bytes(rng))) {
rng.record_generated(&nz, loc);
return nz;
}
}
panic!("gen_non_zero_isize: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_char(rng: &mut Rng) -> char {
let loc = Location::caller();
for _ in 0..64 {
let n = u32::from_le_bytes(raw_bytes(rng)) & 0x1F_FFFF;
if let Some(c) = char::from_u32(n) {
rng.record_generated(&c, loc);
return c;
}
}
panic!("gen_char: rejection sampling exhausted")
}
#[track_caller]
pub fn gen_ascii_char(rng: &mut Rng) -> char {
let loc = Location::caller();
let v = (raw_bytes::<1>(rng)[0] & 0x7F) as char;
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_ascii_printable_char(rng: &mut Rng) -> char {
let loc = Location::caller();
let v = (0x20 + u32::from_le_bytes(raw_bytes(rng)) % 95) as u8 as char;
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_f32(rng: &mut Rng, min: f32, max: f32) -> f32 {
assert!(
min.is_finite() && max.is_finite(),
"gen_f32: min and max must be finite"
);
assert!(min < max, "gen_f32: min must be less than max");
let loc = Location::caller();
let bits = 0x3F80_0000 | (u32::from_le_bytes(raw_bytes(rng)) >> 9);
let unit = f32::from_bits(bits) - 1.0;
let v = min + (max - min) * unit;
rng.record_generated(&v, loc);
v
}
#[track_caller]
pub fn gen_f64(rng: &mut Rng, min: f64, max: f64) -> f64 {
assert!(
min.is_finite() && max.is_finite(),
"gen_f64: min and max must be finite"
);
assert!(min < max, "gen_f64: min must be less than max");
let loc = Location::caller();
let bits = 0x3FF0_0000_0000_0000 | (u64::from_le_bytes(raw_bytes(rng)) >> 12);
let unit = f64::from_bits(bits) - 1.0;
let v = min + (max - min) * unit;
rng.record_generated(&v, loc);
v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primitives_are_deterministic() {
let mut a = Rng::new(123);
let mut b = Rng::new(123);
assert_eq!(gen_bool(&mut a), gen_bool(&mut b));
assert_eq!(gen_u8(&mut a), gen_u8(&mut b));
assert_eq!(gen_u16(&mut a), gen_u16(&mut b));
assert_eq!(gen_u32(&mut a), gen_u32(&mut b));
assert_eq!(gen_u64(&mut a), gen_u64(&mut b));
assert_eq!(gen_u128(&mut a), gen_u128(&mut b));
assert_eq!(gen_usize(&mut a), gen_usize(&mut b));
assert_eq!(gen_i8(&mut a), gen_i8(&mut b));
assert_eq!(gen_i16(&mut a), gen_i16(&mut b));
assert_eq!(gen_i32(&mut a), gen_i32(&mut b));
assert_eq!(gen_i64(&mut a), gen_i64(&mut b));
assert_eq!(gen_i128(&mut a), gen_i128(&mut b));
assert_eq!(gen_isize(&mut a), gen_isize(&mut b));
}
#[test]
fn bool_produces_both_values() {
let mut rng = Rng::new(1);
let (mut t, mut f) = (false, false);
for _ in 0..64 {
match gen_bool(&mut rng) {
true => t = true,
false => f = true,
}
if t && f {
return;
}
}
panic!("bool samples covered only one value");
}
#[test]
fn u8_covers_both_halves_of_range() {
let mut rng = Rng::new(1);
let (mut low, mut high) = (false, false);
for _ in 0..64 {
let v = gen_u8(&mut rng);
low |= v < 128;
high |= v >= 128;
if low && high {
return;
}
}
panic!("u8 samples covered only one half of the range");
}
#[test]
fn i8_can_be_negative_and_nonnegative() {
let mut rng = Rng::new(1);
let (mut neg, mut nonneg) = (false, false);
for _ in 0..64 {
let v = gen_i8(&mut rng);
neg |= v < 0;
nonneg |= v >= 0;
if neg && nonneg {
return;
}
}
panic!("i8 samples covered only one sign");
}
#[test]
fn non_zero_primitives_are_deterministic() {
let mut a = Rng::new(456);
let mut b = Rng::new(456);
assert_eq!(gen_non_zero_u8(&mut a), gen_non_zero_u8(&mut b));
assert_eq!(gen_non_zero_u16(&mut a), gen_non_zero_u16(&mut b));
assert_eq!(gen_non_zero_u32(&mut a), gen_non_zero_u32(&mut b));
assert_eq!(gen_non_zero_u64(&mut a), gen_non_zero_u64(&mut b));
assert_eq!(gen_non_zero_u128(&mut a), gen_non_zero_u128(&mut b));
assert_eq!(gen_non_zero_usize(&mut a), gen_non_zero_usize(&mut b));
assert_eq!(gen_non_zero_i8(&mut a), gen_non_zero_i8(&mut b));
assert_eq!(gen_non_zero_i16(&mut a), gen_non_zero_i16(&mut b));
assert_eq!(gen_non_zero_i32(&mut a), gen_non_zero_i32(&mut b));
assert_eq!(gen_non_zero_i64(&mut a), gen_non_zero_i64(&mut b));
assert_eq!(gen_non_zero_i128(&mut a), gen_non_zero_i128(&mut b));
assert_eq!(gen_non_zero_isize(&mut a), gen_non_zero_isize(&mut b));
}
#[test]
fn non_zero_u8_exercises_rejection_loop() {
let mut rng = Rng::new(1);
for _ in 0..1000 {
let _ = gen_non_zero_u8(&mut rng);
}
}
#[test]
fn gen_choice_returns_only_from_slice() {
let mut rng = Rng::new(1);
let choices = [10, 20, 30];
for _ in 0..256 {
assert!(choices.contains(&gen_choice(&mut rng, &choices)));
}
}
#[test]
fn gen_choice_can_hit_every_element() {
let mut rng = Rng::new(1);
let choices = [10, 20, 30];
let mut seen = [false; 3];
for _ in 0..256 {
let v = gen_choice(&mut rng, &choices);
let idx = choices.iter().position(|&x| x == v).unwrap();
seen[idx] = true;
if seen.iter().all(|&s| s) {
return;
}
}
panic!("gen_choice did not cover all elements");
}
#[test]
#[should_panic(expected = "empty slice")]
fn gen_choice_panics_on_empty() {
let mut rng = Rng::new(0);
let empty: [u32; 0] = [];
let _ = gen_choice(&mut rng, &empty);
}
#[test]
fn gen_choice_works_with_clone_only_types() {
let mut rng = Rng::new(1);
let choices = vec![String::from("a"), String::from("b"), String::from("c")];
let picked = gen_choice(&mut rng, &choices);
assert!(choices.contains(&picked));
}
#[test]
fn char_generators_are_deterministic() {
let mut a = Rng::new(789);
let mut b = Rng::new(789);
assert_eq!(gen_char(&mut a), gen_char(&mut b));
assert_eq!(gen_ascii_char(&mut a), gen_ascii_char(&mut b));
assert_eq!(
gen_ascii_printable_char(&mut a),
gen_ascii_printable_char(&mut b)
);
}
#[test]
fn gen_ascii_char_always_in_ascii_range() {
let mut rng = Rng::new(1);
for _ in 0..1000 {
let c = gen_ascii_char(&mut rng);
assert!(c.is_ascii());
}
}
#[test]
fn gen_ascii_printable_char_always_in_range() {
let mut rng = Rng::new(1);
for _ in 0..1000 {
let c = gen_ascii_printable_char(&mut rng);
let n = c as u32;
assert!((0x20..=0x7E).contains(&n));
}
}
#[test]
fn gen_char_produces_varied_values() {
let mut rng = Rng::new(1);
let mut seen = std::collections::HashSet::new();
for _ in 0..256 {
seen.insert(gen_char(&mut rng));
}
assert!(
seen.len() > 200,
"gen_char produced too few distinct values: {}",
seen.len()
);
}
#[test]
fn float_generators_are_deterministic() {
let mut a = Rng::new(999);
let mut b = Rng::new(999);
assert_eq!(gen_f32(&mut a, 0.0, 1.0), gen_f32(&mut b, 0.0, 1.0));
assert_eq!(
gen_f64(&mut a, -100.0, 100.0),
gen_f64(&mut b, -100.0, 100.0)
);
}
#[test]
fn gen_f32_stays_in_range() {
let mut rng = Rng::new(1);
for _ in 0..1000 {
let v = gen_f32(&mut rng, 10.0, 20.0);
assert!((10.0..20.0).contains(&v), "out of range: {v}");
}
}
#[test]
fn gen_f64_stays_in_range() {
let mut rng = Rng::new(1);
for _ in 0..1000 {
let v = gen_f64(&mut rng, -1.0, 1.0);
assert!((-1.0..1.0).contains(&v), "out of range: {v}");
}
}
#[test]
fn gen_f32_covers_both_halves_of_range() {
let mut rng = Rng::new(1);
let (mut low, mut high) = (false, false);
for _ in 0..64 {
let v = gen_f32(&mut rng, 0.0, 1.0);
low |= v < 0.5;
high |= v >= 0.5;
if low && high {
return;
}
}
panic!("gen_f32 covered only one half of the range");
}
#[test]
#[should_panic(expected = "must be less than")]
fn gen_f32_panics_when_min_equals_max() {
let mut rng = Rng::new(0);
let _ = gen_f32(&mut rng, 5.0, 5.0);
}
#[test]
#[should_panic(expected = "must be finite")]
fn gen_f32_panics_on_nan() {
let mut rng = Rng::new(0);
let _ = gen_f32(&mut rng, f32::NAN, 1.0);
}
#[test]
#[should_panic(expected = "must be finite")]
fn gen_f32_panics_on_infinity() {
let mut rng = Rng::new(0);
let _ = gen_f32(&mut rng, 0.0, f32::INFINITY);
}
#[test]
#[should_panic(expected = "must be finite")]
fn gen_f64_panics_on_nan() {
let mut rng = Rng::new(0);
let _ = gen_f64(&mut rng, 0.0, f64::NAN);
}
}