AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
concat.h
1#ifndef PROBFD_CONCAT_H
2#define PROBFD_CONCAT_H
3
4#include <ranges>
5
6namespace probfd::views {
7
8#ifndef __cpp_lib_ranges_concat
9
10#include <iterator>
11#include <utility>
12#include <variant>
13
14namespace detail {
15
16// Alias for a type that is conditionally const.
17template <bool Const, typename Tp>
18using maybe_const_t = std::conditional_t<Const, const Tp, Tp>;
19
20template <typename... Rs>
21using concat_reference_t =
22 std::common_reference_t<std::ranges::range_reference_t<Rs>...>;
23
24template <typename... Rs>
25using concat_value_t = std::common_type_t<std::ranges::range_value_t<Rs>...>;
26
27template <typename... Rs>
28using concat_rvalue_reference_t =
29 std::common_reference_t<std::ranges::range_rvalue_reference_t<Rs>...>;
30
31template <typename Ref, typename RRef, typename It>
32concept concat_indirectly_readable_impl = requires(const It it) {
33 { *it } -> std::convertible_to<Ref>;
34 { std::ranges::iter_move(it) } -> std::convertible_to<RRef>;
35};
36
37template <typename... Rs>
38concept concat_indirectly_readable = std::common_reference_with<
39 concat_reference_t<Rs...>&&,
40 concat_value_t<Rs...>&> &&
41 std::common_reference_with<
42 concat_reference_t<Rs...>&&,
43 concat_rvalue_reference_t<Rs...>&&> &&
44 std::common_reference_with<
45 concat_rvalue_reference_t<Rs...>&&,
46 concat_value_t<Rs...> const&> &&
47 (concat_indirectly_readable_impl<
48 concat_reference_t<Rs...>,
49 concat_rvalue_reference_t<Rs...>,
50 std::ranges::iterator_t<Rs>> &&
51 ...);
52
53template <typename... Rs>
54concept concatable = requires {
55 typename concat_reference_t<Rs...>;
56 typename concat_value_t<Rs...>;
57 typename concat_rvalue_reference_t<Rs...>;
58} && concat_indirectly_readable<Rs...>;
59
60template <bool Const, typename Range, typename... Rs>
61struct all_but_last_common {
62 static inline constexpr bool value = requires {
63 requires(
64 std::ranges::common_range<maybe_const_t<Const, Range>> &&
65 all_but_last_common<Const, Rs...>::value);
66 };
67};
68
69template <bool Const, typename Range>
70struct all_but_last_common<Const, Range> {
71 static inline constexpr bool value = true;
72};
73
74template <bool Const, typename... Vs>
75concept all_random_access =
76 (std::ranges::random_access_range<maybe_const_t<Const, Vs>> && ...);
77
78template <bool Const, typename... Vs>
79concept all_bidirectional =
80 (std::ranges::bidirectional_range<maybe_const_t<Const, Vs>> && ...);
81
82template <bool Const, typename... Vs>
83concept all_forward =
84 (std::ranges::forward_range<maybe_const_t<Const, Vs>> && ...);
85
86template <bool Const, typename... Rs>
87concept concat_is_random_access =
88 all_random_access<Const, Rs...> && all_but_last_common<Const, Rs...>::value;
89
90template <bool Const, typename... Rs>
91concept concat_is_bidirectional =
92 all_bidirectional<Const, Rs...> && all_but_last_common<Const, Rs...>::value;
93
94template <typename Range, typename... Rs>
95struct last_is_common {
96 static inline constexpr bool value = last_is_common<Rs...>::value;
97};
98
99template <typename Range>
100struct last_is_common<Range> {
101 static inline constexpr bool value = std::ranges::common_range<Range>;
102};
103
104template <typename Fp, typename Tuple>
105constexpr auto tuple_transform(Fp&& f, Tuple&& t)
106{
107 return std::apply(
108 [&]<typename... Ts>(Ts&&... elts) {
109 return tuple<std::invoke_result_t<Fp&, Ts>...>(
110 std::invoke(f, std::forward<Ts>(elts))...);
111 },
112 std::forward<Tuple>(t));
113}
114
115template <typename Range>
116concept simple_view =
117 std::ranges::view<Range> && std::ranges::range<const Range> &&
118 std::same_as<
119 std::ranges::iterator_t<Range>,
120 std::ranges::iterator_t<const Range>> &&
121 std::same_as<
122 std::ranges::sentinel_t<Range>,
123 std::ranges::sentinel_t<const Range>>;
124
125template <std::integral Tp>
126constexpr auto to_unsigned_like(Tp t) noexcept
127{
128 return static_cast<std::make_unsigned_t<Tp>>(t);
129}
130
131template <typename Tp>
132using make_unsigned_like_t =
133 decltype(detail::to_unsigned_like(std::declval<Tp>()));
134
135} // namespace detail
136
137using detail::maybe_const_t;
138
139template <std::ranges::input_range... Vs>
140 requires(std::ranges::view<Vs> && ...) &&
141 (sizeof...(Vs) > 0) && detail::concatable<Vs...>
142class concat_view : public std::ranges::view_interface<concat_view<Vs...>> {
143 std::tuple<Vs...> M_views;
144
145 template <bool Const>
146 class iterator;
147
148public:
149 constexpr concat_view() = default;
150
151 constexpr explicit concat_view(Vs... views)
152 : M_views(std::move(views)...)
153 {
154 }
155
156 constexpr iterator<false> begin()
157 requires(!(detail::simple_view<Vs> && ...))
158 {
159 iterator<false> it(
160 this,
161 std::in_place_index<0>,
162 std::ranges::begin(std::get<0>(M_views)));
163 it.template M_satisfy<0>();
164 return it;
165 }
166
167 constexpr iterator<true> begin() const
168 requires(std::ranges::range<const Vs> && ...) &&
169 detail::concatable<const Vs...>
170 {
171 iterator<true> it(
172 this,
173 std::in_place_index<0>,
174 std::ranges::begin(std::get<0>(M_views)));
175 it.template M_satisfy<0>();
176 return it;
177 }
178
179 constexpr auto end()
180 requires(!(detail::simple_view<Vs> && ...))
181 {
182 if constexpr (detail::last_is_common<Vs...>::value) {
183 constexpr auto n = sizeof...(Vs);
184 return iterator<false>(
185 this,
186 std::in_place_index<n - 1>,
187 std::ranges::end(std::get<n - 1>(M_views)));
188 } else
189 return std::default_sentinel;
190 }
191
192 constexpr auto end() const
193 requires(std::ranges::range<const Vs> && ...) &&
194 detail::concatable<const Vs...>
195 {
196 if constexpr (detail::last_is_common<const Vs...>::value) {
197 constexpr auto n = sizeof...(Vs);
198 return iterator<true>(
199 this,
200 std::in_place_index<n - 1>,
201 std::ranges::end(std::get<n - 1>(M_views)));
202 } else
203 return std::default_sentinel;
204 }
205
206 constexpr auto size()
207 requires(std::ranges::sized_range<Vs> && ...)
208 {
209 return std::apply(
210 [](auto... sizes) {
211 using CT = detail::make_unsigned_like_t<
212 std::common_type_t<decltype(sizes)...>>;
213 return (CT(sizes) + ...);
214 },
215 detail::tuple_transform(std::ranges::size, M_views));
216 }
217
218 constexpr auto size() const
219 requires(std::ranges::sized_range<const Vs> && ...)
220 {
221 return std::apply(
222 [](auto... sizes) {
223 using CT = detail::make_unsigned_like_t<
224 std::common_type_t<decltype(sizes)...>>;
225 return (CT(sizes) + ...);
226 },
227 detail::tuple_transform(std::ranges::size, M_views));
228 }
229};
230
231template <typename... Rs>
232concat_view(Rs&&...) -> concat_view<std::views::all_t<Rs>...>;
233
234namespace detail {
235template <bool Const, typename... Vs>
236struct concat_view_iter_cat {};
237
238template <bool Const, typename... Vs>
239 requires detail::all_forward<Const, Vs...>
240struct concat_view_iter_cat<Const, Vs...> {
241 static auto S_iter_cat()
242 {
243 if constexpr (!std::is_reference_v<
244 concat_reference_t<maybe_const_t<Const, Vs>...>>)
245 return std::input_iterator_tag{};
246 else
247 return []<typename... Cats>(Cats...) {
248 if constexpr (
249 (std::derived_from<Cats, std::random_access_iterator_tag> &&
250 ...) &&
251 concat_is_random_access<Const, Vs...>)
252 return std::random_access_iterator_tag{};
253 else if constexpr (
254 (std::derived_from<Cats, std::bidirectional_iterator_tag> &&
255 ...) &&
256 concat_is_bidirectional<Const, Vs...>)
257 return std::bidirectional_iterator_tag{};
258 else if constexpr ((std::derived_from<
259 Cats,
260 std::forward_iterator_tag> &&
261 ...))
262 return std::forward_iterator_tag{};
263 else
264 return std::input_iterator_tag{};
265 }(typename std::iterator_traits<std::ranges::iterator_t<
266 maybe_const_t<Const, Vs>>>::iterator_category{}...);
267 }
268};
269} // namespace detail
270
271template <std::ranges::input_range... Vs>
272 requires(std::ranges::view<Vs> && ...) &&
273 (sizeof...(Vs) > 0) && detail::concatable<Vs...>
274template <bool Const>
275class concat_view<Vs...>::iterator
276 : public detail::concat_view_iter_cat<Const, Vs...> {
277 static auto S_iter_concept()
278 {
279 if constexpr (detail::concat_is_random_access<Const, Vs...>)
280 return std::random_access_iterator_tag{};
281 else if constexpr (detail::concat_is_bidirectional<Const, Vs...>)
282 return std::bidirectional_iterator_tag{};
283 else if constexpr (detail::all_forward<Const, Vs...>)
284 return std::forward_iterator_tag{};
285 else
286 return std::input_iterator_tag{};
287 }
288
289 friend concat_view;
290 friend iterator<!Const>;
291
292public:
293 // iterator_category defined in concat_view_iter_cat
294 using iterator_concept = decltype(S_iter_concept());
295 using value_type = detail::concat_value_t<maybe_const_t<Const, Vs>...>;
296 using difference_type = std::common_type_t<
297 std::ranges::range_difference_t<maybe_const_t<Const, Vs>>...>;
298
299private:
300 using base_iter =
301 std::variant<std::ranges::iterator_t<maybe_const_t<Const, Vs>>...>;
302
303 maybe_const_t<Const, concat_view>* M_parent = nullptr;
304 base_iter M_it;
305
306 template <size_t Nm>
307 constexpr void M_satisfy()
308 {
309 if constexpr (Nm < (sizeof...(Vs) - 1)) {
310 if (std::get<Nm>(M_it) ==
311 std::ranges::end(std::get<Nm>(M_parent->M_views))) {
312 M_it.template emplace<Nm + 1>(
313 std::ranges::begin(std::get<Nm + 1>(M_parent->M_views)));
314 M_satisfy<Nm + 1>();
315 }
316 }
317 }
318
319 template <size_t Nm>
320 constexpr void M_prev()
321 {
322 if constexpr (Nm == 0)
323 --std::get<0>(M_it);
324 else {
325 if (std::get<Nm>(M_it) ==
326 std::ranges::begin(std::get<Nm>(M_parent->M_views))) {
327 M_it.template emplace<Nm - 1>(
328 std::ranges::end(std::get<Nm - 1>(M_parent->M_views)));
329 M_prev<Nm - 1>();
330 } else
331 --std::get<Nm>(M_it);
332 }
333 }
334
335 template <size_t Nm>
336 constexpr void M_advance_fwd(difference_type offset, difference_type steps)
337 {
338 using Dt =
339 std::iter_difference_t<std::variant_alternative_t<Nm, base_iter>>;
340 if constexpr (Nm == sizeof...(Vs) - 1)
341 std::get<Nm>(M_it) += static_cast<Dt>(steps);
342 else {
343 auto n_size =
344 std::ranges::distance(std::get<Nm>(M_parent->M_views));
345 if (offset + steps < n_size)
346 std::get<Nm>(M_it) += static_cast<Dt>(steps);
347 else {
348 M_it.template emplace<Nm + 1>(
349 std::ranges::begin(std::get<Nm + 1>(M_parent->M_views)));
350 M_advance_fwd<Nm + 1>(0, offset + steps - n_size);
351 }
352 }
353 }
354
355 template <size_t Nm>
356 constexpr void M_advance_bwd(difference_type offset, difference_type steps)
357 {
358 using Dt =
359 std::iter_difference_t<std::variant_alternative_t<Nm, base_iter>>;
360 if constexpr (Nm == 0)
361 std::get<Nm>(M_it) -= static_cast<Dt>(steps);
362 else {
363 if (offset >= steps)
364 std::get<Nm>(M_it) -= static_cast<Dt>(steps);
365 else {
366 auto prev_size =
367 std::ranges::distance(std::get<Nm - 1>(M_parent->M_views));
368 M_it.template emplace<Nm - 1>(
369 std::ranges::end(std::get<Nm - 1>(M_parent->M_views)));
370 M_advance_bwd<Nm - 1>(prev_size, steps - offset);
371 }
372 }
373 }
374
375 // Invoke the function object f, which has a call operator with a size_t
376 // template parameter (corresponding to an index into the pack of views),
377 // using the runtime value of index as the template argument.
378 template <typename Fp>
379 static constexpr auto S_invoke_with_runtime_index(Fp&& f, size_t index)
380 {
381 return [&f, index]<size_t Idx>(this auto&& self) {
382 if (Idx == index) return f.template operator()<Idx>();
383 if constexpr (Idx + 1 < sizeof...(Vs))
384 return self.template operator()<Idx + 1>();
385 }.template operator()<0>();
386 }
387
388 template <typename Fp>
389 constexpr auto M_invoke_with_runtime_index(Fp&& f)
390 {
391 return S_invoke_with_runtime_index(std::forward<Fp>(f), M_it.index());
392 }
393
394 template <typename... Args>
395 explicit constexpr iterator(
396 maybe_const_t<Const, concat_view>* parent,
397 Args&&... args)
398 requires std::constructible_from<base_iter, Args&&...>
399 : M_parent(parent)
400 , M_it(std::forward<Args>(args)...)
401 {
402 }
403
404public:
405 iterator() = default;
406
407 constexpr iterator(iterator<!Const> it)
408 requires Const && (std::convertible_to<
409 std::ranges::iterator_t<Vs>,
410 std::ranges::iterator_t<const Vs>> &&
411 ...)
412 : M_parent(it.M_parent)
413 {
414 M_invoke_with_runtime_index([this, &it]<size_t Idx>() {
415 M_it.template emplace<Idx>(std::get<Idx>(std::move(it.M_it)));
416 });
417 }
418
419 constexpr decltype(auto) operator*() const
420 {
421 glibcxx_assert(!M_it.valueless_by_exception());
422 using reference =
423 detail::concat_reference_t<maybe_const_t<Const, Vs>...>;
424 return std::visit([](auto&& it) -> reference { return *it; }, M_it);
425 }
426
427 constexpr iterator& operator++()
428 {
429 M_invoke_with_runtime_index([this]<size_t Idx>() {
430 ++std::get<Idx>(M_it);
431 M_satisfy<Idx>();
432 });
433 return *this;
434 }
435
436 constexpr void operator++(int) { ++*this; }
437
438 constexpr iterator operator++(int)
439 requires detail::all_forward<Const, Vs...>
440 {
441 auto tmp = *this;
442 ++*this;
443 return tmp;
444 }
445
446 constexpr iterator& operator--()
447 requires detail::concat_is_bidirectional<Const, Vs...>
448 {
449 glibcxx_assert(!M_it.valueless_by_exception());
450 M_invoke_with_runtime_index([this]<size_t Idx>() { M_prev<Idx>(); });
451 return *this;
452 }
453
454 constexpr iterator operator--(int)
455 requires detail::concat_is_bidirectional<Const, Vs...>
456 {
457 auto tmp = *this;
458 --*this;
459 return tmp;
460 }
461
462 constexpr iterator& operator+=(difference_type n)
463 requires detail::concat_is_random_access<Const, Vs...>
464 {
465 glibcxx_assert(!M_it.valueless_by_exception());
466 M_invoke_with_runtime_index([this, n]<size_t Idx>() {
467 auto begin = std::ranges::begin(std::get<Idx>(M_parent->M_views));
468 if (n > 0)
469 M_advance_fwd<Idx>(std::get<Idx>(M_it) - begin, n);
470 else if (n < 0)
471 M_advance_bwd<Idx>(std::get<Idx>(M_it) - begin, -n);
472 });
473 return *this;
474 }
475
476 constexpr iterator& operator-=(difference_type n)
477 requires detail::concat_is_random_access<Const, Vs...>
478 {
479 *this += -n;
480 return *this;
481 }
482
483 constexpr decltype(auto) operator[](difference_type n) const
484 requires detail::concat_is_random_access<Const, Vs...>
485 {
486 return *((*this) + n);
487 }
488
489 friend constexpr bool operator==(const iterator& x, const iterator& y)
490 requires(
491 std::equality_comparable<
492 std::ranges::iterator_t<maybe_const_t<Const, Vs>>> &&
493 ...)
494 {
495 glibcxx_assert(!x.M_it.valueless_by_exception());
496 glibcxx_assert(!y.M_it.valueless_by_exception());
497 return x.M_it == y.M_it;
498 }
499
500 friend constexpr bool
501 operator==(const iterator& it, std::default_sentinel_t)
502 {
503 glibcxx_assert(!it.M_it.valueless_by_exception());
504 constexpr auto last_idx = sizeof...(Vs) - 1;
505 return (
506 it.M_it.index() == last_idx &&
507 (std::get<last_idx>(it.M_it) ==
508 std::ranges::end(std::get<last_idx>(it.M_parent->M_views))));
509 }
510
511 friend constexpr bool operator<(const iterator& x, const iterator& y)
512 requires detail::all_random_access<Const, Vs...>
513 {
514 return x.M_it < y.M_it;
515 }
516
517 friend constexpr bool operator>(const iterator& x, const iterator& y)
518 requires detail::all_random_access<Const, Vs...>
519 {
520 return x.M_it > y.M_it;
521 }
522
523 friend constexpr bool operator<=(const iterator& x, const iterator& y)
524 requires detail::all_random_access<Const, Vs...>
525 {
526 return x.M_it <= y.M_it;
527 }
528
529 friend constexpr bool operator>=(const iterator& x, const iterator& y)
530 requires detail::all_random_access<Const, Vs...>
531 {
532 return x.M_it >= y.M_it;
533 }
534
535 friend constexpr auto operator<=>(const iterator& x, const iterator& y)
536 requires detail::all_random_access<Const, Vs...> &&
537 (std::three_way_comparable<
538 std::ranges::iterator_t<maybe_const_t<Const, Vs>>> &&
539 ...)
540 {
541 return x.M_it <=> y.M_it;
542 }
543
544 friend constexpr iterator operator+(const iterator& it, difference_type n)
545 requires detail::concat_is_random_access<Const, Vs...>
546 {
547 auto r = it;
548 return r += n;
549 }
550
551 friend constexpr iterator operator+(difference_type n, const iterator& it)
552 requires detail::concat_is_random_access<Const, Vs...>
553 {
554 return it + n;
555 }
556
557 friend constexpr iterator operator-(const iterator& it, difference_type n)
558 requires detail::concat_is_random_access<Const, Vs...>
559 {
560 auto r = it;
561 return r -= n;
562 }
563
564 friend constexpr difference_type
565 operator-(const iterator& x, const iterator& y)
566 requires detail::concat_is_random_access<Const, Vs...>
567 {
568 return S_invoke_with_runtime_index(
569 [&]<size_t Ix>() -> difference_type {
570 return S_invoke_with_runtime_index(
571 [&]<size_t Iy>() -> difference_type {
572 if constexpr (Ix > Iy) {
573 auto dy = std::ranges::distance(
574 std::get<Iy>(y.M_it),
575 std::ranges::end(
576 std::get<Iy>(y.M_parent->M_views)));
577 auto dx = std::ranges::distance(
578 std::ranges::begin(
579 std::get<Ix>(x.M_parent->M_views)),
580 std::get<Ix>(x.M_it));
581 difference_type s = 0;
582 [&]<size_t Idx = Iy + 1>(this auto&& self) {
583 if constexpr (Idx < Ix) {
584 s += std::ranges::size(
585 std::get<Idx>(x.M_parent->M_views));
586 self.template operator()<Idx + 1>();
587 }
588 }();
589 return dy + s + dx;
590 } else if constexpr (Ix < Iy)
591 return -(y - x);
592 else
593 return std::get<Ix>(x.M_it) - std::get<Iy>(y.M_it);
594 },
595 y.M_it.index());
596 },
597 x.M_it.index());
598 }
599
600 friend constexpr difference_type
601 operator-(const iterator& x, std::default_sentinel_t)
602 requires detail::concat_is_random_access<Const, Vs...> &&
603 detail::last_is_common<maybe_const_t<Const, Vs>...>::value
604 {
605 return S_invoke_with_runtime_index(
606 [&]<size_t Ix>() -> difference_type {
607 auto dx = std::ranges::distance(
608 std::get<Ix>(x.M_it),
609 std::ranges::end(std::get<Ix>(x.M_parent->M_views)));
610 difference_type s = 0;
611 [&]<size_t Idx = Ix + 1>(this auto&& self) {
612 if constexpr (Idx < sizeof...(Vs)) {
613 s += std::ranges::size(
614 std::get<Idx>(x.M_parent->M_views));
615 self.template operator()<Idx + 1>();
616 }
617 }();
618 return -(dx + s);
619 },
620 x.M_it.index());
621 }
622
623 friend constexpr difference_type
624 operator-(std::default_sentinel_t, const iterator& x)
625 requires detail::concat_is_random_access<Const, Vs...> &&
626 detail::last_is_common<maybe_const_t<Const, Vs>...>::value
627 {
628 return -(x - std::default_sentinel);
629 }
630
631 friend constexpr decltype(auto) iter_move(const iterator& it)
632 {
633 using Res =
634 detail::concat_rvalue_reference_t<maybe_const_t<Const, Vs>...>;
635 return std::visit(
636 [](const auto& i) -> Res { return std::ranges::iter_move(i); },
637 it.M_it);
638 }
639
640 friend constexpr void iter_swap(const iterator& x, const iterator& y)
641 requires std::swappable_with<
642 std::iter_reference_t<iterator>,
643 std::iter_reference_t<iterator>> &&
644 (... && std::indirectly_swappable<
645 std::ranges::iterator_t<maybe_const_t<Const, Vs>>>)
646 {
647 std::visit(
648 [&]<typename Tp, typename Up>(const Tp& it1, const Up& it2) {
649 if constexpr (std::is_same_v<Tp, Up>)
650 std::ranges::iter_swap(it1, it2);
651 else
652 std::ranges::swap(*it1, *it2);
653 },
654 x.M_it,
655 y.M_it);
656 }
657};
658
659namespace detail {
660template <typename... Ts>
661concept can_concat_view = requires { concat_view(std::declval<Ts>()...); };
662} // namespace detail
663
664struct Concat {
665 template <typename... Ts>
666 requires detail::can_concat_view<Ts...>
667 constexpr auto operator() [[nodiscard]] (Ts&&... ts) const
668 {
669 if constexpr (sizeof...(Ts) == 1)
670 return std::views::all(std::forward<Ts>(ts)...);
671 else
672 return concat_view(std::forward<Ts>(ts)...);
673 }
674};
675
676inline constexpr Concat concat;
677
678#else
679
680inline constexpr auto concat = std::views::concat;
681
682#endif
683
684} // namespace probfd::views
685
686#endif
Interval operator+(Interval lhs, Interval rhs)
Computes the component-wise addition of two intervals.
STL namespace.