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