File Coverage

blib/lib/Math/Prime/TiedArray.pm
Criterion Covered Total %
statement 106 141 75.1
branch 45 88 51.1
condition 35 62 56.4
subroutine 13 15 86.6
pod n/a
total 199 306 65.0


line stmt bran cond sub pod time code
1             package Math::Prime::TiedArray;
2              
3 8     8   195631 use warnings;
  8         21  
  8         276  
4 8     8   49 use strict;
  8         18  
  8         273  
5 8     8   46 use Carp;
  8         17  
  8         660  
6              
7 8     8   65 use base 'Tie::Array';
  8         28  
  8         8166  
8              
9             =head1 NAME
10              
11             Math::Prime::TiedArray - Simulate an infinite array of prime numbers
12              
13             =head1 VERSION
14              
15             Version 0.04
16              
17             =cut
18              
19             our $VERSION = '0.04';
20              
21             =head1 SYNOPSIS
22              
23             use Math::Prime::TiedArray;
24             tie my @primes, "Math::Prime::TiedArray";
25              
26             =head1 DESCRIPTION
27              
28             Allows access to an array of prime numbers, that will be extended as-needed:
29              
30             use Math::Prime::TiedArray;
31              
32             my @primes;
33             tie @primes, "Math::Prime::TiedArray";
34              
35             # print the first 100 primes:
36             print join ", ", @primes[0..99];
37              
38             # print the 200th prime:
39             print $primes[199];
40              
41             # print all the primes smaller than 500
42             while ((my $prime = shift @primes) < 500) {
43             print "$prime, ";
44             }
45              
46             =head1 OPTIONS
47              
48             =head2 precompute => number (default: 1000)
49              
50             Pre-calculate all primes smaller than 10,000:
51              
52             my @primes;
53             tie @primes, "Math::Prime::TiedArray", precompute => 10_000;
54              
55             =head2 cache => path
56              
57             Use a persistant cache:
58              
59             my @primes;
60             tie @primes, "Math::Prime::TiedArray", cache => "/path/to/cache.dbm"
61              
62             =head2 extend_step => number (default: 10)
63              
64             How many new primes should be calculated when the cache runs out.
65              
66             =head2 extend_ceiling => number
67              
68             Set a limit, triggering an exception (croak) if an attempt is made to find a
69             prime larger than the ceiling.
70              
71             =head2 debug => level (defualt: 0)
72              
73             Output debug messages:
74             0 - none
75             1 - progress updates for atkin
76             2 - prime calculations
77             3 - tie API
78             4 - internal progress for atkin
79              
80             =cut
81              
82             # Called by: tie @array, "Math::Prime::TiedArray", option => value, options => value
83             sub TIEARRAY {
84 5     5   72 my ( $class, %options ) = @_;
85              
86 5   50     30 my $self = { _options => \%options || {}, };
87              
88 5         18 $self = bless $self, $class;
89              
90 5         27 $self->_verify_options;
91 5         23 $self->_init;
92              
93 5         44 return $self;
94             }
95              
96             # retrieve the nth prime, calculating it if needed
97             sub FETCH {
98 65     65   5928 my ( $self, $idx ) = @_;
99 65 50       177 warn "FETCH(@_)\n" if $self->{_options}{debug} > 2;
100              
101 65 100       150 if ( $idx + 1 > $self->{_cache_size} ) {
102 6         26 $self->EXTEND( $idx + 1 );
103             }
104              
105 65         349 return $self->{_cache}[ $idx + 1 ];
106             }
107              
108             # report how many primes have been calculated (scalar @array)
109             sub FETCHSIZE {
110 1     1   3 my ($self) = @_;
111 1 50       5 warn "FETCHSIZE(@_) @{$self->{_cache}}\n" if $self->{_options}{debug} > 2;
  0         0  
112              
113 1         12 return $self->{_cache_size};
114             }
115              
116             # we don't allow modifying the list of primes
117 0     0   0 sub STORE { croak "Can't modify the list of primes!" }
118 0     0   0 sub STORESIZE { croak "Can't modify the list of primes!" }
119              
120             # pretend we allow to shift the next prime, but don't actually modify the array
121             sub SHIFT {
122 29     29   117 my ($self) = @_;
123 29 50       87 warn "SHIFT(@_)\n" if $self->{_options}{debug} > 2;
124              
125 29         77 return $self->FETCH( $self->{_shift_pointer}++ );
126             }
127              
128             # need to calculate more primes
129             sub EXTEND {
130 6     6   13 my ( $self, $count ) = @_;
131 6 50       22 warn "EXTEND(@_)\n" if $self->{_options}{debug} > 2;
132              
133 6 50       18 return if $count <= $self->{_cache_size};
134              
135             # we can't be sure what we need to raise the celing to, but we can guess
136             # pi(x) < x/(ln(x)-4) for x >= 55
137 6         17 my $needed = $self->{_options}{extend_step};
138              
139             # now that we have an estimate of what the new limit should be, let's try it
140             # and if we fail, try harder.
141 6         19 while ( $self->{_cache_size} < $count ) {
142 71         98 my $new_limit = $self->{_cache_size} + $needed;
143 71 100       145 $new_limit = 55 if ( $new_limit < 55 );
144 71         223 $new_limit = int( $new_limit / ( log($new_limit) - 4 ) );
145 71         168 $self->_atkin($new_limit);
146 71         257 $needed *= 2;
147             }
148             }
149              
150             # when going out of scope, clean up
151             sub DESTROY {
152 5     5   4412 my ($self) = @_;
153 5 50       30 warn "DESTROY(@_)\n" if $self->{_options}{debug} > 2;
154              
155 5 50       13155 if ( $self->{_options}{cache} ) {
156 0 0       0 untie $self->{_cache}
157             or carp "Failed to untie $self->{_options}{cache}: $!";
158             }
159             }
160              
161             # verify the options given are known, and are not insane
162             sub _verify_options {
163 5     5   11 my ($self) = @_;
164              
165             # reset each iterator
166 5         7 keys %{ $self->{_options} };
  5         49  
167 5         12 while ( my ( $key, $value ) = each %{ $self->{_options} } ) {
  8         44  
168 3 100       17 if ( $key eq 'precompute' ) {
    50          
    50          
    50          
    50          
169 2 50 33     33 carp 'precompute no given a positive integer!'
170             unless $value =~ /^\d+$/
171             and $value > 0;
172             }
173             elsif ( $key eq 'cache' ) {
174 0 0 0     0 carp 'cache not writable!' if -e $value and not -w $value;
175             }
176             elsif ( $key eq 'debug' ) {
177 0 0       0 carp 'ignoring invalid debug value!' if $value =~ /\D/;
178             }
179             elsif ( $key eq 'extend_step' ) {
180 0 0       0 carp 'ignoring invalid extend_step value!' if $value =~ /\D/;
181             }
182             elsif ( $key eq 'extend_ceiling' ) {
183 1 50       7 carp 'ignoring invalid extend_ceiling value!' if $value =~ /\D/;
184             }
185             else {
186 0         0 carp "ignoring unknown option '$key'";
187             }
188             }
189             }
190              
191             # if needed, connect to the cache and/or precompute values
192             sub _init {
193 5     5   11 my ($self) = @_;
194              
195             # default values
196 5   50     44 $self->{_options}{debug} ||= 0;
197 5   100     25 $self->{_options}{precompute} ||= 1000;
198 5   50     30 $self->{_options}{extend_step} ||= 10;
199              
200 5 50       18 if ( $self->{_options}{cache} ) {
201 0         0 require DB_File;
202 0         0 DB_File->import;
203              
204 0         0 my @cache;
205 0 0       0 tie @cache, "DB_File", $self->{_options}{cache},
206             &DB_File::O_CREAT | &DB_File::O_RDWR, 0644, $DB_File::DB_RECNO
207             or carp "Failed to tie $self->{_cache}: $!";
208 0         0 $self->{_cache} = \@cache;
209              
210             # sanity check a loaded cache, or init an empty cache
211 0 0 0     0 unless ($self->{_cache}
      0        
      0        
      0        
      0        
212             and ref $self->{_cache} eq 'ARRAY'
213             and defined $self->{_cache}[1]
214             and $self->{_cache}[1] == 2
215             and defined $self->{_cache}[2]
216             and $self->{_cache}[2] == 3 )
217             {
218 0         0 carp "invalid or empty cache - initializing...";
219 0         0 untie @{ $self->{_cache} };
  0         0  
220 0         0 unlink $self->{_cache};
221              
222 0 0       0 tie @cache, "DB_File", $self->{_options}{cache},
223             &DB_File::O_CREAT | &DB_File::O_RDWR, 0644, $DB_File::DB_RECNO
224             or carp "Failed to tie $self->{_cache}: $!";
225 0         0 $self->{_cache} = \@cache;
226              
227 0         0 $self->{_cache}[0] = 3;
228 0         0 $self->{_cache}[1] = 2;
229 0         0 $self->{_cache}[2] = 3;
230             }
231             }
232             else {
233 5         23 $self->{_cache} = [ 3, 2, 3 ];
234             }
235              
236             # record the largest prime found so far
237 5         17 $self->{_max_prime} = $self->{_cache}[-1];
238              
239             # and store/restore the largest number we counted to
240 5         13 $self->{_limit} = $self->{_cache}[0];
241              
242             # and how many primes we have
243 5         10 $self->{_cache_size} = $#{ $self->{_cache} };
  5         14  
244              
245             # prepopulate the sieve with the known primes
246 10         41 $self->{_sieve} =
247 5         16 { map { $_ => 1 } @{ $self->{_cache} }[ 1 .. $self->{_cache_size} ] };
  5         17  
248              
249 5 100 66     37 if ( $self->{_options}{extend_ceiling}
250             and $self->{_options}{extend_ceiling} < 7500 )
251             {
252 1         3 $self->{_options}{precompute} = $self->{_options}{extend_ceiling};
253             }
254              
255 5 100 66     52 if ( $self->{_options}{precompute}
256             and $self->{_options}{precompute} > $self->{_max_prime} )
257             {
258 4         19 $self->_atkin( $self->{_options}{precompute} );
259             }
260              
261             # a pointer to the next prime to be read by shift
262 5         18 $self->{_shift_pointer} = 0;
263             }
264              
265             # implement the sieve of Atkin - useful to calculating all the primes up to a
266             # given number: http://en.wikipedia.org/wiki/Sieve_of_Atkin
267             sub _atkin {
268 75     75   119 my ( $self, $limit ) = @_;
269 75 50       200 warn "DEBUG2: Calculating primes up to $limit\n"
270             if $self->{_options}{debug} > 1;
271              
272 75 100       205 return if $limit <= $self->{_limit};
273              
274 17 50 66     79 croak "Cannot extend beyond $self->{_options}{extend_ceiling}!"
275             if $self->{_options}{extend_ceiling}
276             and $limit > $self->{_options}{extend_ceiling};
277              
278             # put in candidate primes:
279             # integers which have an odd number of representations by certain
280             # quadratic forms
281              
282 17         68 my $sqrt = sqrt($limit);
283 17         30 my $progress = 0;
284 17         50 foreach my $x ( 1 .. $sqrt ) {
285 1484 50       3704 if ( $self->{_options}{debug} > 0 ) {
286 0         0 my $x_p = int( $x / $sqrt * 100 / 3 );
287 0 0       0 if ( $x_p > $progress ) {
288 0         0 warn sprintf "DEBUG1: ($limit) %d%%\n", $x_p;
289 0         0 $progress = $x_p;
290             }
291             }
292              
293 1484         3341 foreach my $y ( 1 .. $sqrt ) {
294              
295 202072 50       445253 warn "DEBUG4: $x, $y\n" if $self->{_options}{debug} > 3;
296 202072         302435 my $n = 3 * $x**2 - $y**2;
297 202072 100 100     825343 if ( $n > $self->{_max_prime}
      100        
      100        
298             and $x > $y
299             and $n <= $limit
300             and $n % 12 == 11 )
301             {
302 5053         17775 $self->{_sieve}{$n} = not $self->{_sieve}{$n};
303             }
304              
305 202072         261946 $n = 3 * $x**2 + $y**2;
306 202072 100 100     812054 if ( $n > $self->{_max_prime} and $n <= $limit and $n % 12 == 7 ) {
      100        
307 6029         25126 $self->{_sieve}{$n} = not $self->{_sieve}{$n};
308             }
309              
310 202072         257021 $n = 4 * $x**2 + $y**2;
311 202072 100 100     975537 if ( $n > $self->{_max_prime}
      100        
      66        
312             and $n <= $limit
313             and ( $n % 12 == 1 or $n % 12 == 5 ) )
314             {
315 13853         62736 $self->{_sieve}{$n} = not $self->{_sieve}{$n};
316             }
317             }
318             }
319              
320             # eliminate composites by sieving
321 17         64 foreach my $n ( 5 .. $sqrt ) {
322 1416 50       3110 if ( $self->{_options}{debug} > 0 ) {
323 0         0 my $x_p = 33 + int( $n / $sqrt * 100 / 3 );
324 0 0       0 if ( $x_p > $progress ) {
325 0         0 warn sprintf "DEBUG1: ($limit) %d%%\n", $x_p;
326 0         0 $progress = $x_p;
327             }
328             }
329              
330 1416 100       3971 next unless $self->{_sieve}{$n};
331 348 50       779 warn "DEBUG4: eliminating multiples of $n**2\n"
332             if $self->{_options}{debug} > 3;
333              
334 348         751 my $k = int($self->{_max_prime}/$n**2) * $n**2;
335 348         729 while ( $k <= $limit ) {
336 7506         19707 $self->{_sieve}{$k} = 0;
337 7506         15115 $k += $n**2;
338             }
339 348 50       1070 warn "DEBUG4: done\n" if $self->{_options}{debug} > 3;
340             }
341              
342             # save the found primes in our cache
343 17         65 foreach my $n ( 1+$self->{_max_prime} .. $limit ) {
344 79797 50       180081 if ( $self->{_options}{debug} > 0 ) {
345 0         0 my $x_p = 66 + int( $n / $limit * 100 / 3 );
346 0 0       0 if ( $x_p > $progress ) {
347 0         0 warn sprintf "DEBUG1: ($limit) %d%%\n", $x_p;
348 0         0 $progress = $x_p;
349             }
350             }
351              
352 79797 100       246966 next unless $self->{_sieve}{$n};
353 8252 50       19111 warn "DEBUG3: caching new prime $n\n" if $self->{_options}{debug} > 2;
354 8252         8523 push @{ $self->{_cache} }, $n;
  8252         22653  
355             }
356              
357 17         123 $self->{_max_prime} = $self->{_cache}[-1];
358 17         59 $self->{_cache}[0] = $self->{_limit} = $limit;
359 17         26 $self->{_cache_size} = $#{ $self->{_cache} };
  17         86  
360             }
361              
362             =head1 AUTHOR
363              
364             Dan Boger, C<< >>
365              
366             =head1 BUGS
367              
368             Please report any bugs or feature requests to
369             C, or through the web interface at
370             L.
371             I will be notified, and then you'll automatically be notified of progress on
372             your bug as I make changes.
373              
374             =head1 SUPPORT
375              
376             You can find documentation for this module with the perldoc command.
377              
378             perldoc Math::Prime::TiedArray
379              
380             You can also look for information at:
381              
382             =over 4
383              
384             =item * AnnoCPAN: Annotated CPAN documentation
385              
386             L
387              
388             =item * CPAN Ratings
389              
390             L
391              
392             =item * RT: CPAN's request tracker
393              
394             L
395              
396             =item * Search CPAN
397              
398             L
399              
400             =back
401              
402             =head1 COPYRIGHT & LICENSE
403              
404             Copyright 2007 Dan Boger, all rights reserved.
405              
406             This program is free software; you can redistribute it and/or modify it
407             under the same terms as Perl itself.
408              
409             =cut
410              
411             1; # End of Math::Prime::TiedArray