File Coverage

blib/lib/Math/NumSeq/LemoineCount.pm
Criterion Covered Total %
statement 99 104 95.1
branch 20 28 71.4
condition 4 8 50.0
subroutine 15 15 100.0
pod 4 4 100.0
total 142 159 89.3


line stmt bran cond sub pod time code
1             # Copyright 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::LemoineCount;
19 1     1   1096 use 5.004;
  1         2  
20 1     1   4 use strict;
  1         1  
  1         26  
21 1     1   4 use List::Util 'max';
  1         0  
  1         68  
22              
23 1     1   4 use vars '$VERSION', '@ISA';
  1         0  
  1         77  
24             $VERSION = 72;
25             @ISA = ('Math::NumSeq');
26             *_is_infinite = \&Math::NumSeq::_is_infinite;
27              
28 1     1   4 use Math::NumSeq::Primes;
  1         1  
  1         28  
29              
30             # uncomment this to run the ### lines
31             #use Smart::Comments;
32              
33              
34             # use constant name => Math::NumSeq::__('Lemoine Count');
35 1     1   3 use constant description => Math::NumSeq::__('The number of ways i can be represented as P+2*Q for primes P and Q.');
  1         2  
  1         3  
36 1     1   3 use constant default_i_start => 1;
  1         1  
  1         38  
37 1     1   6 use constant values_min => 0;
  1         3  
  1         52  
38 1     1   4 use constant characteristic_count => 1;
  1         2  
  1         39  
39 1     1   3 use constant characteristic_smaller => 1;
  1         2  
  1         106  
40              
41 1         4 use constant parameter_info_array =>
42             [
43             {
44             name => 'on_values',
45             share_key => 'on_values_odd',
46             type => 'enum',
47             default => 'all',
48             choices => ['all','odd'],
49             choices_display => [Math::NumSeq::__('All'),
50             Math::NumSeq::__('Odd')],
51             description => Math::NumSeq::__('The values to act on, either all integers of just the odd integers.'),
52             },
53 1     1   4 ];
  1         1  
