• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // -*- C++ -*-
2 //===-- utils.h -----------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // File contains common utilities that tests rely on
11 
12 // Do not #include <algorithm>, because if we do we will not detect accidental dependencies.
13 #include <atomic>
14 #include <cstdint>
15 #include <cstdlib>
16 #include <cstring>
17 #include <iostream>
18 #include <iterator>
19 #include <memory>
20 #include <sstream>
21 #include <vector>
22 
23 #include "pstl_test_config.h"
24 
25 namespace TestUtils
26 {
27 
28 typedef double float64_t;
29 typedef float float32_t;
30 
31 template <class T, std::size_t N>
32 constexpr size_t
const_size(const T (&)[N])33 const_size(const T (&)[N]) noexcept
34 {
35     return N;
36 }
37 
38 template <typename T>
39 class Sequence;
40 
41 // Handy macros for error reporting
42 #define EXPECT_TRUE(condition, message) ::TestUtils::expect(true, condition, __FILE__, __LINE__, message)
43 #define EXPECT_FALSE(condition, message) ::TestUtils::expect(false, condition, __FILE__, __LINE__, message)
44 
45 // Check that expected and actual are equal and have the same type.
46 #define EXPECT_EQ(expected, actual, message) ::TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
47 
48 // Check that sequences started with expected and actual and have had size n are equal and have the same type.
49 #define EXPECT_EQ_N(expected, actual, n, message)                                                                      \
50     ::TestUtils::expect_equal(expected, actual, n, __FILE__, __LINE__, message)
51 
52 // Issue error message from outstr, adding a newline.
53 // Real purpose of this routine is to have a place to hang a breakpoint.
54 inline void
issue_error_message(std::stringstream & outstr)55 issue_error_message(std::stringstream& outstr)
56 {
57     outstr << std::endl;
58     std::cerr << outstr.str();
59     std::exit(EXIT_FAILURE);
60 }
61 
62 inline void
expect(bool expected,bool condition,const char * file,int32_t line,const char * message)63 expect(bool expected, bool condition, const char* file, int32_t line, const char* message)
64 {
65     if (condition != expected)
66     {
67         std::stringstream outstr;
68         outstr << "error at " << file << ":" << line << " - " << message;
69         issue_error_message(outstr);
70     }
71 }
72 
73 // Do not change signature to const T&.
74 // Function must be able to detect const differences between expected and actual.
75 template <typename T>
76 void
expect_equal(T & expected,T & actual,const char * file,int32_t line,const char * message)77 expect_equal(T& expected, T& actual, const char* file, int32_t line, const char* message)
78 {
79     if (!(expected == actual))
80     {
81         std::stringstream outstr;
82         outstr << "error at " << file << ":" << line << " - " << message << ", expected " << expected << " got "
83                << actual;
84         issue_error_message(outstr);
85     }
86 }
87 
88 template <typename T>
89 void
expect_equal(Sequence<T> & expected,Sequence<T> & actual,const char * file,int32_t line,const char * message)90 expect_equal(Sequence<T>& expected, Sequence<T>& actual, const char* file, int32_t line, const char* message)
91 {
92     size_t n = expected.size();
93     size_t m = actual.size();
94     if (n != m)
95     {
96         std::stringstream outstr;
97         outstr << "error at " << file << ":" << line << " - " << message << ", expected sequence of size " << n
98                << " got sequence of size " << m;
99         issue_error_message(outstr);
100         return;
101     }
102     size_t error_count = 0;
103     for (size_t k = 0; k < n && error_count < 10; ++k)
104     {
105         if (!(expected[k] == actual[k]))
106         {
107             std::stringstream outstr;
108             outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k << " expected "
109                    << expected[k] << " got " << actual[k];
110             issue_error_message(outstr);
111             ++error_count;
112         }
113     }
114 }
115 
116 template <typename Iterator1, typename Iterator2, typename Size>
117 void
expect_equal(Iterator1 expected_first,Iterator2 actual_first,Size n,const char * file,int32_t line,const char * message)118 expect_equal(Iterator1 expected_first, Iterator2 actual_first, Size n, const char* file, int32_t line,
119              const char* message)
120 {
121     size_t error_count = 0;
122     for (Size k = 0; k < n && error_count < 10; ++k, ++expected_first, ++actual_first)
123     {
124         if (!(*expected_first == *actual_first))
125         {
126             std::stringstream outstr;
127             outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k;
128             issue_error_message(outstr);
129             ++error_count;
130         }
131     }
132 }
133 
134 // ForwardIterator is like type Iterator, but restricted to be a forward iterator.
135 // Only the forward iterator signatures that are necessary for tests are present.
136 // Post-increment in particular is deliberatly omitted since our templates should avoid using it
137 // because of efficiency considerations.
138 template <typename Iterator, typename IteratorTag>
139 class ForwardIterator
140 {
141   public:
142     typedef IteratorTag iterator_category;
143     typedef typename std::iterator_traits<Iterator>::value_type value_type;
144     typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
145     typedef typename std::iterator_traits<Iterator>::pointer pointer;
146     typedef typename std::iterator_traits<Iterator>::reference reference;
147 
148   protected:
149     Iterator my_iterator;
150     typedef value_type element_type;
151 
152   public:
153     ForwardIterator() = default;
ForwardIterator(Iterator i)154     explicit ForwardIterator(Iterator i) : my_iterator(i) {}
155     reference operator*() const { return *my_iterator; }
156     Iterator operator->() const { return my_iterator; }
157     ForwardIterator
158     operator++()
159     {
160         ++my_iterator;
161         return *this;
162     }
163     ForwardIterator operator++(int32_t)
164     {
165         auto retval = *this;
166         my_iterator++;
167         return retval;
168     }
169     friend bool
170     operator==(const ForwardIterator& i, const ForwardIterator& j)
171     {
172         return i.my_iterator == j.my_iterator;
173     }
174     friend bool
175     operator!=(const ForwardIterator& i, const ForwardIterator& j)
176     {
177         return i.my_iterator != j.my_iterator;
178     }
179 
180     Iterator
iterator()181     iterator() const
182     {
183         return my_iterator;
184     }
185 };
186 
187 template <typename Iterator, typename IteratorTag>
188 class BidirectionalIterator : public ForwardIterator<Iterator, IteratorTag>
189 {
190     typedef ForwardIterator<Iterator, IteratorTag> base_type;
191 
192   public:
193     BidirectionalIterator() = default;
BidirectionalIterator(Iterator i)194     explicit BidirectionalIterator(Iterator i) : base_type(i) {}
BidirectionalIterator(const base_type & i)195     BidirectionalIterator(const base_type& i) : base_type(i.iterator()) {}
196 
197     BidirectionalIterator
198     operator++()
199     {
200         ++base_type::my_iterator;
201         return *this;
202     }
203     BidirectionalIterator
204     operator--()
205     {
206         --base_type::my_iterator;
207         return *this;
208     }
209     BidirectionalIterator operator++(int32_t)
210     {
211         auto retval = *this;
212         base_type::my_iterator++;
213         return retval;
214     }
215     BidirectionalIterator operator--(int32_t)
216     {
217         auto retval = *this;
218         base_type::my_iterator--;
219         return retval;
220     }
221 };
222 
223 template <typename Iterator, typename F>
224 void
fill_data(Iterator first,Iterator last,F f)225 fill_data(Iterator first, Iterator last, F f)
226 {
227     typedef typename std::iterator_traits<Iterator>::value_type T;
228     for (std::size_t i = 0; first != last; ++first, ++i)
229     {
230         *first = T(f(i));
231     }
232 }
233 
234 struct MemoryChecker {
235     // static counters and state tags
236     static std::atomic<std::int64_t> alive_object_counter; // initialized outside
237     static constexpr std::int64_t alive_state = 0xAAAAAAAAAAAAAAAA;
238     static constexpr std::int32_t dead_state = 0; // only used as a set value to cancel alive_state
239 
240     std::int32_t _value; // object value used for algorithms
241     std::int64_t _state; // state tag used for checks
242 
243     // ctors, dtors, assign ops
_valueMemoryChecker244     explicit MemoryChecker(std::int32_t value = 0) : _value(value) {
245         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since we cannot guarantee that
246         // raw memory for object being constructed does not have a bit sequence being equal to alive_state
247 
248         // set constructed state and increment counter for living object
249         inc_alive_objects();
250         _state = alive_state;
251     }
MemoryCheckerMemoryChecker252     MemoryChecker(MemoryChecker&& other) : _value(other.value()) {
253         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
254         // compiler can optimize out the move ctor call that results in false positive failure
255         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(MemoryChecker&&): attemp to construct an object from non-existing object");
256         // set constructed state and increment counter for living object
257         inc_alive_objects();
258         _state = alive_state;
259     }
MemoryCheckerMemoryChecker260     MemoryChecker(const MemoryChecker& other) : _value(other.value()) {
261         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
262         // compiler can optimize out the copy ctor call that results in false positive failure
263         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(const MemoryChecker&): attemp to construct an object from non-existing object");
264         // set constructed state and increment counter for living object
265         inc_alive_objects();
266         _state = alive_state;
267     }
268     MemoryChecker& operator=(MemoryChecker&& other) {
269         // check if we do not assign over uninitialized memory
270         EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign to non-existing object");
271         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign from non-existing object");
272         // just assign new value, counter is the same, state is the same
273         _value = other.value();
274 
275         return *this;
276     }
277     MemoryChecker& operator=(const MemoryChecker& other) {
278         // check if we do not assign over uninitialized memory
279         EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign to non-existing object");
280         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign from non-existing object");
281         // just assign new value, counter is the same, state is the same
282         _value = other.value();
283 
284         return *this;
285     }
~MemoryCheckerMemoryChecker286     ~MemoryChecker() {
287         // check if we do not double destruct the object
288         EXPECT_TRUE(state() == alive_state, "wrong effect from ~MemoryChecker(): attemp to destroy non-existing object");
289         // set destructed state and decrement counter for living object
290         static_cast<volatile std::int64_t&>(_state) = dead_state;
291         dec_alive_objects();
292     }
293 
294     // getters
valueMemoryChecker295     std::int32_t value() const { return _value; }
stateMemoryChecker296     std::int64_t state() const { return _state; }
alive_objectsMemoryChecker297     static std::int32_t alive_objects() { return alive_object_counter.load(); }
298 private:
299     // setters
inc_alive_objectsMemoryChecker300     void inc_alive_objects() { alive_object_counter.fetch_add(1); }
dec_alive_objectsMemoryChecker301     void dec_alive_objects() { alive_object_counter.fetch_sub(1); }
302 };
303 
304 std::atomic<std::int64_t> MemoryChecker::alive_object_counter{0};
305 
306 std::ostream& operator<<(std::ostream& os, const MemoryChecker& val) { return (os << val.value()); }
307 bool operator==(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() == v2.value(); }
308 bool operator<(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() < v2.value(); }
309 
310 // Sequence<T> is a container of a sequence of T with lots of kinds of iterators.
311 // Prefixes on begin/end mean:
312 //      c = "const"
313 //      f = "forward"
314 // No prefix indicates non-const random-access iterator.
315 template <typename T>
316 class Sequence
317 {
318     std::vector<T> m_storage;
319 
320   public:
321     typedef typename std::vector<T>::iterator iterator;
322     typedef typename std::vector<T>::const_iterator const_iterator;
323     typedef ForwardIterator<iterator, std::forward_iterator_tag> forward_iterator;
324     typedef ForwardIterator<const_iterator, std::forward_iterator_tag> const_forward_iterator;
325 
326     typedef BidirectionalIterator<iterator, std::bidirectional_iterator_tag> bidirectional_iterator;
327     typedef BidirectionalIterator<const_iterator, std::bidirectional_iterator_tag> const_bidirectional_iterator;
328 
329     typedef T value_type;
Sequence(size_t size)330     explicit Sequence(size_t size) : m_storage(size) {}
331 
332     // Construct sequence [f(0), f(1), ... f(size-1)]
333     // f can rely on its invocations being sequential from 0 to size-1.
334     template <typename Func>
Sequence(size_t size,Func f)335     Sequence(size_t size, Func f)
336     {
337         m_storage.reserve(size);
338         // Use push_back because T might not have a default constructor
339         for (size_t k = 0; k < size; ++k)
340             m_storage.push_back(T(f(k)));
341     }
Sequence(const std::initializer_list<T> & data)342     Sequence(const std::initializer_list<T>& data) : m_storage(data) {}
343 
344     const_iterator
begin()345     begin() const
346     {
347         return m_storage.begin();
348     }
349     const_iterator
end()350     end() const
351     {
352         return m_storage.end();
353     }
354     iterator
begin()355     begin()
356     {
357         return m_storage.begin();
358     }
359     iterator
end()360     end()
361     {
362         return m_storage.end();
363     }
364     const_iterator
cbegin()365     cbegin() const
366     {
367         return m_storage.cbegin();
368     }
369     const_iterator
cend()370     cend() const
371     {
372         return m_storage.cend();
373     }
374     forward_iterator
fbegin()375     fbegin()
376     {
377         return forward_iterator(m_storage.begin());
378     }
379     forward_iterator
fend()380     fend()
381     {
382         return forward_iterator(m_storage.end());
383     }
384     const_forward_iterator
cfbegin()385     cfbegin() const
386     {
387         return const_forward_iterator(m_storage.cbegin());
388     }
389     const_forward_iterator
cfend()390     cfend() const
391     {
392         return const_forward_iterator(m_storage.cend());
393     }
394     const_forward_iterator
fbegin()395     fbegin() const
396     {
397         return const_forward_iterator(m_storage.cbegin());
398     }
399     const_forward_iterator
fend()400     fend() const
401     {
402         return const_forward_iterator(m_storage.cend());
403     }
404 
405     const_bidirectional_iterator
cbibegin()406     cbibegin() const
407     {
408         return const_bidirectional_iterator(m_storage.cbegin());
409     }
410     const_bidirectional_iterator
cbiend()411     cbiend() const
412     {
413         return const_bidirectional_iterator(m_storage.cend());
414     }
415 
416     bidirectional_iterator
bibegin()417     bibegin()
418     {
419         return bidirectional_iterator(m_storage.begin());
420     }
421     bidirectional_iterator
biend()422     biend()
423     {
424         return bidirectional_iterator(m_storage.end());
425     }
426 
427     std::size_t
size()428     size() const
429     {
430         return m_storage.size();
431     }
432     const T*
data()433     data() const
434     {
435         return m_storage.data();
436     }
437     typename std::vector<T>::reference operator[](size_t j) { return m_storage[j]; }
438     const T& operator[](size_t j) const { return m_storage[j]; }
439 
440     // Fill with given value
441     void
fill(const T & value)442     fill(const T& value)
443     {
444         for (size_t i = 0; i < m_storage.size(); i++)
445             m_storage[i] = value;
446     }
447 
448     void
449     print() const;
450 
451     template <typename Func>
452     void
fill(Func f)453     fill(Func f)
454     {
455         fill_data(m_storage.begin(), m_storage.end(), f);
456     }
457 };
458 
459 template <typename T>
460 void
print()461 Sequence<T>::print() const
462 {
463     std::cout << "size = " << size() << ": { ";
464     std::copy(begin(), end(), std::ostream_iterator<T>(std::cout, " "));
465     std::cout << " } " << std::endl;
466 }
467 
468 // Predicates for algorithms
469 template <typename DataType>
470 struct is_equal_to
471 {
is_equal_tois_equal_to472     is_equal_to(const DataType& expected) : m_expected(expected) {}
473     bool
operatoris_equal_to474     operator()(const DataType& actual) const
475     {
476         return actual == m_expected;
477     }
478 
479   private:
480     DataType m_expected;
481 };
482 
483 // Low-quality hash function, returns value between 0 and (1<<bits)-1
484 // Warning: low-order bits are quite predictable.
485 inline size_t
HashBits(size_t i,size_t bits)486 HashBits(size_t i, size_t bits)
487 {
488     size_t mask = bits >= 8 * sizeof(size_t) ? ~size_t(0) : (size_t(1) << bits) - 1;
489     return (424157 * i ^ 0x24aFa) & mask;
490 }
491 
492 // Stateful unary op
493 template <typename T, typename U>
494 class Complement
495 {
496     int32_t val;
497 
498   public:
Complement(T v)499     Complement(T v) : val(v) {}
500     U
operator()501     operator()(const T& x) const
502     {
503         return U(val - x);
504     }
505 };
506 
507 // Tag used to prevent accidental use of converting constructor, even if use is explicit.
508 struct OddTag
509 {
510 };
511 
512 class Sum;
513 
514 // Type with limited set of operations.  Not default-constructible.
515 // Only available operator is "==".
516 // Typically used as value type in tests.
517 class Number
518 {
519     int32_t value;
520     friend class Add;
521     friend class Sum;
522     friend class IsMultiple;
523     friend class Congruent;
524     friend Sum
525     operator+(const Sum& x, const Sum& y);
526 
527   public:
Number(int32_t val,OddTag)528     Number(int32_t val, OddTag) : value(val) {}
529     friend bool
530     operator==(const Number& x, const Number& y)
531     {
532         return x.value == y.value;
533     }
534     friend std::ostream&
535     operator<<(std::ostream& o, const Number& d)
536     {
537         return o << d.value;
538     }
539 };
540 
541 // Stateful predicate for Number.  Not default-constructible.
542 class IsMultiple
543 {
544     long modulus;
545 
546   public:
547     // True if x is multiple of modulus
548     bool
operator()549     operator()(Number x) const
550     {
551         return x.value % modulus == 0;
552     }
IsMultiple(long modulus_,OddTag)553     IsMultiple(long modulus_, OddTag) : modulus(modulus_) {}
554 };
555 
556 // Stateful equivalence-class predicate for Number.  Not default-constructible.
557 class Congruent
558 {
559     long modulus;
560 
561   public:
562     // True if x and y have same remainder for the given modulus.
563     // Note: this is not quite the same as "equivalent modulo modulus" when x and y have different
564     // sign, but nonetheless AreCongruent is still an equivalence relationship, which is all
565     // we need for testing.
566     bool
operator()567     operator()(Number x, Number y) const
568     {
569         return x.value % modulus == y.value % modulus;
570     }
Congruent(long modulus_,OddTag)571     Congruent(long modulus_, OddTag) : modulus(modulus_) {}
572 };
573 
574 // Stateful reduction operation for Number
575 class Add
576 {
577     long bias;
578 
579   public:
Add(OddTag)580     explicit Add(OddTag) : bias(1) {}
581     Number
operator()582     operator()(Number x, const Number& y)
583     {
584         return Number(x.value + y.value + (bias - 1), OddTag());
585     }
586 };
587 
588 // Class similar to Number, but has default constructor and +.
589 class Sum : public Number
590 {
591   public:
Sum()592     Sum() : Number(0, OddTag()) {}
Sum(long x,OddTag)593     Sum(long x, OddTag) : Number(x, OddTag()) {}
594     friend Sum
595     operator+(const Sum& x, const Sum& y)
596     {
597         return Sum(x.value + y.value, OddTag());
598     }
599 };
600 
601 // Type with limited set of operations, which includes an associative but not commutative operation.
602 // Not default-constructible.
603 // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
604 class MonoidElement
605 {
606     size_t a, b;
607 
608   public:
MonoidElement(size_t a_,size_t b_,OddTag)609     MonoidElement(size_t a_, size_t b_, OddTag) : a(a_), b(b_) {}
610     friend bool
611     operator==(const MonoidElement& x, const MonoidElement& y)
612     {
613         return x.a == y.a && x.b == y.b;
614     }
615     friend std::ostream&
616     operator<<(std::ostream& o, const MonoidElement& x)
617     {
618         return o << "[" << x.a << ".." << x.b << ")";
619     }
620     friend class AssocOp;
621 };
622 
623 // Stateful associative op for MonoidElement
624 // It's not really a monoid since the operation is not allowed for any two elements.
625 // But it's good enough for testing.
626 class AssocOp
627 {
628     unsigned c;
629 
630   public:
AssocOp(OddTag)631     explicit AssocOp(OddTag) : c(5) {}
632     MonoidElement
operator()633     operator()(const MonoidElement& x, const MonoidElement& y)
634     {
635         unsigned d = 5;
636         EXPECT_EQ(d, c, "state lost");
637         EXPECT_EQ(x.b, y.a, "commuted?");
638 
639         return MonoidElement(x.a, y.b, OddTag());
640     }
641 };
642 
643 // Multiplication of matrix is an associative but not commutative operation
644 // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
645 template <typename T>
646 struct Matrix2x2
647 {
648     T a[2][2];
Matrix2x2Matrix2x2649     Matrix2x2() : a{{1, 0}, {0, 1}} {}
Matrix2x2Matrix2x2650     Matrix2x2(T x, T y) : a{{0, x}, {x, y}} {}
651 #if !_PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN
Matrix2x2Matrix2x2652     Matrix2x2(const Matrix2x2& m) : a{{m.a[0][0], m.a[0][1]}, {m.a[1][0], m.a[1][1]}} {}
653     Matrix2x2&
654     operator=(const Matrix2x2& m)
655     {
656         a[0][0] = m.a[0][0], a[0][1] = m.a[0][1], a[1][0] = m.a[1][0], a[1][1] = m.a[1][1];
657         return *this;
658     }
659 #endif
660 };
661 
662 template <typename T>
663 bool
664 operator==(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
665 {
666     return left.a[0][0] == right.a[0][0] && left.a[0][1] == right.a[0][1] && left.a[1][0] == right.a[1][0] &&
667            left.a[1][1] == right.a[1][1];
668 }
669 
670 template <typename T>
671 Matrix2x2<T>
multiply_matrix(const Matrix2x2<T> & left,const Matrix2x2<T> & right)672 multiply_matrix(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
673 {
674     Matrix2x2<T> result;
675     for (int32_t i = 0; i < 2; ++i)
676     {
677         for (int32_t j = 0; j < 2; ++j)
678         {
679             result.a[i][j] = left.a[i][0] * right.a[0][j] + left.a[i][1] * right.a[1][j];
680         }
681     }
682     return result;
683 }
684 
685 //============================================================================
686 // Adapters for creating different types of iterators.
687 //
688 // In this block we implemented some adapters for creating differnet types of iterators.
689 // It's needed for extending the unit testing of Parallel STL algorithms.
690 // We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators.
691 // The input iterator should be const or non-const, non-reverse random access iterator.
692 // Iterator creates in "MakeIterator":
693 // firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator)
694 // then iterator is "packed" by "ReverseAdapter" (if it's possible)
695 // So, from input iterator we may create, for example, reverse bidirectional iterator.
696 // "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
697 
698 // Base adapter
699 template <typename Iterator>
700 struct BaseAdapter
701 {
702     typedef Iterator iterator_type;
703     iterator_type
operatorBaseAdapter704     operator()(Iterator it)
705     {
706         return it;
707     }
708 };
709 
710 // Check if the iterator is reverse iterator
711 // Note: it works only for iterators that created by std::reverse_iterator
712 template <typename NotReverseIterator>
713 struct isReverse : std::false_type
714 {
715 };
716 
717 template <typename Iterator>
718 struct isReverse<std::reverse_iterator<Iterator>> : std::true_type
719 {
720 };
721 
722 // Reverse adapter
723 template <typename Iterator, typename IsReverse>
724 struct ReverseAdapter
725 {
726     typedef std::reverse_iterator<Iterator> iterator_type;
727     iterator_type
728     operator()(Iterator it)
729     {
730 #if _PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT
731         return std::make_reverse_iterator(it);
732 #else
733         return iterator_type(it);
734 #endif
735     }
736 };
737 
738 // Non-reverse adapter
739 template <typename Iterator>
740 struct ReverseAdapter<Iterator, std::false_type> : BaseAdapter<Iterator>
741 {
742 };
743 
744 // Iterator adapter by type (by default std::random_access_iterator_tag)
745 template <typename Iterator, typename IteratorTag>
746 struct IteratorTypeAdapter : BaseAdapter<Iterator>
747 {
748 };
749 
750 // Iterator adapter for forward iterator
751 template <typename Iterator>
752 struct IteratorTypeAdapter<Iterator, std::forward_iterator_tag>
753 {
754     typedef ForwardIterator<Iterator, std::forward_iterator_tag> iterator_type;
755     iterator_type
756     operator()(Iterator it)
757     {
758         return iterator_type(it);
759     }
760 };
761 
762 // Iterator adapter for bidirectional iterator
763 template <typename Iterator>
764 struct IteratorTypeAdapter<Iterator, std::bidirectional_iterator_tag>
765 {
766     typedef BidirectionalIterator<Iterator, std::bidirectional_iterator_tag> iterator_type;
767     iterator_type
768     operator()(Iterator it)
769     {
770         return iterator_type(it);
771     }
772 };
773 
774 //For creating iterator with new type
775 template <typename InputIterator, typename IteratorTag, typename IsReverse>
776 struct MakeIterator
777 {
778     typedef IteratorTypeAdapter<InputIterator, IteratorTag> IterByType;
779     typedef ReverseAdapter<typename IterByType::iterator_type, IsReverse> ReverseIter;
780 
781     typename ReverseIter::iterator_type
782     operator()(InputIterator it)
783     {
784         return ReverseIter()(IterByType()(it));
785     }
786 };
787 
788 // Useful constant variables
789 constexpr std::size_t GuardSize = 5;
790 constexpr std::ptrdiff_t sizeLimit = 1000;
791 
792 template <typename Iter, typename Void = void> // local iterator_traits for non-iterators
793 struct iterator_traits_
794 {
795 };
796 
797 template <typename Iter> // For iterators
798 struct iterator_traits_<Iter,
799                         typename std::enable_if<!std::is_void<typename Iter::iterator_category>::value, void>::type>
800 {
801     typedef typename Iter::iterator_category iterator_category;
802 };
803 
804 template <typename T> // For pointers
805 struct iterator_traits_<T*>
806 {
807     typedef std::random_access_iterator_tag iterator_category;
808 };
809 
810 // is iterator Iter has tag Tag
811 template <typename Iter, typename Tag>
812 using is_same_iterator_category = std::is_same<typename iterator_traits_<Iter>::iterator_category, Tag>;
813 
814 // if we run with reverse or const iterators we shouldn't test the large range
815 template <typename IsReverse, typename IsConst>
816 struct invoke_if_
817 {
818     template <typename Op, typename... Rest>
819     void
820     operator()(bool is_allow, Op op, Rest&&... rest)
821     {
822         if (is_allow)
823             op(std::forward<Rest>(rest)...);
824     }
825 };
826 template <>
827 struct invoke_if_<std::false_type, std::false_type>
828 {
829     template <typename Op, typename... Rest>
830     void
831     operator()(bool, Op op, Rest&&... rest)
832     {
833         op(std::forward<Rest>(rest)...);
834     }
835 };
836 
837 // Base non_const_wrapper struct. It is used to distinguish non_const testcases
838 // from a regular one. For non_const testcases only compilation is checked.
839 struct non_const_wrapper
840 {
841 };
842 
843 // Generic wrapper to specify iterator type to execute callable Op on.
844 // The condition can be either positive(Op is executed only with IteratorTag)
845 // or negative(Op is executed with every type of iterators except IteratorTag)
846 template <typename Op, typename IteratorTag, bool IsPositiveCondition = true>
847 struct non_const_wrapper_tagged : non_const_wrapper
848 {
849     template <typename Policy, typename Iterator>
850     typename std::enable_if<IsPositiveCondition == is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
851     operator()(Policy&& exec, Iterator iter)
852     {
853         Op()(exec, iter);
854     }
855 
856     template <typename Policy, typename InputIterator, typename OutputIterator>
857     typename std::enable_if<IsPositiveCondition == is_same_iterator_category<OutputIterator, IteratorTag>::value,
858                             void>::type
859     operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter)
860     {
861         Op()(exec, input_iter, out_iter);
862     }
863 
864     template <typename Policy, typename Iterator>
865     typename std::enable_if<IsPositiveCondition != is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
866     operator()(Policy&&, Iterator)
867     {
868     }
869 
870     template <typename Policy, typename InputIterator, typename OutputIterator>
871     typename std::enable_if<IsPositiveCondition != is_same_iterator_category<OutputIterator, IteratorTag>::value,
872                             void>::type
873     operator()(Policy&&, InputIterator, OutputIterator)
874     {
875     }
876 };
877 
878 // These run_for_* structures specify with which types of iterators callable object Op
879 // should be executed.
880 template <typename Op>
881 struct run_for_rnd : non_const_wrapper_tagged<Op, std::random_access_iterator_tag>
882 {
883 };
884 
885 template <typename Op>
886 struct run_for_rnd_bi : non_const_wrapper_tagged<Op, std::forward_iterator_tag, false>
887 {
888 };
889 
890 template <typename Op>
891 struct run_for_rnd_fw : non_const_wrapper_tagged<Op, std::bidirectional_iterator_tag, false>
892 {
893 };
894 
895 // Invoker for different types of iterators.
896 template <typename IteratorTag, typename IsReverse>
897 struct iterator_invoker
898 {
899     template <typename Iterator>
900     using make_iterator = MakeIterator<Iterator, IteratorTag, IsReverse>;
901     template <typename Iterator>
902     using IsConst = typename std::is_const<
903         typename std::remove_pointer<typename std::iterator_traits<Iterator>::pointer>::type>::type;
904     template <typename Iterator>
905     using invoke_if = invoke_if_<IsReverse, IsConst<Iterator>>;
906 
907     // A single iterator version which is used for non_const testcases
908     template <typename Policy, typename Op, typename Iterator>
909     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
910                                 std::is_base_of<non_const_wrapper, Op>::value,
911                             void>::type
912     operator()(Policy&& exec, Op op, Iterator iter)
913     {
914         op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
915     }
916 
917     // A version with 2 iterators which is used for non_const testcases
918     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
919     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
920                                 std::is_base_of<non_const_wrapper, Op>::value,
921                             void>::type
922     operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
923     {
924         op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
925            make_iterator<OutputIterator>()(out_iter));
926     }
927 
928     template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
929     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
930     operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
931     {
932         invoke_if<Iterator>()(n <= sizeLimit, op, exec, make_iterator<Iterator>()(begin), n,
933                               std::forward<Rest>(rest)...);
934     }
935 
936     template <typename Policy, typename Op, typename Iterator, typename... Rest>
937     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
938                                 !std::is_base_of<non_const_wrapper, Op>::value,
939                             void>::type
940     operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
941     {
942         invoke_if<Iterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
943                               make_iterator<Iterator>()(inputBegin), make_iterator<Iterator>()(inputEnd),
944                               std::forward<Rest>(rest)...);
945     }
946 
947     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
948     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
949                             void>::type
950     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
951                Rest&&... rest)
952     {
953         invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
954                                    make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
955                                    make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...);
956     }
957 
958     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
959     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
960                             void>::type
961     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
962                OutputIterator outputEnd, Rest&&... rest)
963     {
964         invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
965                                    make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
966                                    make_iterator<OutputIterator>()(outputBegin),
967                                    make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
968     }
969 
970     template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
971               typename... Rest>
972     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
973                             void>::type
974     operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
975                InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
976     {
977         invoke_if<InputIterator1>()(
978             std::distance(inputBegin1, inputEnd1) <= sizeLimit, op, exec, make_iterator<InputIterator1>()(inputBegin1),
979             make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator2>()(inputBegin2),
980             make_iterator<InputIterator2>()(inputEnd2), make_iterator<OutputIterator>()(outputBegin),
981             make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
982     }
983 };
984 
985 // Invoker for reverse iterators only
986 // Note: if we run with reverse iterators we shouldn't test the large range
987 template <typename IteratorTag>
988 struct iterator_invoker<IteratorTag, /* IsReverse = */ std::true_type>
989 {
990 
991     template <typename Iterator>
992     using make_iterator = MakeIterator<Iterator, IteratorTag, std::true_type>;
993 
994     // A single iterator version which is used for non_const testcases
995     template <typename Policy, typename Op, typename Iterator>
996     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
997                                 std::is_base_of<non_const_wrapper, Op>::value,
998                             void>::type
999     operator()(Policy&& exec, Op op, Iterator iter)
1000     {
1001         op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
1002     }
1003 
1004     // A version with 2 iterators which is used for non_const testcases
1005     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
1006     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
1007                                 std::is_base_of<non_const_wrapper, Op>::value,
1008                             void>::type
1009     operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
1010     {
1011         op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
1012            make_iterator<OutputIterator>()(out_iter));
1013     }
1014 
1015     template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
1016     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
1017     operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
1018     {
1019         if (n <= sizeLimit)
1020             op(exec, make_iterator<Iterator>()(begin + n), n, std::forward<Rest>(rest)...);
1021     }
1022 
1023     template <typename Policy, typename Op, typename Iterator, typename... Rest>
1024     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
1025                                 !std::is_base_of<non_const_wrapper, Op>::value,
1026                             void>::type
1027     operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
1028     {
1029         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
1030             op(exec, make_iterator<Iterator>()(inputEnd), make_iterator<Iterator>()(inputBegin),
1031                std::forward<Rest>(rest)...);
1032     }
1033 
1034     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
1035     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
1036                             void>::type
1037     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
1038                Rest&&... rest)
1039     {
1040         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
1041             op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
1042                make_iterator<OutputIterator>()(outputBegin + (inputEnd - inputBegin)), std::forward<Rest>(rest)...);
1043     }
1044 
1045     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
1046     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
1047                             void>::type
1048     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
1049                OutputIterator outputEnd, Rest&&... rest)
1050     {
1051         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
1052             op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
1053                make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
1054                std::forward<Rest>(rest)...);
1055     }
1056 
1057     template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
1058               typename... Rest>
1059     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
1060                             void>::type
1061     operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
1062                InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
1063     {
1064         if (std::distance(inputBegin1, inputEnd1) <= sizeLimit)
1065             op(exec, make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator1>()(inputBegin1),
1066                make_iterator<InputIterator2>()(inputEnd2), make_iterator<InputIterator2>()(inputBegin2),
1067                make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
1068                std::forward<Rest>(rest)...);
1069     }
1070 };
1071 
1072 // We can't create reverse iterator from forward iterator
1073 template <>
1074 struct iterator_invoker<std::forward_iterator_tag, /*isReverse=*/std::true_type>
1075 {
1076     template <typename... Rest>
1077     void
1078     operator()(Rest&&...)
1079     {
1080     }
1081 };
1082 
1083 template <typename IsReverse>
1084 struct reverse_invoker
1085 {
1086     template <typename... Rest>
1087     void
1088     operator()(Rest&&... rest)
1089     {
1090         // Random-access iterator
1091         iterator_invoker<std::random_access_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
1092 
1093         // Forward iterator
1094         iterator_invoker<std::forward_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
1095 
1096         // Bidirectional iterator
1097         iterator_invoker<std::bidirectional_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
1098     }
1099 };
1100 
1101 struct invoke_on_all_iterator_types
1102 {
1103     template <typename... Rest>
1104     void
1105     operator()(Rest&&... rest)
1106     {
1107         reverse_invoker</* IsReverse = */ std::false_type>()(std::forward<Rest>(rest)...);
1108         reverse_invoker</* IsReverse = */ std::true_type>()(std::forward<Rest>(rest)...);
1109     }
1110 };
1111 //============================================================================
1112 
1113 // Invoke op(policy,rest...) for each possible policy.
1114 template <typename Op, typename... T>
1115 void
1116 invoke_on_all_policies(Op op, T&&... rest)
1117 {
1118     using namespace __pstl::execution;
1119 
1120     // Try static execution policies
1121     invoke_on_all_iterator_types()(seq, op, std::forward<T>(rest)...);
1122     invoke_on_all_iterator_types()(unseq, op, std::forward<T>(rest)...);
1123     invoke_on_all_iterator_types()(par, op, std::forward<T>(rest)...);
1124     invoke_on_all_iterator_types()(par_unseq, op, std::forward<T>(rest)...);
1125 }
1126 
1127 template <typename F>
1128 struct NonConstAdapter
1129 {
1130     F my_f;
1131     NonConstAdapter(const F& f) : my_f(f) {}
1132 
1133     template <typename... Types>
1134     auto
1135     operator()(Types&&... args) -> decltype(std::declval<F>().
1136                                             operator()(std::forward<Types>(args)...))
1137     {
1138         return my_f(std::forward<Types>(args)...);
1139     }
1140 };
1141 
1142 template <typename F>
1143 NonConstAdapter<F>
1144 non_const(const F& f)
1145 {
1146     return NonConstAdapter<F>(f);
1147 }
1148 
1149 // Wrapper for types. It's need for counting of constructing and destructing objects
1150 template <typename T>
1151 class Wrapper
1152 {
1153   public:
1154     Wrapper()
1155     {
1156         my_field = std::shared_ptr<T>(new T());
1157         ++my_count;
1158     }
1159     Wrapper(const T& input)
1160     {
1161         my_field = std::shared_ptr<T>(new T(input));
1162         ++my_count;
1163     }
1164     Wrapper(const Wrapper& input)
1165     {
1166         my_field = input.my_field;
1167         ++my_count;
1168     }
1169     Wrapper(Wrapper&& input)
1170     {
1171         my_field = input.my_field;
1172         input.my_field = nullptr;
1173         ++move_count;
1174     }
1175     Wrapper&
1176     operator=(const Wrapper& input)
1177     {
1178         my_field = input.my_field;
1179         return *this;
1180     }
1181     Wrapper&
1182     operator=(Wrapper&& input)
1183     {
1184         my_field = input.my_field;
1185         input.my_field = nullptr;
1186         ++move_count;
1187         return *this;
1188     }
1189     bool
1190     operator==(const Wrapper& input) const
1191     {
1192         return my_field == input.my_field;
1193     }
1194     bool
1195     operator<(const Wrapper& input) const
1196     {
1197         return *my_field < *input.my_field;
1198     }
1199     bool
1200     operator>(const Wrapper& input) const
1201     {
1202         return *my_field > *input.my_field;
1203     }
1204     friend std::ostream&
1205     operator<<(std::ostream& stream, const Wrapper& input)
1206     {
1207         return stream << *(input.my_field);
1208     }
1209     ~Wrapper()
1210     {
1211         --my_count;
1212         if (move_count > 0)
1213         {
1214             --move_count;
1215         }
1216     }
1217     T*
1218     get_my_field() const
1219     {
1220         return my_field.get();
1221     };
1222     static size_t
1223     Count()
1224     {
1225         return my_count;
1226     }
1227     static size_t
1228     MoveCount()
1229     {
1230         return move_count;
1231     }
1232     static void
1233     SetCount(const size_t& n)
1234     {
1235         my_count = n;
1236     }
1237     static void
1238     SetMoveCount(const size_t& n)
1239     {
1240         move_count = n;
1241     }
1242 
1243   private:
1244     static std::atomic<size_t> my_count;
1245     static std::atomic<size_t> move_count;
1246     std::shared_ptr<T> my_field;
1247 };
1248 
1249 template <typename T>
1250 std::atomic<size_t> Wrapper<T>::my_count = {0};
1251 
1252 template <typename T>
1253 std::atomic<size_t> Wrapper<T>::move_count = {0};
1254 
1255 template <typename InputIterator, typename T, typename BinaryOperation, typename UnaryOperation>
1256 T
1257 transform_reduce_serial(InputIterator first, InputIterator last, T init, BinaryOperation binary_op,
1258                         UnaryOperation unary_op) noexcept
1259 {
1260     for (; first != last; ++first)
1261     {
1262         init = binary_op(init, unary_op(*first));
1263     }
1264     return init;
1265 }
1266 
1267 static const char*
1268 done()
1269 {
1270 #if _PSTL_TEST_SUCCESSFUL_KEYWORD
1271     return "done";
1272 #else
1273     return "passed";
1274 #endif
1275 }
1276 
1277 // test_algo_basic_* functions are used to execute
1278 // f on a very basic sequence of elements of type T.
1279 
1280 // Should be used with unary predicate
1281 template <typename T, typename F>
1282 static void
1283 test_algo_basic_single(F&& f)
1284 {
1285     size_t N = 10;
1286     Sequence<T> in(N, [](size_t v) -> T { return T(v); });
1287 
1288     invoke_on_all_policies(f, in.begin());
1289 }
1290 
1291 // Should be used with binary predicate
1292 template <typename T, typename F>
1293 static void
1294 test_algo_basic_double(F&& f)
1295 {
1296     size_t N = 10;
1297     Sequence<T> in(N, [](size_t v) -> T { return T(v); });
1298     Sequence<T> out(N, [](size_t v) -> T { return T(v); });
1299 
1300     invoke_on_all_policies(f, in.begin(), out.begin());
1301 }
1302 
1303 template <typename Policy, typename F>
1304 static void
1305 invoke_if(Policy&&, F f)
1306 {
1307 #if _PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN || _PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN
1308     __pstl::__internal::invoke_if_not(__pstl::__internal::allow_unsequenced<Policy>(), f);
1309 #else
1310     f();
1311 #endif
1312 }
1313 
1314 } /* namespace TestUtils */
1315