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   94348 use 5.010001;
  4         24  
4 4     4   37 use strict;
  4         7  
  4         98  
5 4     4   20 use warnings;
  4         6  
  4         322  
6             our $VERSION = '0.13';
7             #use utf8;
8              
9             our $width = int 0.999+log(~0)/log(2);
10              
11 4     4   2088 use integer;
  4         59  
  4         20  
12 4     4   135 no warnings 'portable'; # for 0xffffffffffffffff
  4         9  
  4         7070  
13              
14             sub new {
15 14     14 1 2564 my $class = shift;
16             # uncoverable condition false
17 14 100 66     125 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  4 100       31  
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 5697658 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         1930 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
39              
40 805   100     4846 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
41 673         844 $amin++;
42 673         2331 $bmin++;
43             }
44 805   100     3326 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
45 532         671 $amax--;
46 532         1708 $bmax--;
47             }
48              
49             #my %positions;
50              
51 805 100       1569 if ($amax < $width ) {
52 779         980 my %positions;
53             #$positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
54 779         1706 for ($amin..$amax) { $positions{$a->[$_]} |= 1 << ($_ % $width); }
  1839         3605  
55              
56 779         1017 my $v = ~0;
57 779         1020 my ($p,$u);
58              
59 779         1263 for ($bmin..$bmax) {
60 2649   100     5371 $p = $positions{$b->[$_]} // 0;
61 2649         3054 $u = $v & $p;
62 2649         3820 $v = ($v + $u) | ($v - $u);
63             }
64 779         1501 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         93 for ($amin..$amax) { $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width); }
  3841         5807  
