|
| 1 | +//! A simple generator that produces deterministic random input, caching to use the same |
| 2 | +//! inputs for all functions. |
| 3 | +
|
| 4 | +use std::sync::LazyLock; |
| 5 | + |
| 6 | +use rand::{Rng, SeedableRng}; |
| 7 | +use rand_chacha::ChaCha8Rng; |
| 8 | + |
| 9 | +use super::CachedInput; |
| 10 | +use crate::GenerateInput; |
| 11 | + |
| 12 | +const SEED: [u8; 32] = *b"3.141592653589793238462643383279"; |
| 13 | + |
| 14 | +/// Number of tests to run. |
| 15 | +const NTESTS: usize = { |
| 16 | + let mut ntests = if cfg!(optimizations_enabled) { 8000 } else { 800 }; |
| 17 | + |
| 18 | + // Tests seem to be pretty slow on non-64-bit targets, emulated ppc, and x86 MacOS |
| 19 | + if !cfg!(target_pointer_width = "64") |
| 20 | + || cfg!(target_arch = "powerpc64") |
| 21 | + || cfg!(all(target_arch = "x86_64", target_vendor = "apple")) |
| 22 | + { |
| 23 | + ntests /= 10; |
| 24 | + } |
| 25 | + |
| 26 | + ntests |
| 27 | +}; |
| 28 | + |
| 29 | +/// Tested inputs. |
| 30 | +static TEST_CASES: LazyLock<CachedInput> = LazyLock::new(|| make_test_cases(NTESTS)); |
| 31 | + |
| 32 | +/// The first argument to `jn` and `jnf` is the number of iterations. Make this a reasonable |
| 33 | +/// value so tests don't run forever. |
| 34 | +static TEST_CASES_JN: LazyLock<CachedInput> = LazyLock::new(|| { |
| 35 | + // It is easy to overflow the stack with these in debug mode |
| 36 | + let max_iterations = if cfg!(optimizations_enabled) && cfg!(target_pointer_width = "64") { |
| 37 | + 0xffff |
| 38 | + } else if cfg!(windows) { |
| 39 | + 0x00ff |
| 40 | + } else { |
| 41 | + 0x0fff |
| 42 | + }; |
| 43 | + |
| 44 | + let mut rng = ChaCha8Rng::from_seed(SEED); |
| 45 | + let mut cases = (&*TEST_CASES).clone(); |
| 46 | + |
| 47 | + // These functions are slow, limit them |
| 48 | + let len = cases.inputs_f32.len(); |
| 49 | + cases.inputs_i32.truncate(len / 10); |
| 50 | + cases.inputs_f32.truncate(len / 10); |
| 51 | + cases.inputs_f64.truncate(len / 10); |
| 52 | + |
| 53 | + for case in cases.inputs_i32.iter_mut() { |
| 54 | + case.0 = rng.gen_range(3..=max_iterations); |
| 55 | + } |
| 56 | + |
| 57 | + cases |
| 58 | +}); |
| 59 | + |
| 60 | +fn make_test_cases(ntests: usize) -> CachedInput { |
| 61 | + let mut rng = ChaCha8Rng::from_seed(SEED); |
| 62 | + |
| 63 | + // make sure we include some basic cases |
| 64 | + let mut inputs_i32 = vec![(0, 0, 0), (1, 1, 1), (-1, -1, -1)]; |
| 65 | + let mut inputs_f32 = vec![ |
| 66 | + (0.0, 0.0, 0.0), |
| 67 | + (f32::EPSILON, f32::EPSILON, f32::EPSILON), |
| 68 | + (f32::INFINITY, f32::INFINITY, f32::INFINITY), |
| 69 | + (f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY), |
| 70 | + (f32::MAX, f32::MAX, f32::MAX), |
| 71 | + (f32::MIN, f32::MIN, f32::MIN), |
| 72 | + (f32::MIN_POSITIVE, f32::MIN_POSITIVE, f32::MIN_POSITIVE), |
| 73 | + (f32::NAN, f32::NAN, f32::NAN), |
| 74 | + ]; |
| 75 | + let mut inputs_f64 = vec![ |
| 76 | + (0.0, 0.0, 0.0), |
| 77 | + (f64::EPSILON, f64::EPSILON, f64::EPSILON), |
| 78 | + (f64::INFINITY, f64::INFINITY, f64::INFINITY), |
| 79 | + (f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY), |
| 80 | + (f64::MAX, f64::MAX, f64::MAX), |
| 81 | + (f64::MIN, f64::MIN, f64::MIN), |
| 82 | + (f64::MIN_POSITIVE, f64::MIN_POSITIVE, f64::MIN_POSITIVE), |
| 83 | + (f64::NAN, f64::NAN, f64::NAN), |
| 84 | + ]; |
| 85 | + |
| 86 | + inputs_i32.extend((0..(ntests - inputs_i32.len())).map(|_| rng.gen::<(i32, i32, i32)>())); |
| 87 | + |
| 88 | + // Generate integers to get a full range of bitpatterns, then convert back to |
| 89 | + // floats. |
| 90 | + inputs_f32.extend((0..(ntests - inputs_f32.len())).map(|_| { |
| 91 | + let ints = rng.gen::<(u32, u32, u32)>(); |
| 92 | + (f32::from_bits(ints.0), f32::from_bits(ints.1), f32::from_bits(ints.2)) |
| 93 | + })); |
| 94 | + inputs_f64.extend((0..(ntests - inputs_f64.len())).map(|_| { |
| 95 | + let ints = rng.gen::<(u64, u64, u64)>(); |
| 96 | + (f64::from_bits(ints.0), f64::from_bits(ints.1), f64::from_bits(ints.2)) |
| 97 | + })); |
| 98 | + |
| 99 | + CachedInput { inputs_f32, inputs_f64, inputs_i32 } |
| 100 | +} |
| 101 | + |
| 102 | +/// Create a test case iterator. |
| 103 | +pub fn get_test_cases<RustArgs>(fname: &str) -> impl Iterator<Item = RustArgs> |
| 104 | +where |
| 105 | + CachedInput: GenerateInput<RustArgs>, |
| 106 | +{ |
| 107 | + let inputs = if fname == "jn" || fname == "jnf" { &TEST_CASES_JN } else { &TEST_CASES }; |
| 108 | + |
| 109 | + CachedInput::get_cases(inputs) |
| 110 | +} |
0 commit comments