File Coverage

blib/lib/LCS/BV.pm
Criterion Covered Total %
statement 161 161 100.0
branch 30 30 100.0
condition 50 51 100.0
subroutine 11 11 100.0
pod 5 5 100.0
total 257 258 100.0


line stmt bran cond sub pod time code
1             package LCS::BV;
2              
3 4     4   96393 use 5.010001;
  4         26  
4 4     4   23 use strict;
  4         6  
  4         101  
5 4     4   21 use warnings;
  4         5  
  4         369  
6             our $VERSION = '0.14';
7             #use utf8;
8              
9             our $width = int 0.999+log(~0)/log(2);
10              
11 4     4   1969 use integer;
  4         56  
  4         19  
12 4     4   127 no warnings 'portable'; # for 0xffffffffffffffff
  4         7  
  4         6585  
13              
14             sub new {
15 14     14 1 2490 my $class = shift;
16             # uncoverable condition false
17 14 100 66     93 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  4 100       30  
18             }
19              
20             # H. Hyyroe. A Note on Bit-Parallel Alignment Computation. In
21             # M. Simanek and J. Holub, editors, Stringology, pages 79-87. Department
22             # of Computer Science and Engineering, Faculty of Electrical
23             # Engineering, Czech Technical University, 2004.
24              
25             sub LLCS {
26 805     805 1 5595387 my ($self,$a,$b) = @_;
27              
28             #use integer;
29             #no warnings 'portable'; # for 0xffffffffffffffff
30              
31             # TODO: maybe faster, if we have fewer expensive iterations
32             #if (@$a < @$b) {
33             # my $temp = $a;
34             # $a = $b;
35             # $b = $temp;
36             #}
37              
38 805         1845 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
39              
40 805   100     5001 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
41 673         877 $amin++;
42 673         2223 $bmin++;
43             }
44 805   100     3453 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
45 532         633 $amax--;
46 532         1676 $bmax--;
47             }
48              
49             #my %positions;
50              
51 805 100       1554 if ($amax < $width ) {
52 779         997 my %positions;
53             #$positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
54 779         1675 for ($amin..$amax) { $positions{$a->[$_]} |= 1 << ($_ % $width); }
  1839         3375  
55              
56 779         1055 my $v = ~0;
57 779         1088 my ($p,$u);
58              
59 779         1319 for ($bmin..$bmax) {
60 2649   100     5360 $p = $positions{$b->[$_]} // 0;
61 2649         3113 $u = $v & $p;
62 2649         3703 $v = ($v + $u) | ($v - $u);
63             }
64 779         1532 return $amin + _count_bits(~$v) + $#$a - $amax;
65             }
66             else {
67 26         59 my %positions;
68             #$positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for $amin..$amax;
69 26         139 for ($amin..$amax) { $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width); }
  3841         5814  
70              
71 26         52 my $S;
72 26         55 my @Vs = (); # $Vs->[$k] = bits;
73              
74 26         54 my ($p, $u, $carry);
75              
76 26         54 my $kmax = ($amax+1) / $width;
77 26 100       77 $kmax++ if (($amax+1) % $width);
78              
79 26         65 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  81         159  
80              
81 26         57 for my $j ($bmin..$bmax) {
82 3036         3416 $carry = 0;
83 3036         4650 for (my $k=0; $k < $kmax; $k++ ) {
84 10258         11544 $S = $Vs[$k];
85 10258   100     20467 $p = $positions{$b->[$j]}->[$k] // 0;
86 10258         11636 $u = $S & $p; # [Hyy04]
87 10258         12952 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
88 10258         20121 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
89             }
90             }
91              
92 26         62 my $bitcount = 0;
93              
94 26         74 for my $k ( @Vs ) {
95 81         174 $bitcount += _count_bits(~$k);
96             }
97 26         299 return $amin + $bitcount + $#$a - $amax;
98             }
99             }
100              
101             sub LLCS_prepared {
102 805     805 1 3042 my ($self,$positions,$b) = @_;
103              
104             #if ( ref($a) eq 'HASH' ) # $a contains $positions
105              
106 805         1274 my ($bmin, $bmax) = (0, $#$b);
107 805         1510 my ($amin, $amax) = (0, $self->{'length_a'} - 1);
108              
109 805 100       1468 if ($amax < $width ) {
110              
111 777         1012 my $v = ~0;
112 777         1110 my ($p,$u);
113              
114 777         1398 for ($bmin..$bmax) {
115 3711   100     7704 $p = $positions->{$b->[$_]} // 0;
116 3711         4414 $u = $v & $p;
117 3711         5050 $v = ($v + $u) | ($v - $u);
118             }
119 777         1519 return _count_bits(~$v);
120             }
121             else {
122              
123 28         58 my $S;
124 28         57 my @Vs = (); # $Vs->[$k] = bits;
125              
126 28         61 my ($p, $u, $carry);
127              
128 28         52 my $kmax = ($amax+1) / $width;
129 28 100       90 $kmax++ if (($amax+1) % $width);
130              
131 28         86 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  85         161  
132              
133 28         61 for my $j ($bmin..$bmax) {
134 3179         3399 $carry = 0;
135 3179         4824 for (my $k=0; $k < $kmax; $k++ ) {
136 10544         12393 $S = $Vs[$k];
137 10544   100     20627 $p = $positions->{$b->[$j]}->[$k] // 0;
138 10544         11873 $u = $S & $p; # [Hyy04]
139 10544         13559 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
140 10544         20180 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
141             }
142             }
143              
144 28         40 my $bitcount = 0;
145              
146 28         65 for my $k ( @Vs ) {
147 85         162 $bitcount += _count_bits(~$k);
148             }
149 28         221 return $bitcount;
150             }
151             }
152              
153              
154             sub LCS {
155 795     795 1 7653745 my ($self, $a, $b) = @_;
156              
157             #use integer;
158             #no warnings 'portable'; # for 0xffffffffffffffff
159              
160 795         2256 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
161              
162 795   100     17314 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
163 2161         2470 $amin++;
164 2161         6847 $bmin++;
165             }
166 795   100     3834 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
167 532         665 $amax--;
168 532         1713 $bmax--;
169             }
170              
171 795         1615 my %positions;
172             my @lcs;
173              
174 795 100       1899 if ($amax < $width ) {
175 777         3896 $positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
176              
177 777         1184 my $S = ~0;
178 777         1768 my @Vs = (~0);
179              
180 777         1074 my ($y,$u);
181              
182             # outer loop
183 777         1448 for my $j ($bmin..$bmax) {
184 1617   100     4136 $y = $positions{$b->[$j]} // 0;
185 1617         2319 $u = $S & $y; # [Hyy04]
186 1617         2223 $S = ($S + $u) | ($S - $u); # [Hyy04]
187 1617         2220 $Vs[$j] = $S;
188             }
189              
190             # recover alignment
191 777         1108 my $i = $amax;
192 777         1040 my $j = $bmax;
193              
194 777   100     2763 while ($i >= $amin && $j >= $bmin) {
195 1263 100       7608 if ($Vs[$j] & (1<<$i)) {
196 850         2026 $i--;
197             }
198             else {
199 413 100 100     1238 unless (
200             $j && ~$Vs[$j-1] & (1<<$i)
201             ) {
202 266         705 unshift @lcs, [$i,$j];
203 266         317 $i--;
204             }
205 413         1091 $j--;
206             }
207             }
208             }
209             else {
210 18         721 $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for $amin..$amax;
211              
212 18         31 my $S;
213 18         65 my @Vs = ([~0]);
214 18         38 my ($y,$u,$carry);
215              
216 18         50 my $kmax = ($amax+1) / $width;
217 18 100       55 $kmax++ if (($amax+1) % $width);
218              
219             # outer loop
220 18         48 for my $j ($bmin..$bmax) {
221 1488         2538 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$j]->[$k] = ~0; }
  5098         7265  
