File Coverage

blib/lib/Math/NumSeq/Runs.pm
Criterion Covered Total %
statement 83 86 96.5
branch 26 30 86.6
condition 12 16 75.0
subroutine 15 15 100.0
pod 4 4 100.0
total 140 151 92.7


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             # A053615 - Nto0toN
20             # cf A165162 - Nto1,N-1to1 cf A057058 making fracs A165200
21             # A010751 - runs incr then decr, up=1,down=2,up=3,down=4
22             # A055087 - runs 0toNtwice each 0 .. N, 0 .. N,
23             # A055086 - n repeat floor(n/2)+1 times, DiagonalsOctant X+Y
24             # A082375 - k to 0 by 2s
25             # A122196 - count down by 2s
26             # A111650 - 2n repeated n times
27             # A000194 - n repeated 2n times
28             # A111651 - n repeated 3n times
29             # A111652 - 3n repeated n times
30             # A121997 - 1toN repeated N times
31             # A079944 - 2^n 0s then 2^n, is second highest bit of n
32             # A004525 - 1 even then 3 odd, OFFSET=0
33             # A007001 - 1toN repeating 1toN
34             #
35             # A049581 diagonals absdiff, abs(x-y) not plain runs
36             # A061579 descending NtoPrev, permutation of the integers
37             # A076478 0 to 2^k-1, each written out in k many bits
38             # A000267 1,2,3,3,4,4,5,5,5,6,6,6, twice repeat each run length
39             # A122196 down by 2s
40             #
41              
42             package Math::NumSeq::Runs;
43 2     2   2503845 use 5.004;
  2         10  
  2         138  
44 2     2   14 use strict;
  2         5  
  2         86  
45 2     2   11 use Carp;
  2         3  
  2         170  
46              
47 2     2   11 use vars '$VERSION','@ISA';
  2         4  
  2         151  
48             $VERSION = 71;
49              
50 2     2   650 use Math::NumSeq 21; # v.21 for oeis_anum field
  2         37  
  2         106  
51             @ISA = ('Math::NumSeq');
52              
53             # uncomment this to run the ### lines
54             #use Smart::Comments;
55              
56              
57             # use constant name => Math::NumSeq::__('Runs of Integers');
58 2     2   12 use constant description => Math::NumSeq::__('Runs of integers of various kinds.');
  2         4  
  2         10  
59 2     2   11 use constant characteristic_smaller => 1;
  2         4  
  2         101  
60 2     2   12 use constant characteristic_increasing => 0;
  2         4  
  2         87  
61 2     2   19 use constant characteristic_integer => 1;
  2         3  
  2         84  
62 2     2   11 use constant default_i_start => 0;
  2         4  
  2         368  
63              
64              
65 2         9 use constant parameter_info_array =>
66             [
67             {
68             name => 'runs_type',
69             display => Math::NumSeq::__('Runs Type'),
70             type => 'enum',
71             default => '0toN',
72             choices => ['0toN',
73             '0to2N',
74             '1toN',
75             '1to2N',
76             '1toFib',
77             '0toNinc',
78             'Nto0',
79             'Nto1',
80             'Nrep',
81             'N+1rep',
82             '2rep',
83             '3rep',
84             ],
85             choices_display => [Math::NumSeq::__('0toN'),
86             Math::NumSeq::__('0to2N'),
87             Math::NumSeq::__('1toN'),
88             Math::NumSeq::__('1to2N'),
89             Math::NumSeq::__('1toFib'),
90             Math::NumSeq::__('0toNinc'),
91             Math::NumSeq::__('Nto0'),
92             Math::NumSeq::__('Nto1'),
93             Math::NumSeq::__('Nrep'),
94             Math::NumSeq::__('N+1rep'),
95             Math::NumSeq::__('2rep'),
96             Math::NumSeq::__('3rep'),
97             ],
98             # description => Math::NumSeq::__(''),
99             },
100 2     2   10 ];
  2         3  
