Compare commits

...

11 Commits

Author SHA1 Message Date
62922bd1ff Replace hand-rolled loop with 'iota_n' and back_insert_iterator 2023-06-25 20:32:15 -07:00
25ab833cae Replace call to 'iota' with hand-rolled loop 2023-06-25 20:20:45 -07:00
a7ae53d615 Update docs; fix copy-pasta 2023-06-23 19:32:48 -07:00
8be54b3eb7 Split test cases 2023-06-20 20:31:08 -07:00
3ae9ee2f92 Add more tests 2023-06-20 20:01:38 -07:00
814f8a5c05 Update docs based on feedback, add 'stable_sort', 'partial_sort' and 'nth_element' 2023-06-18 11:31:32 -07:00
17c47e8061 Add 'indirect_sort' 2023-06-14 18:30:08 -07:00
faac048d59 Merge pull request #116 from jiayuehua/develop
fix range-base gather algorithm
2023-06-14 09:23:41 -07:00
d1ecc8b0a8 fix range-base gather algorithm 2023-05-08 11:06:15 +08:00
3fe40eb999 Merge pull request #21 from MarcelRaad/fix_tests
Fix MSVC warnings in tests
2023-01-01 09:52:45 -08:00
e4d4a685da Fix MSVC warnings in tests
MSVC complains about narrowing conversions and unreferences parameters.
This makes all tests almost level 4 warning clean on MSVC 14.
2022-12-30 23:02:32 +01:00
13 changed files with 703 additions and 31 deletions

View File

@ -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]

111
doc/indirect_sort.qbk Normal file
View File

