]> git.lizzy.rs Git - rust.git/blob - library/portable-simd/crates/core_simd/tests/ops_macros.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / library / portable-simd / 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     { $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,
11                     &$scalar_fn,
12                     &|_| true,
13                 );
14             }
15         }
16     };
17     { $scalar:ty, $trait:ident :: $fn:ident } => {
18         impl_unary_op_test! { $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     { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
28         mod $fn {
29             use super::*;
30             use core_simd::Simd;
31
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,
36                         &$scalar_fn,
37                         &|_, _| true,
38                     );
39                 }
40
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 },
44                         &$scalar_fn,
45                         &|_, _| true,
46                     );
47                 }
48             }
49         }
50     };
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 }
53     };
54 }
55
56 /// Implements a test on a binary operation using proptest.
57 ///
58 /// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
59 /// (like the `proptest_assume` macro).
60 ///
61 /// Compares the vector operation to the equivalent scalar operation.
62 #[macro_export]
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 } => {
65         mod $fn {
66             use super::*;
67             use core_simd::Simd;
68
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,
73                         &$scalar_fn,
74                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
75                     );
76                 }
77
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 },
81                         &$scalar_fn,
82                         &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
83                     )
84                 }
85             }
86         }
87     };
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 }
90     };
91 }
92
93 #[macro_export]
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),
102                     );
103                     Ok(())
104                 });
105             }
106
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),
112                     );
113                     Ok(())
114                 });
115             }
116
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),
122                     );
123                     Ok(())
124                 });
125             }
126
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),
132                     );
133                     Ok(())
134                 });
135             }
136
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),
142                     );
143                     Ok(())
144                 });
145             }
146
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(),
152                     );
153                     Ok(())
154                 });
155             }
156
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(),
162                     );
163                     Ok(())
164                 });
165             }
166         }
167     }
168 }
169
170 /// Implement tests for signed integers.
171 #[macro_export]
172 macro_rules! impl_signed_tests {
173     { $scalar:tt } => {
174         mod $scalar {
175             use core_simd::simd::SimdInt;
176             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
177             type Scalar = $scalar;
178
179             impl_common_integer_tests! { Vector, Scalar }
180
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),
187                     );
188                 }
189
190                 fn is_positive<const LANES: usize>() {
191                     test_helpers::test_unary_mask_elementwise(
192                         &Vector::<LANES>::is_positive,
193                         &Scalar::is_positive,
194                         &|_| true,
195                     );
196                 }
197
198                 fn is_negative<const LANES: usize>() {
199                     test_helpers::test_unary_mask_elementwise(
200                         &Vector::<LANES>::is_negative,
201                         &Scalar::is_negative,
202                         &|_| true,
203                     );
204                 }
205
206                 fn signum<const LANES: usize>() {
207                     test_helpers::test_unary_elementwise(
208                         &Vector::<LANES>::signum,
209                         &Scalar::signum,
210                         &|_| true,
211                     )
212                 }
213
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);
218                 }
219
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));
224                 }
225
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);
234                 }
235
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);
244                 }
245
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);
257                 }
258             }
259
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);
264                     let _ = a / b;
265                 }
266
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);
270                     b[0] = 0 as _;
271                     let _ = a / b;
272                 }
273
274                 fn rem_zero_panic<const LANES: usize>() {
275                     let a = Vector::<LANES>::splat(42);
276                     let b = Vector::<LANES>::splat(0);
277                     let _ = a % b;
278                 }
279             }
280
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);
285                     let _ = a / b;
286                 }
287
288                 fn rem_neg_one_no_panic<const LANES: usize>() {
289                     let a = Vector::<LANES>::splat(42);
290                     let b = Vector::<LANES>::splat(-1);
291                     let _ = a % b;
292                 }
293             }
294
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);
298
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));
302
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);
307         }
308     }
309 }
310
311 /// Implement tests for unsigned integers.
312 #[macro_export]
313 macro_rules! impl_unsigned_tests {
314     { $scalar:tt } => {
315         mod $scalar {
316             use core_simd::simd::SimdUint;
317             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
318             type Scalar = $scalar;
319
320             impl_common_integer_tests! { Vector, Scalar }
321
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);
326                     let _ = a % b;
327                 }
328             }
329
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);
333
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);
337
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);
342         }
343     }
344 }
345
346 /// Implement tests for floating point numbers.
347 #[macro_export]
348 macro_rules! impl_float_tests {
349     { $scalar:tt, $int_scalar:tt } => {
350         mod $scalar {
351             use core_simd::SimdFloat;
352             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
353             type Scalar = $scalar;
354
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);
361
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,
367                         &|_| true,
368                     );
369                 }
370
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,
375                         &|_| true,
376                     );
377                 }
378
379                 fn is_finite<const LANES: usize>() {
380                     test_helpers::test_unary_mask_elementwise(
381                         &Vector::<LANES>::is_finite,
382                         &Scalar::is_finite,
383                         &|_| true,
384                     );
385                 }
386
387                 fn is_infinite<const LANES: usize>() {
388                     test_helpers::test_unary_mask_elementwise(
389                         &Vector::<LANES>::is_infinite,
390                         &Scalar::is_infinite,
391                         &|_| true,
392                     );
393                 }
394
395                 fn is_nan<const LANES: usize>() {
396                     test_helpers::test_unary_mask_elementwise(
397                         &Vector::<LANES>::is_nan,
398                         &Scalar::is_nan,
399                         &|_| true,
400                     );
401                 }
402
403                 fn is_normal<const LANES: usize>() {
404                     test_helpers::test_unary_mask_elementwise(
405                         &Vector::<LANES>::is_normal,
406                         &Scalar::is_normal,
407                         &|_| true,
408                     );
409                 }
410
411                 fn is_subnormal<const LANES: usize>() {
412                     test_helpers::test_unary_mask_elementwise(
413                         &Vector::<LANES>::is_subnormal,
414                         &Scalar::is_subnormal,
415                         &|_| true,
416                     );
417                 }
418
419                 fn abs<const LANES: usize>() {
420                     test_helpers::test_unary_elementwise(
421                         &Vector::<LANES>::abs,
422                         &Scalar::abs,
423                         &|_| true,
424                     )
425                 }
426
427                 fn recip<const LANES: usize>() {
428                     test_helpers::test_unary_elementwise(
429                         &Vector::<LANES>::recip,
430                         &Scalar::recip,
431                         &|_| true,
432                     )
433                 }
434
435                 fn to_degrees<const LANES: usize>() {
436                     test_helpers::test_unary_elementwise(
437                         &Vector::<LANES>::to_degrees,
438                         &Scalar::to_degrees,
439                         &|_| true,
440                     )
441                 }
442
443                 fn to_radians<const LANES: usize>() {
444                     test_helpers::test_unary_elementwise(
445                         &Vector::<LANES>::to_radians,
446                         &Scalar::to_radians,
447                         &|_| true,
448                     )
449                 }
450
451                 fn signum<const LANES: usize>() {
452                     test_helpers::test_unary_elementwise(
453                         &Vector::<LANES>::signum,
454                         &Scalar::signum,
455                         &|_| true,
456                     )
457                 }
458
459                 fn copysign<const LANES: usize>() {
460                     test_helpers::test_binary_elementwise(
461                         &Vector::<LANES>::copysign,
462                         &Scalar::copysign,
463                         &|_, _| true,
464                     )
465                 }
466
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,
471                         &Scalar::min,
472                         // Reject the case where both values are zero with different signs
473                         &|a, b| {
474                             for (a, b) in a.iter().zip(b.iter()) {
475                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
476                                     return false;
477                                 }
478                             }
479                             true
480                         }
481                     );
482
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.));
488                 }
489
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,
494                         &Scalar::max,
495                         // Reject the case where both values are zero with different signs
496                         &|a, b| {
497                             for (a, b) in a.iter().zip(b.iter()) {
498                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
499                                     return false;
500                                 }
501                             }
502                             true
503                         }
504                     );
505
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.));
511                 }
512
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()) {
516                             if max < min {
517                                 core::mem::swap(min, max);
518                             }
519                             if min.is_nan() {
520                                 *min = Scalar::NEG_INFINITY;
521                             }
522                             if max.is_nan() {
523                                 *max = Scalar::INFINITY;
524                             }
525                         }
526
527                         let mut result_scalar = [Scalar::default(); LANES];
528                         for i in 0..LANES {
529                             result_scalar[i] = value[i].clamp(min[i], max[i]);
530                         }
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);
533                         Ok(())
534                     })
535                 }
536
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(),
541                             x.iter().sum(),
542                         );
543                         Ok(())
544                     });
545                 }
546
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(),
551                             x.iter().product(),
552                         );
553                         Ok(())
554                     });
555                 }
556
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);
564                         }
565                         Ok(())
566                     });
567                 }
568
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);
576                         }
577                         Ok(())
578                     });
579                 }
580             }
581
582             #[cfg(feature = "std")]
583             mod std {
584                 use std_float::StdFloat;
585
586                 use super::*;
587                 test_helpers::test_lanes! {
588                     fn sqrt<const LANES: usize>() {
589                         test_helpers::test_unary_elementwise(
590                             &Vector::<LANES>::sqrt,
591                             &Scalar::sqrt,
592                             &|_| true,
593                         )
594                     }
595
596                     fn mul_add<const LANES: usize>() {
597                         test_helpers::test_ternary_elementwise(
598                             &Vector::<LANES>::mul_add,
599                             &Scalar::mul_add,
600                             &|_, _, _| true,
601                         )
602                     }
603                 }
604             }
605         }
606     }
607 }