forked from MichaelAxtmann/cpp-TimSort
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcxx_11_tests.cpp
More file actions
165 lines (138 loc) · 5.25 KB
/
cxx_11_tests.cpp
File metadata and controls
165 lines (138 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
* Copyright (c) 2011 Fuji, Goro (gfx) <gfuji@cpan.org>.
* Copyright (c) 2019 Morwenn.
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm>
#include <deque>
#include <numeric>
#include <stdexcept>
#include <utility>
#include <vector>
#include <catch2/catch.hpp>
#include <gfx/timsort.hpp>
////////////////////////////////////////////////////////////
// Move-only type for benchmarks
//
// std::sort and std::stable_sort are supposed to be able to
// sort collections of types that are move-only and that are
// not default-constructible. The class template move_only
// wraps such a type and can be fed to algorithms to check
// whether they still compile.
//
// Additionally, move_only detects attempts to read the value
// after a move has been performed and throws an exceptions
// when it happens.
//
// It also checks that no self-move is performed, since the
// standard algorithms can't rely on that to work either.
//
namespace
{
template <typename T> struct move_only {
// Not default-constructible
move_only() = delete;
// Move-only
move_only(const move_only &) = delete;
move_only& operator=(const move_only &) = delete;
// Can be constructed from a T for convenience
move_only(const T &value) : can_read(true), value(value) {
}
// Move operators
move_only(move_only &&other) : can_read(true), value(std::move(other.value)) {
if (!exchange(other.can_read, false)) {
throw std::logic_error("illegal read from a moved-from value");
}
}
auto operator=(move_only &&other) -> move_only & {
// Self-move should be ok if the object is already in a moved-from
// state because it incurs no data loss, but should otherwise be
// frowned upon
if (&other == this && can_read) {
throw std::logic_error("illegal self-move was performed");
}
// Assign before overwriting other.can_read
can_read = other.can_read;
value = std::move(other.value);
// If the two objects are not the same and we try to read from an
// object in a moved-from state, then it's a hard error because
// data might be lost
if (!exchange(other.can_read, false) && &other != this) {
throw std::logic_error("illegal read from a moved-from value");
}
return *this;
}
// A C++11 backport of std::exchange()
template <typename U> auto exchange(U &obj, U &&new_val) -> U {
U old_val = std::move(obj);
obj = std::forward<U>(new_val);
return old_val;
}
// Whether the value can be read
bool can_read = false;
// Actual value
T value;
};
}
template <typename T>
bool operator<(const move_only<T> &lhs, const move_only<T> &rhs)
{
return lhs.value < rhs.value;
}
template<typename T>
void swap(move_only<T> &lhs, move_only<T> &rhs)
{
// This function matters because we want to prevent self-moves
// but we don't want to prevent self-swaps because it is the
// responsibility of class authors to make sure that self-swap
// does the right thing, and not the responsibility of algorithm
// authors to prevent them from happening
// Both operands need to be readable
if (!(lhs.can_read || rhs.can_read)) {
throw std::logic_error("illegal read from a moved-from value");
}
// Swapping the values is enough to preserve the preconditions
using std::swap;
swap(lhs.value, rhs.value);
}
TEST_CASE( "shuffle10k_for_move_only_types" ) {
const int size = 1024 * 10; // should be even number of elements
std::vector<move_only<int> > a;
for (int i = 0; i < size; ++i) {
a.push_back((i + 1) * 10);
}
for (int n = 0; n < 100; ++n) {
std::random_shuffle(a.begin(), a.end());
gfx::timsort(a.begin(), a.end(), [](const move_only<int> &x, const move_only<int> &y) { return x.value < y.value; });
for (int i = 0; i < size; ++i) {
CHECK(a[i].value == (i + 1) * 10);
}
}
}
TEST_CASE( "issue14" ) {
int a[] = {15, 7, 16, 20, 25, 28, 13, 27, 34, 24, 19, 1, 6, 30, 32, 29, 10, 9,
3, 31, 21, 26, 8, 2, 22, 14, 4, 12, 5, 0, 23, 33, 11, 17, 18};
std::deque<int> c(std::begin(a), std::end(a));
gfx::timsort(std::begin(c), std::end(c));
CHECK(std::is_sorted(std::begin(c), std::end(c)));
}
TEST_CASE( "range signatures" ) {
std::vector<int> vec(50, 0);
std::iota(vec.begin(), vec.end(), -25);
std::random_shuffle(vec.begin(), vec.end());
SECTION( "range only" ) {
gfx::timsort(vec);
CHECK(std::is_sorted(vec.begin(), vec.end()));
}
SECTION( "range with a comparison function" ) {
using value_type = std::vector<int>::value_type;
gfx::timsort(vec, std::greater<value_type>{});
CHECK(std::is_sorted(vec.begin(), vec.end(), std::greater<value_type>{}));
}
SECTION( "range with comparison and projection functions" ) {
using value_type = std::vector<int>::value_type;
gfx::timsort(vec, std::greater<value_type>{}, std::negate<value_type>{});
CHECK(std::is_sorted(vec.begin(), vec.end()));
}
}