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 reduce_sum<const LANES: usize>() {
98 test_helpers::test_1(&|x| {
99 test_helpers::prop_assert_biteq! (
100 $vector::<LANES>::from_array(x).reduce_sum(),
101 x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
107 fn reduce_product<const LANES: usize>() {
108 test_helpers::test_1(&|x| {
109 test_helpers::prop_assert_biteq! (
110 $vector::<LANES>::from_array(x).reduce_product(),
111 x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
117 fn reduce_and<const LANES: usize>() {
118 test_helpers::test_1(&|x| {
119 test_helpers::prop_assert_biteq! (
120 $vector::<LANES>::from_array(x).reduce_and(),
121 x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
127 fn reduce_or<const LANES: usize>() {
128 test_helpers::test_1(&|x| {
129 test_helpers::prop_assert_biteq! (
130 $vector::<LANES>::from_array(x).reduce_or(),
131 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
137 fn reduce_xor<const LANES: usize>() {
138 test_helpers::test_1(&|x| {
139 test_helpers::prop_assert_biteq! (
140 $vector::<LANES>::from_array(x).reduce_xor(),
141 x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
147 fn reduce_max<const LANES: usize>() {
148 test_helpers::test_1(&|x| {
149 test_helpers::prop_assert_biteq! (
150 $vector::<LANES>::from_array(x).reduce_max(),
151 x.iter().copied().max().unwrap(),
157 fn reduce_min<const LANES: usize>() {
158 test_helpers::test_1(&|x| {
159 test_helpers::prop_assert_biteq! (
160 $vector::<LANES>::from_array(x).reduce_min(),
161 x.iter().copied().min().unwrap(),
170 /// Implement tests for signed integers.
172 macro_rules! impl_signed_tests {
175 use core_simd::simd::SimdInt;
176 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
177 type Scalar = $scalar;
179 impl_common_integer_tests! { Vector, Scalar }
181 test_helpers::test_lanes! {
182 fn neg<const LANES: usize>() {
183 test_helpers::test_unary_elementwise(
184 &<Vector::<LANES> as core::ops::Neg>::neg,
185 &<Scalar as core::ops::Neg>::neg,
186 &|x| !x.contains(&Scalar::MIN),
190 fn is_positive<const LANES: usize>() {
191 test_helpers::test_unary_mask_elementwise(
192 &Vector::<LANES>::is_positive,
193 &Scalar::is_positive,
198 fn is_negative<const LANES: usize>() {
199 test_helpers::test_unary_mask_elementwise(
200 &Vector::<LANES>::is_negative,
201 &Scalar::is_negative,
206 fn signum<const LANES: usize>() {
207 test_helpers::test_unary_elementwise(
208 &Vector::<LANES>::signum,
214 fn div_min_may_overflow<const LANES: usize>() {
215 let a = Vector::<LANES>::splat(Scalar::MIN);
216 let b = Vector::<LANES>::splat(-1);
217 assert_eq!(a / b, a);
220 fn rem_min_may_overflow<const LANES: usize>() {
221 let a = Vector::<LANES>::splat(Scalar::MIN);
222 let b = Vector::<LANES>::splat(-1);
223 assert_eq!(a % b, Vector::<LANES>::splat(0));
226 fn simd_min<const LANES: usize>() {
227 use core_simd::simd::SimdOrd;
228 let a = Vector::<LANES>::splat(Scalar::MIN);
229 let b = Vector::<LANES>::splat(0);
230 assert_eq!(a.simd_min(b), a);
231 let a = Vector::<LANES>::splat(Scalar::MAX);
232 let b = Vector::<LANES>::splat(0);
233 assert_eq!(a.simd_min(b), b);
236 fn simd_max<const LANES: usize>() {
237 use core_simd::simd::SimdOrd;
238 let a = Vector::<LANES>::splat(Scalar::MIN);
239 let b = Vector::<LANES>::splat(0);
240 assert_eq!(a.simd_max(b), b);
241 let a = Vector::<LANES>::splat(Scalar::MAX);
242 let b = Vector::<LANES>::splat(0);
243 assert_eq!(a.simd_max(b), a);
246 fn simd_clamp<const LANES: usize>() {
247 use core_simd::simd::SimdOrd;
248 let min = Vector::<LANES>::splat(Scalar::MIN);
249 let max = Vector::<LANES>::splat(Scalar::MAX);
250 let zero = Vector::<LANES>::splat(0);
251 let one = Vector::<LANES>::splat(1);
252 let negone = Vector::<LANES>::splat(-1);
253 assert_eq!(zero.simd_clamp(min, max), zero);
254 assert_eq!(zero.simd_clamp(min, one), zero);
255 assert_eq!(zero.simd_clamp(one, max), one);
256 assert_eq!(zero.simd_clamp(min, negone), negone);
260 test_helpers::test_lanes_panic! {
261 fn div_by_all_zeros_panics<const LANES: usize>() {
262 let a = Vector::<LANES>::splat(42);
263 let b = Vector::<LANES>::splat(0);
267 fn div_by_one_zero_panics<const LANES: usize>() {
268 let a = Vector::<LANES>::splat(42);
269 let mut b = Vector::<LANES>::splat(21);
274 fn rem_zero_panic<const LANES: usize>() {
275 let a = Vector::<LANES>::splat(42);
276 let b = Vector::<LANES>::splat(0);
281 test_helpers::test_lanes! {
282 fn div_neg_one_no_panic<const LANES: usize>() {
283 let a = Vector::<LANES>::splat(42);
284 let b = Vector::<LANES>::splat(-1);
288 fn rem_neg_one_no_panic<const LANES: usize>() {
289 let a = Vector::<LANES>::splat(42);
290 let b = Vector::<LANES>::splat(-1);
295 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
296 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
297 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
299 // Exclude Div and Rem panicking cases
300 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
301 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
303 impl_unary_op_test!(Scalar, Not::not);
304 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
305 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
306 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
311 /// Implement tests for unsigned integers.
313 macro_rules! impl_unsigned_tests {
316 use core_simd::simd::SimdUint;
317 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
318 type Scalar = $scalar;
320 impl_common_integer_tests! { Vector, Scalar }
322 test_helpers::test_lanes_panic! {
323 fn rem_zero_panic<const LANES: usize>() {
324 let a = Vector::<LANES>::splat(42);
325 let b = Vector::<LANES>::splat(0);
330 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
331 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
332 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
334 // Exclude Div and Rem panicking cases
335 impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
336 impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
338 impl_unary_op_test!(Scalar, Not::not);
339 impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
340 impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
341 impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
346 /// Implement tests for floating point numbers.
348 macro_rules! impl_float_tests {
349 { $scalar:tt, $int_scalar:tt } => {
351 use core_simd::SimdFloat;
352 type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
353 type Scalar = $scalar;
355 impl_unary_op_test!(Scalar, Neg::neg);
356 impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign);
357 impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign);
358 impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign);
359 impl_binary_op_test!(Scalar, Div::div, DivAssign::div_assign);
360 impl_binary_op_test!(Scalar, Rem::rem, RemAssign::rem_assign);
362 test_helpers::test_lanes! {
363 fn is_sign_positive<const LANES: usize>() {
364 test_helpers::test_unary_mask_elementwise(
365 &Vector::<LANES>::is_sign_positive,
366 &Scalar::is_sign_positive,
371 fn is_sign_negative<const LANES: usize>() {
372 test_helpers::test_unary_mask_elementwise(
373 &Vector::<LANES>::is_sign_negative,
374 &Scalar::is_sign_negative,
379 fn is_finite<const LANES: usize>() {
380 test_helpers::test_unary_mask_elementwise(
381 &Vector::<LANES>::is_finite,
387 fn is_infinite<const LANES: usize>() {
388 test_helpers::test_unary_mask_elementwise(
389 &Vector::<LANES>::is_infinite,
390 &Scalar::is_infinite,
395 fn is_nan<const LANES: usize>() {
396 test_helpers::test_unary_mask_elementwise(
397 &Vector::<LANES>::is_nan,
403 fn is_normal<const LANES: usize>() {
404 test_helpers::test_unary_mask_elementwise(
405 &Vector::<LANES>::is_normal,
411 fn is_subnormal<const LANES: usize>() {
412 test_helpers::test_unary_mask_elementwise(
413 &Vector::<LANES>::is_subnormal,
414 &Scalar::is_subnormal,
419 fn abs<const LANES: usize>() {
420 test_helpers::test_unary_elementwise(
421 &Vector::<LANES>::abs,
427 fn recip<const LANES: usize>() {
428 test_helpers::test_unary_elementwise(
429 &Vector::<LANES>::recip,
435 fn to_degrees<const LANES: usize>() {
436 test_helpers::test_unary_elementwise(
437 &Vector::<LANES>::to_degrees,
443 fn to_radians<const LANES: usize>() {
444 test_helpers::test_unary_elementwise(
445 &Vector::<LANES>::to_radians,
451 fn signum<const LANES: usize>() {
452 test_helpers::test_unary_elementwise(
453 &Vector::<LANES>::signum,
459 fn copysign<const LANES: usize>() {
460 test_helpers::test_binary_elementwise(
461 &Vector::<LANES>::copysign,
467 fn simd_min<const LANES: usize>() {
468 // Regular conditions (both values aren't zero)
469 test_helpers::test_binary_elementwise(
470 &Vector::<LANES>::simd_min,
472 // Reject the case where both values are zero with different signs
474 for (a, b) in a.iter().zip(b.iter()) {
475 if *a == 0. && *b == 0. && a.signum() != b.signum() {
483 // Special case where both values are zero
484 let p_zero = Vector::<LANES>::splat(0.);
485 let n_zero = Vector::<LANES>::splat(-0.);
486 assert!(p_zero.simd_min(n_zero).to_array().iter().all(|x| *x == 0.));
487 assert!(n_zero.simd_min(p_zero).to_array().iter().all(|x| *x == 0.));
490 fn simd_max<const LANES: usize>() {
491 // Regular conditions (both values aren't zero)
492 test_helpers::test_binary_elementwise(
493 &Vector::<LANES>::simd_max,
495 // Reject the case where both values are zero with different signs
497 for (a, b) in a.iter().zip(b.iter()) {
498 if *a == 0. && *b == 0. && a.signum() != b.signum() {
506 // Special case where both values are zero
507 let p_zero = Vector::<LANES>::splat(0.);
508 let n_zero = Vector::<LANES>::splat(-0.);
509 assert!(p_zero.simd_max(n_zero).to_array().iter().all(|x| *x == 0.));
510 assert!(n_zero.simd_max(p_zero).to_array().iter().all(|x| *x == 0.));
513 fn simd_clamp<const LANES: usize>() {
514 test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
515 for (min, max) in min.iter_mut().zip(max.iter_mut()) {
517 core::mem::swap(min, max);
520 *min = Scalar::NEG_INFINITY;
523 *max = Scalar::INFINITY;
527 let mut result_scalar = [Scalar::default(); LANES];
529 result_scalar[i] = value[i].clamp(min[i], max[i]);
531 let result_vector = Vector::from_array(value).simd_clamp(min.into(), max.into()).to_array();
532 test_helpers::prop_assert_biteq!(result_scalar, result_vector);
537 fn reduce_sum<const LANES: usize>() {
538 test_helpers::test_1(&|x| {
539 test_helpers::prop_assert_biteq! (
540 Vector::<LANES>::from_array(x).reduce_sum(),
547 fn reduce_product<const LANES: usize>() {
548 test_helpers::test_1(&|x| {
549 test_helpers::prop_assert_biteq! (
550 Vector::<LANES>::from_array(x).reduce_product(),
557 fn reduce_max<const LANES: usize>() {
558 test_helpers::test_1(&|x| {
559 let vmax = Vector::<LANES>::from_array(x).reduce_max();
560 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
561 // 0 and -0 are treated the same
562 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
563 test_helpers::prop_assert_biteq!(vmax, smax);
569 fn reduce_min<const LANES: usize>() {
570 test_helpers::test_1(&|x| {
571 let vmax = Vector::<LANES>::from_array(x).reduce_min();
572 let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
573 // 0 and -0 are treated the same
574 if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
575 test_helpers::prop_assert_biteq!(vmax, smax);
582 #[cfg(feature = "std")]
584 use std_float::StdFloat;
587 test_helpers::test_lanes! {
588 fn sqrt<const LANES: usize>() {
589 test_helpers::test_unary_elementwise(
590 &Vector::<LANES>::sqrt,
596 fn mul_add<const LANES: usize>() {
597 test_helpers::test_ternary_elementwise(
598 &Vector::<LANES>::mul_add,