use super::linear_sieve;
use super::super::traits::bisect;
use std::ops::RangeFrom;
use bisect::Bisect;
use linear_sieve::LinearSieve;
pub fn prime_pi(n: usize) -> usize {
let n_2 = (1_usize..).bisect(|i| i.pow(2) <= n) - 1;
let n_6 = (1_usize..).bisect(|i| i.pow(6) <= n) - 1;
let lg = 1.max(n.next_power_of_two().trailing_zeros() as usize);
let nlg_3 = (1_usize..).bisect(|i| i.pow(3) <= n * lg) - 1;
let n_lg2_3 = (1_usize..).bisect(|i| i.pow(3) * lg.pow(2) <= n) - 1;
let mut h = vec![0];
h.extend((1..=n_2).map(|i| n / i));
h.extend((1..n / n_2).rev());
let ns = h.clone();
for hi in &mut h[1..] {
*hi -= 1;
}
let primes: Vec<_> = LinearSieve::new(n_2).primes().collect();
let mut pi = 0;
while pi < primes.len() && primes[pi] <= n_6 {
let p = primes[pi];
let pp = p * p;
for (&n_i, i) in ns[1..].iter().take_while(|&&n_i| n_i >= pp).zip(1..) {
let ip = i * p;
h[i] -= h[if ip <= n_2 { ip } else { ns.len() - n_i / p }] - pi;
}
pi += 1;
}
let thresh = if nlg_3 <= n_2 { nlg_3 } else { ns.len() - n / nlg_3 };
let mut rsq = FenwickTree::new(ns.len() - thresh);
while pi < primes.len() && primes[pi] <= n_lg2_3 {
let p = primes[pi];
let pp = p * p;
for (&n_i, i) in
ns[1..].iter().take_while(|&&n_i| n_i >= pp).zip(1..=thresh)
{
let ip = i * p;
let index = if ip <= n_2 { ip } else { ns.len() - n_i / p };
let mut sum: usize = h[index];
if index > thresh {
sum -= rsq.sum(index - thresh..);
}
h[i] -= sum - pi;
}
let mut st = vec![(p, pi)];
while let Some((cur, i)) = st.pop() {
for (cur, j) in primes[i..]
.iter()
.map(|&p_j| cur * p_j)
.take_while(|&cur| cur < n / nlg_3)
.zip(i..)
{
let index = if cur <= n_2 { ns.len() - cur } else { n / cur };
if index > thresh {
rsq.add(index - thresh, 1);
}
st.push((cur, j));
}
}
pi += 1;
}
let rsq = rsq.into_suffix_sum();
for i in 0..rsq.len() {
h[i + thresh] -= rsq[i];
}
while pi < primes.len() && primes[pi] <= n_2 {
let p = primes[pi];
let pp = p * p;
for (&n_i, i) in ns[1..].iter().take_while(|&&n_i| n_i >= pp).zip(1..) {
let ip = i * p;
h[i] -= h[if ip <= n_2 { ip } else { ns.len() - n_i / p }] - pi;
}
pi += 1;
}
h[1]
}
struct FenwickTree(Vec<usize>);
impl FenwickTree {
pub fn new(n: usize) -> Self { Self(vec![0; n]) }
pub fn sum(&self, rf: RangeFrom<usize>) -> usize {
let mut i = rf.start;
let mut res = 0;
while i < self.0.len() {
res += self.0[i];
i += i & i.wrapping_neg();
}
res
}
pub fn add(&mut self, mut i: usize, d: usize) {
while i > 0 {
self.0[i] += d;
i -= i & i.wrapping_neg();
}
}
pub fn into_suffix_sum(self) -> Vec<usize> {
let mut res = self.0;
for i in (1..res.len()).rev() {
let j = i + (i & i.wrapping_neg());
if j < res.len() {
res[i] += res[j];
}
}
res
}
}