Skip to content

Commit 38a3a2d

Browse files
committed
Add range overloads to gfx::timsort
1 parent 6af8bfe commit 38a3a2d

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

README.md

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,37 @@ According to the benchmarks, it is slower than `std::sort()` on randomized seque
1616
ones. `gfx::timsort` should be usable as a drop-in replacement for `std::stable_sort`, with the difference that it
1717
can't fallback to a O(n log² n) algorithm when there isn't enough extra heap memory available.
1818

19-
Additionally `gfx::timsort` can take a [projection function](https://ezoeryou.github.io/blog/article/2019-01-22-ranges-projection.html)
20-
after the comparison function. The support is a bit rougher than in the linked article or the C++20 standard library:
21-
unless `std::invoke` is available, only instances of types callable with parentheses can be used, there is no support
22-
for pointer to members.
19+
`gfx::timsort` also has a few additional features and guarantees compared to `std::stable_sort`:
20+
* It can take a [projection function](https://ezoeryou.github.io/blog/article/2019-01-22-ranges-projection.html)
21+
after the comparison function. The support is a bit rougher than in the linked article or the C++20 standard library:
22+
unless `std::invoke` is available, only instances of types callable with parentheses can be used, there is no support
23+
for pointer to members.
24+
* It can also be passed a range instead of a pair of iterators, in which case it will sort the whole range.
25+
* This implementation of timsort notably avoids using the postfix `++` or `--` operators: only their prefix equivalents
26+
are used, which means that timsort will work even if the postfix operators are not present or return an incompatible
27+
type such as `void`.
2328

24-
This implementation of timsort notably avoids using the postfix `++` or `--` operators: only their prefix equivalents
25-
are used, which means that timsort will work even if the postfix operators are not present or return an incompatible
26-
type such as `void`.
29+
30+
The full list of available signatures is as follows (in namespace `gfx`):
31+
32+
```cpp
33+
// Overloads taking a pair of iterators
34+
template <typename RandomAccessIterator>
35+
void timsort(RandomAccessIterator const first, RandomAccessIterator const last);
36+
template <typename RandomAccessIterator, typename Compare>
37+
void timsort(RandomAccessIterator const first, RandomAccessIterator const last,
38+
Compare compare);
39+
template <typename RandomAccessIterator, typename Compare, typename Projection>
40+
void timsort(RandomAccessIterator const first, RandomAccessIterator const last,
41+
Compare compare, Projection projection);
42+
// Overloads taking a range
43+
template <typename RandomAccessRange>
44+
void timsort(RandomAccessRange &range);
45+
template <typename RandomAccessRange, typename Compare>
46+
void timsort(RandomAccessRange &range, Compare compare);
47+
template <typename RandomAccessRange, typename Compare, typename Projection>
48+
void timsort(RandomAccessRange &range, Compare compare, Projection projection);
49+
```
2750
2851
## EXAMPLE
2952
@@ -39,9 +62,8 @@ size_t len(const std::string& str) {
3962
}
4063
4164
// Sort a vector of strings by length
42-
std::vector<std::string> vec;
43-
// ... fill vec ...
44-
gfx::timsort(vec.begin(), vec.end(), std::less<size_t>(), &len);
65+
std::vector<std::string> collection = { /* ... */ };
66+
gfx::timsort(collection, std::less<std::string>{}, &len);
4567
```
4668

4769
## COMPATIBILITY

include/gfx/timsort.hpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ template <typename RandomAccessIterator, typename Compare> class TimSort {
676676

677677
} // namespace detail
678678

679+
679680
// ---------------------------------------
680681
// Public interface implementation
681682
// ---------------------------------------
@@ -687,8 +688,8 @@ template <typename RandomAccessIterator, typename Compare, typename Projection>
687688
void timsort(RandomAccessIterator const first, RandomAccessIterator const last,
688689
Compare compare, Projection projection) {
689690
typedef detail::projection_compare<Compare, Projection> compare_t;
690-
compare_t comp(compare, projection);
691-
detail::TimSort<RandomAccessIterator, compare_t>::sort(first, last, comp);
691+
compare_t comp(std::move(compare), std::move(projection));
692+
detail::TimSort<RandomAccessIterator, compare_t>::sort(first, last, std::move(comp));
692693
}
693694

694695
/**
@@ -708,6 +709,30 @@ void timsort(RandomAccessIterator const first, RandomAccessIterator const last)
708709
gfx::timsort(first, last, std::less<value_type>(), detail::identity());
709710
}
710711

712+
/**
713+
* Stably sorts a range with a comparison function and a projection function.
714+
*/
715+
template <typename RandomAccessRange, typename Compare, typename Projection>
716+
void timsort(RandomAccessRange &range, Compare compare, Projection projection) {
717+
gfx::timsort(std::begin(range), std::end(range), compare, projection);
718+
}
719+
720+
/**
721+
* Same as std::stable_sort(std::begin(range), std::end(range), compare).
722+
*/
723+
template <typename RandomAccessRange, typename Compare>
724+
void timsort(RandomAccessRange &range, Compare compare) {
725+
gfx::timsort(std::begin(range), std::end(range), compare);
726+
}
727+
728+
/**
729+
* Same as std::stable_sort(std::begin(range), std::end(range)).
730+
*/
731+
template <typename RandomAccessRange>
732+
void timsort(RandomAccessRange &range) {
733+
gfx::timsort(std::begin(range), std::end(range));
734+
}
735+
711736
} // namespace gfx
712737

713738
#undef GFX_TIMSORT_ENABLE_ASSERT

tests/cxx_11_tests.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
#include <algorithm>
88
#include <deque>
9+
#include <numeric>
910
#include <stdexcept>
1011
#include <utility>
1112
#include <vector>
@@ -139,3 +140,26 @@ TEST_CASE( "issue14" ) {
139140
gfx::timsort(std::begin(c), std::end(c));
140141
CHECK(std::is_sorted(std::begin(c), std::end(c)));
141142
}
143+
144+
TEST_CASE( "range signatures" ) {
145+
std::vector<int> vec(50, 0);
146+
std::iota(vec.begin(), vec.end(), -25);
147+
std::random_shuffle(vec.begin(), vec.end());
148+
149+
SECTION( "range only" ) {
150+
gfx::timsort(vec);
151+
CHECK(std::is_sorted(vec.begin(), vec.end()));
152+
}
153+
154+
SECTION( "range with a comparison function" ) {
155+
using value_type = std::vector<int>::value_type;
156+
gfx::timsort(vec, std::greater<value_type>{});
157+
CHECK(std::is_sorted(vec.begin(), vec.end(), std::greater<value_type>{}));
158+
}
159+
160+
SECTION( "range with comparison and projection functions" ) {
161+
using value_type = std::vector<int>::value_type;
162+
gfx::timsort(vec, std::greater<value_type>{}, std::negate<value_type>{});
163+
CHECK(std::is_sorted(vec.begin(), vec.end()));
164+
}
165+
}

0 commit comments

Comments
 (0)