File Coverage

blib/lib/Math/NumSeq/GolombSequence.pm
Criterion Covered Total %
statement 118 119 99.1
branch 17 18 94.4
condition 1 3 33.3
subroutine 21 21 100.0
pod 3 3 100.0
total 160 164 97.5


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              
19              
20             # Maybe: using_values => 'primes'
21             #
22             # http://mathworld.wolfram.com/SilvermansSequence.html
23             #
24             # Y.-F. S. Petermann, J.-L. Remy and I. Vardi, Discrete derivatives of
25             # sequences, Adv. in Appl. Math. 27 (2001), 562-84.
26             # http://www.lix.polytechnique.fr/Labo/Ilan.Vardi/publications.html
27             # http://www.lix.polytechnique.fr/Labo/Ilan.Vardi/discrete_derivatives.ps
28             #
29             # cf A112377 self seq sub1 drop 0s 1, 2, 1, 1, 3, 1, 2
30             # A112378 self seq add1 drop 0s
31             # A112379 self seq sub1 drop 0s 1, 2, 1, 3,
32             # A112380 self seq sub1 drop 0s 1, 2, 1, 1, 3, 2, 1
33              
34              
35             package Math::NumSeq::GolombSequence;
36 2     2   131020 use 5.004;
  2         8  
  2         89  
37 2     2   14 use strict;
  2         3  
  2         64  
38 2     2   10 use Carp;
  2         9  
  2         150  
39              
40 2     2   11 use vars '$VERSION', '@ISA';
  2         4  
  2         126  
41             $VERSION = 71;
42              
43 2     2   675 use Math::NumSeq;
  2         5  
  2         89  
44             @ISA = ('Math::NumSeq');
45              
46             # uncomment this to run the ### lines
47             #use Smart::Comments;
48              
49             # use constant name => Math::NumSeq::__('Golomb Sequence');
50 2     2   11 use constant description => Math::NumSeq::__('Golomb sequence 1,2,2,3,3,4,4,4,etc, its own run lengths.');
  2         3  
  2         9  
51 2     2   9 use constant i_start => 1;
  2         3  
  2         77  
52 2     2   10 use constant values_min => 1;
  2         4  
  2         64  
53 2     2   9 use constant characteristic_smaller => 1;
  2         3  
  2         64  
54 2     2   9 use constant characteristic_non_decreasing => 1;
  2         4  
  2         68  
55 2     2   9 use constant characteristic_integer => 1;
  2         2  
  2         323  
56              
57 2         14 use constant parameter_info_array =>
58             [
59             { name => 'using_values',
60             display => Math::NumSeq::__('Using Values'),
61             type => 'enum',
62             default => 'all',
63             choices => ['all','odd','even','3k','squares','primes'],
64             choices_display => [Math::NumSeq::__('All'),
65             Math::NumSeq::__('Odd'),
66             Math::NumSeq::__('Even'),
67             # TRANSLATORS: "3k" meaning triples 3,6,9,12,15, probably no need to translate except into another script if Latin letter "k" won't be recognised
68             Math::NumSeq::__('3k'),
69             Math::NumSeq::__('Squares'),
70             Math::NumSeq::__('Primes'),
71             ],
72             description => Math::NumSeq::__('Which values to use in the sequence. Default "all" means all integers.'),
73             },
74 2     2   9 ];
  2         4  
