File Coverage

blib/lib/Math/NumSeq/ConcatNumbers.pm
Criterion Covered Total %
statement 63 66 95.4
branch 13 16 81.2
condition 2 3 66.6
subroutine 13 13 100.0
pod 3 3 100.0
total 94 101 93.0


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             package Math::NumSeq::ConcatNumbers;
20 1     1   1216 use 5.004;
  1         2  
21 1     1   4 use strict;
  1         1  
  1         21  
22              
23 1     1   3 use vars '$VERSION', '@ISA';
  1         1  
  1         56  
24             $VERSION = 72;
25              
26 1     1   4 use Math::NumSeq;
  1         1  
  1         18  
27 1     1   3 use Math::NumSeq::Base::IterateIth;
  1         1  
  1         46  
28             @ISA = ('Math::NumSeq::Base::IterateIth',
29             'Math::NumSeq');
30              
31             *_is_infinite = \&Math::NumSeq::_is_infinite;
32             *_to_bigint = \&Math::NumSeq::_to_bigint;
33              
34 1     1   3 use Math::NumSeq::NumAronson 8; # new in v.8
  1         10  
  1         38  
35             *_round_down_pow = \&Math::NumSeq::NumAronson::_round_down_pow;
36              
37              
38             # use constant name => Math::NumSeq::__('Concatenate Numbers');
39 1         4 use constant description =>
40 1     1   4 Math::NumSeq::__('Concatenate i and i+1, eg. at i=99 value is 99100.');
  1         1  
41 1     1   3 use constant default_i_start => 0;
  1         1  
  1         38  
42              
43 1     1   4 use Math::NumSeq::Base::Digits;
  1         2  
  1         57  
44 1         3 use constant parameter_info_array =>
45             [
46             {
47             name => 'concat_count',
48             type => 'integer',
49             default => 2,
50             minimum => 1,
51             width => 2,
52             description => Math::NumSeq::__('How many numbers to concatenate.'),
53             },
54             Math::NumSeq::Base::Digits->parameter_info_list, # radix
55 1     1   3 ];
  1         1  
