9#include <initializer_list>
32 static constexpr size_t npos = size_t(-1);
41 constexpr reference&
operator=(
const reference& other)
noexcept {
return *
this = bool(other); }
42 constexpr reference&
flip() noexcept {
47 constexpr operator bool() const noexcept {
return bitset_->test(i_); }
48 constexpr bool operator~() const noexcept {
return !bitset_->test(i_); }
72 constexpr
size_t operator*() const noexcept {
return i_; }
75 i_ = bitset_->next(i_ + 1);
90 const Bitset* bitset_ =
nullptr;
98 constexpr Bitset() noexcept = default;
99 constexpr
Bitset(
std::initializer_list<
size_t> bits) {
104 : num_words_(other.num_words_) {
106 auto heap = new uint64_t[num_words_];
107 std::copy_n(other.words(), num_words_, heap);
108 data_ = bitcast_resize<uint64_t>(heap);
115 if (
on_heap())
delete[] words();
123 swap(b1.data_, b2.data_);
124 swap(b1.num_words_, b2.num_words_);
130 [[nodiscard]]
constexpr bool test(
size_t i)
const noexcept {
132 return w < num_words_ && (words()[w] & bit(i)) != 0;
134 [[nodiscard]]
constexpr bool operator[](
size_t i)
const noexcept {
return test(i); }
138 [[nodiscard]]
constexpr size_t next(
size_t i)
const noexcept {
140 if (w >= num_words_)
return npos;
142 auto words = this->words();
143 for (
auto word = words[w] & (~uint64_t(0) << (i %
Bits_Per_Word));; word = words[w]) {
144 if (word)
return w *
Bits_Per_Word + size_t(std::countr_zero(word));
145 if (++w == num_words_)
return npos;
165 if (
auto w = i /
Bits_Per_Word; w < num_words_) words()[w] &= ~bit(i);
170 if (
on_heap())
delete[] words();
179 [[nodiscard]]
constexpr size_t count() const noexcept {
181 for (
size_t i = 0, e = num_words_; i != e; ++i)
182 res +=
size_t(std::popcount(words()[i]));
185 [[nodiscard]]
constexpr bool any() const noexcept {
return !zeros(words(), num_words_); }
186 [[nodiscard]]
constexpr bool none() const noexcept {
return zeros(words(), num_words_); }
190 [[nodiscard]]
constexpr bool on_heap() const noexcept {
return num_words_ != 1; }
197 auto e = other.used();
199 for (
size_t i = 0; i != e; ++i)
200 words()[i] |= other.words()[i];
204 auto e = std::min(num_words_, other.num_words_);
205 for (
size_t i = 0; i != e; ++i)
206 words()[i] &= other.words()[i];
207 for (
size_t i = e, n = num_words_; i != n; ++i)
212 auto e = other.used();
214 for (
size_t i = 0; i != e; ++i)
215 words()[i] ^= other.words()[i];
219 for (
size_t i = 0, e = std::min(num_words_, other.num_words_); i != e; ++i)
220 words()[i] &= ~other.words()[i];
234 auto e = std::min(num_words_, other.num_words_);
235 for (
size_t i = 0; i != e; ++i)
236 if (words()[i] != other.words()[i])
return false;
237 return zeros(words() + e, num_words_ - e) && zeros(other.words() + e, other.num_words_ - e);
242 auto e = std::min(num_words_, other.num_words_);
243 for (
size_t i = 0; i != e; ++i)
244 if (words()[i] & ~other.words()[i])
return false;
245 return zeros(words() + e, num_words_ - e);
250 for (
size_t i = 0, e = std::min(num_words_, other.num_words_); i != e; ++i)
251 if (words()[i] & other.words()[i])
return true;
260 [[nodiscard]]
constexpr iterator end() const noexcept {
return {}; }
265 [[nodiscard]]
constexpr size_t hash() const noexcept {
267 for (
size_t i = 0, e = used(); i != e; ++i) {
268 auto word = words()[i];
281 friend H AbslHashValue(H h,
const Bitset& bitset) {
282 return H::combine(std::move(h), bitset.
hash());
289 void dump()
const { std::cout << (*this) << std::endl; }
293 for (
auto sep =
"";
auto i : bitset) {
302 static constexpr uint64_t bit(
size_t i)
noexcept {
return uint64_t(1) << (i %
Bits_Per_Word); }
304 static constexpr bool zeros(
const uint64_t* words,
size_t num_words)
noexcept {
305 for (
size_t i = 0; i != num_words; ++i)
306 if (words[i])
return false;
311 constexpr const uint64_t* words() const noexcept {
316 constexpr size_t used() const noexcept {
317 for (
auto i = num_words_; i-- != 0;)
318 if (words()[i])
return i + 1;
322 constexpr void grow(
size_t num_words) {
323 if (num_words <= num_words_)
return;
325 num_words = std::max(num_words, num_words_ * 2);
326 auto heap =
new uint64_t[num_words]();
327 std::copy_n(words(), num_words_, heap);
328 if (
on_heap())
delete[] words();
330 num_words_ = num_words;
334 size_t num_words_ = 1;
337static_assert(
sizeof(
void*) != 8 ||
sizeof(
Bitset) == 16,
"Bitset should stay two machine words");
338static_assert(std::forward_iterator<Bitset::iterator>);
339static_assert(std::ranges::forward_range<Bitset>);
345struct std::hash<
fe::Bitset> {
346 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 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