File Coverage

blib/lib/Math/Prime/Util/PrimeArray.pm
Criterion Covered Total %
statement 92 106 86.7
branch 21 34 61.7
condition 13 15 86.6
subroutine 17 22 77.2
pod n/a
total 143 177 80.7


line stmt bran cond sub pod time code
1             package Math::Prime::Util::PrimeArray;
2 2     2   112701 use strict;
  2         4  
  2         74  
3 2     2   9 use warnings;
  2         4  
  2         211  
4              
5             BEGIN {
6 2     2   7 $Math::Prime::Util::PrimeArray::AUTHORITY = 'cpan:DANAJ';
7 2         50 $Math::Prime::Util::PrimeArray::VERSION = '0.74';
8             }
9              
10             # parent is cleaner, and in the Perl 5.10.1 / 5.12.0 core, but not earlier.
11             # use parent qw( Exporter );
12 2     2   12 use base qw( Exporter );
  2         3  
  2         781  
13             our @EXPORT_OK = qw(@primes @prime @pr @p $probj);
14             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
15              
16             # It would be nice to do this dynamically.
17             our(@primes, @prime, @pr, @p, $probj);
18             sub import {
19 2 50   2   32 tie @primes, __PACKAGE__ if grep { $_ eq '@primes' } @_;
  2         16  
20 2 50       6 tie @prime , __PACKAGE__ if grep { $_ eq '@prime' } @_;
  2         9  
21 2 50       5 tie @pr , __PACKAGE__ if grep { $_ eq '@pr' } @_;
  2         10  
22 2 50       5 tie @p , __PACKAGE__ if grep { $_ eq '@p' } @_;
  2         8  
23 2 50       5 $probj = __PACKAGE__->TIEARRAY if grep { $_ eq '$probj' } @_;
  2         13  
24 2         3346 goto &Exporter::import;
25             }
26              
27 2     2   1297 use Math::Prime::Util qw/nth_prime nth_prime_upper nth_prime_lower primes prime_precalc next_prime prev_prime/;
  2         8  
  2         15  
28 2     2   1249 use Math::Prime::Util::MemFree;
  2         8  
  2         120  
29 2     2   1298 use Tie::Array;
  2         3138  
  2         120  
30 2     2   16 use Carp qw/carp croak confess/;
  2         4  
  2         152  
31              
32 2     2   13 use constant SEGMENT_SIZE => 80_000;
  2         5  
  2         222  
33 2     2   16 use constant HALFSEG => SEGMENT_SIZE >> 1;
  2         4  
  2         133  
34 2     2   13 use constant ALLOW_SKIP => 3_000; # Sieve if skipping up to this
  2         4  
  2         13748  
