use std::env; use std::ops::RangeInclusive; use std::sync::LazyLock; use libm::support::Float; use rand::distr::{Alphanumeric, StandardUniform}; use rand::prelude::Distribution; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; use super::KnownSize; use crate::CheckCtx; use crate::run_cfg::{int_range, iteration_count}; pub(crate) const SEED_ENV: &str = "LIBM_SEED"; pub static SEED: LazyLock<[u8; 32]> = LazyLock::new(|| { let s = env::var(SEED_ENV).unwrap_or_else(|_| { let mut rng = rand::rng(); (0..32).map(|_| rng.sample(Alphanumeric) as char).collect() }); s.as_bytes().try_into().unwrap_or_else(|_| { panic!("Seed must be 32 characters, got `{s}`"); }) }); /// Generate a sequence of random values of this type. pub trait RandomInput: Sized { fn get_cases(ctx: &CheckCtx) -> (impl Iterator + Send, u64); } /// Generate a sequence of deterministically random floats. fn random_floats(count: u64) -> impl Iterator where StandardUniform: Distribution, { let mut rng = ChaCha8Rng::from_seed(*SEED); // Generate integers to get a full range of bitpatterns (including NaNs), then convert back // to the float type. (0..count).map(move |_| F::from_bits(rng.random::())) } /// Generate a sequence of deterministically random `i32`s within a specified range. fn random_ints(count: u64, range: RangeInclusive) -> impl Iterator { let mut rng = ChaCha8Rng::from_seed(*SEED); (0..count).map(move |_| rng.random_range::(range.clone())) } macro_rules! impl_random_input { ($fty:ty) => { impl RandomInput for ($fty,) { fn get_cases(ctx: &CheckCtx) -> (impl Iterator, u64) { let count = iteration_count(ctx, 0); let iter = random_floats(count).map(|f: $fty| (f,)); (iter, count) } } impl RandomInput for ($fty, $fty) { fn get_cases(ctx: &CheckCtx) -> (impl Iterator, u64) { let count0 = iteration_count(ctx, 0); let count1 = iteration_count(ctx, 1); let iter = random_floats(count0) .flat_map(move |f1: $fty| random_floats(count1).map(move |f2: $fty| (f1, f2))); (iter, count0 * count1) } } impl RandomInput for ($fty, $fty, $fty) { fn get_cases(ctx: &CheckCtx) -> (impl Iterator, u64) { let count0 = iteration_count(ctx, 0); let count1 = iteration_count(ctx, 1); let count2 = iteration_count(ctx, 2); let iter = random_floats(count0).flat_map(move |f1: $fty| { random_floats(count1).flat_map(move |f2: $fty| { random_floats(count2).map(move |f3: $fty| (f1, f2, f3)) }) }); (iter, count0 * count1 * count2) } } impl RandomInput for (i32, $fty) { fn get_cases(ctx: &CheckCtx) -> (impl Iterator, u64) { let count0 = iteration_count(ctx, 0); let count1 = iteration_count(ctx, 1); let range0 = int_range(ctx, 0); let iter = random_ints(count0, range0) .flat_map(move |f1: i32| random_floats(count1).map(move |f2: $fty| (f1, f2))); (iter, count0 * count1) } } impl RandomInput for ($fty, i32) { fn get_cases(ctx: &CheckCtx) -> (impl Iterator, u64) { let count0 = iteration_count(ctx, 0); let count1 = iteration_count(ctx, 1); let range1 = int_range(ctx, 1); let iter = random_floats(count0).flat_map(move |f1: $fty| { random_ints(count1, range1.clone()).map(move |f2: i32| (f1, f2)) }); (iter, count0 * count1) } } }; } #[cfg(f16_enabled)] impl_random_input!(f16); impl_random_input!(f32); impl_random_input!(f64); #[cfg(f128_enabled)] impl_random_input!(f128); /// Create a test case iterator. pub fn get_test_cases( ctx: &CheckCtx, ) -> ( impl Iterator + Send + use<'_, RustArgs>, u64, ) { let (iter, count) = RustArgs::get_cases(ctx); // Wrap in `KnownSize` so we get an assertion if the cuunt is wrong. (KnownSize::new(iter, count), count) }