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   90226 use 5.010001;
  4         24  
4 4     4   25 use strict;
  4         8  
  4         101  
5 4     4   21 use warnings;
  4         7  
  4         311  
6             our $VERSION = '0.12';
7             #use utf8;
8              
9             our $width = int 0.999+log(~0)/log(2);
10              
11 4     4   2017 use integer;
  4         56  
  4         18  
12 4     4   131 no warnings 'portable'; # for 0xffffffffffffffff
  4         6  
  4         6800  
13              
14             sub new {
15 14     14 1 2775 my $class = shift;
16             # uncoverable condition false
17 14 100 66     99 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 5784434 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         2042 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
39              
40 805   100     5003 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
41 673         862 $amin++;
42 673         2274 $bmin++;
43             }
44 805   100     3297 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
45 532         648 $amax--;
46 532         1718 $bmax--;
47             }
48              
49             #my %positions;
50              
51 805 100       1599 if ($amax < $width ) {
52 779         1047 my %positions;
53             #$positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
54 779         1693 for ($amin..$amax) { $positions{$a->[$_]} |= 1 << ($_ % $width); }
  1839         3566  
55              
56 779         1089 my $v = ~0;
57 779         1089 my ($p,$u);
58              
59 779         1321 for ($bmin..$bmax) {
60 2649   100     5374 $p = $positions{$b->[$_]} // 0;
61 2649         3175 $u = $v & $p;
62 2649         3741 $v = ($v + $u) | ($v - $u);
63             }
64 779         1589 return $amin + _count_bits(~$v) + $#$a - $amax;
65             }
66             else {
67 26         51 my %positions;
68             #$positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for $amin..$amax;
69 26         85 for ($amin..$amax) { $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width); }
  3841         6013  
70              
71 26         54 my $S;
72 26         70 my @Vs = (); # $Vs->[$k] = bits;
73              
74 26         44 my ($p, $u, $carry);
75              
76 26         49 my $kmax = ($amax+1) / $width;
77 26 100       82 $kmax++ if (($amax+1) % $width);
78              
79 26         85 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  81         150  
80              
81 26         64 for my $j ($bmin..$bmax) {
82 3036         3387 $carry = 0;
83 3036         4631 for (my $k=0; $k < $kmax; $k++ ) {
84 10258         11861 $S = $Vs[$k];
85 10258   100     20541 $p = $positions{$b->[$j]}->[$k] // 0;
86 10258         11474 $u = $S & $p; # [Hyy04]
87 10258         13108 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
88 10258         20053 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
89             }
90             }
91              
92 26         89 my $bitcount = 0;
93              
94 26         65 for my $k ( @Vs ) {
95 81         178 $bitcount += _count_bits(~$k);
96             }
97 26         322 return $amin + $bitcount + $#$a - $amax;
98             }
99             }
100              
101             sub LLCS_prepared {
102 805     805 1 3155 my ($self,$positions,$b) = @_;
103              
104             #if ( ref($a) eq 'HASH' ) # $a contains $positions
105              
106 805         1386 my ($bmin, $bmax) = (0, $#$b);
107 805         1476 my ($amin, $amax) = (0, $self->{'length_a'} - 1);
108              
109 805 100       1429 if ($amax < $width ) {
110              
111 777         1201 my $v = ~0;
112 777         998 my ($p,$u);
113              
114 777         1447 for ($bmin..$bmax) {
115 3711   100     7340 $p = $positions->{$b->[$_]} // 0;
116 3711         4353 $u = $v & $p;
117 3711         5471 $v = ($v + $u) | ($v - $u);
118             }
119 777         1510 return _count_bits(~$v);
120             }
121             else {
122              
123 28         55 my $S;
124 28         51 my @Vs = (); # $Vs->[$k] = bits;
125              
126 28         52 my ($p, $u, $carry);
127              
128 28         62 my $kmax = ($amax+1) / $width;
129 28 100       94 $kmax++ if (($amax+1) % $width);
130              
131 28         71 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  85         157  
132              
133 28         64 for my $j ($bmin..$bmax) {
134 3179         3496 $carry = 0;
135 3179         4861 for (my $k=0; $k < $kmax; $k++ ) {
136 10544         11879 $S = $Vs[$k];
137 10544   100     21049 $p = $positions->{$b->[$j]}->[$k] // 0;
138 10544         12011 $u = $S & $p; # [Hyy04]
139 10544         13518 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
140 10544         20342 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
141             }
142             }
143              
144 28         50 my $bitcount = 0;
145              
146 28         59 for my $k ( @Vs ) {
147 85         165 $bitcount += _count_bits(~$k);
148             }
149 28         223 return $bitcount;
150             }
151             }
152              
153              
154             sub LCS {
155 795     795 1 8103288 my ($self, $a, $b) = @_;
156              
157             #use integer;
158             #no warnings 'portable'; # for 0xffffffffffffffff
159              
160 795         2363 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
161              
162 795   100     4774 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
163 2161         2574 $amin++;
164 2161         7020 $bmin++;
165             }
166 795   100     3596 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
167 532         665 $amax--;
168 532         1743 $bmax--;
169             }
170              
171 795         1141 my %positions;
172             my @lcs;
173              
174 795 100       1508 if ($amax < $width ) {
175 777         3289 $positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
176              
177 777         1194 my $S = ~0;
178 777         1224 my @Vs = (~0);
179              
180 777         1018 my ($y,$u);
181              
182             # outer loop
183 777         1223 for my $j ($bmin..$bmax) {
184 1617   100     4236 $y = $positions{$b->[$j]} // 0;
185 1617         2084 $u = $S & $y; # [Hyy04]
186 1617         2155 $S = ($S + $u) | ($S - $u); # [Hyy04]
187 1617         2212 $Vs[$j] = $S;
188             }
189              
190             # recover alignment
191 777         1030 my $i = $amax;
192 777         973 my $j = $bmax;
193              
194 777   100     2610 while ($i >= $amin && $j >= $bmin) {
195 1263 100       2024 if ($Vs[$j] & (1<<$i)) {
196 850         2261 $i--;
197             }
198             else {
199 413 100 100     1195 unless (
200             $j && ~$Vs[$j-1] & (1<<$i)
201             ) {
202 266         690 unshift @lcs, [$i,$j];
203 266         417 $i--;
204             }
205 413         1187 $j--;
206             }
207             }
208             }
209             else {
210 18         829 $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for $amin..$amax;
211              
212 18         39 my $S;
213 18         66 my @Vs = ([~0]);
214 18         53 my ($y,$u,$carry);
215              
216 18         51 my $kmax = ($amax+1) / $width;
217 18 100       69 $kmax++ if (($amax+1) % $width);
218              
219             # outer loop
220 18         53 for my $j ($bmin..$bmax) {
221 1488         2436 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$j]->[$k] = ~0; }
  5098         9394  
