File Coverage

blib/lib/Algorithm/Tree/NCA.pm
Criterion Covered Total %
statement 111 121 91.7
branch 18 26 69.2
condition 6 6 100.0
subroutine 23 24 95.8
pod 0 3 0.0
total 158 180 87.7


line stmt bran cond sub pod time code
1             # Copyright 2002 by Mats Kindahl. All rights reserved.
2             #
3             # This program is free software; you can redistribute it and/or modify
4             # it under the same terms as Perl itself.
5              
6             package Algorithm::Tree::NCA::Data;
7              
8 3     3   84678 use 5.006;
  3         12  
  3         403  
9 3     3   18 use strict;
  3         6  
  3         112  
10 3     3   15 use warnings;
  3         10  
  3         132  
11              
12 3     3   2732 use fields qw(_run _magic _number _parent _leader _max _node);
  3         7843  
  3         30  
13              
14             sub new ($%) {
15 572     572   646 my $class = shift;
16             # Default values first, then the provided parameters
17 572         2997 my %args = (_run => 0, # Corresponds to I(v)
18             _magic => 0, # Corresponds to A_v
19             _max => 0, # Maximum number assigned to subtree
20             _number => 0, # The DFS number assigned to this node
21             _parent => undef, # The parent node data for this node
22             _leader => undef, # The leader node data for this node
23             _node => undef, # The node that the data is for
24             @_);
25              
26 572         1355 my $self = fields::new($class);
27 572         39835 @$self{keys %args} = values %args;
28 572         2165 return $self;
29             }
30              
31             package Algorithm::Tree::NCA;
32              
33 3     3   1000 use strict;
  3         5  
  3         94  
34 3     3   84 use warnings;
  3         7  
  3         473  
35              
36 3     3   9200 use Data::Dumper;
  3         84077  
  3         704  
37              
38             require Exporter;
39              
40             our @ISA = qw(Exporter);
41              
42             # Items to export into callers namespace by default. Note: do not export
43             # names by default without a very good reason. Use EXPORT_OK instead.
44             # Do not simply export all your public functions/methods/constants.
45              
46             our @EXPORT_OK = ();
47             our @EXPORT = ();
48             our $VERSION = '0.02';
49              
50             # Preloaded methods go here.
51              
52 3     3   33 use fields qw(_get _set _data);
  3         8  
  3         28  
