diff --git a/include/algolib/maths/primes.hpp b/include/algolib/maths/primes.hpp index f708f30..1ed2169 100644 --- a/include/algolib/maths/primes.hpp +++ b/include/algolib/maths/primes.hpp @@ -6,8 +6,6 @@ #define PRIMES_HPP_ #include -#include -#include #include #include "algolib/maths/maths.hpp" @@ -17,20 +15,20 @@ namespace algolib::maths /*! * \brief Finds prime numbers inside a range of integers. - * \param min_number minimal number in range, inclusive - * \param max_number maximal number in range, exclusive + * \param minimum minimal number in range, inclusive + * \param maximum maximal number in range, exclusive * \return vector of prime numbers */ - std::vector find_primes(size_t min_number, size_t max_number); + std::vector find_primes(size_t minimum, size_t maximum); /*! * \brief Finds prime numbers inside a range of integers starting from 0. - * \param max_number maximal number in range, exclusive + * \param maximum maximal number in range, exclusive * \return vector of prime numbers */ - inline std::vector find_primes(size_t max_number) + inline std::vector find_primes(size_t maximum) { - return find_primes(0, max_number); + return find_primes(0, maximum); } #pragma endregion diff --git a/src/algolib/maths/primes.cpp b/src/algolib/maths/primes.cpp index 3739c37..e9d21e9 100644 --- a/src/algolib/maths/primes.cpp +++ b/src/algolib/maths/primes.cpp @@ -3,41 +3,96 @@ * \brief Algorithms for prime numbers */ #include "algolib/maths/primes.hpp" +#include +#include +#include #include namespace alma = algolib::maths; -std::vector alma::find_primes(size_t min_number, size_t max_number) +#pragma region find_primes + +// Extracts prime numbers between 0 and given maximum value +std::vector get_base_primes(size_t base_maximum) { - if(max_number <= min_number) - return std::vector(); + std::vector primes; + std::vector is_prime((base_maximum - 1) / 2, true); + for(size_t i = 0; i < static_cast(sqrt(base_maximum) / 2); ++i) + if(is_prime[i]) + { + size_t prime_value = 2 * i + 3; + + for(size_t j = prime_value * prime_value; j < base_maximum; j += 2 * prime_value) + is_prime[(j - 3) / 2] = false; + } + + for(size_t i = 0; i < is_prime.size(); ++i) + if(is_prime[i]) + primes.push_back(2 * i + 3); + + return primes; +} + +// Extracts prime numbers from given range using given basic prime numbers +std::vector get_segment_primes(size_t segment_start, + size_t segment_end, + const std::vector & base_primes) +{ std::vector primes; std::vector is_prime; - std::vector base_primes(static_cast(sqrt(max_number) / 2), true); + size_t segment_begin = segment_start + 1 - segment_start % 2; - for(size_t i = min_number; i < max_number; ++i) - is_prime.push_back(i == 2 || (i > 2 && i % 2 != 0)); + for(size_t i = segment_begin; i < segment_end; i += 2) + is_prime.push_back(i > 2); - for(size_t i = 0; i < base_primes.size(); ++i) + for(auto && p : base_primes) { - size_t p = i + i + 3; - size_t begin = min_number < p * p ? p * p - min_number : (p - min_number % p) % p; + size_t prime_multiple = (segment_begin + p - 1) / p * p; + size_t multiple_start = prime_multiple % 2 == 0 ? prime_multiple + p : prime_multiple; - for(size_t j = (p * p - 3) / 2; j < base_primes.size(); j += p) - base_primes[j] = false; - - for(size_t j = begin; j < is_prime.size(); j += p) - is_prime[j] = false; + for(size_t i = multiple_start; i < segment_end; i += 2 * p) + is_prime[(i - segment_begin) / 2] = false; } for(size_t i = 0; i < is_prime.size(); ++i) if(is_prime[i]) - primes.push_back(min_number + i); + primes.push_back(segment_begin + 2 * i); return primes; } +std::vector alma::find_primes(size_t minimum, size_t maximum) +{ + if(maximum <= minimum || maximum <= 2) + return std::vector(); + + std::vector primes; + size_t segment_size = static_cast(sqrt(maximum)); + std::vector base_primes = get_base_primes(segment_size); + + if(minimum < segment_size) + { + if(2 >= minimum) + primes.push_back(2); + + for(auto && p : base_primes) + if(p >= minimum) + primes.push_back(p); + } + + for(size_t i = std::max(minimum, segment_size); i < maximum; i += segment_size) + { + std::vector segment_primes = + get_segment_primes(i, std::min(i + segment_size, maximum), base_primes); + + primes.insert(primes.end(), segment_primes.begin(), segment_primes.end()); + } + + return primes; +} + +#pragma endregion #pragma region test_fermat bool alma::test_fermat(int number) diff --git a/test/maths/primes_test.cpp b/test/maths/primes_test.cpp index d51ea8d..31be5e6 100644 --- a/test/maths/primes_test.cpp +++ b/test/maths/primes_test.cpp @@ -9,7 +9,7 @@ namespace alma = algolib::maths; #pragma region findPrimes -TEST(PrimesTest, findPrimes_WhenRangeIsDescending_ThenEmptyResult) +TEST(PrimesTest, findPrimes_WhenMinGreaterThanMax_ThenEmpty) { // when std::vector result = alma::find_primes(100, 30); @@ -17,7 +17,7 @@ TEST(PrimesTest, findPrimes_WhenRangeIsDescending_ThenEmptyResult) EXPECT_TRUE(result.empty()); } -TEST(PrimesTest, findPrimes_WhenSingleMaximumAndRangeFromZero_ThenSameResult) +TEST(PrimesTest, findPrimes_WhenSingleArgument_ThenMinIsZero) { // when std::vector result1 = alma::find_primes(100); @@ -26,7 +26,7 @@ TEST(PrimesTest, findPrimes_WhenSingleMaximumAndRangeFromZero_ThenSameResult) EXPECT_EQ(result1, result2); } -TEST(PrimesTest, findPrimes_WhenSingleMaximum_ThenPrimesFromZero) +TEST(PrimesTest, findPrimes_WhenMaxIsComposite_ThenAllPrimes) { // when std::vector result = alma::find_primes(100); @@ -36,7 +36,7 @@ TEST(PrimesTest, findPrimes_WhenSingleMaximum_ThenPrimesFromZero) result); } -TEST(PrimesTest, findPrimes_WhenMaximumIsPrime_ThenMaximumIsNotContained) +TEST(PrimesTest, findPrimes_WhenMaxIsPrime_ThenMaxExclusive) { // when std::vector result = alma::find_primes(67); @@ -46,15 +46,31 @@ TEST(PrimesTest, findPrimes_WhenMaximumIsPrime_ThenMaximumIsNotContained) result); } -TEST(PrimesTest, findPrimes_WhenMaximumIsLessThanTwo_ThenEmptyResult) +TEST(PrimesTest, findPrimes_WhenMaxIsTwo_ThenEmpty) { // when - std::vector result = alma::find_primes(1); + std::vector result = alma::find_primes(2); // then EXPECT_TRUE(result.empty()); } -TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesInsideRange) +TEST(PrimesTest, findPrimes_WhenMaxIsThree_ThenSingleElement) +{ + // when + std::vector result = alma::find_primes(3); + // then + EXPECT_EQ(std::vector({2}), result); +} + +TEST(PrimesTest, findPrimes_WhenMaxIsFour_ThenAllPrimes) +{ + // when + std::vector result = alma::find_primes(4); + // then + EXPECT_EQ(std::vector({2, 3}), result); +} + +TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesBetween) { // when std::vector result = alma::find_primes(30, 200); @@ -65,7 +81,33 @@ TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesInsideRange) result); } -TEST(PrimesTest, findPrimes_WhenMinimumLessThanSquareRootOfMaximum_ThenPrimesInsideRange) +TEST(PrimesTest, findPrimes_WhenMinimumIsTwo_ThenTwoIncluded) +{ + // when + std::vector result = alma::find_primes(2, 30); + // then + EXPECT_EQ(std::vector({2, 3, 5, 7, 11, 13, 17, 19, 23, 29}), result); +} + +TEST(PrimesTest, findPrimes_WhenMinimumIsThree_ThenTwoNotIncluded) +{ + // when + std::vector result = alma::find_primes(3, 30); + // then + EXPECT_EQ(std::vector({3, 5, 7, 11, 13, 17, 19, 23, 29}), result); +} + +TEST(PrimesTest, findPrimes_WhenMaxIsFourthPowerOfPrime_ThenAllPrimesBetween) +{ + // when + std::vector result = alma::find_primes(9, 81); + // then + EXPECT_EQ(std::vector( + {11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79}), + result); +} + +TEST(PrimesTest, findPrimes_WhenMinIsLessThanSquareRootOfMax_ThenPrimesBetween) { // when std::vector result = alma::find_primes(5, 150); @@ -76,7 +118,7 @@ TEST(PrimesTest, findPrimes_WhenMinimumLessThanSquareRootOfMaximum_ThenPrimesIns result); } -TEST(PrimesTest, findPrimes_WhenRangeBoundariesArePrimes_ThenLeftBoundaryIsIncluded) +TEST(PrimesTest, findPrimes_WhenMinAndMaxArePrimes_ThenMinInclusiveAndMaxExclusive) { // when std::vector result = alma::find_primes(137, 317); @@ -87,7 +129,7 @@ TEST(PrimesTest, findPrimes_WhenRangeBoundariesArePrimes_ThenLeftBoundaryIsInclu result); } -TEST(PrimesTest, findPrimes_WhenRangeBoundariesAreSameAndPrime_ThenEmptyResult) +TEST(PrimesTest, findPrimes_WhenMinEqualsMaxAndPrime_ThenEmpty) { // when std::vector result = alma::find_primes(41, 41); @@ -95,7 +137,7 @@ TEST(PrimesTest, findPrimes_WhenRangeBoundariesAreSameAndPrime_ThenEmptyResult) EXPECT_TRUE(result.empty()); } -TEST(PrimesTest, findPrimes_WhenRangeBoundariesAreSameAndComposite_ThenEmptyResult) +TEST(PrimesTest, findPrimes_WhenMinEqualsMaxAndComposite_ThenEmpty) { // when std::vector result = alma::find_primes(91, 91);