222 1488         1465 $carry = 0;
223              
224 1488         1887 for (my $k=0; $k < $kmax; $k++ ) {
225 5098 100       6227 $S = ($j > $bmin) ? $Vs[$j-1]->[$k] : ~0;
226 5098   100     9453 $y = $positions{$b->[$j]}->[$k] // 0;
227 5098         4674 $u = $S & $y; # [Hyy04]
228              
229 5098         5504 $Vs[$j]->[$k] = ($S + $u + $carry) | ($S - $u);
230              
231 5098         8867 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
232             }
233             }
234              
235             # recover alignment
236 18         45 my $i = $amax;
237 18         32 my $j = $bmax;
238              
239 18   100     96 while ($i >= $amin && $j >= $bmin) {
240 1638         1583 my $k = $i / $width;
241 1638 100       2001 if ($Vs[$j]->[$k] & (1<<($i % $width))) {
242 1369         2982 $i--;
243             }
244             else {
245 269 100 100     674 unless (
246             $j && ~$Vs[$j-1]->[$k] & (1<<($i % $width))
247             ) {
248 169         276 unshift @lcs, [$i,$j];
249 169         206 $i--;
250             }
251 269         625 $j--;
252             }
253             }
254             }
255              
256             return [
257 795         6914 map([$_ => $_], 0 .. ($bmin-1)), ## no critic qw(BuiltinFunctions::RequireBlockMap)
258             @lcs,
259             map([++$amax => $_], ($bmax+1) .. $#$b) ## no critic qw(BuiltinFunctions::RequireBlockMap)
260             ];
261             }
262              
263             sub prepare {
264 805     805 1 5647745 my ($self, $a) = @_;
265              
266 805         1139 $self->{'length_a'} = @{$a};
  805         1434  
267              
268 805         1123 my $positions;
269              
270 805 100       1732 if ($#$a < $width ) {
271 777         4442 $positions->{$a->[$_]} |= 1 << ($_ % $width) for 0..$#$a;
272             }
273             else {
274 28         2109 $positions->{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for 0..$#$a;
275             }
276 805         1787 return $positions;
277             }
278              
279             sub _count_bits {
280 1732     1732   7650 my $v = shift;
281              
282             #use integer;
283             #no warnings 'portable'; # for 0xffffffffffffffff
284              
285 1732 100       3176 if ($width == 64) {
286 1727         2610 $v = $v - (($v >> 1) & 0x5555555555555555);
287 1727         2408 $v = ($v & 0x3333333333333333) + (($v >> 2) & 0x3333333333333333);
288             # (bytesof($v) -1) * bitsofbyte = (8-1)*8 = 56 ----------------------vv
289 1727         2483 $v = (($v + ($v >> 4) & 0x0f0f0f0f0f0f0f0f) * 0x0101010101010101) >> 56;
290 1727         8581 return $v;
291             }
292             else {
293             #$v = $v - (($v >> 1) & 0x55555555);
294             #$v = ($v & 0x33333333) + (($v >> 2) & 0x33333333);
295             ## (bytesof($v) -1) * bitsofbyte = (4-1)*8 = 24 ------vv
296             #$v = (($v + ($v >> 4) & 0x0f0f0f0f) * 0x01010101) >> 24
297              
298 5         7 my $c; # count
299 5         15 for ($c = 0; $v; $c++) {
300 64         102 $v &= $v - 1; # clear the least significant bit set
301             }
302 5         20 return $c;
303             }
304             }
305              
306             1;
307              
308             __END__