222 1488         1696 $carry = 0;
223              
224 1488         2407 for (my $k=0; $k < $kmax; $k++ ) {
225 5098 100       8055 $S = ($j > $bmin) ? $Vs[$j-1]->[$k] : ~0;
226 5098   100     11372 $y = $positions{$b->[$j]}->[$k] // 0;
227 5098         6096 $u = $S & $y; # [Hyy04]
228              
229 5098         6762 $Vs[$j]->[$k] = ($S + $u + $carry) | ($S - $u);
230              
231 5098         10603 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
232             }
233             }
234              
235             # recover alignment
236 18         57 my $i = $amax;
237 18         37 my $j = $bmax;
238              
239 18   100     114 while ($i >= $amin && $j >= $bmin) {
240 1638         1880 my $k = $i / $width;
241 1638 100       2420 if ($Vs[$j]->[$k] & (1<<($i % $width))) {
242 1369         3356 $i--;
243             }
244             else {
245 269 100 100     714 unless (
246             $j && ~$Vs[$j-1]->[$k] & (1<<($i % $width))
247             ) {
248 169         366 unshift @lcs, [$i,$j];
249 169         202 $i--;
250             }
251 269         695 $j--;
252             }
253             }
254             }
255              
256             return [
257 795         5884 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 5667324 my ($self, $a) = @_;
265              
266 805         1188 $self->{'length_a'} = @{$a};
  805         1614  
267              
268 805         1175 my $positions;
269              
270 805 100       1639 if ($#$a < $width ) {
271 777         4483 $positions->{$a->[$_]} |= 1 << ($_ % $width) for 0..$#$a;
272             }
273             else {
274 28         2183 $positions->{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for 0..$#$a;
275             }
276 805         1815 return $positions;
277             }
278              
279             sub _count_bits {
280 1732     1732   8328 my $v = shift;
281              
282             #use integer;
283             #no warnings 'portable'; # for 0xffffffffffffffff
284              
285 1732 100       3077 if ($width == 64) {
286 1727         2597 $v = $v - (($v >> 1) & 0x5555555555555555);
287 1727         2515 $v = ($v & 0x3333333333333333) + (($v >> 2) & 0x3333333333333333);
288             # (bytesof($v) -1) * bitsofbyte = (8-1)*8 = 56 ----------------------vv
289 1727         2737 $v = (($v + ($v >> 4) & 0x0f0f0f0f0f0f0f0f) * 0x0101010101010101) >> 56;
290 1727         7918 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         6 my $c; # count
299 5         16 for ($c = 0; $v; $c++) {
300 64         96 $v &= $v - 1; # clear the least significant bit set
301             }
302 5         21 return $c;
303             }
304             }
305              
306             1;
307              
308             __END__