File Coverage

blib/lib/Math/NumSeq/ProthNumbers.pm
Criterion Covered Total %
statement 85 89 95.5
branch 20 22 90.9
condition 4 6 66.6
subroutine 17 17 100.0
pod 6 6 100.0
total 132 140 94.2


line stmt bran cond sub pod time code
1             # Copyright 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::ProthNumbers;
19 2     2   7694 use 5.004;
  2         6  
20 2     2   7 use strict;
  2         2  
  2         40  
21              
22 2     2   6 use vars '$VERSION', '@ISA';
  2         2  
  2         113  
23             $VERSION = 72;
24              
25 2     2   354 use Math::NumSeq;
  2         4  
  2         67  
26             @ISA = ('Math::NumSeq');
27             *_is_infinite = \&Math::NumSeq::_is_infinite;
28              
29 2     2   341 use Math::NumSeq::NumAronson;
  2         3  
  2         71  
30             *_round_down_pow = \&Math::NumSeq::NumAronson::_round_down_pow;
31              
32             # uncomment this to run the ### lines
33             # use Smart::Comments;
34              
35              
36             # use constant name => Math::NumSeq::__('Proth Numbers');
37 2     2   7 use constant description => Math::NumSeq::__('Proth numbers k*2^n+1 for odd k and k < 2^n.');
  2         2  
  2         5  
38 2     2   6 use constant i_start => 1;
  2         2  
  2         67  
39 2     2   7 use constant values_min => 3;
  2         3  
  2         60  
40 2     2   6 use constant characteristic_increasing => 1;
  2         2  
  2         63  
41 2     2   13 use constant characteristic_integer => 1;
  2         3  
  2         67  
42              
43             # cf A157892 - value of k
44             # A157893 - value of n
45             # A080076 - Proth primes
46             # A134876 - how many Proth primes for given n
47             #
48             # A002253 3*2^n+1 is prime
49             # A002254 5*2^n+1
50             # A032353 7*2^n+1
51             # A002256 9*2^n+1
52             #
53             # A086341 - 2^x+/-1 is sqrt of Proth squares
54             #
55 2     2   6 use constant oeis_anum => 'A080075';
  2         2  
  2         1012  
