// // Copyright (c) 2019 Maxime Pinard // // Distributed under the MIT license // See accompanying file LICENSE or copy at // https://opensource.org/licenses/MIT // #ifndef DYNAMIC_BITSET_DYNAMIC_BITSET_HPP #define DYNAMIC_BITSET_DYNAMIC_BITSET_HPP #include #include #include #include #include #include #include #include #include #include #ifndef DYNAMIC_BITSET_NO_LIBPOPCNT # if __has_include() # include # define DYNAMIC_BITSET_USE_LIBPOPCNT # endif #endif #if defined(__clang__) # define DYNAMIC_BITSET_CLANG # ifdef __has_builtin # if __has_builtin(__builtin_popcount) && __has_builtin(__builtin_popcountl) \ && __has_builtin(__builtin_popcountll) # define DYNAMIC_BITSET_CLANG_builtin_popcount # endif # if __has_builtin(__builtin_ctz) && __has_builtin(__builtin_ctzl) \ && __has_builtin(__builtin_ctzll) # define DYNAMIC_BITSET_CLANG_builtin_ctz # endif # endif #elif defined(__GNUC__) # define DYNAMIC_BITSET_GCC #elif defined(_MSC_VER) # define DYNAMIC_BITSET_MSVC # include # pragma intrinsic(_BitScanForward) # if defined(_M_X64) || defined(_M_ARM64) # define DYNAMIC_BITSET_MSVC_64 # pragma intrinsic(_BitScanForward64) # else # endif #endif template> class dynamic_bitset { static_assert(std::is_unsigned::value, "Block is not an unsigned integral type"); public: typedef size_t size_type; typedef Block block_type; typedef Allocator allocator_type; static constexpr size_type bits_per_block = CHAR_BIT * sizeof(block_type); static constexpr size_type npos = std::numeric_limits::max(); class reference { public: constexpr reference(dynamic_bitset& bitset, size_type bit_pos); constexpr reference(const reference&) noexcept = default; constexpr reference(reference&&) noexcept = default; ~reference() noexcept = default; constexpr reference& operator=(bool v); constexpr reference& operator=(const reference& rhs); constexpr reference& operator=(reference&& rhs) noexcept; constexpr reference& operator&=(bool v); constexpr reference& operator|=(bool v); constexpr reference& operator^=(bool v); constexpr reference& operator-=(bool v); [[nodiscard]] constexpr bool operator~() const; [[nodiscard]] constexpr operator bool() const; constexpr void operator&() = delete; constexpr reference& set(); constexpr reference& reset(); constexpr reference& flip(); constexpr reference& assign(bool v); private: block_type& m_block; block_type m_mask; }; typedef bool const_reference; // copy/move constructors = default constexpr dynamic_bitset(const dynamic_bitset& other) = default; constexpr dynamic_bitset(dynamic_bitset&& other) noexcept = default; constexpr dynamic_bitset& operator=( const dynamic_bitset& other) = default; constexpr dynamic_bitset& operator=( dynamic_bitset&& other) noexcept = default; // other constructors constexpr explicit dynamic_bitset(const allocator_type& allocator = allocator_type()); constexpr explicit dynamic_bitset(size_type nbits, unsigned long long init_val = 0, const allocator_type& allocator = allocator_type()); constexpr dynamic_bitset(std::initializer_list init_vals, const allocator_type& allocator = allocator_type()); // string constructors template constexpr explicit dynamic_bitset( std::basic_string_view<_CharT, _Traits> str, typename std::basic_string_view<_CharT, _Traits>::size_type pos = 0, typename std::basic_string_view<_CharT, _Traits>::size_type n = std::basic_string_view<_CharT, _Traits>::npos, _CharT zero = _CharT('0'), _CharT one = _CharT('1'), const allocator_type& allocator = allocator_type()); template constexpr explicit dynamic_bitset( const std::basic_string<_CharT, _Traits, _Alloc>& str, typename std::basic_string<_CharT, _Traits, _Alloc>::size_type pos = 0, typename std::basic_string<_CharT, _Traits, _Alloc>::size_type n = std::basic_string<_CharT, _Traits, _Alloc>::npos, _CharT zero = _CharT('0'), _CharT one = _CharT('1'), const allocator_type& allocator = allocator_type()); template constexpr explicit dynamic_bitset( const _CharT* str, typename std::basic_string<_CharT>::size_type pos = 0, typename std::basic_string<_CharT>::size_type n = std::basic_string<_CharT>::npos, _CharT zero = _CharT('0'), _CharT one = _CharT('1'), const allocator_type& allocator = allocator_type()); // destructor ~dynamic_bitset() noexcept = default; // size changing operations constexpr void resize(size_type nbits, bool value = false); constexpr void clear(); constexpr void push_back(bool value); constexpr void pop_back(); constexpr void append(block_type block); constexpr void append(std::initializer_list blocks); template constexpr void append(BlockInputIterator first, BlockInputIterator last); // bitset operations constexpr dynamic_bitset& operator&=( const dynamic_bitset& rhs); constexpr dynamic_bitset& operator|=( const dynamic_bitset& rhs); constexpr dynamic_bitset& operator^=( const dynamic_bitset& rhs); constexpr dynamic_bitset& operator-=( const dynamic_bitset& rhs); constexpr dynamic_bitset& operator<<=(size_type shift); constexpr dynamic_bitset& operator>>=(size_type shift); [[nodiscard]] constexpr dynamic_bitset operator<<(size_type shift) const; [[nodiscard]] constexpr dynamic_bitset operator>>(size_type shift) const; [[nodiscard]] constexpr dynamic_bitset operator~() const; // bit operations constexpr dynamic_bitset& set(size_type pos, size_type len, bool value); constexpr dynamic_bitset& set(size_type pos, bool value = true); constexpr dynamic_bitset& set(); constexpr dynamic_bitset& reset(size_type pos, size_type len); constexpr dynamic_bitset& reset(size_type pos); constexpr dynamic_bitset& reset(); constexpr dynamic_bitset& flip(size_type pos, size_type len); constexpr dynamic_bitset& flip(size_type pos); constexpr dynamic_bitset& flip(); [[nodiscard]] constexpr bool test(size_type pos) const; [[nodiscard]] constexpr bool test_set(size_type pos, bool value = true); [[nodiscard]] constexpr bool all() const; [[nodiscard]] constexpr bool any() const; [[nodiscard]] constexpr bool none() const; [[nodiscard]] constexpr size_type count() const noexcept; // subscript operators [[nodiscard]] constexpr reference operator[](size_type pos); [[nodiscard]] constexpr const_reference operator[](size_type pos) const; //container-like functions [[nodiscard]] constexpr size_type size() const noexcept; [[nodiscard]] constexpr size_type num_blocks() const noexcept; [[nodiscard]] constexpr bool empty() const noexcept; [[nodiscard]] constexpr size_type capacity() const noexcept; constexpr void reserve(size_type num_bits); constexpr void shrink_to_fit(); // subsets [[nodiscard]] constexpr bool is_subset_of(const dynamic_bitset& bitset) const; [[nodiscard]] constexpr bool is_proper_subset_of( const dynamic_bitset& bitset) const; [[nodiscard]] constexpr bool intersects(const dynamic_bitset& bitset) const; // find functions [[nodiscard]] constexpr size_type find_first() const; [[nodiscard]] constexpr size_type find_next(size_type prev) const; // utils constexpr void swap(dynamic_bitset& other); [[nodiscard]] constexpr allocator_type get_allocator() const; template, typename _Alloc = std::allocator<_CharT>> [[nodiscard]] constexpr std::basic_string<_CharT, _Traits, _Alloc> to_string( _CharT zero = _CharT('0'), _CharT one = _CharT('1')) const; template constexpr void iterate_bits_on(Function&& function, Parameters&&... parameters) const; // friend external binary operators template friend constexpr bool operator==(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template friend constexpr bool operator<(const dynamic_bitset& lhs, const dynamic_bitset& rhs); private: template struct dependent_false : public std::false_type { }; std::vector m_blocks; size_type m_bits_number; static constexpr block_type zero_block = block_type(0); static constexpr block_type one_block = block_type(~zero_block); static constexpr size_type block_last_bit_index = bits_per_block - 1; static constexpr size_type blocks_required(size_type nbits) noexcept; static constexpr size_type block_index(size_type pos) noexcept; static constexpr size_type bit_index(size_type pos) noexcept; static constexpr block_type bit_mask(size_type pos) noexcept; static constexpr block_type bit_mask(size_type first, size_type last) noexcept; static constexpr void set_block_bits(block_type& block, size_type first, size_type last, bool val = true) noexcept; static constexpr void flip_block_bits(block_type& block, size_type first, size_type last) noexcept; static constexpr size_type block_count(const block_type& block) noexcept; static constexpr size_type block_count(const block_type& block, size_type nbits) noexcept; static constexpr size_type first_on(const block_type& block) noexcept; template constexpr void init_from_string(std::basic_string_view<_CharT, _Traits> str, typename std::basic_string_view<_CharT, _Traits>::size_type pos, typename std::basic_string_view<_CharT, _Traits>::size_type n, _CharT zero, _CharT one); constexpr block_type& get_block(size_type pos); constexpr const block_type& get_block(size_type pos) const; constexpr block_type& last_block(); constexpr block_type last_block() const; // used bits in the last block constexpr size_type extra_bits_number() const noexcept; // unused bits in the last block constexpr size_type unused_bits_number() const noexcept; template constexpr void apply(const dynamic_bitset& other, BinaryOperation binary_op); template constexpr void apply(UnaryOperation unary_op); constexpr void apply_left_shift(size_type shift); constexpr void apply_right_shift(size_type shift); // reset unused bits to 0 constexpr void sanitize(); // check functions used in asserts constexpr bool check_unused_bits() const noexcept; constexpr bool check_size() const noexcept; constexpr bool check_consistency() const noexcept; }; // Deduction guideline for expressions like "dynamic_bitset a(32);" with an integral type as parameter // to use the constructor with the initial size instead of the constructor with the allocator. template>> dynamic_bitset(integral_type)->dynamic_bitset<>; //================================================================================================= // dynamic_bitset external functions declarations //================================================================================================= template constexpr bool operator!=(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr bool operator<=(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr bool operator>(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr bool operator>=(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr dynamic_bitset operator&(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr dynamic_bitset operator|(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr dynamic_bitset operator^(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr dynamic_bitset operator-(const dynamic_bitset& lhs, const dynamic_bitset& rhs); template constexpr std::basic_ostream<_CharT, _Traits>& operator<<( std::basic_ostream<_CharT, _Traits>& os, const dynamic_bitset& bitset); template constexpr std::basic_istream<_CharT, _Traits>& operator>>(std::basic_istream<_CharT, _Traits>& is, dynamic_bitset& bitset); template constexpr void swap(dynamic_bitset& bitset1, dynamic_bitset& bitset2); //================================================================================================= // dynamic_bitset::reference functions implementations //================================================================================================= template constexpr dynamic_bitset::reference::reference( dynamic_bitset& bitset, size_type bit_pos) : m_block(bitset.get_block(bit_pos)), m_mask(dynamic_bitset::bit_mask(bit_pos)) { } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator=(bool v) { assign(v); return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator=(const dynamic_bitset::reference& rhs) { assign(rhs); return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator=(dynamic_bitset::reference&& rhs) noexcept { assign(rhs); return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator&=(bool v) { if(!v) { reset(); } return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator|=(bool v) { if(v) { set(); } return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator^=(bool v) { if(v) { flip(); } return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::operator-=(bool v) { if(v) { reset(); } return *this; } template constexpr bool dynamic_bitset::reference::operator~() const { return (m_block & m_mask) == zero_block; } template constexpr dynamic_bitset::reference::operator bool() const { return (m_block & m_mask) != zero_block; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::set() { m_block |= m_mask; return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::reset() { m_block &= ~m_mask; return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::flip() { m_block ^= m_mask; return *this; } template constexpr typename dynamic_bitset::reference& dynamic_bitset:: reference::assign(bool v) { if(v) { set(); } else { reset(); } return *this; } //================================================================================================= // dynamic_bitset public functions implementations //================================================================================================= template constexpr dynamic_bitset::dynamic_bitset(const allocator_type& allocator) : m_blocks(allocator), m_bits_number(0) { } template constexpr dynamic_bitset::dynamic_bitset(size_type nbits, unsigned long long init_val, const allocator_type& allocator) : m_blocks(blocks_required(nbits), allocator), m_bits_number(nbits) { if(nbits == 0 || init_val == 0) { return; } constexpr size_type init_val_required_blocks = sizeof(unsigned long long) / sizeof(block_type); if constexpr(init_val_required_blocks == 1) { m_blocks[0] = init_val; } else { const unsigned long long block_mask = static_cast(one_block); const size_type blocks_to_init = std::min(m_blocks.size(), init_val_required_blocks); for(size_type i = 0; i < blocks_to_init; ++i) { m_blocks[i] = block_type((init_val >> (i * bits_per_block) & block_mask)); } } sanitize(); } template constexpr dynamic_bitset::dynamic_bitset( std::initializer_list init_vals, const allocator_type& allocator) : m_blocks(allocator), m_bits_number(0) { append(init_vals); } template template constexpr dynamic_bitset::dynamic_bitset( std::basic_string_view<_CharT, _Traits> str, typename std::basic_string_view<_CharT, _Traits>::size_type pos, typename std::basic_string_view<_CharT, _Traits>::size_type n, _CharT zero, _CharT one, const allocator_type& allocator) : m_blocks(allocator), m_bits_number(0) { assert(pos < str.size()); init_from_string(str, pos, n, zero, one); } template template constexpr dynamic_bitset::dynamic_bitset( const std::basic_string<_CharT, _Traits, _Alloc>& str, typename std::basic_string<_CharT, _Traits, _Alloc>::size_type pos, typename std::basic_string<_CharT, _Traits, _Alloc>::size_type n, _CharT zero, _CharT one, const allocator_type& allocator) : m_blocks(allocator), m_bits_number(0) { assert(pos < str.size()); init_from_string(std::basic_string_view<_CharT, _Traits>(str), pos, n, zero, one); } template template constexpr dynamic_bitset::dynamic_bitset( const _CharT* str, typename std::basic_string<_CharT>::size_type pos, typename std::basic_string<_CharT>::size_type n, _CharT zero, _CharT one, const allocator_type& allocator) : m_blocks(allocator), m_bits_number(0) { init_from_string(std::basic_string_view<_CharT>(str), pos, n, zero, one); } template constexpr void dynamic_bitset::resize(size_type nbits, bool value) { if(nbits == m_bits_number) { return; } const size_type old_num_blocks = num_blocks(); const size_type new_num_blocks = blocks_required(nbits); const block_type init_value = value ? one_block : zero_block; if(new_num_blocks != old_num_blocks) { m_blocks.resize(new_num_blocks, init_value); } if(value && nbits > m_bits_number && old_num_blocks > 0) { // set value of the new bits in the old last block const size_type extra_bits = extra_bits_number(); if(extra_bits > 0) { m_blocks[old_num_blocks - 1] |= (init_value << extra_bits); } } m_bits_number = nbits; sanitize(); assert(check_consistency()); } template constexpr void dynamic_bitset::clear() { m_blocks.clear(); m_bits_number = 0; } template constexpr void dynamic_bitset::push_back(bool value) { const size_type new_last_bit = m_bits_number++; if(m_bits_number <= m_blocks.size() * bits_per_block) { if(value) { set(new_last_bit, value); } } else { m_blocks.push_back(block_type(value)); } assert(operator[](new_last_bit) == value); assert(check_consistency()); } template constexpr void dynamic_bitset::pop_back() { if(empty()) { return; } --m_bits_number; if(m_blocks.size() > blocks_required(m_bits_number)) { m_blocks.pop_back(); // no extra bits: sanitize not required assert(extra_bits_number() == 0); } else { sanitize(); } assert(check_consistency()); } template constexpr void dynamic_bitset::append(block_type block) { const size_type extra_bits = extra_bits_number(); if(extra_bits == 0) { m_blocks.push_back(block); } else { last_block() |= (block << extra_bits); m_blocks.push_back(block_type(block >> (bits_per_block - extra_bits))); } m_bits_number += bits_per_block; assert(check_consistency()); } template constexpr void dynamic_bitset::append(std::initializer_list blocks) { if(blocks.size() == 0) { return; } append(std::cbegin(blocks), std::cend(blocks)); } template template constexpr void dynamic_bitset::append(BlockInputIterator first, BlockInputIterator last) { if(first == last) { return; } // if random access iterators, std::distance complexity is constant if constexpr(std::is_same_v< typename std::iterator_traits::iterator_category, std::random_access_iterator_tag>) { assert(std::distance(first, last) > 0); m_blocks.reserve(m_blocks.size() + static_cast(std::distance(first, last))); } const size_type extra_bits = extra_bits_number(); const size_type unused_bits = unused_bits_number(); if(extra_bits == 0) { auto pos = m_blocks.insert(std::end(m_blocks), first, last); assert(std::distance(pos, std::end(m_blocks)) > 0); m_bits_number += static_cast(std::distance(pos, std::end(m_blocks))) * bits_per_block; } else { last_block() |= (*first << extra_bits); block_type block = block_type(*first >> unused_bits); ++first; while(first != last) { block |= (*first << extra_bits); m_blocks.push_back(block); m_bits_number += bits_per_block; block = block_type(*first >> unused_bits); ++first; } m_blocks.push_back(block); m_bits_number += bits_per_block; } assert(check_consistency()); } template constexpr dynamic_bitset& dynamic_bitset::operator&=( const dynamic_bitset& rhs) { assert(size() == rhs.size()); //apply(rhs, std::bit_and()); for(size_type i = 0; i < m_blocks.size(); ++i) { m_blocks[i] &= rhs.m_blocks[i]; } return *this; } template constexpr dynamic_bitset& dynamic_bitset::operator|=( const dynamic_bitset& rhs) { assert(size() == rhs.size()); //apply(rhs, std::bit_or()); for(size_type i = 0; i < m_blocks.size(); ++i) { m_blocks[i] |= rhs.m_blocks[i]; } return *this; } template constexpr dynamic_bitset& dynamic_bitset::operator^=( const dynamic_bitset& rhs) { assert(size() == rhs.size()); //apply(rhs, std::bit_xor()); for(size_type i = 0; i < m_blocks.size(); ++i) { m_blocks[i] ^= rhs.m_blocks[i]; } return *this; } template constexpr dynamic_bitset& dynamic_bitset::operator-=( const dynamic_bitset& rhs) { assert(size() == rhs.size()); //apply(rhs, [](const block_type& x, const block_type& y) { return (x & ~y); }); for(size_type i = 0; i < m_blocks.size(); ++i) { m_blocks[i] &= ~rhs.m_blocks[i]; } return *this; } template constexpr dynamic_bitset& dynamic_bitset::operator<<=( size_type shift) { if(shift != 0) { if(shift >= m_bits_number) { reset(); } else { apply_left_shift(shift); sanitize(); // unused bits can have changed, reset them to 0 } } return *this; } template constexpr dynamic_bitset& dynamic_bitset::operator>>=( size_type shift) { if(shift != 0) { if(shift >= m_bits_number) { reset(); } else { apply_right_shift(shift); } } return *this; } template constexpr dynamic_bitset dynamic_bitset::operator<<( size_type shift) const { return dynamic_bitset(*this) <<= shift; } template constexpr dynamic_bitset dynamic_bitset::operator>>( size_type shift) const { return dynamic_bitset(*this) >>= shift; } template constexpr dynamic_bitset dynamic_bitset::operator~() const { dynamic_bitset bitset(*this); bitset.flip(); return bitset; } template constexpr dynamic_bitset& dynamic_bitset::set(size_type pos, size_type len, bool value) { assert(pos < size()); assert(pos + len - 1 < size()); if(len == 0) { return *this; } const size_type first_block = block_index(pos); const size_type last_block = block_index(pos + len - 1); const size_type first_bit_index = bit_index(pos); const size_type last_bit_index = bit_index(pos + len - 1); if(first_block == last_block) { set_block_bits(m_blocks[first_block], first_bit_index, last_bit_index, value); } else { size_type first_full_block = first_block; size_type last_full_block = last_block; if(first_bit_index != 0) { ++first_full_block; // first block is not full set_block_bits(m_blocks[first_block], first_bit_index, block_last_bit_index, value); } if(last_bit_index != block_last_bit_index) { --last_full_block; // last block is not full set_block_bits(m_blocks[last_block], 0, last_bit_index, value); } const block_type full_block = value ? one_block : zero_block; for(size_type i = first_full_block; i <= last_full_block; ++i) { m_blocks[i] = full_block; } } return *this; } template constexpr dynamic_bitset& dynamic_bitset::set(size_type pos, bool value) { assert(pos < size()); if(value) { m_blocks[block_index(pos)] |= bit_mask(pos); } else { m_blocks[block_index(pos)] &= ~bit_mask(pos); } return *this; } template constexpr dynamic_bitset& dynamic_bitset::set() { std::fill(std::begin(m_blocks), std::end(m_blocks), one_block); sanitize(); return *this; } template constexpr dynamic_bitset& dynamic_bitset::reset(size_type pos, size_type len) { return set(pos, len, false); } template constexpr dynamic_bitset& dynamic_bitset::reset(size_type pos) { return set(pos, false); } template constexpr dynamic_bitset& dynamic_bitset::reset() { std::fill(std::begin(m_blocks), std::end(m_blocks), zero_block); return *this; } template constexpr dynamic_bitset& dynamic_bitset::flip(size_type pos, size_type len) { assert(pos < size()); assert(pos + len - 1 < size()); if(len == 0) { return *this; } const size_type first_block = block_index(pos); const size_type last_block = block_index(pos + len - 1); const size_type first_bit_index = bit_index(pos); const size_type last_bit_index = bit_index(pos + len - 1); if(first_block == last_block) { flip_block_bits(m_blocks[first_block], first_bit_index, last_bit_index); } else { size_type first_full_block = first_block; size_type last_full_block = last_block; if(first_bit_index != 0) { ++first_full_block; // first block is not full flip_block_bits(m_blocks[first_block], first_bit_index, block_last_bit_index); } if(last_bit_index != block_last_bit_index) { --last_full_block; // last block is not full flip_block_bits(m_blocks[last_block], 0, last_bit_index); } for(size_type i = first_full_block; i <= last_full_block; ++i) { m_blocks[i] = block_type(~m_blocks[i]); } } return *this; } template constexpr dynamic_bitset& dynamic_bitset::flip(size_type pos) { assert(pos < size()); m_blocks[block_index(pos)] ^= bit_mask(pos); return *this; } template constexpr dynamic_bitset& dynamic_bitset::flip() { std::transform( std::cbegin(m_blocks), std::cend(m_blocks), std::begin(m_blocks), std::bit_not()); sanitize(); return *this; } template constexpr bool dynamic_bitset::test(size_type pos) const { assert(pos < size()); return (m_blocks[block_index(pos)] & bit_mask(pos)) != zero_block; } template constexpr bool dynamic_bitset::test_set(size_type pos, bool value) { bool const result = test(pos); if(result != value) { set(pos, value); } return result; } template constexpr bool dynamic_bitset::all() const { if(empty()) { return true; } const block_type full_block = one_block; if(extra_bits_number() == 0) { for(const block_type& block: m_blocks) { if(block != full_block) { return false; } } } else { for(size_type i = 0; i < m_blocks.size() - 1; ++i) { if(m_blocks[i] != full_block) { return false; } } if(last_block() != (full_block >> unused_bits_number())) { return false; } } return true; } template constexpr bool dynamic_bitset::any() const { for(const block_type& block: m_blocks) { if(block != zero_block) { return true; } } return false; } template constexpr bool dynamic_bitset::none() const { return !any(); } template constexpr typename dynamic_bitset::size_type dynamic_bitset::count() const noexcept { if(empty()) { return 0; } #ifdef DYNAMIC_BITSET_USE_LIBPOPCNT const size_type count = static_cast(popcnt(m_blocks.data(), m_blocks.size() * sizeof(block_type))); #else size_type count = 0; // full blocks for(size_type i = 0; i < m_blocks.size() - 1; ++i) { count += block_count(m_blocks[i]); } // last block const block_type& block = last_block(); if(block != zero_block) { const size_t extra_bits = extra_bits_number(); if(extra_bits == 0) { count += block_count(block); } else { count += block_count(block, extra_bits); } } #endif return count; } template constexpr typename dynamic_bitset::reference dynamic_bitset::operator[]( size_type pos) { assert(pos < size()); return dynamic_bitset::reference(*this, pos); } template constexpr typename dynamic_bitset:: const_reference dynamic_bitset::operator[](size_type pos) const { return test(pos); } template constexpr typename dynamic_bitset::size_type dynamic_bitset::size() const noexcept { return m_bits_number; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: num_blocks() const noexcept { return m_blocks.size(); } template constexpr bool dynamic_bitset::empty() const noexcept { return size() == 0; } template constexpr typename dynamic_bitset::size_type dynamic_bitset::capacity() const noexcept { return m_blocks.capacity() * bits_per_block; } template constexpr void dynamic_bitset::reserve(size_type num_bits) { m_blocks.reserve(blocks_required(num_bits)); } template constexpr void dynamic_bitset::shrink_to_fit() { m_blocks.shrink_to_fit(); } template constexpr bool dynamic_bitset::is_subset_of( const dynamic_bitset& bitset) const { assert(size() == bitset.size()); for(size_type i = 0; i < m_blocks.size(); ++i) { if((m_blocks[i] & ~bitset.m_blocks[i]) != zero_block) { return false; } } return true; } template constexpr bool dynamic_bitset::is_proper_subset_of( const dynamic_bitset& bitset) const { assert(size() == bitset.size()); bool is_proper = false; for(size_type i = 0; i < m_blocks.size(); ++i) { const block_type& self_block = m_blocks[i]; const block_type& other_block = bitset.m_blocks[i]; if((self_block & ~other_block) != zero_block) { return false; } if((~self_block & other_block) != zero_block) { is_proper = true; } } return is_proper; } template constexpr bool dynamic_bitset::intersects( const dynamic_bitset& bitset) const { const size_type min_blocks_number = std::min(m_blocks.size(), bitset.m_blocks.size()); for(size_type i = 0; i < min_blocks_number; ++i) { if((m_blocks[i] & bitset.m_blocks[i]) != zero_block) { return true; } } return false; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: find_first() const { for(size_type i = 0; i < m_blocks.size(); ++i) { if(m_blocks[i] != zero_block) { return i * bits_per_block + first_on(m_blocks[i]); } } return npos; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: find_next(size_type prev) const { if(empty() || prev >= (size() - 1)) { return npos; } const size_type first_bit = prev + 1; const size_type first_block = block_index(first_bit); const size_type first_bit_index = bit_index(first_bit); const block_type first_block_shifted = block_type(m_blocks[first_block] >> first_bit_index); if(first_block_shifted != zero_block) { return first_bit + first_on(first_block_shifted); } else { for(size_type i = first_block + 1; i < m_blocks.size(); ++i) { if(m_blocks[i] != zero_block) { return i * bits_per_block + first_on(m_blocks[i]); } } } return npos; } template constexpr void dynamic_bitset::swap(dynamic_bitset& other) { std::swap(m_blocks, other.m_blocks); std::swap(m_bits_number, other.m_bits_number); } template constexpr typename dynamic_bitset::allocator_type dynamic_bitset< Block, Allocator>::get_allocator() const { return m_blocks.get_allocator(); } template template constexpr std::basic_string<_CharT, _Traits, _Alloc> dynamic_bitset::to_string( _CharT zero, _CharT one) const { const size_type len = size(); std::basic_string<_CharT, _Traits, _Alloc> str(len, zero); for(size_type i_block = 0; i_block < m_blocks.size(); ++i_block) { if(m_blocks[i_block] == zero_block) { continue; } block_type mask = block_type(1); const size_type limit = i_block * bits_per_block < len ? len - i_block * bits_per_block : bits_per_block; for(size_type i_bit = 0; i_bit < limit; ++i_bit) { if((m_blocks[i_block] & mask) != zero_block) { _Traits::assign(str[len - (i_block * bits_per_block + i_bit + 1)], one); } mask <<= 1; } } return str; } template template constexpr void dynamic_bitset::iterate_bits_on(Function&& function, Parameters&&... parameters) const { if constexpr(!std::is_invocable_v) { static_assert(dependent_false::value, "Function take invalid arguments"); // function should take (size_t, parameters...) as arguments } if constexpr(std::is_same_v, void>) { size_t i_bit = find_first(); while(i_bit != npos) { std::invoke( std::forward(function), i_bit, std::forward(parameters)...); i_bit = find_next(i_bit); } } else if constexpr(std::is_convertible_v, bool>) { size_t i_bit = find_first(); while(i_bit != npos) { if(!std::invoke( std::forward(function), i_bit, std::forward(parameters)...)) { break; } i_bit = find_next(i_bit); } } else { static_assert(dependent_false::value, "Function have invalid return type"); // return type should be void, or convertible to bool } } template [[nodiscard]] constexpr bool operator==(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { return (lhs.m_bits_number == rhs.m_bits_number) && (lhs.m_blocks == rhs.m_blocks); } template [[nodiscard]] constexpr bool operator<(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { using size_type = typename dynamic_bitset::size_type; using block_type = typename dynamic_bitset::block_type; const size_type lhs_size = lhs.size(); const size_type rhs_size = rhs.size(); const size_type lhs_blocks_size = lhs.m_blocks.size(); const size_type rhs_blocks_size = rhs.m_blocks.size(); if(lhs_size == rhs_size) { // if comparison of two empty bitsets if(lhs_size == 0) { return false; } for(size_type i = lhs_blocks_size - 1; i > 0; --i) { if(lhs.m_blocks[i] != rhs.m_blocks[i]) { return lhs.m_blocks[i] < rhs.m_blocks[i]; } } return lhs.m_blocks[0] < rhs.m_blocks[0]; } // empty bitset inferior to 0-only bitset if(lhs_size == 0) { return true; } if(rhs_size == 0) { return false; } const bool rhs_longer = rhs_size > lhs_size; const dynamic_bitset& longest_bitset = rhs_longer ? rhs : lhs; const size_type longest_blocks_size = std::max(lhs_blocks_size, rhs_blocks_size); const size_type shortest_blocks_size = std::min(lhs_blocks_size, rhs_blocks_size); for(size_type i = longest_blocks_size - 1; i >= shortest_blocks_size; --i) { if(longest_bitset.m_blocks[i] != block_type(0)) { return rhs_longer; } } for(size_type i = shortest_blocks_size - 1; i > 0; --i) { if(lhs.m_blocks[i] != rhs.m_blocks[i]) { return lhs.m_blocks[i] < rhs.m_blocks[i]; } } if(lhs.m_blocks[0] != rhs.m_blocks[0]) { return lhs.m_blocks[0] < rhs.m_blocks[0]; } return lhs_size < rhs_size; } //================================================================================================= // dynamic_bitset private functions implementations //================================================================================================= template constexpr typename dynamic_bitset::size_type dynamic_bitset:: blocks_required(size_type nbits) noexcept { return nbits / bits_per_block + static_cast(nbits % bits_per_block > 0); } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: block_index(size_type pos) noexcept { return pos / bits_per_block; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: bit_index(size_type pos) noexcept { return pos % bits_per_block; } template constexpr typename dynamic_bitset::block_type dynamic_bitset:: bit_mask(size_type pos) noexcept { return block_type(block_type(1) << bit_index(pos)); } template constexpr typename dynamic_bitset::block_type dynamic_bitset:: bit_mask(size_type first, size_type last) noexcept { first = bit_index(first); last = bit_index(last); if(last == (block_last_bit_index)) { return block_type(one_block << first); } else { return block_type(((block_type(1) << (last + 1)) - 1) ^ ((block_type(1) << first) - 1)); } } template constexpr void dynamic_bitset::set_block_bits(block_type& block, size_type first, size_type last, bool val) noexcept { if(val) { block |= bit_mask(first, last); } else { block &= ~bit_mask(first, last); } } template constexpr void dynamic_bitset::flip_block_bits(block_type& block, size_type first, size_type last) noexcept { block ^= bit_mask(first, last); } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: block_count(const block_type& block) noexcept { if(block == zero_block) { return 0; } #if defined(DYNAMIC_BITSET_GCC) \ || (defined(DYNAMIC_BITSET_CLANG) && defined(DYNAMIC_BITSET_CLANG_builtin_popcount)) if constexpr(std::is_same_v) { return static_cast(__builtin_popcountll(block)); } if constexpr(std::is_same_v) { return static_cast(__builtin_popcountl(block)); } if constexpr(sizeof(block_type) <= sizeof(unsigned int)) { return static_cast(__builtin_popcount(static_cast(block))); } #endif size_type count = 0; block_type mask = 1; for(size_type bit_index = 0; bit_index < bits_per_block; ++bit_index) { count += ((block & mask) != zero_block); mask <<= 1; } return count; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: block_count(const block_type& block, size_type nbits) noexcept { assert(nbits <= bits_per_block); if(block == zero_block) { return 0; } #if defined(DYNAMIC_BITSET_GCC) \ || (defined(DYNAMIC_BITSET_CLANG) && defined(DYNAMIC_BITSET_CLANG_builtin_popcount)) const block_type shifted_block = block_type(block << (bits_per_block - nbits)); if constexpr(std::is_same_v) { return static_cast(__builtin_popcountll(shifted_block)); } if constexpr(std::is_same_v) { return static_cast(__builtin_popcountl(shifted_block)); } if constexpr(sizeof(block_type) <= sizeof(unsigned int)) { return static_cast(__builtin_popcount(static_cast(shifted_block))); } #endif size_type count = 0; block_type mask = 1; for(size_type bit_index = 0; bit_index < nbits; ++bit_index) { count += ((block & mask) != zero_block); mask <<= 1; } return count; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: first_on(const block_type& block) noexcept { assert(block != zero_block); #if defined(DYNAMIC_BITSET_GCC) \ || (defined(DYNAMIC_BITSET_CLANG) && defined(DYNAMIC_BITSET_CLANG_builtin_ctz)) if constexpr(std::is_same_v) { return static_cast(__builtin_ctzll(block)); } if constexpr(std::is_same_v) { return static_cast(__builtin_ctzl(block)); } if constexpr(sizeof(block_type) <= sizeof(unsigned int)) { return static_cast(__builtin_ctz(static_cast(block))); } #elif defined(DYNAMIC_BITSET_MSVC) # if defined(DYNAMIC_BITSET_MSVC_64) if constexpr(std::is_same_v) { unsigned long index = std::numeric_limits::max(); _BitScanForward64(&index, block); return static_cast(index); } # endif if constexpr(std::is_same_v) { unsigned long index = std::numeric_limits::max(); _BitScanForward(&index, block); return static_cast(index); } if constexpr(sizeof(block_type) <= sizeof(unsigned long)) { unsigned long index = std::numeric_limits::max(); _BitScanForward(&index, static_cast(block)); return static_cast(index); } #endif block_type mask = block_type(1); for(size_type i = 0; i < bits_per_block; ++i) { if((block & mask) != zero_block) { return i; } mask <<= 1; } return npos; } template template constexpr void dynamic_bitset::init_from_string( std::basic_string_view<_CharT, _Traits> str, typename std::basic_string_view<_CharT, _Traits>::size_type pos, typename std::basic_string_view<_CharT, _Traits>::size_type n, [[maybe_unused]] _CharT zero, _CharT one) { assert(pos < str.size()); const size_type size = std::min(n, str.size() - pos); m_bits_number = size; m_blocks.clear(); m_blocks.resize(blocks_required(size)); for(size_t i = 0; i < size; ++i) { const _CharT c = str[(pos + size - 1) - i]; assert(c == zero || c == one); if(c == one) { set(i); } } } template constexpr typename dynamic_bitset::block_type& dynamic_bitset:: get_block(size_type pos) { return m_blocks[block_index(pos)]; } template constexpr const typename dynamic_bitset::block_type& dynamic_bitset< Block, Allocator>::get_block(size_type pos) const { return m_blocks[block_index(pos)]; } template constexpr typename dynamic_bitset::block_type& dynamic_bitset:: last_block() { return m_blocks[m_blocks.size() - 1]; } template constexpr typename dynamic_bitset::block_type dynamic_bitset:: last_block() const { return m_blocks[m_blocks.size() - 1]; } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: extra_bits_number() const noexcept { return bit_index(m_bits_number); } template constexpr typename dynamic_bitset::size_type dynamic_bitset:: unused_bits_number() const noexcept { return bits_per_block - extra_bits_number(); } template template constexpr void dynamic_bitset::apply( const dynamic_bitset& other, BinaryOperation binary_op) { assert(num_blocks() == other.num_blocks()); std::transform(std::cbegin(m_blocks), std::cend(m_blocks), std::cbegin(other.m_blocks), std::begin(m_blocks), binary_op); } template template constexpr void dynamic_bitset::apply(UnaryOperation unary_op) { std::transform(std::cbegin(m_blocks), std::cend(m_blocks), std::begin(m_blocks), unary_op); } template constexpr void dynamic_bitset::apply_left_shift(size_type shift) { assert(shift > 0); assert(shift < capacity()); const size_type blocks_shift = shift / bits_per_block; const size_type bits_offset = shift % bits_per_block; if(bits_offset == 0) { for(size_type i = m_blocks.size() - 1; i >= blocks_shift; --i) { m_blocks[i] = m_blocks[i - blocks_shift]; } } else { const size_type reverse_bits_offset = bits_per_block - bits_offset; for(size_type i = m_blocks.size() - 1; i > blocks_shift; --i) { m_blocks[i] = block_type((m_blocks[i - blocks_shift] << bits_offset) | block_type(m_blocks[i - blocks_shift - 1] >> reverse_bits_offset)); } m_blocks[blocks_shift] = block_type(m_blocks[0] << bits_offset); } // set bit that came at the right to 0 in unmodified blocks std::fill(std::begin(m_blocks), std::begin(m_blocks) + static_cast(blocks_shift), zero_block); } template constexpr void dynamic_bitset::apply_right_shift(size_type shift) { assert(shift > 0); assert(shift < capacity()); const size_type blocks_shift = shift / bits_per_block; const size_type bits_offset = shift % bits_per_block; const size_type last_block_to_shift = m_blocks.size() - blocks_shift - 1; if(bits_offset == 0) { for(size_type i = 0; i <= last_block_to_shift; ++i) { m_blocks[i] = m_blocks[i + blocks_shift]; } } else { const size_type reverse_bits_offset = bits_per_block - bits_offset; for(size_type i = 0; i < last_block_to_shift; ++i) { m_blocks[i] = block_type((m_blocks[i + blocks_shift] >> bits_offset) | block_type(m_blocks[i + blocks_shift + 1] << reverse_bits_offset)); } m_blocks[last_block_to_shift] = block_type(m_blocks[m_blocks.size() - 1] >> bits_offset); } // set bit that came at the left to 0 in unmodified blocks std::fill( std::begin(m_blocks) + static_cast(last_block_to_shift + 1), std::end(m_blocks), zero_block); } template constexpr void dynamic_bitset::sanitize() { size_type shift = m_bits_number % bits_per_block; if(shift > 0) { last_block() &= ~(one_block << shift); } } template constexpr bool dynamic_bitset::check_unused_bits() const noexcept { const size_type extra_bits = extra_bits_number(); if(extra_bits > 0) { return (last_block() & (one_block << extra_bits)) == zero_block; } return true; } template constexpr bool dynamic_bitset::check_size() const noexcept { return blocks_required(size()) == m_blocks.size(); } template constexpr bool dynamic_bitset::check_consistency() const noexcept { return check_unused_bits() && check_size(); } //================================================================================================= // dynamic_bitset external functions implementations //================================================================================================= template constexpr bool operator!=(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { return !(lhs == rhs); } template constexpr bool operator<=(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { return !(rhs < lhs); } template constexpr bool operator>(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { return rhs < lhs; } template constexpr bool operator>=(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { return !(lhs < rhs); } template constexpr dynamic_bitset operator&(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { dynamic_bitset result(lhs); return result &= rhs; } template constexpr dynamic_bitset operator|(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { dynamic_bitset result(lhs); return result |= rhs; } template constexpr dynamic_bitset operator^(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { dynamic_bitset result(lhs); return result ^= rhs; } template constexpr dynamic_bitset operator-(const dynamic_bitset& lhs, const dynamic_bitset& rhs) { dynamic_bitset result(lhs); return result -= rhs; } template constexpr std::basic_ostream<_CharT, _Traits>& operator<<( std::basic_ostream<_CharT, _Traits>& os, const dynamic_bitset& bitset) { // A better implementation is possible return os << bitset.template to_string<_CharT, _Traits>(); } template constexpr std::basic_istream<_CharT, _Traits>& operator>>(std::basic_istream<_CharT, _Traits>& is, dynamic_bitset& bitset) { // A better implementation is possible constexpr _CharT zero = _CharT('0'); constexpr _CharT one = _CharT('1'); typename std::basic_istream<_CharT, _Traits>::sentry s(is); if(!s) { return is; } dynamic_bitset reverse_bitset; _CharT val; is.get(val); while(is.good()) { if(val == one) { reverse_bitset.push_back(true); } else if(val == zero) { reverse_bitset.push_back(false); } else { is.unget(); break; } is.get(val); } bitset.clear(); if(!reverse_bitset.empty()) { for(typename dynamic_bitset::size_type i = reverse_bitset.size() - 1; i > 0; --i) { bitset.push_back(reverse_bitset.test(i)); } bitset.push_back(reverse_bitset.test(0)); } return is; } template constexpr void swap(dynamic_bitset& bitset1, dynamic_bitset& bitset2) { bitset1.swap(bitset2); } #endif //DYNAMIC_BITSET_DYNAMIC_BITSET_HPP