From dbf91f45c7f9136877398f09b3078a0c0d02d43e Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Fri, 3 Jun 2022 22:09:49 +0000 Subject: [PATCH 1/2] Fix misalignments in Jamfile --- test/Jamfile.v2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5f5e73cc8..6b26419a9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -41,8 +41,8 @@ alias unit_test_framework [ run ordered_test.cpp unit_test_framework : : : : ordered_test ] [ run find_if_not_test1.cpp unit_test_framework : : : : find_if_not_test1 ] - [ run copy_if_test1.cpp unit_test_framework : : : : copy_if_test1 ] - [ run copy_n_test1.cpp unit_test_framework : : : : copy_n_test1 ] + [ run copy_if_test1.cpp unit_test_framework : : : : copy_if_test1 ] + [ run copy_n_test1.cpp unit_test_framework : : : : copy_n_test1 ] [ run iota_test1.cpp unit_test_framework : : : : iota_test1 ] [ run is_permutation_test1.cpp unit_test_framework : : : : is_permutation_test1 ] @@ -88,7 +88,7 @@ alias unit_test_framework # Apply_permutation tests [ run apply_permutation_test.cpp unit_test_framework : : : : apply_permutation_test ] # Find tests - [ run find_not_test.cpp unit_test_framework : : : : find_not_test ] + [ run find_not_test.cpp unit_test_framework : : : : find_not_test ] [ run find_backward_test.cpp unit_test_framework : : : : find_backward_test ] ; } From 47092c1f10a101a144c4a31af1be8dbe87bfbc1b Mon Sep 17 00:00:00 2001 From: Jonathan Gopel Date: Fri, 3 Jun 2022 22:30:18 +0000 Subject: [PATCH 2/2] Implement iterate algorithms Problem: - There is no algorithm that generalizes `std::iota`. Filling a range 2 elements at a time is hard, more complex patterns are increasingly difficult. Solution: - Implement `iterate` and `iterate_n` which fill a range based on the execution of a binary operation which takes the previous value. --- doc/algorithm.qbk | 10 ++ include/boost/algorithm/iterate.hpp | 89 ++++++++++++++++ test/Jamfile.v2 | 2 + test/iterate_test.cpp | 160 ++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 include/boost/algorithm/iterate.hpp create mode 100644 test/iterate_test.cpp diff --git a/doc/algorithm.qbk b/doc/algorithm.qbk index 64e8dfb70..925e57a43 100644 --- a/doc/algorithm.qbk +++ b/doc/algorithm.qbk @@ -224,6 +224,16 @@ Write a sequence of n increasing values to an output iterator Raise a value to an integral power ([^constexpr] since C++14) [endsect:power] +[section:iterate iterate ] +[*[^[link header.boost.algorithm.iterate_hpp iterate] ] ] +Store the result of a unary function to each element of a range. This is a generalization of iota. +[endsect:iterate] + +[section:iterate iterate ] +[*[^[link header.boost.algorithm.iterate_hpp iterate] ] ] +Store the result of a unary function to n element of a range. This is a generalization of iota. +[endsect:iterate] + [endsect:misc_inner_algorithms] [endsect:Misc] diff --git a/include/boost/algorithm/iterate.hpp b/include/boost/algorithm/iterate.hpp new file mode 100644 index 000000000..f54c0e0dd --- /dev/null +++ b/include/boost/algorithm/iterate.hpp @@ -0,0 +1,89 @@ +/* + Copyright (c) Jonathan Gopel 2022. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef BOOST_ALGORITHM_ITERATE_HPP +#define BOOST_ALGORITHM_ITERATE_HPP + +#include +#include +#include + +#include + +namespace boost { +namespace algorithm { + +/// \fn iterate ( ForwardIterator first, ForwardIterator last, TInit init, UnaryOperation op ) +/// \brief Fills a range by applying op to the previous result of op. The nth +/// element of the range is calculated by applying op to init n times. IE: The +/// 0th element is init, the 1st element is op(init), the 2nd element is +/// op(op(init)) and so on. +/// \return The final value of init +/// +/// \param first The start of the input range +/// \param last The end of the input range +/// \param init The initial value of the output range +/// \param op The function to apply to generate subsequent elements of the +/// range. Note that the input type and output type of this +/// function must be TInit. +template +BOOST_CXX14_CONSTEXPR TInit iterate(ForwardIterator first, ForwardIterator last, + TInit init, + UnaryOperation op) // TODO: BOOST_NOEXCEPT? +{ + for (; first != last; (void)++first, init = op(init)) { + *first = init; + } + return init; +} + +/// \fn iterate ( Range& r, TInit init, UnaryOperation op ) +/// \brief Fills a range by applying op to the previous result of op. The nth +/// element of the range is calculated by applying op to init n times. IE: The +/// 0th element is init, the 1st element is op(init), the 2nd element is +/// op(op(init)) and so on. +/// \return The final value of init +/// +/// \param first The start of the input range +/// \param last The end of the input range +/// \param init The initial value of the output range +/// \param op The function to apply to generate subsequent elements of the +/// range. Note that the input type and output type of this +/// function must be TInit. +template +BOOST_CXX14_CONSTEXPR TInit iterate(Range &r, TInit init, UnaryOperation op) { + return iterate(boost::begin(r), boost::end(r), init, op); +} + +/// \fn iterate ( ForwardIterator first, Size n, TInit init, UnaryOperation op ) +/// \brief Fills n elements of a range by applying op to the previous result +/// of op. The nth element of the range is calculated by applying op to init +/// n times. IE: The 0th element is init, the 1st element is op(init), the 2nd +/// element is op(op(init)) and so on. +/// \return The updated output iterator and the final value of init +/// +/// \param first The start of the input range +/// \param n The number of elements to fill +/// \param init The initial value of the output range +/// \param op The function to apply to generate subsequent elements of the +/// range. Note that the input type and output type of this +/// function must be TInit. +template +BOOST_CXX14_CONSTEXPR std::pair +iterate(OutputIterator first, Size n, TInit init, + UnaryOperation op) // TODO: BOOST_NOEXCEPT? +{ + for (; n > 0; --n, (void)++first, init = op(init)) { + *first = init; + } + return std::make_pair(first, init); +} + +} // namespace algorithm +} // namespace boost + +#endif // BOOST_ALGORITHM_ITERATE_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6b26419a9..15ed63607 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -90,6 +90,8 @@ alias unit_test_framework # Find tests [ run find_not_test.cpp unit_test_framework : : : : find_not_test ] [ run find_backward_test.cpp unit_test_framework : : : : find_backward_test ] +# Iterate tests + [ run iterate_test.cpp unit_test_framework : : : : iterate_test ] ; } diff --git a/test/iterate_test.cpp b/test/iterate_test.cpp new file mode 100644 index 000000000..5894a5ce7 --- /dev/null +++ b/test/iterate_test.cpp @@ -0,0 +1,160 @@ +/* + Copyright (c) Jonathan Gopel 2022. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include + +#include "iterator_test.hpp" + +#include + +#define BOOST_TEST_MAIN +#include + +#include + +#include +#include +#include +#include + +typedef int data_t; + +static const int NUM_ELEMENTS = 4; + +BOOST_CONSTEXPR data_t add_one(const data_t value) { return value + 1; } + +BOOST_CONSTEXPR data_t add_two(const data_t value) { return value + 2; } + +void test_iterate() { + { // iota + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual.begin(), actual.end(), 15, add_one); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 16; + expected.at(2) = 17; + expected.at(3) = 18; + BOOST_CHECK(actual.size() == expected.size()); + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } + { // striding + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual.begin(), actual.end(), 15, add_two); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 17; + expected.at(2) = 19; + expected.at(3) = 21; + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } +} + +void test_iterate_range() { + { // iota + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual, 15, add_one); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 16; + expected.at(2) = 17; + expected.at(3) = 18; + BOOST_CHECK(actual.size() == expected.size()); + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } + { // striding + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual, 15, add_two); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 17; + expected.at(2) = 19; + expected.at(3) = 21; + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } +} + +void test_iterate_n() { + { // iota + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual.begin(), actual.size(), 15, add_one); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 16; + expected.at(2) = 17; + expected.at(3) = 18; + BOOST_CHECK(actual.size() == expected.size()); + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } + { // striding + std::vector actual(NUM_ELEMENTS); + + boost::algorithm::iterate(actual.begin(), actual.size(), 15, add_two); + + std::vector expected(NUM_ELEMENTS); + expected.at(0) = 15; + expected.at(1) = 17; + expected.at(2) = 19; + expected.at(3) = 21; + BOOST_CHECK(actual.size() == expected.size()); + BOOST_CHECK(std::equal(actual.begin(), actual.end(), expected.begin())); + } +} + +BOOST_CXX14_CONSTEXPR inline bool constexpr_test_iterate() { + int actual[] = {0, 0, 0, 0}; + + boost::algorithm::iterate(input_iterator(actual), + input_iterator(actual + NUM_ELEMENTS), 15, + add_one); + + int expected[] = {15, 16, 17, 18}; + return boost::algorithm::equal( + input_iterator(actual), + input_iterator(actual + NUM_ELEMENTS), + input_iterator(expected), + input_iterator(expected + NUM_ELEMENTS)); +} + +BOOST_CXX14_CONSTEXPR inline bool constexpr_test_iterate_n() { + int actual[] = {0, 0, 0, 0}; + + boost::algorithm::iterate(input_iterator(actual), NUM_ELEMENTS, 15, + add_one); + + int expected[] = {15, 16, 17, 18}; + return boost::algorithm::equal( + input_iterator(actual), + input_iterator(actual + NUM_ELEMENTS), + input_iterator(expected), + input_iterator(expected + NUM_ELEMENTS)); +} + +BOOST_AUTO_TEST_CASE(test_main) { + test_iterate(); + test_iterate_range(); + test_iterate_n(); + + { + BOOST_CXX14_CONSTEXPR bool result = constexpr_test_iterate(); + BOOST_CHECK(result); + } + + { + BOOST_CXX14_CONSTEXPR bool result = constexpr_test_iterate_n(); + BOOST_CHECK(result); + } +}