File Coverage

lib/Heap/Binary.pm
Criterion Covered Total %
statement 64 116 55.1
branch 14 54 25.9
condition 7 9 77.7
subroutine 10 18 55.5
pod 0 16 0.0
total 95 213 44.6


line stmt bran cond sub pod time code
1             package Heap::Binary;
2              
3 2     2   37713 use strict;
  2         4  
  2         96  
4 2     2   12 use vars qw($VERSION);
  2         4  
  2         3630  
5              
6             $VERSION = '0.80';
7              
8             # common names:
9             # h - heap head
10             # i - index of a heap value element
11             # v - user-provided value (to be) stored on the heap
12              
13             ################################################# debugging control
14              
15             my $debug = 0;
16             my $validate = 0;
17              
18             # enable/disable debugging output
19             sub debug {
20 0 0   0 0 0 @_ ? ($debug = shift) : $debug;
21             }
22              
23             # enable/disable validation checks on values
24             sub validate {
25 0 0   0 0 0 @_ ? ($validate = shift) : $validate;
26             }
27              
28             my $width = 3;
29             my $bar = ' | ';
30             my $corner = ' +-';
31             my $vfmt = "%3d";
32              
33             sub set_width {
34 0     0 0 0 $width = shift;
35 0 0       0 $width = 2 if $width < 2;
36              
37 0         0 $vfmt = "%${width}d";
38 0         0 $bar = $corner = ' ' x $width;
39 0         0 substr($bar,-2,1) = '|';
40 0         0 substr($corner,-2,2) = '+-';
41             }
42              
43              
44             sub hdump {
45 0     0 0 0 my $h = shift;
46 0         0 my $i = shift;
47 0         0 my $p = shift;
48 0         0 my $ch = $i*2+1;
49              
50 0 0       0 return if $i >= @$h;
51              
52 0         0 my $space = ' ' x $width;
53              
54 0         0 printf( "%${width}d", $h->[$i]->val );
55 0 0       0 if( $ch+1 < @$h ) {
56 0         0 hdump( $h, $ch, $p . $bar);
57 0         0 print( $p, $corner );
58 0         0 ++$ch;
59             }
60 0 0       0 if( $ch < @$h ) {
61 0         0 hdump( $h, $ch, $p . $space );
62             } else {
63 0         0 print "\n";
64             }
65             }
66              
67             sub heapdump {
68 0     0 0 0 my $h;
69              
70 0         0 while( $h = shift ) {
71 0         0 hdump $h, 0, '';
72 0         0 print "\n";
73             }
74             }
75              
76             sub heapcheck {
77 0     0 0 0 my $h;
78 0         0 while( $h = shift ) {
79 0         0 my $i;
80             my $p;
81 0 0       0 next unless @$h;
82 0         0 for( $p = 0, $i = 1; $i < @$h; ++$p, ++$i ) {
83 0 0       0 $h->[$p]->cmp($h->[$i]) <= 0 or die "not in heap order";
84 0 0       0 last unless ++$i < @$h;
85 0 0       0 $h->[$p]->cmp($h->[$i]) <= 0 or die "not in heap order";
86             }
87 0 0       0 heapdump $h if $validate >= 2;
88             }
89             }
90              
91             ################################################# forward declarations
92              
93             sub moveto;
94             sub heapup;
95             sub heapdown;
96              
97             ################################################# heap methods
98              
99             # new() usually Heap::Binary->new()
100             # return a new empty heap
101             sub new {
102 2     2 0 631 my $self = shift;
103 2   33     16 my $class = ref($self) || $self;
104 2         10 return bless [], $class;
105             }
106              
107             # add($h,$v) usually $h->add($v)
108             # insert value $v into the heap
109             sub add {
110 200     200 0 616 my $h = shift;
111 200         208 my $v = shift;
112 200 50       373 $validate && do {
113 0 0       0 die "Method 'heap' required for element on heap"
114             unless $v->can('heap');
115 0 0       0 die "Method 'cmp' required for element on heap"
116             unless $v->can('cmp');
117             };
118 200         395 heapup $h, scalar(@$h), $v;
119             }
120              
121             # top($h) usually $h->top
122             # the smallest value is returned, but it is still left on the heap
123             sub top {
124 102     102 0 952 my $h = shift;
125 102         227 $h->[0];
126             }
127              
128             *minimum = \⊤
129              
130             # extract_top($h) usually $h->extract_top
131             # the smallest value is returned after removing it from the heap
132             sub extract_top {
133 102     102 0 123 my $h = shift;
134 102         136 my $top = $h->[0];
135 102 100       203 if( @$h ) {
136             # there was at least one item, must decrease the heap
137 100         220 $top->heap(undef);
138 100         138 my $last = pop(@$h);
139 100 50       202 if( @$h ) {
140             # $top was not the only thing left, so re-heap the
141             # remainder by over-writing position zero (where
142             # $top was) using the value popped from the end
143 100         170 heapdown $h, 0, $last;
144             }
145             }
146 102         219 $top;
147             }
148              
149             *extract_minimum = \&extract_top;
150              
151             # absorb($h,$h2) usually $h->absorb($h2)
152             # all of the values in $h2 are inserted into $h instead, $h2 is left
153             # empty.
154             sub absorb {
155 0     0 0 0 my $h = shift;
156 0         0 my $h2 = shift;
157 0         0 my $v;
158              
159 0         0 foreach $v (splice @$h2, 0) {
160 0         0 $h->add($v);
161             }
162 0         0 $h;
163             }
164              
165             # decrease_key($h,$v) usually $h->decrease_key($v)
166             # the key value of $v has just been decreased and so it may need to
167             # be percolated to a higher position in the heap
168             sub decrease_key {
169 0     0 0 0 my $h = shift;
170 0         0 my $v = shift;
171 0 0       0 $validate && do {
172 0 0       0 die "Method 'heap' required for element on heap"
173             unless $v->can('heap');
174 0 0       0 die "Method 'cmp' required for element on heap"
175             unless $v->can('cmp');
176             };
177 0         0 my $i = $v->heap;
178              
179 0         0 heapup $h, $i, $v;
180             }
181              
182             # delete($h,$v) usually: $h->delete($v)
183             # delete value $v from heap $h. It must have previously been
184             # add'ed to $h.
185             sub delete {
186 100     100 0 294 my $h = shift;
187 100         113 my $v = shift;
188 100 50       198 $validate && do {
189 0 0       0 die "Method 'heap' required for element on heap"
190             unless $v->can('heap');
191 0 0       0 die "Method 'cmp' required for element on heap"
192             unless $v->can('cmp');
193             };
194 100         207 my $i = $v->heap;
195              
196 100 50       250 return $v unless defined $i;
197              
198 100 100       185 if( $i == $#$h ) {
199 2         5 pop @$h;
200             } else {
201 98         133 my $v2 = pop @$h;
202 98 100       435 if( $v2->cmp($v) < 0 ) {
203 22         41 heapup $h, $i, $v2;
204             } else {
205 76         313 heapdown $h, $i, $v2;
206             }
207             }
208 100         237 $v->heap(undef);
209 100         150 return $v;
210             }
211              
212              
213             ################################################# internal utility functions
214              
215             # moveto($h,$i,$v)
216             # place value $v at index $i in the heap $h, and update it record
217             # of where it is located
218             sub moveto {
219 1274     1274 0 1488 my $h = shift;
220 1274         1554 my $i = shift;
221 1274         1264 my $v = shift;
222              
223 1274         1754 $h->[$i] = $v;
224 1274         3002 $v->heap($i);
225             }
226              
227             # heapup($h,$i,$v)
228             # value $v is to be placed at index $i in heap $h, but it might
229             # be smaller than some of its parents. Keep pushing parents down
230             # until a smaller parent is found or the top of the heap is reached,
231             # and then place $v there.
232             sub heapup {
233 222     222 0 225 my $h = shift;
234 222         239 my $i = shift;
235 222         219 my $v = shift;
236 222         210 my $pi; # parent index
237              
238 222   100     916 while( $i && $v->cmp($h->[$pi = int( ($i-1)/2 )]) < 0 ) {
239 322         617 moveto $h, $i, $h->[$pi];
240 322         1311 $i = $pi;
241             }
242              
243 222         433 moveto $h, $i, $v;
244 222         510 $v;
245             }
246              
247             # heapdown($h,$i,$v)
248             # value $v is to be placed at index $i in heap $h, but it might
249             # have children that are smaller than it is. Keep popping the smallest
250             # child up until a pair of larger children is found or a leaf node is
251             # reached, and then place $v there.
252             sub heapdown {
253 176     176 0 186 my $h = shift;
254 176         351 my $i = shift;
255 176         170 my $v = shift;
256 176         270 my $leaf = int(@$h/2);
257              
258 176         367 while( $i < $leaf ) {
259 588         800 my $j = $i*2+1;
260 588         639 my $k = $j+1;
261              
262 588 100 100     2438 $j = $k if $k < @$h && $h->[$k]->cmp($h->[$j]) < 0;
263 588 100       1520 if( $v->cmp($h->[$j]) > 0 ) {
264 554         16921 moveto $h, $i, $h->[$j];
265 554         687 $i = $j;
266 554         1340 next;
267             }
268 34         52 last;
269             }
270 176         504 moveto $h, $i, $v;
271             }
272              
273              
274             1;
275              
276             __END__