File Coverage

blib/lib/DateTime/Util/Calc.pm
Criterion Covered Total %
statement 77 112 68.7
branch 16 38 42.1
condition 3 8 37.5
subroutine 26 38 68.4
pod 24 24 100.0
total 146 220 66.3


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-2007 Daisuke Maki
4              
5             package DateTime::Util::Calc;
6 7     7   191271 use strict;
  7         15  
  7         296  
7 7     7   38 use warnings;
  7         16  
  7         216  
8 7     7   36 use Carp qw(carp);
  7         18  
  7         518  
9 7     7   56 use Exporter;
  7         19  
  7         267  
10 7     7   13029 use DateTime;
  7         1493356  
  7         485  
11 7     7   13576 use Math::BigInt ('lib' => 'GMP,Pari,FastCalc');
  7         224815  
  7         47  
12 7     7   185079 use Math::BigFloat ('lib' => 'GMP,Pari,FastCalc');
  7         217589  
  7         58  
13 7     7   21988 use Math::Trig ();
  7         158686  
  7         197  
14 7     7   85 use POSIX();
  7         18  
  7         237  
15              
16 7     7   108 use constant RATA_DIE => DateTime->new(year => 1, time_zone => 'UTC');
  7         15  
  7         77  
17              
18 7     7   3528 use vars qw($VERSION @EXPORT_OK);
  7         15  
  7         468  
19 7     7   38 use vars qw($DOWNGRADE_ACCURACY);
  7         23  
  7         790  
20             BEGIN
21             {
22 7     7   23 *import = \&Exporter::import;
23 7         16 $VERSION = '0.13002';
24 7         85 @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         11845 $DOWNGRADE_ACCURACY = 32;
42             }
43              
44 0     0 1 0 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 2819 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         25 my $x = Math::BigFloat->new(shift @_);
88 3         398 my $v = Math::BigFloat->bzero();
89 3         104 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       221 if ($x == $v) {
94 1         82 return $ret;
95             }
96              
97 2         184 while (@_) {
98 5         1076 $v = $x * ($v + pop @_);
99             }
100 2         783 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       29 return Math::Trig::deg2rad($deg > 360 ? $deg % 360 : $deg);
107             }
108              
109 2     2 1 660 sub sin_deg { CORE::sin(deg2rad($_[0])) }
110 2     2 1 1588 sub cos_deg { CORE::cos(deg2rad($_[0])) }
111 1     1 1 688 sub tan_deg { Math::Trig::tan(deg2rad($_[0])) }
112             sub asin_deg
113             {
114 1 50   1 1 15 my $v = ref($_[0]) ? $_[0]->bstr() : $_[0];
115 1         6 return Math::Trig::rad2deg(Math::Trig::asin($v));
116             }
117              
118             sub acos_deg
119             {
120 1 50   1 1 15 my $v = ref($_[0]) ? $_[0]->bstr() : $_[0];
121 1         7 Math::Trig::rad2deg(Math::Trig::acos($v));
122             }
123              
124             sub mod
125             {
126 17     17 1 5905 my ($x, $y) = @_;
127              
128             # x mod y = x - y * (floor(x/y));
129              
130 17 100 66     84 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       9 $y = Math::BigFloat->new($y) if ! ref ($y);
134              
135 2         128 return $x - $y * ( ($x / $y)->bfloor );
136             } else {
137 15         166 return $x - $y * ( POSIX::floor($x / $y) );
138             }
139             }
140              
141 3 100   3 1 13 sub amod { mod($_[0], $_[1]) || $_[1]; }
142 3 50   3 1 29 sub min { $_[0] > $_[1] ? $_[1] : $_[0] }
143 3 100   3 1 16 sub max { $_[0] < $_[1] ? $_[1] : $_[0] }
144              
145             sub moment
146             {
147 0     0 1 0 my $dt = shift;
148 0         0 my($rd, $seconds) = $dt->utc_rd_values;
149 0         0 return $rd + $seconds / (24 * 3600);
150             }
151              
152             sub dt_from_moment
153             {
154 0     0 1 0 my $moment = Math::BigFloat->new('' . shift);
155              
156             # Truncate the moment down to an int
157 0         0 my $rd_days = $moment->as_int();
158              
159             # Upgrade here to BigFloat to maintain accuracy to the second
160 0         0 my $time = ($moment - $rd_days) * 24 * 3600;
161 0         0 my $dt = rata_die();
162              
163 0 0 0     0 if ($rd_days || $time) {
164 0         0 $dt->add(
165             days => ($rd_days - 1)->bstr(),
166             seconds => $time->as_int()->bstr(),
167             );
168 0         0 $dt->truncate(to => 'second');
169             }
170 0         0 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 22 my %args = @_;
197 1         3 my $x = $args{base};
198 1         3 my $check = $args{check};
199 1   50     6 my $next = $args{next} || \&__increment_one;
200 1         4 while (! $check->($x) ) {
201 6         40 $x = $next->($x);
202             }
203 1         9 return $x;
204             }
205              
206             sub truncate_to_midday
207             {
208 1     1 1 462 $_[0]->truncate(to => 'hour');
209 1         282 $_[0]->set( hour => 12 );
210 1         366 $_[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__