diff --git a/libs/util/include/psemek/util/dynamic_bitset.hpp b/libs/util/include/psemek/util/dynamic_bitset.hpp new file mode 100644 index 00000000..678871cc --- /dev/null +++ b/libs/util/include/psemek/util/dynamic_bitset.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +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(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(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(1 << storage_shift)) != 0; + + if (value) + storage_value |= static_cast(1 << storage_shift); + else + storage_value &= ~static_cast(1 << storage_shift); + + return result; + } + + util::span storage() const + { + return storage_; + } + + private: + std::vector 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; + } + }; + +} diff --git a/libs/util/source/dynamic_bitset.cpp b/libs/util/source/dynamic_bitset.cpp new file mode 100644 index 00000000..ff1f88cc --- /dev/null +++ b/libs/util/source/dynamic_bitset.cpp @@ -0,0 +1,94 @@ +#include + +#include + +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; + } + +}