From 0cf6fb50632111bd85e2e8e4ccce1ccf0d42bffb Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Sun, 5 Jun 2022 14:55:05 +0000 Subject: [PATCH 1/4] Add constexpr to transform_inclusive_scan() Problem: - C++20 added `constexpr` to `transform_inclusive_scan()`. The existing implementations could be `constexpr` but were not updated. Solution: - Add `constexpr`. --- .../cxx17/transform_inclusive_scan.hpp | 2 + test/transform_inclusive_scan_test.cpp | 46 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp b/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp index 9d877c02d..2b463e960 100644 --- a/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp +++ b/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp @@ -39,6 +39,7 @@ namespace boost { namespace algorithm { /// \note This function is part of the C++17 standard library template +BOOST_CXX14_CONSTEXPR OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation bOp, UnaryOperation uOp, @@ -68,6 +69,7 @@ OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, /// \note This function is part of the C++17 standard library template +BOOST_CXX14_CONSTEXPR OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation bOp, UnaryOperation uOp) diff --git a/test/transform_inclusive_scan_test.cpp b/test/transform_inclusive_scan_test.cpp index 1ce01c638..980c1a4d6 100644 --- a/test/transform_inclusive_scan_test.cpp +++ b/test/transform_inclusive_scan_test.cpp @@ -27,7 +27,7 @@ int triangle(int n) { return n*(n+1)/2; } template struct identity { - const _Tp& operator()(const _Tp& __x) const { return __x;} + BOOST_CXX14_CONSTEXPR const _Tp& operator()(const _Tp& __x) const { return __x;} }; @@ -108,6 +108,23 @@ void basic_transform_inclusive_scan_tests() } } +BOOST_CXX14_CONSTEXPR bool constexpr_transform_inclusive_scan_tests() { + typedef random_access_iterator iterator_t; + + const int NUM_ELEMENTS = 3; + + bool status = true; + int input[NUM_ELEMENTS] = {3, 3, 3}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_inclusive_scan( + iterator_t(input), iterator_t(input + NUM_ELEMENTS), + iterator_t(output), + std::plus(), identity()); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == input[0] + (int)(i * 3)); + return status; +} + void test_transform_inclusive_scan() { basic_transform_inclusive_scan_tests(); @@ -119,6 +136,11 @@ void test_transform_inclusive_scan() transform_inclusive_scan_test >(); transform_inclusive_scan_test(); transform_inclusive_scan_test< int*>(); + + { + BOOST_CXX14_CONSTEXPR bool status = constexpr_transform_inclusive_scan_tests(); + BOOST_CHECK(status == true); + } } @@ -211,6 +233,24 @@ void basic_transform_inclusive_scan_init_tests() } } +BOOST_CXX14_CONSTEXPR bool constexpr_transform_inclusive_scan_init_tests() { + typedef random_access_iterator iterator_t; + + const int NUM_ELEMENTS = 3; + + bool status = true; + int input[NUM_ELEMENTS] = {3, 3, 3}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_inclusive_scan( + iterator_t(input), iterator_t(input + NUM_ELEMENTS), + iterator_t(output), + std::plus(), identity(), + 30); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)((i + 1) * 3)); + return status; +} + void test_transform_inclusive_scan_init() { basic_transform_inclusive_scan_init_tests(); @@ -223,6 +263,10 @@ void test_transform_inclusive_scan_init() transform_inclusive_scan_init_test(); transform_inclusive_scan_init_test< int*>(); + { + BOOST_CXX14_CONSTEXPR bool status = constexpr_transform_inclusive_scan_init_tests(); + BOOST_CHECK(status == true); + } } From 7a9d9dffdf3f5ad3102e157ca18350449fd2633b Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Sun, 5 Jun 2022 14:55:26 +0000 Subject: [PATCH 2/4] Add dual-sequence implementation of transform_inclusive_scan() Problem: - `transform()` has an overload that takes two input sequences - in other languages this is called `zip()`. - `transform_reduce()` has an overload that takes two input sequences. - `transform_inclusive_scan()` does not have this overload, despite its obvious utility from the other cases. Solution: - Implement an overload of `transform_inclusive_scan()` that takes two input sequences. --- .../cxx17/transform_inclusive_scan.hpp | 29 ++++ test/transform_inclusive_scan_test.cpp | 139 +++++++++++++++--- 2 files changed, 148 insertions(+), 20 deletions(-) diff --git a/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp b/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp index 2b463e960..cc468cfad 100644 --- a/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp +++ b/include/boost/algorithm/cxx17/transform_inclusive_scan.hpp @@ -85,6 +85,35 @@ OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, return result; } +/// \fn transform_inclusive_scan ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, ScanOperation scan_op, TransformOperation trans_op, T init ) +/// \brief Transforms elements from the input ranges with trans_op and then +/// combines those transformed elements with scan_op such that the n-1th element +/// and the nth element are combined. Inclusivity means that the nth element is +/// included in the nth combination. The first value will be used as the init. +/// \return The updated output iterator +/// +/// \param first1 The start of the first input sequence +/// \param last1 The end of the first input sequence +/// \param first2 The start of the second input sequence +/// \param result The output iterator to write the results into +/// \param scan_op The operation for combining transformed input elements +/// \param trans_op The operation for transforming pairs of input elements +/// \param init The initial value +template +BOOST_CXX14_CONSTEXPR +OutputIterator transform_inclusive_scan(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, + OutputIterator result, + ScanOperation scan_op, TransformOperation trans_op, + T init) +{ + for (; first1 != last1; ++first1, ++first2, ++result) { + init = scan_op(init, trans_op(*first1, *first2)); + *result = init; + } + return result; +} }} // namespace boost and algorithm diff --git a/test/transform_inclusive_scan_test.cpp b/test/transform_inclusive_scan_test.cpp index 980c1a4d6..133907b97 100644 --- a/test/transform_inclusive_scan_test.cpp +++ b/test/transform_inclusive_scan_test.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -146,7 +148,7 @@ void test_transform_inclusive_scan() template void -transform_inclusive_scan_init_test(Iter1 first, Iter1 last, BOp bop, UOp uop, T init, Iter2 rFirst, Iter2 rLast) +transform_inclusive_scan_init_single_input_test(Iter1 first, Iter1 last, BOp bop, UOp uop, T init, Iter2 rFirst, Iter2 rLast) { std::vector::value_type> v; // Test not in-place @@ -162,12 +164,42 @@ transform_inclusive_scan_init_test(Iter1 first, Iter1 last, BOp bop, UOp uop, T BOOST_CHECK(std::equal(v.begin(), v.end(), rFirst)); } +template +void +transform_inclusive_scan_init_dual_input_test(Iter1 first1, Iter1 last1, + Iter2 first2, + ScanOperation scan_op, TransformOperation trans_op, + T init, Iter3 expected_first, Iter3 expected_last) +{ + { // Test not in-place + std::vector::value_type> output; + ba::transform_inclusive_scan(first1, last1, first2, std::back_inserter(output), scan_op, trans_op, init); + const typename std::iterator_traits::difference_type result_size = std::distance(expected_first, expected_last); + BOOST_CHECK(result_size >= 0); + BOOST_CHECK(static_cast(result_size) == output.size()); + BOOST_CHECK(std::equal(output.begin(), output.end(), expected_first)); + } + { // Test in-place + std::vector::value_type> v(first1, last1); + ba::transform_inclusive_scan(v.begin(), v.end(), first2, v.begin(), scan_op, trans_op, init); + const typename std::iterator_traits::difference_type result_size = std::distance(expected_first, expected_last); + BOOST_CHECK(result_size >= 0); + BOOST_CHECK(static_cast(result_size) == v.size()); + BOOST_CHECK(std::equal(v.begin(), v.end(), expected_first)); + } +} template void transform_inclusive_scan_init_test() { - int ia[] = { 1, 3, 5, 7, 9}; + int ia1[] = { 1, 3, 5, 7, 9}; + int ia2[] = { 2, 4, 6, 8, 10}; + + const unsigned sa = sizeof(ia1) / sizeof(ia1[0]); + BOOST_CHECK(sa == sizeof(ia2) / sizeof(ia2[0])); // just to be sure + + // single input results const int pResI0[] = { 1, 4, 9, 16, 25}; // with identity const int mResI0[] = { 0, 0, 0, 0, 0}; const int pResN0[] = { -1, -4, -9, -16, -25}; // with negate @@ -176,7 +208,7 @@ transform_inclusive_scan_init_test() const int mResI2[] = { 2, 6, 30, 210, 1890}; const int pResN2[] = { 1, -2, -7, -14, -23}; // with negate const int mResN2[] = { -2, 6, -30, 210, -1890}; - const unsigned sa = sizeof(ia) / sizeof(ia[0]); + BOOST_CHECK(sa == sizeof(pResI0) / sizeof(pResI0[0])); // just to be sure BOOST_CHECK(sa == sizeof(mResI0) / sizeof(mResI0[0])); // just to be sure BOOST_CHECK(sa == sizeof(pResN0) / sizeof(pResN0[0])); // just to be sure @@ -186,15 +218,67 @@ transform_inclusive_scan_init_test() BOOST_CHECK(sa == sizeof(pResN2) / sizeof(pResN2[0])); // just to be sure BOOST_CHECK(sa == sizeof(mResN2) / sizeof(mResN2[0])); // just to be sure + // dual input results + const int pResP0[] = { 3, 10, 21, 36, 55}; + const int pResP2[] = { 5, 12, 23, 38, 57}; + const int pResM0[] = { -1, -2, -3, -4, -5}; + const int pResM2[] = { 1, 0, -1, -2, -3}; + const int mResP0[] = { -3, -10, -21, -36, -55}; + const int mResP2[] = { -1, -8, -19, -34, -53}; + const int mResM0[] = { 1, 2, 3, 4, 5}; + const int mResM2[] = { 3, 4, 5, 6, 7}; + + BOOST_CHECK(sa == sizeof(pResP0) / sizeof(pResP0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResP2) / sizeof(pResP2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResM0) / sizeof(pResM0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResM2) / sizeof(pResM2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResP0) / sizeof(mResP0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResP2) / sizeof(mResP2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResM0) / sizeof(mResM0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResM2) / sizeof(mResM2[0])); // just to be sure + for (unsigned int i = 0; i < sa; ++i ) { - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::plus(), identity(), 0, pResI0, pResI0 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::multiplies(), identity(), 0, mResI0, mResI0 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::plus(), std::negate(), 0, pResN0, pResN0 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::multiplies(), std::negate(), 0, mResN0, mResN0 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::plus(), identity(), 2, pResI2, pResI2 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::multiplies(), identity(), 2, mResI2, mResI2 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::plus(), std::negate(), 2, pResN2, pResN2 + i); - transform_inclusive_scan_init_test(Iter(ia), Iter(ia + i), std::multiplies(), std::negate(), 2, mResN2, mResN2 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::plus(), identity(), 0, pResI0, pResI0 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::multiplies(), identity(), 0, mResI0, mResI0 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::plus(), std::negate(), 0, pResN0, pResN0 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::multiplies(), std::negate(), 0, mResN0, mResN0 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::plus(), identity(), 2, pResI2, pResI2 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::multiplies(), identity(), 2, mResI2, mResI2 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::plus(), std::negate(), 2, pResN2, pResN2 + i); + transform_inclusive_scan_init_single_input_test(Iter(ia1), Iter(ia1 + i), std::multiplies(), std::negate(), 2, mResN2, mResN2 + i); + + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::plus(), std::plus(), 0, + pResP0, pResP0 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::plus(), std::plus(), 2, + pResP2, pResP2 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::plus(), std::minus(), 0, + pResM0, pResM0 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::plus(), std::minus(), 2, + pResM2, pResM2 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::minus(), std::plus(), 0, + mResP0, mResP0 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::minus(), std::plus(), 2, + mResP2, mResP2 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::minus(), std::minus(), 0, + mResM0, mResM0 + i); + transform_inclusive_scan_init_dual_input_test(Iter(ia1), Iter(ia1 + i), + Iter(ia2), + std::minus(), std::minus(), 2, + mResM2, mResM2 + i); } } @@ -239,15 +323,30 @@ BOOST_CXX14_CONSTEXPR bool constexpr_transform_inclusive_scan_init_tests() { const int NUM_ELEMENTS = 3; bool status = true; - int input[NUM_ELEMENTS] = {3, 3, 3}; - int output[NUM_ELEMENTS] = {0, 0, 0}; - ba::transform_inclusive_scan( - iterator_t(input), iterator_t(input + NUM_ELEMENTS), - iterator_t(output), - std::plus(), identity(), - 30); - for (size_t i = 0; i < NUM_ELEMENTS; ++i) - status &= (output[i] == 30 + (int)((i + 1) * 3)); + { // Single input range + int input[NUM_ELEMENTS] = {3, 3, 3}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_inclusive_scan( + iterator_t(input), iterator_t(input + NUM_ELEMENTS), + iterator_t(output), + std::plus(), identity(), + 30); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)((i + 1) * 3)); + } + { // Dual input ranges + int input1[NUM_ELEMENTS] = {3, 3, 3}; + int input2[NUM_ELEMENTS] = {1, 1, 1}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_inclusive_scan( + iterator_t(input1), iterator_t(input1 + NUM_ELEMENTS), + iterator_t(input2), + iterator_t(output), + std::plus(), std::minus(), + 30); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)((i + 1) * 2)); + } return status; } From fa6e930a14a565bdb3c44aadeee3c419baad1134 Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Sun, 5 Jun 2022 23:23:42 +0000 Subject: [PATCH 3/4] Add constexpr to transform_exclusive_scan() Problem: - C++20 added `constexpr` to `transform_exclusive_scan()`. The existing implementation could be `constexpr` but was not updated. Solution: - Add `constexpr`. --- .../cxx17/transform_exclusive_scan.hpp | 1 + test/transform_exclusive_scan_test.cpp | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp b/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp index 86446b803..a383b7e4f 100644 --- a/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp +++ b/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp @@ -39,6 +39,7 @@ namespace boost { namespace algorithm { /// \note This function is part of the C++17 standard library template +BOOST_CXX14_CONSTEXPR OutputIterator transform_exclusive_scan(InputIterator first, InputIterator last, OutputIterator result, T init, BinaryOperation bOp, UnaryOperation uOp) diff --git a/test/transform_exclusive_scan_test.cpp b/test/transform_exclusive_scan_test.cpp index 6259f2bc3..fbb24953b 100644 --- a/test/transform_exclusive_scan_test.cpp +++ b/test/transform_exclusive_scan_test.cpp @@ -27,7 +27,7 @@ int triangle(int n) { return n*(n+1)/2; } template struct identity { - const _Tp& operator()(const _Tp& __x) const { return __x;} + BOOST_CXX14_CONSTEXPR const _Tp& operator()(const _Tp& __x) const { return __x;} }; @@ -118,6 +118,23 @@ void basic_tests() } } +BOOST_CXX14_CONSTEXPR bool constexpr_transform_exclusive_scan_tests() { + typedef random_access_iterator iterator_t; + + const int NUM_ELEMENTS = 3; + + bool status = true; + int input[NUM_ELEMENTS] = {3, 3, 3}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_exclusive_scan( + iterator_t(input), iterator_t(input + NUM_ELEMENTS), + iterator_t(output), + 30, + std::plus(), identity()); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)(i * 3)); + return status; +} void test_transform_exclusive_scan_init() { @@ -129,6 +146,11 @@ void test_transform_exclusive_scan_init() test >(); test(); test< int*>(); + + { + BOOST_CXX14_CONSTEXPR bool status = constexpr_transform_exclusive_scan_tests(); + BOOST_CHECK(status == true); + } } BOOST_AUTO_TEST_CASE( test_main ) From 2c5d484358604a7ceaef1bb1387b5c2a570c0dcf Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Sun, 5 Jun 2022 23:53:58 +0000 Subject: [PATCH 4/4] Add dual-sequence implementation of transform_exclusive_scan() Problem: - `transform()` has an overload that takes two input sequences - in other languages this is called `zip()`. - `transform_reduce()` has an overload that takes two input sequences. - `transform_inclusive_scan()` has an overload that takes two input sequences. - `transform_exclusive_scan()` does not have this overload, despite its obvious utility from the other cases. Solution: - Implement an overload of `transform_exclusive_scan()` that takes two input ranges. --- .../cxx17/transform_exclusive_scan.hpp | 37 ++++++ test/transform_exclusive_scan_test.cpp | 115 +++++++++++++++--- 2 files changed, 132 insertions(+), 20 deletions(-) diff --git a/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp b/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp index a383b7e4f..f1957d65a 100644 --- a/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp +++ b/include/boost/algorithm/cxx17/transform_exclusive_scan.hpp @@ -58,6 +58,43 @@ OutputIterator transform_exclusive_scan(InputIterator first, InputIterator last, return result; } +/// \fn transform_exclusive_scan ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, ScanOperation scan_op, TransformOperation trans_op, T init ) +/// \brief Transforms elements from the input range with uOp and then combines +/// those transformed elements with bOp such that the n-1th element and the nth +/// element are combined. Exclusivity means that the nth element is not +/// included in the nth combination. +/// \return The updated output iterator +/// +/// \param first1 The start of the first input sequence +/// \param last1 The end of the first input sequence +/// \param first2 The start of the second input sequence +/// \param result The output iterator to write the results into +/// \param scan_op The operation for combining transformed input elements +/// \param trans_op The operation for transforming pairs of input elements +/// \param init The initial value +template +BOOST_CXX14_CONSTEXPR +OutputIterator transform_exclusive_scan(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, + OutputIterator result, T init, + ScanOperation scan_op, TransformOperation trans_op) +{ + if (first1 != last1) + { + T saved = init; + do + { + init = scan_op(init, trans_op(*first1, *first2)); + *result = saved; + saved = init; + ++first2; + ++result; + } while (++first1 != last1); + } + return result; +} + }} // namespace boost and algorithm #endif // BOOST_ALGORITHM_TRANSFORM_EXCLUSIVE_SCAN_HPP diff --git a/test/transform_exclusive_scan_test.cpp b/test/transform_exclusive_scan_test.cpp index fbb24953b..a08046c83 100644 --- a/test/transform_exclusive_scan_test.cpp +++ b/test/transform_exclusive_scan_test.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -33,7 +35,7 @@ struct identity template void -test(Iter1 first, Iter1 last, BOp bop, UOp uop, T init, Iter2 rFirst, Iter2 rLast) +test_single_input(Iter1 first, Iter1 last, BOp bop, UOp uop, T init, Iter2 rFirst, Iter2 rLast) { std::vector::value_type> v; // Test not in-place @@ -49,12 +51,42 @@ test(Iter1 first, Iter1 last, BOp bop, UOp uop, T init, Iter2 rFirst, Iter2 rLas BOOST_CHECK(std::equal(v.begin(), v.end(), rFirst)); } +template +void +test_dual_input(Iter1 first1, Iter1 last1, + Iter2 first2, + ScanOperation scan_op, TransformOperation trans_op, + T init, Iter3 expected_first, Iter3 expected_last) +{ + { // Test not in-place + std::vector::value_type> output; + ba::transform_exclusive_scan(first1, last1, first2, std::back_inserter(output), init, scan_op, trans_op); + const typename std::iterator_traits::difference_type result_size = std::distance(expected_first, expected_last); + BOOST_CHECK(result_size >= 0); + BOOST_CHECK(static_cast(result_size) == output.size()); + BOOST_CHECK(std::equal(output.begin(), output.end(), expected_first)); + } + { // Test in-place + std::vector::value_type> v(first1, last1); + ba::transform_exclusive_scan(v.begin(), v.end(), first2, v.begin(), init, scan_op, trans_op); + const typename std::iterator_traits::difference_type result_size = std::distance(expected_first, expected_last); + BOOST_CHECK(result_size >= 0); + BOOST_CHECK(static_cast(result_size) == v.size()); + BOOST_CHECK(std::equal(v.begin(), v.end(), expected_first)); + } +} template void test() { - int ia[] = { 1, 3, 5, 7, 9}; + int ia1[] = { 1, 3, 5, 7, 9}; + int ia2[] = { 2, 4, 6, 8, 10}; + + const unsigned sa = sizeof(ia1) / sizeof(ia1[0]); + BOOST_CHECK(sa == sizeof(ia2) / sizeof(ia2[0])); // just to be sure + + // single input results const int pResI0[] = { 0, 1, 4, 9, 16}; // with identity const int mResI0[] = { 0, 0, 0, 0, 0}; const int pResN0[] = { 0, -1, -4, -9, -16}; // with negate @@ -63,7 +95,7 @@ test() const int mResI2[] = { 2, 2, 6, 30, 210}; const int pResN2[] = { 2, 1, -2, -7, -14}; // with negate const int mResN2[] = { 2, -2, 6, -30, 210}; - const unsigned sa = sizeof(ia) / sizeof(ia[0]); + BOOST_CHECK(sa == sizeof(pResI0) / sizeof(pResI0[0])); // just to be sure BOOST_CHECK(sa == sizeof(mResI0) / sizeof(mResI0[0])); // just to be sure BOOST_CHECK(sa == sizeof(pResN0) / sizeof(pResN0[0])); // just to be sure @@ -73,15 +105,43 @@ test() BOOST_CHECK(sa == sizeof(pResN2) / sizeof(pResN2[0])); // just to be sure BOOST_CHECK(sa == sizeof(mResN2) / sizeof(mResN2[0])); // just to be sure + // dual input results + const int pResP0[] = { 0, 3, 10, 21, 36}; + const int pResP2[] = { 2, 5, 12, 23, 38}; + const int pResM0[] = { 0, -1, -2, -3, -4}; + const int pResM2[] = { 2, 1, 0, -1, -2}; + const int mResP0[] = { 0, -3, -10, -21, -36}; + const int mResP2[] = { 2, -1, -8, -19, -34}; + const int mResM0[] = { 0, 1, 2, 3, 4}; + const int mResM2[] = { 2, 3, 4, 5, 6}; + + BOOST_CHECK(sa == sizeof(pResP0) / sizeof(pResP0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResP2) / sizeof(pResP2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResM0) / sizeof(pResM0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(pResM2) / sizeof(pResM2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResP0) / sizeof(mResP0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResP2) / sizeof(mResP2[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResM0) / sizeof(mResM0[0])); // just to be sure + BOOST_CHECK(sa == sizeof(mResM2) / sizeof(mResM2[0])); // just to be sure + for (unsigned int i = 0; i < sa; ++i ) { - test(Iter(ia), Iter(ia + i), std::plus(), identity(), 0, pResI0, pResI0 + i); - test(Iter(ia), Iter(ia + i), std::multiplies(), identity(), 0, mResI0, mResI0 + i); - test(Iter(ia), Iter(ia + i), std::plus(), std::negate(), 0, pResN0, pResN0 + i); - test(Iter(ia), Iter(ia + i), std::multiplies(), std::negate(), 0, mResN0, mResN0 + i); - test(Iter(ia), Iter(ia + i), std::plus(), identity(), 2, pResI2, pResI2 + i); - test(Iter(ia), Iter(ia + i), std::multiplies(), identity(), 2, mResI2, mResI2 + i); - test(Iter(ia), Iter(ia + i), std::plus(), std::negate(), 2, pResN2, pResN2 + i); - test(Iter(ia), Iter(ia + i), std::multiplies(), std::negate(), 2, mResN2, mResN2 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::plus(), identity(), 0, pResI0, pResI0 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::multiplies(), identity(), 0, mResI0, mResI0 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::plus(), std::negate(), 0, pResN0, pResN0 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::multiplies(), std::negate(), 0, mResN0, mResN0 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::plus(), identity(), 2, pResI2, pResI2 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::multiplies(), identity(), 2, mResI2, mResI2 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::plus(), std::negate(), 2, pResN2, pResN2 + i); + test_single_input(Iter(ia1), Iter(ia1 + i), std::multiplies(), std::negate(), 2, mResN2, mResN2 + i); + + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::plus(), std::plus(), 0, pResP0, pResP0 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::plus(), std::plus(), 2, pResP2, pResP2 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::plus(), std::minus(), 0, pResM0, pResM0 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::plus(), std::minus(), 2, pResM2, pResM2 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::minus(), std::plus(), 0, mResP0, mResP0 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::minus(), std::plus(), 2, mResP2, mResP2 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::minus(), std::minus(), 0, mResM0, mResM0 + i); + test_dual_input(Iter(ia1), Iter(ia1 + i), Iter(ia2), std::minus(), std::minus(), 2, mResM2, mResM2 + i); } } @@ -124,15 +184,30 @@ BOOST_CXX14_CONSTEXPR bool constexpr_transform_exclusive_scan_tests() { const int NUM_ELEMENTS = 3; bool status = true; - int input[NUM_ELEMENTS] = {3, 3, 3}; - int output[NUM_ELEMENTS] = {0, 0, 0}; - ba::transform_exclusive_scan( - iterator_t(input), iterator_t(input + NUM_ELEMENTS), - iterator_t(output), - 30, - std::plus(), identity()); - for (size_t i = 0; i < NUM_ELEMENTS; ++i) - status &= (output[i] == 30 + (int)(i * 3)); + { // Single input range + int input[NUM_ELEMENTS] = {3, 3, 3}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_exclusive_scan( + iterator_t(input), iterator_t(input + NUM_ELEMENTS), + iterator_t(output), + 30, + std::plus(), identity()); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)(i * 3)); + } + { // Dual input ranges + int input1[NUM_ELEMENTS] = {3, 3, 3}; + int input2[NUM_ELEMENTS] = {1, 1, 1}; + int output[NUM_ELEMENTS] = {0, 0, 0}; + ba::transform_exclusive_scan( + iterator_t(input1), iterator_t(input1 + NUM_ELEMENTS), + iterator_t(input2), + iterator_t(output), + 30, + std::plus(), std::minus()); + for (size_t i = 0; i < NUM_ELEMENTS; ++i) + status &= (output[i] == 30 + (int)(i * 2)); + } return status; }