diff --git a/README.md b/README.md index 172b5ac..1263084 100644 --- a/README.md +++ b/README.md @@ -55,39 +55,142 @@ Notes: This library provides one of the four outlined quadrants: `xstd::bit_set` as a **fixed-size ordered set of `int`**. The other three quadrants are not implemented. -### Hello World +### Hello World: generating (twin) primes -The code below demonstrates how `xstd::bit_set` implements the [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) algorithm to generate all prime numbers below a compile time number `N`. +The code below demonstrates how a standards conforming `set` implementation can be used to implement the [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes). This algorithm generates all [prime numbers](https://en.wikipedia.org/wiki/Prime_number) below a number `n`. Note that the implementation below requires seamless interaction with the C++20 (and beyond) `` library and does not use manual iterator manipulation or index arithmetic. ```cpp -#include -#include +template +auto sift(C& primes, int m) +{ + primes.erase(m); +} -constexpr auto N = 100; -using set_type = xstd::bit_set; +template +struct generate_candidates +{ + auto operator()(int n) const + { + return std::views::iota(2, n) | std::ranges::to(); + } +}; -int main() +template +auto sift_primes(int n) { - // initialize all numbers from [2, N) - auto primes = std::views::iota(2, N) | std::ranges::to(); - - // find all primes below N - // iterate over candidate primes and remove their multiples - for (auto p : primes | std::views::take_while([](auto x) { return x * x < N; })) { - for (auto m : std::views::iota(p * p, N) | std::views::stride(p)) { - primes.erase(m); + auto primes = generate_candidates()(n); + for (auto p + : primes + | std::views::take_while([&](auto x) { return x * x < n; }) + ) { + for (auto m + : std::views::iota(p * p, n) + | std::views::stride(p) + ) { + sift(primes, m); } } + return primes; +} +``` + +Given a set of primes, generating the [twin primes](https://en.wikipedia.org/wiki/Twin_prime) can be done by iterating over pairs of adjacent primes, filtering on their difference being exactly 2, selecting the first element of each pair, and converting that to a new set: + +```cpp +template +auto filter_twins(C const& primes) +{ + return primes + | std::views::pairwise + | std::views::filter([](auto&& x) { auto&& [ first, second ] = x; return first + 2 == second; }) + | std::views::elements<0> + | std::ranges::to() + ; +} +``` + +The calling code for these algorithms is listed below. Here, the pretty-printing as a `set` using `{}` delimiters is triggered by the nested `key_type`. Note that `xstd::bit_set` acts as a **drop-in replacement** for `std::set` (or `boost::container::flat_set`). - // find all twin primes below N - // iterate over adjacent primes, filter if their difference is 2 and select the first - auto const twins = primes | std::views::pairwise | std::views::filter([](auto x) { - return std::get<0>(x) + 2 == std::get<1>(x); - }) | std::views::elements<0> | std::ranges::to(); +[![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/7bfjz15bP) +```cpp +int main() +{ + constexpr auto N = 100; + using C = xstd::bit_set; /* or std::set or boost::container::flat_set */ + + auto primes = sift_primes(N); + assert(fmt::format("{}", primes) == "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); - // pretty-print solution - fmt::println("{}", primes); - fmt::println("{}", twins); + auto twins = filter_twins(primes); + assert(fmt::format("{}", twins) == "{3, 5, 11, 17, 29, 41, 59, 71}"); +} +``` + +## Requirements for `set`-like behaviour + +Looking at the above code, the following four ingredients are necessary to implement the Sieve of Eratosthenes: + +1. **Bidirectional iterators** `begin` and `end` in the `set`'s own namespace (to work with range-`for` and the `` library); +2. **Constructors** taking a pair of iterators or a range (in order for `std::ranges::to` to construct a `set`); +3. A **nested type** `key_type` (in order for the `{fmt}` library to use `{}` delimiters); +4. A **member function** `erase` to remove elements (for other applications: the rest of a `set`'s interface). + +`xstd::bit_set` implements all four of the above requirements. Note that Visual C++ support is finicky at the moment because its `` implementation cannot (yet) handle the `xstd::bit_set` proxy iterators and proxy references correctly. + +## Retrofitting `set`-like behaviour onto `std::bitset` and `boost::dynamic_bitset<>` + +Can we use Without access to their implementations, it's impossible to add nested types and member functions + +## Data-parallelism + +```cpp +template +struct generate_empty +{ + auto operator()(auto) const + { + return C(); + } +}; + +template +auto fill(xstd::bit_set& empty) +{ + empty.fill(); +} + +template +struct generate_candidates +{ + auto operator()(auto n) const + { + auto candidates = generate_empty()(n); + fill(candidates); + sift(candidates, 0); + sift(candidates, 1); + return candidates; + } +}; + +template +auto filter_twins(C const& primes) +{ + return primes & primes >> 2; +} +``` + +[![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/7GWced7Wa) +```cpp +int main() +{ + constexpr auto N = 100; + using C = xstd::bit_set; /* or std::bitset or boost::dynamic_bitset<> */ + + auto const primes = sift_primes(N); + assert(fmt::format("{}", primes | xstd::views::as_set) == "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); + + auto const twins = filter_twins(primes); + assert(fmt::format("{}", twins | xstd::views::as_set) == "{3, 5, 11, 17, 29, 41, 59, 71}"); } ``` @@ -107,31 +210,6 @@ How would the Sieve of Eratosthenes code look when using a sequence of bits? The | `std::bitset` | [![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/zGjofjToj) | | `boost::dynamic_bitset<>` | [![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/PeKoK8zvd) | -### Ordered set of integers - -How would the Sieve of Eratosthenes code look when using an ordered set of integers? The links in the table below provide the full code examples for `std::set` and `boost::container::flat_set`. By design, `xstd::bit_set` is an almost **drop-in replacement** for either of these `set` implementations. - -| Library | Try it online | -| :------ | :------------ | -| `std::set` | [![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/oEchb17d4) | -| `boost::container::flat_set` | [![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/e8easrhKa) | -| `xstd::bit_set` | [![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/YPGEfKzqh) | - -The essential difference is that `std::set` and `boost::container::flat_set` lack the bitwise operators `&` and `>>` to efficiently find twin primes. Instead, one has to iterate over the ordered set of primes using `std::adjacent_find` and write these one-by-one into a new `set`. This style of programming is also supported by `xstd::bit_set` and its proxy iterators seamlessly interact with the `std::adjacent_find` algorithm. - -```cpp -// find all twin primes below N -set_type twins; -for (auto it = primes.begin(); it != primes.end() && std::next(it) != primes.end();) { - it = std::adjacent_find(it, primes.end(), [](auto p, auto q) { - return q - p == 2; - }); - if (it != primes.end()) { - twins.insert(*it++); - } -} -``` - ## Documentation The interface for the class template `xstd::bit_set` is the coherent union of the following building blocks: @@ -211,7 +289,7 @@ The bitwise-shift operators (`<<=`, `>>=`, `<<`, `>>`) from `std::bitset` and `b With the exception of `operator~`, the non-member bitwise operators can be reimagined as **composable** and **data-parallel** versions of the set algorithms on sorted ranges. In C++23, the set algorithms are not (yet) composable, but the [range-v3](https://ericniebler.github.io/range-v3/) library contains lazy views for them. -[![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/1PozoWY5f) +[![Try it online](https://img.shields.io/badge/try%20it-online-brightgreen.svg)](https://godbolt.org/z/G1caEeMPb) | `xstd::bit_set` | `std::set` with the range-v3 set algorithm views | | :---------------- | :----------------------------------------------------------------------------------------------------| @@ -240,7 +318,7 @@ The bitwise shift operators of `xstd::bit_set` can be reimagined as set **tra
 auto b = a
     | std::views::transform([=](auto x) { return x + n; })
