OpenShot Audio Library | OpenShotAudio  0.6.0
juce_SIMDRegister_test.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11  Agreement and JUCE Privacy Policy.
12 
13  End User License Agreement: www.juce.com/juce-7-licence
14  Privacy Policy: www.juce.com/juce-privacy-policy
15 
16  Or: You may also use this code under the terms of the GPL v3 (see
17  www.gnu.org/licenses).
18 
19  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21  DISCLAIMED.
22 
23  ==============================================================================
24 */
25 
26 namespace juce::dsp
27 {
28 
29 namespace SIMDRegister_test_internal
30 {
31  template <typename type>
32  struct RandomPrimitive
33  {
34  static type next (Random& random)
35  {
36  if constexpr (std::is_floating_point_v<type>)
37  {
38  return static_cast<type> (std::is_signed_v<type> ? (random.nextFloat() * 16.0) - 8.0
39  : (random.nextFloat() * 8.0));
40  }
41  else if constexpr (std::is_integral_v<type>)
42  {
43  return static_cast<type> (random.nextInt64());
44  }
45  }
46  };
47 
48  template <typename type>
49  struct RandomValue
50  {
51  static type next (Random& random)
52  {
53  return RandomPrimitive<type>::next (random);
54  }
55  };
56 
57  template <typename type>
58  struct RandomValue<std::complex<type>>
59  {
60  static std::complex<type> next (Random& random)
61  {
62  return {RandomPrimitive<type>::next (random), RandomPrimitive<type>::next (random)};
63  }
64  };
65 
66  template <typename type>
67  static void fillVec (type* dst, Random& random)
68  {
69  std::generate_n (dst, SIMDRegister<type>::SIMDNumElements, [&]
70  {
71  return RandomValue<type>::next (random);
72  });
73  }
74 
75  // Avoid visual studio warning
76  template <typename type>
77  static type safeAbs (type a)
78  {
79  return static_cast<type> (std::abs (static_cast<double> (a)));
80  }
81 
82  template <typename type>
83  static type safeAbs (std::complex<type> a)
84  {
85  return std::abs (a);
86  }
87 
88  template <typename type>
89  static double difference (type a)
90  {
91  return static_cast<double> (safeAbs (a));
92  }
93 
94  template <typename type>
95  static double difference (type a, type b)
96  {
97  return difference (a - b);
98  }
99 } // namespace SIMDRegister_test_internal
100 
101 // These tests need to be strictly run on all platforms supported by JUCE as the
102 // SIMD code is highly platform dependent.
103 
104 class SIMDRegisterUnitTests final : public UnitTest
105 {
106 public:
107  template <typename> struct Tag {};
108 
109  SIMDRegisterUnitTests()
110  : UnitTest ("SIMDRegister UnitTests", UnitTestCategories::dsp)
111  {}
112 
113  //==============================================================================
114  // Some helper classes
115  template <typename type>
116  static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar)
117  {
118  alignas (sizeof (SIMDRegister<type>)) type elements[SIMDRegister<type>::SIMDNumElements];
119 
120  vec.copyToRawArray (elements);
121 
122  // as we do not want to rely on the access operator we cast this to a primitive pointer
123  return std::all_of (std::begin (elements), std::end (elements), [scalar] (const auto x) { return exactlyEqual (x, scalar); });
124  }
125 
126  template <typename type>
127  static bool vecEqualToArray (const SIMDRegister<type>& vec, const type* array)
128  {
129  HeapBlock<type> vecElementsStorage (SIMDRegister<type>::SIMDNumElements * 2);
130  auto* ptr = SIMDRegister<type>::getNextSIMDAlignedPtr (vecElementsStorage.getData());
131  vec.copyToRawArray (ptr);
132 
133  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
134  {
135  double delta = SIMDRegister_test_internal::difference (ptr[i], array[i]);
136  if (delta > 1e-4)
137  {
138  DBG ("a: " << SIMDRegister_test_internal::difference (ptr[i]) << " b: " << SIMDRegister_test_internal::difference (array[i]) << " difference: " << delta);
139  return false;
140  }
141  }
142 
143  return true;
144  }
145 
146  template <typename type>
147  static void copy (SIMDRegister<type>& vec, const type* ptr)
148  {
150  {
152  }
153  else
154  {
155  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
156  vec[i] = ptr[i];
157  }
158  }
159 
160  //==============================================================================
161  // Some useful operations to test
162  struct Addition
163  {
164  template <typename typeOne, typename typeTwo>
165  static void inplace (typeOne& a, const typeTwo& b)
166  {
167  a += b;
168  }
169 
170  template <typename typeOne, typename typeTwo>
171  static typeOne outofplace (const typeOne& a, const typeTwo& b)
172  {
173  return a + b;
174  }
175  };
176 
177  struct Subtraction
178  {
179  template <typename typeOne, typename typeTwo>
180  static void inplace (typeOne& a, const typeTwo& b)
181  {
182  a -= b;
183  }
184 
185  template <typename typeOne, typename typeTwo>
186  static typeOne outofplace (const typeOne& a, const typeTwo& b)
187  {
188  return a - b;
189  }
190  };
191 
192  struct Multiplication
193  {
194  template <typename typeOne, typename typeTwo>
195  static void inplace (typeOne& a, const typeTwo& b)
196  {
197  a *= b;
198  }
199 
200  template <typename typeOne, typename typeTwo>
201  static typeOne outofplace (const typeOne& a, const typeTwo& b)
202  {
203  return a * b;
204  }
205  };
206 
207  struct BitAND
208  {
209  template <typename typeOne, typename typeTwo>
210  static void inplace (typeOne& a, const typeTwo& b)
211  {
212  a &= b;
213  }
214 
215  template <typename typeOne, typename typeTwo>
216  static typeOne outofplace (const typeOne& a, const typeTwo& b)
217  {
218  return a & b;
219  }
220  };
221 
222  struct BitOR
223  {
224  template <typename typeOne, typename typeTwo>
225  static void inplace (typeOne& a, const typeTwo& b)
226  {
227  a |= b;
228  }
229 
230  template <typename typeOne, typename typeTwo>
231  static typeOne outofplace (const typeOne& a, const typeTwo& b)
232  {
233  return a | b;
234  }
235  };
236 
237  struct BitXOR
238  {
239  template <typename typeOne, typename typeTwo>
240  static void inplace (typeOne& a, const typeTwo& b)
241  {
242  a ^= b;
243  }
244 
245  template <typename typeOne, typename typeTwo>
246  static typeOne outofplace (const typeOne& a, const typeTwo& b)
247  {
248  return a ^ b;
249  }
250  };
251 
252  //==============================================================================
253  // the individual tests
254  struct InitializationTest
255  {
256  template <typename type>
257  static void run (UnitTest& u, Random& random, Tag<type>)
258  {
259  u.expect (allValuesEqualTo<type> (SIMDRegister<type>::expand (static_cast<type> (23)), 23));
260 
261  {
262  #ifdef _MSC_VER
263  __declspec (align (sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
264  #else
265  type elements[SIMDRegister<type>::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister<type>))));
266  #endif
267  SIMDRegister_test_internal::fillVec (elements, random);
268  SIMDRegister<type> a (SIMDRegister<type>::fromRawArray (elements));
269 
270  u.expect (vecEqualToArray (a, elements));
271 
272  SIMDRegister<type> b (a);
273  a *= static_cast<type> (2);
274 
275  u.expect (vecEqualToArray (b, elements));
276  }
277  }
278  };
279 
280  struct AccessTest
281  {
282  template <typename type>
283  static void run (UnitTest& u, Random& random, Tag<type>)
284  {
285  // set-up
286  SIMDRegister<type> a;
288 
289  SIMDRegister_test_internal::fillVec (array, random);
290 
291  // Test non-const access operator
292  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
293  a[i] = array[i];
294 
295  u.expect (vecEqualToArray (a, array));
296 
297  // Test const access operator
298  const SIMDRegister<type>& b = a;
299 
300  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
301  u.expect (exactlyEqual (b[i], array[i]));
302  }
303  };
304 
305  template <class Operation>
306  struct OperatorTests
307  {
308  template <typename type>
309  static void run (UnitTest& u, Random& random, Tag<type>)
310  {
311  for (int n = 0; n < 100; ++n)
312  {
313  // set-up
314  SIMDRegister<type> a (static_cast<type> (0));
315  SIMDRegister<type> b (static_cast<type> (0));
316  SIMDRegister<type> c (static_cast<type> (0));
317 
321 
322  SIMDRegister_test_internal::fillVec (array_a, random);
323  SIMDRegister_test_internal::fillVec (array_b, random);
324  SIMDRegister_test_internal::fillVec (array_c, random);
325 
326  copy (a, array_a); copy (b, array_b); copy (c, array_c);
327 
328  // test in-place with both params being vectors
329  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
330  Operation::template inplace<type, type> (array_a[i], array_b[i]);
331 
332  Operation::template inplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
333 
334  u.expect (vecEqualToArray (a, array_a));
335  u.expect (vecEqualToArray (b, array_b));
336 
337  SIMDRegister_test_internal::fillVec (array_a, random);
338  SIMDRegister_test_internal::fillVec (array_b, random);
339  SIMDRegister_test_internal::fillVec (array_c, random);
340 
341  copy (a, array_a); copy (b, array_b); copy (c, array_c);
342 
343  // test in-place with one param being scalar
344  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
345  Operation::template inplace<type, type> (array_b[i], static_cast<type> (2));
346 
347  Operation::template inplace<SIMDRegister<type>, type> (b, 2);
348 
349  u.expect (vecEqualToArray (a, array_a));
350  u.expect (vecEqualToArray (b, array_b));
351 
352  // set-up again
353  SIMDRegister_test_internal::fillVec (array_a, random);
354  SIMDRegister_test_internal::fillVec (array_b, random);
355  SIMDRegister_test_internal::fillVec (array_c, random);
356  copy (a, array_a); copy (b, array_b); copy (c, array_c);
357 
358  // test out-of-place with both params being vectors
359  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
360  array_c[i] = Operation::template outofplace<type, type> (array_a[i], array_b[i]);
361 
362  c = Operation::template outofplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
363 
364  u.expect (vecEqualToArray (a, array_a));
365  u.expect (vecEqualToArray (b, array_b));
366  u.expect (vecEqualToArray (c, array_c));
367 
368  // test out-of-place with one param being scalar
369  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
370  array_c[i] = Operation::template outofplace<type, type> (array_b[i], static_cast<type> (2));
371 
372  c = Operation::template outofplace<SIMDRegister<type>, type> (b, 2);
373 
374  u.expect (vecEqualToArray (a, array_a));
375  u.expect (vecEqualToArray (b, array_b));
376  u.expect (vecEqualToArray (c, array_c));
377  }
378  }
379  };
380 
381  template <class Operation>
382  struct BitOperatorTests
383  {
384  template <typename type>
385  static void run (UnitTest& u, Random& random, Tag<type>)
386  {
387  typedef typename SIMDRegister<type>::vMaskType vMaskType;
388  typedef typename SIMDRegister<type>::MaskType MaskType;
389 
390  for (int n = 0; n < 100; ++n)
391  {
392  // Check flip sign bit and using as a union
393  {
395 
396  union ConversionUnion
397  {
398  inline ConversionUnion() : floatVersion (static_cast<type> (0)) {}
399  inline ~ConversionUnion() {}
400  SIMDRegister<type> floatVersion;
401  vMaskType intVersion;
402  } a, b;
403 
404  vMaskType bitmask = vMaskType::expand (static_cast<MaskType> (1) << (sizeof (MaskType) - 1));
405  SIMDRegister_test_internal::fillVec (array_a, random);
406  copy (a.floatVersion, array_a);
407  copy (b.floatVersion, array_a);
408 
409  Operation::template inplace<SIMDRegister<type>, vMaskType> (a.floatVersion, bitmask);
410  Operation::template inplace<vMaskType, vMaskType> (b.intVersion, bitmask);
411 
412  #ifdef _MSC_VER
413  __declspec (align (sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
414  #else
415  type elements[SIMDRegister<type>::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister<type>))));
416  #endif
417  b.floatVersion.copyToRawArray (elements);
418 
419  u.expect (vecEqualToArray (a.floatVersion, elements));
420  }
421 
422  // set-up
423  SIMDRegister<type> a, c;
424  vMaskType b;
425 
426  MaskType array_a [SIMDRegister<MaskType>::SIMDNumElements];
427  MaskType array_b [SIMDRegister<MaskType>::SIMDNumElements];
428  MaskType array_c [SIMDRegister<MaskType>::SIMDNumElements];
429 
432 
433  SIMDRegister_test_internal::fillVec (float_a, random);
434  SIMDRegister_test_internal::fillVec (array_b, random);
435  SIMDRegister_test_internal::fillVec (float_c, random);
436 
437  memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
438  memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
439  copy (a, float_a); copy (b, array_b); copy (c, float_c);
440 
441  // test in-place with both params being vectors
442  for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
443  Operation::template inplace<MaskType, MaskType> (array_a[i], array_b[i]);
444  memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
445 
446  Operation::template inplace<SIMDRegister<type>, vMaskType> (a, b);
447 
448  u.expect (vecEqualToArray (a, float_a));
449  u.expect (vecEqualToArray (b, array_b));
450 
451  SIMDRegister_test_internal::fillVec (float_a, random);
452  SIMDRegister_test_internal::fillVec (array_b, random);
453  SIMDRegister_test_internal::fillVec (float_c, random);
454  memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
455  memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
456  copy (a, float_a); copy (b, array_b); copy (c, float_c);
457 
458  // test in-place with one param being scalar
459  for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
460  Operation::template inplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
461  memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
462 
463  Operation::template inplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
464 
465  u.expect (vecEqualToArray (a, float_a));
466  u.expect (vecEqualToArray (b, array_b));
467 
468  // set-up again
469  SIMDRegister_test_internal::fillVec (float_a, random);
470  SIMDRegister_test_internal::fillVec (array_b, random);
471  SIMDRegister_test_internal::fillVec (float_c, random);
472  memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
473  memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
474  copy (a, float_a); copy (b, array_b); copy (c, float_c);
475 
476  // test out-of-place with both params being vectors
477  for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
478  {
479  array_c[i] =
480  Operation::template outofplace<MaskType, MaskType> (array_a[i], array_b[i]);
481  }
482  memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
483  memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
484 
485  c = Operation::template outofplace<SIMDRegister<type>, vMaskType> (a, b);
486 
487  u.expect (vecEqualToArray (a, float_a));
488  u.expect (vecEqualToArray (b, array_b));
489  u.expect (vecEqualToArray (c, float_c));
490 
491  // test out-of-place with one param being scalar
492  for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
493  array_c[i] = Operation::template outofplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
494  memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
495  memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
496 
497  c = Operation::template outofplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
498 
499  u.expect (vecEqualToArray (a, float_a));
500  u.expect (vecEqualToArray (b, array_b));
501  u.expect (vecEqualToArray (c, float_c));
502  }
503  }
504  };
505 
506  struct CheckComparisonOps
507  {
508  template <typename type>
509  static void run (UnitTest& u, Random& random, Tag<type>)
510  {
511  typedef typename SIMDRegister<type>::vMaskType vMaskType;
512  typedef typename SIMDRegister<type>::MaskType MaskType;
513 
514  for (int i = 0; i < 100; ++i)
515  {
516  // set-up
519  MaskType array_eq [SIMDRegister<type>::SIMDNumElements];
520  MaskType array_neq [SIMDRegister<type>::SIMDNumElements];
521  MaskType array_lt [SIMDRegister<type>::SIMDNumElements];
522  MaskType array_le [SIMDRegister<type>::SIMDNumElements];
523  MaskType array_gt [SIMDRegister<type>::SIMDNumElements];
524  MaskType array_ge [SIMDRegister<type>::SIMDNumElements];
525 
526 
527  SIMDRegister_test_internal::fillVec (array_a, random);
528  SIMDRegister_test_internal::fillVec (array_b, random);
529 
530  // do check
531  for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
532  {
533  array_eq [j] = ( exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
534  array_neq [j] = (! exactlyEqual (array_a[j], array_b[j])) ? static_cast<MaskType> (-1) : 0;
535  array_lt [j] = (array_a[j] < array_b[j]) ? static_cast<MaskType> (-1) : 0;
536  array_le [j] = (array_a[j] <= array_b[j]) ? static_cast<MaskType> (-1) : 0;
537  array_gt [j] = (array_a[j] > array_b[j]) ? static_cast<MaskType> (-1) : 0;
538  array_ge [j] = (array_a[j] >= array_b[j]) ? static_cast<MaskType> (-1) : 0;
539  }
540 
541  SIMDRegister<type> a (static_cast<type> (0));
542  SIMDRegister<type> b (static_cast<type> (0));
543 
544  vMaskType eq, neq, lt, le, gt, ge;
545 
546  copy (a, array_a);
547  copy (b, array_b);
548 
549  eq = SIMDRegister<type>::equal (a, b);
550  neq = SIMDRegister<type>::notEqual (a, b);
551  lt = SIMDRegister<type>::lessThan (a, b);
555 
556  u.expect (vecEqualToArray (eq, array_eq ));
557  u.expect (vecEqualToArray (neq, array_neq));
558  u.expect (vecEqualToArray (lt, array_lt ));
559  u.expect (vecEqualToArray (le, array_le ));
560  u.expect (vecEqualToArray (gt, array_gt ));
561  u.expect (vecEqualToArray (ge, array_ge ));
562 
563  do
564  {
565  SIMDRegister_test_internal::fillVec (array_a, random);
566  SIMDRegister_test_internal::fillVec (array_b, random);
567  } while (std::equal (array_a, array_a + SIMDRegister<type>::SIMDNumElements, array_b));
568 
569  copy (a, array_a);
570  copy (b, array_b);
571  u.expect (a != b);
572  u.expect (b != a);
573  u.expect (! (a == b));
574  u.expect (! (b == a));
575 
576  SIMDRegister_test_internal::fillVec (array_a, random);
577  copy (a, array_a);
578  copy (b, array_a);
579 
580  u.expect (a == b);
581  u.expect (b == a);
582  u.expect (! (a != b));
583  u.expect (! (b != a));
584 
585  type scalar = a[0];
586  a = SIMDRegister<type>::expand (scalar);
587 
588  u.expect (a == scalar);
589  u.expect (! (a != scalar));
590 
591  scalar--;
592 
593  u.expect (a != scalar);
594  u.expect (! (a == scalar));
595  }
596  }
597  };
598 
599  struct CheckMultiplyAdd
600  {
601  template <typename type>
602  static void run (UnitTest& u, Random& random, Tag<type>)
603  {
604  // set-up
609 
610  SIMDRegister_test_internal::fillVec (array_a, random);
611  SIMDRegister_test_internal::fillVec (array_b, random);
612  SIMDRegister_test_internal::fillVec (array_c, random);
613  SIMDRegister_test_internal::fillVec (array_d, random);
614 
615  // check
616  for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
617  array_d[i] = array_a[i] + (array_b[i] * array_c[i]);
618 
619  SIMDRegister<type> a, b, c, d;
620 
621  copy (a, array_a);
622  copy (b, array_b);
623  copy (c, array_c);
624 
625  d = SIMDRegister<type>::multiplyAdd (a, b, c);
626 
627  u.expect (vecEqualToArray (d, array_d));
628  }
629  };
630 
631  struct CheckMinMax
632  {
633  template <typename type>
634  static void run (UnitTest& u, Random& random, Tag<type>)
635  {
636  for (int i = 0; i < 100; ++i)
637  {
640  type array_min [SIMDRegister<type>::SIMDNumElements];
641  type array_max [SIMDRegister<type>::SIMDNumElements];
642 
643  for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
644  {
645  array_a[j] = static_cast<type> (random.nextInt (127));
646  array_b[j] = static_cast<type> (random.nextInt (127));
647  }
648 
649  for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
650  {
651  array_min[j] = (array_a[j] < array_b[j]) ? array_a[j] : array_b[j];
652  array_max[j] = (array_a[j] > array_b[j]) ? array_a[j] : array_b[j];
653  }
654 
655  SIMDRegister<type> a (static_cast<type> (0));
656  SIMDRegister<type> b (static_cast<type> (0));
657  SIMDRegister<type> vMin (static_cast<type> (0));
658  SIMDRegister<type> vMax (static_cast<type> (0));
659 
660  copy (a, array_a);
661  copy (b, array_b);
662 
663  vMin = jmin (a, b);
664  vMax = jmax (a, b);
665 
666  u.expect (vecEqualToArray (vMin, array_min));
667  u.expect (vecEqualToArray (vMax, array_max));
668 
669  copy (vMin, array_a);
670  copy (vMax, array_a);
671 
672  vMin = SIMDRegister<type>::min (a, b);
673  vMax = SIMDRegister<type>::max (a, b);
674 
675  u.expect (vecEqualToArray (vMin, array_min));
676  u.expect (vecEqualToArray (vMax, array_max));
677  }
678  }
679  };
680 
681  struct CheckSum
682  {
683  template <typename type>
684  static void run (UnitTest& u, Random& random, Tag<type>)
685  {
687 
688  SIMDRegister_test_internal::fillVec (array, random);
689 
690  using AddedType = decltype (type{} + type{});
691  const auto sumCheck = (type) std::accumulate (std::begin (array), std::end (array), AddedType{});
692 
693  SIMDRegister<type> a;
694  copy (a, array);
695 
696  u.expect (SIMDRegister_test_internal::difference (sumCheck, a.sum()) < 1e-4);
697  }
698  };
699 
700  struct CheckAbs
701  {
702  template <typename type>
703  static void run (UnitTest& u, Random& random, Tag<type>)
704  {
707 
708  SIMDRegister_test_internal::fillVec (inArray, random);
709 
710  SIMDRegister<type> a;
711  copy (a, inArray);
712  a = SIMDRegister<type>::abs (a);
713 
714  auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); };
715 
716  for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
717  outArray[j] = calcAbs (inArray[j]);
718 
719  u.expect (vecEqualToArray (a, outArray));
720  }
721  };
722 
723  struct CheckTruncate
724  {
725  template <typename type>
726  static void run (UnitTest& u, Random& random, Tag<type>)
727  {
730 
731  SIMDRegister_test_internal::fillVec (inArray, random);
732 
733  SIMDRegister<type> a;
734  copy (a, inArray);
736 
737  for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
738  outArray[j] = (type) (int) inArray[j];
739 
740  u.expect (vecEqualToArray (a, outArray));
741  }
742  };
743 
744  struct CheckBoolEquals
745  {
746  template <typename type>
747  static void run (UnitTest& u, Random& random, Tag<type>)
748  {
750 
751  auto value = std::is_signed_v<type> ? static_cast<type> ((random.nextFloat() * 16.0) - 8.0)
752  : static_cast<type> (random.nextFloat() * 8.0);
753 
754  std::fill (array, array + SIMDRegister<type>::SIMDNumElements, value);
755  SIMDRegister<type> a, b;
756  copy (a, array);
757 
758  u.expect (a == value);
759  u.expect (! (a != value));
760  value += 1;
761 
762  u.expect (a != value);
763  u.expect (! (a == value));
764 
765  SIMDRegister_test_internal::fillVec (array, random);
766  copy (a, array);
767  copy (b, array);
768 
769  u.expect (a == b);
770  u.expect (! (a != b));
771 
772  SIMDRegister_test_internal::fillVec (array, random);
773  copy (b, array);
774 
775  u.expect (a != b);
776  u.expect (! (a == b));
777  }
778  };
779 
780  //==============================================================================
781  template <class TheTest>
782  void runTestFloatingPoint (const char* unitTestName, TheTest)
783  {
784  beginTest (unitTestName);
785 
786  Random random = getRandom();
787 
788  TheTest::run (*this, random, Tag<float> {});
789  TheTest::run (*this, random, Tag<double>{});
790  }
791 
792  //==============================================================================
793  template <class TheTest>
794  void runTestForAllTypes (const char* unitTestName, TheTest)
795  {
796  beginTest (unitTestName);
797 
798  Random random = getRandom();
799 
800  TheTest::run (*this, random, Tag<float> {});
801  TheTest::run (*this, random, Tag<double> {});
802  TheTest::run (*this, random, Tag<int8_t> {});
803  TheTest::run (*this, random, Tag<uint8_t> {});
804  TheTest::run (*this, random, Tag<int16_t> {});
805  TheTest::run (*this, random, Tag<uint16_t> {});
806  TheTest::run (*this, random, Tag<int32_t> {});
807  TheTest::run (*this, random, Tag<uint32_t> {});
808  TheTest::run (*this, random, Tag<int64_t> {});
809  TheTest::run (*this, random, Tag<uint64_t> {});
810  TheTest::run (*this, random, Tag<std::complex<float>> {});
811  TheTest::run (*this, random, Tag<std::complex<double>>{});
812  }
813 
814  template <class TheTest>
815  void runTestNonComplex (const char* unitTestName, TheTest)
816  {
817  beginTest (unitTestName);
818 
819  Random random = getRandom();
820 
821  TheTest::run (*this, random, Tag<float> {});
822  TheTest::run (*this, random, Tag<double> {});
823  TheTest::run (*this, random, Tag<int8_t> {});
824  TheTest::run (*this, random, Tag<uint8_t> {});
825  TheTest::run (*this, random, Tag<int16_t> {});
826  TheTest::run (*this, random, Tag<uint16_t>{});
827  TheTest::run (*this, random, Tag<int32_t> {});
828  TheTest::run (*this, random, Tag<uint32_t>{});
829  TheTest::run (*this, random, Tag<int64_t> {});
830  TheTest::run (*this, random, Tag<uint64_t>{});
831  }
832 
833  template <class TheTest>
834  void runTestSigned (const char* unitTestName, TheTest)
835  {
836  beginTest (unitTestName);
837 
838  Random random = getRandom();
839 
840  TheTest::run (*this, random, Tag<float> {});
841  TheTest::run (*this, random, Tag<double> {});
842  TheTest::run (*this, random, Tag<int8_t> {});
843  TheTest::run (*this, random, Tag<int16_t>{});
844  TheTest::run (*this, random, Tag<int32_t>{});
845  TheTest::run (*this, random, Tag<int64_t>{});
846  }
847 
848  void runTest() override
849  {
850  runTestForAllTypes ("InitializationTest", InitializationTest{});
851 
852  runTestForAllTypes ("AccessTest", AccessTest{});
853 
854  runTestForAllTypes ("AdditionOperators", OperatorTests<Addition>{});
855  runTestForAllTypes ("SubtractionOperators", OperatorTests<Subtraction>{});
856  runTestForAllTypes ("MultiplicationOperators", OperatorTests<Multiplication>{});
857 
858  runTestForAllTypes ("BitANDOperators", BitOperatorTests<BitAND>{});
859  runTestForAllTypes ("BitOROperators", BitOperatorTests<BitOR>{});
860  runTestForAllTypes ("BitXOROperators", BitOperatorTests<BitXOR>{});
861 
862  runTestNonComplex ("CheckComparisons", CheckComparisonOps{});
863  runTestNonComplex ("CheckBoolEquals", CheckBoolEquals{});
864  runTestNonComplex ("CheckMinMax", CheckMinMax{});
865 
866  runTestForAllTypes ("CheckMultiplyAdd", CheckMultiplyAdd{});
867  runTestForAllTypes ("CheckSum", CheckSum{});
868 
869  runTestSigned ("CheckAbs", CheckAbs{});
870 
871  runTestFloatingPoint ("CheckTruncate", CheckTruncate{});
872  }
873 };
874 
875 static SIMDRegisterUnitTests SIMDRegisterUnitTests;
876 
877 } // namespace juce::dsp
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
Random getRandom() const
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate(SIMDRegister a) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE expand(ElementType s) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE max(SIMDRegister a, SIMDRegister b) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual(SIMDRegister a, SIMDRegister b) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan(SIMDRegister a, SIMDRegister b) noexcept
static bool isSIMDAligned(const ElementType *ptr) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE abs(SIMDRegister a) noexcept
SIMDRegister< MaskType > vMaskType
static vMaskType JUCE_VECTOR_CALLTYPE equal(SIMDRegister a, SIMDRegister b) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd(SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE lessThan(SIMDRegister a, SIMDRegister b) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE min(SIMDRegister a, SIMDRegister b) noexcept
static constexpr size_t SIMDNumElements
SIMDInternal::MaskType< ElementType > MaskType
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual(SIMDRegister a, SIMDRegister b) noexcept
static ElementType * getNextSIMDAlignedPtr(ElementType *ptr) noexcept
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray(const ElementType *a) noexcept
static vMaskType JUCE_VECTOR_CALLTYPE notEqual(SIMDRegister a, SIMDRegister b) noexcept