101              
102             my %runs_type_data
103             = ('0toN' => { i_start => 0,
104             value => -1, # initial
105             values_min => 0,
106             vstart => 0,
107             vstart_inc => 0,
108             value_inc => 1,
109             c => 1, # initial
110             count => 0,
111             count_inc => 1,
112             oeis_anum => 'A002262',
113             # OEIS-Catalogue: A002262 runs_type=0toN
114             },
115              
116             '1toN' => { i_start => 1,
117             value => 0, # initial
118             values_min => 1,
119             vstart => 1,
120             vstart_inc => 0,
121             value_inc => 1,
122             c => 1, # initial
123             count => 0,
124             count_inc => 1,
125             oeis_anum => 'A002260', # 1 to N, is 0toN + 1
126             # OEIS-Catalogue: A002260 runs_type=1toN
127             },
128             '0to2N' => { i_start => 0,
129             value => -1, # initial
130             values_min => 0,
131             vstart => 0,
132             vstart_inc => 0,
133             value_inc => 1,
134             c => 1, # initial
135             count => 0,
136             count_inc => 2,
137             oeis_anum => 'A053186',
138             # OEIS-Catalogue: A053186 runs_type=0to2N
139             },
140             '1to2N' => { i_start => 1,
141             value => 0, # initial
142             values_min => 1,
143             vstart => 1,
144             vstart_inc => 0,
145             value_inc => 1,
146             c => 2, # initial
147             count => 1,
148             count_inc => 2,
149             oeis_anum => 'A074294',
150             # OEIS-Catalogue: A074294 runs_type=1to2N
151             },
152             '1to2N+1' => { i_start => 1,
153             value => 0, # initial
154             values_min => 1,
155             vstart => 1,
156             vstart_inc => 0,
157             value_inc => 1,
158             c => 1, # initial
159             count => 0,
160             count_inc => 2,
161             oeis_anum => 'A071797', # "fractal" of odds
162             # OEIS-Catalogue: A071797 runs_type=1to2N+1
163             },
164             '1toFib' => { i_start => 1,
165             value => 0, # initial
166             values_min => 1,
167             vstart => 1,
168             vstart_inc => 0,
169             value_inc => 1,
170             c => 1, # initial
171             count => 0,
172             count_inc_func => sub {
173             my ($self) = @_;
174             (my $ret, $self->{'f0'}, $self->{'f1'})
175             = ($self->{'f0'},
176             $self->{'f1'},
177             $self->{'f0'}+$self->{'f1'});
178             return $ret-1;
179             },
180             f0 => 1,
181             f1 => 2,
182             oeis_anum => 'A194029', # 1 to Fibonacci(N)
183             # OEIS-Catalogue: A194029 runs_type=1toFib
184             },
185             'Nto0' => { i_start => 0,
186             value => 1, # initial
187             values_min => 0,
188             vstart => 0,
189             vstart_inc => 1,
190             value_inc => -1,
191             c => 1, # initial
192             count => 0,
193             count_inc => 1,
194             oeis_anum => 'A025581',
195             # OEIS-Catalogue: A025581 runs_type=Nto0
196             },
197             'Nto1' => { i_start => 1,
198             value => 2, # initial
199             values_min => 1,
200             vstart => 1,
201             vstart_inc => 1,
202             value_inc => -1,
203             c => 1, # initial
204             count => 0,
205             count_inc => 1,
206             oeis_anum => 'A004736',
207             # OEIS-Catalogue: A004736 runs_type=Nto1
208             },
209             'Nrep' => { i_start => 1,
210             value => 1,
211             values_min => 1,
212             value_inc => 0,
213             vstart => 1,
214             vstart_inc => 1,
215             c => 1, # initial
216             count => 0,
217             count_inc => 1,
218             oeis_anum => 'A002024', # N appears N times
219             # OEIS-Catalogue: A002024 runs_type=Nrep
220             },
221             'N+1rep' => { i_start => 0,
222             value => 0,
223             values_min => 0,
224             value_inc => 0,
225             vstart => 0,
226             vstart_inc => 1,
227             c => 1, # initial
228             count => 0,
229             count_inc => 1,
230             oeis_anum => 'A003056', # N appears N+1 times
231             # OEIS-Catalogue: A003056 runs_type=N+1rep
232             },
233             '0toNinc' => { i_start => 0,
234             value => -1,
235             values_min => 0,
236             value_inc => 1,
237             vstart => 0,
238             vstart_inc => 1,
239             c => 1, # initial
240             count => 0,
241             count_inc => 1,
242             oeis_anum => 'A051162',
243             # OEIS-Catalogue: A051162 runs_type=0toNinc
244             },
245             );
246             # {
247             # my @a = keys %runs_type_data;
248             # ### assert: scalar(@{parameter_info_array()->[0]->{'choices'}}) == scalar(@a)
249             # }
250              
251             my @rep_oeis_anum
252             = ([ undef, # 0rep, nothing
253              
254             'A001477', # 1rep, integers 0 upwards
255             # OEIS-Other: A001477 runs_type=1rep
256              
257             'A004526', # 2rep, N appears 2 times, starting from 0
258             # OEIS-Catalogue: A004526 runs_type=2rep
259              
260             'A002264', # 3rep, N appears 3 times
261             # OEIS-Catalogue: A002264 runs_type=3rep
262              
263             'A002265', # 4rep
264             # OEIS-Catalogue: A002265 runs_type=4rep
265              
266             'A002266', # 5rep
267             # OEIS-Catalogue: A002266 runs_type=5rep
268              
269             'A152467', # 6rep
270             # OEIS-Catalogue: A152467 runs_type=6rep
271              
272             # no, A132270 has OFFSET=1 (with 7 initial 0s)
273             # 'A132270', # 7rep
274             # # OEIS-Catalogue: A132270 runs_type=7rep
275              
276             # no, A132292 has OFFSET=1 (with 8 initial 0s)
277             # 'A132292', # 8rep
278             # # OEIS-Catalogue: A132292 runs_type=8rep
279              
280             ],
281              
282             # starting i=1
283             [ undef, # 0rep, nothing
284              
285             'A000027', # 1rep, integers 1 upwards
286             # OEIS-Other: A000027 runs_type=1rep i_start=1
287              
288             # Not quite, A008619 starts OFFSET=0 value=1,1,2,2,
289             # 'A008619', # 2rep, N appears 2 times, starting from 0
290             # # OEIS-Catalogue: A008619 runs_type=2rep i_start=1
291             ] );
292              
293             sub rewind {
294 46     46 1 33709 my ($self) = @_;
295 46   50     502 $self->{'runs_type'} ||= '0toN';
296              
297 46         85 my $data;
298 46 100       496 if ($self->{'runs_type'} =~ /^(\d+)rep/) {
299 12         37 my $rep = $1;
300 12   50     76 my $i_start = ($self->{'i_start'} || 0);
301 12 50       3075 $data = { i_start => $i_start,
302             value => $i_start,
303             values_min => $i_start,
304             value_inc => 0,
305             vstart => $i_start,
306             vstart_inc => 1,
307             c => $rep, # initial
308             count => $rep-1,
309             count_inc => 0,
310             oeis_anum => ($i_start >= 0
311             ? $rep_oeis_anum[$i_start][$rep]
312             : undef),
313             };
314             } else {
315 34   33     230 $data = $runs_type_data{$self->{'runs_type'}}
316             || croak "Unrecognised runs_type: ", $self->{'runs_type'};
317             }
318 46         1555 %$self = (%$self, %$data);
319 46         378 $self->{'i'} = $self->i_start;
320             }
321              
322             sub next {
323 342     342 1 30505 my ($self) = @_;
324 342         951 my $i = $self->{'i'}++;
325 342 100       818 if (--$self->{'c'} >= 0) {
326 248         1320 return ($i,
327             ($self->{'value'} += $self->{'value_inc'}));
328             } else {
329 94 100       1441 if (my $func = $self->{'count_inc_func'}) {
330 8         19 $self->{'c'} = &$func($self);
331             } else {
332 86         196 $self->{'c'} = ($self->{'count'} += $self->{'count_inc'});
333             }
334 94         520 return ($i,
335             ($self->{'value'} = ($self->{'vstart'} += $self->{'vstart_inc'})));
336             }
337             }
338              
339             sub ith {
340 558     558 1 3779 my ($self, $i) = @_;
341             ### Runs ith(): $i
342              
343 558         9697 my $i_start = $self->{'i_start'};
344 558 100       4216 if ($i < $i_start) {
345 51         141 return undef;
346             }
347              
348 507 100 100     16563 if ($self->{'runs_type'} eq 'Nto0' || $self->{'runs_type'} eq 'Nto1') {
    100 100        
    100 100        
    100          
    100          
    100          
    100          
349             # d-(i-(d-1)*d/2)
350             # = d-i+(d-1)*d/2
351             # = d*(1+(d-1)/2) - i
352             # = d*((d+1)/2) - i
353             # = (d+1)d/2 - i
354              
355 47         72 $i -= $self->{'i_start'};
356 47         2048 my $d = int((sqrt(8*$i+1) + 1) / 2);
357             ### $d
358             ### base: ($d-1)*$d/2
359             ### rem: $i - ($d-1)*$d/2
360 47         227 return -$i + ($d+1)*$d/2 - 1 + $self->{'values_min'};
361              
362             } elsif ($self->{'runs_type'} eq 'Nrep'
363             || $self->{'runs_type'} eq 'N+1rep') {
364 59         151 $i -= $self->{'i_start'};
365 59         330 return int((sqrt(8*$i+1) - 1) / 2) + $self->{'values_min'};
366              
367             } elsif ($self->{'runs_type'} eq '0toNinc') {
368             # i-(d-1)d/2 + d
369             # = i-((d-1)d/2 - d)
370             # = i-(d-3)d/2
371 30         75 my $d = int((sqrt(8*$i+1) + 1) / 2);
372 30         136 return $i - ($d-3)*$d/2 - 1;
373              
374             } elsif ($self->{'runs_type'} =~ /^(\d+)rep/) {
375 129         819 my $rep = $1;
376             ### $rep
377 129 50       520 if ($rep < 1) {
378 0         0 return undef;
379             }
380 129         749 return int($i/$rep);
381              
382             } elsif ($self->{'runs_type'} eq '1to2N') {
383             # runs beginning i=1,3,7,13,21,31 (Math::NumSeq::Pronic + 1)
384             # N = (d^2 - d + 1)
385             # = ($d**2 - $d + 1)
386             # = (($d - 1)*$d + 1)
387             # d = 1/2 + sqrt(1 * $n + -3/4)
388             # = (1 + sqrt(4*$n - 3)) / 2
389              
390 35         133 my $d = int( (sqrt(4*$i-3) + 1) / 2);
391              
392             ### $d
393             ### base: ($d-1)*$d/2
394             ### rem: $i - ($d-1)*$d/2
395              
396 35         141 return $i - ($d-1)*$d;
397              
398             } elsif ($self->{'runs_type'} eq '0to2N'
399             || $self->{'runs_type'} eq '1to2N+1') {
400             ### 1to2N+1 ...
401             # values 1, 1,2,3, 1,2,3,4,5
402             # run beginning i=1,2,5,10,17,26
403             # N = (d^2 - 2 d + 2), starting d=1
404             # d = 1 + sqrt(1 * $n + -1)
405             # = 1 + sqrt($n-1)
406              
407 125         264 $i -= $self->{'i_start'};
408 125         1298 my $d = int(sqrt($i)) + 1;
409              
410             ### $d
411             ### base: ($d-2)*$d
412             ### rem: $i - ($d-2)*$d
413              
414 125         1114 return $i - ($d-2)*$d + $self->{'vstart'}-1;
415              
416             } elsif ($self->{'runs_type'} eq '1toFib') {
417 35         56 my $f0 = ($i*0) + 1; # inherit bignum 1
418 35         52 my $f1 = $f0 + 1; # inherit bignum 2
419 35         83 while ($f1 <= $i) {
420 107         314 ($f0,$f1) = ($f1,$f0+$f1);
421             }
422 35         142 return $i - $f0 + 1;
423              
424             } else { # 0toN, 1toN
425 47         66 $i -= $i_start;
426 47         125 my $d = int((sqrt(8*$i+1) + 1) / 2);
427              
428             ### $d
429             ### base: ($d-1)*$d/2
430             ### rem: $i - ($d-1)*$d/2
431              
432 47         244 return $i - ($d-1)*$d/2 + $self->{'vstart'};
433             }
434             }
435              
436             sub pred {
437 171     171 1 976 my ($self, $value) = @_;
438             ### Runs pred(): $value
439              
440 171 50       9766 unless ($value == int($value)) {
441 0         0 return 0;
442             }
443 171 50       372 if (defined $self->{'values_min'}) {
444 171         454 return ($value >= $self->{'values_min'});
445             } else {
446 0           return ($value <= $self->{'values_max'});
447             }
448             }
449              
450             1;
451             __END__