/src/cppitertools/cppitertools/combinations.hpp
Line | Count | Source |
1 | | #ifndef ITER_COMBINATIONS_HPP_ |
2 | | #define ITER_COMBINATIONS_HPP_ |
3 | | |
4 | | #include "internal/iteratoriterator.hpp" |
5 | | #include "internal/iterbase.hpp" |
6 | | |
7 | | #include <iterator> |
8 | | #include <type_traits> |
9 | | #include <vector> |
10 | | |
11 | | namespace iter { |
12 | | namespace impl { |
13 | | template <typename Container> |
14 | | class Combinator; |
15 | | |
16 | | using CombinationsFn = IterToolFnBindSizeTSecond<Combinator>; |
17 | | } |
18 | | inline constexpr impl::CombinationsFn combinations{}; |
19 | | } |
20 | | |
21 | | template <typename Container> |
22 | | class iter::impl::Combinator { |
23 | | private: |
24 | | Container container_; |
25 | | std::size_t length_; |
26 | | |
27 | | friend CombinationsFn; |
28 | | |
29 | | Combinator(Container&& container, std::size_t length) |
30 | 599 | : container_(std::forward<Container>(container)), length_{length} {} |
31 | | |
32 | | template <typename T> |
33 | | using IndexVector = std::vector<iterator_type<T>>; |
34 | | template <typename T> |
35 | | using CombIteratorDeref = IterIterWrapper<IndexVector<T>>; |
36 | | |
37 | | public: |
38 | | Combinator(Combinator&&) = default; |
39 | | template <typename ContainerT> |
40 | | class Iterator { |
41 | | private: |
42 | | template <typename> |
43 | | friend class Iterator; |
44 | | constexpr static const int COMPLETE = -1; |
45 | | std::remove_reference_t<ContainerT>* container_p_; |
46 | | CombIteratorDeref<ContainerT> indices_; |
47 | | int steps_{}; |
48 | | |
49 | | public: |
50 | | using iterator_category = std::input_iterator_tag; |
51 | | using value_type = CombIteratorDeref<ContainerT>; |
52 | | using difference_type = std::ptrdiff_t; |
53 | | using pointer = value_type*; |
54 | | using reference = value_type&; |
55 | | |
56 | | Iterator(ContainerT& container, std::size_t n) |
57 | 1.19k | : container_p_{&container}, indices_{n} { |
58 | 1.19k | if (n == 0) { |
59 | 599 | steps_ = COMPLETE; |
60 | 599 | return; |
61 | 599 | } |
62 | 599 | size_t inc = 0; |
63 | 2.29k | for (auto& iter : indices_.get()) { |
64 | 2.29k | auto it = get_begin(*container_p_); |
65 | 2.29k | dumb_advance(it, get_end(*container_p_), inc); |
66 | 2.29k | if (it != get_end(*container_p_)) { |
67 | 2.10k | iter = it; |
68 | 2.10k | ++inc; |
69 | 2.10k | } else { |
70 | 192 | steps_ = COMPLETE; |
71 | 192 | break; |
72 | 192 | } |
73 | 2.29k | } |
74 | 599 | } |
75 | | |
76 | 0 | static Iterator zero_length_end(ContainerT& container) { |
77 | 0 | Iterator it{container, 0}; |
78 | 0 | it.steps_ = 0; |
79 | 0 | return it; |
80 | 0 | } |
81 | | |
82 | 325k | CombIteratorDeref<ContainerT>& operator*() { |
83 | 325k | return indices_; |
84 | 325k | } |
85 | | |
86 | | CombIteratorDeref<ContainerT>* operator->() { |
87 | | return &indices_; |
88 | | } |
89 | | |
90 | 324k | Iterator& operator++() { |
91 | 324k | if (indices_.get().empty()) { |
92 | | // zero-length case. |
93 | 0 | ++steps_; |
94 | 0 | return *this; |
95 | 0 | } |
96 | 366k | for (auto iter = indices_.get().rbegin(); iter != indices_.get().rend(); |
97 | 366k | ++iter) { |
98 | 366k | ++(*iter); |
99 | | |
100 | | // what we have to check here is if the distance between |
101 | | // the index and the end of indices_ is >= the distance |
102 | | // between the item and end of item |
103 | 366k | auto dist = std::distance(indices_.get().rbegin(), iter); |
104 | | |
105 | 366k | if (!(dumb_next(*iter, dist) != get_end(*container_p_))) { |
106 | 41.4k | if ((iter + 1) != indices_.get().rend()) { |
107 | 41.2k | size_t inc = 1; |
108 | 62.6k | for (auto down = iter;; --down) { |
109 | 62.6k | (*down) = dumb_next(*(iter + 1), 1 + inc); |
110 | 62.6k | ++inc; |
111 | 62.6k | if (down == indices_.get().rbegin()) break; |
112 | 62.6k | } |
113 | 41.2k | } else { |
114 | 215 | steps_ = COMPLETE; |
115 | 215 | break; |
116 | 215 | } |
117 | 324k | } else { |
118 | 324k | break; |
119 | 324k | } |
120 | | // we break because none of the rest of the items need |
121 | | // to be incremented |
122 | 366k | } |
123 | 324k | if (steps_ != COMPLETE) { |
124 | 324k | ++steps_; |
125 | 324k | } |
126 | 324k | return *this; |
127 | 324k | } |
128 | | |
129 | | Iterator operator++(int) { |
130 | | auto ret = *this; |
131 | | ++*this; |
132 | | return ret; |
133 | | } |
134 | | |
135 | | template <typename T> |
136 | 325k | bool operator!=(const Iterator<T>& other) const { |
137 | 325k | return !(*this == other); |
138 | 325k | } |
139 | | |
140 | | template <typename T> |
141 | 325k | bool operator==(const Iterator<T>& other) const { |
142 | 325k | return steps_ == other.steps_; |
143 | 325k | } |
144 | | }; |
145 | | |
146 | 599 | Iterator<Container> begin() { |
147 | 599 | return {container_, length_}; |
148 | 599 | } |
149 | | |
150 | 599 | Iterator<Container> end() { |
151 | 599 | if (length_ == 0) { |
152 | 0 | return Iterator<Container>::zero_length_end(container_); |
153 | 0 | } |
154 | 599 | return {container_, 0}; |
155 | 599 | } |
156 | | |
157 | | Iterator<AsConst<Container>> begin() const { |
158 | | return {std::as_const(container_), length_}; |
159 | | } |
160 | | |
161 | | Iterator<AsConst<Container>> end() const { |
162 | | if (length_ == 0) { |
163 | | return Iterator<AsConst<Container>>::zero_length_end(container_); |
164 | | } |
165 | | return {std::as_const(container_), 0}; |
166 | | } |
167 | | }; |
168 | | |
169 | | #endif |