Skip to content

Commit

Permalink
Segmented primes finding
Browse files Browse the repository at this point in the history
  • Loading branch information
R. Kaleta committed Jul 31, 2023
1 parent 27d6ba1 commit ac69220
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 34 deletions.
14 changes: 6 additions & 8 deletions include/algolib/maths/primes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#define PRIMES_HPP_

#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <vector>
#include "algolib/maths/maths.hpp"

Expand All @@ -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<size_t> find_primes(size_t min_number, size_t max_number);
std::vector<size_t> 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<size_t> find_primes(size_t max_number)
inline std::vector<size_t> find_primes(size_t maximum)
{
return find_primes(0, max_number);
return find_primes(0, maximum);
}

#pragma endregion
Expand Down
85 changes: 70 additions & 15 deletions src/algolib/maths/primes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,96 @@
* \brief Algorithms for prime numbers
*/
#include "algolib/maths/primes.hpp"
#include <cmath>
#include <ctime>
#include <algorithm>
#include <random>

namespace alma = algolib::maths;

std::vector<size_t> 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<size_t> get_base_primes(size_t base_maximum)
{
if(max_number <= min_number)
return std::vector<size_t>();
std::vector<size_t> primes;
std::vector<bool> is_prime((base_maximum - 1) / 2, true);

for(size_t i = 0; i < static_cast<size_t>(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<size_t> get_segment_primes(size_t segment_start,
size_t segment_end,
const std::vector<size_t> & base_primes)
{
std::vector<size_t> primes;
std::vector<bool> is_prime;
std::vector<bool> base_primes(static_cast<int>(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<size_t> alma::find_primes(size_t minimum, size_t maximum)
{
if(maximum <= minimum || maximum <= 2)
return std::vector<size_t>();

std::vector<size_t> primes;
size_t segment_size = static_cast<size_t>(sqrt(maximum));
std::vector<size_t> 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<size_t> 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)
Expand Down
64 changes: 53 additions & 11 deletions test/maths/primes_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ namespace alma = algolib::maths;

#pragma region findPrimes

TEST(PrimesTest, findPrimes_WhenRangeIsDescending_ThenEmptyResult)
TEST(PrimesTest, findPrimes_WhenMinGreaterThanMax_ThenEmpty)
{
// when
std::vector<size_t> result = alma::find_primes(100, 30);
// then
EXPECT_TRUE(result.empty());
}

TEST(PrimesTest, findPrimes_WhenSingleMaximumAndRangeFromZero_ThenSameResult)
TEST(PrimesTest, findPrimes_WhenSingleArgument_ThenMinIsZero)
{
// when
std::vector<size_t> result1 = alma::find_primes(100);
Expand All @@ -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<size_t> result = alma::find_primes(100);
Expand All @@ -36,7 +36,7 @@ TEST(PrimesTest, findPrimes_WhenSingleMaximum_ThenPrimesFromZero)
result);
}

TEST(PrimesTest, findPrimes_WhenMaximumIsPrime_ThenMaximumIsNotContained)
TEST(PrimesTest, findPrimes_WhenMaxIsPrime_ThenMaxExclusive)
{
// when
std::vector<size_t> result = alma::find_primes(67);
Expand All @@ -46,15 +46,31 @@ TEST(PrimesTest, findPrimes_WhenMaximumIsPrime_ThenMaximumIsNotContained)
result);
}

TEST(PrimesTest, findPrimes_WhenMaximumIsLessThanTwo_ThenEmptyResult)
TEST(PrimesTest, findPrimes_WhenMaxIsTwo_ThenEmpty)
{
// when
std::vector<size_t> result = alma::find_primes(1);
std::vector<size_t> result = alma::find_primes(2);
// then
EXPECT_TRUE(result.empty());
}

TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesInsideRange)
TEST(PrimesTest, findPrimes_WhenMaxIsThree_ThenSingleElement)
{
// when
std::vector<size_t> result = alma::find_primes(3);
// then
EXPECT_EQ(std::vector<size_t>({2}), result);
}

TEST(PrimesTest, findPrimes_WhenMaxIsFour_ThenAllPrimes)
{
// when
std::vector<size_t> result = alma::find_primes(4);
// then
EXPECT_EQ(std::vector<size_t>({2, 3}), result);
}

TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesBetween)
{
// when
std::vector<size_t> result = alma::find_primes(30, 200);
Expand All @@ -65,7 +81,33 @@ TEST(PrimesTest, findPrimes_WhenRange_ThenPrimesInsideRange)
result);
}

TEST(PrimesTest, findPrimes_WhenMinimumLessThanSquareRootOfMaximum_ThenPrimesInsideRange)
TEST(PrimesTest, findPrimes_WhenMinimumIsTwo_ThenTwoIncluded)
{
// when
std::vector<size_t> result = alma::find_primes(2, 30);
// then
EXPECT_EQ(std::vector<size_t>({2, 3, 5, 7, 11, 13, 17, 19, 23, 29}), result);
}

TEST(PrimesTest, findPrimes_WhenMinimumIsThree_ThenTwoNotIncluded)
{
// when
std::vector<size_t> result = alma::find_primes(3, 30);
// then
EXPECT_EQ(std::vector<size_t>({3, 5, 7, 11, 13, 17, 19, 23, 29}), result);
}

TEST(PrimesTest, findPrimes_WhenMaxIsFourthPowerOfPrime_ThenAllPrimesBetween)
{
// when
std::vector<size_t> result = alma::find_primes(9, 81);
// then
EXPECT_EQ(std::vector<size_t>(
{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<size_t> result = alma::find_primes(5, 150);
Expand All @@ -76,7 +118,7 @@ TEST(PrimesTest, findPrimes_WhenMinimumLessThanSquareRootOfMaximum_ThenPrimesIns
result);
}

TEST(PrimesTest, findPrimes_WhenRangeBoundariesArePrimes_ThenLeftBoundaryIsIncluded)
TEST(PrimesTest, findPrimes_WhenMinAndMaxArePrimes_ThenMinInclusiveAndMaxExclusive)
{
// when
std::vector<size_t> result = alma::find_primes(137, 317);
Expand All @@ -87,15 +129,15 @@ TEST(PrimesTest, findPrimes_WhenRangeBoundariesArePrimes_ThenLeftBoundaryIsInclu
result);
}

TEST(PrimesTest, findPrimes_WhenRangeBoundariesAreSameAndPrime_ThenEmptyResult)
TEST(PrimesTest, findPrimes_WhenMinEqualsMaxAndPrime_ThenEmpty)
{
// when
std::vector<size_t> result = alma::find_primes(41, 41);
// then
EXPECT_TRUE(result.empty());
}

TEST(PrimesTest, findPrimes_WhenRangeBoundariesAreSameAndComposite_ThenEmptyResult)
TEST(PrimesTest, findPrimes_WhenMinEqualsMaxAndComposite_ThenEmpty)
{
// when
std::vector<size_t> result = alma::find_primes(91, 91);
Expand Down

0 comments on commit ac69220

Please sign in to comment.