1065dc485SJakub Mazurkiewicz //===----------------------------------------------------------------------===//
2065dc485SJakub Mazurkiewicz //
3065dc485SJakub Mazurkiewicz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4065dc485SJakub Mazurkiewicz // See https://llvm.org/LICENSE.txt for license information.
5065dc485SJakub Mazurkiewicz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6065dc485SJakub Mazurkiewicz //
7065dc485SJakub Mazurkiewicz //===----------------------------------------------------------------------===//
8065dc485SJakub Mazurkiewicz 
9065dc485SJakub Mazurkiewicz // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10065dc485SJakub Mazurkiewicz 
11065dc485SJakub Mazurkiewicz // <ranges>
12065dc485SJakub Mazurkiewicz 
13065dc485SJakub Mazurkiewicz // constexpr iterator& operator--();
14065dc485SJakub Mazurkiewicz // constexpr iterator operator--(int);
15065dc485SJakub Mazurkiewicz 
16065dc485SJakub Mazurkiewicz #include <ranges>
17065dc485SJakub Mazurkiewicz 
18065dc485SJakub Mazurkiewicz #include <array>
19065dc485SJakub Mazurkiewicz #include <cassert>
20065dc485SJakub Mazurkiewicz #include <concepts>
21065dc485SJakub Mazurkiewicz #include <functional>
22065dc485SJakub Mazurkiewicz #include <span>
23065dc485SJakub Mazurkiewicz #include <type_traits>
24065dc485SJakub Mazurkiewicz #include <utility>
25065dc485SJakub Mazurkiewicz 
26065dc485SJakub Mazurkiewicz #include "../types.h"
27065dc485SJakub Mazurkiewicz #include "test_iterators.h"
28065dc485SJakub Mazurkiewicz #include "test_macros.h"
29065dc485SJakub Mazurkiewicz 
30065dc485SJakub Mazurkiewicz template <class T>
31065dc485SJakub Mazurkiewicz concept HasPreDecrement = requires(T t) {
32065dc485SJakub Mazurkiewicz   { --t };
33065dc485SJakub Mazurkiewicz };
34065dc485SJakub Mazurkiewicz 
35065dc485SJakub Mazurkiewicz template <class T>
36065dc485SJakub Mazurkiewicz concept HasPostDecrement = requires(T t) {
37065dc485SJakub Mazurkiewicz   { t-- };
38065dc485SJakub Mazurkiewicz };
39065dc485SJakub Mazurkiewicz 
40065dc485SJakub Mazurkiewicz struct TrackingPred : TrackInitialization {
41065dc485SJakub Mazurkiewicz   using TrackInitialization::TrackInitialization;
operator ()TrackingPred42065dc485SJakub Mazurkiewicz   constexpr bool operator()(int x, int y) const { return x <= y; }
43065dc485SJakub Mazurkiewicz };
44065dc485SJakub Mazurkiewicz 
45*8c0a6112SWill Hawkins template <class Iter, IsConst Constant, class Sent = sentinel_wrapper<Iter>>
test()46065dc485SJakub Mazurkiewicz constexpr void test() {
47*8c0a6112SWill Hawkins   using Underlying      = View<Iter, Sent>;
48065dc485SJakub Mazurkiewicz   using ChunkByView     = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
49065dc485SJakub Mazurkiewicz   using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
50065dc485SJakub Mazurkiewicz 
51065dc485SJakub Mazurkiewicz   static_assert(HasPostDecrement<ChunkByIterator>);
52065dc485SJakub Mazurkiewicz   static_assert(HasPreDecrement<ChunkByIterator>);
53065dc485SJakub Mazurkiewicz 
54f1db578fSStephan T. Lavavej   auto make_chunk_by_view = [](auto& arr) {
55*8c0a6112SWill Hawkins     View view{Iter{arr.data()}, Sent{Iter{arr.data() + arr.size()}}};
56065dc485SJakub Mazurkiewicz     return ChunkByView{std::move(view), std::ranges::less_equal{}};
57065dc485SJakub Mazurkiewicz   };
58065dc485SJakub Mazurkiewicz 
59065dc485SJakub Mazurkiewicz   // Test with a single chunk
60065dc485SJakub Mazurkiewicz   {
61065dc485SJakub Mazurkiewicz     std::array array{0, 1, 2, 3, 4};
62f1db578fSStephan T. Lavavej     ChunkByView view   = make_chunk_by_view(array);
63065dc485SJakub Mazurkiewicz     ChunkByIterator it = std::ranges::next(view.begin(), view.end());
64065dc485SJakub Mazurkiewicz 
65065dc485SJakub Mazurkiewicz     std::same_as<ChunkByIterator&> decltype(auto) result = --it;
66065dc485SJakub Mazurkiewicz     assert(&result == &it);
67f1db578fSStephan T. Lavavej     assert(base((*result).begin()) == array.data());
68065dc485SJakub Mazurkiewicz   }
69065dc485SJakub Mazurkiewicz 
70065dc485SJakub Mazurkiewicz   // Test with two chunks
71065dc485SJakub Mazurkiewicz   {
72065dc485SJakub Mazurkiewicz     std::array array{0, 1, 2, 0, 1, 2};
73f1db578fSStephan T. Lavavej     ChunkByView view   = make_chunk_by_view(array);
74065dc485SJakub Mazurkiewicz     ChunkByIterator it = std::ranges::next(view.begin(), view.end());
75065dc485SJakub Mazurkiewicz 
76065dc485SJakub Mazurkiewicz     std::same_as<ChunkByIterator&> decltype(auto) result = --it;
77065dc485SJakub Mazurkiewicz     assert(&result == &it);
78f1db578fSStephan T. Lavavej     assert(base((*result).begin()) == array.data() + 3);
79065dc485SJakub Mazurkiewicz 
80065dc485SJakub Mazurkiewicz     --it;
81f1db578fSStephan T. Lavavej     assert(base((*result).begin()) == array.data());
82065dc485SJakub Mazurkiewicz   }
83065dc485SJakub Mazurkiewicz 
84065dc485SJakub Mazurkiewicz   // Test going forward and then backward on the same iterator
85065dc485SJakub Mazurkiewicz   {
86065dc485SJakub Mazurkiewicz     std::array array{7, 8, 9, 4, 5, 6, 1, 2, 3, 0};
87f1db578fSStephan T. Lavavej     ChunkByView view   = make_chunk_by_view(array);
88065dc485SJakub Mazurkiewicz     ChunkByIterator it = view.begin();
89065dc485SJakub Mazurkiewicz 
90065dc485SJakub Mazurkiewicz     ++it;
91065dc485SJakub Mazurkiewicz     --it;
92f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data());
93f1db578fSStephan T. Lavavej     assert(base((*it).end()) == array.data() + 3);
94065dc485SJakub Mazurkiewicz 
95065dc485SJakub Mazurkiewicz     ++it;
96065dc485SJakub Mazurkiewicz     ++it;
97065dc485SJakub Mazurkiewicz     --it;
98f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 3);
99f1db578fSStephan T. Lavavej     assert(base((*it).end()) == array.data() + 6);
100065dc485SJakub Mazurkiewicz 
101065dc485SJakub Mazurkiewicz     ++it;
102065dc485SJakub Mazurkiewicz     ++it;
103065dc485SJakub Mazurkiewicz     --it;
104f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 6);
105f1db578fSStephan T. Lavavej     assert(base((*it).end()) == array.data() + 9);
106065dc485SJakub Mazurkiewicz 
107065dc485SJakub Mazurkiewicz     ++it;
108065dc485SJakub Mazurkiewicz     ++it;
109065dc485SJakub Mazurkiewicz     --it;
110f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 9);
111065dc485SJakub Mazurkiewicz   }
112065dc485SJakub Mazurkiewicz 
113065dc485SJakub Mazurkiewicz   // Decrement an iterator multiple times
114065dc485SJakub Mazurkiewicz   if constexpr (std::ranges::common_range<Underlying>) {
115065dc485SJakub Mazurkiewicz     std::array array{1, 2, 1, 2, 1};
116f1db578fSStephan T. Lavavej     ChunkByView view = make_chunk_by_view(array);
117065dc485SJakub Mazurkiewicz 
118065dc485SJakub Mazurkiewicz     ChunkByIterator it = view.end();
119065dc485SJakub Mazurkiewicz     --it;
120065dc485SJakub Mazurkiewicz     --it;
121065dc485SJakub Mazurkiewicz     --it;
122f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data());
123065dc485SJakub Mazurkiewicz   }
124065dc485SJakub Mazurkiewicz 
125065dc485SJakub Mazurkiewicz   // Test with a predicate that takes by non-const reference
126065dc485SJakub Mazurkiewicz   if constexpr (!std::to_underlying(Constant)) {
127065dc485SJakub Mazurkiewicz     std::array array{1, 2, 3, -3, -2, -1};
128*8c0a6112SWill Hawkins     View v{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}};
129065dc485SJakub Mazurkiewicz     auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });
130065dc485SJakub Mazurkiewicz 
131065dc485SJakub Mazurkiewicz     auto it = std::ranges::next(view.begin());
132f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 3);
133065dc485SJakub Mazurkiewicz     --it;
134f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data());
135065dc485SJakub Mazurkiewicz   }
136065dc485SJakub Mazurkiewicz 
137065dc485SJakub Mazurkiewicz   // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')
138065dc485SJakub Mazurkiewicz   {
139065dc485SJakub Mazurkiewicz     std::array array = {1, 2, 3, -3, -2, -1};
140*8c0a6112SWill Hawkins     auto v           = View{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}} |
141f1db578fSStephan T. Lavavej              std::views::transform([](int x) { return IntWrapper{x}; });
142065dc485SJakub Mazurkiewicz     auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);
143065dc485SJakub Mazurkiewicz 
144065dc485SJakub Mazurkiewicz     auto it = std::ranges::next(view.begin());
145f1db578fSStephan T. Lavavej     assert(base((*it).begin().base()) == array.data() + 3);
146065dc485SJakub Mazurkiewicz     --it;
147f1db578fSStephan T. Lavavej     assert(base((*it).begin().base()) == array.data());
148065dc485SJakub Mazurkiewicz   }
149065dc485SJakub Mazurkiewicz 
150065dc485SJakub Mazurkiewicz   // Make sure we do not make a copy of the predicate when we decrement
151065dc485SJakub Mazurkiewicz   if constexpr (std::ranges::common_range<Underlying>) {
152065dc485SJakub Mazurkiewicz     bool moved = false, copied = false;
153065dc485SJakub Mazurkiewicz     std::array array{1, 2, 1, 3};
154*8c0a6112SWill Hawkins     View v{Iter(array.data()), Sent(Iter(array.data() + array.size()))};
155065dc485SJakub Mazurkiewicz     auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));
156065dc485SJakub Mazurkiewicz     assert(std::exchange(moved, false));
157065dc485SJakub Mazurkiewicz     auto it = view.end();
158065dc485SJakub Mazurkiewicz     --it;
159065dc485SJakub Mazurkiewicz     it--;
160065dc485SJakub Mazurkiewicz     assert(!moved);
161065dc485SJakub Mazurkiewicz     assert(!copied);
162065dc485SJakub Mazurkiewicz   }
163065dc485SJakub Mazurkiewicz 
164065dc485SJakub Mazurkiewicz   // Check post-decrement
165065dc485SJakub Mazurkiewicz   {
166065dc485SJakub Mazurkiewicz     std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};
167f1db578fSStephan T. Lavavej     ChunkByView view   = make_chunk_by_view(array);
168065dc485SJakub Mazurkiewicz     ChunkByIterator it = std::ranges::next(view.begin(), view.end());
169065dc485SJakub Mazurkiewicz 
170065dc485SJakub Mazurkiewicz     std::same_as<ChunkByIterator> decltype(auto) result = it--;
171065dc485SJakub Mazurkiewicz     assert(result != it);
172065dc485SJakub Mazurkiewicz     assert(result == std::default_sentinel);
173f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 6);
174065dc485SJakub Mazurkiewicz 
175065dc485SJakub Mazurkiewicz     result = it--;
176f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data() + 3);
177f1db578fSStephan T. Lavavej     assert(base((*result).begin()) == array.data() + 6);
178065dc485SJakub Mazurkiewicz 
179065dc485SJakub Mazurkiewicz     result = it--;
180f1db578fSStephan T. Lavavej     assert(base((*it).begin()) == array.data());
181f1db578fSStephan T. Lavavej     assert(base((*result).begin()) == array.data() + 3);
182065dc485SJakub Mazurkiewicz   }
183065dc485SJakub Mazurkiewicz }
184065dc485SJakub Mazurkiewicz 
185065dc485SJakub Mazurkiewicz template <class Iterator, IsConst Constant>
test_with_pair()186065dc485SJakub Mazurkiewicz constexpr void test_with_pair() {
187065dc485SJakub Mazurkiewicz   // Test with pair of iterators
188065dc485SJakub Mazurkiewicz   test<Iterator, Constant>();
189065dc485SJakub Mazurkiewicz 
190065dc485SJakub Mazurkiewicz   // Test with iterator-sentinel pair
191065dc485SJakub Mazurkiewicz   test<Iterator, Constant, Iterator>();
192065dc485SJakub Mazurkiewicz }
193065dc485SJakub Mazurkiewicz 
tests()194065dc485SJakub Mazurkiewicz constexpr bool tests() {
195065dc485SJakub Mazurkiewicz   test_with_pair<bidirectional_iterator<int*>, IsConst::no>();
196065dc485SJakub Mazurkiewicz   test_with_pair<random_access_iterator<int*>, IsConst::no>();
197065dc485SJakub Mazurkiewicz   test_with_pair<contiguous_iterator<int*>, IsConst::no>();
198065dc485SJakub Mazurkiewicz   test_with_pair<int*, IsConst::no>();
199065dc485SJakub Mazurkiewicz 
200065dc485SJakub Mazurkiewicz   test_with_pair<bidirectional_iterator<int const*>, IsConst::yes>();
201065dc485SJakub Mazurkiewicz   test_with_pair<random_access_iterator<int const*>, IsConst::yes>();
202065dc485SJakub Mazurkiewicz   test_with_pair<contiguous_iterator<int const*>, IsConst::yes>();
203065dc485SJakub Mazurkiewicz   test_with_pair<int const*, IsConst::yes>();
204065dc485SJakub Mazurkiewicz 
205065dc485SJakub Mazurkiewicz   // Make sure `operator--` isn't provided for non bidirectional ranges
206065dc485SJakub Mazurkiewicz   {
207065dc485SJakub Mazurkiewicz     using ForwardView = View<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>;
208065dc485SJakub Mazurkiewicz     using ChunkByView = std::ranges::chunk_by_view<ForwardView, std::ranges::less_equal>;
209065dc485SJakub Mazurkiewicz     static_assert(!HasPreDecrement<std::ranges::iterator_t<ChunkByView>>);
210065dc485SJakub Mazurkiewicz     static_assert(!HasPostDecrement<std::ranges::iterator_t<ChunkByView>>);
211065dc485SJakub Mazurkiewicz   }
212065dc485SJakub Mazurkiewicz 
213065dc485SJakub Mazurkiewicz   return true;
214065dc485SJakub Mazurkiewicz }
215065dc485SJakub Mazurkiewicz 
main(int,char **)216065dc485SJakub Mazurkiewicz int main(int, char**) {
217065dc485SJakub Mazurkiewicz   tests();
218065dc485SJakub Mazurkiewicz   static_assert(tests());
219065dc485SJakub Mazurkiewicz 
220065dc485SJakub Mazurkiewicz   return 0;
221065dc485SJakub Mazurkiewicz }
222