75              
76             #------------------------------------------------------------------------------
77              
78             # cf A001463 golomb seq partial sums
79             # A088517 golomb first diffs, is 0/1 characteristic of partial sums
80             # A163563 a(n)+1 reps
81             # A113722 a(n) reps of 2n+1
82             # A113724 a(n) reps of 2n+2 evens
83             # A103320 condensed 1,22,33,444,555,6666 etc
84             # A104236 n*golomb(n)
85             # A143125 n*golomb(n) cumulative
86             # A095773 generalizing
87             # A116548 divisors occurring fewer than a(n) times
88             # A072649 n occurs Fibonacci(n) times
89             # A108229 n occurs Lucas(n) times
90             # A109167 n appears a(n) times
91             #
92             my %oeis_anum = (all => 'A001462',
93             # OEIS-Catalogue: A001462
94              
95             odd => 'A080605',
96             # OEIS-Catalogue: A080605 using_values=odd
97              
98             even => 'A080606',
99             # OEIS-Catalogue: A080606 using_values=even
100              
101             '3k' => 'A080607',
102             # OEIS-Catalogue: A080607 using_values=3k
103              
104             'squares' => 'A013189',
105             # OEIS-Catalogue: A013189 using_values=squares
106              
107             'primes' => 'A169682',
108             # OEIS-Catalogue: A169682 using_values=primes
109             );
110             sub oeis_anum {
111 6     6 1 44 my ($self) = @_;
112 6         23 return $oeis_anum{$self->{'using_values'}};
113             }
114              
115             #------------------------------------------------------------------------------
116             #
117             # count[0] is how many of value[0] still to be returned
118             # when count[0]==0 must increment value[0] and new count from k=1
119              
120              
121             sub rewind {
122 31     31 1 4160 my ($self) = @_;
123 31         125 $self->{'i'} = $self->i_start;
124              
125 31         59 my $using_values = $self->{'using_values'};
126 31   33     213 $self->{'upto_func'} = $self->can("_upto_$using_values")
127             || croak "Unrecognised using_values \"$using_values\"";
128              
129             # ENHANCE-ME: a hash table for these initializations, but would have to
130             # clone the arrays
131 31 100       188 if ($using_values eq 'all') {
    100          
    100          
    100          
    100          
    50          
132 6         19 $self->{'small'} = [ 1, 2, 2 ];
133 6         16 $self->{'counts'} = [ 2 ];
134 6         14 $self->{'values'} = [ 3 ];
135 6         14 $self->{'upto'} = [ 3 ];
136 6         14 $self->{'extend_count'} = 2;
137 6         13 $self->{'extend_value'} = 3;
138 6         22 $self->{'extend_upto'} = 3;
139              
140             } elsif ($using_values eq 'odd') {
141 5         16 $self->{'counts'} = [ 1, 3 ];
142 5         12 $self->{'upto'} = [ 1, 2 ];
143 5         15 $self->{'values'} = [ 1, 3 ];
144 5         10 $self->{'extend_count'} = 2;
145 5         9 $self->{'extend_value'} = 3;
146 5         17 $self->{'extend_upto'} = 2;
147              
148             } elsif ($using_values eq 'even') {
149 5         20 $self->{'counts'} = [ 2 ];
150 5         16 $self->{'values'} = [ 4 ];
151 5         13 $self->{'upto'} = [ 2 ];
152 5         16 $self->{'small'} = [ 2, 2 ];
153 5         16 $self->{'extend_count'} = 2;
154 5         12 $self->{'extend_value'} = 4;
155 5         24 $self->{'extend_upto'} = 2;
156              
157             } elsif ($using_values eq '3k') {
158 5         17 $self->{'counts'} = [ 3 ];
159 5         16 $self->{'values'} = [ 3 ];
160 5         13 $self->{'upto'} = [ 1 ];
161 5         14 $self->{'extend_count'} = 2;
162 5         12 $self->{'extend_value'} = 3;
163 5         23 $self->{'extend_upto'} = 1;
164              
165             } elsif ($using_values eq 'squares') {
166 5         15 $self->{'counts'} = [ 1, 4 ];
167 5         14 $self->{'values'} = [ 1, 4 ];
168 5         12 $self->{'upto'} = [ 1, 2 ];
169 5         8 $self->{'extend_count'} = 3;
170 5         11 $self->{'extend_value'} = 4;
171 5         19 $self->{'extend_upto'} = 2;
172              
173             } elsif ($using_values eq 'primes') {
174 5         14 $self->{'small'} = [ 2, 2 ];
175 5         13 $self->{'counts'} = [ 2 ];
176 5         12 $self->{'values'} = [ 3 ];
177 5         13 $self->{'upto'} = [ 2 ];
178 5         14 $self->{'extend_count'} = 2;
179 5         10 $self->{'extend_value'} = 3;
180 5         8 $self->{'extend_upto'} = 2;
181 5         22 $self->{'primes'} = [ 2, 3, 5 ];
182              
183             } else {
184 0         0 croak "Unrecognised using_values: ",$using_values;
185             }
186             }
187              
188             sub _upto_all {
189 141     141   341 my ($self, $upto) = @_;
190 141         209 return $upto;
191             }
192             sub _upto_odd {
193 108     108   131 my ($self, $upto) = @_;
194 108         180 return 2*$upto-1;
195             }
196             sub _upto_even {
197 100     100   126 my ($self, $upto) = @_;
198 100         166 return 2*$upto;
199             }
200             sub _upto_3k {
201 90     90   104 my ($self, $upto) = @_;
202 90         136 return 3*$upto;
203             }
204             sub _upto_squares {
205 62     62   111 my ($self, $upto) = @_;
206 62         111 return $upto*$upto;
207             }
208             sub _upto_primes {
209 89     89   109 my ($self, $upto) = @_;
210 89         87 $upto--;
211 89         104 my $primes = $self->{'primes'};
212 89 100       159 if ($upto > $#$primes) {
213 15         897 require Math::NumSeq::Primes;
214 15         48 @$primes = Math::NumSeq::Primes::_primes_list (2, 2 * $primes->[-1]);
215             }
216 89         1137 return $primes->[$upto];
217             }
218              
219             sub next {
220 6542     6542 1 36169 my ($self) = @_;
221             ### GolombSequence next(): "i=$self->{'i'}"
222             ### counts: join(',',@{$self->{'counts'}})
223             ### values: join(',',@{$self->{'values'}})
224              
225 6542 100       6961 if (defined (my $value = shift @{$self->{'small'}})) {
  6542         14261  
226 28         87 return ($self->{'i'}++, $value);
227             }
228              
229 6514         8264 my $values = $self->{'values'};
230 6514         9920 my $upto = $self->{'upto'};
231 6514         7294 my $ret = $values->[0];
232              
233 6514         7665 my $counts = $self->{'counts'};
234 6514         13226 for (my $pos = 0; --$counts->[$pos] <= 0; $pos++) {
235 590 100       1271 if ($pos >= $#$counts) {
236             ### extend ...
237 58         117 push @$counts, $self->{'extend_count'};
238 58         84 push @$upto, $self->{'extend_upto'};
239 58         257 push @$values, $self->{'extend_value'};
240             }
241 590         707 $upto->[$pos]++;
242 590         757 my $upto_func = $self->{'upto_func'};
243 590         1032 $values->[$pos] = $self->$upto_func($upto->[$pos]);
244 590         1651 $counts->[$pos] = $values->[$pos+1];
245             }
246 6514         16184 return ($self->{'i'}++, $ret);
247             }
248              
249             1;
250             __END__