diff --git a/doc/algorithm.qbk b/doc/algorithm.qbk index 8ce6685..3d1230f 100644 --- a/doc/algorithm.qbk +++ b/doc/algorithm.qbk @@ -64,6 +64,8 @@ Thanks to all the people who have reviewed this library and made suggestions for [section:Misc Other Algorithms] [include clamp-hpp.qbk] +[include find_not.qbk] +[include find_backward.qbk] [include gather.qbk] [include hex.qbk] [include is_palindrome.qbk] diff --git a/doc/find_backward.qbk b/doc/find_backward.qbk new file mode 100644 index 0000000..838dbc9 --- /dev/null +++ b/doc/find_backward.qbk @@ -0,0 +1,116 @@ +[/ File find_backward.qbk] + +[section:find_backward find_backward ] + +[/license +Copyright (c) 2018 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_backward.hpp' contains variants of the stl algorithm +`find`. These variants are like `find`, except that the evaluate the elements +of the given sequence in reverse order. + +Consider how finding the last element that is equal to `x` in a range is +typically done: + + // Assume a valid range if elements delimited by [first, last). + while (last-- != first) { + if (*last == x) { + // Use last here... + } + } + +Raw loops are icky though. Perhaps we should do a bit of extra work to allow +the use of `std::find()`: + + auto rfirst = std::make_reverse_iterator(last); + auto rlast = std::make_reverse_iterator(first); + auto it = std::find(rfirst, rlast); + // Use it here... + +That seems nicer in that there is no raw loop, but it has two major drawbacks. +First, it requires an unpleasant amount of typing. Second, it is less +efficient than forward-iterator `find` , since `std::reverse_iterator` calls +its base-iterator's `operator--()` in most of its member functions before +doing the work that the member function requires. + +[heading interface] + + template + BidiIter find_backward(BidiIter first, BidiIter last, const T & x); + + template + boost::range_iterator find_backward(Range & range, const T & x); + +These overloads of `find_backward` return an iterator to the last element that +is equal to `x` in `[first, last)` or `r`, respectively. + + template + BidiIter find_not_backward(BidiIter first, BidiIter last, const T & x); + + template + boost::range_iterator find_not_backward(Range & range, const T & x); + +These overloads of `find_not_backward` return an iterator to the last element +that is not equal to `x` in `[first, last)` or `r`, respectively. + + template + BidiIter find_if_backward(BidiIter first, BidiIter last, Pred p); + + template + boost::range_iterator find_if_backward(Range & range, Pred p); + +These overloads of `find_if_backward` return an iterator to the last element +for which `pred` returns `true` in `[first, last)` or `r`, respectively. + + template + BidiIter find_if_not_backward(BidiIter first, BidiIter last, Pred p); + + template + boost::range_iterator find_if_not_backward(Range & range, Pred p); + +These overloads of `find_if_not_backward` return an iterator to the last +element for which `pred` returns `false` in `[first, last)` or `r`, +respectively. + +[heading Examples] + +Given the container `c1` containing `{ 2, 1, 2 }`, then + + find_backward ( c1.begin(), c1.end(), 2 ) --> --c1.end() + find_backward ( c1.begin(), c1.end(), 3 ) --> c1.end() + find_if_backward ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> --c1.end() + find_if_backward ( c1.begin(), c1.end(), [](int i) {return i == 3;} ) --> c1.end() + find_not_backward ( c1.begin(), c1.end(), 2 ) --> std::prev(c1.end(), 2) + find_not_backward ( c1.begin(), c1.end(), 1 ) --> c1.end() + find_if_not_backward ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> std::prev(c1.end(), 2) + find_if_not_backward ( c1.begin(), c1.end(), [](int i) {return i == 1;} ) --> c1.end() + +[heading Iterator Requirements] + +All variants work on bidirectional 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 equal.qbk +Copyright 2018 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/doc/find_not.qbk b/doc/find_not.qbk new file mode 100644 index 0000000..6df0482 --- /dev/null +++ b/doc/find_not.qbk @@ -0,0 +1,83 @@ +[/ File find_not.qbk] + +[section:find_not find_not ] + +[/license +Copyright (c) 2018 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_not.hpp' contains a variants of a the stl algorithm +`find`. The algorithm finds the first value in the given sequence that is not +equal to the given value. + +Consider this use of `find()`: + + auto std::vector vec = { 1, 1, 2 }; + auto it = std::find(vec.begin(), vec.end(), 1); + +This gives us the first occurance of `1` in `vec`. What if we want to find +the first occurrance of any number besides `1` in `vec`? We have to write an +unfortunate amount of code: + + auto std::vector vec = { 1, 1, 2 }; + auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i != 1; }); + +With `find_not()` the code gets much more terse: + + auto std::vector vec = { 1, 1, 2 }; + auto it = find_not(vec.begin(), vec.end(), 1); + +The existing `find` variants are: `find()`, `find_if()`, and `find_if_not()`. +It seems natural to also have `find_not()`, for the very reason that we have +`find_if_not()` -- to avoid having to write a lambda to wrap the negation of +the find condition. + +[heading interface] + + template + InputIter find_not(InputIter first, Sentinel last, const T & x); + + template + boost::range_iterator find_not(Range & r, const T & x); + +These overloads of `find_not` return the first value that is not equal to `x` +in the sequence `[first, last)` or `r`, respectively. + +[heading Examples] + +Given the container `c1` containing `{ 0, 1, 2 }`, then + + find_not ( c1.begin(), c1.end(), 1 ) --> c1.begin() + find_not ( c1.begin(), c1.end(), 0 ) --> std::next(c1.begin()) + +[heading Iterator Requirements] + +`find_not` works on all iterators except output iterators. + +The template parameter `Sentinel` is allowed to be different from `InputIter`, +or they may be the same. For an `InputIter` `it` and a `Sentinel` `end`, `it +== end` and `it != end` must be well-formed expressions. + +[heading Complexity] + +Linear. + +[heading Exception Safety] + +`find_not` takes its parameters by value and do not depend upon any global +state. Therefore, it provides the strong exception guarantee. + +[heading Notes] + +`constexpr` in C++14 or later. + +[endsect] + +[/ File equal.qbk +Copyright 2018 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/find_backward.hpp b/include/boost/algorithm/find_backward.hpp new file mode 100644 index 0000000..66901a1 --- /dev/null +++ b/include/boost/algorithm/find_backward.hpp @@ -0,0 +1,97 @@ +/* + Copyright (c) T. Zachary Laine 2018. + + 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_BACKWARD_HPP +#define BOOST_ALGORITHM_FIND_BACKWARD_HPP + +#include +#include +#include + +#include + + +namespace boost { namespace algorithm { + +template +BOOST_CXX14_CONSTEXPR +BidiIter find_backward(BidiIter first, BidiIter last, const T & x) +{ + BidiIter it = last; + while (it != first) { + if (*--it == x) + return it; + } + return last; +} + +template +BOOST_CXX14_CONSTEXPR +typename boost::range_iterator::type find_backward(Range & range, const T & x) +{ + return ::boost::algorithm::find_backward(boost::begin(range), boost::end(range), x); +} + +template +BOOST_CXX14_CONSTEXPR +BidiIter find_not_backward(BidiIter first, BidiIter last, const T & x) +{ + BidiIter it = last; + while (it != first) { + if (*--it != x) + return it; + } + return last; +} + +template +BOOST_CXX14_CONSTEXPR +typename boost::range_iterator::type find_not_backward(Range & range, const T & x) +{ + return ::boost::algorithm::find_not_backward(boost::begin(range), boost::end(range), x); +} + +template +BOOST_CXX14_CONSTEXPR +BidiIter find_if_backward(BidiIter first, BidiIter last, Pred p) +{ + BidiIter it = last; + while (it != first) { + if (p(*--it)) + return it; + } + return last; +} + +template +BOOST_CXX14_CONSTEXPR +typename boost::range_iterator::type find_if_backward(Range & range, Pred p) +{ + return ::boost::algorithm::find_if_backward(boost::begin(range), boost::end(range), p); +} + +template +BOOST_CXX14_CONSTEXPR +BidiIter find_if_not_backward(BidiIter first, BidiIter last, Pred p) +{ + BidiIter it = last; + while (it != first) { + if (!p(*--it)) + return it; + } + return last; +} + +template +BOOST_CXX14_CONSTEXPR +typename boost::range_iterator::type find_if_not_backward(Range & range, Pred p) +{ + return ::boost::algorithm::find_if_not_backward(boost::begin(range), boost::end(range), p); +} + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_FIND_BACKWARD_HPP diff --git a/include/boost/algorithm/find_not.hpp b/include/boost/algorithm/find_not.hpp new file mode 100644 index 0000000..ef4df00 --- /dev/null +++ b/include/boost/algorithm/find_not.hpp @@ -0,0 +1,39 @@ +/* + Copyright (c) T. Zachary Laine 2018. + + 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_NOT_HPP +#define BOOST_ALGORITHM_FIND_NOT_HPP + +#include +#include +#include + +#include + + +namespace boost { namespace algorithm { + +template +BOOST_CXX14_CONSTEXPR +InputIter find_not(InputIter first, Sentinel last, const T & x) +{ + for (; first != last; ++first) { + if (*first != x) + break; + } + return first; +} + +template +BOOST_CXX14_CONSTEXPR +typename boost::range_iterator::type find_not(Range & r, const T & x) +{ + return ::boost::algorithm::find_not(boost::begin(r), boost::end(r), x); +} + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_FIND_NOT_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 813be2c..30cb786 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -85,7 +85,10 @@ alias unit_test_framework [ run is_partitioned_until_test.cpp unit_test_framework : : : : is_partitioned_until_test ] # Apply_permutation tests - [ run apply_permutation_test.cpp unit_test_framework : : : : apply_permutation_test ] + [ 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_backward_test.cpp unit_test_framework : : : : find_backward_test ] ; } diff --git a/test/find_backward_test.cpp b/test/find_backward_test.cpp new file mode 100644 index 0000000..f78fadb --- /dev/null +++ b/test/find_backward_test.cpp @@ -0,0 +1,420 @@ +/* + Copyright (c) T. Zachary Laine 2018. + + 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 + + +namespace ba = boost::algorithm; + +template +struct dist_t +{ + dist_t(Container & cont) : cont_(cont) {} + template + std::ptrdiff_t operator()(Iter it) const + { + return std::distance(cont_.begin(), it); + } + + Container & cont_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_backward() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int* from = in_data; + const int* to = in_data + 5; + + const int* start = ba::find_backward(from, to, 1); // stops on first + res = (res && start == from); + + start = ba::find_backward(in_data, 1); // stops on first + res = (res && start == from); + + const int* end = ba::find_backward(from, to, 6); // stops on the end + res = (res && end == to); + + end = ba::find_backward(in_data, 6); // stops on the end + res = (res && end == to); + + const int* three = ba::find_backward(from, to, 3); // stops on third element + res = (res && three == in_data + 2); + + three = ba::find_backward(in_data, 3); // stops on third element + res = (res && three == in_data + 2); + + return res; +} + +void test_find_backward() +{ + { + 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_backward(v1.begin(), v1.end(), 0)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(v1.begin(), v1.end(), 100)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(v1.begin(), v1.end(), v1.back())), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(v1.begin(), v1.end(), v1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_backward(v1, 0)), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_backward(v1, 100)), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(v1, v1.back())), v1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_backward(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_backward(l1.begin(), l1.end(), 0)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(l1.begin(), l1.end(), 100)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(l1.begin(), l1.end(), l1.back())), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(l1.begin(), l1.end(), l1.front())), 0); + + BOOST_CHECK_EQUAL(dist(ba::find_backward(l1, 0)), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_backward(l1, 100)), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_backward(l1, l1.back())), l1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_backward(l1, l1.front())), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_backward(); + BOOST_CHECK(ce_result); +} + +struct equals +{ + BOOST_CXX14_CONSTEXPR equals(int n) : n_(n) {} + BOOST_CXX14_CONSTEXPR bool operator()(int i) { return i == n_; } + int n_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_if_backward() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int* from = in_data; + const int* to = in_data + 5; + + const int* start = ba::find_if_backward(from, to, equals(1)); // stops on first + res = (res && start == from); + + start = ba::find_if_backward(in_data, equals(1)); // stops on first + res = (res && start == from); + + const int* end = ba::find_if_backward(from, to, equals(6)); // stops on the end + res = (res && end == to); + + end = ba::find_if_backward(in_data, equals(6)); // stops on the end + res = (res && end == to); + + const int* three = ba::find_if_backward(from, to, equals(3)); // stops on third element + res = (res && three == in_data + 2); + + three = ba::find_if_backward(in_data, equals(3)); // stops on third element + res = (res && three == in_data + 2); + + return res; +} + +void test_find_if_backward() +{ + { + 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_if_backward(v1.begin(), v1.end(), equals(0))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(v1.begin(), v1.end(), equals(100))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(v1.begin(), v1.end(), equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist( + ba::find_if_backward(v1.begin(), v1.end(), equals(v1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_if_backward(v1, equals(0))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(v1, equals(100))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(v1, equals(v1.back()))), v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(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_if_backward(l1.begin(), l1.end(), equals(0))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(l1.begin(), l1.end(), equals(100))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(l1.begin(), l1.end(), equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist( + ba::find_if_backward(l1.begin(), l1.end(), equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL(dist(ba::find_if_backward(l1, equals(0))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(l1, equals(100))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(l1, equals(l1.back()))), l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_backward(l1, equals(l1.front()))), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_if_backward(); + BOOST_CHECK(ce_result); +} + +struct not_equals +{ + BOOST_CXX14_CONSTEXPR not_equals(int n) : n_(n) {} + BOOST_CXX14_CONSTEXPR bool operator()(int i) { return i != n_; } + int n_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr_if_not_backward() +{ + int in_data[] = {1, 2, 3, 4, 5}; + bool res = true; + + const int* from = in_data; + const int* to = in_data + 5; + + const int* start = ba::find_if_not_backward(from, to, not_equals(1)); // stops on first + res = (res && start == from); + + start = ba::find_if_not_backward(in_data, not_equals(1)); // stops on first + res = (res && start == from); + + const int* end = ba::find_if_not_backward(from, to, not_equals(6)); // stops on the end + res = (res && end == to); + + end = ba::find_if_not_backward(in_data, not_equals(6)); // stops on the end + res = (res && end == to); + + const int* three = ba::find_if_not_backward(from, to, not_equals(3)); // stops on third element + res = (res && three == in_data + 2); + + three = ba::find_if_not_backward(in_data, not_equals(3)); // stops on third element + res = (res && three == in_data + 2); + + return res; +} + +void test_find_if_not_backward() +{ + { + 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_if_not_backward(v1.begin(), v1.end(), not_equals(0))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + v1.begin(), v1.end(), not_equals(100))), + v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + v1.begin(), v1.end(), not_equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + v1.begin(), v1.end(), not_equals(v1.front()))), + 0); + + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(v1, not_equals(0))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(v1, not_equals(100))), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(v1, not_equals(v1.back()))), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(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_if_not_backward(l1.begin(), l1.end(), not_equals(0))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + l1.begin(), l1.end(), not_equals(100))), + l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + l1.begin(), l1.end(), not_equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward( + l1.begin(), l1.end(), not_equals(l1.front()))), + 0); + + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(l1, not_equals(0))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(l1, not_equals(100))), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(l1, not_equals(l1.back()))), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_if_not_backward(l1, not_equals(l1.front()))), 0); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_if_not_backward(); + BOOST_CHECK(ce_result); +} + +BOOST_CXX14_CONSTEXPR bool check_constexpr_not_backward() +{ + int in_data[] = {1, 5, 5, 5, 5}; + bool res = true; + + const int* from = in_data; + const int* to = in_data + 5; + + const int* start = ba::find_not_backward(from, to, 5); // stops on first + res = (res && start == from); + + start = ba::find_not_backward(in_data, 5); // stops on first + res = (res && start == from); + + const int in_data_2[] = {6, 6, 6, 6, 6}; + const int* end = ba::find_not_backward(in_data_2, in_data_2 + 5, 6); // stops on the end + res = (res && end == in_data_2 + 5); + + end = ba::find_not_backward(in_data_2, 6); // stops on the end + res = (res && end == in_data_2 + 5); + + return res; +} + +void test_find_not_backward() +{ + { + std::vector v1; + const dist_t > dist(v1); + + for (int i = 0; i < 5; ++i) + v1.push_back(0); + for (int i = 0; i < 5; ++i) + v1.push_back(1); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(v1.begin(), v1.end(), 1)), 4); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(v1.begin(), v1.end(), 0)), + v1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(v1.begin(), v1.end(), 2)), + v1.size() - 1); + + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(v1, 1)), 4); + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(v1, 0)), v1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(v1, 2)), v1.size() - 1); + + v1.resize(5); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(v1.begin(), v1.end(), 0)), v1.size()); + + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(v1, 0)), v1.size()); + } + + // With bidirectional iterators. + { + std::list l1; + const dist_t > dist(l1); + + for (int i = 0; i < 5; ++i) + l1.push_back(0); + for (int i = 0; i < 5; ++i) + l1.push_back(1); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(l1.begin(), l1.end(), 1)), 4); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(l1.begin(), l1.end(), 0)), + l1.size() - 1); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(l1.begin(), l1.end(), 2)), + l1.size() - 1); + + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(l1, 1)), 4); + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(l1, 0)), l1.size() - 1); + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(l1, 2)), l1.size() - 1); + + l1.resize(5); + BOOST_CHECK_EQUAL( + dist(ba::find_not_backward(l1.begin(), l1.end(), 0)), l1.size()); + + BOOST_CHECK_EQUAL(dist(ba::find_not_backward(l1, 0)), l1.size()); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr_not_backward(); + BOOST_CHECK(ce_result); +} + +BOOST_AUTO_TEST_CASE(test_main) +{ + test_find_backward(); + test_find_if_backward(); + test_find_if_not_backward(); + test_find_not_backward(); +} diff --git a/test/find_not_test.cpp b/test/find_not_test.cpp new file mode 100644 index 0000000..ef7529f --- /dev/null +++ b/test/find_not_test.cpp @@ -0,0 +1,134 @@ +/* + Copyright (c) T. Zachary Laine 2018. + + 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 + + +namespace ba = boost::algorithm; + +template +struct dist_t +{ + dist_t(Container & cont) : cont_(cont) {} + template + std::ptrdiff_t operator()(Iter it) const + { + return std::distance(cont_.begin(), it); + } + + Container & cont_; +}; + +BOOST_CXX14_CONSTEXPR bool check_constexpr() +{ + int in_data[] = {2, 2, 3, 4, 5}; + bool res = true; + + const int* from = in_data; + const int* to = in_data + 5; + + const int* start = ba::find_not(from, to, 1); // stops on first + res = (res && start == from); + + start = ba::find_not(in_data, 1); // stops on first + res = (res && start == from); + + int in_data_2[] = {6, 6, 6, 6, 6}; + const int* end = ba::find_not(in_data_2, in_data_2 + 5, 6); // stops on the end + res = (res && end == in_data_2 + 5); + + end = ba::find_not(in_data_2, 6); // stops on the end + res = (res && end == in_data_2 + 5); + + const int* three = ba::find_not(from, to, 2); // stops on third element + res = (res && three == in_data + 2); + + three = ba::find_not(in_data, 2); // stops on third element + res = (res && three == in_data + 2); + + return res; +} + +void test_sequence() +{ + { + 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_not(v1.begin(), v1.end(), 0)), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(v1.begin(), v1.end(), v1.back())), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(v1.begin(), v1.end(), v1.front())), 1); + + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, 0)), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, v1.back())), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, v1.front())), 1); + + v1 = std::vector(10, 2); + BOOST_CHECK_EQUAL(dist(ba::find_not(v1.begin(), v1.end(), 0)), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(v1.begin(), v1.end(), v1.back())), v1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_not(v1.begin(), v1.end(), v1.front())), v1.size()); + + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, 0)), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, v1.back())), v1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_not(v1, v1.front())), v1.size()); + } + + // 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_not(l1.begin(), l1.end(), 0)), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(l1.begin(), l1.end(), l1.back())), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(l1.begin(), l1.end(), l1.front())), 1); + + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, 0)), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, l1.back())), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, l1.front())), 1); + + l1.clear(); + for (int i = 0; i < 10; ++i) + l1.push_back(2); + BOOST_CHECK_EQUAL(dist(ba::find_not(l1.begin(), l1.end(), 0)), 0); + BOOST_CHECK_EQUAL( + dist(ba::find_not(l1.begin(), l1.end(), l1.back())), l1.size()); + BOOST_CHECK_EQUAL( + dist(ba::find_not(l1.begin(), l1.end(), l1.front())), l1.size()); + + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, 0)), 0); + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, l1.back())), l1.size()); + BOOST_CHECK_EQUAL(dist(ba::find_not(l1, l1.front())), l1.size()); + } + + BOOST_CXX14_CONSTEXPR bool ce_result = check_constexpr(); + BOOST_CHECK(ce_result); +} + + +BOOST_AUTO_TEST_CASE(test_main) +{ + test_sequence(); +}