56              
57              
58             # $uv_limit is the biggest power 4**k which fits in a UV
59             #
60             my $uv_limit = do {
61             my $max = ~0;
62              
63             # limit <= floor(max/4) means 4*limit <= max is still good
64             my $limit = 4;
65             while ($limit <= ($max >> 2)) {
66             $limit <<= 2;
67             }
68              
69             ### ProthNumbers UV limit ...
70             ### $limit
71             ### limit: sprintf('%#X',$limit)
72              
73             $limit
74             };
75              
76             sub rewind {
77 7     7 1 594 my ($self) = @_;
78 7         17 $self->{'value'} = 1;
79 7         11 $self->{'inc'} = 2;
80 7         8 $self->{'limit'} = 4;
81 7         29 $self->{'i'} = $self->i_start;
82             }
83             sub next {
84 1531     1531 1 61278 my ($self) = @_;
85             ### ProthNumbers next(): sprintf('value=%b inc=%b limit=%b', $self->{'value'}, $self->{'inc'}, $self->{'limit'})
86              
87 1531         1322 my $value = ($self->{'value'} += $self->{'inc'});
88 1531 100       2138 if ($value >= $self->{'limit'}) {
89             ### bigger 2^n now ...
90 78 50       97 if ($self->{'limit'} == $uv_limit) {
91             ### go to BigInt
92 0         0 $self->{'value'} = Math::NumSeq::_to_bigint($self->{'value'});
93 0         0 $self->{'inc'} = Math::NumSeq::_to_bigint($self->{'inc'});
94 0         0 $self->{'limit'} = Math::NumSeq::_to_bigint($self->{'limit'});
95             }
96 78         55 $self->{'inc'} *= 2;
97 78         98 $self->{'limit'} *= 4;
98             }
99 1531         1718 return ($self->{'i'}++, $value);
100             }
101              
102             # seek value at i-1, inc and limit ready to make next value
103             #
104             # i i value inc limit next value
105             # 1 1 1 10 100 11
106             # 2 10 11 10 100 101
107             # 3 11 101 100 10000 1001
108             # 4 100 1001 100 10000 1101
109             # 5 101 1101 100 10000 10001
110             # 6 110 10001 1000 1000000 11001
111             # 7 111 11001 1000 1000000 100001
112             # 8 1000 100001 1000 1000000 101001
113             # 9 1001 101001 1000 1000000 110001
114             # 10 1010 110001 1000 1000000 111001
115             # 11 1011 111001 1000 1000000 1000001
116             # 12 1100 1000001 10000 100000000 1010001
117             # 13 1101 1010001 10000 100000000 1100001
118             # 14 1110 1100001 10000 100000000 1110001
119             # 15 1111 1110001 10000 100000000 10000001
120             # 16 10000 10000001 10000 100000000 10010001
121             #
122             sub seek_to_i {
123 575     575 1 1713 my ($self, $i) = @_;
124 575         559 $self->{'i'} = $i;
125              
126 575         1017 my ($pow) = _round_down_pow($i, 2); # i = 1zxxx
127 575         663 $i -= $pow; # without high bit, so i=zxxx
128              
129             # $zpow is the z bit position, or 1 if $i==1
130 575 100       905 my $zpow = ($pow >= 2 ? $pow/2 : 0);
131              
132 575 100       728 if ($i >= $zpow) {
133             # z=1 so return value=1xxx 0 0001, extra low 0-bit
134 278         282 $pow *= 2;
135             } else {
136             # z=0 so return value=1xxx 0001, turn on 1-bit in i
137 297         269 $i += $zpow; # i=0xxx becomes i=1xxx
138             }
139              
140 575 50       839 if ($pow*$pow >= $uv_limit) {
141 0         0 $pow = Math::NumSeq::_to_bigint($pow);
142             }
143 575         584 $self->{'value'} = $pow*$i + 1;
144 575         461 $self->{'inc'} = $pow;
145 575         820 $self->{'limit'} = $pow*$pow;
146             }
147              
148             sub ith {
149 162     162 1 193 my ($self, $i) = @_;
150             ### ProthNumbers ith(): $i
151              
152 162         109 $i += 1;
153 162         187 my ($pow) = _round_down_pow($i, 2); # i = 1zxxx
154 162         100 $i -= $pow; # without high bit, so i=zxxx
155              
156 162         118 my $zpow = $pow/2;
157 162 100       188 if ($i >= $zpow) { # test z bit
158             # z=1 so return value=1xxx 0 0001, extra low 0-bit
159 67         47 $pow *= 2;
160             } else {
161             # z=1 so return value=1xxx 0001, turn on 1-bit in i
162 95         53 $i += $zpow; # i=0xxx becomes i=1xxx
163             }
164 162         193 return $i*$pow + 1;
165             }
166              
167             sub pred {
168 2043     2043 1 4946 my ($self, $value) = @_;
169             ### ProthNumbers pred(): $value
170             {
171 2043         1110 my $int = int($value);
  2043         1294  
172 2043 100       2311 if ($value != $int) { return 0; }
  990         852  
173 1053         728 $value = $int;
174             }
175 1053 100 66     3177 unless ($value >= 3
      66        
176             && ($value % 2)
177             && ! _is_infinite($value)) {
178 500         687 return 0;
179             }
180              
181             # ENHANCE-ME: maybe BigInt as_bin() and string match position of lowest 1
182             # ENHANCE-ME: maybe (v^(v-1))+1 for lowest 1, ask if v < m*m
183             #
184 553         468 my $pow = ($value*0) + 2; # inherit bignum 2
185 553         315 for (;;) {
186             ### at: "$value $pow"
187 1200         846 $value = int($value/2);
188 1200 100       1271 if ($value < $pow) {
189 103         98 return 1;
190             }
191 1097 100       1230 if ($value % 2) {
192 450         532 return ($value < $pow);
193             }
194 647         351 $pow *= 2;
195             }
196             }
197              
198             # use 41/29 ~= sqrt(2) in case $value is a Math::BigInt, since can't
199             # multiply BigInt*float
200             #
201             sub value_to_i_estimate {
202 62     62 1 637 my ($self, $value) = @_;
203 62 100       71 if ($value < 0) { return 0; }
  6         6  
204 56         170 my $est = int(sqrt(int($value)) * 41 / 29);
205             }
206              
207             1;
208             __END__