File Coverage

blib/lib/Math/NumSeq/Cubes.pm
Criterion Covered Total %
statement 91 95 95.7
branch 15 20 75.0
condition 0 3 0.0
subroutine 25 25 100.0
pod 9 9 100.0
total 140 152 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   5998 use 5.004;
  3         7  
20 3     3   10 use strict;
  3         4  
  3         51  
21 3     3   1066 use Math::Libm 'cbrt';
  3         10784  
  3         178  
22 3     3   824 use POSIX 'floor','ceil';
  3         8867  
  3         15  
23 3     3   1668 use List::Util 'max';
  3         3  
  3         174  
24              
25 3     3   10 use vars '$VERSION', '@ISA';
  3         3  
  3         134  
26             $VERSION = 72;
27 3     3   363 use Math::NumSeq;
  3         5  
  3         91  
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   10 use constant description => Math::NumSeq::__('The cubes 1, 8, 27, 64, 125, etc, k*k*k.');
  3         2  
  3         7  
36 3     3   11 use constant i_start => 0;
  3         2  
  3         103  
37 3     3   10 use constant values_min => 0;
  3         5  
  3         120  
38 3     3   8 use constant characteristic_increasing => 1;
  3         4  
  3         113  
39 3     3   21 use constant characteristic_integer => 1;
  3         2  
  3         93  
40 3     3   8 use constant oeis_anum => 'A000578';
  3         3  
  3         287  
41              
42 3         9 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         3 my $max = ~0;
54              
55 3         2 my $bit = 4;
56 3         12 for (my $i = $max; $i != 0; $i=int($i/8)) {
57 66         88 $bit *= 2;
58             }
59             ### $bit
60              
61 3         4 my $cbrt = 0;
62 3         6 for ( ; $bit != 0; $bit=int($bit/2)) {
63 75         43 my $try = $cbrt + $bit;
64 75 100       126 if ($try * $try <= $max / $try) {
65 24         34 $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         1280 $cbrt
78 3     3   21 };
  3         47  
79              
80             sub rewind {
81 8     8 1 332 my ($self) = @_;
82 8         39 $self->{'i'} = $self->i_start;
83             }
84             sub seek_to_i {
85 23     23 1 79 my ($self, $i) = @_;
86 23 50       30 if ($i >= _UV_I_LIMIT) {
87 0         0 $i = Math::NumSeq::_to_bigint($i);
88             }
89 23         27 $self->{'i'} = $i;
90             }
91             sub seek_to_value {
92 17     17 1 555 my ($self, $value) = @_;
93 17         23 $self->seek_to_i($self->value_to_i_ceil($value));
94             }
95             sub next {
96 50     50 1 476 my ($self) = @_;
97 50         41 my $i = $self->{'i'}++;
98 50 50       61 if ($i == _UV_I_LIMIT) {
99 0         0 $self->{'i'} = Math::NumSeq::_to_bigint($self->{'i'});
100             }
101 50         60 return ($i, $i*$i*$i);
102             }
103             sub ith {
104 39     39 1 51 my ($self, $i) = @_;
105 39         59 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 581 my ($self, $value) = @_;
118 258         155 my $int = int($value);
119 258 100       314 if ($value != $int) {
120 125         119 return 0;
121             }
122 133         118 my $i = _cbrt_floor ($value);
123 133         164 return ($i*$i*$i == $value);
124             }
125              
126             sub value_to_i {
127 30     30 1 67 my ($self, $value) = @_;
128 30         34 my $int = int($value);
129 30 100       106 if ($value == $int) {
130 18         123 my $i = _cbrt_floor ($int);
131 18 100       666 if ($int == $self->ith($i)) {
132 13         535 return $i;
133             }
134             }
135 17         19 return undef;
136             }
137             sub value_to_i_floor {
138 70     70 1 430 my ($self, $value) = @_;
139 70         71 return _cbrt_floor($value);
140             }
141             sub value_to_i_ceil {
142 67     67 1 94 my ($self, $value) = @_;
143 67         67 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   492 my ($x) = @_;
153 636 100       735 if (ref $x) {
154 29 50       66 if ($x->isa('Math::BigInt')) {
155 29         44 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         1152 return floor(cbrt($x));
162             }
163              
164             sub _cbrt_ceil {
165 67     67   49 my ($x) = @_;
166 67         55 my $c = _cbrt_floor($x);
167 67 100       635 if ($c*$c*$c < $x) {
168 29         24 $c += 1;
169             }
170 67         696 return $c;
171             }
172              
173             1;
174             __END__