File Coverage

blib/lib/BioX/Seq.pm
Criterion Covered Total %
statement 115 115 100.0
branch 45 46 97.8
condition 28 28 100.0
subroutine 19 19 100.0
pod 11 11 100.0
total 218 219 99.5


line stmt bran cond sub pod time code
1             package BioX::Seq 0.008008;
2              
3 3     3   286894 use 5.016;
  3         32  
4 3     3   33 use strict;
  3         7  
  3         64  
5 3     3   13 use warnings;
  3         6  
  3         220  
6              
7             use overload
8             '""' => \&_stringify,
9             '.=' => \&_concat,
10 3     3   3864 'bool' => sub{return(1)};
  3     101   2980  
  3         30  
  101         395  
11              
12             my %genetic_code = (
13            
14             # Standard codon table
15              
16             TTT => 'F' , TCT => 'S' , TAT => 'Y' , TGT => 'C' ,
17             TTC => 'F' , TCC => 'S' , TAC => 'Y' , TGC => 'C' ,
18             TTA => 'L' , TCA => 'S' , TAA => '*' , TGA => '*' ,
19             TTG => 'L' , TCG => 'S' , TAG => '*' , TGG => 'W' ,
20            
21             CTT => 'L' , CCT => 'P' , CAT => 'H' , CGT => 'R' ,
22             CTC => 'L' , CCC => 'P' , CAC => 'H' , CGC => 'R' ,
23             CTA => 'L' , CCA => 'P' , CAA => 'Q' , CGA => 'R' ,
24             CTG => 'L' , CCG => 'P' , CAG => 'Q' , CGG => 'R' ,
25              
26             ATT => 'I' , ACT => 'T' , AAT => 'N' , AGT => 'S' ,
27             ATC => 'I' , ACC => 'T' , AAC => 'N' , AGC => 'S' ,
28             ATA => 'I' , ACA => 'T' , AAA => 'K' , AGA => 'R' ,
29             ATG => 'M' , ACG => 'T' , AAG => 'K' , AGG => 'R' ,
30              
31             GTT => 'V' , GCT => 'A' , GAT => 'D' , GGT => 'G' ,
32             GTC => 'V' , GCC => 'A' , GAC => 'D' , GGC => 'G' ,
33             GTA => 'V' , GCA => 'A' , GAA => 'E' , GGA => 'G' ,
34             GTG => 'V' , GCG => 'A' , GAG => 'E' , GGG => 'G' ,
35            
36             # Extension containing all permutations using ambiguity codes
37            
38             AAU => 'N' , AAR => 'K' , AAY => 'N' , ACU => 'T' ,
39             ACM => 'T' , ACR => 'T' , ACW => 'T' , ACS => 'T' ,
40             ACY => 'T' , ACK => 'T' , ACV => 'T' , ACH => 'T' ,
41             ACD => 'T' , ACB => 'T' , ACN => 'T' , AGU => 'S' ,
42             AGR => 'R' , AGY => 'S' , ATU => 'I' , ATM => 'I' ,
43             ATW => 'I' , ATY => 'I' , ATH => 'I' , AUA => 'I' ,
44             AUC => 'I' , AUG => 'M' , AUT => 'I' , AUU => 'I' ,
45             AUM => 'I' , AUW => 'I' , AUY => 'I' , AUH => 'I' ,
46             CAU => 'H' , CAR => 'Q' , CAY => 'H' , CCU => 'P' ,
47             CCM => 'P' , CCR => 'P' , CCW => 'P' , CCS => 'P' ,
48             CCY => 'P' , CCK => 'P' , CCV => 'P' , CCH => 'P' ,
49             CCD => 'P' , CCB => 'P' , CCN => 'P' , CGU => 'R' ,
50             CGM => 'R' , CGR => 'R' , CGW => 'R' , CGS => 'R' ,
51             CGY => 'R' , CGK => 'R' , CGV => 'R' , CGH => 'R' ,
52             CGD => 'R' , CGB => 'R' , CGN => 'R' , CTU => 'L' ,
53             CTM => 'L' , CTR => 'L' , CTW => 'L' , CTS => 'L' ,
54             CTY => 'L' , CTK => 'L' , CTV => 'L' , CTH => 'L' ,
55             CTD => 'L' , CTB => 'L' , CTN => 'L' , CUA => 'L' ,
56             CUC => 'L' , CUG => 'L' , CUT => 'L' , CUU => 'L' ,
57             CUM => 'L' , CUR => 'L' , CUW => 'L' , CUS => 'L' ,
58             CUY => 'L' , CUK => 'L' , CUV => 'L' , CUH => 'L' ,
59             CUD => 'L' , CUB => 'L' , CUN => 'L' , GAU => 'D' ,
60             GAR => 'E' , GAY => 'D' , GCU => 'A' , GCM => 'A' ,
61             GCR => 'A' , GCW => 'A' , GCS => 'A' , GCY => 'A' ,
62             GCK => 'A' , GCV => 'A' , GCH => 'A' , GCD => 'A' ,
63             GCB => 'A' , GCN => 'A' , GGU => 'G' , GGM => 'G' ,
64             GGR => 'G' , GGW => 'G' , GGS => 'G' , GGY => 'G' ,
65             GGK => 'G' , GGV => 'G' , GGH => 'G' , GGD => 'G' ,
66             GGB => 'G' , GGN => 'G' , GTU => 'V' , GTM => 'V' ,
67             GTR => 'V' , GTW => 'V' , GTS => 'V' , GTY => 'V' ,
68             GTK => 'V' , GTV => 'V' , GTH => 'V' , GTD => 'V' ,
69             GTB => 'V' , GTN => 'V' , GUA => 'V' , GUC => 'V' ,
70             GUG => 'V' , GUT => 'V' , GUU => 'V' , GUM => 'V' ,
71             GUR => 'V' , GUW => 'V' , GUS => 'V' , GUY => 'V' ,
72             GUK => 'V' , GUV => 'V' , GUH => 'V' , GUD => 'V' ,
73             GUB => 'V' , GUN => 'V' , TAU => 'Y' , TAR => '*' ,
74             TAY => 'Y' , TCU => 'S' , TCM => 'S' , TCR => 'S' ,
75             TCW => 'S' , TCS => 'S' , TCY => 'S' , TCK => 'S' ,
76             TCV => 'S' , TCH => 'S' , TCD => 'S' , TCB => 'S' ,
77             TCN => 'S' , TGU => 'C' , TGY => 'C' , TTU => 'F' ,
78             TTR => 'L' , TTY => 'F' , TUA => 'L' , TUC => 'F' ,
79             TUG => 'L' , TUT => 'F' , TUU => 'F' , TUR => 'L' ,
80             TUY => 'F' , TRA => '*' , UAA => '*' , UAC => 'Y' ,
81             UAG => '*' , UAT => 'Y' , UAU => 'Y' , UAR => '*' ,
82             UAY => 'Y' , UCA => 'S' , UCC => 'S' , UCG => 'S' ,
83             UCT => 'S' , UCU => 'S' , UCM => 'S' , UCR => 'S' ,
84             UCW => 'S' , UCS => 'S' , UCY => 'S' , UCK => 'S' ,
85             UCV => 'S' , UCH => 'S' , UCD => 'S' , UCB => 'S' ,
86             UCN => 'S' , UGA => '*' , UGC => 'C' , UGG => 'W' ,
87             UGT => 'C' , UGU => 'C' , UGY => 'C' , UTA => 'L' ,
88             UTC => 'F' , UTG => 'L' , UTT => 'F' , UTU => 'F' ,
89             UTR => 'L' , UTY => 'F' , UUA => 'L' , UUC => 'F' ,
90             UUG => 'L' , UUT => 'F' , UUU => 'F' , UUR => 'L' ,
91             UUY => 'F' , URA => '*' , MGA => 'R' , MGG => 'R' ,
92             MGR => 'R' , YTA => 'L' , YTG => 'L' , YTR => 'L' ,
93             YUA => 'L' , YUG => 'L' , YUR => 'L' ,
94              
95             );
96              
97             sub new {
98              
99 133     133 1 593 my ($class, $seq, $id, $desc, $qual) = @_;
100              
101 133 100 100     636 if ( defined $seq && defined $qual
      100        
102             && (length($seq) != length($qual))) {
103 1         12 die "Sequence/quality length mismatch";
104             }
105              
106 132         296 my $self = bless {}, $class;
107 132   100     453 $self->{seq} = $seq // '';
108 132   100     361 $self->{id} = $id // undef;
109 132   100     338 $self->{desc} = $desc // undef;
110 132   100     476 $self->{qual} = $qual // undef;
111              
112 132         384 return $self;
113              
114             }
115              
116             sub seq : lvalue {
117              
118 29     29 1 996 my ($self,$new_val) = @_;
119 29 100       100 $self->{seq} = $new_val if (defined $new_val);
120 29         158 return $self->{seq};
121              
122             }
123              
124             sub id : lvalue {
125              
126 9     9 1 976 my ($self,$new_val) = @_;
127 9 100       42 $self->{id} = $new_val if (defined $new_val);
128 9         68 return $self->{id};
129              
130             }
131              
132             sub desc : lvalue {
133              
134 9     9 1 34 my ($self,$new_val) = @_;
135 9 100       32 $self->{desc} = $new_val if (defined $new_val);
136 9         70 return $self->{desc};
137              
138             }
139              
140             sub qual : lvalue {
141              
142 10     10 1 284 my ($self,$new_val) = @_;
143 10 100       26 $self->{qual} = $new_val if (defined $new_val);
144 10         55 return $self->{qual};
145              
146             }
147              
148             sub range {
149              
150 4     4 1 267 my ($self, $start, $end) = @_;
151 4 100 100     21 if ($start < 1 || $end > length($self->{seq})) {
152 2         20 warn "Range outside of sequence length\n";
153 2         66 return undef;
154             }
155 2         7 my $seq = substr $self->{seq}, $start-1, $end-$start+1;
156             my $qual = defined $self->{qual}
157 2 100       13 ? substr $self->{qual}, $start-1, $end-$start+1
158             : undef;
159             my $new = __PACKAGE__->new(
160             $seq,
161             "$self->{id}_$start-$end",
162             $self->{desc},
163 2         12 $qual,
164             );
165 2         5 $new->{_input_format} = $self->{_input_format};
166 2         15 return $new;
167              
168             }
169              
170             sub as_fasta {
171              
172 4     4 1 9 my ($self, $line_length) = @_;
173 4   100     14 my $l = $line_length // 60;
174 4 100       11 if (! defined $self->{id}) {
175 1         10 warn "Can't write FASTA with undefined ID\n";
176 1         8 return undef;
177             }
178 3         7 my $string = '>' . $self->{id};
179 3 100       12 $string .= ' ' . $self->{desc} if (defined $self->{desc});
180 3         6 $string .= "\n";
181 3         3 my $i = 0;
182 3         9 while ($i < length($self->{seq})) {
183 5         12 $string .= substr($self->{seq}, $i, $l) . "\n";
184 5         10 $i += $l;
185             }
186 3         12 return $string;
187              
188             }
189              
190             sub as_fastq {
191              
192 6     6 1 364 my ($self, $qual) = @_;
193 6 100       33 if (! defined $self->{id}) {
194 1         11 warn "Can't write FASTQ with undefined ID\n";
195 1         8 return undef;
196             }
197 5         36 my $string = '@' . $self->{id};
198 5 100       20 $string .= ' ' . $self->{desc} if (defined $self->{desc});
199 5         10 $string .= "\n";
200 5         9 $string .= $self->{seq};
201 5         7 $string .= "\n+\n";
202              
203             # check string lengths
204 5 100       18 if (defined $self->{qual}) {
    100          
205 3 100       25 if (length $self->{qual} != length $self->{seq}) {
206 1         9 die "Sequence/quality length mismatch";
207             }
208             }
209             # check that quality is defined somewhere
210             elsif (! defined $qual) {
211 1         21 die "Attempt to write FASTQ with undefined quality";
212             }
213              
214             # populate qual with constant quality if not defined
215             $string .= defined $qual
216             ? chr($qual+33) x length($self->{seq})
217 3 100       13 : $self->{qual};
218 3         6 $string .= "\n";
219 3         12 return $string;
220              
221             }
222              
223             sub as_input {
224              
225 2     2 1 9 my ($self, $arg2) = @_;
226             die "No input format found"
227 2 50       10 if (! defined $self->{_input_format});
228 2         12 my $method = 'as_' . $self->{_input_format};
229 2         10 $self->$method();
230              
231             }
232              
233             sub rev_com {
234              
235 7     7 1 327 my ($self) = @_;
236              
237 7         14 my $seq = $self->{seq};
238 7         14 $seq =~ tr/Xx/Nn/;
239 7 100       16 if (! _is_nucleic($seq) ) {
240 1         31 warn "Bad input sequence\n";
241 1         9 return undef;
242             }
243 6         16 $seq = reverse $seq;
244 6         18 $seq =~ tr
245             {ACGTMRWSYKVHDBNacgtmrwsykvhdbn-}
246             {TGCAKYWSRMBDHVNtgcakywsrmbdhvn-};
247              
248 6         12 my $qual = $self->{qual};
249 6 100       14 $qual = reverse $qual if (defined $qual);
250              
251             # If in void context, act in-place
252 6 100       15 if (! defined wantarray) {
253 1         3 $self->{seq} = $seq;
254 1         2 $self->{qual} = $qual;
255 1         3 return 1;
256             }
257              
258             # else return a new sequence object
259             my $new = __PACKAGE__->new(
260             $seq,
261             $self->{id},
262             $self->{desc},
263 5         14 $qual,
264             );
265 5         11 $new->{_input_format} = $self->{_input_format};
266 5         20 return $new;
267              
268             }
269              
270             sub translate {
271              
272 6     6 1 89 my ($self,$frame) = @_;
273              
274 6   100     20 $frame = $frame // 0;
275 6 100 100     48 die "$frame is not a valid frame (must be between 0 and 5)\n"
276             if ($frame < 0 || $frame > 5);
277              
278 4 100       11 my $seq = uc( $frame > 2 ? $self->rev_com->seq : $self->seq );
279 4         16 $seq = substr $seq, $frame%3;
280 4         9 $seq =~ tr/X/N/;
281 4 100       8 if (! _is_nucleic($seq) ) {
282 2         31 warn "Input doesn't look like DNA\n";
283 2         12 return undef;
284             }
285              
286 2   100     19 $seq = join('', map {$genetic_code{$_} // 'X'}
  5         29  
287             unpack 'A3' x int(length($seq)/3), $seq);
288              
289             # If in void context, act in-place
290 2 100       17 if (! defined wantarray) {
291 1         2 $self->{seq} = $seq;
292 1         2 $self->{qual} = undef;
293 1         5 return 1;
294             }
295              
296             # else return a new sequence object
297             return __PACKAGE__->new(
298             $seq,
299             $self->{id},
300             $self->{desc},
301 1         7 );
302              
303             }
304              
305             sub _stringify {
306              
307 125     125   1225 my ($self) = @_;
308 125   100     7484 return $self->{seq} // '';
309              
310             }
311              
312             sub _concat {
313              
314 1     1   4 my ($self,$addition,$other) = @_;
315 1         3 $self->{seq} .= $addition;
316 1         2 return $self;
317              
318             }
319              
320             sub _is_nucleic {
321              
322 11     11   17 my ($seq) = @_;
323 11         52 return $seq !~ /[^ACGTUMRWSYKVHDBN-]/i;
324             }
325              
326              
327             1;
328              
329              
330             __END__