File Coverage

blib/lib/Math/NumSeq/Squares.pm
Criterion Covered Total %
statement 87 90 96.6
branch 18 22 81.8
condition n/a
subroutine 22 22 100.0
pod 9 9 100.0
total 136 143 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   5854 use 5.004;
  4         9  
20 4     4   13 use strict;
  4         5  
  4         62  
21 4     4   1148 use POSIX 'ceil';
  4         14421  
  4         21  
22 4     4   2404 use List::Util 'max';
  4         6  
  4         226  
23              
24 4     4   19 use vars '$VERSION','@ISA';
  4         6  
  4         203  
25             $VERSION = 72;
26              
27 4     4   391 use Math::NumSeq;
  4         7  
  4         164  
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   15 use constant description => Math::NumSeq::__('The squares 1,4,9,16,25, etc k*k.');
  4         3  
  4         11  
35 4     4   14 use constant characteristic_increasing => 1;
  4         5  
  4         138  
36 4     4   12 use constant characteristic_integer => 1;
  4         4  
  4         127  
37 4     4   13 use constant i_start => 0;
  4         3  
  4         121  
38 4     4   10 use constant values_min => 0;
  4         5  
  4         134  
39              
40             #------------------------------------------------------------------------------
41             # cf A001105 2*n^2
42             # A000037 non-squares
43             # A010052 characterisic 1/0 for squares
44             #
45 4     4   13 use constant oeis_anum => 'A000290'; # squares
  4         2  
  4         437  
46              
47             #------------------------------------------------------------------------------
48              
49 4         10 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         5 my $max = ~0;
61              
62 4         4 my $bit = 4;
63 4         13 for (my $i = $max; $i != 0; $i=int($i/8)) {
64 88         149 $bit *= 2;
65             }
66             ### $bit
67              
68 4         5 my $sqrt = 0;
69 4         22 for ( ; $bit != 0; $bit=int($bit/2)) {
70 100         70 my $try = $sqrt + $bit;
71 100 50       119 if ($try <= $max / $try) {
72 100         142 $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         1610 $sqrt
85 4     4   15 };
  4         59  
86              
87             sub rewind {
88 7     7 1 335 my ($self) = @_;
89 7         43 $self->{'i'} = $self->i_start;
90             }
91             sub seek_to_i {
92 19     19 1 74 my ($self, $i) = @_;
93 19 50       30 if ($i >= _UV_I_LIMIT) {
94 0         0 $i = Math::NumSeq::_to_bigint($i);
95             }
96 19         23 $self->{'i'} = $i;
97             }
98             sub seek_to_value {
99 13     13 1 1458 my ($self, $value) = @_;
100 13         18 $self->seek_to_i($self->value_to_i_ceil($value));
101             }
102             sub next {
103 46     46 1 479 my ($self) = @_;
104             ### Squares next(): $self->{'i'}
105 46         35 my $i = $self->{'i'}++;
106 46 50       59 if ($i == _UV_I_LIMIT) {
107 0         0 $self->{'i'} = Math::NumSeq::_to_bigint($self->{'i'});
108             }
109 46         48 return ($i, $i*$i);
110             }
111              
112             sub ith {
113 37     37 1 57 my ($self, $i) = @_;
114 37         52 return $i*$i;
115             }
116             sub pred {
117 57     57 1 155 my ($self, $value) = @_;
118             ### Squares pred(): $value
119              
120 57 50       64 if ($value < 0) { return 0; }
  0         0  
121              
122 57         38 my $int = int($value);
123 57 100       63 if ($value != $int) { return 0; }
  25         22  
124              
125 32         26 my $i = int(sqrt($int));
126 32         38 return ($int == $i*$i);
127             }
128              
129             sub value_to_i {
130 30     30 1 67 my ($self, $value) = @_;
131 30 100       52 if ($value >= 0) {
132 26         528 my $int = int($value);
133 26 100       94 if ($value == $int) {
134 16         103 my $i = int(sqrt($int));
135 16 100       260 if ($int == $self->ith($i)) {
136 12         351 return $i;
137             }
138             }
139             }
140 18         19 return undef;
141             }
142             sub value_to_i_floor {
143 114     114 1 424 my ($self, $value) = @_;
144 114 100       170 if ($value < 0) { $value = 0; }
  14         12  
145 114         1190 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 85 my ($self, $value) = @_;
151 55 100       88 if ($value < 0) { return 0; }
  3         7  
152 52         509 my $i = $self->value_to_i_floor($value);
153 52 100       325 if ($i*$i < $value) {
154 17         18 $i += 1;
155             }
156 52         367 return $i;
157             }
158              
159              
160             1;
161             __END__