diff --git a/doc/algorithm.qbk b/doc/algorithm.qbk index 64e8dfb70..a73be45dc 100644 --- a/doc/algorithm.qbk +++ b/doc/algorithm.qbk @@ -110,6 +110,15 @@ Apply a functor to the elements of a sequence [endsect:CXX17] +[section:CXX23 C++23 Algorithms] + +[section:CXX23_inner_algorithms] + +[include find_last.qbk] + +[endsect:CXX23_inner_algorithms] + +[endsect:CXX23] [section:Misc Other Algorithms] diff --git a/doc/find_last.qbk b/doc/find_last.qbk new file mode 100644 index 000000000..ee6647087 --- /dev/null +++ b/doc/find_last.qbk @@ -0,0 +1,96 @@ +[/ File find_last.qbk] + +[section:find_last find_last, find_last_if, find_last_if_not ] + +[/license +Copyright (c) 2022 T. Zachary Laine + +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) +] + +The header file 'find_last.hpp' contains variants of the stl algorithm +`find`. These variants are like `find`, except that they find the last +instance of an element instead of the first. + +The functions in this header are as close to the ones in C++23's `std::ranges` +namespace as possible. In C++20 builds, they are constrained exactly as the +standard does it. They each return a `boost::algorithm::subrange`, which is a +mostly-API-compatible version of `std::ranges::subrange`. It's differences +from the C++20 `subrange` are that it does not support sized ranges, and that +it cannot be implicitly converted to a pair-like type. + +[heading interface] + + template + BOOST_CXX14_CONSTEXPR subrange + find_last(ForwardIterator first, Sentinel last, const T & value, Proj proj = {}) + + template + BOOST_CXX14_CONSTEXPR subrange> + find_last(R && r, const T & value, Proj proj = {}) + +These overloads of `find_last` return a subrange `[it, end)`, where `it` is +the position of the last element that is equal to `x` in the given range, and +`end` is the end of the given range. + + template + BOOST_CXX14_CONSTEXPR subrange + find_last_if(ForwardIterator first, Sentinel last, Pred pred, Proj proj = {}) + + template + BOOST_CXX14_CONSTEXPR subrange> + find_last_if(R && r, Pred pred, Proj proj = {}) + +These overloads of `find_if_last` return a subrange `[it, end)`, where `it` is +the position of the last element for which `pred` returns `true` in the given +range, and `end` is the end of the given range. + + template + BOOST_CXX14_CONSTEXPR subrange + find_last_if_not(ForwardIterator first, Sentinel last, Pred pred, Proj proj = {}) + + template + BOOST_CXX14_CONSTEXPR subrange> + find_last_if_not(R && r, Pred pred, Proj proj = {}) + +These overloads of `find_if_last` return a subrange `[it, end)`, where `it` is +the position of the last element for which `pred` returns `false` in the given +range, and `end` is the end of the given range. + +[heading Examples] + +Given the container `c1` containing `{ 2, 1, 2 }`, then + + find_last ( c1.begin(), c1.end(), 2 ) --> --c1.end() + find_last ( c1.begin(), c1.end(), 3 ) --> c1.end() + find_last_if ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> --c1.end() + find_last_if ( c1.begin(), c1.end(), [](int i) {return i == 3;} ) --> c1.end() + find_last_if_not ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> std::prev(c1.end(), 2) + find_last_if_not ( c1.begin(), c1.end(), [](int i) {return i == 1;} ) --> c1.end() + +[heading Iterator Requirements] + +All variants work on forward iterators. + +[heading Complexity] + +Linear. + +[heading Exception Safety] + +All of the variants take their parameters by value and do not depend upon any +global state. Therefore, all the routines in this file provide the strong +exception guarantee. + +[heading Notes] + +All variants are `constexpr` in C++14 or later. + +[endsect] + +[/ File find_last.qbk +Copyright 2022 T. Zachary Laine +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). +] diff --git a/include/boost/algorithm/cxx20_helper.hpp b/include/boost/algorithm/cxx20_helper.hpp new file mode 100644 index 000000000..5d037c6b3 --- /dev/null +++ b/include/boost/algorithm/cxx20_helper.hpp @@ -0,0 +1,92 @@ +/* + Copyright (c) T. Zachary Laine 2022. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE10.txt or copy at http://www.boost.org/LICENSE10.txt) +*/ +#ifndef BOOST_ALGORITHM_CXX20_HELPER_HPP +#define BOOST_ALGORITHM_CXX20_HELPER_HPP + +#include +#include +#include +#include + +#include + + +namespace boost { namespace algorithm { + + struct identity + { + template + BOOST_CXX14_CONSTEXPR T && operator()(T && x) const noexcept + { + return (T &&) x; + } + }; + +#if defined(__cpp_lib_concepts) + template + using iterator_t = std::ranges::iterator_t; + template + using sentinel_t = std::ranges::sentinel_t; +#else + template + using iterator_t = decltype(boost::begin(std::declval())); + template + using sentinel_t = decltype(boost::end(std::declval())); +#endif + + template + struct subrange + : stl_interfaces::view_interface> + { + BOOST_CXX14_CONSTEXPR subrange() = default; + BOOST_CXX14_CONSTEXPR subrange(Iterator first, Sentinel last) : + first_(first), last_(last) + {} + template + BOOST_CXX14_CONSTEXPR explicit subrange(const R & r) : + first_(boost::begin(r)), last_(boost::end(r)) + {} + + BOOST_CXX14_CONSTEXPR Iterator begin() const { return first_; } + BOOST_CXX14_CONSTEXPR Sentinel end() const { return last_; } + + [[nodiscard]] BOOST_CXX14_CONSTEXPR subrange + next(std::ptrdiff_t n = 1) const + { + return subrange{std::next(first_), last_}; + } + [[nodiscard]] BOOST_CXX14_CONSTEXPR subrange + prev(std::ptrdiff_t n = 1) const + { + return subrange{std::prev(first_), last_}; + } + + BOOST_CXX14_CONSTEXPR subrange & advance(std::ptrdiff_t n) + { + std::advance(first_, n); + return *this; + } + + template< + typename Iterator2, + typename Sentinel2, + typename Enable = std::enable_if_t< + std::is_convertible::value && + std::is_convertible::value>> + BOOST_CXX14_CONSTEXPR operator subrange() const + { + return {first_, last_}; + } + + private: + Iterator first_; + Sentinel last_; + }; + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_CXX20_HELPER diff --git a/include/boost/algorithm/cxx23/find_last.hpp b/include/boost/algorithm/cxx23/find_last.hpp new file mode 100644 index 000000000..25eef2d2d --- /dev/null +++ b/include/boost/algorithm/cxx23/find_last.hpp @@ -0,0 +1,208 @@ +/* + Copyright (c) T. Zachary Laine 2022. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE10.txt or copy at http://www.boost.org/LICENSE10.txt) +*/ +#ifndef BOOST_ALGORITHM_FIND_LAST_HPP +#define BOOST_ALGORITHM_FIND_LAST_HPP + +#include +#include +#include + +#include + + +namespace boost { namespace algorithm { + + namespace detail { + template + BOOST_CXX14_CONSTEXPR subrange find_last_impl( + I first, S last, Pred pred, Proj proj, std::forward_iterator_tag) + { + bool found = false; + I latest_found = first; + for (; first != last; ++first) { + if (pred(proj(*first))) { + latest_found = first; + found = true; + } + } + if (found) + return subrange(latest_found, first); + else + return subrange(first, first); + } + + template + BOOST_CXX14_CONSTEXPR subrange find_last_impl( + I first, + I last, + Pred pred, + Proj proj, + std::bidirectional_iterator_tag) + { + for (auto it = last; first != it;) { + if (pred(proj(*--it))) + return subrange(it, last); + } + return subrange(last, last); + } + + template + struct eq_value + { + BOOST_CXX14_CONSTEXPR eq_value(const T & value) : value_(value) {} + template + BOOST_CXX14_CONSTEXPR bool operator()(const U & other) const + { + return other == value_; + } + const T & value_; + }; + + template + struct not_pred + { + BOOST_CXX14_CONSTEXPR not_pred(Pred pred) : pred_(pred) {} + template + BOOST_CXX14_CONSTEXPR bool operator()(const T & value) const + { + return !pred_(value); + } + Pred pred_; + }; + } + +#if defined(__cpp_lib_concepts) + template< + std::forward_iterator ForwardIterator, + std::sentinel_for Sentinel, + typename T, + typename Proj = identity> + requires std::indirect_binary_predicate < std::ranges::equal_to, + std::projected, + const T * > +#else + template< + typename ForwardIterator, + typename Sentinel, + typename T, + typename Proj = identity> +#endif + BOOST_CXX14_CONSTEXPR subrange find_last( + ForwardIterator first, + Sentinel last, + const T & value, + Proj proj = {}) + { + return detail::find_last_impl( + first, + last, + detail::eq_value(value), + proj, + typename std::iterator_traits< + ForwardIterator>::iterator_category()); + } + +#if defined(__cpp_lib_concepts) + template + requires std::indirect_binary_predicate < std::ranges::equal_to, + std::projected, Proj>, + const T * > +#else + template +#endif + BOOST_CXX14_CONSTEXPR subrange> + find_last(R && r, const T & value, Proj proj = {}) + { + return algorithm::find_last( + boost::begin(r), boost::end(r), value, proj); + } + +#if defined(__cpp_lib_concepts) + template< + std::forward_iterator ForwardIterator, + std::sentinel_for Sentinel, + typename Proj = identity, + std::indirect_unary_predicate> + Pred> +#else + template< + typename ForwardIterator, + typename Sentinel, + typename Pred, + typename Proj = identity> +#endif + BOOST_CXX14_CONSTEXPR subrange find_last_if( + ForwardIterator first, Sentinel last, Pred pred, Proj proj = {}) + { + return detail::find_last_impl( + first, + last, + pred, + proj, + typename std::iterator_traits< + ForwardIterator>::iterator_category()); + } + +#if defined(__cpp_lib_concepts) + template< + std::ranges::forward_range R, + typename Proj = std::identity, + std::indirect_unary_predicate, Proj>> Pred> +#else + template +#endif + BOOST_CXX14_CONSTEXPR subrange> + find_last_if(R && r, Pred pred, Proj proj = {}) + { + return algorithm::find_last_if( + boost::begin(r), boost::end(r), pred, proj); + } + +#if defined(__cpp_lib_concepts) + template< + std::forward_iterator ForwardIterator, + std::sentinel_for Sentinel, + typename Proj = identity, + std::indirect_unary_predicate> + Pred> +#else + template< + typename ForwardIterator, + typename Sentinel, + typename Pred, + typename Proj = identity> +#endif + BOOST_CXX14_CONSTEXPR subrange find_last_if_not( + ForwardIterator first, Sentinel last, Pred pred, Proj proj = {}) + { + return detail::find_last_impl( + first, + last, + detail::not_pred(pred), + proj, + typename std::iterator_traits< + ForwardIterator>::iterator_category()); + } + +#if defined(__cpp_lib_concepts) + template< + std::ranges::forward_range R, + typename Proj = identity, + std::indirect_unary_predicate, Proj>> Pred> +#else + template +#endif + BOOST_CXX14_CONSTEXPR subrange> + find_last_if_not(R && r, Pred pred, Proj proj = {}) + { + return algorithm::find_last_if_not( + boost::begin(r), boost::end(r), pred, proj); + } + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_FIND_LAST_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5f5e73cc8..303bb2bb3 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -64,6 +64,9 @@ alias unit_test_framework [ run transform_exclusive_scan_test.cpp unit_test_framework : : : : transform_exclusive_scan_test ] # Maybe GCD and LCM as well +# Cxx23 tests + [ run find_last_test.cpp unit_test_framework : : : : find_last_test ] + # Hex tests [ run hex_test1.cpp unit_test_framework : : : : hex_test1 ] [ run hex_test2.cpp unit_test_framework : : : : hex_test2 ] diff --git a/test/find_last_test.cpp b/test/find_last_test.cpp new file mode 100644 index 000000000..18b846d0f --- /dev/null +++ b/test/find_last_test.cpp @@ -0,0 +1,498 @@ +/* + Copyright (c) T. Zachary Laine 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) + + For more information, see http://www.boost.org +*/ +#include + +#include + +#define BOOST_TEST_MAIN +#include + +#include +#include +#include + + +namespace ba = boost::algorithm; + +template +struct dist_t +{ + dist_t(Container & cont) : cont_(cont) {} + template + std::ptrdiff_t operator()(Range const & r) const + { + return std::distance(cont_.begin(), r.begin()); + } + + Container & cont_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_last() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int * from = in_data; + const int * to = in_data + 5; + + auto result = ba::find_last(from, to, 1); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last(in_data, 1); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last(from, to, 6); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = ba::find_last(in_data, 6); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = ba::find_last(from, to, 3); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + result = ba::find_last(in_data, 3); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + return res; +} + +template +struct sentinel +{ + friend bool operator==(Iter it, sentinel last) { return it == last.last_; } + friend bool operator!=(Iter it, sentinel last) { return it != last.last_; } + + Iter last_; +}; + +void test_find_last() +{ + { + std::vector v1; + const dist_t> dist(v1); + + for (int i = 5; i < 15; ++i) + v1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1.end(), 0)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1.end(), 100)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1.end(), v1.back())), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1.end(), v1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, 0)), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, 100)), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, v1.back())), v1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, v1.front())), 0); + } + // With sentinels. + { + std::vector v1; + const dist_t> dist(v1); + sentinel::iterator> v1_end; + + for (int i = 5; i < 15; ++i) + v1.push_back(i); + v1_end = sentinel::iterator>{v1.end()}; + + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1_end, 0)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1_end, 100)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1_end, v1.back())), v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last(v1.begin(), v1_end, v1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, 0)), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, 100)), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, v1.back())), v1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last(v1, v1.front())), 0); + } + + // With bidirectional iterators. + { + std::list l1; + const dist_t> dist(l1); + + for (int i = 5; i < 15; ++i) + l1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), 0)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), 100)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.back())), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 0)), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 100)), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.back())), l1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.front())), 0); + } + // With bidirectional iterator/sentinel. + { + std::list l1; + const dist_t> dist(l1); + sentinel::iterator> l1_end; + + for (int i = 5; i < 15; ++i) + l1.push_back(i); + l1_end = sentinel::iterator>{l1.end()}; + + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), 0)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), 100)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.back())), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 0)), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 100)), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.back())), l1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.front())), 0); + } + + // With forward iterators. + { + std::forward_list l1; + const dist_t> dist(l1); + + for (int i = 14; 5 <= i; --i) + l1.push_front(i); + const auto back = *std::next(l1.begin(), 9); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), 0)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), 100)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), back)), 9); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 0)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 100)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, back)), 9); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.front())), 0); + } + // With forward iterator/sentinel. + { + std::forward_list l1; + const dist_t> dist(l1); + sentinel::iterator> l1_end; + + for (int i = 14; 5 <= i; --i) + l1.push_front(i); + l1_end = sentinel::iterator>{l1.end()}; + const auto back = *std::next(l1.begin(), 9); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), 0)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), 100)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1.begin(), l1.end(), back)), 9); + BOOST_CHECK_EQUAL( + dist(ba::find_last(l1.begin(), l1.end(), l1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 0)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, 100)), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, back)), 9); + BOOST_CHECK_EQUAL(dist(ba::find_last(l1, l1.front())), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_last(); + BOOST_CHECK(ce_result); +} + +struct equals +{ + BOOST_CXX14_CONSTEXPR equals(int n) : n_(n) {} + BOOST_CXX14_CONSTEXPR bool operator()(int i) const { return i == n_; } + int n_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_if_last() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int * from = in_data; + const int * to = in_data + 5; + + auto result = ba::find_last_if(from, to, equals(1)); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last_if(in_data, equals(1)); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last_if(from, to, equals(6)); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = ba::find_last_if(in_data, equals(6)); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = ba::find_last_if(from, to, equals(3)); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + result = ba::find_last_if(in_data, equals(3)); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + return res; +} + +void test_find_last_if() +{ + { + std::vector v1; + const dist_t> dist(v1); + + for (int i = 5; i < 15; ++i) + v1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(v1.begin(), v1.end(), equals(0))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(v1.begin(), v1.end(), equals(100))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(v1.begin(), v1.end(), equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(v1.begin(), v1.end(), equals(v1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last_if(v1, equals(0))), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(v1, equals(100))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(v1, equals(v1.back()))), v1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(v1, equals(v1.front()))), 0); + } + + // With bidirectional iterators. + { + std::list l1; + const dist_t> dist(l1); + + for (int i = 5; i < 15; ++i) + l1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(0))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(100))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(0))), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(100))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1, equals(l1.back()))), l1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(l1.front()))), 0); + } + + // With forward iterators. + { + std::forward_list l1; + const dist_t> dist(l1); + + for (int i = 14; 5 <= i; --i) + l1.push_front(i); + const auto back = *std::next(l1.begin(), 9); + + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(0))), 10); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(100))), 10); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(back))), 9); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if(l1.begin(), l1.end(), equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(0))), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(100))), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(back))), 9); + BOOST_CHECK_EQUAL(dist(ba::find_last_if(l1, equals(l1.front()))), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_if_last(); + BOOST_CHECK(ce_result); +} + +struct not_equals +{ + BOOST_CXX14_CONSTEXPR not_equals(int n) : n_(n) {} + BOOST_CXX14_CONSTEXPR bool operator()(int i) const { return i != n_; } + int n_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_if_not_last() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int * from = in_data; + const int * to = in_data + 5; + + auto result = + ba::find_last_if_not(from, to, not_equals(1)); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last_if_not(in_data, not_equals(1)); // stops on first + res = (res && result.begin() == from); + res = (res && result.end() == to); + + result = ba::find_last_if_not(from, to, not_equals(6)); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = ba::find_last_if_not(in_data, not_equals(6)); // stops on the end + res = (res && result.begin() == to); + res = (res && result.end() == to); + + result = + ba::find_last_if_not(from, to, not_equals(3)); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + result = + ba::find_last_if_not(in_data, not_equals(3)); // stops on third element + res = (res && result.begin() == in_data + 2); + res = (res && result.end() == to); + + return res; +} + +void test_find_last_if_not() +{ + { + std::vector v1; + const dist_t> dist(v1); + + for (int i = 5; i < 15; ++i) + v1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1.begin(), v1.end(), not_equals(0))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1.begin(), v1.end(), not_equals(100))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not( + v1.begin(), v1.end(), not_equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not( + v1.begin(), v1.end(), not_equals(v1.front()))), + 0); + + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1, not_equals(0))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1, not_equals(100))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1, not_equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(v1, not_equals(v1.front()))), 0); + } + + // With bidirectional iterators. + { + std::list l1; + const dist_t> dist(l1); + + for (int i = 5; i < 15; ++i) + l1.push_back(i); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1.begin(), l1.end(), not_equals(0))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1.begin(), l1.end(), not_equals(100))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not( + l1.begin(), l1.end(), not_equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not( + l1.begin(), l1.end(), not_equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1, not_equals(0))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1, not_equals(100))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1, not_equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1, not_equals(l1.front()))), 0); + } + + // With forward iterators. + { + std::forward_list l1; + const dist_t> dist(l1); + + for (int i = 14; 5 <= i; --i) + l1.push_front(i); + const auto back = *std::next(l1.begin(), 9); + + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1.begin(), l1.end(), not_equals(0))), + 10); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1.begin(), l1.end(), not_equals(100))), + 10); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1.begin(), l1.end(), not_equals(back))), + 9); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not( + l1.begin(), l1.end(), not_equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_last_if_not(l1, not_equals(0))), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last_if_not(l1, not_equals(100))), 10); + BOOST_CHECK_EQUAL(dist(ba::find_last_if_not(l1, not_equals(back))), 9); + BOOST_CHECK_EQUAL( + dist(ba::find_last_if_not(l1, not_equals(l1.front()))), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_if_not_last(); + BOOST_CHECK(ce_result); +} + +BOOST_AUTO_TEST_CASE(test_main) +{ + test_find_last(); + test_find_last_if(); + test_find_last_if_not(); +}