From 0f27891405d786377f78abec4740f81b1b3fb70a Mon Sep 17 00:00:00 2001 From: rhalbersma Date: Mon, 21 May 2018 15:16:34 +0200 Subject: [PATCH] initial commit --- .appveyor.yml | 36 + .gitignore | 6 + .travis.yml | 38 + CMakeLists.txt | 14 + LICENSE_1_0.txt | 23 + README.md | 26 + include/xstd/int_set.hpp | 1393 +++++++++++++++++++++++++++++++ test/CMakeLists.txt | 88 ++ test/include/exhaustive.hpp | 162 ++++ test/include/legacy/bitset.hpp | 151 ++++ test/include/legacy/int_set.hpp | 102 +++ test/include/primitive.hpp | 1056 +++++++++++++++++++++++ test/include/traits.hpp | 176 ++++ test/src/builtin.cpp | 158 ++++ test/src/test0.cpp | 49 ++ test/src/test1.cpp | 163 ++++ test/src/test2.cpp | 151 ++++ test/src/test3.cpp | 36 + test/src/type_traits.cpp | 43 + 19 files changed, 3871 insertions(+) create mode 100644 .appveyor.yml create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CMakeLists.txt create mode 100644 LICENSE_1_0.txt create mode 100644 README.md create mode 100644 include/xstd/int_set.hpp create mode 100644 test/CMakeLists.txt create mode 100644 test/include/exhaustive.hpp create mode 100644 test/include/legacy/bitset.hpp create mode 100644 test/include/legacy/int_set.hpp create mode 100644 test/include/primitive.hpp create mode 100644 test/include/traits.hpp create mode 100644 test/src/builtin.cpp create mode 100644 test/src/test0.cpp create mode 100644 test/src/test1.cpp create mode 100644 test/src/test2.cpp create mode 100644 test/src/test3.cpp create mode 100644 test/src/type_traits.cpp diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..a538533 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,36 @@ +image: + - Visual Studio 2017 + +environment: + matrix: + - generator: "Visual Studio 15 2017" + BOOST_ROOT: C:/Libraries/boost_1_66_0 + BOOST_LIBRARYDIR: C:/Libraries/boost_1_66_0/lib32-msvc-14.1 + PATH: "%BOOST_LIBRARYDIR%;%PATH%" + + - generator: "Visual Studio 15 2017 Win64" + BOOST_ROOT: C:/Libraries/boost_1_66_0 + BOOST_LIBRARYDIR: C:/Libraries/boost_1_66_0/lib64-msvc-14.1 + PATH: "%BOOST_LIBRARYDIR%;%PATH%" + +configuration: + - Debug + - Release + +before_build: +- cmd: >- + cd %APPVEYOR_BUILD_FOLDER% + + mkdir build + + cd build + + cmake .. -G "%generator%" + +build_script: +- cmd: >- + cmake --build . + +test_script: +- cmd: >- + ctest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f880c23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/build/ +/.cproject +/.project +/.settings +/.vscode/ +*.info diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..817e0b2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: cpp +os: linux +dist: trusty +sudo: false + +matrix: + include: + - env: CXX_COMPILER=clang++-6.0 CXX=g++-7 + addons: { apt: { packages: ["clang-6.0", "g++-7", "libboost-test-dev"], sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-trusty-6.0"] } } + + - env: CXX_COMPILER=clang++-7 CXX=g++-7 + addons: { apt: { packages: ["clang-7", "g++-7", "libboost-test-dev"], sources: ["ubuntu-toolchain-r-test", "llvm-toolchain-trusty"] } } + + - env: CXX_COMPILER=g++-7 + addons: { apt: { packages: ["g++-7", "libboost-test-dev"], sources: ["ubuntu-toolchain-r-test"] } } + + - env: CXX_COMPILER=g++-8 + addons: { apt: { packages: ["g++-8", "libboost-test-dev"], sources: ["ubuntu-toolchain-r-test"] } } + +before_script: +- | + cd ${TRAVIS_BUILD_DIR} + mkdir build && cd build + cmake .. -DCMAKE_CXX_COMPILER=${CXX_COMPILER} + +script: +- | + cmake --build . + ctest + +after_success: +- | + if [[ "${CXX_COMPILER}" == "g++-7" ]]; then + bash <(curl -s https://codecov.io/bash) + fi + +notifications: + email: false diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a225ddb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.9) +project(int_set CXX) + +set(project_include_dir + $ + $/include> +) + +add_library(int_set INTERFACE) +target_include_directories(int_set INTERFACE ${project_include_dir}) +target_compile_features(int_set INTERFACE cxx_std_17) + +include(CTest) +add_subdirectory(test) diff --git a/LICENSE_1_0.txt b/LICENSE_1_0.txt new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f970fb1 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/) +[![Standard](https://img.shields.io/badge/c%2B%2B-17-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) +[![License](https://img.shields.io/badge/license-Boost-blue.svg)](https://opensource.org/licenses/BSL-1.0) +[![](https://tokei.rs/b1/github/rhalbersma/int_set)](https://github.com/rhalbersma/int_set) + +Rebooting the `std::bitset` franchise +------------------------------------- + +An `int_set` is a modern reimagining of `std::bitset`, keeping what time has proven to be effective, and throwing out what is not. `int_set`s are array-backed sets of integers that are compact and fast: they do less (i.e. they don't do bounds-checking, and they don't throw exceptions) and offer more (e.g. iterators to seamlessly interact with the rest of the Standard Library). This enables you to do your bit-twiddling with familiar syntax, typically leading to cleaner, more expressive code. + +Requirements +------------ + +These header-only libraries are continuously being tested with the following conforming [C++17](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf) compilers: + +| Platform | Compiler | Versions | Build | +| :------- | :------- | :------- | :---- | +| Linux | Clang
GCC | 6.0, 7-SVN
7.3, 8.1 | [![codecov](https://codecov.io/gh/rhalbersma/int_set/branch/master/graph/badge.svg)](https://codecov.io/gh/rhalbersma/int_set)
[![Build Status](https://travis-ci.org/rhalbersma/int_set.svg)](https://travis-ci.org/rhalbersma/int_set) | +| Windows | Visual Studio | 15.7 | [![Build status](https://ci.appveyor.com/api/projects/status/pn0u2i8mcfp4d9un?svg=true)](https://ci.appveyor.com/project/rhalbersma/int-set) | + +License +------- + +Copyright Rein Halbersma 2014-2018. +Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/users/license.html). +(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/xstd/int_set.hpp b/include/xstd/int_set.hpp new file mode 100644 index 0000000..7569277 --- /dev/null +++ b/include/xstd/int_set.hpp @@ -0,0 +1,1393 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // all_of, copy, copy_backward, copy_n, equal, fill_n, lexicographical_compare, max, swap_ranges +#include // array +#include // assert +#include // size_t +#include // uint64_t +#include // less, not_fn +#include // initializer_list +#include // bidirectional_iterator_tag, begin, end, next, prev, rbegin, rend, reverse_iterator +#include // digits +#include // accumulate +#include // tie +#include // is_integral_v, is_nothrow_swappable_v, is_unsigned_v +#include // move, swap + +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0202r3.html +#define XSTD_PP_CONSTEXPR_ALGORITHM /* constexpr */ + +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0879r0.html +#define XSTD_PP_CONSTEXPR_SWAP /* constexpr */ + +// no WG21 proposal yet +#define XSTD_PP_CONSTEXPR_NUMERIC /* constexpr */ + +#if defined(__GNUG__) + +#define XSTD_PP_CONSTEXPR_INTRINSIC constexpr + +#elif defined(_MSC_VER) + +#define XSTD_PP_CONSTEXPR_INTRINSIC /* constexpr */ + +#endif + +namespace xstd { +namespace detail { +namespace builtin { + +#if defined(__GNUG__) + +template +constexpr auto get(__uint128_t x) noexcept +{ + static_assert(0 <= N); static_assert(N < 2); + return static_cast(x >> (64 * N)); +} + +struct ctznz +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_ctz(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_ctzl(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long long x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_ctzll(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(__uint128_t x) const // Throws: Nothing. + { + assert(x != 0); + return get<0>(x) != 0 ? ctznz{}(get<0>(x)) : ctznz{}(get<1>(x)) + 64; + } +}; + +using bsfnz = ctznz; + +struct clznz +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_clz(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_clzl(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long long x) const // Throws: Nothing. + { + assert(x != 0); + return __builtin_clzll(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(__uint128_t x) const // Throws: Nothing. + { + assert(x != 0); + return get<1>(x) != 0 ? clznz{}(get<1>(x)) : clznz{}(get<0>(x)) + 64; + } +}; + +struct popcount +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned x) const noexcept + { + return __builtin_popcount(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long x) const noexcept + { + return __builtin_popcountl(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(unsigned long long x) const noexcept + { + return __builtin_popcountll(x); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(__uint128_t x) const noexcept + { + return popcount{}(get<0>(x)) + popcount{}(get<1>(x)); + } +}; + +#elif defined(_MSC_VER) + +#include + +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(__popcnt) + +#if defined(_WIN64) + +#pragma intrinsic(_BitScanForward64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(__popcnt64) + +#endif + +struct bsfnz +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint32_t x) const noexcept + { + assert(x != 0); + unsigned long index; + _BitScanForward(&index, static_cast(x)); + return static_cast(index); + } + +#if defined(_WIN64) + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint64_t x) const noexcept + { + assert(x != 0); + unsigned long index; + _BitScanForward64(&index, x); + return static_cast(index); + } + +#endif + +}; + +using ctznz = bsfnz; + +struct bsrnz +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint32_t x) const noexcept + { + assert(x != 0); + unsigned long index; + _BitScanReverse(&index, static_cast(x)); + return static_cast(index); + } + +#if defined(_WIN64) + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint64_t x) const noexcept + { + assert(x != 0); + unsigned long index; + _BitScanReverse64(&index, x); + return static_cast(index); + } + +#endif + +}; + +struct popcount +{ + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint32_t x) const noexcept + { + return static_cast(__popcnt(static_cast(x))); + } + +#if defined(_WIN64) + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator()(uint64_t x) const noexcept + { + return static_cast(__popcnt64(x)); + } + +#endif + +}; + +#endif + +} // namespace builtin + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto ctznz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return builtin::ctznz{}(x); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto bsfnz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return builtin::bsfnz{}(x); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto ctz(UIntType x) noexcept +{ + return x ? ctznz(x) : std::numeric_limits::digits; +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto bsf(UIntType x) noexcept +{ + return x ? bsfnz(x) : std::numeric_limits::digits; +} + +#if defined(__GNUG__) + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto clznz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return builtin::clznz{}(x); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto bsrnz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return std::numeric_limits::digits - 1 - builtin::clznz{}(x); +} + +#elif defined(_MSC_VER) + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto clznz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return std::numeric_limits::digits - 1 - builtin::bsrnz{}(x); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto bsrnz(UIntType x) // Throws: Nothing. +{ + assert(x != 0); + return builtin::bsrnz{}(x); +} + +#endif + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto clz(UIntType x) noexcept +{ + return x ? clznz(x) : std::numeric_limits::digits; +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto bsr(UIntType x) noexcept +{ + return x ? bsrnz(x) : -1; +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto popcount(UIntType x) noexcept +{ + return builtin::popcount{}(x); +} + +} // namespace detail + +template +class int_set; + +template XSTD_PP_CONSTEXPR_ALGORITHM auto operator== (int_set const& /* lhs */, int_set const& /* rhs */) noexcept; +template XSTD_PP_CONSTEXPR_ALGORITHM auto operator< (int_set const& /* lhs */, int_set const& /* rhs */) noexcept; +template XSTD_PP_CONSTEXPR_ALGORITHM auto is_subset_of(int_set const& /* lhs */, int_set const& /* rhs */) noexcept; +template XSTD_PP_CONSTEXPR_ALGORITHM auto intersects (int_set const& /* lhs */, int_set const& /* rhs */) noexcept; + +template +class int_set +{ + static_assert(0 <= N); + static_assert(std::is_unsigned_v); + static_assert(std::is_integral_v); + static_assert(sizeof(unsigned) <= sizeof(UIntType)); + + #if defined(_MSC_VER) + + static_assert(sizeof(UIntType) <= sizeof(std::size_t)); + + #endif + + constexpr static auto block_size = std::numeric_limits::digits; + constexpr static auto num_blocks = (N - 1 + block_size) / block_size; + constexpr static auto num_bits = num_blocks * block_size; + constexpr static auto excess_bits = num_bits - N; + + class proxy_reference; + class proxy_iterator; + + UIntType m_data[std::max(num_blocks, 1)]{}; // zero-initializated by default +public: + using key_type = int; + using key_compare = std::less<>; + using value_type = int; + using value_compare = std::less<>; + using pointer = proxy_iterator; + using const_pointer = proxy_iterator; + using reference = proxy_reference; + using const_reference = proxy_reference; + using size_type = int; + using difference_type = int; + using iterator = proxy_iterator; + using const_iterator = proxy_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using block_type = UIntType; + using insert_return_type = void; + + int_set() = default; + + template + constexpr int_set(InputIterator first, InputIterator last) // Throws: Nothing. + : + m_data{} + { + insert(first, last); + } + + constexpr int_set(std::initializer_list ilist) // Throws: Nothing. + : + int_set(ilist.begin(), ilist.end()) + {} + + template + auto assign(InputIterator first, InputIterator last) // Throws: Nothing. + { + clear(); + insert(first, last); + } + + auto& operator=(std::initializer_list ilist) // Throws: Nothing. + { + assign(ilist.begin(), ilist.end()); + return *this; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto begin() noexcept { return iterator{data(), find_first()}; } + XSTD_PP_CONSTEXPR_INTRINSIC auto begin() const noexcept { return const_iterator{data(), find_first()}; } + constexpr auto end() noexcept { return iterator{data(), num_bits}; } + constexpr auto end() const noexcept { return const_iterator{data(), num_bits}; } + + constexpr auto rbegin() noexcept { return reverse_iterator{end()}; } + constexpr auto rbegin() const noexcept { return const_reverse_iterator{end()}; } + XSTD_PP_CONSTEXPR_INTRINSIC auto rend() noexcept { return reverse_iterator{begin()}; } + XSTD_PP_CONSTEXPR_INTRINSIC auto rend() const noexcept { return const_reverse_iterator{begin()}; } + + XSTD_PP_CONSTEXPR_INTRINSIC auto cbegin() const noexcept { return const_iterator{begin()}; } + XSTD_PP_CONSTEXPR_INTRINSIC auto cend() const noexcept { return const_iterator{end()}; } + constexpr auto crbegin() const noexcept { return const_reverse_iterator{rbegin()}; } + XSTD_PP_CONSTEXPR_INTRINSIC auto crend() const noexcept { return const_reverse_iterator{rend()}; } + + XSTD_PP_CONSTEXPR_INTRINSIC auto front() const // Throws: Nothing. + -> const_reference + { + assert(!empty()); + return { *data(), find_front() }; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto back() const // Throws: Nothing. + -> const_reference + { + assert(!empty()); + return { *data(), find_back() }; + } + + template + XSTD_PP_CONSTEXPR_INTRINSIC auto any_of(UnaryPredicate pred [[maybe_unused]]) const + { + if constexpr (num_blocks == 1) { + for (auto block = m_data[0]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + if (pred(first)) { + return true; + } + block ^= bit1(first); + } + } else if constexpr (num_blocks >= 2) { + for (auto i = 0, offset = 0; i < num_blocks; ++i, offset += block_size) { + for (auto block = m_data[i]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + if (pred(offset + first)) { + return true; + } + block ^= bit1(first); + } + } + } + return false; + } + + template + XSTD_PP_CONSTEXPR_INTRINSIC auto none_of(UnaryPredicate pred [[maybe_unused]]) const + { + return !any_of(pred); + } + + template + XSTD_PP_CONSTEXPR_INTRINSIC auto all_of(UnaryPredicate pred [[maybe_unused]]) const + { + return !any_of(std::not_fn(pred)); + } + + template> + XSTD_PP_CONSTEXPR_INTRINSIC auto accumulate(T init, BinaryOperation op [[maybe_unused]] = BinaryOperation{}) const + { + auto result = std::move(init); + if constexpr (num_blocks == 1) { + for (auto block = m_data[0]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + result = op(result, first); + block ^= bit1(first); + } + } else if constexpr (num_blocks >= 2) { + for (auto i = 0, offset = 0; i < num_blocks; ++i, offset += block_size) { + for (auto block = m_data[i]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + result = op(result, offset + first); + block ^= bit1(first); + } + } + } + return result; + } + + template + XSTD_PP_CONSTEXPR_INTRINSIC auto for_each(UnaryFunction fun) const + { + if constexpr (num_blocks == 1) { + for (auto block = m_data[0]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + fun(first); + block ^= bit1(first); + } + } else if constexpr (num_blocks >= 2) { + for (auto i = 0, offset = 0; i < num_blocks; ++i, offset += block_size) { + for (auto block = m_data[i]; block != zero; /* update inside loop */) { + auto const first = detail::bsfnz(block); + fun(offset + first); + block ^= bit1(first); + } + } + } + return std::move(fun); + } + + template + XSTD_PP_CONSTEXPR_INTRINSIC auto reverse_for_each(UnaryFunction fun) const + { + if constexpr (num_blocks == 1) { + for (auto block = m_data[0]; block != zero; /* update inside loop */) { + auto const last = detail::bsrnz(block); + fun(last); + block ^= bit1(last); + } + } else if constexpr (num_blocks >= 2) { + for (auto i = num_blocks - 1, offset = (num_blocks - 1) * block_size; i >= 0; --i, offset -= block_size) { + for (auto block = m_data[i]; block != zero; /* update inside loop */) { + auto const last = detail::bsrnz(block); + fun(offset + last); + block ^= bit1(last); + } + } + } + return std::move(fun); + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto full() const noexcept + { + if constexpr (excess_bits == 0) { + if constexpr (num_blocks == 0) { + return true; + } else if constexpr (num_blocks == 1) { + return m_data[0] == ones; + } else if constexpr (num_blocks == 2) { + return m_data[0] == ones && m_data[1] == ones; + } else if constexpr (num_blocks >= 3) { + return std::all_of(std::begin(m_data), std::end(m_data), [](auto const block) { + return block == ones; + }); + } + } else { + if constexpr (num_blocks == 1) { + return m_data[0] == sane; + } else if constexpr (num_blocks == 2) { + return m_data[0] == ones && m_data[1] == sane; + } else { + static_assert(num_blocks >= 3); + return + std::all_of(std::begin(m_data), std::prev(std::end(m_data)), [](auto const block) { + return block == ones; + }) && m_data[num_blocks - 1] == sane; + ; + } + } + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto empty() const noexcept + { + if constexpr (num_blocks == 0) { + return true; + } else if constexpr (num_blocks == 1) { + return m_data[0] == zero; + } else if constexpr (num_blocks == 2) { + return m_data[0] == zero && m_data[1] == zero; + } else if constexpr (num_blocks >= 3) { + return std::all_of(std::begin(m_data), std::end(m_data), [](auto const block) { + return block == zero; + }); + } + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto count() const noexcept + { + if constexpr (num_blocks == 0) { + return 0; + } else if constexpr (num_blocks == 1) { + return detail::popcount(m_data[0]); + } else if constexpr (num_blocks == 2) { + return detail::popcount(m_data[0]) + detail::popcount(m_data[1]); + } else if constexpr (num_blocks >= 3) { + return std::accumulate(std::begin(m_data), std::end(m_data), 0, [](auto const sum, auto const block) { + return sum + detail::popcount(block); + }); + } + } + + constexpr static auto max_size() noexcept { return N; } + constexpr static auto capacity() noexcept { return num_bits; } + + constexpr auto& insert(value_type const n) // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + m_data[0] |= bit1(n); + } else { + m_data[which(n)] |= bit1(where(n)); + } + assert(contains(n)); + return *this; + } + + constexpr auto insert(const_iterator /* hint */, value_type const n) // Throws: Nothing. + -> iterator + { + insert(n); + return { data(), n }; + } + + template + constexpr auto insert(InputIterator first, InputIterator last) // Throws: Nothing. + { + while (first != last) { + insert(*first++); + } + } + + constexpr auto insert(std::initializer_list ilist) // Throws: Nothing. + { + insert(ilist.begin(), ilist.end()); + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto fill() noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] = ones; + } else if constexpr (num_blocks == 2) { + m_data[0] = ones; + m_data[1] = ones; + } else if constexpr (num_blocks >= 3) { + std::fill_n(std::begin(m_data), num_blocks, ones); + } + sanitize_back(); + assert(full()); + } + + constexpr auto& erase(value_type const n) // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + m_data[0] &= ~bit1(n); + } else { + m_data[which(n)] &= ~bit1(where(n)); + } + assert(!contains(n)); + return *this; + } + + template + constexpr auto erase(InputIterator first, InputIterator last) // Throws: Nothing. + { + while (first != last) { + erase(*first++); + } + } + + constexpr auto erase(std::initializer_list ilist) // Throws: Nothing. + { + erase(ilist.begin(), ilist.end()); + } + + XSTD_PP_CONSTEXPR_SWAP auto swap(int_set& other [[maybe_unused]]) noexcept(num_blocks == 0 || std::is_nothrow_swappable_v) + { + if constexpr (num_blocks == 1) { + using std::swap; + swap(m_data[0], other.m_data[0]); + } else if constexpr (num_blocks == 2) { + using std::swap; + swap(m_data[0], other.m_data[0]); + swap(m_data[1], other.m_data[1]); + } else if constexpr (num_blocks >= 3) { + std::swap_ranges(std::begin(m_data), std::end(m_data), std::begin(other.m_data)); + } + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto clear() noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] = zero; + } else if constexpr (num_blocks == 2) { + m_data[0] = zero; + m_data[1] = zero; + } else if constexpr (num_blocks >= 3) { + std::fill_n(std::begin(m_data), num_blocks, zero); + } + assert(empty()); + } + + constexpr auto& toggle(value_type const n) // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + m_data[0] ^= bit1(n); + } else { + m_data[which(n)] ^= bit1(where(n)); + } + return *this; + } + + constexpr auto& toggle() noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] = ~m_data[0]; + } else if constexpr (num_blocks == 2) { + m_data[0] = ~m_data[0]; + m_data[1] = ~m_data[1]; + } else if constexpr (num_blocks >= 3) { + for (auto&& block : m_data) { + block = ~block; + } + } + sanitize_back(); + return *this; + } + + constexpr auto contains(value_type const n) const // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + return (m_data[0] & bit1(n)) != zero; + } else { + return (m_data[which(n)] & bit1(where(n))) != zero; + } + } + + constexpr auto& operator&=(int_set const& other [[maybe_unused]]) noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] &= other.m_data[0]; + } else if constexpr (num_blocks == 2) { + m_data[0] &= other.m_data[0]; + m_data[1] &= other.m_data[1]; + } else if constexpr (num_blocks >= 3) { + for (auto i = 0; i < num_blocks; ++i) { + m_data[i] &= other.m_data[i]; + } + } + return *this; + } + + constexpr auto& operator|=(int_set const& other [[maybe_unused]]) noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] |= other.m_data[0]; + } else if constexpr (num_blocks == 2) { + m_data[0] |= other.m_data[0]; + m_data[1] |= other.m_data[1]; + } else if constexpr (num_blocks >= 3) { + for (auto i = 0; i < num_blocks; ++i) { + m_data[i] |= other.m_data[i]; + } + } + return *this; + } + + constexpr auto& operator^=(int_set const& other [[maybe_unused]]) noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] ^= other.m_data[0]; + } else if constexpr (num_blocks == 2) { + m_data[0] ^= other.m_data[0]; + m_data[1] ^= other.m_data[1]; + } else if constexpr (num_blocks >= 3) { + for (auto i = 0; i < num_blocks; ++i) { + m_data[i] ^= other.m_data[i]; + } + } + return *this; + } + + constexpr auto& operator-=(int_set const& other [[maybe_unused]]) noexcept + { + if constexpr (num_blocks == 1) { + m_data[0] &= ~other.m_data[0]; + } else if constexpr (num_blocks == 2) { + m_data[0] &= ~other.m_data[0]; + m_data[1] &= ~other.m_data[1]; + } else if constexpr (num_blocks >= 3) { + for (auto i = 0; i < num_blocks; ++i) { + m_data[i] &= ~other.m_data[i]; + } + } + return *this; + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto& operator<<=(size_type const n [[maybe_unused]]) // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + m_data[0] <<= n; + } else if constexpr (num_blocks >= 2) { + if (n == 0) { return *this; } + + auto const n_block = n / block_size; + auto const L_shift = n % block_size; + + if (L_shift == 0) { + std::copy_backward(std::begin(m_data), std::prev(std::end(m_data), n_block), std::end(m_data)); + } else { + auto const R_shift = block_size - L_shift; + + for (auto i = num_blocks - 1; i > n_block; --i) { + m_data[i] = + (m_data[i - n_block ] << L_shift) | + (m_data[i - n_block - 1] >> R_shift) + ; + } + m_data[n_block] = m_data[0] << L_shift; + } + std::fill_n(std::begin(m_data), n_block, zero); + } + sanitize_back(); + return *this; + } + + XSTD_PP_CONSTEXPR_ALGORITHM auto& operator>>=(size_type const n) // Throws: Nothing. + { + assert(0 <= n); assert(n < N); + if constexpr (num_blocks == 1) { + m_data[0] >>= n; + } else if constexpr (num_blocks >= 2) { + if (n == 0) { return *this; } + + auto const n_block = n / block_size; + auto const R_shift = n % block_size; + + if (R_shift == 0) { + std::copy_n(std::next(std::begin(m_data), n_block), num_blocks - n_block, std::begin(m_data)); + } else { + auto const L_shift = block_size - R_shift; + + for (auto i = 0; i < num_blocks - 1 - n_block; ++i) { + m_data[i] = + (m_data[i + n_block ] >> R_shift) | + (m_data[i + n_block + 1] << L_shift) + ; + } + m_data[num_blocks - 1 - n_block] = m_data[num_blocks - 1] >> R_shift; + } + std::fill_n(std::rbegin(m_data), n_block, zero); + } + return *this; + } + + template + friend constexpr auto hash_append(HashAlgorithm& h, int_set const& is) noexcept + { + h(is.data(), is.capacity() / std::numeric_limits::digits); + } + +private: + constexpr static auto zero = static_cast(0); + constexpr static auto ones = ~zero; + constexpr static auto sane = ones >> excess_bits; + + constexpr static auto bit1(value_type const n) // Throws: Nothing. + { + assert(0 <= n); assert(n < block_size); + return static_cast(1) << n; + } + + constexpr static auto which(value_type const n) // Throws: Nothing. + { + static_assert(num_blocks != 1); + assert(0 <= n); assert(n < num_bits); + return n / block_size; + } + + constexpr static auto where(value_type const n) // Throws: Nothing. + { + static_assert(num_blocks != 1); + assert(0 <= n); assert(n < num_bits); + return n % block_size; + } + + constexpr auto sanitize_back() noexcept + { + if constexpr (excess_bits != 0) { + if constexpr (num_blocks == 1) { + m_data[0] &= sane; + } else { + static_assert(num_blocks >= 2); + m_data[num_blocks - 1] &= sane; + } + } + } + + constexpr auto const* data() const noexcept + { + return m_data; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto find_first() const noexcept + { + if constexpr (num_blocks == 0) { + return 0; + } else if constexpr (num_blocks == 1) { + return detail::bsf(m_data[0]); + } else if constexpr (num_blocks >= 2) { + auto offset = 0; + for (auto i = 0; i < num_blocks; ++i, offset += block_size) { + if (auto const block = m_data[i]; block != zero) { + offset += detail::ctznz(block); + break; + } + } + return offset; + } + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto find_front() const // Throws: Nothing. + { + assert(!empty()); + if constexpr (num_blocks == 1) { + return detail::bsfnz(m_data[0]); + } else if constexpr (num_blocks == 2) { + return + (m_data[0] != zero) ? + detail::bsfnz(m_data[0]) : + detail::bsfnz(m_data[1]) + block_size + ; + } else { + auto offset = 0; + for (auto i = 0; i < num_blocks - 1; ++i, offset += block_size) { + if (auto const block = m_data[i]; block != zero) { + return offset + detail::ctznz(block); + } + } + return offset + detail::ctznz(m_data[std::max(num_blocks - 1, 0)]); + } + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto find_back() const // Throws: Nothing. + { + assert(!empty()); + if constexpr (num_blocks == 1) { + return detail::bsrnz(m_data[0]); + } else if constexpr (num_blocks == 2) { + return + (m_data[1] != zero) ? + detail::bsrnz(m_data[1]) + block_size : + detail::bsrnz(m_data[0]) + ; + } else { + auto offset = num_bits - 1; + for (auto i = num_blocks - 1; i > 0; --i, offset -= block_size) { + if (auto const block = m_data[i]; block != zero) { + return offset - detail::clznz(block); + } + } + return offset - detail::clznz(m_data[0]); + } + } + + class proxy_reference + { + constexpr auto assert_invariants() const noexcept + { + assert(0 <= m_value); assert(m_value < N); + } + + block_type const& m_block; + value_type const m_value; + public: + ~proxy_reference() = default; + proxy_reference(proxy_reference const&) = default; + proxy_reference(proxy_reference&&) = default; + proxy_reference& operator=(proxy_reference const&) = delete; + proxy_reference& operator=(proxy_reference&&) = delete; + + proxy_reference() = delete; + + constexpr proxy_reference(block_type const& b, value_type const v) noexcept + : + m_block{b}, + m_value{v} + { + assert_invariants(); + } + + proxy_reference& operator=(value_type const) = delete; + + /* implicit */ constexpr operator value_type() const noexcept + { + return m_value; + } + + constexpr auto operator&() const noexcept + -> proxy_iterator + { + return { &m_block, m_value }; + } + }; + + class proxy_iterator + { + public: + using difference_type = typename int_set::difference_type; + using value_type = typename int_set::value_type; + using pointer = proxy_iterator; + using reference = proxy_reference; + using iterator_category = std::bidirectional_iterator_tag; + + private: + constexpr auto assert_invariants() const noexcept + { + assert(m_block != nullptr); + assert(0 <= m_value); assert(m_value < N || m_value == num_bits); + } + + block_type const* m_block; + value_type m_value; + + public: + proxy_iterator() = default; + + constexpr proxy_iterator(block_type const* b, value_type const v) // Throws: Nothing. + : + m_block{b}, + m_value{v} + { + assert_invariants(); + } + + constexpr auto operator*() const // Throws: Nothing. + -> proxy_reference + { + assert(0 <= m_value); assert(m_value < N); + return { *m_block, m_value }; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto& operator++() // Throws: Nothing. + { + assert(0 <= m_value); assert(m_value < N); + increment(); + assert(0 < m_value); assert(m_value < N || m_value == num_bits); + return *this; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator++(int) // Throws: Nothing. + { + auto nrv = *this; ++*this; return nrv; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto& operator--() // Throws:Nothing. + { + assert(0 < m_value); assert(m_value < N || m_value == num_bits); + decrement(); + assert(0 <= m_value); assert(m_value < N); + return *this; + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto operator--(int) // Throws: Nothing. + { + auto nrv = *this; --*this; return nrv; + } + + friend constexpr auto operator==(proxy_iterator const& lhs, proxy_iterator const& rhs) noexcept + { + assert(lhs.m_block == rhs.m_block); + return lhs.m_value == rhs.m_value; + } + + friend constexpr auto operator!=(proxy_iterator const& lhs, proxy_iterator const& rhs) noexcept + { + return !(lhs == rhs); + } + + private: + XSTD_PP_CONSTEXPR_INTRINSIC auto increment() // Throws: Nothing. + { + assert(m_value < N); + if (++m_value == num_bits) { return; } + if constexpr (num_blocks == 1) { + if (auto const block = *m_block >> m_value; block != zero) { + m_value += detail::ctznz(block); + return; + } + m_value = block_size; + } else if constexpr (num_blocks >= 2) { + auto i = which(m_value); + if (auto const offset = where(m_value); offset != 0) { + if (auto const block = m_block[i] >> offset; block != zero) { + m_value += detail::ctznz(block); + return; + } + ++i; + m_value += block_size - offset; + } + for (/* initialized before loop */; i < num_blocks; ++i, m_value += block_size) { + if (auto const block = m_block[i]; block != zero) { + m_value += detail::ctznz(block); + return; + } + } + } + assert(m_value == num_bits); + } + + XSTD_PP_CONSTEXPR_INTRINSIC auto decrement() // Throws: Nothing. + { + assert(0 < m_value); + --m_value; + if constexpr (num_blocks == 1) { + m_value -= detail::clznz(*m_block << (block_size - 1 - m_value)); + } else if constexpr (num_blocks >= 2) { + auto i = which(m_value); + if (auto const offset = where(m_value); offset != block_size - 1) { + if (auto const block = m_block[i] << (block_size - 1 - offset); block != zero) { + m_value -= detail::clznz(block); + return; + } + --i; + m_value -= offset + 1; + } + for (/* initialized before loop */; i >= 0; --i, m_value -= block_size) { + if (auto const block = m_block[i]; block != zero) { + m_value -= detail::clznz(block); + return; + } + } + } + } + }; + + friend XSTD_PP_CONSTEXPR_ALGORITHM auto operator== <>(int_set const& /* lhs */, int_set const& /* rhs */) noexcept; + friend XSTD_PP_CONSTEXPR_ALGORITHM auto operator< <>(int_set const& /* lhs */, int_set const& /* rhs */) noexcept; + friend XSTD_PP_CONSTEXPR_ALGORITHM auto is_subset_of<>(int_set const& /* lhs */, int_set const& /* rhs */) noexcept; + friend XSTD_PP_CONSTEXPR_ALGORITHM auto intersects <>(int_set const& /* lhs */, int_set const& /* rhs */) noexcept; +}; + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator==(int_set const& lhs [[maybe_unused]], int_set const& rhs [[maybe_unused]]) noexcept +{ + constexpr auto num_blocks = int_set::num_blocks; + if constexpr (num_blocks == 0) { + return true; + } else if constexpr (num_blocks == 1) { + return lhs.m_data[0] == rhs.m_data[0]; + } else if constexpr (num_blocks == 2) { + constexpr auto tied = [](auto const& is) { + return std::tie(is.m_data[0], is.m_data[1]); + }; + return tied(lhs) == tied(rhs); + } else if constexpr (num_blocks >= 3) { + return std::equal( + std::begin(lhs.m_data), std::end(lhs.m_data), + std::begin(rhs.m_data), std::end(rhs.m_data) + ); + } +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator!=(int_set const& lhs, int_set const& rhs) noexcept +{ + return !(lhs == rhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator<(int_set const& lhs [[maybe_unused]], int_set const& rhs [[maybe_unused]]) noexcept +{ + constexpr auto num_blocks = int_set::num_blocks; + if constexpr (num_blocks == 0) { + return false; + } else if constexpr (num_blocks == 1) { + return lhs.m_data[0] < rhs.m_data[0]; + } else if constexpr (num_blocks == 2) { + constexpr auto tied = [](auto const& is) { + return std::tie(is.m_data[1], is.m_data[0]); + }; + return tied(lhs) < tied(rhs); + } else if constexpr (num_blocks >= 3) { + return std::lexicographical_compare( + std::rbegin(lhs.m_data), std::rend(lhs.m_data), + std::rbegin(rhs.m_data), std::rend(rhs.m_data) + ); + } +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator>(int_set const& lhs, int_set const& rhs) noexcept +{ + return rhs < lhs; +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator>=(int_set const& lhs, int_set const& rhs) noexcept +{ + return !(lhs < rhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator<=(int_set const& lhs, int_set const& rhs) noexcept +{ + return !(rhs < lhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto is_subset_of(int_set const& lhs [[maybe_unused]], int_set const& rhs [[maybe_unused]]) noexcept +{ + constexpr static auto num_blocks = int_set::num_blocks; + constexpr static auto zero [[maybe_unused]] = int_set::zero; + if constexpr (num_blocks == 0) { + return true; + } else if constexpr (num_blocks == 1) { + return (lhs.m_data[0] & ~rhs.m_data[0]) == zero; + } else if constexpr (num_blocks == 2) { + return + (lhs.m_data[0] & ~rhs.m_data[0]) == zero && + (lhs.m_data[1] & ~rhs.m_data[1]) == zero + ; + } else if constexpr (num_blocks >= 3) { + return std::equal( + std::begin(lhs.m_data), std::end(lhs.m_data), + std::begin(rhs.m_data), std::end(rhs.m_data), + [](auto const wL, auto const wR) { + return (wL & ~wR) == zero; + } + ); + } +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto is_superset_of(int_set const& lhs, int_set const& rhs) noexcept +{ + return is_subset_of(rhs, lhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto is_proper_subset_of(int_set const& lhs, int_set const& rhs) noexcept +{ + return is_subset_of(lhs, rhs) && !is_subset_of(rhs, lhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto is_proper_superset_of(int_set const& lhs, int_set const& rhs) noexcept +{ + return is_superset_of(lhs, rhs) && !is_superset_of(rhs, lhs); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto intersects(int_set const& lhs [[maybe_unused]], int_set const& rhs [[maybe_unused]]) noexcept +{ + constexpr static auto num_blocks = int_set::num_blocks; + constexpr static auto zero [[maybe_unused]] = int_set::zero; + if constexpr (num_blocks == 0) { + return false; + } else if constexpr (num_blocks == 1) { + return (lhs.m_data[0] & rhs.m_data[0]) != zero; + } else if constexpr (num_blocks == 2) { + return + (lhs.m_data[0] & rhs.m_data[0]) != zero || + (lhs.m_data[1] & rhs.m_data[1]) != zero + ; + } else if constexpr (num_blocks >= 3) { + return !std::equal( + std::begin(lhs.m_data), std::end(lhs.m_data), + std::begin(rhs.m_data), std::end(rhs.m_data), + [](auto const wL, auto const wR) { + return (wL & wR) == zero; + } + ); + } +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto disjoint(int_set const& lhs, int_set const& rhs) noexcept +{ + return !intersects(lhs, rhs); +} + +template +XSTD_PP_CONSTEXPR_SWAP auto swap(int_set& lhs, int_set& rhs) noexcept(noexcept(lhs.swap(rhs))) +{ + lhs.swap(rhs); +} + +template +constexpr auto operator~(int_set const& lhs) noexcept +{ + auto nrv{lhs}; nrv.toggle(); return nrv; +} + +template +constexpr auto operator&(int_set const& lhs, int_set const& rhs) noexcept +{ + auto nrv{lhs}; nrv &= rhs; return nrv; +} + +template +constexpr auto operator|(int_set const& lhs, int_set const& rhs) noexcept +{ + auto nrv{lhs}; nrv |= rhs; return nrv; +} + +template +constexpr auto operator^(int_set const& lhs, int_set const& rhs) noexcept +{ + auto nrv{lhs}; nrv ^= rhs; return nrv; +} + +template +constexpr auto operator-(int_set const& lhs, int_set const& rhs) noexcept +{ + auto nrv{lhs}; nrv -= rhs; return nrv; +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator<<(int_set const& lhs, int const n) // Throws: Nothing. +{ + assert(0 <= n); assert(n < N); + auto nrv{lhs}; nrv <<= n; return nrv; +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto operator>>(int_set const& lhs, int const n) // Throws: Nothing. +{ + assert(0 <= n); assert(n < N); + auto nrv{lhs}; nrv >>= n; return nrv; +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto begin(int_set& is) + -> decltype(is.begin()) +{ + return is.begin(); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto begin(int_set const& is) + -> decltype(is.begin()) +{ + return is.begin(); +} + +template +constexpr auto end(int_set& is) + -> decltype(is.end()) +{ + return is.end(); +} + +template +constexpr auto end(int_set const& is) + -> decltype(is.end()) +{ + return is.end(); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto cbegin(int_set const& is) noexcept(noexcept(xstd::begin(is))) + -> decltype(xstd::begin(is)) +{ + return xstd::begin(is); +} + +template +constexpr auto cend(int_set const& is) noexcept(noexcept(xstd::end(is))) + -> decltype(xstd::end(is)) +{ + return xstd::end(is); +} + +template +constexpr auto rbegin(int_set& is) + -> decltype(is.rbegin()) +{ + return is.rbegin(); +} + +template +constexpr auto rbegin(int_set const& is) + -> decltype(is.rbegin()) +{ + return is.rbegin(); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto rend(int_set& is) + -> decltype(is.rend()) +{ + return is.rend(); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto rend(int_set const& is) + -> decltype(is.rend()) +{ + return is.rend(); +} + +template +constexpr auto crbegin(int_set const& is) + -> decltype(xstd::rbegin(is)) +{ + return xstd::rbegin(is); +} + +template +XSTD_PP_CONSTEXPR_INTRINSIC auto crend(int_set const& is) + -> decltype(xstd::rend(is)) +{ + return xstd::rend(is); +} + +template +XSTD_PP_CONSTEXPR_ALGORITHM auto empty(int_set const& is) + -> decltype(is.empty()) +{ + return is.empty(); +} + +} // namespace xstd diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..792364f --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,88 @@ +find_package(Boost COMPONENTS + unit_test_framework +) + +set(cxx_compile_definitions + BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE + BOOST_ALL_NO_LIB + BOOST_ALL_DYN_LINK + BOOST_TEST_MAIN + $<$: + _SCL_SECURE_NO_WARNINGS + > +) + +set(cxx_compile_options_warnings + $<$: + /W4 + > + $<$: + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-disabled-macro-expansion + -Wno-exit-time-destructors + -Wno-global-constructors + -Wno-range-loop-analysis + > + $<$: + -Wall + -Wextra + -Wpedantic + -Wconversion + -Wshadow + -Wsign-compare + -Wsign-conversion + -Wsign-promo + > + $<$,$>: + -Werror + -pedantic-errors + -Wno-padded + > +) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(cxx_compile_options_coverage + -g + --coverage + ) + + set(cxx_linker_options_coverage + --coverage + ) +endif() + +set(test_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(test_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/src) +file(GLOB_RECURSE targets RELATIVE ${test_source_dir} *.cpp) + +foreach(t ${targets}) + get_filename_component(target_path ${t} PATH) + get_filename_component(target_name_we ${t} NAME_WE) + string(REPLACE "/" "." target_id ${target_path}/${target_name_we}) + string(REGEX REPLACE "^[.]" "" target_id ${target_id}) + + add_executable(${target_id} src/${t}) + + target_link_libraries(${target_id} PRIVATE + int_set + Boost::unit_test_framework + ${cxx_linker_options_coverage} + ) + + target_include_directories(${target_id} PRIVATE + ${test_include_dir} + ) + + target_compile_definitions(${target_id} PRIVATE + ${cxx_compile_definitions} + ) + + target_compile_options(${target_id} PRIVATE + ${cxx_compile_options_warnings} + ${cxx_compile_options_coverage} + ) + + add_test(${target_id} ${target_id}) +endforeach() diff --git a/test/include/exhaustive.hpp b/test/include/exhaustive.hpp new file mode 100644 index 0000000..55aa09f --- /dev/null +++ b/test/include/exhaustive.hpp @@ -0,0 +1,162 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // array +#include // initializer_list + +namespace xstd { + +// NOTE: these tests are O(N) + +template +auto all_values(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + fun(i); + } +} + +template +auto all_cardinality_sets(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + auto const ii = ~(~IntSet{} << i); + fun(ii); + } + auto const iN = ~IntSet{}; + fun(iN); +} + +template +auto all_singleton_arrays(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + auto const i1 = std::array{{ i }}; + fun(i1); + } +} + +template +auto all_singleton_ilists(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + auto i1 = { i }; + fun(i1); + } +} + +template +auto all_singleton_sets(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + IntSet tmp; + auto const i1 = set(tmp, i); + fun(i1); + } +} + +// NOTE: this test is O(N^2) + +template +auto all_doubleton_arrays(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto j = SizeType{1}; j < N; ++j) { + for (auto i = SizeType{0}; i < j; ++i) { + auto const ij2 = std::array{{ i, j }}; + fun(ij2); + } + } +} + +template +auto all_doubleton_ilists(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto j = SizeType{1}; j < N; ++j) { + for (auto i = SizeType{0}; i < j; ++i) { + auto ij2 = { i, j }; + fun(ij2); + } + } +} + +template +auto all_doubleton_sets(UnaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto j = SizeType{1}; j < N; ++j) { + for (auto i = SizeType{0}; i < j; ++i) { + IntSet tmp; + auto const ij2 = set(set(tmp, i), j); + fun(ij2); + } + } +} + +template +auto all_singleton_set_pairs(BinaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + IntSet tmp1; + auto const i1 = set(tmp1, i); + for (auto j = SizeType{0}; j < N; ++j) { + IntSet tmp2; + auto const j1 = set(tmp2, j); + fun(i1, j1); + } + } +} + +// NOTE: this test is O(N^3) + +template +auto all_singleton_set_triples(TernaryFunction fun) +{ + constexpr auto N = size(IntSet{}); + using SizeType = decltype(N); + + for (auto i = SizeType{0}; i < N; ++i) { + IntSet tmp1; + auto const i1 = set(tmp1, i); + for (auto j = SizeType{0}; j < N; ++j) { + IntSet tmp2; + auto const j1 = set(tmp2, j); + for (auto k = SizeType{0}; k < N; ++k) { + IntSet tmp3; + auto const k1 = set(tmp3, k); + fun(i1, j1, k1); + } + } + } +} + +} // namespace xstd diff --git a/test/include/legacy/bitset.hpp b/test/include/legacy/bitset.hpp new file mode 100644 index 0000000..9c95327 --- /dev/null +++ b/test/include/legacy/bitset.hpp @@ -0,0 +1,151 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // bitset +#include // size_t + +namespace xstd { + +template +auto& set(std::bitset& bs) noexcept +{ + return bs.set(); +} + +template +auto& set(std::bitset& bs, std::size_t const pos, bool const val = true) +{ + return bs.set(pos, val); +} + +template +auto& reset(std::bitset& bs) noexcept +{ + return bs.reset(); +} + +template +auto& reset(std::bitset& bs, std::size_t const pos) +{ + return bs.reset(pos); +} + +template +auto& flip(std::bitset& bs) noexcept +{ + return bs.flip(); +} + +template +auto& flip(std::bitset& bs, std::size_t const pos) +{ + return bs.flip(pos); +} + +template +constexpr auto size(std::bitset const& bs) noexcept +{ + return bs.size(); +} + +template +auto test(std::bitset const& bs, std::size_t const pos) +{ + return bs.test(pos); +} + +template +auto all(std::bitset const& bs) noexcept +{ + return bs.all(); +} + +template +auto any(std::bitset const& bs) noexcept +{ + return bs.any(); +} + +template +auto none(std::bitset const& bs) noexcept +{ + return bs.none(); +} + +template +auto contains(std::bitset const& bs, std::size_t const pos) // Throws: Nothing. +{ + return bs[pos]; +} + +template +auto operator<(std::bitset const& lhs, std::bitset const& rhs) +{ + return lhs.to_string() < rhs.to_string(); +} + +template +auto operator>(std::bitset const& lhs, std::bitset const& rhs) +{ + return rhs < lhs; +} + +template +auto operator>=(std::bitset const& lhs, std::bitset const& rhs) +{ + return !(lhs < rhs); +} + +template +auto operator<=(std::bitset const& lhs, std::bitset const& rhs) +{ + return !(rhs < lhs); +} + +template +auto is_subset_of(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return (lhs & ~rhs).none(); +} + +template +auto is_superset_of(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return is_subset_of(rhs, lhs); +} + +template +auto is_proper_subset_of(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return is_subset_of(lhs, rhs) && !is_subset_of(rhs, lhs); +} + +template +auto is_proper_superset_of(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return is_superset_of(lhs, rhs) && !is_superset_of(rhs, lhs); +} + +template +auto intersects(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return (lhs & rhs).any(); +} + +template +auto disjoint(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return !intersects(lhs, rhs); +} + +template +auto operator-(std::bitset const& lhs, std::bitset const& rhs) noexcept +{ + return lhs & ~rhs; +} + +} // namespace xstd diff --git a/test/include/legacy/int_set.hpp b/test/include/legacy/int_set.hpp new file mode 100644 index 0000000..f49ec91 --- /dev/null +++ b/test/include/legacy/int_set.hpp @@ -0,0 +1,102 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // int_set +#include // out_of_range + +namespace xstd { + +template +using size_t = typename T::size_type; + +template +auto& set(int_set& is) noexcept +{ + is.fill(); + return is; +} + +template +auto& set(int_set& is, size_t> const pos, bool const val = true) +{ + if (pos >= N) { + throw std::out_of_range{"set(int_set&, int): index out of range"}; + } + return val ? is.insert(pos) : is.erase(pos); +} + +template +auto& reset(int_set& is) noexcept +{ + is.clear(); + return is; +} + +template +auto& reset(int_set& is, size_t> const pos) +{ + if (pos >= N) { + throw std::out_of_range{"reset(int_set&, int): index out of range"}; + } + return is.erase(pos); +} + +template +auto& flip(int_set& is) noexcept +{ + return is.toggle(); +} + +template +auto& flip(int_set& is, size_t> const pos) +{ + if (pos >= N) { + throw std::out_of_range{"flip(int_set&, int): index out of range"}; + } + return is.toggle(pos); +} + +template +constexpr auto size(int_set const& is) noexcept +{ + return is.max_size(); +} + +template +auto test(int_set const& is, size_t> const pos) +{ + if (pos >= N) { + throw std::out_of_range{"test(int_set&, int): index out of range"}; + } + return is.contains(pos); +} + +template +auto all(int_set const& is) noexcept +{ + return is.full(); +} + +template +auto any(int_set const& is) noexcept +{ + return !is.empty(); +} + +template +auto none(int_set const& is) noexcept +{ + return is.empty(); +} + +template +constexpr auto contains(int_set const& is, size_t> const pos) // Throws: Nothing. +{ + return is.contains(pos); +} + +} // namespace xstd diff --git a/test/include/primitive.hpp b/test/include/primitive.hpp new file mode 100644 index 0000000..c6bbb38 --- /dev/null +++ b/test/include/primitive.hpp @@ -0,0 +1,1056 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // has_range_assign_v, has_ilist_assign_v, has_const_iterator_v, has_front_v, has_back_v, + // has_any_of_v, has_none_of_v, has_all_of_v, has_accumulate_v, has_for_each_v, has_reverse_for_each_v, + // has_op_minus_assign_v, has_range_insert_v, has_ilist_insert_v, has_range_erase_v, has_ilist_erase_v, + // has_static_max_size_v, has_static_capacity_v, has_full_v, has_empty_v +#include // BOOST_CHECK, BOOST_CHECK_EQUAL, BOOST_CHECK_NE, BOOST_CHECK_THROW +#include // all_of, any_of, none_of, equal, for_each, includes, is_sorted, lexicographical_compare, + // set_intersection, set_union, set_symmetric_difference, set_difference +#include // greater, plus +#include // initializer_list +#include // basic_istream +#include // distance, inserter, next +#include // addressof +#include // accumulate +#include // out_of_range +#include // is_constructible_v +#include // vector + +namespace xstd { + +template +struct constructor +{ + constexpr auto operator()() const noexcept + { + IntSet is; + BOOST_CHECK(none(is)); // [bitset.cons]/1 + } + + template + auto operator()(InputIterator first [[maybe_unused]], InputIterator last [[maybe_unused]]) const + { + if constexpr (std::is_constructible_v) { + auto const dst = IntSet(first, last); + std::for_each(first, last, [&](auto&& elem) { + BOOST_CHECK(dst.contains(elem)); + }); + BOOST_CHECK_EQUAL(dst.count(), static_cast(std::distance(first, last))); + } + } + + template + constexpr auto operator()(std::initializer_list ilist [[maybe_unused]]) const + { + if constexpr (std::is_constructible_v>) { + auto const dst = IntSet(ilist); + for (auto&& elem : ilist) { + BOOST_CHECK(dst.contains(elem)); + } + BOOST_CHECK_EQUAL(dst.count(), static_cast(ilist.size())); + } + } +}; + +struct mem_assign +{ + template + auto operator()(IntSet const& is [[maybe_unused]], InputIterator first [[maybe_unused]], InputIterator last [[maybe_unused]]) const + { + if constexpr (tti::has_range_assign_v) { + auto const src = is; + auto dst = src; dst.assign(first, last); + std::for_each(first, last, [&](auto&& elem) { + BOOST_CHECK(dst.contains(elem)); + }); + BOOST_CHECK_EQUAL(dst.count(), static_cast(std::distance(first, last))); + } + } + + template + constexpr auto operator()(IntSet const& is [[maybe_unused]], std::initializer_list ilist [[maybe_unused]]) const + { + if constexpr (tti::has_ilist_assign_v) { + auto const src = is; + auto dst = src; dst = ilist; + for (auto&& elem : ilist) { + BOOST_CHECK(dst.contains(elem)); + } + BOOST_CHECK_EQUAL(dst.count(), static_cast(ilist.size())); + } + } +}; + +struct const_reference +{ + template + auto operator()(IntSet const& is [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_const_iterator_v) { + for (auto first = is.begin(), last = is.end(); first != last; ++first) { + auto const ref = *first; + + BOOST_CHECK(&ref == first); + BOOST_CHECK(is.contains(ref)); + } + + for (auto&& ref : is) { + BOOST_CHECK(is.contains(ref)); + } + } + } +}; + +struct const_iterator +{ + template + auto operator()(IntSet const& cis [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_const_iterator_v) { + auto is = cis; + + BOOST_CHECK_EQUAL(std::distance( is. begin(), is. end()), cis.count()); + BOOST_CHECK_EQUAL(std::distance(cis. begin(), cis. end()), cis.count()); + BOOST_CHECK_EQUAL(std::distance( is. rbegin(), is. rend()), cis.count()); + BOOST_CHECK_EQUAL(std::distance(cis. rbegin(), cis. rend()), cis.count()); + BOOST_CHECK_EQUAL(std::distance( is. cbegin(), is. cend()), cis.count()); + BOOST_CHECK_EQUAL(std::distance( is.crbegin(), is.crend()), cis.count()); + BOOST_CHECK_EQUAL(std::distance( begin( is), end( is)), cis.count()); + BOOST_CHECK_EQUAL(std::distance( begin(cis), end(cis)), cis.count()); + BOOST_CHECK_EQUAL(std::distance( rbegin( is), rend( is)), cis.count()); + BOOST_CHECK_EQUAL(std::distance( rbegin(cis), rend(cis)), cis.count()); + BOOST_CHECK_EQUAL(std::distance( cbegin( is), cend( is)), cis.count()); + BOOST_CHECK_EQUAL(std::distance(crbegin( is), crend( is)), cis.count()); + + BOOST_CHECK(std::is_sorted( is. begin(), is. end())); + BOOST_CHECK(std::is_sorted(cis. begin(), cis. end())); + BOOST_CHECK(std::is_sorted( is. rbegin(), is. rend(), std::greater{})); + BOOST_CHECK(std::is_sorted(cis. rbegin(), cis. rend(), std::greater{})); + BOOST_CHECK(std::is_sorted( is. cbegin(), is. cend())); + BOOST_CHECK(std::is_sorted( is.crbegin(), is.crend(), std::greater{})); + BOOST_CHECK(std::is_sorted( begin( is), end( is))); + BOOST_CHECK(std::is_sorted( begin(cis), end(cis))); + BOOST_CHECK(std::is_sorted( rbegin( is), rend( is), std::greater{})); + BOOST_CHECK(std::is_sorted( rbegin(cis), rend(cis), std::greater{})); + BOOST_CHECK(std::is_sorted( cbegin( is), cend( is))); + BOOST_CHECK(std::is_sorted(crbegin( is), crend( is), std::greater{})); + } + } +}; + +struct mem_front +{ + template + auto operator()(IntSet const& is [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_front_v && tti::has_const_iterator_v) { + BOOST_CHECK(is.empty() || (is.front() == *is.begin())); + BOOST_CHECK(is.empty() || (&is.front() == is.begin())); + } + } +}; + +struct mem_back +{ + template + auto operator()(IntSet const& is [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_back_v && tti::has_const_iterator_v) { + BOOST_CHECK(is.empty() || (is.back() == *is.rbegin())); + BOOST_CHECK(is.empty() || (&is.back() == std::next(is.rbegin()).base())); + } + } +}; + +struct mem_any_of +{ + template + auto operator()(IntSet const& is [[maybe_unused]], UnaryPredicate pred [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_any_of_v && tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is.any_of(pred), std::any_of(is.begin(), is.end(), pred)); + } + } +}; + +struct mem_none_of +{ + template + auto operator()(IntSet const& is [[maybe_unused]], UnaryPredicate pred [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_none_of_v && tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is.none_of(pred), std::none_of(is.begin(), is.end(), pred)); + } + } +}; + +struct mem_all_of +{ + template + auto operator()(IntSet const& is [[maybe_unused]], UnaryPredicate pred [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_all_of_v && tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is.all_of(pred), std::all_of(is.begin(), is.end(), pred)); + } + } +}; + +struct mem_accumulate +{ + template> + auto operator()(IntSet const& is [[maybe_unused]], T init [[maybe_unused]], BinaryOperation op [[maybe_unused]] = BinaryOperation{}) const noexcept + { + if constexpr (tti::has_accumulate_v && tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is.accumulate(init, op), std::accumulate(is.begin(), is.end(), init, op)); + } + } +}; + +namespace detail { + +class tracer +{ + std::vector m_trace; +public: + auto operator()(int const elem) + { + m_trace.push_back(elem); + } + + friend auto operator==(tracer const& lhs, tracer const& rhs) noexcept + { + return lhs.m_trace == rhs.m_trace; + } +}; + +} // namespace detail + +struct mem_for_each +{ + template + auto operator()(IntSet const& is [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_for_each_v && tti::has_const_iterator_v) { + BOOST_CHECK(is.for_each(detail::tracer{}) == std::for_each(is.begin(), is.end(), detail::tracer{})); + + { + detail::tracer fun; + for (auto first = is.begin(), last = is.end(); first != last; first++ /* post-increment to hit code coverage */) { + fun(*first); + } + BOOST_CHECK(is.for_each(detail::tracer{}) == std::move(fun)); + } + + { + detail::tracer fun; + for (auto&& elem : is) { + fun(elem); + } + BOOST_CHECK(is.for_each(detail::tracer{}) == std::move(fun)); + } + } + } +}; + +struct mem_reverse_for_each +{ + template + auto operator()(IntSet const& is [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_reverse_for_each_v && tti::has_const_iterator_v) { + BOOST_CHECK(is.reverse_for_each(detail::tracer{}) == std::for_each(is.rbegin(), is.rend(), detail::tracer{})); + + { + detail::tracer fun; + for (auto first = is.end(), last = is.begin(); first != last; first-- /* post-decrement to hit code coverage */) { + fun(*std::prev(first)); + } + BOOST_CHECK(is.reverse_for_each(detail::tracer{}) == std::move(fun)); + } + } + } +}; + +struct op_bitand_assign +{ + template + constexpr auto operator()(IntSet const& lhs, IntSet const& rhs) const noexcept + { + auto const src = lhs; + auto dst = src; + auto const& ret = (dst &= rhs); + + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/1 + BOOST_CHECK_EQUAL(contains(dst, i), !contains(rhs, i) ? false : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/2 + } +}; + +struct op_bitor_assign +{ + template + constexpr auto operator()(IntSet const& lhs, IntSet const& rhs) const noexcept + { + auto const src = lhs; + auto dst = src; + auto const& ret = (dst |= rhs); + + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/3 + BOOST_CHECK_EQUAL(contains(dst, i), contains(rhs, i) ? true : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/4 + } +}; + +struct op_xor_assign +{ + template + constexpr auto operator()(IntSet const& lhs, IntSet const& rhs) const noexcept + { + auto const src = lhs; + auto dst = src; + auto const& ret = (dst ^= rhs); + + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/5 + BOOST_CHECK_EQUAL(contains(dst, i), contains(rhs, i) ? !contains(src, i) : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/6 + } +}; + +struct op_minus_assign +{ + template + constexpr auto operator()(IntSet const& lhs [[maybe_unused]], IntSet const& rhs [[maybe_unused]]) const noexcept + { + if constexpr (tti::has_op_minus_assign_v) { + auto const src = lhs; + auto dst = src; + auto const& ret = (dst -= rhs); + + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { + BOOST_CHECK_EQUAL(contains(dst, i), contains(rhs, i) ? false : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); + } + } +}; + +struct op_shift_left_assign +{ + template + auto operator()(IntSet const& is, SizeType const pos) const + { + auto const src = is; + auto dst = src; + auto const& ret = (dst <<= pos); + + for (auto N = size(is), I = decltype(N){0}; I < N; ++I) { + if (I < pos) { + BOOST_CHECK(!contains(dst, I)); // [bitset.members]/7.1 + } else { // [bitset.members]/7.2 + BOOST_CHECK_EQUAL(contains(dst, I), contains(src, I - pos)); + } + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/8 + } +}; + +struct op_shift_right_assign +{ + template + auto operator()(IntSet const& is, SizeType const pos) const + { + auto const src = is; + auto dst = src; + auto const& ret = (dst >>= pos); + + for (auto N = size(is), I = decltype(N){0}; I < N; ++I) { + if (pos >= N - I) { + BOOST_CHECK(!contains(dst, I)); // [bitset.members]/9.1 + } else { // [bitset.members]/9.2 + BOOST_CHECK_EQUAL(contains(dst, I), contains(src, I + pos)); + } + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/10 + } +}; + +struct fn_set +{ + template + auto operator()(IntSet const& is) const noexcept + { + auto value = is; + auto const& ret = set(value); + + BOOST_CHECK(all(value)); // [bitset.members]/11 + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(value)); // [bitset.members]/12 + BOOST_CHECK_THROW(set(value, size(is)), std::out_of_range); // [bitset.members]/13 + } + + template + auto operator()(IntSet const& is, SizeType const pos) const + { + auto const src = is; + auto dst = src; + auto const& ret = set(dst, pos); + + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/14 + BOOST_CHECK_EQUAL(contains(dst, i), i == pos ? true : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/15 + } + + template + auto operator()(IntSet const& is, SizeType const pos, bool const val) const + { + auto const src = is; + auto dst = src; + auto const& ret = set(dst, pos, val); + + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/14 + BOOST_CHECK_EQUAL(contains(dst, i), i == pos ? val : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/15 + } +}; + +struct mem_insert +{ + template + auto operator()(IntSet const& is [[maybe_unused]], InputIterator first [[maybe_unused]], InputIterator last [[maybe_unused]]) const + { + if constexpr (tti::has_range_insert_v) { + auto const src = is; + auto dst = src; dst.insert(first, last); + std::for_each(first, last, [&](auto&& elem) { + BOOST_CHECK(dst.contains(elem)); + }); + BOOST_CHECK_LE(src.count(), dst.count()); + BOOST_CHECK_LE(dst.count(), src.count() + static_cast(std::distance(first, last))); + } + } + + template + constexpr auto operator()(IntSet const& is [[maybe_unused]], std::initializer_list ilist [[maybe_unused]]) const + { + if constexpr (tti::has_ilist_insert_v) { + auto const src = is; + auto dst = src; dst.insert(ilist); + for (auto&& elem : ilist) { + BOOST_CHECK(dst.contains(elem)); + } + BOOST_CHECK_LE(src.count(), dst.count()); + BOOST_CHECK_LE(dst.count(), src.count() + static_cast(ilist.size())); + } + } +}; + +struct fn_reset +{ + template + auto operator()(IntSet const& is) const noexcept + { + auto value = is; + auto const& ret = reset(value); + + BOOST_CHECK(none(value)); // [bitset.members]/16 + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(value)); // [bitset.members]/17 + BOOST_CHECK_THROW(reset(value, size(is)), std::out_of_range); // [bitset.members]/18 + } + + template + auto operator()(IntSet const& is, SizeType const pos) const + { + auto const src = is; + auto dst = src; + auto const& ret = reset(dst, pos); + + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/19 + BOOST_CHECK_EQUAL(contains(dst, i), i == pos ? false : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/20 + } +}; + +struct mem_erase +{ + template + auto operator()(IntSet const& is [[maybe_unused]], InputIterator first [[maybe_unused]], InputIterator last [[maybe_unused]]) const + { + if constexpr (tti::has_range_erase_v) { + auto const src = is; + auto dst = src; dst.erase(first, last); + std::for_each(first, last, [&](auto&& elem) { + BOOST_CHECK(!dst.contains(elem)); + }); + BOOST_CHECK_LE(dst.count(), src.count()); + BOOST_CHECK_LE(src.count(), dst.count() + static_cast(std::distance(first, last))); + } + } + + template + constexpr auto operator()(IntSet const& is [[maybe_unused]], std::initializer_list ilist [[maybe_unused]]) const + { + if constexpr (tti::has_ilist_erase_v) { + auto const src = is; + auto dst = src; dst.erase(ilist); + for (auto&& elem : ilist) { + BOOST_CHECK(!dst.contains(elem)); + } + BOOST_CHECK_LE(dst.count(), src.count()); + BOOST_CHECK_LE(src.count(), dst.count() + static_cast(ilist.size())); + } + } +}; + +struct fn_swap +{ + template + constexpr auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto src1 = a, src2 = b; + using std::swap; + swap(src1, src2); + BOOST_CHECK(src1 == b && src2 == a); + } +}; + +struct op_compl +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + auto expected = a; flip(expected); + auto const& ret = ~a; + + BOOST_CHECK_NE(std::addressof(ret), std::addressof(expected)); // [bitset.members]/21 + BOOST_CHECK(ret == expected); // [bitset.members]/22 + BOOST_CHECK(~ret == a); // involution + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK(~(a | b) == (~a & ~b)); // De Morgan's Laws + BOOST_CHECK(~(a & b) == (~a | ~b)); // De Morgan's Laws + } +}; + +struct fn_flip +{ + template + auto operator()(IntSet const& is) const noexcept + { + auto const src = is; + auto dst = is; + auto const& ret = flip(dst); + + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { + BOOST_CHECK_NE(contains(dst, i), contains(src, i)); // [bitset.members]/23 + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/24 + BOOST_CHECK_THROW(flip(dst, size(is)), std::out_of_range); // [bitset.members]/25 + + flip(dst); + BOOST_CHECK(dst == src); // involution + } + + template + auto operator()(IntSet const& is, SizeType const pos) const + { + auto const src = is; + auto dst = is; + auto const& ret = flip(dst, pos); + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { // [bitset.members]/26 + BOOST_CHECK_EQUAL(contains(dst, i), i == pos ? !contains(src, i) : contains(src, i)); + } + BOOST_CHECK_EQUAL(std::addressof(ret), std::addressof(dst)); // [bitset.members]/27 + + flip(dst, pos); + BOOST_CHECK(dst == src); // involution + } +}; + +struct mem_count +{ + template + auto operator()(IntSet const& is) noexcept + { + auto expected = decltype(is.count()){0}; + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { + expected += contains(is, i); + } + BOOST_CHECK_EQUAL(is.count(), expected); // [bitset.members]/34 + + if constexpr (tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is.count(), std::distance(is.begin(), is.end())); + } + } +}; + +struct fn_size +{ + template + auto operator()(IntSet const& is) const noexcept + { + BOOST_CHECK_EQUAL(size(is), size(IntSet{})); // [bitset.members]/35 + + if constexpr (tti::has_static_max_size_v) { + BOOST_CHECK_EQUAL(size(is), is.max_size()); + } + } +}; + +template +struct mem_capacity +{ + auto operator()() const noexcept + { + if constexpr (tti::has_static_capacity_v) { + using block_t = typename IntSet::block_type; + constexpr auto block_size = std::numeric_limits::digits; + + BOOST_CHECK_EQUAL(IntSet::capacity() % block_size, 0); + BOOST_CHECK_EQUAL(IntSet::capacity() / block_size, (IntSet::max_size() - 1 + block_size) / block_size); + } + } +}; + +struct op_equal_to +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(a == a); // reflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = true; + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { + expected &= contains(a, i) == contains(b, i); + } + BOOST_CHECK_EQUAL(a == b, expected); // [bitset.members]/36 + BOOST_CHECK_EQUAL(a == b, b == a); // symmetric + + if constexpr (tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(a == b, std::equal(a.begin(), a.end(), b.begin(), b.end())); + } + } +}; + +struct op_not_equal_to +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!(a != a)); // irreflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(a != b, !(a == b)); // [bitset.members]/37 + BOOST_CHECK_EQUAL(a != b, b != a); // symmetric + } +}; + +struct op_less +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!(a < a)); // irreflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = false; + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { + auto const r = N - 1 - i; + if (!contains(a, r) && contains(b, r)) { expected = true; break; } + if (!contains(b, r) && contains(a, r)) { break; } + } + BOOST_CHECK_EQUAL(a < b, expected); + + if constexpr (tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(a < b, std::lexicographical_compare(a.rbegin(), a.rend(), b.rbegin(), b.rend())); + } + } +}; + +struct op_greater +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!(a > a)); // irreflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(a > b, b < a); + } +}; + +struct op_greater_equal +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(a >= a); // reflexive + } + + template + auto operator()(IntSet const& lhs, IntSet const& rhs) const noexcept + { + BOOST_CHECK_EQUAL(lhs >= rhs, !(lhs < rhs)); + } +}; + +struct op_less_equal +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(a <= a); // reflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(a <= b, !(b < a)); + } +}; + +struct fn_is_subset_of +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(is_subset_of(a, a)); // reflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = true; + for (auto N = size(IntSet{}), i = decltype(N){0}; i < N; ++i) { + expected &= !contains(a, i) || contains(b, i); + } + BOOST_CHECK_EQUAL(is_subset_of(a, b), expected); + + if constexpr (tti::has_const_iterator_v) { + BOOST_CHECK_EQUAL(is_subset_of(a, b), std::includes(a.begin(), a.end(), b.begin(), b.end())); + } + } +}; + +struct fn_is_superset_of +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(is_superset_of(a, a)); // reflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(is_superset_of(a, b), is_subset_of(b, a)); + } +}; + +struct fn_is_proper_subset_of +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!is_proper_subset_of(a, a)); // irreflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(is_proper_subset_of(a, b), is_subset_of(a, b) && !is_subset_of(b, a)); + } +}; + +struct fn_is_proper_superset_of +{ + template + auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!is_proper_superset_of(a, a)); // irreflexive + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(is_proper_superset_of(a, b), is_proper_subset_of(b, a)); + } +}; + +struct fn_intersects +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(intersects(a, a) || none(a)); + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(intersects(a, b), any(a & b)); + BOOST_CHECK_EQUAL(intersects(a, b), intersects(b, a)); + } +}; + +struct fn_disjoint +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(!disjoint(a, a) || none(a)); + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + BOOST_CHECK_EQUAL(disjoint(a, b), !intersects(a, b)); + BOOST_CHECK_EQUAL(disjoint(a, b), disjoint(b, a)); + } +}; + +struct fn_test +{ + template + auto operator()(IntSet const& is) const noexcept + { + BOOST_CHECK_THROW(test(is, size(is)), std::out_of_range); // [bitset.members]/38 + } + + template + auto operator()(IntSet const& is, SizeType const pos) const noexcept + { + auto value = is; reset(value); set(value, pos); + for (auto N = size(is), i = decltype(N){0}; i < N; ++i) { + BOOST_CHECK_EQUAL(i == pos, test(value, i)); // [bitset.members]/39 + } + } +}; + +struct fn_all +{ + template + auto operator()(IntSet const& is) const noexcept + { + BOOST_CHECK_EQUAL(all(is), is.count() == size(is)); // [bitset.members]/40 + + if constexpr (tti::has_full_v) { + BOOST_CHECK_EQUAL(all(is), is.full()); + } + } +}; + +struct fn_any +{ + template + auto operator()(IntSet const& is) const noexcept + { + BOOST_CHECK_EQUAL(any(is), is.count() != 0); // [bitset.members]/41 + + if constexpr (tti::has_empty_v) { + BOOST_CHECK_EQUAL(any(is), !is.empty()); + } + } +}; + +struct fn_none +{ + template + auto operator()(IntSet const& is) const noexcept + { + BOOST_CHECK_EQUAL(none(is), is.count() == 0); // [bitset.members]/42 + + if constexpr (tti::has_empty_v) { + BOOST_CHECK_EQUAL(none(is), is.empty()); + BOOST_CHECK_EQUAL(none(is), empty(is)); + } + } +}; + +struct op_shift_left +{ + template + auto operator()(IntSet const& is, SizeType const n) const + { + auto expected = is; expected <<= n; + BOOST_CHECK(is << n == expected); // [bitset.members]/43 + } +}; + +struct op_shift_right +{ + template + auto operator()(IntSet const& is, SizeType const n) const + { + auto expected = is; expected >>= n; + BOOST_CHECK(is >> n == expected); // [bitset.members]/44 + } +}; + +struct op_at +{ + template + auto operator()(IntSet const& is, SizeType const pos) const + { + BOOST_CHECK(0 <= pos && pos < size(is)); // [bitset.members]/45 + BOOST_CHECK_EQUAL(contains(is, pos), test(is, pos)); // [bitset.members]/46 + } +}; + +struct op_bitand +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK((a & a) == a); // idempotent + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = a; expected &= b; + BOOST_CHECK((a & b) == expected); // [bitset.operators]/1 + BOOST_CHECK((a & b) == (b & a)); // commutative + BOOST_CHECK(is_subset_of(a & b, a)); + BOOST_CHECK(is_subset_of(a & b, b)); + + if constexpr (tti::has_const_iterator_v) { + auto const lhs = a & b; + IntSet rhs; + std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::inserter(rhs, rhs.begin())); + BOOST_CHECK(lhs == rhs); + } + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b, IntSet const& c) const noexcept + { + BOOST_CHECK(((a & b) & c) == (a & (b & c))); // associative + BOOST_CHECK((a & (b | c)) == ((a & b) | (a & c))); // distributive + } +}; + +struct op_bitor +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK((a | a) == a); // idempotent + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = a; expected |= b; + BOOST_CHECK((a | b) == expected); // [bitset.operators]/2 + BOOST_CHECK((a | b) == (b | a)); // commutative + BOOST_CHECK(is_subset_of(a, a | b)); + BOOST_CHECK(is_subset_of(b, a | b)); + + if constexpr (tti::has_const_iterator_v) { + auto const lhs = a | b; + IntSet rhs; + std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::inserter(rhs, rhs.begin())); + BOOST_CHECK(lhs == rhs); + } + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b, IntSet const& c) const noexcept + { + BOOST_CHECK(((a | b) | c) == (a | (b | c))); // associative + BOOST_CHECK((a | (b & c)) == ((a | b) & (a | c))); // distributive + } +}; + +struct op_xor +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(none(a ^ a)); // nilpotent + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + auto expected = a; expected ^= b; + BOOST_CHECK((a ^ b) == expected); // [bitset.operators]/3 + BOOST_CHECK((a ^ b) == (b ^ a)); // commutative + BOOST_CHECK((a ^ b) == ((a - b) | (b - a))); + BOOST_CHECK((a ^ b) == (a | b) - (a & b)); + + if constexpr (tti::has_const_iterator_v) { + auto const lhs = a ^ b; + IntSet rhs; + std::set_symmetric_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(rhs, rhs.begin())); + BOOST_CHECK(lhs == rhs); + } + } + + template + constexpr auto operator()(IntSet const& a, IntSet const& b, IntSet const& c) const noexcept + { + BOOST_CHECK(((a ^ b) ^ c) == (a ^ (b ^ c))); // associative + BOOST_CHECK((a & (b ^ c)) == ((a & b) ^ (a & c))); // distributive + } +}; + +struct op_minus +{ + template + constexpr auto operator()(IntSet const& a) const noexcept + { + BOOST_CHECK(none(a - a)); // nilpotent + } + + template + auto operator()(IntSet const& a, IntSet const& b) const noexcept + { + if constexpr (tti::has_op_minus_assign_v) { + auto expected = a; expected -= b; + BOOST_CHECK(a - b == expected); + } + + BOOST_CHECK(a - b == (a & ~b)); + BOOST_CHECK(a - b == (a | b) - b); + BOOST_CHECK(a - b == a - (a & b)); + BOOST_CHECK(is_subset_of(a - b, a)); + BOOST_CHECK(disjoint(a - b, b)); + + if constexpr (tti::has_const_iterator_v) { + auto const lhs = a - b; + IntSet rhs; + std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(rhs, rhs.begin())); + BOOST_CHECK(lhs == rhs); + } + } +}; + +} // namespace xstd diff --git a/test/include/traits.hpp b/test/include/traits.hpp new file mode 100644 index 0000000..19e79f5 --- /dev/null +++ b/test/include/traits.hpp @@ -0,0 +1,176 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // initializer_list +#include // void_t +#include // declval + +namespace xstd { +namespace tti { + +template +constexpr static auto has_op_minus_assign_v = false; + +template +constexpr static auto has_op_minus_assign_v() -= std::declval() +)>> = true; + +template +constexpr static auto has_const_iterator_v = false; + +template +constexpr static auto has_const_iterator_v> = true; + +template +constexpr static auto has_front_v = false; + +template +constexpr static auto has_front_v().front() +)>> = true; + +template +constexpr static auto has_back_v = false; + +template +constexpr static auto has_back_v().back() +)>> = true; + +template +constexpr static auto has_any_of_v = false; + +template +constexpr static auto has_any_of_v().any_of(std::declval()) +)>> = true; + +template +constexpr static auto has_none_of_v = false; + +template +constexpr static auto has_none_of_v().none_of(std::declval()) +)>> = true; + +template +constexpr static auto has_all_of_v = false; + +template +constexpr static auto has_all_of_v().all_of(std::declval()) +)>> = true; + +template +constexpr static auto has_accumulate_v = false; + +template +constexpr static auto has_accumulate_v().accumulate(std::declval(), std::declval()) +)>> = true; + +template +constexpr static auto has_for_each_v = false; + +template +constexpr static auto has_for_each_v().for_each(std::declval()) +)>> = true; + +template +constexpr static auto has_reverse_for_each_v = false; + +template +constexpr static auto has_reverse_for_each_v().reverse_for_each(std::declval()) +)>> = true; + +template +constexpr static auto has_static_max_size_v = false; + +template +constexpr static auto has_static_max_size_v> = true; + +template +constexpr static auto has_static_capacity_v = false; + +template +constexpr static auto has_static_capacity_v> = true; + +template +constexpr static auto has_full_v = false; + +template +constexpr static auto has_full_v().full() +)>> = true; + +template +constexpr static auto has_empty_v = false; + +template +constexpr static auto has_empty_v().empty() +)>> = true; + +template +constexpr static auto has_range_assign_v = false; + +template +constexpr static auto has_range_assign_v().assign(std::declval(), std::declval()) +)>> = true; + +template +constexpr static auto has_ilist_assign_v = false; + +template +constexpr static auto has_ilist_assign_v() = std::declval>() +)>> = true; + +template +constexpr static auto has_range_insert_v = false; + +template +constexpr static auto has_range_insert_v().insert(std::declval(), std::declval()) +)>> = true; + +template +constexpr static auto has_ilist_insert_v = false; + +template +constexpr static auto has_ilist_insert_v().insert(std::declval>()) +)>> = true; + +template +constexpr static auto has_range_erase_v = false; + +template +constexpr static auto has_range_erase_v().erase(std::declval(), std::declval()) +)>> = true; + +template +constexpr static auto has_ilist_erase_v = false; + +template +constexpr static auto has_ilist_erase_v().erase(std::declval>()) +)>> = true; + +} // namespace tti +} // namespace xstd diff --git a/test/src/builtin.cpp b/test/src/builtin.cpp new file mode 100644 index 0000000..dad61fa --- /dev/null +++ b/test/src/builtin.cpp @@ -0,0 +1,158 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // ctznz, clznz, popcount, bsfnz, bsrnz, ctz, clz, bsf, bsr, bit1 +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_CHECK, BOOST_CHECK_EQUAL, BOOST_AUTO_TEST_SUITE_END +#include // digits + +BOOST_AUTO_TEST_SUITE(Builtin) + +#if defined(__GNUG__) + +using UnsignedIntegerTypes = boost::mpl::vector +< uint32_t +, uint64_t +, __uint128_t +>; + +#elif defined(_MSC_VER) + +#if defined(WIN64) + +using UnsignedIntegerTypes = boost::mpl::vector +< uint32_t +, uint64_t +>; + +#else + +using UnsignedIntegerTypes = boost::mpl::vector +< uint32_t +>; + +#endif + +#endif + +using namespace xstd; + +template constexpr auto zero = static_cast(0); +template constexpr auto ones = ~zero; + +template +constexpr auto bit1(int const n) +{ + return static_cast(1) << n; +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(CountTrailingZerosNonZero, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::ctznz(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::ctznz(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(CountLeadingZerosNonZero, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::clznz(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::clznz(b), std::numeric_limits::digits - 1 - i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(Popcount, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::popcount(zero), 0); + BOOST_CHECK_EQUAL(detail::popcount(ones), std::numeric_limits::digits); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = ~(ones << i); + BOOST_CHECK_EQUAL(detail::popcount(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(BitScanForwardNonZero, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::bsfnz(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::bsfnz(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(BitScanReverseNonZero, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::bsrnz(ones), std::numeric_limits::digits - 1); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::bsrnz(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(CountTrailingZeros, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::ctz(zero), std::numeric_limits::digits); + BOOST_CHECK_EQUAL(detail::ctz(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::ctz(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(CountLeadingZeros, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::clz(zero), std::numeric_limits::digits); + BOOST_CHECK_EQUAL(detail::clz(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::clz(b), std::numeric_limits::digits - 1 - i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(BitScanForward, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::bsf(zero), std::numeric_limits::digits); + BOOST_CHECK_EQUAL(detail::bsf(ones), 0); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::bsf(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(BitScanReverse, T, UnsignedIntegerTypes) +{ + BOOST_CHECK_EQUAL(detail::bsr(zero), -1); + BOOST_CHECK_EQUAL(detail::bsr(ones), std::numeric_limits::digits - 1); + + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::bsr(b), i); + } +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(Bit1, T, UnsignedIntegerTypes) +{ + for (auto i = 0; i < std::numeric_limits::digits; ++i) { + auto const b = bit1(i); + BOOST_CHECK_EQUAL(detail::popcount(b), 1); + + // __uint128_t does not have output streaming operator<< required for BOOST_CHECK_EQUAL + BOOST_CHECK(b == static_cast(1) << i); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/test0.cpp b/test/src/test0.cpp new file mode 100644 index 0000000..d5530b6 --- /dev/null +++ b/test/src/test0.cpp @@ -0,0 +1,49 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // bitset +#include // int_set +#include // bitset +#include // int_set +#include // constructor, mem_capacity, fn_test +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END + +BOOST_AUTO_TEST_SUITE(Constant) + +using namespace xstd; + +using SetTypes = boost::mpl::vector +< std::bitset< 0> +, std::bitset< 32> +, std::bitset< 64> +, std::bitset< 96> +, std::bitset<128> +, int_set< 0, uint32_t> +, int_set< 1, uint32_t> +, int_set< 2, uint32_t> +, int_set< 32, uint32_t> +, int_set< 33, uint32_t> +, int_set< 34, uint32_t> +, int_set< 64, uint32_t> +, int_set< 65, uint32_t> +, int_set< 66, uint32_t> +, int_set< 96, uint32_t> +, int_set<128> +#if defined(__GNUG__) +, int_set<128, __uint128_t> +, int_set<256, __uint128_t> +#endif +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(Exhaustive, T, SetTypes) +{ + constructor{}(); + mem_capacity{}(); + fn_test{}(T{}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/test1.cpp b/test/src/test1.cpp new file mode 100644 index 0000000..056b946 --- /dev/null +++ b/test/src/test1.cpp @@ -0,0 +1,163 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // bitset +#include // int_set +#include // bitset +#include // int_set +#include // all_values, all_cardinality_sets, all_singleton_arrays, all_singleton_ilists, all_singleton_sets +#include // constructor, mem_assign, const_reference, const_iterator, mem_front, mem_back, + // mem_accumulate, mem_for_each, mem_reverse_for_each, fn_set, mem_insert, fn_reset, mem_erase, + // op_compl, fn_flip, mem_count, fn_size, op_equal_to, op_less, fn_intersects, fn_is_subset_of, + // op_not_equal_to, op_greater, op_greater_equal, op_less_equal, + // fn_disjoint, fn_is_superset_of, fn_is_proper_subset_of, fn_is_proper_superset_of, + // fn_test, fn_all, fn_any, fn_none, op_at, op_bitand, op_bitor, op_xor, op_minus +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END + +BOOST_AUTO_TEST_SUITE(Linear) + +using namespace xstd; + +using SetTypes = boost::mpl::vector +< std::bitset< 0> +, std::bitset< 32> +, std::bitset< 64> +, std::bitset< 96> +, std::bitset<128> +, int_set< 0, uint32_t> +, int_set< 1, uint32_t> +, int_set< 2, uint32_t> +, int_set< 32, uint32_t> +, int_set< 33, uint32_t> +, int_set< 34, uint32_t> +, int_set< 64, uint32_t> +, int_set< 65, uint32_t> +, int_set< 66, uint32_t> +, int_set< 96, uint32_t> +, int_set<128> +#if defined(__GNUG__) +, int_set<128, __uint128_t> +, int_set<256, __uint128_t> +#endif +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(Exhaustive, T, SetTypes) +{ + all_singleton_arrays([](auto const& a1) { + constructor{}(a1.begin(), a1.end()); + }); + all_singleton_ilists([](auto ilist1) { + constructor{}(ilist1); + }); + + all_singleton_arrays([](auto const& a1) { + mem_assign{}(~T{}, a1.begin(), a1.end()); + }); + all_singleton_ilists([](auto ilist1) { + mem_assign{}(~T{}, ilist1); + }); + + all_cardinality_sets(const_reference{}); + all_singleton_sets(const_reference{}); + + all_cardinality_sets(const_iterator{}); + all_singleton_sets(const_iterator{}); + + all_cardinality_sets(mem_front{}); + all_singleton_sets(mem_front{}); + + all_cardinality_sets(mem_back{}); + all_singleton_sets(mem_back{}); + + all_cardinality_sets([](auto const& in) { + mem_accumulate{}(in, 0); + }); + all_singleton_sets([](auto const& i1) { + mem_accumulate{}(i1, 0); + }); + + all_cardinality_sets(mem_for_each{}); + all_singleton_sets(mem_for_each{}); + + all_cardinality_sets(mem_reverse_for_each{}); + all_singleton_sets(mem_reverse_for_each{}); + + all_singleton_sets(fn_set{}); + all_values([](auto const pos) { + fn_set{}(T{}, pos); + fn_set{}(T{}, pos, true); + fn_set{}(T{}, pos, false); + }); + + all_singleton_arrays([](auto const& a1) { + mem_insert{}(T{}, a1.begin(), a1.end()); + }); + all_singleton_ilists([](auto ilist1) { + mem_insert{}(T{}, ilist1); + }); + + all_singleton_sets(fn_reset{}); + all_values([](auto const pos) { + fn_reset{}(~T{}, pos); + }); + + all_singleton_arrays([](auto const& a1) { + mem_erase{}(~T{}, a1.begin(), a1.end()); + }); + all_singleton_ilists([](auto ilist1) { + mem_erase{}(T{}, ilist1); + }); + + all_singleton_sets(op_compl{}); + all_singleton_sets(fn_flip{}); + all_values([](auto const pos) { + fn_flip{}(T{}, pos); + }); + + all_cardinality_sets(mem_count{}); + all_singleton_sets(fn_size{}); + + all_cardinality_sets(op_equal_to{}); + all_cardinality_sets(op_less{}); + all_cardinality_sets(fn_intersects{}); + all_cardinality_sets(fn_is_subset_of{}); + + all_singleton_sets(op_equal_to{}); + all_singleton_sets(op_not_equal_to{}); + + all_singleton_sets(op_less{}); + all_singleton_sets(op_greater{}); + all_singleton_sets(op_greater_equal{}); + all_singleton_sets(op_less_equal{}); + + all_singleton_sets(fn_intersects{}); + all_singleton_sets(fn_disjoint{}); + + all_singleton_sets(fn_is_subset_of{}); + all_singleton_sets(fn_is_superset_of{}); + all_singleton_sets(fn_is_proper_subset_of{}); + all_singleton_sets(fn_is_proper_superset_of{}); + + all_values([](auto const pos) { + fn_test{}(T{}, pos); + }); + + all_cardinality_sets(fn_all{}); + all_cardinality_sets(fn_any{}); + all_cardinality_sets(fn_none{}); + + all_values([](auto const pos) { + op_at{}(T{}, pos); + }); + + all_singleton_sets(op_bitand{}); + all_singleton_sets(op_bitor{}); + all_singleton_sets(op_xor{}); + all_singleton_sets(op_minus{}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/test2.cpp b/test/src/test2.cpp new file mode 100644 index 0000000..21b87fc --- /dev/null +++ b/test/src/test2.cpp @@ -0,0 +1,151 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // bitset +#include // int_set +#include // bitset +#include // int_set +#include // all_values, all_singleton_sets, all_singleton_set_pairs, + // all_doubleton_arrays, all_doubleton_ilists, all_doubleton_sets, +#include // constructor, mem_assign, const_reference, const_iterator, mem_all_of, mem_any_of, me_none_of, + // mem_for_each, mem_reverse_for_each, op_bitand_assign, op_bitor_assign, op_xor_assign, op_minus_assign, + // op_shift_left_assign, op_shift_right_assign, mem_insert, mem_erase, fn_swap, op_compl, + // op_equal_to, op_not_equal_to, op_less, op_greater, op_greater_equal, op_less_equal, + // fn_is_subset_of, fn_is_superset_of, fn_is_proper_subset_of, fn_is_proper_superset_of, + // fn_intersect, fn_disjoint, op_shift_left, op_shift_right, op_bitand, op_bitor, op_xor, op_minus +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END + +BOOST_AUTO_TEST_SUITE(Quadratic) + +using namespace xstd; + +using SetTypes = boost::mpl::vector +< std::bitset< 0> +, std::bitset< 32> +, std::bitset< 64> +, int_set< 0, uint32_t> +, int_set< 1, uint32_t> +, int_set< 33, uint32_t> +, int_set< 65, uint32_t> +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(Exhaustive, T, SetTypes) +{ + all_doubleton_arrays([](auto const& a2) { + constructor{}(a2.begin(), a2.end()); + }); + all_doubleton_ilists([](auto ilist2) { + constructor{}(ilist2); + }); + + all_doubleton_arrays([](auto const& a2) { + mem_assign{}(~T{}, a2.begin(), a2.end()); + }); + all_doubleton_ilists([](auto ilist2) { + mem_assign{}(~T{}, ilist2); + }); + + all_doubleton_sets(const_reference{}); + all_doubleton_sets(const_iterator{}); + + all_values([](auto const pos) { + all_singleton_sets([&](auto const& i1) { + mem_any_of{}(i1, [&](auto const elem) { + return elem == pos; + }); + }); + }); + + all_values([](auto const pos) { + all_singleton_sets([&](auto const& i1) { + mem_none_of{}(i1, [&](auto const elem) { + return elem == pos; + }); + }); + }); + + all_values([](auto const pos) { + all_singleton_sets([&](auto const& i1) { + mem_all_of{}(i1, [&](auto const elem) { + return elem == pos; + }); + }); + }); + + all_doubleton_sets(mem_for_each{}); + all_doubleton_sets(mem_reverse_for_each{}); + + all_singleton_set_pairs(op_bitand_assign{}); + all_singleton_set_pairs(op_bitor_assign{}); + all_singleton_set_pairs(op_xor_assign{}); + all_singleton_set_pairs(op_minus_assign{}); + + all_values([](auto const pos) { + all_singleton_sets([&](auto const& i1){ + op_shift_left_assign{}(i1, pos); + }); + }); + + all_values([](auto const pos) { + all_singleton_sets([&](auto const& i1){ + op_shift_right_assign{}(i1, pos); + }); + }); + + all_doubleton_arrays([](auto const& a2) { + mem_insert{}(T{}, a2.begin(), a2.end()); + }); + all_doubleton_ilists([](auto ilist2) { + mem_insert{}(T{}, ilist2); + }); + + all_doubleton_arrays([](auto const& a2) { + mem_erase{}(~T{}, a2.begin(), a2.end()); + }); + all_doubleton_ilists([](auto ilist2) { + mem_erase{}(T{}, ilist2); + }); + + all_singleton_set_pairs(fn_swap{}); + + all_singleton_set_pairs(op_compl{}); + + all_singleton_set_pairs(op_equal_to{}); + all_singleton_set_pairs(op_not_equal_to{}); + + all_singleton_set_pairs(op_less{}); + all_singleton_set_pairs(op_greater{}); + all_singleton_set_pairs(op_greater_equal{}); + all_singleton_set_pairs(op_less_equal{}); + + all_singleton_set_pairs(fn_is_subset_of{}); + all_singleton_set_pairs(fn_is_superset_of{}); + all_singleton_set_pairs(fn_is_proper_subset_of{}); + all_singleton_set_pairs(fn_is_proper_superset_of{}); + + all_singleton_set_pairs(fn_intersects{}); + all_singleton_set_pairs(fn_disjoint{}); + + all_values([](auto const pos){ + all_singleton_sets([&](auto const& i1){ + op_shift_left{}(i1, pos); + }); + }); + + all_values([](auto const pos){ + all_singleton_sets([&](auto const& i1){ + op_shift_right{}(i1, pos); + }); + }); + + all_singleton_set_pairs(op_bitand{}); + all_singleton_set_pairs(op_bitor{}); + all_singleton_set_pairs(op_xor{}); + all_singleton_set_pairs(op_minus{}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/test3.cpp b/test/src/test3.cpp new file mode 100644 index 0000000..c8f3f41 --- /dev/null +++ b/test/src/test3.cpp @@ -0,0 +1,36 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // bitset +#include // int_set +#include // bitset +#include // int_set +#include // all_singleton_set_triples +#include // op_bitand, op_bitor, op_xor +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END + +BOOST_AUTO_TEST_SUITE(Cubic) + +using namespace xstd; + +using SetTypes = boost::mpl::vector +< std::bitset< 0> +, std::bitset<32> +, std::bitset<64> +, int_set< 0, uint32_t> +, int_set<32, uint32_t> +, int_set<64, uint32_t> +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(ExhaustivelyTestAssociativityAndDistributivity, T, SetTypes) +{ + all_singleton_set_triples(op_bitand{}); + all_singleton_set_triples(op_bitor{}); + all_singleton_set_triples(op_xor{}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/type_traits.cpp b/test/src/type_traits.cpp new file mode 100644 index 0000000..b5172af --- /dev/null +++ b/test/src/type_traits.cpp @@ -0,0 +1,43 @@ +// Copyright Rein Halbersma 2014-2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include // int_set +#include // vector +#include // BOOST_AUTO_TEST_CASE_TEMPLATE +#include // BOOST_AUTO_TEST_SUITE, BOOST_CHECK, BOOST_CHECK_EQUAL, BOOST_AUTO_TEST_SUITE_END +#include // bitset +#include // is_nothrow_default_constructible_v, is_trivially_copyable, is_standard_layout + +BOOST_AUTO_TEST_SUITE(TypeTraits) + +using SetTypes = boost::mpl::vector +< std::bitset< 0> +, std::bitset< 32> +, std::bitset< 64> +, std::bitset<128> +, std::bitset<256> +, xstd::int_set< 0> +, xstd::int_set< 32> +, xstd::int_set< 64> +, xstd::int_set<128> +, xstd::int_set<256> +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(IsNothrowDefaultConstructible, T, SetTypes) +{ + static_assert(std::is_nothrow_default_constructible_v); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(IsTriviallyCopyable, T, SetTypes) +{ + static_assert(std::is_trivially_copyable_v); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(IsStandardLayout, T, SetTypes) +{ + static_assert(std::is_standard_layout_v); +} + +BOOST_AUTO_TEST_SUITE_END()