File Coverage

blib/lib/DateTime/Util/Calc.pm
Criterion Covered Total %
statement 89 112 79.4
branch 17 38 44.7
condition 4 8 50.0
subroutine 29 38 76.3
pod 24 24 100.0
total 163 220 74.0


line stmt bran cond sub pod time code
1             # $Id: /local/datetime/modules/DateTime-Util-Calc/trunk/lib/DateTime/Util/Calc.pm 11779 2007-05-29T22:12:48.788920Z daisuke $
2             #
3             # Copyright (c) 2004-2015 Daisuke Maki
4              
5             package DateTime::Util::Calc;
6 7     7   115669 use strict;
  7         13  
  7         266  
7 7     7   30 use warnings;
  7         7  
  7         181  
8 7     7   25 use Carp qw(carp);
  7         13  
  7         459  
9 7     7   39 use Exporter;
  7         18  
  7         196  
10 7     7   5769 use DateTime;
  7         821057  
  7         317  
11 7     7   7850 use Math::BigInt ('lib' => 'GMP,Pari,FastCalc');
  7         118619  
  7         40  
12 7     7   106625 use Math::BigFloat ('lib' => 'GMP,Pari,FastCalc');
  7         124127  
  7         42  
13 7     7   15840 use Math::Trig ();
  7         111888  
  7         173  
14 7     7   60 use POSIX();
  7         9  
  7         185  
15              
16 7     7   81 use constant RATA_DIE => DateTime->new(year => 1, time_zone => 'UTC');
  7         11  
  7         59  
17              
18 7     7   2979 use vars qw($VERSION @EXPORT_OK);
  7         11  
  7         388  
19 7     7   29 use vars qw($DOWNGRADE_ACCURACY);
  7         9  
  7         595  
