]> git.lizzy.rs Git - rust.git/blob - crates/core_simd/tests/ops_macros.rs
37f3b49a33061e8bb0d992cfc6ec6e47fb7b28b2
[rust.git] / crates / core_simd / tests / ops_macros.rs
1 /// Implements a test on a unary operation using proptest.
2 ///
3 /// Compares the vector operation to the equivalent scalar operation.
4 #[macro_export]
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,
11                     &$scalar_fn,
12                     &|_| true,
13                 );
14             }
15         }
16     };
17     { $vector:ty, $scalar:ty, $trait:ident :: $fn:ident } => {
18         impl_unary_op_test! { $vector, $scalar, $trait::$fn, <$scalar as core::ops::$trait>::$fn }
19     };
20 }
21
22 /// Implements a test on a binary operation using proptest.
23 ///
24 /// Compares the vector operation to the equivalent scalar operation.
25 #[macro_export]
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 } => {
28         mod $fn {
29             use super::*;
30
31             test_helpers::test_lanes! {
32                 fn normal<const LANES: usize>() {
33                     test_helpers::test_binary_elementwise(
34                         &<$vector as core::ops::$trait>::$fn,
35                         &$scalar_fn,
36                         &|_, _| true,
37                     );
38                 }
39
40                 fn scalar_rhs<const LANES: usize>() {
41                     test_helpers::test_binary_scalar_rhs_elementwise(
42                         &<$vector as core::ops::$trait<$scalar>>::$fn,
43                         &$scalar_fn,
44                         &|_, _| true,
45                     );
46                 }
47
48                 fn scalar_lhs<const LANES: usize>() {
49                     test_helpers::test_binary_scalar_lhs_elementwise(
50                         &<$scalar as core::ops::$trait<$vector>>::$fn,
51                         &$scalar_fn,
52                         &|_, _| true,
53                     );
54                 }
55
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 },
59                         &$scalar_fn,
60                         &|_, _| true,
61                     );
62                 }
63
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 },
67                         &$scalar_fn,
68                         &|_, _| true,
69                     );
70                 }
71             }
72         }
73     };
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 }
76     };
77 }
78
79 /// Implements a test on a binary operation using proptest.
80 ///
81 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
82 /// (like the `proptest_assume` macro).
83 ///
84 /// Compares the vector operation to the equivalent scalar operation.
85 #[macro_export]
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 } => {
88         mod $fn {
89             use super::*;
90
91             test_helpers::test_lanes! {
92                 fn normal<const LANES: usize>() {
93                     test_helpers::test_binary_elementwise(
94                         &<$vector as core::ops::$trait>::$fn,
95                         &$scalar_fn,
96                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
97                     );
98                 }
99
100                 fn scalar_rhs<const LANES: usize>() {
101                     test_helpers::test_binary_scalar_rhs_elementwise(
102                         &<$vector as core::ops::$trait<$scalar>>::$fn,
103                         &$scalar_fn,
104                         &|x, y| x.iter().all(|x| $check_fn(*x, y)),
105                     );
106                 }
107
108                 fn scalar_lhs<const LANES: usize>() {
109                     test_helpers::test_binary_scalar_lhs_elementwise(
110                         &<$scalar as core::ops::$trait<$vector>>::$fn,
111                         &$scalar_fn,
112                         &|x, y| y.iter().all(|y| $check_fn(x, *y)),
113                     );
114                 }
115
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 },
119                         &$scalar_fn,
120                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
121                     )
122                 }
123
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 },
127                         &$scalar_fn,
128                         &|x, y| x.iter().all(|x| $check_fn(*x, y)),
129                     )
130                 }
131             }
132         }
133     };
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 }
136     };
137 }
138
139 #[macro_export]
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),
148                     );
149                     Ok(())
150                 });
151             }
152
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),
158                     );
159                     Ok(())
160                 });
161             }
162
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),
168                     );
169                     Ok(())
170                 });
171             }
172
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),
178                     );
179                     Ok(())
180                 });
181             }
182
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),
188                     );
189                     Ok(())
190                 });
191             }
192
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(),
198                     );
199                     Ok(())
200                 });
201             }
202
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(),
208                     );
209                     Ok(())
210                 });
211             }
212         }
213     }
214 }
215
216 /// Implement tests for signed integers.
217 #[macro_export]
218 macro_rules! impl_signed_tests {
219     { $vector:ident, $scalar:tt } => {
220         mod $scalar {
221             type Vector<const LANES: usize> = core_simd::$vector<LANES>;
222             type Scalar = $scalar;
223
224             impl_common_integer_tests! { Vector, Scalar }
225
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),
232                     );
233                 }
234
235                 fn is_positive<const LANES: usize>() {
236                     test_helpers::test_unary_mask_elementwise(
237                         &Vector::<LANES>::is_positive,
238                         &Scalar::is_positive,
239                         &|_| true,
240                     );
241                 }
242
243                 fn is_negative<const LANES: usize>() {
244                     test_helpers::test_unary_mask_elementwise(
245                         &Vector::<LANES>::is_negative,
246                         &Scalar::is_negative,
247                         &|_| true,
248                     );
249                 }
250             }
251
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);
256                     let _ = a / b;
257                 }
258
259                 fn div_by_all_zeros_panics<const LANES: usize>() {
260                     let a = Vector::<LANES>::splat(42);
261                     let b = Vector::<LANES>::splat(0);
262                     let _ = a / b;
263                 }
264
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);
268                     b[0] = 0 as _;
269                     let _ = a / b;
270                 }
271
272                 fn rem_min_overflow_panic<const LANES: usize>() {
273                     let a = Vector::<LANES>::splat(Scalar::MIN);
274                     let b = Vector::<LANES>::splat(-1);
275                     let _ = a % b;
276                 }
277
278                 fn rem_zero_panic<const LANES: usize>() {
279                     let a = Vector::<LANES>::splat(42);
280                     let b = Vector::<LANES>::splat(0);
281                     let _ = a % b;
282                 }
283             }
284
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);
289                     let _ = a / b;
290                 }
291
292                 fn rem_neg_one_no_panic<const LANES: usize>() {
293                     let a = Vector::<LANES>::splat(42);
294                     let b = Vector::<LANES>::splat(-1);
295                     let _ = a % b;
296                 }
297             }
298
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);
302
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));
306
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);
311         }
312     }
313 }
314
315 /// Implement tests for unsigned integers.
316 #[macro_export]
317 macro_rules! impl_unsigned_tests {
318     { $vector:ident, $scalar:tt } => {
319         mod $scalar {
320             type Vector<const LANES: usize> = core_simd::$vector<LANES>;
321             type Scalar = $scalar;
322
323             impl_common_integer_tests! { Vector, Scalar }
324
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);
329                     let _ = a % b;
330                 }
331             }
332
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);
336
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);
340
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);
345         }
346     }
347 }
348
349 /// Implement tests for floating point numbers.
350 #[macro_export]
351 macro_rules! impl_float_tests {
352     { $vector:ident, $scalar:tt, $int_scalar:tt } => {
353         mod $scalar {
354             type Vector<const LANES: usize> = core_simd::$vector<LANES>;
355             type Scalar = $scalar;
356             type IntScalar = $int_scalar;
357
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);
364
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,
370                         &Scalar::ceil,
371                         &|_| true,
372                     )
373                 }
374
375                 fn floor<const LANES: usize>() {
376                     test_helpers::test_unary_elementwise(
377                         &Vector::<LANES>::floor,
378                         &Scalar::floor,
379                         &|_| true,
380                     )
381                 }
382             }
383
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,
389                         &|_| true,
390                     );
391                 }
392
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,
397                         &|_| true,
398                     );
399                 }
400
401                 fn is_finite<const LANES: usize>() {
402                     test_helpers::test_unary_mask_elementwise(
403                         &Vector::<LANES>::is_finite,
404                         &Scalar::is_finite,
405                         &|_| true,
406                     );
407                 }
408
409                 fn is_infinite<const LANES: usize>() {
410                     test_helpers::test_unary_mask_elementwise(
411                         &Vector::<LANES>::is_infinite,
412                         &Scalar::is_infinite,
413                         &|_| true,
414                     );
415                 }
416
417                 fn is_nan<const LANES: usize>() {
418                     test_helpers::test_unary_mask_elementwise(
419                         &Vector::<LANES>::is_nan,
420                         &Scalar::is_nan,
421                         &|_| true,
422                     );
423                 }
424
425                 fn is_normal<const LANES: usize>() {
426                     test_helpers::test_unary_mask_elementwise(
427                         &Vector::<LANES>::is_normal,
428                         &Scalar::is_normal,
429                         &|_| true,
430                     );
431                 }
432
433                 fn is_subnormal<const LANES: usize>() {
434                     test_helpers::test_unary_mask_elementwise(
435                         &Vector::<LANES>::is_subnormal,
436                         &Scalar::is_subnormal,
437                         &|_| true,
438                     );
439                 }
440
441                 fn abs<const LANES: usize>() {
442                     test_helpers::test_unary_elementwise(
443                         &Vector::<LANES>::abs,
444                         &Scalar::abs,
445                         &|_| true,
446                     )
447                 }
448
449                 fn round_from_int<const LANES: usize>() {
450                     test_helpers::test_unary_elementwise(
451                         &Vector::<LANES>::round_from_int,
452                         &|x| x as Scalar,
453                         &|_| true,
454                     )
455                 }
456
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;
463
464                     let mut runner = proptest::test_runner::TestRunner::default();
465                     runner.run(
466                         &test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
467                         |x| {
468                             let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
469                             let result_2 = {
470                                 let mut result = [0; LANES];
471                                 for (i, o) in x.iter().zip(result.iter_mut()) {
472                                     *o = unsafe { i.to_int_unchecked() };
473                                 }
474                                 result
475                             };
476                             test_helpers::prop_assert_biteq!(result_1, result_2);
477                             Ok(())
478                         },
479                     ).unwrap();
480                 }
481
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(),
486                             x.iter().sum(),
487                         );
488                         Ok(())
489                     });
490                 }
491
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(),
496                             x.iter().product(),
497                         );
498                         Ok(())
499                     });
500                 }
501
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);
509                         }
510                         Ok(())
511                     });
512                 }
513
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);
521                         }
522                         Ok(())
523                     });
524                 }
525             }
526         }
527     }
528 }