11#include "min-cpp-version/cpp20.h"
21 using std::ranges::random_access_range;
22 using std::ranges::contiguous_range;
23 using std::random_access_iterator;
27 template <
typename RANGE>
28 using range_value = std::ranges::range_value_t<std::decay_t<RANGE>>;
31 template <
typename RANGE>
35 template <
typename RANGE,
typename FUNC>
36 concept range_predicate =
requires (FUNC func, RANGE range) { { func(*std::ranges::begin(range)) } -> std::convertible_to<bool>; };
39 [[nodiscard]]
constexpr bool valid_index(random_access_range
auto& range, std::integral
auto index)
41 return index >= 0 && index < std::ranges::size(range);
46 [[nodiscard]]
constexpr auto modulo_index(
size_t range_size, std::integral
auto index)
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);
54 [[nodiscard]]
constexpr auto modulo_index(random_access_range
auto& range, std::integral
auto index)
56 const auto range_size = std::ranges::size(range);
61 [[nodiscard]]
constexpr decltype(
auto)
at(random_access_range
auto& range, std::integral
auto index)
64 throw std::invalid_argument(
"index");
65 return *(std::ranges::begin(range) + index);
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))
74 return std::to_address(std::ranges::begin(range) + index);
79 [[nodiscard]]
constexpr decltype(
auto)
modulo_at(random_access_range
auto& range, std::integral
auto index)
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>; }
89 const auto it = std::ranges::find(range, std::forward<T>(value));
90 if (it == std::ranges::end(range))
92 return std::ranges::distance(std::ranges::begin(range), it);
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)
99 if (!
valid_index(range, index)) [[unlikely]]
return std::forward<T>(default_value);
100 return at(range, index);
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>>
108 const auto it = std::ranges::find_if(range, std::forward<FUNC>(
pred));
109 if (it == std::ranges::end(range))
111 return std::ranges::distance(std::ranges::begin(range), it);
115 template <std::ranges::range RANGE,
typename FUNC>
116 requires range_predicate<RANGE, FUNC>
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);
124 template <random_access_range RANGE,
typename FUNC,
typename DEF_TYPE = range_value<RANGE>>
125 requires range_predicate<RANGE, FUNC>
128 auto it = std::ranges::find_if(range, std::forward<FUNC>(pred));
129 if (it == std::ranges::end(range))
135 [[nodiscard]]
constexpr auto to_index(random_access_iterator
auto iterator, random_access_range
auto const& range)
137 return std::ranges::distance(std::ranges::begin(range), iterator);
141 template <contiguous_range RANGE>
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));
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)
154 if (index >= spn.size())
155 return { spn, span<T>{} };
156 return { spn.subspan(0, index), spn.subspan(index) };
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)
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) };
167 template <
typename T,
size_t N = std::dynamic_extent>
168 [[nodiscard]]
constexpr std::pair<T*, T*> as_range(span<T, N> spn)
170 return { spn.data(), spn.data() + spn.size() };
173 [[nodiscard]]
constexpr auto as_span(std::ranges::range
auto & range)
175 return std::span{ range };
180 template <
typename TO,
typename FROM,
size_t N = std::dynamic_extent>
181 [[nodiscard]]
auto span_cast(span<FROM, N> bytes)
noexcept
183 return span<TO>{
reinterpret_cast<TO*
>(bytes.data()), (bytes.size() *
sizeof(FROM)) /
sizeof(TO) };
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)
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());
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)
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)
203 if (s1.size() < s2.size())
return false;
204 return std::ranges::equal(s1.subspan(s1.size() - s2.size()), s2);
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)
211 if (s1.size() < s2.size())
return false;
212 return std::ranges::equal(s1.subspan(0, s2.size()), s2);
220 template <
typename T, std::size_t... Ns>
221 [[nodiscard]]
constexpr auto join(std::array<T, Ns>... arrays)
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), ...);
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)
233 std::array<T, LL +
sizeof...(ARGS)> ar;
234 auto current = std::move(rhs.begin(), rhs.end(), ar.begin());
235 ((*current++ = args), ...);
244#ifndef __cpp_lib_ranges_contains
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
252 return std::ranges::find(std::move(first), last, value, proj) != last;
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
259 return (*
this)(std::ranges::begin(r), std::ranges::end(r), std::move(value), proj);
267 struct __contains_subrange_fn
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
273 return (first2 == last2) || !std::ranges::search(first1, last1, first2, last2, pred, proj1, proj2).empty();
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
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));
290#ifndef __cpp_lib_ranges_to_container
294 struct from_range_t {
explicit from_range_t() =
default; };
295 constexpr inline from_range_t from_range;
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>>;
300 template <
class RANGE,
class CONTAINER,
class... TYPES>
301 concept _Converts_direct_constructible = _Ref_converts<RANGE, CONTAINER>
302 && std::constructible_from<CONTAINER, RANGE, TYPES...>;
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...>;
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...>;
313 template <
class CONTAINER,
class REFERENCE>
314 concept can_push_back =
requires (CONTAINER& container, REFERENCE ref) {
315 container.push_back(ref);
318 template <
class CONTAINER,
class REFERENCE>
319 concept can_insert_at_end =
requires (CONTAINER & container, REFERENCE ref) {
320 container.insert(container.end(), ref);
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>>);
331 template <
class REFERENCE,
class CONTAINER>
332 [[nodiscard]]
constexpr auto container_inserter(CONTAINER& _Cont)
334 if constexpr (can_push_back<CONTAINER, REFERENCE>) {
335 return std::back_insert_iterator{ _Cont };
338 return std::insert_iterator{ _Cont, _Cont.end() };
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>>;
352 template <std::ranges::input_range RANGE>
353 struct phony_input_iterator
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>;
361 reference operator*()
const;
362 pointer operator->()
const;
364 phony_input_iterator& operator++();
365 phony_input_iterator operator++(
int);
367 bool operator==(
const phony_input_iterator&)
const;
370 template <
template <
class...>
class CONTAINER,
class RANGE,
class... ARGS>
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);
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)
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...>)
397 CONTAINER container(std::forward<TYPES>(args)...);
398 if constexpr (sized_and_reservable<RANGE, CONTAINER>) {
399 container.reserve(std::ranges::size(range));
401 std::ranges::copy(range, detail::container_inserter<std::ranges::range_reference_t<RANGE>>(container));
404 else if constexpr (std::ranges::input_range<std::ranges::range_reference_t<RANGE>>)
406 const auto transform = [](
auto&& _Elem) {
412 static_assert(::ghassanpl::always_false<CONTAINER>,
"the program is ill-formed per N4910 [range.utility.conv.to]/1.3");
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)
418 return to<_Deduced>(std::forward<RANGE>(_Range), std::forward<TYPES>(ARGS)...);
424 using std::ranges::to;
Concept to check if a function is a predicate on the range elements (i.e. returns boolable).
auto at_ptr(std::map< K, V, C > const &map, VAL &&value)
Same as map_find().
auto index(T &cont, RANDOM &rng=::ghassanpl::random::default_random_engine)
Returns an index of a random element in cont.
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.
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.
auto span_cast(span< FROM, N > bytes) noexcept
Casts a span of objects of type FROM to a span of objects of type TO.
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.
constexpr decltype(auto) at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at index of range.
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.
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.
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.
constexpr __contains_subrange_fn contains_subrange
contains_subrange(range, subrange)
constexpr auto to_index(random_access_iterator auto iterator, random_access_range auto const &range)
Turns an iterator to range to an index.
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.
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.
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.
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.
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
constexpr __contains_fn contains
contains(range, el)
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.
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.
Primary namespace for everything in this library.