File Coverage

blib/lib/Heap/MinMax.pm
Criterion Covered Total %
statement 264 397 66.5
branch 92 164 56.1
condition 4 15 26.6
subroutine 32 42 76.1
pod 13 39 33.3
total 405 657 61.6


line stmt bran cond sub pod time code
1             #
2             # MinMax.pm
3             #
4             # An implementation of a Min-Max Binary Heap, based on 1986 article
5             # "Min-Max Heaps and Generalized Priority Queues" by Atkinson, Sack,
6             # Santoro, and Strothotte, published in Communications of the ACM.
7             #
8             # In a Min-Max heap, objects are stored in partial order such that both the
9             # minimum element and maximum element are available in constant time. This
10             # is accomplished through a modification of the standard heap algorithm that
11             # introduces the notion of 'min' (even) levels and 'max' (odd) levels in the
12             # binary tree structure of the heap.
13             #
14             # With a Min-Max heap you get all this, plus insertion into a Min-Max heap is
15             # actually *faster* than with a normal heap (by a constant factor of 0.5).
16             #
17             #
18             package Heap::MinMax;
19              
20 1     1   41688 use Carp;
  1         3  
  1         218  
21 1     1   6 use strict;
  1         2  
  1         49  
22 1     1   5 use warnings;
  1         6  
  1         5870  
