File Coverage

blib/lib/Math/NumSeq/LemoineCount.pm
Criterion Covered Total %
statement 100 105 95.2
branch 20 28 71.4
condition 4 8 50.0
subroutine 15 15 100.0
pod 4 4 100.0
total 143 160 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   8502 use 5.004;
  1         4  
  1         55  
20 1     1   9 use strict;
  1         3  
  1         60  
21 1     1   8 use List::Util 'max';
  1         2  
  1         98  
22              
23 1     1   7 use vars '$VERSION', '@ISA';
  1         3  
  1         102  
24             $VERSION = 71;
25             @ISA = ('Math::NumSeq');
26             *_is_infinite = \&Math::NumSeq::_is_infinite;
27              
28 1     1   8 use Math::NumSeq::Primes;
  1         2  
  1         48  
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   5 use constant description => Math::NumSeq::__('The number of ways i can be represented as P+2*Q for primes P and Q.');
  1         3  
  1         7  
36 1     1   5 use constant default_i_start => 1;
  1         4  
  1         57  
37 1     1   7 use constant values_min => 0;
  1         2  
  1         62  
38 1     1   7 use constant characteristic_count => 1;
  1         2  
  1         57  
39 1     1   6 use constant characteristic_smaller => 1;
  1         3  
  1         112  
40              
41 1         7 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   7 ];
  1         2  
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 5 my ($self) = @_;
77 1         5 return $oeis_anum{$self->{'on_values'}}->[!$self->{'one_as_prime'}];
78             }
79              
80             #-----------------------------------------------------------------------------
81              
82             sub rewind {
83 3     3 1 1310 my ($self) = @_;
84             ### LemoineCount rewind() ...
85 3 50       18 $self->{'i_start'} = ($self->{'on_values'} eq 'odd' ? 0 : 1);
86 3         21 $self->{'i'} = $self->i_start;
87 3         7 $self->{'a'} = 1;
88 3         11 $self->{'array'} = [];
89 3         34 $self->{'size'} = 500;
90             }
91              
92             sub next {
93 36     36 1 914 my ($self) = @_;
94             ### next(): "i=$self->{'i'}"
95              
96 36         41 for (;;) {
97 36 100       49 unless (@{$self->{'array'}}) {
  36         105  
98 2         9 $self->{'size'} = int (1.08 * $self->{'size'});
99              
100 2         7 my $lo = $self->{'a'};
101 2         4 my $hi = $lo + $self->{'size'};
102 2         6 $self->{'next_lo'} = $hi+1;
103             ### range: "lo=$lo to hi=$hi"
104              
105 2         5 my @array;
106 2         16 $array[$hi-$lo] = 0; # array size
107 2         6 $self->{'array'} = \@array;
108              
109 2 50       20 my @primes = (($self->{'one_as_prime'} ? (1) : ()),
110             Math::NumSeq::Primes::_primes_list (0, $hi));
111             {
112 2         276 my $qamax = $#primes;
  2         5  
113 2         8 foreach my $pa (0 .. $#primes-1) {
114 198         450 foreach my $qa (reverse $pa .. $qamax) {
115 2574         3302 my $sum = $primes[$pa] + 2*$primes[$qa];
116             ### at: "p=$primes[$pa] q=$primes[$qa] sum=$sum incr ".($sum-$lo)
117 2574 100       4790 if ($sum > $hi) {
    50          
118 118         158 $qamax = $qa-1;
119             } elsif ($sum < $lo) {
120 0         0 last;
121             } else {
122 2456         4162 $array[$sum-$lo]++;
123             }
124             }
125             }
126             }
127             {
128 2         5 my $qamax = $#primes;
  2         9  
129 2         7 foreach my $pa (0 .. $#primes-1) {
130 198         536 foreach my $qa (reverse $pa+1 .. $qamax) {
131 4556         6111 my $sum = 2*$primes[$pa] + $primes[$qa];
132             ### at: "p=$primes[$pa] q=$primes[$qa] sum=$sum incr ".($sum-$lo)
133 4556 100       9146 if ($sum > $hi) {
    50          
134 116         165 $qamax = $qa-1;
135             } elsif ($sum < $lo) {
136 0         0 last;
137             } else {
138 4440         7495 $array[$sum-$lo]++;
139             }
140             }
141             }
142             }
143             ### @array
144             }
145              
146 36         64 my $a = $self->{'a'}++;
147 36   100     43 my $value = shift @{$self->{'array'}} || 0;
148 36 50 33     115 if ($self->{'on_values'} eq 'odd' && !($a&1)) {
149 0         0 next;
150             }
151 36         121 return ($self->{'i'}++, $value);
152             }
153             }
154              
155             sub ith {
156 57     57 1 211 my ($self, $i) = @_;
157             ### ith(): $i
158              
159 57 50       166 if ($self->{'on_values'} eq 'odd') {
160 0         0 $i = 2*$i+1;
161             }
162 57 100       125 if ($i < 3) {
163 9         27 return 0;
164             }
165 48 50 33     218 unless ($i >= 0 && $i <= 0xFF_FFFF) {
166 0         0 return undef;
167             }
168 48         71 $i = "$i"; # numize any Math::BigInt for speed
169              
170 48         68 my $count = 0;
171 48 50       229 my @primes = (($self->{'one_as_prime'} ? (1) : ()),
172             Math::NumSeq::Primes::_primes_list (0, $i-1));
173             ### @primes
174             {
175 48         2921 my $pa = 0;
  48         66  
176 48         67 my $qa = $#primes;
177 48         121 while ($pa <= $qa) {
178 222         342 my $sum = $primes[$pa] + 2*$primes[$qa];
179 222 100       417 if ($sum <= $i) {
180             ### at: "p=$primes[$pa] q=$primes[$qa] count ".($sum == $i)
181 89         114 $count += ($sum == $i);
182 89         254 $pa++;
183             } else {
184 133         288 $qa--;
185             }
186             }
187             }
188             {
189 48         59 my $pa = 0;
  48         94  
190 48         64 my $qa = $#primes;
191 48         114 while ($pa < $qa) {
192 174         258 my $sum = 2*$primes[$pa] + $primes[$qa];
193 174 100       295 if ($sum <= $i) {
194             ### at: "p=$primes[$pa] q=$primes[$qa] count ".($sum == $i)
195 74         97 $count += ($sum == $i);
196 74         164 $pa++;
197             } else {
198 100         221 $qa--;
199             }
200             }
201             }
202 48         199 return $count;
203             }
204              
205             1;
206             __END__