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 assign<const LANES: usize>() {
42 test_helpers::test_binary_elementwise(
43 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
51 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
52 impl_binary_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
56 /// Implements a test on a binary operation using proptest.
58 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
59 /// (like the `proptest_assume` macro).
61 /// Compares the vector operation to the equivalent scalar operation.
63 macro_rules! impl_binary_checked_op_test {
64 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
69 test_helpers::test_lanes! {
70 fn normal<const LANES: usize>() {
71 test_helpers::test_binary_elementwise(
72 &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
74 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
78 fn assign<const LANES: usize>() {
79 test_helpers::test_binary_elementwise(
80 &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
82 &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
88 { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
89 impl_binary_checked_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
94 macro_rules! impl_common_integer_tests {
95 { $vector:ident, $scalar:ident } => {
96 test_helpers::test_lanes! {
97 fn horizontal_sum<const LANES: usize>() {
98 test_helpers::test_1(&|x| {
99 test_helpers::prop_assert_biteq! (
100 $vector::<LANES>::from_array(x).horizontal_sum(),
101 x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
107 fn horizontal_product<const LANES: usize>() {
108 test_helpers::test_1(&|x| {
109 test_helpers::prop_assert_biteq! (
110 $vector::<LANES>::from_array(x).horizontal_product(),
111 x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
117 fn horizontal_and<const LANES: usize>() {
118 test_helpers::test_1(&|x| {
119 test_helpers::prop_assert_biteq! (
120 $vector::<LANES>::from_array(x).horizontal_and(),
121 x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
127 fn horizontal_or<const LANES: usize>() {
128 test_helpers::test_1(&|x| {
129 test_helpers::prop_assert_biteq! (
130 $vector::<LANES>::from_array(x).horizontal_or(),
131 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
137 fn horizontal_xor<const LANES: usize>() {
138 test_helpers::test_1(&|x| {
139 test_helpers::prop_assert_biteq! (
140 $vector::<LANES>::from_array(x).horizontal_xor(),
141 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
147 fn horizontal_max<const LANES: usize>() {
148 test_helpers::test_1(&|x| {
149 test_helpers::prop_assert_biteq! (
150 $vector::<LANES>::from_array(x).horizontal_max(),
151 x.iter().copied().max().unwrap(),
157 fn horizontal_min<const LANES: usize>() {
158 test_helpers::test_1(&|x| {
159 test_helpers::prop_assert_biteq! (
160 $vector::<LANES>::from_array(x).horizontal_min(),
161 x.iter().copied().min().unwrap(),
170 /// Implement tests for signed integers.
172 macro_rules! impl_signed_tests {
175 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
176 type Scalar = $scalar;
178 impl_common_integer_tests! { Vector, Scalar }
180 test_helpers::test_lanes! {
181 fn neg<const LANES: usize>() {
182 test_helpers::test_unary_elementwise(
183 &<Vector::<LANES> as core::ops::Neg>::neg,
184 &<Scalar as core::ops::Neg>::neg,
185 &|x| !x.contains(&Scalar::MIN),
189 fn is_positive<const LANES: usize>() {
190 test_helpers::test_unary_mask_elementwise(
191 &Vector::<LANES>::is_positive,
192 &Scalar::is_positive,
197 fn is_negative<const LANES: usize>() {
198 test_helpers::test_unary_mask_elementwise(
199 &Vector::<LANES>::is_negative,
200 &Scalar::is_negative,
205 fn signum<const LANES: usize>() {
206 test_helpers::test_unary_elementwise(
207 &Vector::<LANES>::signum,
215 test_helpers::test_lanes_panic! {
216 fn div_min_overflow_panics<const LANES: usize>() {
217 let a = Vector::<LANES>::splat(Scalar::MIN);
218 let b = Vector::<LANES>::splat(-1);
222 fn div_by_all_zeros_panics<const LANES: usize>() {
223 let a = Vector::<LANES>::splat(42);
224 let b = Vector::<LANES>::splat(0);
228 fn div_by_one_zero_panics<const LANES: usize>() {
229 let a = Vector::<LANES>::splat(42);
230 let mut b = Vector::<LANES>::splat(21);
235 fn rem_min_overflow_panic<const LANES: usize>() {
236 let a = Vector::<LANES>::splat(Scalar::MIN);
237 let b = Vector::<LANES>::splat(-1);
241 fn rem_zero_panic<const LANES: usize>() {
242 let a = Vector::<LANES>::splat(42);
243 let b = Vector::<LANES>::splat(0);
248 test_helpers::test_lanes! {
249 fn div_neg_one_no_panic<const LANES: usize>() {
250 let a = Vector::<LANES>::splat(42);
251 let b = Vector::<LANES>::splat(-1);
255 fn rem_neg_one_no_panic<const LANES: usize>() {
256 let a = Vector::<LANES>::splat(42);
257 let b = Vector::<LANES>::splat(-1);
262 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
263 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
264 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
266 // Exclude Div and Rem panicking cases
267 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
268 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
270 impl_unary_op_test!(Scalar, Not::not);
271 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
272 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
273 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
278 /// Implement tests for unsigned integers.
280 macro_rules! impl_unsigned_tests {
283 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
284 type Scalar = $scalar;
286 impl_common_integer_tests! { Vector, Scalar }
288 test_helpers::test_lanes_panic! {
289 fn rem_zero_panic<const LANES: usize>() {
290 let a = Vector::<LANES>::splat(42);
291 let b = Vector::<LANES>::splat(0);
296 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
297 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
298 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
300 // Exclude Div and Rem panicking cases
301 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
302 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
304 impl_unary_op_test!(Scalar, Not::not);
305 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
306 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
307 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
312 /// Implement tests for floating point numbers.
314 macro_rules! impl_float_tests {
315 { $scalar:tt, $int_scalar:tt } => {
317 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
318 type Scalar = $scalar;
320 impl_unary_op_test!(Scalar, Neg::neg);
321 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign);
322 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign);
323 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign);
324 impl_binary_op_test!(Scalar, Div::div, DivAssign::div_assign);
325 impl_binary_op_test!(Scalar, Rem::rem, RemAssign::rem_assign);
327 test_helpers::test_lanes! {
328 fn is_sign_positive<const LANES: usize>() {
329 test_helpers::test_unary_mask_elementwise(
330 &Vector::<LANES>::is_sign_positive,
331 &Scalar::is_sign_positive,
336 fn is_sign_negative<const LANES: usize>() {
337 test_helpers::test_unary_mask_elementwise(
338 &Vector::<LANES>::is_sign_negative,
339 &Scalar::is_sign_negative,
344 fn is_finite<const LANES: usize>() {
345 test_helpers::test_unary_mask_elementwise(
346 &Vector::<LANES>::is_finite,
352 fn is_infinite<const LANES: usize>() {
353 test_helpers::test_unary_mask_elementwise(
354 &Vector::<LANES>::is_infinite,
355 &Scalar::is_infinite,
360 fn is_nan<const LANES: usize>() {
361 test_helpers::test_unary_mask_elementwise(
362 &Vector::<LANES>::is_nan,
368 fn is_normal<const LANES: usize>() {
369 test_helpers::test_unary_mask_elementwise(
370 &Vector::<LANES>::is_normal,
376 fn is_subnormal<const LANES: usize>() {
377 test_helpers::test_unary_mask_elementwise(
378 &Vector::<LANES>::is_subnormal,
379 &Scalar::is_subnormal,
384 fn abs<const LANES: usize>() {
385 test_helpers::test_unary_elementwise(
386 &Vector::<LANES>::abs,
392 fn recip<const LANES: usize>() {
393 test_helpers::test_unary_elementwise(
394 &Vector::<LANES>::recip,
400 fn to_degrees<const LANES: usize>() {
401 test_helpers::test_unary_elementwise(
402 &Vector::<LANES>::to_degrees,
408 fn to_radians<const LANES: usize>() {
409 test_helpers::test_unary_elementwise(
410 &Vector::<LANES>::to_radians,
416 fn signum<const LANES: usize>() {
417 test_helpers::test_unary_elementwise(
418 &Vector::<LANES>::signum,
424 fn copysign<const LANES: usize>() {
425 test_helpers::test_binary_elementwise(
426 &Vector::<LANES>::copysign,
432 fn min<const LANES: usize>() {
433 // Regular conditions (both values aren't zero)
434 test_helpers::test_binary_elementwise(
435 &Vector::<LANES>::min,
437 // Reject the case where both values are zero with different signs
439 for (a, b) in a.iter().zip(b.iter()) {
440 if *a == 0. && *b == 0. && a.signum() != b.signum() {
448 // Special case where both values are zero
449 let p_zero = Vector::<LANES>::splat(0.);
450 let n_zero = Vector::<LANES>::splat(-0.);
451 assert!(p_zero.min(n_zero).to_array().iter().all(|x| *x == 0.));
452 assert!(n_zero.min(p_zero).to_array().iter().all(|x| *x == 0.));
455 fn max<const LANES: usize>() {
456 // Regular conditions (both values aren't zero)
457 test_helpers::test_binary_elementwise(
458 &Vector::<LANES>::max,
460 // Reject the case where both values are zero with different signs
462 for (a, b) in a.iter().zip(b.iter()) {
463 if *a == 0. && *b == 0. && a.signum() != b.signum() {
471 // Special case where both values are zero
472 let p_zero = Vector::<LANES>::splat(0.);
473 let n_zero = Vector::<LANES>::splat(-0.);
474 assert!(p_zero.max(n_zero).to_array().iter().all(|x| *x == 0.));
475 assert!(n_zero.max(p_zero).to_array().iter().all(|x| *x == 0.));
478 fn clamp<const LANES: usize>() {
479 test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
480 for (min, max) in min.iter_mut().zip(max.iter_mut()) {
482 core::mem::swap(min, max);
485 *min = Scalar::NEG_INFINITY;
488 *max = Scalar::INFINITY;
492 let mut result_scalar = [Scalar::default(); LANES];
494 result_scalar[i] = value[i].clamp(min[i], max[i]);
496 let result_vector = Vector::from_array(value).clamp(min.into(), max.into()).to_array();
497 test_helpers::prop_assert_biteq!(result_scalar, result_vector);
502 fn horizontal_sum<const LANES: usize>() {
503 test_helpers::test_1(&|x| {
504 test_helpers::prop_assert_biteq! (
505 Vector::<LANES>::from_array(x).horizontal_sum(),
512 fn horizontal_product<const LANES: usize>() {
513 test_helpers::test_1(&|x| {
514 test_helpers::prop_assert_biteq! (
515 Vector::<LANES>::from_array(x).horizontal_product(),
522 fn horizontal_max<const LANES: usize>() {
523 test_helpers::test_1(&|x| {
524 let vmax = Vector::<LANES>::from_array(x).horizontal_max();
525 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
526 // 0 and -0 are treated the same
527 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
528 test_helpers::prop_assert_biteq!(vmax, smax);
534 fn horizontal_min<const LANES: usize>() {
535 test_helpers::test_1(&|x| {
536 let vmax = Vector::<LANES>::from_array(x).horizontal_min();
537 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
538 // 0 and -0 are treated the same
539 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
540 test_helpers::prop_assert_biteq!(vmax, smax);
547 #[cfg(feature = "std")]
550 test_helpers::test_lanes! {
551 fn sqrt<const LANES: usize>() {
552 test_helpers::test_unary_elementwise(
553 &Vector::<LANES>::sqrt,
559 fn mul_add<const LANES: usize>() {
560 test_helpers::test_ternary_elementwise(
561 &Vector::<LANES>::mul_add,