forked from boostorg/algorithm
Add more tests
This commit is contained in:
@ -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 {
|
struct AType {
|
||||||
Type0 key;
|
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<size_t>` 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<size_t>` 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 <typename RAIterator>
|
template <typename RAIterator>
|
||||||
std::vector<size_t> indirect_sort (RAIterator first, RAIterator last);
|
std::vector<size_t> indirect_sort (RAIterator first, RAIterator last);
|
||||||
|
|
||||||
template <typename RAIterator, typename BinaryPredicate>
|
template <typename RAIterator, typename BinaryPredicate>
|
||||||
std::vector<size_t> indirect_sort (RAIterator first, RAIterator last, BinaryPredicate pred);
|
std::vector<size_t> indirect_sort (RAIterator first, RAIterator last, BinaryPredicate pred);
|
||||||
|
|
||||||
|
template <typename RAIterator>
|
||||||
|
std::vector<size_t> indirect_stable_sort (RAIterator first, RAIterator last);
|
||||||
|
|
||||||
|
template <typename RAIterator, typename BinaryPredicate>
|
||||||
|
std::vector<size_t> indirect_stable_sort (RAIterator first, RAIterator last, BinaryPredicate pred);
|
||||||
```
|
```
|
||||||
|
|
||||||
[heading Examples]
|
[heading Examples]
|
||||||
@ -86,12 +94,14 @@ std::vector<size_t> indirect_sort (RAIterator first, RAIterator last, BinaryPred
|
|||||||
|
|
||||||
[heading Complexity]
|
[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 Exception Safety]
|
||||||
|
|
||||||
[heading Notes]
|
[heading Notes]
|
||||||
|
|
||||||
|
In numpy, this algorithm is known as `argsort`.
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[/ File indirect_sort.qbk
|
[/ File indirect_sort.qbk
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <boost/algorithm/indirect_sort.hpp>
|
#include <boost/algorithm/indirect_sort.hpp>
|
||||||
#include <boost/algorithm/apply_permutation.hpp>
|
#include <boost/algorithm/apply_permutation.hpp>
|
||||||
#include <boost/algorithm/cxx11/is_sorted.hpp>
|
#include <boost/algorithm/cxx11/is_sorted.hpp>
|
||||||
|
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||||
|
|
||||||
#define BOOST_TEST_MAIN
|
#define BOOST_TEST_MAIN
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
@ -20,7 +21,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
typedef std::vector<size_t> Permutation;
|
using boost::algorithm::Permutation;
|
||||||
|
|
||||||
// A permutation of size N is a sequence of values in the range [0..N)
|
// 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.
|
// such that no value appears more than once in the permutation.
|
||||||
@ -46,6 +47,9 @@ struct indirect_comp {
|
|||||||
Comp comp_;
|
Comp comp_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//// =======================
|
||||||
|
//// ==== indirect_sort ====
|
||||||
|
//// =======================
|
||||||
template <typename Iter>
|
template <typename Iter>
|
||||||
void test_one_sort(Iter first, Iter last) {
|
void test_one_sort(Iter first, Iter last) {
|
||||||
Permutation perm = boost::algorithm::indirect_sort(first, 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<Iter>(first)));
|
BOOST_CHECK (boost::algorithm::is_sorted(perm.begin(), perm.end(), indirect_comp<Iter>(first)));
|
||||||
|
|
||||||
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
||||||
std::vector<typename std::iterator_traits<Iter>::value_type> v(first, last);
|
typedef std::vector<typename std::iterator_traits<Iter>::value_type> Vector;
|
||||||
|
Vector v(first, last);
|
||||||
boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end());
|
boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end());
|
||||||
BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.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<Iter, Comp>(first, comp)));
|
indirect_comp<Iter, Comp>(first, comp)));
|
||||||
|
|
||||||
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
||||||
std::vector<typename std::iterator_traits<Iter>::value_type> v(first, last);
|
typedef std::vector<typename std::iterator_traits<Iter>::value_type> Vector;
|
||||||
|
Vector v(first, last);
|
||||||
boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end());
|
boost::algorithm::apply_permutation(v.begin(), v.end(), perm.begin(), perm.end());
|
||||||
BOOST_CHECK (boost::algorithm::is_sorted(v.begin(), v.end(), comp));
|
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());
|
||||||
test_one_sort(v.begin(), v.end(), std::greater<int>());
|
test_one_sort(v.begin(), v.end(), std::greater<int>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//// ==============================
|
||||||
|
//// ==== indirect_stable_sort ====
|
||||||
|
//// ==============================
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
struct MyPair {
|
||||||
|
MyPair () {}
|
||||||
|
|
||||||
|
MyPair (const T1 &t1, const T2 &t2)
|
||||||
|
: first(t1), second(t2) {}
|
||||||
|
|
||||||
|
T1 first;
|
||||||
|
T2 second;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool operator < (const MyPair<T1, T2>& lhs, const MyPair<T1, T2>& rhs) {
|
||||||
|
return lhs.first < rhs.first; // compare only the first elements
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool MyGreater (const MyPair<T1, T2>& lhs, const MyPair<T1, T2>& rhs) {
|
||||||
|
return lhs.first > rhs.first; // compare only the first elements
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
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<Iter>(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<typename std::iterator_traits<Iter>::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 <typename Iter, typename Comp>
|
||||||
|
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<Iter, 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<typename std::iterator_traits<Iter>::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<int, long> 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<int, long>);
|
||||||
|
|
||||||
|
// test with constant inputs
|
||||||
|
test_one_sort(cFirst, cFirst + i);
|
||||||
|
test_one_sort(cFirst, cFirst + i, MyGreater<int, long>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//// ===============================
|
||||||
|
//// ==== indirect_partial_sort ====
|
||||||
|
//// ===============================
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
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<Iter>(first)));
|
||||||
|
|
||||||
|
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
||||||
|
typedef std::vector<typename std::iterator_traits<Iter>::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 <typename Iter, typename Comp>
|
||||||
|
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<Iter, Comp>(first, comp)));
|
||||||
|
|
||||||
|
// Make a copy of the data, apply the permutation, and ensure that it is sorted.
|
||||||
|
typedef std::vector<typename std::iterator_traits<Iter>::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<int>());
|
||||||
|
|
||||||
|
// test with constant inputs
|
||||||
|
test_one_partial_sort(cFirst, cFirst + j, cFirst + i);
|
||||||
|
test_one_partial_sort(cFirst, cFirst + j, cFirst + i, std::greater<int>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we work with iterators as well as pointers
|
||||||
|
std::vector<int> 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<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//// ===================================
|
||||||
|
//// ==== indirect_nth_element_sort ====
|
||||||
|
//// ===================================
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
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<typename std::iterator_traits<Iter>::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 <typename Iter, typename Comp>
|
||||||
|
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<typename std::iterator_traits<Iter>::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<int>());
|
||||||
|
|
||||||
|
// test with constant inputs
|
||||||
|
test_one_nth_element(cFirst, cFirst + j, cFirst + i);
|
||||||
|
test_one_nth_element(cFirst, cFirst + j, cFirst + i, std::greater<int>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we work with iterators as well as pointers
|
||||||
|
std::vector<int> 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<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( test_main )
|
BOOST_AUTO_TEST_CASE( test_main )
|
||||||
{
|
{
|
||||||
test_sort ();
|
test_sort ();
|
||||||
|
test_stable_sort ();
|
||||||
|
test_partial_sort ();
|
||||||
|
test_nth_element ();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user