Cuda battery library
Loading...
Searching...
No Matches
bitset.hpp
Go to the documentation of this file.
1// Copyright 2021 Pierre Talbot, Cem Guvel
2
3#ifndef CUDA_BATTERY_BITSET_HPP
4#define CUDA_BATTERY_BITSET_HPP
5
6#include <cstdio>
7#include <cassert>
8#include "utility.hpp"
9#include "memory.hpp"
10#include "string.hpp"
11
12/** \file bitset.hpp
13 * An extension of the C++ STL bitset class with support for set operations (e.g., union and intersection) and for atomic read and write on the bitset blocks.
14 */
15
16namespace battery {
17
18/**
19 * \tparam `N` is the number of bits in the bitset.
20 * \tparam `Mem` is a memory consistency, for instance `local_memory`, see `memory.hpp`.
21 * \tparam `T` is the underlying type defining the blocks of the bitset.
22*/
23template <size_t N, class Mem, class T = unsigned long long>
24class bitset {
25public:
27 using memory_type = Mem;
28
29private:
30 constexpr static size_t MAX_SIZE = N;
31 constexpr static size_t BITS_PER_BLOCK = sizeof(T) * CHAR_BIT;
32 constexpr static size_t BITS_LAST_BLOCK = (N % BITS_PER_BLOCK) == 0 ? BITS_PER_BLOCK : (N % BITS_PER_BLOCK);
33 constexpr static size_t PADDING_LAST_BLOCK = BITS_PER_BLOCK - BITS_LAST_BLOCK;
34 constexpr static size_t BLOCKS = N / BITS_PER_BLOCK + (N % BITS_PER_BLOCK != 0);
35 constexpr static T ZERO = T{0};
36 constexpr static T ONES = ~T{0};
37 /** The last block might not be fully used.
38 If we have two blocks of size 8 each, but N = 10, only 2 bits are relevant in the last block.
39 We have 10 % 8 = 2, then ONES << 2 gives 11111100, and ~(ONES << 2) = 00000011. */
40#ifdef _MSC_VER
41#pragma warning(disable: 4293) // The C++ standard says that the behavior of uintptr64_t << 64 is undefined (works ok though)
42#endif
43 constexpr static T ONES_LAST = PADDING_LAST_BLOCK == 0 ? ONES : (T)(~(ONES << BITS_LAST_BLOCK));
44
45 using block_type = typename Mem::template atomic_type<T>;
46
47 /** Suppose T = char, with 2 blocks. Then the bitset "00000000 00100000" is represented as:
48 *
49 * blocks index: 0 1
50 * blocks: 00100000 00000000
51 * indexes: 76543210 ...98
52 *
53 * We have `bitset.test(5) == true`.
54 * This follows the C++ standard where the least significant bit is at index 0.
55 *
56 * Note that the last block is the one carrying the most significant bits, and also the one that is potentially padded with zeroes.
57 * */
58 block_type blocks[BLOCKS];
59
60 template <size_t N2, class Mem2, class T2>
61 friend class bitset;
62
63public:
64 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "Block of a bitset must be defined on an unsigned integer type.");
65
66 CUDA constexpr bitset(): blocks() {}
67
68 CUDA constexpr bitset(const this_type& other) {
69 for(int i = 0; i < BLOCKS; ++i) {
70 store(blocks[i], Mem::load(other.blocks[i]));
71 }
72 }
73
74 template<class Mem2>
75 CUDA constexpr bitset(const bitset<N, Mem2, T>& other) {
76 for(int i = 0; i < BLOCKS; ++i) {
77 store(blocks[i], Mem2::load(other.blocks[i]));
78 }
79 }
80
81 /** Create a bitset which is the intersection between bitset().set() and [start..end] (all bits set between start and end).
82 * `end >= start`. */
83 CUDA bitset(size_t start, size_t end): bitset() {
84 assert(end >= start);
85 int block_start = start / BITS_PER_BLOCK;
86 // The range is completely out of the bitset.
87 if(block_start >= BLOCKS) {
88 return;
89 }
90 end = min(N-1, end);
91 int block_end = min(end / BITS_PER_BLOCK, BLOCKS - 1);
92 store(blocks[block_start], ONES << (start % BITS_PER_BLOCK));
93 for(int k = block_start + 1; k <= block_end; ++k) {
94 store(blocks[k], ONES);
95 }
96 store(blocks[block_end], Mem::load(blocks[block_end]) & (ONES >> ((BITS_PER_BLOCK-(end % BITS_PER_BLOCK)-1))));
97 }
98
99 CUDA constexpr bitset(const char* bit_str): blocks() {
100 size_t n = min(strlen(bit_str), MAX_SIZE);
101 for(int i = n, j = 0; i > 0; --i, ++j) {
102 if(bit_str[i-1] == '1') {
103 set(j);
104 }
105 }
106 }
107
108 CUDA static constexpr bitset zeroes() {
109 return bitset();
110 }
111
112 CUDA static constexpr bitset ones() {
113 return bitset().set();
114 }
115
116private:
117 CUDA constexpr void store(block_type& block, T data) {
118 Mem::store(block, data);
119 }
120
121 CUDA constexpr block_type& block_of(size_t pos) {
122 return blocks[pos / BITS_PER_BLOCK];
123 }
124
125 CUDA constexpr const block_type& block_of(size_t pos) const {
126 return blocks[pos / BITS_PER_BLOCK];
127 }
128
129 CUDA constexpr T load_block(size_t pos) const {
130 return Mem::load(block_of(pos));
131 }
132
133 CUDA constexpr void store_block(size_t pos, T data) {
134 store(block_of(pos), data);
135 }
136
137 CUDA constexpr T bit_of(size_t pos) const {
138 return static_cast<T>(1) << (pos % BITS_PER_BLOCK);
139 }
140
141public:
142 CUDA constexpr bool test(size_t pos) const {
143 assert(pos < MAX_SIZE);
144 return load_block(pos) & bit_of(pos);
145 }
146
147 CUDA constexpr bool all() const {
148 int i = 0;
149 for(; i < BLOCKS - 1; ++i) {
150 if(Mem::load(blocks[i]) != ONES) {
151 return false;
152 }
153 }
154 return Mem::load(blocks[i]) == ONES_LAST;
155 }
156
157 CUDA constexpr bool any() const {
158 for(int i = 0; i < BLOCKS; ++i) {
159 if(Mem::load(blocks[i]) > ZERO) {
160 return true;
161 }
162 }
163 return false;
164 }
165
166 CUDA constexpr bool none() const {
167 for(int i = 0; i < BLOCKS; ++i) {
168 if(Mem::load(blocks[i]) != ZERO) {
169 return false;
170 }
171 }
172 return true;
173 }
174
175 CUDA constexpr size_t size() const {
176 return MAX_SIZE;
177 }
178
179 CUDA constexpr size_t count() const {
180 size_t bits_at_one = 0;
181 for(int i = 0; i < BLOCKS; ++i){
182 bits_at_one += battery::popcount(Mem::load(blocks[i]));
183 }
184 return bits_at_one;
185 }
186
187 CUDA constexpr bitset& set() {
188 int i = 0;
189 for(; i < BLOCKS - 1; ++i) {
190 store(blocks[i], ONES);
191 }
192 store(blocks[i], ONES_LAST);
193 return *this;
194 }
195
196 CUDA constexpr bitset& set(size_t pos) {
197 assert(pos < MAX_SIZE);
198 store_block(pos, load_block(pos) | bit_of(pos));
199 return *this;
200 }
201
202 CUDA constexpr bitset& set(size_t pos, bool value) {
203 assert(pos < MAX_SIZE);
204 return value ? set(pos) : reset(pos);
205 }
206
207 CUDA constexpr bitset& reset() {
208 for(int i = 0; i < BLOCKS; ++i) {
209 store(blocks[i], ZERO);
210 }
211 return *this;
212 }
213
214 CUDA constexpr bitset& reset(size_t pos) {
215 assert(pos < MAX_SIZE);
216 store_block(pos, load_block(pos) & ~bit_of(pos));
217 return *this;
218 }
219
220 CUDA constexpr bitset& flip() {
221 int i = 0;
222 for(; i < BLOCKS - 1; ++i) {
223 store(blocks[i], ~Mem::load(blocks[i]));
224 }
225 store(blocks[i], ONES_LAST & ~Mem::load(blocks[i]));
226 return *this;
227 }
228
229 CUDA constexpr bitset& flip(size_t pos) {
230 assert(pos < MAX_SIZE);
231 store_block(pos, load_block(pos) ^ bit_of(pos));
232 return *this;
233 }
234
235 template <class Mem2>
236 CUDA constexpr bool is_subset_of(const bitset<N, Mem2, T>& other) const {
237 for(int i = 0; i < BLOCKS; ++i) {
238 T block = Mem::load(blocks[i]);
239 if((block & Mem2::load(other.blocks[i])) != block) {
240 return false;
241 }
242 }
243 return true;
244 }
245
246 template <class Mem2>
247 CUDA constexpr bool is_proper_subset_of(const bitset<N, Mem2, T>& other) const {
248 bool proper = false;
249 for(int i = 0; i < BLOCKS; ++i) {
250 T block = Mem::load(blocks[i]);
251 T block2 = Mem2::load(other.blocks[i]);
252 if((block & block2) != block) {
253 return false;
254 }
255 proper |= block != block2;
256 }
257 return proper;
258 }
259
260 template<class Mem2>
261 CUDA constexpr bitset& operator&=(const bitset<N, Mem2, T>& other) {
262 for(int i = 0; i < BLOCKS; ++i) {
263 store(blocks[i], Mem::load(blocks[i]) & Mem2::load(other.blocks[i]));
264 }
265 return *this;
266 }
267
268 template<class Mem2>
269 CUDA constexpr bitset& operator|=(const bitset<N, Mem2, T>& other) {
270 for(int i = 0; i < BLOCKS; ++i) {
271 store(blocks[i], Mem::load(blocks[i]) | Mem2::load(other.blocks[i]));
272 }
273 return *this;
274 }
275
276 template<class Mem2>
277 CUDA constexpr bitset& operator^=(const bitset<N, Mem2, T>& other) {
278 for(int i = 0; i < BLOCKS; ++i) {
279 store(blocks[i], Mem::load(blocks[i]) ^ Mem2::load(other.blocks[i]));
280 }
281 return *this;
282 }
283
284 CUDA constexpr bitset operator~() const {
285 return bitset(*this).flip();
286 }
287
288 template<class Mem2>
289 CUDA constexpr bool operator==(const bitset<N, Mem2, T>& other) const {
290 for(int i = 0; i < BLOCKS; ++i) {
291 if(Mem::load(blocks[i]) != Mem2::load(other.blocks[i])) {
292 return false;
293 }
294 }
295 return true;
296 }
297
298 template<class Mem2>
299 CUDA constexpr bool operator!=(const bitset<N, Mem2, T>& other) const {
300 return !(*this == other);
301 }
302
303 CUDA constexpr int countl_zero() const {
304 int k = BLOCKS - 1;
305 if(Mem::load(blocks[k]) > ZERO) {
306 return ::battery::countl_zero(Mem::load(blocks[k])) - PADDING_LAST_BLOCK;
307 }
308 --k;
309 int i = 0;
310 for(; k >= 0 && Mem::load(blocks[k]) == ZERO; --k, ++i) {}
311 return i * BITS_PER_BLOCK
312 + BITS_LAST_BLOCK
313 + (k == -1 ? 0 : ::battery::countl_zero(Mem::load(blocks[k])));
314 }
315
316 CUDA constexpr int countl_one() const {
317 int k = BLOCKS - 1;
318 if(Mem::load(blocks[k]) != ONES_LAST) {
319 return battery::countl_one((T)(Mem::load(blocks[k]) << PADDING_LAST_BLOCK));
320 }
321 --k;
322 int i = 0;
323 for(; k >= 0 && Mem::load(blocks[k]) == ONES; --k, ++i) {}
324 return i * BITS_PER_BLOCK
325 + BITS_LAST_BLOCK
326 + (k == -1 ? 0 : ::battery::countl_one(Mem::load(blocks[k])));
327 }
328
329 CUDA constexpr int countr_zero() const {
330 int k = 0;
331 for(; k < BLOCKS && Mem::load(blocks[k]) == ZERO; ++k) {}
332 return k * BITS_PER_BLOCK
333 + (k == BLOCKS ? -PADDING_LAST_BLOCK : ::battery::countr_zero(Mem::load(blocks[k])));
334 }
335
336 CUDA constexpr int countr_one() const {
337 int k = 0;
338 for(; k < BLOCKS && Mem::load(blocks[k]) == ONES; ++k) {}
339 return k * BITS_PER_BLOCK
340 + (k == BLOCKS ? 0 : ::battery::countr_one(Mem::load(blocks[k])));
341 }
342
343 CUDA constexpr void print() const {
344 for(int i = size() - 1; i >= 0; --i) {
345 printf("%c", test(i) ? '1' : '0');
346 }
347 }
348
349 template <class Allocator>
350 CUDA string<Allocator> to_string(Allocator allocator = Allocator()) const {
351 string<Allocator> bits_str(size(), allocator);
352 for(int i = size() - 1, j = 0; i >= 0; --i, ++j) {
353 bits_str[j] = test(i) ? '1' : '0';
354 }
355 return bits_str;
356 }
357};
358
359template<size_t N, class M1, class M2, class T>
361 return bitset<N, local_memory, T>(lhs) &= rhs;
362}
363
364template<size_t N, class M1, class M2, class T>
366 return bitset<N, local_memory, T>(lhs) |= rhs;
367}
368
369template<size_t N, class M1, class M2, class T>
371 return bitset<N, local_memory, T>(lhs) ^= rhs;
372}
373
374} // namespace battery
375
376#endif
Definition bitset.hpp:24
CUDA constexpr bitset & set()
Definition bitset.hpp:187
CUDA constexpr bitset & set(size_t pos)
Definition bitset.hpp:196
CUDA constexpr bool test(size_t pos) const
Definition bitset.hpp:142
CUDA constexpr bool is_proper_subset_of(const bitset< N, Mem2, T > &other) const
Definition bitset.hpp:247
CUDA constexpr bitset(const bitset< N, Mem2, T > &other)
Definition bitset.hpp:75
CUDA constexpr bitset(const this_type &other)
Definition bitset.hpp:68
CUDA constexpr bool is_subset_of(const bitset< N, Mem2, T > &other) const
Definition bitset.hpp:236
CUDA constexpr bitset & reset(size_t pos)
Definition bitset.hpp:214
CUDA constexpr int countr_one() const
Definition bitset.hpp:336
CUDA constexpr bitset()
Definition bitset.hpp:66
friend class bitset
Definition bitset.hpp:61
static CUDA constexpr bitset zeroes()
Definition bitset.hpp:108
CUDA constexpr size_t size() const
Definition bitset.hpp:175
CUDA constexpr bitset & flip()
Definition bitset.hpp:220
CUDA constexpr int countr_zero() const
Definition bitset.hpp:329
CUDA constexpr bool all() const
Definition bitset.hpp:147
CUDA constexpr void print() const
Definition bitset.hpp:343
CUDA constexpr bitset & set(size_t pos, bool value)
Definition bitset.hpp:202
Mem memory_type
Definition bitset.hpp:27
CUDA bitset(size_t start, size_t end)
Definition bitset.hpp:83
CUDA constexpr int countl_one() const
Definition bitset.hpp:316
CUDA constexpr bool operator==(const bitset< N, Mem2, T > &other) const
Definition bitset.hpp:289
CUDA constexpr bool none() const
Definition bitset.hpp:166
CUDA constexpr bool any() const
Definition bitset.hpp:157
CUDA constexpr bitset & flip(size_t pos)
Definition bitset.hpp:229
CUDA string< Allocator > to_string(Allocator allocator=Allocator()) const
Definition bitset.hpp:350
CUDA constexpr bitset operator~() const
Definition bitset.hpp:284
static CUDA constexpr bitset ones()
Definition bitset.hpp:112
CUDA constexpr int countl_zero() const
Definition bitset.hpp:303
CUDA constexpr bitset(const char *bit_str)
Definition bitset.hpp:99
CUDA constexpr size_t count() const
Definition bitset.hpp:179
CUDA constexpr bitset & reset()
Definition bitset.hpp:207
Definition string.hpp:15
#define ONES
Definition dynamic_bitset.hpp:33
#define ZERO
Definition dynamic_bitset.hpp:32
Definition algorithm.hpp:10
CUDA NI constexpr int countl_zero(T x)
Definition utility.hpp:419
CUDA constexpr bitset< N, local_memory, T > operator|(const bitset< N, M1, T > &lhs, const bitset< N, M2, T > &rhs)
Definition bitset.hpp:365
CUDA NI constexpr int countr_one(T x)
Definition utility.hpp:496
CUDA constexpr bitset< N, local_memory, T > operator&(const bitset< N, M1, T > &lhs, const bitset< N, M2, T > &rhs)
Definition bitset.hpp:360
CUDA constexpr bitset< N, local_memory, T > operator^(const bitset< N, M1, T > &lhs, const bitset< N, M2, T > &rhs)
Definition bitset.hpp:370
CUDA INLINE constexpr T min(T a, T b)
Definition utility.hpp:116
CUDA NI constexpr int countr_zero(T x)
Definition utility.hpp:466
CUDA NI constexpr int popcount(T x)
Definition utility.hpp:394
CUDA size_t strlen(const char *str)
Definition utility.hpp:99
CUDA NI constexpr int countl_one(T x)
Definition utility.hpp:447
#define CUDA
Definition utility.hpp:59