Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prime Number Functions #400

Draft
wants to merge 117 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
4015fbc
Initial Commit
mborland Jul 9, 2020
3ee737b
Changed init of least_divisors
mborland Jul 10, 2020
9512bb6
Cleanup
mborland Jul 10, 2020
d762398
Cleanup
mborland Jul 10, 2020
5375a1d
Added additional tests, benchmarks, and overflow checks
mborland Jul 10, 2020
a684dbd
Fix include guard naming
mborland Jul 10, 2020
3e4db8a
Complete revamp of algorithm. Hide implementation behind detail names…
mborland Jul 12, 2020
dd8a61c
Re-added support and tests for boost::multiprecision::cpp_int [CI SKIP]
mborland Jul 12, 2020
4debd0d
Changed benchmarks to support threading [CI SKIP]
mborland Jul 13, 2020
2a7e031
Added execution policies. Increased performance for dynamically linke…
mborland Jul 14, 2020
a68910e
Added massively parallel section to prime_sieve. Increased length of …
mborland Jul 14, 2020
386cc4d
Add test for multi-threading section and add to Jamfile [CI SKIP]
mborland Jul 15, 2020
d79eddb
Added prime sieve to existing prime numbers documentation [CI SKIP]
mborland Jul 15, 2020
3fdf917
Revisions to documentation and send to CI
mborland Jul 15, 2020
6ca245b
Changed include guards to be compatible with C++11 and 14.
mborland Jul 15, 2020
7d3a520
Fixed doc, and ensured that primes are sorted
mborland Jul 15, 2020
a1ac504
Fixed documentation. Complete re-design of mask_sieve algo. Pre-gener…
mborland Jul 16, 2020
35d2aa1
All vectors now init {}. Change include guards. Replace raw pointer.
mborland Jul 17, 2020
243a299
Changed from [lower_bound, upper_bound] to [lower_bound, upper_bound)…
mborland Jul 18, 2020
2eadeff
Fixed documentation [CI SKIP]
mborland Jul 18, 2020
173ce0d
segmented_sieve now runs using std::async. [CI SKIP]
mborland Jul 18, 2020
23fba36
Removed extraneous operations [CI SKIP]
mborland Jul 19, 2020
f7b45fd
Cleanup style, and delete unused function. Enable par_unseq mask_siev…
mborland Jul 19, 2020
d687d5e
Small Cleanup and limit reduction for sieving methods.
mborland Jul 20, 2020
e51d727
Add pritchard's sub-linear algo [WIP][CI SKIP]
mborland Jul 25, 2020
1245d27
Added pritchards segmented sieve [WIP][CI SKIP]
mborland Jul 26, 2020
2052081
Implemented binary search in SetS [WIP][CI SKIP]
mborland Jul 27, 2020
9f81e0d
Tests and performance compairsons [WIP][CI SKIP]
mborland Jul 27, 2020
55ea045
More tests. Segmented sieve small cases fix
mborland Jul 27, 2020
31a2105
Imporve segmented performance [WIP][CI SKIP]
mborland Jul 29, 2020
f545928
Replaced SetS members with stl algos [WIP][CI SKIP]
mborland Jul 29, 2020
d780db5
Replace searching with tracked index. General performance improvement…
mborland Jul 31, 2020
2639bed
Minor change to SetS remove [WIP][CI SKIP]
mborland Aug 1, 2020
94fc1ac
Pritchard segmented performance improvements [WIP][CI SKIP]
mborland Aug 1, 2020
6ebb906
Fixed failed test [WIP][CI SKIP]
mborland Aug 1, 2020
0521854
Various performance improvements [WIP][CI SKIP]
mborland Aug 2, 2020
b233a80
Build primes in situ [WIP][CI SKIP]
mborland Aug 2, 2020
f95c2cf
Refactoring. Now requires C++17
mborland Aug 2, 2020
8e2e29a
Add segmented bit sieve [WIP][CI SKIP]
mborland Aug 4, 2020
1a24f16
Removed segmented bit sieve and excess headers [CI SKIP]
mborland Aug 6, 2020
c63f1f1
Added wheel class [WIP][CI SKIP]
Aug 15, 2020
b7d4256
Added fixed mod 210 wheel [WIP][CI SKIP]
Aug 23, 2020
fbc38c8
New segmented sieve algorithm [WIP][CI SKIP]
Aug 23, 2020
2e46b81
Added interval sieve to performance test [CI SKIP]
Aug 23, 2020
6759ede
Added unit tests [WIP][CI SKIP]
Aug 23, 2020
1b40403
Implemented interval sieve [CI SKIP]
Aug 23, 2020
97244be
Performance improvements and bug fixes [CI SKIP]
Aug 24, 2020
fa04133
Significant refactoring [WIP][CI SKIP]
Aug 25, 2020
c4a89c8
Seq policy actually sequential [CI SKIP]
Aug 25, 2020
91836f6
Fixes for multiprecision and policies [CI SKIP]
Aug 25, 2020
6d6b19f
cpp_int now passes all tests [CI SKIP]
Aug 27, 2020
81e4a6c
mpz_int passes unit tests [CI SKIP]
Aug 27, 2020
0b8f1d5
Documentation edits [CI SKIP]
mborland Aug 30, 2020
5ba0a1d
Replace magic number w variable template [CI SKIP]
mborland Aug 31, 2020
8e240f7
Better prime_range implementation [WIP][CI SKIP]
mborland Aug 31, 2020
7c0cbbf
Merge branch 'develop' into prime_functions
mborland Sep 1, 2020
3d9b77c
Minor changes and doc updates
mborland Sep 2, 2020
302fb5f
Merge branch 'develop' into prime_functions
mborland Sep 5, 2020
9792a23
Minor change to policy handling [CI SKIP]
mborland Sep 6, 2020
84a69f0
diffs from @jzmaddock [CI SKIP]
mborland Sep 8, 2020
5dc3523
Implemented linear sieve with iterators
mborland Sep 9, 2020
0dbe69c
Sanitize linear prime sieve and add wheel
mborland Sep 9, 2020
eee2c86
Added container method
mborland Sep 26, 2020
f5d789a
Improved Linear Algo and testing
mborland Sep 27, 2020
f2277e3
Merge branch 'prime_iterator' into mborland/prime_iterator
mborland Sep 27, 2020
1d2f03c
Linear sieve with output iterator and refactoring [CI SKIP][WIP]
mborland Sep 27, 2020
0d9d31b
Merge branch 'prime_functions' into mborland/prime_functions
mborland Sep 27, 2020
c361cde
Linear output iterator and refactoring [CI SKIP]
mborland Sep 27, 2020
66c2642
Added prime approximation function
mborland Sep 27, 2020
de1f331
prime approximation function for a range
mborland Sep 27, 2020
eaea5f9
Added interval sieve for output iterators
mborland Sep 27, 2020
e8196f3
Merge remote-tracking branch 'origin/develop' into prime_iterator
mborland Sep 27, 2020
cb5d978
Interval sieve with OI passes unit tests [CI SKIP]
mborland Sep 27, 2020
830ccc4
Merge branch 'prime_iterator' into prime_functions
mborland Sep 27, 2020
4dc3eb2
Add sequential composite sieve with OI [CI SKIP]
mborland Sep 30, 2020
90be100
Add threaded method. Currently INOP [CI SKIP]
mborland Sep 30, 2020
a772782
Threaded method completed and validated [CI SKIP]
mborland Oct 1, 2020
2d1461f
Resolves issue #439 [CI SKIP]
mborland Oct 1, 2020
e7cdb32
Updated benchmarks [CI SKIP]
mborland Oct 2, 2020
29eef88
Correct benchmark memory allocation [CI SKIP]
mborland Oct 3, 2020
b5a28b5
Add linear sieve direct from stepanov [CI SKIP]
mborland Oct 4, 2020
6c26b53
First draft of eratosthenes w/ wheel [CI SKIP]
mborland Oct 4, 2020
980bfe7
Fixes for multiprecision types [CI SKIP]
mborland Oct 4, 2020
07e6f58
wheel sieve passes standard battery [CI SKIP]
mborland Oct 4, 2020
86b9e5a
Add prime sieve wrapper [CI SKIP]
mborland Oct 5, 2020
f19149e
Implement dual interface for prime sieve [CI SKIP]
mborland Oct 7, 2020
1ad0d51
Implement dual interface for prime_range [CI SKIP]
mborland Oct 7, 2020
5f6d06a
Remove uneeded vals from wheel [CI SKIP]
mborland Nov 22, 2020
670b06d
Internal data structure changes [CI SKIP]
mborland Nov 22, 2020
7c7a491
Rename type alias [CI SKIP]
mborland Nov 23, 2020
e507ba5
Swap mod 210 wheel for mod 30 wheel [CI SKIP]
mborland Nov 23, 2020
c9cb41c
Add simple bitset file [CI SKIP]
mborland Nov 23, 2020
e8b71a0
Reduced unnecessary traversing of bitset [CI SKIP]
mborland Nov 24, 2020
d649f65
Updated bitset [CI SKIP]
mborland Nov 24, 2020
ed98892
Add resize to simple_bitset [CI SKIP]
mborland Nov 26, 2020
c3b3934
Implement simple_bitset as internal data structure
mborland Nov 26, 2020
e9aa05d
Add prime-composite ratio to wheel to size bitset
mborland Nov 27, 2020
7c5d792
minor refactoring [CI SKIP]
mborland Nov 27, 2020
2bbfad2
Fix PrimeRatio [CI SKIP]
mborland Nov 27, 2020
ddc5c8a
Improved prime range [CI SKIP]
mborland Nov 27, 2020
f517c00
Add hamming weight calc to simple bitset [CI SKIP]
mborland Nov 28, 2020
a356b47
Add forward and reverse bit scan to simple bitset
mborland Nov 28, 2020
2212e45
Fix count, enable bit scan by type, cleanup
mborland Nov 28, 2020
b12e2dc
Add dual interface. Avoid using multiprecision
mborland Nov 28, 2020
f39a4d8
Seperate approximation for multiprecision types
mborland Nov 29, 2020
eafbefc
Remove all mulitples of wheel basis from bitset
mborland Nov 29, 2020
cb36b89
IntervalSieve performance improvements [CI SKIP]
mborland Nov 29, 2020
16c2354
Add small prime optimization [CI SKIP]
mborland Dec 5, 2020
0b1a690
Add small primes to full sieve [CI SKIP]
mborland Dec 8, 2020
8c883d1
Merge remote-tracking branch 'origin/develop' into prime_functions
mborland Dec 12, 2020
f357e0f
Implement bit scanning in sieve [CI SKIP]
mborland Dec 12, 2020
a4f2d89
Fix minor errors
mborland Dec 12, 2020
d16e562
Add trial divison to verify tests [CI SKIP]
mborland Dec 12, 2020
91be2c3
Fix small primes [CI SKIP]
mborland Dec 12, 2020
d9ae8c2
Revert interval_prime_sieve.hpp to f39a4d8
mborland Dec 13, 2020
7d22010
Remove cruft [CI SKIP]
mborland Dec 13, 2020
b541987
Continue cleanup [CI SKIP]
mborland Dec 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions include/boost/math/special_functions/prime_sieve.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2020 Matt Borland
//
// Use, modification and distribution are subject to 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)

