File Coverage

blib/lib/Math/NumSeq/Triangular.pm
Criterion Covered Total %
statement 68 69 98.5
branch 15 16 93.7
condition n/a
subroutine 18 18 100.0
pod 5 5 100.0
total 106 108 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::Triangular;
19 2     2   28771 use 5.004;
  2         9  
  2         99  
20 2     2   13 use strict;
  2         5  
  2         97  
21 2     2   1406 use POSIX 'ceil';
  2         9736  
  2         17  
22 2     2   1773 use List::Util 'max';
  2         5  
  2         208  
23              
24 2     2   12 use vars '$VERSION','@ISA';
  2         5  
  2         155  
25             $VERSION = 71;
26              
27 2     2   2724 use Math::NumSeq;
  2         4  
  2         52  
28 2     2   1586 use Math::NumSeq::Base::IterateIth;
  2         5  
  2         113  
29             @ISA = ('Math::NumSeq::Base::IterateIth',
30             'Math::NumSeq');
31              
32             # uncomment this to run the ### lines
33             # use Smart::Comments;
34              
35              
36             # use constant name => Math::NumSeq::__('Triangular Numbers');
37 2     2   11 use constant description => Math::NumSeq::__('The triangular numbers 0, 1, 3, 6, 10, 15, 21, 28, etc, i*(i+1)/2.');
  2         3  
  2         16  
38 2     2   10 use constant characteristic_increasing => 1;
  2         13  
  2         98  
39 2     2   10 use constant characteristic_integer => 1;
  2         5  
  2         86  
40 2     2   9 use constant default_i_start => 0;
  2         5  
  2         85  
41 2     2   11 use constant values_min => 0; # at i=0
  2         5  
  2         94  
42              
43             #------------------------------------------------------------------------------
44             # cf A062828 gcd(2n,triangular(n))
45             #
46              
47 2     2   11 use constant oeis_anum => 'A000217'; # starting from i=0 value=0
  2         3  
  2         876  
48              
49             #------------------------------------------------------------------------------
50              
51             sub ith {
52 126     126 1 229 my ($self, $i) = @_;
53 126         483 return $i*($i+1)/2;
54             }
55              
56             # [1,2,3,4,5],[1,3,6,10,15]
57             # N = ((1/2*$d + 1/2)*$d)
58             # d = -1/2 + sqrt(2 * $n + 1/4)
59             # = (-1 + 2*sqrt(2 * $n + 1/4))/2
60             # = (sqrt(4*2*$n + 1) - 1)/2
61             # = (sqrt(8*$n + 1) - 1)/2
62             sub pred {
63 50     50 1 448 my ($self, $value) = @_;
64             ### Triangular pred(): $value
65              
66 50 50       99 if ($value < 0) { return 0; }
  0         0  
67 50         63 my $int = int($value);
68 50 100       97 if ($value != $int) { return 0; }
  21         54  
69              
70 29         37 $int *= 2;
71 29         64 my $i = int((sqrt(4*$int + 1) - 1)/2);
72              
73             ### $int
74             ### $i
75             ### triangular: ($i+1)*$i/2
76              
77 29         85 return ($int == ($i+1)*$i);
78             }
79              
80             sub value_to_i {
81 35     35 1 170 my ($self, $value) = @_;
82 35 100       100 if ($value >= 0) {
83 31         974 my $int = int($value);
84 31 100       201 if ($value == $int) {
85 19         220 my $i = int((sqrt(8*$int + 1) - 1)/2);
86 19 100       4385 if ($int == $self->ith($i)) {
87 14         2554 return $i;
88             }
89             }
90             }
91 21         51 return undef;
92             }
93             sub value_to_i_floor {
94 110     110 1 1434 my ($self, $value) = @_;
95 110 100       251 if ($value < 0) {
96 14         41 return 0;
97             }
98 96         2224 return int((sqrt(8*int($value) + 1) - 1)/2);
99             }
100             *value_to_i_estimate = \&value_to_i_floor;
101              
102             sub value_to_i_ceil {
103 48     48 1 196 my ($self, $value) = @_;
104             ### value_to_i_ceil(): $value
105 48 100       116 if ($value <= 0) {
106 7         147 return 0;
107             }
108 41         839 my $i = $self->value_to_i_floor($value);
109 41 100       4236 if ($self->ith($i) < $value) {
110 16         55 return $i+1;
111             } else {
112 25         2234 return $i;
113             }
114             }
115              
116             1;
117             __END__