23              
24              
25             our $VERSION = '1.04';
26              
27              
28              
29             ##################################################
30             # the MinMax Heap constructor
31             ##################################################
32             sub new {
33 3     3 1 18 my $invocant = shift;
34 3   33     15 my $class = ref($invocant) || $invocant;
35 3         17 my $self = {
36             _arr => [], # Array containing heap
37             fcompare => \&default_cmp_func,
38             feval => \&default_eval,
39             @_, # Override previous attributes
40             };
41 3         7 $self = bless $self, $class;
42 3         13 return $self;
43             }
44              
45             ##############################################
46             # accessor methods
47             ##############################################
48              
49             sub array
50             {
51 0     0 1 0 my $self = shift;
52 0 0       0 if (@_) { $self->{_arr} = shift }
  0         0  
53 0         0 return $self->{_arr};
54             }
55              
56              
57              
58              
59             #=========================================================================
60             #
61             # main heap functions
62             #
63             #=========================================================================
64              
65              
66             ##########################################################################
67             #
68             # build_heap
69             #
70             # $mm_heap->build_heap();
71             #
72             # builds a heap from MinMax object's array
73             #
74             ##########################################################################
75             sub build_heap
76             {
77 0     0 1 0 my ($self) = @_;
78 0         0 my $array = $self->{_arr};
79 0         0 my $arr_length = @$array;
80 0         0 my $val;
81              
82 0         0 for(my $i = $arr_length/2; $i >= 0; $i--){
83 0         0 $val = $self->trickledown($i);
84             }
85 0         0 return $val;
86             }
87              
88             ##########################################################################
89             #
90             # insert
91             #
92             # $mm_heap->insert($value);
93             # $mm_heap->insert(@values);
94             #
95             # Insertion works by placing the new node in the first available
96             # leaf position and then calling bubble_up to re-establish min-max
97             # ordering of the heap.
98             #
99             ##########################################################################
100             sub insert
101             {
102 13     13 1 1118 my ($self,
103             @values) = @_;
104            
105 13         32 while(defined(my $val = shift(@values))){
106 18         25 my $array = $self->{_arr};
107 18         26 push(@$array, $val); # put the new element in the next available leaf slot
108 18         23 my $arr_length = @$array;
109 18         21 my $index = $arr_length - 1;
110            
111             # call bubble_up
112 18         41 $self->bubble_up($index);
113             }
114             }
115              
116             #########################################################################
117             #
118             # remove
119             #
120             # $mm_heap->remove($object);
121             #
122             # Not the same as pop_. really expensive arbitrary remove
123             # operation that iterates over the array and finds the object it needs,
124             # removes it, then calls trickledown from the index where the object
125             # was found to re-establish min-max ordering of the heap.
126             #
127             #########################################################################
128             sub remove
129             {
130 0     0 1 0 my ($self,
131             $obj) = @_;
132            
133              
134 0         0 my ($pkg, $filename, $line) = caller();
135              
136 0         0 my $array = $self->{_arr};
137 0         0 my $arr_length = @$array;
138 0         0 my $evalfunc = $self->{feval};
139 0         0 my $value = $evalfunc->($obj);
140            
141 0         0 my $index;
142              
143 0         0 my $i = 0;
144 0         0 foreach my $elt (@$array){
145              
146 0 0       0 if($self->{fcompare}->($obj, $elt) == 0){
147 0         0 $index = $i;
148 0         0 last;
149             }
150 0         0 $i++;
151             }
152              
153 0 0       0 if(defined $index){
154 0         0 my $obj = $array->[$index];
155              
156 0         0 $array->[$index] = $array->[$arr_length-1];
157 0         0 pop(@$array);
158              
159 0         0 $self->trickledown($index);
160              
161 0         0 return $obj;
162             }
163 0         0 return;
164             }
165              
166             ############################################################
167             #
168             # min
169             #
170             # my $min_obj = $mm_heap->min();
171             #
172             # return the minimum object in heap
173             #
174             ############################################################
175             sub min
176             {
177 3     3 1 909 my ($self) = @_;
178 3         6 my $array = $self->{_arr};
179 3         4 my $arr_length = @$array;
180              
181             #array is empty
182 3 50       7 if(!$arr_length){
183 0         0 return;
184             }
185              
186 3         5 my $top = $array->[0];
187 3         12 return $top;
188             }
189              
190             ############################################################
191             #
192             # pop_min
193             #
194             # my $min_obj = $mm_heap->pop_min();
195             #
196             # pop the minimum object from the heap and return it
197             #
198             ############################################################
199             sub pop_min
200             {
201 2     2 1 582 my ($self) = @_;
202 2         5 my $array = $self->{_arr};
203 2         4 my $arr_length = @$array;
204              
205             #array is empty
206 2 50       6 if(!$arr_length){
207 0         0 return;
208             }
209              
210 2         4 my $top = $array->[0];
211            
212 2         4 $array->[0] = $array->[$arr_length-1];
213 2         4 pop(@$array);
214            
215 2         10 $self->trickledown(0);
216 2         6 return $top;
217             }
218              
219              
220             ############################################################
221             #
222             # min_non_zero
223             #
224             # my $min_obj = $mm_heap->min_no_zero();
225             #
226             # get minimum, non-zero valued object from the heap
227             # and return it. This only makes sense if you have an
228             # evaluation function that can return 0.
229             #
230             ############################################################
231             sub min_non_zero # the smallest non-zero element
232             {
233 0     0 1 0 my ($self) = @_;
234 0         0 my $array = $self->{_arr};
235 0         0 my $arr_length = @$array;
236 0         0 my $evalfunc = $self->{feval};
237 0         0 my $index = 0;
238              
239             #array is empty
240 0 0       0 if(!$arr_length){
241 0         0 return;
242             }
243              
244 0         0 my $top = $array->[$index];
245            
246 0 0       0 if($evalfunc->($top) == 0){ # find min of grandchildren
247 0         0 my $n = 0;
248 0         0 my $smallest;
249 0         0 for my $i (3,4,5,6) {
250 0 0       0 if($n == 0){
251 0         0 $smallest = $i;
252 0         0 $n++;
253             }
254             else{
255 0 0 0     0 if($array->[$i] && $self->{fcompare}->($array->[$i], $array->[$smallest]) == -1){
256 0         0 $smallest = $i;
257             }
258             }
259             }
260 0         0 $index = $smallest;
261             }
262            
263 0         0 $top = $array->[$index];
264              
265 0         0 return $top;
266             }
267              
268              
269             ############################################################
270             #
271             # pop_min_non_zero
272             #
273             # my $min_obj = $mm_heap->pop_min_no_zero();
274             #
275             # pop the minimum, non-zero valued object from the heap
276             # and return it. This only makes sense if you have an
277             # evaluation function that can return 0.
278             #
279             ############################################################
280             sub pop_min_non_zero # pop the smallest non-zero element
281             {
282 0     0 1 0 my ($self) = @_;
283 0         0 my $array = $self->{_arr};
284 0         0 my $arr_length = @$array;
285 0         0 my $evalfunc = $self->{feval};
286 0         0 my $index = 0;
287              
288             #array is empty
289 0 0       0 if(!$arr_length){
290 0         0 return;
291             }
292              
293 0         0 my $top = $array->[$index];
294            
295 0 0       0 if($evalfunc->($top) == 0){ # find min of grandchildren
296 0         0 my $n = 0;
297 0         0 my $smallest;
298 0         0 for my $i (3,4,5,6) {
299 0 0       0 if($n == 0){
300 0         0 $smallest = $i;
301 0         0 $n++;
302             }
303             else{
304 0 0 0     0 if($array->[$i] && $self->{fcompare}->($array->[$i], $array->[$smallest]) == -1){
305 0         0 $smallest = $i;
306             }
307             }
308             }
309 0         0 $index = $smallest;
310             }
311            
312 0         0 $top = $array->[$index];
313 0         0 $array->[$index] = $array->[$arr_length-1];
314 0         0 pop(@$array);
315 0         0 $self->trickledown($index);
316            
317 0         0 return $top;
318             }
319              
320             ############################################################
321             #
322             # max
323             #
324             # my $max_obj = $mm_heap->max();
325             #
326             # get maximum object in the heap and return it
327             #
328             ############################################################
329             sub max
330             {
331 2     2 1 488 my ($self) = @_;
332 2         4 my $array = $self->{_arr};
333 2         2 my $arr_length = @$array;
334 2         4 my $evalfunc = $self->{feval};
335              
336             #array is empty
337 2 50       5 if(!$arr_length){
338 0         0 return;
339             }
340            
341             # array has only one element
342 2 50       6 if($arr_length == 1){
343 0         0 return $array->[0];
344             }
345              
346             # array has only two elements
347 2 50       3 if($arr_length == 2){
348 0         0 return $array->[1];
349             }
350              
351 2         6 my $result = $self->{fcompare}->($array->[1], $array->[2]);
352 2 100       11 my $max_index = ($result >= 0) ? 1 : 2;
353 2         3 my $top = $array->[$max_index];
354              
355 2         7 return $top;
356             }
357              
358              
359              
360             ############################################################
361             #
362             # pop_max
363             #
364             # my $max_obj = $mm_heap->pop_max();
365             #
366             # pop the maximum object from the heap and return it
367             #
368             ############################################################
369             sub pop_max
370             {
371 1     1 1 3 my ($self) = @_;
372 1         2 my $array = $self->{_arr};
373 1         2 my $arr_length = @$array;
374 1         2 my $evalfunc = $self->{feval};
375              
376 1         1 my $top;
377             my $max_index;
378              
379             #array is empty
380 1 50       5 if(!$arr_length){
381 0         0 return;
382             }
383              
384             # array has only one element
385 1 50       5 if($arr_length == 1){
    50          
386 0         0 $max_index = 0;
387             }
388             # array has only two elements
389             elsif($arr_length == 2){
390 0         0 $max_index = 1;
391             }
392             else{
393 1         4 my $result = $self->{fcompare}->($array->[1], $array->[2]);
394 1 50       3 $max_index = ($result >= 0) ? 1 : 2;
395             }
396            
397 1         2 $top = $array->[$max_index];
398            
399 1         2 $array->[$max_index] = $array->[$arr_length-1];
400 1         1 pop(@$array);
401              
402 1         4 $self->trickledown($max_index);
403            
404 1         3 return $top;
405             }
406              
407              
408              
409              
410              
411             ############################################################
412             #
413             # trickledown() is called during heap construction. it
414             # determines whether current level is a min-level or max-level,
415             # and calls the appropriate trickledown{min,max}() function.
416             #
417             ############################################################
418             sub trickledown
419             {
420 3     3 0 6 my ($self, $i) = @_;
421 3         4 my $array = $self->{_arr};
422              
423 3 50       10 if($i >= @$array){
424 0         0 return;
425             }
426 3         7 my $level = $self->get_level($i);
427              
428 3 100       9 if($level == 0){
    50          
429 2         7 $self->trickledown_min($i);
430             }
431             elsif(($level % 2) == 0){
432 0         0 $self->trickledown_min($i);
433             }
434             else{
435 1         4 $self->trickledown_max($i);
436             }
437 3         3 return;
438             }
439              
440             ############################################################
441             #
442             # trickledown_min is called during heap construction when examining
443             # a subtree rooted at an even level. Compares the values of the root
444             # node with the smallest of its children and grand-children.
445             # if the root node is larger, the values are swapped and the
446             # function recurses.
447             #
448             #
449             # Note: this function is very similar to trickle_down_max, but
450             # they are kept separate for purposes of readability.
451             #
452             ############################################################
453             sub trickledown_min
454             {
455 3     3 0 4 my ($self, $index) = @_;
456 3         5 my $array = $self->{_arr};
457 3         9 my $m = $self->get_smallest_descendant_index($index);
458            
459 3         6 my $level = $self->get_level($index);
460              
461 3 100       14 if(!$m){
462 1         3 return;
463             }
464              
465 2 100       9 if($self->is_grandchild($index, $m)){
    50          
466 1 50       3 if($self->{fcompare}->($array->[$m], $array->[$index]) == -1){
467 1         3 $self->swap($index, $m);
468            
469 1 50       7 if($self->{fcompare}->($array->[$m], $self->parent($m)) == 1){
470 0         0 my $parent_index = $self->parent_node_index($m);
471 0         0 $self->swap($m, $parent_index);
472             }
473            
474 1         5 $self->trickledown_min($m);
475             }
476             }
477             elsif($self->{fcompare}->($array->[$m], $array->[$index]) == -1){
478 1         12 $self->swap($index, $m);
479             }
480             }
481              
482             ############################################################
483             #
484             # trickledown_max is called during heap construction when examining
485             # a subtree rooted at an odd level. Compares the values of the root
486             # node with the largest of its children and grand-children.
487             # if the root node is smaller, the values are swapped and the
488             # function recurses.
489             #
490             #
491             # Note: this function is very similar to trickle_down_min, but
492             # they are kept separate for purposes of readability.
493             #
494             ############################################################
495             sub trickledown_max
496             {
497 1     1 0 2 my ($self, $index) = @_;
498 1         2 my $array = $self->{_arr};
499 1         3 my $m = $self->get_largest_descendant_index($index);
500            
501 1         2 my $level = $self->get_level($index);
502              
503 1 50       4 if(!$m){ return; }
  0         0  
504              
505 1 50       2 if($self->is_grandchild($index, $m)){
    50          
506 0 0       0 if($self->{fcompare}->($array->[$m], $array->[$index]) == 1){
507 0         0 $self->swap($m, $index);
508            
509 0 0       0 if($self->{fcompare}->($array->[$m], $self->parent($m)) == -1){
510 0         0 my $parent_index = $self->parent_node_index($m);
511 0         0 $self->swap($m, $parent_index);
512             }
513            
514 0         0 $self->trickledown_max($m);
515             }
516             }
517             elsif($self->{fcompare}->($array->[$m], $array->[$index]) == 1){
518 1         3 $self->swap($index, $m);
519             }
520             }
521              
522             ############################################################
523             #
524             # bubble_up() is called during insertion. determines whether the
525             # current level is an even (min) or odd (max) level, and
526             # then either calls bubble_up_min or bubble_up_max.
527             #
528             #
529             ############################################################
530             sub bubble_up
531             {
532 18     18 0 19 my ($self, $i) = @_;
533 18         21 my $array = $self->{_arr};
534            
535 18         32 my $level = $self->get_level($i);
536              
537 18 100       37 if(($level % 2) == 0){
538 11 100       22 if($self->has_parent($i) != -1){
539 8         14 my $parent_index = $self->parent_node_index($i);
540            
541 8 50       16 if($self->{fcompare}->($array->[$i], $array->[$parent_index]) == 1){
542 8         15 $self->swap($i, $parent_index);
543 8         16 $self->bubble_up_max($parent_index);
544             }
545             else{
546 0         0 $self->bubble_up_min($i);
547             }
548             }
549             }
550             else{
551 7 50       13 if($self->has_parent($i) != -1){
552 7         12 my $parent_index = $self->parent_node_index($i);
553              
554 7 100       20 if($self->{fcompare}->($array->[$i], $array->[$parent_index]) == -1){
555 4         17 $self->swap($i, $parent_index);
556 4         607 $self->bubble_up_min($parent_index);
557             }
558             else{
559 3         12 $self->bubble_up_max($i);
560             }
561             }
562             }
563             }
564              
565             ############################################################
566             #
567             # bubble_up_min is called during insertion. after inserting
568             # a new leaf on the heap, the object is then "bubbled-up" to
569             # maintain heap-ness.
570             #
571             # Note: this function is *very* similar to bubble_up_max, but
572             # they are kept separate for purposes of readability.
573             #
574             ############################################################
575             sub bubble_up_min
576             {
577 5     5 0 8 my ($self, $i) = @_;
578 5         6 my $array = $self->{_arr};
579            
580 5 100       10 if($self->has_grandparent($i)){
581 1         3 my $gp_index = $self->grandparent_node_index($i);
582              
583 1 50       7 if($self->{fcompare}->($array->[$i], $array->[$gp_index]) == -1){
584 1         4 $self->swap($i, $gp_index);
585 1         5 $self->bubble_up_min($gp_index);
586             }
587             }
588             }
589              
590             ############################################################
591             #
592             # bubble_up_max is called during insertion. after inserting
593             # a new leaf on the heap, the object is then "bubbled-up" to
594             # maintain heap-ness.
595             #
596             # Note: this function is *very* similar to bubble_up_min, but
597             # they are kept separate for purposes of readability.
598             #
599             ############################################################
600             sub bubble_up_max
601             {
602 11     11 0 11 my ($self, $i) = @_;
603 11         12 my $array = $self->{_arr};
604            
605 11 50       20 if($self->has_grandparent($i)){
606 0         0 my $gp_index = $self->grandparent_node_index($i);
607              
608 0 0       0 if($self->{fcompare}->($array->[$i], $array->[$gp_index]) == 1){
609 0         0 $self->swap($i, $gp_index);
610 0         0 $self->bubble_up_max($gp_index);
611             }
612             }
613             }
614              
615              
616              
617             ############################################################
618             #
619             # swap two elements in the array
620             #
621             ############################################################
622             sub swap
623             {
624 16     16 0 22 my ($self, $m, $index) = @_;
625 16         19 my $array = $self->{_arr};
626            
627 16 50 33     74 if($m < @$array && $index < @$array){
628 16         17 my $tmp = $array->[$index];
629 16         23 $array->[$index] = $array->[$m];
630 16         19 $array->[$m] = $tmp;
631             }
632              
633 16         25 $self->{_arr} = $array;
634             }
635              
636              
637              
638             ############################################################
639             #
640             # get_smallest_descendant_index() returns the index of the
641             # smallest descendant of this node.
642             #
643             ############################################################
644             sub get_smallest_descendant_index
645             {
646 3     3 0 4 my ($self, $index) = @_;
647 3         5 my $array = $self->{_arr};
648              
649 3 100       7 if($self->has_children($index)){ # if has children
650 2         3 my %descendants;
651              
652             # right node and right node descendants
653 2         5 my $rightnode = $self->right_node($index);
654 2         3 my $r_index = $self->right_node_index($index);
655              
656 2 100       5 if($rightnode){
657 1         4 $descendants{$r_index} = $rightnode;
658             }
659              
660 2         4 my $right_leftnode = $self->left_node($r_index);
661 2         5 my $right_leftnode_index = $self->left_node_index($r_index);
662              
663 2 100       8 if($right_leftnode){
664 1         2 $descendants{$right_leftnode_index} = $right_leftnode;
665             }
666              
667 2         3 my $right_rightnode = $self->right_node($r_index);
668 2         5 my $right_rightnode_index = $self->right_node_index($r_index);
669              
670 2 50       5 if($right_rightnode){
671 0         0 $descendants{$right_rightnode_index} = $right_rightnode;
672             }
673              
674             # left node and left node descendants
675 2         4 my $leftnode = $self->left_node($index);
676 2         5 my $l_index = $self->left_node_index($index);
677              
678 2 50       5 if($leftnode){
679 2         5 $descendants{$l_index} = $leftnode;
680             }
681            
682 2         4 my $left_leftnode = $self->left_node($l_index);
683 2         4 my $left_leftnode_index = $self->left_node_index($l_index);
684              
685 2 100       4 if($left_leftnode){
686 1         2 $descendants{$left_leftnode_index} = $left_leftnode;
687             }
688              
689 2         3 my $left_rightnode = $self->right_node($l_index);
690 2         4 my $left_rightnode_index = $self->right_node_index($l_index);
691 2 100       3 if($left_rightnode){
692 1         2 $descendants{$left_rightnode_index} = $left_rightnode;
693             }
694            
695 2         2 my $index;
696            
697             # extract minimum
698             my $min_descendant;
699 2         3 my $i = 0;
700 2         6 foreach my $key (keys %descendants){
701 6 100       13 if($i == 0){
    100          
702 2         4 $min_descendant = $descendants{$key};
703              
704 2         2 $index = $key;
705 2         2 $i++;
706             }
707             elsif($self->{fcompare}->($descendants{$key}, $min_descendant) == -1){
708 2         2 $min_descendant = $descendants{$key};
709 2         3 $index = $key;
710             }
711             }
712 2         8 return $index;
713             }
714            
715 1         2 return;
716             }
717              
718              
719             ############################################################
720             #
721             # get_largest_descendant_index() returns the index of the
722             # largest descendant of this node.
723             #
724             ############################################################
725             sub get_largest_descendant_index
726             {
727 1     1 0 2 my ($self, $index) = @_;
728 1         2 my $array = $self->{_arr};
729            
730 1 50       42 if($self->has_children($index)){ # if has children
731 1         2 my %descendants;
732              
733             # right node and right node descendants
734 1         2 my $rightnode = $self->right_node($index);
735 1         3 my $r_index = $self->right_node_index($index);
736              
737 1 50       3 if($rightnode){
738 1         3 $descendants{$r_index} = $rightnode;
739             }
740              
741 1         2 my $right_leftnode = $self->left_node($r_index);
742 1         2 my $right_leftnode_index = $self->left_node_index($r_index);
743              
744 1 50       3 if($right_leftnode){
745 0         0 $descendants{$right_leftnode_index} = $right_leftnode;
746             }
747              
748 1         2 my $right_rightnode = $self->right_node($r_index);
749 1         2 my $right_rightnode_index = $self->right_node_index($r_index);
750              
751 1 50       3 if($right_rightnode){
752 0         0 $descendants{$right_rightnode_index} = $right_rightnode;
753             }
754              
755             # left node and left node descendants
756 1         1 my $leftnode = $self->left_node($index);
757 1         2 my $l_index = $self->left_node_index($index);
758              
759 1 50       2 if($leftnode){
760 1         3 $descendants{$l_index} = $leftnode;
761             }
762            
763 1         2 my $left_leftnode = $self->left_node($l_index);
764 1         2 my $left_leftnode_index = $self->left_node_index($l_index);
765              
766 1 50       3 if($left_leftnode){
767 0         0 $descendants{$left_leftnode_index} = $left_leftnode;
768             }
769              
770 1         2 my $left_rightnode = $self->right_node($l_index);
771 1         2 my $left_rightnode_index = $self->right_node_index($l_index);
772 1 50       3 if($left_rightnode){
773 0         0 $descendants{$left_rightnode_index} = $left_rightnode;
774             }
775            
776 1         1 my $index;
777            
778             # extract maximum
779             my $max_descendant;
780 1         1 my $i = 0;
781              
782 1         3 foreach my $key (keys %descendants){
783 2 100       5 if($i == 0){
    50          
784 1         2 $max_descendant = $descendants{$key};
785              
786 1         1 $index = $key;
787 1         1 $i++;
788             }
789             elsif($self->{fcompare}->($descendants{$key}, $max_descendant) == 1){
790 1         1 $max_descendant = $descendants{$key};
791 1         3 $index = $key;
792             }
793             }
794            
795 1         3 return $index;
796             }
797              
798 0         0 return;
799             }
800              
801              
802              
803              
804              
805             ################################################
806             #
807             # utilities for the heap algorithms
808             #
809             ################################################
810             sub default_cmp_func
811             {
812 24     24 0 25 my ($obj1, $obj2) = @_;
813            
814 24 50       42 if(fp_equal($obj1, $obj2, 10)){
815 0         0 return 0;
816             }
817 24 100       48 if($obj1 < $obj2){
818 8         37 return -1;
819             }
820 16         42 return 1;
821             }
822              
823             sub default_eval
824             {
825 0     0 0 0 my ($elt) = @_;
826 0         0 return $elt;
827             }
828              
829              
830             sub parent_node_index
831             {
832 57     57 0 55 my ($self,
833             $index) = @_;
834            
835 57 100       116 if($index == 0){
836 7         17 return -1;
837             }
838              
839 50         101 return int(($index-1)/2);
840             }
841              
842              
843             sub grandparent_node_index
844             {
845 1     1 0 2 my ($self,
846             $index) = @_;
847            
848 1 50       3 if($index == 0){
849 0         0 return;
850             }
851 1         2 my $parent_index = $self->parent_node_index($index);
852 1 50       11 if($parent_index){
853 1         7 return $self->parent_node_index($parent_index);
854             }
855 0         0 return;
856             }
857              
858             sub right_node
859             {
860 10     10 0 12 my ($self, $index) = @_;
861              
862 10         18 my $r_index = $self->right_node_index($index);
863 10         13 my $array = $self->{_arr};
864              
865 10 100       18 if($r_index < @$array){
866 3         5 return $array->[$r_index];
867             }
868 7         14 return;
869             }
870              
871             sub right_node_index
872             {
873 26     26 0 28 my ($self,
874             $index) = @_;
875 26         41 return $index*2 + 2;
876             }
877              
878              
879             sub left_node
880             {
881 13     13 0 17 my ($self, $index) = @_;
882 13         30 my $l_index = $self->left_node_index($index);
883 13         18 my $array = $self->{_arr};
884            
885 13 100       25 if($l_index < @$array){
886 8         19 return $array->[$l_index];
887             }
888 5         10 return;
889             }
890              
891              
892             sub left_node_index
893             {
894 30     30 0 32 my ($self,
895             $index) = @_;
896 30         45 return $index*2 + 1;
897             }
898              
899              
900             sub parent
901             {
902 1     1 0 1 my ($self,
903             $index) = @_;
904 1         2 my $array = $self->{_arr};
905              
906 1 50       3 if($index == 0){
907 0         0 return;
908             }
909 1         2 my $parent_index = $self->parent_node_index($index);
910            
911 1 50       3 if($parent_index){
912 1         3 return $self->{_arr}->[$parent_index];
913             }
914 0         0 return;
915             }
916              
917              
918             sub get_size
919             {
920 0     0 1 0 my $self = shift;
921 0         0 my $array = $self->{_arr};
922 0         0 return @$array;
923             }
924              
925             sub is_empty
926             {
927 0     0 0 0 my ($self) = @_;
928 0         0 my $array = $self->{_arr};
929 0 0       0 if(@$array == 0){
930 0         0 return 1;
931             }
932 0         0 return 0;
933             }
934              
935              
936             sub has_grandparent
937             {
938 16     16 0 18 my ($self, $i) = @_;
939              
940 16         20 my $parent_node_index = $self->parent_node_index($i);
941            
942 16 100       29 if($parent_node_index){
943 5 100       20 if($self->parent_node_index($parent_node_index) != -1){
944 1         3 return 1;
945              
946             }
947             }
948 15         85 return 0;
949             }
950              
951             sub has_parent
952             {
953 18     18 0 19 my ($self, $i) = @_;
954            
955 18 100       35 if($self->parent_node_index($i) != -1){
956 15         38 return 1;
957             }
958 3         20 return -1;
959             }
960              
961             sub has_children
962             {
963 4     4 0 6 my ($self, $i) = @_;
964              
965 4 100 66     10 if($self->left_node($i) || $self->right_node($i)){ # if has children
966 3         9 return 1;
967             }
968 1         2 return 0;
969             }
970              
971             sub is_grandchild
972             {
973 3     3 0 4 my ($self, $index, $gindex) = @_;
974            
975 3         6 my $l_index = $self->left_node_index($index);
976 3         5 my $r_index = $self->right_node_index($index);
977            
978 3         6 my $l_l_index = $self->left_node_index($l_index);
979 3 100       8 if($gindex == $l_l_index){
980 1         3 return 1;
981             }
982 2         4 my $l_r_index = $self->right_node_index($l_index);
983 2 50       4 if($gindex == $l_r_index){
984 0         0 return 1;
985             }
986 2         4 my $r_l_index = $self->left_node_index($r_index);
987 2 50       5 if($gindex == $r_l_index){
988 0         0 return 1;
989             }
990 2         3 my $r_r_index = $self->right_node_index($r_index);
991 2 50       4 if($gindex == $r_r_index){
992 0         0 return 1;
993             }
994              
995 2         17 return 0;
996             }
997              
998              
999              
1000             sub get_level
1001             {
1002 25     25 0 40 my ($self, $i) = @_;
1003 25         26 my $log;
1004 25         82 my ($pkg, $filename, $line) = caller();
1005              
1006 25 100       48 if($i == 0){
1007 7         25 return 0;
1008             }
1009 18         35 $log = log($i + 1) / log(2);
1010 18         33 return int($log);
1011             }
1012              
1013              
1014              
1015             ############################################################
1016             #
1017             # print
1018             #
1019             # $mm_heap->print();
1020             #
1021             # Dump the contents of the heap to STDOUT
1022             #
1023             ############################################################
1024             sub print{
1025 0     0 1 0 $_[0]->print_heap();
1026             }
1027              
1028             sub print_heap{
1029 0     0 0 0 my ($self) = @_;
1030 0         0 my $array = $self->{_arr};
1031 0         0 my $eval_func = $self->{feval};
1032              
1033 0         0 my $i = 0;
1034 0         0 foreach my $elt (@$array){
1035 0         0 my $val = $eval_func->($elt);
1036 0 0       0 if(!defined($val)){
1037 0         0 croak "Error: evaluation function provided to Heap::MinMax object returned null\n";
1038             }
1039 0         0 print $eval_func->($elt) . "\n";
1040 0         0 $i++;
1041             }
1042             }
1043              
1044              
1045              
1046             sub fp_equal {
1047 24     24 0 22 my ($A, $B, $dp) = @_;
1048              
1049 24         162 return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $B);
1050             }
1051              
1052              
1053              
1054              
1055              
1056             1;
1057             __END__