Add util::dynamic_bitset

This commit is contained in:
Nikita Lisitsa 2023-08-19 15:21:08 +03:00
parent 58215fedc1
commit 981629cb74
2 changed files with 203 additions and 0 deletions

View file

@ -0,0 +1,109 @@
#pragma once
#include <psemek/util/hash.hpp>
#include <psemek/util/span.hpp>
#include <vector>
#include <cstdint>
#include <climits>
#include <compare>
namespace psemek::util
{
struct dynamic_bitset
{
using storage_type = std::uint64_t;
static constexpr std::size_t storage_bits = sizeof(storage_type) * CHAR_BIT;
static constexpr std::size_t storage_mask = storage_bits - 1;
[[nodiscard]] static std::size_t bits_count_to_storage_size(std::size_t bits_count)
{
return (bits_count + storage_bits - 1) / storage_bits;
}
dynamic_bitset() = default;
dynamic_bitset(std::size_t size)
: storage_(bits_count_to_storage_size(size), 0)
{}
void resize(std::size_t size)
{
storage_.resize(bits_count_to_storage_size(size));
}
void assign(std::size_t size, bool value)
{
storage_type storage_value = value ? -1 : 0;
storage_.assign(bits_count_to_storage_size(size), storage_value);
if (value)
storage_.back() &= ((static_cast<storage_type>(1) << (size % storage_bits)) - 1);
}
[[nodiscard]] std::size_t size() const
{
return storage_.size() * storage_bits;
}
[[nodiscard]] bool get(std::size_t index) const
{
std::size_t storage_index = index / storage_bits;
std::size_t storage_shift = index & storage_mask;
if (storage_index > storage_.size())
return false;
return (storage_[storage_index] & static_cast<storage_type>(1 << storage_shift)) != 0;
}
bool set(std::size_t index, bool value)
{
resize(index + 1);
std::size_t storage_index = index / storage_bits;
std::size_t storage_shift = index & storage_mask;
auto & storage_value = storage_[storage_index];
bool result = (storage_value & static_cast<storage_type>(1 << storage_shift)) != 0;
if (value)
storage_value |= static_cast<storage_type>(1 << storage_shift);
else
storage_value &= ~static_cast<storage_type>(1 << storage_shift);
return result;
}
util::span<storage_type const> storage() const
{
return storage_;
}
private:
std::vector<storage_type> storage_;
};
[[nodiscard]] bool operator == (dynamic_bitset const & b1, dynamic_bitset const & b2);
[[nodiscard]] std::strong_ordering operator <=> (dynamic_bitset const & b1, dynamic_bitset const & b2);
[[nodiscard]] bool is_subset(dynamic_bitset const & b1, dynamic_bitset const & b2);
[[nodiscard]] std::size_t popcount(dynamic_bitset const & b);
}
namespace std
{
template <>
struct hash<::psemek::util::dynamic_bitset>
{
std::size_t operator()(::psemek::util::dynamic_bitset const & b)
{
std::size_t result = 0x23d2ef655094f9aeull;
for (auto value : b.storage())
::psemek::util::hash_combine(result, value);
return result;
}
};
}

View file

@ -0,0 +1,94 @@
#include <psemek/util/dynamic_bitset.hpp>
#include <bit>
namespace psemek::util
{
[[nodiscard]] bool operator == (dynamic_bitset const & b1, dynamic_bitset const & b2)
{
auto storage1 = b1.storage();
auto storage2 = b2.storage();
auto const min_size = std::min(storage1.size(), storage2.size());
std::size_t i = 0;
for (; i < min_size; ++i)
if (storage1[i] != storage2[i])
return false;
if (storage1.size() < storage2.size())
{
for (; i < b2.size(); ++i)
if (0 != storage2[i])
return false;
}
else
{
for (; i < b1.size(); ++i)
if (storage1[i] != 0)
return false;
}
return true;
}
[[nodiscard]] std::strong_ordering operator <=> (dynamic_bitset const & b1, dynamic_bitset const & b2)
{
auto storage1 = b1.storage();
auto storage2 = b2.storage();
auto const min_size = std::min(storage1.size(), storage2.size());
std::size_t i = 0;
for (; i < min_size; ++i)
if (auto res = storage1[i] <=> storage2[i]; res != std::strong_ordering::equal)
return res;
if (storage1.size() < storage2.size())
{
for (; i < b2.size(); ++i)
if (auto res = 0 <=> storage2[i]; res != std::strong_ordering::equal)
return res;
}
else
{
for (; i < b1.size(); ++i)
if (auto res = storage1[i] <=> 0; res != std::strong_ordering::equal)
return res;
}
return std::strong_ordering::equal;
}
[[nodiscard]] bool is_subset(dynamic_bitset const & b1, dynamic_bitset const & b2)
{
auto storage1 = b1.storage();
auto storage2 = b2.storage();
auto const min_size = std::min(storage1.size(), storage2.size());
std::size_t i = 0;
for (; i < min_size; ++i)
if ((storage1[i] & storage2[i]) != storage1[i])
return false;
if (storage1.size() > storage2.size())
{
for (; i < b1.size(); ++i)
if (storage1[i] != 0)
return false;
}
return true;
}
[[nodiscard]] std::size_t popcount(dynamic_bitset const & b)
{
std::size_t result = 0;
for (auto value : b.storage())
result += std::popcount(value);
return result;
}
}