#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace stdx { inline namespace v1 { template ())> class bitset { constexpr static std::size_t N = to_underlying(Size); using elem_t = StorageElem; static_assert(std::is_unsigned_v, "Storage element for bitset must be an unsigned type"); constexpr static auto storage_elem_size = std::numeric_limits::digits; constexpr static auto storage_size = (N + storage_elem_size - 1) / storage_elem_size; constexpr static auto bit = elem_t{1U}; constexpr static auto allbits = std::numeric_limits::max(); std::array storage{}; constexpr static auto lastmask = []() -> elem_t { if constexpr (N % storage_elem_size != 0) { return allbits >> (storage_elem_size - N % storage_elem_size); } else { return allbits; } }(); constexpr auto highbits() const -> elem_t { return storage.back() & lastmask; } [[nodiscard]] constexpr static auto indices(std::size_t pos) { struct locator { std::size_t index; std::size_t offset; }; return locator{pos / storage_elem_size, pos % storage_elem_size}; } [[nodiscard]] friend constexpr auto operator==(bitset const &lhs, bitset const &rhs) -> bool { for (auto i = std::size_t{}; i < storage_size - 1; ++i) { if (lhs.storage[i] != rhs.storage[i]) { return false; } } return lhs.highbits() == rhs.highbits(); } #if __cpp_impl_three_way_comparison < 201907L [[nodiscard]] friend constexpr auto operator!=(bitset const &lhs, bitset const &rhs) -> bool { return not(lhs == rhs); } #endif friend constexpr auto operator|(bitset lhs, bitset const &rhs) -> bitset { lhs |= rhs; return lhs; } friend constexpr auto operator&(bitset lhs, bitset const &rhs) -> bitset { lhs &= rhs; return lhs; } friend constexpr auto operator^(bitset lhs, bitset const &rhs) -> bitset { lhs ^= rhs; return lhs; } friend constexpr auto operator-(bitset const &lhs, bitset rhs) -> bitset { rhs.flip(); return lhs & rhs; } friend constexpr auto operator<<(bitset lhs, std::size_t pos) -> bitset { lhs <<= pos; return lhs; } friend constexpr auto operator>>(bitset lhs, std::size_t pos) -> bitset { lhs >>= pos; return lhs; } template constexpr auto for_each(F &&f) const -> F { std::size_t i = 0; for (auto e : storage) { while (e != 0) { auto const offset = static_cast(countr_zero(e)); e &= static_cast(~(bit << offset)); f(i + offset); } i += std::numeric_limits::digits; } return std::forward(f); } template friend constexpr auto for_each(F &&f, bitset const &...bs) -> F; public: constexpr bitset() = default; constexpr explicit bitset(std::uint64_t value) { if constexpr (std::is_same_v) { storage[0] = value; } else { for (auto &elem : storage) { if (value == 0) { break; } elem = value & allbits; value >>= storage_elem_size; } } } template constexpr explicit bitset(place_bits_t, Bs... bs) { static_assert(((std::is_integral_v or std::is_enum_v) and ...), "Bit places must be integral or enumeration types!"); (set(static_cast(bs)), ...); } constexpr explicit bitset(all_bits_t) { for (auto &elem : storage) { elem = allbits; } storage.back() &= lastmask; } constexpr explicit bitset(std::string_view str, std::size_t pos = 0, std::size_t n = std::string_view::npos, char one = '1') { auto const len = std::min(n, str.size() - pos); auto i = std::size_t{}; auto const s = str.substr(pos, std::min(len, N)); // NOLINTNEXTLINE(modernize-loop-convert) for (auto it = std::rbegin(s); it != std::rend(s); ++it) { set(i++, *it == one); } } #if __cplusplus >= 202002L constexpr explicit bitset(ct_string s) : bitset{static_cast(s)} {} #endif template [[nodiscard]] constexpr auto to() const -> T { using U = underlying_type_t; static_assert( unsigned_integral, "Conversion must be to an unsigned integral type or enum!"); static_assert(N <= std::numeric_limits::digits, "Bitset too big for conversion to T"); if constexpr (std::is_same_v) { return static_cast(storage[0] & lastmask); } else { U result{highbits()}; for (auto i = storage_size - 2u; i < storage_size; --i) { result = static_cast(result << storage_elem_size); result |= storage[i]; } return static_cast(result); } } [[nodiscard]] constexpr auto to_natural() const { using T = smallest_uint_t; static_assert(N <= std::numeric_limits::digits, "Bitset too big for conversion to T"); return to(); } constexpr static std::integral_constant size{}; template [[nodiscard]] constexpr auto operator[](T idx) const -> bool { auto const pos = static_cast(to_underlying(idx)); auto const [index, offset] = indices(pos); return (storage[index] & (bit << offset)) != 0; } template constexpr auto set(T idx, bool value = true) LIFETIMEBOUND -> bitset & { auto const pos = static_cast(to_underlying(idx)); auto const [index, offset] = indices(pos); if (value) { storage[index] |= static_cast(bit << offset); } else { storage[index] &= static_cast(~(bit << offset)); } return *this; } constexpr auto set(lsb_t lsb, msb_t msb, bool value = true) LIFETIMEBOUND -> bitset & { auto const l = to_underlying(lsb); auto const m = to_underlying(msb); auto [l_index, l_offset] = indices(l); auto const [m_index, m_offset] = indices(m); using setfn = auto (*)(elem_t *, elem_t)->void; auto const fn = [&]() -> setfn { if (value) { return [](elem_t *ptr, elem_t val) { *ptr |= val; }; } return [](elem_t *ptr, elem_t val) { *ptr &= ~val; }; }(); auto l_mask = std::numeric_limits::max() << l_offset; if (l_index != m_index) { fn(&storage[l_index++], static_cast(l_mask)); l_mask = std::numeric_limits::max(); } while (l_index != m_index) { fn(&storage[l_index++], static_cast(l_mask)); } auto const m_mask = std::numeric_limits::max() >> (storage_elem_size - m_offset - 1); fn(&storage[l_index], static_cast(l_mask & m_mask)); return *this; } constexpr auto set(lsb_t lsb, length_t len, bool value = true) LIFETIMEBOUND -> bitset & { auto const l = to_underlying(lsb); auto const length = to_underlying(len); return set(lsb, static_cast(l + length - 1), value); } constexpr auto set() LIFETIMEBOUND -> bitset & { for (auto &elem : storage) { elem = allbits; } return *this; } template constexpr auto reset(T idx) LIFETIMEBOUND -> bitset & { auto const pos = static_cast(to_underlying(idx)); auto const [index, offset] = indices(pos); storage[index] &= static_cast(~(bit << offset)); return *this; } constexpr auto reset() LIFETIMEBOUND -> bitset & { for (auto &elem : storage) { elem = {}; } return *this; } constexpr auto reset(lsb_t lsb, msb_t msb) LIFETIMEBOUND -> bitset & { return set(lsb, msb, false); } constexpr auto reset(lsb_t lsb, length_t len) LIFETIMEBOUND -> bitset & { return set(lsb, len, false); } template constexpr auto flip(T idx) LIFETIMEBOUND -> bitset & { auto const pos = static_cast(to_underlying(idx)); auto const [index, offset] = indices(pos); storage[index] ^= static_cast(bit << offset); return *this; } constexpr auto flip() LIFETIMEBOUND -> bitset & { for (auto &elem : storage) { elem ^= allbits; } return *this; } [[nodiscard]] constexpr auto all() const -> bool { for (auto i = std::size_t{}; i < storage_size - 1; ++i) { if (storage[i] != allbits) { return false; } } return highbits() == lastmask; } [[nodiscard]] constexpr auto any() const -> bool { for (auto i = std::size_t{}; i < storage_size - 1; ++i) { if (storage[i] != 0) { return true; } } return highbits() != 0; } [[nodiscard]] constexpr auto none() const -> bool { return not any(); } [[nodiscard]] constexpr auto count() const -> std::size_t { std::size_t n{}; for (auto i = std::size_t{}; i < storage_size - 1; ++i) { n += static_cast(popcount(storage[i])); } return n + static_cast(popcount(highbits())); } [[nodiscard]] constexpr auto lowest_unset() const -> std::size_t { std::size_t i = 0; for (auto e : storage) { if (auto offset = static_cast(countr_one(e)); offset != std::numeric_limits::digits) { return i + offset; } i += std::numeric_limits::digits; } return i; } [[nodiscard]] constexpr auto operator~() const -> bitset { bitset result{}; for (auto i = std::size_t{}; i < storage_size; ++i) { result.storage[i] = ~storage[i]; } return result; } constexpr auto operator|=(bitset const &rhs) LIFETIMEBOUND->bitset & { for (auto i = std::size_t{}; i < storage_size; ++i) { storage[i] |= rhs.storage[i]; } return *this; } constexpr auto operator&=(bitset const &rhs) LIFETIMEBOUND->bitset & { for (auto i = std::size_t{}; i < storage_size; ++i) { storage[i] &= rhs.storage[i]; } return *this; } constexpr auto operator^=(bitset const &rhs) LIFETIMEBOUND->bitset & { for (auto i = std::size_t{}; i < storage_size; ++i) { storage[i] ^= rhs.storage[i]; } return *this; } constexpr auto operator<<=(std::size_t pos) LIFETIMEBOUND->bitset & { auto dst = storage_size - 1; auto const start = dst - pos / storage_elem_size; pos %= storage_elem_size; if (pos == 0) { for (auto i = start; i > std::size_t{}; --i) { storage[dst] = storage[i]; --dst; } } else { auto const borrow_shift = storage_elem_size - pos; for (auto i = start; i > std::size_t{}; --i) { storage[dst] = static_cast(storage[i] << pos); storage[dst] |= static_cast(storage[i - 1] >> borrow_shift); --dst; } } storage[dst] = static_cast(storage.front() << pos); while (dst > std::size_t{}) { storage[--dst] = 0; } return *this; } constexpr auto operator>>=(std::size_t pos) LIFETIMEBOUND->bitset & { auto dst = std::size_t{}; auto const start = pos / storage_elem_size; pos %= storage_elem_size; if (pos == 0) { for (auto i = start; i < storage_size - 1; ++i) { storage[dst] = storage[i]; ++dst; } } else { auto const borrow_shift = storage_elem_size - pos; for (auto i = start; i < storage_size - 1; ++i) { storage[dst] = static_cast(storage[i] >> pos); storage[dst] |= static_cast(storage[i + 1] << borrow_shift); ++dst; } } storage[dst++] = static_cast(storage.back() >> pos); while (dst < storage_size) { storage[dst++] = 0; } return *this; } }; template constexpr auto for_each(F &&f, bitset const &...bs) -> F { if constexpr (sizeof...(bs) == 1) { return (bs.for_each(std::forward(f)), ...); } else { static_assert(stdx::always_false_v, "unimplemented"); return f; } } #if __cplusplus >= 202002L template bitset(ct_string) -> bitset; #endif } // namespace v1 } // namespace stdx