70              
71 26         79 my $S;
72 26         57 my @Vs = (); # $Vs->[$k] = bits;
73              
74 26         64 my ($p, $u, $carry);
75              
76 26         55 my $kmax = ($amax+1) / $width;
77 26 100       71 $kmax++ if (($amax+1) % $width);
78              
79 26         72 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  81         155  
80              
81 26         56 for my $j ($bmin..$bmax) {
82 3036         3320 $carry = 0;
83 3036         4650 for (my $k=0; $k < $kmax; $k++ ) {
84 10258         11830 $S = $Vs[$k];
85 10258   100     20480 $p = $positions{$b->[$j]}->[$k] // 0;
86 10258         11642 $u = $S & $p; # [Hyy04]
87 10258         13006 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
88 10258         19350 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
89             }
90             }
91              
92 26         63 my $bitcount = 0;
93              
94 26         75 for my $k ( @Vs ) {
95 81         163 $bitcount += _count_bits(~$k);
96             }
97 26         312 return $amin + $bitcount + $#$a - $amax;
98             }
99             }
100              
101             sub LLCS_prepared {
102 805     805 1 3041 my ($self,$positions,$b) = @_;
103              
104             #if ( ref($a) eq 'HASH' ) # $a contains $positions
105              
106 805         1409 my ($bmin, $bmax) = (0, $#$b);
107 805         1691 my ($amin, $amax) = (0, $self->{'length_a'} - 1);
108              
109 805 100       1448 if ($amax < $width ) {
110              
111 777         1022 my $v = ~0;
112 777         1077 my ($p,$u);
113              
114 777         1357 for ($bmin..$bmax) {
115 3711   100     7557 $p = $positions->{$b->[$_]} // 0;
116 3711         4418 $u = $v & $p;
117 3711         5227 $v = ($v + $u) | ($v - $u);
118             }
119 777         1497 return _count_bits(~$v);
120             }
121             else {
122              
123 28         53 my $S;
124 28         64 my @Vs = (); # $Vs->[$k] = bits;
125              
126 28         49 my ($p, $u, $carry);
127              
128 28         83 my $kmax = ($amax+1) / $width;
129 28 100       79 $kmax++ if (($amax+1) % $width);
130              
131 28         75 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$k] = ~0; }
  85         197  
132              
133 28         82 for my $j ($bmin..$bmax) {
134 3179         3455 $carry = 0;
135 3179         4801 for (my $k=0; $k < $kmax; $k++ ) {
136 10544         12056 $S = $Vs[$k];
137 10544   100     20820 $p = $positions->{$b->[$j]}->[$k] // 0;
138 10544         12836 $u = $S & $p; # [Hyy04]
139 10544         14396 $Vs[$k] = ($S + $u + $carry) | ($S - $u);
140 10544         20559 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
141             }
142             }
143              
144 28         62 my $bitcount = 0;
145              
146 28         58 for my $k ( @Vs ) {
147 85         173 $bitcount += _count_bits(~$k);
148             }
149 28         217 return $bitcount;
150             }
151             }
152              
153              
154             sub LCS {
155 795     795 1 8139204 my ($self, $a, $b) = @_;
156              
157             #use integer;
158             #no warnings 'portable'; # for 0xffffffffffffffff
159              
160 795         2212 my ($amin, $amax, $bmin, $bmax) = (0, $#$a, 0, $#$b);
161              
162 795   100     5385 while ($amin <= $amax and $bmin <= $bmax and $a->[$amin] eq $b->[$bmin]) {
      100        
163 2161         2725 $amin++;
164 2161         7553 $bmin++;
165             }
166 795   100     3323 while ($amin <= $amax and $bmin <= $bmax and $a->[$amax] eq $b->[$bmax]) {
      100        
167 532         702 $amax--;
168 532         1743 $bmax--;
169             }
170              
171 795         1176 my %positions;
172             my @lcs;
173              
174 795 100       1575 if ($amax < $width ) {
175 777         3401 $positions{$a->[$_]} |= 1 << ($_ % $width) for $amin..$amax;
176              
177 777         1185 my $S = ~0;
178 777         1347 my @Vs = (~0);
179              
180 777         1108 my ($y,$u);
181              
182             # outer loop
183 777         1298 for my $j ($bmin..$bmax) {
184 1617   100     4100 $y = $positions{$b->[$j]} // 0;
185 1617         2189 $u = $S & $y; # [Hyy04]
186 1617         2235 $S = ($S + $u) | ($S - $u); # [Hyy04]
187 1617         2386 $Vs[$j] = $S;
188             }
189              
190             # recover alignment
191 777         1085 my $i = $amax;
192 777         1043 my $j = $bmax;
193              
194 777   100     2587 while ($i >= $amin && $j >= $bmin) {
195 1263 100       2161 if ($Vs[$j] & (1<<$i)) {
196 850         2279 $i--;
197             }
198             else {
199 413 100 100     1263 unless (
200             $j && ~$Vs[$j-1] & (1<<$i)
201             ) {
202 266         644 unshift @lcs, [$i,$j];
203 266         353 $i--;
204             }
205 413         1167 $j--;
206             }
207             }
208             }
209             else {
210 18         893 $positions{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for $amin..$amax;
211              
212 18         47 my $S;
213 18         75 my @Vs = ([~0]);
214 18         62 my ($y,$u,$carry);
215              
216 18         58 my $kmax = ($amax+1) / $width;
217 18 100       78 $kmax++ if (($amax+1) % $width);
218              
219             # outer loop
220 18         55 for my $j ($bmin..$bmax) {
221 1488         2494 for (my $k=0; $k < $kmax; $k++ ) { $Vs[$j]->[$k] = ~0; }
  5098         9359  
222 1488         1717 $carry = 0;
223              
224 1488         2394 for (my $k=0; $k < $kmax; $k++ ) {
225 5098 100       8320 $S = ($j > $bmin) ? $Vs[$j-1]->[$k] : ~0;
226 5098   100     11602 $y = $positions{$b->[$j]}->[$k] // 0;
227 5098         6317 $u = $S & $y; # [Hyy04]
228              
229 5098         7244 $Vs[$j]->[$k] = ($S + $u + $carry) | ($S - $u);
230              
231 5098         11097 $carry = (($S & $u) | (($S | $u) & ~($S + $u + $carry))) >> ($width-1) & 1;
232             }
233             }
234              
235             # recover alignment
236 18         31 my $i = $amax;
237 18         35 my $j = $bmax;
238              
239 18   100     85 while ($i >= $amin && $j >= $bmin) {
240 1638         2001 my $k = $i / $width;
241 1638 100       2625 if ($Vs[$j]->[$k] & (1<<($i % $width))) {
242 1369         3552 $i--;
243             }
244             else {
245 269 100 100     729 unless (
246             $j && ~$Vs[$j-1]->[$k] & (1<<($i % $width))
247             ) {
248 169         332 unshift @lcs, [$i,$j];
249 169         208 $i--;
250             }
251 269         745 $j--;
252             }
253             }
254             }
255              
256             return [
257 795         6345 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 5611727 my ($self, $a) = @_;
265              
266 805         1141 $self->{'length_a'} = @{$a};
  805         1505  
267              
268 805         1145 my $positions;
269              
270 805 100       1813 if ($#$a < $width ) {
271 777         4424 $positions->{$a->[$_]} |= 1 << ($_ % $width) for 0..$#$a;
272             }
273             else {
274 28         2161 $positions->{$a->[$_]}->[$_ / $width] |= 1 << ($_ % $width) for 0..$#$a;
275             }
276 805         1801 return $positions;
277             }
278              
279             sub _count_bits {
280 1732     1732   7738 my $v = shift;
281              
282             #use integer;
283             #no warnings 'portable'; # for 0xffffffffffffffff
284              
285 1732 100       3045 if ($width == 64) {
286 1727         2724 $v = $v - (($v >> 1) & 0x5555555555555555);
287 1727         2498 $v = ($v & 0x3333333333333333) + (($v >> 2) & 0x3333333333333333);
288             # (bytesof($v) -1) * bitsofbyte = (8-1)*8 = 56 ----------------------vv
289 1727         2569 $v = (($v + ($v >> 4) & 0x0f0f0f0f0f0f0f0f) * 0x0101010101010101) >> 56;
290 1727         8012 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         8 my $c; # count
299 5         14 for ($c = 0; $v; $c++) {
300 64         94 $v &= $v - 1; # clear the least significant bit set
301             }
302 5         22 return $c;
303             }
304             }
305              
306             1;
307              
308             __END__