diff --git a/include/boost/algorithm/sort_subrange.hpp b/include/boost/algorithm/sort_subrange.hpp new file mode 100644 index 0000000..9f60370 --- /dev/null +++ b/include/boost/algorithm/sort_subrange.hpp @@ -0,0 +1,72 @@ +/* + 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) + + Revision history: + 28 Sep 2015 mtc First version + +*/ + +/// \file sort_subrange.hpp +/// \brief Sort a subrange +/// \author Marshall Clow +/// +/// Suggested by Sean Parent in his CppCon 2015 keynote + +#ifndef BOOST_ALGORITHM_SORT_SUBRANGE_HPP +#define BOOST_ALGORITHM_SORT_SUBRANGE_HPP + +#include // For std::less +#include // For std::iterator_traits +#include // For nth_element and partial_sort + +#include +#include + +namespace boost { namespace algorithm { + +/// \fn sort_subrange ( T const& val, +/// Iterator first, Iterator last, +/// Iterator sub_first, Iterator sub_last, +/// Pred p ) +/// \brief Sort the subrange [sub_first, sub_last) that is inside +/// the range [first, last) as if you had sorted the entire range. +/// +/// \param first The start of the larger range +/// \param last The end of the larger range +/// \param sub_first The start of the sub range +/// \param sub_last The end of the sub range +/// \param p A predicate to use to compare the values. +/// p ( a, b ) returns a boolean. +/// + template + void sort_subrange ( + Iterator first, Iterator last, + Iterator sub_first, Iterator sub_last, + Pred p) + { + if (sub_first == sub_last) return; // the empty sub-range is already sorted. + + if (sub_first != first) { // sub-range is at the start, don't need to partition + (void) std::nth_element(first, sub_first, last, p); + ++sub_first; + } + std::partial_sort(sub_first, sub_last, last, p); + } + + + + template + void sort_subrange (Iterator first, Iterator last, Iterator sub_first, Iterator sub_last) + { + typedef typename std::iterator_traits::value_type value_type; + return sort_subrange(first, last, sub_first, sub_last, std::less()); + } + +/// range versions? + +}} + +#endif // BOOST_ALGORITHM_SORT_SUBRANGE_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9b5d30b..9843516 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -64,6 +64,9 @@ alias unit_test_framework [ run gather_test1.cpp unit_test_framework : : : : gather_test1 ] [ compile-fail gather_fail1.cpp ] +# SortSubrange tests + [ run sort_subrange_test.cpp unit_test_framework : : : : sort_subrange_test ] + ; } diff --git a/test/sort_subrange_test.cpp b/test/sort_subrange_test.cpp new file mode 100644 index 0000000..4c1192e --- /dev/null +++ b/test/sort_subrange_test.cpp @@ -0,0 +1,137 @@ +#include +#include +#include + +#define BOOST_TEST_MAIN +#include + +#include +#include +namespace ba = boost::algorithm; + +template +void check_sequence ( Iter first, Iter last, Iter sf, Iter sl ) +{ + if (sf == sl) return; + for (Iter i = first; i < sf; ++i) + BOOST_CHECK(*i < *sf); + BOOST_CHECK(ba::is_sorted(sf, sl)); + for (Iter i = sl; i < last; ++i) + BOOST_CHECK(*(sl-1) < *i); +} + +template +void check_sequence ( Iter first, Iter last, Iter sf, Iter sl, Pred p ) +{ + if (sf == sl) return; + for (Iter i = first; i < sf; ++i) + BOOST_CHECK(p(*i, *sf)); + BOOST_CHECK(ba::is_sorted(sf, sl, p)); + for (Iter i = sl; i < last; ++i) + BOOST_CHECK(p(*(sl-1), *i)); + +} + +// for ( int i = 0; i < v.size(); ++i ) +// std::cout << v[i] << ' '; +// std::cout << std::endl; + + +BOOST_AUTO_TEST_CASE( test_main ) +{ + { + std::vector v; + for ( int i = 0; i < 10; ++i ) + v.push_back(i); + + const std::vector::iterator b = v.begin(); + ba::sort_subrange(b, v.end(), b + 3, b + 6); + check_sequence (b, v.end(), b + 3, b + 6); + + BOOST_CHECK_EQUAL(v[3], 3); + BOOST_CHECK_EQUAL(v[4], 4); + BOOST_CHECK_EQUAL(v[5], 5); + +// Mix them up and try again - single element + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b + 7, b + 8); + check_sequence (b, v.end(), b + 7, b + 8); + + BOOST_CHECK_EQUAL(v[7], 7); + +// Mix them up and try again - at the end + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b + 7, v.end()); + check_sequence (b, v.end(), b + 7, v.end()); + + BOOST_CHECK_EQUAL(v[7], 7); + BOOST_CHECK_EQUAL(v[8], 8); + BOOST_CHECK_EQUAL(v[9], 9); + +// Mix them up and try again - at the beginning + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, b + 2); + check_sequence (b, v.end(), b, b + 2); + + BOOST_CHECK_EQUAL(v[0], 0); + BOOST_CHECK_EQUAL(v[1], 1); + +// Mix them up and try again - empty subrange + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, b); + check_sequence (b, v.end(), b, b); + +// Mix them up and try again - entire subrange + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, v.end()); + check_sequence (b, v.end(), b, v.end()); + } + + { + std::vector v; + for ( int i = 0; i < 10; ++i ) + v.push_back(i); + + const std::vector::iterator b = v.begin(); + ba::sort_subrange(b, v.end(), b + 3, b + 6, std::greater()); + check_sequence (b, v.end(), b + 3, b + 6, std::greater()); + + BOOST_CHECK_EQUAL(v[3], 6); + BOOST_CHECK_EQUAL(v[4], 5); + BOOST_CHECK_EQUAL(v[5], 4); + +// Mix them up and try again - single element + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b + 7, b + 8, std::greater()); + check_sequence (b, v.end(), b + 7, b + 8, std::greater()); + + BOOST_CHECK_EQUAL(v[7], 2); + +// Mix them up and try again - at the end + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b + 7, v.end(), std::greater()); + check_sequence (b, v.end(), b + 7, v.end(), std::greater()); + + BOOST_CHECK_EQUAL(v[7], 2); + BOOST_CHECK_EQUAL(v[8], 1); + BOOST_CHECK_EQUAL(v[9], 0); + +// Mix them up and try again - at the beginning + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, b + 2, std::greater()); + check_sequence (b, v.end(), b, b + 2, std::greater()); + + BOOST_CHECK_EQUAL(v[0], 9); + BOOST_CHECK_EQUAL(v[1], 8); + +// Mix them up and try again - empty subrange + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, b, std::greater()); + check_sequence (b, v.end(), b, b, std::greater()); + +// Mix them up and try again - entire subrange + std::random_shuffle(v.begin(), v.end()); + ba::sort_subrange(b, v.end(), b, v.end(), std::greater()); + check_sequence (b, v.end(), b, v.end(), std::greater()); + } +}