/* This code copied from David Oswald's FastSieve.pm (Math-Prime-FastSieve-0.07)
 * "Sieve" class renamed to "_Sieve" to accommodate --write_export_ok_all option
 */

#include <vector>
using namespace std;

/* Class _Sieve below.  Perl sees it as a class named
 * "Math::Prime::FastSieve::Sieve".  The constructor is mapped to
 * "new()" within Perl, and the destructor to "DESTROY()".  All other
 * methods are mapped with the same name as declared in the class.
 *
 * Therefore, Perl sees this class approximately like this:
 *
 * package Math::Prime::FastSieve;
 *
 * sub new {
 *     my $class = shift;
 *     my $n     = shift;
 *     my $self = bless {}, $class;
 *     $self->{max_n} = n;
 *     $self->{num_primes} = 0;
 *     # Build the sieve here...
 *     # I won't bother translating it to Perl.
 *     $self->{sieve} = $primes;  // A reference to a bit vector.
 *     return $self;
 *  }
 *
 */


class _Sieve
{
    public:
        _Sieve ( int n ); // Constructor. Perl sees "new()".
        ~_Sieve(       ); // Destructor. Seen as "DESTROY()".
        bool isprime( int n ); // Test if n is prime.
        SV*  primes ( int n ); // Return all primes in an aref.
        unsigned int  nearest_le ( int n ); // Return nearest prime <= n.
        unsigned int  nearest_ge ( int n ); // Return nearest prime >= n.
        unsigned int  nth_prime  ( int n ); // Return the nth prime.
        unsigned int  count_sieve(       ); // Retrn count of primes in sieve.
        unsigned int  count_le   ( int n ); // Return number of primes <= n.
        SV*  ranged_primes( int lower, int upper );
                  // Return all primes where "lower <= primes <= upper".
    private:
        std::vector<bool>::size_type    max_n;
        unsigned int                    num_primes;
        vector<bool>* sieve;
};


// Set up a primes sieve of 0 .. n inclusive.
_Sieve::_Sieve( int n )
{
    std::vector<bool>* primes = new std::vector<bool>( n + 1, 0 );
    num_primes = 0;
    if( n < 0 ) // Trap negative n's before we start wielding unsigned ints.
        max_n = 0;
    else
    {
        max_n      = n;
        for( std::vector<bool>::size_type i = 3; i * i <= n; i+=2 )
            if( ! (*primes)[i] )
                for( std::vector<bool>::size_type k, j=i; (k=i*j) <= n; j++ )
                    (*primes)[k] = 1;
    }
    sieve = primes;
}


// Deallocate memory for primes sieve.
_Sieve::~_Sieve() {
    delete sieve;
}


// Yes or no: Is the number a prime?  Must be within the range of
// 0 through max_n (the upper limit set by the constructor).
bool _Sieve::isprime( int n )
{
    if( n < 2 || n > max_n )  return false; // Bounds checking.
    if( n == 2 )              return true;  // 2 is prime.
    if( ! ( n % 2 ) )         return false; // No other evens are prime.
    if( ! ( (*sieve)[n] ) )   return true;  // 0 bit signifies prime.
    return false;                           // default: not prime.
}


// Return a reference to an array containing the list of all primes
// less than or equal to n.  n must be within the range set in the
// constructor.
SV* _Sieve::primes( int n )
{
    AV* av = newAV();
    if( n < 2 || n > max_n ) // Logical short circuit order is significant
                             // since we're about to wield unsigned ints.
        return newRV_noinc( (SV*) av );
    av_push( av, newSVuv( 2U ) );
    num_primes = 1; // Count 2; it's prime.
    for( std::vector<bool>::size_type i = 3; i <= n; i += 2 )
        if( ! (*sieve)[i] )
            av_push( av, newSVuv( i ) );
    return newRV_noinc( (SV*) av );
}

SV* _Sieve::ranged_primes( int lower, int upper )
{
    AV* av = newAV();
    if(
        upper > max_n ||        // upper exceeds upper bound.
        lower > max_n ||        // lower exceeds upper bound.
        upper < 2     ||        // No possible primes.
        lower < 0     ||        // lower underruns bounds.
        lower > upper ||        // zero-width range.
        ( lower == upper && lower > 2 && !( lower % 2 ) ) // Even.
    )
        return newRV_noinc( (SV*) av );  // No primes possible.
    if( lower <= 2 && upper >= 2 )
        av_push( av, newSVuv( 2U ) );    // Lower limit needs to be odd
    if( lower < 3 ) lower = 3;           // Lower limit cannot < 3.
    if( ( upper - lower ) > 0 && ! ( lower % 2 ) ) lower++;
    for( std::vector<bool>::size_type i = lower; i <= upper; i += 2 )
        if( ! (*sieve)[i] )
            av_push( av, newSVuv( i ) );
    return newRV_noinc( (SV*) av );
}


