/*
 * 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;
}