Skip to content

Commit 838aea0

Browse files
committed
Support simple projections
1 parent f61add2 commit 838aea0

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,29 @@ See also the following links for a detailed description of TimSort:
1212

1313
According to the benchmarks, it is a bit slower than `std::sort()` on randomized sequences, but much faster on
1414
partially-sorted ones. `gfx::timsort` should be usable as a drop-in replacement for `std::stable_sort`, with the
15-
difference that it's can't fallback to a O(n log² n) algorithm when there isn't enough extra heap memory available.
15+
difference that it can't fallback to a O(n log² n) algorithm when there isn't enough extra heap memory available.
16+
17+
Additionally `gfx::timsort` can take a [projection function](https://ezoeryou.github.io/blog/article/2019-01-22-ranges-projection.html)
18+
after the comparison function. The support is a bit rougher than in the linked article: only instances of types
19+
callable with parenthesis can be used, there is no support for pointer to members.
1620

1721
## EXAMPLE
1822

23+
Example using timsort with a comparison function and a projection function to sort a vector of strings by length:
24+
1925
```cpp
2026
#include <string>
2127
#include <vector>
2228
#include <gfx/timsort.hpp>
2329

24-
std::vector<std::string> a;
25-
// ... initialize a ...
26-
gfx::timsort(a.begin(), a.end(), std::less<string>());
30+
size_t len(const std::string& str) {
31+
return str.size();
32+
}
33+
34+
// Sort a vector of strings by length
35+
std::vector<std::string> vec;
36+
// ... fill vec ...
37+
gfx::timsort(vec.begin(), vec.end(), std::less<size_t>(), &len);
2738
```
2839
2940
## COMPATIBILITY

include/gfx/timsort.hpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,52 @@ namespace gfx {
103103

104104
namespace detail {
105105

106+
// Equivalent to C++20 std::identity
107+
struct identity {
108+
#if GFX_TIMSORT_USE_STD_MOVE
109+
template <typename T>
110+
T&& operator()(T&& value) const
111+
{
112+
return std::forward<T>(value);
113+
}
114+
#else
115+
template <typename T>
116+
T& operator()(T& value) const {
117+
return value;
118+
}
119+
120+
template <typename T>
121+
T const& operator()(T const& value) const {
122+
return value;
123+
}
124+
#endif
125+
};
126+
127+
// Merge a predicate and a projection function
128+
template <typename Compare, typename Projection>
129+
struct projection_compare {
130+
projection_compare(Compare comp, Projection proj) : compare(comp), projection(proj) {
131+
}
132+
133+
#if GFX_TIMSORT_USE_STD_MOVE
134+
template <typename T, typename U>
135+
bool operator()(T &&lhs, U &&rhs) {
136+
return static_cast<bool>(compare(
137+
projection(std::forward<T>(lhs)),
138+
projection(std::forward<U>(rhs))
139+
));
140+
}
141+
#else
142+
template <typename T, typename U>
143+
bool operator()(T &lhs, U &rhs) {
144+
return static_cast<bool>(compare(projection(lhs), projection(rhs)));
145+
}
146+
#endif
147+
148+
Compare compare;
149+
Projection projection;
150+
};
151+
106152
template <typename Iterator> struct run {
107153
typedef typename std::iterator_traits<Iterator>::difference_type diff_t;
108154

@@ -669,12 +715,23 @@ template <typename RandomAccessIterator, typename Compare> class TimSort {
669715
// Public interface implementation
670716
// ---------------------------------------
671717

718+
/**
719+
* Stably sorts a range with a comparison function and a projection function.
720+
*/
721+
template <typename RandomAccessIterator, typename Compare, typename Projection>
722+
void timsort(RandomAccessIterator const first, RandomAccessIterator const last,
723+
Compare compare, Projection projection) {
724+
typedef detail::projection_compare<Compare, Projection> compare_t;
725+
compare_t comp(compare, projection);
726+
detail::TimSort<RandomAccessIterator, compare_t>::sort(first, last, comp);
727+
}
728+
672729
/**
673730
* Same as std::stable_sort(first, last, compare).
674731
*/
675732
template <typename RandomAccessIterator, typename Compare>
676733
void timsort(RandomAccessIterator const first, RandomAccessIterator const last, Compare compare) {
677-
detail::TimSort<RandomAccessIterator, Compare>::sort(first, last, compare);
734+
gfx::timsort(first, last, compare, detail::identity());
678735
}
679736

680737
/**
@@ -683,7 +740,7 @@ void timsort(RandomAccessIterator const first, RandomAccessIterator const last,
683740
template <typename RandomAccessIterator>
684741
void timsort(RandomAccessIterator const first, RandomAccessIterator const last) {
685742
typedef typename std::iterator_traits<RandomAccessIterator>::value_type value_type;
686-
gfx::timsort(first, last, std::less<value_type>());
743+
gfx::timsort(first, last, std::less<value_type>(), detail::identity());
687744
}
688745

689746
} // namespace gfx

tests/cxx_98_tests.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,18 @@ TEST_CASE( "issue2_duplication" ) {
516516
CHECK(a[1001].first == expected[1001].first);
517517
CHECK(a[1001].second == expected[1001].second);
518518
}
519+
520+
TEST_CASE( "projection" ) {
521+
const int size = 128;
522+
523+
std::vector<int> vec;
524+
for (int i = 0; i < size; ++i) {
525+
vec.push_back(i - 40);
526+
}
527+
std::random_shuffle(vec.begin(), vec.end());
528+
529+
gfx::timsort(vec.begin(), vec.end(), std::greater<int>(), std::negate<int>());
530+
for (int i = 0; i < size; ++i) {
531+
CHECK(vec[i] == i - 40);
532+
}
533+
}

0 commit comments

Comments
 (0)