diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..11961b1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,171 @@ +# Copyright 2016, 2017, 2018 Peter Dimov +# 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://boost.org/LICENSE_1_0.txt) + +language: cpp + +sudo: false + +branches: + only: + - master + - develop + - /feature\/.*/ + +env: + matrix: + - BOGUS_JOB=true + +matrix: + + exclude: + - env: BOGUS_JOB=true + + include: + - os: linux + env: TOOLSET=gcc COMPILER=g++ CXXSTD=11 + + - os: linux + env: TOOLSET=gcc COMPILER=g++-4.7 CXXSTD=11 + addons: + apt: + packages: + - g++-4.7 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-4.8 CXXSTD=11 + addons: + apt: + packages: + - g++-4.8 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-4.9 CXXSTD=11 + addons: + apt: + packages: + - g++-4.9 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-5 CXXSTD=11,14,1z + addons: + apt: + packages: + - g++-5 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-6 CXXSTD=11,14,1z + addons: + apt: + packages: + - g++-6 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-6 CXXSTD=11,14,1z CXXSTD_DIALECT=cxxstd-dialect=gnu + addons: + apt: + packages: + - g++-6 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=gcc COMPILER=g++-7 CXXSTD=11,14,17 + addons: + apt: + packages: + - g++-7 + sources: + - ubuntu-toolchain-r-test + + - os: linux + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11 + + - os: linux + compiler: clang++-libc++ + env: TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,1z + addons: + apt: + packages: + - libc++-dev + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode9.1 + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode9 + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode8.3 + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode8 + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode7.3 + + - os: osx + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + osx_image: xcode6.4 + +install: + - BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true + - cd .. + - git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + - cd boost-root + - git submodule update --init tools/build + - git submodule update --init libs/config + - git submodule update --init libs/predef + - git submodule update --init libs/core + - git submodule update --init libs/detail + - git submodule update --init libs/range + - git submodule update --init libs/assert + - git submodule update --init libs/array + - git submodule update --init libs/type_traits + - git submodule update --init libs/static_assert + - git submodule update --init libs/iterator + - git submodule update --init libs/preprocessor + - git submodule update --init libs/mpl + - git submodule update --init libs/smart_ptr + - git submodule update --init libs/callable_traits + - git submodule update --init libs/type_index + - git submodule update --init libs/exception + - git submodule update --init libs/throw_exception + - git submodule update --init libs/utility + - git submodule update --init libs/bind + - git submodule update --init libs/ratio + - git submodule update --init libs/function + - git submodule update --init libs/integer + - git submodule update --init libs/numeric + - git submodule update --init libs/move + - git submodule update --init libs/container_hash + - git submodule update --init libs/io + - git submodule update --init libs/concept_check + - git submodule update --init libs/test + - git submodule update --init libs/timer + - git submodule update --init libs/chrono + - git submodule update --init libs/system + - cp -r $TRAVIS_BUILD_DIR/* libs/algorithm + - ./bootstrap.sh + - ./b2 headers + +script: + - |- + echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam + - ./b2 -j3 libs/algorithm/test toolset=$TOOLSET cxxstd=$CXXSTD $CXXSTD_DIALECT diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..6cdae88 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,93 @@ +# Copyright 2016 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +version: 1.0.{build}-{branch} + +shallow_clone: true + +branches: + only: + - master + - develop + +platform: + - x64 + +environment: + matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ARGS: --toolset=msvc-14.1 address-model=64 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ARGS: --toolset=msvc-14.1 address-model=32 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + ARGS: --toolset=msvc-14.1 address-model=64 cxxflags=-std:c++latest cxxflags=-permissive- + - ARGS: --toolset=msvc-9.0 address-model=32 + - ARGS: --toolset=msvc-10.0 address-model=32 + - ARGS: --toolset=msvc-11.0 address-model=32 + - ARGS: --toolset=msvc-12.0 address-model=32 + - ARGS: --toolset=msvc-14.0 address-model=32 + - ARGS: --toolset=msvc-12.0 address-model=64 + - ARGS: --toolset=msvc-14.0 address-model=64 + - ARGS: --toolset=msvc-14.0 address-model=64 cxxflags=-std:c++latest + - ARGS: --toolset=gcc address-model=64 + CXXSTD: 03,11,14,1z + PATH: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH% + - ARGS: --toolset=gcc address-model=64 + CXXSTD: 03,11,14,1z + PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH% + - ARGS: --toolset=gcc address-model=32 linkflags=-Wl,-allow-multiple-definition + CXXSTD: 03,11,14,1z + PATH: C:\MinGW\bin;%PATH% + - ARGS: --toolset=gcc address-model=64 + CXXSTD: 03,11,14,1z + PATH: C:\cygwin64\bin;%PATH% + - ARGS: --toolset=gcc address-model=32 + CXXSTD: 03,11,14,1z + PATH: C:\cygwin\bin;%PATH% + +install: + - cd .. + - git clone -b %APPVEYOR_REPO_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + - cd boost-root + - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\algorithm + - git submodule update --init tools/build + - git submodule update --init libs/config + - git submodule update --init libs/predef + - git submodule update --init libs/core + - git submodule update --init libs/detail + - git submodule update --init libs/range + - git submodule update --init libs/assert + - git submodule update --init libs/array + - git submodule update --init libs/type_traits + - git submodule update --init libs/static_assert + - git submodule update --init libs/iterator + - git submodule update --init libs/preprocessor + - git submodule update --init libs/mpl + - git submodule update --init libs/smart_ptr + - git submodule update --init libs/callable_traits + - git submodule update --init libs/type_index + - git submodule update --init libs/exception + - git submodule update --init libs/throw_exception + - git submodule update --init libs/utility + - git submodule update --init libs/bind + - git submodule update --init libs/ratio + - git submodule update --init libs/function + - git submodule update --init libs/integer + - git submodule update --init libs/numeric + - git submodule update --init libs/move + - git submodule update --init libs/container_hash + - git submodule update --init libs/io + - git submodule update --init libs/concept_check + - git submodule update --init libs/test + - git submodule update --init libs/timer + - git submodule update --init libs/chrono + - git submodule update --init libs/system + - bootstrap + - b2 headers + +build: off + +test_script: + - cd libs\config\test + - ..\..\..\b2 -j3 %ARGS% cxxstd=%CXXSTD% 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/minmax/fuzzing/minmax_element.fuzz.cpp b/minmax/fuzzing/minmax_element.fuzz.cpp new file mode 100644 index 0000000..63b6a9b --- /dev/null +++ b/minmax/fuzzing/minmax_element.fuzz.cpp @@ -0,0 +1,81 @@ +// (C) Copyright Marshall Clow 2018 +// Use, modification and distribution are subject to 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 // for std::distance +#include // for assert + +#include +#include + +// Fuzzing tests for: +// +// template +// std::pair +// minmax_element(ForwardIterator first, ForwardIterator last); +// +// template +// std::pair +// minmax_element(ForwardIterator first, ForwardIterator last, +// BinaryPredicate comp); + + +bool greater(uint8_t lhs, uint8_t rhs) { return lhs > rhs; } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) { + typedef std::pair result_t; + if (sz == 0) return 0; // we need at least one element + + { +// Find the min and max + result_t result = boost::minmax_element(data, data + sz); + +// The iterators have to be in the sequence - and not at the end! + assert(std::distance(data, result.first) < sz); + assert(std::distance(data, result.second) < sz); + +// the minimum element can't be bigger than the max element + uint8_t min_value = *result.first; + uint8_t max_value = *result.second; + + assert(min_value <= max_value); + +// None of the elements in the sequence can be less than the min, nor greater than the max + for (size_t i = 0; i < sz; ++i) { + assert(min_value <= data[i]); + assert(data[i] <= max_value); + } + +// We returned the first min element, and the first max element + assert(boost::algorithm::none_of_equal(data, result.first, min_value)); + assert(boost::algorithm::none_of_equal(data, result.second, max_value)); + } + + { +// Find the min and max + result_t result = boost::minmax_element(data, data + sz, greater); + +// The iterators have to be in the sequence - and not at the end! + assert(std::distance(data, result.first) < sz); + assert(std::distance(data, result.second) < sz); + +// the minimum element can't be bigger than the max element + uint8_t min_value = *result.first; + uint8_t max_value = *result.second; + + assert (!greater(max_value, min_value)); + +// None of the elements in the sequence can be less than the min, nor greater than the max + for (size_t i = 0; i < sz; ++i) { + assert(!greater(data[i], min_value)); + assert(!greater(max_value, data[i])); + } + +// We returned the first min element, and the first max element + assert(boost::algorithm::none_of_equal(data, result.first, min_value)); + assert(boost::algorithm::none_of_equal(data, result.second, max_value)); + } + + return 0; +} diff --git a/minmax/fuzzing/minmax_element_variants.fuzz.cpp b/minmax/fuzzing/minmax_element_variants.fuzz.cpp new file mode 100644 index 0000000..ba517e2 --- /dev/null +++ b/minmax/fuzzing/minmax_element_variants.fuzz.cpp @@ -0,0 +1,141 @@ +// (C) Copyright Marshall Clow 2018 +// Use, modification and distribution are subject to 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 // for std::distance +#include // for assert + +#include +#include + +// Fuzzing tests for: +// +// template +// std::pair +// first_min_first_max_element(ForwardIterator first, ForwardIterator last); +// +// template +// std::pair +// first_min_first_max_element(ForwardIterator first, ForwardIterator last, +// BinaryPredicate comp); +// +// identical signatures for: +// first_min_last_max_element +// last_min_first_max_element +// last_min_last_max_element + +bool greater(uint8_t lhs, uint8_t rhs) { return lhs > rhs; } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) { + typedef std::pair result_t; + const uint8_t * const dend = data + sz; + if (sz == 0) return 0; // we need at least one element + + { +// Find the min and max + result_t resultff = boost::first_min_first_max_element(data, dend); + result_t resultfl = boost::first_min_last_max_element (data, dend); + result_t resultlf = boost::last_min_first_max_element (data, dend); + result_t resultll = boost::last_min_last_max_element (data, dend); + +// The iterators have to be in the sequence - and not at the end! + assert(std::distance(data, resultff.first) < sz); + assert(std::distance(data, resultff.second) < sz); + assert(std::distance(data, resultfl.first) < sz); + assert(std::distance(data, resultfl.second) < sz); + assert(std::distance(data, resultlf.first) < sz); + assert(std::distance(data, resultlf.second) < sz); + assert(std::distance(data, resultll.first) < sz); + assert(std::distance(data, resultll.second) < sz); + +// the minimum element can't be bigger than the max element + +// Did we find the same min value and max value? + uint8_t min_value = *resultff.first; + uint8_t max_value = *resultff.second; + assert(min_value <= max_value); + +// Each variant should have found the same min/max values + assert(*resultff.first == min_value); + assert(*resultfl.first == min_value); + assert(*resultlf.first == min_value); + assert(*resultll.first == min_value); + + assert(*resultff.second == max_value); + assert(*resultfl.second == max_value); + assert(*resultlf.second == max_value); + assert(*resultll.second == max_value); + +// None of the elements in the sequence can be less than the min, nor greater than the max + for (size_t i = 0; i < sz; ++i) { + assert(min_value <= data[i]); + assert(data[i] <= max_value); + } + +// Make sure we returned the "right" first and last element + assert(boost::algorithm::none_of_equal(data, resultff.first, min_value)); + assert(boost::algorithm::none_of_equal(data, resultfl.first, min_value)); + assert(boost::algorithm::none_of_equal(resultlf.first + 1, dend, min_value)); + assert(boost::algorithm::none_of_equal(resultll.first + 1, dend, min_value)); + + assert(boost::algorithm::none_of_equal(data, resultff.second, max_value)); + assert(boost::algorithm::none_of_equal(resultfl.second + 1, dend, max_value)); + assert(boost::algorithm::none_of_equal(data, resultlf.second, max_value)); + assert(boost::algorithm::none_of_equal(resultll.second + 1, dend, max_value)); + } + + { +// Find the min and max + result_t resultff = boost::first_min_first_max_element(data, dend, greater); + result_t resultfl = boost::first_min_last_max_element (data, dend, greater); + result_t resultlf = boost::last_min_first_max_element (data, dend, greater); + result_t resultll = boost::last_min_last_max_element (data, dend, greater); + +// The iterators have to be in the sequence - and not at the end! + assert(std::distance(data, resultff.first) < sz); + assert(std::distance(data, resultff.second) < sz); + assert(std::distance(data, resultfl.first) < sz); + assert(std::distance(data, resultfl.second) < sz); + assert(std::distance(data, resultlf.first) < sz); + assert(std::distance(data, resultlf.second) < sz); + assert(std::distance(data, resultll.first) < sz); + assert(std::distance(data, resultll.second) < sz); + +// the minimum element can't be bigger than the max element + uint8_t min_value = *resultff.first; + uint8_t max_value = *resultff.second; + + assert (!greater(max_value, min_value)); + +// Each variant should have found the same min/max values + assert(*resultff.first == min_value); + assert(*resultfl.first == min_value); + assert(*resultlf.first == min_value); + assert(*resultll.first == min_value); + + assert(*resultff.second == max_value); + assert(*resultfl.second == max_value); + assert(*resultlf.second == max_value); + assert(*resultll.second == max_value); + +// None of the elements in the sequence can be less than the min, nor greater than the max + for (size_t i = 0; i < sz; ++i) { + assert(!greater(data[i], min_value)); + assert(!greater(max_value, data[i])); + } + +// We returned the first min element, and the first max element + assert(boost::algorithm::none_of_equal(data, resultff.first, min_value)); + assert(boost::algorithm::none_of_equal(data, resultfl.first, min_value)); + assert(boost::algorithm::none_of_equal(resultlf.first + 1, dend, min_value)); + assert(boost::algorithm::none_of_equal(resultll.first + 1, dend, min_value)); + + assert(boost::algorithm::none_of_equal(data, resultff.second, max_value)); + assert(boost::algorithm::none_of_equal(resultfl.second + 1, dend, max_value)); + assert(boost::algorithm::none_of_equal(data, resultlf.second, max_value)); + assert(boost::algorithm::none_of_equal(resultll.second + 1, dend, max_value)); + } + + return 0; +} 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(); +}