#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <imath.hpp>
#include <LoadBalancerP2.hpp>
#include <print.hpp>
#include <stdint.h>
#include <algorithm>
#include <cassert>
using namespace primecount;
namespace {
template <typename T>
T P2_thread(T x,
int64_t y,
int64_t low,
int64_t high)
{
assert(low > 0);
assert(low < high);
int64_t sqrtx = isqrt(x);
int64_t start = max(y, min(x / high, sqrtx));
int64_t stop = min(x / low, sqrtx);
primesieve::iterator rit(stop + 1, start);
int64_t prime = rit.prev_prime();
if (prime <= start)
return 0;
int threads = 1;
int64_t xp = (int64_t)(x / prime);
int64_t pi_xp = pi_noprint(xp, threads);
T sum = pi_xp;
prime = rit.prev_prime();
primesieve::iterator it(xp, high);
int64_t p = it.next_prime();
for (; prime > start; prime = rit.prev_prime())
{
xp = (int64_t)(x / prime);
for (; p <= xp; p = it.next_prime())
pi_xp++;
sum += pi_xp;
}
return sum;
}
template <typename T>
T P2_OpenMP(T x,
int64_t y,
int threads,
bool is_print)
{
static_assert(std::is_signed<T>::value,
"T must be signed integer type");
if (x < 4)
return 0;
int64_t sqrtx = isqrt(x);
T a = pi_noprint(y, threads);
T b = pi_noprint(sqrtx, threads);
if (a >= b)
return 0;
T sum = (a - 2) * (a + 1) / 2 - (b - 2) * (b + 1) / 2;
int64_t xy = (int64_t)(x / max(y, 1));
LoadBalancerP2 loadBalancer(x, xy, threads, is_print);
threads = loadBalancer.get_threads();
#pragma omp parallel num_threads(threads) reduction(+:sum)
{
int64_t low, high;
while (loadBalancer.get_work(low, high))
sum += P2_thread(x, y, low, high);
}
return sum;
}
}
namespace primecount {
int64_t P2(int64_t x,
int64_t y,
int threads,
bool is_print)
{
if (is_print)
{
print("");
print("=== P2(x, y) ===");
print_vars(x, y, threads);
}
double time = get_time();
int64_t sum = P2_OpenMP(x, y, threads, is_print);
if (is_print)
print("P2", sum, time);
return sum;
}
#ifdef HAVE_INT128_T
int128_t P2(int128_t x,
int64_t y,
int threads,
bool is_print)
{
if (is_print)
{
print("");
print("=== P2(x, y) ===");
print_vars(x, y, threads);
}
double time = get_time();
int128_t sum = P2_OpenMP(x, y, threads, is_print);
if (is_print)
print("P2", sum, time);
return sum;
}
#endif
}