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 { $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 &<core_simd::Simd<$scalar, LANES> as core::ops::$trait>::$fn,
17 { $scalar:ty, $trait:ident :: $fn:ident } => {
18 impl_unary_op_test! { $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 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
32 test_helpers::test_lanes! {
33 fn normal<const LANES: usize>() {
34 test_helpers::test_binary_elementwise(
35 &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
41 fn scalar_rhs<const LANES: usize>() {
42 test_helpers::test_binary_scalar_rhs_elementwise(
43 &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
49 fn scalar_lhs<const LANES: usize>() {
50 test_helpers::test_binary_scalar_lhs_elementwise(
51 &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
57 fn assign<const LANES: usize>() {
58 test_helpers::test_binary_elementwise(
59 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
65 fn assign_scalar_rhs<const LANES: usize>() {
66 test_helpers::test_binary_scalar_rhs_elementwise(
67 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
75 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
76 impl_binary_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
80 /// Implements a test on a binary operation using proptest.
82 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
83 /// (like the `proptest_assume` macro).
85 /// Compares the vector operation to the equivalent scalar operation.
87 macro_rules! impl_binary_checked_op_test {
88 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
93 test_helpers::test_lanes! {
94 fn normal<const LANES: usize>() {
95 test_helpers::test_binary_elementwise(
96 &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
98 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
102 fn scalar_rhs<const LANES: usize>() {
103 test_helpers::test_binary_scalar_rhs_elementwise(
104 &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
106 &|x, y| x.iter().all(|x| $check_fn(*x, y)),
110 fn scalar_lhs<const LANES: usize>() {
111 test_helpers::test_binary_scalar_lhs_elementwise(
112 &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
114 &|x, y| y.iter().all(|y| $check_fn(x, *y)),
118 fn assign<const LANES: usize>() {
119 test_helpers::test_binary_elementwise(
120 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
122 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
126 fn assign_scalar_rhs<const LANES: usize>() {
127 test_helpers::test_binary_scalar_rhs_elementwise(
128 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
130 &|x, y| x.iter().all(|x| $check_fn(*x, y)),
136 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
137 impl_binary_checked_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
142 macro_rules! impl_common_integer_tests {
143 { $vector:ident, $scalar:ident } => {
144 test_helpers::test_lanes! {
145 fn horizontal_sum<const LANES: usize>() {
146 test_helpers::test_1(&|x| {
147 test_helpers::prop_assert_biteq! (
148 $vector::<LANES>::from_array(x).horizontal_sum(),
149 x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
155 fn horizontal_product<const LANES: usize>() {
156 test_helpers::test_1(&|x| {
157 test_helpers::prop_assert_biteq! (
158 $vector::<LANES>::from_array(x).horizontal_product(),
159 x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
165 fn horizontal_and<const LANES: usize>() {
166 test_helpers::test_1(&|x| {
167 test_helpers::prop_assert_biteq! (
168 $vector::<LANES>::from_array(x).horizontal_and(),
169 x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
175 fn horizontal_or<const LANES: usize>() {
176 test_helpers::test_1(&|x| {
177 test_helpers::prop_assert_biteq! (
178 $vector::<LANES>::from_array(x).horizontal_or(),
179 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
185 fn horizontal_xor<const LANES: usize>() {
186 test_helpers::test_1(&|x| {
187 test_helpers::prop_assert_biteq! (
188 $vector::<LANES>::from_array(x).horizontal_xor(),
189 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
195 fn horizontal_max<const LANES: usize>() {
196 test_helpers::test_1(&|x| {
197 test_helpers::prop_assert_biteq! (
198 $vector::<LANES>::from_array(x).horizontal_max(),
199 x.iter().copied().max().unwrap(),
205 fn horizontal_min<const LANES: usize>() {
206 test_helpers::test_1(&|x| {
207 test_helpers::prop_assert_biteq! (
208 $vector::<LANES>::from_array(x).horizontal_min(),
209 x.iter().copied().min().unwrap(),
218 /// Implement tests for signed integers.
220 macro_rules! impl_signed_tests {
223 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
224 type Scalar = $scalar;
226 impl_common_integer_tests! { Vector, Scalar }
228 test_helpers::test_lanes! {
229 fn neg<const LANES: usize>() {
230 test_helpers::test_unary_elementwise(
231 &<Vector::<LANES> as core::ops::Neg>::neg,
232 &<Scalar as core::ops::Neg>::neg,
233 &|x| !x.contains(&Scalar::MIN),
237 fn is_positive<const LANES: usize>() {
238 test_helpers::test_unary_mask_elementwise(
239 &Vector::<LANES>::is_positive,
240 &Scalar::is_positive,
245 fn is_negative<const LANES: usize>() {
246 test_helpers::test_unary_mask_elementwise(
247 &Vector::<LANES>::is_negative,
248 &Scalar::is_negative,
253 fn signum<const LANES: usize>() {
254 test_helpers::test_unary_elementwise(
255 &Vector::<LANES>::signum,
263 test_helpers::test_lanes_panic! {
264 fn div_min_overflow_panics<const LANES: usize>() {
265 let a = Vector::<LANES>::splat(Scalar::MIN);
266 let b = Vector::<LANES>::splat(-1);
270 fn div_by_all_zeros_panics<const LANES: usize>() {
271 let a = Vector::<LANES>::splat(42);
272 let b = Vector::<LANES>::splat(0);
276 fn div_by_one_zero_panics<const LANES: usize>() {
277 let a = Vector::<LANES>::splat(42);
278 let mut b = Vector::<LANES>::splat(21);
283 fn rem_min_overflow_panic<const LANES: usize>() {
284 let a = Vector::<LANES>::splat(Scalar::MIN);
285 let b = Vector::<LANES>::splat(-1);
289 fn rem_zero_panic<const LANES: usize>() {
290 let a = Vector::<LANES>::splat(42);
291 let b = Vector::<LANES>::splat(0);
296 test_helpers::test_lanes! {
297 fn div_neg_one_no_panic<const LANES: usize>() {
298 let a = Vector::<LANES>::splat(42);
299 let b = Vector::<LANES>::splat(-1);
303 fn rem_neg_one_no_panic<const LANES: usize>() {
304 let a = Vector::<LANES>::splat(42);
305 let b = Vector::<LANES>::splat(-1);
310 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
311 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
312 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
314 // Exclude Div and Rem panicking cases
315 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
316 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
318 impl_unary_op_test!(Scalar, Not::not);
319 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
320 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
321 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
326 /// Implement tests for unsigned integers.
328 macro_rules! impl_unsigned_tests {
331 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
332 type Scalar = $scalar;
334 impl_common_integer_tests! { Vector, Scalar }
336 test_helpers::test_lanes_panic! {
337 fn rem_zero_panic<const LANES: usize>() {
338 let a = Vector::<LANES>::splat(42);
339 let b = Vector::<LANES>::splat(0);
344 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
345 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
346 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
348 // Exclude Div and Rem panicking cases
349 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
350 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
352 impl_unary_op_test!(Scalar, Not::not);
353 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
354 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
355 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
360 /// Implement tests for floating point numbers.
362 macro_rules! impl_float_tests {
363 { $scalar:tt, $int_scalar:tt } => {
365 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
366 type Scalar = $scalar;
368 impl_unary_op_test!(Scalar, Neg::neg);
369 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign);
370 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign);
371 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign);
372 impl_binary_op_test!(Scalar, Div::div, DivAssign::div_assign);
373 impl_binary_op_test!(Scalar, Rem::rem, RemAssign::rem_assign);
375 test_helpers::test_lanes! {
376 fn is_sign_positive<const LANES: usize>() {
377 test_helpers::test_unary_mask_elementwise(
378 &Vector::<LANES>::is_sign_positive,
379 &Scalar::is_sign_positive,
384 fn is_sign_negative<const LANES: usize>() {
385 test_helpers::test_unary_mask_elementwise(
386 &Vector::<LANES>::is_sign_negative,
387 &Scalar::is_sign_negative,
392 fn is_finite<const LANES: usize>() {
393 test_helpers::test_unary_mask_elementwise(
394 &Vector::<LANES>::is_finite,
400 fn is_infinite<const LANES: usize>() {
401 test_helpers::test_unary_mask_elementwise(
402 &Vector::<LANES>::is_infinite,
403 &Scalar::is_infinite,
408 fn is_nan<const LANES: usize>() {
409 test_helpers::test_unary_mask_elementwise(
410 &Vector::<LANES>::is_nan,
416 fn is_normal<const LANES: usize>() {
417 test_helpers::test_unary_mask_elementwise(
418 &Vector::<LANES>::is_normal,
424 fn is_subnormal<const LANES: usize>() {
425 test_helpers::test_unary_mask_elementwise(
426 &Vector::<LANES>::is_subnormal,
427 &Scalar::is_subnormal,
432 fn abs<const LANES: usize>() {
433 test_helpers::test_unary_elementwise(
434 &Vector::<LANES>::abs,
440 fn recip<const LANES: usize>() {
441 test_helpers::test_unary_elementwise(
442 &Vector::<LANES>::recip,
448 fn to_degrees<const LANES: usize>() {
449 test_helpers::test_unary_elementwise(
450 &Vector::<LANES>::to_degrees,
456 fn to_radians<const LANES: usize>() {
457 test_helpers::test_unary_elementwise(
458 &Vector::<LANES>::to_radians,
464 fn signum<const LANES: usize>() {
465 test_helpers::test_unary_elementwise(
466 &Vector::<LANES>::signum,
472 fn copysign<const LANES: usize>() {
473 test_helpers::test_binary_elementwise(
474 &Vector::<LANES>::copysign,
480 fn min<const LANES: usize>() {
481 // Regular conditions (both values aren't zero)
482 test_helpers::test_binary_elementwise(
483 &Vector::<LANES>::min,
485 // Reject the case where both values are zero with different signs
487 for (a, b) in a.iter().zip(b.iter()) {
488 if *a == 0. && *b == 0. && a.signum() != b.signum() {
496 // Special case where both values are zero
497 let p_zero = Vector::<LANES>::splat(0.);
498 let n_zero = Vector::<LANES>::splat(-0.);
499 assert!(p_zero.min(n_zero).to_array().iter().all(|x| *x == 0.));
500 assert!(n_zero.min(p_zero).to_array().iter().all(|x| *x == 0.));
503 fn max<const LANES: usize>() {
504 // Regular conditions (both values aren't zero)
505 test_helpers::test_binary_elementwise(
506 &Vector::<LANES>::max,
508 // Reject the case where both values are zero with different signs
510 for (a, b) in a.iter().zip(b.iter()) {
511 if *a == 0. && *b == 0. && a.signum() != b.signum() {
519 // Special case where both values are zero
520 let p_zero = Vector::<LANES>::splat(0.);
521 let n_zero = Vector::<LANES>::splat(-0.);
522 assert!(p_zero.max(n_zero).to_array().iter().all(|x| *x == 0.));
523 assert!(n_zero.max(p_zero).to_array().iter().all(|x| *x == 0.));
526 fn clamp<const LANES: usize>() {
527 test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
528 for (min, max) in min.iter_mut().zip(max.iter_mut()) {
530 core::mem::swap(min, max);
533 *min = Scalar::NEG_INFINITY;
536 *max = Scalar::INFINITY;
540 let mut result_scalar = [Scalar::default(); LANES];
542 result_scalar[i] = value[i].clamp(min[i], max[i]);
544 let result_vector = Vector::from_array(value).clamp(min.into(), max.into()).to_array();
545 test_helpers::prop_assert_biteq!(result_scalar, result_vector);
550 fn horizontal_sum<const LANES: usize>() {
551 test_helpers::test_1(&|x| {
552 test_helpers::prop_assert_biteq! (
553 Vector::<LANES>::from_array(x).horizontal_sum(),
560 fn horizontal_product<const LANES: usize>() {
561 test_helpers::test_1(&|x| {
562 test_helpers::prop_assert_biteq! (
563 Vector::<LANES>::from_array(x).horizontal_product(),
570 fn horizontal_max<const LANES: usize>() {
571 test_helpers::test_1(&|x| {
572 let vmax = Vector::<LANES>::from_array(x).horizontal_max();
573 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
574 // 0 and -0 are treated the same
575 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
576 test_helpers::prop_assert_biteq!(vmax, smax);
582 fn horizontal_min<const LANES: usize>() {
583 test_helpers::test_1(&|x| {
584 let vmax = Vector::<LANES>::from_array(x).horizontal_min();
585 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
586 // 0 and -0 are treated the same
587 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
588 test_helpers::prop_assert_biteq!(vmax, smax);
595 #[cfg(feature = "std")]
598 test_helpers::test_lanes! {
599 fn sqrt<const LANES: usize>() {
600 test_helpers::test_unary_elementwise(
601 &Vector::<LANES>::sqrt,
607 fn mul_add<const LANES: usize>() {
608 test_helpers::test_ternary_elementwise(
609 &Vector::<LANES>::mul_add,