1 #pragma once
2 
3 /// \file IteratorAdapters.h
4 /// \brief Helper objects allowing to iterate in reverse, iterate over multiple containeres, etc.
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7 
8 #include "objects/containers/Tuple.h"
9 #include "objects/utility/Iterator.h"
10 
11 NAMESPACE_SPH_BEGIN
12 
13 //-----------------------------------------------------------------------------------------------------------
14 // ReverseIterator
15 //-----------------------------------------------------------------------------------------------------------
16 
17 /// Generic reverse iterator over continuous array of objects of type T.
18 template <typename TIterator>
19 class ReverseIterator {
20 protected:
21     TIterator iter;
22 
23 public:
24     ReverseIterator() = default;
25 
ReverseIterator(TIterator iter)26     explicit ReverseIterator(TIterator iter)
27         : iter(iter) {}
28 
decltype(auto)29     decltype(auto) operator*() const {
30         return *iter;
31     }
decltype(auto)32     decltype(auto) operator*() {
33         return *iter;
34     }
35     ReverseIterator& operator++() {
36         --iter;
37         return *this;
38     }
39     ReverseIterator operator++(int) {
40         ReverseIterator tmp(*this);
41         operator++();
42         return tmp;
43     }
44     ReverseIterator& operator--() {
45         ++iter;
46         return *this;
47     }
48     ReverseIterator operator--(int) {
49         ReverseIterator tmp(*this);
50         operator--();
51         return tmp;
52     }
53     bool operator==(const ReverseIterator& other) const {
54         return iter == other.iter;
55     }
56     bool operator!=(const ReverseIterator& other) const {
57         return iter != other.iter;
58     }
59 };
60 
61 /// Creates reverse iterator by wrapping forward iterator, utilizes type deduction.
62 template <typename TIterator>
reverseIterator(const TIterator iter)63 ReverseIterator<TIterator> reverseIterator(const TIterator iter) {
64     return ReverseIterator<TIterator>(iter);
65 }
66 
67 
68 /// Wrapper of generic container allowing to iterate over its elements in reverse order. The wrapper can hold
69 /// l-value reference, or the container can be moved into the wrapper.
70 template <typename TContainer>
71 class ReverseAdapter {
72 private:
73     TContainer container;
74 
75     using Iterator = decltype(std::declval<std::decay_t<TContainer>>().begin());
76 
77 public:
78     template <typename T>
ReverseAdapter(T && container)79     explicit ReverseAdapter(T&& container)
80         : container(std::forward<T>(container)) {}
81 
82     /// Returns iterator pointing to the last element in container.
begin()83     ReverseIterator<Iterator> begin() {
84         if (Iterator iter = container.end()) {
85             --iter;
86             return reverseIterator(iter);
87         } else {
88             return ReverseIterator<Iterator>(nullptr);
89         }
90     }
91 
92     /// Returns iterator pointiing to one before the first element.
end()93     ReverseIterator<Iterator> end() {
94         if (Iterator iter = container.begin()) {
95             --iter;
96             return reverseIterator(iter);
97         } else {
98             return ReverseIterator<Iterator>(nullptr);
99         }
100     }
101 
size()102     Size size() const {
103         return container.size();
104     }
105 };
106 
107 /// Creates the ReverseAdapter over given container, utilizing type deduction.
108 /// To iterate over elements of container in reverse order, use
109 /// \code
110 /// for (T& value : reverse(container)) {
111 ///    // do something with value
112 /// }
113 /// \endcode
114 template <typename TContainer>
reverse(TContainer && container)115 ReverseAdapter<TContainer> reverse(TContainer&& container) {
116     return ReverseAdapter<TContainer>(std::forward<TContainer>(container));
117 }
118 
119 //-----------------------------------------------------------------------------------------------------------
120 // TupleIterator
121 //-----------------------------------------------------------------------------------------------------------
122 
123 /// \brief Holds multiple iterators, advancing all of them at the same time.
124 ///
125 /// Has only the necessary functions to use in range-based for loops.
126 template <typename TElement, typename... TIterator>
127 class TupleIterator {
128 private:
129     Tuple<TIterator...> iterators;
130 
131 public:
TupleIterator(const TIterator &...iters)132     explicit TupleIterator(const TIterator&... iters)
133         : iterators(iters...) {}
134 
135     TupleIterator& operator++() {
136         forEach(iterators, [](auto& iter) { ++iter; });
137         return *this;
138     }
139 
140     TElement operator*() {
141         return apply(iterators, [](auto&... values) -> TElement { return { *values... }; });
142     }
143 
144     const TElement operator*() const {
145         return apply(iterators, [](const auto&... values) -> TElement { return { *values... }; });
146     }
147 
148     bool operator==(const TupleIterator& other) const {
149         const bool result = iterators.template get<0>() == other.iterators.template get<0>();
150         // check that all iterators return the same result (all containers have the same size)
151         SPH_ASSERT(checkEqual(result, other, std::index_sequence_for<TIterator...>{}));
152         return result;
153     }
154 
155     bool operator!=(const TupleIterator& other) const {
156         const bool result = iterators.template get<0>() != other.iterators.template get<0>();
157         // check that all iterators return the same result (all containers have the same size)
158         SPH_ASSERT(checkEqual(!result, other, std::index_sequence_for<TIterator...>{}));
159         return result;
160     }
161 
162 private:
163     template <std::size_t First, std::size_t... Rest>
checkEqual(const bool equal,const TupleIterator & other,std::index_sequence<First,Rest...>)164     bool checkEqual(const bool equal, const TupleIterator& other, std::index_sequence<First, Rest...>) const {
165         if (equal != (iterators.template get<First>() == other.iterators.template get<First>())) {
166             // different size
167             return false;
168         }
169         return checkEqual(equal, other, std::index_sequence<Rest...>{});
170     }
171 
172     // last step to end the recursion
checkEqual(const bool,const TupleIterator &,std::index_sequence<>)173     bool checkEqual(const bool, const TupleIterator&, std::index_sequence<>) const {
174         return true;
175     }
176 };
177 
178 /// Creates TupleIterator from individual iterators, utilizing type deduction.
179 template <typename TElement, typename... TIterators>
makeTupleIterator(const TIterators &...iterators)180 TupleIterator<TElement, TIterators...> makeTupleIterator(const TIterators&... iterators) {
181     return TupleIterator<TElement, TIterators...>(iterators...);
182 }
183 
184 /// Wraps any number of containers, providing means to iterate over all of them at the same time. This can
185 /// only be used for containers of the same size.
186 template <typename TElement, typename... TContainers>
187 class TupleAdapter {
188 private:
189     Tuple<TContainers...> tuple;
190 
191 public:
TupleAdapter(TContainers &&...containers)192     explicit TupleAdapter(TContainers&&... containers)
193         : tuple(std::forward<TContainers>(containers)...) {
194         SPH_ASSERT(tuple.size() > 1);
195     }
196 
begin()197     auto begin() {
198         return apply(tuple, [](auto&... containers) { //
199             return makeTupleIterator<TElement>(containers.begin()...);
200         });
201     }
202 
begin()203     auto begin() const {
204         return apply(tuple, [](const auto&... containers) { //
205             return makeTupleIterator<TElement>(containers.begin()...);
206         });
207     }
208 
end()209     auto end() {
210         return apply(tuple, [](auto&... containers) { //
211             return makeTupleIterator<TElement>(containers.end()...);
212         });
213     }
214 
end()215     auto end() const {
216         return apply(tuple, [](const auto&... containers) { //
217             return makeTupleIterator<TElement>(containers.end()...);
218         });
219     }
220 
size()221     Size size() const {
222         return tuple.template get<0>().size();
223     }
224 };
225 
226 template <typename TElement, typename... TContainers>
iterateTuple(TContainers &&...containers)227 TupleAdapter<TElement, TContainers...> iterateTuple(TContainers&&... containers) {
228     return TupleAdapter<TElement, TContainers...>(std::forward<TContainers>(containers)...);
229 }
230 
231 //-----------------------------------------------------------------------------------------------------------
232 // IteratorWithIndex
233 //-----------------------------------------------------------------------------------------------------------
234 
235 template <typename TValue>
236 class ElementWithIndex {
237 private:
238     TValue data;
239     Size idx;
240 
241 public:
ElementWithIndex(TValue && value,const Size index)242     ElementWithIndex(TValue&& value, const Size index)
243         : data(std::forward<TValue>(value))
244         , idx(index) {}
245 
value()246     INLINE TValue& value() {
247         return data;
248     }
249 
value()250     INLINE const TValue& value() const {
251         return data;
252     }
253 
254     INLINE operator TValue&() {
255         return value();
256     }
257 
258     INLINE operator const TValue&() const {
259         return value();
260     }
261 
index()262     INLINE Size index() const {
263         return idx;
264     }
265 };
266 
267 template <typename TValue>
makeElementWithIndex(TValue && value,const Size index)268 ElementWithIndex<TValue> makeElementWithIndex(TValue&& value, const Size index) {
269     return ElementWithIndex<TValue>(std::forward<TValue>(value), index);
270 }
271 
272 /// Wrapper of iterator keeping also an index of current element.
273 template <typename TIterator>
274 class IteratorWithIndex {
275 private:
276     TIterator iterator;
277     Size index;
278 
279     using TValue = decltype(*std::declval<TIterator>());
280 
281 public:
IteratorWithIndex(const TIterator iterator,const Size index)282     IteratorWithIndex(const TIterator iterator, const Size index)
283         : iterator(iterator)
284         , index(index) {}
285 
286     ElementWithIndex<TValue> operator*() {
287         return makeElementWithIndex(*iterator, index);
288     }
289 
290     ElementWithIndex<const TValue> operator*() const {
291         return makeElementWithIndex(*iterator, index);
292     }
293 
294     IteratorWithIndex& operator++() {
295         ++iterator;
296         ++index;
297         return *this;
298     }
299 
300     bool operator!=(const IteratorWithIndex& other) const {
301         return iterator != other.iterator;
302     }
303 };
304 
305 template <typename TIterator>
makeIteratorWithIndex(TIterator && iterator,const Size index)306 IteratorWithIndex<TIterator> makeIteratorWithIndex(TIterator&& iterator, const Size index) {
307     return IteratorWithIndex<TIterator>(std::forward<TIterator>(iterator), index);
308 }
309 
310 
311 template <typename TContainer>
312 class IndexAdapter {
313 private:
314     TContainer container;
315 
316 public:
IndexAdapter(TContainer && container)317     explicit IndexAdapter(TContainer&& container)
318         : container(std::forward<TContainer>(container)) {}
319 
begin()320     auto begin() {
321         return makeIteratorWithIndex(container.begin(), 0);
322     }
323 
end()324     auto end() {
325         return makeIteratorWithIndex(container.end(), container.size());
326     }
327 };
328 
329 template <typename TContainer>
iterateWithIndex(TContainer && container)330 IndexAdapter<TContainer> iterateWithIndex(TContainer&& container) {
331     return IndexAdapter<TContainer>(std::forward<TContainer>(container));
332 }
333 
334 //-----------------------------------------------------------------------------------------------------------
335 // Subrange
336 //-----------------------------------------------------------------------------------------------------------
337 
338 template <typename TIterator>
339 struct SubRange : public Noncopyable {
340 private:
341     TIterator first;
342     TIterator last;
343 
344 public:
345     template <typename TContainer>
SubRangeSubRange346     explicit SubRange(const TContainer& container, const Size firstIdx, const Size lastIdx) {
347         SPH_ASSERT(lastIdx <= container.size());
348         first = container.begin() + firstIdx;
349         last = container.begin() + lastIdx;
350     }
351 
beginSubRange352     INLINE TIterator begin() const {
353         return first;
354     }
355 
endSubRange356     INLINE TIterator end() const {
357         return last;
358     }
359 };
360 
361 template <typename TContainer>
subrange(const TContainer & container,const Size firstIdx,const Size lastIdx)362 INLINE auto subrange(const TContainer& container, const Size firstIdx, const Size lastIdx) {
363     return SubRange<TContainer>(container, firstIdx, lastIdx);
364 }
365 
366 //-----------------------------------------------------------------------------------------------------------
367 // SubsetIterator
368 //-----------------------------------------------------------------------------------------------------------
369 
370 /// Allows to iterate over a given subset of a container, given by condition functor.
371 template <typename TIterator, typename TCondition>
372 class SubsetIterator {
373 private:
374     TIterator iter;
375     TIterator end;
376     TCondition condition;
377 
378 public:
SubsetIterator(const TIterator & iterator,const TIterator & end,TCondition && condition)379     SubsetIterator(const TIterator& iterator, const TIterator& end, TCondition&& condition)
380         : iter(iterator)
381         , end(end)
382         , condition(std::forward<TCondition>(condition)) {
383         // move to first element of the subset
384         while (iter != end && !condition(*iter)) {
385             ++iter;
386         }
387     }
388 
389     INLINE SubsetIterator& operator++() {
390         do {
391             ++iter;
392         } while (iter != end && !condition(*iter));
393         return *this;
394     }
395 
decltype(auto)396     INLINE decltype(auto) operator*() {
397         SPH_ASSERT(iter != end);
398         return *iter;
399     }
400 
decltype(auto)401     INLINE decltype(auto) operator*() const {
402         SPH_ASSERT(iter != end);
403         return *iter;
404     }
405 
406     INLINE bool operator==(const SubsetIterator& other) const {
407         return iter == other.iter;
408     }
409 
410     INLINE bool operator!=(const SubsetIterator& other) const {
411         return iter != other.iter;
412     }
413 };
414 
415 /// \todo test
416 template <typename TIterator, typename TCondition>
makeSubsetIterator(const TIterator & iterator,const TIterator & end,TCondition && condition)417 INLINE auto makeSubsetIterator(const TIterator& iterator, const TIterator& end, TCondition&& condition) {
418     return SubsetIterator<TIterator, TCondition>(iterator, end, std::forward<TCondition>(condition));
419 }
420 
421 /// Non-owning view to access and iterate over subset of container
422 template <typename TContainer, typename TCondition>
423 class SubsetAdapter {
424 private:
425     TContainer container;
426     TCondition condition;
427 
428 public:
SubsetAdapter(TContainer && container,TCondition && condition)429     SubsetAdapter(TContainer&& container, TCondition&& condition)
430         : container(std::forward<TContainer>(container))
431         , condition(std::forward<TCondition>(condition)) {}
432 
begin()433     auto begin() {
434         return makeSubsetIterator(container.begin(), container.end(), condition);
435     }
436 
end()437     auto end() {
438         return makeSubsetIterator(container.end(), container.end(), condition);
439     }
440 };
441 
442 /// \todo test
443 template <typename TContainer, typename TCondition>
subset(TContainer && container,TCondition && condition)444 auto subset(TContainer&& container, TCondition&& condition) {
445     return SubsetAdapter<TContainer, TCondition>(
446         std::forward<TContainer>(container), std::forward<TCondition>(condition));
447 }
448 
449 //-----------------------------------------------------------------------------------------------------------
450 // IndexIterator
451 //-----------------------------------------------------------------------------------------------------------
452 
453 class IndexIterator {
454 protected:
455     Size idx;
456 
457 public:
IndexIterator(const Size idx)458     INLINE explicit IndexIterator(const Size idx)
459         : idx(idx) {}
460 
461     INLINE Size operator*() const {
462         return idx;
463     }
464 
465     INLINE IndexIterator& operator++() {
466         ++idx;
467         return *this;
468     }
469 
470     INLINE bool operator!=(const IndexIterator other) const {
471         return idx != other.idx;
472     }
473 };
474 
475 class IndexSequence {
476 protected:
477     Size from;
478     Size to;
479 
480 public:
IndexSequence(const Size from,const Size to)481     INLINE IndexSequence(const Size from, const Size to)
482         : from(from)
483         , to(to) {
484         SPH_ASSERT(from <= to);
485     }
486 
begin()487     INLINE IndexIterator begin() const {
488         return IndexIterator(from);
489     }
490 
end()491     INLINE IndexIterator end() const {
492         return IndexIterator(to);
493     }
494 
size()495     INLINE Size size() const {
496         return to - from;
497     }
498 
499     INLINE bool operator==(const IndexSequence& other) const {
500         return from == other.from && to == other.to;
501     }
502 
503     friend std::ostream& operator<<(std::ostream& ofs, const IndexSequence& sequence) {
504         ofs << sequence.from << " - " << sequence.to;
505         return ofs;
506     }
507 };
508 
509 NAMESPACE_SPH_END
510