File Coverage

blib/lib/Math/NumSeq/Squares.pm
Criterion Covered Total %
statement 88 91 96.7
branch 18 22 81.8
condition n/a
subroutine 22 22 100.0
pod 9 9 100.0
total 137 144 95.1


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::Squares;
19 4     4   11164 use 5.004;
  4         13  
  4         143  
20 4     4   21 use strict;
  4         8  
  4         118  
21 4     4   3413 use POSIX 'ceil';
  4         26215  
  4         33  
22 4     4   4205 use List::Util 'max';
  4         10  
  4         352  
23              
24 4     4   26 use vars '$VERSION','@ISA';
  4         7  
  4         264  
25             $VERSION = 71;
26              
27 4     4   612 use Math::NumSeq;
  4         8  
  4         197  
28             @ISA = ('Math::NumSeq');
29              
30             # uncomment this to run the ### lines
31             #use Smart::Comments;
32              
33             # use constant name => Math::NumSeq::__('Perfect Squares');
34 4     4   30 use constant description => Math::NumSeq::__('The squares 1,4,9,16,25, etc k*k.');
  4         10  
  4         16  
35 4     4   19 use constant characteristic_increasing => 1;
  4         8  
  4         213  
36 4     4   22 use constant characteristic_integer => 1;
  4         8  
  4         249  
37 4     4   21 use constant i_start => 0;
  4         8  
  4         190  
38 4     4   21 use constant values_min => 0;
  4         8  
  4         192  
39              
40             #------------------------------------------------------------------------------
41             # cf A001105 2*n^2
42             # A000037 non-squares
43             # A010052 characterisic 1/0 for squares
44             #
45 4     4   19 use constant oeis_anum => 'A000290'; # squares
  4         7  
  4         556  
46              
47             #------------------------------------------------------------------------------
48              
49 4         15 use constant 1.02 _UV_I_LIMIT => do {
50             # Float integers too when IV=32bits ?
51             # my $max = 1;
52             # for (1 .. 256) {
53             # my $try = $max*2 + 1;
54             # ### $try
55             # if ($try == 2*$max || $try == 2*$max+2) {
56             # last;
57             # }
58             # $max = $try;
59             # }
60 4         9 my $max = ~0;
61              
62 4         7 my $bit = 4;
63 4         26 for (my $i = $max; $i != 0; $i=int($i/8)) {
64 88         170 $bit *= 2;
65             }
66             ### $bit
67              
68 4         6 my $sqrt = 0;
69 4         52 for ( ; $bit != 0; $bit=int($bit/2)) {
70 100         99 my $try = $sqrt + $bit;
71 100 50       185 if ($try <= $max / $try) {
72 100         217 $sqrt = $try;
73             }
74             }
75              
76             ### $max
77             ### limit sqrt: $sqrt
78             ### limit square: $sqrt*$sqrt
79             ### sqrt hex: sprintf '%X', $sqrt
80             ### square hex: sprintf '%X', $sqrt*$sqrt
81              
82             ### assert: $sqrt <= $max/$sqrt
83              
84 4         2664 $sqrt
85 4     4   23 };
  4         112  
86              
87             sub rewind {
88 7     7 1 469 my ($self) = @_;
89 7         50 $self->{'i'} = $self->i_start;
90             }
91             sub seek_to_i {
92 19     19 1 137 my ($self, $i) = @_;
93 19 50       40 if ($i >= _UV_I_LIMIT) {
94 0         0 $i = Math::NumSeq::_to_bigint($i);
95             }
96 19         50 $self->{'i'} = $i;
97             }
98             sub seek_to_value {
99 13     13 1 536 my ($self, $value) = @_;
100 13         30 $self->seek_to_i($self->value_to_i_ceil($value));
101             }
102             sub next {
103 46     46 1 707 my ($self) = @_;
104             ### Squares next(): $self->{'i'}
105 46         73 my $i = $self->{'i'}++;
106 46 50       88 if ($i == _UV_I_LIMIT) {
107 0         0 $self->{'i'} = Math::NumSeq::_to_bigint($self->{'i'});
108             }
109 46         103 return ($i, $i*$i);
110             }
111              
112             sub ith {
113 37     37 1 80 my ($self, $i) = @_;
114 37         94 return $i*$i;
115             }
116             sub pred {
117 57     57 1 289 my ($self, $value) = @_;
118             ### Squares pred(): $value
119              
120 57 50       105 if ($value < 0) { return 0; }
  0         0  
121              
122 57         70 my $int = int($value);
123 57 100       102 if ($value != $int) { return 0; }
  25         51  
124              
125 32         42 my $i = int(sqrt($int));
126 32         77 return ($int == $i*$i);
127             }
128              
129             sub value_to_i {
130 30     30 1 120 my ($self, $value) = @_;
131 30 100       72 if ($value >= 0) {
132 26         717 my $int = int($value);
133 26 100       151 if ($value == $int) {
134 16         212 my $i = int(sqrt($int));
135 16 100       470 if ($int == $self->ith($i)) {
136 12         596 return $i;
137             }
138             }
139             }
140 18         32 return undef;
141             }
142             sub value_to_i_floor {
143 114     114 1 648 my ($self, $value) = @_;
144 114 100       246 if ($value < 0) { $value = 0; }
  14         18  
145 114         1779 return int(sqrt(int($value)));
146             }
147             *value_to_i_estimate = \&value_to_i_floor;
148              
149             sub value_to_i_ceil {
150 55     55 1 158 my ($self, $value) = @_;
151 55 100       133 if ($value < 0) { return 0; }
  3         14  
152 52         724 my $i = $self->value_to_i_floor($value);
153 52 100       507 if ($i*$i < $value) {
154 17         26 $i += 1;
155             }
156 52         593 return $i;
157             }
158              
159              
160             1;
161             __END__