56              
57             #------------------------------------------------------------------------------
58              
59             # cf A033308 - concatenate primes
60             # A127421 - concat 2 starting n=1 for value 1, 12, 23 etc so n-1,n
61             # A074991 - concat 3, divided by 3
62             # A030655 - concat 2, step by 2, starting 1
63             # A193492 - concat 4
64             # A077298 - concat 5, step by 5
65             #
66             # A098080 - digits making an increasing sequence
67             #
68             # A058935 - binary concatenate successively, to make bignums
69             # A047778 - binary in decimal
70             # A001855 - binary number of digits
71             # A007908 - decimal concatenate successively, to make bignums
72             #
73             my @oeis_anum;
74              
75             $oeis_anum[1]->[2]->[2] = 'A087737'; # binary i,i+1 i=1
76             # OEIS-Catalogue: A087737 radix=2 i_start=1
77              
78             # A127421 OFFSET=1 so i-1,i rather than i,i+1
79             # $oeis_anum[0]->[10]->[2] = 'A127421'; # decimal i,i+1 starting i=1
80             # # OEIS-Catalogue: A127421
81              
82             $oeis_anum[1]->[10]->[2] = 'A001704'; # decimal i,i+1 starting i=1
83             # OEIS-Catalogue: A001704 i_start=1
84              
85             $oeis_anum[1]->[10]->[3] = 'A001703'; # decimal i,i+1,i+2 starting i=1
86             # OEIS-Catalogue: A001703 i_start=1 concat_count=3
87              
88             sub oeis_anum {
89 8     8 1 27 my ($self) = @_;
90              
91 8 50       26 if ($self->{'concat_count'} == 1) {
92             # all integers, starting from 0 or 1 according to i_start
93 0         0 require Math::NumSeq::All;
94 0         0 return $self->Math::NumSeq::All::oeis_anum;
95             # OEIS-Other: A001477 concat_count=1
96             # OEIS-Other: A001477 concat_count=1 radix=2
97             # OEIS-Other: A000027 concat_count=1 i_start=1
98             # OEIS-Other: A000027 concat_count=1 i_start=1 radix=2
99             }
100              
101             return $oeis_anum[$self->i_start]
102             ->[$self->{'radix'}]
103 8         18 ->[$self->{'concat_count'}];
104             }
105              
106             #------------------------------------------------------------------------------
107              
108             sub ith {
109 1096     1096 1 1032 my ($self, $i) = @_;
110             ### ConcatNumbers ith(): $i
111 1096 100       1340 if ($i < 0) {
112 24         24 return undef;
113             }
114 1072 50       1455 if (_is_infinite($i)) {
115 0         0 return $i;
116             }
117              
118 1072         1003 my $radix = $self->{'radix'};
119 1072         710 my $count = $self->{'concat_count'};
120              
121 1072         780 my $value = $i + --$count;
122             ### initial value: "$value"
123              
124 1072 50       1320 if ($count > 0) {
125 1072         689 my $v = $value;
126 1072 100 66     1945 if ($count > 1 && ! ref $i) {
127 264         444 $value = _to_bigint($value);
128             }
129              
130 1072         7074 my ($pow) = _round_down_pow ($v, $radix);
131             ### $pow
132 1072         814 my $value_pow = $pow * $radix;
133              
134 1072         670 for (;;) {
135             ### $v
136 1399         942 $v -= 1;
137 1399         1488 $value += $value_pow * $v;
138 1399 100       43524 last unless --$count > 0;
139              
140 327 100       510 if ($v < $pow) {
141 82         84 $pow /= $radix;
142             }
143 327         273 $value_pow *= $pow * $radix;
144             }
145             ### final value: "$value"
146              
147             ### assert: $v == $i
148             }
149              
150 1072         1905 return $value;
151             }
152              
153             # sub _NOTWORKING_pred {
154             # my ($self, $value) = @_;
155             # ### ConcatNumbers pred(): $value
156             #
157             # my $int = int($value);
158             # if ($value != $int) {
159             # return 0;
160             # }
161             #
162             # my $count = $self->{'concat_count'};
163             # if ($count <= 1) {
164             # return ($count == 1);
165             # }
166             #
167             # my $radix = $self->{'radix'};
168             # my ($pow, $exp) = _round_down_pow ($int, $radix);
169             #
170             # $exp = int(($exp+1)/$count);
171             # $pow = $radix ** $exp;
172             #
173             # my $v = $value % $pow;
174             # my $rem = $value;
175             # for (;;) {
176             # $rem = int($rem/$pow);
177             # $v2 = $rem % $pow;
178             # }
179             # ### half exp: $exp
180             # ### half pow: $pow
181             # ### high: int($value/$pow)
182             # ### low: $value % $pow
183             #
184             # # return ( + 1 == ());
185             # }
186              
187             # 123124 round down pow=100_000 exp=5 want to chop low 3 digits
188             # 999_1000 round down pow=100_0000 exp=6 want to chop low 4 digits
189             # so floor (exp+2)/2
190             #
191             # 123_124_125 round down pow=100_000_000 exp=8 want to chop low 6 digits
192             # 999_1000_1001 round down pow=100_0000_0000 exp=4+4+2=10 want to chop low 8
193             # so floor (exp+2)*2/3
194             #
195             # 123_124_125_126 round down pow=100_000_000_000 exp=11 want to chop low 9
196             # 999_1000_1001_1002 round down pow=100_0000_0000_0000 exp=14 chop low 12
197             # so floor (exp+2)*3/4
198             #
199             # usual case multiple of k digits, get exp=k*ilen-1 so ilen=(exp+1)/k
200             # in between round up, so ilen=(exp+1+k-1)/k = 1+floor(exp/k)
201             #
202             sub value_to_i_estimate {
203 163     163 1 3489 my ($self, $value) = @_;
204             ### value_to_i_estimate(): "$value"
205              
206 163         140 my $radix = $self->{'radix'};
207 163         103 my $count = $self->{'concat_count'};
208 163         107 $value = int($value);
209              
210 163         304 my ($pow, $exp) = _round_down_pow ($value, $radix);
211             # if $value==infinity then $exp==$pow==infinity here
212              
213 163         875 $exp += 1;
214 163         738 my $ilen = int($exp/$count);
215             ### $ilen
216              
217 163 100       986 if ($exp % $count) {
218             ### intermediate accumulation, treat as i=100-1 ...
219 92         723 return $radix ** $ilen - 1;
220             } else {
221             ### keep high ilen digits of value ...
222 71         283 return int ($value / ($radix ** ($exp-$ilen)))
223             }
224             }
225              
226             1;
227             __END__