File Coverage

blib/lib/Math/NumSeq/Primes.pm
Criterion Covered Total %
statement 89 95 93.6
branch 16 22 72.7
condition 5 6 83.3
subroutine 20 20 100.0
pod 4 4 100.0
total 134 147 91.1


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2013, 2014 Kevin Ryde
2              
3             # This file is part of Math-NumSeq.
4             #
5             # Math-NumSeq is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-NumSeq is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-NumSeq. If not, see .
17              
18             package Math::NumSeq::Primes;
19 11     11   12750 use 5.004;
  11         35  
  11         435  
20 11     11   58 use strict;
  11         17  
  11         364  
21 11     11   7644 use POSIX ();
  11         75666  
  11         338  
22 11     11   7755 use Math::Prime::XS 0.23 'is_prime'; # version 0.23 fix for 1928099
  11         981054  
  11         706  
23              
24 11     11   97 use vars '$VERSION', '@ISA';
  11         22  
  11         641  
25             $VERSION = 71;
26 11     11   1540 use Math::NumSeq;
  11         21  
  11         585  
27             @ISA = ('Math::NumSeq');
28             *_is_infinite = \&Math::NumSeq::_is_infinite;
29              
30             # uncomment this to run the ### lines
31             # use Smart::Comments;
32              
33              
34             # use constant name => Math::NumSeq::__('Prime Numbers');
35 11     11   55 use constant description => Math::NumSeq::__('The prime numbers 2, 3, 5, 7, 11, 13, 17, etc.');
  11         21  
  11         52  
36 11     11   55 use constant characteristic_increasing => 1;
  11         20  
  11         465  
37 11     11   69 use constant characteristic_integer => 1;
  11         19  
  11         412  
38 11     11   51 use constant values_min => 2;
  11         19  
  11         445  
39 11     11   52 use constant i_start => 1;
  11         18  
  11         501  
40              
41             #------------------------------------------------------------------------------
42             # cf A010051 - characteristic boolean 0 or 1 according as N is prime
43             # A051006 characteristic as binary fraction, in decimal
44             # A051007 characteristic as binary fraction, continued fraction
45             # A000720 - pi(n) num primes <= n
46             # A018252 - the non-primes
47             # A002476 - primes 3k+1, which is also 6k+1
48             #
49 11     11   54 use constant oeis_anum => 'A000040'; # primes
  11         23  
  11         455  
50              
51             #------------------------------------------------------------------------------
52              
53 11     11   57 use constant 1.02; # for leading underscore
  11         212  
  11         588  
54 11         16 use constant _MAX_PRIME_XS => do {
55 11         18 my $umax = POSIX::UINT_MAX() / 2;
56             # if ($umax > 0x8000_0000) {
57             # $umax = 0x8000_0000;
58             # }
59 11         5368 $umax;
60 11     11   53 };
  11         20  
