Add util::dynamic_bitset
This commit is contained in:
parent
58215fedc1
commit
981629cb74
2 changed files with 203 additions and 0 deletions
109
libs/util/include/psemek/util/dynamic_bitset.hpp
Normal file
109
libs/util/include/psemek/util/dynamic_bitset.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
94
libs/util/source/dynamic_bitset.cpp
Normal file
94
libs/util/source/dynamic_bitset.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue