Peano 4
Loading...
Searching...
No Matches
cartesian_product.h
Go to the documentation of this file.
1#pragma once
2//#define DISABLE_CART_PROD_IOTA_SPEC
3/*
4Adapted from TartanLlama/ranges: https://github.com/TartanLlama/ranges
5Original version License CC0 1.0 Universal (see below)
6
7Modified by Gonzalo Brito Gadeschi, NVIDIA corporation
8Modifications under MIT license.
9
10---
11
12SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
13SPDX-License-Identifier: MIT
14
15Permission is hereby granted, free of charge, to any person obtaining a
16copy of this software and associated documentation files (the "Software"),
17to deal in the Software without restriction, including without limitation
18the rights to use, copy, modify, merge, publish, distribute, sublicense,
19and/or sell copies of the Software, and to permit persons to whom the
20Software is furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in
23all copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31DEALINGS IN THE SOFTWARE.
32
33---
34
35Creative Commons Legal Code
36
37CC0 1.0 Universal
38
39 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
40 LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
41 ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
42 INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
43 REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
44 PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
45 THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
46 HEREUNDER.
47
48Statement of Purpose
49
50The laws of most jurisdictions throughout the world automatically confer
51exclusive Copyright and Related Rights (defined below) upon the creator
52and subsequent owner(s) (each and all, an "owner") of an original work of
53authorship and/or a database (each, a "Work").
54
55Certain owners wish to permanently relinquish those rights to a Work for
56the purpose of contributing to a commons of creative, cultural and
57scientific works ("Commons") that the public can reliably and without fear
58of later claims of infringement build upon, modify, incorporate in other
59works, reuse and redistribute as freely as possible in any form whatsoever
60and for any purposes, including without limitation commercial purposes.
61These owners may contribute to the Commons to promote the ideal of a free
62culture and the further production of creative, cultural and scientific
63works, or to gain reputation or greater distribution for their Work in
64part through the use and efforts of others.
65
66For these and/or other purposes and motivations, and without any
67expectation of additional consideration or compensation, the person
68associating CC0 with a Work (the "Affirmer"), to the extent that he or she
69is an owner of Copyright and Related Rights in the Work, voluntarily
70elects to apply CC0 to the Work and publicly distribute the Work under its
71terms, with knowledge of his or her Copyright and Related Rights in the
72Work and the meaning and intended legal effect of CC0 on those rights.
73
741. Copyright and Related Rights. A Work made available under CC0 may be
75protected by copyright and related or neighboring rights ("Copyright and
76Related Rights"). Copyright and Related Rights include, but are not
77limited to, the following:
78
79 i. the right to reproduce, adapt, distribute, perform, display,
80 communicate, and translate a Work;
81 ii. moral rights retained by the original author(s) and/or performer(s);
82iii. publicity and privacy rights pertaining to a person's image or
83 likeness depicted in a Work;
84 iv. rights protecting against unfair competition in regards to a Work,
85 subject to the limitations in paragraph 4(a), below;
86 v. rights protecting the extraction, dissemination, use and reuse of data
87 in a Work;
88 vi. database rights (such as those arising under Directive 96/9/EC of the
89 European Parliament and of the Council of 11 March 1996 on the legal
90 protection of databases, and under any national implementation
91 thereof, including any amended or successor version of such
92 directive); and
93vii. other similar, equivalent or corresponding rights throughout the
94 world based on applicable law or treaty, and any national
95 implementations thereof.
96
972. Waiver. To the greatest extent permitted by, but not in contravention
98of, applicable law, Affirmer hereby overtly, fully, permanently,
99irrevocably and unconditionally waives, abandons, and surrenders all of
100Affirmer's Copyright and Related Rights and associated claims and causes
101of action, whether now known or unknown (including existing as well as
102future claims and causes of action), in the Work (i) in all territories
103worldwide, (ii) for the maximum duration provided by applicable law or
104treaty (including future time extensions), (iii) in any current or future
105medium and for any number of copies, and (iv) for any purpose whatsoever,
106including without limitation commercial, advertising or promotional
107purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
108member of the public at large and to the detriment of Affirmer's heirs and
109successors, fully intending that such Waiver shall not be subject to
110revocation, rescission, cancellation, termination, or any other legal or
111equitable action to disrupt the quiet enjoyment of the Work by the public
112as contemplated by Affirmer's express Statement of Purpose.
113
1143. Public License Fallback. Should any part of the Waiver for any reason
115be judged legally invalid or ineffective under applicable law, then the
116Waiver shall be preserved to the maximum extent permitted taking into
117account Affirmer's express Statement of Purpose. In addition, to the
118extent the Waiver is so judged Affirmer hereby grants to each affected
119person a royalty-free, non transferable, non sublicensable, non exclusive,
120irrevocable and unconditional license to exercise Affirmer's Copyright and
121Related Rights in the Work (i) in all territories worldwide, (ii) for the
122maximum duration provided by applicable law or treaty (including future
123time extensions), (iii) in any current or future medium and for any number
124of copies, and (iv) for any purpose whatsoever, including without
125limitation commercial, advertising or promotional purposes (the
126"License"). The License shall be deemed effective as of the date CC0 was
127applied by Affirmer to the Work. Should any part of the License for any
128reason be judged legally invalid or ineffective under applicable law, such
129partial invalidity or ineffectiveness shall not invalidate the remainder
130of the License, and in such case Affirmer hereby affirms that he or she
131will not (i) exercise any of his or her remaining Copyright and Related
132Rights in the Work or (ii) assert any associated claims and causes of
133action with respect to the Work, in either case contrary to Affirmer's
134express Statement of Purpose.
135
1364. Limitations and Disclaimers.
137
138 a. No trademark or patent rights held by Affirmer are waived, abandoned,
139 surrendered, licensed or otherwise affected by this document.
140 b. Affirmer offers the Work as-is and makes no representations or
141 warranties of any kind concerning the Work, express, implied,
142 statutory or otherwise, including without limitation warranties of
143 title, merchantability, fitness for a particular purpose, non
144 infringement, or the absence of latent or other defects, accuracy, or
145 the present or absence of errors, whether or not discoverable, all to
146 the greatest extent permissible under applicable law.
147 c. Affirmer disclaims responsibility for clearing rights of other persons
148 that may apply to the Work or any use thereof, including without
149 limitation any person's Copyright and Related Rights in the Work.
150 Further, Affirmer disclaims responsibility for obtaining any necessary
151 consents, permissions or other rights required for any use of the
152 Work.
153 d. Affirmer understands and acknowledges that Creative Commons is not a
154 party to this document and has no duty or obligation with respect to
155 this CC0 or use of the Work.
156*/
157
158#include <algorithm>
159#include <concepts>
160#include <functional>
161#include <iterator>
162#include <ranges>
163#include <tuple>
164#include <type_traits>
165#include <utility>
166
167namespace tl {
168 namespace detail {
169 template <class I>
170 concept single_pass_iterator = std::input_or_output_iterator<I> && !std::forward_iterator<I>;
171
172 template <typename... V>
173 constexpr auto common_iterator_category() {
174 if constexpr ((std::ranges::random_access_range<V> && ...))
175 return std::random_access_iterator_tag{};
176 else if constexpr ((std::ranges::bidirectional_range<V> && ...))
177 return std::bidirectional_iterator_tag{};
178 else if constexpr ((std::ranges::forward_range<V> && ...))
179 return std::forward_iterator_tag{};
180 else if constexpr ((std::ranges::input_range<V> && ...))
181 return std::input_iterator_tag{};
182 else
183 return std::output_iterator_tag{};
184 }
185 }
186
187 template <class... V>
189
190 template <class R>
191 concept simple_view = std::ranges::view<R> && std::ranges::range<const R> &&
192 std::same_as<std::ranges::iterator_t<R>, std::ranges::iterator_t<const R>> &&
193 std::same_as<std::ranges::sentinel_t<R>,
194 std::ranges::sentinel_t<const R>>;
195
196 struct as_sentinel_t {};
197 constexpr inline as_sentinel_t as_sentinel;
198
199 template <bool Const, class T>
200 using maybe_const = std::conditional_t<Const, const T, T>;
201
202 template <std::destructible T>
203 class basic_mixin : protected T {
204 public:
205 constexpr basic_mixin()
206 noexcept(std::is_nothrow_default_constructible<T>::value)
207 requires std::default_initializable<T> :
208 T() {}
209 constexpr basic_mixin(const T& t)
210 noexcept(std::is_nothrow_copy_constructible<T>::value)
211 requires std::copy_constructible<T> :
212 T(t) {}
213 constexpr basic_mixin(T&& t)
214 noexcept(std::is_nothrow_move_constructible<T>::value)
215 requires std::move_constructible<T> :
216 T(std::move(t)) {}
217
218
219 constexpr T& get() & noexcept { return *static_cast<T*>(this); }
220 constexpr const T& get() const& noexcept { return *static_cast<T const*>(this); }
221 constexpr T&& get() && noexcept { return std::move(*static_cast<T*>(this)); }
222 constexpr const T&& get() const&& noexcept { return std::move(*static_cast<T const*>(this)); }
223 };
224
225 namespace cursor {
226 namespace detail {
227 template <class C>
228 struct tags {
229 static constexpr auto single_pass() requires requires { { C::single_pass } -> std::convertible_to<bool>; } {
230 return C::single_pass;
231 }
232 static constexpr auto single_pass() { return false; }
233
234 static constexpr auto contiguous() requires requires { { C::contiguous } -> std::convertible_to<bool>; } {
235 return C::contiguous;
236 }
237 static constexpr auto contiguous() { return false; }
238 };
239 }
240 template <class C>
242
243 template <class C>
245
246 namespace detail {
247 template <class C>
249 template <class T> static auto deduce(int)-> typename T::mixin;
250 template <class T> static auto deduce(...)->tl::basic_mixin<T>;
251 using type = decltype(deduce<C>(0));
252 };
253 }
254
255 template <class C>
257
258 template <class C>
259 requires
260 requires(const C& c) { c.read(); }
261 using reference_t = decltype(std::declval<const C&>().read());
262
263 namespace detail {
264 template <class C>
266 template<class T> static auto deduce(int)-> typename T::value_type;
267 template<class T> static auto deduce(...)->std::decay_t<reference_t<T>>;
268
269 using type = decltype(deduce<C>(0));
270 };
271 }
272
273 template <class C>
274 requires std::same_as<typename detail::deduced_value_t<C>::type, std::decay_t<typename detail::deduced_value_t<C>::type>>
276
277 namespace detail {
278 template <class C>
280 template <class T> static auto deduce(int)-> typename T::difference_type;
281 template <class T>
282 static auto deduce(long)->decltype(std::declval<const T&>().distance_to(std::declval<const T&>()));
283 template <class T>
284 static auto deduce(...)->std::ptrdiff_t;
285
286 using type = decltype(deduce<C>(0));
287 };
288 }
289
290 template <class C>
292
293 template <class C>
294 concept cursor = std::semiregular<std::remove_cv_t<C>>
295 && std::semiregular<mixin_t<std::remove_cv_t<C>>>
296 && requires {typename difference_type_t<C>; };
297
298 template <class C>
299 concept readable = cursor<C> && requires(const C & c) {
300 c.read();
301 typename reference_t<C>;
302 typename value_type_t<C>;
303 };
304
305 template <class C>
307 && requires(const C & c) { c.arrow(); };
308
309 template <class C, class T>
311 && requires(C & c, T && t) { c.write(std::forward<T>(t)); };
312
313 template <class S, class C>
314 concept sentinel_for = cursor<C> && std::semiregular<S>
315 && requires(const C & c, const S & s) { {c.equal(s)} -> std::same_as<bool>; };
316
317 template <class S, class C>
319 requires(const C & c, const S & s) {
320 {c.distance_to(s)} -> std::same_as<difference_type_t<C>>;
321 };
322
323 template <class C>
324 concept next = cursor<C> && requires(C & c) { c.next(); };
325
326 template <class C>
327 concept prev = cursor<C> && requires(C & c) { c.prev(); };
328
329 template <class C>
331 && requires(C & c, difference_type_t<C> n) { c.advance(n); };
332
333 template <class C>
335 && requires(const C & c) { c.indirect_move(); };
336
337 template <class C, class O>
339 && requires(const C & c, const O & o) {
340 c.indirect_swap(o);
341 o.indirect_swap(c);
342 };
343
344 template <class C>
346 template <class C>
347 concept forward = input<C> && sentinel_for<C, C> && !single_pass<C>;
348 template <class C>
350 template <class C>
352 template <class C>
353 concept contiguous = random_access<C> && tagged_contiguous<C> && std::is_reference_v<reference_t<C>>;
354
355 template <class C>
356 constexpr auto cpp20_iterator_category() {
357 if constexpr (contiguous<C>)
358 return std::contiguous_iterator_tag{};
359 else if constexpr (random_access<C>)
360 return std::random_access_iterator_tag{};
361 else if constexpr (bidirectional<C>)
362 return std::bidirectional_iterator_tag{};
363 else if constexpr (forward<C>)
364 return std::forward_iterator_tag{};
365 else
366 return std::input_iterator_tag{};
367 }
368 template <class C>
369 using cpp20_iterator_category_t = decltype(cpp20_iterator_category<C>());
370
371 //There were a few changes in requirements on iterators between C++17 and C++20
372 //See https://wg21.link/p2259 for discussion
373 //- C++17 didn't have contiguous iterators
374 //- C++17 input iterators required *it++ to be valid
375 //- C++17 forward iterators required the reference type to be exactly value_type&/value_type const& (i.e. not a proxy)
377
378 template <class C>
380 (std::same_as<reference_t<C>, value_type_t<C>&> || std::same_as<reference_t<C>, value_type_t<C> const&>);
381
382 template <class C>
384 (std::move_constructible<value_type_t<C>> && std::constructible_from<value_type_t<C>, reference_t<C>>);
385
386 template <class C>
387 constexpr auto cpp17_iterator_category() {
388 if constexpr (random_access<C>
389#if !defined(__NVCOMPILER)
390 // YOLO: with nvc++ proxy iterators can be random access . . .
391 // BUG: Need to update Thrust to C++20 iterator categories
393#endif
394 )
395 return std::random_access_iterator_tag{};
397 return std::bidirectional_iterator_tag{};
398 else if constexpr (forward<C> && reference_is_value_type_ref<C>)
399 return std::forward_iterator_tag{};
400 else if constexpr (can_create_postincrement_proxy<C>)
401 return std::input_iterator_tag{};
402 else
403 return not_a_cpp17_iterator{};
404 }
405 template <class C>
406 using cpp17_iterator_category_t = decltype(cpp17_iterator_category<C>());
407
408 //iterator_concept and iterator_category are tricky; this abstracts them out.
409 //Since the rules for iterator categories changed between C++17 and C++20
410 //a C++20 iterator may have a weaker category in C++17,
411 //or it might not be a valid C++17 iterator at all.
412 //iterator_concept will be the C++20 iterator category.
413 //iterator_category will be the C++17 iterator category, or it will not exist
414 //in the case that the iterator is not a valid C++17 iterator.
415 template <cursor C, class category = cpp17_iterator_category_t<C>>
417 using iterator_category = category;
418 };
419 template <cursor C>
421
422 template <cursor C>
429
430 namespace detail {
431 // We assume a cursor is writeable if it's either not readable
432 // or it is writeable with the same type it reads to
433 template <class C>
435 template <readable T>
436 requires requires (C c) {
437 c.write(c.read());
438 }
439 static auto deduce()->std::true_type;
440
441 template <readable T>
442 static auto deduce()->std::false_type;
443
444 template <class T>
445 static auto deduce()->std::true_type;
446
447 static constexpr bool value = decltype(deduce<C>())::value;
448 };
449 }
450 }
451
452 namespace detail {
453 template <class T>
455 private:
457
458 public:
459 template<typename U>
460 constexpr post_increment_proxy(U&& t)
461 : cache_(std::forward<U>(t))
462 {}
463 constexpr T const& operator*() const noexcept
464 {
465 return cache_;
466 }
467 };
468 }
469
470
471 template <cursor::input C>
473 public cursor::mixin_t<C>
474 {
475 private:
477
478 constexpr auto& cursor() noexcept { return this->mixin::get(); }
479 constexpr auto const& cursor() const noexcept { return this->mixin::get(); }
480
481 template <cursor::input>
482 friend class basic_iterator;
483
484 //TODO these need to change to support output iterators
485 using reference_t = decltype(std::declval<C>().read());
487
488 public:
489 using mixin::get;
490
494
495 basic_iterator() = default;
496
497 using mixin::mixin;
498
499 constexpr explicit basic_iterator(C&& c)
500 noexcept(std::is_nothrow_constructible_v<mixin, C&&>) :
501 mixin(std::move(c)) {}
502
503
504 constexpr explicit basic_iterator(C const& c)
505 noexcept(std::is_nothrow_constructible_v<mixin, C const&>) :
506 mixin(c) {}
507
508 template <std::convertible_to<C> O>
510 noexcept(std::is_nothrow_constructible<mixin, O&&>::value) :
511 mixin(that.cursor()) {}
512
513 template <std::convertible_to<C> O>
514 constexpr basic_iterator(const basic_iterator<O>& that)
515 noexcept(std::is_nothrow_constructible<mixin, const O&>::value) :
516 mixin(std::move(that.cursor())) {}
517
518 template <std::convertible_to<C> O>
520 noexcept(std::is_nothrow_assignable<C&, O&&>::value) {
521 cursor() = std::move(that.cursor());
522 return *this;
523 }
524
525 template <std::convertible_to<C> O>
527 noexcept(std::is_nothrow_assignable<C&, const O&>::value) {
528 cursor() = that.cursor();
529 return *this;
530 }
531
532 template <class T>
533 requires
534 (!std::same_as<std::decay_t<T>, basic_iterator> &&
537 constexpr basic_iterator& operator=(T&& t) &
538 noexcept(noexcept(std::declval<C&>().write(static_cast<T&&>(t)))) {
539 cursor() = std::forward<T>(t);
540 return *this;
541 }
542
543 friend constexpr decltype(auto) iter_move(const basic_iterator& i)
544#if !defined(__NVCOMPILER)
545 noexcept(noexcept(i.cursor().indirect_move()))
546#endif
547 requires cursor::indirect_move<C> {
548 return i.cursor().indirect_move();
549 }
550
551 template <class O>
553 friend constexpr void iter_swap(
554 const basic_iterator& x, const basic_iterator<O>& y)
555#if !defined(__NVCOMPILER)
556 noexcept(noexcept((void)x.indirect_swap(y)))
557#endif
558 {
559 x.indirect_swap(y);
560 }
561
562 //Input iterator
563 constexpr decltype(auto) operator*() const
564 noexcept(noexcept(std::declval<const C&>().read()))
566 return cursor().read();
567 }
568
569 //Output iterator
570 constexpr decltype(auto) operator*()
571 noexcept(noexcept(reference_t{ cursor() }))
573 return reference_t{ cursor() };
574 }
575
576 //Output iterator
577 constexpr decltype(auto) operator*() const
578 noexcept(noexcept(
581 return const_reference_t{ cursor() };
582 }
583
584 constexpr basic_iterator& operator*() noexcept
585 requires (!cursor::next<C>) {
586 return *this;
587 }
588
589 // operator->: "Manual" deduction override,
590 constexpr decltype(auto) operator->() const
591 noexcept(noexcept(cursor().arrow()))
592 requires cursor::arrow<C> {
593 return cursor().arrow();
594 }
595 // operator->: Otherwise, if reference_t is an lvalue reference,
596 constexpr decltype(auto) operator->() const
597 noexcept(noexcept(*std::declval<const basic_iterator&>()))
598 requires (cursor::readable<C> && !cursor::arrow<C>)
599 && std::is_lvalue_reference<const_reference_t>::value{
600 return std::addressof(**this);
601 }
602
603 // modifiers
604 constexpr basic_iterator& operator++() & noexcept {
605 return *this;
606 }
608 noexcept(noexcept(std::declval<basic_iterator>().cursor().next()))
609 requires cursor::next<C> {
610 cursor().next();
611 return *this;
612 }
613
614 //C++17 required that *it++ was valid.
615 //For input iterators, we can't copy *this, so we need to create a proxy reference.
616 constexpr auto operator++(int) &
617 noexcept(noexcept(++std::declval<basic_iterator&>()) &&
618 std::is_nothrow_move_constructible_v<value_type>&&
619 std::is_nothrow_constructible_v<value_type, reference>)
620 requires (cursor::single_pass<C>&&
621 std::move_constructible<value_type>&&
622 std::constructible_from<value_type, reference>) {
624 ++* this;
625 return p;
626 }
627
628 //If we can't create a proxy reference, it++ is going to return void
629 constexpr void operator++(int) &
630 noexcept(noexcept(++std::declval<basic_iterator&>()))
631 requires (cursor::single_pass<C> && !(std::move_constructible<value_type>&&
632 std::constructible_from<value_type, reference>)) {
633 (void)(++(*this));
634 }
635
636 //If C is a forward cursor then copying it is fine
637 constexpr basic_iterator operator++(int) &
638 noexcept(std::is_nothrow_copy_constructible_v<C>&&
639 std::is_nothrow_move_constructible_v<C> &&
640 noexcept(++std::declval<basic_iterator&>()))
641 requires (!cursor::single_pass<C>) {
642 auto temp = *this;
643 ++* this;
644 return temp;
645 }
646
648 noexcept(noexcept(cursor().prev()))
649 requires cursor::bidirectional<C> {
650 cursor().prev();
651 return *this;
652 }
653
654 //Postfix decrement doesn't have the same issue as postfix increment
655 //because bidirectional requires the cursor to be a forward cursor anyway
656 //so copying it is fine.
657 constexpr basic_iterator operator--(int) &
658 noexcept(std::is_nothrow_copy_constructible<basic_iterator>::value&&
659 std::is_nothrow_move_constructible<basic_iterator>::value &&
660 noexcept(--std::declval<basic_iterator&>()))
661 requires cursor::bidirectional<C> {
662 auto tmp = *this;
663 --* this;
664 return tmp;
665 }
666
668 noexcept(noexcept(cursor().advance(n)))
669 requires cursor::random_access<C> {
670 cursor().advance(n);
671 return *this;
672 }
673
675 noexcept(noexcept(cursor().advance(-n)))
676 requires cursor::random_access<C> {
677 cursor().advance(-n);
678 return *this;
679 }
680
681 constexpr decltype(auto) operator[](difference_type n) const
682 noexcept(noexcept(*(std::declval<basic_iterator&>() + n)))
683 requires cursor::random_access<C> {
684 return *(*this + n);
685 }
686
687 // non-template type-symmetric ops to enable implicit conversions
688 friend constexpr difference_type operator-(
689 const basic_iterator& x, const basic_iterator& y)
690 noexcept(noexcept(y.cursor().distance_to(x.cursor())))
692 return y.cursor().distance_to(x.cursor());
693 }
694 friend constexpr bool operator==(
695 const basic_iterator& x, const basic_iterator& y)
696#if !defined(__NVCOMPILER)
697 noexcept(noexcept(x.cursor().equal(y.cursor())))
698 requires cursor::sentinel_for<C, C>
699#endif
700 {
701 return x.cursor().equal(y.cursor());
702 }
703 friend constexpr bool operator!=(
704 const basic_iterator& x, const basic_iterator& y)
705#if !defined(__NVCOMPILER)
706 noexcept(noexcept(!(x == y)))
708#endif
709 {
710 return !(x == y);
711 }
712 friend constexpr bool operator<(
713 const basic_iterator& x, const basic_iterator& y)
714#if !defined(__NVCOMPILER)
715 noexcept(noexcept(y - x))
716#endif
718 return 0 < (y - x);
719 }
720 friend constexpr bool operator>(
721 const basic_iterator& x, const basic_iterator& y)
722#if !defined(__NVCOMPILER)
723 noexcept(noexcept(y - x))
724#endif
726 return 0 > (y - x);
727 }
728 friend constexpr bool operator<=(
729 const basic_iterator& x, const basic_iterator& y)
730#if !defined(__NVCOMPILER)
731 noexcept(noexcept(y - x))
732#endif
734 return 0 <= (y - x);
735 }
736 friend constexpr bool operator>=(
737 const basic_iterator& x, const basic_iterator& y)
738#if !defined(__NVCOMPILER)
739 noexcept(noexcept(y - x))
740#endif
742 return 0 >= (y - x);
743 }
744 };
745
746 namespace detail {
747 template <class C>
749 template <class T>
750 static auto deduce(basic_iterator<T> const&)->std::true_type;
751 template <class T>
752 static auto deduce(...)->std::false_type;
753 static constexpr inline bool value = decltype(deduce(std::declval<C>()))::value;
754 };
755 }
756
757 // basic_iterator nonmember functions
758 template <class C>
761 noexcept(std::is_nothrow_copy_constructible<basic_iterator<C>>::value&&
762 std::is_nothrow_move_constructible<basic_iterator<C>>::value &&
763 noexcept(std::declval<basic_iterator<C>&>() += n))
764 requires cursor::random_access<C> {
765 auto tmp = i;
766 tmp += n;
767 return tmp;
768 }
769 template <class C>
772 noexcept(noexcept(i + n))
773 requires cursor::random_access<C> {
774 return i + n;
775 }
776
777 template <class C>
780 noexcept(noexcept(i + (-n)))
781 requires cursor::random_access<C> {
782 return i + (-n);
783 }
784 template <class C1, class C2>
785 requires cursor::sized_sentinel_for<C1, C2>
787 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
788 noexcept(noexcept(
789 rhs.get().distance_to(lhs.get()))) {
790 return rhs.get().distance_to(lhs.get());
791 }
792 template <class C, class S>
793 requires cursor::sized_sentinel_for<S, C>
795 const S& lhs, const basic_iterator<C>& rhs)
796 noexcept(noexcept(rhs.get().distance_to(lhs))) {
797 return rhs.get().distance_to(lhs);
798 }
799 template <class C, class S>
800 requires cursor::sized_sentinel_for<S, C>
802 const basic_iterator<C>& lhs, const S& rhs)
803 noexcept(noexcept(-(rhs - lhs))) {
804 return -(rhs - lhs);
805 }
806
807 template <class C1, class C2>
808 requires cursor::sentinel_for<C2, C1>
809 constexpr bool operator==(
810 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
811 noexcept(noexcept(lhs.get().equal(rhs.get()))) {
812 return lhs.get().equal(rhs.get());
813 }
814 template <class C, class S>
815 requires cursor::sentinel_for<S, C>
816 constexpr bool operator==(
817 const basic_iterator<C>& lhs, const S& rhs)
818 noexcept(noexcept(lhs.get().equal(rhs))) {
819 return lhs.get().equal(rhs);
820 }
821 template <class C, class S>
822 requires cursor::sentinel_for<S, C>
823 constexpr bool operator==(
824 const S& lhs, const basic_iterator<C>& rhs)
825 noexcept(noexcept(rhs == lhs)) {
826 return rhs == lhs;
827 }
828
829 template <class C1, class C2>
830 requires cursor::sentinel_for<C2, C1>
831 constexpr bool operator!=(
832 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
833 noexcept(noexcept(!(lhs == rhs))) {
834 return !(lhs == rhs);
835 }
836 template <class C, class S>
837 requires cursor::sentinel_for<S, C>
838 constexpr bool operator!=(
839 const basic_iterator<C>& lhs, const S& rhs)
840 noexcept(noexcept(!lhs.get().equal(rhs))) {
841 return !lhs.get().equal(rhs);
842 }
843 template <class C, class S>
844 requires cursor::sentinel_for<S, C>
845 constexpr bool operator!=(
846 const S& lhs, const basic_iterator<C>& rhs)
847 noexcept(noexcept(!rhs.get().equal(lhs))) {
848 return !rhs.get().equal(lhs);
849 }
850
851 template <class C1, class C2>
852 requires cursor::sized_sentinel_for<C1, C2>
853 constexpr bool operator<(
854 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
855 noexcept(noexcept(lhs - rhs < 0)) {
856 return (lhs - rhs) < 0;
857 }
858
859 template <class C1, class C2>
860 requires cursor::sized_sentinel_for<C1, C2>
861 constexpr bool operator>(
862 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
863 noexcept(noexcept((lhs - rhs) > 0)) {
864 return (lhs - rhs) > 0;
865 }
866
867 template <class C1, class C2>
868 requires cursor::sized_sentinel_for<C1, C2>
869 constexpr bool operator<=(
870 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
871 noexcept(noexcept((lhs - rhs) <= 0)) {
872 return (lhs - rhs) <= 0;
873 }
874
875 template <class C1, class C2>
876 requires cursor::sized_sentinel_for<C1, C2>
877 constexpr bool operator>=(
878 const basic_iterator<C1>& lhs, const basic_iterator<C2>& rhs)
879 noexcept(noexcept((lhs - rhs) >= 0)) {
880 return (lhs - rhs) >= 0;
881 }
882
883 template <class V, bool Const>
885 using Base = std::conditional_t<Const, const V, V>;
886
887 public:
888 std::ranges::sentinel_t<Base> end_{};
889 basic_sentinel() = default;
890 constexpr explicit basic_sentinel(std::ranges::sentinel_t<Base> end)
891 : end_{ std::move(end) } {}
892
893 constexpr basic_sentinel(basic_sentinel<V, !Const> other) requires Const&& std::
894 convertible_to<std::ranges::sentinel_t<V>,
895 std::ranges::sentinel_t<Base>>
896 : end_{ std::move(other.end_) } {}
897
898 constexpr auto end() const {
899 return end_;
900 }
901
902 friend class basic_sentinel<V, !Const>;
903 };
904
905 //tl::compose composes f and g such that compose(f,g)(args...) is f(g(args...)), i.e. g is called first
906 template <class F, class G>
907 struct compose_fn {
908 [[no_unique_address]] F f;
909 [[no_unique_address]] G g;
910
911 template <class A, class B>
912 compose_fn(A&& a, B&& b) : f(std::forward<A>(a)), g(std::forward<B>(b)) {}
913
914 template <class A, class B, class ... Args>
915 static constexpr auto call(A&& a, B&& b, Args&&... args) {
916 if constexpr (std::is_void_v<std::invoke_result_t<G, Args...>>) {
917 std::invoke(std::forward<B>(b), std::forward<Args>(args)...);
918 return std::invoke(std::forward<A>(a));
919 }
920 else {
921 return std::invoke(std::forward<A>(a), std::invoke(std::forward<B>(b), std::forward<Args>(args)...));
922 }
923 }
924
925 template <class... Args>
926 constexpr auto operator()(Args&&... args) & {
927 return call(f, g, std::forward<Args>(args)...);
928 }
929
930 template <class... Args>
931 constexpr auto operator()(Args&&... args) const& {
932 return call(f, g, std::forward<Args>(args)...);
933 }
934
935 template <class... Args>
936 constexpr auto operator()(Args&&... args)&& {
937 return call(std::move(f), std::move(g), std::forward<Args>(args)...);
938 }
939
940 template <class... Args>
941 constexpr auto operator()(Args&&... args) const&& {
942 return call(std::move(f), std::move(g), std::forward<Args>(args)...);
943 }
944 };
945
946 template <class F, class G>
947 constexpr auto compose(F&& f, G&& g) {
948 return compose_fn<std::remove_cvref_t<F>, std::remove_cvref_t<G>>(std::forward<F>(f), std::forward<G>(g));
949 }
950
951 //tl::pipeable takes some invocable and enables:
952 //- Piping a single argument to it such that a | pipeable is the same as pipeable(a)
953 //- Piping it to another pipeable object, such that a | b is the same as tl::compose(b, a)
954 struct pipeable_base {};
955 template <class T>
956 concept is_pipeable = std::is_base_of_v<pipeable_base, std::remove_cvref_t<T>>;
957
958 template <class F>
960 [[no_unique_address]] F f_;
961
962 constexpr pipeable_fn(F f) : f_(std::move(f)) {}
963
964 template <class... Args>
965 constexpr auto operator()(Args&&... args) const requires std::invocable<F, Args...> {
966 return std::invoke(f_, std::forward<Args>(args)...);
967 }
968 };
969
970 template <class F>
971 constexpr auto pipeable(F f) {
972 return pipeable_fn{ std::move(f) };
973 }
974
975 template <class V, class Pipe>
976 constexpr auto operator|(V&& v, Pipe&& fn)
977 requires (!is_pipeable<V> && is_pipeable<Pipe> && std::invocable<Pipe, V>) {
978 return std::invoke(std::forward<Pipe>(fn).f_, std::forward<V>(v));
979 }
980
981 template <class Pipe1, class Pipe2>
982 constexpr auto operator|(Pipe1&& p1, Pipe2&& p2)
984 return pipeable(compose(std::forward<Pipe2>(p2).f_, std::forward<Pipe1>(p1).f_));
985 }
986
987 //tl::bind_back binds the last N arguments of f to the given ones, returning a new closure
988 template <class F, class... Args>
989 constexpr auto bind_back(F&& f, Args&&... args) {
990 return[f_ = std::forward<F>(f), ...args_ = std::forward<Args>(args)]
991 (auto&&... other_args)
992 requires std::invocable<F&, decltype(other_args)..., Args&...> {
993 return std::invoke(f_, std::forward<decltype(other_args)>(other_args)..., args_...);
994 };
995 }
996}
997
998namespace std {
999 template <class C>
1000 struct iterator_traits<tl::basic_iterator<C>> : tl::cursor::associated_types<C> {};
1001}
1002
1003namespace tl {
1004
1005 template <class Tuple>
1006 constexpr inline std::size_t tuple_size = std::tuple_size_v<std::remove_cvref_t<Tuple>>;
1007
1008 template <std::size_t N>
1009 using index_constant = std::integral_constant<std::size_t, N>;
1010
1011 namespace meta {
1012 //Partially-apply the given template with the given arguments
1013 template <template <class...> class T, class... Args>
1014 struct partial {
1015 template <class... MoreArgs>
1016 struct apply {
1017 using type = T<Args..., MoreArgs...>;
1018 };
1019 };
1020
1021 namespace detail {
1022 template <class T, template<class...> class Into, std::size_t... Idx>
1023 constexpr auto repeat_into_impl(std::index_sequence<Idx...>)
1024 ->Into < typename decltype((Idx, std::type_identity<T>{}))::type... > ;
1025 }
1026
1027 //Repeat the given type T into the template Into N times
1028 template <class T, std::size_t N, template <class...> class Into>
1029 using repeat_into = decltype(tl::meta::detail::repeat_into_impl<T,Into>(std::make_index_sequence<N>{}));
1030 }
1031
1032 //If the size of Ts is 2, returns pair<Ts...>, otherwise returns tuple<Ts...>
1033 namespace detail {
1034 template<class... Ts>
1035 struct tuple_or_pair_impl : std::type_identity<std::tuple<Ts...>> {};
1036 template<class Fst, class Snd>
1037 struct tuple_or_pair_impl<Fst, Snd> : std::type_identity<std::pair<Fst, Snd>> {};
1038 }
1039 template<class... Ts>
1040 using tuple_or_pair = typename detail::tuple_or_pair_impl<Ts...>::type;
1041
1042 template <class Tuple>
1043 constexpr auto min_tuple(Tuple&& tuple) {
1044 return std::apply([](auto... sizes) {
1045 return std::ranges::min({
1046 std::common_type_t<decltype(sizes)...>(sizes)...
1047 });
1048 }, std::forward<Tuple>(tuple));
1049 }
1050
1051 template <class Tuple>
1052 constexpr auto max_tuple(Tuple&& tuple) {
1053 return std::apply([](auto... sizes) {
1054 return std::ranges::max({
1055 std::common_type_t<decltype(sizes)...>(sizes)...
1056 });
1057 }, std::forward<Tuple>(tuple));
1058 }
1059
1060 //Call f on every element of the tuple, returning a new one
1061 template<class F, class... Tuples>
1062 constexpr auto tuple_transform(F&& f, Tuples&&... tuples)
1063 {
1064 if constexpr (sizeof...(Tuples) > 1) {
1065 auto call_at_index = []<std::size_t Idx, class Fu, class... Ts>
1066 (tl::index_constant<Idx>, Fu f, Ts&&... tuples) {
1067 return f(std::get<Idx>(std::forward<Ts>(tuples))...);
1068 };
1069
1070 constexpr auto min_size = tl::min_tuple(std::tuple(tl::tuple_size<Tuples>...));
1071
1072 return[&] <std::size_t... Idx>(std::index_sequence<Idx...>) {
1073 return tuple_or_pair < std::decay_t<decltype(call_at_index(tl::index_constant<Idx>{}, std::move(f), std::forward<Tuples>(tuples)...)) > ... >
1074 (call_at_index(tl::index_constant<Idx>{}, std::move(f), std::forward<Tuples>(tuples)...)...);
1075 }(std::make_index_sequence<min_size>{});
1076 }
1077 else if constexpr (sizeof...(Tuples) == 1) {
1078 return std::apply([&]<class... Ts>(Ts&&... elements) {
1080 std::invoke(f, std::forward<Ts>(elements))...
1081 );
1082 }, std::forward<Tuples>(tuples)...);
1083 }
1084 else {
1085 return std::tuple{};
1086 }
1087 }
1088
1089 //Call f on every element of the tuple
1090 template<class F, class Tuple>
1091 constexpr auto tuple_for_each(F&& f, Tuple&& tuple)
1092 {
1093 return std::apply([&]<class... Ts>(Ts&&... elements) {
1094 (std::invoke(f, std::forward<Ts>(elements)), ...);
1095 }, std::forward<Tuple>(tuple));
1096 }
1097
1098 template <class Tuple>
1099 constexpr auto tuple_pop_front(Tuple&& tuple) {
1100 return std::apply([](auto&& head, auto&&... tail) {
1101 return std::pair(std::forward<decltype(head)>(head), std::tuple(std::forward<decltype(tail)>(tail)...));
1102 }, std::forward<Tuple>(tuple));
1103 }
1104
1105 namespace detail {
1106 template <class F, class V>
1107 constexpr auto tuple_fold_impl(F, V v) {
1108 return v;
1109 }
1110 template <class F, class V, class Arg, class... Args>
1111 constexpr auto tuple_fold_impl(F f, V v, Arg arg, Args&&... args) {
1113 std::invoke(f, std::move(v), std::move(arg)),
1114 std::forward<Args>(args)...);
1115 }
1116 }
1117
1118 template <class F, class T, class Tuple>
1119 constexpr auto tuple_fold(Tuple tuple, T t, F f) {
1120 return std::apply([&](auto&&... args) {
1121 return tl::detail::tuple_fold_impl(std::move(f), std::move(t), std::forward<decltype(args)>(args)...);
1122 }, std::forward<Tuple>(tuple));
1123 }
1124
1125 template<class... Tuples>
1126 constexpr auto tuple_zip(Tuples&&... tuples) {
1127 auto zip_at_index = []<std::size_t Idx, class... Ts>
1128 (tl::index_constant<Idx>, Ts&&... tuples) {
1129 return tuple_or_pair<std::decay_t<decltype(std::get<Idx>(std::forward<Ts>(tuples)))>...>(std::get<Idx>(std::forward<Ts>(tuples))...);
1130 };
1131
1132 constexpr auto min_size = tl::min_tuple(std::tuple(tl::tuple_size<Tuples>...));
1133
1134 return[&] <std::size_t... Idx>(std::index_sequence<Idx...>) {
1135 return tuple_or_pair<std::decay_t<decltype(zip_at_index(tl::index_constant<Idx>{}, std::forward<Tuples>(tuples)...))>... >
1136 (zip_at_index(tl::index_constant<Idx>{}, std::forward<Tuples>(tuples)...)...);
1137 }(std::make_index_sequence<min_size>{});
1138 }
1139
1140 template <std::ranges::forward_range... Vs>
1141 requires (std::ranges::view<Vs> && ...) class cartesian_product_view
1142 : public std::ranges::view_interface<cartesian_product_view<Vs...>> {
1143
1144 //random access + sized is allowed because we can jump all iterators to the end
1145 template <class... Ts>
1146 static constexpr bool am_common = (std::ranges::common_range<Ts> && ...)
1147 || ((std::ranges::random_access_range<Ts> && ...) && (std::ranges::sized_range<Ts> && ...));
1148
1149 template <class... Ts>
1150 static constexpr bool am_sized = (std::ranges::sized_range<Ts> && ...);
1151
1152 //requires common because we need to be able to cycle the iterators from begin to end in O(n)
1153 template <class... Ts>
1154 static constexpr bool am_bidirectional =
1155 ((std::ranges::bidirectional_range<Ts> && ...) && (std::ranges::common_range<Ts> && ...));
1156
1157 //requires sized because we need to calculate new positions for iterators using arithmetic modulo the range size
1158 template <class... Ts>
1159 static constexpr bool am_random_access =
1160 ((std::ranges::random_access_range<Ts> && ...) &&
1161 (std::ranges::sized_range<Ts> && ...));
1162
1163 template <class... Ts>
1164 static constexpr bool am_distanceable =
1165 ((std::sized_sentinel_for<std::ranges::iterator_t<Ts>, std::ranges::iterator_t<Ts>>) && ...)
1166 && am_sized<Ts...>;
1167
1168 std::tuple<Vs...> bases_;
1169
1170 template <bool Const>
1171 class cursor;
1172
1173 //Wraps the end iterator for the 0th range.
1174 //This is all that's required because the cursor will only ever set the 0th iterator to end when
1175 //the cartesian product operation has completed.
1176 template <bool Const>
1177 class sentinel {
1178 using parent = std::conditional_t<Const, const cartesian_product_view, cartesian_product_view>;
1179 using first_base = decltype(std::get<0>(std::declval<parent>().bases_));
1180 std::ranges::sentinel_t<first_base> end_;
1181
1182 public:
1183 sentinel() = default;
1184 sentinel(std::ranges::sentinel_t<first_base> end) : end_(std::move(end)) {}
1185
1186 //const-converting constructor
1187 constexpr sentinel(sentinel<!Const> other) requires Const &&
1188 (std::convertible_to<std::ranges::sentinel_t<first_base>, std::ranges::sentinel_t<const first_base>>)
1189 : end_(std::move(other.end_)) {
1190 }
1191
1192 template <bool>
1193 friend class cursor;
1194 };
1195
1196 template <bool Const>
1197 class cursor {
1198 template<class T>
1199 using constify = std::conditional_t<Const, const T, T>;
1200
1201 template<class T>
1202 using intify = std::conditional_t<true, std::int64_t, T>;
1203 // Instead of storing a pointer to the views, we'll store the sentinels:
1204 // constify<std::tuple<Vs...>>* bases_;
1205 std::tuple<std::ranges::iterator_t<constify<Vs>>...> currents_{};
1206 std::tuple<std::ranges::iterator_t<constify<Vs>>...> begins_{};
1207 std::tuple<std::ranges::sentinel_t<constify<Vs>>...> ends_{};
1208 std::tuple<intify<Vs>...> counts_{};
1209 std::int64_t idx_;
1210
1211 public:
1213 std::tuple<std::ranges::range_reference_t<constify<Vs>>...>;
1215 std::tuple<std::ranges::range_value_t<constify<Vs>>...>;
1216
1217 using difference_type = std::ptrdiff_t;
1218
1219 cursor() = default;
1220 constexpr explicit cursor(constify<std::tuple<Vs...>>* bases)
1221 : currents_( tl::tuple_transform(std::ranges::begin, *bases) )
1222 , begins_( currents_ )
1223 , ends_( tl::tuple_transform(std::ranges::end, *bases) )
1224 , counts_( tl::tuple_transform(std::ranges::size, *bases) )
1225 , idx_(0)
1226 {}
1227
1228 //If the underlying ranges are common, we can get to the end by assigning from end
1229 constexpr explicit cursor(as_sentinel_t, constify<std::tuple<Vs...>>* bases)
1230 requires(std::ranges::common_range<Vs> && ...)
1231 : cursor{ bases }
1232 {
1233 std::get<0>(currents_) = std::get<0>(ends_);
1234 }
1235
1236 //If the underlying ranges are sized and random access, we can get to the end by moving it forward by size
1237 constexpr explicit cursor(as_sentinel_t, constify<std::tuple<Vs...>>* bases)
1238 requires(!(std::ranges::common_range<Vs> && ...) && (std::ranges::random_access_range<Vs> && ...) && (std::ranges::sized_range<Vs> && ...))
1239 : cursor{ bases }
1240 {
1241 std::get<0>(currents_) += std::ranges::size(std::ranges::subrange(std::get<0>(begins_), std::get<0>(ends_)));
1242 }
1243
1244 //const-converting constructor
1245 constexpr cursor(cursor<!Const> i) requires Const && (std::convertible_to<
1246 std::ranges::iterator_t<Vs>,
1247 std::ranges::iterator_t<constify<Vs>>> && ...)
1248 : currents_{ std::move(i.currents_) } {}
1249
1250
1251 constexpr decltype(auto) read() const {
1252 return tuple_transform([](auto& i) -> decltype(auto) { return *i; }, currents_);
1253 }
1254
1255 template <std::size_t N = (sizeof...(Vs) - 1)>
1256 void update(int idx) {
1257 if constexpr(N == 0)
1258 std::get<N>(currents_) = idx + std::get<N>(begins_);
1259 else
1260 std::get<N>(currents_) = idx % std::get<N>(counts_) + std::get<N>(begins_);
1261 if constexpr (N > 0) {
1262 idx /= std::get<N>(counts_);
1263 update<N-1>(idx);
1264 }
1265 }
1266
1267 //Increment the iterator at std::get<N>(currents_)
1268 //If that iterator hits its end, recurse to std::get<N-1>
1269 void next() {
1270 advance(1);
1271 }
1272
1273 //Decrement the iterator at std::get<N>(currents_)
1274 //If that iterator was at its begin, cycle it to end and recurse to std::get<N-1>
1275 void prev() requires (am_bidirectional<constify<Vs>...>) {
1276 advance(-1);
1277 }
1278
1279 void advance(difference_type n) requires (am_random_access<constify<Vs>...>) {
1280 idx_ += n;
1281 update(idx_);
1282 }
1283
1284 constexpr bool equal(const cursor& rhs) const
1285#if !defined(__NVCOMPILER)
1286 requires (std::equality_comparable<std::ranges::iterator_t<constify<Vs>>> && ...)
1287#endif
1288 {
1289 return currents_ == rhs.currents_;
1290 }
1291
1292 constexpr bool equal(const sentinel<Const>& s) const {
1293 return std::get<0>(currents_) == s.end_;
1294 }
1295
1296 template <std::size_t N = (sizeof...(Vs) - 1)>
1297 constexpr auto distance_to(cursor const& other) const
1298 requires (am_distanceable<constify<Vs>...>) {
1299 if constexpr (N == 0) {
1300 return std::ranges::distance(std::get<0>(currents_), std::get<0>(other.currents_));
1301 }
1302 else {
1303 auto distance = distance_to<N - 1>(other);
1304 auto scale = std::ranges::distance(std::get<N>(begins_), std::get<N>(ends_));
1305 auto diff = std::ranges::distance(std::get<N>(currents_), std::get<N>(other.currents_));
1306#if !defined(__NVCOMPILER)
1307 return difference_type{ distance * scale + diff };
1308#else
1309 return static_cast<difference_type>(distance * scale + diff);
1310#endif
1311 }
1312 }
1313
1314 friend class cursor<!Const>;
1315 };
1316
1317 public:
1319 explicit cartesian_product_view(Vs... bases) : bases_(std::move(bases)...) {}
1320
1321 constexpr auto begin() requires (!(simple_view<Vs> && ...)) {
1322 return basic_iterator{ cursor<false>(std::addressof(bases_)) };
1323 }
1324 constexpr auto begin() const requires (std::ranges::range<const Vs> && ...) {
1325 return basic_iterator{ cursor<true>(std::addressof(bases_)) };
1326 }
1327
1328 constexpr auto end() requires (!(simple_view<Vs> && ...) && am_common<Vs...>) {
1329 return basic_iterator{ cursor<false>(as_sentinel, std::addressof(bases_)) };
1330 }
1331
1332 constexpr auto end() const requires (am_common<const Vs...>) {
1333 return basic_iterator{ cursor<true>(as_sentinel, std::addressof(bases_)) };
1334 }
1335
1336 constexpr auto end() requires(!(simple_view<Vs> && ...) && !am_common<Vs...>) {
1337 return sentinel<false>(std::ranges::end(std::get<0>(bases_)));
1338 }
1339
1340 constexpr auto end() const requires ((std::ranges::range<const Vs> && ...) && !am_common<const Vs...>) {
1341 return sentinel<true>(std::ranges::end(std::get<0>(bases_)));
1342 }
1343
1344 constexpr auto size() requires (am_sized<Vs...>) {
1345 //Multiply all the sizes together, returning the common type of all of them
1346 return std::apply([](auto&&... bases) {
1347 using size_type = std::common_type_t<std::ranges::range_size_t<decltype(bases)>...>;
1348 return (static_cast<size_type>(std::ranges::size(bases)) * ...);
1349 }, bases_);
1350 }
1351
1352 constexpr auto size() const requires (am_sized<const Vs...>) {
1353 return std::apply([](auto&&... bases) {
1354 using size_type = std::common_type_t<std::ranges::range_size_t<decltype(bases)>...>;
1355 return (static_cast<size_type>(std::ranges::size(bases)) * ...);
1356 }, bases_);
1357 }
1358 };
1359
1360#ifndef DISABLE_CART_PROD_IOTA_SPEC
1361 template <typename W, typename B>
1362 requires std::is_integral_v<W> && std::is_integral_v<B>
1364 std::ranges::iota_view<W, B>,
1365 std::ranges::iota_view<W, B>,
1366 std::ranges::iota_view<W, B>
1367 >
1368 : public std::ranges::view_interface<
1369 cartesian_product_view<
1370 std::ranges::iota_view<W, B>,
1371 std::ranges::iota_view<W, B>,
1372 std::ranges::iota_view<W, B>
1373 >
1374 > {
1375
1377 std::ranges::iota_view<W, B>,
1378 std::ranges::iota_view<W, B>,
1379 std::ranges::iota_view<W, B>
1380 >;
1381 public:
1382 W x_b, y_b, z_b;
1383 B nx, ny, nz;
1384 private:
1385
1386 template <bool Const>
1387 class cursor;
1388
1389 template <bool Const>
1390 class cursor {
1391 W x_i, y_i, z_i, x_0, y_0, z_0;
1392 B nx, ny, nz;
1393
1394 public:
1395 using reference = std::tuple<W const&, W const&, W const&>;
1396 using value_type = std::tuple<W, W, W>;
1397
1398 using difference_type = std::ptrdiff_t;
1399
1400 cursor() = default;
1401 constexpr explicit cursor(
1402 W x,
1403 W y,
1404 W z,
1405 B nx,
1406 B ny,
1407 B nz)
1408 :
1409 x_i(x),
1410 y_i(y),
1411 z_i(z),
1412 x_0(x),
1413 y_0(y),
1414 z_0(z),
1415 nx(nx),
1416 ny(ny),
1417 nz(nz)
1418 {}
1419
1420 constexpr decltype(auto) read() const {
1421 return std::make_tuple<W const&, W const&, W const&>(x_i, y_i, z_i);
1422 }
1423 constexpr auto linear() const {
1424 return (z_i - z_0) + nz * (y_i - y_0) + nz * ny * (x_i - x_0);
1425 }
1426
1428 auto idx = linear() + n;
1429 x_i = idx / (nz * ny) + x_0;
1430 y_i = (idx / ny) % nz + y_0;
1431 z_i = idx % nz + z_0;
1432 }
1433 void next() {
1434 //advance(1);
1435 ++z_i;
1436 if (z_i == (z_0 + nz)) {
1437 z_i = z_0;
1438 ++y_i;
1439 if (y_i == (y_0 + ny)) {
1440 y_i = y_0;
1441 ++x_i;
1442 }
1443 }
1444 }
1445 void prev(){
1446 advance(-1);
1447 }
1448
1449 constexpr bool equal(const cursor& rhs) const {
1450 return x_i == rhs.x_i && y_i == rhs.y_i && z_i == rhs.z_i;
1451 }
1452
1453 constexpr auto distance_to(cursor const& other) const {
1454 auto idx = linear();
1455 auto oidx = other.linear();
1456 return static_cast<difference_type>(oidx) - static_cast<difference_type>(idx);
1457 }
1458
1459 friend class cursor<!Const>;
1460 };
1461
1462 public:
1464 constexpr explicit cartesian_product_view(
1465 std::ranges::iota_view<W, B> xs,
1466 std::ranges::iota_view<W, B> ys,
1467 std::ranges::iota_view<W, B> zs
1468 ) : x_b(*std::ranges::begin(xs))
1469 , y_b(*std::ranges::begin(ys))
1470 , z_b(*std::ranges::begin(zs))
1471 , nx(std::ranges::size(xs))
1472 , ny(std::ranges::size(ys))
1473 , nz(std::ranges::size(zs))
1474 {}
1475
1476 constexpr auto begin() {
1477 return basic_iterator{ cursor<false>(x_b, y_b, z_b, nx, ny, nz) };
1478 }
1479 constexpr auto begin() const {
1480 return basic_iterator{ cursor<true>(x_b, y_b, z_b, nx, ny, nz) };
1481 }
1482
1483 constexpr auto size() {
1484 return nx * ny * nz;
1485 }
1486
1487 constexpr auto size() const {
1488 return nx * ny * nz;
1489 }
1490
1491 constexpr auto end() {
1492 return begin() + size();
1493 }
1494
1495 constexpr auto end() const {
1496 return begin() + size();
1497 }
1498 };
1499
1500 template <typename W, typename B>
1501 requires std::is_integral_v<W> && std::is_integral_v<B>
1503 std::ranges::iota_view<W, B>,
1504 std::ranges::iota_view<W, B>
1505 >
1506 : public std::ranges::view_interface<
1507 cartesian_product_view<
1508 std::ranges::iota_view<W, B>,
1509 std::ranges::iota_view<W, B>
1510 >
1511 > {
1512
1514 std::ranges::iota_view<W, B>,
1515 std::ranges::iota_view<W, B>
1516 >;
1517 public:
1518 W x_b, y_b;
1519 B nx, ny;
1520 private:
1521
1522 template <bool Const>
1523 class cursor;
1524
1525 template <bool Const>
1526 class cursor {
1527 W x_i, y_i, x_0, y_0;
1528 B nx, ny;
1529
1530 public:
1531 using reference = std::tuple<W const&, W const&>;
1532 using value_type = std::tuple<W, W>;
1533
1534 using difference_type = std::ptrdiff_t;
1535
1536 cursor() = default;
1537 constexpr explicit cursor(
1538 W x,
1539 W y,
1540 B nx,
1541 B ny)
1542 :
1543 x_i(x),
1544 y_i(y),
1545 x_0(x),
1546 y_0(y),
1547 nx(nx),
1548 ny(ny)
1549 {}
1550
1551 constexpr decltype(auto) read() const {
1552 return std::make_tuple<W const&, W const&>(x_i, y_i);
1553 }
1554 constexpr auto linear() const {
1555 return (y_i - y_0) + ny * (x_i - x_0);
1556 }
1557
1559 auto idx = linear() + n;
1560 x_i = idx / ny + x_0;
1561 y_i = idx % ny + y_0;
1562 }
1563 void next() {
1564 //advance(1);
1565 ++y_i;
1566 if (y_i == (y_0 + ny)) {
1567 y_i = y_0;
1568 ++x_i;
1569 }
1570 }
1571 void prev(){
1572 advance(-1);
1573 }
1574
1575 constexpr bool equal(const cursor& rhs) const {
1576 return x_i == rhs.x_i && y_i == rhs.y_i;
1577 }
1578
1579 constexpr auto distance_to(cursor const& other) const {
1580 auto idx = linear();
1581 auto oidx = other.linear();
1582 return static_cast<difference_type>(oidx) - static_cast<difference_type>(idx);
1583 }
1584
1585 friend class cursor<!Const>;
1586 };
1587
1588 public:
1590 constexpr explicit cartesian_product_view(
1591 std::ranges::iota_view<W, B> xs,
1592 std::ranges::iota_view<W, B> ys
1593 ) : x_b(*std::ranges::begin(xs))
1594 , y_b(*std::ranges::begin(ys))
1595 , nx(std::ranges::size(xs))
1596 , ny(std::ranges::size(ys))
1597 {}
1598
1599 constexpr auto begin() {
1600 return basic_iterator{ cursor<false>(x_b, y_b, nx, ny) };
1601 }
1602 constexpr auto begin() const {
1603 return basic_iterator{ cursor<true>(x_b, y_b, nx, ny) };
1604 }
1605
1606 constexpr auto size() {
1607 return nx * ny;
1608 }
1609
1610 constexpr auto size() const {
1611 return nx * ny;
1612 }
1613
1614 constexpr auto end() {
1615 return begin() + size();
1616 }
1617
1618 constexpr auto end() const {
1619 return begin() + size();
1620 }
1621
1622 };
1623
1624 template <typename W, typename B>
1625 requires std::is_integral_v<W> && std::is_integral_v<B>
1627 std::ranges::iota_view<W, B>
1628 >
1629 : public std::ranges::view_interface<
1630 cartesian_product_view<
1631 std::ranges::iota_view<W, B>
1632 >
1633 > {
1634
1635 int x_i, x_e;
1636
1637 template <bool Const>
1638 class cursor;
1639
1640 template <bool Const>
1641 class cursor {
1642 long x_i, x_e;
1643
1644 public:
1645 using reference = std::tuple<int const&>;
1646 using value_type = std::tuple<int>;
1647
1648 using difference_type = std::ptrdiff_t;
1649
1650 cursor() = default;
1651 constexpr explicit cursor(
1652 int x, int x_e)
1653 :
1654 x_i(x),
1655 x_e(x_e)
1656 {}
1657
1658 constexpr decltype(auto) read() const {
1659 return std::make_tuple<int const&>(x_i);
1660 }
1662 x_i += n;
1663 }
1664 void next() {
1665 advance(1);
1666 }
1667 void prev(){
1668 advance(-1);
1669 }
1670
1671 constexpr bool equal(const cursor& rhs) const {
1672 return x_i == rhs.x_i;
1673 }
1674
1675 constexpr auto distance_to(cursor const& other) const {
1676 return static_cast<difference_type>(other.x_i - x_i);
1677 }
1678
1679 friend class cursor<!Const>;
1680 };
1681
1682 public:
1684 constexpr explicit cartesian_product_view(
1685 std::ranges::iota_view<W, B> xs
1686 ) : x_i(*std::ranges::begin(xs))
1687 , x_e(*std::ranges::begin(xs) + std::ranges::size(xs))
1688 {}
1689
1690 constexpr auto begin() {
1691 return basic_iterator{ cursor<false>(x_i, x_e) };
1692 }
1693 constexpr auto begin() const {
1694 return basic_iterator{ cursor<true>(x_i, x_e) };
1695 }
1696
1697 constexpr auto size() {
1698 return x_e - x_i;
1699 }
1700
1701 constexpr auto size() const {
1702 return x_e - x_i;
1703 }
1704
1705 constexpr auto end() {
1706 return begin() + size();
1707 }
1708
1709 constexpr auto end() const {
1710 return begin() + size();
1711 }
1712 };
1713#endif // DISABLE_CART_PROD_IOTA_SPEC
1714
1715 template <class... Rs>
1716 cartesian_product_view(Rs&&...)->cartesian_product_view<std::views::all_t<Rs>...>;
1717
1718 namespace views {
1719 namespace detail {
1721 public:
1722 constexpr std::ranges::empty_view<std::tuple<>> operator()() const noexcept {
1723 return {};
1724 }
1725#ifndef DISABLE_CART_PROD_IOTA_SPEC
1726 template <typename W, typename B>
1727 constexpr auto operator()(
1728 std::ranges::iota_view<W, B> xs
1729 ) const {
1731 std::ranges::iota_view<W, B>
1732 >{ std::move(xs) };
1733 }
1734
1735 template <typename W, typename B>
1736 constexpr auto operator()(
1737 std::ranges::iota_view<W, B> xs,
1738 std::ranges::iota_view<W, B> ys
1739 ) const {
1741 std::ranges::iota_view<W, B>,
1742 std::ranges::iota_view<W, B>
1743 >{ std::move(xs), std::move(ys) };
1744 }
1745 template <typename W, typename B>
1746 constexpr auto operator()(
1747 std::ranges::iota_view<W, B> xs,
1748 std::ranges::iota_view<W, B> ys,
1749 std::ranges::iota_view<W, B> zs
1750 ) const {
1752 std::ranges::iota_view<W, B>,
1753 std::ranges::iota_view<W, B>,
1754 std::ranges::iota_view<W, B>
1755 >{ std::move(xs), std::move(ys), std::move(zs) };
1756 }
1757
1758#endif
1759 template <std::ranges::viewable_range... V>
1760 requires ((std::ranges::forward_range<V> && ...) && (sizeof...(V) != 0))
1761 constexpr auto operator()(V&&... vs) const {
1762 return tl::cartesian_product_view{ std::views::all(std::forward<V>(vs))... };
1763 }
1764 };
1765 } // namespace detail
1766
1768 } // namespace views
1769
1770} // namespace tl
1771
1773// Strided View
1774
1775namespace tl {
1776
1777template <std::ranges::forward_range V>
1778 requires std::ranges::view<V> class stride_view
1779 : public std::ranges::view_interface<stride_view<V>> {
1780 private:
1781 //Cannot be common for non-sized bidirectional ranges because calculating the predecessor to end would be O(n).
1782 template <class T>
1783 static constexpr bool am_common = std::ranges::common_range<T> &&
1784 (!std::ranges::bidirectional_range<T> || std::ranges::sized_range<T>);
1785
1786 template <class T> static constexpr bool am_sized = std::ranges::sized_range<T>;
1787
1789 std::ranges::range_difference_t<V> stride_size_;
1790
1791 //The cursor for stride_view may need to keep track of additional state.
1792 //Consider the case where you have a vector of 0,1,2 and you stride with a size of 2.
1793 //If you take the end iterator for the stride_view and decrement it, then you should end up at 2, not at 1.
1794 //As such, there's additional work required to track where to decrement to in the case that the underlying range is bidirectional.
1795 //This is handled by a bunch of base classes for the cursor.
1796
1797 //Case when underlying range is not bidirectional, i.e. we don't care about calculating offsets
1798 template <bool Const, class Base = std::conditional_t<Const, const V, V>, bool = std::ranges::bidirectional_range<Base>, bool = std::ranges::sized_range<Base>>
1800 cursor_base() = default;
1801 constexpr explicit cursor_base(std::ranges::range_difference_t<Base> offs) {}
1802
1803 //These are both no-ops
1804 void set_offset(std::ranges::range_difference_t<Base> off) {}
1805 std::ranges::range_difference_t<Base> get_offset() {
1806 return 0;
1807 }
1808 };
1809
1810 //Case when underlying range is bidirectional but not sized. We need to keep track of the offset if we hit the end iterator.
1811 template <bool Const, class Base>
1812 struct cursor_base<Const, Base, true, false> {
1813 using difference_type = std::ranges::range_difference_t<Base>;
1815
1816 cursor_base() = default;
1817 constexpr explicit cursor_base(difference_type offset)
1818 : offset_{ offset } {}
1819
1821 offset_ = off;
1822 }
1823
1825 return offset_;
1826 }
1827 };
1828
1829 //Case where underlying is bidirectional and sized. We can calculate offsets from the end on construction.
1830 template <bool Const, class Base>
1831 struct cursor_base<Const, Base, true, true> {
1832 using difference_type = std::ranges::range_difference_t<Base>;
1834
1835 cursor_base() = default;
1836 constexpr explicit cursor_base(difference_type offset)
1837 : offset_{ offset } {}
1838
1839 //No-op because we're precomputing the offset
1840 void set_offset(std::ranges::range_difference_t<Base>) {}
1841
1842 std::ranges::range_difference_t<Base> get_offset() {
1843 return offset_;
1844 }
1845 };
1846
1847 template <bool Const>
1848 struct cursor : cursor_base<Const> {
1849 template <class T>
1850 using constify = std::conditional_t<Const, const T, T>;
1852
1853 using difference_type = std::ranges::range_difference_t<Base>;
1854
1855 std::ranges::iterator_t<Base> current_{};
1856 std::ranges::sentinel_t<Base> end_{};
1857 std::ranges::range_difference_t<Base> stride_size_{};
1858
1859 cursor() = default;
1860
1861 //Pre-calculate the offset for sized ranges
1862 constexpr cursor(std::ranges::iterator_t<Base> begin, Base* base, std::ranges::range_difference_t<Base> stride_size)
1863 requires(std::ranges::sized_range<Base>)
1864 : cursor_base<Const>(stride_size - (std::ranges::size(*base) % stride_size)),
1865 current_(std::move(begin)), end_(std::ranges::end(*base)), stride_size_(stride_size) {}
1866
1867 constexpr cursor(std::ranges::iterator_t<Base> begin, Base* base, std::ranges::range_difference_t<Base> stride_size)
1868 requires(!std::ranges::sized_range<Base>)
1869 : cursor_base<Const>(), current_(std::move(begin)), end_(std::ranges::end(*base)), stride_size_(stride_size) {}
1870
1871 //const-converting constructor
1872 constexpr cursor(cursor<!Const> i) requires Const&& std::convertible_to<
1873 std::ranges::iterator_t<V>,
1874 std::ranges::iterator_t<const V>>
1875 : cursor_base<Const>(i.get_offset()) {}
1876
1877 constexpr decltype(auto) read() const {
1878 return *current_;
1879 }
1880
1881 constexpr void next() {
1882 auto delta = std::ranges::advance(current_, stride_size_, end_);
1883 //This will track the amount we actually moved by last advance,
1884 //which will be less than the stride size if range_size % stride_size != 0
1885 this->set_offset(delta);
1886 }
1887
1888 constexpr void prev() requires std::ranges::bidirectional_range<Base> {
1889 auto delta = -stride_size_;
1890 //If we're at the end we may need to offset the amount to move back by
1891 if (current_ == end_) {
1892 delta += this->get_offset();
1893 }
1894 std::advance(current_, delta);
1895 }
1896
1897 constexpr void advance(difference_type x)
1898 requires std::ranges::random_access_range<Base> {
1899 if (x == 0) return;
1900
1901 x *= stride_size_;
1902
1903 if (x > 0) {
1904 auto delta = std::ranges::advance(current_, x, end_); // TODO: submit PR with this bugfix
1905 this->set_offset(delta);
1906 }
1907 else if (x < 0) {
1908 if (current_ == end_) {
1909 x += this->get_offset();
1910 }
1911 std::advance(this->current_, x); // TODO: submit PR with this bugfix
1912 }
1913 }
1914
1915 // TODO: submit PR with distance_to
1916 constexpr auto distance_to(cursor const& other) const
1917 // am_distanceable<V>:
1918 requires (std::sized_sentinel_for<std::ranges::iterator_t<V>, std::ranges::iterator_t<V>>)
1919 && std::ranges::sized_range<V>
1920 {
1921 auto delta = std::ranges::distance(this->current_, other.current_);
1922 if (delta < 0)
1923 delta -= stride_size_ - 1;
1924 else
1925 delta += stride_size_ -1;
1926 return delta / stride_size_;
1927 }
1928
1929 constexpr bool equal(cursor const& rhs) const {
1930 return current_ == rhs.current_;
1931 }
1932 constexpr bool equal(basic_sentinel<V, Const> const& rhs) const {
1933 return current_ == rhs.end_;
1934 }
1935
1936 friend struct cursor<!Const>;
1937 };
1938
1939 public:
1940 stride_view() = default;
1941 stride_view(V v, std::ranges::range_difference_t<V> n) : base_(std::move(v)), stride_size_(n) {}
1942
1943 constexpr auto begin() requires (!simple_view<V>) {
1944 return basic_iterator{ cursor<false>{ std::ranges::begin(base_), std::addressof(base_), stride_size_ } };
1945 }
1946
1947 constexpr auto begin() const requires (std::ranges::range<const V>) {
1948 return basic_iterator{ cursor<true>{ std::ranges::begin(base_), std::addressof(base_), stride_size_ } };
1949 }
1950
1951 constexpr auto end() requires (!simple_view<V>&& am_common<V>) {
1952 return basic_iterator{ cursor<false>(std::ranges::end(base_), std::addressof(base_), stride_size_) };
1953 }
1954
1955 constexpr auto end() const requires (std::ranges::range<const V>&& am_common<const V>) {
1956 return basic_iterator{ cursor<true>(std::ranges::end(base_), std::addressof(base_), stride_size_) };
1957 }
1958
1959 constexpr auto end() requires (!simple_view<V> && !am_common<V>) {
1960 return basic_sentinel<V, false>(std::ranges::end(base_));
1961 }
1962
1963 constexpr auto end() const requires (std::ranges::range<const V> && !am_common<const V>) {
1964 return basic_sentinel<V, true>(std::ranges::end(base_));
1965 }
1966
1967 constexpr auto size() requires (am_sized<V>) {
1968 return (std::ranges::size(base_) + stride_size_ - 1) / stride_size_;
1969 }
1970
1971 constexpr auto size() const requires (am_sized<const V>) {
1972 return (std::ranges::size(base_) + stride_size_ - 1) / stride_size_;
1973 }
1974
1975 auto& base() {
1976 return base_;
1977 }
1978
1979 auto const& base() const {
1980 return base_;
1981 }
1982 };
1983
1984 template <class R, class N>
1985 stride_view(R&&, N n)->stride_view<std::views::all_t<R>>;
1986
1987 namespace views {
1988 namespace detail {
1990 template <std::ranges::viewable_range R>
1991 constexpr auto operator()(R&& r, std::ranges::range_difference_t<R> n) const
1992 requires std::ranges::forward_range<R> {
1993 return stride_view(std::forward<R>(r), n);
1994 }
1995 };
1996
1998 using stride_fn_base::operator();
1999
2000 template <std::integral N>
2001 constexpr auto operator()(N n) const {
2002 return pipeable(bind_back(stride_fn_base{}, n));
2003 }
2004 };
2005 }
2006
2007 constexpr inline detail::stride_fn stride;
2008 }
2009}
2010
2011namespace std::ranges {
2012 template <class R>
2013 inline constexpr bool enable_borrowed_range<tl::stride_view<R>> = enable_borrowed_range<R>;
2014}
2015
2017// std::ranges::views re-export:
2018
2019#if __cplusplus <= 202002L
2020namespace std {
2021 namespace ranges {
2023 namespace views {
2026 } // namespace views
2027 } // namespace ranges
2028} // namespace std
2029#endif
And from this we can write down f$ nabla phi_i nabla phi_i dx but since we are constructing matrix elements
friend constexpr bool operator<(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(y - x))
basic_iterator()=default
constexpr basic_iterator operator++(int) &noexcept(std::is_nothrow_copy_constructible_v< C > &&std::is_nothrow_move_constructible_v< C > &&noexcept(++std::declval< basic_iterator & >()))
constexpr basic_iterator & operator=(basic_iterator< O > &&that) &noexcept(std::is_nothrow_assignable< C &, O && >::value)
reference_t const_reference_t
constexpr basic_iterator & operator*() noexcept
constexpr basic_iterator(C &&c) noexcept(std::is_nothrow_constructible_v< mixin, C && >)
constexpr basic_iterator(basic_iterator< O > &&that) noexcept(std::is_nothrow_constructible< mixin, O && >::value)
constexpr auto & cursor() noexcept
constexpr basic_iterator & operator=(const basic_iterator< O > &that) &noexcept(std::is_nothrow_assignable< C &, const O & >::value)
friend constexpr bool operator>=(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(y - x))
friend constexpr difference_type operator-(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(y.cursor().distance_to(x.cursor())))
constexpr auto const & cursor() const noexcept
constexpr void operator++(int) &noexcept(noexcept(++std::declval< basic_iterator & >()))
constexpr basic_iterator & operator-=(difference_type n) &noexcept(noexcept(cursor().advance(-n)))
friend constexpr bool operator==(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(x.cursor().equal(y.cursor())))
constexpr basic_iterator(C const &c) noexcept(std::is_nothrow_constructible_v< mixin, C const & >)
constexpr basic_iterator & operator--() &noexcept(noexcept(cursor().prev()))
constexpr basic_iterator & operator++() &noexcept
constexpr basic_iterator & operator+=(difference_type n) &noexcept(noexcept(cursor().advance(n)))
constexpr basic_iterator(const basic_iterator< O > &that) noexcept(std::is_nothrow_constructible< mixin, const O & >::value)
constexpr basic_iterator operator--(int) &noexcept(std::is_nothrow_copy_constructible< basic_iterator >::value &&std::is_nothrow_move_constructible< basic_iterator >::value &&noexcept(--std::declval< basic_iterator & >()))
decltype(std::declval< C >().read()) reference_t
cursor::reference_t< C > reference
cursor::value_type_t< C > value_type
constexpr auto operator++(int) &noexcept(noexcept(++std::declval< basic_iterator & >()) &&std::is_nothrow_move_constructible_v< value_type > &&std::is_nothrow_constructible_v< value_type, reference >)
friend constexpr bool operator>(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(y - x))
cursor::difference_type_t< C > difference_type
cursor::mixin_t< C > mixin
friend constexpr bool operator<=(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(y - x))
constexpr basic_iterator & operator++() &noexcept(noexcept(std::declval< basic_iterator >().cursor().next()))
friend constexpr bool operator!=(const basic_iterator &x, const basic_iterator &y) noexcept(noexcept(!(x==y)))
friend constexpr void iter_swap(const basic_iterator &x, const basic_iterator< O > &y) noexcept(noexcept((void) x.indirect_swap(y)))
friend constexpr decltype(auto) iter_move(const basic_iterator &i) noexcept(noexcept(i.cursor().indirect_move()))
constexpr const T && get() const &&noexcept
constexpr T && get() &&noexcept
constexpr const T & get() const &noexcept
constexpr basic_mixin(T &&t) noexcept(std::is_nothrow_move_constructible< T >::value)
constexpr T & get() &noexcept
constexpr basic_mixin() noexcept(std::is_nothrow_default_constructible< T >::value)
constexpr basic_mixin(const T &t) noexcept(std::is_nothrow_copy_constructible< T >::value)
constexpr auto end() const
basic_sentinel()=default
constexpr basic_sentinel(basic_sentinel< V, !Const > other) &&std
constexpr basic_sentinel(std::ranges::sentinel_t< Base > end)
std::conditional_t< Const, const V, V > Base
std::ranges::sentinel_t< Base > end_
std::tuple< std::ranges::iterator_t< constify< Vs > >... > currents_
constexpr cursor(as_sentinel_t, constify< std::tuple< Vs... > > *bases)
constexpr bool equal(const cursor &rhs) const
constexpr cursor(constify< std::tuple< Vs... > > *bases)
constexpr cursor(as_sentinel_t, constify< std::tuple< Vs... > > *bases)
std::tuple< intify< Vs >... > counts_
std::conditional_t< Const, const T, T > constify
std::tuple< std::ranges::range_reference_t< constify< Vs > >... > reference
std::conditional_t< true, std::int64_t, T > intify
std::tuple< std::ranges::iterator_t< constify< Vs > >... > begins_
constexpr cursor(cursor<!Const > i) &&(std
std::tuple< std::ranges::range_value_t< constify< Vs > >... > value_type
constexpr auto distance_to(cursor const &other) const
constexpr bool equal(const sentinel< Const > &s) const
std::tuple< std::ranges::sentinel_t< constify< Vs > >... > ends_
constexpr decltype(auto) read() const
constexpr sentinel(sentinel<!Const > other) &&(std
decltype(std::get< 0 >(std::declval< parent >().bases_)) first_base
std::ranges::sentinel_t< first_base > end_
std::conditional_t< Const, const cartesian_product_view, cartesian_product_view > parent
sentinel(std::ranges::sentinel_t< first_base > end)
constexpr cartesian_product_view(std::ranges::iota_view< W, B > xs, std::ranges::iota_view< W, B > ys, std::ranges::iota_view< W, B > zs)
constexpr cartesian_product_view(std::ranges::iota_view< W, B > xs, std::ranges::iota_view< W, B > ys)
constexpr cartesian_product_view(std::ranges::iota_view< W, B > xs)
constexpr auto end() const
static constexpr bool am_distanceable
static constexpr bool am_random_access
static constexpr bool am_bidirectional
static constexpr bool am_sized
constexpr auto size() const
constexpr auto begin() const
static constexpr bool am_common
constexpr auto end() const
constexpr auto begin()
constexpr auto end()
constexpr auto end() const
constexpr auto size() const
auto const & base() const
stride_view()=default
constexpr auto end() const
constexpr auto begin() const
constexpr auto end()
static constexpr bool am_common
stride_view(V v, std::ranges::range_difference_t< V > n)
std::ranges::range_difference_t< V > stride_size_
static constexpr bool am_sized
constexpr auto size()
constexpr auto operator()(std::ranges::iota_view< W, B > xs, std::ranges::iota_view< W, B > ys, std::ranges::iota_view< W, B > zs) const
constexpr std::ranges::empty_view< std::tuple<> > operator()() const noexcept
constexpr auto operator()(std::ranges::iota_view< W, B > xs) const
constexpr auto operator()(std::ranges::iota_view< W, B > xs, std::ranges::iota_view< W, B > ys) const
examples::exahype2::elastic::VariableShortcuts s
Definition loh.cpp:10
double f(const tarch::la::Vector< Dimensions, double > &x)
constexpr tl::views::detail::cartesian_product_fn cartesian_product
constexpr tl::views::detail::stride_fn stride
STL namespace.
decltype(cpp20_iterator_category< C >()) cpp20_iterator_category_t
decltype(cpp17_iterator_category< C >()) cpp17_iterator_category_t
typename detail::deduced_mixin_t< C >::type mixin_t
constexpr auto cpp17_iterator_category()
constexpr auto cpp20_iterator_category()
constexpr bool tagged_contiguous
typename detail::deduced_value_t< C >::type value_type_t
typename detail::deduced_difference_t< C >::type difference_type_t
decltype(std::declval< const C & >().read()) reference_t
constexpr bool single_pass
constexpr auto common_iterator_category()
constexpr auto tuple_fold_impl(F, V v)
constexpr auto repeat_into_impl(std::index_sequence< Idx... >) -> Into< typename decltype((Idx, std::type_identity< T >{}))::type... >
decltype(tl::meta::detail::repeat_into_impl< T, Into >(std::make_index_sequence< N >{})) repeat_into
constexpr detail::cartesian_product_fn cartesian_product
constexpr detail::stride_fn stride
constexpr auto tuple_zip(Tuples &&... tuples)
constexpr auto operator|(V &&v, Pipe &&fn)
constexpr as_sentinel_t as_sentinel
constexpr std::size_t tuple_size
typename detail::tuple_or_pair_impl< Ts... >::type tuple_or_pair
stride_view(R &&, N n) -> stride_view< std::views::all_t< R > >
constexpr auto bind_back(F &&f, Args &&... args)
constexpr auto min_tuple(Tuple &&tuple)
constexpr auto tuple_for_each(F &&f, Tuple &&tuple)
constexpr auto pipeable(F f)
std::integral_constant< std::size_t, N > index_constant
constexpr auto compose(F &&f, G &&g)
constexpr auto tuple_pop_front(Tuple &&tuple)
constexpr bool operator>(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept((lhs - rhs) > 0))
constexpr bool operator==(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept(lhs.get().equal(rhs.get())))
cartesian_product_view(Rs &&...) -> cartesian_product_view< std::views::all_t< Rs >... >
constexpr bool operator<(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept(lhs - rhs< 0))
constexpr bool operator!=(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept(!(lhs==rhs)))
decltype(detail::common_iterator_category< V... >()) common_iterator_category
constexpr bool operator>=(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept((lhs - rhs) >=0))
std::conditional_t< Const, const T, T > maybe_const
constexpr basic_iterator< C > operator+(const basic_iterator< C > &i, cursor::difference_type_t< C > n) noexcept(std::is_nothrow_copy_constructible< basic_iterator< C > >::value &&std::is_nothrow_move_constructible< basic_iterator< C > >::value &&noexcept(std::declval< basic_iterator< C > & >()+=n))
constexpr auto tuple_fold(Tuple tuple, T t, F f)
constexpr basic_iterator< C > operator-(const basic_iterator< C > &i, cursor::difference_type_t< C > n) noexcept(noexcept(i+(-n)))
constexpr auto tuple_transform(F &&f, Tuples &&... tuples)
constexpr auto max_tuple(Tuple &&tuple)
constexpr bool operator<=(const basic_iterator< C1 > &lhs, const basic_iterator< C2 > &rhs) noexcept(noexcept((lhs - rhs)<=0))
compose_fn(A &&a, B &&b)
constexpr auto operator()(Args &&... args) const &
constexpr auto operator()(Args &&... args) &&
constexpr auto operator()(Args &&... args) const &&
static constexpr auto call(A &&a, B &&b, Args &&... args)
constexpr auto operator()(Args &&... args) &
cpp20_iterator_category_t< C > iterator_concept
cursor::value_type_t< C > value_type
cursor::difference_type_t< C > difference_type
cursor::reference_t< C > reference
static auto deduce(...) -> std::ptrdiff_t
static auto deduce(long) -> decltype(std::declval< const T & >().distance_to(std::declval< const T & >()))
static auto deduce(int) -> typename T::difference_type
static auto deduce(...) -> tl::basic_mixin< T >
static auto deduce(int) -> typename T::mixin
static auto deduce(...) -> std::decay_t< reference_t< T > >
static auto deduce(int) -> typename T::value_type
static auto deduce() -> std::false_type
static auto deduce() -> std::true_type
static auto deduce() -> std::true_type
static constexpr auto contiguous()
static constexpr auto single_pass()
static constexpr auto contiguous()
static constexpr auto single_pass()
static auto deduce(basic_iterator< T > const &) -> std::true_type
static auto deduce(...) -> std::false_type
constexpr T const & operator*() const noexcept
T< Args..., MoreArgs... > type
constexpr pipeable_fn(F f)
constexpr auto operator()(Args &&... args) const
std::ranges::range_difference_t< Base > difference_type
std::ranges::range_difference_t< Base > difference_type
std::ranges::range_difference_t< Base > get_offset()
void set_offset(std::ranges::range_difference_t< Base >)
std::ranges::range_difference_t< Base > get_offset()
constexpr cursor_base(std::ranges::range_difference_t< Base > offs)
void set_offset(std::ranges::range_difference_t< Base > off)
constexpr cursor(std::ranges::iterator_t< Base > begin, Base *base, std::ranges::range_difference_t< Base > stride_size)
std::ranges::sentinel_t< Base > end_
constexpr cursor(cursor<!Const > i) &&std
constexpr cursor(std::ranges::iterator_t< Base > begin, Base *base, std::ranges::range_difference_t< Base > stride_size)
std::ranges::iterator_t< Base > current_
std::ranges::range_difference_t< Base > difference_type
std::conditional_t< Const, const T, T > constify
constexpr auto distance_to(cursor const &other) const
constexpr bool equal(cursor const &rhs) const
constexpr decltype(auto) read() const
std::ranges::range_difference_t< Base > stride_size_
constexpr void advance(difference_type x)
constexpr auto operator()(R &&r, std::ranges::range_difference_t< R > n) const
constexpr auto operator()(N n) const