]> git.lizzy.rs Git - rust.git/blob - library/portable-simd/crates/core_simd/tests/ops_macros.rs
Auto merge of #91500 - ehuss:update-mdbook, r=Mark-Simulacrum
[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 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),
102                     );
103                     Ok(())
104                 });
105             }
106
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),
112                     );
113                     Ok(())
114                 });
115             }
116
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),
122                     );
123                     Ok(())
124                 });
125             }
126
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),
132                     );
133                     Ok(())
134                 });
135             }
136
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),
142                     );
143                     Ok(())
144                 });
145             }
146
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(),
152                     );
153                     Ok(())
154                 });
155             }
156
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(),
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             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
176             type Scalar = $scalar;
177
178             impl_common_integer_tests! { Vector, Scalar }
179
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),
186                     );
187                 }
188
189                 fn is_positive<const LANES: usize>() {
190                     test_helpers::test_unary_mask_elementwise(
191                         &Vector::<LANES>::is_positive,
192                         &Scalar::is_positive,
193                         &|_| true,
194                     );
195                 }
196
197                 fn is_negative<const LANES: usize>() {
198                     test_helpers::test_unary_mask_elementwise(
199                         &Vector::<LANES>::is_negative,
200                         &Scalar::is_negative,
201                         &|_| true,
202                     );
203                 }
204
205                 fn signum<const LANES: usize>() {
206                     test_helpers::test_unary_elementwise(
207                         &Vector::<LANES>::signum,
208                         &Scalar::signum,
209                         &|_| true,
210                     )
211                 }
212
213             }
214
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);
219                     let _ = a / b;
220                 }
221
222                 fn div_by_all_zeros_panics<const LANES: usize>() {
223                     let a = Vector::<LANES>::splat(42);
224                     let b = Vector::<LANES>::splat(0);
225                     let _ = a / b;
226                 }
227
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);
231                     b[0] = 0 as _;
232                     let _ = a / b;
233                 }
234
235                 fn rem_min_overflow_panic<const LANES: usize>() {
236                     let a = Vector::<LANES>::splat(Scalar::MIN);
237                     let b = Vector::<LANES>::splat(-1);
238                     let _ = a % b;
239                 }
240
241                 fn rem_zero_panic<const LANES: usize>() {
242                     let a = Vector::<LANES>::splat(42);
243                     let b = Vector::<LANES>::splat(0);
244                     let _ = a % b;
245                 }
246             }
247
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);
252                     let _ = a / b;
253                 }
254
255                 fn rem_neg_one_no_panic<const LANES: usize>() {
256                     let a = Vector::<LANES>::splat(42);
257                     let b = Vector::<LANES>::splat(-1);
258                     let _ = a % b;
259                 }
260             }
261
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);
265
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));
269
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);
274         }
275     }
276 }
277
278 /// Implement tests for unsigned integers.
279 #[macro_export]
280 macro_rules! impl_unsigned_tests {
281     { $scalar:tt } => {
282         mod $scalar {
283             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
284             type Scalar = $scalar;
285
286             impl_common_integer_tests! { Vector, Scalar }
287
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);
292                     let _ = a % b;
293                 }
294             }
295
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);
299
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);
303
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);
308         }
309     }
310 }
311
312 /// Implement tests for floating point numbers.
313 #[macro_export]
314 macro_rules! impl_float_tests {
315     { $scalar:tt, $int_scalar:tt } => {
316         mod $scalar {
317             type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
318             type Scalar = $scalar;
319
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);
326
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,
332                         &|_| true,
333                     );
334                 }
335
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,
340                         &|_| true,
341                     );
342                 }
343
344                 fn is_finite<const LANES: usize>() {
345                     test_helpers::test_unary_mask_elementwise(
346                         &Vector::<LANES>::is_finite,
347                         &Scalar::is_finite,
348                         &|_| true,
349                     );
350                 }
351
352                 fn is_infinite<const LANES: usize>() {
353                     test_helpers::test_unary_mask_elementwise(
354                         &Vector::<LANES>::is_infinite,
355                         &Scalar::is_infinite,
356                         &|_| true,
357                     );
358                 }
359
360                 fn is_nan<const LANES: usize>() {
361                     test_helpers::test_unary_mask_elementwise(
362                         &Vector::<LANES>::is_nan,
363                         &Scalar::is_nan,
364                         &|_| true,
365                     );
366                 }
367
368                 fn is_normal<const LANES: usize>() {
369                     test_helpers::test_unary_mask_elementwise(
370                         &Vector::<LANES>::is_normal,
371                         &Scalar::is_normal,
372                         &|_| true,
373                     );
374                 }
375
376                 fn is_subnormal<const LANES: usize>() {
377                     test_helpers::test_unary_mask_elementwise(
378                         &Vector::<LANES>::is_subnormal,
379                         &Scalar::is_subnormal,
380                         &|_| true,
381                     );
382                 }
383
384                 fn abs<const LANES: usize>() {
385                     test_helpers::test_unary_elementwise(
386                         &Vector::<LANES>::abs,
387                         &Scalar::abs,
388                         &|_| true,
389                     )
390                 }
391
392                 fn recip<const LANES: usize>() {
393                     test_helpers::test_unary_elementwise(
394                         &Vector::<LANES>::recip,
395                         &Scalar::recip,
396                         &|_| true,
397                     )
398                 }
399
400                 fn to_degrees<const LANES: usize>() {
401                     test_helpers::test_unary_elementwise(
402                         &Vector::<LANES>::to_degrees,
403                         &Scalar::to_degrees,
404                         &|_| true,
405                     )
406                 }
407
408                 fn to_radians<const LANES: usize>() {
409                     test_helpers::test_unary_elementwise(
410                         &Vector::<LANES>::to_radians,
411                         &Scalar::to_radians,
412                         &|_| true,
413                     )
414                 }
415
416                 fn signum<const LANES: usize>() {
417                     test_helpers::test_unary_elementwise(
418                         &Vector::<LANES>::signum,
419                         &Scalar::signum,
420                         &|_| true,
421                     )
422                 }
423
424                 fn copysign<const LANES: usize>() {
425                     test_helpers::test_binary_elementwise(
426                         &Vector::<LANES>::copysign,
427                         &Scalar::copysign,
428                         &|_, _| true,
429                     )
430                 }
431
432                 fn min<const LANES: usize>() {
433                     // Regular conditions (both values aren't zero)
434                     test_helpers::test_binary_elementwise(
435                         &Vector::<LANES>::min,
436                         &Scalar::min,
437                         // Reject the case where both values are zero with different signs
438                         &|a, b| {
439                             for (a, b) in a.iter().zip(b.iter()) {
440                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
441                                     return false;
442                                 }
443                             }
444                             true
445                         }
446                     );
447
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.));
453                 }
454
455                 fn max<const LANES: usize>() {
456                     // Regular conditions (both values aren't zero)
457                     test_helpers::test_binary_elementwise(
458                         &Vector::<LANES>::max,
459                         &Scalar::max,
460                         // Reject the case where both values are zero with different signs
461                         &|a, b| {
462                             for (a, b) in a.iter().zip(b.iter()) {
463                                 if *a == 0. && *b == 0. && a.signum() != b.signum() {
464                                     return false;
465                                 }
466                             }
467                             true
468                         }
469                     );
470
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.));
476                 }
477
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()) {
481                             if max < min {
482                                 core::mem::swap(min, max);
483                             }
484                             if min.is_nan() {
485                                 *min = Scalar::NEG_INFINITY;
486                             }
487                             if max.is_nan() {
488                                 *max = Scalar::INFINITY;
489                             }
490                         }
491
492                         let mut result_scalar = [Scalar::default(); LANES];
493                         for i in 0..LANES {
494                             result_scalar[i] = value[i].clamp(min[i], max[i]);
495                         }
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);
498                         Ok(())
499                     })
500                 }
501
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(),
506                             x.iter().sum(),
507                         );
508                         Ok(())
509                     });
510                 }
511
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(),
516                             x.iter().product(),
517                         );
518                         Ok(())
519                     });
520                 }
521
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);
529                         }
530                         Ok(())
531                     });
532                 }
533
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);
541                         }
542                         Ok(())
543                     });
544                 }
545             }
546
547             #[cfg(feature = "std")]
548             mod std {
549                 use super::*;
550                 test_helpers::test_lanes! {
551                     fn sqrt<const LANES: usize>() {
552                         test_helpers::test_unary_elementwise(
553                             &Vector::<LANES>::sqrt,
554                             &Scalar::sqrt,
555                             &|_| true,
556                         )
557                     }
558
559                     fn mul_add<const LANES: usize>() {
560                         test_helpers::test_ternary_elementwise(
561                             &Vector::<LANES>::mul_add,
562                             &Scalar::mul_add,
563                             &|_, _, _| true,
564                         )
565                     }
566                 }
567             }
568         }
569     }
570 }