diff --git a/include/seqan3/range/container/dynamic_bitset.hpp b/include/seqan3/range/container/dynamic_bitset.hpp new file mode 100644 index 00000000000..c8542dd95da --- /dev/null +++ b/include/seqan3/range/container/dynamic_bitset.hpp @@ -0,0 +1,760 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +/*!\file + * \author Enrico Seiler + * \brief A constexpr bitset implementation with dynamic size at compile time. + */ + +#pragma once + +#include +#include +#include + +namespace seqan3 +{ + +template +class dynamic_bitset +{ +public: + static_assert(capacity_ <= 58, "at most 58 bit"); + + class reference_proxy_type + { + public: + constexpr reference_proxy_type() noexcept = default; //!< Defaulted. + constexpr reference_proxy_type(reference_proxy_type const &) noexcept = default; //!< Defaulted. + constexpr reference_proxy_type(reference_proxy_type &&) noexcept = default; //!< Defaulted. + constexpr reference_proxy_type & operator=(reference_proxy_type rhs) noexcept + { + rhs ? set() : reset(); + return *this; + } + ~reference_proxy_type() noexcept = default; //!< Defaulted. + + constexpr reference_proxy_type(uint64_t & internal_, size_t pos) noexcept : + internal{internal_}, mask{1ULL<(internal & mask); + } + + constexpr bool operator~() const noexcept + { + return !static_cast(internal & mask); + } + + constexpr reference_proxy_type & operator=(bool const value) + { + value ? set() : reset(); + return *this; + } + + constexpr reference_proxy_type & operator|=(bool const value) + { + if (value) + set(); + return *this; + } + + constexpr reference_proxy_type & operator&=(bool const value) + { + if (!value) + reset(); + return *this; + } + + constexpr reference_proxy_type & operator^=(bool const value) + { + operator bool() && value ? reset() : set(); + return *this; + } + + constexpr reference_proxy_type & operator-=(bool const value) + { + if (value) + reset(); + return *this; + } + + private: + ranges::semiregular_t internal; + uint64_t mask; + + constexpr void set() noexcept + { + internal |= mask; + } + + constexpr void reset() noexcept + { + uint64_t size_part = internal >> 58 << 58; + internal &= ~mask << 6 >> 6 | size_part; + } + }; + + using value_type = bool; + using reference = reference_proxy_type; + using const_reference = bool; + using iterator = detail::random_access_iterator; + using const_iterator = detail::random_access_iterator; + using difference_type = ptrdiff_t; + using size_type = detail::min_viable_uint_t; + + //!\cond + // this signals to range-v3 that something is a container :| + using allocator_type = void; + //!\endcond + + /*!\name Constructors, destructor and assignment + * \{ + */ + constexpr dynamic_bitset() noexcept = default; //!< Defaulted. + constexpr dynamic_bitset(dynamic_bitset const &) noexcept = default; //!< Defaulted. + constexpr dynamic_bitset(dynamic_bitset &&) noexcept = default; //!< Defaulted. + constexpr dynamic_bitset & operator=(dynamic_bitset const &) noexcept = default; //!< Defaulted. + constexpr dynamic_bitset & operator=(dynamic_bitset &&) noexcept = default; //!< Defaulted. + ~dynamic_bitset() noexcept = default; //!< Defaulted. + + template + requires detail::sizeof_bits <= 64 + constexpr dynamic_bitset(t value) : dynamic_bitset(static_cast(value)) + {} + + constexpr dynamic_bitset(uint64_t value) + { + if (detail::popcount(value >> 58) != 0) + throw std::invalid_argument{"The dynamic_bitset can be at most 58 long."}; + data_ |= value; + update_size(); + } + + template end_it_type> + //!\cond + requires std::Constructible> + //!\endcond + constexpr dynamic_bitset(begin_it_type begin_it, end_it_type end_it) noexcept: + dynamic_bitset{} + { + assign(begin_it, end_it); + } + + template + //!\cond + requires !std::is_same_v, dynamic_bitset> + /*ICE: && std::Constructible>*/ + //!\endcond + explicit constexpr dynamic_bitset(other_range_t && range) noexcept : + dynamic_bitset{std::ranges::begin(range), std::ranges::end(range)} + {} + + constexpr dynamic_bitset(size_type n, value_type value) noexcept : + dynamic_bitset{} + { + assign(n, value); + } + + constexpr dynamic_bitset & operator=(std::initializer_list ilist) noexcept + { + assign(std::ranges::begin(ilist), std::ranges::end(ilist)); + return *this; + } + + template + constexpr dynamic_bitset(char const (&_lit)[N]) : dynamic_bitset{} + { + static_assert(N <= capacity_ + 1, "Length of string literal exceeds capacity of dynamic_bitset."); + assign(_lit); + } + + template + constexpr dynamic_bitset & operator=(char const (&_lit)[N]) + { + static_assert(N <= capacity_ + 1, "Length of string literal exceeds capacity of dynamic_bitset."); + assign(_lit); + return *this; + } + + template + constexpr void assign(char const (&_lit)[N]) + { + static_assert(N <= capacity_ + 1, "Length of string literal exceeds capacity of dynamic_bitset."); + assert(_lit[N - 1] == '\0'); + uint64_t value{}; + for (size_t i = 0; i != N - 1; ++i) + { + if (_lit[i] == '0') + { + value <<= 1; + } + else if (_lit[i] == '1') + { + if (i != 0) + { + value <<= 1; + } + value |= 1u; + } + else + { + throw std::invalid_argument{"Only 1 or 0."}; + } + } + + (*this) = value; + } + + constexpr void assign(std::initializer_list ilist) noexcept + { + assign(std::ranges::begin(ilist), std::ranges::end(ilist)); + } + + constexpr void assign(size_type const count, value_type const value) noexcept + { + clear(); + auto tmp = view::repeat_n(value, count); + assign(std::ranges::begin(tmp), std::ranges::end(tmp)); + } + + template + //!\cond + requires std::Constructible> + //!\endcond + constexpr void assign(other_range_t && range) + { + assign(std::ranges::begin(range), std::ranges::end(range)); + } + + template end_it_type> + constexpr void assign(begin_it_type begin_it, end_it_type end_it) noexcept + //!\cond + requires std::Constructible> + //!\endcond + { + clear(); + insert(cbegin(), begin_it, end_it); + } + //!\} + + /*!\name Iterators + * \{ + */ + //!\brief Returns the begin to the string. + constexpr iterator begin() noexcept + { + return iterator{*this}; + } + + //!\copydoc begin() + constexpr const_iterator begin() const noexcept + { + return const_iterator{*this}; + } + + //!\copydoc begin() + constexpr const_iterator cbegin() const noexcept + { + return const_iterator{*this}; + } + + //!\brief Returns iterator past the end of the vector. + constexpr iterator end() noexcept + { + return iterator{*this, size()}; + } + + //!\copydoc end() + constexpr const_iterator end() const noexcept + { + return const_iterator{*this, size()}; + } + + //!\copydoc end() + constexpr const_iterator cend() const noexcept + { + return const_iterator{*this, size()}; + } + //!\} + + constexpr bool all() const noexcept + { + return count() == size(); + } + + constexpr bool any() const noexcept + { + return count() != 0; + } + + constexpr bool none() const noexcept + { + return count() == 0; + } + + constexpr size_type count() const noexcept + { + return detail::popcount(data_part()); + } + + constexpr size_type size() const noexcept + { + return static_cast(data_ >> 58); + } + + constexpr dynamic_bitset & operator&=(dynamic_bitset const & rhs) noexcept + { + assert(size() == rhs.size()); + data_ &= rhs.data_; + return *this; + } + + //!\brief Bitwise and. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr dynamic_bitset operator&(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + assert(lhs.size() == rhs.size()); + dynamic_bitset tmp{lhs}; + tmp &= rhs; + return tmp; + } + + constexpr dynamic_bitset & operator|=(dynamic_bitset const & rhs) noexcept + { + assert(size() == rhs.size()); + data_ |= rhs.data_; + return *this; + } + + //!\brief Bitwise or. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr dynamic_bitset operator|(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + assert(lhs.size() == rhs.size()); + dynamic_bitset tmp{lhs}; + tmp |= rhs; + return tmp; + } + + constexpr dynamic_bitset & operator^=(dynamic_bitset const & rhs) noexcept + { + assert(size() == rhs.size()); + uint64_t tmp_size = size_part(); + data_ ^= rhs.data_; + data_ |= tmp_size; + return *this; + } + + //!\brief Bitwise or. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr dynamic_bitset operator^(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + assert(lhs.size() == rhs.size()); + dynamic_bitset tmp{lhs}; + tmp ^= rhs; + return tmp; + } + + [[nodiscard]] constexpr dynamic_bitset operator~() const noexcept + { + dynamic_bitset tmp{~(data_ << offset()) >> offset()}; + tmp.resize(size()); + return tmp; + } + + constexpr dynamic_bitset & operator<<=(size_t const count) noexcept + { + assert(count > 0); + assert(count < size()); + uint64_t new_data = data_part(); + new_data <<= offset() + count; + new_data >>= offset(); + new_data |= size_part(); + data_ = std::move(new_data); + return *this; + } + + constexpr dynamic_bitset & operator>>=(size_t const count) noexcept + { + assert(count > 0); + assert(count < size()); + uint64_t new_data = data_part(); + new_data >>= count; + new_data |= size_part(); + data_ = std::move(new_data); + return *this; + } + + constexpr dynamic_bitset operator>>(size_t const count) const noexcept + { + assert(count > 0); + assert(count < size()); + dynamic_bitset tmp{*this}; + tmp >>= count; + return tmp; + } + + constexpr dynamic_bitset operator<<(size_t const count) const noexcept + { + assert(count > 0); + assert(count < size()); + dynamic_bitset tmp{*this}; + tmp <<= count; + return tmp; + } + + constexpr dynamic_bitset & set() noexcept + { + data_ |= ~0ULL >> offset(); + return *this; + } + + constexpr dynamic_bitset & set(size_t const pos, bool const value = true) + { + at(pos) = value; + return *this; + } + + constexpr dynamic_bitset & reset() noexcept + { + data_ = size_part(); + return *this; + } + + constexpr dynamic_bitset & reset(size_t const pos) + { + set(pos, false); + return *this; + } + + constexpr dynamic_bitset & flip() noexcept + { + data_ = ~(data_part() << offset()) >> offset() | size_part(); + return *this; + } + + constexpr dynamic_bitset & flip(size_t const pos) + { + at(pos) ? reset(pos) : set(pos); + return *this; + } + + constexpr reference at(size_t pos) + { + if (size() <= pos) + throw std::out_of_range{"Trying to access position " + std::to_string(pos) + + " in a seqan3::dynamic_bitset of size " + std::to_string(size()) + "."}; + return (*this)[pos]; + } + + constexpr const_reference at(size_t pos) const + { + if (size() <= pos) + throw std::out_of_range{"Trying to access position " + std::to_string(pos) + + " in a seqan3::dynamic_bitset of size " + std::to_string(size()) + "."}; + return (*this)[pos]; + } + + constexpr reference test(size_t pos) + { + return at(pos); + } + + constexpr const_reference test(size_t pos) const + { + return at(pos); + } + + constexpr reference operator[](size_t pos) + { + return {data_, pos}; + } + + constexpr const_reference operator[](size_t pos) const + { + return data_ << (63 - pos) >> 63; + } + + constexpr reference front() noexcept + { + assert(size() > 0); + return (*this)[0]; + } + + //!\copydoc front() + constexpr const_reference front() const noexcept + { + assert(size() > 0); + return (*this)[0]; + } + + constexpr reference back() noexcept + { + assert(size() > 0); + return (*this)[size()-1]; + } + + //!\copydoc back() + constexpr const_reference back() const noexcept + { + assert(size() > 0); + return (*this)[size()-1]; + } + + constexpr bool empty() const noexcept + { + return size() == 0; + } + + constexpr size_type max_size() const noexcept + { + return capacity(); + } + + constexpr size_type capacity() const noexcept + { + return capacity_; + } + + constexpr void reserve(size_t const) + {} + + constexpr void shrink_to_fit() + {} + + constexpr void clear() noexcept + { + data_ &= 0ULL; + } + + constexpr iterator insert(const_iterator pos, value_type const value) + { + return insert(pos, 1, value); + } + + constexpr iterator insert(const_iterator pos, size_type const count, value_type const value) noexcept + { + auto tmp = view::repeat_n(value, count); + return insert(pos, std::ranges::begin(tmp), std::ranges::end(tmp)); + } + + template end_it_type> + //!\cond + requires std::Constructible> + //!\endcond + constexpr iterator insert(const_iterator pos, begin_it_type begin_it, end_it_type end_it) noexcept + { + auto const pos_as_num = std::ranges::distance(cbegin(), pos); + auto const length = std::ranges::distance(begin_it, end_it); + + if (length == 0) + return begin(); // nothing to insert + + for (size_type i = size() + length - 1; i > pos_as_num + length - 1; --i) + (*this)[i] = (*this)[i - length]; + + // std::ranges::copy(begin_it, end_it, (*this)[pos_as_num]); + for (auto i = pos_as_num; begin_it != end_it; ++i, ++begin_it) + (*this)[i] = *begin_it; + + resize(size() + length); + return begin() + pos_as_num; + } + + constexpr iterator insert(const_iterator pos, std::initializer_list const & ilist) noexcept + { + return insert(pos, ilist.begin(), ilist.end()); + } + + constexpr iterator erase(const_iterator begin_it, const_iterator end_it) noexcept + { + if (begin_it >= end_it) // [[unlikely]] + return begin() + std::ranges::distance(cbegin(), end_it); + + auto const length = std::ranges::distance(begin_it, end_it); + auto out_it = begin() + std::ranges::distance(cbegin(), begin_it); + + while (end_it != cend()) + *(out_it++) = *(end_it++); + + resize(size() - length); + return begin() + std::ranges::distance(cbegin(), begin_it); + } + + constexpr iterator erase(const_iterator pos) noexcept + { + return erase(pos, pos + 1); + } + + constexpr void push_back(value_type const value) noexcept + { + assert(size() < capacity_); + (*this)[size()] = value; + resize(size() + 1); + } + + constexpr void pop_back() noexcept + { + assert(size() > 0); + (*this)[size()] = false; + resize(size() - 1); + } + + constexpr void resize(size_type const count) noexcept + { + assert(count <= capacity_); + erase_size(); + data_ |= static_cast(count) << 58; + } + + constexpr void resize(size_type const count, value_type const value) noexcept + { + assert(count <= capacity_); + for (size_t i = size(); i < count; ++i) + (*this)[i] = value; + erase_size(); + data_ |= static_cast(count) << 58; + } + + constexpr void swap(dynamic_bitset & rhs) noexcept + { + uint64_t tmp = std::move(data_); + data_ = std::move(rhs.data_); + rhs.data_ = std::move(tmp); + } + + //!\overload + constexpr void swap(dynamic_bitset && rhs) noexcept + { + data_ = std::move(rhs.data_); + } + + friend constexpr void swap(dynamic_bitset & lhs, dynamic_bitset & rhs) noexcept + { + lhs.swap(rhs); + } + + //!\overload + friend constexpr void swap(dynamic_bitset && lhs, dynamic_bitset && rhs) noexcept + { + lhs.swap(rhs); + } + + //!\name Comparison operators + //!\{ + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ /* resolves ambiguousness when comparing two dynamic_bitsets of unequal capacity */ + //!\endcond + friend constexpr bool operator==(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return std::ranges::equal(lhs, rhs);//lhs.data_ == rhs.data_; + } + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr bool operator!=(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return !(lhs == rhs); + } + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr bool operator<(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return lhs.data_part() < rhs.data_part(); + } + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr bool operator>(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return lhs.data_part() > rhs.data_part(); + } + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr bool operator<=(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return !(lhs > rhs); + } + + //!\brief Performs element-wise comparison. + template + //!\cond + requires cap2 <= capacity_ + //!\endcond + friend constexpr bool operator>=(dynamic_bitset const & lhs, dynamic_bitset const & rhs) noexcept + { + return !(lhs < rhs); + } + //!\} + +private: + uint64_t data_{}; + + constexpr void update_size() noexcept + { + erase_size(); + data_ |= data_ ? static_cast(detail::most_significant_bit_set(data_) + 1) << 58 : 0u; + } + + constexpr void erase_size() noexcept + { + data_ &= data_ << 6 >> 6; + } + + constexpr uint64_t size_part() const noexcept + { + return data_ >> 58 << 58; + } + + constexpr uint64_t data_part() const noexcept + { + return data_ << 6 >> 6; + } + + constexpr size_type offset() const noexcept + { + return 64 - size(); + } + + template + void CEREAL_SERIALIZE_FUNCTION_NAME(archive_t & archive) + { + archive(data_); + } + +}; + +} // namespace seqan3 diff --git a/test/unit/range/container/CMakeLists.txt b/test/unit/range/container/CMakeLists.txt index d8662945688..d686152f54f 100644 --- a/test/unit/range/container/CMakeLists.txt +++ b/test/unit/range/container/CMakeLists.txt @@ -2,5 +2,6 @@ seqan3_test(aligned_allocator_test.cpp) seqan3_test(container_concept_test.cpp) seqan3_test(container_of_container_test.cpp) seqan3_test(container_test.cpp) +seqan3_test(dynamic_bitset_test.cpp) seqan3_test(small_string_test.cpp) seqan3_test(small_vector_test.cpp) diff --git a/test/unit/range/container/dynamic_bitset_test.cpp b/test/unit/range/container/dynamic_bitset_test.cpp new file mode 100644 index 00000000000..8fb06dd4de3 --- /dev/null +++ b/test/unit/range/container/dynamic_bitset_test.cpp @@ -0,0 +1,650 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +#include + +#include +#include +#include + +using namespace seqan3; + +// Standard construction. +TEST(dynamic_bitset, standard_construction) +{ + EXPECT_TRUE((std::is_default_constructible_v>)); + EXPECT_TRUE((std::is_nothrow_default_constructible_v>)); + EXPECT_TRUE((std::is_copy_constructible_v>)); + EXPECT_TRUE((std::is_trivially_copy_constructible_v>)); + EXPECT_TRUE((std::is_nothrow_copy_constructible_v>)); + EXPECT_TRUE((std::is_move_constructible_v>)); + EXPECT_TRUE((std::is_trivially_move_constructible_v>)); + EXPECT_TRUE((std::is_nothrow_move_constructible_v>)); + EXPECT_TRUE((std::is_copy_assignable_v>)); + EXPECT_TRUE((std::is_trivially_copy_assignable_v>)); + EXPECT_TRUE((std::is_nothrow_copy_assignable_v>)); + EXPECT_TRUE((std::is_move_assignable_v>)); + EXPECT_TRUE((std::is_trivially_move_assignable_v>)); + EXPECT_TRUE((std::is_nothrow_move_assignable_v>)); +} + +TEST(dynamic_bitset, concepts) +{ + EXPECT_TRUE((ReservableContainer>)); + EXPECT_TRUE((std::ranges::RandomAccessRange>)); +} + +constexpr bool comparison_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1100}; + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + constexpr dynamic_bitset t4{"1111111111111111111111111111111111111111111111111111111111"}; + constexpr dynamic_bitset t5{"1111111111111111111111110011111111111111111111111111111100"}; + constexpr dynamic_bitset t6{"0011111111111111111111111111111111111111111111111111111111"}; + + bool res = t1 == t4; + res &= (t2 == t5); + res &= (t3 == t6); + + res &= (t1 > t2); + res &= (t2 > t3); + res &= (t1 > t3); + + res &= (t1 >= t2); + res &= (t2 >= t3); + res &= (t1 >= t3); + + res &= (t2 <= t1); + res &= (t3 <= t2); + res &= (t3 <= t1); + + res &= (t2 < t1); + res &= (t3 < t2); + res &= (t3 < t1); + + res &= (t1 != t2); + res &= (t2 != t3); + res &= (t1 != t3); + + return res; +} + +TEST(dynamic_bitset, comparison) +{ + constexpr bool b = comparison_test(); + EXPECT_TRUE(b); +} + +constexpr bool size_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + bool res = t1.size() == 58u; + + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t2.size() == 58u; + + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t3.size() == 56u; + + constexpr dynamic_bitset t4{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1100}; + res &= t4.size() == 58u; + + constexpr dynamic_bitset t5; + res &= t5.size() == 0u; + + return res; +} + +TEST(dynamic_bitset, size) +{ + constexpr bool b = size_test(); + EXPECT_TRUE(b); +} + +constexpr bool count_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + bool res = t1.count() == 58u; + + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t2.count() == 56u; + + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t3.count() == 56u; + + constexpr dynamic_bitset t4{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1100}; + res &= t4.count() == 56u; + + constexpr dynamic_bitset t5; + res &= t5.count() == 0u; + + return res; +} + +TEST(dynamic_bitset, count) +{ + constexpr bool b = count_test(); + EXPECT_TRUE(b); +} + +constexpr bool all_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + bool res = t1.all(); + + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= !t2.all(); + + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t3.all(); + + constexpr dynamic_bitset t4{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1100}; + res &= !t4.all(); + + constexpr dynamic_bitset t5; + res &= t5.all(); + + return res; +} + +TEST(dynamic_bitset, all) +{ + constexpr bool b = all_test(); + EXPECT_TRUE(b); +} + +constexpr bool any_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + bool res = t1.any(); + + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t2.any(); + + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= t3.any(); + + constexpr dynamic_bitset t4{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1100}; + res &= t4.any(); + + constexpr dynamic_bitset t5; + res &= !t5.any(); + + return res; +} + +TEST(dynamic_bitset, any) +{ + constexpr bool b = any_test(); + EXPECT_TRUE(b); +} + +constexpr bool none_test() +{ + constexpr dynamic_bitset t1{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + bool res = !t1.none(); + + constexpr dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= !t2.none(); + + constexpr dynamic_bitset t3{0b00'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111}; + res &= !t3.none(); + + constexpr dynamic_bitset t4{0b11'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1100}; + res &= !t4.none(); + + constexpr dynamic_bitset t5; + res &= t5.none(); + + return res; +} + +TEST(dynamic_bitset, none) +{ + constexpr bool b = none_test(); + EXPECT_TRUE(b); +} + +constexpr bool set_test() +{ + dynamic_bitset t1{0b11'1110'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1110}; + bool res = !t1.all(); + t1.set(); + res &= t1.all(); + + dynamic_bitset t2{0b11'1111'1111'1111'1111'1111'1101'1111'1111'1111'1111'1111'1111'1111'1110}; + res &= !t2.all(); + t2.set(0, true); + t2.set(33, true); + res &= t2.all(); + + return res; +} + +TEST(dynamic_bitset, set) +{ + constexpr bool b = set_test(); + EXPECT_TRUE(b); +} + +constexpr bool reset_test() +{ + dynamic_bitset t1{0b11'1110'1111'1111'1111'1111'1100'1111'1111'1111'1111'1111'1111'1111'1110}; + bool res = !t1.none(); + t1.reset(); + res &= t1.none(); + + dynamic_bitset t2{0b10'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001}; + res &= !t2.none(); + t2.reset(0); + t2.reset(57); + res &= t2.none(); + + return res; +} + +TEST(dynamic_bitset, reset) +{ + constexpr bool b = reset_test(); + EXPECT_TRUE(b); +} + +constexpr bool flip_test() +{ + dynamic_bitset t1{0b1111111111111111111111111111111111111111111111111111111111}; + bool res = t1.all(); + t1.flip(); + res &= t1.none(); + + dynamic_bitset t2{0b1111111111111111111111111111111111111111111111111111111111}; + res &= t2.all(); + t2.flip(0); + res &= !t2.all(); + res &= t2.any(); + + return res; +} + +TEST(dynamic_bitset, flip) +{ + constexpr bool b = flip_test(); + EXPECT_TRUE(b); +} + +constexpr bool access_test() +{ + bool res = true; + + dynamic_bitset t1{0b1111'0000'0000'0000}; + dynamic_bitset const t2{0b1111'0000'0000'0000}; + dynamic_bitset expected{0b0111'0000'0000'0011}; + expected.resize(t1.size()); + + for (size_t i = 0u; i < t1.size() - 4u; ++i) + { + res &= t1.at(i) == 0 && t1.test(i) == 0 && t1[i] == 0; + res &= t2.at(i) == 0 && t2.test(i) == 0 && t2[i] == 0; + } + + for (size_t i = t1.size() - 4u; i < t1.size(); ++i) + { + res &= t1.at(i) == 1 && t1.test(i) == 1 && t1[i] == 1; + res &= t2.at(i) == 1 && t2.test(i) == 1 && t2[i] == 1; + } + + res &= !t1.front(); + res &= t1.back(); + res &= !t2.front(); + res &= t2.back(); + + t1[1] = true; + res &= t1 == dynamic_bitset{0b1111'0000'0000'0010}; + + t1.front() = true; + res &= t1 == dynamic_bitset{0b1111'0000'0000'0011}; + + t1.back() = false; + res &= t1 == expected; + + return res; +} + +TEST(dynamic_bitset, access) +{ + constexpr bool b = access_test(); + EXPECT_TRUE(b); + + constexpr dynamic_bitset t1{0b1111'0000'0000'0000}; + EXPECT_THROW(t1.at(16), std::out_of_range); + EXPECT_THROW(t1.test(16), std::out_of_range); +} + +constexpr bool bitwise_and_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset t2{0b1010'0001'0000'0011}; + dynamic_bitset expected{0b1010'0001'0000'0000}; + + bool res = (t1 & t2) == expected; + t1 &= t2; + res = t1 == expected; + + return res; +} + +TEST(dynamic_bitset, bitwise_and) +{ + constexpr bool b = bitwise_and_test(); + EXPECT_TRUE(b); +} + +constexpr bool bitwise_or_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset t2{0b1010'0001'0000'0011}; + dynamic_bitset expected{0b1111'0001'0000'1111}; + + bool res = (t1 | t2) == expected; + t1 |= t2; + res = t1 == expected; + + return res; +} + +TEST(dynamic_bitset, bitwise_or) +{ + constexpr bool b = bitwise_or_test(); + EXPECT_TRUE(b); +} + +constexpr bool bitwise_xor_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset t2{0b1010'0001'0000'0011}; + dynamic_bitset expected{0b0101'0000'0000'1111}; + expected.resize(t1.size()); + + bool res = (t1 ^ t2) == expected; + t1 ^= t2; + res = t1 == expected; + + return res; +} + +TEST(dynamic_bitset, bitwise_xor) +{ + constexpr bool b = bitwise_xor_test(); + EXPECT_TRUE(b); +} + +constexpr bool bitwise_not_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset expected{0b0000'1110'1111'0011}; + expected.resize(t1.size()); + + return ~t1 == expected; +} + +TEST(dynamic_bitset, bitwise_not) +{ + constexpr bool b = bitwise_not_test(); + EXPECT_TRUE(b); +} + +constexpr bool swap_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset t2; + dynamic_bitset expected{t1}; + + t1.swap(t2); + bool res = t2 == expected; + + swap(t1, t2); + res &= t1 == expected; + + t2.swap(std::move(t1)); + res &= t2 == expected; + + swap(std::move(t1), std::move(t2)); + res &= t1 == expected; + + return res; +} + +TEST(dynamic_bitset, swap) +{ + constexpr bool b = swap_test(); + EXPECT_TRUE(b); +} + +constexpr bool assign_test() +{ + dynamic_bitset t1{0b1111}; + dynamic_bitset t2{0b1001}; + dynamic_bitset t3, t4, t5, t6; + + t3.assign(4, true); + bool res = t3 == t1; + + t4.assign(t2.cbegin(), t2.cend()); + res &= t4 == t2; + + t5.assign({true, true, true, true}); + res &= t5 == t1; + + t6 = {1, 0, 0, 1}; + res &= t6 == t2; + + return res; +} + +TEST(dynamic_bitset, assign) +{ + constexpr bool b = assign_test(); + EXPECT_TRUE(b); +} + +constexpr bool iterator_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset const t2{0b1010'0001'0000'0011}; + + bool res = !*t1.begin(); + res &= !*t1.cbegin(); + res &= *t2.begin(); + res &= *t2.cbegin(); + + res &= *(t1.end() - 1); + res &= *(t1.cend() - 1); + res &= *(t2.end() - 1); + res &= *(t2.cend() - 1); + + res &= t1.end() == t1.cend(); + + *t1.begin() = true; + res &= t1 == dynamic_bitset{0b1111'0001'0000'1101}; + + return res; +} + +TEST(dynamic_bitset, iterators) +{ + constexpr bool b = iterator_test(); + EXPECT_TRUE(b); +} + +constexpr bool capacity_test() +{ + dynamic_bitset t0{}; + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset const t2{0b1010'0001'0000'0011}; + + bool res = t0.empty(); + res &= !t1.empty(); + res &= !t2.empty(); + + res &= t0.size() == 0u; + res &= t1.size() == 16u; + res &= t2.size() == 16u; + + res &= t0.capacity() >= t0.size(); + res &= t1.capacity() >= t1.size(); + res &= t2.capacity() >= t2.size(); + + res &= t0.max_size() == t0.capacity(); + res &= t1.max_size() == t1.capacity(); + res &= t2.max_size() == t2.capacity(); + + size_t cap = t0.capacity(); + t0.reserve(1000); + res &= t0.capacity() == cap; + t0.shrink_to_fit(); + res &= t0.capacity() == cap; + + res &= t1.capacity() == 58u; + dynamic_bitset<30> t3; + res &= t3.capacity() == 30u; + + return res; +} + +TEST(dynamic_bitset, capacity) +{ + constexpr bool b = capacity_test(); + EXPECT_TRUE(b); +} + +constexpr bool clear_test() +{ + dynamic_bitset t1{0b1111'0001'0000'1100}; + dynamic_bitset expected{}; + + t1.clear(); + + return t1 == expected; +} + +TEST(dynamic_bitset, clear) +{ + constexpr bool b = clear_test(); + EXPECT_TRUE(b); +} + +constexpr bool insert_test() +{ + dynamic_bitset t0{}; + dynamic_bitset t1{0b100101}; + + t0.insert(t0.cend(), 1); + t0.insert(t0.cend(), 0); + t0.insert(t0.cend(), 1); + t0.insert(t0.cend(), 0); + t0.insert(t0.cend(), 1); + t0.insert(t0.cbegin() + 3, 0); + bool res = t0 == t1; + + t0.clear(); + t0.insert(t0.cend(), 3, true); + t0.insert(t0.cbegin() + 1, false); + t0.insert(t0.cbegin() + 3, 2, false); + res &= t0 == t1; + + return res; +} + +TEST(dynamic_bitset, insert) +{ + constexpr bool b = insert_test(); + EXPECT_TRUE(b); +} + +constexpr bool erase_test() +{ + dynamic_bitset t1{0b100101}; + + t1.erase(t1.begin()); + bool res = t1 == dynamic_bitset{0b10010}; + + t1.erase(t1.begin() + 1, t1.begin() + 3); + res &= t1 == dynamic_bitset{0b100}; + + t1.erase(t1.begin(), t1.begin()); + res &= t1 == dynamic_bitset{0b100}; + + return res; +} + +TEST(dynamic_bitset, erase) +{ + constexpr bool b = erase_test(); + EXPECT_TRUE(b); +} + +constexpr bool push_pop_test() +{ + dynamic_bitset t1{}; + dynamic_bitset expected{0b01}; + expected.resize(2); + + t1.push_back(true); + bool res = t1 == dynamic_bitset{0b1}; + + t1.push_back(false); + res &= t1 == expected; + + t1.pop_back(); + res &= t1 == dynamic_bitset{0b1}; + + t1.pop_back(); + res &= t1 == dynamic_bitset{}; + + return res; +} + +TEST(dynamic_bitset, push_pop) +{ + constexpr bool b = push_pop_test(); + EXPECT_TRUE(b); +} + +constexpr bool resize_test() +{ + dynamic_bitset t1{}; + + t1.resize(2); + bool res = !t1.at(0) && !t1.at(1); + + t1.resize(5, true); + res &= t1 == dynamic_bitset{0b11100}; + + t1.resize(4, true); + res &= t1 == dynamic_bitset{0b1100}; + + t1.resize(3); + res &= t1 == dynamic_bitset{0b100}; + + return res; +} + +TEST(dynamic_bitset, resize) +{ + constexpr bool b = resize_test(); + EXPECT_TRUE(b); +} + +// TEST(dynamic_bitset, streamable) +// { +// } + +TEST(dynamic_bitset, serialisation) +{ + dynamic_bitset t1{0b100101}; + test::do_serialisation(t1); +}