File Coverage

blib/lib/Math/NumSeq/SqrtDigits.pm
Criterion Covered Total %
statement 84 93 90.3
branch 20 28 71.4
condition 5 18 27.7
subroutine 13 13 100.0
pod 3 3 100.0
total 125 155 80.6


line stmt bran cond sub pod time code
1             # Copyright 2010, 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             package Math::NumSeq::SqrtDigits;
19 4     4   42975 use 5.004;
  4         18  
  4         189  
20 4     4   64 use strict;
  4         8  
  4         160  
21 4     4   22 use Carp;
  4         9  
  4         409  
22 4     4   628 use Math::NumSeq;
  4         9  
  4         121  
23              
24 4     4   21 use vars '$VERSION','@ISA';
  4         40  
  4         280  
25             $VERSION = 71;
26              
27 4     4   1821 use Math::NumSeq::Base::Digits;
  4         14  
  4         281  
28             @ISA = ('Math::NumSeq::Base::Digits');
29              
30             # uncomment this to run the ### lines
31             #use Smart::Comments;
32              
33              
34             # use constant name => Math::NumSeq::__('Square Root Digits');
35 4     4   23 use constant description => Math::NumSeq::__('The square root of a given number written out in decimal or a given radix.');
  4         6  
  4         21  
36 4     4   22 use constant i_start => 1;
  4         7  
  4         659  
37 4         17 use constant parameter_info_array =>
38             [
39             {
40             name => 'sqrt',
41             display => Math::NumSeq::__('Sqrt'),
42             type => 'integer',
43             default => 2,
44             minimum => 2,
45             width => 5,
46             description => Math::NumSeq::__('Number to take the square root of. If this is a perfect square then there\'s just a handful of digits, non squares go on infinitely.'),
47             },
48             Math::NumSeq::Base::Digits->parameter_info_list,
49 4     4   22 ];
  4         73  