53              
54             sub _set_method {
55 572     572   624 my($node,$value) = @_;
56              
57 572         1684 $node->{'_nca_number'} = $value;
58             }
59              
60             sub _get_method {
61 41237     41237   46732 my($node) = @_;
62              
63 41237         119099 return $node->{'_nca_number'};
64             }
65              
66              
67             sub new ($%) {
68 9     9 0 16896 my($class,%o) = @_;
69              
70 9 50       89 $o{-get} = \&_get_method unless defined $o{-get};
71 9 50       84 $o{-set} = \&_set_method unless defined $o{-set};
72              
73 9         49 my $self = fields::new($class);
74              
75 9         26099 $self->{_get} = $o{'-get'}; # Get method to use
76 9         30 $self->{_set} = $o{'-set'}; # Set method to use
77 9         25 $self->{_data} = []; # Array of node data
78              
79              
80             # Preprocess the tree if there is one supplied
81 9 100       62 $self->preprocess($o{-tree}) if exists $o{-tree};
82              
83 9         149 return $self;
84             }
85              
86             sub _get ($$) {
87 41237     41237   51698 my($self,$node) = @_;
88 41237         77729 $self->{_get}->($node);
89             }
90              
91             sub _set ($$$) {
92 572     572   716 my($self,$node,$val) = @_;
93 572         1038 $self->{_set}->($node,$val);
94             }
95              
96             sub _lssb ($) {
97 60900     60900   74035 my($v) = @_;
98 60900         92426 return $v & -$v;
99             }
100              
101             sub _mssb ($) {
102 58296     58296   73834 my($v) = @_;
103              
104 58296         70726 $v |= $v >> 1;
105 58296         58248 $v |= $v >> 2;
106 58296         60586 $v |= $v >> 4;
107 58296         57649 $v |= $v >> 8;
108 58296         64256 $v |= $v >> 16;
109              
110 58296         101554 return $v - ($v >> 1);
111             }
112              
113             sub _data ($$) {
114 41237     41237   56968 my($self,$node) = @_;
115 41237         95329 return $self->{_data}->[$self->_get($node)];
116             }
117              
118             sub preprocess ($$) {
119 9     9 0 50 my($self,$root) = @_;
120              
121             # Enumeration phase
122 9         41 $self->_enumerate($root, 1);
123              
124             # Computing magic number and leaders
125 9         39 $self->_compute_magic($root, $self->_data($root), 0);
126             }
127              
128             # Enumerate each node of the tree with a number v and compute the run
129             # I(v) for each node. Also set the parent for each node.
130             sub _enumerate ($$$;$) {
131 572     572   761 my($self,$node,$number,$parent) = @_;
132              
133 572         1328 my $data = Algorithm::Tree::NCA::Data
134             ->new(_node => $node,
135             _run => $number,
136             _parent => $parent,
137             _number => $number);
138              
139 572         1178 $self->{_data}->[$number] = $data;
140              
141 572         1171 $self->_set($node,$number);
142              
143 572         1841 my $run = $number++;
144            
145 572         1484 for my $c ($node->children()) {
146 563         2274 ($number, $run) = $self->_enumerate($c, $number, $data);
147 563 100       1054 if (_lssb($run) > _lssb($data->{_run})) {
148 311         601 $data->{_run} = $run;
149             }
150             }
151 572         2352 $data->{_max} = $number;
152 572         1497 return ($number,$data->{_run});
153             }
154              
155             # Compute the magic number A_v and the leader L(v) for each node v.
156             sub _compute_magic ($$$$) {
157 572     572   730 my($self,$node,$ldata,$magic) = @_;
158              
159 572         948 my $ndata = $self->_data($node);
160              
161 572         1250 $ndata->{_magic} = $magic | _lssb($ndata->{_run});
162              
163 572 100       1074 if ($ndata->{_run} != $ldata->{_run}) {
164 322         411 $ndata->{_leader} = $ndata;
165             } else {
166 250         309 $ndata->{_leader} = $ldata;
167             }
168              
169 572         1213 foreach my $c ($node->children()) {
170 563         3840 $self->_compute_magic($c,
171             $ndata->{_leader},
172             $ndata->{_magic});
173             }
174             }
175              
176             sub _display_data ($) {
177 0     0   0 my($self) = @_;
178              
179 0         0 my(@L,@I,@A);
180 0         0 foreach my $d (@{$self->{_data}}) {
  0         0  
181 0 0       0 push(@L, defined $d ? $d->{_leader}->{_number} : "*");
182 0 0       0 push(@I, defined $d ? $d->{_run} : "*");
183 0 0       0 push(@A, defined $d ? $d->{_magic} : "*");
184             }
185              
186 0         0 print STDERR "L = (@L)\n";
187 0         0 print STDERR "I = (@I)\n";
188 0         0 print STDERR "A = (@A)\n";
189             }
190              
191             # Compute the nearest common ancestor of nodes I(x) and I(y)
192             sub _bin_nca ($$$) {
193 19734     19734   27547 my($self,$xd,$yd)= @_;
194              
195 19734 100 100     82650 if ($xd->{_number} <= $yd->{_number} && $yd->{_number} < $xd->{_max}) {
196 36         89 return $xd->{_run};
197             }
198              
199 19698 100 100     82696 if ($yd->{_number} <= $xd->{_number} && $xd->{_number} < $yd->{_max}) {
200 36         1233 return $yd->{_run};
201             }
202              
203 19662         48415 my $k = _mssb($xd->{_run} ^ $yd->{_run});
204 19662         30034 my $m = $k ^ ($k - 1); # Mask off the k-1 most significant bits
205 19662         32673 my $r = ~$m & $xd->{_run}; # Take the k-1 most significant bits
206              
207             # Return k-1 least significant bits of I(x) with a 1 in position k
208 19662         37354 return ($r | $k);
209              
210             }
211              
212             # Find the node closest to 'x' but on the same run as the NCA.
213             sub _closest ($$$) {
214 39468     39468   103563 my($self,$xd,$j) = @_;
215              
216             # a. Find the position l of the right-most 1-bit in A_x
217 39468         76841 my $l = _lssb($xd->{_magic});
218              
219             # b. If l == j then nx is x (since x and z are on the same run)
220 39468 100       81760 if ($l == $j) {
221 834         1507 return $xd;
222             }
223              
224             # c. Find the position k of the left-most 1-bit in A_x that is to
225             # the right of position j.
226 38634         92991 my $k = _mssb(($j - 1) & $xd->{_magic});
227              
228             # Form the number u consisting of the bits of I(x) to the left
229             # of position k, followed by a 1-bit in position k, followed by
230             # all zeroes. (u will be I(w))
231 38634         71080 my $u = ~(($k - 1) | $k) & $xd->{_run} | $k;
232              
233             # Look up node L(I(w)), which must be node w. nx is then the parent
234             # of node w.
235 38634         74878 my $wd = $self->{_data}->[$u]->{_leader};
236              
237 38634         87210 return $wd->{_parent};
238            
239             }
240              
241             sub nca ($$$) {
242 20028     20028 0 31673899 my($self,$x,$y) = @_;
243 20028         43254 my $xd = $self->_data($x);
244 20028         40527 my $yd = $self->_data($y);
245              
246 20028 100       69888 if ($xd->{_number} == $yd->{_number}) {
247 294         742 return $x;
248             }
249              
250             # 1. Find the [nearest] common ancestor b in B of nodes I(x) and I(y).
251 19734         41592 my $b = $self->_bin_nca($xd,$yd);
252              
253             # 2. Find the smallest position j greater than or equal to h(b) such
254             # that both numbers A_x and A_y have 1-bits in position j. j is
255             # then h(I(z)).
256 19734         28060 my $m = ~$b & ($b - 1); # Mask for the h(b)-1 least significant bits
257 19734         37629 my $c = $xd->{_magic} & $yd->{_magic};
258             # The common set bits in A_x and A_y
259 19734         22814 my $u = $c & ~$m; # The upper bits of the common set bits
260 19734         34670 my $j = _lssb($u); # Isolate the rightmost 1-bit of u
261              
262             # 3a. Find node nx, the closest node to x on the same run as z.
263 19734         40786 my $nxd = $self->_closest($xd,$j);
264             # 3b. Find node ny, the closest node to y on the same run as z.
265 19734         37089 my $nyd = $self->_closest($yd,$j);
266              
267             # 4. If nx < ny then z is nx, else z is ny
268 19734 100       49718 if ($nxd->{_number} < $nyd->{_number}) {
269 9764         29333 return $nxd->{_node};
270             } else {
271 9970         32109 return $nyd->{_node};
272             }
273             }
274              
275             # Autoload methods go after =cut, and are processed by the autosplit program.
276              
277             1;
278             __END__