header_utils
Loading...
Searching...
No Matches
ranges.h
1
4
5#pragma once
6
7#include <ranges>
8#include <stdexcept>
9#include <algorithm>
10#include "span.h"
11#include "min-cpp-version/cpp20.h"
12
13namespace ghassanpl
14{
17
20
21 using std::ranges::random_access_range;
22 using std::ranges::contiguous_range;
23 using std::random_access_iterator;
24
27 template <typename RANGE>
28 using range_value = std::ranges::range_value_t<std::decay_t<RANGE>>;
29
31 template <typename RANGE>
32 using range_iterator = std::ranges::iterator_t<std::decay_t<RANGE>>;
33
35 template <typename RANGE, typename FUNC>
36 concept range_predicate = requires (FUNC func, RANGE range) { { func(*std::ranges::begin(range)) } -> std::convertible_to<bool>; };
37
39 [[nodiscard]] constexpr bool valid_index(random_access_range auto& range, std::integral auto index)
40 {
41 return index >= 0 && index < std::ranges::size(range);
42 }
43
46 [[nodiscard]] constexpr auto modulo_index(size_t range_size, std::integral auto index)
47 {
48 const auto cpp_modulo_sucks = (index % static_cast<decltype(index)>(range_size));
49 return ((index < 0) ? (range_size + cpp_modulo_sucks) : cpp_modulo_sucks) % static_cast<decltype(index)>(range_size);
50 }
51
54 [[nodiscard]] constexpr auto modulo_index(random_access_range auto& range, std::integral auto index)
55 {
56 const auto range_size = std::ranges::size(range);
57 return modulo_index(range_size, index);
58 }
59
61 [[nodiscard]] constexpr decltype(auto) at(random_access_range auto& range, std::integral auto index)
62 {
63 if (!valid_index(range, index))
64 throw std::invalid_argument("index");
65 return *(std::ranges::begin(range) + index);
66 }
67
69 [[nodiscard]] constexpr auto at_ptr(random_access_range auto& range, std::integral auto index)
70 -> decltype(std::to_address(std::ranges::begin(range) + index))
71 {
72 if (!valid_index(range, index))
73 return nullptr;
74 return std::to_address(std::ranges::begin(range) + index);
75 }
76
79 [[nodiscard]] constexpr decltype(auto) modulo_at(random_access_range auto& range, std::integral auto index)
80 {
81 return at(range, modulo_index(range, index));
82 }
83
85 template <random_access_range RANGE, typename T>
86 [[nodiscard]] constexpr auto index_of(RANGE const& range, T&& value) -> std::iter_difference_t<range_iterator<RANGE>>
87 requires requires (T value, RANGE range) { { *std::ranges::begin(range) == value } -> std::convertible_to<bool>; }
88 {
89 const auto it = std::ranges::find(range, std::forward<T>(value));
90 if (it == std::ranges::end(range))
91 return -1;
92 return std::ranges::distance(std::ranges::begin(range), it);
93 }
94
96 template <random_access_range RANGE, typename T = range_value<RANGE>>
97 [[nodiscard]] constexpr auto at_or_default(RANGE&& range, std::integral auto index, T&& default_value)
98 {
99 if (!valid_index(range, index)) [[unlikely]] return std::forward<T>(default_value);
100 return at(range, index);
101 }
102
104 template <random_access_range RANGE, typename FUNC>
105 requires range_predicate<RANGE, FUNC>
106 [[nodiscard]] constexpr auto find_index(RANGE const& range, FUNC&& pred) -> std::iter_difference_t<range_iterator<RANGE>>
107 {
108 const auto it = std::ranges::find_if(range, std::forward<FUNC>(pred));
109 if (it == std::ranges::end(range))
110 return -1;
111 return std::ranges::distance(std::ranges::begin(range), it);
112 }
113
115 template <std::ranges::range RANGE, typename FUNC>
116 requires range_predicate<RANGE, FUNC>
117 [[nodiscard]] constexpr auto find_ptr(RANGE& range, FUNC&& pred)
118 {
119 const auto it = std::ranges::find_if(range, std::forward<FUNC>(pred));
120 return it == std::ranges::end(range) ? nullptr : std::to_address(it);
121 }
122
124 template <random_access_range RANGE, typename FUNC, typename DEF_TYPE = range_value<RANGE>>
125 requires range_predicate<RANGE, FUNC>
126 [[nodiscard]] auto find_if_or_default(RANGE& range, FUNC&& pred, DEF_TYPE&& default_value = DEF_TYPE{})
127 {
128 auto it = std::ranges::find_if(range, std::forward<FUNC>(pred));
129 if (it == std::ranges::end(range))
130 return range_value<RANGE>{std::forward<DEF_TYPE>(default_value)};
131 return *it;
132 }
133
135 [[nodiscard]] constexpr auto to_index(random_access_iterator auto iterator, random_access_range auto const& range)
136 {
137 return std::ranges::distance(std::ranges::begin(range), iterator);
138 }
139
141 template <contiguous_range RANGE>
142 [[nodiscard]] constexpr bool valid_address(RANGE&& range, range_value<RANGE>* pointer)
143 {
144 if (std::ranges::empty(range)) return false;
145 return pointer >= std::to_address(std::ranges::begin(range)) && pointer < std::to_address(std::ranges::end(range));
146 }
147
150
151 template <typename T, size_t N = std::dynamic_extent>
152 [[nodiscard]] constexpr std::pair<span<T>, span<T>> split_at(span<T, N> spn, size_t index)
153 {
154 if (index >= spn.size())
155 return { spn, span<T>{} };
156 return { spn.subspan(0, index), spn.subspan(index) };
157 }
158
159 template <typename T, size_t N = std::dynamic_extent>
160 [[nodiscard]] constexpr std::array<span<T>, 3> split_at(span<T, N> spn, size_t index, size_t size)
161 {
162 if (index >= spn.size() || index + size >= spn.size())
163 return { spn, span<T>{}, span<T>{} };
164 return { spn.subspan(0, index), spn.subspan(index, size), spn.subspan(index + size) };
165 }
166
167 template <typename T, size_t N = std::dynamic_extent>
168 [[nodiscard]] constexpr std::pair<T*, T*> as_range(span<T, N> spn)
169 {
170 return { spn.data(), spn.data() + spn.size() };
171 }
172
173 [[nodiscard]] constexpr auto as_span(std::ranges::range auto & range)
174 {
175 return std::span{ range };
176 }
177
180 template <typename TO, typename FROM, size_t N = std::dynamic_extent>
181 [[nodiscard]] auto span_cast(span<FROM, N> bytes) noexcept
182 {
183 return span<TO>{ reinterpret_cast<TO*>(bytes.data()), (bytes.size() * sizeof(FROM)) / sizeof(TO) };
184 }
185
186 template <typename T1, size_t N1, typename T2, size_t N2>
187 [[nodiscard]] constexpr bool are_adjacent(span<T1, N1> s1, span<T2, N2> s2)
188 {
189 return reinterpret_cast<char const*>(s1.data() + s1.size()) == reinterpret_cast<char const*>(s2.data())
190 || reinterpret_cast<char const*>(s2.data() + s2.size()) == reinterpret_cast<char const*>(s1.data());
191 }
192
193 template <typename T1, size_t N1, typename T2, size_t N2>
194 [[nodiscard]] constexpr bool are_overlapping(span<T1, N1> s1, span<T2, N2> s2)
195 {
196 return valid_address(s1, s2.data()) || valid_address(s2, s1.data());
197 }
198
199 template <typename T, size_t N1, typename U, size_t N2>
200 requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>
201 [[nodiscard]] constexpr bool ends_with(span<T, N1> s1, span<U, N2> s2)
202 {
203 if (s1.size() < s2.size()) return false;
204 return std::ranges::equal(s1.subspan(s1.size() - s2.size()), s2);
205 }
206
207 template <typename T, size_t N1, typename U, size_t N2>
208 requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>
209 [[nodiscard]] constexpr bool starts_with(span<T, N1> s1, span<U, N2> s2)
210 {
211 if (s1.size() < s2.size()) return false;
212 return std::ranges::equal(s1.subspan(0, s2.size()), s2);
213 }
214
216
219
220 template <typename T, std::size_t... Ns>
221 [[nodiscard]] constexpr auto join(std::array<T, Ns>... arrays)
222 {
223 std::array<T, (Ns + ...)> result;
224 std::size_t index = 0;
225 ((std::copy_n(std::make_move_iterator(arrays.begin()), Ns, result.begin() + index), index += Ns), ...);
226 return result;
227 }
228
229 template<typename T, size_t LL, typename... ARGS>
230 requires (std::same_as<std::remove_cvref_t<ARGS>, T> && ...)
231 [[nodiscard]] constexpr auto join(std::array<T, LL> rhs, ARGS&&... args)
232 {
233 std::array<T, LL + sizeof...(ARGS)> ar;
234 auto current = std::move(rhs.begin(), rhs.end(), ar.begin());
235 ((*current++ = args), ...);
236 return ar;
237 }
238
240
243
244#ifndef __cpp_lib_ranges_contains
245
246 struct __contains_fn
247 {
248 template <std::input_iterator I, std::sentinel_for<I> S, class T, class Proj = std::identity>
249 requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<I, Proj>, const T*>
250 constexpr bool operator()(I first, S last, const T& value, Proj proj = {}) const
251 {
252 return std::ranges::find(std::move(first), last, value, proj) != last;
253 }
254
255 template <std::ranges::input_range R, class T, class Proj = std::identity>
256 requires std::indirect_binary_predicate<std::ranges::equal_to, std::projected<std::ranges::iterator_t<R>, Proj>, const T*>
257 constexpr bool operator()(R&& r, const T& value, Proj proj = {}) const
258 {
259 return (*this)(std::ranges::begin(r), std::ranges::end(r), std::move(value), proj);
260 }
261 };
262
264 constexpr inline __contains_fn contains{};
265
266
267 struct __contains_subrange_fn
268 {
269 template <std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2, std::sentinel_for<I2> S2, class Pred = std::ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity>
270 requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
271 constexpr bool operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
272 {
273 return (first2 == last2) || !std::ranges::search(first1, last1, first2, last2, pred, proj1, proj2).empty();
274 }
275
276 template <std::ranges::forward_range R1, std::ranges::forward_range R2, class Pred = std::ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity>
277 requires std::indirectly_comparable<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>, Pred, Proj1, Proj2>
278 constexpr bool operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const
279 {
280 return (*this)(std::ranges::begin(r1), std::ranges::end(r1), std::ranges::begin(r2), std::ranges::end(r2), std::move(pred), std::move(proj1), std::move(proj2));
281 }
282 };
283
284
286 constexpr inline __contains_subrange_fn contains_subrange{};
287
288#endif
289
290#ifndef __cpp_lib_ranges_to_container
291
292 namespace detail
293 {
294 struct from_range_t { explicit from_range_t() = default; };
295 constexpr inline from_range_t from_range;
296
297 template <class RANGE, class CONTAINER>
298 concept _Ref_converts = std::convertible_to<std::ranges::range_reference_t<RANGE>, std::ranges::range_value_t<CONTAINER>>;
299
300 template <class RANGE, class CONTAINER, class... TYPES>
301 concept _Converts_direct_constructible = _Ref_converts<RANGE, CONTAINER>
302 && std::constructible_from<CONTAINER, RANGE, TYPES...>;
303
304 template <class RANGE, class CONTAINER, class... TYPES>
305 concept _Converts_tag_constructible = _Ref_converts<RANGE, CONTAINER>
306 && std::constructible_from<CONTAINER, const from_range_t&, RANGE, TYPES...>;
307
308 template <class RANGE, class CONTAINER, class... TYPES>
309 concept _Converts_and_common_constructible = _Ref_converts<RANGE, CONTAINER> && std::ranges::common_range<RANGE> //
310 && std::input_iterator<std::ranges::iterator_t<RANGE>> //
311 && std::constructible_from<CONTAINER, std::ranges::iterator_t<RANGE>, std::ranges::iterator_t<RANGE>, TYPES...>;
312
313 template <class CONTAINER, class REFERENCE>
314 concept can_push_back = requires (CONTAINER& container, REFERENCE ref) {
315 container.push_back(ref);
316 };
317
318 template <class CONTAINER, class REFERENCE>
319 concept can_insert_at_end = requires (CONTAINER & container, REFERENCE ref) {
320 container.insert(container.end(), ref);
321 };
322
323 // clang-format off
324 template <class RANGE, class CONTAINER, class... TYPES>
325 concept _Converts_constructible_insertable = _Ref_converts<RANGE, CONTAINER>
326 && std::constructible_from<CONTAINER, TYPES...>
327 && (can_push_back<CONTAINER, std::ranges::range_reference_t<RANGE>>
328 || can_insert_at_end<CONTAINER, std::ranges::range_reference_t<RANGE>>);
329 // clang-format on
330
331 template <class REFERENCE, class CONTAINER>
332 [[nodiscard]] constexpr auto container_inserter(CONTAINER& _Cont)
333 {
334 if constexpr (can_push_back<CONTAINER, REFERENCE>) {
335 return std::back_insert_iterator{ _Cont };
336 }
337 else {
338 return std::insert_iterator{ _Cont, _Cont.end() };
339 }
340 }
341
342 template <class RANGE, class CONTAINER>
343 concept sized_and_reservable =
344 std::ranges::sized_range<RANGE> &&
345 std::ranges::sized_range<CONTAINER> &&
346 requires(CONTAINER & container, const std::ranges::range_size_t<CONTAINER> count) {
347 container.reserve(count);
348 { container.capacity() } -> std::same_as<std::ranges::range_size_t<CONTAINER>>;
349 { container.max_size() } -> std::same_as<std::ranges::range_size_t<CONTAINER>>;
350 };
351
352 template <std::ranges::input_range RANGE>
353 struct phony_input_iterator
354 {
355 using iterator_category = std::input_iterator_tag;
356 using value_type = std::ranges::range_value_t<RANGE>;
357 using difference_type = ptrdiff_t;
358 using pointer = std::add_pointer_t<std::ranges::range_reference_t<RANGE>>;
359 using reference = std::ranges::range_reference_t<RANGE>;
360
361 reference operator*() const;
362 pointer operator->() const;
363
364 phony_input_iterator& operator++();
365 phony_input_iterator operator++(int);
366
367 bool operator==(const phony_input_iterator&) const;
368 };
369
370 template <template <class...> class CONTAINER, class RANGE, class... ARGS>
371 auto to_helper()
372 {
373 if constexpr (requires (RANGE range, ARGS... args) { CONTAINER(range, args...); })
374 return static_cast<decltype(CONTAINER(std::declval<RANGE>(), std::declval<ARGS>()...))*>(nullptr);
375 else if constexpr (requires (RANGE range, ARGS... args) { CONTAINER(from_range, range, args...); })
376 return static_cast<decltype(CONTAINER(from_range, std::declval<RANGE>(), std::declval<ARGS>()...))*>(nullptr);
377 else if constexpr (requires (phony_input_iterator<RANGE> it, ARGS... args) { CONTAINER(it, it, args...);})
378 return static_cast<decltype(CONTAINER(std::declval<phony_input_iterator<RANGE>>(), std::declval<phony_input_iterator<RANGE>>(), std::declval<ARGS>()...))*>(nullptr);
379 }
380
381 }
382
384 template <class CONTAINER, std::ranges::input_range RANGE, class... TYPES>
385 requires (!std::ranges::view<CONTAINER>)
386 [[nodiscard]] constexpr CONTAINER to(RANGE&& range, TYPES&&... args)
387 {
388 using namespace detail;
389 if constexpr (_Converts_direct_constructible<RANGE, CONTAINER, TYPES...>)
390 return CONTAINER(std::forward<RANGE>(range), std::forward<TYPES>(args)...);
391 else if constexpr (_Converts_tag_constructible<RANGE, CONTAINER, TYPES...>)
392 return CONTAINER(from_range, std::forward<RANGE>(range), std::forward<TYPES>(args)...);
393 else if constexpr (_Converts_and_common_constructible<RANGE, CONTAINER, TYPES...>)
394 return CONTAINER(std::ranges::begin(range), std::ranges::end(range), std::forward<TYPES>(args)...);
395 else if constexpr (_Converts_constructible_insertable<RANGE, CONTAINER, TYPES...>)
396 {
397 CONTAINER container(std::forward<TYPES>(args)...);
398 if constexpr (sized_and_reservable<RANGE, CONTAINER>) {
399 container.reserve(std::ranges::size(range));
400 }
401 std::ranges::copy(range, detail::container_inserter<std::ranges::range_reference_t<RANGE>>(container));
402 return container;
403 }
404 else if constexpr (std::ranges::input_range<std::ranges::range_reference_t<RANGE>>)
405 {
406 const auto transform = [](auto&& _Elem) {
407 return ghassanpl::to<std::ranges::range_value_t<CONTAINER>>(std::forward<decltype(_Elem)>(_Elem));
408 };
409 return ghassanpl::to<CONTAINER>(std::ranges::views::transform(range, transform), std::forward<TYPES>(args)...);
410 }
411 else
412 static_assert(::ghassanpl::always_false<CONTAINER>, "the program is ill-formed per N4910 [range.utility.conv.to]/1.3");
413 }
414
415 template <template <class...> class CONTAINER, std::ranges::input_range RANGE, class... TYPES, class _Deduced = std::remove_pointer_t<decltype(detail::to_helper<CONTAINER, RANGE, TYPES...>())>>
416 [[nodiscard]] constexpr _Deduced to(RANGE&& _Range, TYPES&&... ARGS)
417 {
418 return to<_Deduced>(std::forward<RANGE>(_Range), std::forward<TYPES>(ARGS)...);
419 }
420
422#else
423
424 using std::ranges::to;
425
426#endif
427
429
430 /*
431 template <class _Ty = void>
432 struct plus_equals
433 {
434 [[nodiscard]] constexpr _Ty operator()(_Ty& left, const _Ty& right) const { return left += right; }
435 };
436
437 template <>
438 struct plus_equals<void>
439 {
440 template <class _Ty1, class _Ty2>
441 [[nodiscard]] constexpr auto operator()(_Ty1& _Left, _Ty2&& _Right) const noexcept(noexcept(static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right)))
442 -> decltype(static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right))
443 {
444 return static_cast<_Ty1&>(_Left) + static_cast<_Ty2&&>(_Right);
445 }
446 using is_transparent = int;
447 };
448
449 template <std::ranges::range RANGE, std::movable FOLD_VALUE, typename FOLD_FUNC = std::plus<>>
450 requires std::is_invocable_v<FOLD_FUNC, FOLD_VALUE&, std::ranges::range_reference_t<RANGE>>
451 auto fold(RANGE&& r, FOLD_VALUE init, FOLD_FUNC&& op = {})
452 {
453 auto first = std::ranges::begin(r);
454 auto const last = std::ranges::end(r);
455 for (; first != last; ++first)
456 init = std::invoke(op, std::move(init), *first);
457 return init;
458 }
459
460 template <std::ranges::range RANGE, typename FOLD_VALUE, typename FOLD_FUNC = plus_equals<>>
461 decltype(auto) fold_while(RANGE&& r, FOLD_VALUE&& init, FOLD_FUNC&& op = {})
462 requires std::is_invocable_r_v<bool, FOLD_FUNC, FOLD_VALUE&, std::ranges::range_reference_t<RANGE>>
463 {
464 auto first = std::ranges::begin(r);
465 auto const last = std::ranges::end(r);
466 for (; first != last; ++first)
467 {
468 if (!std::invoke(op, std::ref(init), *first))
469 break;
470 }
471 return init;
472 }
473 */
474}
Concept to check if a function is a predicate on the range elements (i.e. returns boolable).
Definition ranges.h:36
auto at_ptr(std::map< K, V, C > const &map, VAL &&value)
Same as map_find().
Definition containers.h:191
auto index(T &cont, RANDOM &rng=::ghassanpl::random::default_random_engine)
Returns an index of a random element in cont.
Definition random.h:331
constexpr auto index_of(RANGE const &range, T &&value) -> std::iter_difference_t< range_iterator< RANGE > >
Find a value in range and returns an index to it.
Definition ranges.h:86
std::ranges::iterator_t< std::decay_t< RANGE > > range_iterator
Helper template to get the iterator type of a type that decays to a range.
Definition ranges.h:32
auto span_cast(span< FROM, N > bytes) noexcept
Casts a span of objects of type FROM to a span of objects of type TO.
Definition ranges.h:181
auto find_if_or_default(RANGE &range, FUNC &&pred, DEF_TYPE &&default_value=DEF_TYPE{})
Find a value in range that matches pred, and returns it, or default_value if none found.
Definition ranges.h:126
constexpr decltype(auto) at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at index of range.
Definition ranges.h:61
constexpr auto find_index(RANGE const &range, FUNC &&pred) -> std::iter_difference_t< range_iterator< RANGE > >
Find a value in range that matches pred, and returns an index to it.
Definition ranges.h:106
constexpr auto find_ptr(RANGE &range, FUNC &&pred)
Find a value in range that matches pred, and returns a pointer to it, or null if none found.
Definition ranges.h:117
constexpr bool valid_address(RANGE &&range, range_value< RANGE > *pointer)
Returns whether or not pointer is a valid pointer to an element in the contiguous range.
Definition ranges.h:142
constexpr __contains_subrange_fn contains_subrange
contains_subrange(range, subrange)
Definition ranges.h:286
constexpr auto to_index(random_access_iterator auto iterator, random_access_range auto const &range)
Turns an iterator to range to an index.
Definition ranges.h:135
constexpr bool valid_index(random_access_range auto &range, std::integral auto index)
Returns whether or not a given integer is a valid index to a random access range.
Definition ranges.h:39
constexpr auto at_or_default(RANGE &&range, std::integral auto index, T &&default_value)
Return the element at index or default_value if not a valid index.
Definition ranges.h:97
constexpr auto modulo_index(size_t range_size, std::integral auto index)
Returns index converted to a valid index into a range of range_size as if using modulo arithmetic.
Definition ranges.h:46
std::ranges::range_value_t< std::decay_t< RANGE > > range_value
Helper template to get the value type of a type that decays to a range.
Definition ranges.h:28
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:386
constexpr __contains_fn contains
contains(range, el)
Definition ranges.h:264
constexpr decltype(auto) modulo_at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at modulo_index of range.
Definition ranges.h:79
The below code is based on Sun's libm library code, which is licensed under the following license:
Contains functions that return predicates; appropriate for functions like std::all_of.
Definition functional.h:82
Primary namespace for everything in this library.
Definition align+rec2.h:10