File Coverage

blib/lib/SkewHeap/PP.pm
Criterion Covered Total %
statement 118 138 85.5
branch 16 22 72.7
condition 5 5 100.0
subroutine 28 33 84.8
pod 18 22 81.8
total 185 220 84.0


line stmt bran cond sub pod time code
1             package SkewHeap::PP;
2             # ABSTRACT: a fast and flexible heap structure
3             $SkewHeap::PP::VERSION = '0.01';
4              
5             #-------------------------------------------------------------------------------
6             # Boilerplate
7             #-------------------------------------------------------------------------------
8 1     1   185967 use strict;
  1         5  
  1         23  
9 1     1   4 use warnings;
  1         2  
  1         22  
10              
11 1     1   4 use feature 'signatures';
  1         1  
  1         124  
12 1     1   6 no warnings 'experimental::signatures';
  1         1  
  1         40  
13              
14             #-------------------------------------------------------------------------------
15             # Node array index constants
16             #-------------------------------------------------------------------------------
17 1     1   5 use constant KEY => 0;
  1         1  
  1         60  
18 1     1   5 use constant LEFT => 1;
  1         1  
  1         46  
19 1     1   5 use constant RIGHT => 2;
  1         2  
  1         43  
20              
21             #-------------------------------------------------------------------------------
22             # Skew heap array index constants
23             #-------------------------------------------------------------------------------
24 1     1   5 use constant CMP => 0;
  1         2  
  1         36  
25 1     1   4 use constant SIZE => 1;
  1         1  
  1         41  
26 1     1   6 use constant ROOT => 2;
  1         2  
  1         45  
27              
28             #-------------------------------------------------------------------------------
29             # Exports
30             #-------------------------------------------------------------------------------
31 1     1   414 use parent 'Exporter';
  1         244  
  1         4  