#ifndef BOOST_MATH_SPECIAL_FUNCTIONS_PRIME_FUNCTIONS_HPP
mborland marked this conversation as resolved.
Show resolved Hide resolved
#define BOOST_MATH_SPECIAL_FUNCTIONS_PRIME_FUNCTIONS_HPP

#include <boost/math/special_functions/prime.hpp>
#include <boost/assert.hpp>
#include <deque>
#include <vector>
#include <iterator>
#include <iostream>

namespace boost { namespace math
{

// https://mathworld.wolfram.com/SieveofEratosthenes.html
// https://www.cs.utexas.edu/users/misra/scannedPdf.dir/linearSieve.pdf
template<class Z, class OutputIterator>
auto prime_sieve(Z lower_bound, Z upper_bound, OutputIterator output) -> decltype(output)
{
static_assert(std::is_integral<Z>::value, "No primes for floating point types");
BOOST_ASSERT_MSG(upper_bound + 1 < std::numeric_limits<Z>::max(), "Type Overflow");
mborland marked this conversation as resolved.
Show resolved Hide resolved
std::vector<Z> least_divisors(upper_bound + 1, 0);
std::deque<Z> primes;
mborland marked this conversation as resolved.
Show resolved Hide resolved

for (Z i{2}; i <= upper_bound; ++i)
{
if (least_divisors[i] == 0)
{
least_divisors[i] = i;
primes.emplace_back(i);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would wager that this is a factor in the performance difference: Kim's implementation estimates the number of primes and reserves that number of elements upfront.
I didn't look into exactly how it is estimated but presumably it is something like this: https://en.wikipedia.org/wiki/Prime-counting_function

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; I would agree-I thought we had this earlier . . .

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did. The snippets in question are below:

template<typename T>
struct IsVector
{
    using type = T;
    constexpr static bool value = false;
};

template<typename T>
struct IsVector<std::vector<T>>
{
    using type = std::vector<T>;
    constexpr static bool value = true;
};

template<typename T>
constexpr bool is_vector_v = IsVector<T>::value;

and this was in the main prime_sieve (now callable from prime_reserve)

    if constexpr (detail::is_vector_v<decltype(primes)>)
    {
        primes.reserve(upper_bound / std::log(static_cast<double>(upper_bound)));
    }

I thought making it a little less generic to gain performance was worthwhile.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call me a purist, but you'll make it faster by making it more generic. Allocating memory for the output is not the responsibility of the algorithm.
Now that I've seen KW's implementation, I guess you essentially copied the interface so that it was easier to compare performance?
We don't have to copy the interface, ours can be better. And performance is not even the most important factor: allocating memory on the heap is simply a no-go in some contexts.
To make it concrete, if the system that I work on at work needed to generate primes, we could not use KW's or this implementation (as it is), we would have to write our own. I wasn't always aware of such systems and limitations, it just comes with experience.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially was returning via output iterator instead of using a provided container. Profiling showed that more than 10% of my runtime was the call to std::move for the output iterator so I changed approaches to the current one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced this interface change is worth ~5x more CPU runtime. Care to weigh in @pabristow @jzmaddock ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have a number of issues here:

  • Using a container mandates dynamic memory allocation which may be a no-go in some contexts, and excludes things like std::array.
  • Using an output iterator is fundamentally unsafe in some contexts (ie where the output iterator is a pointer to a C array).
  • Using an output iterator on a container is non-trivial as it has to check and resize the container on each output :(

Question: how far can we go with a range-based output? As in "put the next N primes here". The range could either be a pair of iterators, an iterator and the value N, or a boost::range. If that's not faster than a container based interface (no memory allocation) then there's something wrong somewhere.

But... I don't know how you would divide that up for multithreading?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mborland
I can't see the code that is testing the output iterator, but yeah, a 5x difference is not acceptable. But it's also bizarre. Why is move being used at all? int64_t is a built-in data type, right, I thought it would just be copied? Is there the same huge difference for int32_t?

I would test it something like this. Create the vector to the size required outside the loop.
If you're doing something different, I'd be curious to see it and know why.

template <typename Integer>
Integer conservative_prime_count(Integer x)
{
    auto const c = 30 * log(113) / 113; // Magic numbers from Wikipedia.
    return floor(c * x / log(x));
}

template <class Integer>
void prime_sieve(benchmark::State& state)
{
    Integer upper = static_cast<Integer>(state.range(0));
    std::vector<Integer> primes(conservative_prime_count(upper));

    for(auto _ : state)
    {
        benchmark::DoNotOptimize(prime_sieve_helper(std::execution::par, upper, begin(primes)));
    }
    state.SetComplexityN(state.range(0));
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzmaddock What about building a wrapper class that would support range-based output and other functions like last/next value, random value, etc? I think that is currently the best way to go about supporting a wider variety of data structures. As for removing dynamic memory allocation? I think it is in the realm of the possible, but would require massive retooling. You would become highly dependent on mutex locking which is not a trivial operation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzmaddock

  • Right. Doesn't matter how fast an algorithm is if it's not actually useful.
  • Not sure why this scenario is different (more or less safe) to any other scenario with an output iterator. Clients are responsible for creating and using output iterators safely. They can use a back_insert_iterator on vector.
  • Yes, but that's an issue for the client, not for the algorithm, right?

(Btw, I'm going to use x for prime numbers and n for data sizes.)

It's funny, but the output iterator interface is almost a range interface. As you say, either an (iterator, iterator) or an (iterator, n) pair define a range. The interface to this algorithm is essentially (iterator, x), which means "generate prime numbers up to x", which translates to an output range of (iterator, prime_count(x)).
So yeah, an alternative interface is (iterator, n), i.e., generate the first n prime numbers. Then it's up to the client to calculate n from x if they know what x they want to calculate up to.


for (size_t j{}; j < least_divisors.size(); ++j)
{
if (j >= primes.size())
{
break;
}

else if (primes[j] > least_divisors[i])
{
break;
}

else if (i * primes[j] > upper_bound)
{
break;
}

else
{
least_divisors[i * primes[j]] = primes[j];
}
}
mborland marked this conversation as resolved.
Show resolved Hide resolved
}

auto it{primes.begin()};
while (*it < lower_bound && it != primes.end())
{
primes.pop_front();
++it;
}

return std::move(primes.begin(), primes.end(), output);
}

template<class Z, class OutputIterator>
auto prime_range(Z lower_bound, Z upper_bound, OutputIterator output) -> decltype(output)
{
if (upper_bound <= 104729)
{
Z i{2};
unsigned counter {};
std::deque<Z> primes;
while (i <= upper_bound)
{
if (i >= lower_bound)
{
primes.emplace_back(i);
}

++counter;
i = static_cast<Z>(boost::math::prime(counter));
}

return std::move(primes.begin(), primes.end(), output);
}

else
{
return prime_sieve(lower_bound, upper_bound, output);
}
}

template<class Z, class OutputIterator>
inline auto prime_range(Z upper_bound, OutputIterator output) -> decltype(output)
{
return prime_range(static_cast<Z>(2), upper_bound, output);
}
}}

#endif //BOOST_MATH_SPECIAL_FUNCTIONS_PRIME_FUNCTIONS_HPP
66 changes: 66 additions & 0 deletions reporting/performance/prime_sieve_performance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2020 Matt Borland
//
// Use, modification and distribution are subject to 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 <boost/math/special_functions/prime_sieve.hpp>
#include <benchmark/benchmark.h>

template <class Z>
void prime_sieve(benchmark::State& state)
{
Z upper = static_cast<Z>(state.range(0));
for(auto _ : state)
{
std::vector<Z> primes;
benchmark::DoNotOptimize(boost::math::prime_sieve(static_cast<Z>(2), upper, std::back_inserter(primes)));
}
state.SetComplexityN(state.range(0));
}

template <typename Z>
void prime_range(benchmark::State& state)
{
Z upper = static_cast<Z>(state.range(0));
for(auto _ : state)
{
std::vector<Z> primes;
benchmark::DoNotOptimize(boost::math::prime_range(static_cast<Z>(2), upper, std::back_inserter(primes)));
}
state.SetComplexityN(state.range(0));
}

template <class Z>
void prime_sieve_partial_range(benchmark::State& state)
{
Z upper = static_cast<Z>(state.range(0));
Z lower = static_cast<Z>(state.range(0)) > 2 ? static_cast<Z>(state.range(0)) : 2;
for(auto _ : state)
{
std::vector<Z> primes;
benchmark::DoNotOptimize(boost::math::prime_sieve(lower, upper, std::back_inserter(primes)));
}
state.SetComplexityN(state.range(0));
}

BENCHMARK_TEMPLATE(prime_sieve, int32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve, int64_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve, uint32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve_partial_range, int32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve_partial_range, int64_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve_partial_range, uint32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_range, int32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_range, int64_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();
BENCHMARK_TEMPLATE(prime_range, uint32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 22)->Complexity();

// Direct comparison of lookup vs sieve using only range of lookup
BENCHMARK_TEMPLATE(prime_sieve, int32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();
BENCHMARK_TEMPLATE(prime_range, int32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve, int64_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();
BENCHMARK_TEMPLATE(prime_range, int64_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();
BENCHMARK_TEMPLATE(prime_sieve, uint32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();
BENCHMARK_TEMPLATE(prime_range, uint32_t)->RangeMultiplier(2)->Range(1 << 1, 1 << 16)->Complexity();

BENCHMARK_MAIN();
127 changes: 127 additions & 0 deletions test/test_prime_sieve.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2020 Matt Borland
//
// Use, modification and distribution are subject to 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 <boost/math/special_functions/prime_sieve.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <list>
#include <deque>
#include <array>

template<typename Z>
void test_prime_sieve()
{
std::vector<Z> primes;
Z ref {168}; // Calculated with wolfram-alpha

// Does the function work with a vector
boost::math::prime_sieve(2, 1000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), ref);

// Tests for correctness
// 100
primes.clear();
boost::math::prime_sieve(2, 100, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), 25);

// 10'000
primes.clear();
boost::math::prime_sieve(2, 10000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), 1229);

// 100'000
primes.clear();
boost::math::prime_sieve(2, 100000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), 9592);

// 1'000'000
primes.clear();
boost::math::prime_sieve(2, 1000000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), 78498);

// Does the function work with a list?
std::list<Z> l_primes;
boost::math::prime_sieve(2, 1000, std::back_inserter(l_primes));
BOOST_TEST_EQ(l_primes.size(), ref);

// Does the function work with a deque?
std::deque<Z> d_primes;
boost::math::prime_sieve(2, 1000, std::back_inserter(d_primes));
BOOST_TEST_EQ(d_primes.size(), ref);
}

template<typename Z>
void test_prime_range()
{
std::vector<Z> primes;
Z ref {168}; // Calculated with wolfram-alpha

// Does the upper and lower bound call work
boost::math::prime_range(2, 1000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), ref);

// Does the upper bound call work
primes.clear();
boost::math::prime_range(1000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), ref);

// Does it work with a deque?
std::deque<Z> d_primes;
boost::math::prime_range(1000, std::back_inserter(d_primes));
BOOST_TEST_EQ(d_primes.size(), ref);

// Does it work with a list?
std::list<Z> l_primes;
boost::math::prime_range(1000, std::front_inserter(l_primes));
BOOST_TEST_EQ(l_primes.size(), ref);

// Does the lower bound change the results?
ref = 143; // Calculated with wolfram-alpha
primes.clear();
boost::math::prime_range(100, 1000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), ref);

// Does it work with 0 difference?
primes.clear();
boost::math::prime_range(2, 2, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), 1);

// Will it call the sieve for large input
ref = 78498; // Calculated with wolfram-alpha
primes.clear();
boost::math::prime_range(1000000, std::back_inserter(primes));
BOOST_TEST_EQ(primes.size(), ref);
}

template<typename Z>
void test_prime_sieve_overflow()
{
std::vector<Z> primes;

// Should die with call to BOOST_ASSERT
boost::math::prime_sieve(static_cast<Z>(2), static_cast<Z>(std::numeric_limits<Z>::max()),
std::back_inserter(primes));
}

int main()
{
test_prime_sieve<int>();
test_prime_sieve<int32_t>();
test_prime_sieve<int64_t>();
test_prime_sieve<uint32_t>();

test_prime_range<int>();
test_prime_range<int32_t>();
test_prime_range<int64_t>();
test_prime_range<uint32_t>();

test_prime_sieve<boost::multiprecision::cpp_int>();

//test_prime_sieve_overflow<int16_t>();

boost::report_errors();
}