File Coverage

blib/lib/LCS.pm
Criterion Covered Total %
statement 165 165 100.0
branch 39 40 97.5
condition 17 18 100.0
subroutine 19 19 100.0
pod 13 13 100.0
total 253 255 99.6


line stmt bran cond sub pod time code
1             package LCS;
2              
3 2     2   16527 use strict;
  2         2  
  2         60  
4 2     2   8 use warnings;
  2         2  
  2         46  
5              
6 2     2   37 use 5.006;
  2         5  
7             our $VERSION = '0.11';
8              
9 2     2   670 use Data::Dumper;
  2         7803  
  2         3065  
10              
11             sub new {
12 6     6 1 1256 my $class = shift;
13             # uncoverable condition false
14 6 100 66     38 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  2 100       13  
15             }
16              
17             sub align {
18 24     24 1 145354 my ($self, $X, $Y) = @_;
19              
20 24         52 return $self->lcs2align(
21             $X, $Y, $self->LCS($X,$Y)
22             );
23             }
24              
25             sub lcs2align {
26 126     126 1 235 my ($self, $X, $Y, $LCS) = @_;
27              
28 126         135 my $hunks = [];
29              
30 126         115 my $Xcurrent = -1;
31 126         103 my $Ycurrent = -1;
32 126         109 my $Xtemp;
33             my $Ytemp;
34              
35 126         159 for my $hunk (@$LCS) {
36 506   100     1503 while ( ($Xcurrent+1 < $hunk->[0] || $Ycurrent+1 < $hunk->[1]) ) {
37 250         210 $Xtemp = '';
38 250         196 $Ytemp = '';
39 250 100       385 if ($Xcurrent+1 < $hunk->[0]) {
40 186         199 $Xcurrent++;
41 186         188 $Xtemp = $X->[$Xcurrent];
42             }
43 250 100       383 if ($Ycurrent+1 < $hunk->[1]) {
44 118         81 $Ycurrent++;
45 118         102 $Ytemp = $Y->[$Ycurrent];
46             }
47 250         940 push @$hunks,[$Xtemp,$Ytemp];
48             }
49              
50 506         385 $Xcurrent = $hunk->[0];
51 506         342 $Ycurrent = $hunk->[1];
52 506         950 push @$hunks,[$X->[$Xcurrent],$Y->[$Ycurrent]]; # elements
53             }
54 126   100     404 while ( ($Xcurrent+1 <= $#$X || $Ycurrent+1 <= $#$Y) ) {
55 136         122 $Xtemp = '';
56 136         114 $Ytemp = '';
57 136 100       208 if ($Xcurrent+1 <= $#$X) {
58 102         113 $Xcurrent++;
59 102         90 $Xtemp = $X->[$Xcurrent];
60             }
61 136 100       210 if ($Ycurrent+1 <= $#$Y) {
62 76         52 $Ycurrent++;
63 76         71 $Ytemp = $Y->[$Ycurrent];
64             }
65 136         516 push @$hunks,[$Xtemp,$Ytemp];
66             }
67 126         356 return $hunks;
68             }
69              
70             sub sequences2hunks {
71 72     72 1 31664 my ($self, $a, $b) = @_;
72 72         177 return [ map { [ $a->[$_], $b->[$_] ] } 0..$#$a ];
  519         815  
73             }
74              
75             sub clcs2lcs {
76 6     6 1 3730 my ($self, $clcs) = @_;
77 6         11 my $lcs = [];
78 6         8 for my $entry (@$clcs) {
79 8         21 for (my $i = 0; $i < $entry->[2];$i++) {
80 15         42 push @$lcs,[$entry->[0]+$i,$entry->[1]+$i];
81             }
82             }
83 6         15 return $lcs;
84             }
85              
86             sub lcs2clcs {
87 6     6 1 11 my ($self, $lcs) = @_;
88 6         10 my $clcs = [];
89 6         41 for my $entry (@$lcs) {
90 15 100 100     57 if (@$clcs && $clcs->[-1]->[0] + $clcs->[-1]->[2] == $entry->[0]) {
91 7         12 $clcs->[-1]->[2]++;
92             }
93             else {
94 8         22 push @$clcs,[$entry->[0],$entry->[1],1];
95             }
96             }
97 6         16 return $clcs;
98             }
99              
100             sub hunks2sequences {
101 24     24 1 25 my ($self, $hunks) = @_;
102              
103 24         25 my $a = [];
104 24         21 my $b = [];
105              
106 24         33 for my $hunk (@$hunks) {
107 173         179 push @$a, $hunk->[0];
108 173         181 push @$b, $hunk->[1];
109             }
110 24         111 return ($a,$b);
111             }
112              
113             sub align2strings {
114 48     48 1 59 my ($self, $hunks,$gap) = @_;
115             #$gap //= '_';
116 48 100       83 $gap = (defined $gap) ? $gap : '_';
117              
118 48         40 my $a = '';
119 48         47 my $b = '';
120              
121 48         62 for my $hunk (@$hunks) {
122 346         461 my ($ae,$be) = $self->fill_strings($hunk->[0],$hunk->[1],$gap);
123 346         306 $a .= $ae;
124 346         324 $b .= $be;
125             }
126 48         225 return ($a,$b);
127             }
128              
129             sub fill_strings {
130 346     346 1 367 my ($self, $string1,$string2, $gap) = @_;
131             #$gap //= '_';
132 346 50       383 $gap = (defined $gap) ? $gap : '_';
133              
134 346         810 my @m = $string1 =~ m/(\X)/g;
135 346         577 my @n = $string2 =~ m/(\X)/g;
136 346         428 my $max = max(scalar(@m),scalar(@n));
137 346 100       597 if ($max - scalar(@m) > 0) {
138 28         40 for (1..$max-scalar(@m)) {
139 28         41 $string1 .= $gap;
140             }
141             }
142 346 100       471 if ($max - scalar(@n) > 0) {
143 68         115 for (1..$max-scalar(@n)) {
144 68         93 $string2 .= $gap;
145             }
146             }
147 346         555 return ($string1,$string2);
148             }
149              
150             sub LLCS {
151 24     24 1 41014 my ($self,$X,$Y) = @_;
152              
153 24         30 my $m = scalar @$X;
154 24         23 my $n = scalar @$Y;
155              
156 24         31 my $c = [];
157              
158 24         57 for my $i (0..1) {
159 48         57 for my $j (0..$n) {
160 326         337 $c->[$i][$j]=0;
161             }
162             }
163              
164 24         20 my ($i,$j);
165              
166 24         57 for ($i=1; $i <= $m; $i++) {
167 159         238 for ($j=1; $j <= $n; $j++) {
168 2984 100       3638 if ($X->[$i-1] eq $Y->[$j-1]) {
169 146         262 $c->[1][$j] = $c->[0][$j-1]+1;
170             }
171             else {
172 2838         3218 $c->[1][$j] = max($c->[1][$j-1],$c->[0][$j]);
173             }
174             }
175 159         240 for ($j = 1; $j <= $n; $j++) {
176 2984         4470 $c->[0][$j] = $c->[1][$j];
177             }
178             }
179 24         62 return ($c->[1][$n]);
180             }
181              
182              
183             sub LCS {
184 96     96 1 243441 my ($self,$X,$Y) = @_;
185              
186 96         152 my $m = scalar @$X;
187 96         90 my $n = scalar @$Y;
188              
189 96         118 my $c = [];
190 96         102 my ($i,$j);
191 96         235 for ($i=0;$i<=$m;$i++) {
192 732         1035 for ($j=0;$j<=$n;$j++) {
193 13224         19232 $c->[$i][$j]=0;
194             }
195             }
196 96         169 for ($i=1;$i<=$m;$i++) {
197 636         863 for ($j=1;$j<=$n;$j++) {
198 11936 100       14481 if ($X->[$i-1] eq $Y->[$j-1]) {
199 584         1120 $c->[$i][$j] = $c->[$i-1][$j-1]+1;
200             }
201             else {
202 11352         13792 $c->[$i][$j] = max($c->[$i][$j-1], $c->[$i-1][$j]);
203             }
204             }
205             }
206 96         184 my $path = $self->_lcs($X,$Y,$c,$m,$n,[]);
207              
208 96         510 return $path;
209             }
210              
211              
212             sub max {
213 14536 100   14536 1 29948 ($_[0] > $_[1]) ? $_[0] : $_[1];
214             }
215              
216              
217             sub _lcs {
218 96     96   156 my ($self,$X,$Y,$c,$i,$j,$L) = @_;
219              
220 96   100     403 while ($i > 0 && $j > 0) {
221 692 100       1129 if ($X->[$i-1] eq $Y->[$j-1]) {
    100          
222 412         309 unshift @{$L},[$i-1,$j-1];
  412         695  
223 412         353 $i--;
224 412         1187 $j--;
225             }
226             elsif ($c->[$i][$j] == $c->[$i-1][$j]) {
227 180         509 $i--;
228             }
229             else {
230 100         280 $j--;
231             }
232             }
233 96         157 return $L;
234             }
235              
236              
237             sub _all_lcs {
238 96     96   110 my ($self,$ranks,$rank,$max) = @_;
239              
240 96         129 my $R = [[]];
241              
242 96         155 while ($rank <= $max) {
243 412         312 my @temp;
244 412         402 for my $path (@$R) {
245 612         416 for my $hunk (@{$ranks->{$rank}}) {
  612         761  
246 1020 100 100     665 if (scalar @{$path} == 0) {
  1020 100       3370  
247 152         247 push @temp,[$hunk];
248             }
249             elsif (($path->[-1][0] < $hunk->[0]) && ($path->[-1][1] < $hunk->[1])) {
250 520         1045 push @temp,[@$path,$hunk];
251             }
252             }
253             }
254 412         666 @$R = @temp;
255 412         697 $rank++;
256             }
257 96         1019 return $R;
258             }
259              
260             # get all LCS of two arrays
261             # records the matches by rank
262             sub allLCS {
263 96     96 1 292 my ($self,$X,$Y) = @_;
264              
265 96         102 my $m = scalar @$X;
266 96         84 my $n = scalar @$Y;
267              
268 96         129 my $ranks = {}; # e.g. '4' => [[3,6],[4,5]]
269 96         90 my $c = [];
270 96         87 my ($i,$j);
271              
272 96         205 for (0..$m) {$c->[$_][0]=0;}
  732         834  
273 96         113 for (0..$n) {$c->[0][$_]=0;}
  652         604  
274 96         194 for ($i=1;$i<=$m;$i++) {
275 636         959 for ($j=1;$j<=$n;$j++) {
276 11936 100       14223 if ($X->[$i-1] eq $Y->[$j-1]) {
277 584         678 $c->[$i][$j] = $c->[$i-1][$j-1]+1;
278 584         405 push @{$ranks->{$c->[$i][$j]}},[$i-1,$j-1];
  584         2036  
279             }
280             else {
281 11352 100       28621 $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         167 my $max = scalar keys %$ranks;
289 96         195 return $self->_all_lcs($ranks,1,$max);
290             }
291              
292             1;
293             __END__