libsemigroups  v3.6.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
bitset.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2020-2026 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
21#ifndef LIBSEMIGROUPS_BITSET_HPP_
22#define LIBSEMIGROUPS_BITSET_HPP_
23
24#include <bitset> // for bitset
25#include <climits> // for CHAR_BIT
26#include <cstddef> // for size_t
27#include <cstdint> // for uint64_t, uint_fast16_t, uint_fast32_t
28#include <functional> // for hash
29#include <iosfwd> // for ostringstream, ostream
30#include <iterator> // for distance
31#include <memory> // for hash
32#include <type_traits> // for conditional, conditional_t, false_type
33
34#include "config.hpp" // for LIBSEMIGROUPS_SIZEOF_VOID_P
35#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
36#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
37
38#include "detail/string.hpp" // for detail::to_string
39
40namespace libsemigroups {
41
42 // The code below for popcnt is borrowed/adapted from GAP.
43
44#if LIBSEMIGROUPS_USE_POPCNT && defined(LIBSEMIGROUPS_HAVE___BUILTIN_POPCOUNTL)
45 template <typename T>
46 static inline size_t COUNT_TRUES_BLOCK(T block) {
47 return __builtin_popcountl(block);
48 }
49#else
50#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
51 template <typename T>
52 static inline auto COUNT_TRUES_BLOCK(T block)
53 -> std::enable_if_t<sizeof(T) == 8, size_t> {
54 block
55 = (block & 0x5555555555555555L) + ((block >> 1) & 0x5555555555555555L);
56 block
57 = (block & 0x3333333333333333L) + ((block >> 2) & 0x3333333333333333L);
58 block = (block + (block >> 4)) & 0x0f0f0f0f0f0f0f0fL;
59 block = (block + (block >> 8));
60 block = (block + (block >> 16));
61 block = (block + (block >> 32)) & 0x00000000000000ffL;
62 return block;
63 }
64#endif
65
66 template <typename T>
67 static inline auto COUNT_TRUES_BLOCK(T block)
68 -> std::enable_if_t<sizeof(T) == 4, size_t> {
69 block = (block & 0x55555555) + ((block >> 1) & 0x55555555);
70 block = (block & 0x33333333) + ((block >> 2) & 0x33333333);
71 block = (block + (block >> 4)) & 0x0f0f0f0f;
72 block = (block + (block >> 8));
73 block = (block + (block >> 16)) & 0x000000ff;
74 return block;
75 }
76
77 template <typename T>
78 static inline auto COUNT_TRUES_BLOCK(T block)
79 -> std::enable_if_t<sizeof(T) == 2, size_t> {
80 block = (block & 0x5555) + ((block >> 1) & 0x5555);
81 block = (block & 0x3333) + ((block >> 2) & 0x3333);
82 block = (block + (block >> 4)) & 0x0f0f;
83 block = (block + (block >> 8)) & 0x00ff;
84 return block;
85 }
86
87 template <typename T>
88 static inline auto COUNT_TRUES_BLOCK(T block)
89 -> std::enable_if_t<sizeof(T) == 1, size_t> {
90 static constexpr std::array<size_t, 256> const lookup = {
91 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
92 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
93 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
94 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
95 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
96 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
97 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
98 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
99 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
100 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
101 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
102 return lookup[block];
103 }
104#endif
105
106 template <size_t N>
107 class BitSet {
108 static_assert(N > 0, "BitSet does not support 0 entries");
109#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
110 static_assert(N <= 64, "BitSet does not support more than 64 entries");
111#else
112 static_assert(N <= 32, "BitSet does not support more than 32 entries");
113#endif
114
115 public:
116 using block_type = std::conditional_t<
117 N <= 8,
118 uint_fast8_t,
119 std::conditional_t<
120 N <= 16,
121 uint_fast16_t,
122 std::conditional_t<N <= 32, uint_fast32_t, uint64_t>>>;
123
124 explicit constexpr BitSet(block_type block) noexcept : _block(block) {}
125 constexpr BitSet() noexcept : BitSet(0) {}
126 constexpr BitSet(BitSet const&) noexcept = default;
127 constexpr BitSet(BitSet&&) noexcept = default;
128 BitSet& operator=(BitSet const&) noexcept = default;
129 BitSet& operator=(BitSet&&) noexcept = default;
130
131 template <typename T>
132 BitSet(T first, T last);
133
134 ~BitSet() = default;
135
136 // Could be static
137 constexpr size_t size() const noexcept {
138 return N;
139 }
140
141 static constexpr size_t max_size() noexcept {
142#if LIBSEMIGROUPS_SIZEOF_VOID_P == 8
143 return 64;
144#else
145 return 32;
146#endif
147 }
148
149 bool operator<(BitSet const& that) const noexcept {
150 clear_hi_bits();
151 that.clear_hi_bits();
152 return _block < that._block;
153 }
154
155 bool operator==(BitSet const& that) const noexcept {
156 clear_hi_bits();
157 that.clear_hi_bits();
158 return _block == that._block;
159 }
160
161 bool operator!=(BitSet const& that) const noexcept {
162 return !(*this == that);
163 }
164
165 void operator&=(BitSet const& that) const noexcept {
166 _block &= that._block;
167 }
168
169 BitSet<N> operator&(BitSet const& that) const noexcept {
170 return BitSet(_block & that._block);
171 }
172
173 void operator|=(BitSet const& that) const noexcept {
174 _block |= that._block;
175 }
176
177 bool test(size_t pos) const noexcept {
178 LIBSEMIGROUPS_ASSERT(pos < N);
179 return _block & mask(pos);
180 }
181
182 bool operator[](size_t pos) const noexcept {
183 return test(pos);
184 }
185
186 BitSet& set() noexcept {
187 _block = ~0;
188 return *this;
189 }
190
191 BitSet& set(size_t pos, bool value = true) noexcept;
192
193 BitSet& set(size_t first, size_t last, bool value) noexcept;
194
195 BitSet& reset() noexcept {
196 _block = 0;
197 return *this;
198 }
199
200 BitSet& reset(size_t pos) noexcept {
201 LIBSEMIGROUPS_ASSERT(pos < N);
202 _block &= ~mask(pos);
203 return *this;
204 }
205
206 BitSet& reset(size_t first, size_t last);
207
208 size_t count() const noexcept {
209 clear_hi_bits();
210 return COUNT_TRUES_BLOCK(_block);
211 }
212
213 template <typename S>
214 void apply(S&& func) const;
215
216 block_type to_int() const noexcept {
217 clear_hi_bits();
218 return _block;
219 }
220
221 private:
222 void clear_hi_bits() const noexcept {
223 size_t s = block_count() - N;
224 _block = _block << s;
225 _block = _block >> s;
226 }
227
228 constexpr size_t block_count() const noexcept {
229 return sizeof(block_type) * CHAR_BIT;
230 }
231
232 constexpr block_type mask(size_t i) const noexcept {
233 // LIBSEMIGROUPS_ASSERT(i < size());
234 return static_cast<block_type>(MASK[i]);
235 }
236
237 static constexpr uint64_t MASK[64] = {0x1,
238 0x2,
239 0x4,
240 0x8,
241 0x10,
242 0x20,
243 0x40,
244 0x80,
245 0x100,
246 0x200,
247 0x400,
248 0x800,
249 0x1000,
250 0x2000,
251 0x4000,
252 0x8000,
253 0x10000,
254 0x20000,
255 0x40000,
256 0x80000,
257 0x100000,
258 0x200000,
259 0x400000,
260 0x800000,
261 0x1000000,
262 0x2000000,
263 0x4000000,
264 0x8000000,
265 0x10000000,
266 0x20000000,
267 0x40000000,
268 0x80000000,
269 0x100000000,
270 0x200000000,
271 0x400000000,
272 0x800000000,
273 0x1000000000,
274 0x2000000000,
275 0x4000000000,
276 0x8000000000,
277 0x10000000000,
278 0x20000000000,
279 0x40000000000,
280 0x80000000000,
281 0x100000000000,
282 0x200000000000,
283 0x400000000000,
284 0x800000000000,
285 0x1000000000000,
286 0x2000000000000,
287 0x4000000000000,
288 0x8000000000000,
289 0x10000000000000,
290 0x20000000000000,
291 0x40000000000000,
292 0x80000000000000,
293 0x100000000000000,
294 0x200000000000000,
295 0x400000000000000,
296 0x800000000000000,
297 0x1000000000000000,
298 0x2000000000000000,
299 0x4000000000000000,
300 0x8000000000000000};
301 mutable block_type _block;
302 };
303
304 template <size_t N>
305 constexpr uint64_t BitSet<N>::MASK[64];
306
307 namespace detail {
308 template <typename T>
309 struct IsBitSetHelper : std::false_type {};
310
311 template <size_t N>
312 struct IsBitSetHelper<BitSet<N>> : std::true_type {};
313 } // namespace detail
314
315 template <typename T>
316 static constexpr bool IsBitSet = detail::IsBitSetHelper<T>::value;
317
318 template <size_t N>
319 std::ostringstream& operator<<(std::ostringstream& os, BitSet<N> const& bs);
320
321 template <size_t N>
322 std::ostream& operator<<(std::ostream& os, BitSet<N> const& bs) {
323 os << detail::to_string(bs);
324 return os;
325 }
326
327 namespace detail {
328 struct LessBitSet {
329 // not noexcept because std::bitset<N>::to_ullong throws
330 // std::overflow_error if N exceeds the capacity of a unsigned long
331 // long.
332 template <size_t N>
333 bool operator()(std::bitset<N> const& x, std::bitset<N> const& y) const {
334 return x.to_ullong() < y.to_ullong();
335 }
336
337 template <size_t N>
338 bool operator()(BitSet<N> const& x, BitSet<N> const& y) const noexcept {
339 return x < y;
340 }
341 };
342 } // namespace detail
343} // namespace libsemigroups
344
345namespace std {
346 template <size_t N>
347 struct hash<libsemigroups::BitSet<N>> {
348 using block_type = typename libsemigroups::BitSet<N>::block_type;
349 size_t operator()(libsemigroups::BitSet<N> const& bs) const noexcept(
350 std::is_nothrow_default_constructible<hash<block_type>>::value) {
351 return hash<block_type>()(bs.to_int());
352 }
353 };
354} // namespace std
355
356#include "bitset.tpp"
357
358#endif // LIBSEMIGROUPS_BITSET_HPP_
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
T operator()(T... args)
T to_ullong(T... args)