File Coverage

blib/lib/Math/FFT.pm
Criterion Covered Total %
statement 334 370 90.2
branch 77 164 46.9
condition 18 36 50.0
subroutine 31 31 100.0
pod 23 23 100.0
total 483 624 77.4


line stmt bran cond sub pod time code
1             package Math::FFT;
2              
3 3     3   196834 use strict;
  3         32  
  3         85  
4 3     3   16 use warnings;
  3         5  
  3         82  
5              
6 3     3   74 use 5.008;
  3         9  
7              
8 3     3   17 use vars qw(@ISA);
  3         6  
  3         12051  
9             require DynaLoader;
10              
11             @ISA = qw(DynaLoader);
12              
13             # Items to export into callers namespace by default. Note: do not export
14             # names by default without a very good reason. Use EXPORT_OK instead.
15             # Do not simply export all your public functions/methods/constants.
16             our $VERSION = '1.36';
17              
18             bootstrap Math::FFT $VERSION;
19              
20             # Preloaded methods go here.
21              
22             sub new
23             {
24 21     21 1 429552 my ( $class, $data ) = @_;
25 21 50       107 die 'Must call constructor with an array reference for the data'
26             unless ref($data) eq 'ARRAY';
27 21   100     77 $data->[0] ||= 0; # keep warnings happy
28 21         41 my $n = @$data;
29 21         70 my $nip = int( 3 + sqrt($n) );
30 21         57 my $nw = int( 2 + 5 * $n / 4 );
31 21         148 my $ip = pack( "i$nip", () );
32 21         1839 my $w = pack( "d$nw", () );
33 21         204 return bless {
34             type => '',
35             mean => '',
36             coeff => '',
37             n => $n,
38             data => $data,
39             ip => \$ip,
40             w => \$w,
41             }, $class;
42             }
43              
44             # clone method to copy the ip and w arrays for data of equal size
45             sub clone
46             {
47 1     1 1 31989 my ( $self, $data ) = @_;
48 1 50       9 die 'Must call clone with an array reference for the data'
49             unless ref($data) eq 'ARRAY';
50 1   50     4 $data->[0] ||= 0; # keep warnings happy
51 1         3 my $n = @$data;
52 1 50       6 die "Cannot clone data of unequal sizes" unless $n == $self->{n};
53 1         51 my $class = ref($self);
54             return bless {
55             type => '',
56             coeff => '',
57             mean => '',
58             n => $self->{n},
59             data => $data,
60             ip => $self->{ip},
61             w => $self->{w},
62 1         13 }, $class;
63             }
64              
65             # Complex Discrete Fourier Transform
66             sub cdft
67             {
68 4     4 1 27 my $self = shift;
69 4         23 my $n = $self->{n};
70 4 50       15 die "data size ($n) must be an integer power of 2" unless _check_n($n);
71 4         13 my $data = [ @{ $self->{data} } ];
  4         6051  
72 4         14643 _cdft( $n, 1, $data, $self->{ip}, $self->{w} );
73 4         73 $self->{type} = 'cdft';
74 4         80 $self->{coeff} = $data;
75 4         18 return $data;
76             }
77              
78             # Inverse Complex Discrete Fourier Transform
79             sub invcdft
80             {
81 4     4 1 340 my $self = shift;
82 4         8 my $data;
83 4         9 my $n = $self->{n};
84 4 50       16 if ( my $arg = shift )
85             {
86 0 0       0 if ( ref($arg) ne 'ARRAY' )
87             {
88 0         0 die 'Must pass an array reference to invcdft';
89             }
90 0 0       0 if ( $n != @$arg )
91             {
92 0         0 die "Size of data set must be $n";
93             }
94 0         0 $data = [@$arg];
95             }
96             else
97             {
98             die 'Must invert data created with cdft'
99 4 50       16 unless $self->{type} eq 'cdft';
100 4         7 $data = [ @{ $self->{coeff} } ];
  4         5438  
101             }
102 4         14213 _cdft( $n, -1, $data, $self->{ip}, $self->{w} );
103 4         14121 $_ *= 2.0 / $n for (@$data);
104 4         491 return $data;
105             }
106              
107             # Real Discrete Fourier Transform
108             sub rdft
109             {
110 7     7 1 27 my $self = shift;
111 7         12 my $n = $self->{n};
112 7 50       22 if ( !_check_n($n) )
113             {
114 0         0 die "data size ($n) must be an integer power of 2";
115             }
116 7         13 my $data = [ @{ $self->{data} } ];
  7         1024  
117 7         5506 _rdft( $n, 1, $data, $self->{ip}, $self->{w} );
118 7         16 $self->{type} = 'rdft';
119 7         15 $self->{coeff} = $data;
120 7         24 return $data;
121             }
122              
123             # Inverse Real Discrete Fourier Transform
124             sub invrdft
125             {
126 3     3 1 763 my $self = shift;
127 3         6 my $data;
128 3         7 my $n = $self->{n};
129 3 50       11 if ( my $arg = shift )
130             {
131 0 0       0 die 'Must pass an array reference to invrdft'
132             unless ref($arg) eq 'ARRAY';
133 0 0       0 die "Size of data set must be $n"
134             unless $n == @$arg;
135 0         0 $data = [@$arg];
136             }
137             else
138             {
139             die 'Must invert data created with rdft'
140 3 50       13 unless $self->{type} eq 'rdft';
141 3         8 $data = [ @{ $self->{coeff} } ];
  3         1039  
142             }
143 3         5415 _rdft( $n, -1, $data, $self->{ip}, $self->{w} );
144 3         4537 $_ *= 2.0 / $n for (@$data);
145 3         14 return $data;
146             }
147              
148             # Discrete Cosine Transform
149             sub ddct
150             {
151 2     2 1 16 my $self = shift;
152 2         7 my $n = $self->{n};
153 2 50       8 die "data size ($n) must be an integer power of 2" unless _check_n($n);
154 2         6 my $data = [ @{ $self->{data} } ];
  2         1027  
155 2         6212 _ddct( $n, -1, $data, $self->{ip}, $self->{w} );
156 2         15 $self->{type} = 'ddct';
157 2         8 $self->{coeff} = $data;
158 2         9 return $data;
159             }
160              
161             # Inverse Discrete Cosine Transform
162             sub invddct
163             {
164 2     2 1 330 my $self = shift;
165 2         4 my $data;
166 2         6 my $n = $self->{n};
167 2 50       8 if ( my $arg = shift )
168             {
169 0 0       0 die 'Must pass an array reference to invddct'
170             unless ref($arg) eq 'ARRAY';
171 0 0       0 die "Size of data set must be $n"
172             unless $n == @$arg;
173 0         0 $data = [@$arg];
174             }
175             else
176             {
177             die 'Must invert data created with ddct'
178 2 50       11 unless $self->{type} eq 'ddct';
179 2         4 $data = [ @{ $self->{coeff} } ];
  2         1084  
180             }
181 2         8 $data->[0] *= 0.5;
182 2         5195 _ddct( $n, 1, $data, $self->{ip}, $self->{w} );
183 2         4653 $_ *= 2.0 / $n for (@$data);
184 2         11 return $data;
185             }
186              
187             # Discrete Sine Transform
188             sub ddst
189             {
190 2     2 1 15 my $self = shift;
191 2         6 my $n = $self->{n};
192 2 50       7 die "data size ($n) must be an integer power of 2" unless _check_n($n);
193 2         6 my $data = [ @{ $self->{data} } ];
  2         1000  
194 2         6260 _ddst( $n, -1, $data, $self->{ip}, $self->{w} );
195 2         9 $self->{type} = 'ddst';
196 2         6 $self->{coeff} = $data;
197 2         5 return $data;
198             }
199              
200             # Inverse Discrete Sine Transform
201             sub invddst
202             {
203 2     2 1 325 my $self = shift;
204 2         5 my $data;
205 2         5 my $n = $self->{n};
206 2 50       8 if ( my $arg = shift )
207             {
208 0 0       0 die 'Must pass an array reference to invddst'
209             unless ref($arg) eq 'ARRAY';
210 0 0       0 die "Size of data set must be $n"
211             unless $n == @$arg;
212 0         0 $data = [@$arg];
213             }
214             else
215             {
216             die 'Must invert data created with ddst'
217 2 50       8 unless $self->{type} eq 'ddst';
218 2         4 $data = [ @{ $self->{coeff} } ];
  2         1001  
219             }
220 2         7 $data->[0] *= 0.5;
221 2         5091 _ddst( $n, 1, $data, $self->{ip}, $self->{w} );
222 2         4592 $_ *= 2.0 / $n for (@$data);
223 2         11 return $data;
224             }
225              
226             # Cosine Transform of RDFT (Real Symmetric DFT)
227             sub dfct
228             {
229 2     2 1 17 my $self = shift;
230 2         7 my $np1 = $self->{n};
231 2         7 my $n = $np1 - 1;
232 2 50       9 die "data size ($n) must be an integer power of 2" unless _check_n($n);
233 2         8 my $nt = int( 2 + $n / 2 );
234 2         5 my $t = [];
235 2         5 my $data = [ @{ $self->{data} } ];
  2         1091  
236 2         8485 pdfct( $nt, $n, $data, $t, $self->{ip}, $self->{w} );
237 2         12 $self->{type} = 'dfct';
238 2         6 $self->{coeff} = $data;
239 2         252 return $data;
240             }
241              
242             # Inverse Cosine Transform of RDFT (Real Symmetric DFT)
243             sub invdfct
244             {
245 2     2 1 353 my $self = shift;
246 2         4 my $data;
247 2         6 my $np1 = $self->{n};
248 2         6 my $n = $np1 - 1;
249 2 50       9 if ( my $arg = shift )
250             {
251 0 0       0 die 'Must pass an array reference to invdfct'
252             unless ref($arg) eq 'ARRAY';
253 0 0       0 die "Size of data set must be $n"
254             unless $np1 == @$data;
255 0         0 $data = [@$arg];
256             }
257             else
258             {
259             die 'Must invert data created with dfct'
260 2 50       10 unless $self->{type} eq 'dfct';
261 2         6 $data = [ @{ $self->{coeff} } ];
  2         983  
262             }
263 2         9 my $nt = int( 2 + $n / 2 );
264 2         5 my $t = [];
265 2         6 $data->[0] *= 0.5;
266 2         4 $data->[$n] *= 0.5;
267 2         8423 pdfct( $nt, $n, $data, $t, $self->{ip}, $self->{w} );
268 2         9 $data->[0] *= 0.5;
269 2         6 $data->[$n] *= 0.5;
270 2         4573 $_ *= 2.0 / $n for (@$data);
271 2         256 return $data;
272             }
273              
274             # Sine Transform of RDFT (Real Anti-symmetric DFT)
275             sub dfst
276             {
277 2     2 1 15 my $self = shift;
278 2         5 my $n = $self->{n};
279 2 50       8 die "data size ($n) must be an integer power of 2" unless _check_n($n);
280 2         4 my $data = [ @{ $self->{data} } ];
  2         1008  
281 2         8 my $nt = int( 2 + $n / 2 );
282 2         5 my $t = [];
283 2         7751 pdfst( $nt, $n, $data, $t, $self->{ip}, $self->{w} );
284 2         11 $self->{type} = 'dfst';
285 2         5 $self->{coeff} = $data;
286 2         271 return $data;
287             }
288              
289             # Inverse Sine Transform of RDFT (Real Anti-symmetric DFT)
290             sub invdfst
291             {
292 2     2 1 333 my $self = shift;
293 2         5 my $n = $self->{n};
294 2         4 my $data;
295 2 50       8 if ( my $arg = shift )
296             {
297 0 0       0 die 'Must pass an array reference to invdfst'
298             unless ref($arg) eq 'ARRAY';
299 0 0       0 die "Size of data set must be $n"
300             unless $n == @$arg;
301 0         0 $data = [@$arg];
302             }
303             else
304             {
305             die 'Must invert data created with dfst'
306 2 50       8 unless $self->{type} eq 'dfst';
307 2         5 $data = [ @{ $self->{coeff} } ];
  2         982  
308             }
309 2         10 my $nt = int( 2 + $n / 2 );
310 2         3 my $t = [];
311 2         7194 pdfst( $nt, $n, $data, $t, $self->{ip}, $self->{w} );
312 2         4617 $_ *= 2.0 / $n for (@$data);
313 2         259 return $data;
314             }
315              
316             # check if $n is a power of 2
317             sub _check_n
318             {
319 31     31   83 my ($n) = @_;
320              
321 31   33     242 return scalar( $n == int($n) and $n > 0 and ( !( $n & ( $n - 1 ) ) ) );
322             }
323              
324             sub correl
325             {
326 4     4 1 29 my ( $self, $other ) = @_;
327 4         10 my $n = $self->{n};
328             my $d1 =
329             $self->{type}
330             ? (
331             $self->{type} eq 'rdft'
332 3         9 ? [ @{ $self->{coeff} } ]
333             : die 'correl must involve a real function'
334             )
335 4 50 50     12 : $self->rdft && [ @{ $self->{coeff} } ];
    100          
336 4         6 my $d2 = [];
337 4 100       14 if ( ref($other) eq 'Math::FFT' )
    50          
338             {
339             $d2 =
340             $other->{type}
341             ? (
342             $other->{type} eq 'rdft'
343 1         3 ? [ @{ $other->{coeff} } ]
344             : die 'correl must involve a real function'
345             )
346 2 50 50     8 : $other->rdft && [ @{ $other->{coeff} } ];
    100          
347             }
348             elsif ( ref($other) eq 'ARRAY' )
349             {
350 2         5 $d2 = [@$other];
351 2         12 _rdft( $n, 1, $d2, $self->{ip}, $self->{w} );
352             }
353             else
354             {
355 0         0 die 'Must call correl with either a Math::FFT object or an array ref';
356             }
357 4         17 my $corr = [];
358 4         53 _correl( $n, $corr, $d1, $d2, $self->{ip}, $self->{w} );
359 4         13 return $corr;
360             }
361              
362             sub convlv
363             {
364 2     2 1 877 my ( $self, $r ) = @_;
365 2 50       6 die 'Must call convlv with an array reference for the response data'
366             unless ref($r) eq 'ARRAY';
367 2         4 my $respn = [@$r];
368 2         4 my $m = @$respn;
369 2 50       5 die 'size of response data must be an odd integer' unless $m % 2 == 1;
370 2         4 my $n = $self->{n};
371             my $d1 =
372             $self->{type}
373             ? (
374             $self->{type} eq 'rdft'
375 2         4 ? [ @{ $self->{coeff} } ]
376             : die 'correl must involve a real function'
377             )
378 2 50 0     6 : $self->rdft && [ @{ $self->{coeff} } ];
    50          
379 2         7 for ( my $i = 1 ; $i <= ( $m - 1 ) / 2 ; ++$i )
380             {
381 8         18 $respn->[ $n - $i ] = $respn->[ $m - $i ];
382             }
383 2         8 for ( my $i = ( $m + 3 ) / 2 ; $i <= $n - ( $m - 1 ) / 2 ; ++$i )
384             {
385 14         23 $respn->[ $i - 1 ] = 0.0;
386             }
387 2         3 my $convlv = [];
388 2         27 _convlv( $n, $convlv, $d1, $respn, $self->{ip}, $self->{w} );
389 2         8 return $convlv;
390             }
391              
392             sub deconvlv
393             {
394 1     1 1 8 my ( $self, $r ) = @_;
395 1 50       3 die 'Must call deconvlv with an array reference for the response data'
396             unless ref($r) eq 'ARRAY';
397 1         2 my $respn = [@$r];
398 1         3 my $m = @$respn;
399 1 50       4 die 'size of response data must be an odd integer' unless $m % 2 == 1;
400 1         2 my $n = $self->{n};
401             my $d1 =
402             $self->{type}
403             ? (
404             $self->{type} eq 'rdft'
405 0         0 ? [ @{ $self->{coeff} } ]
406             : die 'correl must involve a real function'
407             )
408 1 0 50     4 : $self->rdft && [ @{ $self->{coeff} } ];
    50          
409 1         5 for ( my $i = 1 ; $i <= ( $m - 1 ) / 2 ; ++$i )
410             {
411 4         10 $respn->[ $n - $i ] = $respn->[ $m - $i ];
412             }
413 1         5 for ( my $i = ( $m + 3 ) / 2 ; $i <= $n - ( $m - 1 ) / 2 ; ++$i )
414             {
415 7         12 $respn->[ $i - 1 ] = 0.0;
416             }
417 1         1 my $convlv = [];
418 1 50       13 if ( _deconvlv( $n, $convlv, $d1, $respn, $self->{ip}, $self->{w} ) != 0 )
419             {
420 0         0 die "Singularity encountered for response in deconvlv";
421             }
422 1         5 return $convlv;
423             }
424              
425             {
426              
427             my $PI2 = 2 * 4.0 * atan2( 1, 1 );
428              
429             sub spctrm
430             {
431 12     12 1 4060 my ( $self, %args ) = @_;
432 12         37 my %accept = map { $_ => 1 } qw(window segments number overlap);
  48         74  
433 12         32 for ( keys %args )
434             {
435 39 50       83 die "`$_' is not a valid argument to spctrm" if not $accept{$_};
436             }
437 12         18 my $win_fun = $args{window};
438 12 100 100     39 if ( $win_fun and ref($win_fun) ne 'CODE' )
439             {
440 7         10 my %accept = map { $_ => 1 } qw(hamm hann welch bartlett);
  28         42  
441             die "`$win_fun' is not a known window function in spctrm"
442 7 50       20 if not $accept{$win_fun};
443             }
444             die 'Please specify a value for "segments" in spctrm()'
445 12 50 66     38 if ( $args{number} and !$args{segments} );
446 12         17 my $n = $self->{n};
447 12         13 my $d;
448 12         12 my $n2 = 0;
449 12         19 my $spctrm = [];
450             my $win_sub = do
451 12         14 {
452             my $h = sub {
453 64     64   75 my ( $j, $n ) = @_;
454 64         98 return ( 1 - cos( $PI2 * $j / $n ) ) / 2;
455 12         95 };
456              
457             +{
458             'hamm' => $h,
459             'hann' => $h,
460             'welch' => sub {
461 64     64   70 my ( $j, $n ) = @_;
462 64         104 return 1 - 4 * ( $j - $n / 2 ) * ( $j - $n / 2 ) / $n / $n;
463             },
464             'bartlett' => sub {
465 80     80   86 my ( $j, $n ) = @_;
466 80         124 return 1 - abs( 2 * ( $j - $n / 2 ) / $n );
467             },
468 12         69 };
469             };
470 12 100 33     63 if ( not $args{segments}
      66        
471             or ( $args{segments} == 1 and not $args{number} ) )
472             {
473 2 50       29 die "data size ($n) must be an integer power of 2"
474             unless _check_n($n);
475 2 100       5 if ($win_fun)
476             {
477 1         10 $d = [ @{ $self->{data} } ];
  1         5  
478 1 50       6 $win_fun = $win_sub->{$win_fun} if ref($win_fun) ne 'CODE';
479 1         4 for ( my $j = 0 ; $j < $n ; ++$j )
480             {
481 16         19 my $w = $win_fun->( $j, $n );
482 16         19 $d->[$j] *= $w;
483 16         22 $n2 += $w * $w;
484             }
485 1         1 $n2 *= $n;
486 1         11 _spctrm( $n, $spctrm, $d, $self->{ip}, $self->{w}, $n2, 1 );
487             }
488             else
489             {
490             $d =
491             $self->{type}
492             ? (
493             $self->{type} eq 'rdft'
494             ? $self->{coeff}
495             : die 'correl must involve a real function'
496             )
497 1 0 33     11 : $self->rdft && $self->{coeff};
    50          
498 1         2 $n2 = $n * $n;
499 1         9 _spctrm( $n, $spctrm, $d, $self->{ip}, $self->{w}, $n2, 0 );
500             }
501             }
502             else
503             {
504 10         11 $d = [ @{ $self->{data} } ];
  10         708  
505 10         18 my ( $data, @w );
506 10         12 my $k = $args{segments};
507 10         15 my $m = $args{number};
508 10 50 33     25 die 'Please specify a value for "number" in spctrm()'
509             if ( $k and !$m );
510 10 50       21 die "number ($m) must be an integer power of 2" unless _check_n($m);
511 10         20 my $m2 = $m + $m;
512 10         11 my $overlap = $args{overlap};
513 10 100       19 my $N = $overlap ? ( $k + 1 ) * $m : 2 * $k * $m;
514 10 50       14 die "Need $N data points (data only has $n)" if $N > $n;
515              
516 10 100       17 if ($win_fun)
517             {
518 8 100       19 $win_fun = $win_sub->{$win_fun} if ref($win_fun) ne 'CODE';
519 8         11 for ( my $j = 0 ; $j < $m2 ; ++$j )
520             {
521 256         287 $w[$j] = $win_fun->( $j, $m2 );
522 256         542 $n2 += $w[$j] * $w[$j];
523             }
524             }
525             else
526             {
527 2         2 $n2 = $m2;
528             }
529 10 100       13 if ($overlap)
530             {
531 5         12 my @old = splice( @$d, 0, $m );
532 5         11 for ( 0 .. $k - 1 )
533             {
534 80         93 push @{ $data->[$_] }, @old;
  80         317  
535 80         153 my @new = splice( @$d, 0, $m );
536 80         77 push @{ $data->[$_] }, @new;
  80         216  
537 80         199 @old = @new;
538 80 100       108 if ($win_fun)
539             {
540 64         62 my $j = 0;
541             $data->[$_] =
542 64         60 [ map { $w[ $j++ ] * $_ } @{ $data->[$_] } ];
  2048         2914  
  64         81  
543             }
544             }
545             }
546             else
547             {
548 5         11 for ( 0 .. $k - 1 )
549             {
550 160         192 push @{ $data->[$_] }, splice( @$d, 0, $m2 );
  160         864  
551 160 100       360 if ($win_fun)
552             {
553 128         125 my $j = 0;
554             $data->[$_] =
555 128         141 [ map { $w[ $j++ ] * $_ } @{ $data->[$_] } ];
  4096         5691  
  128         161  
556             }
557             }
558             }
559 10         22 my $tmp = [];
560 10         23 my $nip = int( 3 + sqrt($m2) );
561 10         16 my $nw = int( 2 + 5 * $m2 / 4 );
562 10         32 my $ip = pack( "i$nip", () );
563 10         20 my $w = pack( "d$nw", () );
564 10         1228 _spctrm_bin( $k, $m2, $spctrm, $data, \$ip, \$w, $n2, $tmp );
565             }
566 12         272 return $spctrm;
567             }
568             }
569              
570             sub mean
571             {
572 1     1 1 6 my $self = shift;
573 1         2 my $sum = 0;
574 1         3 my ( $n, $data );
575 1         2 my $flag = 0;
576 1 50       3 if ( $data = shift )
577             {
578 0 0       0 die 'Must call with an array reference'
579             unless ref($data) eq 'ARRAY';
580 0         0 $n = @$data;
581 0         0 $flag = 1;
582             }
583             else
584             {
585 1         8 $data = $self->{data};
586 1         3 $n = $self->{n};
587             }
588 1         4 $sum += $_ for @$data;
589 1         3 my $mean = $sum / $n;
590 1 50       16 $self->{mean} = $mean unless $flag == 1;
591 1         3 return $mean;
592             }
593              
594             sub rms
595             {
596 1     1 1 316 my $self = shift;
597 1         2 my $sum = 0;
598 1         2 my ( $n, $data );
599 1 50       4 if ( $data = shift )
600             {
601 0 0       0 die 'Must call with an array reference'
602             unless ref($data) eq 'ARRAY';
603 0         0 $n = @$data;
604             }
605             else
606             {
607 1         2 $data = $self->{data};
608 1         2 $n = $self->{n};
609             }
610 1         4 $sum += $_ * $_ for @$data;
611 1         4 return sqrt( $sum / $n );
612             }
613              
614             sub stdev
615             {
616 1     1 1 567 my $self = shift;
617 1         2 my ( $n, $data, $mean );
618 1 50       4 if ( $data = shift )
619             {
620 0 0       0 die 'Must call with an array reference'
621             unless ref($data) eq 'ARRAY';
622 0         0 $n = @$data;
623 0         0 $mean = $self->mean($data);
624             }
625             else
626             {
627 1         2 $data = $self->{data};
628 1         3 $n = $self->{n};
629 1   33     4 $mean = $self->{mean} || $self->mean;
630             }
631 1 50       4 die 'Cannot find the standard deviation with n = 1'
632             if $n == 1;
633 1         2 my $sum = 0;
634 1         5 $sum += ( $_ - $mean ) * ( $_ - $mean ) for @$data;
635 1         5 return sqrt( $sum / ( $n - 1 ) );
636             }
637              
638             sub range
639             {
640 1     1 1 298 my $self = shift;
641 1         3 my ( $n, $data );
642 1 50       3 if ( $data = shift )
643             {
644 0 0       0 die 'Must call with an array reference'
645             unless ref($data) eq 'ARRAY';
646 0         0 $n = @$data;
647             }
648             else
649             {
650 1         3 $data = $self->{data};
651 1         2 $n = $self->{n};
652             }
653 1         2 my $min = $data->[0];
654 1         2 my $max = $data->[0];
655 1         3 for (@$data)
656             {
657 5 50       11 $min = $_ if $_ < $min;
658 5 100       9 $max = $_ if $_ > $max;
659             }
660 1         4 return ( $min, $max );
661             }
662              
663             sub median
664             {
665 2     2 1 658 my $self = shift;
666 2         5 my ( $n, $data );
667 2 50       5 if ( $data = shift )
668             {
669 0 0       0 die 'Must call with an array reference'
670             unless ref($data) eq 'ARRAY';
671 0         0 $n = @$data;
672             }
673             else
674             {
675 2         5 $data = $self->{data};
676 2         5 $n = $self->{n};
677             }
678 2         38 my @sorted = sort { $a <=> $b } @$data;
  12         22  
679             return (
680 2 100       18 ( $n & 0x1 )
681             ? $sorted[ ( $n - 1 ) / 2 ]
682             : ( $sorted[ $n / 2 ] + $sorted[ $n / 2 - 1 ] ) / 2
683             );
684             }
685              
686             # Autoload methods go after =cut, and are processed by the autosplit program.
687              
688             1;
689              
690             __END__