20             BEGIN
21             {
22 7     7   20 *import = \&Exporter::import;
23 7         13 $VERSION = '0.13003';
24 7         33 @EXPORT_OK = qw(
25             bf_downgrade
26             bi_downgrade
27             binary_search
28             search_next
29             angle
30             polynomial
31             sin_deg
32             cos_deg
33             tan_deg
34             asin_deg acos_deg mod amod min
35             max bigfloat bigint moment dt_from_moment rata_die
36             truncate_to_midday
37             revolution
38             rev180
39             );
40              
41 7         7349 $DOWNGRADE_ACCURACY = 32;
42             }
43              
44 10     10 1 34 sub rata_die { RATA_DIE->clone }
45              
46             sub bigfloat
47             {
48             return
49 0 0   0 1 0 UNIVERSAL::isa($_[0], 'Math::BigFloat') ? $_[0] :
50             Math::BigFloat->new($_[0]);
51             }
52              
53             sub bigint
54             {
55 0 0   0 1 0 return UNIVERSAL::isa($_[0], 'Math::BigInt') ? $_[0] : Math::BigInt->new($_[0]);
56             }
57              
58             my $warn_bf_downgrade = 0;
59             my $warn_bi_downgrade = 0;
60             sub bf_downgrade
61             {
62 0 0   0 1 0 $warn_bf_downgrade++ or carp "DateTime::Util::Calc::bf_downgrade has been deprecated, and will be removed in future versions.";
63 0         0 return $_[0];
64             }
65              
66             sub bi_downgrade
67             {
68 0 0   0 1 0 $warn_bi_downgrade++ or carp "DateTime::Util::Calc::bi_downgrade has been deprecated, and will be removed in future versions.";
69 0         0 return $_[0];
70             }
71              
72             sub angle
73             {
74 0     0 1 0 Math::BigFloat->new($_[0]) + (Math::BigFloat->new($_[1]) + (Math::BigFloat->new($_[2]) / 60)) / 60;
75             }
76              
77             # polynomial($x, $a(0) ... $a(n))
78             sub polynomial
79             {
80 3 50   3 1 1661 if (@_ == 1) {
81 0         0 require Carp;
82 0         0 Carp::croak('polynomial requires at least two arguments: polynomial($x, @coeffients)');
83             }
84              
85             # XXX - There seems to be a bug in adding BigInt and BigFloat
86             # Math::BigFloat->bzero must be used
87 3         22 my $x = Math::BigFloat->new(shift @_);
88 3         382 my $v = Math::BigFloat->bzero();
89 3         114 my $ret = Math::BigFloat->new(shift @_);
90              
91             # reuse $v for sake of efficiency. we just want to check if $x
92             # is zero or not
93 3 100       162 if ($x == $v) {
94 1         112 return $ret;
95             }
96              
97 2         151 while (@_) {
98 5         869 $v = $x * ($v + pop @_);
99             }
100 2         595 return $ret + $v;
101             }
102              
103             sub deg2rad
104             {
105 5 50   5 1 19 my $deg = ref($_[0]) ? $_[0]->bstr() : $_[0];
106 5 50       31 return Math::Trig::deg2rad($deg > 360 ? $deg % 360 : $deg);
107             }
108              
109 2     2 1 716 sub sin_deg { CORE::sin(deg2rad($_[0])) }
110 2     2 1 14961 sub cos_deg { CORE::cos(deg2rad($_[0])) }
111 1     1 1 665 sub tan_deg { Math::Trig::tan(deg2rad($_[0])) }
112             sub asin_deg
113             {
114 1 50   1 1 17 my $v = ref($_[0]) ? $_[0]->bstr() : $_[0];
115 1         7 return Math::Trig::rad2deg(Math::Trig::asin($v));
116             }
117              
118             sub acos_deg
119             {
120 1 50   1 1 16 my $v = ref($_[0]) ? $_[0]->bstr() : $_[0];
121 1         4 Math::Trig::rad2deg(Math::Trig::acos($v));
122             }
123              
124             sub mod
125             {
126 17     17 1 4454 my ($x, $y) = @_;
127              
128             # x mod y = x - y * (floor(x/y));
129              
130 17 100 66     73 if (ref($x) || ref($y)) {
131             # Make sure both are M::BF
132 2 50       9 $x = Math::BigFloat->new($x) if ! ref ($x);
133 2 50       8 $y = Math::BigFloat->new($y) if ! ref ($y);
134              
135 2         112 return $x - $y * ( ($x / $y)->bfloor );
136             } else {
137 15         151 return $x - $y * ( POSIX::floor($x / $y) );
138             }
139             }
140              
141 3 100   3 1 12 sub amod { mod($_[0], $_[1]) || $_[1]; }
142 3 50   3 1 25 sub min { $_[0] > $_[1] ? $_[1] : $_[0] }
143 3 100   3 1 15 sub max { $_[0] < $_[1] ? $_[1] : $_[0] }
144              
145             sub moment
146             {
147 10     10 1 27652 my $dt = shift;
148 10         28 my($rd, $seconds) = $dt->utc_rd_values;
149 10         171 return $rd + $seconds / (24 * 3600);
150             }
151              
152             sub dt_from_moment
153             {
154 10     10 1 3886 my $moment = Math::BigFloat->new('' . shift);
155              
156             # Truncate the moment down to an int
157 10         1493 my $rd_days = $moment->as_int();
158              
159             # Upgrade here to BigFloat to maintain accuracy to the second
160 10         1160 my $time = ($moment - $rd_days) * 24 * 3600;
161 10         6580 my $dt = rata_die();
162              
163 10 50 33     127 if ($rd_days || $time) {
164 10         210 $dt->add(
165             days => ($rd_days - 1)->bstr(),
166             seconds => $time->as_int()->bstr(),
167             );
168 10         7199 $dt->truncate(to => 'second');
169             }
170 10         2650 return $dt;
171             }
172              
173            
174             sub binary_search
175             {
176 0     0 1 0 my ($lo, $hi, $mu, $phi) = @_;
177              
178 0         0 $lo = Math::BigFloat->new($lo);
179 0         0 $hi = Math::BigFloat->new($hi);
180              
181 0         0 while (1) {
182 0         0 my $x = ($lo + $hi) / 2;
183 0 0       0 if ($mu->($lo, $hi)) {
    0          
184 0         0 return $x;
185             } elsif ($phi->($x)) {
186 0         0 $hi = $x;
187             } else {
188 0         0 $lo = $x;
189             }
190             }
191             }
192              
193 0     0   0 sub __increment_one { $_[0] + 1 }
194             sub search_next
195             {
196 1     1 1 16 my %args = @_;
197 1         2 my $x = $args{base};
198 1         3 my $check = $args{check};
199 1   50     4 my $next = $args{next} || \&__increment_one;
200 1         3 while (! $check->($x) ) {
201 6         24 $x = $next->($x);
202             }
203 1         6 return $x;
204             }
205              
206             sub truncate_to_midday
207             {
208 1     1 1 320 $_[0]->truncate(to => 'hour');
209 1         192 $_[0]->set( hour => 12 );
210 1         247 $_[0];
211             }
212              
213             sub revolution
214             {
215             #
216             #
217             # FUNCTIONAL SEQUENCE for revolution
218             #
219             # _GIVEN
220             # any angle
221             #
222             # _THEN
223             #
224             # reduces any angle to within the first revolution
225             # by subtracting or adding even multiples of 360.0
226             #
227             #
228             # _RETURN
229             #
230             # the value of the input is >= 0.0 and < 360.0
231             #
232              
233 0     0 1   my $x = $_[0];
234 0           return ( $x - 360.0 * POSIX::floor( $x * ( 1.0 / 360.0 ) ) );
235             }
236              
237             sub rev180
238             {
239             #
240             #
241             # FUNCTIONAL SEQUENCE for rev180
242             #
243             # _GIVEN
244             #
245             # any angle
246             #
247             # _THEN
248             #
249             # Reduce input to within +180..+180 degrees
250             #
251             #
252             # _RETURN
253             #
254             # angle that was reduced
255             #
256 0     0 1   my ($x) = @_;
257 0           return ( $x - 360.0 * POSIX::floor( $x * ( 1.0 / 360.0 ) + 0.5 ) );
258             }
259              
260              
261             1;
262              
263             __END__