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 5f5e73cc8..15ed63607 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,8 +88,10 @@ 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 ] +# 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); + } +}