From a4b907197e93b481a0debbc4c263815398e94769 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Thu, 9 May 2013 17:22:46 +0000 Subject: [PATCH] Add C++14 extensions to 'equal' and 'mismatch' [SVN r84206] --- include/boost/algorithm/cxx14/equal.hpp | 97 +++++++++++ .../boost/algorithm/cxx14/is_permutation.hpp | 130 ++++++++++++++ include/boost/algorithm/cxx14/mismatch.hpp | 66 +++++++ test/Jamfile.v2 | 4 + test/equal_test.cpp | 126 ++++++++++++++ test/mismatch_test.cpp | 162 ++++++++++++++++++ 6 files changed, 585 insertions(+) create mode 100644 include/boost/algorithm/cxx14/equal.hpp create mode 100644 include/boost/algorithm/cxx14/is_permutation.hpp create mode 100644 include/boost/algorithm/cxx14/mismatch.hpp create mode 100644 test/equal_test.cpp create mode 100644 test/mismatch_test.cpp diff --git a/include/boost/algorithm/cxx14/equal.hpp b/include/boost/algorithm/cxx14/equal.hpp new file mode 100644 index 0000000..d10c096 --- /dev/null +++ b/include/boost/algorithm/cxx14/equal.hpp @@ -0,0 +1,97 @@ +/* + Copyright (c) Marshall Clow 2008-2012. + + 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) +*/ + +/// \file equal.hpp +/// \brief Test ranges to if they are equal +/// \author Marshall Clow + +#ifndef BOOST_ALGORITHM_EQUAL_HPP +#define BOOST_ALGORITHM_EQUAL_HPP + +#include // for std::equal +#include // for std::equal_to + +namespace boost { namespace algorithm { + +namespace detail { + + template + struct eq : public std::binary_function { + bool operator () ( const T1& v1, const T2& v2 ) const { return v1 == v2 ;} + }; + + template + bool equal ( RandomAccessIterator1 first1, RandomAccessIterator1 last1, + RandomAccessIterator2 first2, RandomAccessIterator2 last2, BinaryPredicate pred, + std::random_access_iterator_tag, std::random_access_iterator_tag ) + { + // Random-access iterators let is check the sizes in constant time + if ( std::distance ( first1, last1 ) != std::distance ( first2, last2 )) + return false; + // If we know that the sequences are the same size, the original version is fine + return std::equal ( first1, last1, first2, pred ); + } + + template + bool equal ( InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, BinaryPredicate pred, + std::input_iterator_tag, std::input_iterator_tag ) + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2 ) + if ( !pred(*first1, *first2 )) + return false; + + return first1 == last1 && first2 == last2; + } +} + +/// \fn equal ( InputIterator1 first1, InputIterator1 last1, +/// InputIterator2 first2, InputIterator2 last2, +/// BinaryPredicate pred ) +/// \return true if all elements in the two ranges are equal +/// +/// \param first1 The start of the first range. +/// \param last1 One past the end of the first range. +/// \param first2 The start of the second range. +/// \param last2 One past the end of the second range. +/// \param pred A predicate for comparing the elements of the ranges +template +bool equal ( InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, BinaryPredicate pred ) +{ + return boost::algorithm::detail::equal ( + first1, last1, first2, last2, pred, + typename std::iterator_traits::iterator_category (), + typename std::iterator_traits::iterator_category ()); +} + +/// \fn equal ( InputIterator1 first1, InputIterator1 last1, +/// InputIterator2 first2, InputIterator2 last2 ) +/// \return true if all elements in the two ranges are equal +/// +/// \param first1 The start of the first range. +/// \param last1 One past the end of the first range. +/// \param first2 The start of the second range. +/// \param last2 One past the end of the second range. +template +bool equal ( InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2 ) +{ + return boost::algorithm::detail::equal ( + first1, last1, first2, last2, + boost::algorithm::detail::eq< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type> (), + typename std::iterator_traits::iterator_category (), + typename std::iterator_traits::iterator_category ()); +} + +// There are already range-based versions of these. + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_EQUAL_HPP diff --git a/include/boost/algorithm/cxx14/is_permutation.hpp b/include/boost/algorithm/cxx14/is_permutation.hpp new file mode 100644 index 0000000..779acef --- /dev/null +++ b/include/boost/algorithm/cxx14/is_permutation.hpp @@ -0,0 +1,130 @@ +/* + Copyright (c) Marshall Clow 2013 + + 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) +*/ + +/// \file equal.hpp +/// \brief Determines if one +/// \author Marshall Clow + +#ifndef BOOST_ALGORITHM_IS_PERMUTATION_HPP +#define BOOST_ALGORITHM_IS_PERMUTATION_HPP + +#include +#include // for std::equal_to + +namespace boost { namespace algorithm { + +namespace detail { + + template + struct is_perm_eq : public std::binary_function { + bool operator () ( const T1& v1, const T2& v2 ) const { return v1 == v2 ;} + }; + + + template + bool is_permutation ( RandomAccessIterator1 first1, RandomAccessIterator1 last1, + RandomAccessIterator2 first2, RandomAccessIterator2 last2, BinaryPredicate pred, + std::random_access_iterator_tag, std::random_access_iterator_tag ) + { + // Random-access iterators let is check the sizes in constant time + if ( std::distance ( first1, last1 ) != std::distance ( first2, last2 )) + return false; + // If we know that the sequences are the same size, the original version is fine + return std::is_permutation ( first1, last1, first2, pred ); + } + + + template + bool is_permutation ( + ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate pred, + std::forward_iterator_tag, std::forward_iterator_tag ) + { + + // Look for common prefix + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + if (!pred(*first1, *first2)) + goto not_done; + // We've reached the end of one of the sequences without a mismatch. + return first1 == last1 && first2 == last2; + not_done: + + // Check and make sure that we have the same # of elements left + typedef typename std::iterator_traits::difference_type diff1_t; + diff1_t len1 = _VSTD::distance(first1, last1); + typedef typename std::iterator_traits::difference_type diff2_t; + diff2_t len2 = _VSTD::distance(first2, last2); + if (len1 != len2) + return false; + + // For each element in [f1, l1) see if there are the + // same number of equal elements in [f2, l2) + for ( ForwardIterator1 i = first1; i != last1; ++i ) + { + // Have we already counted this value? + ForwardIterator1 j; + for ( j = first1; j != i; ++j ) + if (pred(*j, *i)) + break; + if ( j == i ) // didn't find it... + { + // Count number of *i in [f2, l2) + diff1_t c2 = 0; + for ( ForwardIterator2 iter2 = first2; iter2 != last2; ++iter2 ) + if (pred(*i, *iter2)) + ++c2; + if (c2 == 0) + return false; + + // Count number of *i in [i, l1) + diff1_t c1 = 0; + for (_ForwardIterator1 iter1 = i; iter1 != last1; ++iter1 ) + if (pred(*i, *iter1)) + ++c1; + if (c1 != c2) + return false; + } + } + return true; + } + +} + + +template +bool is_permutation ( + ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate pred ) +{ + return boost::algorithm::detail::is_permutation ( + first1, last1, first2, last2, pred, + typename std::iterator_traits::iterator_category (), + typename std::iterator_traits::iterator_category ()); +} + +template +bool is_permutation ( ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2 ) +{ + typedef typename iterator_traits<_ForwardIterator1>::value_type value1_t; + typedef typename iterator_traits<_ForwardIterator2>::value_type value2_t; + return boost::algorithm::detail::is_permutation ( + first1, last1, first2, last2, + boost::algorithm::detail::is_perm_eq< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type> (), + typename std::iterator_traits::iterator_category (), + typename std::iterator_traits::iterator_category ()); +} + +// There are already range-based versions of these. + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_IS_PERMUTATION_HPP diff --git a/include/boost/algorithm/cxx14/mismatch.hpp b/include/boost/algorithm/cxx14/mismatch.hpp new file mode 100644 index 0000000..5229e3b --- /dev/null +++ b/include/boost/algorithm/cxx14/mismatch.hpp @@ -0,0 +1,66 @@ +/* + Copyright (c) Marshall Clow 2008-2012. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE10.txt or copy at http://www.boost.org/LICENSE10.txt) +*/ + +/// \file mismatch.hpp +/// \brief Find the first mismatched element in a sequence +/// \author Marshall Clow + +#ifndef BOOST_ALGORITHM_MISMATCH_HPP +#define BOOST_ALGORITHM_MISMATCH_HPP + +#include // for std::mismatch +#include // for std::pair + +namespace boost { namespace algorithm { + +template + +/// \fn mismatch ( InputIterator1 first1, InputIterator1 last1, +/// InputIterator2 first2, InputIterator2 last2, +/// BinaryPredicate pred ) +/// \return a pair of iterators pointing to the first elements in the sequence that do not match +/// +/// \param first1 The start of the first range. +/// \param last1 One past the end of the first range. +/// \param first2 The start of the second range. +/// \param last2 One past the end of the second range. +/// \param pred A predicate for comparing the elements of the ranges +std::pair mismatch ( + InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + BinaryPredicate pred ) +{ + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + if ( !pred ( *first1, *first2 )) + break; + return std::pair(first1, first2); +} + +/// \fn mismatch ( InputIterator1 first1, InputIterator1 last1, +/// InputIterator2 first2, InputIterator2 last2 ) +/// \return a pair of iterators pointing to the first elements in the sequence that do not match +/// +/// \param first1 The start of the first range. +/// \param last1 One past the end of the first range. +/// \param first2 The start of the second range. +/// \param last2 One past the end of the second range. +template +std::pair mismatch ( + InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2 ) +{ + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + if ( *first1 != *first2 ) + break; + return std::pair(first1, first2); +} + +// There are already range-based versions of these. + +}} // namespace boost and algorithm + +#endif // BOOST_ALGORITHM_MISMATCH_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 08a5f3f..bb4dff6 100755 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -47,6 +47,10 @@ alias unit_test_framework [ run is_partitioned_test1.cpp unit_test_framework : : : : is_partitioned_test1 ] [ run partition_copy_test1.cpp unit_test_framework : : : : partition_copy_test1 ] +# Cxx14 tests + [ run equal_test.cpp unit_test_framework : : : : equal_test ] + [ run mismatch_test.cpp unit_test_framework : : : : mismatch_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/equal_test.cpp b/test/equal_test.cpp new file mode 100644 index 0000000..a1bda1a --- /dev/null +++ b/test/equal_test.cpp @@ -0,0 +1,126 @@ +/* + Copyright (c) Marshall Clow 2013. + + 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 + +#include "iterator_test.hpp" + +#define BOOST_TEST_MAIN +#include + +template +bool eq ( const T& a, const T& b ) { return a == b; } + +template +bool never_eq ( const T&, const T& ) { return false; } + +int comparison_count = 0; +template +bool counting_equals ( const T &a, const T &b ) { + ++comparison_count; + return a == b; + } + +namespace ba = boost::algorithm; + +void test_equal () +{ +// Note: The literal values here are tested against directly, careful if you change them: + int num[] = { 1, 1, 2, 3, 5 }; + const int sz = sizeof (num)/sizeof(num[0]); + + +// Empty sequences are equal to each other, but not to non-empty sequences + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num))); + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num), + never_eq )); + BOOST_CHECK ( ba::equal ( random_access_iterator(num), random_access_iterator(num), + random_access_iterator(num), random_access_iterator(num), + never_eq )); + + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num + 1))); + BOOST_CHECK (!ba::equal ( input_iterator(num + 1), input_iterator(num + 2), + input_iterator(num), input_iterator(num))); + BOOST_CHECK (!ba::equal ( random_access_iterator(num + 1), random_access_iterator(num + 2), + random_access_iterator(num), random_access_iterator(num))); + +// Single element sequences are equal if they contain the same value + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1))); + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1), + eq )); + BOOST_CHECK ( ba::equal ( random_access_iterator(num), random_access_iterator(num + 1), + random_access_iterator(num), random_access_iterator(num + 1), + eq )); + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1), + never_eq )); + BOOST_CHECK (!ba::equal ( random_access_iterator(num), random_access_iterator(num + 1), + random_access_iterator(num), random_access_iterator(num + 1), + never_eq )); + + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + 1), + input_iterator(num + 1), input_iterator(num + 2))); + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + 1), + input_iterator(num + 1), input_iterator(num + 2), + eq )); + + BOOST_CHECK (!ba::equal ( input_iterator(num + 2), input_iterator(num + 3), + input_iterator(num), input_iterator(num + 1))); + BOOST_CHECK (!ba::equal ( input_iterator(num + 2), input_iterator(num + 3), + input_iterator(num), input_iterator(num + 1), + eq )); + +// Identical long sequences are equal. + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz))); + BOOST_CHECK ( ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + eq )); + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + never_eq )); + +// different sequences are different + BOOST_CHECK (!ba::equal ( input_iterator(num + 1), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz))); + BOOST_CHECK (!ba::equal ( input_iterator(num + 1), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + eq )); + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz - 1))); + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz - 1), + eq )); + +// When there's a cheap check, bail early + comparison_count = 0; + BOOST_CHECK (!ba::equal ( random_access_iterator(num), random_access_iterator(num + sz), + random_access_iterator(num), random_access_iterator(num + sz - 1), + counting_equals )); + BOOST_CHECK ( comparison_count == 0 ); +// And when there's not, we can't + comparison_count = 0; + BOOST_CHECK (!ba::equal ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz - 1), + counting_equals )); + BOOST_CHECK ( comparison_count > 0 ); + +} + + +BOOST_AUTO_TEST_CASE( test_main ) +{ + test_equal (); +} diff --git a/test/mismatch_test.cpp b/test/mismatch_test.cpp new file mode 100644 index 0000000..19e0477 --- /dev/null +++ b/test/mismatch_test.cpp @@ -0,0 +1,162 @@ +/* + Copyright (c) Marshall Clow 2013. + + 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 + +#include "iterator_test.hpp" + +#define BOOST_TEST_MAIN +#include + +template +bool eq ( const T& a, const T& b ) { return a == b; } + +template +bool never_eq ( const T&, const T& ) { return false; } + +namespace ba = boost::algorithm; + +template +bool iter_eq ( std::pair pr, Iter1 first, Iter2 second ) { + return pr.first == first && pr.second == second; + } + +void test_mismatch () +{ +// Note: The literal values here are tested against directly, careful if you change them: + int num[] = { 1, 1, 2, 3, 5 }; + const int sz = sizeof (num)/sizeof(num[0]); + + +// No mismatch for empty sequences + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num)), + input_iterator(num), input_iterator(num))); + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num), + never_eq ), + input_iterator(num), input_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( random_access_iterator(num), random_access_iterator(num), + random_access_iterator(num), random_access_iterator(num), + never_eq ), + random_access_iterator(num), random_access_iterator(num))); + +// Empty vs. non-empty mismatch immediately + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num), + input_iterator(num), input_iterator(num + 1)), + input_iterator(num), input_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num + 1), input_iterator(num + 2), + input_iterator(num), input_iterator(num)), + input_iterator(num + 1), input_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( random_access_iterator(num + 1), random_access_iterator(num + 2), + random_access_iterator(num), random_access_iterator(num)), + random_access_iterator(num + 1), random_access_iterator(num))); + +// Single element sequences are equal if they contain the same value + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1)), + input_iterator(num + 1), input_iterator(num + 1))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1), + eq ), + input_iterator(num + 1), input_iterator(num + 1))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( random_access_iterator(num), random_access_iterator(num + 1), + random_access_iterator(num), random_access_iterator(num + 1), + eq ), + random_access_iterator(num + 1), random_access_iterator(num + 1))); + + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + 1), + input_iterator(num), input_iterator(num + 1), + never_eq ), + input_iterator(num), input_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( random_access_iterator(num), random_access_iterator(num + 1), + random_access_iterator(num), random_access_iterator(num + 1), + never_eq ), + random_access_iterator(num), random_access_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + 1), + input_iterator(num + 1), input_iterator(num + 2)), + input_iterator(num + 1), input_iterator(num + 2))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + 1), + input_iterator(num + 1), input_iterator(num + 2), + eq ), + input_iterator(num + 1), input_iterator(num + 2))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num + 2), input_iterator(num + 3), + input_iterator(num), input_iterator(num + 1)), + input_iterator(num + 2), input_iterator(num))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num + 2), input_iterator(num + 3), + input_iterator(num), input_iterator(num + 1), + eq ), + input_iterator(num + 2), input_iterator(num))); + + + +// Identical long sequences are equal. + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz)), + input_iterator(num + sz), input_iterator(num + sz))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + eq ), + input_iterator(num + sz), input_iterator(num + sz))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + never_eq ), + input_iterator(num), input_iterator(num))); + +// different sequences are different + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num + 1), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz)), + input_iterator(num + 2), input_iterator(num + 1))); + + BOOST_CHECK ( iter_eq ( + ba::mismatch ( input_iterator(num + 1), input_iterator(num + sz), + input_iterator(num), input_iterator(num + sz), + eq ), + input_iterator(num + 2), input_iterator(num + 1))); + +} + + +BOOST_AUTO_TEST_CASE( test_main ) +{ + test_mismatch (); +}