/*
* This program generates prime numbers fairly quickly using a sieve.
* It prints prime numbers until the first of
* o You get tired and kill it.
* o It runs out of memory and crashes.
* o The integers start to overflow, in which case you have a computer
* with a whole lot of memory.
*/
#include <vector>
#include <string>
#include <iostream>
#include <queue>
using namespace std;
/*
* Prime eliminator. It is essentially a pair of numbers, the first prime
* and the second a multiple of the first. The program keeps a collection
* of these in a heap to eliminate candidate primes.
*/
class Eliminator
{
public:
// Create at a prime, ready to eliminate the first multiple thereof.
Eliminator(int p): base(p), target(2*p) { }
// See what number is eliminated next.
int eliminates() const
{
return target;
}
// Advance to the next elimination.
void advance()
{
target += base;
}
// Compare Eliminators according to their eliminated value.
bool operator<(const Eliminator &e) const
{
return target > e.target; // Note: Inverted sense
}
bool operator==(const Eliminator &e) const
{
return target == e.target;
}
void print() const
{
cout << "(" << base << ", " << target << ")";
}
private:
// The base is a prime number, and the target is the next multiple
// which it has not yet eliminated.
int base, target;
};
// This is a collection of Eliminators organized as a heap, so the next largest
// one can be found quickly.
class EliminatorCollection
{
public:
// What does the heap eliminate just now?
int eliminates() const
{
return heap.top().eliminates();
}
// Insert a new prime eliminator into the collection.
void insert(int i)
{
heap.push(Eliminator(i));
}
// Move on to the next elimination target.
void advance()
{
// Get the current eliminee.
int you_die = eliminates();
// Remove that and any other which eliminate the same thing,
// advance it, and stick it back on.
while(!heap.empty() && heap.top().eliminates() == you_die) {
Eliminator e = heap.top();
heap.pop();
e.advance();
heap.push(e);
}
}
private:
// This holds the heap data structure.
priority_queue<Eliminator> heap;
};
/*
* Trivial class to output each number with appropriate line breaking.
*/
class OutLiner {
private:
int m_outlen; // Current output line length
const int m_linemax; // Maximum output line length.
public:
OutLiner(int linemax): m_linemax(linemax), m_outlen(0) { }
void outprime(int n)
{
// Convert the number to string, so we know how long
// it will be. Then see if it fits on the line.
string strint = to_string(n);
if(m_outlen + strint.length() + 1 > m_linemax) {
// Nope. End the line has zero on it.
cout << endl;
m_outlen = 0;
} else {
// Yep. Add a space and count it.
cout << " ";
m_outlen++;
}
// Print it, and update the count.
cout << strint;
m_outlen += strint.length();
}
};
/*
* Generate primes until you get tired of it (or run out of
* ints). This uses a form of the seive method which
* eliminates non-prime candidate primes by checking if they are a
* multiple of any previous prime found. This is done efficiently by
* keeping the set of multiples of previously-found primes in a heap.
* A heap is a data structure which can quickly find the smallest
* number it contains.
*/
int main()
{
OutLiner lpr(75);
lpr.outprime(2);
EliminatorCollection ec;
ec.insert(2);
for(int m=3; 1; m++)
{
if(m == ec.eliminates())
ec.advance();
else {
//cout << m << endl;
lpr.outprime(m);
ec.insert(m);
}
}
cout << endl;
}