]> git.lizzy.rs Git - rust.git/blob - crates/core_simd/tests/ops_macros.rs
Give rounding intrinsics their own modules
[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
357             impl_unary_op_test!(Vector<LANES>, Scalar, Neg::neg);
358             impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign);
359             impl_binary_op_test!(Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign);
360             impl_binary_op_test!(Vector<LANES>, Scalar, Mul::mul, MulAssign::mul_assign);
361             impl_binary_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign);
362             impl_binary_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign);
363
364             test_helpers::test_lanes! {
365                 fn is_sign_positive<const LANES: usize>() {
366                     test_helpers::test_unary_mask_elementwise(
367                         &Vector::<LANES>::is_sign_positive,
368                         &Scalar::is_sign_positive,
369                         &|_| true,
370                     );
371                 }
372
373                 fn is_sign_negative<const LANES: usize>() {
374                     test_helpers::test_unary_mask_elementwise(
375                         &Vector::<LANES>::is_sign_negative,
376                         &Scalar::is_sign_negative,
377                         &|_| true,
378                     );
379                 }
380
381                 fn is_finite<const LANES: usize>() {
382                     test_helpers::test_unary_mask_elementwise(
383                         &Vector::<LANES>::is_finite,
384                         &Scalar::is_finite,
385                         &|_| true,
386                     );
387                 }
388
389                 fn is_infinite<const LANES: usize>() {
390                     test_helpers::test_unary_mask_elementwise(
391                         &Vector::<LANES>::is_infinite,
392                         &Scalar::is_infinite,
393                         &|_| true,
394                     );
395                 }
396
397                 fn is_nan<const LANES: usize>() {
398                     test_helpers::test_unary_mask_elementwise(
399                         &Vector::<LANES>::is_nan,
400                         &Scalar::is_nan,
401                         &|_| true,
402                     );
403                 }
404
405                 fn is_normal<const LANES: usize>() {
406                     test_helpers::test_unary_mask_elementwise(
407                         &Vector::<LANES>::is_normal,
408                         &Scalar::is_normal,
409                         &|_| true,
410                     );
411                 }
412
413                 fn is_subnormal<const LANES: usize>() {
414                     test_helpers::test_unary_mask_elementwise(
415                         &Vector::<LANES>::is_subnormal,
416                         &Scalar::is_subnormal,
417                         &|_| true,
418                     );
419                 }
420
421                 fn abs<const LANES: usize>() {
422                     test_helpers::test_unary_elementwise(
423                         &Vector::<LANES>::abs,
424                         &Scalar::abs,
425                         &|_| true,
426                     )
427                 }
428
429                 fn horizontal_sum<const LANES: usize>() {
430                     test_helpers::test_1(&|x| {
431                         test_helpers::prop_assert_biteq! (
432                             Vector::<LANES>::from_array(x).horizontal_sum(),
433                             x.iter().sum(),
434                         );
435                         Ok(())
436                     });
437                 }
438
439                 fn horizontal_product<const LANES: usize>() {
440                     test_helpers::test_1(&|x| {
441                         test_helpers::prop_assert_biteq! (
442                             Vector::<LANES>::from_array(x).horizontal_product(),
443                             x.iter().product(),
444                         );
445                         Ok(())
446                     });
447                 }
448
449                 fn horizontal_max<const LANES: usize>() {
450                     test_helpers::test_1(&|x| {
451                         let vmax = Vector::<LANES>::from_array(x).horizontal_max();
452                         let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
453                         // 0 and -0 are treated the same
454                         if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
455                             test_helpers::prop_assert_biteq!(vmax, smax);
456                         }
457                         Ok(())
458                     });
459                 }
460
461                 fn horizontal_min<const LANES: usize>() {
462                     test_helpers::test_1(&|x| {
463                         let vmax = Vector::<LANES>::from_array(x).horizontal_min();
464                         let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
465                         // 0 and -0 are treated the same
466                         if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
467                             test_helpers::prop_assert_biteq!(vmax, smax);
468                         }
469                         Ok(())
470                     });
471                 }
472             }
473         }
474     }
475 }