Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit 858a56c

Browse files
committed
Replace random tests
1 parent d895e81 commit 858a56c

File tree

7 files changed

+93
-187
lines changed

7 files changed

+93
-187
lines changed

crates/libm-test/benches/random.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use std::hint::black_box;
22
use std::time::Duration;
33

44
use criterion::{Criterion, criterion_main};
5-
use libm_test::gen::{CachedInput, random};
6-
use libm_test::{CheckBasis, CheckCtx, GenerateInput, MathOp, TupleCall};
5+
use libm_test::gen::random;
6+
use libm_test::gen::random::RandomInput;
7+
use libm_test::{CheckBasis, CheckCtx, MathOp, TupleCall};
78

89
/// Benchmark with this many items to get a variety
910
const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") { 50 } else { 500 };
@@ -45,7 +46,7 @@ macro_rules! musl_rand_benches {
4546
fn bench_one<Op>(c: &mut Criterion, musl_extra: MuslExtra<Op::CFn>)
4647
where
4748
Op: MathOp,
48-
CachedInput: GenerateInput<Op::RustArgs>,
49+
<Op as MathOp>::RustArgs: RandomInput,
4950
{
5051
let name = Op::NAME;
5152

crates/libm-test/src/gen.rs

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,5 @@
11
//! Different generators that can create random or systematic bit patterns.
22
3-
use crate::GenerateInput;
43
pub mod domain_logspace;
54
pub mod edge_cases;
65
pub mod random;
7-
8-
/// Helper type to turn any reusable input into a generator.
9-
#[derive(Clone, Debug, Default)]
10-
pub struct CachedInput {
11-
pub inputs_f32: Vec<(f32, f32, f32)>,
12-
pub inputs_f64: Vec<(f64, f64, f64)>,
13-
pub inputs_i32: Vec<(i32, i32, i32)>,
14-
}
15-
16-
impl GenerateInput<(f32,)> for CachedInput {
17-
fn get_cases(&self) -> impl Iterator<Item = (f32,)> {
18-
self.inputs_f32.iter().map(|f| (f.0,))
19-
}
20-
}
21-
22-
impl GenerateInput<(f32, f32)> for CachedInput {
23-
fn get_cases(&self) -> impl Iterator<Item = (f32, f32)> {
24-
self.inputs_f32.iter().map(|f| (f.0, f.1))
25-
}
26-
}
27-
28-
impl GenerateInput<(i32, f32)> for CachedInput {
29-
fn get_cases(&self) -> impl Iterator<Item = (i32, f32)> {
30-
self.inputs_i32.iter().zip(self.inputs_f32.iter()).map(|(i, f)| (i.0, f.0))
31-
}
32-
}
33-
34-
impl GenerateInput<(f32, i32)> for CachedInput {
35-
fn get_cases(&self) -> impl Iterator<Item = (f32, i32)> {
36-
GenerateInput::<(i32, f32)>::get_cases(self).map(|(i, f)| (f, i))
37-
}
38-
}
39-
40-
impl GenerateInput<(f32, f32, f32)> for CachedInput {
41-
fn get_cases(&self) -> impl Iterator<Item = (f32, f32, f32)> {
42-
self.inputs_f32.iter().copied()
43-
}
44-
}
45-
46-
impl GenerateInput<(f64,)> for CachedInput {
47-
fn get_cases(&self) -> impl Iterator<Item = (f64,)> {
48-
self.inputs_f64.iter().map(|f| (f.0,))
49-
}
50-
}
51-
52-
impl GenerateInput<(f64, f64)> for CachedInput {
53-
fn get_cases(&self) -> impl Iterator<Item = (f64, f64)> {
54-
self.inputs_f64.iter().map(|f| (f.0, f.1))
55-
}
56-
}
57-
58-
impl GenerateInput<(i32, f64)> for CachedInput {
59-
fn get_cases(&self) -> impl Iterator<Item = (i32, f64)> {
60-
self.inputs_i32.iter().zip(self.inputs_f64.iter()).map(|(i, f)| (i.0, f.0))
61-
}
62-
}
63-
64-
impl GenerateInput<(f64, i32)> for CachedInput {
65-
fn get_cases(&self) -> impl Iterator<Item = (f64, i32)> {
66-
GenerateInput::<(i32, f64)>::get_cases(self).map(|(i, f)| (f, i))
67-
}
68-
}
69-
70-
impl GenerateInput<(f64, f64, f64)> for CachedInput {
71-
fn get_cases(&self) -> impl Iterator<Item = (f64, f64, f64)> {
72-
self.inputs_f64.iter().copied()
73-
}
74-
}

crates/libm-test/src/gen/random.rs

Lines changed: 76 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,96 @@
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;
1+
use std::ops::RangeInclusive;
52

3+
use libm::support::Float;
4+
use rand::distributions::Standard;
5+
use rand::prelude::Distribution;
66
use rand::{Rng, SeedableRng};
77
use rand_chacha::ChaCha8Rng;
88

9-
use super::CachedInput;
10-
use crate::{BaseName, CheckCtx, GenerateInput};
9+
use crate::run_cfg::{int_range, iteration_count};
10+
use crate::{CheckCtx, GeneratorKind};
1111

1212
const SEED: [u8; 32] = *b"3.141592653589793238462643383279";
1313

14-
/// Number of tests to run.
15-
// FIXME(ntests): clean this up when possible
16-
const NTESTS: usize = {
17-
if cfg!(optimizations_enabled) {
18-
if crate::emulated()
19-
|| !cfg!(target_pointer_width = "64")
20-
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"))
21-
{
22-
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run
23-
// in QEMU.
24-
100_000
25-
} else {
26-
5_000_000
27-
}
28-
} else {
29-
// Without optimizations just run a quick check
30-
800
31-
}
32-
};
33-
34-
/// Tested inputs.
35-
static TEST_CASES: LazyLock<CachedInput> = LazyLock::new(|| make_test_cases(NTESTS));
36-
37-
/// The first argument to `jn` and `jnf` is the number of iterations. Make this a reasonable
38-
/// value so tests don't run forever.
39-
static TEST_CASES_JN: LazyLock<CachedInput> = LazyLock::new(|| {
40-
// Start with regular test cases
41-
let mut cases = (*TEST_CASES).clone();
42-
43-
// These functions are extremely slow, limit them
44-
let ntests_jn = (NTESTS / 1000).max(80);
45-
cases.inputs_i32.truncate(ntests_jn);
46-
cases.inputs_f32.truncate(ntests_jn);
47-
cases.inputs_f64.truncate(ntests_jn);
48-
49-
// It is easy to overflow the stack with these in debug mode
50-
let max_iterations = if cfg!(optimizations_enabled) && cfg!(target_pointer_width = "64") {
51-
0xffff
52-
} else if cfg!(windows) {
53-
0x00ff
54-
} else {
55-
0x0fff
56-
};
14+
pub trait RandomInput {
15+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self>;
16+
}
5717

18+
fn random_floats<F: Float>(count: u64) -> impl Iterator<Item = F>
19+
where
20+
Standard: Distribution<F::Int>,
21+
{
5822
let mut rng = ChaCha8Rng::from_seed(SEED);
5923

60-
for case in cases.inputs_i32.iter_mut() {
61-
case.0 = rng.gen_range(3..=max_iterations);
62-
}
63-
64-
cases
65-
});
24+
// Generate integers to get a full range of bitpatterns, then convert back to
25+
// floats.
26+
(0..count).map(move |_| F::from_bits(rng.gen::<F::Int>()))
27+
}
6628

67-
fn make_test_cases(ntests: usize) -> CachedInput {
29+
fn random_ints(count: u64, range: RangeInclusive<i32>) -> impl Iterator<Item = i32> {
6830
let mut rng = ChaCha8Rng::from_seed(SEED);
31+
(0..count).map(move |_| rng.gen_range::<i32, _>(range.clone()))
32+
}
6933

70-
// make sure we include some basic cases
71-
let mut inputs_i32 = vec![(0, 0, 0), (1, 1, 1), (-1, -1, -1)];
72-
let mut inputs_f32 = vec![
73-
(0.0, 0.0, 0.0),
74-
(f32::EPSILON, f32::EPSILON, f32::EPSILON),
75-
(f32::INFINITY, f32::INFINITY, f32::INFINITY),
76-
(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
77-
(f32::MAX, f32::MAX, f32::MAX),
78-
(f32::MIN, f32::MIN, f32::MIN),
79-
(f32::MIN_POSITIVE, f32::MIN_POSITIVE, f32::MIN_POSITIVE),
80-
(f32::NAN, f32::NAN, f32::NAN),
81-
];
82-
let mut inputs_f64 = vec![
83-
(0.0, 0.0, 0.0),
84-
(f64::EPSILON, f64::EPSILON, f64::EPSILON),
85-
(f64::INFINITY, f64::INFINITY, f64::INFINITY),
86-
(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
87-
(f64::MAX, f64::MAX, f64::MAX),
88-
(f64::MIN, f64::MIN, f64::MIN),
89-
(f64::MIN_POSITIVE, f64::MIN_POSITIVE, f64::MIN_POSITIVE),
90-
(f64::NAN, f64::NAN, f64::NAN),
91-
];
34+
macro_rules! impl_random_input {
35+
($fty:ty) => {
36+
impl RandomInput for ($fty,) {
37+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self> {
38+
let count = iteration_count(ctx, GeneratorKind::Random, 0);
39+
random_floats(count).map(|f: $fty| (f,))
40+
}
41+
}
9242

93-
inputs_i32.extend((0..(ntests - inputs_i32.len())).map(|_| rng.gen::<(i32, i32, i32)>()));
43+
impl RandomInput for ($fty, $fty) {
44+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self> {
45+
let count0 = iteration_count(ctx, GeneratorKind::Random, 0);
46+
let count1 = iteration_count(ctx, GeneratorKind::Random, 1);
47+
random_floats(count0)
48+
.flat_map(move |f1: $fty| random_floats(count1).map(move |f2: $fty| (f1, f2)))
49+
}
50+
}
9451

95-
// Generate integers to get a full range of bitpatterns, then convert back to
96-
// floats.
97-
inputs_f32.extend((0..(ntests - inputs_f32.len())).map(|_| {
98-
let ints = rng.gen::<(u32, u32, u32)>();
99-
(f32::from_bits(ints.0), f32::from_bits(ints.1), f32::from_bits(ints.2))
100-
}));
101-
inputs_f64.extend((0..(ntests - inputs_f64.len())).map(|_| {
102-
let ints = rng.gen::<(u64, u64, u64)>();
103-
(f64::from_bits(ints.0), f64::from_bits(ints.1), f64::from_bits(ints.2))
104-
}));
52+
impl RandomInput for ($fty, $fty, $fty) {
53+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self> {
54+
let count0 = iteration_count(ctx, GeneratorKind::Random, 0);
55+
let count1 = iteration_count(ctx, GeneratorKind::Random, 1);
56+
let count2 = iteration_count(ctx, GeneratorKind::Random, 2);
57+
random_floats(count0).flat_map(move |f1: $fty| {
58+
random_floats(count1).flat_map(move |f2: $fty| {
59+
random_floats(count2).map(move |f3: $fty| (f1, f2, f3))
60+
})
61+
})
62+
}
63+
}
10564

106-
CachedInput { inputs_f32, inputs_f64, inputs_i32 }
65+
impl RandomInput for (i32, $fty) {
66+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self> {
67+
let count0 = iteration_count(ctx, GeneratorKind::Random, 0);
68+
let count1 = iteration_count(ctx, GeneratorKind::Random, 1);
69+
let range0 = int_range(ctx, 0);
70+
random_ints(count0, range0)
71+
.flat_map(move |f1: i32| random_floats(count1).map(move |f2: $fty| (f1, f2)))
72+
}
73+
}
74+
75+
impl RandomInput for ($fty, i32) {
76+
fn get_cases(ctx: &CheckCtx) -> impl Iterator<Item = Self> {
77+
let count0 = iteration_count(ctx, GeneratorKind::Random, 0);
78+
let count1 = iteration_count(ctx, GeneratorKind::Random, 1);
79+
let range1 = int_range(ctx, 1);
80+
random_floats(count0).flat_map(move |f1: $fty| {
81+
random_ints(count1, range1.clone()).map(move |f2: i32| (f1, f2))
82+
})
83+
}
84+
}
85+
};
10786
}
10887

88+
impl_random_input!(f32);
89+
impl_random_input!(f64);
90+
10991
/// Create a test case iterator.
110-
pub fn get_test_cases<RustArgs>(ctx: &CheckCtx) -> impl Iterator<Item = RustArgs>
111-
where
112-
CachedInput: GenerateInput<RustArgs>,
113-
{
114-
let inputs = if ctx.base_name == BaseName::Jn { &TEST_CASES_JN } else { &TEST_CASES };
115-
inputs.get_cases()
92+
pub fn get_test_cases<RustArgs: RandomInput>(
93+
ctx: &CheckCtx,
94+
) -> impl Iterator<Item = RustArgs> + use<'_, RustArgs> {
95+
RustArgs::get_cases(ctx)
11696
}

crates/libm-test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub use num::{FloatExt, logspace};
2424
pub use op::{BaseName, FloatTy, Identifier, MathOp, OpCFn, OpFTy, OpRustFn, OpRustRet, Ty};
2525
pub use precision::{MaybeOverride, SpecialCase, default_ulp};
2626
pub use run_cfg::{CheckBasis, CheckCtx, EXTENSIVE_ENV, GeneratorKind, TestAction};
27-
pub use test_traits::{CheckOutput, GenerateInput, Hex, TupleCall};
27+
pub use test_traits::{CheckOutput, Hex, TupleCall};
2828

2929
/// Result type for tests is usually from `anyhow`. Most times there is no success value to
3030
/// propagate.

crates/libm-test/src/test_traits.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//! Traits related to testing.
22
//!
3-
//! There are three main traits in this module:
3+
//! There are two main traits in this module:
44
//!
5-
//! - `GenerateInput`: implemented on any types that create test cases.
65
//! - `TupleCall`: implemented on tuples to allow calling them as function arguments.
76
//! - `CheckOutput`: implemented on anything that is an output type for validation against an
87
//! expected value.
@@ -13,11 +12,6 @@ use anyhow::{Context, bail, ensure};
1312

1413
use crate::{CheckCtx, Float, Int, MaybeOverride, SpecialCase, TestResult};
1514

16-
/// Implement this on types that can generate a sequence of tuples for test input.
17-
pub trait GenerateInput<TupleArgs> {
18-
fn get_cases(&self) -> impl Iterator<Item = TupleArgs>;
19-
}
20-
2115
/// Trait for calling a function with a tuple as arguments.
2216
///
2317
/// Implemented on the tuple with the function signature as the generic (so we can use the same

crates/libm-test/tests/compare_built_musl.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
// There are some targets we can't build musl for
1010
#![cfg(feature = "build-musl")]
1111

12-
use libm_test::gen::{CachedInput, random};
13-
use libm_test::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, MathOp, TupleCall};
12+
use libm_test::gen::random;
13+
use libm_test::gen::random::RandomInput;
14+
use libm_test::{CheckBasis, CheckCtx, CheckOutput, MathOp, TupleCall};
1415

15-
macro_rules! musl_rand_tests {
16+
macro_rules! musl_rand_tests{
1617
(
1718
fn_name: $fn_name:ident,
1819
attrs: [$($meta:meta)*]
@@ -21,16 +22,16 @@ macro_rules! musl_rand_tests {
2122
#[test]
2223
$(#[$meta])*
2324
fn [< musl_random_ $fn_name >]() {
24-
test_one::<libm_test::op::$fn_name::Routine>(musl_math_sys::$fn_name);
25+
test_one_random::<libm_test::op::$fn_name::Routine>(musl_math_sys::$fn_name);
2526
}
2627
}
2728
};
2829
}
2930

30-
fn test_one<Op>(musl_fn: Op::CFn)
31+
fn test_one_random<Op>(musl_fn: Op::CFn)
3132
where
3233
Op: MathOp,
33-
CachedInput: GenerateInput<Op::RustArgs>,
34+
<Op as MathOp>::RustArgs: RandomInput,
3435
{
3536
let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Musl);
3637
let cases = random::get_test_cases::<Op::RustArgs>(&ctx);

crates/libm-test/tests/multiprecision.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
#![cfg(feature = "test-multiprecision")]
44

55
use libm_test::domain::HasDomain;
6-
use libm_test::gen::{CachedInput, domain_logspace, edge_cases, random};
6+
use libm_test::gen::random::RandomInput;
7+
use libm_test::gen::{domain_logspace, edge_cases, random};
78
use libm_test::mpfloat::MpOp;
8-
use libm_test::{
9-
CheckBasis, CheckCtx, CheckOutput, GenerateInput, MathOp, OpFTy, OpRustFn, OpRustRet, TupleCall,
10-
};
9+
use libm_test::{CheckBasis, CheckCtx, CheckOutput, MathOp, OpFTy, OpRustFn, OpRustRet, TupleCall};
1110

1211
/// Test against MPFR with random inputs.
1312
macro_rules! mp_rand_tests {
@@ -29,7 +28,7 @@ macro_rules! mp_rand_tests {
2928
fn test_one_random<Op>()
3029
where
3130
Op: MathOp + MpOp,
32-
CachedInput: GenerateInput<Op::RustArgs>,
31+
<Op as MathOp>::RustArgs: RandomInput,
3332
{
3433
let mut mp_vals = Op::new_mp();
3534
let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr);

0 commit comments

Comments
 (0)