File Coverage

blib/lib/App/Fasops/Command/links.pm
Criterion Covered Total %
statement 98 101 97.0
branch 18 24 75.0
condition 6 9 66.6
subroutine 13 13 100.0
pod 6 6 100.0
total 141 153 92.1


line stmt bran cond sub pod time code
1             package App::Fasops::Command::links;
2 20     20   13898 use strict;
  20         47  
  20         641  
3 20     20   101 use warnings;
  20         45  
  20         514  
4 20     20   98 use autodie;
  20         42  
  20         107  
5              
6 20     20   105034 use App::Fasops -command;
  20         44  
  20         201  
7 20     20   6801 use App::RL::Common;
  20         45  
  20         578  
8 20     20   110 use App::Fasops::Common;
  20         39  
  20         25664  
9              
10             sub abstract {
11 2     2 1 49 return 'scan blocked fasta files and output bi/multi-lateral range links';
12             }
13              
14             sub opt_spec {
15             return (
16 6     6 1 29 [ "outfile|o=s", "Output filename. [stdout] for screen." ],
17             [ "pair|p", "pairwise links" ],
18             [ "best|b", "best-to-best pairwise links" ],
19             { show_defaults => 1, }
20             );
21             }
22              
23             sub usage_desc {
24 6     6 1 44857 return "fasops links [options] [more infiles]";
25             }
26              
27             sub description {
28 1     1 1 740 my $desc;
29 1         3 $desc .= ucfirst(abstract) . ".\n";
30 1         2 $desc .= <<'MARKDOWN';
31              
32             * are paths to axt files, .axt.gz is supported
33             * infile == stdin means reading from STDIN
34              
35             MARKDOWN
36              
37 1         3 return $desc;
38             }
39              
40             sub validate_args {
41 5     5 1 4260 my ( $self, $opt, $args ) = @_;
42              
43 5 100       10 if ( !@{$args} ) {
  5         16  
44 1         2 my $message = "This command need one or more input files.\n\tIt found";
45 1         1 $message .= sprintf " [%s]", $_ for @{$args};
  1         3  
46 1         3 $message .= ".\n";
47 1         8 $self->usage_error($message);
48             }
49 4         8 for ( @{$args} ) {
  4         8  
50 4 50       14 next if lc $_ eq "stdin";
51 4 100       19 if ( !Path::Tiny::path($_)->is_file ) {
52 1         90 $self->usage_error("The input file [$_] doesn't exist.");
53             }
54             }
55              
56 3 50       217 if ( !exists $opt->{outfile} ) {
57 0         0 $opt->{outfile} = Path::Tiny::path( $args->[0] )->absolute . ".tsv";
58             }
59             }
60              
61             sub execute {
62 3     3 1 21 my ( $self, $opt, $args ) = @_;
63              
64 3         4 my @links;
65 3         6 for my $infile ( @{$args} ) {
  3         7  
66 3         7 my $in_fh;
67 3 50       10 if ( lc $infile eq "stdin" ) {
68 0         0 $in_fh = *STDIN{IO};
69             }
70             else {
71 3         24 $in_fh = IO::Zlib->new( $infile, "rb" );
72             }
73              
74 3         4435 my $content = ''; # content of one block
75 3         7 while (1) {
76 84 100 66     325 last if $in_fh->eof and $content eq '';
77 81         2854 my $line = '';
78 81 50       207 if ( !$in_fh->eof ) {
79 81         2620 $line = $in_fh->getline;
80             }
81 81 50       7634 next if substr( $line, 0, 1 ) eq "#";
82              
83 81 100 66     341 if ( ( $line eq '' or $line =~ /^\s+$/ ) and $content ne '' ) {
      66        
84 9         33 my $info_of = App::Fasops::Common::parse_block( $content, 1 );
85 9         13 $content = '';
86              
87 9         14 my @headers = keys %{$info_of};
  9         44  
88              
89 9 100       224 if ( $opt->{best} ) {
    100          
90 3         8 my @matrix = map { [ (undef) x ( scalar @headers ) ] } 0 .. $#headers;
  12         26  
91              
92             # distance is 0 for same sequence
93 3         9 for my $i ( 0 .. $#headers ) {
94 12         16 $matrix[$i][$i] = 0;
95             }
96              
97             # compute a triangle, fill full matrix
98 3         9 for ( my $i = 0; $i <= $#headers; $i++ ) {
99 12         26 for ( my $j = $i + 1; $j <= $#headers; $j++ ) {
100             my $D = App::Fasops::Common::pair_D(
101             [ $info_of->{ $headers[$i] }{seq},
102             $info_of->{ $headers[$j] }{seq},
103 18         63 ]
104             );
105 18         54 $matrix[$i][$j] = $D;
106 18         61 $matrix[$j][$i] = $D;
107             }
108             }
109              
110             # print YAML::Syck::Dump \@matrix;
111              
112             # best_pairwise
113 3         4 my @pair_ary;
114 3         8 for my $i ( 0 .. $#headers ) {
115 12         15 my @row = @{ $matrix[$i] };
  12         22  
116 12         15 $row[$i] = 999; # remove the score (zero) of this item
117 12         26 my $min = List::Util::min(@row);
118 12     19   53 my $min_idx = App::Fasops::Common::firstidx { $_ == $min } @row;
  19         65  
119              
120             # to remove duplications of a:b and b:a
121 12         43 push @pair_ary, join ":", sort { $a <=> $b } ( $i, $min_idx );
  12         40  
122             }
123 3         9 @pair_ary = App::Fasops::Common::uniq(@pair_ary);
124              
125 3         10 for (@pair_ary) {
126 9         21 my ( $i, $j ) = split ":";
127 9         59 push @links, [ $headers[$i], $headers[$j] ];
128             }
129             }
130             elsif ( $opt->{pair} ) {
131 3         9 for ( my $i = 0; $i <= $#headers; $i++ ) {
132 12         54 for ( my $j = $i + 1; $j <= $#headers; $j++ ) {
133 18         50 push @links, [ $headers[$i], $headers[$j] ];
134             }
135             }
136             }
137             else {
138 3         29 push @links, \@headers;
139             }
140             }
141             else {
142 72         116 $content .= $line;
143             }
144             }
145              
146 3         442 $in_fh->close;
147             }
148              
149 3         477 my $out_fh;
150 3 50       16 if ( lc( $opt->{outfile} ) eq "stdout" ) {
151 3         9 $out_fh = *STDOUT{IO};
152             }
153             else {
154 0         0 open $out_fh, ">", $opt->{outfile};
155             }
156              
157 3         6 for my $link (@links) {
158 30         351 printf {$out_fh} join( "\t", @{$link} );
  30         48  
  30         81  
159 30         440 printf {$out_fh} "\n";
  30         63  
160             }
161 3         53 close $out_fh;
162             }
163              
164             1;