File Coverage

blib/lib/Math/NumSeq/Pronic.pm
Criterion Covered Total %
statement 68 69 98.5
branch 11 12 91.6
condition n/a
subroutine 21 21 100.0
pod 8 8 100.0
total 108 110 98.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::Pronic;
19 2     2   14169 use 5.004;
  2         9  
  2         91  
20 2     2   13 use strict;
  2         4  
  2         87  
21 2     2   820 use POSIX 'ceil';
  2         8297  
  2         25  
22 2     2   1526 use List::Util 'max';
  2         5  
  2         213  
23              
24 2     2   13 use vars '$VERSION','@ISA';
  2         5  
  2         157  
25             $VERSION = 71;
26              
27 2     2   739 use Math::NumSeq;
  2         4  
  2         70  
28 2     2   532 use Math::NumSeq::Base::IterateIth;
  2         4  
  2         111  
29             @ISA = ('Math::NumSeq::Base::IterateIth',
30             'Math::NumSeq');
31              
32             # uncomment this to run the ### lines
33             #use Smart::Comments;
34              
35             # use constant name => Math::NumSeq::__('Pronic Numbers');
36 2     2   11 use constant description => Math::NumSeq::__('The pronic numbers 0, 2, 6, 12, 20, 30, etc, etc, i*(i+1). These are twice the triangular numbers, and half way between perfect squares.');
  2         4  
  2         11  
37 2     2   11 use constant default_i_start => 0;
  2         4  
  2         83  
38 2     2   9 use constant characteristic_increasing => 1;
  2         5  
  2         81  
39 2     2   10 use constant characteristic_integer => 1;
  2         4  
  2         164  
40 2     2   10 use constant values_min => 0; # at i=0
  2         4  
  2         94  
41 2     2   9 use constant oeis_anum => 'A002378'; # pronic, starting from 0
  2         6  
  2         906  
42              
43             sub rewind {
44 8     8 1 552 my ($self) = @_;
45 8         35 $self->{'i'} = $self->i_start;
46             }
47             sub seek_to_i {
48 23     23 1 483 my ($self, $i) = @_;
49 23         57 $self->{'i'} = $i;
50             }
51             sub seek_to_value {
52 14     14 1 1795 my ($self, $value) = @_;
53 14         31 $self->seek_to_i($self->value_to_i_ceil($value));
54             }
55             sub ith {
56 166     166 1 259 my ($self, $i) = @_;
57 166         475 return $i*($i+1);
58             }
59              
60             # [0,1,2,3,4],[0,2,6,12,20]
61             # N = (d^2 + d)
62             # = ($d**2 + $d)
63             # = (($d + 1)*$d)
64             # d = -1/2 + sqrt(1 * $n + 1/4)
65             # = (-1 + 2*sqrt(1 * $n + 1/4))/2
66             # = (-1 + sqrt(4 * $n + 1))/2
67             # = (sqrt(4*$n + 1) - 1)/2
68              
69             sub pred {
70 92     92 1 498 my ($self, $value) = @_;
71 92 50       184 if ($value < 0) { return 0; }
  0         0  
72 92         164 my $i = $self->value_to_i_floor($value);
73 92         254 return ($value == $i*($i+1));
74             }
75              
76             sub value_to_i {
77 38     38 1 171 my ($self, $value) = @_;
78 38 100       107 if ($value >= 0) {
79 34         974 my $int = int($value);
80 34 100       201 if ($value == $int) {
81 20         218 my $i = int((sqrt(4*$int + 1) - 1)/2);
82 20 100       4387 if ($int == $self->ith($i)) {
83 14         1475 return $i;
84             }
85             }
86             }
87 24         58 return undef;
88             }
89             sub value_to_i_floor {
90 227     227 1 1515 my ($self, $value) = @_;
91 227 100       477 if ($value < 0) {
92 17         46 return 0;
93             }
94 210         2603 return int((sqrt(4*int($value) + 1) - 1)/2);
95             }
96             sub value_to_i_ceil {
97 64     64 1 215 my ($self, $value) = @_;
98 64         120 my $i = $self->value_to_i_floor($value);
99 64 100       4519 if ($self->ith($i) < $value) {
100 21         69 return $i+1;
101             } else {
102 43         1580 return $i;
103             }
104             }
105             *value_to_i_estimate = \&value_to_i_floor;
106              
107             1;
108             __END__