9#include <initializer_list>
31 static constexpr size_t npos = size_t(-1);
40 constexpr reference&
operator=(
const reference& other)
noexcept {
return *
this = bool(other); }
41 constexpr reference&
flip() noexcept {
46 constexpr operator bool() const noexcept {
return bitset_->test(i_); }
47 constexpr bool operator~() const noexcept {
return !bitset_->test(i_); }
71 constexpr
size_t operator*() const noexcept {
return i_; }
74 i_ = bitset_->next(i_ + 1);
89 const Bitset* bitset_ =
nullptr;
97 constexpr Bitset() noexcept = default;
98 constexpr
Bitset(
std::initializer_list<
size_t> bits) {
106 constexpr Bitset(
size_t num_bits,
bool value) {
107 if (num_bits == 0)
return;
112 for (
size_t i = 0; i != full; ++i)
114 if (
auto rest = num_bits %
Bits_Per_Word) w[full] = (uint64_t(1) << rest) - 1;
117 : num_words_(other.num_words_) {
119 auto heap = new uint64_t[num_words_];
120 std::copy_n(other.words(), num_words_, heap);
121 data_ = bitcast_resize<uint64_t>(heap);
128 if (
on_heap())
delete[] words();
136 swap(b1.data_, b2.data_);
137 swap(b1.num_words_, b2.num_words_);
143 [[nodiscard]]
constexpr bool test(
size_t i)
const noexcept {
145 return w < num_words_ && (words()[w] & bit(i)) != 0;
147 [[nodiscard]]
constexpr bool operator[](
size_t i)
const noexcept {
return test(i); }
151 [[nodiscard]]
constexpr size_t next(
size_t i)
const noexcept {
153 if (w >= num_words_)
return npos;
155 auto words = this->words();
156 for (
auto word = words[w] & (~uint64_t(0) << (i %
Bits_Per_Word));; word = words[w]) {
157 if (word)
return w *
Bits_Per_Word + size_t(std::countr_zero(word));
158 if (++w == num_words_)
return npos;
178 if (
auto w = i /
Bits_Per_Word; w < num_words_) words()[w] &= ~bit(i);
183 if (
on_heap())
delete[] words();
192 [[nodiscard]]
constexpr size_t count() const noexcept {
194 for (
size_t i = 0, e = num_words_; i != e; ++i)
195 res +=
size_t(std::popcount(words()[i]));
198 [[nodiscard]]
constexpr bool any() const noexcept {
return !zeros(words(), num_words_); }
199 [[nodiscard]]
constexpr bool none() const noexcept {
return zeros(words(), num_words_); }
203 [[nodiscard]]
constexpr bool on_heap() const noexcept {
return num_words_ != 1; }
210 auto e = other.used();
212 for (
size_t i = 0; i != e; ++i)
213 words()[i] |= other.words()[i];
217 auto e = std::min(num_words_, other.num_words_);
218 for (
size_t i = 0; i != e; ++i)
219 words()[i] &= other.words()[i];
220 for (
size_t i = e, n = num_words_; i != n; ++i)
225 auto e = other.used();
227 for (
size_t i = 0; i != e; ++i)
228 words()[i] ^= other.words()[i];
232 for (
size_t i = 0, e = std::min(num_words_, other.num_words_); i != e; ++i)
233 words()[i] &= ~other.words()[i];
247 auto e = std::min(num_words_, other.num_words_);
248 for (
size_t i = 0; i != e; ++i)
249 if (words()[i] != other.words()[i])
return false;
250 return zeros(words() + e, num_words_ - e) && zeros(other.words() + e, other.num_words_ - e);
255 auto e = std::min(num_words_, other.num_words_);
256 for (
size_t i = 0; i != e; ++i)
257 if (words()[i] & ~other.words()[i])
return false;
258 return zeros(words() + e, num_words_ - e);
263 for (
size_t i = 0, e = std::min(num_words_, other.num_words_); i != e; ++i)
264 if (words()[i] & other.words()[i])
return true;
273 [[nodiscard]]
constexpr iterator end() const noexcept {
return {}; }
278 [[nodiscard]]
constexpr size_t hash() const noexcept {
280 for (
size_t i = 0, e = used(); i != e; ++i) {
281 auto word = words()[i];
294 friend H AbslHashValue(H h,
const Bitset& bitset) {
295 return H::combine(std::move(h), bitset.
hash());
308 static constexpr uint64_t bit(
size_t i)
noexcept {
return uint64_t(1) << (i %
Bits_Per_Word); }
310 static constexpr bool zeros(
const uint64_t* words,
size_t num_words)
noexcept {
311 for (
size_t i = 0; i != num_words; ++i)
312 if (words[i])
return false;
317 constexpr const uint64_t* words() const noexcept {
322 constexpr size_t used() const noexcept {
323 for (
auto i = num_words_; i-- != 0;)
324 if (words()[i])
return i + 1;
328 constexpr void grow(
size_t num_words) {
329 if (num_words <= num_words_)
return;
331 num_words = std::max(num_words, num_words_ * 2);
332 auto heap =
new uint64_t[num_words]();
333 std::copy_n(words(), num_words_, heap);
334 if (
on_heap())
delete[] words();
336 num_words_ = num_words;
340 size_t num_words_ = 1;
343static_assert(
sizeof(
void*) != 8 ||
sizeof(
Bitset) == 16,
"Bitset should stay two machine words");
344static_assert(std::forward_iterator<Bitset::iterator>);
345static_assert(std::ranges::forward_range<Bitset>);
351struct std::hash<
fe::Bitset> {
352 constexpr size_t operator()(
const fe::Bitset& bitset)
const noexcept {
return bitset.
hash(); }
Iterates over the indices of all set bits in ascending order.
constexpr iterator & operator++() noexcept
constexpr iterator() noexcept=default
std::forward_iterator_tag iterator_category
std::ptrdiff_t difference_type
constexpr iterator operator++(int) noexcept
constexpr bool operator==(iterator other) const noexcept
Proxy that Bitset::operator[] hands out to read/write the bit it refers to.
constexpr bool operator~() const noexcept
constexpr reference & operator=(bool b) noexcept
constexpr reference & flip() noexcept
constexpr reference & operator=(const reference &other) noexcept
A dynamically growing set of bits with small storage optimization.
constexpr Bitset(const Bitset &other)
constexpr size_t capacity() const noexcept
Number of bits available without growing.
constexpr bool none() const noexcept
constexpr ~Bitset() noexcept
constexpr size_t next(size_t i) const noexcept
Index of the first bit that is set at or after i - or npos, if there is none.
constexpr Bitset & operator^=(const Bitset &other)
constexpr Bitset & clear(size_t i) noexcept
constexpr bool test(size_t i) const noexcept
friend constexpr void swap(Bitset &b1, Bitset &b2) noexcept
constexpr reference operator[](size_t i) noexcept
friend constexpr Bitset operator-(Bitset b1, const Bitset &b2)
constexpr Bitset() noexcept=default
constexpr Bitset & operator|=(const Bitset &other)
constexpr iterator end() const noexcept
constexpr Bitset(size_t num_bits, bool value)
Constructs the set of the first num_bits bits; with value false the empty set that has room for them ...
constexpr iterator begin() const noexcept
constexpr bool any() const noexcept
constexpr bool intersects(const Bitset &other) const noexcept
Do this and other have at least one bit in common?
constexpr size_t hash() const noexcept
constexpr Bitset & clear() noexcept
Clears all bits and releases the heap storage again.
static constexpr size_t Inline_Bits
Number of bits available without allocating.
constexpr size_t count() const noexcept
constexpr bool operator==(const Bitset &other) const noexcept
friend constexpr Bitset operator&(Bitset b1, const Bitset &b2)
constexpr Bitset & set(size_t i, bool b)
static constexpr size_t Bits_Per_Word
Number of bits in one word.
constexpr Bitset(Bitset &&other) noexcept
constexpr Bitset & operator=(Bitset other) noexcept
constexpr Bitset & flip(size_t i)
constexpr bool operator[](size_t i) const noexcept
constexpr Bitset & operator-=(const Bitset &other) noexcept
friend std::ostream & operator<<(std::ostream &os, const Bitset &bitset)
static constexpr size_t npos
Returned by Bitset::next if there is no set bit.
constexpr Bitset & set(size_t i)
friend constexpr Bitset operator^(Bitset b1, const Bitset &b2)
constexpr bool on_heap() const noexcept
Outgrown the inline storage?
constexpr Bitset & operator&=(const Bitset &other) noexcept
friend constexpr Bitset operator|(Bitset b1, const Bitset &b2)
constexpr bool subset_of(const Bitset &other) const noexcept
Is every bit set in this also set in other?
constexpr D bitcast_resize(const S &src) noexcept
A bitcast from src of type S to D, supporting different sizes.
constexpr size_t hash_begin() noexcept
Seeds a hash chain with the FNV-1 offset basis.
constexpr size_t hash_combine(size_t seed, T v) noexcept
Mixes v into seed word-wise, reusing the FNV-1 prime as multiplier.
constexpr size_t operator()(const Bitset &bitset) const noexcept