61              
62             sub rewind {
63 98     98 1 746 my ($self) = @_;
64 98         622 $self->{'i'} = $self->i_start;
65 98         334 $self->{'array_lo'} = 1;
66 98         161 $self->{'array_hi'} = 1;
67 98         151 @{$self->{'array'}} = ();
  98         386  
68             }
69             # needs a prime_count() for arbitrary seek
70             #
71             # sub _UNTESTED__seek_to_value {
72             # my ($self, $value) = @_;
73             # my $array = $self->{'array'};
74             # if (@array) {
75             # if ($value >= $array->[0] && $value <= $array->[-1]) {
76             # # seek forward within $array
77             # while ($value > $array->[0]) {
78             # shift @$array;
79             # $self->{'i'}++;
80             # }
81             # return;
82             # }
83             # }
84             # $value = int($value);
85             # if ($value > _MAX_PRIME_XS) {
86             # # past limit
87             # $self->{'array'} = undef;
88             # return;
89             # }
90             # $self->{'i'} = _primes_count(0,$value);
91             # $self->{'array_lo'} = 0;
92             # $self->{'array_hi'} = $value-1;
93             # @{$self->{'array'}} = ();
94             # }
95              
96             sub next {
97 1606     1606 1 5046 my ($self) = @_;
98              
99 1606         2278 while (! @{$self->{'array'}}) {
  1691         8015  
100             # fill array
101 85         179 my $lo = $self->{'array_lo'};
102 85         220 my $hi = $self->{'array_hi'};
103              
104 85         184 $lo = $self->{'array_lo'} = $hi+1;
105 85 50       243 if ($lo > _MAX_PRIME_XS) {
106 0         0 return;
107             }
108              
109 85         189 my $len = int ($lo / 2);
110 85 50       203 if ($len > 100_000) {
111 0         0 $len = 100_000;
112             }
113              
114 85         205 $hi = $lo + $len;
115 85 100       205 if ($hi < 500) {
116 73         1643 $hi = 500;
117             }
118 85 50       212 if ($hi > _MAX_PRIME_XS) {
119 0         0 $hi = _MAX_PRIME_XS;
120             }
121 85         166 $self->{'array_hi'} = $hi;
122              
123 85         249 @{$self->{'array'}} = _primes_list ($lo, $hi);
  85         1015165  
124             }
125 1606         3955 return ($self->{'i'}++, shift @{$self->{'array'}});
  1606         7385  
126             }
127              
128              
129              
130             sub _primes_list {
131 246     246   663 my ($lo, $hi) = @_;
132             ### _my_primes_list: "$lo to $hi"
133 246 50       703 if ($lo < 0) {
134 0         0 $lo = 0;
135             }
136 246 50       578 if ($hi > _MAX_PRIME_XS) {
137 0         0 $hi = _MAX_PRIME_XS;
138             }
139              
140 246 50       513 if ($hi < $lo) {
141             # Math::Prime::XS errors out if hi
142 0         0 return;
143             }
144 246         1200 return Math::Prime::XS::sieve_primes ($lo, $hi);
145             }
146              
147             sub pred {
148 5271     5271 1 48328 my ($self, $value) = @_;
149             ### pred(): "$value"
150              
151 5271 100 66     14039 if (_is_infinite($value) || $value > 0xFFFF_FFFF) {
152 2         481 return undef;
153             }
154 5269 100 100     22712 if ($value != int($value) || $value < 0) {
155 2031         13668 return 0;
156             }
157 3238         22804 return is_prime($value);
158             }
159              
160             # sub ith {
161             # my ($self, $i) = @_;
162             # my $array = $self->{'array'};
163             # if ($i > $#$array) {
164             # my $hi = int ($i/log($i) * 2 + 5);
165             # do {
166             # $array = $self->{'array'} = [ undef, _my_primes_list (0, $hi) ];
167             # $hi *= 2;
168             # } while ($i > $#$array);
169             # }
170             # return $array->[$i];
171             # }
172              
173 11     11   7551 use Math::NumSeq::Fibonacci;
  11         28  
  11         1599  
174             *_blog2_estimate = \&Math::NumSeq::Fibonacci::_blog2_estimate;
175              
176             sub value_to_i_estimate {
177 235     235 1 3376 my ($self, $value) = @_;
178             ### value_to_i_estimate(): "$value"
179              
180 235 100       498 if ($value < 2) { return 0; }
  83         237  
181              
182 152         1227 $value = int($value);
183 152 100       628 if (defined (my $blog2 = _blog2_estimate($value))) {
184             # est = v/log(v)
185             # log2(v) = log(v)/log(2)
186             # est = v/((log2(v)*log(2)))
187             # = v/log2(v) * 1/log(2)
188             # ~= v/log2(v) * 13/9
189             # ~= (13*v) / (9*log2(v))
190             # using 13/9 as an approximation to 1/log(2) to stay in BigInt
191             #
192             ### $blog2
193             ### num: $value*13
194             ### den: 9 * $blog2
195 12         4801 return ($value * 13) / (9 * $blog2);
196             }
197              
198             ### log: log($value)
199             ### div: $value/log($value)
200 140         718 return int($value/log($value));
201             }
202              
203             1;
204             __END__