// Find the nearest prime less than or equal to n.  Very fast.
unsigned int _Sieve::nearest_le( int n )
{
    // Remember that order of testing is significant; we have to
    // disqualify negative numbers before we do comparisons against
    // unsigned ints.
    if( n < 2 || n > max_n ) return 0; // Bounds checking.
    if( n == 2 ) return 2U;            // 2 is the only even prime.
    if( ! ( n % 2 ) ) n--;             // Result has to be odd.
    std::vector<bool>::size_type n_index = n;
    while( n_index > 2 )
    {
        if( ! (*sieve)[n_index] ) return n_index;
        n_index -= 2;  // Only test odds.
    }
    return 0; // We should never get here.
}


// Find the nearest prime greater than or equal to n.  Very fast.
unsigned int _Sieve::nearest_ge( int n )
{
    // Order of bounds tests IS significant.
    // Because max_n is unsigned, testing "n > max_n" for values where
    // n is negative results in n being treated as a real big unsigned value.
    // Thus we MUST handle negatives before testing max_n.
    if( n <= 2 ) return 2U;              // 2 is only even prime.
    if( n > max_n ) return 0U;           // Bounds checking.
    std::vector<bool>::size_type n_idx = n;
    if( ! ( n_idx % 2 ) ) n_idx++;              // Start with an odd number.
    while( n_idx <= max_n )
    {
        if( ! (*sieve)[n_idx] ) return n_idx;
        n_idx+=2; // Only test odds.
    }
    return 0U;   // We've run out of sieve to test.
}


// Since we're only storing the sieve (not the primes list), this is a
// linear time operation: O(n).
unsigned int _Sieve::nth_prime( int n )
{
    if( n <  1     ) return 0; // Why would anyone want the 0th prime?
    if( n >  max_n ) return 0; // There can't be more primes than sieve.
    if( n == 1     ) return 2; // We have to handle the only even prime.
    unsigned int count = 1;
    for( std::vector<bool>::size_type i = 3; i <= max_n; i += 2 )
    {
        if( ! (* sieve)[i] ) count++;
        if( count == n ) return i;
    }
    return 0;
}


// Return the number of primes in the sieve.  Once results are
// calculated, they're cached.  First time through is O(n).
unsigned int _Sieve::count_sieve ()
{
    if( num_primes > 0 ) return num_primes;
    num_primes = this->count_le( max_n );
    return num_primes;
}


// Return the number of primes less than or equal to n.  If n == max_n
// the data member num_primes will be set.
unsigned int _Sieve::count_le( int n )
{
    if( n <= 1 || n > max_n ) return 0;
    unsigned int count = 1;      // 2 is prime. Count it.
    for( std::vector<bool>::size_type i = 3; i <= n; i+=2 )
        if( !(*sieve)[i] ) count++;
    if( n == max_n && num_primes == 0 ) num_primes = count;
    return count;
}


// ---------------- For export: Not part of _Sieve class ----------------

/* Sieve of Eratosthenes.  Return a reference to an array containing all
 * prime numbers less than or equal to search_to.
 */


SV* primes( int search_to )
{
    AV* av = newAV();
    if( search_to < 2 )
        return newRV_noinc( (SV*) av ); // Return an empty list ref.
    av_push( av, newSVuv( 2U ) );
    std::vector<bool> primes( search_to + 1, 0 );
    for( std::vector<bool>::size_type i = 3; i * i <= search_to; i+=2 )
        if( ! primes[i] )
            for
            (
                    std::vector<bool>::size_type k, j = i;
                    ( k = i * j ) <= search_to;
                    j++
            )
                primes[ k ] = 1;
    for( std::vector<bool>::size_type i = 3; i <= search_to; i+=2 )
        if( ! primes[i] )
            av_push( av, newSVuv( static_cast<unsigned int>( i ) ) );
    return newRV_noinc( (SV*) av );
}



