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.008009;
2              
3 4     4   657647 use 5.016;
  4         16  
4 4     4   22 use strict;
  4         10  
  4         118  
5 4     4   27 use warnings;
  4         6  
  4         473  
6              
7             use overload
8             '""' => \&_stringify,
9             '.=' => \&_concat,
10 4     4   1792 'bool' => sub{return(1)};
  4     101   5703  
  4         49  
  101         337  
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 175805 my ($class, $seq, $id, $desc, $qual) = @_;
100              
101 133 100 100     576 if ( defined $seq && defined $qual
      100        
102             && (length($seq) != length($qual))) {
103 1         8 die "Sequence/quality length mismatch";
104             }
105              
106 132         279 my $self = bless {}, $class;
107 132   100     490 $self->{seq} = $seq // '';
108 132   100     364 $self->{id} = $id // undef;
109 132   100     360 $self->{desc} = $desc // undef;
110 132   100     607 $self->{qual} = $qual // undef;
111              
112 132         368 return $self;
113              
114             }
115              
116             sub seq : lvalue {
117              
118 29     29 1 847 my ($self,$new_val) = @_;
119 29 100       90 $self->{seq} = $new_val if (defined $new_val);
120 29         238 return $self->{seq};
121              
122             }
123              
124             sub id : lvalue {
125              
126 9     9 1 1371 my ($self,$new_val) = @_;
127 9 100       39 $self->{id} = $new_val if (defined $new_val);
128 9         91 return $self->{id};
129              
130             }
131              
132             sub desc : lvalue {
133              
134 9     9 1 35 my ($self,$new_val) = @_;
135 9 100       33 $self->{desc} = $new_val if (defined $new_val);
136 9         59 return $self->{desc};
137              
138             }
139              
140             sub qual : lvalue {
141              
142 10     10 1 317 my ($self,$new_val) = @_;
143 10 100       34 $self->{qual} = $new_val if (defined $new_val);
144 10         72 return $self->{qual};
145              
146             }
147              
148             sub range {
149              
150 4     4 1 245 my ($self, $start, $end) = @_;
151 4 100 100     22 if ($start < 1 || $end > length($self->{seq})) {
152 2         17 warn "Range outside of sequence length\n";
153 2         7 return undef;
154             }
155 2         9 my $seq = substr $self->{seq}, $start-1, $end-$start+1;
156             my $qual = defined $self->{qual}
157 2 100       7 ? 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         28 $qual,
164             );
165 2         3 $new->{_input_format} = $self->{_input_format};
166 2         5 return $new;
167              
168             }
169              
170             sub as_fasta {
171              
172 4     4 1 10 my ($self, $line_length) = @_;
173 4   100     14 my $l = $line_length // 60;
174 4 100       10 if (! defined $self->{id}) {
175 1         7 warn "Can't write FASTA with undefined ID\n";
176 1         5 return undef;
177             }
178 3         7 my $string = '>' . $self->{id};
179 3 100       9 $string .= ' ' . $self->{desc} if (defined $self->{desc});
180 3         26 $string .= "\n";
181 3         5 my $i = 0;
182 3         9 while ($i < length($self->{seq})) {
183 5         10 $string .= substr($self->{seq}, $i, $l) . "\n";
184 5         10 $i += $l;
185             }
186 3         11 return $string;
187              
188             }
189              
190             sub as_fastq {
191              
192 6     6 1 317 my ($self, $qual) = @_;
193 6 100       14 if (! defined $self->{id}) {
194 1         7 warn "Can't write FASTQ with undefined ID\n";
195 1         4 return undef;
196             }
197 5         9 my $string = '@' . $self->{id};
198 5 100       12 $string .= ' ' . $self->{desc} if (defined $self->{desc});
199 5         7 $string .= "\n";
200 5         9 $string .= $self->{seq};
201 5         6 $string .= "\n+\n";
202              
203             # check string lengths
204 5 100       13 if (defined $self->{qual}) {
    100          
205 3 100       9 if (length $self->{qual} != length $self->{seq}) {
206 1         8 die "Sequence/quality length mismatch";
207             }
208             }
209             # check that quality is defined somewhere
210             elsif (! defined $qual) {
211 1         8 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       12 : $self->{qual};
218 3         5 $string .= "\n";
219 3         12 return $string;
220              
221             }
222              
223             sub as_input {
224              
225 2     2 1 8 my ($self, $arg2) = @_;
226             die "No input format found"
227 2 50       12 if (! defined $self->{_input_format});
228 2         10 my $method = 'as_' . $self->{_input_format};
229 2         10 $self->$method();
230              
231             }
232              
233             sub rev_com {
234              
235 7     7 1 246 my ($self) = @_;
236              
237 7         25 my $seq = $self->{seq};
238 7         12 $seq =~ tr/Xx/Nn/;
239 7 100       14 if (! _is_nucleic($seq) ) {
240 1         25 warn "Bad input sequence\n";
241 1         9 return undef;
242             }
243 6         12 $seq = reverse $seq;
244 6         12 $seq =~ tr
245             {ACGTMRWSYKVHDBNacgtmrwsykvhdbn-}
246             {TGCAKYWSRMBDHVNtgcakywsrmbdhvn-};
247              
248 6         37 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       12 if (! defined wantarray) {
253 1         2 $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         15 $qual,
264             );
265 5         8 $new->{_input_format} = $self->{_input_format};
266 5         16 return $new;
267              
268             }
269              
270             sub translate {
271              
272 6     6 1 73 my ($self,$frame) = @_;
273              
274 6   100     21 $frame = $frame // 0;
275 6 100 100     32 die "$frame is not a valid frame (must be between 0 and 5)\n"
276             if ($frame < 0 || $frame > 5);
277              
278 4 100       13 my $seq = uc( $frame > 2 ? $self->rev_com->seq : $self->seq );
279 4         16 $seq = substr $seq, $frame%3;
280 4         8 $seq =~ tr/X/N/;
281 4 100       9 if (! _is_nucleic($seq) ) {
282 2         28 warn "Input doesn't look like DNA\n";
283 2         8 return undef;
284             }
285              
286 2   100     14 $seq = join('', map {$genetic_code{$_} // 'X'}
  5         17  
287             unpack 'A3' x int(length($seq)/3), $seq);
288              
289             # If in void context, act in-place
290 2 100       5 if (! defined wantarray) {
291 1         3 $self->{seq} = $seq;
292 1         1 $self->{qual} = undef;
293 1         3 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         3 );
302              
303             }
304              
305             sub _stringify {
306              
307 125     125   1376 my ($self) = @_;
308 125   100     13961 return $self->{seq} // '';
309              
310             }
311              
312             sub _concat {
313              
314 1     1   3 my ($self,$addition,$other) = @_;
315 1         3 $self->{seq} .= $addition;
316 1         3 return $self;
317              
318             }
319              
320             sub _is_nucleic {
321              
322 11     11   16 my ($seq) = @_;
323 11         66 return $seq !~ /[^ACGTUMRWSYKVHDBN-]/i;
324             }
325              
326              
327             1;
328              
329              
330             __END__