xref: /llvm-project/pstl/test/support/utils.h (revision 843c12d6a0cdfd64c5a92e24eb58ba9ee17ca1ee)
13b62047bSLouis Dionne // -*- C++ -*-
23b62047bSLouis Dionne //===-- utils.h -----------------------------------------------------------===//
33b62047bSLouis Dionne //
43b62047bSLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
53b62047bSLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
63b62047bSLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
73b62047bSLouis Dionne //
83b62047bSLouis Dionne //===----------------------------------------------------------------------===//
93b62047bSLouis Dionne 
103b62047bSLouis Dionne // File contains common utilities that tests rely on
113b62047bSLouis Dionne 
123b62047bSLouis Dionne // Do not #include <algorithm>, because if we do we will not detect accidental dependencies.
133b62047bSLouis Dionne #include <atomic>
143b62047bSLouis Dionne #include <cstdint>
153b62047bSLouis Dionne #include <cstdlib>
163b62047bSLouis Dionne #include <cstring>
173b62047bSLouis Dionne #include <iostream>
183b62047bSLouis Dionne #include <iterator>
193b62047bSLouis Dionne #include <memory>
203b62047bSLouis Dionne #include <sstream>
213b62047bSLouis Dionne #include <vector>
223b62047bSLouis Dionne 
233b62047bSLouis Dionne #include "pstl_test_config.h"
243b62047bSLouis Dionne 
253b62047bSLouis Dionne namespace TestUtils
263b62047bSLouis Dionne {
273b62047bSLouis Dionne 
283b62047bSLouis Dionne typedef double float64_t;
293b62047bSLouis Dionne typedef float float32_t;
303b62047bSLouis Dionne 
313b62047bSLouis Dionne template <class T, std::size_t N>
323b62047bSLouis Dionne constexpr size_t
const_size(const T (&)[N])335c4c4431SLouis Dionne const_size(const T (&)[N]) noexcept
343b62047bSLouis Dionne {
353b62047bSLouis Dionne     return N;
363b62047bSLouis Dionne }
373b62047bSLouis Dionne 
383b62047bSLouis Dionne template <typename T>
393b62047bSLouis Dionne class Sequence;
403b62047bSLouis Dionne 
413b62047bSLouis Dionne // Handy macros for error reporting
423b62047bSLouis Dionne #define EXPECT_TRUE(condition, message) ::TestUtils::expect(true, condition, __FILE__, __LINE__, message)
433b62047bSLouis Dionne #define EXPECT_FALSE(condition, message) ::TestUtils::expect(false, condition, __FILE__, __LINE__, message)
443b62047bSLouis Dionne 
453b62047bSLouis Dionne // Check that expected and actual are equal and have the same type.
463b62047bSLouis Dionne #define EXPECT_EQ(expected, actual, message) ::TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
473b62047bSLouis Dionne 
483b62047bSLouis Dionne // Check that sequences started with expected and actual and have had size n are equal and have the same type.
493b62047bSLouis Dionne #define EXPECT_EQ_N(expected, actual, n, message)                                                                      \
503b62047bSLouis Dionne     ::TestUtils::expect_equal(expected, actual, n, __FILE__, __LINE__, message)
513b62047bSLouis Dionne 
523b62047bSLouis Dionne // Issue error message from outstr, adding a newline.
533b62047bSLouis Dionne // Real purpose of this routine is to have a place to hang a breakpoint.
543b62047bSLouis Dionne inline void
issue_error_message(std::stringstream & outstr)553b62047bSLouis Dionne issue_error_message(std::stringstream& outstr)
563b62047bSLouis Dionne {
573b62047bSLouis Dionne     outstr << std::endl;
583b62047bSLouis Dionne     std::cerr << outstr.str();
593b62047bSLouis Dionne     std::exit(EXIT_FAILURE);
603b62047bSLouis Dionne }
613b62047bSLouis Dionne 
623b62047bSLouis Dionne inline void
expect(bool expected,bool condition,const char * file,int32_t line,const char * message)633b62047bSLouis Dionne expect(bool expected, bool condition, const char* file, int32_t line, const char* message)
643b62047bSLouis Dionne {
653b62047bSLouis Dionne     if (condition != expected)
663b62047bSLouis Dionne     {
673b62047bSLouis Dionne         std::stringstream outstr;
683b62047bSLouis Dionne         outstr << "error at " << file << ":" << line << " - " << message;
693b62047bSLouis Dionne         issue_error_message(outstr);
703b62047bSLouis Dionne     }
713b62047bSLouis Dionne }
723b62047bSLouis Dionne 
733b62047bSLouis Dionne // Do not change signature to const T&.
743b62047bSLouis Dionne // Function must be able to detect const differences between expected and actual.
753b62047bSLouis Dionne template <typename T>
763b62047bSLouis Dionne void
expect_equal(T & expected,T & actual,const char * file,int32_t line,const char * message)773b62047bSLouis Dionne expect_equal(T& expected, T& actual, const char* file, int32_t line, const char* message)
783b62047bSLouis Dionne {
793b62047bSLouis Dionne     if (!(expected == actual))
803b62047bSLouis Dionne     {
813b62047bSLouis Dionne         std::stringstream outstr;
823b62047bSLouis Dionne         outstr << "error at " << file << ":" << line << " - " << message << ", expected " << expected << " got "
833b62047bSLouis Dionne                << actual;
843b62047bSLouis Dionne         issue_error_message(outstr);
853b62047bSLouis Dionne     }
863b62047bSLouis Dionne }
873b62047bSLouis Dionne 
883b62047bSLouis Dionne template <typename T>
893b62047bSLouis Dionne void
expect_equal(Sequence<T> & expected,Sequence<T> & actual,const char * file,int32_t line,const char * message)903b62047bSLouis Dionne expect_equal(Sequence<T>& expected, Sequence<T>& actual, const char* file, int32_t line, const char* message)
913b62047bSLouis Dionne {
923b62047bSLouis Dionne     size_t n = expected.size();
933b62047bSLouis Dionne     size_t m = actual.size();
943b62047bSLouis Dionne     if (n != m)
953b62047bSLouis Dionne     {
963b62047bSLouis Dionne         std::stringstream outstr;
973b62047bSLouis Dionne         outstr << "error at " << file << ":" << line << " - " << message << ", expected sequence of size " << n
983b62047bSLouis Dionne                << " got sequence of size " << m;
993b62047bSLouis Dionne         issue_error_message(outstr);
1003b62047bSLouis Dionne         return;
1013b62047bSLouis Dionne     }
1023b62047bSLouis Dionne     size_t error_count = 0;
1033b62047bSLouis Dionne     for (size_t k = 0; k < n && error_count < 10; ++k)
1043b62047bSLouis Dionne     {
1053b62047bSLouis Dionne         if (!(expected[k] == actual[k]))
1063b62047bSLouis Dionne         {
1073b62047bSLouis Dionne             std::stringstream outstr;
1083b62047bSLouis Dionne             outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k << " expected "
1093b62047bSLouis Dionne                    << expected[k] << " got " << actual[k];
1103b62047bSLouis Dionne             issue_error_message(outstr);
1113b62047bSLouis Dionne             ++error_count;
1123b62047bSLouis Dionne         }
1133b62047bSLouis Dionne     }
1143b62047bSLouis Dionne }
1153b62047bSLouis Dionne 
1163b62047bSLouis Dionne template <typename Iterator1, typename Iterator2, typename Size>
1173b62047bSLouis Dionne void
expect_equal(Iterator1 expected_first,Iterator2 actual_first,Size n,const char * file,int32_t line,const char * message)1183b62047bSLouis Dionne expect_equal(Iterator1 expected_first, Iterator2 actual_first, Size n, const char* file, int32_t line,
1193b62047bSLouis Dionne              const char* message)
1203b62047bSLouis Dionne {
1213b62047bSLouis Dionne     size_t error_count = 0;
1225c4c4431SLouis Dionne     for (Size k = 0; k < n && error_count < 10; ++k, ++expected_first, ++actual_first)
1233b62047bSLouis Dionne     {
1243b62047bSLouis Dionne         if (!(*expected_first == *actual_first))
1253b62047bSLouis Dionne         {
1263b62047bSLouis Dionne             std::stringstream outstr;
1273b62047bSLouis Dionne             outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k;
1283b62047bSLouis Dionne             issue_error_message(outstr);
1293b62047bSLouis Dionne             ++error_count;
1303b62047bSLouis Dionne         }
1313b62047bSLouis Dionne     }
1323b62047bSLouis Dionne }
1333b62047bSLouis Dionne 
1343b62047bSLouis Dionne // ForwardIterator is like type Iterator, but restricted to be a forward iterator.
1353b62047bSLouis Dionne // Only the forward iterator signatures that are necessary for tests are present.
1363b62047bSLouis Dionne // Post-increment in particular is deliberatly omitted since our templates should avoid using it
1373b62047bSLouis Dionne // because of efficiency considerations.
1383b62047bSLouis Dionne template <typename Iterator, typename IteratorTag>
1393b62047bSLouis Dionne class ForwardIterator
1403b62047bSLouis Dionne {
1413b62047bSLouis Dionne   public:
1423b62047bSLouis Dionne     typedef IteratorTag iterator_category;
1433b62047bSLouis Dionne     typedef typename std::iterator_traits<Iterator>::value_type value_type;
1443b62047bSLouis Dionne     typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
1453b62047bSLouis Dionne     typedef typename std::iterator_traits<Iterator>::pointer pointer;
1463b62047bSLouis Dionne     typedef typename std::iterator_traits<Iterator>::reference reference;
1473b62047bSLouis Dionne 
1483b62047bSLouis Dionne   protected:
1493b62047bSLouis Dionne     Iterator my_iterator;
1503b62047bSLouis Dionne     typedef value_type element_type;
1513b62047bSLouis Dionne 
1523b62047bSLouis Dionne   public:
1533b62047bSLouis Dionne     ForwardIterator() = default;
ForwardIterator(Iterator i)1543b62047bSLouis Dionne     explicit ForwardIterator(Iterator i) : my_iterator(i) {}
1553b62047bSLouis Dionne     reference operator*() const { return *my_iterator; }
1563b62047bSLouis Dionne     Iterator operator->() const { return my_iterator; }
1573b62047bSLouis Dionne     ForwardIterator
1583b62047bSLouis Dionne     operator++()
1593b62047bSLouis Dionne     {
1603b62047bSLouis Dionne         ++my_iterator;
1613b62047bSLouis Dionne         return *this;
1623b62047bSLouis Dionne     }
1633b62047bSLouis Dionne     ForwardIterator operator++(int32_t)
1643b62047bSLouis Dionne     {
1653b62047bSLouis Dionne         auto retval = *this;
1663b62047bSLouis Dionne         my_iterator++;
1673b62047bSLouis Dionne         return retval;
1683b62047bSLouis Dionne     }
1693b62047bSLouis Dionne     friend bool
1703b62047bSLouis Dionne     operator==(const ForwardIterator& i, const ForwardIterator& j)
1713b62047bSLouis Dionne     {
1723b62047bSLouis Dionne         return i.my_iterator == j.my_iterator;
1733b62047bSLouis Dionne     }
1743b62047bSLouis Dionne     friend bool
1753b62047bSLouis Dionne     operator!=(const ForwardIterator& i, const ForwardIterator& j)
1763b62047bSLouis Dionne     {
1773b62047bSLouis Dionne         return i.my_iterator != j.my_iterator;
1783b62047bSLouis Dionne     }
1793b62047bSLouis Dionne 
1803b62047bSLouis Dionne     Iterator
iterator()1813b62047bSLouis Dionne     iterator() const
1823b62047bSLouis Dionne     {
1833b62047bSLouis Dionne         return my_iterator;
1843b62047bSLouis Dionne     }
1853b62047bSLouis Dionne };
1863b62047bSLouis Dionne 
1873b62047bSLouis Dionne template <typename Iterator, typename IteratorTag>
1883b62047bSLouis Dionne class BidirectionalIterator : public ForwardIterator<Iterator, IteratorTag>
1893b62047bSLouis Dionne {
1903b62047bSLouis Dionne     typedef ForwardIterator<Iterator, IteratorTag> base_type;
1913b62047bSLouis Dionne 
1923b62047bSLouis Dionne   public:
1933b62047bSLouis Dionne     BidirectionalIterator() = default;
BidirectionalIterator(Iterator i)1943b62047bSLouis Dionne     explicit BidirectionalIterator(Iterator i) : base_type(i) {}
BidirectionalIterator(const base_type & i)1953b62047bSLouis Dionne     BidirectionalIterator(const base_type& i) : base_type(i.iterator()) {}
1963b62047bSLouis Dionne 
1973b62047bSLouis Dionne     BidirectionalIterator
1983b62047bSLouis Dionne     operator++()
1993b62047bSLouis Dionne     {
2003b62047bSLouis Dionne         ++base_type::my_iterator;
2013b62047bSLouis Dionne         return *this;
2023b62047bSLouis Dionne     }
2033b62047bSLouis Dionne     BidirectionalIterator
2043b62047bSLouis Dionne     operator--()
2053b62047bSLouis Dionne     {
2063b62047bSLouis Dionne         --base_type::my_iterator;
2073b62047bSLouis Dionne         return *this;
2083b62047bSLouis Dionne     }
2093b62047bSLouis Dionne     BidirectionalIterator operator++(int32_t)
2103b62047bSLouis Dionne     {
2113b62047bSLouis Dionne         auto retval = *this;
2123b62047bSLouis Dionne         base_type::my_iterator++;
2133b62047bSLouis Dionne         return retval;
2143b62047bSLouis Dionne     }
2153b62047bSLouis Dionne     BidirectionalIterator operator--(int32_t)
2163b62047bSLouis Dionne     {
2173b62047bSLouis Dionne         auto retval = *this;
2183b62047bSLouis Dionne         base_type::my_iterator--;
2193b62047bSLouis Dionne         return retval;
2203b62047bSLouis Dionne     }
2213b62047bSLouis Dionne };
2223b62047bSLouis Dionne 
2233b62047bSLouis Dionne template <typename Iterator, typename F>
2243b62047bSLouis Dionne void
fill_data(Iterator first,Iterator last,F f)2253b62047bSLouis Dionne fill_data(Iterator first, Iterator last, F f)
2263b62047bSLouis Dionne {
2273b62047bSLouis Dionne     typedef typename std::iterator_traits<Iterator>::value_type T;
2283b62047bSLouis Dionne     for (std::size_t i = 0; first != last; ++first, ++i)
2293b62047bSLouis Dionne     {
2303b62047bSLouis Dionne         *first = T(f(i));
2313b62047bSLouis Dionne     }
2323b62047bSLouis Dionne }
2333b62047bSLouis Dionne 
2345b0502dfSDvorskiy, Mikhail struct MemoryChecker {
2355b0502dfSDvorskiy, Mikhail     // static counters and state tags
2365b0502dfSDvorskiy, Mikhail     static std::atomic<std::int64_t> alive_object_counter; // initialized outside
2375b0502dfSDvorskiy, Mikhail     static constexpr std::int64_t alive_state = 0xAAAAAAAAAAAAAAAA;
2385b0502dfSDvorskiy, Mikhail     static constexpr std::int32_t dead_state = 0; // only used as a set value to cancel alive_state
2395b0502dfSDvorskiy, Mikhail 
2405b0502dfSDvorskiy, Mikhail     std::int32_t _value; // object value used for algorithms
2415b0502dfSDvorskiy, Mikhail     std::int64_t _state; // state tag used for checks
2425b0502dfSDvorskiy, Mikhail 
2435b0502dfSDvorskiy, Mikhail     // ctors, dtors, assign ops
_valueMemoryChecker2445b0502dfSDvorskiy, Mikhail     explicit MemoryChecker(std::int32_t value = 0) : _value(value) {
2455b0502dfSDvorskiy, Mikhail         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since we cannot guarantee that
2465b0502dfSDvorskiy, Mikhail         // raw memory for object being constructed does not have a bit sequence being equal to alive_state
2475b0502dfSDvorskiy, Mikhail 
2485b0502dfSDvorskiy, Mikhail         // set constructed state and increment counter for living object
2495b0502dfSDvorskiy, Mikhail         inc_alive_objects();
2505b0502dfSDvorskiy, Mikhail         _state = alive_state;
2515b0502dfSDvorskiy, Mikhail     }
MemoryCheckerMemoryChecker2525b0502dfSDvorskiy, Mikhail     MemoryChecker(MemoryChecker&& other) : _value(other.value()) {
2535b0502dfSDvorskiy, Mikhail         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
2545b0502dfSDvorskiy, Mikhail         // compiler can optimize out the move ctor call that results in false positive failure
2555b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(MemoryChecker&&): attemp to construct an object from non-existing object");
2565b0502dfSDvorskiy, Mikhail         // set constructed state and increment counter for living object
2575b0502dfSDvorskiy, Mikhail         inc_alive_objects();
2585b0502dfSDvorskiy, Mikhail         _state = alive_state;
2595b0502dfSDvorskiy, Mikhail     }
MemoryCheckerMemoryChecker2605b0502dfSDvorskiy, Mikhail     MemoryChecker(const MemoryChecker& other) : _value(other.value()) {
2615b0502dfSDvorskiy, Mikhail         // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
2625b0502dfSDvorskiy, Mikhail         // compiler can optimize out the copy ctor call that results in false positive failure
2635b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(const MemoryChecker&): attemp to construct an object from non-existing object");
2645b0502dfSDvorskiy, Mikhail         // set constructed state and increment counter for living object
2655b0502dfSDvorskiy, Mikhail         inc_alive_objects();
2665b0502dfSDvorskiy, Mikhail         _state = alive_state;
2675b0502dfSDvorskiy, Mikhail     }
2685b0502dfSDvorskiy, Mikhail     MemoryChecker& operator=(MemoryChecker&& other) {
2695b0502dfSDvorskiy, Mikhail         // check if we do not assign over uninitialized memory
2705b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign to non-existing object");
2715b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign from non-existing object");
2725b0502dfSDvorskiy, Mikhail         // just assign new value, counter is the same, state is the same
2735b0502dfSDvorskiy, Mikhail         _value = other.value();
2745b0502dfSDvorskiy, Mikhail 
2755b0502dfSDvorskiy, Mikhail         return *this;
2765b0502dfSDvorskiy, Mikhail     }
2775b0502dfSDvorskiy, Mikhail     MemoryChecker& operator=(const MemoryChecker& other) {
2785b0502dfSDvorskiy, Mikhail         // check if we do not assign over uninitialized memory
2795b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign to non-existing object");
2805b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign from non-existing object");
2815b0502dfSDvorskiy, Mikhail         // just assign new value, counter is the same, state is the same
2825b0502dfSDvorskiy, Mikhail         _value = other.value();
2835b0502dfSDvorskiy, Mikhail 
2845b0502dfSDvorskiy, Mikhail         return *this;
2855b0502dfSDvorskiy, Mikhail     }
~MemoryCheckerMemoryChecker2865b0502dfSDvorskiy, Mikhail     ~MemoryChecker() {
2875b0502dfSDvorskiy, Mikhail         // check if we do not double destruct the object
2885b0502dfSDvorskiy, Mikhail         EXPECT_TRUE(state() == alive_state, "wrong effect from ~MemoryChecker(): attemp to destroy non-existing object");
2895b0502dfSDvorskiy, Mikhail         // set destructed state and decrement counter for living object
2905b0502dfSDvorskiy, Mikhail         static_cast<volatile std::int64_t&>(_state) = dead_state;
2915b0502dfSDvorskiy, Mikhail         dec_alive_objects();
2925b0502dfSDvorskiy, Mikhail     }
2935b0502dfSDvorskiy, Mikhail 
2945b0502dfSDvorskiy, Mikhail     // getters
valueMemoryChecker2955b0502dfSDvorskiy, Mikhail     std::int32_t value() const { return _value; }
stateMemoryChecker2965b0502dfSDvorskiy, Mikhail     std::int64_t state() const { return _state; }
alive_objectsMemoryChecker2975b0502dfSDvorskiy, Mikhail     static std::int32_t alive_objects() { return alive_object_counter.load(); }
2985b0502dfSDvorskiy, Mikhail private:
2995b0502dfSDvorskiy, Mikhail     // setters
inc_alive_objectsMemoryChecker3005b0502dfSDvorskiy, Mikhail     void inc_alive_objects() { alive_object_counter.fetch_add(1); }
dec_alive_objectsMemoryChecker3015b0502dfSDvorskiy, Mikhail     void dec_alive_objects() { alive_object_counter.fetch_sub(1); }
3025b0502dfSDvorskiy, Mikhail };
3035b0502dfSDvorskiy, Mikhail 
3045b0502dfSDvorskiy, Mikhail std::atomic<std::int64_t> MemoryChecker::alive_object_counter{0};
3055b0502dfSDvorskiy, Mikhail 
3065b0502dfSDvorskiy, Mikhail std::ostream& operator<<(std::ostream& os, const MemoryChecker& val) { return (os << val.value()); }
3075b0502dfSDvorskiy, Mikhail bool operator==(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() == v2.value(); }
3085b0502dfSDvorskiy, Mikhail bool operator<(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() < v2.value(); }
3095b0502dfSDvorskiy, Mikhail 
3103b62047bSLouis Dionne // Sequence<T> is a container of a sequence of T with lots of kinds of iterators.
3113b62047bSLouis Dionne // Prefixes on begin/end mean:
3123b62047bSLouis Dionne //      c = "const"
3133b62047bSLouis Dionne //      f = "forward"
3143b62047bSLouis Dionne // No prefix indicates non-const random-access iterator.
3153b62047bSLouis Dionne template <typename T>
3163b62047bSLouis Dionne class Sequence
3173b62047bSLouis Dionne {
3183b62047bSLouis Dionne     std::vector<T> m_storage;
3193b62047bSLouis Dionne 
3203b62047bSLouis Dionne   public:
3213b62047bSLouis Dionne     typedef typename std::vector<T>::iterator iterator;
3223b62047bSLouis Dionne     typedef typename std::vector<T>::const_iterator const_iterator;
3233b62047bSLouis Dionne     typedef ForwardIterator<iterator, std::forward_iterator_tag> forward_iterator;
3243b62047bSLouis Dionne     typedef ForwardIterator<const_iterator, std::forward_iterator_tag> const_forward_iterator;
3253b62047bSLouis Dionne 
3263b62047bSLouis Dionne     typedef BidirectionalIterator<iterator, std::bidirectional_iterator_tag> bidirectional_iterator;
3273b62047bSLouis Dionne     typedef BidirectionalIterator<const_iterator, std::bidirectional_iterator_tag> const_bidirectional_iterator;
3283b62047bSLouis Dionne 
3293b62047bSLouis Dionne     typedef T value_type;
Sequence(size_t size)3303b62047bSLouis Dionne     explicit Sequence(size_t size) : m_storage(size) {}
3313b62047bSLouis Dionne 
3323b62047bSLouis Dionne     // Construct sequence [f(0), f(1), ... f(size-1)]
3333b62047bSLouis Dionne     // f can rely on its invocations being sequential from 0 to size-1.
3343b62047bSLouis Dionne     template <typename Func>
Sequence(size_t size,Func f)3353b62047bSLouis Dionne     Sequence(size_t size, Func f)
3363b62047bSLouis Dionne     {
3373b62047bSLouis Dionne         m_storage.reserve(size);
3383b62047bSLouis Dionne         // Use push_back because T might not have a default constructor
3393b62047bSLouis Dionne         for (size_t k = 0; k < size; ++k)
3403b62047bSLouis Dionne             m_storage.push_back(T(f(k)));
3413b62047bSLouis Dionne     }
Sequence(const std::initializer_list<T> & data)3423b62047bSLouis Dionne     Sequence(const std::initializer_list<T>& data) : m_storage(data) {}
3433b62047bSLouis Dionne 
3443b62047bSLouis Dionne     const_iterator
begin()3453b62047bSLouis Dionne     begin() const
3463b62047bSLouis Dionne     {
3473b62047bSLouis Dionne         return m_storage.begin();
3483b62047bSLouis Dionne     }
3493b62047bSLouis Dionne     const_iterator
end()3503b62047bSLouis Dionne     end() const
3513b62047bSLouis Dionne     {
3523b62047bSLouis Dionne         return m_storage.end();
3533b62047bSLouis Dionne     }
3543b62047bSLouis Dionne     iterator
begin()3553b62047bSLouis Dionne     begin()
3563b62047bSLouis Dionne     {
3573b62047bSLouis Dionne         return m_storage.begin();
3583b62047bSLouis Dionne     }
3593b62047bSLouis Dionne     iterator
end()3603b62047bSLouis Dionne     end()
3613b62047bSLouis Dionne     {
3623b62047bSLouis Dionne         return m_storage.end();
3633b62047bSLouis Dionne     }
3643b62047bSLouis Dionne     const_iterator
cbegin()3653b62047bSLouis Dionne     cbegin() const
3663b62047bSLouis Dionne     {
3673b62047bSLouis Dionne         return m_storage.cbegin();
3683b62047bSLouis Dionne     }
3693b62047bSLouis Dionne     const_iterator
cend()3703b62047bSLouis Dionne     cend() const
3713b62047bSLouis Dionne     {
3723b62047bSLouis Dionne         return m_storage.cend();
3733b62047bSLouis Dionne     }
3743b62047bSLouis Dionne     forward_iterator
fbegin()3753b62047bSLouis Dionne     fbegin()
3763b62047bSLouis Dionne     {
3773b62047bSLouis Dionne         return forward_iterator(m_storage.begin());
3783b62047bSLouis Dionne     }
3793b62047bSLouis Dionne     forward_iterator
fend()3803b62047bSLouis Dionne     fend()
3813b62047bSLouis Dionne     {
3823b62047bSLouis Dionne         return forward_iterator(m_storage.end());
3833b62047bSLouis Dionne     }
3843b62047bSLouis Dionne     const_forward_iterator
cfbegin()3853b62047bSLouis Dionne     cfbegin() const
3863b62047bSLouis Dionne     {
3873b62047bSLouis Dionne         return const_forward_iterator(m_storage.cbegin());
3883b62047bSLouis Dionne     }
3893b62047bSLouis Dionne     const_forward_iterator
cfend()3903b62047bSLouis Dionne     cfend() const
3913b62047bSLouis Dionne     {
3923b62047bSLouis Dionne         return const_forward_iterator(m_storage.cend());
3933b62047bSLouis Dionne     }
3943b62047bSLouis Dionne     const_forward_iterator
fbegin()3953b62047bSLouis Dionne     fbegin() const
3963b62047bSLouis Dionne     {
3973b62047bSLouis Dionne         return const_forward_iterator(m_storage.cbegin());
3983b62047bSLouis Dionne     }
3993b62047bSLouis Dionne     const_forward_iterator
fend()4003b62047bSLouis Dionne     fend() const
4013b62047bSLouis Dionne     {
4023b62047bSLouis Dionne         return const_forward_iterator(m_storage.cend());
4033b62047bSLouis Dionne     }
4043b62047bSLouis Dionne 
4053b62047bSLouis Dionne     const_bidirectional_iterator
cbibegin()4063b62047bSLouis Dionne     cbibegin() const
4073b62047bSLouis Dionne     {
4083b62047bSLouis Dionne         return const_bidirectional_iterator(m_storage.cbegin());
4093b62047bSLouis Dionne     }
4103b62047bSLouis Dionne     const_bidirectional_iterator
cbiend()4113b62047bSLouis Dionne     cbiend() const
4123b62047bSLouis Dionne     {
4133b62047bSLouis Dionne         return const_bidirectional_iterator(m_storage.cend());
4143b62047bSLouis Dionne     }
4153b62047bSLouis Dionne 
4163b62047bSLouis Dionne     bidirectional_iterator
bibegin()4173b62047bSLouis Dionne     bibegin()
4183b62047bSLouis Dionne     {
4193b62047bSLouis Dionne         return bidirectional_iterator(m_storage.begin());
4203b62047bSLouis Dionne     }
4213b62047bSLouis Dionne     bidirectional_iterator
biend()4223b62047bSLouis Dionne     biend()
4233b62047bSLouis Dionne     {
4243b62047bSLouis Dionne         return bidirectional_iterator(m_storage.end());
4253b62047bSLouis Dionne     }
4263b62047bSLouis Dionne 
4273b62047bSLouis Dionne     std::size_t
size()4283b62047bSLouis Dionne     size() const
4293b62047bSLouis Dionne     {
4303b62047bSLouis Dionne         return m_storage.size();
4313b62047bSLouis Dionne     }
4323b62047bSLouis Dionne     const T*
data()4333b62047bSLouis Dionne     data() const
4343b62047bSLouis Dionne     {
4353b62047bSLouis Dionne         return m_storage.data();
4363b62047bSLouis Dionne     }
4373b62047bSLouis Dionne     typename std::vector<T>::reference operator[](size_t j) { return m_storage[j]; }
4383b62047bSLouis Dionne     const T& operator[](size_t j) const { return m_storage[j]; }
4393b62047bSLouis Dionne 
4403b62047bSLouis Dionne     // Fill with given value
4413b62047bSLouis Dionne     void
fill(const T & value)4423b62047bSLouis Dionne     fill(const T& value)
4433b62047bSLouis Dionne     {
4443b62047bSLouis Dionne         for (size_t i = 0; i < m_storage.size(); i++)
4453b62047bSLouis Dionne             m_storage[i] = value;
4463b62047bSLouis Dionne     }
4473b62047bSLouis Dionne 
4483b62047bSLouis Dionne     void
4493b62047bSLouis Dionne     print() const;
4503b62047bSLouis Dionne 
4513b62047bSLouis Dionne     template <typename Func>
4523b62047bSLouis Dionne     void
fill(Func f)4533b62047bSLouis Dionne     fill(Func f)
4543b62047bSLouis Dionne     {
4553b62047bSLouis Dionne         fill_data(m_storage.begin(), m_storage.end(), f);
4563b62047bSLouis Dionne     }
4573b62047bSLouis Dionne };
4583b62047bSLouis Dionne 
4593b62047bSLouis Dionne template <typename T>
4603b62047bSLouis Dionne void
print()4613b62047bSLouis Dionne Sequence<T>::print() const
4623b62047bSLouis Dionne {
4633b62047bSLouis Dionne     std::cout << "size = " << size() << ": { ";
4643b62047bSLouis Dionne     std::copy(begin(), end(), std::ostream_iterator<T>(std::cout, " "));
4653b62047bSLouis Dionne     std::cout << " } " << std::endl;
4663b62047bSLouis Dionne }
4673b62047bSLouis Dionne 
4683b62047bSLouis Dionne // Predicates for algorithms
4693b62047bSLouis Dionne template <typename DataType>
4703b62047bSLouis Dionne struct is_equal_to
4713b62047bSLouis Dionne {
is_equal_tois_equal_to4723b62047bSLouis Dionne     is_equal_to(const DataType& expected) : m_expected(expected) {}
4733b62047bSLouis Dionne     bool
operatoris_equal_to4743b62047bSLouis Dionne     operator()(const DataType& actual) const
4753b62047bSLouis Dionne     {
4763b62047bSLouis Dionne         return actual == m_expected;
4773b62047bSLouis Dionne     }
4783b62047bSLouis Dionne 
4793b62047bSLouis Dionne   private:
4803b62047bSLouis Dionne     DataType m_expected;
4813b62047bSLouis Dionne };
4823b62047bSLouis Dionne 
4833b62047bSLouis Dionne // Low-quality hash function, returns value between 0 and (1<<bits)-1
4843b62047bSLouis Dionne // Warning: low-order bits are quite predictable.
4853b62047bSLouis Dionne inline size_t
HashBits(size_t i,size_t bits)4863b62047bSLouis Dionne HashBits(size_t i, size_t bits)
4873b62047bSLouis Dionne {
4883b62047bSLouis Dionne     size_t mask = bits >= 8 * sizeof(size_t) ? ~size_t(0) : (size_t(1) << bits) - 1;
4893b62047bSLouis Dionne     return (424157 * i ^ 0x24aFa) & mask;
4903b62047bSLouis Dionne }
4913b62047bSLouis Dionne 
4923b62047bSLouis Dionne // Stateful unary op
4933b62047bSLouis Dionne template <typename T, typename U>
4943b62047bSLouis Dionne class Complement
4953b62047bSLouis Dionne {
4963b62047bSLouis Dionne     int32_t val;
4973b62047bSLouis Dionne 
4983b62047bSLouis Dionne   public:
Complement(T v)4993b62047bSLouis Dionne     Complement(T v) : val(v) {}
5003b62047bSLouis Dionne     U
operator()5013b62047bSLouis Dionne     operator()(const T& x) const
5023b62047bSLouis Dionne     {
5033b62047bSLouis Dionne         return U(val - x);
5043b62047bSLouis Dionne     }
5053b62047bSLouis Dionne };
5063b62047bSLouis Dionne 
5073b62047bSLouis Dionne // Tag used to prevent accidental use of converting constructor, even if use is explicit.
5083b62047bSLouis Dionne struct OddTag
5093b62047bSLouis Dionne {
5103b62047bSLouis Dionne };
5113b62047bSLouis Dionne 
5123b62047bSLouis Dionne class Sum;
5133b62047bSLouis Dionne 
5143b62047bSLouis Dionne // Type with limited set of operations.  Not default-constructible.
5153b62047bSLouis Dionne // Only available operator is "==".
5163b62047bSLouis Dionne // Typically used as value type in tests.
5173b62047bSLouis Dionne class Number
5183b62047bSLouis Dionne {
5193b62047bSLouis Dionne     int32_t value;
5203b62047bSLouis Dionne     friend class Add;
5213b62047bSLouis Dionne     friend class Sum;
5223b62047bSLouis Dionne     friend class IsMultiple;
5233b62047bSLouis Dionne     friend class Congruent;
5243b62047bSLouis Dionne     friend Sum
5253b62047bSLouis Dionne     operator+(const Sum& x, const Sum& y);
5263b62047bSLouis Dionne 
5273b62047bSLouis Dionne   public:
Number(int32_t val,OddTag)5283b62047bSLouis Dionne     Number(int32_t val, OddTag) : value(val) {}
5293b62047bSLouis Dionne     friend bool
5303b62047bSLouis Dionne     operator==(const Number& x, const Number& y)
5313b62047bSLouis Dionne     {
5323b62047bSLouis Dionne         return x.value == y.value;
5333b62047bSLouis Dionne     }
5343b62047bSLouis Dionne     friend std::ostream&
5353b62047bSLouis Dionne     operator<<(std::ostream& o, const Number& d)
5363b62047bSLouis Dionne     {
5373b62047bSLouis Dionne         return o << d.value;
5383b62047bSLouis Dionne     }
5393b62047bSLouis Dionne };
5403b62047bSLouis Dionne 
5413b62047bSLouis Dionne // Stateful predicate for Number.  Not default-constructible.
5423b62047bSLouis Dionne class IsMultiple
5433b62047bSLouis Dionne {
5443b62047bSLouis Dionne     long modulus;
5453b62047bSLouis Dionne 
5463b62047bSLouis Dionne   public:
5473b62047bSLouis Dionne     // True if x is multiple of modulus
5483b62047bSLouis Dionne     bool
operator()5493b62047bSLouis Dionne     operator()(Number x) const
5503b62047bSLouis Dionne     {
5513b62047bSLouis Dionne         return x.value % modulus == 0;
5523b62047bSLouis Dionne     }
IsMultiple(long modulus_,OddTag)5533b62047bSLouis Dionne     IsMultiple(long modulus_, OddTag) : modulus(modulus_) {}
5543b62047bSLouis Dionne };
5553b62047bSLouis Dionne 
5563b62047bSLouis Dionne // Stateful equivalence-class predicate for Number.  Not default-constructible.
5573b62047bSLouis Dionne class Congruent
5583b62047bSLouis Dionne {
5593b62047bSLouis Dionne     long modulus;
5603b62047bSLouis Dionne 
5613b62047bSLouis Dionne   public:
5623b62047bSLouis Dionne     // True if x and y have same remainder for the given modulus.
5633b62047bSLouis Dionne     // Note: this is not quite the same as "equivalent modulo modulus" when x and y have different
5643b62047bSLouis Dionne     // sign, but nonetheless AreCongruent is still an equivalence relationship, which is all
5653b62047bSLouis Dionne     // we need for testing.
5663b62047bSLouis Dionne     bool
operator()5673b62047bSLouis Dionne     operator()(Number x, Number y) const
5683b62047bSLouis Dionne     {
5693b62047bSLouis Dionne         return x.value % modulus == y.value % modulus;
5703b62047bSLouis Dionne     }
Congruent(long modulus_,OddTag)5713b62047bSLouis Dionne     Congruent(long modulus_, OddTag) : modulus(modulus_) {}
5723b62047bSLouis Dionne };
5733b62047bSLouis Dionne 
5743b62047bSLouis Dionne // Stateful reduction operation for Number
5753b62047bSLouis Dionne class Add
5763b62047bSLouis Dionne {
5773b62047bSLouis Dionne     long bias;
5783b62047bSLouis Dionne 
5793b62047bSLouis Dionne   public:
Add(OddTag)5803b62047bSLouis Dionne     explicit Add(OddTag) : bias(1) {}
5813b62047bSLouis Dionne     Number
operator()5823b62047bSLouis Dionne     operator()(Number x, const Number& y)
5833b62047bSLouis Dionne     {
5843b62047bSLouis Dionne         return Number(x.value + y.value + (bias - 1), OddTag());
5853b62047bSLouis Dionne     }
5863b62047bSLouis Dionne };
5873b62047bSLouis Dionne 
5883b62047bSLouis Dionne // Class similar to Number, but has default constructor and +.
5893b62047bSLouis Dionne class Sum : public Number
5903b62047bSLouis Dionne {
5913b62047bSLouis Dionne   public:
Sum()5923b62047bSLouis Dionne     Sum() : Number(0, OddTag()) {}
Sum(long x,OddTag)5933b62047bSLouis Dionne     Sum(long x, OddTag) : Number(x, OddTag()) {}
5943b62047bSLouis Dionne     friend Sum
5953b62047bSLouis Dionne     operator+(const Sum& x, const Sum& y)
5963b62047bSLouis Dionne     {
5973b62047bSLouis Dionne         return Sum(x.value + y.value, OddTag());
5983b62047bSLouis Dionne     }
5993b62047bSLouis Dionne };
6003b62047bSLouis Dionne 
6013b62047bSLouis Dionne // Type with limited set of operations, which includes an associative but not commutative operation.
6023b62047bSLouis Dionne // Not default-constructible.
6033b62047bSLouis Dionne // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
6043b62047bSLouis Dionne class MonoidElement
6053b62047bSLouis Dionne {
6063b62047bSLouis Dionne     size_t a, b;
6073b62047bSLouis Dionne 
6083b62047bSLouis Dionne   public:
MonoidElement(size_t a_,size_t b_,OddTag)6093b62047bSLouis Dionne     MonoidElement(size_t a_, size_t b_, OddTag) : a(a_), b(b_) {}
6103b62047bSLouis Dionne     friend bool
6113b62047bSLouis Dionne     operator==(const MonoidElement& x, const MonoidElement& y)
6123b62047bSLouis Dionne     {
6133b62047bSLouis Dionne         return x.a == y.a && x.b == y.b;
6143b62047bSLouis Dionne     }
6153b62047bSLouis Dionne     friend std::ostream&
6163b62047bSLouis Dionne     operator<<(std::ostream& o, const MonoidElement& x)
6173b62047bSLouis Dionne     {
6183b62047bSLouis Dionne         return o << "[" << x.a << ".." << x.b << ")";
6193b62047bSLouis Dionne     }
6203b62047bSLouis Dionne     friend class AssocOp;
6213b62047bSLouis Dionne };
6223b62047bSLouis Dionne 
6233b62047bSLouis Dionne // Stateful associative op for MonoidElement
6243b62047bSLouis Dionne // It's not really a monoid since the operation is not allowed for any two elements.
6253b62047bSLouis Dionne // But it's good enough for testing.
6263b62047bSLouis Dionne class AssocOp
6273b62047bSLouis Dionne {
6283b62047bSLouis Dionne     unsigned c;
6293b62047bSLouis Dionne 
6303b62047bSLouis Dionne   public:
AssocOp(OddTag)6313b62047bSLouis Dionne     explicit AssocOp(OddTag) : c(5) {}
6323b62047bSLouis Dionne     MonoidElement
operator()6333b62047bSLouis Dionne     operator()(const MonoidElement& x, const MonoidElement& y)
6343b62047bSLouis Dionne     {
6353b62047bSLouis Dionne         unsigned d = 5;
6363b62047bSLouis Dionne         EXPECT_EQ(d, c, "state lost");
6373b62047bSLouis Dionne         EXPECT_EQ(x.b, y.a, "commuted?");
6383b62047bSLouis Dionne 
6393b62047bSLouis Dionne         return MonoidElement(x.a, y.b, OddTag());
6403b62047bSLouis Dionne     }
6413b62047bSLouis Dionne };
6423b62047bSLouis Dionne 
6433b62047bSLouis Dionne // Multiplication of matrix is an associative but not commutative operation
6443b62047bSLouis Dionne // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
6453b62047bSLouis Dionne template <typename T>
6463b62047bSLouis Dionne struct Matrix2x2
6473b62047bSLouis Dionne {
6483b62047bSLouis Dionne     T a[2][2];
Matrix2x2Matrix2x26493b62047bSLouis Dionne     Matrix2x2() : a{{1, 0}, {0, 1}} {}
Matrix2x2Matrix2x26503b62047bSLouis Dionne     Matrix2x2(T x, T y) : a{{0, x}, {x, y}} {}
6513b9a1bb1SLouis Dionne #if !defined(_PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN)
Matrix2x2Matrix2x26523b62047bSLouis Dionne     Matrix2x2(const Matrix2x2& m) : a{{m.a[0][0], m.a[0][1]}, {m.a[1][0], m.a[1][1]}} {}
6533b62047bSLouis Dionne     Matrix2x2&
6543b62047bSLouis Dionne     operator=(const Matrix2x2& m)
6553b62047bSLouis Dionne     {
6563b62047bSLouis Dionne         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];
6573b62047bSLouis Dionne         return *this;
6583b62047bSLouis Dionne     }
6593b62047bSLouis Dionne #endif
6603b62047bSLouis Dionne };
6613b62047bSLouis Dionne 
6623b62047bSLouis Dionne template <typename T>
6633b62047bSLouis Dionne bool
6643b62047bSLouis Dionne operator==(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
6653b62047bSLouis Dionne {
6663b62047bSLouis Dionne     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] &&
6673b62047bSLouis Dionne            left.a[1][1] == right.a[1][1];
6683b62047bSLouis Dionne }
6693b62047bSLouis Dionne 
6703b62047bSLouis Dionne template <typename T>
6713b62047bSLouis Dionne Matrix2x2<T>
multiply_matrix(const Matrix2x2<T> & left,const Matrix2x2<T> & right)6723b62047bSLouis Dionne multiply_matrix(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
6733b62047bSLouis Dionne {
6743b62047bSLouis Dionne     Matrix2x2<T> result;
6753b62047bSLouis Dionne     for (int32_t i = 0; i < 2; ++i)
6763b62047bSLouis Dionne     {
6773b62047bSLouis Dionne         for (int32_t j = 0; j < 2; ++j)
6783b62047bSLouis Dionne         {
6793b62047bSLouis Dionne             result.a[i][j] = left.a[i][0] * right.a[0][j] + left.a[i][1] * right.a[1][j];
6803b62047bSLouis Dionne         }
6813b62047bSLouis Dionne     }
6823b62047bSLouis Dionne     return result;
6833b62047bSLouis Dionne }
6843b62047bSLouis Dionne 
6853b62047bSLouis Dionne //============================================================================
6863b62047bSLouis Dionne // Adapters for creating different types of iterators.
6873b62047bSLouis Dionne //
6883b62047bSLouis Dionne // In this block we implemented some adapters for creating differnet types of iterators.
6893b62047bSLouis Dionne // It's needed for extending the unit testing of Parallel STL algorithms.
6903b62047bSLouis Dionne // We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators.
6913b62047bSLouis Dionne // The input iterator should be const or non-const, non-reverse random access iterator.
6923b62047bSLouis Dionne // Iterator creates in "MakeIterator":
6933b62047bSLouis Dionne // firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator)
6943b62047bSLouis Dionne // then iterator is "packed" by "ReverseAdapter" (if it's possible)
6953b62047bSLouis Dionne // So, from input iterator we may create, for example, reverse bidirectional iterator.
6963b62047bSLouis Dionne // "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
6973b62047bSLouis Dionne 
6983b62047bSLouis Dionne // Base adapter
6993b62047bSLouis Dionne template <typename Iterator>
7003b62047bSLouis Dionne struct BaseAdapter
7013b62047bSLouis Dionne {
7023b62047bSLouis Dionne     typedef Iterator iterator_type;
7033b62047bSLouis Dionne     iterator_type
operatorBaseAdapter7043b62047bSLouis Dionne     operator()(Iterator it)
7053b62047bSLouis Dionne     {
7063b62047bSLouis Dionne         return it;
7073b62047bSLouis Dionne     }
7083b62047bSLouis Dionne };
7093b62047bSLouis Dionne 
7103b62047bSLouis Dionne // Check if the iterator is reverse iterator
7113b62047bSLouis Dionne // Note: it works only for iterators that created by std::reverse_iterator
7123b62047bSLouis Dionne template <typename NotReverseIterator>
7133b62047bSLouis Dionne struct isReverse : std::false_type
7143b62047bSLouis Dionne {
7153b62047bSLouis Dionne };
7163b62047bSLouis Dionne 
7173b62047bSLouis Dionne template <typename Iterator>
7183b62047bSLouis Dionne struct isReverse<std::reverse_iterator<Iterator>> : std::true_type
7193b62047bSLouis Dionne {
7203b62047bSLouis Dionne };
7213b62047bSLouis Dionne 
7223b62047bSLouis Dionne // Reverse adapter
7233b62047bSLouis Dionne template <typename Iterator, typename IsReverse>
7243b62047bSLouis Dionne struct ReverseAdapter
7253b62047bSLouis Dionne {
7263b62047bSLouis Dionne     typedef std::reverse_iterator<Iterator> iterator_type;
7273b62047bSLouis Dionne     iterator_type
7283b62047bSLouis Dionne     operator()(Iterator it)
7293b62047bSLouis Dionne     {
7303b9a1bb1SLouis Dionne #if defined(_PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT)
7313b62047bSLouis Dionne         return std::make_reverse_iterator(it);
7323b62047bSLouis Dionne #else
7333b62047bSLouis Dionne         return iterator_type(it);
7343b62047bSLouis Dionne #endif
7353b62047bSLouis Dionne     }
7363b62047bSLouis Dionne };
7373b62047bSLouis Dionne 
7383b62047bSLouis Dionne // Non-reverse adapter
7393b62047bSLouis Dionne template <typename Iterator>
7403b62047bSLouis Dionne struct ReverseAdapter<Iterator, std::false_type> : BaseAdapter<Iterator>
7413b62047bSLouis Dionne {
7423b62047bSLouis Dionne };
7433b62047bSLouis Dionne 
7443b62047bSLouis Dionne // Iterator adapter by type (by default std::random_access_iterator_tag)
7453b62047bSLouis Dionne template <typename Iterator, typename IteratorTag>
7463b62047bSLouis Dionne struct IteratorTypeAdapter : BaseAdapter<Iterator>
7473b62047bSLouis Dionne {
7483b62047bSLouis Dionne };
7493b62047bSLouis Dionne 
7503b62047bSLouis Dionne // Iterator adapter for forward iterator
7513b62047bSLouis Dionne template <typename Iterator>
7523b62047bSLouis Dionne struct IteratorTypeAdapter<Iterator, std::forward_iterator_tag>
7533b62047bSLouis Dionne {
7543b62047bSLouis Dionne     typedef ForwardIterator<Iterator, std::forward_iterator_tag> iterator_type;
7553b62047bSLouis Dionne     iterator_type
7563b62047bSLouis Dionne     operator()(Iterator it)
7573b62047bSLouis Dionne     {
7583b62047bSLouis Dionne         return iterator_type(it);
7593b62047bSLouis Dionne     }
7603b62047bSLouis Dionne };
7613b62047bSLouis Dionne 
7623b62047bSLouis Dionne // Iterator adapter for bidirectional iterator
7633b62047bSLouis Dionne template <typename Iterator>
7643b62047bSLouis Dionne struct IteratorTypeAdapter<Iterator, std::bidirectional_iterator_tag>
7653b62047bSLouis Dionne {
7663b62047bSLouis Dionne     typedef BidirectionalIterator<Iterator, std::bidirectional_iterator_tag> iterator_type;
7673b62047bSLouis Dionne     iterator_type
7683b62047bSLouis Dionne     operator()(Iterator it)
7693b62047bSLouis Dionne     {
7703b62047bSLouis Dionne         return iterator_type(it);
7713b62047bSLouis Dionne     }
7723b62047bSLouis Dionne };
7733b62047bSLouis Dionne 
7743b62047bSLouis Dionne //For creating iterator with new type
7753b62047bSLouis Dionne template <typename InputIterator, typename IteratorTag, typename IsReverse>
7763b62047bSLouis Dionne struct MakeIterator
7773b62047bSLouis Dionne {
7783b62047bSLouis Dionne     typedef IteratorTypeAdapter<InputIterator, IteratorTag> IterByType;
7793b62047bSLouis Dionne     typedef ReverseAdapter<typename IterByType::iterator_type, IsReverse> ReverseIter;
7803b62047bSLouis Dionne 
7813b62047bSLouis Dionne     typename ReverseIter::iterator_type
7823b62047bSLouis Dionne     operator()(InputIterator it)
7833b62047bSLouis Dionne     {
7843b62047bSLouis Dionne         return ReverseIter()(IterByType()(it));
7853b62047bSLouis Dionne     }
7863b62047bSLouis Dionne };
7873b62047bSLouis Dionne 
7883b62047bSLouis Dionne // Useful constant variables
7893b62047bSLouis Dionne constexpr std::size_t GuardSize = 5;
7903b62047bSLouis Dionne constexpr std::ptrdiff_t sizeLimit = 1000;
7913b62047bSLouis Dionne 
7923b62047bSLouis Dionne template <typename Iter, typename Void = void> // local iterator_traits for non-iterators
7933b62047bSLouis Dionne struct iterator_traits_
7943b62047bSLouis Dionne {
7953b62047bSLouis Dionne };
7963b62047bSLouis Dionne 
7973b62047bSLouis Dionne template <typename Iter> // For iterators
7983b62047bSLouis Dionne struct iterator_traits_<Iter,
7993b62047bSLouis Dionne                         typename std::enable_if<!std::is_void<typename Iter::iterator_category>::value, void>::type>
8003b62047bSLouis Dionne {
8013b62047bSLouis Dionne     typedef typename Iter::iterator_category iterator_category;
8023b62047bSLouis Dionne };
8033b62047bSLouis Dionne 
8043b62047bSLouis Dionne template <typename T> // For pointers
8053b62047bSLouis Dionne struct iterator_traits_<T*>
8063b62047bSLouis Dionne {
8073b62047bSLouis Dionne     typedef std::random_access_iterator_tag iterator_category;
8083b62047bSLouis Dionne };
8093b62047bSLouis Dionne 
8103b62047bSLouis Dionne // is iterator Iter has tag Tag
8113b62047bSLouis Dionne template <typename Iter, typename Tag>
8123b62047bSLouis Dionne using is_same_iterator_category = std::is_same<typename iterator_traits_<Iter>::iterator_category, Tag>;
8133b62047bSLouis Dionne 
8143b62047bSLouis Dionne // if we run with reverse or const iterators we shouldn't test the large range
8153b62047bSLouis Dionne template <typename IsReverse, typename IsConst>
8163b62047bSLouis Dionne struct invoke_if_
8173b62047bSLouis Dionne {
8183b62047bSLouis Dionne     template <typename Op, typename... Rest>
8193b62047bSLouis Dionne     void
8203b62047bSLouis Dionne     operator()(bool is_allow, Op op, Rest&&... rest)
8213b62047bSLouis Dionne     {
8223b62047bSLouis Dionne         if (is_allow)
8233b62047bSLouis Dionne             op(std::forward<Rest>(rest)...);
8243b62047bSLouis Dionne     }
8253b62047bSLouis Dionne };
8263b62047bSLouis Dionne template <>
8273b62047bSLouis Dionne struct invoke_if_<std::false_type, std::false_type>
8283b62047bSLouis Dionne {
8293b62047bSLouis Dionne     template <typename Op, typename... Rest>
8303b62047bSLouis Dionne     void
8315c4c4431SLouis Dionne     operator()(bool, Op op, Rest&&... rest)
8323b62047bSLouis Dionne     {
8333b62047bSLouis Dionne         op(std::forward<Rest>(rest)...);
8343b62047bSLouis Dionne     }
8353b62047bSLouis Dionne };
8363b62047bSLouis Dionne 
8373b62047bSLouis Dionne // Base non_const_wrapper struct. It is used to distinguish non_const testcases
8383b62047bSLouis Dionne // from a regular one. For non_const testcases only compilation is checked.
8393b62047bSLouis Dionne struct non_const_wrapper
8403b62047bSLouis Dionne {
8413b62047bSLouis Dionne };
8423b62047bSLouis Dionne 
8433b62047bSLouis Dionne // Generic wrapper to specify iterator type to execute callable Op on.
8443b62047bSLouis Dionne // The condition can be either positive(Op is executed only with IteratorTag)
8453b62047bSLouis Dionne // or negative(Op is executed with every type of iterators except IteratorTag)
8463b62047bSLouis Dionne template <typename Op, typename IteratorTag, bool IsPositiveCondition = true>
8473b62047bSLouis Dionne struct non_const_wrapper_tagged : non_const_wrapper
8483b62047bSLouis Dionne {
8493b62047bSLouis Dionne     template <typename Policy, typename Iterator>
8503b62047bSLouis Dionne     typename std::enable_if<IsPositiveCondition == is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
8513b62047bSLouis Dionne     operator()(Policy&& exec, Iterator iter)
8523b62047bSLouis Dionne     {
8533b62047bSLouis Dionne         Op()(exec, iter);
8543b62047bSLouis Dionne     }
8553b62047bSLouis Dionne 
8563b62047bSLouis Dionne     template <typename Policy, typename InputIterator, typename OutputIterator>
8573b62047bSLouis Dionne     typename std::enable_if<IsPositiveCondition == is_same_iterator_category<OutputIterator, IteratorTag>::value,
8583b62047bSLouis Dionne                             void>::type
8593b62047bSLouis Dionne     operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter)
8603b62047bSLouis Dionne     {
8613b62047bSLouis Dionne         Op()(exec, input_iter, out_iter);
8623b62047bSLouis Dionne     }
8633b62047bSLouis Dionne 
8643b62047bSLouis Dionne     template <typename Policy, typename Iterator>
8653b62047bSLouis Dionne     typename std::enable_if<IsPositiveCondition != is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
8665c4c4431SLouis Dionne     operator()(Policy&&, Iterator)
8673b62047bSLouis Dionne     {
8683b62047bSLouis Dionne     }
8693b62047bSLouis Dionne 
8703b62047bSLouis Dionne     template <typename Policy, typename InputIterator, typename OutputIterator>
8713b62047bSLouis Dionne     typename std::enable_if<IsPositiveCondition != is_same_iterator_category<OutputIterator, IteratorTag>::value,
8723b62047bSLouis Dionne                             void>::type
8735c4c4431SLouis Dionne     operator()(Policy&&, InputIterator, OutputIterator)
8743b62047bSLouis Dionne     {
8753b62047bSLouis Dionne     }
8763b62047bSLouis Dionne };
8773b62047bSLouis Dionne 
8783b62047bSLouis Dionne // These run_for_* structures specify with which types of iterators callable object Op
8793b62047bSLouis Dionne // should be executed.
8803b62047bSLouis Dionne template <typename Op>
8813b62047bSLouis Dionne struct run_for_rnd : non_const_wrapper_tagged<Op, std::random_access_iterator_tag>
8823b62047bSLouis Dionne {
8833b62047bSLouis Dionne };
8843b62047bSLouis Dionne 
8853b62047bSLouis Dionne template <typename Op>
8863b62047bSLouis Dionne struct run_for_rnd_bi : non_const_wrapper_tagged<Op, std::forward_iterator_tag, false>
8873b62047bSLouis Dionne {
8883b62047bSLouis Dionne };
8893b62047bSLouis Dionne 
8903b62047bSLouis Dionne template <typename Op>
8913b62047bSLouis Dionne struct run_for_rnd_fw : non_const_wrapper_tagged<Op, std::bidirectional_iterator_tag, false>
8923b62047bSLouis Dionne {
8933b62047bSLouis Dionne };
8943b62047bSLouis Dionne 
8953b62047bSLouis Dionne // Invoker for different types of iterators.
8963b62047bSLouis Dionne template <typename IteratorTag, typename IsReverse>
8973b62047bSLouis Dionne struct iterator_invoker
8983b62047bSLouis Dionne {
8993b62047bSLouis Dionne     template <typename Iterator>
9003b62047bSLouis Dionne     using make_iterator = MakeIterator<Iterator, IteratorTag, IsReverse>;
9013b62047bSLouis Dionne     template <typename Iterator>
9023b62047bSLouis Dionne     using IsConst = typename std::is_const<
9033b62047bSLouis Dionne         typename std::remove_pointer<typename std::iterator_traits<Iterator>::pointer>::type>::type;
9043b62047bSLouis Dionne     template <typename Iterator>
9053b62047bSLouis Dionne     using invoke_if = invoke_if_<IsReverse, IsConst<Iterator>>;
9063b62047bSLouis Dionne 
9073b62047bSLouis Dionne     // A single iterator version which is used for non_const testcases
9083b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator>
9093b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
9103b62047bSLouis Dionne                                 std::is_base_of<non_const_wrapper, Op>::value,
9113b62047bSLouis Dionne                             void>::type
9123b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator iter)
9133b62047bSLouis Dionne     {
9143b62047bSLouis Dionne         op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
9153b62047bSLouis Dionne     }
9163b62047bSLouis Dionne 
9173b62047bSLouis Dionne     // A version with 2 iterators which is used for non_const testcases
9183b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
9193b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
9203b62047bSLouis Dionne                                 std::is_base_of<non_const_wrapper, Op>::value,
9213b62047bSLouis Dionne                             void>::type
9223b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
9233b62047bSLouis Dionne     {
9243b62047bSLouis Dionne         op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
9253b62047bSLouis Dionne            make_iterator<OutputIterator>()(out_iter));
9263b62047bSLouis Dionne     }
9273b62047bSLouis Dionne 
9283b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
9293b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
9303b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
9313b62047bSLouis Dionne     {
9323b62047bSLouis Dionne         invoke_if<Iterator>()(n <= sizeLimit, op, exec, make_iterator<Iterator>()(begin), n,
9333b62047bSLouis Dionne                               std::forward<Rest>(rest)...);
9343b62047bSLouis Dionne     }
9353b62047bSLouis Dionne 
9363b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator, typename... Rest>
9373b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
9383b62047bSLouis Dionne                                 !std::is_base_of<non_const_wrapper, Op>::value,
9393b62047bSLouis Dionne                             void>::type
9403b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
9413b62047bSLouis Dionne     {
9423b62047bSLouis Dionne         invoke_if<Iterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
9433b62047bSLouis Dionne                               make_iterator<Iterator>()(inputBegin), make_iterator<Iterator>()(inputEnd),
9443b62047bSLouis Dionne                               std::forward<Rest>(rest)...);
9453b62047bSLouis Dionne     }
9463b62047bSLouis Dionne 
9473b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
9483b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
9493b62047bSLouis Dionne                             void>::type
9503b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
9513b62047bSLouis Dionne                Rest&&... rest)
9523b62047bSLouis Dionne     {
9533b62047bSLouis Dionne         invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
9543b62047bSLouis Dionne                                    make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
9553b62047bSLouis Dionne                                    make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...);
9563b62047bSLouis Dionne     }
9573b62047bSLouis Dionne 
9583b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
9593b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
9603b62047bSLouis Dionne                             void>::type
9613b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
9623b62047bSLouis Dionne                OutputIterator outputEnd, Rest&&... rest)
9633b62047bSLouis Dionne     {
9643b62047bSLouis Dionne         invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
9653b62047bSLouis Dionne                                    make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
9663b62047bSLouis Dionne                                    make_iterator<OutputIterator>()(outputBegin),
9673b62047bSLouis Dionne                                    make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
9683b62047bSLouis Dionne     }
9693b62047bSLouis Dionne 
9703b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
9713b62047bSLouis Dionne               typename... Rest>
9723b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
9733b62047bSLouis Dionne                             void>::type
9743b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
9753b62047bSLouis Dionne                InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
9763b62047bSLouis Dionne     {
9773b62047bSLouis Dionne         invoke_if<InputIterator1>()(
9783b62047bSLouis Dionne             std::distance(inputBegin1, inputEnd1) <= sizeLimit, op, exec, make_iterator<InputIterator1>()(inputBegin1),
9793b62047bSLouis Dionne             make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator2>()(inputBegin2),
9803b62047bSLouis Dionne             make_iterator<InputIterator2>()(inputEnd2), make_iterator<OutputIterator>()(outputBegin),
9813b62047bSLouis Dionne             make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
9823b62047bSLouis Dionne     }
9833b62047bSLouis Dionne };
9843b62047bSLouis Dionne 
9853b62047bSLouis Dionne // Invoker for reverse iterators only
9863b62047bSLouis Dionne // Note: if we run with reverse iterators we shouldn't test the large range
9873b62047bSLouis Dionne template <typename IteratorTag>
9883b62047bSLouis Dionne struct iterator_invoker<IteratorTag, /* IsReverse = */ std::true_type>
9893b62047bSLouis Dionne {
9903b62047bSLouis Dionne 
9913b62047bSLouis Dionne     template <typename Iterator>
9923b62047bSLouis Dionne     using make_iterator = MakeIterator<Iterator, IteratorTag, std::true_type>;
9933b62047bSLouis Dionne 
9943b62047bSLouis Dionne     // A single iterator version which is used for non_const testcases
9953b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator>
9963b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
9973b62047bSLouis Dionne                                 std::is_base_of<non_const_wrapper, Op>::value,
9983b62047bSLouis Dionne                             void>::type
9993b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator iter)
10003b62047bSLouis Dionne     {
10013b62047bSLouis Dionne         op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
10023b62047bSLouis Dionne     }
10033b62047bSLouis Dionne 
10043b62047bSLouis Dionne     // A version with 2 iterators which is used for non_const testcases
10053b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
10063b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
10073b62047bSLouis Dionne                                 std::is_base_of<non_const_wrapper, Op>::value,
10083b62047bSLouis Dionne                             void>::type
10093b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
10103b62047bSLouis Dionne     {
10113b62047bSLouis Dionne         op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
10123b62047bSLouis Dionne            make_iterator<OutputIterator>()(out_iter));
10133b62047bSLouis Dionne     }
10143b62047bSLouis Dionne 
10153b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
10163b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
10173b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
10183b62047bSLouis Dionne     {
10193b62047bSLouis Dionne         if (n <= sizeLimit)
10203b62047bSLouis Dionne             op(exec, make_iterator<Iterator>()(begin + n), n, std::forward<Rest>(rest)...);
10213b62047bSLouis Dionne     }
10223b62047bSLouis Dionne 
10233b62047bSLouis Dionne     template <typename Policy, typename Op, typename Iterator, typename... Rest>
10243b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
10253b62047bSLouis Dionne                                 !std::is_base_of<non_const_wrapper, Op>::value,
10263b62047bSLouis Dionne                             void>::type
10273b62047bSLouis Dionne     operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
10283b62047bSLouis Dionne     {
10293b62047bSLouis Dionne         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
10303b62047bSLouis Dionne             op(exec, make_iterator<Iterator>()(inputEnd), make_iterator<Iterator>()(inputBegin),
10313b62047bSLouis Dionne                std::forward<Rest>(rest)...);
10323b62047bSLouis Dionne     }
10333b62047bSLouis Dionne 
10343b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
10353b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
10363b62047bSLouis Dionne                             void>::type
10373b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
10383b62047bSLouis Dionne                Rest&&... rest)
10393b62047bSLouis Dionne     {
10403b62047bSLouis Dionne         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
10413b62047bSLouis Dionne             op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
10423b62047bSLouis Dionne                make_iterator<OutputIterator>()(outputBegin + (inputEnd - inputBegin)), std::forward<Rest>(rest)...);
10433b62047bSLouis Dionne     }
10443b62047bSLouis Dionne 
10453b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
10463b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
10473b62047bSLouis Dionne                             void>::type
10483b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
10493b62047bSLouis Dionne                OutputIterator outputEnd, Rest&&... rest)
10503b62047bSLouis Dionne     {
10513b62047bSLouis Dionne         if (std::distance(inputBegin, inputEnd) <= sizeLimit)
10523b62047bSLouis Dionne             op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
10533b62047bSLouis Dionne                make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
10543b62047bSLouis Dionne                std::forward<Rest>(rest)...);
10553b62047bSLouis Dionne     }
10563b62047bSLouis Dionne 
10573b62047bSLouis Dionne     template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
10583b62047bSLouis Dionne               typename... Rest>
10593b62047bSLouis Dionne     typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
10603b62047bSLouis Dionne                             void>::type
10613b62047bSLouis Dionne     operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
10623b62047bSLouis Dionne                InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
10633b62047bSLouis Dionne     {
10643b62047bSLouis Dionne         if (std::distance(inputBegin1, inputEnd1) <= sizeLimit)
10653b62047bSLouis Dionne             op(exec, make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator1>()(inputBegin1),
10663b62047bSLouis Dionne                make_iterator<InputIterator2>()(inputEnd2), make_iterator<InputIterator2>()(inputBegin2),
10673b62047bSLouis Dionne                make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
10683b62047bSLouis Dionne                std::forward<Rest>(rest)...);
10693b62047bSLouis Dionne     }
10703b62047bSLouis Dionne };
10713b62047bSLouis Dionne 
10723b62047bSLouis Dionne // We can't create reverse iterator from forward iterator
10733b62047bSLouis Dionne template <>
10743b62047bSLouis Dionne struct iterator_invoker<std::forward_iterator_tag, /*isReverse=*/std::true_type>
10753b62047bSLouis Dionne {
10763b62047bSLouis Dionne     template <typename... Rest>
10773b62047bSLouis Dionne     void
10785c4c4431SLouis Dionne     operator()(Rest&&...)
10793b62047bSLouis Dionne     {
10803b62047bSLouis Dionne     }
10813b62047bSLouis Dionne };
10823b62047bSLouis Dionne 
10833b62047bSLouis Dionne template <typename IsReverse>
10843b62047bSLouis Dionne struct reverse_invoker
10853b62047bSLouis Dionne {
10863b62047bSLouis Dionne     template <typename... Rest>
10873b62047bSLouis Dionne     void
10883b62047bSLouis Dionne     operator()(Rest&&... rest)
10893b62047bSLouis Dionne     {
10903b62047bSLouis Dionne         // Random-access iterator
10913b62047bSLouis Dionne         iterator_invoker<std::random_access_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
10923b62047bSLouis Dionne 
10933b62047bSLouis Dionne         // Forward iterator
10943b62047bSLouis Dionne         iterator_invoker<std::forward_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
10953b62047bSLouis Dionne 
10963b62047bSLouis Dionne         // Bidirectional iterator
10973b62047bSLouis Dionne         iterator_invoker<std::bidirectional_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
10983b62047bSLouis Dionne     }
10993b62047bSLouis Dionne };
11003b62047bSLouis Dionne 
11013b62047bSLouis Dionne struct invoke_on_all_iterator_types
11023b62047bSLouis Dionne {
11033b62047bSLouis Dionne     template <typename... Rest>
11043b62047bSLouis Dionne     void
11053b62047bSLouis Dionne     operator()(Rest&&... rest)
11063b62047bSLouis Dionne     {
11073b62047bSLouis Dionne         reverse_invoker</* IsReverse = */ std::false_type>()(std::forward<Rest>(rest)...);
11083b62047bSLouis Dionne         reverse_invoker</* IsReverse = */ std::true_type>()(std::forward<Rest>(rest)...);
11093b62047bSLouis Dionne     }
11103b62047bSLouis Dionne };
11113b62047bSLouis Dionne //============================================================================
11123b62047bSLouis Dionne 
11133b62047bSLouis Dionne // Invoke op(policy,rest...) for each possible policy.
11143b62047bSLouis Dionne template <typename Op, typename... T>
11153b62047bSLouis Dionne void
11163b62047bSLouis Dionne invoke_on_all_policies(Op op, T&&... rest)
11173b62047bSLouis Dionne {
11183b62047bSLouis Dionne     using namespace __pstl::execution;
11193b62047bSLouis Dionne 
11203b62047bSLouis Dionne     // Try static execution policies
11213b62047bSLouis Dionne     invoke_on_all_iterator_types()(seq, op, std::forward<T>(rest)...);
11223b62047bSLouis Dionne     invoke_on_all_iterator_types()(unseq, op, std::forward<T>(rest)...);
11233b62047bSLouis Dionne     invoke_on_all_iterator_types()(par, op, std::forward<T>(rest)...);
11243b62047bSLouis Dionne     invoke_on_all_iterator_types()(par_unseq, op, std::forward<T>(rest)...);
11253b62047bSLouis Dionne }
11263b62047bSLouis Dionne 
11273b62047bSLouis Dionne template <typename F>
11283b62047bSLouis Dionne struct NonConstAdapter
11293b62047bSLouis Dionne {
11303b62047bSLouis Dionne     F my_f;
11313b62047bSLouis Dionne     NonConstAdapter(const F& f) : my_f(f) {}
11323b62047bSLouis Dionne 
11333b62047bSLouis Dionne     template <typename... Types>
11343b62047bSLouis Dionne     auto
11353b62047bSLouis Dionne     operator()(Types&&... args) -> decltype(std::declval<F>().
11363b62047bSLouis Dionne                                             operator()(std::forward<Types>(args)...))
11373b62047bSLouis Dionne     {
11383b62047bSLouis Dionne         return my_f(std::forward<Types>(args)...);
11393b62047bSLouis Dionne     }
11403b62047bSLouis Dionne };
11413b62047bSLouis Dionne 
11423b62047bSLouis Dionne template <typename F>
11433b62047bSLouis Dionne NonConstAdapter<F>
11443b62047bSLouis Dionne non_const(const F& f)
11453b62047bSLouis Dionne {
11463b62047bSLouis Dionne     return NonConstAdapter<F>(f);
11473b62047bSLouis Dionne }
11483b62047bSLouis Dionne 
11493b62047bSLouis Dionne // Wrapper for types. It's need for counting of constructing and destructing objects
11503b62047bSLouis Dionne template <typename T>
11513b62047bSLouis Dionne class Wrapper
11523b62047bSLouis Dionne {
11533b62047bSLouis Dionne   public:
11543b62047bSLouis Dionne     Wrapper()
11553b62047bSLouis Dionne     {
11563b62047bSLouis Dionne         my_field = std::shared_ptr<T>(new T());
11573b62047bSLouis Dionne         ++my_count;
11583b62047bSLouis Dionne     }
11593b62047bSLouis Dionne     Wrapper(const T& input)
11603b62047bSLouis Dionne     {
11613b62047bSLouis Dionne         my_field = std::shared_ptr<T>(new T(input));
11623b62047bSLouis Dionne         ++my_count;
11633b62047bSLouis Dionne     }
11643b62047bSLouis Dionne     Wrapper(const Wrapper& input)
11653b62047bSLouis Dionne     {
11663b62047bSLouis Dionne         my_field = input.my_field;
11673b62047bSLouis Dionne         ++my_count;
11683b62047bSLouis Dionne     }
11693b62047bSLouis Dionne     Wrapper(Wrapper&& input)
11703b62047bSLouis Dionne     {
11713b62047bSLouis Dionne         my_field = input.my_field;
11723b62047bSLouis Dionne         input.my_field = nullptr;
11733b62047bSLouis Dionne         ++move_count;
11743b62047bSLouis Dionne     }
11753b62047bSLouis Dionne     Wrapper&
11763b62047bSLouis Dionne     operator=(const Wrapper& input)
11773b62047bSLouis Dionne     {
11783b62047bSLouis Dionne         my_field = input.my_field;
11793b62047bSLouis Dionne         return *this;
11803b62047bSLouis Dionne     }
11813b62047bSLouis Dionne     Wrapper&
11823b62047bSLouis Dionne     operator=(Wrapper&& input)
11833b62047bSLouis Dionne     {
11843b62047bSLouis Dionne         my_field = input.my_field;
11853b62047bSLouis Dionne         input.my_field = nullptr;
11863b62047bSLouis Dionne         ++move_count;
11873b62047bSLouis Dionne         return *this;
11883b62047bSLouis Dionne     }
11893b62047bSLouis Dionne     bool
11903b62047bSLouis Dionne     operator==(const Wrapper& input) const
11913b62047bSLouis Dionne     {
11923b62047bSLouis Dionne         return my_field == input.my_field;
11933b62047bSLouis Dionne     }
11943b62047bSLouis Dionne     bool
11953b62047bSLouis Dionne     operator<(const Wrapper& input) const
11963b62047bSLouis Dionne     {
11973b62047bSLouis Dionne         return *my_field < *input.my_field;
11983b62047bSLouis Dionne     }
11993b62047bSLouis Dionne     bool
12003b62047bSLouis Dionne     operator>(const Wrapper& input) const
12013b62047bSLouis Dionne     {
12023b62047bSLouis Dionne         return *my_field > *input.my_field;
12033b62047bSLouis Dionne     }
12043b62047bSLouis Dionne     friend std::ostream&
12053b62047bSLouis Dionne     operator<<(std::ostream& stream, const Wrapper& input)
12063b62047bSLouis Dionne     {
12073b62047bSLouis Dionne         return stream << *(input.my_field);
12083b62047bSLouis Dionne     }
12093b62047bSLouis Dionne     ~Wrapper()
12103b62047bSLouis Dionne     {
12113b62047bSLouis Dionne         --my_count;
12123b62047bSLouis Dionne         if (move_count > 0)
12133b62047bSLouis Dionne         {
12143b62047bSLouis Dionne             --move_count;
12153b62047bSLouis Dionne         }
12163b62047bSLouis Dionne     }
12173b62047bSLouis Dionne     T*
12183b62047bSLouis Dionne     get_my_field() const
12193b62047bSLouis Dionne     {
12203b62047bSLouis Dionne         return my_field.get();
12213b62047bSLouis Dionne     };
12223b62047bSLouis Dionne     static size_t
12233b62047bSLouis Dionne     Count()
12243b62047bSLouis Dionne     {
12253b62047bSLouis Dionne         return my_count;
12263b62047bSLouis Dionne     }
12273b62047bSLouis Dionne     static size_t
12283b62047bSLouis Dionne     MoveCount()
12293b62047bSLouis Dionne     {
12303b62047bSLouis Dionne         return move_count;
12313b62047bSLouis Dionne     }
12323b62047bSLouis Dionne     static void
12333b62047bSLouis Dionne     SetCount(const size_t& n)
12343b62047bSLouis Dionne     {
12353b62047bSLouis Dionne         my_count = n;
12363b62047bSLouis Dionne     }
12373b62047bSLouis Dionne     static void
12383b62047bSLouis Dionne     SetMoveCount(const size_t& n)
12393b62047bSLouis Dionne     {
12403b62047bSLouis Dionne         move_count = n;
12413b62047bSLouis Dionne     }
12423b62047bSLouis Dionne 
12433b62047bSLouis Dionne   private:
12443b62047bSLouis Dionne     static std::atomic<size_t> my_count;
12453b62047bSLouis Dionne     static std::atomic<size_t> move_count;
12463b62047bSLouis Dionne     std::shared_ptr<T> my_field;
12473b62047bSLouis Dionne };
12483b62047bSLouis Dionne 
12493b62047bSLouis Dionne template <typename T>
12503b62047bSLouis Dionne std::atomic<size_t> Wrapper<T>::my_count = {0};
12513b62047bSLouis Dionne 
12523b62047bSLouis Dionne template <typename T>
12533b62047bSLouis Dionne std::atomic<size_t> Wrapper<T>::move_count = {0};
12543b62047bSLouis Dionne 
12553b62047bSLouis Dionne template <typename InputIterator, typename T, typename BinaryOperation, typename UnaryOperation>
12563b62047bSLouis Dionne T
12573b62047bSLouis Dionne transform_reduce_serial(InputIterator first, InputIterator last, T init, BinaryOperation binary_op,
12583b62047bSLouis Dionne                         UnaryOperation unary_op) noexcept
12593b62047bSLouis Dionne {
12603b62047bSLouis Dionne     for (; first != last; ++first)
12613b62047bSLouis Dionne     {
12623b62047bSLouis Dionne         init = binary_op(init, unary_op(*first));
12633b62047bSLouis Dionne     }
12643b62047bSLouis Dionne     return init;
12653b62047bSLouis Dionne }
12663b62047bSLouis Dionne 
12673b62047bSLouis Dionne static const char*
12683b62047bSLouis Dionne done()
12693b62047bSLouis Dionne {
12703b9a1bb1SLouis Dionne #if defined(_PSTL_TEST_SUCCESSFUL_KEYWORD)
12713b62047bSLouis Dionne     return "done";
12723b62047bSLouis Dionne #else
12733b62047bSLouis Dionne     return "passed";
12743b62047bSLouis Dionne #endif
12753b62047bSLouis Dionne }
12763b62047bSLouis Dionne 
12773b62047bSLouis Dionne // test_algo_basic_* functions are used to execute
12783b62047bSLouis Dionne // f on a very basic sequence of elements of type T.
12793b62047bSLouis Dionne 
12803b62047bSLouis Dionne // Should be used with unary predicate
12813b62047bSLouis Dionne template <typename T, typename F>
12823b62047bSLouis Dionne static void
12833b62047bSLouis Dionne test_algo_basic_single(F&& f)
12843b62047bSLouis Dionne {
12853b62047bSLouis Dionne     size_t N = 10;
12863b62047bSLouis Dionne     Sequence<T> in(N, [](size_t v) -> T { return T(v); });
12873b62047bSLouis Dionne 
12883b62047bSLouis Dionne     invoke_on_all_policies(f, in.begin());
12893b62047bSLouis Dionne }
12903b62047bSLouis Dionne 
12913b62047bSLouis Dionne // Should be used with binary predicate
12923b62047bSLouis Dionne template <typename T, typename F>
12933b62047bSLouis Dionne static void
12943b62047bSLouis Dionne test_algo_basic_double(F&& f)
12953b62047bSLouis Dionne {
12963b62047bSLouis Dionne     size_t N = 10;
12973b62047bSLouis Dionne     Sequence<T> in(N, [](size_t v) -> T { return T(v); });
12983b62047bSLouis Dionne     Sequence<T> out(N, [](size_t v) -> T { return T(v); });
12993b62047bSLouis Dionne 
13003b62047bSLouis Dionne     invoke_on_all_policies(f, in.begin(), out.begin());
13013b62047bSLouis Dionne }
13023b62047bSLouis Dionne 
13033b62047bSLouis Dionne template <typename Policy, typename F>
13043b62047bSLouis Dionne static void
13055c4c4431SLouis Dionne invoke_if(Policy&&, F f)
13063b62047bSLouis Dionne {
13073b9a1bb1SLouis Dionne #if defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN)
1308*843c12d6SRuslan Arutyunyan     using decay_policy = typename std::decay<Policy>::type;
1309*843c12d6SRuslan Arutyunyan     using allow_unsequenced =
1310*843c12d6SRuslan Arutyunyan         std::integral_constant<bool, (std::is_same<decay_policy, std::execution::unsequenced_policy>::value ||
1311*843c12d6SRuslan Arutyunyan                                       std::is_same<decay_policy, std::execution::parallel_unsequenced_policy>::value)>;
1312*843c12d6SRuslan Arutyunyan     __pstl::__internal::__invoke_if_not(allow_unsequenced{}, f);
13133b62047bSLouis Dionne #else
13143b62047bSLouis Dionne     f();
13153b62047bSLouis Dionne #endif
13163b62047bSLouis Dionne }
13173b62047bSLouis Dionne 
13183b62047bSLouis Dionne } /* namespace TestUtils */
1319