| 1 | // -*- C++ -*- |
| 2 | //===-- transform_scan.pass.cpp -------------------------------------------===// |
| 3 | // |
| 4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 5 | // See https://llvm.org/LICENSE.txt for license information. |
| 6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | // UNSUPPORTED: c++03, c++11, c++14 |
| 11 | |
| 12 | #include "support/pstl_test_config.h" |
| 13 | |
| 14 | #include <execution> |
| 15 | #include <numeric> |
| 16 | |
| 17 | #include "support/utils.h" |
| 18 | |
| 19 | using namespace TestUtils; |
| 20 | |
| 21 | // Most of the framework required for testing inclusive and exclusive transform-scans is identical, |
| 22 | // so the tests for both are in this file. Which is being tested is controlled by the global |
| 23 | // flag inclusive, which is set to each alternative by main(). |
| 24 | static bool inclusive; |
| 25 | |
| 26 | template <typename Iterator, typename Size, typename T> |
| 27 | void |
| 28 | check_and_reset(Iterator expected_first, Iterator out_first, Size n, T trash) |
| 29 | { |
| 30 | EXPECT_EQ_N(expected_first, out_first, n, |
| 31 | inclusive ? "wrong result from transform_inclusive_scan" |
| 32 | : "wrong result from transform_exclusive_scan" ); |
| 33 | std::fill_n(out_first, n, trash); |
| 34 | } |
| 35 | |
| 36 | struct test_transform_scan |
| 37 | { |
| 38 | template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename UnaryOp, |
| 39 | typename T, typename BinaryOp> |
| 40 | typename std::enable_if<!TestUtils::isReverse<InputIterator>::value, void>::type |
| 41 | operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first, |
| 42 | OutputIterator out_last, OutputIterator expected_first, OutputIterator, Size n, UnaryOp unary_op, T init, |
| 43 | BinaryOp binary_op, T trash) |
| 44 | { |
| 45 | using namespace std; |
| 46 | |
| 47 | auto orr1 = |
| 48 | inclusive |
| 49 | ? transform_inclusive_scan(std::execution::seq, first, last, expected_first, binary_op, unary_op, init) |
| 50 | : transform_exclusive_scan(std::execution::seq, first, last, expected_first, init, binary_op, unary_op); |
| 51 | auto orr2 = inclusive ? transform_inclusive_scan(exec, first, last, out_first, binary_op, unary_op, init) |
| 52 | : transform_exclusive_scan(exec, first, last, out_first, init, binary_op, unary_op); |
| 53 | EXPECT_TRUE(out_last == orr2, "transform...scan returned wrong iterator" ); |
| 54 | check_and_reset(expected_first, out_first, n, trash); |
| 55 | |
| 56 | // Checks inclusive scan if init is not provided |
| 57 | if (inclusive && n > 0) |
| 58 | { |
| 59 | orr1 = transform_inclusive_scan(std::execution::seq, first, last, expected_first, binary_op, unary_op); |
| 60 | orr2 = transform_inclusive_scan(exec, first, last, out_first, binary_op, unary_op); |
| 61 | EXPECT_TRUE(out_last == orr2, "transform...scan returned wrong iterator" ); |
| 62 | check_and_reset(expected_first, out_first, n, trash); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename UnaryOp, |
| 67 | typename T, typename BinaryOp> |
| 68 | typename std::enable_if<TestUtils::isReverse<InputIterator>::value, void>::type |
| 69 | operator()(Policy&&, InputIterator, InputIterator, OutputIterator, OutputIterator, OutputIterator, OutputIterator, |
| 70 | Size, UnaryOp, T, BinaryOp, T) |
| 71 | { |
| 72 | } |
| 73 | }; |
| 74 | |
| 75 | const uint32_t encryption_mask = 0x314; |
| 76 | |
| 77 | template <typename InputIterator, typename OutputIterator, typename UnaryOperation, typename T, |
| 78 | typename BinaryOperation> |
| 79 | std::pair<OutputIterator, T> |
| 80 | transform_inclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unary_op, |
| 81 | T init, BinaryOperation binary_op) noexcept |
| 82 | { |
| 83 | for (; first != last; ++first, ++result) |
| 84 | { |
| 85 | init = binary_op(init, unary_op(*first)); |
| 86 | *result = init; |
| 87 | } |
| 88 | return std::make_pair(result, init); |
| 89 | } |
| 90 | |
| 91 | template <typename InputIterator, typename OutputIterator, typename UnaryOperation, typename T, |
| 92 | typename BinaryOperation> |
| 93 | std::pair<OutputIterator, T> |
| 94 | transform_exclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unary_op, |
| 95 | T init, BinaryOperation binary_op) noexcept |
| 96 | { |
| 97 | for (; first != last; ++first, ++result) |
| 98 | { |
| 99 | *result = init; |
| 100 | init = binary_op(init, unary_op(*first)); |
| 101 | } |
| 102 | return std::make_pair(result, init); |
| 103 | } |
| 104 | |
| 105 | template <typename In, typename Out, typename UnaryOp, typename BinaryOp> |
| 106 | void |
| 107 | test(UnaryOp unary_op, Out init, BinaryOp binary_op, Out trash) |
| 108 | { |
| 109 | for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n)) |
| 110 | { |
| 111 | Sequence<In> in(n, [](size_t k) { return In(k ^ encryption_mask); }); |
| 112 | |
| 113 | Out tmp = init; |
| 114 | Sequence<Out> expected(n, [&](size_t k) -> Out { |
| 115 | if (inclusive) |
| 116 | { |
| 117 | tmp = binary_op(tmp, unary_op(in[k])); |
| 118 | return tmp; |
| 119 | } |
| 120 | else |
| 121 | { |
| 122 | Out val = tmp; |
| 123 | tmp = binary_op(tmp, unary_op(in[k])); |
| 124 | return val; |
| 125 | } |
| 126 | }); |
| 127 | |
| 128 | Sequence<Out> out(n, [&](size_t) { return trash; }); |
| 129 | |
| 130 | auto result = |
| 131 | inclusive |
| 132 | ? transform_inclusive_scan_serial(in.cbegin(), in.cend(), out.fbegin(), unary_op, init, binary_op) |
| 133 | : transform_exclusive_scan_serial(in.cbegin(), in.cend(), out.fbegin(), unary_op, init, binary_op); |
| 134 | (void)result; |
| 135 | check_and_reset(expected.begin(), out.begin(), out.size(), trash); |
| 136 | |
| 137 | invoke_on_all_policies(test_transform_scan(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(), |
| 138 | expected.end(), in.size(), unary_op, init, binary_op, trash); |
| 139 | invoke_on_all_policies(test_transform_scan(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(), |
| 140 | expected.end(), in.size(), unary_op, init, binary_op, trash); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | template <typename In, typename Out, typename UnaryOp, typename BinaryOp> |
| 145 | void |
| 146 | test_matrix(UnaryOp unary_op, Out init, BinaryOp binary_op, Out trash) |
| 147 | { |
| 148 | for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n)) |
| 149 | { |
| 150 | Sequence<In> in(n, [](size_t k) { return In(k, k + 1); }); |
| 151 | |
| 152 | Sequence<Out> out(n, [&](size_t) { return trash; }); |
| 153 | Sequence<Out> expected(n, [&](size_t) { return trash; }); |
| 154 | |
| 155 | invoke_on_all_policies(test_transform_scan(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(), |
| 156 | expected.end(), in.size(), unary_op, init, binary_op, trash); |
| 157 | invoke_on_all_policies(test_transform_scan(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(), |
| 158 | expected.end(), in.size(), unary_op, init, binary_op, trash); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | int |
| 163 | main() |
| 164 | { |
| 165 | for (int32_t mode = 0; mode < 2; ++mode) |
| 166 | { |
| 167 | inclusive = mode != 0; |
| 168 | #if !defined(_PSTL_ICC_19_TEST_SIMD_UDS_WINDOWS_RELEASE_BROKEN) |
| 169 | test_matrix<Matrix2x2<int32_t>, Matrix2x2<int32_t>>(unary_op: [](const Matrix2x2<int32_t> x) { return x; }, |
| 170 | init: Matrix2x2<int32_t>(), binary_op: multiply_matrix<int32_t>, |
| 171 | trash: Matrix2x2<int32_t>(-666, 666)); |
| 172 | #endif |
| 173 | test<int32_t, uint32_t>(unary_op: [](int32_t x) { return x++; }, init: -123, binary_op: [](int32_t x, int32_t y) { return x + y; }, trash: 666); |
| 174 | } |
| 175 | std::cout << done() << std::endl; |
| 176 | return 0; |
| 177 | } |
| 178 | |