#include <primecount-internal.hpp>
#include <imath.hpp>
#include <generate.hpp>
#include <PhiTiny.hpp>
#include <BinaryIndexedTree.hpp>
#include <S.hpp>
#include <stdint.h>
#include <algorithm>
#include <vector>
using std::min;
using std::max;
using std::vector;
using namespace primecount;
namespace {
template <typename S, typename T>
void cross_off(int64_t prime,
int64_t low,
int64_t high,
int64_t& next_multiple,
S& sieve,
T& tree)
{
int64_t m = next_multiple;
for (; m < high; m += prime * 2)
{
if (sieve[m - low])
{
sieve[m - low] = 0;
tree.update(m - low);
}
}
next_multiple = m;
}
int64_t S2(int64_t x,
int64_t y,
int64_t c,
int64_t pi_y,
const vector<int32_t>& primes,
const vector<int32_t>& lpf,
const vector<int32_t>& mu)
{
int64_t limit = x / y;
int64_t segment_size = next_power_of_2(isqrt(limit));
int64_t s2 = 0;
vector<char> sieve(segment_size);
vector<int64_t> next(primes.begin(), primes.end());
vector<int64_t> phi(primes.size(), 0);
BinaryIndexedTree tree;
for (int64_t low = 1; low < limit; low += segment_size)
{
int64_t high = min(low + segment_size, limit);
std::fill(sieve.begin(), sieve.end(), 1);
for (int64_t b = 1; b <= c; b++)
{
int64_t k = next[b];
for (int64_t prime = primes[b]; k < high; k += prime)
sieve[k - low] = 0;
next[b] = k;
}
tree.init(sieve);
for (int64_t b = c + 1; b < pi_y; b++)
{
int64_t prime = primes[b];
int64_t min_m = max(x / (prime * high), y / prime);
int64_t max_m = min(x / (prime * low), y);
if (prime >= max_m)
break;
for (int64_t m = max_m; m > min_m; m--)
{
if (mu[m] != 0 && prime < lpf[m])
{
int64_t n = prime * m;
int64_t count = tree.count(low, x / n);
int64_t phi_xn = phi[b] + count;
s2 -= mu[m] * phi_xn;
}
}
phi[b] += tree.count(low, high - 1);
cross_off(prime, low, high, next[b], sieve, tree);
}
}
return s2;
}
}
namespace primecount {
int64_t pi_lmo4(int64_t x)
{
if (x < 2)
return 0;
bool threads = 1;
double alpha = get_alpha_lmo(x);
int64_t x13 = iroot<3>(x);
int64_t y = (int64_t) (x13 * alpha);
int64_t c = PhiTiny::get_c(y);
int64_t p2 = P2(x, y, threads);
auto primes = generate_primes<int32_t>(y);
auto lpf = generate_lpf(y);
auto mu = generate_moebius(y);
int64_t pi_y = primes.size() - 1;
int64_t s1 = S1(x, y, c, threads);
int64_t s2 = S2(x, y, c, pi_y, primes, lpf, mu);
int64_t phi = s1 + s2;
int64_t sum = phi + pi_y - 1 - p2;
return sum;
}
}