diff --git a/include/bmath/sieve.hh b/include/bmath/sieve.hh index a0db78f..d9886d7 100644 --- a/include/bmath/sieve.hh +++ b/include/bmath/sieve.hh @@ -1,6 +1,60 @@ #ifndef BMATH_SIEVE_HH #define BMATH_SIEVE_HH -namespace bmath {}; +#include +#include +#include +#include + +namespace bmath { + +template + requires(Limit > 0) +class Sieve { + public: + constexpr explicit Sieve() { static_cast(this)->build(); } + + protected: + std::bitset sieve; +}; + +template + requires(Limit > 0) +class Eratosthenes : public Sieve, Limit> { + public: + constexpr void build() noexcept { + this->sieve.set(); + this->sieve[0] = false; + this->sieve[1] = false; + + for (size_t x = 2; x * x <= Limit; ++x) { + if (!this->sieve[x]) { + continue; + } + + for (size_t current = x + x; current <= Limit; current += x) { + this->sieve[current] = false; + } + } + } + + [[nodiscard]] constexpr bool operator[](size_t const number) const { + if consteval { + if (number > Limit) { + throw std::out_of_range(std::format( + "cannot determine primality of {} > LIMIT={}", number, Limit)); + } + } else { + if (number > Limit) { + throw std::out_of_range(std::format( + "cannot determine primality of {} > LIMIT={}", number, Limit)); + } + } + + return this->sieve[number]; + }; +}; + +} // namespace bmath #endif diff --git a/readme.md b/readme.md index 70350b5..9688ab3 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,7 @@ header-only c++ 23 math library ## todo -[] sieves -[] factorization -[x] `std::unsigned_integral` as modulus type +- sieves (pritchard, eratosthenes) +- factorization (pollard-rho, rabin-miller) +- profiling +- saturating arithmetic operations diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2fb3c98..822c9e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,16 +2,19 @@ include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.14.0 -) + GIT_TAG v1.14.0) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -add_executable(test_bmath test_bmath.cc) +file(GLOB TEST_SOURCES "*.cc") -target_link_libraries(test_bmath PRIVATE bmath::bmath gtest_main) +foreach(TEST_SOURCE ${TEST_SOURCES}) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + add_executable(${TEST_NAME} ${TEST_SOURCE}) + target_link_libraries(${TEST_NAME} PRIVATE bmath::bmath gtest_main) -include(GoogleTest) -gtest_discover_tests(test_bmath) + include(GoogleTest) + gtest_discover_tests(${TEST_NAME}) +endforeach() diff --git a/tests/test_sieve.cc b/tests/test_sieve.cc new file mode 100644 index 0000000..19fa66d --- /dev/null +++ b/tests/test_sieve.cc @@ -0,0 +1,35 @@ +#include + +#include +#include + +#include "../include/bmath/bmath.hh" + +using namespace bmath; + +bool naive_prime(size_t n) { + if (n < 2) { + return false; + } + if (n == 2) { + return true; + } + if (n % 2 == 0) { + return false; + } + for (size_t i = 3; i * i <= n; i += 2) { + if (n % i == 0) return false; + } + return true; +} + +TEST(SieveTest, BasicConstruction) { Eratosthenes<100> sieve; } + +TEST(SieveTest, ValidateAgainstNaive) { + Eratosthenes<1000> sieve; + + for (size_t i = 0; i <= 1000; ++i) { + EXPECT_EQ(sieve[i], naive_prime(i)) + << "Sieve disagrees with naive checker for " << i; + } +}