Skip Menu |

This queue is for tickets about the Math-Prime-XS CPAN distribution.

Report information
The Basics
Id: 83774
Status: resolved
Priority: 0/
Queue: Math-Prime-XS

People
Owner: Nobody in particular
Requestors: DANAJ [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: 0.27



Subject: Possible feature: prime count
Consider adding a prime count function, so one can retrieve counts without having to get a giant array back. Here is a simple implementation for XS.xs. void xs_count_primes (number, base) unsigned long number unsigned long base PROTOTYPE: $$ INIT: unsigned char *composite = NULL; unsigned long i, n; unsigned long count = 0; PPCODE: const unsigned long square_root = sqrt (number); /* truncates */ if (base > number) XSRETURN_UV(0); Newxz (composite, number/16 + 1, unsigned char); for (n = 3; n <= square_root; n += 2) { if ((composite[n/16] & (1 << ((n/2)%8))) == 0) for (i = n*n; i <= number; i += 2*n) composite[i/16] |= 1 << ((i/2)%8); } if (base <= 2) { if (number >= 2) count++; base = 3; } base |= 1; /* make odd */ for (n = base; n <= number; n += 2) if ((composite[n/16] & (1 << ((n/2)%8))) == 0) count++; Safefree (composite); XSRETURN_UV(count); There are faster solutions and this doesn't segment so isn't fast for things like 10^14 to 10^14+1000, but those are shared by the existing sieve code. For large ranges this is faster and much less memory than getting an array back. It's about 4x faster than using the sieve from v0.26. The usual changes would need to additionally be made to lib/Math/Prime/XS.pm for exporting and documentation.
Examples of calculating this in Perl. Using the current Math::Prime::XS: use Math::Prime::XS qw/sieve_primes/; my @p = sieve_primes(500_000_000); say scalar @p; [ 26355867 in 22.4s and lots of memory ] Using code above added to Math::Prime::XS: use Math::Prime::XS qw/count_primes/; say count_primes(500_000_000); [ 26355867 in 5.0s ] Math::Prime::FastSieve does something very similar: use Math::Prime::FastSieve; my $sieve = Math::Prime::FastSieve::Sieve->new( 500_000_000 ); say $sieve->count_sieve(); [ 26355867 in 5.0s ] Math::Primality version 0.08 is much faster than previous versions, but still not really good at this. use Math::Primality qw/prime_count/; say prime_count(500_000_000) [ result in about 50 minutes ] Math::Prime::Util uses Lehmer's method and optimized phi(x,a): use Math::Prime::Util qw/prime_count/; say prime_count(500_000_000); [ 26355867 in 0.06s ] Bit::Vector can do this also: use Bit::Vector; my $vector = Bit::Vector->new(500_000_000); $vector->Primes(); say $vector->Norm(); [ 26355867 in 31.7s ] Assuming Math::Pari is compiled with Pari-2.3.5. By default the below will segfault on the init, and doesn't have primepi. use Math::PariInit qw(primes=500000000); use Math::Pari; say PARI("primepi(500000000)");' [ 26355867 in 1.44s ]
On Tue Mar 05 15:32:19 2013, DANAJ wrote: Show quoted text
> Consider adding a prime count function, so one can retrieve counts > without having to get a giant array back.
Sounds fair. Show quoted text
> Here is a simple implementation for XS.xs.
Is that about the same approach as xs_sieve_primes()? A variation can be made there with the "ALIAS:" mechanism to share code. (The xsub wrapping adds a fair bit of boilerplate bloat so often two funcs with the same style of args can helpfully share.)
CC: DANAJ [...] cpan.org
Subject: Re: [rt.cpan.org #83774] Possible feature: prime count
Date: Tue, 5 Mar 2013 14:42:37 -0800
To: bug-Math-Prime-XS [...] rt.cpan.org
From: Dana Jacobsen <dana.jacobsen [...] gmail.com>
Show quoted text
> Is that about the same approach as xs_sieve_primes()? A variation can > be made there with the "ALIAS:" mechanism to share code. (The xsub > wrapping adds a fair bit of boilerplate bloat so often two funcs with > the same style of args can helpfully share.)
Here's another idea: have xs_sieve_primes look at gimme_v and do the appropriate thing. Here's an example: if (GIMME_V == G_VOID) XSRETURN(0); if (base > number) { (GIMME_V == G_SCALAR) ? XSRETURN_UV(0) : XSRETURN_EMPTY; } Newxz (composite, number/16 + 1, unsigned char); for (n = 3; n <= square_root; n += 2) { if ((composite[n/16] & (1 << ((n/2)%8))) == 0) for (i = n*n; i <= number; i += 2*n) composite[i/16] |= 1 << ((i/2)%8); } if (GIMME_V == G_SCALAR) { if (base <= 2 && number >= 2) count++; if (base <= 2) base = 3; base |= 1; /* make odd */ for (n = base; n <= number; n += 2) if ((composite[n/16] & (1 << ((n/2)%8))) == 0) count++; XPUSHs (sv_2mortal(newSVuv(count))); } else { if (base <= 2 && number >= 2) XPUSHs (sv_2mortal(newSVuv(2))); if (base <= 2) base = 3; base |= 1; /* make odd */ for (n = base; n <= number; n += 2) if ((composite[n/16] & (1 << ((n/2)%8))) == 0) XPUSHs (sv_2mortal(newSVuv(n))); } Safefree (composite); Then make count_primes in XS.pm make the call to scalar xs_sieve_primes(). This does require changing the two tests in t/functions.t that call primes(13,13) in scalar context and expect 13 to be returned. The ALIAS: mechanism could be done just like the above but tossing the void context bit at the top and just using (ix == 2) or whatever to determine whether we want the count or array of primes returned. I think that's a fine idea and would have less API impact. On Tue, Mar 5, 2013 at 2:04 PM, Kevin Ryde via RT < bug-Math-Prime-XS@rt.cpan.org> wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=83774 > > > On Tue Mar 05 15:32:19 2013, DANAJ wrote:
> > Consider adding a prime count function, so one can retrieve counts > > without having to get a giant array back.
> > Sounds fair. >
> > Here is a simple implementation for XS.xs.
> > Is that about the same approach as xs_sieve_primes()? A variation can > be made there with the "ALIAS:" mechanism to share code. (The xsub > wrapping adds a fair bit of boilerplate bloat so often two funcs with > the same style of args can helpfully share.) > >
On Tue Mar 05 17:42:51 2013, dana.jacobsen@gmail.com wrote: Show quoted text
> have xs_sieve_primes look at gimme_v
That context stuff tends to be prone to subtle trouble. Show quoted text
> This does require changing the two tests in t/functions.t that call > primes(13,13) in scalar context and expect 13 to be returned.
Such as that :-). Ie. if someone had a call that gave one prime returned it could conceivably be in scalar context. Show quoted text
> using (ix == 2) or whatever
Yes, that'd be the ticket.