openMSX
ranges.hh
Go to the documentation of this file.
1#ifndef RANGES_HH
2#define RANGES_HH
3
4#include <algorithm>
5#include <cassert>
6#include <functional>
7#include <iterator> // for std::begin(), std::end()
8#include <numeric>
9#include <span>
10#include <version> // for _LIBCPP_VERSION
11
12// Range based versions of the standard algorithms, these will likely become
13// part of c++20. For example see this post:
14// http://ericniebler.com/2018/12/05/standard-ranges/
15// In the future we can remove our implementation and instead use the standard
16// version (e.g. by a namespace alias like 'namespace ranges = std::ranges').
17//
18// All of the range algorithms below do nothing more than delegate to the
19// corresponding iterator-pair version of the algorithm.
20//
21// This list of algorithms is not complete. But it's easy to extend if/when we
22// need more.
23
24namespace ranges {
25
26// (Simplified) implementation of c++20 "range" and "sized_range" concepts.
27// * A "range" is something that has a begin() and end().
28// * A "sized_range" in addition has a size().
29template<typename T>
30concept range = requires(T& t) {
32 std::end (t);
33};
34
35template<typename T>
36concept sized_range = range<T> && requires(T& t) { std::size(t); };
37
38
39template<typename ForwardRange, typename Compare = std::less<>, typename Proj = std::identity>
40[[nodiscard]] bool is_sorted(ForwardRange&& range, Compare comp = {}, Proj proj = {})
41{
42 return std::is_sorted(std::begin(range), std::end(range),
43 [&](const auto& x, const auto& y) {
44 return comp(std::invoke(proj, x), std::invoke(proj, y));
45 });
46}
47
48template<typename RandomAccessRange>
49constexpr void sort(RandomAccessRange&& range)
50{
52}
53
54template<typename RandomAccessRange, typename Compare>
55constexpr void sort(RandomAccessRange&& range, Compare comp)
56{
58}
59
60template<typename RAIter, typename Compare = std::less<>, typename Proj>
61void sort(RAIter first, RAIter last, Compare comp, Proj proj)
62{
63 std::sort(first, last,
64 [&](const auto& x, const auto& y) {
65 return comp(std::invoke(proj, x), std::invoke(proj, y));
66 });
67}
68
69template<typename RandomAccessRange, typename Compare = std::less<>, typename Proj>
70void sort(RandomAccessRange&& range, Compare comp, Proj proj)
71{
73}
74
75template<typename RandomAccessRange>
76void stable_sort(RandomAccessRange&& range)
77{
79}
80
81template<typename RandomAccessRange, typename Compare>
82void stable_sort(RandomAccessRange&& range, Compare comp)
83{
85}
86
87template<typename RAIter, typename Compare = std::less<>, typename Proj>
88void stable_sort(RAIter first, RAIter last, Compare comp, Proj proj)
89{
90 std::stable_sort(first, last,
91 [&](const auto& x, const auto& y) {
92 return comp(std::invoke(proj, x), std::invoke(proj, y));
93 });
94}
95
96template<typename RandomAccessRange, typename Compare = std::less<>, typename Proj>
97void stable_sort(RandomAccessRange&& range, Compare comp, Proj proj)
98{
100}
101
102template<typename ForwardRange, typename T>
103[[nodiscard]] bool binary_search(ForwardRange&& range, const T& value)
104{
106}
107
108template<typename ForwardRange, typename T, typename Compare>
109[[nodiscard]] bool binary_search(ForwardRange&& range, const T& value, Compare comp)
110{
112}
113
114template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
115[[nodiscard]] auto lower_bound(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
116{
117 auto comp2 = [&](const auto& x, const auto& y) {
118 return comp(std::invoke(proj, x), y);
119 };
120 return std::lower_bound(std::begin(range), std::end(range), value, comp2);
121}
122
123template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
124[[nodiscard]] auto upper_bound(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
125{
126 auto comp2 = [&](const auto& x, const auto& y) {
127 return comp(x, std::invoke(proj, y));
128 };
129 return std::upper_bound(std::begin(range), std::end(range), value, comp2);
130}
131
132template<typename ForwardRange, typename T, typename Compare = std::less<>>
133[[nodiscard]] auto equal_range(ForwardRange&& range, const T& value, Compare comp = {})
134{
135 return std::equal_range(std::begin(range), std::end(range), value, comp);
136}
137template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
138[[nodiscard]] auto equal_range(ForwardRange&& range, const T& value, Compare comp, Proj proj)
139{
140 using Iter = decltype(std::begin(range));
141 using R = typename std::iterator_traits<Iter>::value_type;
142 struct Comp2 {
143 Compare comp;
144 Proj proj;
145
146 bool operator()(const R& x, const R& y) const {
147 return comp(std::invoke(proj, x), std::invoke(proj, y));
148 }
149 bool operator()(const R& x, const T& y) const {
150 return comp(std::invoke(proj, x), y);
151 }
152 bool operator()(const T& x, const R& y) const {
153 return comp(x, std::invoke(proj, y));
154 }
155 };
156 return std::equal_range(std::begin(range), std::end(range), value, Comp2{comp, proj});
157}
158
159template<typename InputRange, typename T>
160[[nodiscard]] auto find(InputRange&& range, const T& value)
161{
162 return std::find(std::begin(range), std::end(range), value);
163}
164
165template<typename InputRange, typename T, typename Proj>
166[[nodiscard]] auto find(InputRange&& range, const T& value, Proj proj)
167{
168 return find_if(std::forward<InputRange>(range),
169 [&](const auto& e) { return std::invoke(proj, e) == value; });
170}
171
172template<typename InputRange, typename UnaryPredicate>
173[[nodiscard]] auto find_if(InputRange&& range, UnaryPredicate pred)
174{
175 auto it = std::begin(range);
176 auto et = std::end(range);
177 for (; it != et; ++it) {
178 if (std::invoke(pred, *it)) {
179 return it;
180 }
181 }
182 return it;
183}
184
185template<typename InputRange, typename UnaryPredicate>
186[[nodiscard]] bool all_of(InputRange&& range, UnaryPredicate pred)
187{
188 return std::all_of(std::begin(range), std::end(range), pred);
189}
190
191template<typename InputRange, typename UnaryPredicate>
192[[nodiscard]] bool any_of(InputRange&& range, UnaryPredicate pred)
193{
194 return std::any_of(std::begin(range), std::end(range), pred);
195}
196
197template<typename InputRange, typename UnaryPredicate>
198[[nodiscard]] bool none_of(InputRange&& range, UnaryPredicate pred)
199{
200 return std::none_of(std::begin(range), std::end(range), pred);
201}
202
203template<typename ForwardRange>
204[[nodiscard]] auto unique(ForwardRange&& range)
205{
207}
208
209template<typename ForwardRange, typename BinaryPredicate>
210[[nodiscard]] auto unique(ForwardRange&& range, BinaryPredicate pred)
211{
212 return std::unique(std::begin(range), std::end(range), pred);
213}
214
215template<typename RAIter, typename Compare = std::equal_to<>, typename Proj>
216[[nodiscard]] auto unique(RAIter first, RAIter last, Compare comp, Proj proj)
217{
218 return std::unique(first, last,
219 [&](const auto& x, const auto& y) {
220 return comp(std::invoke(proj, x), std::invoke(proj, y));
221 });
222}
223
224template<typename RandomAccessRange, typename Compare = std::equal_to<>, typename Proj>
225[[nodiscard]] auto unique(RandomAccessRange&& range, Compare comp, Proj proj)
226{
227 return unique(std::begin(range), std::end(range), comp, proj);
228}
229
230template<typename InputRange, typename OutputIter>
231 requires(!range<OutputIter>)
232auto copy(InputRange&& range, OutputIter out)
233{
234 return std::copy(std::begin(range), std::end(range), out);
235}
236
237template<sized_range Input, sized_range Output>
238auto copy(Input&& in, Output&& out)
239{
240 assert(std::size(in) <= std::size(out));
241 return std::copy(std::begin(in), std::end(in), std::begin(out));
242}
243
244template<typename InputRange, typename OutputIter, typename UnaryPredicate>
245auto copy_if(InputRange&& range, OutputIter out, UnaryPredicate pred)
246{
247 return std::copy_if(std::begin(range), std::end(range), out, pred);
248}
249
250template<typename InputRange, typename OutputIter, typename UnaryOperation>
251auto transform(InputRange&& range, OutputIter out, UnaryOperation op)
252{
253 return std::transform(std::begin(range), std::end(range), out, op);
254}
255
256template<typename ForwardRange, typename Generator>
257void generate(ForwardRange&& range, Generator&& g)
258{
259 std::generate(std::begin(range), std::end(range), std::forward<Generator>(g));
260}
261
262template<typename ForwardRange, typename T>
263[[nodiscard]] auto remove(ForwardRange&& range, const T& value)
264{
265 return std::remove(std::begin(range), std::end(range), value);
266}
267
268template<typename ForwardRange, typename UnaryPredicate>
269[[nodiscard]] auto remove_if(ForwardRange&& range, UnaryPredicate pred)
270{
272}
273
274template<typename ForwardRange, typename T>
275constexpr void replace(ForwardRange&& range, const T& old_value, const T& new_value)
276{
277 std::replace(std::begin(range), std::end(range), old_value, new_value);
278}
279
280template<typename ForwardRange, typename UnaryPredicate, typename T>
281void replace_if(ForwardRange&& range, UnaryPredicate pred, const T& new_value)
282{
283 std::replace_if(std::begin(range), std::end(range), pred, new_value);
284}
285
286template<typename ForwardRange, typename T>
287constexpr void fill(ForwardRange&& range, const T& value)
288{
290}
291
292// part of c++20
293template<typename ForwardIt, typename T>
294constexpr void iota(ForwardIt first, ForwardIt last, T value)
295{
296 while (first != last) {
297 *first++ = value;
298 ++value;
299 }
300}
301
302template<typename ForwardRange, typename T>
303constexpr void iota(ForwardRange&& range, T&& value)
304{
305 ::ranges::iota(std::begin(range), std::end(range), std::forward<T>(value));
306}
307
308template<typename InputRange, typename T>
309[[nodiscard]] T accumulate(InputRange&& range, T init)
310{
312}
313
314template<typename InputRange, typename T, typename BinaryOperation>
315[[nodiscard]] T accumulate(InputRange&& range, T init, BinaryOperation op)
316{
317 return std::accumulate(std::begin(range), std::end(range), init, op);
318}
319
320template<typename InputRange, typename T>
321[[nodiscard]] auto count(InputRange&& range, const T& value)
322{
323 return std::count(std::begin(range), std::end(range), value);
324}
325
326template<typename InputRange, typename UnaryPredicate>
327[[nodiscard]] auto count_if(InputRange&& range, UnaryPredicate pred)
328{
330}
331
332template<typename InputRange1, typename InputRange2, typename OutputIter>
333auto set_difference(InputRange1&& range1, InputRange2&& range2, OutputIter out)
334{
335 return std::set_difference(std::begin(range1), std::end(range1),
336 std::begin(range2), std::end(range2),
337 out);
338}
339
340template<range InputRange1, range InputRange2,
341 typename Pred = std::equal_to<void>,
342 typename Proj1 = std::identity, typename Proj2 = std::identity>
343bool equal(InputRange1&& range1, InputRange2&& range2, Pred pred = {},
344 Proj1 proj1 = {}, Proj2 proj2 = {})
345{
346 auto it1 = std::begin(range1);
347 auto it2 = std::begin(range2);
348 auto et1 = std::end(range1);
349 auto et2 = std::end(range2);
350 for (; (it1 != et1) && (it2 != et2); ++it1, ++it2) {
351 if (!std::invoke(pred, std::invoke(proj1, *it1), std::invoke(proj2, *it2))) {
352 return false;
353 }
354 }
355 return (it1 == et1) && (it2 == et2);
356}
357
358template<sized_range SizedRange1, sized_range SizedRange2,
359 typename Pred = std::equal_to<void>,
360 typename Proj1 = std::identity, typename Proj2 = std::identity>
361bool equal(SizedRange1&& range1, SizedRange2&& range2, Pred pred = {},
362 Proj1 proj1 = {}, Proj2 proj2 = {})
363{
364 if (std::size(range1) != std::size(range2)) return false;
365
366 auto it1 = std::begin(range1);
367 auto it2 = std::begin(range2);
368 auto et1 = std::end(range1);
369 for (; it1 != et1; ++it1, ++it2) {
370 if (!std::invoke(pred, std::invoke(proj1, *it1), std::invoke(proj2, *it2))) {
371 return false;
372 }
373 }
374 return true;
375}
376
377// Test whether all elements in the given range are equal to each other (after
378// applying a projection).
379template<typename InputRange, typename Proj = std::identity>
380bool all_equal(InputRange&& range, Proj proj = {})
381{
382 auto it = std::begin(range);
383 auto et = std::end(range);
384 if (it == et) return true;
385
386 auto val = std::invoke(proj, *it);
387 for (++it; it != et; ++it) {
388 if (std::invoke(proj, *it) != val) {
389 return false;
390 }
391 }
392 return true;
393}
394
395} // namespace ranges
396
397// Perform a binary-search in a sorted range.
398// The given 'range' must be sorted according to 'comp'.
399// We search for 'value' after applying 'proj' to the elements in the range.
400// Returns a pointer to the element in 'range', or nullptr if not found.
401//
402// This helper function is typically used to simplify code like:
403// auto it = ranges::lower_bound(myMap, name, {}, &Element::name);
404// if ((it != myMap.end()) && (it->name == name)) {
405// ... use *it
406// }
407// Note that this code needs to check both for the end-iterator and that the
408// element was actually found. By using binary_find() this complexity is hidden:
409// if (auto m = binary_find(myMap, name, {}, &Element::name)) {
410// ... use *m
411// }
412template<typename ForwardRange, typename T, typename Compare = std::less<>, typename Proj = std::identity>
413[[nodiscard]] auto* binary_find(ForwardRange&& range, const T& value, Compare comp = {}, Proj proj = {})
414{
415 auto it = ranges::lower_bound(range, value, comp, proj);
416 return ((it != std::end(range)) && (!std::invoke(comp, value, std::invoke(proj, *it))))
417 ? &*it
418 : nullptr;
419}
420
421// Convenience function to convert part of an array (or vector, ...) into a
422// span. These are not part of the c++ ranges namespace, but often these results
423// are then further used in range algorithms, that's why I placed them in this
424// header.
425
426template<typename Range>
427constexpr auto make_span(Range&& range)
428{
429#ifndef _LIBCPP_VERSION
430 // C++20 version, works with gcc/visual studio
431 // Simple enough that we don't actually need this helper function.
432 return std::span(range);
433#else
434 // Unfortunately we do need a workaround for clang/libc++, version 14 and 15
435 // return std::span(std::begin(range), std::end(range));
436
437 // Further workaround for Xcode-14, clang-13
438 // Don't always use this workaround, because '&*begin()' is actually
439 // undefined behavior when the range is empty. It works fine in
440 // practice, except when using a DEBUG-STL version.
441 return std::span(&*std::begin(range), std::size(range));
442#endif
443}
444
445template<typename Range>
446constexpr auto subspan(Range&& range, size_t offset, size_t count = std::dynamic_extent)
447{
448 return make_span(std::forward<Range>(range)).subspan(offset, count);
449}
450
451template<size_t Count, typename Range>
452constexpr auto subspan(Range&& range, size_t offset = 0)
453{
454 return make_span(std::forward<Range>(range)).subspan(offset).template first<Count>();
455}
456
457#endif
int g
TclObject t
constexpr double e
Definition: Math.hh:21
bool comp(const uint8_t *p, const uint8_t *q)
Definition: ranges.hh:24
bool is_sorted(ForwardRange &&range, Compare comp={}, Proj proj={})
Definition: ranges.hh:40
bool binary_search(ForwardRange &&range, const T &value)
Definition: ranges.hh:103
bool any_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:192
auto unique(ForwardRange &&range)
Definition: ranges.hh:204
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:186
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:263
void replace_if(ForwardRange &&range, UnaryPredicate pred, const T &new_value)
Definition: ranges.hh:281
void stable_sort(RandomAccessRange &&range, Compare comp, Proj proj)
Definition: ranges.hh:97
bool all_equal(InputRange &&range, Proj proj={})
Definition: ranges.hh:380
void sort(RandomAccessRange &&range, Compare comp, Proj proj)
Definition: ranges.hh:70
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:173
bool binary_search(ForwardRange &&range, const T &value, Compare comp)
Definition: ranges.hh:109
bool none_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:198
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:232
auto count(InputRange &&range, const T &value)
Definition: ranges.hh:321
auto find(InputRange &&range, const T &value, Proj proj)
Definition: ranges.hh:166
auto copy(Input &&in, Output &&out)
Definition: ranges.hh:238
auto equal_range(ForwardRange &&range, const T &value, Compare comp, Proj proj)
Definition: ranges.hh:138
auto remove_if(ForwardRange &&range, UnaryPredicate pred)
Definition: ranges.hh:269
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition: ranges.hh:294
T accumulate(InputRange &&range, T init)
Definition: ranges.hh:309
void stable_sort(RandomAccessRange &&range)
Definition: ranges.hh:76
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:160
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
Definition: ranges.hh:343
auto unique(RandomAccessRange &&range, Compare comp, Proj proj)
Definition: ranges.hh:225
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:124
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:133
auto set_difference(InputRange1 &&range1, InputRange2 &&range2, OutputIter out)
Definition: ranges.hh:333
void generate(ForwardRange &&range, Generator &&g)
Definition: ranges.hh:257
constexpr void iota(ForwardRange &&range, T &&value)
Definition: ranges.hh:303
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:251
auto copy_if(InputRange &&range, OutputIter out, UnaryPredicate pred)
Definition: ranges.hh:245
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
Definition: ranges.hh:275
T accumulate(InputRange &&range, T init, BinaryOperation op)
Definition: ranges.hh:315
constexpr void sort(RandomAccessRange &&range)
Definition: ranges.hh:49
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:115
auto count_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:327
size_t size(std::string_view utf8)
constexpr auto make_span(Range &&range)
Definition: ranges.hh:427
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition: ranges.hh:446
auto * binary_find(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:413
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)