libsemigroups  v3.0.0
C++ library for semigroups and monoids
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
Loading...
Searching...
No Matches
bitset.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2020-2025 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// 1) doc TODO(1)
20// 2) tpp file TODO(1)
21
22#ifndef LIBSEMIGROUPS_BITSET_HPP_
23#define LIBSEMIGROUPS_BITSET_HPP_
24
25#include <array> // for array
26#include <bitset> // for bitset
27#include <climits> // for CHAR_BIT
28#include <cstddef> // for size_t
29#include <iosfwd> // for operator<<, ostringstream
30#include <type_traits> // for false_type
31#include <utility> // for hash
32
33#include "config.hpp" // for LIBSEMIGROUPS_SIZEOF_VOID_P
34#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
35#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
36
37#include "detail/string.hpp" // for detail::to_string
38
39namespace libsemigroups {
40
41 // The code below for popcnt is borrowed/adapted from GAP.
42
43#if LIBSEMIGROUPS_USE_POPCNT && defined(LIBSEMIGROUPS_HAVE___BUILTIN_POPCOUNTL)
44 template <typename T>
45 static inline size_t COUNT_TRUES_BLOCK(T block) {
46 return __builtin_popcountl(block);
47 }
48#else
49#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
50 template <typename T>
51 static inline auto COUNT_TRUES_BLOCK(T block)
52 -> std::enable_if_t<sizeof(T) == 8, size_t> {
53 block
54 = (block & 0x5555555555555555L) + ((block >> 1) & 0x5555555555555555L);
55 block
56 = (block & 0x3333333333333333L) + ((block >> 2) & 0x3333333333333333L);
57 block = (block + (block >> 4)) & 0x0f0f0f0f0f0f0f0fL;
58 block = (block + (block >> 8));
59 block = (block + (block >> 16));
60 block = (block + (block >> 32)) & 0x00000000000000ffL;
61 return block;
62 }
63#endif
64
65 template <typename T>
66 static inline auto COUNT_TRUES_BLOCK(T block)
67 -> std::enable_if_t<sizeof(T) == 4, size_t> {
68 block = (block & 0x55555555) + ((block >> 1) & 0x55555555);
69 block = (block & 0x33333333) + ((block >> 2) & 0x33333333);
70 block = (block + (block >> 4)) & 0x0f0f0f0f;
71 block = (block + (block >> 8));
72 block = (block + (block >> 16)) & 0x000000ff;
73 return block;
74 }
75
76 template <typename T>
77 static inline auto COUNT_TRUES_BLOCK(T block)
78 -> std::enable_if_t<sizeof(T) == 2, size_t> {
79 block = (block & 0x5555) + ((block >> 1) & 0x5555);
80 block = (block & 0x3333) + ((block >> 2) & 0x3333);
81 block = (block + (block >> 4)) & 0x0f0f;
82 block = (block + (block >> 8)) & 0x00ff;
83 return block;
84 }
85
86 template <typename T>
87 static inline auto COUNT_TRUES_BLOCK(T block)
88 -> std::enable_if_t<sizeof(T) == 1, size_t> {
89 static constexpr std::array<size_t, 256> const lookup = {
90 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
91 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
92 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
93 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
94 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
95 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
96 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
97 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
98 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
99 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
100 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
101 return lookup[block];
102 }
103#endif
104
105 template <size_t N>
106 class BitSet {
107 static_assert(N > 0, "BitSet does not support 0 entries");
108#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
109 static_assert(N <= 64, "BitSet does not support more than 64 entries");
110#else
111 static_assert(N <= 32, "BitSet does not support more than 32 entries");
112#endif
113
114 public:
115 using block_type = std::conditional_t<
116 N <= 8,
117 uint_fast8_t,
118 std::conditional_t<
119 N <= 16,
120 uint_fast16_t,
121 std::conditional_t<N <= 32, uint_fast32_t, uint64_t>>>;
122
123 explicit constexpr BitSet(block_type block) noexcept : _block(block) {}
124 constexpr BitSet() noexcept : BitSet(0) {}
125 constexpr BitSet(BitSet const&) noexcept = default;
126 constexpr BitSet(BitSet&&) noexcept = default;
127 BitSet& operator=(BitSet const&) noexcept = default;
128 BitSet& operator=(BitSet&&) noexcept = default;
129
130 template <typename T>
131 BitSet(T first, T last) : BitSet() {
132 LIBSEMIGROUPS_ASSERT(first <= last);
133 size_t const K = std::distance(first, last);
134 if (K > size()) {
135 LIBSEMIGROUPS_EXCEPTION("the size of the container is {}, trying to "
136 "initialize with {} items",
137 size(),
138 K)
139 }
140 auto it = first;
141 for (size_t i = 0; i < K; ++i, ++it) {
142 set(i, *it);
143 }
144 }
145
146 ~BitSet() = default;
147
148 // Could be static
149 constexpr size_t size() const noexcept {
150 return N;
151 }
152
153 static constexpr size_t max_size() noexcept {
154#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
155 return 64;
156#else
157 return 32;
158#endif
159 }
160
161 bool operator<(BitSet const& that) const noexcept {
162 clear_hi_bits();
163 that.clear_hi_bits();
164 return _block < that._block;
165 }
166
167 bool operator==(BitSet const& that) const noexcept {
168 clear_hi_bits();
169 that.clear_hi_bits();
170 return _block == that._block;
171 }
172
173 bool operator!=(BitSet const& that) const noexcept {
174 return !(*this == that);
175 }
176
177 void operator&=(BitSet const& that) const noexcept {
178 _block &= that._block;
179 }
180
181 BitSet<N> operator&(BitSet const& that) const noexcept {
182 return BitSet(_block & that._block);
183 }
184
185 void operator|=(BitSet const& that) const noexcept {
186 _block |= that._block;
187 }
188
189 bool test(size_t pos) const noexcept {
190 LIBSEMIGROUPS_ASSERT(pos < N);
191 return _block & mask(pos);
192 }
193
194 bool operator[](size_t pos) const noexcept {
195 return test(pos);
196 }
197
198 BitSet& set() noexcept {
199 _block = ~0;
200 return *this;
201 }
202
203 BitSet& set(size_t pos, bool value = true) noexcept {
204 LIBSEMIGROUPS_ASSERT(pos < N);
205 if (value) {
206 _block |= mask(pos);
207 } else {
208 _block &= ~mask(pos);
209 }
210 return *this;
211 }
212
213 BitSet& set(size_t first, size_t last, bool value) noexcept {
214 LIBSEMIGROUPS_ASSERT(first < N);
215 LIBSEMIGROUPS_ASSERT(last <= N);
216 LIBSEMIGROUPS_ASSERT(first < last);
217 block_type m = ~0;
218 m = (m >> first);
219 m = (m << (first + (block_count() - last)));
220 m = (m >> (block_count() - last));
221 if (value) {
222 _block |= m;
223 } else {
224 _block &= ~m;
225 }
226 return *this;
227 }
228
229 BitSet& reset() noexcept {
230 _block = 0;
231 return *this;
232 }
233
234 BitSet& reset(size_t pos) noexcept {
235 LIBSEMIGROUPS_ASSERT(pos < N);
236 _block &= ~mask(pos);
237 return *this;
238 }
239
240 BitSet& reset(size_t first, size_t last) {
241 LIBSEMIGROUPS_ASSERT(first < N);
242 LIBSEMIGROUPS_ASSERT(last <= N);
243 LIBSEMIGROUPS_ASSERT(first < last);
244 return set(first, last, false);
245 }
246
247 size_t count() const noexcept {
248 clear_hi_bits();
249 return COUNT_TRUES_BLOCK(_block);
250 }
251
252 template <typename S>
253 void apply(S&& func) const {
254#if LIBSEMIGROUPS_USE_CLZLL && defined(LIBSEMIGROUPS_HAVE___BUILTIN_CLZLL)
255 block_type block = _block;
256 while (block != 0) {
257 block_type t = block & -block;
258 size_t i = static_cast<size_t>(__builtin_ctzll(block));
259 if (i >= size()) {
260 break;
261 }
262 func(i);
263 block ^= t;
264 }
265#else
266 for (size_t i = 0; i < size(); ++i) {
267 if (test(i)) {
268 func(i);
269 }
270 }
271#endif
272 }
273
274 block_type to_int() const noexcept {
275 clear_hi_bits();
276 return _block;
277 }
278
279 friend std::ostringstream& operator<<(std::ostringstream& os,
280 BitSet<N> const& bs) {
281 for (size_t i = 0; i < N; ++i) {
282 os << bs.test(i);
283 }
284 return os;
285 }
286
287 friend std::ostream& operator<<(std::ostream& os, BitSet<N> const& bs) {
288 os << detail::to_string(bs);
289 return os;
290 }
291
292 private:
293 void clear_hi_bits() const noexcept {
294 size_t s = block_count() - N;
295 _block = _block << s;
296 _block = _block >> s;
297 }
298
299 constexpr size_t block_count() const noexcept {
300 return sizeof(block_type) * CHAR_BIT;
301 }
302
303 constexpr block_type mask(size_t i) const noexcept {
304 // LIBSEMIGROUPS_ASSERT(i < size());
305 return static_cast<block_type>(MASK[i]);
306 }
307
308 static constexpr uint64_t MASK[64] = {0x1,
309 0x2,
310 0x4,
311 0x8,
312 0x10,
313 0x20,
314 0x40,
315 0x80,
316 0x100,
317 0x200,
318 0x400,
319 0x800,
320 0x1000,
321 0x2000,
322 0x4000,
323 0x8000,
324 0x10000,
325 0x20000,
326 0x40000,
327 0x80000,
328 0x100000,
329 0x200000,
330 0x400000,
331 0x800000,
332 0x1000000,
333 0x2000000,
334 0x4000000,
335 0x8000000,
336 0x10000000,
337 0x20000000,
338 0x40000000,
339 0x80000000,
340 0x100000000,
341 0x200000000,
342 0x400000000,
343 0x800000000,
344 0x1000000000,
345 0x2000000000,
346 0x4000000000,
347 0x8000000000,
348 0x10000000000,
349 0x20000000000,
350 0x40000000000,
351 0x80000000000,
352 0x100000000000,
353 0x200000000000,
354 0x400000000000,
355 0x800000000000,
356 0x1000000000000,
357 0x2000000000000,
358 0x4000000000000,
359 0x8000000000000,
360 0x10000000000000,
361 0x20000000000000,
362 0x40000000000000,
363 0x80000000000000,
364 0x100000000000000,
365 0x200000000000000,
366 0x400000000000000,
367 0x800000000000000,
368 0x1000000000000000,
369 0x2000000000000000,
370 0x4000000000000000,
371 0x8000000000000000};
372 mutable block_type _block;
373 };
374
375 template <size_t N>
376 constexpr uint64_t BitSet<N>::MASK[64];
377
378 namespace detail {
379 template <typename T>
380 struct IsBitSetHelper : std::false_type {};
381
382 template <size_t N>
383 struct IsBitSetHelper<BitSet<N>> : std::true_type {};
384 } // namespace detail
385
386 template <typename T>
387 static constexpr bool IsBitSet = detail::IsBitSetHelper<T>::value;
388
389 namespace detail {
390 struct LessBitSet {
391 // not noexcept because std::bitset<N>::to_ullong throws
392 // std::overflow_error if N exceeds the capacity of a unsigned long
393 // long.
394 template <size_t N>
395 bool operator()(std::bitset<N> const& x, std::bitset<N> const& y) const {
396 return x.to_ullong() < y.to_ullong();
397 }
398
399 template <size_t N>
400 bool operator()(BitSet<N> const& x, BitSet<N> const& y) const noexcept {
401 return x < y;
402 }
403 };
404 } // namespace detail
405} // namespace libsemigroups
406
407namespace std {
408 template <size_t N>
409 struct hash<libsemigroups::BitSet<N>> {
410 using block_type = typename libsemigroups::BitSet<N>::block_type;
411 size_t operator()(libsemigroups::BitSet<N> const& bs) const noexcept(
412 std::is_nothrow_default_constructible<hash<block_type>>::value) {
413 return hash<block_type>()(bs.to_int());
414 }
415 };
416} // namespace std
417#endif // LIBSEMIGROUPS_BITSET_HPP_
T distance(T... args)
#define LIBSEMIGROUPS_EXCEPTION(...)
Throw a LibsemigroupsException.
Definition exception.hpp:95
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T operator()(T... args)
T to_ullong(T... args)