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()) } } /// Test a function that takes a single value. pub fn test_1( f: &dyn Fn(A) -> proptest::test_runner::TestCaseResult, ) { let mut runner = proptest::test_runner::TestRunner::default(); 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 = proptest::test_runner::TestRunner::default(); runner .run(&(A::default_strategy(), B::default_strategy()), |(a, b)| { f(a, b) }) .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 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(()) }); } /// 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::SimdU8<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU16<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU64<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU128<$lanes>: core_simd::LanesAtMost64, core_simd::SimdUsize<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI8<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI16<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI64<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI128<$lanes>: core_simd::LanesAtMost64, core_simd::SimdIsize<$lanes>: core_simd::LanesAtMost64, core_simd::SimdF32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdF64<$lanes>: core_simd::LanesAtMost64, core_simd::BitMask<$lanes>: core_simd::LanesAtMost64, $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)] fn lanes_8() { implementation::<8>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn lanes_16() { implementation::<16>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] fn lanes_32() { implementation::<32>(); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] 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::SimdU8<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU16<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU64<$lanes>: core_simd::LanesAtMost64, core_simd::SimdU128<$lanes>: core_simd::LanesAtMost64, core_simd::SimdUsize<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI8<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI16<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI64<$lanes>: core_simd::LanesAtMost64, core_simd::SimdI128<$lanes>: core_simd::LanesAtMost64, core_simd::SimdIsize<$lanes>: core_simd::LanesAtMost64, core_simd::SimdF32<$lanes>: core_simd::LanesAtMost64, core_simd::SimdF64<$lanes>: core_simd::LanesAtMost64, core_simd::BitMask<$lanes>: core_simd::LanesAtMost64, $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>(); } } )* } }