-    | std::views::filter([](auto x) { return x < N;  })
+    | std::views::filter   ([=](auto x) { return x < N; })
     | std::ranges::to<std::set>()
 ;
@@ -252,8 +330,8 @@ auto b = a
 auto b = a
-    | std::views::transform([=](auto x) { return x - n; })
-    | std::views::filter([](auto x) { return 0 <= x;  })
+    | std::views::transform([=](auto x) { return x - n;  })
+    | std::views::filter   ([=](auto x) { return 0 <= x; })
     | std::ranges::to<std::set>()
 ;
diff --git a/include/ext/std/bitset.hpp b/include/ext/std/bitset.hpp index 758bab8..dc78c64 100644 --- a/include/ext/std/bitset.hpp +++ b/include/ext/std/bitset.hpp @@ -337,6 +337,6 @@ template return std::ranges::rend(bs); } -} // namespace xstd +} // namespace std #endif // include guard diff --git a/include/xstd/bit_set.hpp b/include/xstd/bit_set.hpp index d61c36e..b477d23 100644 --- a/include/xstd/bit_set.hpp +++ b/include/xstd/bit_set.hpp @@ -27,7 +27,7 @@ namespace xstd { -template +template requires (N >= 0) class bit_set; @@ -158,6 +158,7 @@ class bit_set using value_type = bit_set::value_type; rimpl_type m_ref; value_type m_val; + public: reference() = delete; reference& operator=(reference const&) = delete; diff --git a/include/ext/xstd/bitset.hpp b/include/xstd/bitset.hpp similarity index 65% rename from include/ext/xstd/bitset.hpp rename to include/xstd/bitset.hpp index bbd7297..ebc72e3 100644 --- a/include/ext/xstd/bitset.hpp +++ b/include/xstd/bitset.hpp @@ -1,4 +1,5 @@ -#pragma once +#ifndef XSTD_BITSET_HPP +#define XSTD_BITSET_HPP // Copyright Rein Halbersma 2014-2024. // Distributed under the Boost Software License, Version 1.0. @@ -37,15 +38,126 @@ class bitset { bit_set m_impl; - constexpr explicit bitset(bit_set const& bs) noexcept + [[nodiscard]] constexpr explicit bitset(bit_set const& bs) noexcept : m_impl(bs) {} public: - bitset() = default; + using key_type = std::size_t; + using key_compare = std::less; + using value_type = key_type; + using value_compare = key_compare; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using block_type = Block; + + class reference; + + class iterator + { + using iter_type = bit_set::iterator; + iter_type m_it; + + public: + using iterator_category = iter_type::iterator_category; + using value_type = bitset::value_type; + using difference_type = bitset::difference_type; + using pointer = void; + using reference = bitset::reference; + + [[nodiscard]] constexpr iterator() noexcept = default; + + [[nodiscard]] constexpr explicit iterator(iter_type const& it) noexcept + : + m_it(it) + {} + + [[nodiscard]] constexpr auto operator==(iterator const& other) const noexcept -> bool = default; + + [[nodiscard]] constexpr auto operator*() const noexcept + { + return reference(*m_it); + } + + constexpr auto& operator++() noexcept + { + ++m_it; + return *this; + } + + constexpr auto operator++(int) noexcept + { + auto nrv = *this; ++*this; return nrv; + } + + constexpr auto& operator--() noexcept + { + --m_it; + return *this; + } + + constexpr auto operator--(int) noexcept + { + auto nrv = *this; --*this; return nrv; + } + }; + + class reference + { + using ref_type = bit_set::reference; + ref_type m_ref; + + public: + reference() = delete; + reference& operator=(reference const&) = delete; + reference& operator=(reference &&) = delete; + + constexpr ~reference() noexcept = default; + [[nodiscard]] constexpr reference(reference const&) noexcept = default; + [[nodiscard]] constexpr reference(reference &&) noexcept = default; + + [[nodiscard]] constexpr explicit reference(ref_type const& ref) noexcept + : + m_ref(ref) + {} + + [[nodiscard]] constexpr auto operator==(reference const& other) const noexcept -> bool = default; + + [[nodiscard]] constexpr auto operator&() const noexcept + { + return iterator(&m_ref); + } + + [[nodiscard]] constexpr explicit(false) operator value_type() const noexcept + { + return static_cast(m_ref); + } + + template + [[nodiscard]] constexpr explicit(false) operator T() const noexcept(noexcept(T(m_ref))) + requires std::is_class_v && std::constructible_from + { + return static_cast(m_ref); + } + }; + + [[nodiscard]] friend constexpr auto format_as(reference const& ref) noexcept + -> std::iter_value_t + { + return ref; + } + + using pointer = iterator; + using const_pointer = iterator; + using const_reference = reference; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + [[nodiscard]] constexpr bitset() noexcept = default; template - constexpr explicit bitset( + [[nodiscard]] constexpr explicit bitset( std::basic_string const& str, std::basic_string::size_type pos = 0, std::basic_string::size_type n = std::basic_string::npos, @@ -72,7 +184,7 @@ class bitset } template - constexpr explicit bitset( + [[nodiscard]] constexpr explicit bitset( CharT const* str, std::basic_string::size_type n = std::basic_string::npos, CharT zero = CharT('0'), @@ -273,6 +385,21 @@ class bitset friend constexpr auto operator|<>(bitset const&, bitset const&) noexcept; friend constexpr auto operator^<>(bitset const&, bitset const&) noexcept; friend constexpr auto operator-<>(bitset const&, bitset const&) noexcept; + + [[nodiscard]] constexpr auto begin() noexcept { return iterator(m_impl.begin()); } + [[nodiscard]] constexpr auto begin() const noexcept { return const_iterator(m_impl.begin()); } + [[nodiscard]] constexpr auto end() noexcept { return iterator(m_impl.end()); } + [[nodiscard]] constexpr auto end() const noexcept { return const_iterator(m_impl.end()); } + + [[nodiscard]] constexpr auto rbegin() noexcept { return reverse_iterator(m_impl.rbegin()); } + [[nodiscard]] constexpr auto rbegin() const noexcept { return const_reverse_iterator(m_impl.rbegin()); } + [[nodiscard]] constexpr auto rend() noexcept { return reverse_iterator(m_impl.rend()); } + [[nodiscard]] constexpr auto rend() const noexcept { return const_reverse_iterator(m_impl.rend()); } + + [[nodiscard]] constexpr auto cbegin() const noexcept { return const_iterator(m_impl.cbegin()); } + [[nodiscard]] constexpr auto cend() const noexcept { return const_iterator(m_impl.cend()); } + [[nodiscard]] constexpr auto crbegin() const noexcept { return const_reverse_iterator(m_impl.crbegin()); } + [[nodiscard]] constexpr auto crend() const noexcept { return const_reverse_iterator(m_impl.crend()); } }; template @@ -328,3 +455,5 @@ auto& operator<<(std::basic_ostream& os, bitset const& } } // namespace xstd + +#endif // include guard diff --git a/test/include/bitset/factory.hpp b/test/include/bitset/factory.hpp index a24de9a..918879e 100644 --- a/test/include/bitset/factory.hpp +++ b/test/include/bitset/factory.hpp @@ -7,7 +7,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // unsigned_integral #include // size_t diff --git a/test/include/bitset/sieve.hpp b/test/include/bitset/sieve.hpp new file mode 100644 index 0000000..58df99c --- /dev/null +++ b/test/include/bitset/sieve.hpp @@ -0,0 +1,110 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2024. +// 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 // dynamic_bitset +#include // iota, stride, take_while +#include // conditional_t + +namespace xstd { + +template + requires (N >= 0) +class bit_set; + +template +concept legacy_bitset = requires(C& bs, std::size_t pos) +{ + bs.set(); + bs.reset(pos); +}; + +template +auto size(C const& c) +{ + return c.count(); +} + +template +struct generate_empty +{ + auto operator()(auto) const + { + return C(); + } +}; + +template +struct generate_empty> +{ + auto operator()(boost::dynamic_bitset::size_type n) const + { + return boost::dynamic_bitset(n); + } +}; + +template +auto fill(C& empty) +{ + empty.set(); +} + +template +auto fill(xstd::bit_set& empty) +{ + empty.fill(); +} + +template +auto sift(C& primes, std::size_t m) +{ + primes.reset(m); +} + +template +auto sift(xstd::bit_set& primes, int m) +{ + primes.pop(m); +} + +template +struct generate_candidates +{ + auto operator()(auto n) const + { + auto candidates = generate_empty()(n); + fill(candidates); + sift(candidates, 0); + sift(candidates, 1); + return candidates; + } +}; + +template +auto sift_primes(std::conditional_t, std::size_t, int> n) +{ + auto primes = generate_candidates()(n); + for (auto p + : primes + | std::views::take_while([&](auto x) { return x * x < n; }) + ) { + for (auto m + : std::views::iota(p * p, n) + | std::views::stride(p) + ) { + sift(primes, m); + } + } + return primes; +} + +template +auto filter_twins(C const& primes) +{ + return primes & (primes << 2 | primes >> 2); +} + +} // namespace xstd diff --git a/test/include/set/algorithms.hpp b/test/include/set/algorithms.hpp index ff159a6..cddf02b 100644 --- a/test/include/set/algorithms.hpp +++ b/test/include/set/algorithms.hpp @@ -77,7 +77,7 @@ struct transform_increment_filter BOOST_CHECK( (is << n) == (is | std::views::transform([=](auto x) { return x + n; }) - | std::views::filter([](auto x) { return x < N; }) + | std::views::filter ([=](auto x) { return x < N; }) | std::ranges::to() ) ); @@ -93,8 +93,8 @@ struct transform_decrement_filter using set_type = std::remove_cvref_t; BOOST_CHECK( (is >> n) == (is - | std::views::transform([=](auto x) { return x - n; }) - | std::views::filter([](auto x) { return 0 <= x; }) + | std::views::transform([=](auto x) { return x - n; }) + | std::views::filter ([=](auto x) { return 0 <= x; }) | std::ranges::to() ) ); diff --git a/test/include/set/sieve.hpp b/test/include/set/sieve.hpp new file mode 100644 index 0000000..b686b48 --- /dev/null +++ b/test/include/set/sieve.hpp @@ -0,0 +1,56 @@ +#pragma once + +// Copyright Rein Halbersma 2014-2024. +// 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 // adjacent, elements, filter, iota, stride, take_while, to + +namespace xstd { + +template +auto sift(C& primes, int m) +{ + primes.erase(m); +} + +template +struct generate_candidates +{ + auto operator()(int n) const + { + return std::views::iota(2, n) | std::ranges::to(); + } +}; + +template +auto sift_primes(int n) +{ + auto primes = generate_candidates()(n); + for (auto p + : primes + | std::views::take_while([&](auto x) { return x * x < n; }) + ) { + for (auto m + : std::views::iota(p * p, n) + | std::views::stride(p) + ) { + sift(primes, m); + } + } + return primes; +} + +template +auto filter_twins(C const& primes) +{ + return primes + | std::views::adjacent<3> + | std::views::filter([](auto&& x) { auto&& [ prev, self, next ] = x; return prev + 2 == self || self + 2 == next; }) + | std::views::elements<1> + | std::ranges::to() + ; +} + +} // namespace xstd diff --git a/test/src/bitset/o0.cpp b/test/src/bitset/o0.cpp index c695de8..7060f58 100644 --- a/test/src/bitset/o0.cpp +++ b/test/src/bitset/o0.cpp @@ -8,7 +8,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // empty_set_pair #include // constructor, op_bit_and_assign, op_bit_or_assign, op_bit_xor_assign, op_minus_assign, // op_equal_to, op_bit_and, op_bit_or, op_bit_xor, op_minus, diff --git a/test/src/bitset/o1.cpp b/test/src/bitset/o1.cpp index a73e6cb..723bc3f 100644 --- a/test/src/bitset/o1.cpp +++ b/test/src/bitset/o1.cpp @@ -5,7 +5,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // all_cardinality_sets, all_singleton_sets, all_valid, any_value, empty_set, full_set #include // mem_set, mem_reset, op_bit_not, mem_flip, mem_count, mem_size, mem_test, mem_all, mem_any, mem_none // mem_at, op_bit_and, op_bit_or, op_bit_xor, op_minus, mem_is_subset_of, mem_intersects, fn_iostream diff --git a/test/src/bitset/o2.cpp b/test/src/bitset/o2.cpp index a8bb559..a080ebb 100644 --- a/test/src/bitset/o2.cpp +++ b/test/src/bitset/o2.cpp @@ -5,7 +5,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // all_singleton_sets, all_singleton_set_pairs, any_value #include // op_bit_and_assign, op_bit_or_assign, op_bit_xor_assign, op_minus_assign, // op_shift_left_assign, op_shift_right_assign, op_bit_not, op_equal_to, diff --git a/test/src/bitset/o4.cpp b/test/src/bitset/o4.cpp index eb0ba3a..c0acbd3 100644 --- a/test/src/bitset/o4.cpp +++ b/test/src/bitset/o4.cpp @@ -5,7 +5,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // all_doubleton_set_pairs #include // mem_is_subset_of, mem_is_proper_subset_of #include // vector diff --git a/test/src/bitset/sieve.cpp b/test/src/bitset/sieve.cpp index 37ebbd6..9e82cc8 100644 --- a/test/src/bitset/sieve.cpp +++ b/test/src/bitset/sieve.cpp @@ -6,117 +6,45 @@ #include // dynamic_bitset #include // bitset #include // as_set +#include // bitset #include // bit_set +#include // filter_twins, sift_primes #include // vector #include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE +#include #include // format #include -#include // iota, stride, take_while -#include // conditional_t - -template -concept bitset = requires(C& bs, std::size_t pos) -{ - bs.set(); - bs.reset(pos); -}; - -template -struct generate_empty -{ - auto operator()(auto) const - { - return C(); - } -}; - -template -struct generate_empty> -{ - auto operator()(boost::dynamic_bitset::size_type n) const - { - return boost::dynamic_bitset(n); - } -}; - -template -auto fill(xstd::bit_set& empty) -{ - empty.fill(); -} - -template -auto fill(C& empty) -{ - empty.set(); -} - -template -auto sift(xstd::bit_set& primes, int m) -{ - primes.erase(m); -} - -template -auto sift(C& primes, std::size_t m) -{ - primes.reset(m); -} - -template -struct generate_candidates -{ - auto operator()(auto n) const - { - auto candidates = generate_empty()(n); - fill(candidates); - sift(candidates, 0); - sift(candidates, 1); - return candidates; - } -}; - -template -auto sift_primes(std::conditional_t, std::size_t, int> n) -{ - auto primes = generate_candidates()(n); - for (auto p - : primes - | std::views::take_while([&](auto x) { return x * x < n; }) - ) { - for (auto m - : std::views::iota(p * p, n) - | std::views::stride(p) - ) { - sift(primes, m); - } - } - return primes; -} - -template -auto filter_twins(C const& primes) -{ - return primes & primes >> 2; -} +#include // duration_cast, system_clock +#include // locale BOOST_AUTO_TEST_SUITE(Sieve) -inline constexpr auto N = 100; +inline constexpr auto N = 10'000'000; using set_types = boost::mpl::vector -< std::bitset -, boost::dynamic_bitset<> -, xstd::bit_set +< boost::dynamic_bitset<> +, std::bitset +, xstd::bitset +, xstd::bit_set >; BOOST_AUTO_TEST_CASE_TEMPLATE(Format, C, set_types) { - auto const primes = sift_primes(N); - BOOST_CHECK_EQUAL(fmt::format("{}", primes | xstd::views::as_set), "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); - - auto const twins = filter_twins(primes); - BOOST_CHECK_EQUAL(fmt::format("{}", twins | xstd::views::as_set), "{3, 5, 11, 17, 29, 41, 59, 71}"); + std::locale::global(std::locale("en_US.UTF-8")); + + auto t0 = std::chrono::system_clock::now(); + auto const primes = xstd::sift_primes(N); + auto t1 = std::chrono::system_clock::now(); + auto d0 = std::chrono::duration_cast(t1-t0); + fmt::println("Generated {:>7L} primes in {:>10L}", xstd::size(primes), d0); + // BOOST_CHECK_EQUAL(fmt::format("{}", primes | xstd::views::as_set), "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); + + auto t2 = std::chrono::system_clock::now(); + auto const twins = xstd::filter_twins(primes); + auto t3 = std::chrono::system_clock::now(); + auto d1 = std::chrono::duration_cast(t3 - t2); + fmt::println("Generated {:>7L} twins in {:>10}", xstd::size(twins), d1); + // BOOST_CHECK_EQUAL(fmt::format("{}", twins | xstd::views::as_set), "{3, 5, 11, 17, 29, 41, 59, 71}"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/src/bitset/type_traits.cpp b/test/src/bitset/type_traits.cpp index 3330a96..04df438 100644 --- a/test/src/bitset/type_traits.cpp +++ b/test/src/bitset/type_traits.cpp @@ -8,7 +8,7 @@ #include // dynamic_bitset #include // bitset -#include // bitset +#include // bitset #include // vector #include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE_TEMPLATE #include // regular diff --git a/test/src/set/sieve.cpp b/test/src/set/sieve.cpp index a174ac4..8e88483 100644 --- a/test/src/set/sieve.cpp +++ b/test/src/set/sieve.cpp @@ -4,76 +4,46 @@ // http://www.boost.org/LICENSE_1_0.txt) #include // bit_set +#include // sift_primes #include // flat_set #include // vector #include // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE_TEMPLATE -#include // format +#include +#include // println #include -#include // elements, filter, iota, pairwise, stride, take_while, to +#include // duration_cast, system_clock +#include // locale #include // set -#include // pair - -template -auto sift(C& primes, int m) -{ - primes.erase(m); -} - -template -struct generate_candidates -{ - auto operator()(int n) const - { - return std::views::iota(2, n) | std::ranges::to(); - } -}; - -template -auto sift_primes(int n) -{ - auto primes = generate_candidates()(n); - for (auto p - : primes - | std::views::take_while([&](auto x) { return x * x < n; }) - ) { - for (auto m - : std::views::iota(p * p, n) - | std::views::stride(p) - ) { - sift(primes, m); - } - } - return primes; -} - -template -auto filter_twins(C const& primes) -{ - return primes - | std::views::pairwise - | std::views::filter([](auto&& x) { auto&& [ first, second ] = x; return first + 2 == second; }) - | std::views::elements<0> - | std::ranges::to() - ; -} BOOST_AUTO_TEST_SUITE(Sieve) -inline constexpr auto N = 100; +using namespace xstd; + +inline constexpr auto N = 1'000'000; using set_types = boost::mpl::vector < std::set , boost::container::flat_set -, xstd::bit_set +, bit_set >; -BOOST_AUTO_TEST_CASE_TEMPLATE(Format, C, set_types) +BOOST_AUTO_TEST_CASE_TEMPLATE(Format, T, set_types) { - auto const primes = sift_primes(N); - BOOST_CHECK_EQUAL(fmt::format("{}", primes), "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); - + std::locale::global(std::locale("en_US.UTF-8")); + + auto t0 = std::chrono::system_clock::now(); + auto const primes = sift_primes(N); + auto t1 = std::chrono::system_clock::now(); + auto d0 = std::chrono::duration_cast(t1-t0); + fmt::println("Generated {:>7L} primes in {:>10}", size(primes), d0); + // BOOST_CHECK_EQUAL(fmt::format("{}", primes), "{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}"); + + auto t2 = std::chrono::system_clock::now(); auto const twins = filter_twins(primes); - BOOST_CHECK_EQUAL(fmt::format("{}", twins), "{3, 5, 11, 17, 29, 41, 59, 71}"); + auto t3 = std::chrono::system_clock::now(); + auto d1 = std::chrono::duration_cast(t3 - t2); + fmt::println("Generated {:>7L} twins in {:>10}", size(twins), d1); + // BOOST_CHECK_EQUAL(fmt::format("{}", twins), "{3, 5, 11, 17, 29, 41, 59, 71}"); } BOOST_AUTO_TEST_SUITE_END()