//! Test cases to verify specific values. //! //! Each routine can have a set of inputs and, optinoally, outputs. If an output is provided, it //! will be used to check against. If only inputs are provided, the case will be checked against //! a basis. //! //! This is useful for adding regression tests or expected failures. use libm::hf64; #[cfg(f128_enabled)] use libm::hf128; use crate::{CheckBasis, CheckCtx, GeneratorKind, MathOp, op}; pub struct TestCase { pub input: Op::RustArgs, pub output: Option, } impl TestCase { #[expect(dead_code)] fn append_inputs(v: &mut Vec, l: &[Op::RustArgs]) { v.extend(l.iter().copied().map(|input| Self { input, output: None, })); } fn append_pairs(v: &mut Vec, l: &[(Op::RustArgs, Option)]) where Op::RustRet: Copy, { v.extend( l.iter() .copied() .map(|(input, output)| Self { input, output }), ); } } fn acos_cases() -> Vec> { vec![] } fn acosf_cases() -> Vec> { vec![] } fn acosh_cases() -> Vec> { vec![] } fn acoshf_cases() -> Vec> { vec![] } fn asin_cases() -> Vec> { vec![] } fn asinf_cases() -> Vec> { vec![] } fn asinh_cases() -> Vec> { vec![] } fn asinhf_cases() -> Vec> { vec![] } fn atan_cases() -> Vec> { vec![] } fn atan2_cases() -> Vec> { vec![] } fn atan2f_cases() -> Vec> { vec![] } fn atanf_cases() -> Vec> { vec![] } fn atanh_cases() -> Vec> { vec![] } fn atanhf_cases() -> Vec> { vec![] } fn cbrt_cases() -> Vec> { vec![] } fn cbrtf_cases() -> Vec> { vec![] } fn ceil_cases() -> Vec> { vec![] } fn ceilf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn ceilf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn ceilf16_cases() -> Vec> { vec![] } fn copysign_cases() -> Vec> { vec![] } fn copysignf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn copysignf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn copysignf16_cases() -> Vec> { vec![] } fn cos_cases() -> Vec> { vec![] } fn cosf_cases() -> Vec> { vec![] } fn cosh_cases() -> Vec> { vec![] } fn coshf_cases() -> Vec> { vec![] } fn erf_cases() -> Vec> { vec![] } fn erfc_cases() -> Vec> { vec![] } fn erfcf_cases() -> Vec> { vec![] } fn erff_cases() -> Vec> { vec![] } fn exp_cases() -> Vec> { vec![] } fn exp10_cases() -> Vec> { vec![] } fn exp10f_cases() -> Vec> { vec![] } fn exp2_cases() -> Vec> { vec![] } fn exp2f_cases() -> Vec> { vec![] } fn expf_cases() -> Vec> { vec![] } fn expm1_cases() -> Vec> { vec![] } fn expm1f_cases() -> Vec> { vec![] } fn fabs_cases() -> Vec> { vec![] } fn fabsf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fabsf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fabsf16_cases() -> Vec> { vec![] } fn fdim_cases() -> Vec> { vec![] } fn fdimf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fdimf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fdimf16_cases() -> Vec> { vec![] } fn floor_cases() -> Vec> { vec![] } fn floorf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn floorf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn floorf16_cases() -> Vec> { vec![] } fn fma_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Previous failure with incorrect sign ((5e-324, -5e-324, 0.0), Some(-0.0)), ], ); v } fn fmaf_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Known rounding error for some implementations (notably MinGW) ( (-1.9369631e13f32, 2.1513551e-7, -1.7354427e-24), Some(-4167095.8), ), ], ); v } #[cfg(f128_enabled)] fn fmaf128_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ ( // Tricky rounding case that previously failed in extensive tests ( hf128!("-0x1.1966cc01966cc01966cc01966f06p-25"), hf128!("-0x1.669933fe69933fe69933fe6997c9p-16358"), hf128!("-0x0.000000000000000000000000048ap-16382"), ), Some(hf128!("0x0.c5171470a3ff5e0f68d751491b18p-16382")), ), ( // Subnormal edge case that caused a failure ( hf128!("0x0.7ffffffffffffffffffffffffff7p-16382"), hf128!("0x1.ffffffffffffffffffffffffffffp-1"), hf128!("0x0.8000000000000000000000000009p-16382"), ), Some(hf128!("0x1.0000000000000000000000000000p-16382")), ), ], ); v } #[cfg(f16_enabled)] fn fmaxf16_cases() -> Vec> { vec![] } fn fmaxf_cases() -> Vec> { vec![] } fn fmax_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fmaxf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fmaximumf16_cases() -> Vec> { vec![] } fn fmaximumf_cases() -> Vec> { vec![] } fn fmaximum_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fmaximumf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fmaximum_numf16_cases() -> Vec> { vec![] } fn fmaximum_numf_cases() -> Vec> { vec![] } fn fmaximum_num_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fmaximum_numf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fminf16_cases() -> Vec> { vec![] } fn fminf_cases() -> Vec> { vec![] } fn fmin_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fminf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fminimumf16_cases() -> Vec> { vec![] } fn fminimumf_cases() -> Vec> { vec![] } fn fminimum_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fminimumf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fminimum_numf16_cases() -> Vec> { vec![] } fn fminimum_numf_cases() -> Vec> { vec![] } fn fminimum_num_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn fminimum_numf128_cases() -> Vec> { vec![] } fn fmod_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Previous failure with incorrect loop iteration // ((2.1, 3.123e-320), Some(2.0696e-320)), ((2.1, 2.253547e-318), Some(1.772535e-318)), ], ); v } fn fmodf_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Previous failure with incorrect loop iteration // ((2.1, 8.858e-42), Some(8.085e-42)), ((2.1, 6.39164e-40), Some(6.1636e-40)), ((5.5, 6.39164e-40), Some(4.77036e-40)), ((-151.189, 6.39164e-40), Some(-5.64734e-40)), ], ); v } #[cfg(f128_enabled)] fn fmodf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn fmodf16_cases() -> Vec> { vec![] } fn frexp_cases() -> Vec> { vec![] } fn frexpf_cases() -> Vec> { vec![] } fn hypot_cases() -> Vec> { vec![] } fn hypotf_cases() -> Vec> { vec![] } fn ilogb_cases() -> Vec> { vec![] } fn ilogbf_cases() -> Vec> { vec![] } fn j0_cases() -> Vec> { vec![] } fn j0f_cases() -> Vec> { vec![] } fn j1_cases() -> Vec> { vec![] } fn j1f_cases() -> Vec> { vec![] } fn jn_cases() -> Vec> { vec![] } fn jnf_cases() -> Vec> { vec![] } fn ldexp_cases() -> Vec> { vec![] } fn ldexpf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn ldexpf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn ldexpf16_cases() -> Vec> { vec![] } fn lgamma_cases() -> Vec> { vec![] } fn lgamma_r_cases() -> Vec> { vec![] } fn lgammaf_cases() -> Vec> { vec![] } fn lgammaf_r_cases() -> Vec> { vec![] } fn log_cases() -> Vec> { vec![] } fn log10_cases() -> Vec> { vec![] } fn log10f_cases() -> Vec> { vec![] } fn log1p_cases() -> Vec> { vec![] } fn log1pf_cases() -> Vec> { vec![] } fn log2_cases() -> Vec> { vec![] } fn log2f_cases() -> Vec> { vec![] } fn logf_cases() -> Vec> { vec![] } fn modf_cases() -> Vec> { vec![] } fn modff_cases() -> Vec> { vec![] } fn nextafter_cases() -> Vec> { vec![] } fn nextafterf_cases() -> Vec> { vec![] } fn pow_cases() -> Vec> { vec![] } fn powf_cases() -> Vec> { vec![] } fn remainder_cases() -> Vec> { vec![] } fn remainderf_cases() -> Vec> { vec![] } fn remquo_cases() -> Vec> { vec![] } fn remquof_cases() -> Vec> { vec![] } fn rint_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Known failure on i586 #[cfg(not(x86_no_sse))] ( (hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38")), ), #[cfg(x86_no_sse)] ( (hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff998000p+38")), ), ], ); v } fn rintf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn rintf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn rintf16_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn roundf16_cases() -> Vec> { vec![] } fn round_cases() -> Vec> { vec![] } fn roundf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn roundf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn roundevenf16_cases() -> Vec> { vec![] } fn roundeven_cases() -> Vec> { let mut v = vec![]; TestCase::append_pairs( &mut v, &[ // Known failure on i586 #[cfg(not(x86_no_sse))] ( (hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff994000p+38")), ), #[cfg(x86_no_sse)] ( (hf64!("-0x1.e3f13ff995ffcp+38"),), Some(hf64!("-0x1.e3f13ff998000p+38")), ), ], ); v } fn roundevenf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn roundevenf128_cases() -> Vec> { vec![] } fn scalbn_cases() -> Vec> { vec![] } fn scalbnf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn scalbnf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn scalbnf16_cases() -> Vec> { vec![] } fn sin_cases() -> Vec> { vec![] } fn sincos_cases() -> Vec> { vec![] } fn sincosf_cases() -> Vec> { vec![] } fn sinf_cases() -> Vec> { vec![] } fn sinh_cases() -> Vec> { vec![] } fn sinhf_cases() -> Vec> { vec![] } fn sqrt_cases() -> Vec> { vec![] } fn sqrtf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn sqrtf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn sqrtf16_cases() -> Vec> { vec![] } fn tan_cases() -> Vec> { vec![] } fn tanf_cases() -> Vec> { vec![] } fn tanh_cases() -> Vec> { vec![] } fn tanhf_cases() -> Vec> { vec![] } fn tgamma_cases() -> Vec> { vec![] } fn tgammaf_cases() -> Vec> { vec![] } fn trunc_cases() -> Vec> { vec![] } fn truncf_cases() -> Vec> { vec![] } #[cfg(f128_enabled)] fn truncf128_cases() -> Vec> { vec![] } #[cfg(f16_enabled)] fn truncf16_cases() -> Vec> { vec![] } fn y0_cases() -> Vec> { vec![] } fn y0f_cases() -> Vec> { vec![] } fn y1_cases() -> Vec> { vec![] } fn y1f_cases() -> Vec> { vec![] } fn yn_cases() -> Vec> { vec![] } fn ynf_cases() -> Vec> { vec![] } pub trait CaseListInput: MathOp + Sized { fn get_cases() -> Vec>; } macro_rules! impl_case_list { ( fn_name: $fn_name:ident, attrs: [$($attr:meta),*], ) => { paste::paste! { $(#[$attr])* impl CaseListInput for crate::op::$fn_name::Routine { fn get_cases() -> Vec> { [< $fn_name _cases >]() } } } }; } libm_macros::for_each_function! { callback: impl_case_list, } /// This is the test generator for standalone tests, i.e. those with no basis. For this, it /// only extracts tests with a known output. pub fn get_test_cases_standalone( ctx: &CheckCtx, ) -> impl Iterator + use<'_, Op> where Op: MathOp + CaseListInput, { assert_eq!(ctx.basis, CheckBasis::None); assert_eq!(ctx.gen_kind, GeneratorKind::List); Op::get_cases() .into_iter() .filter_map(|x| x.output.map(|o| (x.input, o))) } /// Opposite of the above; extract only test cases that don't have a known output, to be run /// against a basis. pub fn get_test_cases_basis( ctx: &CheckCtx, ) -> (impl Iterator + use<'_, Op>, u64) where Op: MathOp + CaseListInput, { assert_ne!(ctx.basis, CheckBasis::None); assert_eq!(ctx.gen_kind, GeneratorKind::List); let cases = Op::get_cases(); let count: u64 = cases .iter() .filter(|case| case.output.is_none()) .count() .try_into() .unwrap(); ( cases .into_iter() .filter(|x| x.output.is_none()) .map(|x| x.input), count, ) }