File Coverage

blib/lib/Math/NumSeq/Cubes.pm
Criterion Covered Total %
statement 92 96 95.8
branch 15 20 75.0
condition 0 3 0.0
subroutine 25 25 100.0
pod 9 9 100.0
total 141 153 92.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::Cubes;
19 3     3   13023 use 5.004;
  3         10  
  3         117  
20 3     3   16 use strict;
  3         7  
  3         98  
21 3     3   2555 use Math::Libm 'cbrt';
  3         24879  
  3         461  
22 3     3   1933 use POSIX 'floor','ceil';
  3         24509  
  3         36  
23 3     3   3681 use List::Util 'max';
  3         10  
  3         394  
24              
25 3     3   25 use vars '$VERSION', '@ISA';
  3         8  
  3         276  
26             $VERSION = 71;
27 3     3   895 use Math::NumSeq;
  3         9  
  3         197  
28             @ISA = ('Math::NumSeq');
29              
30             # uncomment this to run the ### lines
31             #use Smart::Comments;
32              
33              
34             # use constant name => Math::NumSeq::__('Cubes');
35 3     3   21 use constant description => Math::NumSeq::__('The cubes 1, 8, 27, 64, 125, etc, k*k*k.');
  3         9  
  3         1437  
36 3     3   23 use constant i_start => 0;
  3         9  
  3         190  
37 3     3   21 use constant values_min => 0;
  3         10  
  3         180  
38 3     3   19 use constant characteristic_increasing => 1;
  3         7  
  3         489  
39 3     3   45 use constant characteristic_integer => 1;
  3         6  
  3         177  
40 3     3   24 use constant oeis_anum => 'A000578';
  3         7  
  3         536  
41              
42 3         14 use constant 1.02 _UV_I_LIMIT => do {
43             # Float integers too when IV=32bits ?
44             # my $max = 1;
45             # for (1 .. 256) {
46             # my $try = $max*2 + 1;
47             # ### $try
48             # if ($try == 2*$max || $try == 2*$max+2) {
49             # last;
50             # }
51             # $max = $try;
52             # }
53 3         7 my $max = ~0;
54              
55 3         5 my $bit = 4;
56 3         22 for (my $i = $max; $i != 0; $i=int($i/8)) {
57 66         153 $bit *= 2;
58             }
59             ### $bit
60              
61 3         5 my $cbrt = 0;
62 3         14 for ( ; $bit != 0; $bit=int($bit/2)) {
63 75         97 my $try = $cbrt + $bit;
64 75 100       238 if ($try * $try <= $max / $try) {
65 24         63 $cbrt = $try;
66             }
67             }
68              
69             ### $cbrt
70             ### cube: $cbrt*$cbrt*$cbrt
71             ### $max
72             ### cube hex: sprintf '%X', $cbrt*$cbrt*$cbrt
73              
74             ### assert: $cbrt*$cbrt <= $max/$cbrt
75             ### assert: ($cbrt+1)*($cbrt+1) > $max/($cbrt+1)
76              
77 3         3130 $cbrt
78 3     3   19 };
  3         110  
79              
80             sub rewind {
81 8     8 1 481 my ($self) = @_;
82 8         53 $self->{'i'} = $self->i_start;
83             }
84             sub seek_to_i {
85 23     23 1 145 my ($self, $i) = @_;
86 23 50       52 if ($i >= _UV_I_LIMIT) {
87 0         0 $i = Math::NumSeq::_to_bigint($i);
88             }
89 23         59 $self->{'i'} = $i;
90             }
91             sub seek_to_value {
92 17     17 1 3481 my ($self, $value) = @_;
93 17         55 $self->seek_to_i($self->value_to_i_ceil($value));
94             }
95             sub next {
96 50     50 1 808 my ($self) = @_;
97 50         79 my $i = $self->{'i'}++;
98 50 50       108 if ($i == _UV_I_LIMIT) {
99 0         0 $self->{'i'} = Math::NumSeq::_to_bigint($self->{'i'});
100             }
101 50         123 return ($i, $i*$i*$i);
102             }
103             sub ith {
104 39     39 1 86 my ($self, $i) = @_;
105 39         105 return $i*$i*$i;
106             }
107              
108             # This used to be a test for cbrt($n) being an integer, but found some amd64
109             # glibc where cbrt(27) was not 3 but instead 3.00000000000000044. Dunno if
110             # cbrt(cube) ought to be an exact integer, so instead try multiplying back
111             # the integer nearest cbrt().
112             #
113             # Multiplying back should also ensure that a floating point $n bigger than
114             # 2^53 won't look like a cube due to rounding.
115             #
116             sub pred {
117 258     258 1 1202 my ($self, $value) = @_;
118 258         313 my $int = int($value);
119 258 100       525 if ($value != $int) {
120 125         243 return 0;
121             }
122 133         217 my $i = _cbrt_floor ($value);
123 133         345 return ($i*$i*$i == $value);
124             }
125              
126             sub value_to_i {
127 30     30 1 119 my ($self, $value) = @_;
128 30         52 my $int = int($value);
129 30 100       163 if ($value == $int) {
130 18         171 my $i = _cbrt_floor ($int);
131 18 100       1055 if ($int == $self->ith($i)) {
132 13         860 return $i;
133             }
134             }
135 17         36 return undef;
136             }
137             sub value_to_i_floor {
138 70     70 1 823 my ($self, $value) = @_;
139 70         118 return _cbrt_floor($value);
140             }
141             sub value_to_i_ceil {
142 67     67 1 179 my ($self, $value) = @_;
143 67         115 return _cbrt_ceil($value);
144             }
145             *value_to_i_estimate = \&value_to_i_floor;
146              
147              
148             #------------------------------------------------------------------------------
149             # generic, shared
150              
151             sub _cbrt_floor {
152 636     636   1176 my ($x) = @_;
153 636 100       1552 if (ref $x) {
154 29 50       114 if ($x->isa('Math::BigInt')) {
155 29         79 return $x->copy->broot(3);
156             }
157 0 0 0     0 if ($x->isa('Math::BigRat') || $x->isa('Math::BigFloat')) {
158 0         0 return $x->as_int->broot(3);
159             }
160             }
161 607         3997 return floor(cbrt($x));
162             }
163              
164             sub _cbrt_ceil {
165 67     67   81 my ($x) = @_;
166 67         98 my $c = _cbrt_floor($x);
167 67 100       1513 if ($c*$c*$c < $x) {
168 29         43 $c += 1;
169             }
170 67         1130 return $c;
171             }
172              
173             1;
174             __END__