50              
51             #------------------------------------------------------------------------------
52             # cf
53             # A020807 - sqrt(1/50) decimal
54             # A020811 - sqrt(1/54) decimal
55             # A010503 - sqrt(1/2) decimal == sqrt(2)/2 = sqrt(50)/10
56             # A155781 - log15(22) decimal
57             # A011368 - 16^(1/9) decimal
58             #
59             # A092855 - the bit positions of sqrt(2)-1 in binary
60             # A029683 - nth digit of cbrt(n)
61             #
62             my @oeis_anum;
63              
64             # base 2 binary
65             $oeis_anum[2]->[6] = 'A004609'; # base 2, sqrt6
66             # OEIS-Catalogue: A004609 sqrt=6 radix=2
67              
68             # sqrt 2
69             $oeis_anum[2]->[2] = 'A004539'; # base 2, sqrt2
70             $oeis_anum[3]->[2] = 'A004540'; # base 3, sqrt2
71             $oeis_anum[4]->[2] = 'A004541'; # base 4, sqrt2
72             $oeis_anum[5]->[2] = 'A004542'; # base 5, sqrt2
73             $oeis_anum[60]->[2] = 'A070197'; # base 60, sqrt2
74             # OEIS-Catalogue: A004539 sqrt=2 radix=2
75             # OEIS-Catalogue: A004540 sqrt=2 radix=3
76             # OEIS-Catalogue: A004541 sqrt=2 radix=4
77             # OEIS-Catalogue: A004542 sqrt=2 radix=5
78             # OEIS-Catalogue: A070197 sqrt=2 radix=60
79              
80             # sqrt 3
81             $oeis_anum[2]->[3] = 'A004547'; # base 2, sqrt3
82             $oeis_anum[3]->[3] = 'A004548'; # base 3, sqrt3
83             # OEIS-Catalogue: A004548 sqrt=3 radix=3
84             # OEIS-Catalogue: A004547 sqrt=3 radix=2
85              
86             # sqrt 5
87             $oeis_anum[2]->[5] = 'A004555'; # base 2, sqrt5
88             $oeis_anum[3]->[5] = 'A004556'; # base 3, sqrt5
89             $oeis_anum[4]->[5] = 'A004557'; # base 4, sqrt5
90             $oeis_anum[5]->[5] = 'A004558'; # base 5, sqrt5
91             $oeis_anum[6]->[5] = 'A004559'; # base 6, sqrt5
92             $oeis_anum[7]->[5] = 'A004560'; # base 7, sqrt5
93             $oeis_anum[8]->[5] = 'A004561'; # base 8, sqrt5
94             $oeis_anum[9]->[5] = 'A004562'; # base 9, sqrt5
95             # OEIS-Catalogue: A004555 sqrt=5 radix=2
96             # OEIS-Catalogue: A004556 sqrt=5 radix=3
97             # OEIS-Catalogue: A004557 sqrt=5 radix=4
98             # OEIS-Catalogue: A004558 sqrt=5 radix=5
99             # OEIS-Catalogue: A004559 sqrt=5 radix=6
100             # OEIS-Catalogue: A004560 sqrt=5 radix=7
101             # OEIS-Catalogue: A004561 sqrt=5 radix=8
102             # OEIS-Catalogue: A004562 sqrt=5 radix=9
103              
104             # sqrt 6
105             $oeis_anum[4]->[6] = 'A004563'; # base 4, sqrt6
106             $oeis_anum[5]->[6] = 'A004564'; # base 5, sqrt6
107             $oeis_anum[6]->[6] = 'A004565'; # base 6, sqrt6
108             $oeis_anum[7]->[6] = 'A004566'; # base 7, sqrt6
109             $oeis_anum[8]->[6] = 'A004567'; # base 8, sqrt6
110             $oeis_anum[9]->[6] = 'A004568'; # base 9, sqrt6
111             # OEIS-Catalogue: A004563 sqrt=6 radix=4
112             # OEIS-Catalogue: A004564 sqrt=6 radix=5
113             # OEIS-Catalogue: A004565 sqrt=6 radix=6
114             # OEIS-Catalogue: A004566 sqrt=6 radix=7
115             # OEIS-Catalogue: A004567 sqrt=6 radix=8
116             # OEIS-Catalogue: A004568 sqrt=6 radix=9
117              
118             # sqrt 7
119             $oeis_anum[2]->[7] = 'A004569'; # base 2, sqrt7
120             $oeis_anum[3]->[7] = 'A004570'; # base 3, sqrt7
121             $oeis_anum[4]->[7] = 'A004571'; # base 4, sqrt7
122             $oeis_anum[5]->[7] = 'A004572'; # base 5, sqrt7
123             $oeis_anum[6]->[7] = 'A004573'; # base 6, sqrt7
124             $oeis_anum[7]->[7] = 'A004574'; # base 7, sqrt7
125             $oeis_anum[8]->[7] = 'A004575'; # base 8, sqrt7
126             $oeis_anum[9]->[7] = 'A004576'; # base 9, sqrt7
127             # OEIS-Catalogue: A004569 sqrt=7 radix=2
128             # OEIS-Catalogue: A004570 sqrt=7 radix=3
129             # OEIS-Catalogue: A004571 sqrt=7 radix=4
130             # OEIS-Catalogue: A004572 sqrt=7 radix=5
131             # OEIS-Catalogue: A004573 sqrt=7 radix=6
132             # OEIS-Catalogue: A004574 sqrt=7 radix=7
133             # OEIS-Catalogue: A004575 sqrt=7 radix=8
134             # OEIS-Catalogue: A004576 sqrt=7 radix=9
135             #
136             $oeis_anum[10]->[7] = 'A010465'; # base 10, sqrt7
137             # OEIS-Catalogue: A010465 sqrt=7 radix=10
138              
139             # sqrt 8
140             $oeis_anum[3]->[8] = 'A004578'; # base 3, sqrt8
141             $oeis_anum[4]->[8] = 'A004579'; # base 4, sqrt8
142             $oeis_anum[5]->[8] = 'A004580'; # base 5, sqrt8
143             $oeis_anum[6]->[8] = 'A004581'; # base 6, sqrt8
144             $oeis_anum[7]->[8] = 'A004582'; # base 7, sqrt8
145             $oeis_anum[8]->[8] = 'A004583'; # base 8, sqrt8
146             $oeis_anum[9]->[8] = 'A004584'; # base 9, sqrt8
147             # OEIS-Catalogue: A004578 sqrt=8 radix=3
148             # OEIS-Catalogue: A004579 sqrt=8 radix=4
149             # OEIS-Catalogue: A004580 sqrt=8 radix=5
150             # OEIS-Catalogue: A004581 sqrt=8 radix=6
151             # OEIS-Catalogue: A004582 sqrt=8 radix=7
152             # OEIS-Catalogue: A004583 sqrt=8 radix=8
153             # OEIS-Catalogue: A004584 sqrt=8 radix=9
154              
155             # sqrt 10
156             $oeis_anum[2]->[10] = 'A004585'; # base 2, sqrt10
157             $oeis_anum[3]->[10] = 'A004586'; # base 3, sqrt10
158             $oeis_anum[4]->[10] = 'A004587'; # base 4, sqrt10
159             $oeis_anum[5]->[10] = 'A004588'; # base 5, sqrt10
160             # OEIS-Catalogue: A004585 sqrt=10 radix=2
161             # OEIS-Catalogue: A004586 sqrt=10 radix=3
162             # OEIS-Catalogue: A004587 sqrt=10 radix=4
163             # OEIS-Catalogue: A004588 sqrt=10 radix=5
164              
165             # base 10 decimal
166             $oeis_anum[10]->[2] = 'A002193'; # sqrt2
167             $oeis_anum[10]->[3] = 'A002194'; # sqrt3
168             $oeis_anum[10]->[5] = 'A002163'; # sqrt5
169             # OEIS-Catalogue: A002193 sqrt=2
170             # OEIS-Catalogue: A002194 sqrt=3
171             # OEIS-Catalogue: A002163 sqrt=5
172              
173             my %perfect_square = (16 => 1,
174             25 => 1,
175             36 => 1,
176             49 => 1,
177             64 => 1,
178             81 => 1);
179             sub oeis_anum {
180 2     2 1 12 my ($self) = @_;
181             ### oeis_anum() ...
182 2         6 my $sqrt = $self->{'sqrt'};
183 2         6 my $radix = $self->{'radix'};
184              
185             # No, the values are the same, but i is offset by the power removed ...
186             # # so that sqrt(8) gives A-num of sqrt(2), etc
187             # {
188             # my $radix_squared = $radix * $radix;
189             # while (($sqrt % $radix_squared) == 0) {
190             # $sqrt /= $radix_squared;
191             # }
192             # }
193             # # OEIS-Other: A004539 sqrt=8 radix=2
194             # # OEIS-Other: A004547 sqrt=12 radix=2
195             # # OEIS-Other: A004569 sqrt=28 radix=2
196             # # OEIS-Other: A004585 sqrt=40 radix=2
197              
198 2 0 33     13 if ($radix == 10
      33        
      0        
      0        
      0        
199             && $sqrt >= 10 && $sqrt <= 99
200             && $sqrt != 50 && $sqrt != 75
201             && ! $perfect_square{$sqrt}) {
202             ### calculated ...
203 0         0 my $offset = 0;
204 0         0 foreach my $i (11 .. $sqrt) {
205 0         0 $offset += ! $perfect_square{$i};
206             }
207 0         0 return 'A0'.(10467+$offset);
208             }
209 2         7 return $oeis_anum[$radix]->[$sqrt];
210             }
211             # these in sequence, but skipping perfect squares 16,25,36,49,64,81
212             # OEIS-Catalogue: A010467 sqrt=10
213             # OEIS-Catalogue: A010468 sqrt=11
214             # OEIS-Catalogue: A010469 sqrt=12
215             # OEIS-Catalogue: A010470 sqrt=13
216             # OEIS-Catalogue: A010471 sqrt=14
217             # OEIS-Catalogue: A010472 sqrt=15
218             # not 16
219             # OEIS-Catalogue: A010473 sqrt=17
220             # OEIS-Catalogue: A010474 sqrt=18
221             # OEIS-Catalogue: A010475 sqrt=19
222             # OEIS-Catalogue: A010476 sqrt=20
223             # OEIS-Catalogue: A010477 sqrt=21
224             # OEIS-Catalogue: A010478 sqrt=22
225             # OEIS-Catalogue: A010479 sqrt=23
226             # OEIS-Catalogue: A010480 sqrt=24
227             # not 25
228             # OEIS-Catalogue: A010481 sqrt=26
229             # OEIS-Catalogue: A010482 sqrt=27
230             # OEIS-Catalogue: A010483 sqrt=28
231             # OEIS-Catalogue: A010484 sqrt=29
232             # OEIS-Catalogue: A010485 sqrt=30
233             # OEIS-Catalogue: A010486 sqrt=31
234             # OEIS-Catalogue: A010487 sqrt=32
235             # OEIS-Catalogue: A010488 sqrt=33
236             # OEIS-Catalogue: A010489 sqrt=34
237             # OEIS-Catalogue: A010490 sqrt=35
238             # not 36
239             # OEIS-Catalogue: A010491 sqrt=37
240             # OEIS-Catalogue: A010492 sqrt=38
241             # OEIS-Catalogue: A010493 sqrt=39
242             # OEIS-Catalogue: A010494 sqrt=40
243             # OEIS-Catalogue: A010495 sqrt=41
244             # OEIS-Catalogue: A010496 sqrt=42
245             # OEIS-Catalogue: A010497 sqrt=43
246             # OEIS-Catalogue: A010498 sqrt=44
247             # OEIS-Catalogue: A010499 sqrt=45
248             # OEIS-Catalogue: A010500 sqrt=46
249             # OEIS-Catalogue: A010501 sqrt=47
250             # OEIS-Catalogue: A010502 sqrt=48
251             # # OEIS-Catalogue: A010503 sqrt=50 OFFSET=0 ...
252             # OEIS-Catalogue: A010504 sqrt=51
253             # OEIS-Catalogue: A010505 sqrt=52
254             # OEIS-Catalogue: A010506 sqrt=53
255             # OEIS-Catalogue: A010507 sqrt=54
256             # OEIS-Catalogue: A010508 sqrt=55
257             # OEIS-Catalogue: A010509 sqrt=56
258             # OEIS-Catalogue: A010510 sqrt=57
259             # OEIS-Catalogue: A010511 sqrt=58
260             # OEIS-Catalogue: A010512 sqrt=59
261             # OEIS-Catalogue: A010513 sqrt=60
262             # OEIS-Catalogue: A010514 sqrt=61
263             # OEIS-Catalogue: A010515 sqrt=62
264             # OEIS-Catalogue: A010516 sqrt=63
265             # not 64
266             # OEIS-Catalogue: A010517 sqrt=65
267             # OEIS-Catalogue: A010518 sqrt=66
268             # OEIS-Catalogue: A010519 sqrt=67
269             # OEIS-Catalogue: A010520 sqrt=68
270             # OEIS-Catalogue: A010521 sqrt=69
271             # OEIS-Catalogue: A010522 sqrt=70
272             # OEIS-Catalogue: A010523 sqrt=71
273             # OEIS-Catalogue: A010524 sqrt=72
274             # OEIS-Catalogue: A010525 sqrt=73
275             # OEIS-Catalogue: A010526 sqrt=74
276             # # OEIS-Catalogue: A010527 sqrt=75 OFFSET=0 for sqrt(3)/2
277             # OEIS-Catalogue: A010528 sqrt=76
278             # OEIS-Catalogue: A010529 sqrt=77
279             # OEIS-Catalogue: A010530 sqrt=78
280             # OEIS-Catalogue: A010531 sqrt=79
281             # OEIS-Catalogue: A010532 sqrt=80
282             # not 81
283             # OEIS-Catalogue: A010533 sqrt=82
284             # OEIS-Catalogue: A010534 sqrt=83
285             # OEIS-Catalogue: A010535 sqrt=84
286             # OEIS-Catalogue: A010536 sqrt=85
287             # OEIS-Catalogue: A010537 sqrt=86
288             # OEIS-Catalogue: A010538 sqrt=87
289             # OEIS-Catalogue: A010539 sqrt=88
290             # OEIS-Catalogue: A010540 sqrt=89
291             # OEIS-Catalogue: A010541 sqrt=90
292             # OEIS-Catalogue: A010542 sqrt=91
293             # OEIS-Catalogue: A010543 sqrt=92
294             # OEIS-Catalogue: A010544 sqrt=93
295             # OEIS-Catalogue: A010545 sqrt=94
296             # OEIS-Catalogue: A010546 sqrt=95
297             # OEIS-Catalogue: A010547 sqrt=96
298             # OEIS-Catalogue: A010548 sqrt=97
299             # OEIS-Catalogue: A010549 sqrt=98
300             # OEIS-Catalogue: A010550 sqrt=99
301              
302              
303             #------------------------------------------------------------------------------
304              
305             my %radix_to_stringize_method = ((Math::NumSeq::_bigint()->can('as_bin')
306             ? (2 => 'as_bin')
307             : ()),
308             (Math::NumSeq::_bigint()->can('as_oct')
309             ? (8 => 'as_oct')
310             : ()),
311             (Math::NumSeq::_bigint()->can('bstr')
312             ? (10 => 'bstr')
313             : ()),
314             (Math::NumSeq::_bigint()->can('as_hex')
315             ? (16 => 'as_hex')
316             : ()));
317              
318             sub rewind {
319 43     43 1 2333 my ($self) = @_;
320 43         336 $self->{'i_extended'} = $self->{'i'} = $self->i_start;
321             }
322              
323             sub _extend {
324 76     76   149 my ($self) = @_;
325              
326 76         199 my $sqrt = $self->{'sqrt'};
327 76 50       194 if (defined $sqrt) {
328 76 50       558 if ($sqrt =~ m{^\s*(\d+)\s*$}) {
329 76         315 $sqrt = $1;
330             } else {
331 0         0 croak 'Unrecognised SqrtDigits parameter: ', $self->{'sqrt'};
332             }
333             } else {
334 0         0 $sqrt = $self->parameter_default('sqrt');
335             }
336              
337 76         214 my $calcdigits = int(2*$self->{'i_extended'} + 32);
338              
339 76         141 my $radix = $self->{'radix'};
340 76         145 my $power;
341             my $root;
342 76         168 my $halfdigits = int($calcdigits/2);
343 76 100       186 if ($radix == 2) {
344 8         38 $root = Math::NumSeq::_to_bigint(1);
345 8         356 $root->blsft ($calcdigits);
346             } else {
347 68         372 $power = Math::NumSeq::_to_bigint($radix);
348 68         2705 $power->bpow ($halfdigits);
349 68         153740 $root = Math::NumSeq::_to_bigint($power);
350 68         12591 $root->bmul ($root);
351             }
352 76         325440 $root->bmul ($sqrt);
353             ### $radix
354             ### $calcdigits
355             ### root of: "$root"
356 76         16409 $root->bsqrt();
357             ### root is: "$root"
358              
359 76 100       5370270 if (my $method = $radix_to_stringize_method{$radix}) {
360 28         200 $self->{'string'} = $root->$method();
361             ### string: $self->{'string'}
362              
363             # one leading zero for i=1 start
364 28 100 100     79618 if ($radix == 2 || $radix == 16) {
    100          
365 16         265 substr($self->{'string'},0,2) = '0'; # replacing 0b or 0x
366             } elsif ($radix != 8) {
367             # decimal insert 0, cf as_oct() already gives leading zero
368 6         142 substr($self->{'string'},0,0) = '0';
369             }
370              
371             } else {
372 48         239 $self->{'root'} = $root;
373              
374 48 50       334 if ($radix > 1) {
375 48         347 while ($power <= $root) {
376 88         13359 $power->bmul($radix);
377             }
378             }
379 48 100       10884 if (my $i = $self->{'i'} - 1) {
380 24         169 my $div = Math::BigInt->new($radix);
381 24         741 $div->bpow ($i);
382 24         23669 $power->bdiv ($div);
383 24         95320 $root->bmod ($power);
384             }
385 48         92642 $self->{'root'} = $root;
386 48         223 $self->{'power'} = $power;
387             }
388             }
389              
390             sub next {
391 7311     7311 1 36950 my ($self) = @_;
392              
393 7311         11074 my $radix = $self->{'radix'};
394 7311 50       14682 if ($radix < 2) {
395 0         0 return;
396             }
397              
398 7311 100       17977 if ($self->{'i'} >= $self->{'i_extended'}) {
399 76         276 $self->{'i_extended'} = int(($self->{'i_extended'} + 100) * 1.5);
400 76         389 _extend($self);
401             }
402              
403             ### SqrtDigits next(): $self->{'i'}
404 7311 100       13462 if (defined $self->{'string'}) {
405 2467         2941 my $i = $self->{'i'}++;
406 2467 50       4640 if ($i > length($self->{'string'})) {
407             ### oops, past end of string ...
408 0         0 return;
409             }
410             ### string char: "i=$i substr=".substr($self->{'string'},$i,1)
411 2467         6294 return ($i, hex(substr($self->{'string'},$i,1)));
412              
413             } else {
414             # digit by digit from the top like this is a bit slow, should chop into
415             # repeated halves instead
416              
417 4844         6407 my $power = $self->{'power'};
418 4844 50       14615 if ($power == 0) {
419 0         0 return;
420             }
421 4844         619836 my $root = $self->{'root'};
422             ### root: "$root"
423             ### power: "$power"
424              
425 4844         15715 $self->{'power'}->bdiv($self->{'radix'});
426 4844         806468 (my $digit, $self->{'root'}) = $root->bdiv ($self->{'power'});
427             ### digit: "$digit"
428 4844         1398860 return (++$self->{'i'}, $digit);
429             }
430             }
431              
432             # ENHANCE-ME: which digits can occur? all of them?
433             # sub pred {
434             # my ($self, $n) = @_;
435             # return ($n < $self->{'radix'});
436             # }
437              
438             1;
439             __END__