File Coverage

blib/lib/LCS.pm
Criterion Covered Total %
statement 169 169 100.0
branch 39 40 97.5
condition 17 18 100.0
subroutine 20 20 100.0
pod 13 13 100.0
total 258 260 99.6


line stmt bran cond sub pod time code
1             package LCS;
2              
3 2     2   16462 use strict;
  2         4  
  2         73  
4 2     2   7 use warnings;
  2         4  
  2         51  
5              
6 2     2   60 use 5.006;
  2         6  
  2         84  
7             our $VERSION = '0.10';
8              
9 2     2   605 use Data::Dumper;
  2         7456  
  2         2802  
10              
11             sub new {
12 6     6 1 669 my $class = shift;
13             # uncoverable condition false
14 6 100 66     34 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  2 100       11  
15             }
16              
17             sub align {
18 24     24 1 140963 my ($self, $X, $Y) = @_;
19              
20 24         81 return $self->lcs2align(
21             $X, $Y, $self->LCS($X,$Y)
22             );
23             }
24              
25             sub lcs2align {
26 126     126 1 254 my ($self, $X, $Y, $LCS) = @_;
27              
28 126         134 my $hunks = [];
29              
30 126         113 my $Xcurrent = -1;
31 126         107 my $Ycurrent = -1;
32 126         192 my $Xtemp;
33             my $Ytemp;
34              
35 126         174 for my $hunk (@$LCS) {
36 506   100     1359 while ( ($Xcurrent+1 < $hunk->[0] || $Ycurrent+1 < $hunk->[1]) ) {
37 250         270 $Xtemp = '';
38 250         171 $Ytemp = '';
39 250 100       408 if ($Xcurrent+1 < $hunk->[0]) {
40 186         122 $Xcurrent++;
41 186         185 $Xtemp = $X->[$Xcurrent];
42             }
43 250 100       362 if ($Ycurrent+1 < $hunk->[1]) {
44 118         92 $Ycurrent++;
45 118         116 $Ytemp = $Y->[$Ycurrent];
46             }
47 250         864 push @$hunks,[$Xtemp,$Ytemp];
48             }
49              
50 506         367 $Xcurrent = $hunk->[0];
51 506         352 $Ycurrent = $hunk->[1];
52 506         824 push @$hunks,[$X->[$Xcurrent],$Y->[$Ycurrent]]; # elements
53             }
54 126   100     412 while ( ($Xcurrent+1 <= $#$X || $Ycurrent+1 <= $#$Y) ) {
55 136         120 $Xtemp = '';
56 136         101 $Ytemp = '';
57 136 100       200 if ($Xcurrent+1 <= $#$X) {
58 102         93 $Xcurrent++;
59 102         86 $Xtemp = $X->[$Xcurrent];
60             }
61 136 100       223 if ($Ycurrent+1 <= $#$Y) {
62 76         78 $Ycurrent++;
63 76         75 $Ytemp = $Y->[$Ycurrent];
64             }
65 136         464 push @$hunks,[$Xtemp,$Ytemp];
66             }
67 126         387 return $hunks;
68             }
69              
70             sub sequences2hunks {
71 72     72 1 31862 my ($self, $a, $b) = @_;
72 72         135 return [ map { [ $a->[$_], $b->[$_] ] } 0..$#$a ];
  519         858  
73             }
74              
75             sub clcs2lcs {
76 6     6 1 3726 my ($self, $clcs) = @_;
77 6         10 my $lcs = [];
78 6         13 for my $entry (@$clcs) {
79 8         19 for (my $i = 0; $i < $entry->[2];$i++) {
80 15         49 push @$lcs,[$entry->[0]+$i,$entry->[1]+$i];
81             }
82             }
83 6         20 return $lcs;
84             }
85              
86             sub lcs2clcs {
87 6     6 1 11 my ($self, $lcs) = @_;
88 6         13 my $clcs = [];
89 6         12 for my $entry (@$lcs) {
90 15 100 100     59 if (@$clcs && $clcs->[-1]->[0] + $clcs->[-1]->[2] == $entry->[0]) {
91 7         25 $clcs->[-1]->[2]++;
92             }
93             else {
94 8         19 push @$clcs,[$entry->[0],$entry->[1],1];
95             }
96             }
97 6         17 return $clcs;
98             }
99              
100             sub hunks2sequences {
101 24     24 1 26 my ($self, $hunks) = @_;
102              
103 24         26 my $a = [];
104 24         27 my $b = [];
105              
106 24         34 for my $hunk (@$hunks) {
107 173         179 push @$a, $hunk->[0];
108 173         200 push @$b, $hunk->[1];
109             }
110 24         108 return ($a,$b);
111             }
112              
113             sub align2strings {
114 48     48 1 42 my ($self, $hunks,$gap) = @_;
115             #$gap //= '_';
116 48 100       79 $gap = (defined $gap) ? $gap : '_';
117              
118 48         47 my $a = '';
119 48         45 my $b = '';
120              
121 48         56 for my $hunk (@$hunks) {
122 346         468 my ($ae,$be) = $self->fill_strings($hunk->[0],$hunk->[1],$gap);
123 346         288 $a .= $ae;
124 346         271 $b .= $be;
125             }
126 48         216 return ($a,$b);
127             }
128              
129             sub fill_strings {
130 346     346 1 316 my ($self, $string1,$string2, $gap) = @_;
131             #$gap //= '_';
132 346 50       612 $gap = (defined $gap) ? $gap : '_';
133              
134 1     1   13 my @m = $string1 =~ m/(\X)/g;
  1         3  
  1         21  
  346         852  
135 346         18221 my @n = $string2 =~ m/(\X)/g;
136 346         431 my $max = max(scalar(@m),scalar(@n));
137 346 100       555 if ($max - scalar(@m) > 0) {
138 28         34 for (1..$max-scalar(@m)) {
139 28         45 $string1 .= $gap;
140             }
141             }
142 346 100       445 if ($max - scalar(@n) > 0) {
143 68         92 for (1..$max-scalar(@n)) {
144 68         93 $string2 .= $gap;
145             }
146             }
147 346         538 return ($string1,$string2);
148             }
149              
150             sub LLCS {
151 24     24 1 49303 my ($self,$X,$Y) = @_;
152              
153 24         36 my $m = scalar @$X;
154 24         23 my $n = scalar @$Y;
155              
156 24         30 my $c = [];
157              
158 24         49 for my $i (0..1) {
159 48         49 for my $j (0..$n) {
160 326         348 $c->[$i][$j]=0;
161             }
162             }
163              
164 24         21 my ($i,$j);
165              
166 24         61 for ($i=1; $i <= $m; $i++) {
167 159         216 for ($j=1; $j <= $n; $j++) {
168 2984 100       3535 if ($X->[$i-1] eq $Y->[$j-1]) {
169 146         271 $c->[1][$j] = $c->[0][$j-1]+1;
170             }
171             else {
172 2838         3466 $c->[1][$j] = max($c->[1][$j-1],$c->[0][$j]);
173             }
174             }
175 159         218 for ($j = 1; $j <= $n; $j++) {
176 2984         4692 $c->[0][$j] = $c->[1][$j];
177             }
178             }
179 24         70 return ($c->[1][$n]);
180             }
181              
182              
183             sub LCS {
184 96     96 1 280464 my ($self,$X,$Y) = @_;
185              
186 96         167 my $m = scalar @$X;
187 96         123 my $n = scalar @$Y;
188              
189 96         160 my $c = [];
190 96         113 my ($i,$j);
191 96         319 for ($i=0;$i<=$m;$i++) {
192 732         1218 for ($j=0;$j<=$n;$j++) {
193 13224         21520 $c->[$i][$j]=0;
194             }
195             }
196 96         328 for ($i=1;$i<=$m;$i++) {
197 636         991 for ($j=1;$j<=$n;$j++) {
198 11936 100       15358 if ($X->[$i-1] eq $Y->[$j-1]) {
199 584         1291 $c->[$i][$j] = $c->[$i-1][$j-1]+1;
200             }
201             else {
202 11352         16226 $c->[$i][$j] = max($c->[$i][$j-1], $c->[$i-1][$j]);
203             }
204             }
205             }
206 96         299 my $path = $self->_lcs($X,$Y,$c,$m,$n,[]);
207              
208 96         787 return $path;
209             }
210              
211              
212             sub max {
213 14536 100   14536 1 32494 ($_[0] > $_[1]) ? $_[0] : $_[1];
214             }
215              
216              
217             sub _lcs {
218 96     96   159 my ($self,$X,$Y,$c,$i,$j,$L) = @_;
219              
220 96   100     483 while ($i > 0 && $j > 0) {
221 692 100       1323 if ($X->[$i-1] eq $Y->[$j-1]) {
    100          
222 412         348 unshift @{$L},[$i-1,$j-1];
  412         872  
223 412         355 $i--;
224 412         1126 $j--;
225             }
226             elsif ($c->[$i][$j] == $c->[$i-1][$j]) {
227 180         490 $i--;
228             }
229             else {
230 100         295 $j--;
231             }
232             }
233 96         158 return $L;
234             }
235              
236              
237             sub _all_lcs {
238 96     96   138 my ($self,$ranks,$rank,$max) = @_;
239              
240 96         155 my $R = [[]];
241              
242 96         220 while ($rank <= $max) {
243 412         285 my @temp;
244 412         432 for my $path (@$R) {
245 612         382 for my $hunk (@{$ranks->{$rank}}) {
  612         804  
246 1020 100 100     662 if (scalar @{$path} == 0) {
  1020 100       3462  
247 152         374 push @temp,[$hunk];
248             }
249             elsif (($path->[-1][0] < $hunk->[0]) && ($path->[-1][1] < $hunk->[1])) {
250 520         1234 push @temp,[@$path,$hunk];
251             }
252             }
253             }
254 412         877 @$R = @temp;
255 412         725 $rank++;
256             }
257 96         1627 return $R;
258             }
259              
260             # get all LCS of two arrays
261             # records the matches by rank
262             sub allLCS {
263 96     96 1 453 my ($self,$X,$Y) = @_;
264              
265 96         149 my $m = scalar @$X;
266 96         110 my $n = scalar @$Y;
267              
268 96         142 my $ranks = {}; # e.g. '4' => [[3,6],[4,5]]
269 96         131 my $c = [];
270 96         96 my ($i,$j);
271              
272 96         236 for (0..$m) {$c->[$_][0]=0;}
  732         1066  
273 96         181 for (0..$n) {$c->[0][$_]=0;}
  652         744  
274 96         278 for ($i=1;$i<=$m;$i++) {
275 636         1040 for ($j=1;$j<=$n;$j++) {
276 11936 100       14737 if ($X->[$i-1] eq $Y->[$j-1]) {
277 584         839 $c->[$i][$j] = $c->[$i-1][$j-1]+1;
278 584         489 push @{$ranks->{$c->[$i][$j]}},[$i-1,$j-1];
  584         2291  
279             }
280             else {
281 11352 100       31194 $c->[$i][$j] =
282             ($c->[$i][$j-1] > $c->[$i-1][$j])
283             ? $c->[$i][$j-1]
284             : $c->[$i-1][$j];
285             }
286             }
287             }
288 96         225 my $max = scalar keys %$ranks;
289 96         289 return $self->_all_lcs($ranks,1,$max);
290             }
291              
292             1;
293             __END__