From 17c47e8061d61237a32b6f7903becbb2328ac298 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Wed, 14 Jun 2023 18:30:08 -0700 Subject: [PATCH 1/7] Add 'indirect_sort' --- doc/algorithm.qbk | 2 + doc/indirect_sort.qbk | 71 +++++++++++++++ include/boost/algorithm/indirect_sort.hpp | 83 ++++++++++++++++++ test/Jamfile.v2 | 4 + test/indirect_sort_test.cpp | 100 ++++++++++++++++++++++ 5 files changed, 260 insertions(+) create mode 100644 doc/indirect_sort.qbk create mode 100644 include/boost/algorithm/indirect_sort.hpp create mode 100644 test/indirect_sort_test.cpp diff --git a/doc/algorithm.qbk b/doc/algorithm.qbk index 02a156236..be2cd8d65 100644 --- a/doc/algorithm.qbk +++ b/doc/algorithm.qbk @@ -233,6 +233,8 @@ Convert a sequence of hexadecimal characters into a sequence of integers or char Convert a sequence of integral types into a lower case hexadecimal sequence of characters [endsect:hex_lower] +[include indirect_sort.qbk] + [include is_palindrome.qbk] [include is_partitioned_until.qbk] diff --git a/doc/indirect_sort.qbk b/doc/indirect_sort.qbk new file mode 100644 index 000000000..ac4e5a205 --- /dev/null +++ b/doc/indirect_sort.qbk @@ -0,0 +1,71 @@ +[/ File indirect_sort.qbk] + +[section:indirect_sort indirect_sort ] + +[/license +Copyright (c) 2023 Marshall Clow + +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) +] + +There are times that you want a sorted version of a sequence, but for some reason or another, you don't really want to sort them. Maybe the elements in the sequence are non-copyable (or non-movable), or the sequence is const, or they're just really expensive to move around. An example of this might be a sequence of records from a database. + +Nevertheless, you might want to sort them. That's where indirect sorting comes in. In a "normal" sort, the elements of the sequence to be sorted are shuffled in place. In indirect sorting, the elements are unchanged, but the sort algorithm returns to you a "permutation" of the elements that, when applied, will leave the elements in the sequence in a sorted order. + +Say you have a sequence `[first, last)` of 1000 items that are expensive to swap: +``` + std::sort(first, last); // ['O(N ln N)] comparisons and ['O(N ln N)] swaps (of the element type). +``` + +On the other hand, using indirect sorting: +``` + auto permutation = boost::algorithm::indirect_sort(first, last); // ['O(N lg N)] comparisons and ['O(N lg N)] swaps (of size_t). + boost::algorithm::apply_permutation(first, last, perm.begin(), perm.end()); // ['O(N)] swaps (of the element type) +``` + +If the element type is sufficiently expensive to swap, then 10,000 swaps of size_t + 1000 swaps of the element_type could be cheaper than 10,000 swaps of the element_type. + +Or maybe you don't need the elements to actually be sorted - you just want to traverse them in a sorted order: +``` + auto permutation = boost::algorithm::indirect_sort(first, last); + for (size_t idx: permutation) + std::cout << first[idx] << std::endl; +``` + + +More to come here .... + +[heading interface] + +The function `indirect_sort` a `vector` containing the permutation necessary to put the input sequence into a sorted order. One version uses `std::less` to do the comparisons; the other lets the caller pass predicate to do the comparisons. + +``` +template +std::vector indirect_sort (RAIterator first, RAIterator last); + +template +std::vector indirect_sort (RAIterator first, RAIterator last, BinaryPredicate pred); +``` + +[heading Examples] + +[heading Iterator Requirements] + +`indirect_sort` requires random-access iterators. + +[heading Complexity] + +Both of the variants of `indirect_sort` run in ['O(N lg N)] time; they are not more (or less) efficient than `std::sort`. There is an extra layer of indirection on each comparison, but all off the swaps are done on values of type `size_t` + +[heading Exception Safety] + +[heading Notes] + +[endsect] + +[/ File indirect_sort.qbk +Copyright 2023 Marshall Clow +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/indirect_sort.hpp b/include/boost/algorithm/indirect_sort.hpp new file mode 100644 index 000000000..a551670e5 --- /dev/null +++ b/include/boost/algorithm/indirect_sort.hpp @@ -0,0 +1,83 @@ +/* + Copyright (c) Marshall Clow 2023. + + 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 indirect_sort.hpp +/// \brief indirect sorting algorithms +/// \author Marshall Clow +/// + +#ifndef BOOST_ALGORITHM_IS_INDIRECT_SORT +#define BOOST_ALGORITHM_IS_INDIRECT_SORT + +#include // for std::sort (and others) +#include // for std::less +#include // for std:;vector + +#include + +namespace boost { namespace algorithm { + +namespace detail { + + template + struct indirect_predicate { + indirect_predicate (Predicate pred, Iter iter) + : pred_(pred), iter_(iter) {} + + bool operator ()(size_t a, size_t b) const { + return pred_(iter_[a], iter_[b]); + } + + Predicate pred_; + Iter iter_; + }; + +} + +typedef std::vector Permutation; + + // ===== sort ===== + +/// \fn indirect_sort (RAIterator first, RAIterator last, Predicate p) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted according to the predicate pred. +/// +/// \param first The start of the input sequence +/// \param last The end of the input sequence +/// \param pred The predicate to compare elements with +/// +template +std::vector indirect_sort (RAIterator first, RAIterator last, Pred pred) { + Permutation ret(std::distance(first, last)); + boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + std::sort(ret.begin(), ret.end(), + detail::indirect_predicate(pred, first)); + return ret; +} + +/// \fn indirect_sort (RAIterator first, RAIterator las ) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted according to the predicate pred. +/// +/// \param first The start of the input sequence +/// \param last The end of the input sequence +/// +template +std::vector indirect_sort (RAIterator first, RAIterator last) { + return indirect_sort(first, last, + std::less::value_type>()); +} + + // ===== stable_sort ===== + // ===== partial_sort ===== + // ===== nth_element ===== +}} + +#endif // BOOST_ALGORITHM_IS_INDIRECT_SORT diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index aef6bdb38..3390234f2 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -88,6 +88,10 @@ alias unit_test_framework # Apply_permutation tests [ run apply_permutation_test.cpp unit_test_framework : : : : apply_permutation_test ] + +# Indirect_sort tests + [ run indirect_sort_test.cpp unit_test_framework : : : : indirect_sort_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/indirect_sort_test.cpp b/test/indirect_sort_test.cpp new file mode 100644 index 000000000..54bf1966d --- /dev/null +++ b/test/indirect_sort_test.cpp @@ -0,0 +1,100 @@ +/* + Copyright (c) Marshall Clow 2011-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) + + For more information, see http://www.boost.org +*/ + +#include +#include +#include +#include + +#define BOOST_TEST_MAIN +#include + +#include +#include +#include +#include + +typedef std::vector Permutation; + +// A permutation of size N is a sequence of values in the range [0..N) +// such that no value appears more than once in the permutation. +bool isa_permutation(Permutation p, size_t N) { + if (p.size() != N) return false; + +// Sort the permutation, and ensure that each value appears exactly once. + std::sort(p.begin(), p.end()); + for (size_t i = 0; i < N; ++i) + if (p[i] != i) return false; + return true; +} + +template ::value_type> > +struct indirect_comp { + indirect_comp (Iter it, Comp c = Comp()) + : iter_(it), comp_(c) {} + + bool operator ()(size_t a, size_t b) const { return comp_(iter_[a], iter_[b]);} + + Iter iter_; + Comp comp_; +}; + +template +void test_one_sort(Iter first, Iter last) { + Permutation perm = boost::algorithm::indirect_sort(first, last); + BOOST_CHECK (isa_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first))); + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + std::vector::value_type> v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end())); +} + +template +void test_one_sort(Iter first, Iter last, Comp comp) { + Permutation perm = boost::algorithm::indirect_sort(first, last, comp); + BOOST_CHECK (isa_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), + indirect_comp(first, comp))); + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + std::vector::value_type> v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end(), comp)); +} + + +void test_sort () { + BOOST_CXX14_CONSTEXPR int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; + const int sz = sizeof (num)/sizeof(num[0]); + int *first = &num[0]; + int const *cFirst = &num[0]; + +// Test subsets + for (size_t i = 0; i <= sz; ++i) { + test_one_sort(first, first + i); + test_one_sort(first, first + i, std::greater()); + + // test with constant inputs + test_one_sort(cFirst, cFirst + i); + test_one_sort(cFirst, cFirst + i, std::greater()); + } + +// make sure we work with iterators as well as pointers + std::vector v(first, first + sz); + test_one_sort(v.begin(), v.end()); + test_one_sort(v.begin(), v.end(), std::greater()); + } + +BOOST_AUTO_TEST_CASE( test_main ) +{ + test_sort (); +} From 814f8a5c058507ea2c61f61e6544d840d5091c07 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Sun, 18 Jun 2023 11:31:32 -0700 Subject: [PATCH 2/7] Update docs based on feedback, add 'stable_sort', 'partial_sort' and 'nth_element' --- doc/indirect_sort.qbk | 46 ++++++-- include/boost/algorithm/indirect_sort.hpp | 125 ++++++++++++++++++++-- test/indirect_sort_test.cpp | 10 +- 3 files changed, 160 insertions(+), 21 deletions(-) diff --git a/doc/indirect_sort.qbk b/doc/indirect_sort.qbk index ac4e5a205..3c78936dd 100644 --- a/doc/indirect_sort.qbk +++ b/doc/indirect_sort.qbk @@ -9,36 +9,66 @@ 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) ] -There are times that you want a sorted version of a sequence, but for some reason or another, you don't really want to sort them. Maybe the elements in the sequence are non-copyable (or non-movable), or the sequence is const, or they're just really expensive to move around. An example of this might be a sequence of records from a database. +There are times that you want a sorted version of a sequence, but for some reason you don't want to modify it. Maybe the elements in the sequence can't be moved/copied, e.g. the sequence is const, or they're just really expensive to move around. An example of this might be a sequence of records from a database. -Nevertheless, you might want to sort them. That's where indirect sorting comes in. In a "normal" sort, the elements of the sequence to be sorted are shuffled in place. In indirect sorting, the elements are unchanged, but the sort algorithm returns to you a "permutation" of the elements that, when applied, will leave the elements in the sequence in a sorted order. +That's where indirect sorting comes in. In a "normal" sort, the elements of the sequence to be sorted are shuffled in place. In indirect sorting, the elements are unchanged, but the sort algorithm returns a "permutation" of the elements that, when applied, will put the elements in the sequence in a sorted order. -Say you have a sequence `[first, last)` of 1000 items that are expensive to swap: +Assume have a sequence `[first, last)` of 1000 items that are expensive to swap: ``` std::sort(first, last); // ['O(N ln N)] comparisons and ['O(N ln N)] swaps (of the element type). ``` On the other hand, using indirect sorting: ``` - auto permutation = boost::algorithm::indirect_sort(first, last); // ['O(N lg N)] comparisons and ['O(N lg N)] swaps (of size_t). - boost::algorithm::apply_permutation(first, last, perm.begin(), perm.end()); // ['O(N)] swaps (of the element type) + auto perm = indirect_sort(first, last); // ['O(N lg N)] comparisons and ['O(N lg N)] swaps (of size_t). + apply_permutation(first, last, perm.begin(), perm.end()); // ['O(N)] swaps (of the element type) ``` If the element type is sufficiently expensive to swap, then 10,000 swaps of size_t + 1000 swaps of the element_type could be cheaper than 10,000 swaps of the element_type. Or maybe you don't need the elements to actually be sorted - you just want to traverse them in a sorted order: ``` - auto permutation = boost::algorithm::indirect_sort(first, last); + auto permutation = indirect_sort(first, last); for (size_t idx: permutation) std::cout << first[idx] << std::endl; ``` -More to come here .... +Assume that instead of an "array of structures", you have a "struct of arrays" +``` +struct AType { + Type0 key; + Type1 value1; + Type1 value2; + }; + +std::array arrayOfStruct; +``` + +versus: + +``` +template +struct AType { + std::array key; + std::array value1; + std::array value2; + }; + +AType<1000> structOfArrays; +``` + +Sorting the first one is easy, because each set of fields (`key`, `value1`, `value2`) are part of the same struct. But with indirect sorting, the second one is easy to sort as well - just sort the keys, then apply the permutation to the keys and the values: +``` + auto perm = indirect_sort(std::begin(structOfArrays.key), std::end(structOfArrays.key)); + apply_permutation(structOfArrays.key.begin(), structOfArrays.key.end(), perm.begin(), perm.end()); + apply_permutation(structOfArrays.value1.begin(), structOfArrays.value1.end(), perm.begin(), perm.end()); + apply_permutation(structOfArrays.value2.begin(), structOfArrays.value2.end(), perm.begin(), perm.end()); +``` [heading interface] -The function `indirect_sort` a `vector` containing the permutation necessary to put the input sequence into a sorted order. One version uses `std::less` to do the comparisons; the other lets the caller pass predicate to do the comparisons. +The function `indirect_sort` returns a `vector` containing the permutation necessary to put the input sequence into a sorted order. One version uses `std::less` to do the comparisons; the other lets the caller pass predicate to do the comparisons. ``` template diff --git a/include/boost/algorithm/indirect_sort.hpp b/include/boost/algorithm/indirect_sort.hpp index a551670e5..f0a98626e 100644 --- a/include/boost/algorithm/indirect_sort.hpp +++ b/include/boost/algorithm/indirect_sort.hpp @@ -11,12 +11,12 @@ /// \author Marshall Clow /// -#ifndef BOOST_ALGORITHM_IS_INDIRECT_SORT -#define BOOST_ALGORITHM_IS_INDIRECT_SORT +#ifndef BOOST_ALGORITHM_INDIRECT_SORT +#define BOOST_ALGORITHM_INDIRECT_SORT #include // for std::sort (and others) #include // for std::less -#include // for std:;vector +#include // for std::vector #include @@ -53,7 +53,7 @@ typedef std::vector Permutation; /// \param pred The predicate to compare elements with /// template -std::vector indirect_sort (RAIterator first, RAIterator last, Pred pred) { +Permutation indirect_sort (RAIterator first, RAIterator last, Pred pred) { Permutation ret(std::distance(first, last)); boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); std::sort(ret.begin(), ret.end(), @@ -61,23 +61,132 @@ std::vector indirect_sort (RAIterator first, RAIterator last, Pred pred) return ret; } -/// \fn indirect_sort (RAIterator first, RAIterator las ) +/// \fn indirect_sort (RAIterator first, RAIterator last) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted according to the predicate pred. +/// the result is sorted in non-descending order. /// /// \param first The start of the input sequence /// \param last The end of the input sequence /// template -std::vector indirect_sort (RAIterator first, RAIterator last) { +Permutation indirect_sort (RAIterator first, RAIterator last) { return indirect_sort(first, last, std::less::value_type>()); } // ===== stable_sort ===== + +/// \fn indirect_stable_sort (RAIterator first, RAIterator last, Predicate p) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted according to the predicate pred. +/// +/// \param first The start of the input sequence +/// \param last The end of the input sequence +/// \param pred The predicate to compare elements with +/// +template +Permutation indirect_stable_sort (RAIterator first, RAIterator last, Pred pred) { + Permutation ret(std::distance(first, last)); + boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + std::stable_sort(ret.begin(), ret.end(), + detail::indirect_predicate(pred, first)); + return ret; +} + +/// \fn indirect_stable_sort (RAIterator first, RAIterator last) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted in non-descending order. +/// +/// \param first The start of the input sequence +/// \param last The end of the input sequence +/// +template +Permutation indirect_stable_sort (RAIterator first, RAIterator last) { + return indirect_stable_sort(first, last, + std::less::value_type>()); +} + // ===== partial_sort ===== + +/// \fn indirect_partial_sort (RAIterator first, RAIterator last, Predicate p) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the resulting range [first, middle) is sorted and the range [middle,last) +/// consists of elements that are "larger" than then ones in [first, middle), +/// according to the predicate pred. +/// +/// \param first The start of the input sequence +/// \param middle The end of the range to be sorted +/// \param last The end of the input sequence +/// \param pred The predicate to compare elements with +/// +template +Permutation indirect_partial_sort (RAIterator first, RAIterator middle, + RAIterator last, Pred pred) { + Permutation ret(std::distance(first, last)); + + boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + std::partial_sort(ret.begin(), ret.begin() + std::distance(first, middle), ret.end(), + detail::indirect_predicate(pred, first)); + return ret; +} + +/// \fn indirect_partial_sort (RAIterator first, RAIterator last) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the resulting range [first, middle) is sorted in non-descending order, +/// and the range [middle,last) consists of elements that are larger than +/// then ones in [first, middle). +/// +/// \param first The start of the input sequence +/// \param last The end of the input sequence +/// +template +Permutation indirect_partial_sort (RAIterator first, RAIterator middle, RAIterator last) { + return indirect_partial_sort(first, middle, last, + std::less::value_type>()); +} + // ===== nth_element ===== + +/// \fn indirect_nth_element (RAIterator first, RAIterator last, Predicate p) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted according to the predicate pred. +/// +/// \param first The start of the input sequence +/// \param nth The sort partition point in the input sequence +/// \param last The end of the input sequence +/// \param pred The predicate to compare elements with +/// +template +Permutation indirect_nth_element (RAIterator first, RAIterator nth, + RAIterator last, Pred pred) { + Permutation ret(std::distance(first, last)); + boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + std::nth_element(ret.begin(), ret.begin() + std::distance(first, nth), ret.end(), + detail::indirect_predicate(pred, first)); + return ret; +} + +/// \fn indirect_nth_element (RAIterator first, RAIterator last) +/// \returns a permutation of the elements in the range [first, last) +/// such that when the permutation is applied to the sequence, +/// the result is sorted in non-descending order. +/// +/// \param first The start of the input sequence +/// \param nth The sort partition point in the input sequence +/// \param last The end of the input sequence +/// +template +Permutation indirect_nth_element (RAIterator first, RAIterator nth, RAIterator last) { + return indirect_nth_element(first, nth, last, + std::less::value_type>()); +} + }} -#endif // BOOST_ALGORITHM_IS_INDIRECT_SORT +#endif // BOOST_ALGORITHM_INDIRECT_SORT diff --git a/test/indirect_sort_test.cpp b/test/indirect_sort_test.cpp index 54bf1966d..21bd3bdce 100644 --- a/test/indirect_sort_test.cpp +++ b/test/indirect_sort_test.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) Marshall Clow 2011-2012. + Copyright (c) Marshall Clow 2023. 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) @@ -24,7 +24,7 @@ typedef std::vector Permutation; // A permutation of size N is a sequence of values in the range [0..N) // such that no value appears more than once in the permutation. -bool isa_permutation(Permutation p, size_t N) { +bool is_a_permutation(Permutation p, size_t N) { if (p.size() != N) return false; // Sort the permutation, and ensure that each value appears exactly once. @@ -49,7 +49,7 @@ struct indirect_comp { template void test_one_sort(Iter first, Iter last) { Permutation perm = boost::algorithm::indirect_sort(first, last); - BOOST_CHECK (isa_permutation(perm, std::distance(first, last))); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first))); // Make a copy of the data, apply the permutation, and ensure that it is sorted. @@ -61,7 +61,7 @@ void test_one_sort(Iter first, Iter last) { template void test_one_sort(Iter first, Iter last, Comp comp) { Permutation perm = boost::algorithm::indirect_sort(first, last, comp); - BOOST_CHECK (isa_permutation(perm, std::distance(first, last))); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first, comp))); @@ -73,7 +73,7 @@ void test_one_sort(Iter first, Iter last, Comp comp) { void test_sort () { - BOOST_CXX14_CONSTEXPR int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; + int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; const int sz = sizeof (num)/sizeof(num[0]); int *first = &num[0]; int const *cFirst = &num[0]; From 3ae9ee2f929e2b6a06703bd312c45a7217e83d84 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Tue, 20 Jun 2023 20:01:38 -0700 Subject: [PATCH 3/7] Add more tests --- doc/indirect_sort.qbk | 14 +- test/indirect_sort_test.cpp | 263 +++++++++++++++++++++++++++++++++++- 2 files changed, 272 insertions(+), 5 deletions(-) diff --git a/doc/indirect_sort.qbk b/doc/indirect_sort.qbk index 3c78936dd..6ac6de569 100644 --- a/doc/indirect_sort.qbk +++ b/doc/indirect_sort.qbk @@ -34,7 +34,7 @@ Or maybe you don't need the elements to actually be sorted - you just want to tr ``` -Assume that instead of an "array of structures", you have a "struct of arrays" +Assume that instead of an "array of structures", you have a "struct of arrays". ``` struct AType { Type0 key; @@ -70,12 +70,20 @@ Sorting the first one is easy, because each set of fields (`key`, `value1`, `val The function `indirect_sort` returns a `vector` containing the permutation necessary to put the input sequence into a sorted order. One version uses `std::less` to do the comparisons; the other lets the caller pass predicate to do the comparisons. +There is also a variant called `indirect_stable_sort`; it bears the same relation to `indirect_sort` that `std::stable_sort` does to `std::sort`. + ``` template std::vector indirect_sort (RAIterator first, RAIterator last); template std::vector indirect_sort (RAIterator first, RAIterator last, BinaryPredicate pred); + +template +std::vector indirect_stable_sort (RAIterator first, RAIterator last); + +template +std::vector indirect_stable_sort (RAIterator first, RAIterator last, BinaryPredicate pred); ``` [heading Examples] @@ -86,12 +94,14 @@ std::vector indirect_sort (RAIterator first, RAIterator last, BinaryPred [heading Complexity] -Both of the variants of `indirect_sort` run in ['O(N lg N)] time; they are not more (or less) efficient than `std::sort`. There is an extra layer of indirection on each comparison, but all off the swaps are done on values of type `size_t` +Both of the variants of `indirect_sort` run in ['O(N lg N)] time; they are not more (or less) efficient than `std::sort`. There is an extra layer of indirection on each comparison, but all of the swaps are done on values of type `size_t` [heading Exception Safety] [heading Notes] +In numpy, this algorithm is known as `argsort`. + [endsect] [/ File indirect_sort.qbk diff --git a/test/indirect_sort_test.cpp b/test/indirect_sort_test.cpp index 21bd3bdce..dfef087ef 100644 --- a/test/indirect_sort_test.cpp +++ b/test/indirect_sort_test.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #define BOOST_TEST_MAIN #include @@ -20,7 +21,7 @@ #include #include -typedef std::vector Permutation; +using boost::algorithm::Permutation; // A permutation of size N is a sequence of values in the range [0..N) // such that no value appears more than once in the permutation. @@ -46,6 +47,9 @@ struct indirect_comp { Comp comp_; }; + //// ======================= + //// ==== indirect_sort ==== + //// ======================= template void test_one_sort(Iter first, Iter last) { Permutation perm = boost::algorithm::indirect_sort(first, last); @@ -53,7 +57,8 @@ void test_one_sort(Iter first, Iter last) { BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first))); // Make a copy of the data, apply the permutation, and ensure that it is sorted. - std::vector::value_type> v(first, last); + typedef std::vector::value_type> Vector; + Vector v(first, last); boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end())); } @@ -66,7 +71,8 @@ void test_one_sort(Iter first, Iter last, Comp comp) { indirect_comp(first, comp))); // Make a copy of the data, apply the permutation, and ensure that it is sorted. - std::vector::value_type> v(first, last); + typedef std::vector::value_type> Vector; + Vector v(first, last); boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end(), comp)); } @@ -93,8 +99,259 @@ void test_sort () { test_one_sort(v.begin(), v.end()); test_one_sort(v.begin(), v.end(), std::greater()); } + + + //// ============================== + //// ==== indirect_stable_sort ==== + //// ============================== + +template +struct MyPair { + MyPair () {} + + MyPair (const T1 &t1, const T2 &t2) + : first(t1), second(t2) {} + + T1 first; + T2 second; +}; + +template +bool operator < (const MyPair& lhs, const MyPair& rhs) { + return lhs.first < rhs.first; // compare only the first elements +} + +template +bool MyGreater (const MyPair& lhs, const MyPair& rhs) { + return lhs.first > rhs.first; // compare only the first elements +} + +template +void test_one_stable_sort(Iter first, Iter last) { + Permutation perm = boost::algorithm::indirect_stable_sort(first, last); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first))); + + if (first != last) { + Iter iFirst = first; + Iter iSecond = first; ++iSecond; + + while (iSecond != last) { + if (iFirst->first == iSecond->first) + BOOST_CHECK(iFirst->second < iSecond->second); + ++iFirst; + ++iSecond; + } + } + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end())); +} + +template +void test_one_stable_sort(Iter first, Iter last, Comp comp) { + Permutation perm = boost::algorithm::indirect_stable_sort(first, last, comp); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp(first, comp))); + + if (first != last) { + Iter iFirst = first; + Iter iSecond = first; ++iSecond; + + while (iSecond != last) { + if (iFirst->first == iSecond->first) + BOOST_CHECK(iFirst->second < iSecond->second); + ++iFirst; + ++iSecond; + } + } + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end(), comp)); +} + +void test_stable_sort () { + typedef MyPair Pair; + const int sz = 10; + Pair vals[sz]; + + for (int i = 0; i < sz; ++i) { + vals[i].first = 100 - (i >> 1); + vals[i].second = i; + } + + Pair *first = &vals[0]; + Pair const *cFirst = &vals[0]; + +// Test subsets + for (size_t i = 0; i <= sz; ++i) { + test_one_stable_sort(first, first + i); + test_one_stable_sort(first, first + i, MyGreater); + + // test with constant inputs + test_one_sort(cFirst, cFirst + i); + test_one_sort(cFirst, cFirst + i, MyGreater); + } +} + + //// =============================== + //// ==== indirect_partial_sort ==== + //// =============================== + +template +void test_one_partial_sort(Iter first, Iter middle, Iter last) { + const size_t middleIdx = std::distance(first, middle); + Permutation perm = boost::algorithm::indirect_partial_sort(first, middle, last); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.begin() + middleIdx, indirect_comp(first))); + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.begin() + middleIdx)); + +// Make sure that [middle, end) are all "greater" than the sorted part + if (middleIdx > 0) { + typename Vector::iterator lastSorted = v.begin() + middleIdx - 1; + for (typename Vector::iterator it = v.begin () + middleIdx; it != v.end(); ++it) + BOOST_CHECK(*lastSorted < *it); + } +} + +template +void test_one_partial_sort(Iter first, Iter middle, Iter last, Comp comp) { + const size_t middleIdx = std::distance(first, middle); + Permutation perm = boost::algorithm::indirect_partial_sort(first, middle, last, comp); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.begin() + middleIdx, + indirect_comp(first, comp))); + +// Make a copy of the data, apply the permutation, and ensure that it is sorted. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.begin() + middleIdx, comp)); + +// Make sure that [middle, end) are all "greater" than the sorted part + if (middleIdx > 0) { + typename Vector::iterator lastSorted = v.begin() + middleIdx - 1; + for (typename Vector::iterator it = v.begin () + middleIdx; it != v.end(); ++it) + BOOST_CHECK(comp(*lastSorted, *it)); + } +} + + +void test_partial_sort () { + int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; + const int sz = sizeof (num)/sizeof(num[0]); + int *first = &num[0]; + int const *cFirst = &num[0]; +// Test subsets + for (size_t i = 0; i <= sz; ++i) { + for (size_t j = 0; j < i; ++j) { + test_one_partial_sort(first, first + j, first + i); + test_one_partial_sort(first, first + j, first + i, std::greater()); + + // test with constant inputs + test_one_partial_sort(cFirst, cFirst + j, cFirst + i); + test_one_partial_sort(cFirst, cFirst + j, cFirst + i, std::greater()); + } + } + +// make sure we work with iterators as well as pointers + std::vector v(first, first + sz); + test_one_partial_sort(v.begin(), v.begin() + (sz / 2), v.end()); + test_one_partial_sort(v.begin(), v.begin() + (sz / 2), v.end(), std::greater()); + } + + + //// =================================== + //// ==== indirect_nth_element_sort ==== + //// =================================== + +template +void test_one_nth_element(Iter first, Iter nth, Iter last) { + const size_t nthIdx = std::distance(first, nth); + Permutation perm = boost::algorithm::indirect_nth_element(first, nth, last); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + + for (size_t i = 0; i < nthIdx; ++i) + BOOST_CHECK(!(first[perm[nthIdx]] < first[perm[i]])); // all items before the nth element are <= the nth element + for (size_t i = nthIdx; i < std::distance(first, last); ++i) + BOOST_CHECK(!(first[perm[i]] < first[perm[nthIdx]])); // all items before the nth element are >= the nth element + +// Make a copy of the data, apply the permutation, and ensure that the result is correct. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + + for (size_t i = 0; i < nthIdx; ++i) + BOOST_CHECK(!(v[nthIdx] < v[i])); // all items before the nth element are <= the nth element + for (size_t i = nthIdx; i < v.size(); ++i) + BOOST_CHECK(!(v[i] < v[nthIdx])); // all items before the nth element are >= the nth element +} + +template +void test_one_nth_element(Iter first, Iter nth, Iter last, Comp comp) { + const size_t nthIdx = std::distance(first, nth); + + Permutation perm = boost::algorithm::indirect_nth_element(first, nth, last, comp); + BOOST_CHECK (is_a_permutation(perm, std::distance(first, last))); + for (size_t i = 0; i < nthIdx; ++i) + BOOST_CHECK(!comp(first[perm[nthIdx]], first[perm[i]])); // all items before the nth element are <= the nth element + for (size_t i = nthIdx; i < std::distance(first, last); ++i) + BOOST_CHECK(!comp(first[perm[i]], first[perm[nthIdx]])); // all items before the nth element are >= the nth element + + +// Make a copy of the data, apply the permutation, and ensure that the result is correct. + typedef std::vector::value_type> Vector; + Vector v(first, last); + boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end()); + + for (size_t i = 0; i < nthIdx; ++i) + BOOST_CHECK(!comp(v[nthIdx], v[i])); // all items before the nth element are <= the nth element + for (size_t i = nthIdx; i < v.size(); ++i) + BOOST_CHECK(!comp(v[i], v[nthIdx])); // all items before the nth element are >= the nth element +} + + +void test_nth_element () { + int num[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 1, 2, 3, 4, 5 }; + const int sz = sizeof (num)/sizeof(num[0]); + int *first = &num[0]; + int const *cFirst = &num[0]; + +// Test subsets + for (size_t i = 0; i <= sz; ++i) { + for (size_t j = 0; j < i; ++j) { + test_one_nth_element(first, first + j, first + i); + test_one_nth_element(first, first + j, first + i, std::greater()); + + // test with constant inputs + test_one_nth_element(cFirst, cFirst + j, cFirst + i); + test_one_nth_element(cFirst, cFirst + j, cFirst + i, std::greater()); + } + } + +// make sure we work with iterators as well as pointers + std::vector v(first, first + sz); + test_one_nth_element(v.begin(), v.begin() + (sz / 2), v.end()); + test_one_nth_element(v.begin(), v.begin() + (sz / 2), v.end(), std::greater()); + } + + BOOST_AUTO_TEST_CASE( test_main ) { test_sort (); + test_stable_sort (); + test_partial_sort (); + test_nth_element (); } From 8be54b3eb7c485fb7171404cfc99c0fed8f90332 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Tue, 20 Jun 2023 20:31:08 -0700 Subject: [PATCH 4/7] Split test cases --- test/indirect_sort_test.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/test/indirect_sort_test.cpp b/test/indirect_sort_test.cpp index dfef087ef..fa1b7e24f 100644 --- a/test/indirect_sort_test.cpp +++ b/test/indirect_sort_test.cpp @@ -78,7 +78,7 @@ void test_one_sort(Iter first, Iter last, Comp comp) { } -void test_sort () { +BOOST_AUTO_TEST_CASE(test_sort) { int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; const int sz = sizeof (num)/sizeof(num[0]); int *first = &num[0]; @@ -176,7 +176,7 @@ void test_one_stable_sort(Iter first, Iter last, Comp comp) { BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end(), comp)); } -void test_stable_sort () { +BOOST_AUTO_TEST_CASE(test_stable_sort) { typedef MyPair Pair; const int sz = 10; Pair vals[sz]; @@ -248,7 +248,7 @@ void test_one_partial_sort(Iter first, Iter middle, Iter last, Comp comp) { } -void test_partial_sort () { +BOOST_AUTO_TEST_CASE(test_partial_sort) { int num[] = { 1,3,5,7,9, 2, 4, 6, 8, 10 }; const int sz = sizeof (num)/sizeof(num[0]); int *first = &num[0]; @@ -323,7 +323,7 @@ void test_one_nth_element(Iter first, Iter nth, Iter last, Comp comp) { } -void test_nth_element () { +BOOST_AUTO_TEST_CASE(test_nth_element) { int num[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 1, 2, 3, 4, 5 }; const int sz = sizeof (num)/sizeof(num[0]); int *first = &num[0]; @@ -346,12 +346,3 @@ void test_nth_element () { test_one_nth_element(v.begin(), v.begin() + (sz / 2), v.end()); test_one_nth_element(v.begin(), v.begin() + (sz / 2), v.end(), std::greater()); } - - -BOOST_AUTO_TEST_CASE( test_main ) -{ - test_sort (); - test_stable_sort (); - test_partial_sort (); - test_nth_element (); -} From a7ae53d615255e8ac389694a7de44a1b214be173 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Fri, 23 Jun 2023 19:32:48 -0700 Subject: [PATCH 5/7] Update docs; fix copy-pasta --- include/boost/algorithm/indirect_sort.hpp | 41 +++++++++++++---------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/include/boost/algorithm/indirect_sort.hpp b/include/boost/algorithm/indirect_sort.hpp index f0a98626e..8708c3dbe 100644 --- a/include/boost/algorithm/indirect_sort.hpp +++ b/include/boost/algorithm/indirect_sort.hpp @@ -43,10 +43,11 @@ typedef std::vector Permutation; // ===== sort ===== -/// \fn indirect_sort (RAIterator first, RAIterator last, Predicate p) +/// \fn indirect_sort (RAIterator first, RAIterator last, Predicate pred) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted according to the predicate pred. +/// the result is ordered as if 'std::sort(first, last, pred)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param last The end of the input sequence @@ -64,7 +65,8 @@ Permutation indirect_sort (RAIterator first, RAIterator last, Pred pred) { /// \fn indirect_sort (RAIterator first, RAIterator last) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted in non-descending order. +/// the result is ordered as if 'std::sort(first, last)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param last The end of the input sequence @@ -77,10 +79,11 @@ Permutation indirect_sort (RAIterator first, RAIterator last) { // ===== stable_sort ===== -/// \fn indirect_stable_sort (RAIterator first, RAIterator last, Predicate p) +/// \fn indirect_stable_sort (RAIterator first, RAIterator last, Predicate pred) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted according to the predicate pred. +/// the result is ordered as if 'std::stable_sort(first, last, pred)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param last The end of the input sequence @@ -98,7 +101,8 @@ Permutation indirect_stable_sort (RAIterator first, RAIterator last, Pred pred) /// \fn indirect_stable_sort (RAIterator first, RAIterator last) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted in non-descending order. +/// the result is ordered as if 'std::stable_sort(first, last)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param last The end of the input sequence @@ -111,12 +115,11 @@ Permutation indirect_stable_sort (RAIterator first, RAIterator last) { // ===== partial_sort ===== -/// \fn indirect_partial_sort (RAIterator first, RAIterator last, Predicate p) +/// \fn indirect_partial_sort (RAIterator first, RAIterator middle, RAIterator last, Predicate pred) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the resulting range [first, middle) is sorted and the range [middle,last) -/// consists of elements that are "larger" than then ones in [first, middle), -/// according to the predicate pred. +/// the result is ordered as if 'std::partial_sort(first, middle, last, pred)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param middle The end of the range to be sorted @@ -134,14 +137,14 @@ Permutation indirect_partial_sort (RAIterator first, RAIterator middle, return ret; } -/// \fn indirect_partial_sort (RAIterator first, RAIterator last) +/// \fn indirect_partial_sort (RAIterator first, RAIterator middle, RAIterator last) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the resulting range [first, middle) is sorted in non-descending order, -/// and the range [middle,last) consists of elements that are larger than -/// then ones in [first, middle). +/// the result is ordered as if 'std::partial_sort(first, middle, last)' +// was called on the sequence. /// /// \param first The start of the input sequence +/// \param middle The end of the range to be sorted /// \param last The end of the input sequence /// template @@ -152,10 +155,11 @@ Permutation indirect_partial_sort (RAIterator first, RAIterator middle, RAIterat // ===== nth_element ===== -/// \fn indirect_nth_element (RAIterator first, RAIterator last, Predicate p) +/// \fn indirect_nth_element (RAIterator first, RAIterator nth, RAIterator last, Predicate p) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted according to the predicate pred. +/// the result is ordered as if 'std::nth_element(first, nth, last, p)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param nth The sort partition point in the input sequence @@ -172,10 +176,11 @@ Permutation indirect_nth_element (RAIterator first, RAIterator nth, return ret; } -/// \fn indirect_nth_element (RAIterator first, RAIterator last) +/// \fn indirect_nth_element (RAIterator first, RAIterator nth, RAIterator last) /// \returns a permutation of the elements in the range [first, last) /// such that when the permutation is applied to the sequence, -/// the result is sorted in non-descending order. +/// the result is ordered as if 'std::nth_element(first, nth, last)' +// was called on the sequence. /// /// \param first The start of the input sequence /// \param nth The sort partition point in the input sequence From 25ab833caec7d8b6f3be4bc7254f979dc46e3104 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Sun, 25 Jun 2023 20:20:45 -0700 Subject: [PATCH 6/7] Replace call to 'iota' with hand-rolled loop --- include/boost/algorithm/indirect_sort.hpp | 34 ++++++++++++++--------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/include/boost/algorithm/indirect_sort.hpp b/include/boost/algorithm/indirect_sort.hpp index 8708c3dbe..c66bbb489 100644 --- a/include/boost/algorithm/indirect_sort.hpp +++ b/include/boost/algorithm/indirect_sort.hpp @@ -18,10 +18,10 @@ #include // for std::less #include // for std::vector -#include - namespace boost { namespace algorithm { +typedef std::vector Permutation; + namespace detail { template @@ -37,10 +37,18 @@ namespace detail { Iter iter_; }; + // Initialize a permutation + // it would be nice to use 'iota' here, but that call writes over + // existing elements - not append them. I don't want to initialize + // the elements of the permutation to zero, and then immediately + // overwrite them. + void init_permutation (Permutation &p, size_t size) { + p.reserve(size); + for (size_t i = 0; i < size; ++i) + p.push_back(i); + } } -typedef std::vector Permutation; - // ===== sort ===== /// \fn indirect_sort (RAIterator first, RAIterator last, Predicate pred) @@ -55,8 +63,9 @@ typedef std::vector Permutation; /// template Permutation indirect_sort (RAIterator first, RAIterator last, Pred pred) { - Permutation ret(std::distance(first, last)); - boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + + Permutation ret; + detail::init_permutation(ret, std::distance(first, last)); std::sort(ret.begin(), ret.end(), detail::indirect_predicate(pred, first)); return ret; @@ -91,8 +100,8 @@ Permutation indirect_sort (RAIterator first, RAIterator last) { /// template Permutation indirect_stable_sort (RAIterator first, RAIterator last, Pred pred) { - Permutation ret(std::distance(first, last)); - boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + Permutation ret; + detail::init_permutation(ret, std::distance(first, last)); std::stable_sort(ret.begin(), ret.end(), detail::indirect_predicate(pred, first)); return ret; @@ -129,9 +138,8 @@ Permutation indirect_stable_sort (RAIterator first, RAIterator last) { template Permutation indirect_partial_sort (RAIterator first, RAIterator middle, RAIterator last, Pred pred) { - Permutation ret(std::distance(first, last)); - - boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + Permutation ret; + detail::init_permutation(ret, std::distance(first, last)); std::partial_sort(ret.begin(), ret.begin() + std::distance(first, middle), ret.end(), detail::indirect_predicate(pred, first)); return ret; @@ -169,8 +177,8 @@ Permutation indirect_partial_sort (RAIterator first, RAIterator middle, RAIterat template Permutation indirect_nth_element (RAIterator first, RAIterator nth, RAIterator last, Pred pred) { - Permutation ret(std::distance(first, last)); - boost::algorithm::iota(ret.begin(), ret.end(), size_t(0)); + Permutation ret; + detail::init_permutation(ret, std::distance(first, last)); std::nth_element(ret.begin(), ret.begin() + std::distance(first, nth), ret.end(), detail::indirect_predicate(pred, first)); return ret; From 62922bd1ff36d3e476908ae060c6dd308ed54d13 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Sun, 25 Jun 2023 20:32:15 -0700 Subject: [PATCH 7/7] Replace hand-rolled loop with 'iota_n' and back_insert_iterator --- include/boost/algorithm/indirect_sort.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/boost/algorithm/indirect_sort.hpp b/include/boost/algorithm/indirect_sort.hpp index c66bbb489..72c2f2e12 100644 --- a/include/boost/algorithm/indirect_sort.hpp +++ b/include/boost/algorithm/indirect_sort.hpp @@ -18,6 +18,8 @@ #include // for std::less #include // for std::vector +#include + namespace boost { namespace algorithm { typedef std::vector Permutation; @@ -37,15 +39,15 @@ namespace detail { Iter iter_; }; - // Initialize a permutation - // it would be nice to use 'iota' here, but that call writes over + // Initialize a permutation of size 'size'. [ 0, 1, 2, ... size-1 ] + // Note: it would be nice to use 'iota' here, but that call writes over // existing elements - not append them. I don't want to initialize // the elements of the permutation to zero, and then immediately // overwrite them. void init_permutation (Permutation &p, size_t size) { p.reserve(size); - for (size_t i = 0; i < size; ++i) - p.push_back(i); + boost::algorithm::iota_n( + std::back_insert_iterator(p), size_t(0), size); } }