|
| 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 ntests = if cfg!(optimizations_enabled) { |
| 17 | + if cfg!(target_arch = "x86_64") || cfg!(target_arch = "aarch64") { |
| 18 | + 5_000_000 |
| 19 | + } else if !cfg!(target_pointer_width = "64") |
| 20 | + || cfg!(all(target_arch = "x86_64", target_vendor = "apple")) |
| 21 | + || option_env!("EMULATED").is_some() |
| 22 | + && cfg!(any(target_arch = "aarch64", target_arch = "powerpc64")) |
| 23 | + { |
| 24 | + // Tests are pretty slow on: |
| 25 | + // - Non-64-bit targets |
| 26 | + // - Emulated ppc |
| 27 | + // - Emulated aarch64 |
| 28 | + // - x86 MacOS |
| 29 | + // So reduce the number of iterations |
| 30 | + 100_000 |
| 31 | + } else { |
| 32 | + // Most everything else gets tested in docker and works okay, but we still |
| 33 | + // don't need 20 minutes of tests. |
| 34 | + 1_000_000 |
| 35 | + } |
| 36 | + } else { |
| 37 | + 800 |
| 38 | + }; |
| 39 | + |
| 40 | + ntests |
| 41 | +}; |
| 42 | + |
| 43 | +/// Tested inputs. |
| 44 | +static TEST_CASES: LazyLock<CachedInput> = LazyLock::new(|| make_test_cases(NTESTS)); |
| 45 | + |
| 46 | +/// The first argument to `jn` and `jnf` is the number of iterations. Make this a reasonable |
| 47 | +/// value so tests don't run forever. |
| 48 | +static TEST_CASES_JN: LazyLock<CachedInput> = LazyLock::new(|| { |
| 49 | + // Start with regular test cases |
| 50 | + let mut cases = (&*TEST_CASES).clone(); |
| 51 | + |
| 52 | + // These functions are extremely slow, limit them |
| 53 | + cases.inputs_i32.truncate((NTESTS / 1000).max(80)); |
| 54 | + cases.inputs_f32.truncate((NTESTS / 1000).max(80)); |
| 55 | + cases.inputs_f64.truncate((NTESTS / 1000).max(80)); |
| 56 | + |
| 57 | + // It is easy to overflow the stack with these in debug mode |
| 58 | + let max_iterations = if cfg!(optimizations_enabled) && cfg!(target_pointer_width = "64") { |
| 59 | + 0xffff |
| 60 | + } else if cfg!(windows) { |
| 61 | + 0x00ff |
| 62 | + } else { |
| 63 | + 0x0fff |
| 64 | + }; |
| 65 | + |
| 66 | + let mut rng = ChaCha8Rng::from_seed(SEED); |
| 67 | + |
| 68 | + for case in cases.inputs_i32.iter_mut() { |
| 69 | + case.0 = rng.gen_range(3..=max_iterations); |
| 70 | + } |
| 71 | + |
| 72 | + cases |
| 73 | +}); |
| 74 | + |
| 75 | +fn make_test_cases(ntests: usize) -> CachedInput { |
| 76 | + let mut rng = ChaCha8Rng::from_seed(SEED); |
| 77 | + |
| 78 | + // make sure we include some basic cases |
| 79 | + let mut inputs_i32 = vec![(0, 0, 0), (1, 1, 1), (-1, -1, -1)]; |
| 80 | + let mut inputs_f32 = vec![ |
| 81 | + (0.0, 0.0, 0.0), |
| 82 | + (f32::EPSILON, f32::EPSILON, f32::EPSILON), |
| 83 | + (f32::INFINITY, f32::INFINITY, f32::INFINITY), |
| 84 | + (f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY), |
| 85 | + (f32::MAX, f32::MAX, f32::MAX), |
| 86 | + (f32::MIN, f32::MIN, f32::MIN), |
| 87 | + (f32::MIN_POSITIVE, f32::MIN_POSITIVE, f32::MIN_POSITIVE), |
| 88 | + (f32::NAN, f32::NAN, f32::NAN), |
| 89 | + ]; |
| 90 | + let mut inputs_f64 = vec![ |
| 91 | + (0.0, 0.0, 0.0), |
| 92 | + (f64::EPSILON, f64::EPSILON, f64::EPSILON), |
| 93 | + (f64::INFINITY, f64::INFINITY, f64::INFINITY), |
| 94 | + (f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY), |
| 95 | + (f64::MAX, f64::MAX, f64::MAX), |
| 96 | + (f64::MIN, f64::MIN, f64::MIN), |
| 97 | + (f64::MIN_POSITIVE, f64::MIN_POSITIVE, f64::MIN_POSITIVE), |
| 98 | + (f64::NAN, f64::NAN, f64::NAN), |
| 99 | + ]; |
| 100 | + |
| 101 | + inputs_i32.extend((0..(ntests - inputs_i32.len())).map(|_| rng.gen::<(i32, i32, i32)>())); |
| 102 | + |
| 103 | + // Generate integers to get a full range of bitpatterns, then convert back to |
| 104 | + // floats. |
| 105 | + inputs_f32.extend((0..(ntests - inputs_f32.len())).map(|_| { |
| 106 | + let ints = rng.gen::<(u32, u32, u32)>(); |
| 107 | + (f32::from_bits(ints.0), f32::from_bits(ints.1), f32::from_bits(ints.2)) |
| 108 | + })); |
| 109 | + inputs_f64.extend((0..(ntests - inputs_f64.len())).map(|_| { |
| 110 | + let ints = rng.gen::<(u64, u64, u64)>(); |
| 111 | + (f64::from_bits(ints.0), f64::from_bits(ints.1), f64::from_bits(ints.2)) |
| 112 | + })); |
| 113 | + |
| 114 | + CachedInput { inputs_f32, inputs_f64, inputs_i32 } |
| 115 | +} |
| 116 | + |
| 117 | +/// Create a test case iterator. |
| 118 | +pub fn get_test_cases<RustArgs>(fname: &str) -> impl Iterator<Item = RustArgs> |
| 119 | +where |
| 120 | + CachedInput: GenerateInput<RustArgs>, |
| 121 | +{ |
| 122 | + let inputs = if fname == "jn" || fname == "jnf" { &TEST_CASES_JN } else { &TEST_CASES }; |
| 123 | + |
| 124 | + CachedInput::get_cases(inputs) |
| 125 | +} |
0 commit comments