File Coverage

blib/lib/Math/IntervalSearch.pm
Criterion Covered Total %
statement 74 79 93.6
branch 39 44 88.6
condition 8 12 66.6
subroutine 7 8 87.5
pod 1 4 25.0
total 129 147 87.7


line stmt bran cond sub pod time code
1             package Math::IntervalSearch;
2              
3             require 5.004_01;
4              
5 2     2   2746 use strict;
  2         5  
  2         83  
6 2     2   11 use vars qw(@EXPORT_OK @ISA $VERSION);
  2         4  
  2         122  
7 2     2   14 use Exporter;
  2         7  
  2         79  
8 2     2   10 use Carp;
  2         3  
  2         4170  
9              
10             @EXPORT_OK = qw(interval_search);
11             @ISA = qw(Exporter);
12             $VERSION = 1.06;
13             $VERSION = eval $VERSION;
14              
15 0     0 0 0 sub cluck { warn Carp::longmess @_ }
16              
17             sub LessThan {
18 17080     17080 0 60114 $_[0] < $_[1];
19             }
20              
21             sub LessThanEqualTo {
22 12043     12043 0 35406 $_[0] <= $_[1];
23             }
24              
25             # This holds the result from the last interval search.
26             my $last_interval_result = undef;
27              
28             sub interval_search {
29 5517 50   5517 1 36091 if ( @_ > 4 ) {
30 0         0 cluck "interval called with too many parameters";
31 0         0 return;
32             }
33              
34             # Get the input arguments.
35 5517         6926 my $x = shift;
36 5517         6247 my $sequenceRef = shift;
37              
38 5517 100       10436 return unless defined($x);
39 5516 100       15370 return unless defined($sequenceRef);
40 5515 100       10275 return unless ref($sequenceRef);
41              
42             # Check the input arguments for any code references and use them.
43 5514         7753 my $LessThan = \&LessThan;
44 5514         7330 my $LessThanEqualTo = \&LessThanEqualTo;
45 5514 50 66     14755 @_ and defined(ref($_[0])) and ref($_[0]) eq 'CODE' and
      66        
46             $LessThan = shift;
47 5514 50 66     22588 @_ and defined(ref($_[0])) and ref($_[0]) eq 'CODE' and
      66        
48             $LessThanEqualTo = shift;
49              
50             # Get the number of points in the data.
51 5514         6531 my $num = @$sequenceRef;
52              
53             # Return -1 if there's no data.
54 5514 100       10032 if ( $num <= 0 ) {
55 2         3 $last_interval_result = 0;
56 2         7 return -1;
57             }
58              
59             # Use the result from the last time through the subroutine, if it
60             # exists. Force the result into the range required by the array
61             # size.
62 5512 100       9776 $last_interval_result = 0 unless defined($last_interval_result);
63              
64             # Which side of the data point is x on if there's only one point?
65 5512 100       9557 if ( $num == 1 ) {
66 1         1 $last_interval_result = 0;
67 1 50       4 if ( &$LessThan($x, $sequenceRef->[0]) ) {
68 1         3 return -1;
69             }
70             else {
71 0         0 return 0;
72             }
73             }
74              
75             # Is the point less than the smallest point in the sequence?
76 5511 100       12017 if ( &$LessThan($x, $sequenceRef->[0]) ) {
77 181         402 $last_interval_result = 0;
78 181         484 return -1;
79             }
80              
81             # Is the point greater than the largest point in the sequence?
82 5330 100       13550 if ( &$LessThanEqualTo($sequenceRef->[$num-1], $x) ) {
83 171         678 return $last_interval_result = $num - 1;
84             }
85              
86             # Use the result from the last run as a start for this run.
87 5159 50       11489 if ( $last_interval_result > $num-1 ) {
88 0         0 $last_interval_result = $num - 2;
89             }
90 5159         5893 my $ilo = $last_interval_result;
91 5159         6539 my $ihi = $ilo + 1;
92              
93             # Is the new upper ihi beyond the extent of the sequence?
94 5159 100       9606 if ( $ihi >= $num ) {
95 111         116 $ihi = $num - 1;
96 111         135 $ilo = $ihi - 1;
97             }
98              
99             # If x < sequence(ilo), then decrease ilo to capture x.
100 5159 100       9812 if ( &$LessThan($x, $sequenceRef->[$ilo]) ) {
101 558         1121 my $istep = 1;
102 558         596 for (;;) {
103 3855         4081 $ihi = $ilo;
104 3855         4075 $ilo = $ihi - $istep;
105 3855 100       8014 if ( $ilo <= 0 ) {
106 192         202 $ilo = 0;
107 192         276 last;
108             }
109 3663 100       6748 if ( &$LessThanEqualTo($sequenceRef->[$ilo], $x) ) {
110 366         828 last;
111             }
112 3297         7103 $istep *= 2;
113             }
114             }
115              
116             # If x >= sequence(ihi), then increase ihi to capture x.
117 5159 100       10115 if ( &$LessThanEqualTo($sequenceRef->[$ihi], $x) ) {
118 600         1128 my $istep = 1;
119 600         677 for (;;) {
120 4066         4310 $ilo = $ihi;
121 4066         4301 $ihi = $ilo + $istep;
122 4066 100       7274 if ( $ihi >= $num-1 ) {
123 200         209 $ihi = $num - 1;
124 200         278 last;
125             }
126 3866 100       6864 if ( &$LessThan($x, $sequenceRef->[$ihi]) ) {
127 400         899 last;
128             }
129 3466         10146 $istep *= 2;
130             }
131             }
132              
133             # Now sequence(ilo) <= x < sequence(ihi). Narrow the interval.
134 5159         7059 for (;;) {
135             # Find the middle point of the sequence.
136 11708         18106 my $middle = int(($ilo + $ihi)/2);
137              
138             # The division above was integer, so if ihi = ilo+1, then
139             # middle=ilo, which tests if x has been trapped.
140 11708 100       23568 if ( $middle == $ilo ) {
141 5159         5441 $last_interval_result = $ilo;
142 5159         17407 return $ilo;
143             }
144 6549 100       11811 if ( &$LessThan($x, $sequenceRef->[$middle]) ) {
145 3145         8880 $ihi = $middle;
146             }
147             else {
148 3404         7870 $ilo = $middle;
149             }
150             }
151             }
152              
153             1;
154              
155             __END__