File Coverage

blib/lib/Math/NumSeq/Triangular.pm
Criterion Covered Total %
statement 67 68 98.5
branch 15 16 93.7
condition n/a
subroutine 18 18 100.0
pod 5 5 100.0
total 105 107 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   7221 use 5.004;
  2         6  
20 2     2   6 use strict;
  2         1  
  2         38  
21 2     2   366 use POSIX 'ceil';
  2         4883  
  2         12  
22 2     2   842 use List::Util 'max';
  2         2  
  2         137  
23              
24 2     2   7 use vars '$VERSION','@ISA';
  2         6  
  2         100  
25             $VERSION = 72;
26              
27 2     2   353 use Math::NumSeq;
  2         3  
  2         35  
28 2     2   380 use Math::NumSeq::Base::IterateIth;
  2         3  
  2         71  
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   6 use constant description => Math::NumSeq::__('The triangular numbers 0, 1, 3, 6, 10, 15, 21, 28, etc, i*(i+1)/2.');
  2         2  
  2         5  
38 2     2   6 use constant characteristic_increasing => 1;
  2         3  
  2         63  
39 2     2   6 use constant characteristic_integer => 1;
  2         2  
  2         63  
40 2     2   5 use constant default_i_start => 0;
  2         2  
  2         58  
41 2     2   6 use constant values_min => 0; # at i=0
  2         2  
  2         61  
42              
43             #------------------------------------------------------------------------------
44             # cf A062828 gcd(2n,triangular(n))
45             #
46              
47 2     2   6 use constant oeis_anum => 'A000217'; # starting from i=0 value=0
  2         3  
  2         519  
48              
49             #------------------------------------------------------------------------------
50              
51             sub ith {
52 126     126 1 122 my ($self, $i) = @_;
53 126         209 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 131 my ($self, $value) = @_;
64             ### Triangular pred(): $value
65              
66 50 50       57 if ($value < 0) { return 0; }
  0         0  
67 50         33 my $int = int($value);
68 50 100       55 if ($value != $int) { return 0; }
  21         20  
69              
70 29         12 $int *= 2;
71 29         25 my $i = int((sqrt(4*$int + 1) - 1)/2);
72              
73             ### $int
74             ### $i
75             ### triangular: ($i+1)*$i/2
76              
77 29         34 return ($int == ($i+1)*$i);
78             }
79              
80             sub value_to_i {
81 35     35 1 82 my ($self, $value) = @_;
82 35 100       61 if ($value >= 0) {
83 31         638 my $int = int($value);
84 31 100       113 if ($value == $int) {
85 19         134 my $i = int((sqrt(8*$int + 1) - 1)/2);
86 19 100       2692 if ($int == $self->ith($i)) {
87 14         1428 return $i;
88             }
89             }
90             }
91 21         23 return undef;
92             }
93             sub value_to_i_floor {
94 110     110 1 713 my ($self, $value) = @_;
95 110 100       139 if ($value < 0) {
96 14         19 return 0;
97             }
98 96         1303 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 104 my ($self, $value) = @_;
104             ### value_to_i_ceil(): $value
105 48 100       77 if ($value <= 0) {
106 7         87 return 0;
107             }
108 41         584 my $i = $self->value_to_i_floor($value);
109 41 100       2561 if ($self->ith($i) < $value) {
110 16         32 return $i+1;
111             } else {
112 25         1319 return $i;
113             }
114             }
115              
116             1;
117             __END__