@ -0,0 +1,111 @@
[/ 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 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.
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.
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 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 = indirect_sort(first, last);
for (size_t idx: permutation)
std::cout << first[idx] << std::endl;
```
Assume that instead of an "array of structures", you have a "struct of arrays".
```
struct AType {
Type0 key;
Type1 value1;
Type1 value2;
};
std::array<AType, 1000> arrayOfStruct;
```
versus:
```
template <size_t N>
struct AType {
std::array<Type0, N> key;
std::array<Type1, N> value1;
std::array<Type2, N> 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` 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>
std::vector<size_t> indirect_sort (RAIterator first, RAIterator last);
template <typename RAIterator, typename BinaryPredicate>
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 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 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
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).
]

View File

@ -1,4 +1,4 @@
/*
/*
Copyright 2008 Adobe Systems Incorporated
Distributed under the Boost Software License, Version 1.0. (See accompanying
@ -84,7 +84,7 @@ namespace boost { namespace algorithm {
template <
typename BidirectionalIterator, // models BidirectionalIterator
typename Pred> // models UnaryPredicate
std::pair<BidirectionalIterator, BidirectionalIterator> gather
std::pair<BidirectionalIterator, BidirectionalIterator> gather
( BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator pivot, Pred pred )
{
// The first call partitions everything up to (but not including) the pivot element,
@ -106,11 +106,11 @@ template <
typename BidirectionalRange, //
typename Pred> // Pred models UnaryPredicate
std::pair<
typename boost::range_iterator<const BidirectionalRange>::type,
typename boost::range_iterator<const BidirectionalRange>::type>
typename boost::range_iterator<BidirectionalRange>::type,
typename boost::range_iterator<BidirectionalRange>::type>
gather (
const BidirectionalRange &range,
typename boost::range_iterator<const BidirectionalRange>::type pivot,
BidirectionalRange &range,
typename boost::range_iterator<BidirectionalRange>::type pivot,
Pred pred )
{
return boost::algorithm::gather ( boost::begin ( range ), boost::end ( range ), pivot, pred );

View File

@ -0,0 +1,207 @@
/*
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_INDIRECT_SORT
#define BOOST_ALGORITHM_INDIRECT_SORT
#include <algorithm> // for std::sort (and others)
#include <functional> // for std::less
#include <vector> // for std::vector
#include <boost/algorithm/cxx11/iota.hpp>
namespace boost { namespace algorithm {
typedef std::vector<size_t> Permutation;
namespace detail {
template <class Predicate, class Iter>
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_;
};
// 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);
boost::algorithm::iota_n(
std::back_insert_iterator<Permutation>(p), size_t(0), size);
}
}
// ===== sort =====
/// \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 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
/// \param pred The predicate to compare elements with
///
template <typename RAIterator, typename Pred>
Permutation indirect_sort (RAIterator first, RAIterator last, Pred pred) {
Permutation ret;
detail::init_permutation(ret, std::distance(first, last));
std::sort(ret.begin(), ret.end(),
detail::indirect_predicate<Pred, RAIterator>(pred, first));
return ret;
}
/// \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 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
///
template <typename RAIterator>
Permutation indirect_sort (RAIterator first, RAIterator last) {
return indirect_sort(first, last,
std::less<typename std::iterator_traits<RAIterator>::value_type>());
}
// ===== stable_sort =====
/// \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 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
/// \param pred The predicate to compare elements with
///
template <typename RAIterator, typename Pred>
Permutation indirect_stable_sort (RAIterator first, RAIterator last, Pred pred) {
Permutation ret;
detail::init_permutation(ret, std::distance(first, last));
std::stable_sort(ret.begin(), ret.end(),
detail::indirect_predicate<Pred, RAIterator>(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 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
///
template <typename RAIterator>
Permutation indirect_stable_sort (RAIterator first, RAIterator last) {
return indirect_stable_sort(first, last,
std::less<typename std::iterator_traits<RAIterator>::value_type>());
}
// ===== partial_sort =====
/// \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 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
/// \param last The end of the input sequence
/// \param pred The predicate to compare elements with
///
template <typename RAIterator, typename Pred>
Permutation indirect_partial_sort (RAIterator first, RAIterator middle,
RAIterator last, Pred pred) {
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, RAIterator>(pred, first));
return ret;
}
/// \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 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 <typename RAIterator>
Permutation indirect_partial_sort (RAIterator first, RAIterator middle, RAIterator last) {
return indirect_partial_sort(first, middle, last,
std::less<typename std::iterator_traits<RAIterator>::value_type>());
}
// ===== nth_element =====
/// \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 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
/// \param last The end of the input sequence
/// \param pred The predicate to compare elements with
///
template <typename RAIterator, typename Pred>
Permutation indirect_nth_element (RAIterator first, RAIterator nth,
RAIterator last, Pred pred) {
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, RAIterator>(pred, first));
return ret;
}
/// \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 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
/// \param last The end of the input sequence
///
template <typename RAIterator>
Permutation indirect_nth_element (RAIterator first, RAIterator nth, RAIterator last) {
return indirect_nth_element(first, nth, last,
std::less<typename std::iterator_traits<RAIterator>::value_type>());
}
}}
#endif // BOOST_ALGORITHM_INDIRECT_SORT

View File

@ -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 ]

View File

@ -28,8 +28,8 @@
namespace ba = boost::algorithm;
// namespace ba = boost;
BOOST_CXX14_CONSTEXPR bool is_true ( int v ) { return true; }
BOOST_CXX14_CONSTEXPR bool is_false ( int v ) { return false; }
BOOST_CXX14_CONSTEXPR bool is_true ( int ) { return true; }
BOOST_CXX14_CONSTEXPR bool is_false ( int ) { return false; }
BOOST_CXX14_CONSTEXPR bool is_even ( int v ) { return v % 2 == 0; }
BOOST_CXX14_CONSTEXPR bool is_odd ( int v ) { return v % 2 == 1; }
BOOST_CXX14_CONSTEXPR bool is_zero ( int v ) { return v == 0; }

View File

@ -25,7 +25,7 @@ void test_short_input1 () {
std::string s;
try { ba::unhex ( std::string ( "A" ), std::back_inserter(s)); }
catch ( const std::exception &ex ) { return; }
catch ( const std::exception & ) { return; }
BOOST_TEST_MESSAGE ( "Failed to catch std::exception in test_short_input1" );
BOOST_CHECK ( false );
}
@ -34,7 +34,7 @@ void test_short_input2 () {
std::string s;
try { ba::unhex ( std::string ( "A" ), std::back_inserter(s)); }
catch ( const ba::hex_decode_error &ex ) { return; }
catch ( const ba::hex_decode_error & ) { return; }
BOOST_TEST_MESSAGE ( "Failed to catch ba::hex_decode_error in test_short_input2" );
BOOST_CHECK ( false );
}
@ -43,7 +43,7 @@ void test_short_input3 () {
std::string s;
try { ba::unhex ( std::string ( "A" ), std::back_inserter(s)); }
catch ( const ba::not_enough_input &ex ) { return; }
catch ( const ba::not_enough_input & ) { return; }
BOOST_TEST_MESSAGE ( "Failed to catch ba::not_enough_input in test_short_input3" );
BOOST_CHECK ( false );
}
@ -53,8 +53,8 @@ void test_short_input4 () {
std::string s;
try { ba::unhex ( std::string ( "A" ), std::back_inserter(s)); }
catch ( const ba::non_hex_input &ex ) { BOOST_CHECK ( false ); }
catch ( const ba::not_enough_input &ex ) { return; }
catch ( const ba::non_hex_input & ) { BOOST_CHECK ( false ); }
catch ( const ba::not_enough_input & ) { return; }
catch ( ... ) { BOOST_CHECK ( false ); }
BOOST_CHECK ( false );
}
@ -64,8 +64,8 @@ void test_short_input5 () {
std::string s;
try { ba::unhex ( "A", std::back_inserter(s)); }
catch ( const ba::non_hex_input &ex ) { BOOST_CHECK ( false ); }
catch ( const ba::not_enough_input &ex ) { return; }
catch ( const ba::non_hex_input & ) { BOOST_CHECK ( false ); }
catch ( const ba::not_enough_input & ) { return; }
catch ( ... ) { BOOST_CHECK ( false ); }
BOOST_CHECK ( false );
}
@ -125,8 +125,8 @@ void test_nonhex_input4 () {
std::string s;
try { ba::unhex ( "P1234FA1234", std::back_inserter(s)); }
catch ( const ba::not_enough_input &ex ) { BOOST_CHECK ( false ); }
catch ( const ba::non_hex_input &ex ) { return; }
catch ( const ba::not_enough_input & ) { BOOST_CHECK ( false ); }
catch ( const ba::non_hex_input & ) { return; }
catch ( ... ) { BOOST_CHECK ( false ); }
BOOST_CHECK ( false );
}

348
test/indirect_sort_test.cpp Normal file
View File

@ -0,0 +1,348 @@
/*
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)
For more information, see http://www.boost.org
*/
#include <boost/config.hpp>
#include <boost/algorithm/indirect_sort.hpp>
#include <boost/algorithm/apply_permutation.hpp>
#include <boost/algorithm/cxx11/is_sorted.hpp>
#include <boost/algorithm/cxx11/all_of.hpp>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <list>
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.
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.
std::sort(p.begin(), p.end());
for (size_t i = 0; i < N; ++i)
if (p[i] != i) return false;
return true;
}
template <typename Iter,
typename Comp = typename std::less<typename std::iterator_traits<Iter>::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_;
};
//// =======================
//// ==== indirect_sort ====
//// =======================
template <typename Iter>
void test_one_sort(Iter first, Iter last) {
Permutation perm = boost::algorithm::indirect_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)));
// 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_sort(Iter first, Iter last, Comp comp) {
Permutation perm = boost::algorithm::indirect_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)));
// 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));
}
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];
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<int>());
// test with constant inputs
test_one_sort(cFirst, cFirst + i);
test_one_sort(cFirst, cFirst + i, std::greater<int>());
}
// make sure we work with iterators as well as pointers
std::vector<int> v(first, first + sz);
test_one_sort(v.begin(), v.end());
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));
}
BOOST_AUTO_TEST_CASE(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));
}
}
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];
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
}
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];
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>());
}

View File

@ -112,7 +112,7 @@ void test_sequence1 () {
std::vector<int> v, v1;
v.clear ();
for ( std::size_t i = 5; i < 15; ++i )
for ( int i = 5; i < 15; ++i )
v.push_back ( i );
v1 = v;
BOOST_CHECK ( ba::is_permutation ( v.begin (), v.end (), v.begin ())); // better be a permutation of itself!

View File

@ -32,7 +32,7 @@ namespace {
// Check using iterators
template<typename Container>
void check_one_iter ( const Container &haystack, const std::string &needle, int expected ) {
void check_one_iter ( const Container &haystack, const std::string &needle, std::ptrdiff_t expected ) {
typedef typename Container::const_iterator iter_type;
typedef typename std::pair<iter_type, iter_type> ret_type;
typedef std::string::const_iterator pattern_type;
@ -53,7 +53,7 @@ namespace {
// iter_type it1r = ret1r.first;
// iter_type it2 = ret2.first;
// iter_type it3 = ret3.first;
const int dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
const std::ptrdiff_t dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
std::cout << "(Iterators) Pattern is " << needle.length () << ", haysstack is " << haystack.length () << " chars long; " << std::endl;
try {
@ -97,7 +97,7 @@ namespace {
// Check using pointers
// We're assuming that the container implements contiguous storage here.
template<typename Container>
void check_one_pointer ( const Container &haystack, const std::string &needle, int expected ) {
void check_one_pointer ( const Container &haystack, const std::string &needle, std::ptrdiff_t expected ) {
typedef const typename Container::value_type *ptr_type;
typedef typename std::pair<ptr_type, ptr_type> ret_type;
@ -110,7 +110,7 @@ namespace {
ret_type ret1 = ba::boyer_moore_search (hBeg, hEnd, nBeg, nEnd);
ret_type ret2 = ba::boyer_moore_horspool_search (hBeg, hEnd, nBeg, nEnd);
ret_type ret3 = ba::knuth_morris_pratt_search (hBeg, hEnd, nBeg, nEnd);
const int dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
const std::ptrdiff_t dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
std::cout << "(Pointers) Pattern is " << needle.length () << ", haysstack is " << haystack.length () << " chars long; " << std::endl;
try {
@ -147,7 +147,7 @@ namespace {
// Check using objects
template<typename Container>
void check_one_object ( const Container &haystack, const std::string &needle, int expected ) {
void check_one_object ( const Container &haystack, const std::string &needle, std::ptrdiff_t expected ) {
typedef typename Container::const_iterator iter_type;
typedef typename std::pair<iter_type, iter_type> ret_type;
typedef std::string::const_iterator pattern_type;
@ -169,7 +169,7 @@ namespace {
ret_type retr1r = bm_r (haystack);
ret_type ret2 = bmh (hBeg, hEnd);
ret_type ret3 = kmp (hBeg, hEnd);
const int dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
const std::ptrdiff_t dist = ret1.first == hEnd ? -1 : std::distance ( hBeg, ret1.first );
std::cout << "(Objects) Pattern is " << needle.length () << ", haysstack is " << haystack.length () << " chars long; " << std::endl;
try {
@ -224,7 +224,7 @@ namespace {
template<typename Container>
void check_one ( const Container &haystack, const std::string &needle, int expected ) {
void check_one ( const Container &haystack, const std::string &needle, std::ptrdiff_t expected ) {
check_one_iter ( haystack, needle, expected );
check_one_pointer ( haystack, needle, expected );
check_one_object ( haystack, needle, expected );

View File

@ -85,7 +85,7 @@ namespace {
std::cout << std::endl;
}
void check_one ( const vec &haystack, const vec &needle, int expected ) {
void check_one ( const vec &haystack, const vec &needle, std::ptrdiff_t expected ) {
std::size_t i;
std::clock_t sTime;
unsigned long stdDiff;
@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE( test_main )
std::cout << "---- Middle -----" << std::endl;
check_one ( c1, p1f, -2 ); // Don't know answer
std::cout << "------ End ------" << std::endl;
check_one ( c1, p1e, c1.size() - p1e.size ());
check_one ( c1, p1e, static_cast<std::ptrdiff_t>(c1.size() - p1e.size ()));
std::cout << "--- Not found ---" << std::endl;
check_one ( c1, p1n, -1 ); // Not found
}

View File

@ -85,7 +85,7 @@ namespace {
std::cout << std::endl;
}
void check_one ( const vec &haystack, const vec &needle, int expected ) {
void check_one ( const vec &haystack, const vec &needle, std::ptrdiff_t expected ) {
std::size_t i;
std::clock_t sTime;
unsigned long stdDiff;
@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE( test_main )
std::cout << "---- Middle -----" << std::endl;
check_one ( c1, p1f, -2 ); // Don't know answer
std::cout << "------ End ------" << std::endl;
check_one ( c1, p1e, c1.size() - p1e.size ());
check_one ( c1, p1e, static_cast<std::ptrdiff_t>(c1.size() - p1e.size ()));
std::cout << "--- Not found ---" << std::endl;
check_one ( c1, p1n, -1 ); // Not found
}

View File

@ -62,7 +62,7 @@ namespace {
return retVal;
}
void check_one ( const vec &haystack, const vec &needle, int expected ) {
void check_one ( const vec &haystack, const vec &needle, std::ptrdiff_t expected ) {
std::pair<vec::const_iterator, vec::const_iterator> res;
std::pair<vec::const_iterator, vec::const_iterator> exp; // the expected result
@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE( test_main )
std::cout << "---- Middle -----" << std::endl;
check_one ( c1, p1f, -2 ); // Don't know answer
std::cout << "------ End ------" << std::endl;
check_one ( c1, p1e, c1.size() - p1e.size ());
check_one ( c1, p1e, static_cast<std::ptrdiff_t>(c1.size() - p1e.size ()));
std::cout << "--- Not found ---" << std::endl;
check_one ( c1, p1n, -1 ); // Not found
}