pub mod array; #[cfg(target_arch = "wasm32")] pub mod wasm; #[macro_use] pub mod biteq; /// Specifies the default strategy for testing a type. /// /// This strategy should be what "makes sense" to test. pub trait DefaultStrategy { type Strategy: proptest::strategy::Strategy; fn default_strategy() -> Self::Strategy; } macro_rules! impl_num { { $type:tt } => { impl DefaultStrategy for $type { type Strategy = proptest::num::$type::Any; fn default_strategy() -> Self::Strategy { proptest::num::$type::ANY } } } } impl_num! { i8 } impl_num! { i16 } impl_num! { i32 } impl_num! { i64 } impl_num! { isize } impl_num! { u8 } impl_num! { u16 } impl_num! { u32 } impl_num! { u64 } impl_num! { usize } impl_num! { f32 } impl_num! { f64 } #[cfg(not(target_arch = "wasm32"))] impl DefaultStrategy for u128 { type Strategy = proptest::num::u128::Any; fn default_strategy() -> Self::Strategy { proptest::num::u128::ANY } } #[cfg(not(target_arch = "wasm32"))] impl DefaultStrategy for i128 { type Strategy = proptest::num::i128::Any; fn default_strategy() -> Self::Strategy { proptest::num::i128::ANY } } #[cfg(target_arch = "wasm32")] impl DefaultStrategy for u128 { type Strategy = crate::wasm::u128::Any; fn default_strategy() -> Self::Strategy { crate::wasm::u128::ANY } } #[cfg(target_arch = "wasm32")] impl DefaultStrategy for i128 { type Strategy = crate::wasm::i128::Any; fn default_strategy() -> Self::Strategy { crate::wasm::i128::ANY } } impl DefaultStrategy for [T; LANES] { type Strategy = crate::array::UniformArrayStrategy; fn default_strategy() -> Self::Strategy { Self::Strategy::new(T::default_strategy()) } } #[cfg(not(miri))] pub fn make_runner() -> proptest::test_runner::TestRunner { Default::default() } #[cfg(miri)] pub fn make_runner() -> proptest::test_runner::TestRunner { // Only run a few tests on Miri proptest::test_runner::TestRunner::new(proptest::test_runner::Config::with_cases(4)) } /// Test a function that takes a single value. pub fn test_1( f: &dyn Fn(A) -> proptest::test_runner::TestCaseResult, ) { let mut runner = make_runner(); runner.run(&A::default_strategy(), f).unwrap(); } /// Test a function that takes two values. pub fn test_2( f: &dyn Fn(A, B) -> proptest::test_runner::TestCaseResult, ) { let mut runner = make_runner(); runner .run(&(A::default_strategy(), B::default_strategy()), |(a, b)| { f(a, b) }) .unwrap(); } /// Test a function that takes two values. pub fn test_3< A: core::fmt::Debug + DefaultStrategy, B: core::fmt::Debug + DefaultStrategy, C: core::fmt::Debug + DefaultStrategy, >( f: &dyn Fn(A, B, C) -> proptest::test_runner::TestCaseResult, ) { let mut runner = make_runner(); runner .run( &( A::default_strategy(), B::default_strategy(), C::default_strategy(), ), |(a, b, c)| f(a, b, c), ) .unwrap(); } /// Test a unary vector function against a unary scalar function, applied elementwise. #[inline(never)] pub fn test_unary_elementwise( fv: &dyn Fn(Vector) -> VectorResult, fs: &dyn Fn(Scalar) -> ScalarResult, check: &dyn Fn([Scalar; LANES]) -> bool, ) where Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy, ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_1(&|x: [Scalar; LANES]| { proptest::prop_assume!(check(x)); let result_1: [ScalarResult; LANES] = fv(x.into()).into(); let result_2: [ScalarResult; LANES] = { let mut result = [ScalarResult::default(); LANES]; for (i, o) in x.iter().zip(result.iter_mut()) { *o = fs(*i); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); } /// Test a unary vector function against a unary scalar function, applied elementwise. #[inline(never)] pub fn test_unary_mask_elementwise( fv: &dyn Fn(Vector) -> Mask, fs: &dyn Fn(Scalar) -> bool, check: &dyn Fn([Scalar; LANES]) -> bool, ) where Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy, { test_1(&|x: [Scalar; LANES]| { proptest::prop_assume!(check(x)); let result_1: [bool; LANES] = fv(x.into()).into(); let result_2: [bool; LANES] = { let mut result = [false; LANES]; for (i, o) in x.iter().zip(result.iter_mut()) { *o = fs(*i); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); } /// Test a binary vector function against a binary scalar function, applied elementwise. #[inline(never)] pub fn test_binary_elementwise< Scalar1, Scalar2, ScalarResult, Vector1, Vector2, VectorResult, const LANES: usize, >( fv: &dyn Fn(Vector1, Vector2) -> VectorResult, fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, ) where Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { proptest::prop_assume!(check(x, y)); let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); let result_2: [ScalarResult; LANES] = { let mut result = [ScalarResult::default(); LANES]; for ((i1, i2), o) in x.iter().zip(y.iter()).zip(result.iter_mut()) { *o = fs(*i1, *i2); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); } /// Test a binary vector-scalar function against a binary scalar function, applied elementwise. #[inline(never)] pub fn test_binary_scalar_rhs_elementwise< Scalar1, Scalar2, ScalarResult, Vector, VectorResult, const LANES: usize, >( fv: &dyn Fn(Vector, Scalar2) -> VectorResult, fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, check: &dyn Fn([Scalar1; LANES], Scalar2) -> bool, ) where Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_2(&|x: [Scalar1; LANES], y: Scalar2| { proptest::prop_assume!(check(x, y)); let result_1: [ScalarResult; LANES] = fv(x.into(), y).into(); let result_2: [ScalarResult; LANES] = { let mut result = [ScalarResult::default(); LANES]; for (i, o) in x.iter().zip(result.iter_mut()) { *o = fs(*i, y); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); } /// Test a binary vector-scalar function against a binary scalar function, applied elementwise. #[inline(never)] pub fn test_binary_scalar_lhs_elementwise< Scalar1, Scalar2, ScalarResult, Vector, VectorResult, const LANES: usize, >( fv: &dyn Fn(Scalar1, Vector) -> VectorResult, fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, check: &dyn Fn(Scalar1, [Scalar2; LANES]) -> bool, ) where Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_2(&|x: Scalar1, y: [Scalar2; LANES]| { proptest::prop_assume!(check(x, y)); let result_1: [ScalarResult; LANES] = fv(x, y.into()).into(); let result_2: [ScalarResult; LANES] = { let mut result = [ScalarResult::default(); LANES]; for (i, o) in y.iter().zip(result.iter_mut()) { *o = fs(x, *i); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); } /// Test a ternary vector function against a ternary scalar function, applied elementwise. #[inline(never)] pub fn test_ternary_elementwise< Scalar1, Scalar2, Scalar3, ScalarResult, Vector1, Vector2, Vector3, VectorResult, const LANES: usize, >( fv: &dyn Fn(Vector1, Vector2, Vector3) -> VectorResult, fs: &dyn Fn(Scalar1, Scalar2, Scalar3) -> ScalarResult, check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES], [Scalar3; LANES]) -> bool, ) where Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, Scalar3: Copy + Default + core::fmt::Debug + DefaultStrategy, ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, Vector3: Into<[Scalar3; LANES]> + From<[Scalar3; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_3( &|x: [Scalar1; LANES], y: [Scalar2; LANES], z: [Scalar3; LANES]| { proptest::prop_assume!(check(x, y, z)); let result_1: [ScalarResult; LANES] = fv(x.into(), y.into(), z.into()).into(); let result_2: [ScalarResult; LANES] = { let mut result = [ScalarResult::default(); LANES]; for ((i1, (i2, i3)), o) in x.iter().zip(y.iter().zip(z.iter())).zip(result.iter_mut()) { *o = fs(*i1, *i2, *i3); } result }; crate::prop_assert_biteq!(result_1, result_2); Ok(()) }, ); } /// Expand a const-generic test into separate tests for each possible lane count. #[macro_export] macro_rules! test_lanes { { $(fn $test:ident() $body:tt)* } => { $( mod $test { use super::*; fn implementation() where core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount, $body #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn lanes_1() { implementation::<1>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn lanes_2() { implementation::<2>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn lanes_4() { implementation::<4>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_8() { implementation::<8>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_16() { implementation::<16>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_32() { implementation::<32>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow fn lanes_64() { implementation::<64>(); } } )* } } /// Expand a const-generic `#[should_panic]` test into separate tests for each possible lane count. #[macro_export] macro_rules! test_lanes_panic { { $(fn $test:ident() $body:tt)* } => { $( mod $test { use super::*; fn implementation() where core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount, $body #[test] #[should_panic] fn lanes_1() { implementation::<1>(); } #[test] #[should_panic] fn lanes_2() { implementation::<2>(); } #[test] #[should_panic] fn lanes_4() { implementation::<4>(); } #[test] #[should_panic] fn lanes_8() { implementation::<8>(); } #[test] #[should_panic] fn lanes_16() { implementation::<16>(); } #[test] #[should_panic] fn lanes_32() { implementation::<32>(); } #[test] #[should_panic] fn lanes_64() { implementation::<64>(); } } )* } }