summaryrefslogtreecommitdiffstats
path: root/pstl/test/utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'pstl/test/utils.h')
-rw-r--r--pstl/test/utils.h1249
1 files changed, 1249 insertions, 0 deletions
diff --git a/pstl/test/utils.h b/pstl/test/utils.h
new file mode 100644
index 00000000000..f8a2fde19c3
--- /dev/null
+++ b/pstl/test/utils.h
@@ -0,0 +1,1249 @@
+// -*- C++ -*-
+//===-- utils.h -----------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// File contains common utilities that tests rely on
+
+// Do not #include <algorithm>, because if we do we will not detect accidental dependencies.
+#include <sstream>
+#include <iostream>
+#include <cstring>
+#include <iterator>
+#include <vector>
+#include <atomic>
+#include <memory>
+#include <cstdint>
+
+#include "pstl_test_config.h"
+
+namespace TestUtils
+{
+
+typedef double float64_t;
+typedef float float32_t;
+
+template <class T, std::size_t N>
+constexpr size_t
+const_size(const T (&array)[N]) noexcept
+{
+ return N;
+}
+
+template <typename T>
+class Sequence;
+
+// Handy macros for error reporting
+#define EXPECT_TRUE(condition, message) TestUtils::expect<true>(condition, __FILE__, __LINE__, message)
+#define EXPECT_FALSE(condition, message) TestUtils::expect<false>(condition, __FILE__, __LINE__, message)
+
+// Check that expected and actual are equal and have the same type.
+#define EXPECT_EQ(expected, actual, message) TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
+
+// Check that sequences started with expected and actual and have had size n are equal and have the same type.
+#define EXPECT_EQ_N(expected, actual, n, message) \
+ TestUtils::expect_equal(expected, actual, n, __FILE__, __LINE__, message)
+
+// Issue error message from outstr, adding a newline.
+// Real purpose of this routine is to have a place to hang a breakpoint.
+static void
+issue_error_message(std::stringstream& outstr)
+{
+ outstr << std::endl;
+ std::cerr << outstr.str();
+}
+
+template <bool B>
+void
+expect(bool condition, const char* file, int32_t line, const char* message)
+{
+ // Templating this function is somewhat silly, but avoids the need to declare it static
+ // or have a separate translation unit.
+ if (condition != B)
+ {
+ std::stringstream outstr;
+ outstr << "error at " << file << ":" << line << " - " << message;
+ issue_error_message(outstr);
+ }
+}
+
+// Do not change signature to const T&.
+// Function must be able to detect const differences between expected and actual.
+template <typename T>
+void
+expect_equal(T& expected, T& actual, const char* file, int32_t line, const char* message)
+{
+ if (!(expected == actual))
+ {
+ std::stringstream outstr;
+ outstr << "error at " << file << ":" << line << " - " << message << ", expected " << expected << " got "
+ << actual;
+ issue_error_message(outstr);
+ }
+}
+
+template <typename T>
+void
+expect_equal(Sequence<T>& expected, Sequence<T>& actual, const char* file, int32_t line, const char* message)
+{
+ size_t n = expected.size();
+ size_t m = actual.size();
+ if (n != m)
+ {
+ std::stringstream outstr;
+ outstr << "error at " << file << ":" << line << " - " << message << ", expected sequence of size " << n
+ << " got sequence of size " << m;
+ issue_error_message(outstr);
+ return;
+ }
+ size_t error_count = 0;
+ for (size_t k = 0; k < n && error_count < 10; ++k)
+ {
+ if (!(expected[k] == actual[k]))
+ {
+ std::stringstream outstr;
+ outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k << " expected "
+ << expected[k] << " got " << actual[k];
+ issue_error_message(outstr);
+ ++error_count;
+ }
+ }
+}
+
+template <typename Iterator1, typename Iterator2, typename Size>
+void
+expect_equal(Iterator1 expected_first, Iterator2 actual_first, Size n, const char* file, int32_t line,
+ const char* message)
+{
+ size_t error_count = 0;
+ for (size_t k = 0; k < n && error_count < 10; ++k, ++expected_first, ++actual_first)
+ {
+ if (!(*expected_first == *actual_first))
+ {
+ std::stringstream outstr;
+ outstr << "error at " << file << ":" << line << " - " << message << ", at index " << k;
+ issue_error_message(outstr);
+ ++error_count;
+ }
+ }
+}
+
+// ForwardIterator is like type Iterator, but restricted to be a forward iterator.
+// Only the forward iterator signatures that are necessary for tests are present.
+// Post-increment in particular is deliberatly omitted since our templates should avoid using it
+// because of efficiency considerations.
+template <typename Iterator, typename IteratorTag>
+class ForwardIterator
+{
+ public:
+ typedef IteratorTag iterator_category;
+ typedef typename std::iterator_traits<Iterator>::value_type value_type;
+ typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
+ typedef typename std::iterator_traits<Iterator>::pointer pointer;
+ typedef typename std::iterator_traits<Iterator>::reference reference;
+
+ protected:
+ Iterator my_iterator;
+ typedef value_type element_type;
+
+ public:
+ ForwardIterator() = default;
+ explicit ForwardIterator(Iterator i) : my_iterator(i) {}
+ reference operator*() const { return *my_iterator; }
+ Iterator operator->() const { return my_iterator; }
+ ForwardIterator
+ operator++()
+ {
+ ++my_iterator;
+ return *this;
+ }
+ ForwardIterator operator++(int32_t)
+ {
+ auto retval = *this;
+ my_iterator++;
+ return retval;
+ }
+ friend bool
+ operator==(const ForwardIterator& i, const ForwardIterator& j)
+ {
+ return i.my_iterator == j.my_iterator;
+ }
+ friend bool
+ operator!=(const ForwardIterator& i, const ForwardIterator& j)
+ {
+ return i.my_iterator != j.my_iterator;
+ }
+
+ Iterator
+ iterator() const
+ {
+ return my_iterator;
+ }
+};
+
+template <typename Iterator, typename IteratorTag>
+class BidirectionalIterator : public ForwardIterator<Iterator, IteratorTag>
+{
+ typedef ForwardIterator<Iterator, IteratorTag> base_type;
+
+ public:
+ BidirectionalIterator() = default;
+ explicit BidirectionalIterator(Iterator i) : base_type(i) {}
+ BidirectionalIterator(const base_type& i) : base_type(i.iterator()) {}
+
+ BidirectionalIterator
+ operator++()
+ {
+ ++base_type::my_iterator;
+ return *this;
+ }
+ BidirectionalIterator
+ operator--()
+ {
+ --base_type::my_iterator;
+ return *this;
+ }
+ BidirectionalIterator operator++(int32_t)
+ {
+ auto retval = *this;
+ base_type::my_iterator++;
+ return retval;
+ }
+ BidirectionalIterator operator--(int32_t)
+ {
+ auto retval = *this;
+ base_type::my_iterator--;
+ return retval;
+ }
+};
+
+template <typename Iterator, typename F>
+void
+fill_data(Iterator first, Iterator last, F f)
+{
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+ for (std::size_t i = 0; first != last; ++first, ++i)
+ {
+ *first = T(f(i));
+ }
+}
+
+// Sequence<T> is a container of a sequence of T with lots of kinds of iterators.
+// Prefixes on begin/end mean:
+// c = "const"
+// f = "forward"
+// No prefix indicates non-const random-access iterator.
+template <typename T>
+class Sequence
+{
+ std::vector<T> m_storage;
+
+ public:
+ typedef typename std::vector<T>::iterator iterator;
+ typedef typename std::vector<T>::const_iterator const_iterator;
+ typedef ForwardIterator<iterator, std::forward_iterator_tag> forward_iterator;
+ typedef ForwardIterator<const_iterator, std::forward_iterator_tag> const_forward_iterator;
+
+ typedef BidirectionalIterator<iterator, std::bidirectional_iterator_tag> bidirectional_iterator;
+ typedef BidirectionalIterator<const_iterator, std::bidirectional_iterator_tag> const_bidirectional_iterator;
+
+ typedef T value_type;
+ explicit Sequence(size_t size) : m_storage(size) {}
+
+ // Construct sequence [f(0), f(1), ... f(size-1)]
+ // f can rely on its invocations being sequential from 0 to size-1.
+ template <typename Func>
+ Sequence(size_t size, Func f)
+ {
+ m_storage.reserve(size);
+ // Use push_back because T might not have a default constructor
+ for (size_t k = 0; k < size; ++k)
+ m_storage.push_back(T(f(k)));
+ }
+ Sequence(const std::initializer_list<T>& data) : m_storage(data) {}
+
+ const_iterator
+ begin() const
+ {
+ return m_storage.begin();
+ }
+ const_iterator
+ end() const
+ {
+ return m_storage.end();
+ }
+ iterator
+ begin()
+ {
+ return m_storage.begin();
+ }
+ iterator
+ end()
+ {
+ return m_storage.end();
+ }
+ const_iterator
+ cbegin() const
+ {
+ return m_storage.cbegin();
+ }
+ const_iterator
+ cend() const
+ {
+ return m_storage.cend();
+ }
+ forward_iterator
+ fbegin()
+ {
+ return forward_iterator(m_storage.begin());
+ }
+ forward_iterator
+ fend()
+ {
+ return forward_iterator(m_storage.end());
+ }
+ const_forward_iterator
+ cfbegin() const
+ {
+ return const_forward_iterator(m_storage.cbegin());
+ }
+ const_forward_iterator
+ cfend() const
+ {
+ return const_forward_iterator(m_storage.cend());
+ }
+ const_forward_iterator
+ fbegin() const
+ {
+ return const_forward_iterator(m_storage.cbegin());
+ }
+ const_forward_iterator
+ fend() const
+ {
+ return const_forward_iterator(m_storage.cend());
+ }
+
+ const_bidirectional_iterator
+ cbibegin() const
+ {
+ return const_bidirectional_iterator(m_storage.cbegin());
+ }
+ const_bidirectional_iterator
+ cbiend() const
+ {
+ return const_bidirectional_iterator(m_storage.cend());
+ }
+
+ bidirectional_iterator
+ bibegin()
+ {
+ return bidirectional_iterator(m_storage.begin());
+ }
+ bidirectional_iterator
+ biend()
+ {
+ return bidirectional_iterator(m_storage.end());
+ }
+
+ std::size_t
+ size() const
+ {
+ return m_storage.size();
+ }
+ const T*
+ data() const
+ {
+ return m_storage.data();
+ }
+ typename std::vector<T>::reference operator[](size_t j) { return m_storage[j]; }
+ const T& operator[](size_t j) const { return m_storage[j]; }
+
+ // Fill with given value
+ void
+ fill(const T& value)
+ {
+ for (size_t i = 0; i < m_storage.size(); i++)
+ m_storage[i] = value;
+ }
+
+ void
+ print() const;
+
+ template <typename Func>
+ void
+ fill(Func f)
+ {
+ fill_data(m_storage.begin(), m_storage.end(), f);
+ }
+};
+
+template <typename T>
+void
+Sequence<T>::print() const
+{
+ std::cout << "size = " << size() << ": { ";
+ std::copy(begin(), end(), std::ostream_iterator<T>(std::cout, " "));
+ std::cout << " } " << std::endl;
+}
+
+// Predicates for algorithms
+template <typename DataType>
+struct is_equal_to
+{
+ is_equal_to(const DataType& expected) : m_expected(expected) {}
+ bool
+ operator()(const DataType& actual) const
+ {
+ return actual == m_expected;
+ }
+
+ private:
+ DataType m_expected;
+};
+
+// Low-quality hash function, returns value between 0 and (1<<bits)-1
+// Warning: low-order bits are quite predictable.
+inline size_t
+HashBits(size_t i, size_t bits)
+{
+ size_t mask = bits >= 8 * sizeof(size_t) ? ~size_t(0) : (size_t(1) << bits) - 1;
+ return (424157 * i ^ 0x24aFa) & mask;
+}
+
+// Stateful unary op
+template <typename T, typename U>
+class Complement
+{
+ int32_t val;
+
+ public:
+ Complement(T v) : val(v) {}
+ U
+ operator()(const T& x) const
+ {
+ return U(val - x);
+ }
+};
+
+// Tag used to prevent accidental use of converting constructor, even if use is explicit.
+struct OddTag
+{
+};
+
+class Sum;
+
+// Type with limited set of operations. Not default-constructible.
+// Only available operator is "==".
+// Typically used as value type in tests.
+class Number
+{
+ int32_t value;
+ friend class Add;
+ friend class Sum;
+ friend class IsMultiple;
+ friend class Congruent;
+ friend Sum
+ operator+(const Sum& x, const Sum& y);
+
+ public:
+ Number(int32_t val, OddTag) : value(val) {}
+ friend bool
+ operator==(const Number& x, const Number& y)
+ {
+ return x.value == y.value;
+ }
+ friend std::ostream&
+ operator<<(std::ostream& o, const Number& d)
+ {
+ return o << d.value;
+ }
+};
+
+// Stateful predicate for Number. Not default-constructible.
+class IsMultiple
+{
+ long modulus;
+
+ public:
+ // True if x is multiple of modulus
+ bool
+ operator()(Number x) const
+ {
+ return x.value % modulus == 0;
+ }
+ IsMultiple(long modulus_, OddTag) : modulus(modulus_) {}
+};
+
+// Stateful equivalence-class predicate for Number. Not default-constructible.
+class Congruent
+{
+ long modulus;
+
+ public:
+ // True if x and y have same remainder for the given modulus.
+ // Note: this is not quite the same as "equivalent modulo modulus" when x and y have different
+ // sign, but nonetheless AreCongruent is still an equivalence relationship, which is all
+ // we need for testing.
+ bool
+ operator()(Number x, Number y) const
+ {
+ return x.value % modulus == y.value % modulus;
+ }
+ Congruent(long modulus_, OddTag) : modulus(modulus_) {}
+};
+
+// Stateful reduction operation for Number
+class Add
+{
+ long bias;
+
+ public:
+ explicit Add(OddTag) : bias(1) {}
+ Number
+ operator()(Number x, const Number& y)
+ {
+ return Number(x.value + y.value + (bias - 1), OddTag());
+ }
+};
+
+// Class similar to Number, but has default constructor and +.
+class Sum : public Number
+{
+ public:
+ Sum() : Number(0, OddTag()) {}
+ Sum(long x, OddTag) : Number(x, OddTag()) {}
+ friend Sum
+ operator+(const Sum& x, const Sum& y)
+ {
+ return Sum(x.value + y.value, OddTag());
+ }
+};
+
+// Type with limited set of operations, which includes an associative but not commutative operation.
+// Not default-constructible.
+// Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
+class MonoidElement
+{
+ size_t a, b;
+
+ public:
+ MonoidElement(size_t a_, size_t b_, OddTag) : a(a_), b(b_) {}
+ friend bool
+ operator==(const MonoidElement& x, const MonoidElement& y)
+ {
+ return x.a == y.a && x.b == y.b;
+ }
+ friend std::ostream&
+ operator<<(std::ostream& o, const MonoidElement& x)
+ {
+ return o << "[" << x.a << ".." << x.b << ")";
+ }
+ friend class AssocOp;
+};
+
+// Stateful associative op for MonoidElement
+// It's not really a monoid since the operation is not allowed for any two elements.
+// But it's good enough for testing.
+class AssocOp
+{
+ unsigned c;
+
+ public:
+ explicit AssocOp(OddTag) : c(5) {}
+ MonoidElement
+ operator()(const MonoidElement& x, const MonoidElement& y)
+ {
+ unsigned d = 5;
+ EXPECT_EQ(d, c, "state lost");
+ EXPECT_EQ(x.b, y.a, "commuted?");
+
+ return MonoidElement(x.a, y.b, OddTag());
+ }
+};
+
+// Multiplication of matrix is an associative but not commutative operation
+// Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
+template <typename T>
+struct Matrix2x2
+{
+ T a[2][2];
+ Matrix2x2() : a{{1, 0}, {0, 1}} {}
+ Matrix2x2(T x, T y) : a{{0, x}, {x, y}} {}
+#if !__PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN
+ Matrix2x2(const Matrix2x2& m) : a{{m.a[0][0], m.a[0][1]}, {m.a[1][0], m.a[1][1]}} {}
+ Matrix2x2&
+ operator=(const Matrix2x2& m)
+ {
+ 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];
+ return *this;
+ }
+#endif
+};
+
+template <typename T>
+bool
+operator==(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
+{
+ 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] &&
+ left.a[1][1] == right.a[1][1];
+}
+
+template <typename T>
+Matrix2x2<T>
+multiply_matrix(const Matrix2x2<T>& left, const Matrix2x2<T>& right)
+{
+ Matrix2x2<T> result;
+ for (int32_t i = 0; i < 2; ++i)
+ {
+ for (int32_t j = 0; j < 2; ++j)
+ {
+ result.a[i][j] = left.a[i][0] * right.a[0][j] + left.a[i][1] * right.a[1][j];
+ }
+ }
+ return result;
+}
+
+// Check that Intel(R) Threading Building Blocks header files are not used when parallel policies are off
+#if !__PSTL_USE_PAR_POLICIES
+#if defined(TBB_INTERFACE_VERSION)
+#error The parallel backend is used while it should not (__PSTL_USE_PAR_POLICIES==0)
+#endif
+#endif
+
+//============================================================================
+// Adapters for creating different types of iterators.
+//
+// In this block we implemented some adapters for creating differnet types of iterators.
+// It's needed for extending the unit testing of Parallel STL algorithms.
+// We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators.
+// The input iterator should be const or non-const, non-reverse random access iterator.
+// Iterator creates in "MakeIterator":
+// firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator)
+// then iterator is "packed" by "ReverseAdapter" (if it's possible)
+// So, from input iterator we may create, for example, reverse bidirectional iterator.
+// "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
+
+// Base adapter
+template <typename Iterator>
+struct BaseAdapter
+{
+ typedef Iterator iterator_type;
+ iterator_type
+ operator()(Iterator it)
+ {
+ return it;
+ }
+};
+
+// Check if the iterator is reverse iterator
+// Note: it works only for iterators that created by std::reverse_iterator
+template <typename NotReverseIterator>
+struct isReverse : std::false_type
+{
+};
+
+template <typename Iterator>
+struct isReverse<std::reverse_iterator<Iterator>> : std::true_type
+{
+};
+
+// Reverse adapter
+template <typename Iterator, typename IsReverse>
+struct ReverseAdapter
+{
+ typedef std::reverse_iterator<Iterator> iterator_type;
+ iterator_type
+ operator()(Iterator it)
+ {
+#if __PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT
+ return std::make_reverse_iterator(it);
+#else
+ return iterator_type(it);
+#endif
+ }
+};
+
+// Non-reverse adapter
+template <typename Iterator>
+struct ReverseAdapter<Iterator, std::false_type> : BaseAdapter<Iterator>
+{
+};
+
+// Iterator adapter by type (by default std::random_access_iterator_tag)
+template <typename Iterator, typename IteratorTag>
+struct IteratorTypeAdapter : BaseAdapter<Iterator>
+{
+};
+
+// Iterator adapter for forward iterator
+template <typename Iterator>
+struct IteratorTypeAdapter<Iterator, std::forward_iterator_tag>
+{
+ typedef ForwardIterator<Iterator, std::forward_iterator_tag> iterator_type;
+ iterator_type
+ operator()(Iterator it)
+ {
+ return iterator_type(it);
+ }
+};
+
+// Iterator adapter for bidirectional iterator
+template <typename Iterator>
+struct IteratorTypeAdapter<Iterator, std::bidirectional_iterator_tag>
+{
+ typedef BidirectionalIterator<Iterator, std::bidirectional_iterator_tag> iterator_type;
+ iterator_type
+ operator()(Iterator it)
+ {
+ return iterator_type(it);
+ }
+};
+
+//For creating iterator with new type
+template <typename InputIterator, typename IteratorTag, typename IsReverse>
+struct MakeIterator
+{
+ typedef IteratorTypeAdapter<InputIterator, IteratorTag> IterByType;
+ typedef ReverseAdapter<typename IterByType::iterator_type, IsReverse> ReverseIter;
+
+ typename ReverseIter::iterator_type
+ operator()(InputIterator it)
+ {
+ return ReverseIter()(IterByType()(it));
+ }
+};
+
+// Useful constant variables
+constexpr std::size_t GuardSize = 5;
+constexpr std::size_t sizeLimit = 1000;
+
+template <typename Iter, typename Void = void> // local iterator_traits for non-iterators
+struct iterator_traits_
+{
+};
+
+template <typename Iter> // For iterators
+struct iterator_traits_<Iter,
+ typename std::enable_if<!std::is_void<typename Iter::iterator_category>::value, void>::type>
+{
+ typedef typename Iter::iterator_category iterator_category;
+};
+
+template <typename T> // For pointers
+struct iterator_traits_<T*>
+{
+ typedef std::random_access_iterator_tag iterator_category;
+};
+
+// is iterator Iter has tag Tag
+template <typename Iter, typename Tag>
+using is_same_iterator_category = std::is_same<typename iterator_traits_<Iter>::iterator_category, Tag>;
+
+// if we run with reverse or const iterators we shouldn't test the large range
+template <typename IsReverse, typename IsConst>
+struct invoke_if_
+{
+ template <typename Op, typename... Rest>
+ void
+ operator()(bool is_allow, Op op, Rest&&... rest)
+ {
+ if (is_allow)
+ op(std::forward<Rest>(rest)...);
+ }
+};
+template <>
+struct invoke_if_<std::false_type, std::false_type>
+{
+ template <typename Op, typename... Rest>
+ void
+ operator()(bool is_allow, Op op, Rest&&... rest)
+ {
+ op(std::forward<Rest>(rest)...);
+ }
+};
+
+// Base non_const_wrapper struct. It is used to distinguish non_const testcases
+// from a regular one. For non_const testcases only compilation is checked.
+struct non_const_wrapper
+{
+};
+
+// Generic wrapper to specify iterator type to execute callable Op on.
+// The condition can be either positive(Op is executed only with IteratorTag)
+// or negative(Op is executed with every type of iterators except IteratorTag)
+template <typename Op, typename IteratorTag, bool IsPositiveCondition = true>
+struct non_const_wrapper_tagged : non_const_wrapper
+{
+ template <typename Policy, typename Iterator>
+ typename std::enable_if<IsPositiveCondition == is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
+ operator()(Policy&& exec, Iterator iter)
+ {
+ Op()(exec, iter);
+ }
+
+ template <typename Policy, typename InputIterator, typename OutputIterator>
+ typename std::enable_if<IsPositiveCondition == is_same_iterator_category<OutputIterator, IteratorTag>::value,
+ void>::type
+ operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter)
+ {
+ Op()(exec, input_iter, out_iter);
+ }
+
+ template <typename Policy, typename Iterator>
+ typename std::enable_if<IsPositiveCondition != is_same_iterator_category<Iterator, IteratorTag>::value, void>::type
+ operator()(Policy&& exec, Iterator iter)
+ {
+ }
+
+ template <typename Policy, typename InputIterator, typename OutputIterator>
+ typename std::enable_if<IsPositiveCondition != is_same_iterator_category<OutputIterator, IteratorTag>::value,
+ void>::type
+ operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter)
+ {
+ }
+};
+
+// These run_for_* structures specify with which types of iterators callable object Op
+// should be executed.
+template <typename Op>
+struct run_for_rnd : non_const_wrapper_tagged<Op, std::random_access_iterator_tag>
+{
+};
+
+template <typename Op>
+struct run_for_rnd_bi : non_const_wrapper_tagged<Op, std::forward_iterator_tag, false>
+{
+};
+
+template <typename Op>
+struct run_for_rnd_fw : non_const_wrapper_tagged<Op, std::bidirectional_iterator_tag, false>
+{
+};
+
+// Invoker for different types of iterators.
+template <typename IteratorTag, typename IsReverse>
+struct iterator_invoker
+{
+ template <typename Iterator>
+ using make_iterator = MakeIterator<Iterator, IteratorTag, IsReverse>;
+ template <typename Iterator>
+ using IsConst = typename std::is_const<
+ typename std::remove_pointer<typename std::iterator_traits<Iterator>::pointer>::type>::type;
+ template <typename Iterator>
+ using invoke_if = invoke_if_<IsReverse, IsConst<Iterator>>;
+
+ // A single iterator version which is used for non_const testcases
+ template <typename Policy, typename Op, typename Iterator>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, Iterator iter)
+ {
+ op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
+ }
+
+ // A version with 2 iterators which is used for non_const testcases
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
+ std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
+ {
+ op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
+ make_iterator<OutputIterator>()(out_iter));
+ }
+
+ template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
+ {
+ invoke_if<Iterator>()(n <= sizeLimit, op, exec, make_iterator<Iterator>()(begin), n,
+ std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename Iterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ !std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
+ {
+ invoke_if<Iterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
+ make_iterator<Iterator>()(inputBegin), make_iterator<Iterator>()(inputEnd),
+ std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
+ Rest&&... rest)
+ {
+ invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
+ make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
+ make_iterator<OutputIterator>()(outputBegin), std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
+ OutputIterator outputEnd, Rest&&... rest)
+ {
+ invoke_if<InputIterator>()(std::distance(inputBegin, inputEnd) <= sizeLimit, op, exec,
+ make_iterator<InputIterator>()(inputBegin), make_iterator<InputIterator>()(inputEnd),
+ make_iterator<OutputIterator>()(outputBegin),
+ make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
+ typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
+ InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
+ {
+ invoke_if<InputIterator1>()(
+ std::distance(inputBegin1, inputEnd1) <= sizeLimit, op, exec, make_iterator<InputIterator1>()(inputBegin1),
+ make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator2>()(inputBegin2),
+ make_iterator<InputIterator2>()(inputEnd2), make_iterator<OutputIterator>()(outputBegin),
+ make_iterator<OutputIterator>()(outputEnd), std::forward<Rest>(rest)...);
+ }
+};
+
+// Invoker for reverse iterators only
+// Note: if we run with reverse iterators we shouldn't test the large range
+template <typename IteratorTag>
+struct iterator_invoker<IteratorTag, /* IsReverse = */ std::true_type>
+{
+
+ template <typename Iterator>
+ using make_iterator = MakeIterator<Iterator, IteratorTag, std::true_type>;
+
+ // A single iterator version which is used for non_const testcases
+ template <typename Policy, typename Op, typename Iterator>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, Iterator iter)
+ {
+ op(std::forward<Policy>(exec), make_iterator<Iterator>()(iter));
+ }
+
+ // A version with 2 iterators which is used for non_const testcases
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value &&
+ std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator input_iter, OutputIterator out_iter)
+ {
+ op(std::forward<Policy>(exec), make_iterator<InputIterator>()(input_iter),
+ make_iterator<OutputIterator>()(out_iter));
+ }
+
+ template <typename Policy, typename Op, typename Iterator, typename Size, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, Op op, Iterator begin, Size n, Rest&&... rest)
+ {
+ if (n <= sizeLimit)
+ op(exec, make_iterator<Iterator>()(begin + n), n, std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename Iterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ !std::is_base_of<non_const_wrapper, Op>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, Iterator inputBegin, Iterator inputEnd, Rest&&... rest)
+ {
+ if (std::distance(inputBegin, inputEnd) <= sizeLimit)
+ op(exec, make_iterator<Iterator>()(inputEnd), make_iterator<Iterator>()(inputBegin),
+ std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
+ Rest&&... rest)
+ {
+ if (std::distance(inputBegin, inputEnd) <= sizeLimit)
+ op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
+ make_iterator<OutputIterator>()(outputBegin + (inputEnd - inputBegin)), std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator, typename OutputIterator, typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator inputBegin, InputIterator inputEnd, OutputIterator outputBegin,
+ OutputIterator outputEnd, Rest&&... rest)
+ {
+ if (std::distance(inputBegin, inputEnd) <= sizeLimit)
+ op(exec, make_iterator<InputIterator>()(inputEnd), make_iterator<InputIterator>()(inputBegin),
+ make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
+ std::forward<Rest>(rest)...);
+ }
+
+ template <typename Policy, typename Op, typename InputIterator1, typename InputIterator2, typename OutputIterator,
+ typename... Rest>
+ typename std::enable_if<is_same_iterator_category<OutputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, Op op, InputIterator1 inputBegin1, InputIterator1 inputEnd1, InputIterator2 inputBegin2,
+ InputIterator2 inputEnd2, OutputIterator outputBegin, OutputIterator outputEnd, Rest&&... rest)
+ {
+ if (std::distance(inputBegin1, inputEnd1) <= sizeLimit)
+ op(exec, make_iterator<InputIterator1>()(inputEnd1), make_iterator<InputIterator1>()(inputBegin1),
+ make_iterator<InputIterator2>()(inputEnd2), make_iterator<InputIterator2>()(inputBegin2),
+ make_iterator<OutputIterator>()(outputEnd), make_iterator<OutputIterator>()(outputBegin),
+ std::forward<Rest>(rest)...);
+ }
+};
+
+// We can't create reverse iterator from forward iterator
+template <>
+struct iterator_invoker<std::forward_iterator_tag, /*isReverse=*/std::true_type>
+{
+ template <typename... Rest>
+ void
+ operator()(Rest&&... rest)
+ {
+ }
+};
+
+template <typename IsReverse>
+struct reverse_invoker
+{
+ template <typename... Rest>
+ void
+ operator()(Rest&&... rest)
+ {
+ // Random-access iterator
+ iterator_invoker<std::random_access_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
+
+ // Forward iterator
+ iterator_invoker<std::forward_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
+
+ // Bidirectional iterator
+ iterator_invoker<std::bidirectional_iterator_tag, IsReverse>()(std::forward<Rest>(rest)...);
+ }
+};
+
+struct invoke_on_all_iterator_types
+{
+ template <typename... Rest>
+ void
+ operator()(Rest&&... rest)
+ {
+ reverse_invoker</* IsReverse = */ std::false_type>()(std::forward<Rest>(rest)...);
+ reverse_invoker</* IsReverse = */ std::true_type>()(std::forward<Rest>(rest)...);
+ }
+};
+//============================================================================
+
+// Invoke op(policy,rest...) for each possible policy.
+template <typename Op, typename... T>
+void
+invoke_on_all_policies(Op op, T&&... rest)
+{
+ using namespace __pstl::execution;
+
+ // Try static execution policies
+ invoke_on_all_iterator_types()(seq, op, std::forward<T>(rest)...);
+ invoke_on_all_iterator_types()(unseq, op, std::forward<T>(rest)...);
+#if __PSTL_USE_PAR_POLICIES
+ invoke_on_all_iterator_types()(par, op, std::forward<T>(rest)...);
+ invoke_on_all_iterator_types()(par_unseq, op, std::forward<T>(rest)...);
+#endif
+}
+
+template <typename F>
+struct NonConstAdapter
+{
+ F my_f;
+ NonConstAdapter(const F& f) : my_f(f) {}
+
+ template <typename... Types>
+ auto
+ operator()(Types&&... args) -> decltype(std::declval<F>().
+ operator()(std::forward<Types>(args)...))
+ {
+ return my_f(std::forward<Types>(args)...);
+ }
+};
+
+template <typename F>
+NonConstAdapter<F>
+non_const(const F& f)
+{
+ return NonConstAdapter<F>(f);
+}
+
+// Wrapper for types. It's need for counting of constructing and destructing objects
+template <typename T>
+class Wrapper
+{
+ public:
+ Wrapper()
+ {
+ my_field = std::shared_ptr<T>(new T());
+ ++my_count;
+ }
+ Wrapper(const T& input)
+ {
+ my_field = std::shared_ptr<T>(new T(input));
+ ++my_count;
+ }
+ Wrapper(const Wrapper& input)
+ {
+ my_field = input.my_field;
+ ++my_count;
+ }
+ Wrapper(Wrapper&& input)
+ {
+ my_field = input.my_field;
+ input.my_field = nullptr;
+ ++move_count;
+ }
+ Wrapper&
+ operator=(const Wrapper& input)
+ {
+ my_field = input.my_field;
+ return *this;
+ }
+ Wrapper&
+ operator=(Wrapper&& input)
+ {
+ my_field = input.my_field;
+ input.my_field = nullptr;
+ ++move_count;
+ return *this;
+ }
+ bool
+ operator==(const Wrapper& input) const
+ {
+ return my_field == input.my_field;
+ }
+ bool
+ operator<(const Wrapper& input) const
+ {
+ return *my_field < *input.my_field;
+ }
+ bool
+ operator>(const Wrapper& input) const
+ {
+ return *my_field > *input.my_field;
+ }
+ friend std::ostream&
+ operator<<(std::ostream& stream, const Wrapper& input)
+ {
+ return stream << *(input.my_field);
+ }
+ ~Wrapper()
+ {
+ --my_count;
+ if (move_count > 0)
+ {
+ --move_count;
+ }
+ }
+ T*
+ get_my_field() const
+ {
+ return my_field.get();
+ };
+ static size_t
+ Count()
+ {
+ return my_count;
+ }
+ static size_t
+ MoveCount()
+ {
+ return move_count;
+ }
+ static void
+ SetCount(const size_t& n)
+ {
+ my_count = n;
+ }
+ static void
+ SetMoveCount(const size_t& n)
+ {
+ move_count = n;
+ }
+
+ private:
+ static std::atomic<size_t> my_count;
+ static std::atomic<size_t> move_count;
+ std::shared_ptr<T> my_field;
+};
+
+template <typename T>
+std::atomic<size_t> Wrapper<T>::my_count = {0};
+
+template <typename T>
+std::atomic<size_t> Wrapper<T>::move_count = {0};
+
+template <typename InputIterator, typename T, typename BinaryOperation, typename UnaryOperation>
+T
+transform_reduce_serial(InputIterator first, InputIterator last, T init, BinaryOperation binary_op,
+ UnaryOperation unary_op) noexcept
+{
+ for (; first != last; ++first)
+ {
+ init = binary_op(init, unary_op(*first));
+ }
+ return init;
+}
+
+static const char*
+done()
+{
+#if __PSTL_TEST_SUCCESSFUL_KEYWORD
+ return "done";
+#else
+ return "passed";
+#endif
+}
+
+// test_algo_basic_* functions are used to execute
+// f on a very basic sequence of elements of type T.
+
+// Should be used with unary predicate
+template <typename T, typename F>
+static void
+test_algo_basic_single(F&& f)
+{
+ size_t N = 10;
+ Sequence<T> in(N, [](size_t v) -> T { return T(v); });
+
+ invoke_on_all_policies(f, in.begin());
+}
+
+// Should be used with binary predicate
+template <typename T, typename F>
+static void
+test_algo_basic_double(F&& f)
+{
+ size_t N = 10;
+ Sequence<T> in(N, [](size_t v) -> T { return T(v); });
+ Sequence<T> out(N, [](size_t v) -> T { return T(v); });
+
+ invoke_on_all_policies(f, in.begin(), out.begin());
+}
+
+template <typename Policy, typename F>
+static void
+invoke_if(Policy&& p, F f)
+{
+#if __PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN || __PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN
+ __pstl::internal::invoke_if_not(__pstl::internal::allow_unsequenced<Policy>(), f);
+#else
+ f();
+#endif
+}
+
+} /* namespace TestUtils */
OpenPOWER on IntegriCloud