File Coverage

blib/lib/Math/NumSeq/SqrtDigits.pm
Criterion Covered Total %
statement 83 92 90.2
branch 20 28 71.4
condition 5 18 27.7
subroutine 13 13 100.0
pod 3 3 100.0
total 124 154 80.5


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