line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::NeedlemanWunsch; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
32962
|
use warnings; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
172
|
|
4
|
6
|
|
|
6
|
|
30
|
use strict; |
|
6
|
|
|
|
|
7
|
|
|
6
|
|
|
|
|
179
|
|
5
|
|
|
|
|
|
|
|
6
|
6
|
|
|
6
|
|
31
|
use List::Util qw(max); |
|
6
|
|
|
|
|
19
|
|
|
6
|
|
|
|
|
561
|
|
7
|
6
|
|
|
6
|
|
32
|
use Carp; |
|
6
|
|
|
|
|
7
|
|
|
6
|
|
|
|
|
21191
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $from_diag = 1; |
12
|
|
|
|
|
|
|
my $from_up = 2; |
13
|
|
|
|
|
|
|
my $from_left = 4; |
14
|
|
|
|
|
|
|
my $from_diag_idx = 0; |
15
|
|
|
|
|
|
|
my $from_up_idx = 1; |
16
|
|
|
|
|
|
|
my $from_left_idx = 2; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub _curry_callback { |
19
|
6
|
|
|
6
|
|
12
|
my ($univ_cb, $spec_name) = @_; |
20
|
|
|
|
|
|
|
|
21
|
6
|
|
|
|
|
6
|
my $cb; |
22
|
6
|
100
|
|
|
|
40
|
if ($spec_name eq 'align') { |
23
|
|
|
|
|
|
|
$cb = sub { |
24
|
9
|
|
|
9
|
|
25
|
my $arg = { align => [ @_ ] }; |
25
|
9
|
|
|
|
|
24
|
my $rv = &$univ_cb($arg); |
26
|
9
|
50
|
|
|
|
102
|
croak "select_align callback returned invalid selection $rv." |
27
|
|
|
|
|
|
|
unless $rv eq 'align'; |
28
|
2
|
|
|
|
|
10
|
}; |
29
|
|
|
|
|
|
|
} else { |
30
|
|
|
|
|
|
|
$cb = sub { |
31
|
4
|
|
|
4
|
|
8
|
my $arg = { $spec_name => $_[0] }; |
32
|
4
|
|
|
|
|
9
|
my $rv = &$univ_cb($arg); |
33
|
4
|
50
|
|
|
|
43
|
croak "select_align callback returned invalid selection $rv." |
34
|
|
|
|
|
|
|
unless $rv eq $spec_name; |
35
|
4
|
|
|
|
|
16
|
}; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
6
|
|
|
|
|
24
|
return $cb; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub _canonicalize_callbacks { |
42
|
33
|
|
|
33
|
|
40
|
my $cb; |
43
|
33
|
100
|
|
|
|
78
|
if (@_) { |
44
|
26
|
|
|
|
|
40
|
$cb = $_[0]; |
45
|
|
|
|
|
|
|
} else { |
46
|
7
|
|
|
|
|
13
|
$cb = { }; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
33
|
100
|
|
|
|
105
|
if (exists($cb->{select_align})) { |
50
|
4
|
|
|
|
|
11
|
my @cn = qw(align shift_a shift_b); |
51
|
4
|
|
|
|
|
11
|
foreach (@cn) { |
52
|
12
|
100
|
|
|
|
40
|
if (!exists($cb->{$_})) { |
53
|
6
|
|
|
|
|
17
|
$cb->{$_} = _curry_callback($cb->{select_align}, $_); |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
33
|
|
|
|
|
99
|
return $cb; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub new { |
62
|
12
|
|
|
12
|
1
|
11833
|
my $class = shift; |
63
|
12
|
|
|
|
|
22
|
my $score_sub = shift; |
64
|
|
|
|
|
|
|
|
65
|
12
|
|
|
|
|
46
|
my $self = { score_sub => $score_sub, local => 0 }; |
66
|
12
|
100
|
|
|
|
44
|
if (@_) { |
67
|
6
|
|
|
|
|
17
|
$self->{gap_penalty} = $_[0]; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
12
|
|
|
|
|
45
|
return bless $self, $class; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub local { |
74
|
12
|
|
|
12
|
1
|
18854
|
my $self = shift; |
75
|
|
|
|
|
|
|
|
76
|
12
|
50
|
|
|
|
36
|
if (@_) { |
77
|
12
|
|
|
|
|
28
|
$self->{local} = $_[0]; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
12
|
|
|
|
|
27
|
return $self->{local}; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub gap_open_penalty { |
84
|
3
|
|
|
3
|
1
|
19
|
my $self = shift; |
85
|
|
|
|
|
|
|
|
86
|
3
|
50
|
|
|
|
13
|
if (@_) { |
87
|
3
|
|
|
|
|
13
|
$self->{gap_open_penalty} = $_[0]; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
3
|
|
|
|
|
10
|
return $self->{gap_open_penalty}; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub gap_extend_penalty { |
94
|
3
|
|
|
3
|
1
|
13
|
my $self = shift; |
95
|
|
|
|
|
|
|
|
96
|
3
|
50
|
|
|
|
18
|
if (@_) { |
97
|
3
|
|
|
|
|
8
|
$self->{gap_extend_penalty} = $_[0]; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
3
|
|
|
|
|
8
|
return $self->{gap_extend_penalty}; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub align { |
104
|
33
|
|
|
33
|
1
|
10961
|
my $self = shift; |
105
|
|
|
|
|
|
|
|
106
|
33
|
|
|
|
|
50
|
my $a = shift; |
107
|
33
|
|
|
|
|
43
|
my $b = shift; |
108
|
|
|
|
|
|
|
|
109
|
33
|
|
|
|
|
83
|
$self->{callbacks} = _canonicalize_callbacks(@_); |
110
|
|
|
|
|
|
|
|
111
|
33
|
100
|
|
|
|
114
|
if (!exists($self->{gap_open_penalty})) { |
112
|
27
|
50
|
|
|
|
70
|
if (exists($self->{gap_extend_penalty})) { |
113
|
0
|
|
|
|
|
0
|
croak "gap_open_penalty must be defined together with gap_extend_penalty"; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
27
|
100
|
|
|
|
76
|
if (!exists($self->{gap_penalty})) { |
117
|
3
|
|
|
|
|
3
|
$self->{gap_penalty} = &{$self->{score_sub}}(); |
|
3
|
|
|
|
|
11
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
27
|
|
|
|
|
73
|
return $self->_align_basic($a, $b); |
121
|
|
|
|
|
|
|
} else { |
122
|
6
|
50
|
|
|
|
27
|
if (!exists($self->{gap_extend_penalty})) { |
123
|
0
|
|
|
|
|
0
|
croak "gap_extend_penalty must be defined together with gap_open_penalty"; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
6
|
50
|
|
|
|
26
|
if ($self->{gap_open_penalty} >= $self->{gap_extend_penalty}) { |
127
|
0
|
|
|
|
|
0
|
croak "gap_open_penalty must be smaller than gap_extend_penalty"; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
6
|
|
|
|
|
29
|
return $self->_align_affine($a, $b); |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _align_basic { |
135
|
27
|
|
|
27
|
|
40
|
my $self = shift; |
136
|
27
|
|
|
|
|
32
|
my $a = shift; |
137
|
27
|
|
|
|
|
31
|
my $b = shift; |
138
|
|
|
|
|
|
|
|
139
|
27
|
|
|
|
|
63
|
my $A = [ [ 0 ] ]; |
140
|
27
|
|
|
|
|
58
|
my $D = [ [ 0 ] ]; |
141
|
27
|
|
|
|
|
40
|
my $m = scalar(@$b); |
142
|
27
|
|
|
|
|
35
|
my $n = scalar(@$a); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
my $score_diag = sub { |
145
|
2065
|
|
|
2065
|
|
2226
|
my ($i, $j) = @_; |
146
|
|
|
|
|
|
|
|
147
|
2065
|
|
|
|
|
5810
|
$D->[$i - 1]->[$j - 1] + |
148
|
2065
|
|
|
|
|
3544
|
&{$self->{score_sub}}($a->[$j - 1], $b->[$i - 1]); |
149
|
27
|
|
|
|
|
139
|
}; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
my $score_up = sub { |
152
|
2065
|
|
|
2065
|
|
2386
|
my ($i, $j) = @_; |
153
|
|
|
|
|
|
|
|
154
|
2065
|
|
|
|
|
4617
|
$D->[$i - 1]->[$j] + $self->{gap_penalty}; |
155
|
27
|
|
|
|
|
101
|
}; |
156
|
|
|
|
|
|
|
|
157
|
27
|
|
|
|
|
35
|
my $score_left; |
158
|
27
|
100
|
|
|
|
68
|
if (!$self->{local}) { |
159
|
|
|
|
|
|
|
$score_left = sub { |
160
|
940
|
|
|
940
|
|
1002
|
my ($i, $j) = @_; |
161
|
|
|
|
|
|
|
|
162
|
940
|
|
|
|
|
2543
|
$D->[$i]->[$j - 1] + $self->{gap_penalty}; |
163
|
20
|
|
|
|
|
73
|
}; |
164
|
|
|
|
|
|
|
} else { |
165
|
|
|
|
|
|
|
$score_left = sub { |
166
|
1125
|
|
|
1125
|
|
1226
|
my ($i, $j) = @_; |
167
|
|
|
|
|
|
|
|
168
|
1125
|
100
|
|
|
|
4046
|
($i < $m) ? |
169
|
|
|
|
|
|
|
$D->[$i]->[$j - 1] + $self->{gap_penalty} : |
170
|
|
|
|
|
|
|
$D->[$i]->[$j - 1]; |
171
|
7
|
|
|
|
|
34
|
}; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# order must correspond to $from_* constants |
175
|
27
|
|
|
|
|
55
|
my @subproblems = ( $score_diag, $score_up, $score_left ); |
176
|
|
|
|
|
|
|
|
177
|
27
|
|
|
|
|
31
|
my $j = 1; |
178
|
27
|
|
|
|
|
64
|
while ($j <= $n) { |
179
|
193
|
|
|
|
|
280
|
$A->[0]->[$j] = $from_left; |
180
|
193
|
|
|
|
|
362
|
++$j; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
27
|
100
|
|
|
|
65
|
if (!$self->{local}) { |
184
|
20
|
|
|
|
|
40
|
$j = 1; |
185
|
20
|
|
|
|
|
70
|
while ($j <= $n) { |
186
|
110
|
|
|
|
|
176
|
$D->[0]->[$j] = $j * $self->{gap_penalty}; |
187
|
110
|
|
|
|
|
191
|
++$j; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
} else { |
190
|
7
|
|
|
|
|
9
|
$j = 1; |
191
|
7
|
|
|
|
|
18
|
while ($j <= $n) { |
192
|
83
|
|
|
|
|
104
|
$D->[0]->[$j] = 0; |
193
|
83
|
|
|
|
|
132
|
++$j; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
27
|
|
|
|
|
30
|
my $i = 1; |
198
|
27
|
|
|
|
|
70
|
while ($i <= $m) { |
199
|
186
|
|
|
|
|
288
|
$A->[$i]->[0] = $from_up; |
200
|
186
|
|
|
|
|
323
|
++$i; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
27
|
|
|
|
|
33
|
$i = 1; |
204
|
27
|
|
|
|
|
57
|
while ($i <= $m) { |
205
|
186
|
|
|
|
|
354
|
$D->[$i]->[0] = $i * $self->{gap_penalty}; |
206
|
186
|
|
|
|
|
365
|
++$i; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
27
|
|
|
|
|
32
|
$i = 1; |
210
|
27
|
|
|
|
|
50
|
while ($i <= $m) { |
211
|
186
|
|
|
|
|
208
|
$j = 1; |
212
|
186
|
|
|
|
|
1315
|
while ($j <= $n) { |
213
|
2065
|
|
|
|
|
2540
|
my @scores = map { &$_($i, $j); } @subproblems; |
|
6195
|
|
|
|
|
18526
|
|
214
|
2065
|
|
|
|
|
3774
|
my $d = max(@scores); |
215
|
|
|
|
|
|
|
|
216
|
2065
|
|
|
|
|
1960
|
my $a = 0; |
217
|
2065
|
|
|
|
|
1906
|
my $from = 1; |
218
|
2065
|
|
|
|
|
1953
|
my $k = 0; |
219
|
2065
|
|
|
|
|
3978
|
while ($k < scalar(@scores)) { |
220
|
6195
|
100
|
|
|
|
10695
|
if ($scores[$k] == $d) { |
221
|
2671
|
|
|
|
|
2886
|
$a |= $from; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
6195
|
|
|
|
|
5758
|
$from *= 2; |
225
|
6195
|
|
|
|
|
11195
|
++$k; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
2065
|
|
|
|
|
3078
|
$A->[$i]->[$j] = $a; |
229
|
2065
|
|
|
|
|
2891
|
$D->[$i]->[$j] = $d; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# my $x = join ', ', @scores; |
232
|
|
|
|
|
|
|
# warn "$i, $j: $x -> ", $D->[$i]->[$j], "\n"; |
233
|
2065
|
|
|
|
|
4908
|
++$j; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
186
|
|
|
|
|
357
|
++$i; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
27
|
|
|
|
|
35
|
$i = $m; |
240
|
27
|
|
|
|
|
30
|
$j = $n; |
241
|
27
|
|
100
|
|
|
82
|
while (($i > 0) || ($j > 0)) { |
242
|
230
|
|
|
|
|
298
|
my $a = $A->[$i]->[$j]; |
243
|
230
|
|
|
|
|
233
|
my @alt; |
244
|
230
|
100
|
|
|
|
400
|
if ($a & $from_diag) { |
245
|
151
|
50
|
33
|
|
|
563
|
die "internal error" unless ($i > 0) && ($j > 0); |
246
|
151
|
|
|
|
|
318
|
push @alt, [ $i - 1, $j - 1 ]; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
230
|
100
|
|
|
|
428
|
if ($a & $from_up) { |
250
|
60
|
50
|
|
|
|
106
|
die "internal error" unless ($i > 0); |
251
|
60
|
|
|
|
|
109
|
push @alt, [ $i - 1, $j ]; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
230
|
100
|
|
|
|
379
|
if ($a & $from_left) { |
255
|
59
|
50
|
|
|
|
101
|
die "internal error" unless ($j > 0); |
256
|
59
|
|
|
|
|
125
|
push @alt, [ $i, $j - 1]; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
230
|
50
|
|
|
|
425
|
if (!@alt) { |
260
|
0
|
|
|
|
|
0
|
die "internal error"; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
230
|
|
|
|
|
361
|
my $cur = [ $i, $j ]; |
264
|
230
|
|
|
|
|
237
|
my $move; |
265
|
230
|
100
|
|
|
|
353
|
if (@alt == 1) { |
266
|
197
|
|
|
|
|
460
|
$move = $self->_simple_trace_back($cur, $alt[0], |
267
|
|
|
|
|
|
|
$self->{callbacks}); |
268
|
|
|
|
|
|
|
} else { |
269
|
33
|
|
|
|
|
85
|
$move = $self->_trace_back($cur, \@alt); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
230
|
100
|
|
|
|
523
|
if ($move eq 'align') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
273
|
149
|
|
|
|
|
147
|
--$i; |
274
|
149
|
|
|
|
|
555
|
--$j; |
275
|
|
|
|
|
|
|
} elsif ($move eq 'shift_a') { |
276
|
44
|
|
|
|
|
180
|
--$j; |
277
|
|
|
|
|
|
|
} elsif ($move eq 'shift_b') { |
278
|
37
|
|
|
|
|
142
|
--$i; |
279
|
|
|
|
|
|
|
} else { |
280
|
0
|
|
|
|
|
0
|
die "internal error"; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
27
|
|
|
|
|
409
|
return $D->[$m]->[$n]; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _align_affine { |
288
|
6
|
|
|
6
|
|
10
|
my $self = shift; |
289
|
6
|
|
|
|
|
14
|
my $a = shift; |
290
|
6
|
|
|
|
|
10
|
my $b = shift; |
291
|
|
|
|
|
|
|
|
292
|
6
|
|
|
|
|
37
|
my @D = ([ [ 0 ] ], [ [ 0 ] ], [ [ 0 ] ]); # indexed by $from_*_idx |
293
|
6
|
|
|
|
|
13
|
my $m = scalar(@$b); |
294
|
6
|
|
|
|
|
13
|
my $n = scalar(@$a); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
my $score_diag = sub { |
297
|
1713
|
|
|
1713
|
|
2040
|
my ($i, $j) = @_; |
298
|
|
|
|
|
|
|
|
299
|
1713
|
|
|
|
|
2154
|
my @base = map { $_->[$i - 1]->[$j - 1]; } @D; |
|
5139
|
|
|
|
|
10041
|
|
300
|
1713
|
|
|
|
|
3593
|
my $base = max(@base); |
301
|
1713
|
|
|
|
|
2504
|
$base + &{$self->{score_sub}}($a->[$j - 1], $b->[$i - 1]); |
|
1713
|
|
|
|
|
4087
|
|
302
|
6
|
|
|
|
|
38
|
}; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
my $score_up = sub { |
305
|
1713
|
|
|
1713
|
|
2005
|
my ($i, $j) = @_; |
306
|
|
|
|
|
|
|
|
307
|
1713
|
|
|
|
|
2169
|
my @base = map { $_->[$i - 1]->[$j]; } @D; |
|
5139
|
|
|
|
|
9228
|
|
308
|
1713
|
|
|
|
|
2746
|
$base[$from_diag_idx] += $self->{gap_open_penalty}; |
309
|
1713
|
|
|
|
|
2186
|
$base[$from_up_idx] += $self->{gap_extend_penalty}; |
310
|
1713
|
|
|
|
|
2335
|
$base[$from_left_idx] += $self->{gap_open_penalty}; |
311
|
1713
|
|
|
|
|
5036
|
max(@base); |
312
|
6
|
|
|
|
|
72
|
}; |
313
|
|
|
|
|
|
|
|
314
|
6
|
|
|
|
|
9
|
my $score_left; |
315
|
6
|
100
|
|
|
|
25
|
if (!$self->{local}) { |
316
|
|
|
|
|
|
|
$score_left = sub { |
317
|
876
|
|
|
876
|
|
954
|
my ($i, $j) = @_; |
318
|
|
|
|
|
|
|
|
319
|
876
|
|
|
|
|
1035
|
my @base = map { $_->[$i]->[$j - 1]; } @D; |
|
2628
|
|
|
|
|
4402
|
|
320
|
876
|
|
|
|
|
1301
|
$base[$from_diag_idx] += $self->{gap_open_penalty}; |
321
|
876
|
|
|
|
|
1051
|
$base[$from_up_idx] += $self->{gap_open_penalty}; |
322
|
876
|
|
|
|
|
1032
|
$base[$from_left_idx] += $self->{gap_extend_penalty}; |
323
|
|
|
|
|
|
|
|
324
|
876
|
|
|
|
|
2461
|
max(@base); |
325
|
4
|
|
|
|
|
23
|
}; |
326
|
|
|
|
|
|
|
} else { |
327
|
|
|
|
|
|
|
$score_left = sub { |
328
|
837
|
|
|
837
|
|
1001
|
my ($i, $j) = @_; |
329
|
|
|
|
|
|
|
|
330
|
837
|
|
|
|
|
1064
|
my @base = map { $_->[$i]->[$j - 1]; } @D; |
|
2511
|
|
|
|
|
4829
|
|
331
|
837
|
100
|
|
|
|
1686
|
if ($i < $m) { |
332
|
792
|
|
|
|
|
1118
|
$base[$from_diag_idx] += $self->{gap_open_penalty}; |
333
|
792
|
|
|
|
|
987
|
$base[$from_up_idx] += $self->{gap_open_penalty}; |
334
|
792
|
|
|
|
|
1141
|
$base[$from_left_idx] += $self->{gap_extend_penalty}; |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
837
|
|
|
|
|
2365
|
max(@base); |
338
|
2
|
|
|
|
|
12
|
}; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
6
|
|
|
|
|
11
|
my $j; |
342
|
6
|
100
|
|
|
|
36
|
if (!$self->{local}) { |
343
|
4
|
|
|
|
|
7
|
$j = 1; |
344
|
4
|
|
|
|
|
14
|
while ($j <= $n) { |
345
|
59
|
|
|
|
|
88
|
foreach (@D) { |
346
|
177
|
|
|
|
|
507
|
$_->[0]->[$j] = $self->{gap_open_penalty} + |
347
|
|
|
|
|
|
|
($j - 1) * $self->{gap_extend_penalty}; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
59
|
|
|
|
|
135
|
++$j; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
} else { |
353
|
2
|
|
|
|
|
4
|
$j = 1; |
354
|
2
|
|
|
|
|
10
|
while ($j <= $n) { |
355
|
45
|
|
|
|
|
57
|
foreach (@D) { |
356
|
135
|
|
|
|
|
241
|
$_->[0]->[$j] = 0; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
45
|
|
|
|
|
95
|
++$j; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
6
|
|
|
|
|
12
|
my $i = 1; |
364
|
6
|
|
|
|
|
20
|
while ($i <= $m) { |
365
|
92
|
|
|
|
|
131
|
foreach (@D) { |
366
|
276
|
|
|
|
|
792
|
$_->[$i]->[0] = $self->{gap_open_penalty} + |
367
|
|
|
|
|
|
|
($i - 1) * $self->{gap_extend_penalty}; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
92
|
|
|
|
|
278
|
++$i; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# order must correspond to $from_* constants |
374
|
6
|
|
|
|
|
16
|
my @subproblems = ( $score_diag, $score_up, $score_left ); |
375
|
|
|
|
|
|
|
|
376
|
6
|
|
|
|
|
9
|
$i = 1; |
377
|
6
|
|
|
|
|
25
|
while ($i <= $m) { |
378
|
92
|
|
|
|
|
108
|
$j = 1; |
379
|
92
|
|
|
|
|
164
|
while ($j <= $n) { |
380
|
1713
|
|
|
|
|
1848
|
my $k = 0; |
381
|
1713
|
|
|
|
|
3154
|
while ($k < 3) { # scalar(@D), scalar(@subproblems) |
382
|
5139
|
|
|
|
|
5883
|
$D[$k]->[$i]->[$j] = &{$subproblems[$k]}($i, $j); |
|
5139
|
|
|
|
|
9131
|
|
383
|
5139
|
|
|
|
|
19442
|
++$k; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# my $x = join ', ', map { $_->[$i]->[$j]; } @D; |
387
|
|
|
|
|
|
|
# warn "$i, $j: $x\n"; |
388
|
|
|
|
|
|
|
|
389
|
1713
|
|
|
|
|
3742
|
++$j; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
92
|
|
|
|
|
184
|
++$i; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# like $score_up |
396
|
6
|
|
|
|
|
22
|
my @delta_up = ( $self->{gap_open_penalty}, $self->{gap_extend_penalty}, |
397
|
|
|
|
|
|
|
$self->{gap_open_penalty} ); |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# like $score_left |
400
|
6
|
|
|
|
|
20
|
my @delta_left = ( $self->{gap_open_penalty}, $self->{gap_open_penalty}, |
401
|
|
|
|
|
|
|
$self->{gap_extend_penalty} ); |
402
|
|
|
|
|
|
|
|
403
|
6
|
|
|
|
|
14
|
my @no_delta = (0, 0, 0); |
404
|
|
|
|
|
|
|
|
405
|
6
|
|
|
|
|
13
|
my @score = map { $_->[$m]->[$n]; } @D; |
|
18
|
|
|
|
|
44
|
|
406
|
6
|
|
|
|
|
17
|
my $res = max(@score); |
407
|
|
|
|
|
|
|
|
408
|
6
|
|
|
|
|
11
|
my $arrow = 0; |
409
|
6
|
|
|
|
|
14
|
my $flag = 1; |
410
|
6
|
|
|
|
|
10
|
my $idx = 0; |
411
|
6
|
|
|
|
|
23
|
while ($idx < 3) { # scalar(@score) |
412
|
18
|
100
|
|
|
|
41
|
if ($score[$idx] == $res) { |
413
|
8
|
|
|
|
|
32
|
$arrow |= $flag; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
18
|
|
|
|
|
23
|
$flag *= 2; |
417
|
18
|
|
|
|
|
60
|
++$idx; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
6
|
|
|
|
|
13
|
$i = $m; |
421
|
6
|
|
|
|
|
10
|
$j = $n; |
422
|
6
|
|
100
|
|
|
34
|
while (($i > 0) || ($j > 0)) { |
423
|
132
|
|
|
|
|
151
|
my @alt; |
424
|
132
|
100
|
|
|
|
260
|
if ($arrow & $from_diag) { |
425
|
64
|
50
|
33
|
|
|
266
|
die "internal error" unless ($i > 0) && ($j > 0); |
426
|
64
|
|
|
|
|
153
|
push @alt, [ $i - 1, $j - 1 ]; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
132
|
100
|
|
|
|
284
|
if ($arrow & $from_up) { |
430
|
49
|
50
|
|
|
|
97
|
die "internal error" unless ($i > 0); |
431
|
49
|
|
|
|
|
102
|
push @alt, [ $i - 1, $j ]; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
132
|
100
|
|
|
|
362
|
if ($arrow & $from_left) { |
435
|
45
|
50
|
|
|
|
90
|
die "internal error" unless ($j > 0); |
436
|
45
|
|
|
|
|
98
|
push @alt, [ $i, $j - 1]; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
132
|
50
|
|
|
|
257
|
if (!@alt) { |
440
|
0
|
|
|
|
|
0
|
die "internal error"; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# my $x = join ', ', map { "[ " . $_->[0] . ", " . $_->[1] . " ]"; } @alt; |
444
|
|
|
|
|
|
|
# warn "$i, $j: $x\n"; |
445
|
|
|
|
|
|
|
|
446
|
132
|
|
|
|
|
216
|
my $cur = [ $i, $j ]; |
447
|
132
|
|
|
|
|
175
|
my $move; |
448
|
132
|
100
|
|
|
|
244
|
if (@alt == 1) { |
449
|
107
|
|
|
|
|
295
|
$move = $self->_simple_trace_back($cur, $alt[0], |
450
|
|
|
|
|
|
|
$self->{callbacks}); |
451
|
|
|
|
|
|
|
} else { |
452
|
25
|
|
|
|
|
75
|
$move = $self->_trace_back($cur, \@alt); |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
132
|
100
|
|
|
|
361
|
if ($move eq 'align') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
456
|
64
|
|
|
|
|
69
|
--$i; |
457
|
64
|
|
|
|
|
62
|
--$j; |
458
|
|
|
|
|
|
|
|
459
|
64
|
|
|
|
|
90
|
@score = map { $_->[$i]->[$j]; } @D; |
|
192
|
|
|
|
|
393
|
|
460
|
64
|
100
|
|
|
|
175
|
if ($i == 0) { |
|
|
100
|
|
|
|
|
|
461
|
2
|
|
|
|
|
13
|
$arrow = $from_left; |
462
|
|
|
|
|
|
|
} elsif ($j == 0) { |
463
|
2
|
|
|
|
|
9
|
$arrow = $from_up; |
464
|
|
|
|
|
|
|
} else { |
465
|
60
|
|
|
|
|
124
|
my $d = max(@score); |
466
|
60
|
|
|
|
|
65
|
$arrow = 0; |
467
|
60
|
|
|
|
|
64
|
$flag = 1; |
468
|
60
|
|
|
|
|
64
|
$idx = 0; |
469
|
60
|
|
|
|
|
132
|
while ($idx < 3) { # scalar(@score) |
470
|
180
|
100
|
|
|
|
355
|
if ($score[$idx] == $d) { |
471
|
77
|
|
|
|
|
92
|
$arrow |= $flag; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
180
|
|
|
|
|
175
|
$flag *= 2; |
475
|
180
|
|
|
|
|
558
|
++$idx; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
} elsif ($move eq 'shift_a') { |
479
|
40
|
|
|
|
|
49
|
--$j; |
480
|
|
|
|
|
|
|
|
481
|
40
|
|
|
|
|
53
|
my @base = map { $_->[$i]->[$j] } @D; |
|
120
|
|
|
|
|
254
|
|
482
|
40
|
|
|
|
|
49
|
my $delta; |
483
|
40
|
100
|
100
|
|
|
207
|
if ($self->{local} && ($i == $m)) { |
484
|
5
|
|
|
|
|
9
|
$delta = \@no_delta; |
485
|
|
|
|
|
|
|
} else { |
486
|
35
|
|
|
|
|
51
|
$delta = \@delta_left; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
40
|
|
|
|
|
104
|
$arrow = $self->_retread($score[$from_left_idx], $i, $j, |
490
|
|
|
|
|
|
|
\@base, $delta); |
491
|
40
|
|
|
|
|
232
|
@score = @base; |
492
|
|
|
|
|
|
|
} elsif ($move eq 'shift_b') { |
493
|
28
|
|
|
|
|
29
|
--$i; |
494
|
|
|
|
|
|
|
|
495
|
28
|
|
|
|
|
47
|
my @base = map { $_->[$i]->[$j] } @D; |
|
84
|
|
|
|
|
161
|
|
496
|
28
|
|
|
|
|
79
|
$arrow = $self->_retread($score[$from_up_idx], $i, $j, |
497
|
|
|
|
|
|
|
\@base, \@delta_up); |
498
|
28
|
|
|
|
|
143
|
@score = @base; |
499
|
|
|
|
|
|
|
} else { |
500
|
0
|
|
|
|
|
0
|
die "internal error"; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
6
|
|
|
|
|
234
|
return $res; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub _retread { |
508
|
68
|
|
|
68
|
|
118
|
my ($self, $to_score, $i, $j, $base, $delta) = @_; |
509
|
|
|
|
|
|
|
|
510
|
68
|
100
|
|
|
|
177
|
if ($i == 0) { |
|
|
50
|
|
|
|
|
|
511
|
22
|
|
|
|
|
39
|
return $from_left; |
512
|
|
|
|
|
|
|
} elsif ($j == 0) { |
513
|
0
|
|
|
|
|
0
|
return $from_up; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
46
|
|
|
|
|
59
|
my $a = 0; |
517
|
46
|
|
|
|
|
49
|
my $flag = 1; |
518
|
46
|
|
|
|
|
55
|
my $idx = 0; |
519
|
46
|
|
|
|
|
94
|
while ($idx < 3) { |
520
|
138
|
100
|
|
|
|
298
|
if ($base->[$idx] + $delta->[$idx] == $to_score) { |
521
|
53
|
|
|
|
|
71
|
$a |= $flag; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
|
524
|
138
|
|
|
|
|
134
|
$flag *= 2; |
525
|
138
|
|
|
|
|
260
|
++$idx; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
46
|
|
|
|
|
91
|
return $a; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub _trace_back { |
532
|
58
|
|
|
58
|
|
79
|
my ($self, $cur, $sources) = @_; |
533
|
|
|
|
|
|
|
|
534
|
58
|
|
|
|
|
88
|
my $arg = { }; |
535
|
58
|
|
|
|
|
104
|
foreach my $next (@$sources) { |
536
|
124
|
|
|
|
|
277
|
my $m = $self->_simple_trace_back($cur, $next, { }); |
537
|
124
|
100
|
|
|
|
375
|
if ($m eq 'align') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
538
|
48
|
|
|
|
|
174
|
$arg->{align} = [ $cur->[1] - 1, $cur->[0] - 1 ]; |
539
|
|
|
|
|
|
|
} elsif ($m eq 'shift_a') { |
540
|
32
|
|
|
|
|
130
|
$arg->{shift_a} = $cur->[1] - 1; |
541
|
|
|
|
|
|
|
} elsif ($m eq 'shift_b') { |
542
|
44
|
|
|
|
|
140
|
$arg->{shift_b} = $cur->[0] - 1; |
543
|
|
|
|
|
|
|
} else { |
544
|
0
|
|
|
|
|
0
|
die "internal error"; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
58
|
|
|
|
|
78
|
my $move; |
549
|
58
|
|
|
|
|
94
|
my $cb = $self->{callbacks}; |
550
|
58
|
100
|
|
|
|
119
|
if (exists($cb->{select_align})) { |
551
|
4
|
|
|
|
|
5
|
$move = &{$cb->{select_align}}($arg); |
|
4
|
|
|
|
|
16
|
|
552
|
4
|
50
|
|
|
|
47
|
if (!exists($arg->{$move})) { |
553
|
0
|
|
|
|
|
0
|
die "select_align callback returned invalid selection $move."; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
} else { |
556
|
54
|
|
|
|
|
116
|
my @cn = qw(align shift_a shift_b); |
557
|
54
|
|
|
|
|
88
|
foreach my $m (@cn) { |
558
|
64
|
100
|
|
|
|
139
|
if (exists($arg->{$m})) { |
559
|
54
|
|
|
|
|
60
|
$move = $m; |
560
|
54
|
|
|
|
|
77
|
last; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
54
|
50
|
|
|
|
99
|
if (!$move) { |
565
|
0
|
|
|
|
|
0
|
die "internal error"; |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
54
|
100
|
|
|
|
122
|
if (exists($cb->{$move})) { |
569
|
47
|
100
|
|
|
|
84
|
if ($move eq 'align') { |
570
|
37
|
|
|
|
|
41
|
&{$cb->{align}}(@{$arg->{align}}); |
|
37
|
|
|
|
|
97
|
|
|
37
|
|
|
|
|
64
|
|
571
|
|
|
|
|
|
|
} else { |
572
|
10
|
|
|
|
|
15
|
&{$cb->{$move}}($arg->{$move}); |
|
10
|
|
|
|
|
48
|
|
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
58
|
|
|
|
|
434
|
return $move; |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
sub _simple_trace_back { |
581
|
428
|
|
|
428
|
|
555
|
my ($self, $cur, $next, $cb) = @_; |
582
|
|
|
|
|
|
|
|
583
|
428
|
100
|
|
|
|
848
|
if ($next->[0] == $cur->[0] - 1) { |
584
|
324
|
100
|
|
|
|
578
|
if ($next->[1] == $cur->[1] - 1) { |
585
|
215
|
100
|
|
|
|
475
|
if (exists($cb->{align})) { |
586
|
155
|
|
|
|
|
200
|
&{$cb->{align}}($next->[1], $next->[0]); |
|
155
|
|
|
|
|
354
|
|
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
215
|
|
|
|
|
1091
|
return 'align'; |
590
|
|
|
|
|
|
|
} else { |
591
|
109
|
50
|
|
|
|
226
|
if ($next->[1] != $cur->[1]) { |
592
|
0
|
|
|
|
|
0
|
die "internal error"; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
109
|
100
|
|
|
|
212
|
if (exists($cb->{shift_b})) { |
596
|
59
|
|
|
|
|
80
|
&{$cb->{shift_b}}($cur->[0] - 1); |
|
59
|
|
|
|
|
144
|
|
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
109
|
|
|
|
|
492
|
return 'shift_b'; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
} else { |
602
|
104
|
50
|
|
|
|
232
|
if ($next->[0] != $cur->[0]) { |
603
|
0
|
|
|
|
|
0
|
die "internal error"; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
104
|
50
|
|
|
|
206
|
if ($next->[1] != $cur->[1] - 1) { |
607
|
0
|
|
|
|
|
0
|
die "internal error"; |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
104
|
100
|
|
|
|
212
|
if (exists($cb->{shift_a})) { |
611
|
70
|
|
|
|
|
86
|
&{$cb->{shift_a}}($cur->[1] - 1); |
|
70
|
|
|
|
|
182
|
|
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
104
|
|
|
|
|
551
|
return 'shift_a'; |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
1; |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
__END__ |