#include <iostream>
#include <fstream>
#include <algorithm>
#include <list>
#include <vector>
#include <cmath>
#include <cassert>
using namespace std;
class PrimeGenerator {
public:
PrimeGenerator() {
normalSolution();
}
int nextPrime() {
int p = m_primes.front();
m_primes.pop_front();
// https://e...content-available-to-author-only...a.org/wiki/Prime-counting_function
// pi(m_size) > m_size / ln(m_size) for m_size >= 17 (which starts at 100 so its true)
// pi(m_size / 4) <= 1.25506 * (m_size / 4) / ln(m_size / 4) <= 1.25506 * (m_size / 4) / ln(m_size ^ 0.5) = 1.25506 * (m_size / 2) / ln(m_size)
// so the number of primes bigger than m_size / 4 but lower than or equal to m_size is
// > m_size / ln(m_size) - 1.25506 * (m_size / 2) / ln(m_size) = m_size / ln(m_size) * 0.37247
// so doSomeSteps is called at least m_size / ln(m_size) * 0.37247
// and because doSomeSteps does O(ln(m_size)) operations and p > m_size / 4 it does O(ln p) operations
if (p > m_size / 4)
doSomeSteps();
return p;
}
private:
// iterator, meant to hold a state in normalSolution
struct Iterator {
int step; // 0 -> filling with 0, 1 -> doing the sieve, 2 -> removing primes
int i;
list<int>::iterator p;
} m_iterator;
void normalSolution() {
m_size = START_SIZE;
m_calls = ceil(log(m_size) * 43);
m_lowest_prime = vector<int>(m_size + 1, 0);
// Sieve of erathostenes in linear time,
// this algorithm makes N + pi(N) + N operations, upper bounded by 3N
// the if is true in pi(N) cases <= N
// we update at most once each element => N operations
// the for condition will have to break one for each element => N operations
for (int i = 2; i <= m_size; ++i) {
if (m_lowest_prime[i] == 0) {
m_lowest_prime[i] = i;
m_primes.push_back(i);
}
for (auto &prime : m_primes) {
if (prime > m_lowest_prime[i])
break;
if (i * prime > m_size)
break;
m_lowest_prime[prime * i] = prime;
}
}
m_lowest_prime_second = vector<int>(m_size * 4 + 1);
m_iterator = {0, 0, m_primes_second.begin()};
}
// to do the sieve on 4 * m_size we need 16 * m_size operations
// 4 * m_size to fill the array with 0
// 8 * m_size to simulate the sieve in linear time
// pi(m_size * 4) to remove the already printed primes, <= 4 * m_size
// and we have at least m_size / ln(m_size) * 0.37247 calls, so to distribute them all
// we need to do at least 16ln(m_size) / 0.37247 ~ 43 ln(m_size) which is O(ln(m_size))
void doSomeSteps() {
int operations = m_calls;
for (int i = 0; i < operations; ++i)
doStep();
if (done())
swap();
}
// complexity of this is O(1)
void doStep() {
if (m_iterator.step == 0) { // filling with 0, this will happen exactly 4 * m_size times
m_lowest_prime_second[m_iterator.i++] = 0;
if (m_iterator.i > m_size * 4)
m_iterator = {1, 2, m_primes_second.begin()};
return;
}
if (m_iterator.step == 1) { // doing the actual sieve
// when its prime, this will be at most pi(4 * m_size) <= 4 * m_size
if (m_lowest_prime_second[m_iterator.i] == 0) {
m_lowest_prime_second[m_iterator.i] = m_iterator.i;
m_primes_second.push_back(m_iterator.i);
m_iterator.p = m_primes_second.begin();
return;
}
// break condition, this will happen exactly 4 * m_size
if (m_iterator.p == m_primes_second.end()
|| *m_iterator.p > m_lowest_prime_second[m_iterator.i]
|| *m_iterator.p * m_iterator.i > m_size * 4) {
m_iterator = {1, m_iterator.i + 1, m_primes_second.begin()};
if (m_iterator.i > 4 * m_size)
m_iterator = {2, 0, m_primes_second.end()};
} else {
m_lowest_prime_second[*m_iterator.p * m_iterator.i] = *m_iterator.p;
m_iterator.p++;
}
return;
}
if (m_iterator.step == 2) { // removing primes
if (m_primes_second.front() <= m_size)
m_primes_second.pop_front();
else
m_iterator.step = 3;
return;
}
}
// when we have finished creating the second sieve (which is 4 times bigger) and exhausted the primes from the first
bool done() {
return m_iterator.step == 3 && m_primes.begin() == m_primes.end();
}
void swap() {
::swap(m_lowest_prime, m_lowest_prime_second);
::swap(m_primes, m_primes_second);
m_size *= 4;
m_calls = ceil(43 * log(m_size));
m_lowest_prime_second = vector<int>(4 * m_size + 1);
m_iterator = {0, 0, m_primes.begin()};
}
list<int> m_primes, m_primes_second;
vector<int> m_lowest_prime, m_lowest_prime_second;
int m_size, m_calls;
static const int START_SIZE = 100;
};
vector<int> primes(int N) {
vector<bool> sieve(N + 1, true);
vector<int> primes;
for (int i = 2; i * i <= N; ++i)
if (sieve[i] == true) {
for (int j = i * i; j <= N; j += i)
sieve[j] = false;
}
for (int i = 2; i <= N; ++i)
if (sieve[i] == true)
primes.push_back(i);
return primes;
}
vector<int> generator(int P) {
vector<int> primes;
PrimeGenerator G;
while (P--)
primes.push_back(G.nextPrime());
return primes;
}
int main() {
ios::sync_with_stdio(false);
int N = 10 * 1000 * 1000;
auto normalSieve = primes(N);
auto smartGenerator = generator(normalSieve.size());
assert(normalSieve == smartGenerator);
}