GF2++
Loading...
Searching...
No Matches
Iterators.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2025 Nessan Fitzmaurice <nzznfitz+gh@icloud.com>
3// SPDX-License-Identifier: MIT
4
8
9#include <gf2/BitStore.h>
10#include <gf2/BitRef.h>
11
12namespace gf2 {
13
14// --------------------------------------------------------------------------------------------------------------------
15// `Bits` ...
16// --------------------------------------------------------------------------------------------------------------------
17
37template<BitStore Store, bool is_const>
38class Bits {
39public:
40 // We keep a pointer to the parent bit-store which is either const or non-const.
41 using store_pointer = std::conditional_t<is_const, const Store*, Store*>;
42
43 // The value type is either a `bool` or a `BitRef` depending on the constness of the iterator.
44 using value_type = std::conditional_t<is_const, bool, BitRef<Store>>;
45
46 // The difference type is always a `std::ptrdiff_t` (required by the iterator concept).
47 using difference_type = std::ptrdiff_t;
48
49private:
50 store_pointer m_store = nullptr;
51 usize m_index = 0;
52
53public:
54 // Our constructor captures a (const or non-const) pointer to the store and the index for the bit of interest.
55 Bits(store_pointer store, usize idx) : m_store(store), m_index{idx} {}
56
57 // Iterator are required to act like value types so must be default constructible, moveable, and copyable.
58 Bits() = default;
59 Bits(Bits&&) = default;
60 Bits(Bits const&) = default;
61 Bits& operator=(Bits&&) = default;
62 Bits& operator=(Bits const&) = default;
63
64 // Iterators must be comparable (this is needed so that any range-for loop will work).
65 constexpr bool operator==(Bits const& other) const { return m_store == other.m_store && m_index == other.m_index; }
66
67 // Returns the item that the iterator is currently pointing to.
68 // The `decltype(auto)` will either be a `bool` or a `BitRef` depending on the constness of the pointer.
69 constexpr decltype(auto) operator*() const { return m_store->operator[](m_index); }
70
71 // Any call to `begin()` returns a new iterator pointing to the first bit in the store.
72 constexpr auto begin() const { return Bits(m_store, 0); }
73
74 // Any call to `end()` returns a new iterator pointing to the bit after the last bit in the store.
75 constexpr auto end() const { return Bits(m_store, m_store->size()); }
76
77 // Increment the iterator, returning the new value (the `i++` operator).
78 constexpr Bits& operator++() {
79 ++m_index;
80 return *this;
81 }
82
83 // Increment the iterator, returning the previous value (the `++i` operator).
84 constexpr Bits operator++(int) {
85 auto prev = *this;
86 ++*this;
87 return prev;
88 }
89
90 // Decrement the iterator, returning the new value (the `--i` operator).
91 constexpr Bits& operator--() {
92 --m_index;
93 return *this;
94 }
95
96 // Decrement the iterator, returning the previous value (the `--i` operator).
97 constexpr Bits operator--(int) {
98 auto prev = *this;
99 --*this;
100 return prev;
101 }
102};
103
104// --------------------------------------------------------------------------------------------------------------------
105// `SetBits` ...
106// --------------------------------------------------------------------------------------------------------------------
107
120template<BitStore Store>
121class SetBits {
122private:
123 const Store* m_store = nullptr; // The store we're iterating over
124 std::optional<usize> m_index = std::nullopt; // Search after this index.
125
126public:
127 // The value type is a `usize` (the index of the set bit).
128 using value_type = usize;
129
130 // The difference type is always a `std::ptrdiff_t` (required by the iterator concept).
131 using difference_type = std::ptrdiff_t;
132
133 // Our constructor captures a pointer to the store and sets the index to uninitialized.
134 SetBits(Store const* store, std::optional<usize> idx = std::nullopt) : m_store(store), m_index{idx} {
135 // Empty body ...
136 }
137
138 // Iterator are required to act like value types so must be default constructible, moveable, and copyable.
139 SetBits() = default;
140 SetBits(SetBits&&) = default;
141 SetBits(SetBits const&) = default;
142 SetBits& operator=(SetBits&&) = default;
143 SetBits& operator=(SetBits const&) = default;
144
145 // Iterators must be comparable (this is how the range-for loops work).
146 constexpr bool operator==(SetBits const& other) const {
147 return m_store == other.m_store && m_index == other.m_index;
148 }
149
150 // What is the iterator currently pointing to? (this is what the range-for loop will use).
151 constexpr value_type operator*() const { return *m_index; }
152
153 // Any call to `begin` returns a new iterator pointing to the first set bit in the store.
154 constexpr auto begin() const { return SetBits(m_store, m_store->first_set()); }
155
156 // Any call to `end` returns a new iterator that is not initialized.
157 constexpr auto end() const { return SetBits(m_store, std::nullopt); }
158
159 // Increment the iterator, returning the new value (the `i++` operator).
160 constexpr SetBits& operator++() {
161 if (m_index.has_value()) m_index = m_store->next_set(*m_index);
162 return *this;
163 }
164
165 // Increment the iterator, returning the previous value (the `++i` operator).
166 constexpr SetBits operator++(int) {
167 auto prev = *this;
168 ++*this;
169 return prev;
170 }
171
172 // Decrement the iterator, returning the new value (the `--i` operator).
173 constexpr SetBits& operator--() {
174 if (m_index.has_value()) m_index = previous_set(*m_store, *m_index);
175 return *this;
176 }
177
178 // Decrement the iterator, returning the previous value (the `--i` operator).
179 constexpr SetBits operator--(int) {
180 auto prev = *this;
181 --*this;
182 return prev;
183 }
184};
185
186// --------------------------------------------------------------------------------------------------------------------
187// `UnsetBits` ...
188// --------------------------------------------------------------------------------------------------------------------
189
202template<BitStore Store>
203class UnsetBits {
204private:
205 const Store* m_store = nullptr; // The store we're iterating over
206 std::optional<usize> m_index = std::nullopt; // Search after this index.
207
208public:
209 // The value type is a `usize` (the index of the unset bit).
210 using value_type = usize;
211
212 // The difference type is always a `std::ptrdiff_t` (required by the iterator concept).
213 using difference_type = std::ptrdiff_t;
214
215 // Our constructor captures a pointer to the store and sets the index to uninitialized.
216 UnsetBits(Store const* store, std::optional<usize> idx = std::nullopt) : m_store(store), m_index{idx} {
217 // Empty body ...
218 }
219
220 // Iterator are required to act like value types so must be default constructible, moveable, and copyable.
221 UnsetBits() = default;
222 UnsetBits(UnsetBits&&) = default;
223 UnsetBits(UnsetBits const&) = default;
224 UnsetBits& operator=(UnsetBits&&) = default;
225 UnsetBits& operator=(UnsetBits const&) = default;
226
227 // Iterators must be comparable (this is how the range-for loops work).
228 constexpr bool operator==(UnsetBits const& other) const {
229 return m_store == other.m_store && m_index == other.m_index;
230 }
231
232 // What is the iterator currently pointing to? (this is what the range-for loop will use).
233 constexpr value_type operator*() const { return *m_index; }
234
235 // Any call to `begin` returns a new iterator pointing to the first unset bit in the store.
236 constexpr auto begin() const { return UnsetBits(m_store, m_store->first_unset()); }
237
238 // Any call to `end` returns a new iterator that is not initialized.
239 constexpr auto end() const { return UnsetBits(m_store, std::nullopt); }
240
241 // Increment the iterator, returning the new value (the `i++` operator).
242 constexpr UnsetBits& operator++() {
243 if (m_index.has_value()) m_index = m_store->next_unset(*m_index);
244 return *this;
245 }
246
247 // Increment the iterator, returning the previous value (the `++i` operator).
248 constexpr UnsetBits operator++(int) {
249 auto prev = *this;
250 ++*this;
251 return prev;
252 }
253
254 // Decrement the iterator, returning the new value (the `--i` operator).
255 constexpr UnsetBits& operator--() {
256 if (m_index.has_value()) m_index = previous_unset(*m_store, *m_index);
257 return *this;
258 }
259
260 // Decrement the iterator, returning the previous value (the `--i` operator).
261 constexpr UnsetBits operator--(int) {
262 auto prev = *this;
263 --*this;
264 return prev;
265 }
266};
267
268// --------------------------------------------------------------------------------------------------------------------
269// `Words` ...
270// --------------------------------------------------------------------------------------------------------------------
271
284template<BitStore Store>
285class Words {
286private:
287 const Store* m_store = nullptr;
288 usize m_index = 0;
289
290public:
291 // The value type is the word type of the store with any const qualification dropped.
292 using value_type = typename Store::word_type;
293
294 // The difference type is always a `std::ptrdiff_t` (required by the iterator concept).
295 using difference_type = std::ptrdiff_t;
296
297 // Our constructor captures a pointer to the store and sets the index to 0
298 Words(Store const* store, usize idx = 0) : m_store(store), m_index{idx} {
299 // Empty body ...
300 }
301
302 // Iterator are required to act like value types so must be default constructible, moveable, and copyable.
303 Words() = default;
304 Words(Words&&) = default;
305 Words(Words const&) = default;
306 Words& operator=(Words&&) = default;
307 Words& operator=(Words const&) = default;
308
309 // Iterators must be comparable (this is how the range-for loops work).
310 constexpr bool operator==(Words const& other) const { return m_store == other.m_store && m_index == other.m_index; }
311
312 // What is the iterator currently pointing to? (this is what the range-for loop will use).
313 constexpr value_type operator*() const { return m_store->word(m_index); }
314
315 // Any call to `begin` returns a new iterator pointing to the first unset bit in the store.
316 constexpr auto begin() const { return Words(m_store, 0); }
317
318 // Any call to `end` returns a new iterator that is not initialized.
319 constexpr auto end() const { return Words(m_store, m_store->words()); }
320
321 // Increment the iterator, returning the new value (the `i++` operator).
322 constexpr Words& operator++() {
323 ++m_index;
324 return *this;
325 }
326
327 // Increment the iterator, returning the previous value (the `++i` operator).
328 constexpr Words operator++(int) {
329 auto prev = *this;
330 ++*this;
331 return prev;
332 }
333
334 // Decrement the iterator, returning the new value (the `--i` operator).
335 constexpr Words& operator--() {
336 --m_index;
337 return *this;
338 }
339
340 // Decrement the iterator, returning the previous value (the `--i` operator).
341 constexpr Words operator--(int) {
342 auto prev = *this;
343 --*this;
344 return prev;
345 }
346};
347
348} // namespace gf2
A BitRef is a proxy type that references a single bit in a bit-store. See the BitRef page for more ...
A concept for types that can access individual bits packed into contiguous primitive unsigned words,...
The namespace for the gf2 library.
Definition assert.h:119
constexpr std::optional< usize > previous_set(Store const &store, usize index)
Returns the index of the previous set bit before index in the store or {} if there are none.
Definition BitStore.h:928
constexpr std::optional< usize > previous_unset(Store const &store, usize index)
Returns the index of the previous unset bit before index in the store or {} if no more unset bits exi...
Definition BitStore.h:1084
std::size_t usize
Word type alias for the platform's "native"-sized unsigned integer.
Definition Unsigned.h:42