54              
55             #-----------------------------------------------------------------------------
56             # "one_as_prime" secret undocumented parameter ...
57             # some sort of odd i only option too maybe ...
58             #
59             # A046925 ways 2n+1, including 1 as a prime
60             # A046927 ways 2n+1, not including 1 as a prime
61             # A194831 records of A046927
62             # A194830 positions of records
63              
64             my %oeis_anum = (all => [ 'A046924', # ways n, including 1 as a prime
65             'A046926', # ways n, not including 1 as a prime
66             ],
67             odd => [ 'A046925', # ways n, including 1 as a prime
68             'A046927', # ways n, not including 1 as a prime
69             ],
70             );
71             # OEIS-Catalogue: A046926
72             # OEIS-Catalogue: A046924 one_as_prime=1
73             # OEIS-Catalogue: A046927 on_values=odd # starting n=0 for odd=1
74             # OEIS-Catalogue: A046925 on_values=odd one_as_prime=1
75             sub oeis_anum {
76 1     1 1 3 my ($self) = @_;
77 1         2 return $oeis_anum{$self->{'on_values'}}->[!$self->{'one_as_prime'}];
78             }
79              
80             #-----------------------------------------------------------------------------
81              
82             sub rewind {
83 3     3 1 606 my ($self) = @_;
84             ### LemoineCount rewind() ...
85 3 50       10 $self->{'i_start'} = ($self->{'on_values'} eq 'odd' ? 0 : 1);
86 3         14 $self->{'i'} = $self->i_start;
87 3         6 $self->{'a'} = 1;
88 3         3 $self->{'array'} = [];
89 3         19 $self->{'size'} = 500;
90             }
91              
92             sub next {
93 36     36 1 447 my ($self) = @_;
94             ### next(): "i=$self->{'i'}"
95              
96 36         24 for (;;) {
97 36 100       19 unless (@{$self->{'array'}}) {
  36         51  
98 2         6 $self->{'size'} = int (1.08 * $self->{'size'});
99              
100 2         4 my $lo = $self->{'a'};
101 2         3 my $hi = $lo + $self->{'size'};
102 2         3 $self->{'next_lo'} = $hi+1;
103             ### range: "lo=$lo to hi=$hi"
104              
105 2         2 my @array;
106 2         8 $array[$hi-$lo] = 0; # array size
107 2         3 $self->{'array'} = \@array;
108              
109 2 50       13 my @primes = (($self->{'one_as_prime'} ? (1) : ()),
110             Math::NumSeq::Primes::_primes_list (0, $hi));
111             {
112 2         3 my $qamax = $#primes;
113 2         4 foreach my $pa (0 .. $#primes-1) {
114 198         170 foreach my $qa (reverse $pa .. $qamax) {
115 2574         1745 my $sum = $primes[$pa] + 2*$primes[$qa];
116             ### at: "p=$primes[$pa] q=$primes[$qa] sum=$sum incr ".($sum-$lo)
117 2574 100       3137 if ($sum > $hi) {
    50          
118 118         81 $qamax = $qa-1;
119             } elsif ($sum < $lo) {
120 0         0 last;
121             } else {
122 2456         1890 $array[$sum-$lo]++;
123             }
124             }
125             }
126             }
127             {
128 2         152 my $qamax = $#primes;
  2         5  
  2         2  
129 2         4 foreach my $pa (0 .. $#primes-1) {
130 198         215 foreach my $qa (reverse $pa+1 .. $qamax) {
131 4556         2928 my $sum = 2*$primes[$pa] + $primes[$qa];
132             ### at: "p=$primes[$pa] q=$primes[$qa] sum=$sum incr ".($sum-$lo)
133 4556 100       5057 if ($sum > $hi) {
    50          
134 116         86 $qamax = $qa-1;
135             } elsif ($sum < $lo) {
136 0         0 last;
137             } else {
138 4440         3340 $array[$sum-$lo]++;
139             }
140             }
141             }
142             }
143             ### @array
144             }
145              
146 36         28 my $a = $self->{'a'}++;
147 36   100     26 my $value = shift @{$self->{'array'}} || 0;
148 36 50 33     54 if ($self->{'on_values'} eq 'odd' && !($a&1)) {
149 0         0 next;
150             }
151 36         45 return ($self->{'i'}++, $value);
152             }
153             }
154              
155             sub ith {
156 57     57 1 89 my ($self, $i) = @_;
157             ### ith(): $i
158              
159 57 50       81 if ($self->{'on_values'} eq 'odd') {
160 0         0 $i = 2*$i+1;
161             }
162 57 100       67 if ($i < 3) {
163 9         11 return 0;
164             }
165 48 50 33     135 unless ($i >= 0 && $i <= 0xFF_FFFF) {
166 0         0 return undef;
167             }
168 48         48 $i = "$i"; # numize any Math::BigInt for speed
169              
170 48         30 my $count = 0;
171 48 50       122 my @primes = (($self->{'one_as_prime'} ? (1) : ()),
172             Math::NumSeq::Primes::_primes_list (0, $i-1));
173             ### @primes
174             {
175 48         36 my $pa = 0;
176 48         33 my $qa = $#primes;
177 48         73 while ($pa <= $qa) {
178 222         181 my $sum = $primes[$pa] + 2*$primes[$qa];
179 222 100       193 if ($sum <= $i) {
180             ### at: "p=$primes[$pa] q=$primes[$qa] count ".($sum == $i)
181 89         49 $count += ($sum == $i);
182 89         110 $pa++;
183             } else {
184 133         161 $qa--;
185             }
186             }
187             }
188             {
189 48         1321 my $pa = 0;
  48         22  
  48         36  
190 48         28 my $qa = $#primes;
191 48         63 while ($pa < $qa) {
192 174         129 my $sum = 2*$primes[$pa] + $primes[$qa];
193 174 100       143 if ($sum <= $i) {
194             ### at: "p=$primes[$pa] q=$primes[$qa] count ".($sum == $i)
195 74         43 $count += ($sum == $i);
196 74         82 $pa++;
197             } else {
198 100         113 $qa--;
199             }
200             }
201             }
202 48         75 return $count;
203             }
204              
205             1;
206             __END__