File Coverage

blib/lib/AlignDB/Window.pm
Criterion Covered Total %
statement 381 391 97.4
branch 93 130 71.5
condition 19 50 38.0
subroutine 14 14 100.0
pod 8 8 100.0
total 515 593 86.8


line stmt bran cond sub pod time code
1             package AlignDB::Window;
2 6     6   115402 use Moose;
  6         1791386  
  6         34  
3 6     6   28564 use Carp;
  6         10  
  6         397  
4              
5 6     6   23 use List::Util;
  6         15  
  6         242  
6 6     6   3610 use YAML::Syck;
  6         8895  
  6         330  
7 6     6   506 use AlignDB::IntSpan;
  6         5542  
  6         13124  
8              
9             our $VERSION = '1.1.0';
10              
11             has 'sw_size' => ( is => 'rw', isa => 'Int', default => sub {100}, );
12             has 'min_interval' => ( is => 'rw', isa => 'Int', default => sub {11}, );
13             has 'max_out_distance' => ( is => 'rw', isa => 'Int', default => sub {10}, );
14             has 'max_in_distance' => ( is => 'rw', isa => 'Int', default => sub {5}, );
15              
16             sub interval_window {
17 8     8 1 5011 my $self = shift;
18 8         9 my AlignDB::IntSpan $comparable_set = shift;
19 8         9 my ( $interval_start, $interval_end, $sw_size, $min_interval ) = @_;
20              
21             # if undefined, set to default values
22 8   33     224 $sw_size ||= $self->sw_size;
23 8   33     179 $min_interval ||= $self->min_interval;
24              
25 8         19 my $interval_set = AlignDB::IntSpan->new->add_pair( $interval_start, $interval_end );
26 8         239 $interval_set = $interval_set->intersect($comparable_set);
27 8         1133 my $interval_length = $interval_set->size;
28              
29 8         75 my $density = int( $interval_length / $sw_size ) - 1;
30              
31             # used for mark windows
32 8         10 my ( $tmp_start, $tmp_end ) = ( 1, $interval_length );
33              
34 8         7 my @interval_windows;
35              
36             # More windows will be submitted in the following for-loop
37             # and if section
38 8 100       22 if ( $interval_length < $min_interval ) {
    100          
    100          
39 1         3 return @interval_windows;
40             }
41             elsif ( $interval_length < $sw_size ) {
42              
43             # $interval_length between $min_interval and 99 bp
44 2         3 my %window_info;
45 2         3 $window_info{set} = $interval_set;
46 2         3 $window_info{distance} = -1;
47 2         3 $window_info{density} = $density;
48 2         2 $window_info{type} = 'S';
49 2         3 push @interval_windows, \%window_info;
50              
51 2         5 return @interval_windows;
52             }
53             elsif ( $interval_length < $sw_size * 2 ) {
54              
55             # $interval_length between 100 and 199 bp
56             # LO between 50 and 100 bp
57             # R0 between 50 and 99 bp
58             # left windows
59 2         2 my %l_window_info;
60 2         22 my $l_start = $tmp_start;
61 2         4 my $l_end = $l_start + int( $interval_length / 2 ) - 1;
62 2 100       5 if ( $interval_length % 2 ) {
63 1         2 $l_end++;
64             }
65 2         6 $l_window_info{set} = $interval_set->slice( $l_start, $l_end );
66 2         116 $l_window_info{distance} = 0;
67 2         4 $l_window_info{density} = $density;
68 2         2 $l_window_info{type} = 'L';
69 2         3 push @interval_windows, \%l_window_info;
70              
71             # right windows
72 2         2 my %r_window_info;
73 2         2 my $r_end = $tmp_end;
74 2         4 my $r_start = $r_end - int( $interval_length / 2 ) + 1;
75 2         4 $r_window_info{set} = $interval_set->slice( $r_start, $r_end );
76 2         90 $r_window_info{distance} = 0;
77 2         3 $r_window_info{density} = $density;
78 2         2 $r_window_info{type} = 'R';
79 2         3 push @interval_windows, \%r_window_info;
80              
81 2         7 return @interval_windows;
82             }
83             else {
84              
85             # LO => 50 bp, R0 => 50 bp
86             # left windows
87 3         3 my %l_window_info;
88 3         3 my $l_start = $tmp_start;
89 3         4 my $l_end = $l_start + $sw_size / 2 - 1;
90 3         7 $l_window_info{set} = $interval_set->slice( $l_start, $l_end );
91 3         143 $l_window_info{distance} = 0;
92 3         2 $l_window_info{density} = $density;
93 3         4 $l_window_info{type} = 'L';
94 3         4 push @interval_windows, \%l_window_info;
95 3         2 $tmp_start = $l_end + 1;
96              
97             # right windows
98 3         2 my %r_window_info;
99 3         4 my $r_end = $tmp_end;
100 3         3 my $r_start = $r_end - $sw_size / 2 + 1;
101 3         5 $r_window_info{set} = $interval_set->slice( $r_start, $r_end );
102 3         131 $r_window_info{distance} = 0;
103 3         4 $r_window_info{density} = $density;
104 3         2 $r_window_info{type} = 'R';
105 3         3 push @interval_windows, \%r_window_info;
106 3         4 $tmp_end = $r_start - 1;
107             }
108              
109             # regular 100 bp windows
110             # When density IN (1, 2), this loop will be bypassed.
111 3         6 my $half_windows_number = int( ( $density - 1 ) / 2 );
112 3         11 for ( my $i = 1; $i <= $half_windows_number; $i++ ) {
113              
114             # left windows
115 3         2 my %l_window_info;
116 3         4 my $l_start = $tmp_start;
117 3         3 my $l_end = $l_start + $sw_size - 1;
118 3         5 $l_window_info{set} = $interval_set->slice( $l_start, $l_end );
119 3         129 $l_window_info{distance} = $i;
120 3         4 $l_window_info{density} = $density;
121 3         3 $l_window_info{type} = 'L';
122 3         4 push @interval_windows, \%l_window_info;
123 3         2 $tmp_start = $l_end + 1;
124              
125             # right windows
126 3         10 my %r_window_info;
127 3         3 my $r_end = $tmp_end;
128 3         2 my $r_start = $r_end - $sw_size + 1;
129 3         7 $r_window_info{set} = $interval_set->slice( $r_start, $r_end );
130 3         131 $r_window_info{distance} = $i;
131 3         2 $r_window_info{density} = $density;
132 3         4 $r_window_info{type} = 'R';
133 3         3 push @interval_windows, \%r_window_info;
134 3         7 $tmp_end = $r_start - 1;
135             }
136              
137             # Three special windwos:
138             # 1. Ln, range from 100 to 150 bp when even density number
139             # 2. Rn, range from 100 to 149 bp when even density number
140             # 3. Ln or Rn, range from 100 to 199 bp when odd density number
141 3 100       6 if ( $density % 2 ) {
142              
143             # middle windows
144 2         2 my %window_info;
145 2         6 $window_info{set} = $interval_set->slice( $tmp_start, $tmp_end );
146 2         91 $window_info{distance} = $half_windows_number + 1;
147 2         1 $window_info{density} = $density;
148 2         3 $window_info{type} = "L"; # rand > 0.5 ? "L" : "R";
149 2         3 push @interval_windows, \%window_info;
150             }
151             else {
152 1         2 my $remain_length = $tmp_end - $tmp_start + 1;
153              
154             # left windows
155 1         1 my %l_window_info;
156 1         2 my $l_start = $tmp_start;
157 1         2 my $l_end = $l_start + int( $remain_length / 2 ) - 1;
158 1 50       3 if ( $remain_length % 2 ) {
159 1         1 $l_end++;
160             }
161 1         2 $l_window_info{set} = $interval_set->slice( $l_start, $l_end );
162 1         52 $l_window_info{distance} = $half_windows_number + 1;
163 1         1 $l_window_info{density} = $density;
164 1         2 $l_window_info{type} = 'L';
165 1         2 push @interval_windows, \%l_window_info;
166              
167             # right windows
168 1         1 my %r_window_info;
169 1         1 my $r_end = $tmp_end;
170 1         3 my $r_start = $r_end - int( $remain_length / 2 ) + 1;
171 1         2 $r_window_info{set} = $interval_set->slice( $r_start, $r_end );
172 1         45 $r_window_info{distance} = $half_windows_number + 1;
173 1         2 $r_window_info{density} = $density;
174 1         1 $r_window_info{type} = 'R';
175 1         2 push @interval_windows, \%r_window_info;
176             }
177              
178 3         9 return @interval_windows;
179             }
180              
181             sub outside_window {
182 3     3 1 2887 my $self = shift;
183 3         4 my AlignDB::IntSpan $comparable_set = shift;
184 3         4 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
185              
186             # if undefined, set to default values
187 3   33     90 $sw_size ||= $self->sw_size;
188 3   33     73 $maximal_distance ||= $self->max_out_distance;
189              
190 3         9 my $comparable_number = $comparable_set->size;
191              
192 3         37 my @outside_windows;
193              
194 3         4 for my $sw_type (qw{L R}) {
195              
196             # $sw_start and $sw_end are both index of $comparable_set
197 6         4 my ( $sw_start, $sw_end );
198              
199 6 100       14 if ( $sw_type eq 'R' ) {
    50          
200 3         5 $sw_start = $comparable_set->index($internal_end) + 1;
201 3         28 $sw_end = $sw_start + $sw_size - 1;
202             }
203             elsif ( $sw_type eq 'L' ) {
204 3         6 $sw_end = $comparable_set->index($internal_start) - 1;
205 3         37 $sw_start = $sw_end - $sw_size + 1;
206             }
207              
208             # $sw_distance is from 1 to $sw_max_distance
209 6         10 OSW: for my $sw_distance ( 1 .. $maximal_distance ) {
210 12 100       19 last if $sw_start < 1;
211 11 50       14 last if $sw_end > $comparable_number;
212 11         16 my $sw_set = $comparable_set->slice( $sw_start, $sw_end );
213 11         536 my $sw_set_member_number = $sw_set->size;
214 11 50       92 if ( $sw_set_member_number < $sw_size ) {
215 0         0 last OSW;
216             }
217              
218 11         5 my %window_info;
219 11         15 $window_info{type} = $sw_type;
220 11         9 $window_info{set} = $sw_set;
221 11         10 $window_info{distance} = $sw_distance;
222              
223 11         27 push @outside_windows, \%window_info;
224              
225 11 100       21 if ( $sw_type eq 'R' ) {
    50          
226 6         3 $sw_start = $sw_end + 1;
227 6         8 $sw_end = $sw_start + $sw_size - 1;
228             }
229             elsif ( $sw_type eq 'L' ) {
230 5         4 $sw_end = $sw_start - 1;
231 5         8 $sw_start = $sw_end - $sw_size + 1;
232             }
233             }
234             }
235              
236 3         7 return @outside_windows;
237             }
238              
239             sub outside_window_2 {
240 2     2 1 1343 my $self = shift;
241 2         3 my AlignDB::IntSpan $comparable_set = shift;
242 2         2 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
243              
244             # if undefined, set to default values
245 2   33     70 $sw_size ||= $self->sw_size;
246 2   33     49 $maximal_distance ||= $self->max_out_distance;
247              
248 2         4 my $window0_size = int( $sw_size / 2 );
249              
250 2         7 my $comparable_number = $comparable_set->size;
251              
252 2         22 my @outside_windows;
253              
254 2         3 for my $sw_type (qw{L R}) {
255              
256             # $sw_start and $sw_end are both index of $comparable_set
257 4         3 my ( $sw_start, $sw_end );
258              
259 4 100       9 if ( $sw_type eq 'R' ) {
    50          
260 2         5 $sw_start = $comparable_set->index($internal_end) + 1;
261 2         19 $sw_end = $sw_start + $window0_size - 1;
262             }
263             elsif ( $sw_type eq 'L' ) {
264 2         4 $sw_end = $comparable_set->index($internal_start) - 1;
265 2         23 $sw_start = $sw_end - $window0_size + 1;
266             }
267              
268             # distance is from 0 to $maximal_distance
269 4         8 OSW2: for my $sw_distance ( 0 .. $maximal_distance ) {
270 8 100       12 last if $sw_start < 1;
271 7 50       16 last if $sw_end > $comparable_number;
272 7         12 my $sw_set = $comparable_set->slice( $sw_start, $sw_end );
273 7         342 my $sw_set_member_number = $sw_set->size;
274 7 50       65 if ( $sw_set_member_number < $window0_size ) {
275              
276             # warn YAML::Syck::Dump {
277             # sw_type => $sw_type,
278             # sw_start => $sw_start,
279             # sw_end => $sw_end,
280             # };
281 0         0 last OSW2;
282             }
283              
284 7         6 my %window_info;
285 7         9 $window_info{type} = $sw_type;
286 7         7 $window_info{set} = $sw_set;
287 7         6 $window_info{distance} = $sw_distance;
288              
289 7         8 push @outside_windows, \%window_info;
290              
291 7 100       18 if ( $sw_type eq 'R' ) {
    50          
292 4         4 $sw_start = $sw_end + 1;
293 4         6 $sw_end = $sw_start + $sw_size - 1;
294             }
295             elsif ( $sw_type eq 'L' ) {
296 3         3 $sw_end = $sw_start - 1;
297 3         4 $sw_start = $sw_end - $sw_size + 1;
298             }
299             }
300             }
301              
302 2         5 return @outside_windows;
303             }
304              
305             sub inside_window {
306 3     3 1 1888 my $self = shift;
307 3         4 my AlignDB::IntSpan $comparable_set = shift;
308 3         4 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
309              
310             # if undefined, set to default values
311 3   33     89 $sw_size ||= $self->sw_size;
312 3   33     73 $maximal_distance ||= $self->max_in_distance;
313              
314 3         3 my @inside_windows;
315              
316 3         5 for my $sw_type (qw{l r}) {
317              
318             # $sw_start and $sw_end are both index of $comparable_set
319 6         4 my ( $sw_start, $sw_end );
320              
321 6         18 my $working_set = $comparable_set->intersect("$internal_start-$internal_end");
322 6         1212 my $working_length = $working_set->size;
323 6 50       63 last if $working_length < $sw_size;
324              
325             # the 'r' and 'l' windows may overlap each other
326 6 100       15 if ( $sw_type eq 'r' ) {
    50          
327 3         5 $sw_end = $working_set->size;
328 3         23 $sw_start = $sw_end - $sw_size + 1;
329             }
330             elsif ( $sw_type eq 'l' ) {
331 3         2 $sw_start = 1;
332 3         8 $sw_end = $sw_start + $sw_size - 1;
333             }
334              
335 6         12 my $available_distance = int( $working_length / ( $sw_size * 2 ) );
336 6         14 my $max_distance = List::Util::min( $available_distance, $maximal_distance );
337              
338             # sw_distance is from -1 to -max_distance
339 6         12 ISW: for my $i ( 1 .. $max_distance ) {
340 6         13 my $sw_set = $working_set->slice( $sw_start, $sw_end );
341 6         304 my $sw_set_member_number = $sw_set->size;
342 6 50       73 if ( $sw_set_member_number < $sw_size ) {
343 0         0 last ISW;
344             }
345 6         4 my $sw_distance = -$i;
346              
347 6         5 my %window_info;
348 6         8 $window_info{type} = $sw_type;
349 6         6 $window_info{set} = $sw_set;
350 6         4 $window_info{distance} = $sw_distance;
351              
352 6         7 push @inside_windows, \%window_info;
353              
354 6 100       12 if ( $sw_type eq 'r' ) {
    50          
355 3         3 $sw_end = $sw_start - 1;
356 3         5 $sw_start = $sw_end - $sw_size + 1;
357             }
358             elsif ( $sw_type eq 'l' ) {
359 3         3 $sw_start = $sw_end + 1;
360 3         7 $sw_end = $sw_start + $sw_size - 1;
361             }
362             }
363             }
364              
365 3         8 return @inside_windows;
366             }
367              
368             sub inside_window_2 {
369 3     3 1 1480 my $self = shift;
370 3         4 my AlignDB::IntSpan $comparable_set = shift;
371 3         5 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
372              
373             # if undefined, set to default values
374 3   33     85 $sw_size ||= $self->sw_size;
375 3   33     70 $maximal_distance ||= $self->max_in_distance;
376              
377 3         3 my @inside_windows;
378              
379 3         4 for my $sw_type (qw{l r}) {
380              
381             # $sw_start and $sw_end are both index of $comparable_set
382 6         7 my ( $sw_start, $sw_end );
383              
384 6         29 my $working_set = $comparable_set->intersect("$internal_start-$internal_end");
385 6         1090 my $working_length = $working_set->size;
386 6 50       56 last if $working_length < $sw_size;
387              
388             # the windows start from the center
389 6 100       14 if ( $sw_type eq 'r' ) {
    50          
390 3         5 $sw_start = int( $working_length / 2 ) + 1;
391 3         4 $sw_end = $sw_start + $sw_size - 1;
392             }
393             elsif ( $sw_type eq 'l' ) {
394 3         4 $sw_end = int( $working_length / 2 );
395 3         4 $sw_start = $sw_end - $sw_size + 1;
396             }
397              
398 6         7 my $available_distance = int( $working_length / ( $sw_size * 2 ) );
399 6         14 my $max_distance = List::Util::min( $available_distance, $maximal_distance );
400              
401             # sw_distance is from -90 to -90 + max_distance - 1
402 6         12 ISW2: for my $i ( -90 .. ( -90 + $max_distance - 1 ) ) {
403 6         9 my $sw_set = $working_set->slice( $sw_start, $sw_end );
404 6         270 my $sw_set_member_number = $sw_set->size;
405 6 50       50 if ( $sw_set_member_number < $sw_size ) {
406 0         0 last ISW2;
407             }
408 6         6 my $sw_distance = $i;
409              
410 6         5 my %window_info;
411 6         7 $window_info{type} = $sw_type;
412 6         5 $window_info{set} = $sw_set;
413 6         9 $window_info{distance} = $sw_distance;
414              
415 6         6 push @inside_windows, \%window_info;
416              
417 6 100       11 if ( $sw_type eq 'r' ) {
    50          
418 3         4 $sw_start = $sw_end + 1;
419 3         5 $sw_end = $sw_start + $sw_size - 1;
420             }
421             elsif ( $sw_type eq 'l' ) {
422 3         3 $sw_end = $sw_start - 1;
423 3         6 $sw_start = $sw_end - $sw_size + 1;
424             }
425             }
426             }
427              
428 3         8 return @inside_windows;
429             }
430              
431             sub center_window {
432 3     3 1 2541 my $self = shift;
433 3         3 my AlignDB::IntSpan $comparable_set = shift;
434 3         4 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
435              
436             # if undefined, set to default values
437 3   33     88 $sw_size ||= $self->sw_size;
438 3   33     70 $maximal_distance ||= $self->max_out_distance;
439              
440 3         9 my $comparable_number = $comparable_set->size;
441              
442 3         38 my @center_windows;
443              
444             my $original_set;
445 3 100       8 if ( $internal_start < $internal_end ) {
    50          
446 1         5 $original_set = AlignDB::IntSpan->new("$internal_start-$internal_end");
447             }
448             elsif ( $internal_start == $internal_end ) {
449 2         4 $original_set = AlignDB::IntSpan->new($internal_start);
450             }
451             else {
452 0         0 return;
453             }
454              
455 3         148 my AlignDB::IntSpan $window0_set = _center_resize( $original_set, $comparable_set, $sw_size );
456 3 50       8 return unless $window0_set;
457              
458 3         45 my $window0_start = $window0_set->min;
459 3         30 my $window0_end = $window0_set->max;
460 3         27 push @center_windows,
461             {
462             type => 'M',
463             set => $window0_set,
464             distance => 0,
465             };
466              
467 3         6 for my $sw_type (qw{L R}) {
468              
469             # $sw_start and $sw_end are both index of $comparable_set
470 6         6 my ( $sw_start, $sw_end );
471              
472 6 100       12 if ( $sw_type eq 'R' ) {
    50          
473 3         3 $sw_start = $comparable_set->index($window0_end) + 1;
474 3         30 $sw_end = $sw_start + $sw_size - 1;
475             }
476             elsif ( $sw_type eq 'L' ) {
477 3         5 $sw_end = $comparable_set->index($window0_start) - 1;
478 3         30 $sw_start = $sw_end - $sw_size + 1;
479             }
480              
481             # $sw_distance is from 1 to $maximal_distance
482 6         8 CSW: for my $sw_distance ( 1 .. $maximal_distance ) {
483 6 100       10 last if $sw_start < 1;
484 5 50       6 last if $sw_end > $comparable_number;
485 5         7 my $sw_set = $comparable_set->slice( $sw_start, $sw_end );
486 5         236 my $sw_set_member_number = $sw_set->size;
487 5 50       44 if ( $sw_set_member_number < $sw_size ) {
488 0         0 last CSW;
489             }
490              
491 5         9 push @center_windows,
492             {
493             type => $sw_type,
494             set => $sw_set,
495             distance => $sw_distance,
496             };
497              
498 5 100       8 if ( $sw_type eq 'R' ) {
    50          
499 3         3 $sw_start = $sw_end + 1;
500 3         5 $sw_end = $sw_start + $sw_size - 1;
501             }
502             elsif ( $sw_type eq 'L' ) {
503 2         2 $sw_end = $sw_start - 1;
504 2         3 $sw_start = $sw_end - $sw_size + 1;
505             }
506             }
507             }
508              
509 3         14 return @center_windows;
510             }
511              
512             sub _center_resize {
513 5     5   6 my AlignDB::IntSpan $old_set = shift;
514 5         2 my AlignDB::IntSpan $parent_set = shift;
515 5         4 my $resize = shift;
516              
517             # find the middles of old_set
518 5         5 my ( $midleft_parent_idx, $midright_parent_idx );
519             {
520 5 100       2 if ( $old_set->size == 1 ) {
  5         18  
521 4         42 my $mid = $old_set->at(1);
522 4         88 $midleft_parent_idx = $parent_set->index($mid);
523 4         46 $midright_parent_idx = $parent_set->index($mid);
524             }
525             else {
526 1         10 my $half_size = int( $old_set->size / 2 );
527 1         12 my $midleft = $old_set->at($half_size);
528 1         21 my $midright = $old_set->at( $half_size + 1 );
529 1         19 $midleft_parent_idx = $parent_set->index($midleft);
530 1         10 $midright_parent_idx = $parent_set->index($midright);
531             }
532             }
533 5 50 33     60 return unless $midleft_parent_idx and $midright_parent_idx;
534              
535             # map to parent
536 5         9 my $parent_size = $parent_set->size;
537 5         43 my $half_resize = int( $resize / 2 );
538 5         15 my $new_left_idx = $midleft_parent_idx - $half_resize + 1;
539 5 50       8 $new_left_idx = 1 if $new_left_idx < 1;
540 5         5 my $new_right_idx = $midright_parent_idx + $half_resize - 1;
541 5 50       12 $new_right_idx = $parent_size if $new_right_idx > $parent_size;
542              
543 5         8 my $new_set = $parent_set->slice( $new_left_idx, $new_right_idx );
544              
545 5         267 return $new_set;
546             }
547              
548             sub center_intact_window {
549 3     3 1 2022 my $self = shift;
550 3         4 my AlignDB::IntSpan $comparable_set = shift;
551 3         4 my ( $internal_start, $internal_end, $sw_size, $maximal_distance ) = @_;
552              
553             # if undefined, set to default values
554 3   33     98 $sw_size ||= $self->sw_size;
555 3   33     73 $maximal_distance ||= $self->max_out_distance;
556              
557 3         7 my $comparable_number = $comparable_set->size;
558              
559 3         34 my @center_windows;
560              
561             my $original_set;
562 3 100       9 if ( $internal_start < $internal_end ) {
    50          
563 1         5 $original_set = AlignDB::IntSpan->new("$internal_start-$internal_end");
564             }
565             elsif ( $internal_start == $internal_end ) {
566 2         5 $original_set = AlignDB::IntSpan->new($internal_start);
567             }
568             else {
569 0         0 return;
570             }
571              
572 3         119 my $window0_set;
573 3 100       5 if ( $original_set->size < $sw_size ) {
574 2         20 $window0_set = _center_resize( $original_set, $comparable_set, $sw_size );
575             }
576             else {
577 1         12 $window0_set = $original_set->intersect($comparable_set);
578             }
579              
580 3 50       176 return unless $window0_set;
581 3 50       41 return unless $window0_set->size;
582              
583 3         31 my $window0_start = $window0_set->min;
584 3         25 my $window0_end = $window0_set->max;
585 3         29 push @center_windows,
586             {
587             type => 'M',
588             set => $window0_set,
589             distance => 0,
590             };
591              
592 3         3 for my $sw_type (qw{L R}) {
593              
594             # $sw_start and $sw_end are both index of $comparable_set
595 6         6 my ( $sw_start, $sw_end );
596              
597 6 100       13 if ( $sw_type eq 'R' ) {
    50          
598 3         5 $sw_start = $comparable_set->index($window0_end) + 1;
599 3         29 $sw_end = $sw_start + $sw_size - 1;
600             }
601             elsif ( $sw_type eq 'L' ) {
602 3         5 $sw_end = $comparable_set->index($window0_start) - 1;
603 3         32 $sw_start = $sw_end - $sw_size + 1;
604             }
605              
606             # $sw_distance is from 1 to $maximal_distance
607 6         12 CISW: for my $sw_distance ( 1 .. $maximal_distance ) {
608 6 100       9 last if $sw_start < 1;
609 5 50       6 last if $sw_end > $comparable_number;
610 5         11 my $sw_set = $comparable_set->slice( $sw_start, $sw_end );
611 5         241 my $sw_set_member_number = $sw_set->size;
612 5 50       43 if ( $sw_set_member_number < $sw_size ) {
613 0         0 last CISW;
614             }
615              
616 5         9 push @center_windows,
617             {
618             type => $sw_type,
619             set => $sw_set,
620             distance => $sw_distance,
621             };
622              
623 5 100       10 if ( $sw_type eq 'R' ) {
    50          
624 3         6 $sw_start = $sw_end + 1;
625 3         5 $sw_end = $sw_start + $sw_size - 1;
626             }
627             elsif ( $sw_type eq 'L' ) {
628 2         2 $sw_end = $sw_start - 1;
629 2         4 $sw_start = $sw_end - $sw_size + 1;
630             }
631             }
632             }
633              
634 3         10 return @center_windows;
635             }
636              
637             sub strand_window {
638 3     3 1 1451 my $self = shift;
639 3         4 my AlignDB::IntSpan $comparable_set = shift;
640 3         4 my ( $internal_start, $internal_end, $sw_size, $strand ) = @_;
641              
642             # if undefined, set to default values
643 3   66     61 $sw_size ||= $self->sw_size;
644 3   100     11 $strand ||= '+';
645              
646 3         2 my @windows;
647              
648             my $working_set;
649 3 100       6 if ( $internal_start < $internal_end ) {
650 2         10 $working_set = $comparable_set->intersect("$internal_start-$internal_end");
651             }
652             else {
653 1         3 return @windows;
654             }
655              
656 2         463 my $working_length = $working_set->size;
657 2 50       26 return @windows if $working_length < $sw_size;
658              
659             # $sw_start and $sw_end are both index of $comparable_set
660 2         3 my ( $sw_start, $sw_end );
661              
662 2 100       7 if ( $strand eq '+' ) {
    50          
663 1         1 $sw_start = 1;
664 1         2 $sw_end = $sw_start + $sw_size - 1;
665             }
666             elsif ( $strand eq '-' ) {
667 1         2 $sw_end = $working_set->size;
668 1         10 $sw_start = $sw_end - $sw_size + 1;
669             }
670             else {
671 0         0 return @windows;
672             }
673              
674 2         4 my $available_distance = int( $working_length / ($sw_size) );
675              
676             # sw_distance is from 1 to max_distance
677 2         3 SSW: for my $i ( 1 .. $available_distance ) {
678 6         11 my $sw_set = $working_set->slice( $sw_start, $sw_end );
679 6         366 my $sw_set_member_number = $sw_set->size;
680 6 50       59 if ( $sw_set_member_number < $sw_size ) {
681 0         0 last SSW;
682             }
683 6         3 my $sw_distance = $i;
684              
685 6         6 my %window_info;
686 6         9 $window_info{type} = $strand;
687 6         5 $window_info{set} = $sw_set;
688 6         20 $window_info{distance} = $sw_distance;
689              
690 6         7 push @windows, \%window_info;
691              
692 6 100       13 if ( $strand eq '+' ) {
    50          
693 5         5 $sw_start = $sw_end + 1;
694 5         7 $sw_end = $sw_start + $sw_size - 1;
695             }
696             elsif ( $strand eq '-' ) {
697 1         2 $sw_end = $sw_start - 1;
698 1         2 $sw_start = $sw_end - $sw_size + 1;
699             }
700             }
701              
702 2         6 return @windows;
703             }
704              
705             1; # Magic true value required at end of module
706              
707             __END__
708              
709             =pod
710              
711             =encoding UTF-8
712              
713             =head1 NAME
714              
715             AlignDB::Window - Split integer spans into a series of windows
716              
717             =head1 DESCRIPTION
718              
719             AlignDB::Window provides methods to split integer spans, including interval, inside area and
720             outside area, into a series of windows.
721              
722             =head1 ATTRIBUTES
723              
724             C<sw_size> - sliding windows' size, default is 100
725              
726             C<min_interval> - mininal indel interval length, default is 11
727              
728             C<max_out_distance> - maximal outside distance, default is 10
729              
730             C<max_in_distance> - maximal inside distance, default is 5
731              
732             =head1 METHODS
733              
734             =head2 common parameters
735              
736             C<$comparable_set> - AlignDB::IntSpan object
737              
738             C<$interval_start> - start point of the interval
739              
740             C<$interval_end> - end point in the interval
741              
742             C<$internal_start> - start position in the internal region
743              
744             C<$internal_end> - end position in the internal region
745              
746             C<$sw_size> - size of windows
747              
748             C<$min_interval> - minimal size of intervals
749              
750             C<$maximal_distance> - maximal distance
751              
752             C<$strand> - '+' or '-'
753              
754             =head2 interval_window
755              
756             my @interval_windows = $self->interval_window(
757             $comparable_set, $interval_start, $interval_end,
758             $sw_size, $min_interval,
759             );
760              
761             Split an interval to windows.
762              
763             Length of windows are variable, but all positions of the interval are counted.
764              
765             =head2 outside_window
766              
767             my @outside_windows = $self->outside_window(
768             $comparable_set, $internal_start, $internal_end,
769             $sw_size, $maximal_distance,
770             );
771              
772             Draw outside windows from a internal region.
773              
774             All windows are 100 bp length. Start from 1 and end to $maximal_distance.
775              
776             =head2 outside_window_2
777              
778             my @outside_windows = $self->outside_window_2(
779             $comparable_set, $internal_start, $internal_end,
780             $sw_size, $maximal_distance,
781             );
782              
783             Draw outside windows from a internal region.
784              
785             The first window is 50 bp and all others are 100 bp length.
786             Start from 0 and end to $maximal_distance.
787              
788             =head2 inside_window
789              
790             my @inside_windows = $self->inside_window(
791             $comparable_set, $interval_start, $interval_end,
792             $sw_size, $maximal_distance,
793             );
794              
795             Draw inside windows from a internal region.
796              
797             All windows are 100 bp length. Start counting from the edges.
798              
799             =head2 inside_window_2
800              
801             my @inside_windows = $self->inside_window_2(
802             $comparable_set, $interval_start, $interval_end,
803             $sw_size, $maximal_distance,
804             );
805              
806             Draw inside windows from a internal region.
807              
808             All windows are 100 bp length. Start counting from the center.
809              
810             =head2 center_window
811              
812             my @center_windows = $self->center_window(
813             $comparable_set, $internal_start, $internal_end,
814             $sw_size, $maximal_distance,
815             );
816              
817             Draw windows for a certain region.
818              
819             Center is 0, and the first window is 50 bp and all others are 100 bp length.
820             Start from 0 and end to $maximal_distance.
821              
822             =head2 center_intact_window
823              
824             my @center_intact_windows = $self->center_intact_window(
825             $comparable_set, $internal_start, $internal_end,
826             $sw_size, $maximal_distance,
827             );
828              
829             Draw windows for a certain region.
830              
831             Center is 0, and the first window is 50 bp and all others are 100 bp length.
832             Start from 0 and end to $maximal_distance.
833              
834             =head2 strand_window
835              
836             my @windows = $self->strand_window(
837             $comparable_set, $interval_start, $interval_end,
838             $sw_size, $strand,
839             );
840              
841             Draw windows for a certain region
842              
843             =head1 AUTHOR
844              
845             Qiang Wang <wang-q@outlook.com>
846              
847             =head1 COPYRIGHT AND LICENSE
848              
849             This software is copyright (c) 2008 by Qiang Wang.
850              
851             This is free software; you can redistribute it and/or modify it under
852             the same terms as the Perl 5 programming language system itself.
853              
854             =cut