AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
dynamic_cartesian_product.h
1#ifndef PROBFD_DYNAMIC_CARTESIAN_PRODUCT_H
2#define PROBFD_DYNAMIC_CARTESIAN_PRODUCT_H
3
4#include <algorithm>
5#include <ranges>
6#include <tuple>
7#include <type_traits>
8#include <vector>
9
10#include "probfd/views/fold.h"
11
12namespace probfd::views {
13
14template <bool Const, typename T>
15using maybe_const_t = std::conditional_t<Const, const T, T>;
16
17template <std::ranges::viewable_range Range>
18using all_t = decltype(std::views::all(std::declval<Range>()));
19
20template <typename Range>
21concept dsimple_view =
22 std::ranges::view<Range> && std::ranges::range<const Range> &&
23 std::same_as<
24 std::ranges::iterator_t<Range>,
25 std::ranges::iterator_t<const Range>> &&
26 std::same_as<
27 std::ranges::sentinel_t<Range>,
28 std::ranges::sentinel_t<const Range>>;
29
30namespace detail {
31
32template <bool Const, typename R>
33concept dcartesian_product_is_random_access =
34 std::ranges::random_access_range<maybe_const_t<Const, R>> &&
35 std::ranges::sized_range<maybe_const_t<Const, R>>;
36
37template <typename Range>
38concept dcartesian_product_common_arg =
39 std::ranges::common_range<Range> ||
40 (std::ranges::sized_range<Range> &&
41 std::ranges::random_access_range<Range>);
42
43template <bool Const, typename R>
44concept dcartesian_product_is_bidirectional =
45 std::ranges::bidirectional_range<maybe_const_t<Const, R>>;
46
47template <bool Const, template <typename> class RSent, typename R>
48concept dcartesian_is_sized_sentinel = std::sized_sentinel_for<
49 RSent<maybe_const_t<Const, R>>,
50 std::ranges::iterator_t<maybe_const_t<Const, R>>>;
51
52template <dcartesian_product_common_arg Range>
53constexpr auto dcartesian_common_arg_end(Range&& r)
54{
55 if constexpr (std::ranges::common_range<Range>)
56 return std::ranges::end(r);
57 else
58 return std::ranges::begin(r) + std::ranges::distance(r);
59}
60} // namespace detail
61
62template <typename R, typename T>
63concept container_compatible_range =
64 std::ranges::input_range<R> &&
65 std::convertible_to<std::ranges::range_reference_t<R>, T>;
66
67template <typename T>
68 requires std::is_object_v<T>
69class array;
70
71template <typename T>
72bool operator==(const array<T>& left, const array<T>& right);
73
74template <typename T>
75bool operator<=>(const array<T>& left, const array<T>& right);
76
77template <typename T>
78 requires std::is_object_v<T>
79class array {
80 std::vector<T> data;
81
82 template <bool Const>
83 class Iterator;
84
85public:
86 using iterator = Iterator<false>;
87 using const_iterator = Iterator<true>;
88
89 template <container_compatible_range<T> R>
90 array(std::from_range_t, R&& rg)
91 : data(std::from_range, std::forward<R>(rg))
92 {
93 }
94
95 template <typename U>
96 explicit(false) array(const array<U>& other)
97 requires(
98 std::convertible_to<std::ranges::range_reference_t<array<U>>, T>)
99 : data(std::from_range, other)
100 {
101 }
102
103 array(const array<T>& other) = default;
104 array(array<T>&& other) = default;
105
106 array<T>& operator=(const array<T>& other) = default;
107 array<T>& operator=(array<T>&& other) = default;
108
109 auto begin() { return Iterator<false>(data.begin()); }
110 auto begin() const { return Iterator<true>(data.begin()); }
111
112 auto end() { return Iterator<false>(data.end()); }
113 auto end() const { return Iterator<true>(data.end()); }
114
115 auto size() const { return data.size(); }
116
117 decltype(auto) operator[](typename decltype(data)::size_type i)
118 {
119 return data[i];
120 }
121
122 decltype(auto) operator[](decltype(data)::size_type i) const
123 {
124 return data[i];
125 }
126
127 friend bool operator== <>(const array<T>& left, const array<T>& right);
128 friend bool operator<=> <>(const array<T>& left, const array<T>& right);
129};
130
131template <typename T>
132inline bool operator==(const array<T>& left, const array<T>& right)
133{
134 return left.data == right.data;
135}
136
137template <typename T>
138inline bool operator<=>(const array<T>& left, const array<T>& right)
139{
140 return left.data <=> right.data;
141}
142
143template <typename T>
144 requires std::is_object_v<T>
145template <bool Const>
146class array<T>::Iterator {
147 friend class array<T>;
148
149 using IType = std::conditional_t<
150 Const,
151 typename std::vector<T>::const_iterator,
152 typename std::vector<T>::iterator>;
153
154 IType underlying;
155
156 explicit Iterator(IType underlying)
157 : underlying(underlying)
158 {
159 }
160
161public:
162 using iterator_category =
163 typename std::iterator_traits<IType>::iterator_category;
164
165 using difference_type =
166 typename std::iterator_traits<IType>::difference_type;
167
168 using iterator_concept = typename IType::iterator_concept;
169
170 using value_type = std::iter_value_t<IType>;
171
172 Iterator() = default;
173
174 constexpr explicit(false) Iterator(const Iterator<Const>& i)
175 : underlying(i.underlying)
176 {
177 }
178
179 constexpr explicit(false) Iterator(Iterator<!Const> i)
180 requires Const
181 : underlying(i.underlying)
182 {
183 }
184
185 constexpr decltype(auto) operator*() const { return *underlying; }
186
187 constexpr decltype(auto) operator->() const
188 {
189 return underlying.operator->();
190 }
191
192 constexpr Iterator& operator++()
193 {
194 ++underlying;
195 return *this;
196 }
197
198 constexpr Iterator operator++(int)
199 {
200 auto tmp = *this;
201 ++*this;
202 return tmp;
203 }
204
205 constexpr Iterator& operator--()
206 {
207 --underlying;
208 return *this;
209 }
210
211 constexpr Iterator operator--(int)
212 {
213 auto tmp = *this;
214 --*this;
215 return tmp;
216 }
217
218 constexpr Iterator& operator+=(difference_type x)
219 {
220 underlying += x;
221 return *this;
222 }
223
224 constexpr Iterator& operator-=(difference_type x) { return *this += -x; }
225
226 constexpr decltype(auto) operator[](difference_type n) const
227 {
228 return *((*this) + n);
229 }
230
231 friend constexpr bool operator==(const Iterator& x, const Iterator& y)
232 {
233 return x.underlying == y.underlying;
234 }
235
236 friend constexpr auto operator<=>(const Iterator& x, const Iterator& y)
237 {
238 return x.underlying <=> y.underlying;
239 }
240
241 friend constexpr Iterator operator+(Iterator x, difference_type y)
242 {
243 return x += y;
244 }
245
246 friend constexpr Iterator operator+(difference_type x, Iterator y)
247 {
248 return y += x;
249 }
250
251 friend constexpr Iterator operator-(Iterator x, difference_type y)
252 {
253 return x -= y;
254 }
255
256 friend constexpr difference_type
257 operator-(const Iterator& x, const Iterator& y)
258 {
259 return x.underlying - y.underlying;
260 }
261
262 friend constexpr void iter_swap(const Iterator& l, const Iterator& r)
263 {
264 using std::iter_swap;
265 iter_swap(l.underlying, r.underlying);
266 }
267
268 friend constexpr decltype(auto) iter_move(const Iterator& l)
269 {
270 using std::ranges::iter_move;
271 return iter_move(l.underlying);
272 }
273};
274
275template <std::ranges::input_range R>
276array(std::from_range_t, R&&) -> array<std::ranges::range_reference_t<R>>;
277
278template <std::ranges::bidirectional_range OR>
279 requires std::ranges::view<OR> &&
280 std::ranges::input_range<std::ranges::range_reference_t<OR>> &&
281 std::ranges::borrowed_range<std::ranges::range_reference_t<OR>> &&
282 std::ranges::viewable_range<std::ranges::range_reference_t<OR>>
283class dynamic_cartesian_product_view
284 : public std::ranges::view_interface<dynamic_cartesian_product_view<OR>> {
285 using IR = std::views::all_t<std::ranges::range_reference_t<OR>>;
286 using IRIT = std::ranges::iterator_t<IR>;
287
288 using CIR = std::views::all_t<std::ranges::range_reference_t<const OR>>;
289 using CIRIT = std::ranges::iterator_t<CIR>;
290
291 template <bool>
292 class Iterator;
293
294 using S_difference_type =
295 std::common_type_t<ptrdiff_t, std::ranges::range_difference_t<IR>>;
296
297 using ST = std::make_unsigned_t<S_difference_type>;
298
299 OR M_bases;
300 std::vector<ST> M_multipliers;
301 ST size_;
302
303public:
304 dynamic_cartesian_product_view() = default;
305
306 constexpr explicit dynamic_cartesian_product_view(OR r)
307 : M_bases(std::move(r))
308 , M_multipliers(std::ranges::size(r))
309 {
310 ST multiplier = 1;
311
312 auto rit = M_bases.end();
313 auto rend = M_bases.begin();
314 auto it = M_multipliers.end();
315
316 while (rit != rend) {
317 --rit;
318 --it;
319 *it = multiplier;
320 multiplier *=
321 static_cast<ST>(std::ranges::size(std::views::all(*rit)));
322 }
323
324 size_ = multiplier;
325 }
326
327 constexpr Iterator<false> begin()
328 requires(!dsimple_view<OR>)
329 {
330 return Iterator<false>(
331 *this,
332 std::vector<IRIT>(
333 std::from_range,
334 M_bases | std::views::transform(std::views::all) |
335 std::views::transform(std::ranges::begin)));
336 }
337
338 constexpr Iterator<true> begin() const
339 {
340 return Iterator<true>(
341 *this,
342 std::vector<CIRIT>(
343 std::from_range,
344 M_bases | std::views::transform(std::views::all) |
345 std::views::transform(std::ranges::begin)));
346 }
347
348 constexpr Iterator<false> end()
349 requires(!dsimple_view<OR> && detail::dcartesian_product_common_arg<IR>)
350 {
351 std::vector<IRIT> ends(
352 std::from_range,
353 M_bases | std::views::transform(std::views::all) |
354 std::views::transform(std::ranges::begin));
355
356 if (std::ranges::none_of(M_bases, std::ranges::empty)) {
357 ends.front() = detail::dcartesian_common_arg_end(
358 *std::ranges::begin(M_bases) | std::views::all);
359 }
360
361 return Iterator<false>(*this, std::move(ends));
362 }
363
364 constexpr Iterator<true> end() const
365 requires detail::dcartesian_product_common_arg<const IR>
366 {
367 std::vector<CIRIT> ends(
368 std::from_range,
369 M_bases | std::views::transform(std::views::all) |
370 std::views::transform(std::ranges::begin));
371
372 if (std::ranges::none_of(M_bases, std::ranges::empty)) {
373 ends.front() = detail::dcartesian_common_arg_end(
374 *std::ranges::begin(M_bases) | std::views::all);
375 }
376
377 return Iterator<true>(*this, std::move(ends));
378 }
379
380 constexpr std::default_sentinel_t end() const noexcept
381 {
382 return std::default_sentinel;
383 }
384
385 constexpr auto size()
386 requires std::ranges::sized_range<IR>
387 {
388 return size_;
389 }
390
391 constexpr auto size() const
392 requires std::ranges::sized_range<const IR>
393 {
394 return size_;
395 }
396};
397
398template <typename R>
399dynamic_cartesian_product_view(R&&)
400 -> dynamic_cartesian_product_view<std::views::all_t<R>>;
401
402template <std::ranges::bidirectional_range OR>
403 requires std::ranges::view<OR> &&
404 std::ranges::input_range<std::ranges::range_reference_t<OR>> &&
405 std::ranges::borrowed_range<std::ranges::range_reference_t<OR>> &&
406 std::ranges::viewable_range<std::ranges::range_reference_t<OR>>
407template <bool Const>
408class dynamic_cartesian_product_view<OR>::Iterator {
409 using IR = std::views::all_t<
410 std::ranges::range_reference_t<maybe_const_t<Const, OR>>>;
411 using Parent = maybe_const_t<Const, dynamic_cartesian_product_view>;
412
413 Parent* M_parent = nullptr;
414
415 std::vector<std::ranges::iterator_t<IR>> M_current;
416
417 constexpr Iterator(Parent& parent, decltype(M_current) current)
418 : M_parent(std::addressof(parent))
419 , M_current(std::move(current))
420 {
421 }
422
423 static auto S_iter_concept()
424 {
425 if constexpr (detail::dcartesian_product_is_random_access<Const, IR>)
426 return std::random_access_iterator_tag{};
427 else if constexpr (detail::
428 dcartesian_product_is_bidirectional<Const, IR>)
429 return std::bidirectional_iterator_tag{};
430 else if constexpr (std::ranges::forward_range<maybe_const_t<Const, IR>>)
431 return std::forward_iterator_tag{};
432 else
433 return std::input_iterator_tag{};
434 }
435
436 friend dynamic_cartesian_product_view;
437
438public:
439 using iterator_category = std::input_iterator_tag;
440 using iterator_concept = decltype(S_iter_concept());
441 using value_type = array<std::ranges::range_value_t<IR>>;
442 using difference_type = dynamic_cartesian_product_view::S_difference_type;
443
444 Iterator() = default;
445
446 constexpr explicit(false) Iterator(Iterator<!Const> i)
447 requires Const && std::convertible_to<
448 std::ranges::iterator_t<IR>,
449 std::ranges::iterator_t<const IR>>
450 : M_parent(i.M_parent)
451 , M_current(std::from_range, i.M_current)
452 {
453 }
454
455 constexpr auto operator*() const
456 {
457 auto f = [](std::ranges::iterator_t<IR> i) -> auto {
458 if constexpr (std::is_lvalue_reference_v<decltype(*i)>)
459 return std::ref(*i);
460 else
461 return *i;
462 };
463
464 return M_current | std::views::transform(f) | std::ranges::to<array>();
465 }
466
467 constexpr Iterator& operator++()
468 {
469 auto base_it = std::prev(M_parent->M_bases.end());
470 auto it = std::prev(M_current.end());
471
472 for (; base_it != M_parent->M_bases.begin(); --base_it, --it) {
473 if (++*it != std::ranges::end(*base_it)) return *this;
474 *it = std::ranges::begin(*base_it);
475 }
476
477 ++*it;
478 return *this;
479 }
480
481 constexpr void operator++(int) { ++*this; }
482
483 constexpr Iterator operator++(int)
484 requires std::ranges::forward_range<maybe_const_t<Const, IR>>
485 {
486 auto tmp = *this;
487 ++*this;
488 return tmp;
489 }
490
491 constexpr Iterator& operator--()
492 requires detail::dcartesian_product_is_bidirectional<Const, IR>
493 {
494 auto base_it = std::prev(M_parent->M_bases.end());
495 auto it = std::prev(M_current.end());
496
497 for (; base_it != M_parent->M_bases.begin(); --base_it, --it) {
498 if (*it != std::ranges::begin(*base_it)) {
499 --*it;
500 return *this;
501 }
502 *it = detail::dcartesian_common_arg_end(*base_it);
503 }
504
505 --*it;
506 return *this;
507 }
508
509 constexpr Iterator operator--(int)
510 requires detail::dcartesian_product_is_bidirectional<Const, IR>
511 {
512 auto tmp = *this;
513 --*this;
514 return tmp;
515 }
516
517 constexpr Iterator& operator+=(difference_type x)
518 requires detail::dcartesian_product_is_random_access<Const, IR>
519 {
520 auto base_it = std::prev(M_parent->M_bases.end());
521 auto it = std::prev(M_current.end());
522
523 for (; base_it != M_parent->M_bases.begin(); --base_it, --it) {
524 if (x == 0) return *this;
525
526 auto size = std::ranges::ssize(*base_it);
527 auto begin = std::ranges::begin(*base_it);
528
529 auto offset = *it - begin;
530 offset += x;
531 x = offset / size;
532 offset %= size;
533
534 if (offset < 0) {
535 offset = size + offset;
536 --x;
537 }
538
539 *it = begin + offset;
540 }
541
542 *it += x;
543
544 return *this;
545 }
546
547 constexpr Iterator& operator-=(difference_type x)
548 requires detail::dcartesian_product_is_random_access<Const, IR>
549 {
550 return *this += -x;
551 }
552
553 constexpr decltype(auto) operator[](difference_type n) const
554 requires detail::dcartesian_product_is_random_access<Const, IR>
555 {
556 return *((*this) + n);
557 }
558
559 friend constexpr bool operator==(const Iterator& x, const Iterator& y)
560 requires std::equality_comparable<
561 std::ranges::iterator_t<maybe_const_t<Const, IR>>>
562 {
563 return x.M_current == y.M_current;
564 }
565
566 friend constexpr bool operator==(const Iterator& x, std::default_sentinel_t)
567 {
568 return std::ranges::any_of(
569 std::views::zip(x.M_current, x.M_parent->M_bases),
570 std::equal_to<decltype(x.M_current)>{});
571 }
572
573 friend constexpr auto operator<=>(const Iterator& x, const Iterator& y)
574 requires std::ranges::random_access_range<maybe_const_t<Const, IR>>
575 {
576 return x.M_current <=> y.M_current;
577 }
578
579 friend constexpr Iterator operator+(Iterator x, difference_type y)
580 requires detail::dcartesian_product_is_random_access<Const, IR>
581 {
582 return x += y;
583 }
584
585 friend constexpr Iterator operator+(difference_type x, Iterator y)
586 requires detail::dcartesian_product_is_random_access<Const, IR>
587 {
588 return y += x;
589 }
590
591 friend constexpr Iterator operator-(Iterator x, difference_type y)
592 requires detail::dcartesian_product_is_random_access<Const, IR>
593 {
594 return x -= y;
595 }
596
597 friend constexpr difference_type
598 operator-(const Iterator& x, const Iterator& y)
599 requires detail::
600 dcartesian_is_sized_sentinel<Const, std::ranges::iterator_t, IR>
601 {
602 auto mit = x.get_multipliers().begin();
603 auto mend = std::prev(x.get_multipliers().end());
604
605 auto xit = x.M_current.begin();
606 auto yit = y.M_current.begin();
607
608 difference_type dist = 0;
609
610 for (; mit != mend; ++mit, ++xit, ++yit) {
611 dist += *mit * static_cast<difference_type>(*xit - *yit);
612 }
613
614 return dist;
615 }
616
617 friend constexpr difference_type
618 operator-(const Iterator& x, std::default_sentinel_t)
619 requires detail::
620 dcartesian_is_sized_sentinel<Const, std::ranges::sentinel_t, IR>
621 {
622 auto mit = x.get_multipliers().begin();
623 auto mend = std::prev(x.get_multipliers().end());
624
625 auto xit = x.M_current.begin();
626 auto rit = x.M_parent->M_bases.begin();
627
628 difference_type dist = 0;
629
630 for (; mit != mend; ++mit, ++xit, ++rit) {
631 dist += *mit *
632 static_cast<difference_type>(*xit - std::ranges::end(*rit));
633 }
634
635 return dist;
636 }
637
638 friend constexpr difference_type
639 operator-(std::default_sentinel_t, const Iterator& i)
640 requires detail::
641 dcartesian_is_sized_sentinel<Const, std::ranges::sentinel_t, IR>
642 {
643 return -(i - std::default_sentinel);
644 }
645
646 friend constexpr void iter_swap(const Iterator& l, const Iterator& r)
647 requires(std::indirectly_swappable<
648 std::ranges::iterator_t<maybe_const_t<Const, IR>>>)
649 {
650 for (auto& [lit, rit] : std::views::zip(l.M_current, r.M_current)) {
651 std::ranges::iter_swap(lit, rit);
652 }
653 }
654
655private:
656 const auto& get_multipliers() const { return M_parent->M_multipliers; }
657};
658
659namespace detail {
660
661template <typename T>
662using all_t = decltype(std::views::all(std::declval<T>()));
663
664template <typename T>
665concept can_dynamic_cartesian_product_view =
666 std::ranges::view<all_t<T>> &&
667 std::ranges::input_range<std::ranges::range_reference_t<all_t<T>>> &&
668 std::ranges::borrowed_range<std::ranges::range_reference_t<all_t<T>>> &&
669 std::ranges::viewable_range<std::ranges::range_reference_t<all_t<T>>>;
670
671struct DynamicCartesianProduct
672 : public std::ranges::range_adaptor_closure<DynamicCartesianProduct> {
673 template <typename T>
674 requires(detail::can_dynamic_cartesian_product_view<T>)
675 constexpr auto operator() [[nodiscard]] (T&& t) const
676 {
677 return dynamic_cartesian_product_view<all_t<T>>(std::forward<T>(t));
678 }
679};
680
681template <typename T>
682struct wrap_ref {
683 using type = T;
684};
685
686template <typename T>
687 requires std::is_reference_v<T>
688struct wrap_ref<T> {
689 using type = std::reference_wrapper<std::remove_reference_t<T>>;
690};
691
692template <typename T>
693using wrap_ref_t = typename wrap_ref<T>::type;
694
695} // namespace detail
696
697inline constexpr detail::DynamicCartesianProduct dynamic_cartesian_product;
698
699} // namespace probfd::views
700
701template <typename T>
702constexpr bool std::ranges::enable_borrowed_range<probfd::views::array<T>> =
703 true;
704
705template <
706 class T,
707 class U,
708 template <class>
709 class TQual,
710 template <class>
711 class UQual>
712struct std::basic_common_reference<
713 probfd::views::array<T>,
714 probfd::views::array<U>,
715 TQual,
716 UQual> {
717 using type = probfd::views::array<probfd::views::detail::wrap_ref_t<
718 std::common_reference_t<TQual<T>, UQual<U>>>>;
719};
720
721#endif // PROBFD_DYNAMIC_CARTESIAN_PRODUCT_H
The top-level namespace of probabilistic Fast Downward.
Definition command_line.h:8
Interval operator*(value_t scale_factor, Interval val)
Scales an interval.
Interval operator+(Interval lhs, Interval rhs)
Computes the component-wise addition of two intervals.
STL namespace.