AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
cartesian_product.h
1#ifndef PROBFD_CARTESIAN_PRODUCT_H
2#define PROBFD_CARTESIAN_PRODUCT_H
3
4#include <ranges>
5
6namespace probfd::views {
7
8#ifndef __cpp_lib_ranges_cartesian_product
9
10#include <tuple>
11#include <type_traits>
12
13template <bool Const, typename T>
14using maybe_const_t = std::conditional_t<Const, const T, T>;
15
16template <bool Const, typename... Vs>
17concept all_random_access =
18 (std::ranges::random_access_range<maybe_const_t<Const, Vs>> && ...);
19
20template <std::ranges::viewable_range Range>
21using all_t = decltype(std::views::all(std::declval<Range>()));
22
23template <std::integral Tp>
24constexpr auto to_unsigned_like(Tp t) noexcept
25{
26 return static_cast<std::make_unsigned_t<Tp>>(t);
27}
28
29template <typename Tp>
30using make_unsigned_like_t = decltype(to_unsigned_like(std::declval<Tp>()));
31
32template <typename Fp, typename Tuple>
33constexpr auto tuple_transform(Fp&& f, Tuple&& tuple)
34{
35 return std::apply(
36 [&]<typename... Ts>(Ts&&... elts) {
37 return std::tuple<std::invoke_result_t<Fp&, Ts>...>(
38 std::invoke(f, std::forward<Ts>(elts))...);
39 },
40 std::forward<Tuple>(tuple));
41}
42
43template <typename _Range>
44concept simple_view =
45 std::ranges::view<_Range> && std::ranges::range<const _Range> &&
46 std::same_as<
47 std::ranges::iterator_t<_Range>,
48 std::ranges::iterator_t<const _Range>> &&
49 std::same_as<
50 std::ranges::sentinel_t<_Range>,
51 std::ranges::sentinel_t<const _Range>>;
52
53namespace detail {
54template <bool Const, typename First, typename... Vs>
55concept cartesian_product_is_random_access =
56 (std::ranges::random_access_range<maybe_const_t<Const, First>> && ... &&
57 (std::ranges::random_access_range<maybe_const_t<Const, Vs>> &&
58 std::ranges::sized_range<maybe_const_t<Const, Vs>>));
59
60template <typename Range>
61concept cartesian_product_common_arg =
62 std::ranges::common_range<Range> ||
63 (std::ranges::sized_range<Range> &&
64 std::ranges::random_access_range<Range>);
65
66template <bool Const, typename First, typename... Vs>
67concept cartesian_product_is_bidirectional =
68 (std::ranges::bidirectional_range<maybe_const_t<Const, First>> && ... &&
69 (std::ranges::bidirectional_range<maybe_const_t<Const, Vs>> &&
70 cartesian_product_common_arg<maybe_const_t<Const, Vs>>));
71
72template <typename First, typename... Vs>
73concept cartesian_product_is_common = cartesian_product_common_arg<First>;
74
75template <typename... Vs>
76concept cartesian_product_is_sized = (std::ranges::sized_range<Vs> && ...);
77
78template <
79 bool Const,
80 template <typename>
81 class FirstSent,
82 typename First,
83 typename... Vs>
84concept cartesian_is_sized_sentinel =
85 (std::sized_sentinel_for<
86 FirstSent<maybe_const_t<Const, First>>,
87 std::ranges::iterator_t<maybe_const_t<Const, First>>> &&
88 ... &&
89 (std::ranges::sized_range<maybe_const_t<Const, Vs>> &&
90 std::sized_sentinel_for<
91 std::ranges::iterator_t<maybe_const_t<Const, Vs>>,
92 std::ranges::iterator_t<maybe_const_t<Const, Vs>>>));
93
94template <cartesian_product_common_arg _Range>
95constexpr auto cartesian_common_arg_end(_Range& __r)
96{
97 if constexpr (std::ranges::common_range<_Range>)
98 return std::ranges::end(__r);
99 else
100 return std::ranges::begin(__r) + std::ranges::distance(__r);
101}
102} // namespace detail
103
104template <std::ranges::input_range First, std::ranges::forward_range... Vs>
105 requires(std::ranges::view<First> && ... && std::ranges::view<Vs>)
106class cartesian_product_view
107 : public std::ranges::view_interface<cartesian_product_view<First, Vs...>> {
108 std::tuple<First, Vs...> _M_bases;
109
110 template <bool>
111 class Iterator;
112
113 static auto S_difference_type()
114 {
115 // TODO: Implement the recommended practice of using the smallest
116 // sufficiently wide type according to the maximum sizes of the
117 // underlying ranges?
118 return std::common_type_t<
119 ptrdiff_t,
120 std::ranges::range_difference_t<First>,
121 std::ranges::range_difference_t<Vs>...>{};
122 }
123
124public:
125 cartesian_product_view() = default;
126
127 constexpr explicit cartesian_product_view(First first, Vs... rest)
128 : _M_bases(std::move(first), std::move(rest)...)
129 {
130 }
131
132 constexpr Iterator<false> begin()
133 requires(!simple_view<First> || ... || !simple_view<Vs>)
134 {
135 return Iterator<false>(
136 *this,
137 tuple_transform(std::ranges::begin, _M_bases));
138 }
139
140 constexpr Iterator<true> begin() const
141 requires(
142 std::ranges::range<const First> && ... &&
143 std::ranges::range<const Vs>)
144 {
145 return Iterator<true>(
146 *this,
147 tuple_transform(std::ranges::begin, _M_bases));
148 }
149
150 constexpr Iterator<false> end()
151 requires(
152 (!simple_view<First> || ... || !simple_view<Vs>) &&
153 detail::cartesian_product_is_common<First, Vs...>)
154 {
155 auto its = [this]<size_t... Is>(std::index_sequence<Is...>) {
156 using Ret = std::tuple<
157 std::ranges::iterator_t<First>,
158 std::ranges::iterator_t<Vs>...>;
159 bool empty_tail =
160 (std::ranges::empty(std::get<1 + Is>(_M_bases)) || ...);
161 auto& first = std::get<0>(_M_bases);
162 return Ret{
163 (empty_tail ? std::ranges::begin(first)
164 : detail::cartesian_common_arg_end(first)),
165 std::ranges::begin(std::get<1 + Is>(_M_bases))...};
166 }(std::make_index_sequence<sizeof...(Vs)>{});
167
168 return Iterator<false>{*this, std::move(its)};
169 }
170
171 constexpr Iterator<true> end() const
172 requires detail::cartesian_product_is_common<const First, const Vs...>
173 {
174 auto its = [this]<size_t... Is>(std::index_sequence<Is...>) {
175 using Ret = std::tuple<
176 std::ranges::iterator_t<const First>,
177 std::ranges::iterator_t<const Vs>...>;
178 bool empty_tail =
179 (std::ranges::empty(std::get<1 + Is>(_M_bases)) || ...);
180 auto& first = std::get<0>(_M_bases);
181 return Ret{
182 (empty_tail ? std::ranges::begin(first)
183 : detail::cartesian_common_arg_end(first)),
184 std::ranges::begin(std::get<1 + Is>(_M_bases))...};
185 }(std::make_index_sequence<sizeof...(Vs)>{});
186
187 return Iterator<true>(*this, std::move(its));
188 }
189
190 constexpr std::default_sentinel_t end() const noexcept
191 {
192 return std::default_sentinel;
193 }
194
195 constexpr auto size()
196 requires detail::cartesian_product_is_sized<First, Vs...>
197 {
198 using ST = make_unsigned_like_t<decltype(S_difference_type())>;
199 return [&]<size_t... Is>(std::index_sequence<Is...>) {
200 return (
201 static_cast<ST>(std::ranges::size(std::get<Is>(_M_bases))) *
202 ...);
203 }(std::make_index_sequence<1 + sizeof...(Vs)>{});
204 }
205
206 constexpr auto size() const
207 requires detail::cartesian_product_is_sized<const First, const Vs...>
208 {
209 using ST = make_unsigned_like_t<decltype(S_difference_type())>;
210 return [&]<size_t... Is>(std::index_sequence<Is...>) {
211 return (
212 static_cast<ST>(std::ranges::size(std::get<Is>(_M_bases))) *
213 ...);
214 }(std::make_index_sequence<1 + sizeof...(Vs)>{});
215 }
216};
217
218template <typename... Vs>
219cartesian_product_view(Vs&&...)
220 -> cartesian_product_view<std::ranges::views::all_t<Vs>...>;
221
222template <std::ranges::input_range First, std::ranges::forward_range... Vs>
223 requires(std::ranges::view<First> && ... && std::ranges::view<Vs>)
224template <bool Const>
225class cartesian_product_view<First, Vs...>::Iterator {
226 using Parent = maybe_const_t<Const, cartesian_product_view>;
227 Parent* _M_parent = nullptr;
228 std::tuple<
229 std::ranges::iterator_t<maybe_const_t<Const, First>>,
230 std::ranges::iterator_t<maybe_const_t<Const, Vs>>...>
231 _M_current;
232
233 constexpr Iterator(Parent& parent, decltype(_M_current) current)
234 : _M_parent(std::addressof(parent))
235 , _M_current(std::move(current))
236 {
237 }
238
239 static auto S_iter_concept()
240 {
241 if constexpr (detail::cartesian_product_is_random_access<
242 Const,
243 First,
244 Vs...>)
245 return std::random_access_iterator_tag{};
246 else if constexpr (detail::cartesian_product_is_bidirectional<
247 Const,
248 First,
249 Vs...>)
250 return std::bidirectional_iterator_tag{};
251 else if constexpr (std::ranges::forward_range<
252 maybe_const_t<Const, First>>)
253 return std::forward_iterator_tag{};
254 else
255 return std::input_iterator_tag{};
256 }
257
258 friend cartesian_product_view;
259
260public:
261 using iterator_category = std::input_iterator_tag;
262 using iterator_concept = decltype(S_iter_concept());
263 using value_type = std::tuple<
264 std::ranges::range_value_t<maybe_const_t<Const, First>>,
265 std::ranges::range_value_t<maybe_const_t<Const, Vs>>...>;
266 using reference = std::tuple<
267 std::ranges::range_reference_t<maybe_const_t<Const, First>>,
268 std::ranges::range_reference_t<maybe_const_t<Const, Vs>>...>;
269 using difference_type =
270 decltype(cartesian_product_view::S_difference_type());
271
272 Iterator() = default;
273
274 constexpr Iterator(Iterator<!Const> i)
275 requires Const && (std::convertible_to<
276 std::ranges::iterator_t<First>,
277 std::ranges::iterator_t<const First>> &&
278 ... &&
279 std::convertible_to<
280 std::ranges::iterator_t<Vs>,
281 std::ranges::iterator_t<const Vs>>)
282 : _M_parent(std::addressof(i._M_parent))
283 , _M_current(std::move(i._M_current))
284 {
285 }
286
287 constexpr auto operator*() const
288 {
289 auto f = [](auto& i) -> decltype(auto) { return *i; };
290 return tuple_transform(f, _M_current);
291 }
292
293 constexpr Iterator& operator++()
294 {
295 M_next();
296 return *this;
297 }
298
299 constexpr void operator++(int) { ++*this; }
300
301 constexpr Iterator operator++(int)
302 requires std::ranges::forward_range<maybe_const_t<Const, First>>
303 {
304 auto tmp = *this;
305 ++*this;
306 return tmp;
307 }
308
309 constexpr Iterator& operator--()
310 requires detail::cartesian_product_is_bidirectional<Const, First, Vs...>
311 {
312 M_prev();
313 return *this;
314 }
315
316 constexpr Iterator operator--(int)
317 requires detail::cartesian_product_is_bidirectional<Const, First, Vs...>
318 {
319 auto tmp = *this;
320 --*this;
321 return tmp;
322 }
323
324 constexpr Iterator& operator+=(difference_type x)
325 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
326 {
327 M_advance(x);
328 return *this;
329 }
330
331 constexpr Iterator& operator-=(difference_type x)
332 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
333 {
334 return *this += -x;
335 }
336
337 constexpr reference operator[](difference_type n) const
338 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
339 {
340 return *((*this) + n);
341 }
342
343 friend constexpr bool operator==(const Iterator& x, const Iterator& y)
344 requires std::equality_comparable<
345 std::ranges::iterator_t<maybe_const_t<Const, First>>>
346 {
347 return x._M_current == y._M_current;
348 }
349
350 friend constexpr bool operator==(const Iterator& x, std::default_sentinel_t)
351 {
352 return [&]<size_t... Is>(std::index_sequence<Is...>) {
353 return (
354 (std::get<Is>(x._M_current) ==
355 std::ranges::end(std::get<Is>(x._M_parent->_M_bases))) ||
356 ...);
357 }(std::make_index_sequence<1 + sizeof...(Vs)>{});
358 }
359
360 friend constexpr auto operator<=>(const Iterator& x, const Iterator& y)
361 requires all_random_access<Const, First, Vs...>
362 {
363 return x._M_current <=> y._M_current;
364 }
365
366 friend constexpr Iterator operator+(Iterator x, difference_type y)
367 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
368 {
369 return x += y;
370 }
371
372 friend constexpr Iterator operator+(difference_type x, Iterator y)
373 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
374 {
375 return y += x;
376 }
377
378 friend constexpr Iterator operator-(Iterator x, difference_type y)
379 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
380 {
381 return x -= y;
382 }
383
384 friend constexpr difference_type
385 operator-(const Iterator& x, const Iterator& y)
386 requires detail::cartesian_is_sized_sentinel<
387 Const,
388 std::ranges::iterator_t,
389 First,
390 Vs...>
391 {
392 return x.M_distance_from(y._M_current);
393 }
394
395 friend constexpr difference_type
396 operator-(const Iterator& i, std::default_sentinel_t)
397 requires detail::cartesian_is_sized_sentinel<
398 Const,
399 std::ranges::sentinel_t,
400 First,
401 Vs...>
402 {
403 std::tuple end_tuple = [&]<size_t... Is>(std::index_sequence<Is...>) {
404 return std::tuple{
405 std::ranges::end(std::get<0>(i._M_parent->_M_bases)),
406 std::ranges::begin(std::get<1 + Is>(i._M_parent->_M_bases))...};
407 }(std::make_index_sequence<sizeof...(Vs)>{});
408 return i.M_distance_from(end_tuple);
409 }
410
411 friend constexpr difference_type
412 operator-(std::default_sentinel_t, const Iterator& i)
413 requires detail::cartesian_is_sized_sentinel<
414 Const,
415 std::ranges::sentinel_t,
416 First,
417 Vs...>
418 {
419 return -(i - std::default_sentinel);
420 }
421
422 friend constexpr auto iter_move(const Iterator& i)
423 {
424 return tuple_transform(std::ranges::iter_move, i._M_current);
425 }
426
427 friend constexpr void iter_swap(const Iterator& l, const Iterator& r)
428 requires(
429 std::indirectly_swappable<
430 std::ranges::iterator_t<maybe_const_t<Const, First>>> &&
431 ... &&
432 std::indirectly_swappable<
433 std::ranges::iterator_t<maybe_const_t<Const, Vs>>>)
434 {
435 [&]<size_t... Is>(std::index_sequence<Is...>) {
436 (std::ranges::iter_swap(
437 std::get<Is>(l._M_current),
438 std::get<Is>(r._M_current)),
439 ...);
440 }(std::make_index_sequence<1 + sizeof...(Vs)>{});
441 }
442
443private:
444 template <size_t Nm = sizeof...(Vs)>
445 constexpr void M_next()
446 {
447 auto& it = std::get<Nm>(_M_current);
448 ++it;
449 if constexpr (Nm > 0)
450 if (it == std::ranges::end(std::get<Nm>(_M_parent->_M_bases))) {
451 it = std::ranges::begin(std::get<Nm>(_M_parent->_M_bases));
452 M_next<Nm - 1>();
453 }
454 }
455
456 template <size_t Nm = sizeof...(Vs)>
457 constexpr void M_prev()
458 {
459 auto& it = std::get<Nm>(_M_current);
460 if constexpr (Nm > 0)
461 if (it == std::ranges::begin(std::get<Nm>(_M_parent->_M_bases))) {
462 it = detail::cartesian_common_arg_end(
463 std::get<Nm>(_M_parent->_M_bases));
464 M_prev<Nm - 1>();
465 }
466 --it;
467 }
468
469 template <size_t Nm = sizeof...(Vs)>
470 constexpr void M_advance(difference_type x)
471 requires detail::cartesian_product_is_random_access<Const, First, Vs...>
472 {
473 if (x == 1)
474 M_next<Nm>();
475 else if (x == -1)
476 M_prev<Nm>();
477 else if (x != 0) {
478 // Constant time iterator advancement.
479 auto& r = std::get<Nm>(_M_parent->_M_bases);
480 auto& it = std::get<Nm>(_M_current);
481 if constexpr (Nm == 0) {
482 it += x;
483 } else {
484 auto size = std::ranges::ssize(r);
485 auto begin = std::ranges::begin(r);
486 auto offset = it - begin;
487 offset += x;
488 x = offset / size;
489 offset %= size;
490 if (offset < 0) {
491 offset = size + offset;
492 --x;
493 }
494 it = begin + offset;
495 M_advance<Nm - 1>(x);
496 }
497 }
498 }
499
500 template <typename Tuple>
501 constexpr difference_type M_distance_from(const Tuple& t) const
502 {
503 return [&]<size_t... Is>(std::index_sequence<Is...>) {
504 return (M_scaled_distance<Is>(t) + ...);
505 }(std::make_index_sequence<1 + sizeof...(Vs)>{});
506 }
507
508 template <size_t Nm, typename Tuple>
509 constexpr difference_type M_scaled_distance(const Tuple& t) const
510 {
511 auto dist = static_cast<difference_type>(
512 std::get<Nm>(_M_current) - std::get<Nm>(t));
513 dist *= M_scaled_size<Nm + 1>();
514 return dist;
515 }
516
517 template <size_t Nm>
518 constexpr difference_type M_scaled_size() const
519 {
520 if constexpr (Nm <= sizeof...(Vs)) {
521 auto size = static_cast<difference_type>(
522 std::ranges::size(std::get<Nm>(_M_parent->_M_bases)));
523 size *= M_scaled_size<Nm + 1>();
524 return size;
525 } else
526 return static_cast<difference_type>(1);
527 }
528};
529
530namespace detail {
531template <typename... Ts>
532concept can_cartesian_product_view =
533 requires { cartesian_product_view<all_t<Ts>...>(std::declval<Ts>()...); };
534
535struct CartesianProduct
536 : public std::ranges::range_adaptor_closure<CartesianProduct> {
537 template <typename... Ts>
538 requires(
539 sizeof...(Ts) == 0 || detail::can_cartesian_product_view<Ts...>)
540 constexpr auto operator() [[nodiscard]] (Ts&&... ts) const
541 {
542 if constexpr (sizeof...(Ts) == 0)
543 return std::ranges::views::single(std::tuple{});
544 else
545 return cartesian_product_view<all_t<Ts>...>(
546 std::forward<Ts>(ts)...);
547 }
548};
549
550} // namespace detail
551
552inline constexpr detail::CartesianProduct cartesian_product;
553
554#else
555
556inline constexpr auto cartesian_product = std::views::cartesian_product;
557
558#endif
559
560} // namespace probfd::views
561
562#endif // PROBFD_CARTESIAN_PRODUCT_H
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.