32             our @EXPORT = qw(
33             skew
34             skew_count
35             skew_is_empty
36             skew_peek
37             skew_put
38             skew_take
39             skew_merge
40             skew_explain
41             );
42              
43             #-------------------------------------------------------------------------------
44             # Common interface
45             #-------------------------------------------------------------------------------
46             sub skew :prototype(&) {
47 8     8 1 5035 return [$_[0], 0, undef];
48             }
49              
50 10627     10627 0 9614 sub merge_nodes ($skew, $l, $r) {
  10627         9981  
  10627         9862  
  10627         10014  
  10627         9369  
51 10627 100       13624 return $l unless defined $r;
52 7483 100       8577 return $r unless defined $l;
53              
54 7473 100       9761 if ($skew->[CMP]->($l->[KEY], $r->[KEY]) > 0) {
55 4547         10499 ($l, $r) = ($r, $l);
56             }
57              
58 7473         11025 my $tmp = $l->[RIGHT];
59 7473         7457 $l->[RIGHT] = $l->[LEFT];
60 7473         8488 $l->[LEFT] = merge_nodes($skew, $r, $tmp);
61              
62 7473         8473 return $l;
63             }
64              
65 120     120 0 107 sub clone_node ($node) {
  120         146  
  120         109  
66 120 100       196 return unless defined $node;
67              
68             return [
69 57         80 $node->[KEY],
70             clone_node($node->[LEFT]),
71             clone_node($node->[RIGHT]),
72             ];
73             }
74              
75 6     6 0 7 sub merge_nodes_non_destructive ($skew, $l, $r) {
  6         7  
  6         6  
  6         8  
  6         6  
76 6 100       15 return clone_node($l) unless defined $r;
77 4 100       8 return clone_node($r) unless defined $l;
78              
79 3 100       6 if ($skew->[CMP]->($l->[KEY], $r->[KEY]) > 0) {
80 1         5 ($l, $r) = ($r, $l);
81             }
82              
83             return [
84 3         15 $l->[KEY],
85             merge_nodes_non_destructive($skew, $r, $l->[RIGHT]),
86             clone_node($l->[LEFT]),
87             ];
88             }
89              
90             sub skew_count :prototype($) {
91 17     17 1 452 return $_[0][SIZE];
92             }
93              
94             sub skew_is_empty :prototype($) {
95 6     6 1 561 return $_[0][SIZE] == 0;
96             }
97              
98             sub skew_peek :prototype($) {
99 0 0   0 1 0 return $_[0][ROOT][KEY] unless skew_is_empty($_[0]);
100 0         0 return;
101             }
102              
103 508     508 1 1487 sub skew_take ($skew, $want = undef) {
  508         513  
  508         485  
  508         467  
104 508         469 my @taken;
105 508   100     1499 while (($want || 1) > @taken && $skew->[SIZE] > 0) {
      100        
106 1591         1756 push @taken, $skew->[ROOT][KEY];
107 1591         1983 $skew->[ROOT] = merge_nodes($skew, $skew->[ROOT][LEFT], $skew->[ROOT][RIGHT]);
108 1591         3922 --$skew->[SIZE];
109             }
110              
111 508 100       935 return defined $want ? @taken : $taken[0];
112             }
113              
114 509     509 1 983 sub skew_put ($skew, @items) {
  509         487  
  509         578  
  509         456  
115 509         694 for (sort{ $skew->[CMP]->($b, $a) } @items) {
  7828         13931  
116 1561         2504 $skew->[ROOT] = merge_nodes($skew, $skew->[ROOT], [$_, undef, undef]);
117 1561         1835 ++$skew->[SIZE];
118             }
119              
120 509         645 return $skew->[SIZE];
121             }
122              
123 1     1 1 2 sub skew_merge ($skew, @heaps) {
  1         2  
  1         2  
  1         1  
124 1         2 for (@heaps) {
125 2         5 $skew->[ROOT] = merge_nodes($skew, $skew->[ROOT], $_->[ROOT]);
126 2         3 $skew->[SIZE] += $_->[SIZE];
127 2         4 $_->[ROOT] = undef;
128 2         4 $_->[SIZE] = 0;
129             }
130              
131 1         2 return $skew;
132             }
133              
134 1     1 1 2 sub skew_merge_safe (@heaps) {
  1         1  
  1         3  
135 1         2 my $skew = [$heaps[0][CMP], 0, undef];
136              
137 1         3 for (@heaps) {
138 3         6 $skew->[ROOT] = merge_nodes_non_destructive($skew, $skew->[ROOT], $_->[ROOT]);
139 3         7 $skew->[SIZE] += $_->[SIZE];
140             }
141              
142 1         3 return $skew;
143             }
144              
145 0     0 0 0 sub node_explain ($node, $indent_size=0) {
  0         0  
  0         0  
  0         0  
146 0         0 my $indent = ' ' x $indent_size;
147 0         0 print $indent.'- Node: '.$node->[KEY]."\n";
148              
149 0 0       0 if ($node->[LEFT]) {
150 0         0 node_explain($node->[LEFT], $indent_size + 1);
151             }
152              
153 0 0       0 if ($node->[RIGHT]) {
154 0         0 node_explain($node->[RIGHT], $indent_size + 1);
155             }
156             }
157              
158 0     0 1 0 sub skew_explain ($skew) {
  0         0  
  0         0  
159 0         0 my $n = skew_count($skew);
160 0         0 print "SkewHeap\n";
161 0         0 node_explain($skew->[ROOT], 1);
162             }
163              
164             #-------------------------------------------------------------------------------
165             # Object inteface
166             #-------------------------------------------------------------------------------
167 7     7 1 63921 sub new ($class, $cmp) {
  7         12  
  7         9  
  7         6  
168 7         18 my $skew = skew \&$cmp;
169 7         19 bless $skew, $class;
170             }
171              
172 14     14 1 385 sub count { goto \&skew_count }
173 3     3 1 448 sub is_empty { goto \&skew_is_empty }
174 0     0 1 0 sub peek { goto \&skew_peek }
175 8     8 1 36 sub put { goto \&skew_put }
176 7     7 1 19 sub take { goto \&skew_take }
177 1     1 1 7 sub merge { goto \&skew_merge }
178 0     0 1 0 sub explain { goto \&skew_explain }
179              
180 1     1 1 6 sub merge_safe ($self, @heaps) {
  1         2  
  1         3  
  1         2  
181 1         3 my $new = skew_merge_safe($self, @heaps);
182 1         4 bless $new, ref($self);
183             }
184              
185             1;
186              
187             __END__