1 /// Implements a test on a unary operation using proptest.
3 /// Compares the vector operation to the equivalent scalar operation.
5 macro_rules! impl_unary_op_test {
6 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $scalar_fn:expr } => {
7 test_helpers::test_lanes! {
8 fn $fn<const LANES: usize>() {
9 test_helpers::test_unary_elementwise(
10 &<$vector as core::ops::$trait>::$fn,
17 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident } => {
18 impl_unary_op_test! { $vector, $scalar, $trait::$fn, <$scalar as core::ops::$trait>::$fn }
22 /// Implements a test on a binary operation using proptest.
24 /// Compares the vector operation to the equivalent scalar operation.
26 macro_rules! impl_binary_op_test {
27 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
31 test_helpers::test_lanes! {
32 fn normal<const LANES: usize>() {
33 test_helpers::test_binary_elementwise(
34 &<$vector as core::ops::$trait>::$fn,
40 fn scalar_rhs<const LANES: usize>() {
41 test_helpers::test_binary_scalar_rhs_elementwise(
42 &<$vector as core::ops::$trait<$scalar>>::$fn,
48 fn scalar_lhs<const LANES: usize>() {
49 test_helpers::test_binary_scalar_lhs_elementwise(
50 &<$scalar as core::ops::$trait<$vector>>::$fn,
56 fn assign<const LANES: usize>() {
57 test_helpers::test_binary_elementwise(
58 &|mut a, b| { <$vector as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
64 fn assign_scalar_rhs<const LANES: usize>() {
65 test_helpers::test_binary_scalar_rhs_elementwise(
66 &|mut a, b| { <$vector as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
74 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
75 impl_binary_op_test! { $vector, $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
79 /// Implements a test on a binary operation using proptest.
81 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
82 /// (like the `proptest_assume` macro).
84 /// Compares the vector operation to the equivalent scalar operation.
86 macro_rules! impl_binary_checked_op_test {
87 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
91 test_helpers::test_lanes! {
92 fn normal<const LANES: usize>() {
93 test_helpers::test_binary_elementwise(
94 &<$vector as core::ops::$trait>::$fn,
96 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
100 fn scalar_rhs<const LANES: usize>() {
101 test_helpers::test_binary_scalar_rhs_elementwise(
102 &<$vector as core::ops::$trait<$scalar>>::$fn,
104 &|x, y| x.iter().all(|x| $check_fn(*x, y)),
108 fn scalar_lhs<const LANES: usize>() {
109 test_helpers::test_binary_scalar_lhs_elementwise(
110 &<$scalar as core::ops::$trait<$vector>>::$fn,
112 &|x, y| y.iter().all(|y| $check_fn(x, *y)),
116 fn assign<const LANES: usize>() {
117 test_helpers::test_binary_elementwise(
118 &|mut a, b| { <$vector as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
120 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
124 fn assign_scalar_rhs<const LANES: usize>() {
125 test_helpers::test_binary_scalar_rhs_elementwise(
126 &|mut a, b| { <$vector as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
128 &|x, y| x.iter().all(|x| $check_fn(*x, y)),
134 { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
135 impl_binary_nonzero_rhs_op_test! { $vector, $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
140 macro_rules! impl_common_integer_tests {
141 { $vector:ident, $scalar:ident } => {
142 test_helpers::test_lanes! {
143 fn horizontal_sum<const LANES: usize>() {
144 test_helpers::test_1(&|x| {
145 test_helpers::prop_assert_biteq! (
146 $vector::<LANES>::from_array(x).horizontal_sum(),
147 x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
153 fn horizontal_product<const LANES: usize>() {
154 test_helpers::test_1(&|x| {
155 test_helpers::prop_assert_biteq! (
156 $vector::<LANES>::from_array(x).horizontal_product(),
157 x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
163 fn horizontal_and<const LANES: usize>() {
164 test_helpers::test_1(&|x| {
165 test_helpers::prop_assert_biteq! (
166 $vector::<LANES>::from_array(x).horizontal_and(),
167 x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
173 fn horizontal_or<const LANES: usize>() {
174 test_helpers::test_1(&|x| {
175 test_helpers::prop_assert_biteq! (
176 $vector::<LANES>::from_array(x).horizontal_or(),
177 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
183 fn horizontal_xor<const LANES: usize>() {
184 test_helpers::test_1(&|x| {
185 test_helpers::prop_assert_biteq! (
186 $vector::<LANES>::from_array(x).horizontal_xor(),
187 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
193 fn horizontal_max<const LANES: usize>() {
194 test_helpers::test_1(&|x| {
195 test_helpers::prop_assert_biteq! (
196 $vector::<LANES>::from_array(x).horizontal_max(),
197 x.iter().copied().max().unwrap(),
203 fn horizontal_min<const LANES: usize>() {
204 test_helpers::test_1(&|x| {
205 test_helpers::prop_assert_biteq! (
206 $vector::<LANES>::from_array(x).horizontal_min(),
207 x.iter().copied().min().unwrap(),
216 /// Implement tests for signed integers.
218 macro_rules! impl_signed_tests {
219 { $vector:ident, $scalar:tt } => {
221 type Vector<const LANES: usize> = core_simd::$vector<LANES>;
222 type Scalar = $scalar;
224 impl_common_integer_tests! { Vector, Scalar }
226 test_helpers::test_lanes! {
227 fn neg<const LANES: usize>() {
228 test_helpers::test_unary_elementwise(
229 &<Vector::<LANES> as core::ops::Neg>::neg,
230 &<Scalar as core::ops::Neg>::neg,
231 &|x| !x.contains(&Scalar::MIN),
235 fn is_positive<const LANES: usize>() {
236 test_helpers::test_unary_mask_elementwise(
237 &Vector::<LANES>::is_positive,
238 &Scalar::is_positive,
243 fn is_negative<const LANES: usize>() {
244 test_helpers::test_unary_mask_elementwise(
245 &Vector::<LANES>::is_negative,
246 &Scalar::is_negative,
252 test_helpers::test_lanes_panic! {
253 fn div_min_overflow_panics<const LANES: usize>() {
254 let a = Vector::<LANES>::splat(Scalar::MIN);
255 let b = Vector::<LANES>::splat(-1);
259 fn div_by_all_zeros_panics<const LANES: usize>() {
260 let a = Vector::<LANES>::splat(42);
261 let b = Vector::<LANES>::splat(0);
265 fn div_by_one_zero_panics<const LANES: usize>() {
266 let a = Vector::<LANES>::splat(42);
267 let mut b = Vector::<LANES>::splat(21);
272 fn rem_min_overflow_panic<const LANES: usize>() {
273 let a = Vector::<LANES>::splat(Scalar::MIN);
274 let b = Vector::<LANES>::splat(-1);
278 fn rem_zero_panic<const LANES: usize>() {
279 let a = Vector::<LANES>::splat(42);
280 let b = Vector::<LANES>::splat(0);
285 test_helpers::test_lanes! {
286 fn div_neg_one_no_panic<const LANES: usize>() {
287 let a = Vector::<LANES>::splat(42);
288 let b = Vector::<LANES>::splat(-1);
292 fn rem_neg_one_no_panic<const LANES: usize>() {
293 let a = Vector::<LANES>::splat(42);
294 let b = Vector::<LANES>::splat(-1);
299 impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
300 impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
301 impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
303 // Exclude Div and Rem panicking cases
304 impl_binary_checked_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
305 impl_binary_checked_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
307 impl_unary_op_test!(Vector<LANES>, Scalar, Not::not);
308 impl_binary_op_test!(Vector<LANES>, Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
309 impl_binary_op_test!(Vector<LANES>, Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
310 impl_binary_op_test!(Vector<LANES>, Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
315 /// Implement tests for unsigned integers.
317 macro_rules! impl_unsigned_tests {
318 { $vector:ident, $scalar:tt } => {
320 type Vector<const LANES: usize> = core_simd::$vector<LANES>;
321 type Scalar = $scalar;
323 impl_common_integer_tests! { Vector, Scalar }
325 test_helpers::test_lanes_panic! {
326 fn rem_zero_panic<const LANES: usize>() {
327 let a = Vector::<LANES>::splat(42);
328 let b = Vector::<LANES>::splat(0);
333 impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
334 impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
335 impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
337 // Exclude Div and Rem panicking cases
338 impl_binary_checked_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
339 impl_binary_checked_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
341 impl_unary_op_test!(Vector<LANES>, Scalar, Not::not);
342 impl_binary_op_test!(Vector<LANES>, Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
343 impl_binary_op_test!(Vector<LANES>, Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
344 impl_binary_op_test!(Vector<LANES>, Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
349 /// Implement tests for floating point numbers.
351 macro_rules! impl_float_tests {
352 { $vector:ident, $scalar:tt, $int_scalar:tt } => {
354 type Vector<const LANES: usize> = core_simd::$vector<LANES>;
355 type Scalar = $scalar;
356 type IntScalar = $int_scalar;
358 impl_unary_op_test!(Vector<LANES>, Scalar, Neg::neg);
359 impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign);
360 impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign);
361 impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign);
362 impl_binary_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign);
363 impl_binary_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign);
365 #[cfg(feature = "std")]
366 test_helpers::test_lanes! {
367 fn ceil<const LANES: usize>() {
368 test_helpers::test_unary_elementwise(
369 &Vector::<LANES>::ceil,
375 fn floor<const LANES: usize>() {
376 test_helpers::test_unary_elementwise(
377 &Vector::<LANES>::floor,
384 test_helpers::test_lanes! {
385 fn is_sign_positive<const LANES: usize>() {
386 test_helpers::test_unary_mask_elementwise(
387 &Vector::<LANES>::is_sign_positive,
388 &Scalar::is_sign_positive,
393 fn is_sign_negative<const LANES: usize>() {
394 test_helpers::test_unary_mask_elementwise(
395 &Vector::<LANES>::is_sign_negative,
396 &Scalar::is_sign_negative,
401 fn is_finite<const LANES: usize>() {
402 test_helpers::test_unary_mask_elementwise(
403 &Vector::<LANES>::is_finite,
409 fn is_infinite<const LANES: usize>() {
410 test_helpers::test_unary_mask_elementwise(
411 &Vector::<LANES>::is_infinite,
412 &Scalar::is_infinite,
417 fn is_nan<const LANES: usize>() {
418 test_helpers::test_unary_mask_elementwise(
419 &Vector::<LANES>::is_nan,
425 fn is_normal<const LANES: usize>() {
426 test_helpers::test_unary_mask_elementwise(
427 &Vector::<LANES>::is_normal,
433 fn is_subnormal<const LANES: usize>() {
434 test_helpers::test_unary_mask_elementwise(
435 &Vector::<LANES>::is_subnormal,
436 &Scalar::is_subnormal,
441 fn abs<const LANES: usize>() {
442 test_helpers::test_unary_elementwise(
443 &Vector::<LANES>::abs,
449 fn round_from_int<const LANES: usize>() {
450 test_helpers::test_unary_elementwise(
451 &Vector::<LANES>::round_from_int,
457 fn to_int_unchecked<const LANES: usize>() {
458 // The maximum integer that can be represented by the equivalently sized float has
459 // all of the mantissa digits set to 1, pushed up to the MSB.
460 const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
461 const MAX_REPRESENTABLE_VALUE: Scalar =
462 (ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
464 let mut runner = proptest::test_runner::TestRunner::default();
466 &test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
468 let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
470 let mut result = [0; LANES];
471 for (i, o) in x.iter().zip(result.iter_mut()) {
472 *o = unsafe { i.to_int_unchecked() };
476 test_helpers::prop_assert_biteq!(result_1, result_2);
482 fn horizontal_sum<const LANES: usize>() {
483 test_helpers::test_1(&|x| {
484 test_helpers::prop_assert_biteq! (
485 Vector::<LANES>::from_array(x).horizontal_sum(),
492 fn horizontal_product<const LANES: usize>() {
493 test_helpers::test_1(&|x| {
494 test_helpers::prop_assert_biteq! (
495 Vector::<LANES>::from_array(x).horizontal_product(),
502 fn horizontal_max<const LANES: usize>() {
503 test_helpers::test_1(&|x| {
504 let vmax = Vector::<LANES>::from_array(x).horizontal_max();
505 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
506 // 0 and -0 are treated the same
507 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
508 test_helpers::prop_assert_biteq!(vmax, smax);
514 fn horizontal_min<const LANES: usize>() {
515 test_helpers::test_1(&|x| {
516 let vmax = Vector::<LANES>::from_array(x).horizontal_min();
517 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
518 // 0 and -0 are treated the same
519 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
520 test_helpers::prop_assert_biteq!(vmax, smax);