comparison miRDeep_plant.pl @ 0:87fe81de0931 draft default tip

Uploaded
author bigrna
date Sun, 04 Jan 2015 02:47:25 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:87fe81de0931
1 #!/usr/bin/perl
2
3 use warnings;
4 use strict;
5 use Getopt::Std;
6 #use RNA;
7
8
9 ################################# MIRDEEP #################################################
10
11 ################################## USAGE ##################################################
12
13
14 my $usage=
15 "$0 file_signature file_structure temp_out_directory
16
17 This is the core algorithm of miRDeep. It takes as input a file in blastparsed format with
18 information on the positions of reads aligned to potential precursor sequences (signature).
19 It also takes as input an RNAfold output file, giving information on the sequence, structure
20 and mimimum free energy of the potential precursor sequences.
21
22 Extra arguments can be given. -s specifies a fastafile containing the known mature miRNA
23 sequences that should be considered for conservation purposes. -t prints out the potential
24 precursor sequences that do _not_ exceed the cut-off (default prints out the sequences that
25 exceeds the cut-off). -u gives limited output, that is only the ids of the potential precursors
26 that exceed the cut-off. -v varies the cut-off. -x is a sensitive option for Sanger sequences
27 obtained through conventional cloning. -z consider the number of base pairings in the lower
28 stems (this option is not well tested).
29
30 -h print this usage
31 -s fasta file with known miRNAs
32 #-o temp directory ,maked befor running the program.
33 -t print filtered
34 -u limited output (only ids)
35 -v cut-off (default 1)
36 -x sensitive option for Sanger sequences
37 -y use Randfold
38 -z consider Drosha processing
39 ";
40
41
42
43
44
45 ############################################################################################
46
47 ################################### INPUT ##################################################
48
49
50 #signature file in blast_parsed format
51 my $file_blast_parsed=shift or die $usage;
52
53 #structure file outputted from RNAfold
54 my $file_struct=shift or die $usage;
55
56 my $tmpdir=shift or die $usage;
57 #options
58 my %options=();
59 getopts("hs:tuv:xyz",\%options);
60
61
62
63
64
65
66 #############################################################################################
67
68 ############################# GLOBAL VARIABLES ##############################################
69
70
71 #parameters
72 my $nucleus_lng=11;
73
74 my $score_star=3.9;
75 my $score_star_not=-1.3;
76 my $score_nucleus=7.63;
77 my $score_nucleus_not=-1.17;
78 my $score_randfold=1.37;
79 my $score_randfold_not=-3.624;
80 my $score_intercept=0.3;
81 my @scores_stem=(-3.1,-2.3,-2.2,-1.6,-1.5,0.1,0.6,0.8,0.9,0.9,0);
82 my $score_min=1;
83 if($options{v}){$score_min=$options{v};}
84 if($options{x}){$score_min=-5;}
85
86 my $e=2.718281828;
87
88 #hashes
89 my %hash_desc;
90 my %hash_seq;
91 my %hash_struct;
92 my %hash_mfe;
93 my %hash_nuclei;
94 my %hash_mirs;
95 my %hash_query;
96 my %hash_comp;
97 my %hash_bp;
98
99 #other variables
100 my $subject_old;
101 my $message_filter;
102 my $message_score;
103 my $lines;
104 my $out_of_bound;
105
106
107
108 ##############################################################################################
109
110 ################################ MAIN ######################################################
111
112
113 #print help if that option is used
114 if($options{h}){die $usage;}
115 unless ($tmpdir=~/\/$/) {$tmpdir .="/";}
116 if(!(-s $tmpdir)){mkdir $tmpdir;}
117 $tmpdir .="TMP_DIR/";
118 mkdir $tmpdir;
119
120 #parse structure file outputted from RNAfold
121 parse_file_struct($file_struct);
122
123 #if conservation is scored, the fasta file of known miRNA sequences is parsed
124 if($options{s}){create_hash_nuclei($options{s})};
125
126 #parse signature file in blast_parsed format and resolve each potential precursor
127 parse_file_blast_parsed($file_blast_parsed);
128 #`rm -rf $tmpdir`;
129 exit;
130
131
132
133
134 ##############################################################################################
135
136 ############################## SUBROUTINES ###################################################
137
138
139
140 sub parse_file_blast_parsed{
141
142 # read through the signature blastparsed file, fills up a hash with information on queries
143 # (deep sequences) mapping to the current subject (potential precursor) and resolve each
144 # potential precursor in turn
145
146 my $file_blast_parsed=shift;
147
148 open (FILE_BLAST_PARSED, "<$file_blast_parsed") or die "can not open $file_blast_parsed\n";
149 while (my $line=<FILE_BLAST_PARSED>){
150 if($line=~/^(\S+)\s+(\S+)\s+(\d+)\.+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\.+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/){
151 my $query=$1;
152 my $query_lng=$2;
153 my $query_beg=$3;
154 my $query_end=$4;
155 my $subject=$5;
156 my $subject_lng=$6;
157 my $subject_beg=$7;
158 my $subject_end=$8;
159 my $e_value=$9;
160 my $pid=$10;
161 my $bitscore=$11;
162 my $other=$12;
163
164 #if the new line concerns a new subject (potential precursor) then the old subject must be resolved
165 if($subject_old and $subject_old ne $subject){
166 resolve_potential_precursor();
167 }
168
169 #resolve the strand
170 my $strand=find_strand($other);
171
172 #resolve the number of reads that the deep sequence represents
173 my $freq=find_freq($query);
174
175 #read information of the query (deep sequence) into hash
176 $hash_query{$query}{"subject_beg"}=$subject_beg;
177 $hash_query{$query}{"subject_end"}=$subject_end;
178 $hash_query{$query}{"strand"}=$strand;
179 $hash_query{$query}{"freq"}=$freq;
180
181 #save the signature information
182 $lines.=$line;
183
184 $subject_old=$subject;
185 }
186 }
187 resolve_potential_precursor();
188 }
189
190 sub resolve_potential_precursor{
191
192 # dissects the potential precursor in parts by filling hashes, and tests if it passes the
193 # initial filter and the scoring filter
194
195 # binary variable whether the potential precursor is still viable
196 my $ret=1;
197 #print STDERR ">$subject_old\n";
198
199 fill_structure();
200 #print STDERR "\%hash_bp",scalar keys %hash_bp,"\n";
201 fill_pri();
202 #print STDERR "\%hash_comp",scalar keys %hash_comp,"\n";
203
204 fill_mature();
205 #print STDERR "\%hash_comp",scalar keys %hash_comp,"\n";
206
207 fill_star();
208 #print STDERR "\%hash_comp",scalar keys %hash_comp,"\n";
209
210 fill_loop();
211 #print STDERR "\%hash_comp",scalar keys %hash_comp,"\n";
212
213 fill_lower_flanks();
214 #print STDERR "\%hash_comp",scalar keys %hash_comp,"\n";
215
216 # do_test_assemble();
217
218 # this is the actual classification
219 unless(pass_filtering_initial() and pass_threshold_score()){$ret=0;}
220
221 print_results($ret);
222
223 reset_variables();
224
225 return;
226
227 }
228
229
230
231 sub print_results{
232
233 my $ret=shift;
234
235 # print out if the precursor is accepted and accepted precursors should be printed out
236 # or if the potential precursor is discarded and discarded potential precursors should
237 # be printed out
238
239 if((!$options{t} and $ret) or ($options{t} and !$ret)){
240 #full output
241 unless($options{u}){
242 if($message_filter){print $message_filter;}
243 if($message_score){print $message_score;}
244 print_hash_comp();
245 print $lines,"\n\n";
246 return;
247 }
248 #limited output (only ids)
249 my $id=$hash_comp{"pri_id"};
250 print "$id\n";
251 }
252 }
253
254
255
256
257
258
259
260 sub pass_threshold_score{
261
262 # this is the scoring
263
264 #minimum free energy of the potential precursor
265 # my $score_mfe=score_mfe($hash_comp{"pri_mfe"});
266 my $score_mfe=score_mfe($hash_comp{"pri_mfe"},$hash_comp{"pri_end"});
267
268 #count of reads that map in accordance with Dicer processing
269 my $score_freq=score_freq($hash_comp{"freq"});
270 #print STDERR "score_mfe: $score_mfe\nscore_freq: $score_freq\n";
271
272 #basic score
273 my $score=$score_mfe+$score_freq;
274
275 #scoring of conserved nucleus/seed (optional)
276 if($options{s}){
277
278 #if the nucleus is conserved
279 if(test_nucleus_conservation()){
280
281 #nucleus from position 2-8
282 my $nucleus=substr($hash_comp{"mature_seq"},1,$nucleus_lng);
283
284 #resolve DNA/RNA ambiguities
285 $nucleus=~tr/[T]/[U]/;
286
287 #print score contribution
288 score_s("score_nucleus\t$score_nucleus");
289
290 #print the ids of known miRNAs with same nucleus
291 score_s("$hash_mirs{$nucleus}");
292 #print STDERR "score_nucleus\t$score_nucleus\n";
293
294 #add to score
295 $score+=$score_nucleus;
296
297 #if the nucleus is not conserved
298 }else{
299 #print (negative) score contribution
300 score_s("score_nucleus\t$score_nucleus_not");
301
302 #add (negative) score contribution
303 $score+=$score_nucleus_not;
304 }
305 }
306
307 #if the majority of potential star reads fall as expected from Dicer processing
308 if($hash_comp{"star_read"}){
309 score_s("score_star\t$score_star");
310 #print STDERR "score_star\t$score_star\n";
311 $score+=$score_star;
312 }else{
313 score_s("score_star\t$score_star_not");
314 #print STDERR "score_star_not\t$score_star_not\n";
315 $score+=$score_star_not;
316 }
317
318 #score lower stems for potential for Drosha recognition (highly optional)
319 if($options{z}){
320 my $stem_bp=$hash_comp{"stem_bp"};
321 my $score_stem=$scores_stem[$stem_bp];
322 $score+=$score_stem;
323 score_s("score_stem\t$score_stem");
324 }
325
326 #print STDERR "score_intercept\t$score_intercept\n";
327
328 $score+=$score_intercept;
329
330 #score for randfold (optional)
331 if($options{y}){
332
333 # only calculate randfold value if it can make the difference between the potential precursor
334 # being accepted or discarded
335 if($score+$score_randfold>=$score_min and $score+$score_randfold_not<=$score_min){
336
337 #randfold value<0.05
338 if(test_randfold()){$score+=$score_randfold;score_s("score_randfold\t$score_randfold");}
339
340 #randfold value>0.05
341 else{$score+=$score_randfold_not;score_s("score_randfold\t$score_randfold_not");}
342 }
343 }
344
345 #round off values to one decimal
346 my $round_mfe=round($score_mfe*10)/10;
347 my $round_freq=round($score_freq*10)/10;
348 my $round=round($score*10)/10;
349
350 #print scores
351 score_s("score_mfe\t$round_mfe\nscore_freq\t$round_freq\nscore\t$round");
352
353 #return 1 if the potential precursor is accepted, return 0 if discarded
354 unless($score>=$score_min){return 0;}
355 return 1;
356 }
357
358 sub test_randfold{
359
360 #print sequence to temporary file, test randfold value, return 1 or 0
361
362 # print_file("pri_seq.fa",">pri_seq\n".$hash_comp{"pri_seq"});
363 #my $tmpfile=$tmpdir.$hash_comp{"pri_id"};
364 #open(FILE, ">$tmpfile");
365 #print FILE ">pri_seq\n",$hash_comp{"pri_seq"};
366 #close FILE;
367
368 # my $p_value=`randfold -s $tmpfile 999 | cut -f 3`;
369 #my $p1=`randfold -s $tmpfile 999 | cut -f 3`;
370 #my $p2=`randfold -s $tmpfile 999 | cut -f 3`;
371 my $p1=&randfold_pvalue($hash_comp{"pri_seq"},999);
372 my $p2=&randfold_pvalue($hash_comp{"pri_seq"},999);
373 my $p_value=($p1+$p2)/2;
374 wait;
375 # system "rm $tmpfile";
376
377 if($p_value<=0.05){return 1;}
378
379 return 0;
380 }
381
382 sub randfold_pvalue{
383 my $cpt_sup = 0;
384 my $cpt_inf = 0;
385 my $cpt_ega = 1;
386
387 my ($seq,$number_of_randomizations)=@_;
388 #my $str =$seq;
389 #my $mfe = RNA::fold($seq,$str);
390 my $rnafold=`perl -e 'print "$seq"' | RNAfold --noPS`;
391 my @rawfolds=split/\s+/,$rnafold;
392 my $str=$rawfolds[1];
393 my $mfe=$rawfolds[-1];
394 $mfe=~s/\(//;
395 $mfe=~s/\)//;
396
397 for (my $i=0;$i<$number_of_randomizations;$i++) {
398 $seq = shuffle_sequence_dinucleotide($seq);
399 #$str = $seq;
400
401 #my $rand_mfe = RNA::fold($str,$str);
402 $rnafold=`perl -e 'print "$seq"' | RNAfold --noPS`;
403 my @rawfolds=split/\s+/,$rnafold;
404 my $str=$rawfolds[1];
405 my $rand_mfe=$rawfolds[-1];
406 $rand_mfe=~s/\(//;
407 $rand_mfe=~s/\)//;
408
409 if ($rand_mfe < $mfe) {
410 $cpt_inf++;
411 }
412 if ($rand_mfe == $mfe) {
413 $cpt_ega++;
414 }
415 if ($rand_mfe > $mfe) {
416 $cpt_sup++;
417 }
418 }
419
420 my $proba = ($cpt_ega + $cpt_inf) / ($number_of_randomizations + 1);
421
422 #print "$name\t$mfe\t$proba\n";
423 return $proba;
424 }
425
426 sub shuffle_sequence_dinucleotide {
427
428 my ($str) = @_;
429
430 # upper case and convert to ATGC
431 $str = uc($str);
432 $str =~ s/U/T/g;
433
434 my @nuc = ('A','T','G','C');
435 my $count_swap = 0;
436 # set maximum number of permutations
437 my $stop = length($str) * 10;
438
439 while($count_swap < $stop) {
440
441 my @pos;
442
443 # look start and end letters
444 my $firstnuc = $nuc[int(rand 4)];
445 my $thirdnuc = $nuc[int(rand 4)];
446
447 # get positions for matching nucleotides
448 for (my $i=0;$i<(length($str)-2);$i++) {
449 if ((substr($str,$i,1) eq $firstnuc) && (substr($str,$i+2,1) eq $thirdnuc)) {
450 push (@pos,($i+1));
451 $i++;
452 }
453 }
454
455 # swap at random trinucleotides
456 my $max = scalar(@pos);
457 for (my $i=0;$i<$max;$i++) {
458 my $swap = int(rand($max));
459 if ((abs($pos[$swap] - $pos[$i]) >= 3) && (substr($str,$pos[$i],1) ne substr($str,$pos[$swap],1))) {
460 $count_swap++;
461 my $w1 = substr($str,$pos[$i],1);
462 my $w2 = substr($str,$pos[$swap],1);
463 substr($str,$pos[$i],1,$w2);
464 substr($str,$pos[$swap],1,$w1);
465 }
466 }
467 }
468 return($str);
469 }
470
471 sub test_nucleus_conservation{
472
473 #test if nucleus is identical to nucleus from known miRNA, return 1 or 0
474
475 my $nucleus=substr($hash_comp{"mature_seq"},1,$nucleus_lng);
476 $nucleus=~tr/[T]/[U]/;
477 if($hash_nuclei{$nucleus}){return 1;}
478
479 return 0;
480 }
481
482
483
484 sub pass_filtering_initial{
485
486 #test if the structure forms a plausible hairpin
487 unless(pass_filtering_structure()){filter_p("structure problem"); return 0;}
488
489 #test if >90% of reads map to the hairpin in consistence with Dicer processing
490 unless(pass_filtering_signature()){filter_p("signature problem");return 0;}
491
492 return 1;
493
494 }
495
496
497 sub pass_filtering_signature{
498
499 #number of reads that map in consistence with Dicer processing
500 my $consistent=0;
501
502 #number of reads that map inconsistent with Dicer processing
503 my $inconsistent=0;
504
505 # number of potential star reads map in good consistence with Drosha/Dicer processing
506 # (3' overhangs relative to mature product)
507 my $star_perfect=0;
508
509 # number of potential star reads that do not map in good consistence with 3' overhang
510 my $star_fuzzy=0;
511
512
513 #sort queries (deep sequences) by their position on the hairpin
514 my @queries=sort {$hash_query{$a}{"subject_beg"} <=> $hash_query{$b}{"subject_beg"}} keys %hash_query;
515
516 foreach my $query(@queries){
517
518 #number of reads that the deep sequence represents
519 unless(defined($hash_query{$query}{"freq"})){next;}
520 my $query_freq=$hash_query{$query}{"freq"};
521
522 #test which Dicer product (if any) the deep sequence corresponds to
523 my $product=test_query($query);
524
525 #if the deep sequence corresponds to a Dicer product, add to the 'consistent' variable
526 if($product){$consistent+=$query_freq;}
527
528 #if the deep sequence do not correspond to a Dicer product, add to the 'inconsistent' variable
529 else{$inconsistent+=$query_freq;}
530
531 #test a potential star sequence has good 3' overhang
532 if($product eq "star"){
533 if(test_star($query)){$star_perfect+=$query_freq;}
534 else{$star_fuzzy+=$query_freq;}
535 }
536 }
537
538 # if the majority of potential star sequences map in good accordance with 3' overhang
539 # score for the presence of star evidence
540 if($star_perfect>$star_fuzzy){$hash_comp{"star_read"}=1;}
541
542 #total number of reads mapping to the hairpin
543 my $freq=$consistent+$inconsistent;
544 $hash_comp{"freq"}=$freq;
545 unless($freq>0){filter_s("read frequency too low"); return 0;}
546
547 #unless >90% of the reads map in consistence with Dicer processing, the hairpin is discarded
548 my $inconsistent_fraction=$inconsistent/($inconsistent+$consistent);
549 unless($inconsistent_fraction<=0.1){filter_p("inconsistent\t$inconsistent\nconsistent\t$consistent"); return 0;}
550
551 #the hairpin is retained
552 return 1;
553 }
554
555 sub test_star{
556
557 #test if a deep sequence maps in good consistence with 3' overhang
558
559 my $query=shift;
560
561 #5' begin and 3' end positions
562 my $beg=$hash_query{$query}{"subject_beg"};
563 my $end=$hash_query{$query}{"subject_end"};
564
565 #the difference between observed and expected begin positions must be 0 or 1
566 my $offset=$beg-$hash_comp{"star_beg"};
567 if($offset==0 or $offset==1 or $offset==-1){return 1;}
568
569 return 0;
570 }
571
572
573
574 sub test_query{
575
576 #test if deep sequence maps in consistence with Dicer processing
577
578 my $query=shift;
579
580 #begin, end, strand and read count
581 my $beg=$hash_query{$query}{"subject_beg"};
582 my $end=$hash_query{$query}{"subject_end"};
583 my $strand=$hash_query{$query}{"strand"};
584 my $freq=$hash_query{$query}{"freq"};
585
586 #should not be on the minus strand (although this has in fact anecdotally been observed for known miRNAs)
587 if($strand eq '-'){return 0;}
588
589 #the deep sequence is allowed to stretch 2 nt beyond the expected 5' end
590 my $fuzz_beg=2;
591 #the deep sequence is allowed to stretch 5 nt beyond the expected 3' end
592 my $fuzz_end=2;
593
594 #if in accordance with Dicer processing, return the type of Dicer product
595 if(contained($beg,$end,$hash_comp{"mature_beg"}-$fuzz_beg,$hash_comp{"mature_end"}+$fuzz_end)){return "mature";}
596 if(contained($beg,$end,$hash_comp{"star_beg"}-$fuzz_beg,$hash_comp{"star_end"}+$fuzz_end)){return "star";}
597 if(contained($beg,$end,$hash_comp{"loop_beg"}-$fuzz_beg,$hash_comp{"loop_end"}+$fuzz_end)){return "loop";}
598
599 #if not in accordance, return 0
600 return 0;
601 }
602
603
604 sub pass_filtering_structure{
605
606 #The potential precursor must form a hairpin with miRNA precursor-like characteristics
607
608 #return value
609 my $ret=1;
610
611 #potential mature, star, loop and lower flank parts must be identifiable
612 unless(test_components()){return 0;}
613
614 #no bifurcations
615 unless(no_bifurcations_precursor()){$ret=0;}
616
617 #minimum 14 base pairings in duplex
618 unless(bp_duplex()>=15){$ret=0;filter_s("too few pairings in duplex");}
619
620 #not more than 6 nt difference between mature and star length
621 unless(-6<diff_lng() and diff_lng()<6){$ret=0; filter_s("too big difference between mature and star length") }
622
623 return $ret;
624 }
625
626
627
628
629
630
631 sub test_components{
632
633 #tests whether potential mature, star, loop and lower flank parts are identifiable
634
635 unless($hash_comp{"mature_struct"}){
636 filter_s("no mature");
637 # print STDERR "no mature\n";
638 return 0;
639 }
640
641 unless($hash_comp{"star_struct"}){
642 filter_s("no star");
643 # print STDERR "no star\n";
644 return 0;
645 }
646
647 unless($hash_comp{"loop_struct"}){
648 filter_s("no loop");
649 # print STDERR "no loop\n";
650 return 0;
651 }
652
653 unless($hash_comp{"flank_first_struct"}){
654 filter_s("no flanks");
655 #print STDERR "no flanks_first_struct\n";
656 return 0;
657 }
658
659 unless($hash_comp{"flank_second_struct"}){
660 filter_s("no flanks");
661 # print STDERR "no flanks_second_struct\n";
662 return 0;
663 }
664 return 1;
665 }
666
667
668
669
670
671 sub no_bifurcations_precursor{
672
673 #tests whether there are bifurcations in the hairpin
674
675 #assembles the potential precursor sequence and structure from the expected Dicer products
676 #this is the expected biological precursor, in contrast with 'pri_seq' that includes
677 #some genomic flanks on both sides
678
679 my $pre_struct;
680 my $pre_seq;
681 if($hash_comp{"mature_arm"} eq "first"){
682 $pre_struct.=$hash_comp{"mature_struct"}.$hash_comp{"loop_struct"}.$hash_comp{"star_struct"};
683 $pre_seq.=$hash_comp{"mature_seq"}.$hash_comp{"loop_seq"}.$hash_comp{"star_seq"};
684 }else{
685 $pre_struct.=$hash_comp{"star_struct"}.$hash_comp{"loop_struct"}.$hash_comp{"mature_struct"};
686 $pre_seq.=$hash_comp{"star_seq"}.$hash_comp{"loop_seq"}.$hash_comp{"mature_seq"};
687 }
688
689 #read into hash
690 $hash_comp{"pre_struct"}=$pre_struct;
691 $hash_comp{"pre_seq"}=$pre_seq;
692
693 #simple pattern matching checks for bifurcations
694 unless($pre_struct=~/^((\.|\()+..(\.|\))+)$/){
695 filter_s("bifurcation in precursor");
696 # print STDERR "bifurcation in precursor\n";
697 return 0;
698 }
699
700 return 1;
701 }
702
703 sub bp_precursor{
704
705 #total number of bps in the precursor
706
707 my $pre_struct=$hash_comp{"pre_struct"};
708
709 #simple pattern matching
710 my $pre_bps=0;
711 while($pre_struct=~/\(/g){
712 $pre_bps++;
713 }
714 return $pre_bps;
715 }
716
717
718 sub bp_duplex{
719
720 #total number of bps in the duplex
721
722 my $duplex_bps=0;
723 my $mature_struct=$hash_comp{"mature_struct"};
724
725 #simple pattern matching
726 while($mature_struct=~/(\(|\))/g){
727 $duplex_bps++;
728 }
729 return $duplex_bps;
730 }
731
732 sub diff_lng{
733
734 #find difference between mature and star lengths
735
736 my $mature_lng=length $hash_comp{"mature_struct"};
737 my $star_lng=length $hash_comp{"star_struct"};
738 my $diff_lng=$mature_lng-$star_lng;
739 return $diff_lng;
740 }
741
742
743
744 sub do_test_assemble{
745
746 # not currently used, tests if the 'pri_struct' as assembled from the parts (Dicer products, lower flanks)
747 # is identical to 'pri_struct' before disassembly into parts
748
749 my $assemble_struct;
750
751 if($hash_comp{"flank_first_struct"} and $hash_comp{"mature_struct"} and $hash_comp{"loop_struct"} and $hash_comp{"star_struct"} and $hash_comp{"flank_second_struct"}){
752 if($hash_comp{"mature_arm"} eq "first"){
753 $assemble_struct.=$hash_comp{"flank_first_struct"}.$hash_comp{"mature_struct"}.$hash_comp{"loop_struct"}.$hash_comp{"star_struct"}.$hash_comp{"flank_second_struct"};
754 }else{
755 $assemble_struct.=$hash_comp{"flank_first_struct"}.$hash_comp{"star_struct"}.$hash_comp{"loop_struct"}.$hash_comp{"mature_struct"}.$hash_comp{"flank_second_struct"};
756 }
757 unless($assemble_struct eq $hash_comp{"pri_struct"}){
758 $hash_comp{"test_assemble"}=$assemble_struct;
759 print_hash_comp();
760 }
761 }
762 return;
763 }
764
765
766
767 sub fill_structure{
768
769 #reads the dot bracket structure into the 'bp' hash where each key and value are basepaired
770
771 my $struct=$hash_struct{$subject_old};
772 my $lng=length $struct;
773
774 #local stack for keeping track of basepairings
775 my @bps;
776
777 for(my $pos=1;$pos<=$lng;$pos++){
778 my $struct_pos=excise_struct($struct,$pos,$pos,"+");
779
780 if($struct_pos eq "("){
781 push(@bps,$pos);
782 }
783
784 if($struct_pos eq ")"){
785 my $pos_prev=pop(@bps);
786 $hash_bp{$pos_prev}=$pos;
787 $hash_bp{$pos}=$pos_prev;
788 }
789 }
790 return;
791 }
792
793
794
795 sub fill_star{
796
797 #fills specifics on the expected star strand into 'comp' hash ('component' hash)
798
799 #if the mature sequence is not plausible, don't look for the star arm
800 my $mature_arm=$hash_comp{"mature_arm"};
801 unless($mature_arm){$hash_comp{"star_arm"}=0; return;}
802
803 #if the star sequence is not plausible, don't fill into the hash
804 my($star_beg,$star_end)=find_star();
805 my $star_arm=arm_star($star_beg,$star_end);
806 unless($star_arm){return;}
807
808 #excise expected star sequence and structure
809 my $star_seq=excise_seq($hash_comp{"pri_seq"},$star_beg,$star_end,"+");
810 my $star_struct=excise_seq($hash_comp{"pri_struct"},$star_beg,$star_end,"+");
811
812 #fill into hash
813 $hash_comp{"star_beg"}=$star_beg;
814 $hash_comp{"star_end"}=$star_end;
815 $hash_comp{"star_seq"}=$star_seq;
816 $hash_comp{"star_struct"}=$star_struct;
817 $hash_comp{"star_arm"}=$star_arm;
818
819 return;
820 }
821
822
823 sub find_star{
824
825 #uses the 'bp' hash to find the expected star begin and end positions from the mature positions
826
827 #the -2 is for the overhang
828 my $mature_beg=$hash_comp{"mature_beg"};
829 my $mature_end=$hash_comp{"mature_end"}-2;
830 my $mature_lng=$mature_end-$mature_beg+1;
831
832 #in some cases, the last nucleotide of the mature sequence does not form a base pair,
833 #and therefore does not basepair with the first nucleotide of the star sequence.
834 #In this case, the algorithm searches for the last nucleotide of the mature sequence
835 #to form a base pair. The offset is the number of nucleotides searched through.
836 my $offset_star_beg=0;
837 my $offset_beg=0;
838
839 #the offset should not be longer than the length of the mature sequence, then it
840 #means that the mature sequence does not form any base pairs
841 while(!$offset_star_beg and $offset_beg<$mature_lng){
842 if($hash_bp{$mature_end-$offset_beg}){
843 $offset_star_beg=$hash_bp{$mature_end-$offset_beg};
844 }else{
845 $offset_beg++;
846 }
847 }
848 #when defining the beginning of the star sequence, compensate for the offset
849 my $star_beg=$offset_star_beg-$offset_beg;
850
851 #same as above
852 my $offset_star_end=0;
853 my $offset_end=0;
854 while(!$offset_star_end and $offset_end<$mature_lng){
855 if($hash_bp{$mature_beg+$offset_end}){
856 $offset_star_end=$hash_bp{$mature_beg+$offset_end};
857 }else{
858 $offset_end++;
859 }
860 }
861 #the +2 is for the overhang
862 my $star_end=$offset_star_end+$offset_end+2;
863
864 return($star_beg,$star_end);
865 }
866
867
868 sub fill_pri{
869
870 #fills basic specifics on the precursor into the 'comp' hash
871
872 my $seq=$hash_seq{$subject_old};
873 my $struct=$hash_struct{$subject_old};
874 my $mfe=$hash_mfe{$subject_old};
875 my $length=length $seq;
876
877 $hash_comp{"pri_id"}=$subject_old;
878 $hash_comp{"pri_seq"}=$seq;
879 $hash_comp{"pri_struct"}=$struct;
880 $hash_comp{"pri_mfe"}=$mfe;
881 $hash_comp{"pri_beg"}=1;
882 $hash_comp{"pri_end"}=$length;
883
884 return;
885 }
886
887
888 sub fill_mature{
889
890 #fills specifics on the mature sequence into the 'comp' hash
891
892 my $mature_query=find_mature_query();
893 my($mature_beg,$mature_end)=find_positions_query($mature_query);
894 my $mature_strand=find_strand_query($mature_query);
895 my $mature_seq=excise_seq($hash_comp{"pri_seq"},$mature_beg,$mature_end,$mature_strand);
896 my $mature_struct=excise_struct($hash_comp{"pri_struct"},$mature_beg,$mature_end,$mature_strand);
897 my $mature_arm=arm_mature($mature_beg,$mature_end,$mature_strand);
898
899 $hash_comp{"mature_query"}=$mature_query;
900 $hash_comp{"mature_beg"}=$mature_beg;
901 $hash_comp{"mature_end"}=$mature_end;
902 $hash_comp{"mature_strand"}=$mature_strand;
903 $hash_comp{"mature_struct"}=$mature_struct;
904 $hash_comp{"mature_seq"}=$mature_seq;
905 $hash_comp{"mature_arm"}=$mature_arm;
906
907 return;
908 }
909
910
911
912 sub fill_loop{
913
914 #fills specifics on the loop sequence into the 'comp' hash
915
916 #unless both mature and star sequences are plausible, do not look for the loop
917 unless($hash_comp{"mature_arm"} and $hash_comp{"star_arm"}){return;}
918
919 my $loop_beg;
920 my $loop_end;
921
922 #defining the begin and end positions of the loop from the mature and star positions
923 #excision depends on whether the mature or star sequence is 5' of the loop ('first')
924 if($hash_comp{"mature_arm"} eq "first"){
925 $loop_beg=$hash_comp{"mature_end"}+1;
926 }else{
927 $loop_end=$hash_comp{"mature_beg"}-1;
928 }
929
930 if($hash_comp{"star_arm"} eq "first"){
931 $loop_beg=$hash_comp{"star_end"}+1;
932 }else{
933 $loop_end=$hash_comp{"star_beg"}-1;
934 }
935
936 #unless the positions are plausible, do not fill into hash
937 unless(test_loop($loop_beg,$loop_end)){return;}
938
939 my $loop_seq=excise_seq($hash_comp{"pri_seq"},$loop_beg,$loop_end,"+");
940 my $loop_struct=excise_struct($hash_comp{"pri_struct"},$loop_beg,$loop_end,"+");
941
942 $hash_comp{"loop_beg"}=$loop_beg;
943 $hash_comp{"loop_end"}=$loop_end;
944 $hash_comp{"loop_seq"}=$loop_seq;
945 $hash_comp{"loop_struct"}=$loop_struct;
946
947 return;
948 }
949
950
951 sub fill_lower_flanks{
952
953 #fills specifics on the lower flanks and unpaired strands into the 'comp' hash
954
955 #unless both mature and star sequences are plausible, do not look for the flanks
956 unless($hash_comp{"mature_arm"} and $hash_comp{"star_arm"}){return;}
957
958 my $flank_first_end;
959 my $flank_second_beg;
960
961 #defining the begin and end positions of the flanks from the mature and star positions
962 #excision depends on whether the mature or star sequence is 5' in the potenitial precursor ('first')
963 if($hash_comp{"mature_arm"} eq "first"){
964 $flank_first_end=$hash_comp{"mature_beg"}-1;
965 }else{
966 $flank_second_beg=$hash_comp{"mature_end"}+1;
967 }
968
969 if($hash_comp{"star_arm"} eq "first"){
970 $flank_first_end=$hash_comp{"star_beg"}-1;
971 }else{
972 $flank_second_beg=$hash_comp{"star_end"}+1;
973 }
974
975 #unless the positions are plausible, do not fill into hash
976 unless(test_flanks($flank_first_end,$flank_second_beg)){return;}
977
978 $hash_comp{"flank_first_end"}=$flank_first_end;
979 $hash_comp{"flank_second_beg"}=$flank_second_beg;
980 $hash_comp{"flank_first_seq"}=excise_seq($hash_comp{"pri_seq"},$hash_comp{"pri_beg"},$hash_comp{"flank_first_end"},"+");
981 $hash_comp{"flank_second_seq"}=excise_seq($hash_comp{"pri_seq"},$hash_comp{"flank_second_beg"},$hash_comp{"pri_end"},"+");
982 $hash_comp{"flank_first_struct"}=excise_struct($hash_comp{"pri_struct"},$hash_comp{"pri_beg"},$hash_comp{"flank_first_end"},"+");
983 $hash_comp{"flank_second_struct"}=excise_struct($hash_comp{"pri_struct"},$hash_comp{"flank_second_beg"},$hash_comp{"pri_end"},"+");
984
985 if($options{z}){
986 fill_stems_drosha();
987 }
988
989 return;
990 }
991
992
993 sub fill_stems_drosha{
994
995 #scores the number of base pairings formed by the first ten nt of the lower stems
996 #in general, the more stems, the higher the score contribution
997 #warning: this options has not been thoroughly tested
998
999 my $flank_first_struct=$hash_comp{"flank_first_struct"};
1000 my $flank_second_struct=$hash_comp{"flank_second_struct"};
1001
1002 my $stem_first=substr($flank_first_struct,-10);
1003 my $stem_second=substr($flank_second_struct,0,10);
1004
1005 my $stem_bp_first=0;
1006 my $stem_bp_second=0;
1007
1008 #find base pairings by simple pattern matching
1009 while($stem_first=~/\(/g){
1010 $stem_bp_first++;
1011 }
1012
1013 while($stem_second=~/\)/g){
1014 $stem_bp_second++;
1015 }
1016
1017 my $stem_bp=min2($stem_bp_first,$stem_bp_second);
1018
1019 $hash_comp{"stem_first"}=$stem_first;
1020 $hash_comp{"stem_second"}=$stem_second;
1021 $hash_comp{"stem_bp_first"}=$stem_bp_first;
1022 $hash_comp{"stem_bp_second"}=$stem_bp_second;
1023 $hash_comp{"stem_bp"}=$stem_bp;
1024
1025 return;
1026 }
1027
1028
1029
1030
1031 sub arm_mature{
1032
1033 #tests whether the mature sequence is in the 5' ('first') or 3' ('second') arm of the potential precursor
1034
1035 my ($beg,$end,$strand)=@_;
1036
1037 #mature and star sequences should alway be on plus strand
1038 if($strand eq "-"){return 0;}
1039
1040 #there should be no bifurcations and minimum one base pairing
1041 my $struct=excise_seq($hash_comp{"pri_struct"},$beg,$end,$strand);
1042 if(defined($struct) and $struct=~/^(\(|\.)+$/ and $struct=~/\(/){
1043 return "first";
1044 }elsif(defined($struct) and $struct=~/^(\)|\.)+$/ and $struct=~/\)/){
1045 return "second";
1046 }
1047 return 0;
1048 }
1049
1050
1051 sub arm_star{
1052
1053 #tests whether the star sequence is in the 5' ('first') or 3' ('second') arm of the potential precursor
1054
1055 my ($beg,$end)=@_;
1056
1057 #unless the begin and end positions are plausible, test negative
1058 unless($beg>0 and $beg<=$hash_comp{"pri_end"} and $end>0 and $end<=$hash_comp{"pri_end"} and $beg<=$end){return 0;}
1059
1060 #no overlap between the mature and the star sequence
1061 if($hash_comp{"mature_arm"} eq "first"){
1062 ($hash_comp{"mature_end"}<$beg) or return 0;
1063 }elsif($hash_comp{"mature_arm"} eq "second"){
1064 ($end<$hash_comp{"mature_beg"}) or return 0;
1065 }
1066
1067 #there should be no bifurcations and minimum one base pairing
1068 my $struct=excise_seq($hash_comp{"pri_struct"},$beg,$end,"+");
1069 if($struct=~/^(\(|\.)+$/ and $struct=~/\(/){
1070 return "first";
1071 }elsif($struct=~/^(\)|\.)+$/ and $struct=~/\)/){
1072 return "second";
1073 }
1074 return 0;
1075 }
1076
1077
1078 sub test_loop{
1079
1080 #tests the loop positions
1081
1082 my ($beg,$end)=@_;
1083
1084 #unless the begin and end positions are plausible, test negative
1085 unless($beg>0 and $beg<=$hash_comp{"pri_end"} and $end>0 and $end<=$hash_comp{"pri_end"} and $beg<=$end){return 0;}
1086
1087 return 1;
1088 }
1089
1090
1091 sub test_flanks{
1092
1093 #tests the positions of the lower flanks
1094
1095 my ($beg,$end)=@_;
1096
1097 #unless the begin and end positions are plausible, test negative
1098 unless($beg>0 and $beg<=$hash_comp{"pri_end"} and $end>0 and $end<=$hash_comp{"pri_end"} and $beg<=$end){return 0;}
1099
1100 return 1;
1101 }
1102
1103
1104 sub comp{
1105
1106 #subroutine to retrive from the 'comp' hash
1107
1108 my $type=shift;
1109 my $component=$hash_comp{$type};
1110 return $component;
1111 }
1112
1113
1114 sub find_strand_query{
1115
1116 #subroutine to find the strand for a given query
1117
1118 my $query=shift;
1119 my $strand=$hash_query{$query}{"strand"};
1120 return $strand;
1121 }
1122
1123
1124 sub find_positions_query{
1125
1126 #subroutine to find the begin and end positions for a given query
1127
1128 my $query=shift;
1129 my $beg=$hash_query{$query}{"subject_beg"};
1130 my $end=$hash_query{$query}{"subject_end"};
1131 return ($beg,$end);
1132 }
1133
1134
1135
1136 sub find_mature_query{
1137
1138 #finds the query with the highest frequency of reads and returns it
1139 #is used to determine the positions of the potential mature sequence
1140
1141 my @queries=sort {$hash_query{$b}{"freq"} <=> $hash_query{$a}{"freq"}} keys %hash_query;
1142 my $mature_query=$queries[0];
1143 return $mature_query;
1144 }
1145
1146
1147
1148
1149 sub reset_variables{
1150
1151 #resets the hashes for the next potential precursor
1152
1153 # %hash_query=();
1154 # %hash_comp=();
1155 # %hash_bp=();
1156 foreach my $key (keys %hash_query) {delete($hash_query{$key});}
1157 foreach my $key (keys %hash_comp) {delete($hash_comp{$key});}
1158 foreach my $key (keys %hash_bp) {delete($hash_bp{$key});}
1159
1160 # $message_filter=();
1161 # $message_score=();
1162 # $lines=();
1163 undef($message_filter);
1164 undef($message_score);
1165 undef($lines);
1166 return;
1167 }
1168
1169
1170
1171 sub excise_seq{
1172
1173 #excise sub sequence from the potential precursor
1174
1175 my($seq,$beg,$end,$strand)=@_;
1176
1177 #begin can be equal to end if only one nucleotide is excised
1178 unless($beg<=$end){print STDERR "begin can not be smaller than end for $subject_old\n";exit;}
1179
1180 #rarely, permuted combinations of signature and structure cause out of bound excision errors.
1181 #this happens once appr. every two thousand combinations
1182 unless($beg<=length($seq)){$out_of_bound++;return 0;}
1183
1184 #if on the minus strand, the reverse complement should be excised
1185 if($strand eq "-"){$seq=revcom($seq);}
1186
1187 #the blast parsed format is 1-indexed, substr is 0-indexed
1188 my $sub_seq=substr($seq,$beg-1,$end-$beg+1);
1189
1190 return $sub_seq;
1191
1192 }
1193
1194 sub excise_struct{
1195
1196 #excise sub structure
1197
1198 my($struct,$beg,$end,$strand)=@_;
1199 my $lng=length $struct;
1200
1201 #begin can be equal to end if only one nucleotide is excised
1202 unless($beg<=$end){print STDERR "begin can not be smaller than end for $subject_old\n";exit;}
1203
1204 #rarely, permuted combinations of signature and structure cause out of bound excision errors.
1205 #this happens once appr. every two thousand combinations
1206 unless($beg<=length($struct)){return 0;}
1207
1208 #if excising relative to minus strand, positions are reversed
1209 if($strand eq "-"){($beg,$end)=rev_pos($beg,$end,$lng);}
1210
1211 #the blast parsed format is 1-indexed, substr is 0-indexed
1212 my $sub_struct=substr($struct,$beg-1,$end-$beg+1);
1213
1214 return $sub_struct;
1215 }
1216
1217
1218 sub create_hash_nuclei{
1219 #parses a fasta file with sequences of known miRNAs considered for conservation purposes
1220 #reads the nuclei into a hash
1221
1222 my ($file) = @_;
1223 my ($id, $desc, $sequence, $nucleus) = ();
1224
1225 open (FASTA, "<$file") or die "can not open $file\n";
1226 while (<FASTA>)
1227 {
1228 chomp;
1229 if (/^>(\S+)(.*)/)
1230 {
1231 $id = $1;
1232 $desc = $2;
1233 $sequence = "";
1234 $nucleus = "";
1235 while (<FASTA>){
1236 chomp;
1237 if (/^>(\S+)(.*)/){
1238 $nucleus = substr($sequence,1,$nucleus_lng);
1239 $nucleus =~ tr/[T]/[U]/;
1240 $hash_mirs{$nucleus} .="$id\t";
1241 $hash_nuclei{$nucleus} += 1;
1242
1243 $id = $1;
1244 $desc = $2;
1245 $sequence = "";
1246 $nucleus = "";
1247 next;
1248 }
1249 $sequence .= $_;
1250 }
1251 }
1252 }
1253 $nucleus = substr($sequence,1,$nucleus_lng);
1254 $nucleus =~ tr/[T]/[U]/;
1255 $hash_mirs{$nucleus} .="$id\t";
1256 $hash_nuclei{$nucleus} += 1;
1257 close FASTA;
1258 }
1259
1260
1261 sub parse_file_struct{
1262 #parses the output from RNAfoldand reads it into hashes
1263 my($file) = @_;
1264 my($id,$desc,$seq,$struct,$mfe) = ();
1265 open (FILE_STRUCT, "<$file") or die "can not open $file\n";
1266 while (<FILE_STRUCT>){
1267 chomp;
1268 if (/^>(\S+)\s*(.*)/){
1269 $id= $1;
1270 $desc= $2;
1271 $seq= "";
1272 $struct= "";
1273 $mfe= "";
1274 while (<FILE_STRUCT>){
1275 chomp;
1276 if (/^>(\S+)\s*(.*)/){
1277 $hash_desc{$id} = $desc;
1278 $hash_seq{$id} = $seq;
1279 $hash_struct{$id} = $struct;
1280 $hash_mfe{$id} = $mfe;
1281 $id = $1;
1282 $desc = $2;
1283 $seq = "";
1284 $struct = "";
1285 $mfe = "";
1286 next;
1287 }
1288 if(/^\w/){
1289 tr/uU/tT/;
1290 $seq .= $_;
1291 next;
1292 }
1293 if(/((\.|\(|\))+)/){$struct .=$1;}
1294 if(/\((\s*-\d+\.\d+)\)/){$mfe = $1;}
1295 }
1296 }
1297 }
1298 $hash_desc{$id} = $desc;
1299 $hash_seq{$id} = $seq;
1300 $hash_struct{$id} = $struct;
1301 $hash_mfe{$id} = $mfe;
1302 close FILE_STRUCT;
1303 return;
1304 }
1305
1306
1307 sub score_s{
1308
1309 #this score message is appended to the end of the string of score messages outputted for the potential precursor
1310
1311 my $message=shift;
1312 $message_score.=$message."\n";;
1313 return;
1314 }
1315
1316
1317
1318 sub score_p{
1319
1320 #this score message is appended to the beginning of the string of score messages outputted for the potential precursor
1321
1322 my $message=shift;
1323 $message_score=$message."\n".$message_score;
1324 return;
1325 }
1326
1327
1328
1329 sub filter_s{
1330
1331 #this filtering message is appended to the end of the string of filtering messages outputted for the potential precursor
1332
1333 my $message=shift;
1334 $message_filter.=$message."\n";
1335 return;
1336 }
1337
1338
1339 sub filter_p{
1340
1341 #this filtering message is appended to the beginning of the string of filtering messages outputted for the potential precursor
1342
1343 my $message=shift;
1344 if(defined $message_filter){$message_filter=$message."\n".$message_filter;}
1345 else{$message_filter=$message."\n";}
1346 return;
1347 }
1348
1349
1350 sub find_freq{
1351
1352 #finds the frequency of a given read query from its id.
1353
1354 my($query)=@_;
1355
1356 if($query=~/x(\d+)/i){
1357 my $freq=$1;
1358 return $freq;
1359 }else{
1360 #print STDERR "Problem with read format\n";
1361 return 0;
1362 }
1363 }
1364
1365
1366 sub print_hash_comp{
1367
1368 #prints the 'comp' hash
1369
1370 my @keys=sort keys %hash_comp;
1371 foreach my $key(@keys){
1372 my $value=$hash_comp{$key};
1373 print "$key \t$value\n";
1374 }
1375 }
1376
1377
1378
1379 sub print_hash_bp{
1380
1381 #prints the 'bp' hash
1382
1383 my @keys=sort {$a<=>$b} keys %hash_bp;
1384 foreach my $key(@keys){
1385 my $value=$hash_bp{$key};
1386 print "$key\t$value\n";
1387 }
1388 print "\n";
1389 }
1390
1391
1392
1393 sub find_strand{
1394
1395 #A subroutine to find the strand, parsing different blast formats
1396
1397 my($other)=@_;
1398
1399 my $strand="+";
1400
1401 if($other=~/-/){
1402 $strand="-";
1403 }
1404
1405 if($other=~/minus/i){
1406 $strand="-";
1407 }
1408 return($strand);
1409 }
1410
1411
1412 sub contained{
1413
1414 #Is the stretch defined by the first positions contained in the stretch defined by the second?
1415
1416 my($beg1,$end1,$beg2,$end2)=@_;
1417
1418 testbeginend($beg1,$end1,$beg2,$end2);
1419
1420 if($beg2<=$beg1 and $end1<=$end2){
1421 return 1;
1422 }else{
1423 return 0;
1424 }
1425 }
1426
1427
1428 sub testbeginend{
1429
1430 #Are the beginposition numerically smaller than the endposition for each pair?
1431
1432 my($begin1,$end1,$begin2,$end2)=@_;
1433
1434 unless($begin1<=$end1 and $begin2<=$end2){
1435 print STDERR "beg can not be larger than end for $subject_old\n";
1436 exit;
1437 }
1438 }
1439
1440
1441 sub rev_pos{
1442
1443 # The blast_parsed format always uses positions that are relative to the 5' of the given strand
1444 # This means that for a sequence of length n, the first nucleotide on the minus strand base pairs with
1445 # the n't nucleotide on the plus strand
1446
1447 # This subroutine reverses the begin and end positions of positions of the minus strand so that they
1448 # are relative to the 5' end of the plus strand
1449
1450 my($beg,$end,$lng)=@_;
1451
1452 my $new_end=$lng-$beg+1;
1453 my $new_beg=$lng-$end+1;
1454
1455 return($new_beg,$new_end);
1456 }
1457
1458 sub round {
1459
1460 #rounds to nearest integer
1461
1462 my($number) = shift;
1463 return int($number + .5);
1464
1465 }
1466
1467
1468 sub rev{
1469
1470 #reverses the order of nucleotides in a sequence
1471
1472 my($sequence)=@_;
1473
1474 my $rev=reverse $sequence;
1475
1476 return $rev;
1477 }
1478
1479 sub com{
1480
1481 #the complementary of a sequence
1482
1483 my($sequence)=@_;
1484
1485 $sequence=~tr/acgtuACGTU/TGCAATGCAA/;
1486
1487 return $sequence;
1488 }
1489
1490 sub revcom{
1491
1492 #reverse complement
1493
1494 my($sequence)=@_;
1495
1496 my $revcom=rev(com($sequence));
1497
1498 return $revcom;
1499 }
1500
1501
1502 sub max2 {
1503
1504 #max of two numbers
1505
1506 my($a, $b) = @_;
1507 return ($a>$b ? $a : $b);
1508 }
1509
1510 sub min2 {
1511
1512 #min of two numbers
1513
1514 my($a, $b) = @_;
1515 return ($a<$b ? $a : $b);
1516 }
1517
1518
1519
1520 sub score_freq{
1521
1522 # scores the count of reads that map to the potential precursor
1523 # Assumes geometric distribution as described in methods section of manuscript
1524
1525 my $freq=shift;
1526
1527 #parameters of known precursors and background hairpins
1528 my $parameter_test=0.999;
1529 my $parameter_control=0.6;
1530
1531 #log_odds calculated directly to avoid underflow
1532 my $intercept=log((1-$parameter_test)/(1-$parameter_control));
1533 my $slope=log($parameter_test/$parameter_control);
1534 my $log_odds=$slope*$freq+$intercept;
1535
1536 #if no strong evidence for 3' overhangs, limit the score contribution to 0
1537 unless($options{x} or $hash_comp{"star_read"}){$log_odds=min2($log_odds,0);}
1538
1539 return $log_odds;
1540 }
1541
1542
1543
1544 ##sub score_mfe{
1545
1546 # scores the minimum free energy in kCal/mol of the potential precursor
1547 # Assumes Gumbel distribution as described in methods section of manuscript
1548
1549 ## my $mfe=shift;
1550
1551 #numerical value, minimum 1
1552 ## my $mfe_adj=max2(1,-$mfe);
1553
1554 #parameters of known precursors and background hairpins, scale and location
1555 ## my $prob_test=prob_gumbel_discretized($mfe_adj,5.5,32);
1556 ## my $prob_background=prob_gumbel_discretized($mfe_adj,4.8,23);
1557
1558 ## my $odds=$prob_test/$prob_background;
1559 ## my $log_odds=log($odds);
1560
1561 ## return $log_odds;
1562 ##}
1563
1564 sub score_mfe{
1565 # use bignum;
1566
1567 # scores the minimum free energy in kCal/mol of the potential precursor
1568 # Assumes Gumbel distribution as described in methods section of manuscript
1569
1570 my ($mfe,$mlng)=@_;
1571
1572 #numerical value, minimum 1
1573 my $mfe_adj=max2(1,-$mfe);
1574 my $mfe_adj1=$mfe/$mlng;
1575 #parameters of known precursors and background hairpins, scale and location
1576 my $a=1.339e-12;my $b=2.778e-13;my $c=45.834;
1577 my $ev=$e**($mfe_adj1*$c);
1578 #print STDERR "\n***",$ev,"**\t",$ev+$b,"\t";
1579 my $log_odds=($a/($b+$ev));
1580
1581
1582 my $prob_test=prob_gumbel_discretized($mfe_adj,5.5,32);
1583 my $prob_background=prob_gumbel_discretized($mfe_adj,4.8,23);
1584
1585 my $odds=$prob_test/$prob_background;
1586 my $log_odds_2=log($odds);
1587 #print STDERR "log_odds :",$log_odds,"\t",$log_odds_2,"\n";
1588 return $log_odds;
1589 }
1590
1591
1592
1593 sub prob_gumbel_discretized{
1594
1595 # discretized Gumbel distribution, probabilities within windows of 1 kCal/mol
1596 # uses the subroutine that calculates the cdf to find the probabilities
1597
1598 my ($var,$scale,$location)=@_;
1599
1600 my $bound_lower=$var-0.5;
1601 my $bound_upper=$var+0.5;
1602
1603 my $cdf_lower=cdf_gumbel($bound_lower,$scale,$location);
1604 my $cdf_upper=cdf_gumbel($bound_upper,$scale,$location);
1605
1606 my $prob=$cdf_upper-$cdf_lower;
1607
1608 return $prob;
1609 }
1610
1611
1612 sub cdf_gumbel{
1613
1614 # calculates the cumulative distribution function of the Gumbel distribution
1615
1616 my ($var,$scale,$location)=@_;
1617
1618 my $cdf=$e**(-($e**(-($var-$location)/$scale)));
1619
1620 return $cdf;
1621 }
1622