35              
36             sub TIEARRAY {
37 4     4   208350 my $class = shift;
38 4 50       21 if (@_) {
39 0         0 croak "usage: tie ARRAY, '" . __PACKAGE__ . "";
40             }
41 4         38 return bless {
42             # used to keep track of shift
43             SHIFTINDEX => 0,
44             # Remove all extra prime memory when we go out of scope
45             MEMFREE => Math::Prime::Util::MemFree->new,
46             # A chunk of primes
47             PRIMES => [2, 3, 5, 7, 11, 13, 17],
48             # What's the index of the first one?
49             BEG_INDEX => 0,
50             # What's the index of the last one?
51             END_INDEX => 6,
52             # positive = forward, negative = backward, 0 = random
53             ACCESS_TYPE => 0,
54             }, $class;
55             }
56 0     0   0 sub STORE { carp "You cannot write to the prime array"; }
57 0     0   0 sub DELETE { carp "You cannot write to the prime array"; }
58 0     0   0 sub STORESIZE { carp "You cannot write to the prime array"; }
59 0     0   0 sub EXISTS { 1 }
60             #sub EXTEND { my $self = shift; my $count = shift; prime_precalc($count); }
61 0     0   0 sub EXTEND { 1 }
62 2     2   5 sub FETCHSIZE { 0x7FFF_FFFF } # Even on 64-bit
63             # Simple FETCH:
64             # sub FETCH { return nth_prime($_[1]+1); }
65              
66             sub FETCH {
67 2067     2067   31371 my ($self, $index) = @_;
68 2067 50       4280 $index = 0xFFFFFFFF + $index + 1 if $index < 0;
69 2067         3445 $index += $self->{SHIFTINDEX}; # take into account any shifts
70 2067         3323 my $begidx = $self->{BEG_INDEX};
71 2067         3303 my $endidx = $self->{END_INDEX};
72              
73 2067 100 100     7878 if ( $index < $begidx || $index > $endidx ) {
74              
75 12 100 100     79 if ($index > $endidx && $index < $endidx + ALLOW_SKIP) { # Forward iteration
    100 100        
76              
77 4         11 $self->{ACCESS_TYPE}++;
78 4 100 100     19 if ($self->{ACCESS_TYPE} > 2 || $index > $endidx+1) {
79 2         4 my $prlen = scalar @{$self->{PRIMES}};
  2         6  
80             # Keep up to HALFSEG elements from the previous array
81 2 50       10 if ($prlen > HALFSEG) {
82 0         0 @{$self->{PRIMES}} = @{$self->{PRIMES}}[-(HALFSEG) .. -1];
  0         0  
  0         0  
83 0         0 $begidx += $prlen - HALFSEG;
84             }
85             # Add HALFSEG elements to the end
86 2         68 my $end_prime = nth_prime_upper($index + HALFSEG);
87 2         6 push @{$self->{PRIMES}}, @{primes($self->{PRIMES}->[-1]+1, $end_prime)};
  2         7  
  2         14926  
88             } else {
89 2         4 push @{$self->{PRIMES}}, next_prime($self->{PRIMES}->[-1]);
  2         41  
90             }
91              
92             } elsif ($index < $begidx && $index > $begidx - ALLOW_SKIP) { # Bk iteration
93              
94 2         8 $self->{ACCESS_TYPE}--;
95 2 50 33     27 if ($self->{ACCESS_TYPE} < -2 || $index < $begidx-1) {
96 2         5 my $prlen = scalar @{$self->{PRIMES}};
  2         5  
97 2 50       9 my $beg_prime = $index <= HALFSEG
98             ? 2 : nth_prime_lower($index - HALFSEG);
99 2         4 unshift @{$self->{PRIMES}}, @{primes($beg_prime, $self->{PRIMES}->[0]-1)};
  2         5  
  2         683  
100 2         101 my $prnewlen = scalar @{$self->{PRIMES}};
  2         10  
101 2         5 $begidx -= $prnewlen - $prlen;
102 2 50       10 $#{$self->{PRIMES}} = SEGMENT_SIZE-1 if $prnewlen > SEGMENT_SIZE;
  0         0  
103             } else {
104 0         0 $begidx--;
105 0         0 unshift @{$self->{PRIMES}}, prev_prime($self->{PRIMES}->[0]);
  0         0  
106             }
107              
108             } else { # Random access
109              
110 6         24 $self->{ACCESS_TYPE} = int($self->{ACCESS_TYPE} / 2);
111             # TODO: we are destroying the primes array, just to get $begidx set.
112             # We should instead have an additional single last-index-result.
113             # return nth_prime($index+1);
114 6         12 $begidx = $index;
115 6         864 $self->{PRIMES} = [nth_prime($begidx+1)];
116              
117             }
118 12         1262 $self->{BEG_INDEX} = $begidx;
119 12         26 $self->{END_INDEX} = $begidx + scalar @{$self->{PRIMES}} - 1;
  12         45  
120             }
121 2067         6888 return $self->{PRIMES}->[ $index - $begidx ];
122             }
123              
124             # Fake out shift and unshift
125             sub SHIFT {
126 5     5   24 my $self = shift;
127 5         19 my $head = $self->FETCH(0);
128 5         15 $self->{SHIFTINDEX}++;
129 5         45 $head;
130             }
131             sub UNSHIFT {
132 2     2   917 my ($self, $shiftamount) = @_;
133 2 50       9 $shiftamount = 1 unless defined $shiftamount;
134             $self->{SHIFTINDEX} = ($shiftamount >= $self->{SHIFTINDEX})
135             ? 0
136 2 50       10 : $self->{SHIFTINDEX} - $shiftamount;
137 2         9 $self->FETCHSIZE;
138             }
139             # CLEAR this
140             # PUSH this, LIST
141             # POP this
142             # SPLICE this, offset, len, LIST
143             # DESTROY this
144             # UNTIE this
145              
146             1;
147              
148             __END__