Programa



#!/usr/bin/perl -w

use strict;

#####################################################
####################             ####################
#################### INFORMACIO  ####################
####################             ####################
#####################################################
#
### NOM DEL PROGRAMA: EXON-finding
#
### AUTORES: Alba Llop, Marta Mancebo i Mireya Plass
#
### DATA: 18 -03 - 2004
#
### 4t BIOLOGIA HUMANA, Universitat Pompeu Fabra
#
#####################################################


#####################################################
####################             ####################
####################  PRINCIPAL  ####################
####################             ####################
#####################################################



##### DECLARACIO DE VARIABLES GLOBALS #############################################################################################      


my $id;             # emmagatzema el nom de la nostra sequencia.
my $sequencia;      # guarda la sequencia en format fasta en un string.
my %donors;         # emmagatzema la matriu dels donors en un hash.
my $pos1;           # emmagatzema el numero de posicions que s'examinen amb la matriu de donors.
my %acceptors;      # emmagatzema la matriu dels acceptors en un hash.
my $pos2;           # emmagatzema el numero de posicions que s'examinen amb la matriu d'acceptors.
my %codons;         # emmagatzema la matriu dels codons en un hash.
my $llindar;        # emmagatzema el llindar que utilitzarem per determinar si un senyal (acceptor o donor) es bo o no.
my $global;         # emmagatzema la puntuacio minima que volem que tingui un exo predit.
my $prediccio;      # guarda el nombre d'exons predits que es vol obtenir per cada senyal (emparellaments).
my $tamany;         # guarda la longitud minima que volem que tingui un exo predit.
my %ORF;            # emmagatzema per a cadascuna de les pautes de lectura els diferents ORFs trobats.
my @exons;          # guarda un array d'arrays on per cada exo guardem la posicio d'inici, la de final i la puntuacio i el frame.
my @first;          # guarda els resultats amb els diferents exons First predits.
my @internal;       # guarda els resultats amb els diferents exons Internal predits.
my @terminal;       # guarda els resultats amb els diferents exons Terminal predits.
my @final;          # guarda els resultats finals amb els diferents gens predits (inici, final, puntuació i frame).
my @resultats;      # variable de treball. Guarda resultats de funcions que son arrays.
my $resultats;      # variable de treball. Guarda resultats de funcions que son scalars.

###################################################################################################################################      



##### OBRIM ELS ARGUMENTS QUE PASSEM A LA FUNCIO I ELS GUARDEM EN DIFERENTS VARIABLES #############################################

if (scalar(@ARGV) < 8) {
  print "entrada de dades incorrecta.\nPosar fitxer sequencia fasta, fitxer matriu donors,\nfitxer matriu acceptors, fitxer matriu codons,\n llindar, nombre de prediccions,\n puntuacio global minima d'exo longitud minima d'exo\n";
  exit(1);
}

# assignem en diferents variables els diferents arguments introduits per l'execucio del programa.
 
my $seq   = $ARGV[0];              # guarda el nom del fitxer de la sequencia que hem d'obrir.   
my $donor = $ARGV[1];              # guarda el nom del fitxer de la matriu de donors.
my $accep = $ARGV[2];              # guarda el nom del fitxer de la matriu dels acceptors.
my $codon = $ARGV[3];              # guarda el nom del fitxer de la matriu dels codons.

$llindar  = $ARGV[4];               
$prediccio = $ARGV[5];             
$global = $ARGV[6];             
$tamany = $ARGV[7];                

################################################################################################################################### 



##### CRIDEM LA FUNCIO form #######################################################################################################

@resultats = &form;                        # assignem els resultats de la funcio form a la variable @resultats.          

$id = ${$resultats[0]};                    # desreferenciem l'identificador i l'emmagatzemem en una variable singular.
$sequencia = ${$resultats[1]};             # desreferenciem la sequencia i l'emmagatzemem en un string.

###################################################################################################################################



##### CRIDEM LA FUNCIO mat ########################################################################################################   

@resultats = mat($donor);                  # assignem els resultats de la funcio mat sobre l'arxiu que conte 
                                           # la matriu de pesos dels donors a la variable @resultats.   
$pos1 = ${$resultats[0]};                  # desreferenciem i assignem l'element que hi ha a la 1a posicio de @resultats.   
%donors = %{$resultats[1]};                # desreferenciem i assignem l'element que hi ha a la 2a posicio de @resultats.   
                                            

@resultats = mat($accep);                  # assignem els resultats de la funcio mat sobre l'arxiu que conte 
                                           # la matriu de pesos dels acceptors a la variable @resultats
$pos2 = ${$resultats[0]};                  # desreferenciem i assignem l'element que hi ha a la 1a posicio de @resultats.
%acceptors = %{$resultats[1]};             # desreferenciem i assignem l'element que hi ha a la 2a posicio de @resultats
                                           
###################################################################################################################################



##### CRIDEM LA FUNCIO ORF ########################################################################################################

# cridem la funcio ORF i guardem el hash que conte els diferents ORFs trobats 
# en una de les claus que identifica el frame en que el trobem.

$resultats = ORF($sequencia,0);            # assignem la referencia dels resultats de la funcio ORF. 
%{$ORF{0}} = %{$resultats};                # desreferenciem i asignem els resultats sl hash que hi ha dins de $ORF{0}. 
                                    

$resultats = ORF($sequencia,1);            # assignem la referencia dels resultats de la funcio ORF. 
%{$ORF{1}} = %{$resultats};                # desreferenciem i asignem els resultats sl hash que hi ha dins de $ORF{1}. 


$resultats = ORF($sequencia,2);            # assignem la referencia dels resultats de la funcio ORF.                
%{$ORF{2}} = %{$resultats};                # desreferenciem i asignem els resultats sl hash que hi ha dins de $ORF{2}. 

###################################################################################################################################



##### CRIDEM LA FUNCIO pes ########################################################################################################      

# guardem per cada frame (key 0, 1 o 2) un hash que indexa els 
# diferents ORFs i que conte on array on s'emmagatzemen l'inici, 
# el final, un hash amb els donors i un hash amb els acceptors.


$resultats = pes($sequencia, \%{$ORF{0}}, \%donors, $pos1, $llindar, 2);      # assignem la referencia als resultats de la funcio pes.
%{$ORF{0}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{0}.
                                             
$resultats = pes($sequencia, \%{$ORF{1}}, \%donors, $pos1, $llindar, 2);      # assignem la referencia als resultats de la funcio pes.
%{$ORF{1}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{1}.


$resultats = pes($sequencia, \%{$ORF{2}}, \%donors, $pos1, $llindar, 2);      # assignem la referencia als resultats de la funcio pes.
%{$ORF{2}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{2}.



$resultats = pes($sequencia, \%{$ORF{0}}, \%acceptors, $pos2, $llindar, 3);   # assignem la referencia als resultats de la funcio pes.   
%{$ORF{0}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{0}.
                                                                     
                              
$resultats = pes($sequencia, \%{$ORF{1}}, \%acceptors, $pos2, $llindar, 3);   # assignem la referencia als resultats de la funcio pes.
%{$ORF{1}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{1}.


$resultats = pes($sequencia, \%{$ORF{2}}, \%acceptors, $pos2, $llindar, 3);   # assignem la referencia als resultats de la funcio pes.
%{$ORF{2}} = %{$resultats};                                                   # desreferciem i guardem els resultats que hi ha a $ORF{2}.

##################################################################################################################################       



##### CRIDEM LA FUNCIO def #######################################################################################################

@resultats = def(\%{$ORF{0}},$prediccio, $sequencia, 0, $tamany);         # assignem les referencies als resultats de la funcio def.
                                                                    
@{$exons[0][0]} = @{$resultats[0]};                                       # guardem els arrays dels exons First del frame 0.
@{$exons[0][1]} = @{$resultats[1]};                                       # guardem els arrays dels exons Internal del frame 0.
@{$exons[0][2]} = @{$resultats[2]};                                       # guardem els arrays dels exons Terminal del frame 0.



@resultats = def(\%{$ORF{1}},$prediccio, $sequencia, 1, $tamany);         # assignem les referencies als resultats de la funcio def.

@{$exons[1][0]} = @{$resultats[0]};                                       # guardem els arrays dels exons First del frame 1.
@{$exons[1][1]} = @{$resultats[1]};                                       # guardem els arrays dels exons Internal del frame 1. 
@{$exons[1][2]} = @{$resultats[2]};                                       # guardem els arrays dels exons Terminal del frame 1.


@resultats = def(\%{$ORF{2}},$prediccio, $sequencia, 2, $tamany);         # assignem les referencia als resultats de la funcio def.
           
@{$exons[2][0]} = @{$resultats[0]};                                       # guardem els arrays dels exons First del frame 2.
@{$exons[2][1]} = @{$resultats[1]};                                       # guardem els arrays dels exons Internal del frame 2.
@{$exons[2][2]} = @{$resultats[2]};                                       # guardem els arrays dels exons Terminal del frame 2.

###################################################################################################################################      



##### CRIDEM LA FUNCIO bcod #######################################################################################################

$resultats = bcod($codon);                # guardem la referencia als resultats de la funcio bcod.
%codons = %{$resultats};                  # desreferenciem i guardem el hash dels codons a %codons.

###################################################################################################################################



##### CRIDEM LA FUNCIO biaix ######################################################################################################

@resultats = @{biaix($sequencia, \%codons, \@{$exons[0]}, $global)};    # guardem les referencies dels resultats de la funcio biaix 

$first[@first] = $resultats[0];                                         # guardem els arrays dels exons First del exons del frame 0.
$internal[@internal] = $resultats[1];                                   # guardem els arrays dels exons Internal del frame 0.
$terminal[@terminal] = $resultats[2];                                   # guardem els arrays dels exons Terminal del frame 0;


@resultats = @{biaix($sequencia, \%codons, \@{$exons[1]}, $global)};    # guardem les referencies dels resultats de la funcio biaix 

$first[@first] = $resultats[0];                                         # guardem els arrays dels exons First del frame 1.
$internal[@internal] = $resultats[1];                                   # guardem els arrays dels exons Internal del frame 1
$terminal[@terminal] = $resultats[2];                                   # guardem els arrays dels exons Terminal del frame 1.


@resultats = @{biaix($sequencia, \%codons, \@{$exons[2]}, $global)};    # guardem les referencies dels resultats de la funcio biaix 

$first[@first] = $resultats[0];                                         # guardem els arrays dels exons First del frame 2.
$internal[@internal] = $resultats[1];                                   # guardem els arrays dels exons Internal del frame 2.
$terminal[@terminal] = $resultats[2];                                   # guardem els arrays dels exons Terminal del frame 2.

###################################################################################################################################      



##### CRIDEM LA FUNCIO junta ######################################################################################################

@{$final[0]} = @{junta(\@first)};                        # assignem l'array de resultats de la funcio junta a la posicio 0 de @final.

@{$final[1]} = @{junta(\@internal)};                     # assignem l'array de resultats de la funcio junta a la posicio 1 de @final.

@{$final[2]} = @{junta(\@terminal)};                     # assignem l'array de resultats de la funcio junta a la posicio 2 de @final.

@final = (@{$final[0]},@{$final[1]},@{$final[2]});       # guarda totes les prediccions juntes al mateix vector

###################################################################################################################################	 



##### ORDENEM ELS ARRAYS QUE HI HA AL HASH %final #################################################################################

@final = sort{${$a}[0] <=> ${$b}[0] || ${$a}[1] <=> ${$b}[1]}(@final);             # ordenem els exons  per la posicio d'inici
                                                                                   # i la posicio de final (1a i 2a columna).


###################################################################################################################################      



##### FEM EL PRINT DE LES DADES EN FORMAT gff #####################################################################################

$id =~ s/>//;                                       # eliminem el simbol ">" de l'identificador de la sequencia.



##### RESSEGUIM L'ARRAY QUE GUARDA ELS EXONS #####

    my $c = 0;                                      # inicialitzem el comptador de la iteracio a zero.

         
    while ($c < @final){                            # mentre hi hagi prediccions d'exons.
	my @print = @{$final[$c]};                  # assignem la informacio de l'exo al vector @print a cada volta.
	

	    print $id, "\t", "EXON-finding\t", $print[4],"\t",  $print[0], "\t", $print[1], "\t"; 
	    printf "%.3f",$print[2];
	    print "\t","+\t",$print [3],"\n";


	$c++;                                       # incrementem el comptador de la iteracio.
    }


###################################################################################################################################      
###################################################################################################################################


####################################################
####################            ####################
####################  FUNCIONS  ####################
####################            ####################
####################################################




# FUNCIO 1(form): obre l'argument que conte tot l'arxiu i retorna la referencia de l'identificador i 
# la referencia de la sequencia directa en majuscules guardada en una string
#
# Parametres requerits: cap (la funcio obre directament el filehandle).


sub form {
    
    my $s = "";       # guarda la sequencia en una variable singular (tota seguida) i la inicialitzem amb la paraula buida
    my $id;           # guarda l'identificador de la sequencia
    my @d;            # vector que guarda la seq directa
    my $c = 0;        # comptador de la iteracio


    if (!open(SEQ,"< $seq")) {                                            # obrim la sequencia emmagatzemada a l'argument
	print " NO S'HA POGUT OBRIR EL FITXER DE LA SEQUENCIA!!!\n";      # i, en cas que no es pugui obrir,fem saltar el 
	exit(1);                                                          # programa i que aparegui un missatge d'error
    }
                        
	  

    my $tmp = ;                        # llegim la primera linia del format FASTA (l'identificador) i la guardem 
    @d  = ;                            # llegim les linies de la sequencia, cada linia es una posicio de l'array

    chomp $tmp;                             # treiem el canvi de linia que hi ha al final de l'identificador
    $tmp=~/([\w>]+)/;                       # encaixem l'identificador amb l'expressio regular i
    $id = $1;                               # el guardem a una variable singular (per evitar simbols estranys)
    
    ##### GUARDEM LA SEQUENCIA EN UN STRING #####

    while ($c < scalar(@d)) {               # llegim les linies de la sequencia i les guardem totes
	chomp($d[$c]);                      # concatenades en una variable singular
        $d[$c]=~/([acgtACGT]+)/;            # eliminem qualsevol caracter que no sigui una base
	$s = $s . $1;                       
	$c = $c + 1;
    }
    
    
    $s = "\U$s";                            # passem totes les bases a majuscules
   
    close(SEQ);                             # tanquem el filehandle

    return (\$id, \$s);                     # retornem en un vector la referencia de l'identificador i el vector en un string
}
   



# FUNCIO 2(mat): obre un argument que conte una matriu de pesos, l'emmagatzema en un hash
# i retorna la referencia al hash i el nombre de posicions que conte.
#
# Parametres requerits: la variable que emmagatzema el nom del fitxer de matriu de pesos que hem d'obrir.


sub mat {
 
    my $ent= $_[0];              # variable que emmagatzema l'arxiu que hem d'obrir
    my %mat;                     # hash on enregistrarem la matriu de pesos
    my @n;                       # vector on enregistrarem els nucleotids
    my $llegint=0;               # variable indicadora de quan llegim la matriu
    my $posicio=0;               # posicio de la matriu que s'esta llegint
    my $i=0;                     # comptador de la iteracio.

  
    if (!open(MAT,"< $ent")) {                                              # obrim la matriu de pesos emmagatzemada a l'argument
	print " NO S'HA POGUT OBRIR EL FITXER DE LA MATRIU!!!\n";           # i, en cas que no es pugui obrir, fem saltar el 
	exit(1);                                                            # programa i que aparegui un missatge d'error
    }
     
   
    ##### INTRODUIM LA MATRIU DE PESOS EN UN HASH ##### 

    while () {                                                         # mentre llegim el fitxer de la matriu, si $llegint val 0 
	if ($llegint == 0 && m/\AP0\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)/) {     # i la linia que llegim comença amb P0
	    $n[0] = $1;                                                     # i encaixa amb l'expressio regular li direm que 
	    $n[1] = $2;                                                     # emmagatzemi els elements que es troben en les diferents
	    $n[2] = $3;                                                     # columnes ($1, $2, etc) en les diferents posicions 
	    $n[3] = $4;                                                     # del vector @n
	    $llegint = 1;                                                   # donem el valor de 1 a $llegint (es un flag)
	}

	
	if ($llegint == 1 &&                                                      # si $llegint val 1 i encaixa la linia (des del
	    m/\A\d+\s+([\-\d\.]+)\s+([\-\d\.]+)\s+([\-\d\.]+)\s+([\-\d\.]+)/) {   # principi) amb l'expressio regular, emmagatzemem
	    $mat{$n[0]}[$posicio] = $1;                                           # en un hash els elements continguts en les     
	    $mat{$n[1]}[$posicio] = $2;                                           # diferents columnes, cada columna de les quals  
	    $mat{$n[2]}[$posicio] = $3;                                           # te una clau determinada, emmagatzemada al vector 
	    $mat{$n[3]}[$posicio] = $4;                                           # @n
	    $posicio = $posicio + 1;                                              # cada cop que llegim una linia sumem +1 a $posicio
	}
	
	
	if ($llegint == 1 && m/\AXX\s+(\d+)/) {                             # si el flag val 1 i encaixa amb l'expressio regular
	    $mat{P} = $1;                                                   # donada, guardem el valor que hi ha a la primera 
	    $llegint = 0;                                                   # columna en la clau P del hash
	}                                                                   # posem el flag a 0
	
    }

    close(MAT);

    return (\$posicio , \%mat);    # retornem el nombre d'elements que te la matriu i la matriu emmagatzemada en un hash
}




# FUNCIO 3(ORF): donada una sequencia en format fasta emmagatzemada en un vector, examina aquesta 
# sequencia per a una determinada pauta de lectura en blocs de longitud determinada i retorna un 
# hash on cada key es un array on s'emmagatzemen les posicions d'inici i de final d'un ORF 
# (determinem un ORF com la sequencia que esta definida entre dos codons de STOP i, en el seu 
# defecte, entre l'inici i el primer STOP o entre l'ultim STOP i el final de la sequencia).
#
# Parametres requerits: la sequencia en un string, la pauta de lectura.


sub ORF {

    my $seq = $_[0];             # variable que emmagatzema la sequencia tota seguida         
    my $i = $_[1];               # inicialitzem el comptador de la iteracio amb el valor de la pauta de lectura que farem servir (0,1,2)
    my $pos = 3;                 # emmagatzema el valor de la longitud de la finestra que mirem (en aquest cas 3)
    my $s;                       # emmagatzema la sequencia tota seguida
    my @stop;                    # guarda la posicio de l'ultim nucleotid de codo de stop trobat
    my $finestra;                # guarda el tros de sequencia que volem analitzar en la iteracio
       
    $stop[@stop] = $i;           # inicialitzem el vector que ens guarda la posicio dels codons de stop
                                 # amb el valor de la posicio per on començarem a llegir la sequencia
                                 # (determina l'inici del primer ORF)  



   ##### BUSQUEM CODONS D'STOP ##### 

   while ($i <= length ($seq) - $pos){
	   	
	$finestra = substr ($seq, $i, $pos);     # definim el tros de sequencia que explorem cada cop
	

	if ($finestra =~m(TAA|TAG|TGA)){         # si fem un match d'un dels diferents codons de stop guardem
	    $stop[@stop] = $i + 2;               # la ultima posicio del codo en un vector
	}

	$i = $i + 3;                             # augmentem tres el comptador (cada codo te tres elements)
    }

    $stop[@stop] = length ($seq);                # assignem a l'ultim element del vector l'ultima posicio de la sequencia

                  
    my %results;                                 # definim un hash en que emmagatzemarem els diferents ORF                 
    my $count=0;                                 # definim un comptador que sera les keys del hash
    my @c;                                       # definim el vector que emmagatzema els parells inici-final de ORF


    $i = 0;                                      # posem el comptador de la iteracio a 0


    ##### DEFINIM ORFs #####

    while ($i < (@stop - 1)){                    # fem un hash on cada key tindra un parell inici i 
	
	if ($i == 0){                            # final de ORF
	    $results{$count}[0] = $stop[$i];     # resseguim el vector amb els codons de STOP,
 	 }                                       # la posició d'inici
	                                         # i la ultima posicio del vector
	                                         # guardem les diferents parelles dins d'un array 
	else{                                  
	    $results{$count}[0] = $stop[$i]+1;
	}                                      
	
 
	$results{$count}[1] = $stop[$i+1];       # introduim com a ultim element del vector la ultima 
	                                         # posicio de la sequencia
	$i++;                                    # incrementem el comptador del while
	$count++;                                # incrementem el comptador de les keys
	    
    }
     
   
    return \%results;                            # retornem el hash amb els ORFs trobats
}




# FUNCIO 4(pes): fem una funcio que donada una matriu de pesos puntui tots els 
# possibles senyals i retorni la posicio i la puntuacio de cada senyal.
# 
# Parametres requerits (en aquest ordre): la sequencia en un string ($sequencia), 
# el hash amb els ORFs, la matriu de pesos en un hash, el nombre de posicions 
# de la matriu de pesos ($pos1) i el llindar que s'ha d'aplicar.L'ultim parametre
# de la funció indica si estem buscant donors (2) o acceptors(3) i la posicio en 
# que s'ha d'emmagatzemar el hash resultant dins de l'array que esta emmagatzemat 
# en cadascuna de les keys del hash %ORFs.
 

sub pes{

    my $seq = $_[0];                           # emmagatzemem la sequencia tota junta en un string 
    my %orf = %{$_[1]};                        # guarda el hash amb els ORF trobats en un frame determinat
    my %matriu = %{$_[2]};                     # guarda el hash de la matriu de pesos que s'aplica    
    my $elements = $_[3];                      # guarda el nombre d'elements que te la matriu que s'examina
    my $llindar = $_[4];                       # guarda la puntuacio minima que volem que tingui un senyal
    my $tipus = $_[5];                         # emmagatzema un escalar que indica la posicio en que hem
                                               # d'emmagatzemar el hash resultant dins l'array de %ORF.
    my $finestra;                              # emmagatzema el tros de seq que hem de mirar a cada volta,
                                               # corresponent a un ORF
    my @keysorf = keys(%orf);                  # guardem les claus del hash %orf en un array.

    my $i = 0;                                 # inicialitzem el comptador de la iteracio a zero.
    my $h = 0;                                 # inicialitzem el comptador de les keys del hash a zero.
                      
    
    ########## RESSEGUEIX ORFs ##########

    while ($i < @keysorf){                       
	my %punt;                                   # guarda la posicio i la puntuació dels senyals trobats en un ORF
	my $count = 0;                              # comptador de les keys de %punt.

	my @examinar = @{$orf{$h}};                      
	my $inici = $examinar[0];                   # definim les posicions d'inici i de final de 
	my $final = $examinar[1];                   # cada ORF que volem analitzar.
    
	if ($i == 0){                               # si estem mirant el primer orf   

	    if ($tipus == 2){                       # estem buscant donors
		$inici = $examinar[0];              # l'inici  es igual a l'inici de l'ORF.           
		$final = $examinar[1] + 9;          # el final es el final de l'ORF mes el nombre de posicions que te la matriu de pesos.
	    }
    
	    if ($tipus == 3){                       # estem buscant acceptors
		$inici = $examinar[0];              # l'inici es igual a l'inici de l'ORF.           
		$final = $examinar[1];              # el final es igual al final de l'ORF. 
	    }
    

	}

	if ($i == (@keysorf - 1)){                  # si estem mirant l'ultim ORF

	    if ($tipus == 2){                       # estem buscant donors
		$inici = $examinar[0];              # l'inici de la sequencia es igual al'inici de l'ORF.           
		$final = $examinar[1];              # el final de la sequencia es igual al final de l'ORF.
	    }
	    
	    if ($tipus == 3){                       # estem buscant acceptors
		$inici = $examinar[0] - 21;         # l'inici es l'inici de l'ORF menys el nombre de posicions que te la matriu de pesos.
		$final = $examinar[1];              # el final de la sequencia es igual al final de l'ORF.
	    }
    
	}

	if ($i != 0  &&  $i != (@keysorf-1)) {      # si mirem qualsevol altre ORF

	    if ($tipus == 2){                       # estem buscant donors
		$inici = $examinar[0];              # l'inici de la sequencia es igual al'inici de l'ORF.             
		$final = $examinar[1] + 9;          # el final es el final de l'ORF mes el nombre de posicions que te la matriu de pesos.
	    }
	    
	    if ($tipus == 3){                      # estem buscant acceptors
		$inici = $examinar[0] - 21;        # l'inici es l'inici de l'ORF menys el nombre de posicions que te la matriu de pesos.
		$final = $examinar[1];             # el final de la sequencia es igual al final de l'ORF.
	    }
	    
	    
	}
	

	my $llarg = $final - $inici +1;                # definim la llargada de sequencia que utilitzarem per la cerca dels senyals 
	                                               # (variara en funcio de la senyal i l'ORF).
	$finestra = substr ($seq, $inici, $llarg);     # definim el tros de sequencia que explorem cada cop i
	my @vseq = split (//,$finestra);               # el guardem en un vector	
	

	########## BUSCA SENYALS DINS D'UN ORF ##########
	
	my $c = 0;                                                 # comptador del segon while a zero.
 
	while ( $c < @vseq - $elements){                           # analitzem la sequencia de cada ORF.
	    my $p = 0;                                             # emmagatzema la puntuacio de la sequencia. 
	    my $k = 0;                                             # inicialitzem  el comptador de posicio. 
	    

	    ###### SUMA LA PUNTUACIO DE LES POSICIONS ######

	    while ( $k < $elements){                               # analitzem un fragment de la sequencia de l'ORF de longitud. 
		$p = $p + $matriu{$vseq[$c + $k]}[$k];            # igual al nombre d'elements de la matriu de pesos i sumem. 
		$k = $k + 1;                                       # les posicions dels diferents elements.
	    }
      	   
	    if ($p > $llindar){                                    # si la puntuació es superior al llindar donat
		$punt{$count}[0] = $c + $matriu{P} + $inici;       # emmagatzemem la posicio i la puntuacio de la 
		$punt{$count}[1] = $p;                             # senyal en un array
		$count++;                                          # incrementem el comptador de les keys del segon hash
	    }

	    $c++;                                 # incrementem el comptador del segon while
 	}

	$orf{$h}[$tipus]= \%punt;                 # guarda a cada volta la referencia al hash amb els resultats trobats 
                                                  # en una posició determinada en funcio de les senyals que estem
                                                  # buscant dins el hash %orf

	$i++;                                     # incrementem el comptador del primer while
	$h++;                                     # incrementem el comptador de les keys del hash

    }
    

    return \%orf;          # retornem el hash amb les senyals trobades.                 
}




# FUNCIO 5 (def): donades una serie de senyals donadores d'splicing i acceptores d'splicing 
# fa els emparellaments de les diferents senyals acceptor-donor que es troben dins d'un mateix
# ORF (un acceptor amb tants donors com hi hagi al frame fins a un màxim determinat) i en suma 
# les puntuacions.
# També defineix per cada ORF els emparellaments ATG-acceptor (determinen un exó inicial)
# i donor-STOP (defineixen un exo terminal)
# Retorna un array d'arrays on a cada subarray es un exo, del que retornem la posicio d'inici, 
# la posicio de final i la seva puntuacio. 
# Introduim a la funcio el hash ORF d'un frame determinat que conte un hash amb els diferents 
# ORFs trobats al frame (que conte un array amb la posicio d'inici i final de l'ORF, un hash amb 
# les senyals dels donors i la seva puntuació i un hash amb les senyals dels acceptors i la seva 
# puntuació i el nombre d'emparellaments maxim que volem que faci. 
#
# Parametres requerits: hash amb les senyals predites per cada ORF, nombre de prediccions,
# la sequencia, el frame, la longitud minima que volem per la prediccio.


sub def{

    my %orf = %{$_[0]};           # guarda el hash amb les diferents senyals que hem de mirar. 
    my $max = $_[1];              # guarda el nombre maxim de prediccions que volem fer per cada senyal.
    my $seq = $_[2];              # guarda la sequencia global que analitzem en una string.
    my $frame = $_[3];            # guarda el frame en que estan les diferents senyals.
    my $tamany = $_[4];           # guarda la longitud minima que volem que tingui un exo predit.
    my $i = 0;                    # posem el comptador del primer while a 0. 
    my $results;                  # variable de treball que emmagatzema els resultats de les diferents funcions.
    my @first;                    # guarda a cada posicio la referencia al array amb la informacio d'un exo first.
    my @internal;                 # guarda a cada posicio la referencia al array amb la informacio d'un exo intern.
    my @terminal;                 # guarda a cada posicio la referencia al array amb la informacio d'un exo final.



  ##### RESSEGUEIX ORFs #####

    while ($i < keys(%orf)){                                  # mentre hi hagi ORFs     

	if ($orf{$i}[2]) {                                    # si hi ha almenys un donor
	    my %atg;                                          # guarda la primera posicio dels diferents ATG trobats
	    my @examinar = @{$orf{$i}};                      
  
	    my $inici = $examinar[0];                         # definim les posicions d'inici i de final de
	    my $final = $examinar[1];                         # cada ORF que volem analitzar
	   
	    my $llarg = $final - $inici + 1;                  # definim la llargada de sequencia inclosa a cada ORF
	    my $finestra = substr ($seq, $inici, $llarg);     # definim la sequencia que hi ha a cada ORF
	    
        
	##### BUSQUEM ATGs A CADA ORF #####

	    my $c = 0;                                        # posem el comptador del segon while a 0;
	    my $pos = 0;                                      # posem a cero el comptador de les keys de %atg.

	    while ($c <= length ($finestra) - 3){             # mirem la sequencia que hi ha dins l'ORF
		my $busca = substr ($finestra, $c, 3);        # per codons
	
		if ($busca =~ m/ATG/g) {                      # si trobem un ATG emmagatzemem la posicio de
		    $atg{$pos}[0] = $c + $inici +1;           # la primera base del codo la primera key del hash %atg.
	
		    $pos++;                                   # incrementem el comptador de les keys del hash %atg.
		}
		
		$c = $c + 3;                                  # augmentem tres el comptador (cada codo te tres elements)
	    }

	    
	    ##### EMPARELLEM ATG-DONOR #####

	    if (%atg  && %{$orf{$i}[2]}){                     # si s'ha trobat algun ATG en un frame que tambe te donors 
		
	       
		##### CRIDEM LA FUNCIO PAR #####

		$resultats = par(\%atg, \%{$orf{$i}[2]}, $max, $frame, $tamany);  # assignem la referencia a l'array d'arrays 
		                                                                     # que retorna la funcio par a $resultats.
		
		my $n = 0;                                         # inicialitzem a cero el comptador de la iteracio
		my @mirem = @{$resultats};                         # definim a cada volta l'array amb la informacio d'un exo que 
                                                                   # volem mirar.
		  
		while ($n < @mirem){                               # resseguim els elements (referencies d'arrays) de @resultats 
		    $first[@first] = $mirem[$n];                   # guardem la referencia als diferents exons trobats en els
		                                                   # diferents ORFs tots en el mateix array.
		    $n++;                                          # incrementem el comptador de la primera iteracio
		}
	    }
	

      
	##### EMAPARELLEM ACEPTOR-DONOR #####

    	if ($orf{$i}[2] && $orf{$i}[3]) {                          # si hi ha almenys un donor i un acceptor per un orf determinat
	    
		
	    ##### CRIDEM LA FUNCIO PAR #####
	  
	    $resultats = par (\%{$orf{$i}[3]}, \%{$orf{$i}[2]}, $max, $frame, $tamany);  # assignem la referencia a l'array d'arrays 
                                                                                         # que retorna la funcio par a $resultats.
	    	                                                        
       	    my $n = 0;                                                  # inicialitzem el comptador de la iteracio a cero.
	    my @mirem = @{$resultats};                                  # definim a cada volta l'array amb la informacio
	                                                                # d'un exo que volem mirar. 

	    while ($n < @mirem){                                        # resseguim els elements (referencies d'arrays) de @mirem 
		$internal[@internal] = $mirem[$n];                      # i assignem cadscuna de les subarrays en @internal (guardem
	                                                                # les prediccions dels diferents ORFs en el mateix array).
		$n++;                                                   # incrementem el comptador de la primera iteracio
	    }
	}
	
	
		
	##### EMPARELLEM DONOR-STOP #####

	if (%{$orf{$i}[3]}){                           # mentre hi hagi algun acceptor.

	    my %donors = %{$orf{$i}[3]};               # assignem a cada volta el hash que hi ha als donors.
	    my @n = keys (%{$orf{$i}[3]});             # guardem les keys del hash en un vector.
	    my $k = scalar (@n);                       # guardem el nombre d'elements que te el hash.
	    my $final = $orf {$i}[1];                  # guarda la posició de final d'exo (que es la de final d'Orf).    
	    my $c = 0;                                 # posem el comptador de la iteracio a cero.
	    my @resultats;                             # guarda la referencia als diferents exons trobats
	    
	    
		while ($c < $max){                                  # mentre no hagim fet el nombre d'emparellaments maxim
		    if ($donors{$k}){                               # si hi ha algun donor
			my @exo;                                    # definim el vector exo
		    
			$exo[0] = $donors{$k}[0];                   # guardem la posicio d'inici de l'exo a la primera posicio del vector
			$exo[1] = $final + 1;                       # guardem la posicio de final d'exo a la segona posicio del vector
			$exo[2] = $donors{$k}[1];                   # guardem la puntuacio de l'exo a la tercera posicio del vector
			$exo[3] = $frame;                           # guardem el frame en que s'ha predit l'exo.
			$exo[4] = "Terminal";                       # guardem l'identificador de tipus d'exo.
			if (($exo[1] - $exo[0] + 1) > $tamany){     # si la longitud de la prediccio es superior a la minima definida 
			    $terminal[@terminal] = \@exo;           # guardem la referencia a cadascun dels exons terminals predits en 
			}                                           # el tots els ORFs en el vector @terminal.
		    
		    }
		    $c++;                                           # incrementem el comptador del hash
		    $k--;	                                    # disminuim el comptador de les claus dels ORFs
		}
	}
	}

	$i++;                                                  #  incrementem el comptador de les keys del hash %orf

    }
 
    return (\@first, \@internal, \@terminal);                  # retornem les referencies als arrays pels diferents tipus d'exons predits
}
    




# FUNCIO 6 (par): realitza els diferents emparellaments que es puguin fer entre dos tipus de
# senyals definides cadascuna d'elles en un hash i retorna l'emparellament i la puntuació de
# l'exo trobat en un hash, on cadascuna de les claus conte un array amb l'inici, el final i 
# la puntuació del exo predit.
#
# Parametres requerits: hash amb les senyals d'inici d'exo, hash amb les senyals de final, nombre 
# de prediccions maximes per senyal, frame en que hem trobat l'exo i longitud minima de l'exo.


sub par{

    my %seni = %{$_[0]};                # guarda el hash amb les senyals que determinen l'inici d'exo.
    my %senf = %{$_[1]};                # guarda el hash amb les senyals que determinen final d'exo.
    my $max = $_[2];                    # guarda el nombre màxim d'emparellaments que volem fer per cada senyal d'inici.
    my $frame = $_[3];                  # guarda el frame en que s'ha predit les senyals
    my $tamany = $_[4];                 # longitud minima que volem que tingui l'exo predit
    my $f = 0;                          # posem el comptador del primer while a zero.
    my @resultats;                      # guarda els arrays amb la informacio de un exo en un array.
    
	

    ##### MIREM LES SENYALS D'INICI #####

    while ($f < keys (%seni)){                       # mentre hi hagi senyals d'inici         	
	my @inici = @{$seni{$f}};                    # definim l'array que conte la informacio de la senyal d'inici
	my $k = 0;                                   # posem el while del segon comptador a zero.
	my $count = 0;                               # posem el comptador dels emparellaments a zero.

	##### MIREM LES SENYALS DE FINAL #####
	
	while ($k < keys (%senf)){                   # examinen el hash %senf mentre hi hagi senyals  
	    my @final = @{$senf{$k}};                # definim a cada volta l'array que hi ha dins de 
	                                             # la key del hash %senf

	    if ($seni{$f}[0] < $final[0]){           # si la senyal d'inici d'exo esta abans que la de final d'exo
		my @exo;                             # definim un array que guardara la informacio del exo
	
		$exo[0] = $inici[0];                 # guardem a la primera posicio la senyal d'inici de l'exo
		$exo[1] = $final[0];                 # guardem a la segona posicio la senyal de final de l'exo
		 

		if ($inici[1]){                            # si existeix la posicio $inici[1](l'inici ve definit per un acceptor)  
		    $exo[2] = $inici[1] + $final[1];       # la tercera posicio es la suma de les puntuacions de la senyal
		    $exo[4] = "Internal";                 # d'inici i la de final (donor).
		}                                          


		else{	                                   # si no existeix la posicio $inici[1](l'inici ve definit per un ATG
		    $exo[2] = $final[1];                   # la tercera posicio del vector es igual a la puntuacio de la senyal
		    $exo[4] = "First   ";                  # de final (donor).
		}

		$exo[3] = $frame;
	
		if ($count < $max && ($exo[1] - $exo[0] + 1) > $tamany){   # guardem els resultats dels exons predit fins a un nombre   
		$resultats[@resultats] = \@exo;                            # maxim determinat per $max en @resultats.
		
		$count++;                                                  # incrementem el comptador d'@resultats
		}
		
	    }	    
	    $k++;           # incrementem el comptador del while.   

	}
	$f++;               # incrementem el comptador de les keys de les senyals de final d'exo                   

    }

    return \@resultats;     # retornem un array amb les referencies als arrays dels diferents exons trobats.
}




# FUNCIO 7 (bcod): donat un fitxer que conte una matriu de biaix codificant, l'obre i
# i l'emmagatzema en un hash. Retorna el hash que conte la matriu de codons.


sub bcod {

    my $fitxercod = $_[0];                       # variable que emmagatzema les distribucions de probabilitat dels codons
    my %pcodons;                                 # guarda el hash amb el biaix codificant
    

    if (!open(PROPCODONS,"< $fitxercod")) {      # comprova que s'obri el fitxer que conte la matriu de codons i sino 
    print "impossible obrir $fitxercod\n";       # fa saltar el programa i un missatge d'error.
    exit(1);
    }

    
    ##### LLEGIM EL FITXER I L'EMMAGATZEMEM EN UN HASH #####

    while () {              # obrim el filehandle
	$_ =~m/(\w+) (\d+\.\d+)/;       # expressio regular que encaixa amb dues 
	$pcodons{$1} = $2;              # paraules la primera de les quals es alfabetica $1 i 
                                        # la segona un numero amb decimals $2
    }

    close(PROPCODONS);                  # tanquem el filehandle

    return \%pcodons;                   # retorna el hash amb la matriu introduida

}



# FUNCIO 8 (biaix): calcula la rao de versemblança dels possibles exons en una pauta de
# lectura donada i retorna el valor d'aquesta rao en escala logaritmica.
# 
# Parametres requerits: sequencia en una string, el vector amb la informacio de l'exo 
# (inici, final i puntuacio) i el frame. Retorna el mateix vector amb la puntuacio 
# del biaix codificant inclosa nomes en cas de que sigui superior al llindar


sub biaix {
    
    my $seq = $_[0];                      # emmagatzemem la sequencia emmagatzmada en un string.
    my %codons = %{$_[1]};                # emmagatzema el hash amb el biaix codificant.
    my @prediccions = @{$_[2]};           # guarda l'array que conte els arrays amb les prediccions dels diferents tipus de
                                          # gens trobats per un ORF determinat.
    my $llindar = $_[3];                  # guarda la puntucio minima global que volem que tingui una prediccio.
    my $i = 0;                            # inicialitzem el comptador del while a zero.
    my @resultats;                        # guarda l'array que conte els arrays dels diferents exons que puntuin per sobre del llindar

   
    ##### RESSEGUIM ELS ELEMENTS DE L'ARRAY @PREDICCIONS #####

    while ($i < 3){                                           # mentre hi hagi un tipus d'exo (nomes mirem inicials,interns i terminals)
	
	my @mirem = @{$prediccions[$i]};                      # definim a cada volta l'array que te les prediccions per 
                                                              # un tipus d'exo determinat.
	my $n = 0;                                            # inicialitzem el comptador de la segona iteracio a zero.
	
	
        ##### RESSEGUIM ELS DIFERENTS EXONS QUE HI HA DE CADA TIPUS #####
	
	while ($n < @mirem){                                  # mentre hi hagi prediccions d'un tipus d'exo determinat
	    my @exo = @{$mirem[$n]};                          # definim l'array amb cadascun dels exons

	    my $longexo = ($exo[1]-1) - ($exo[0] - 1) + 1;        # trobem la longitud de l'exo
	    my $seqexo = substr($seq,$exo[0] - 1,$longexo);   # prenem el tros de sequencia corresponent a l'exo
	    my $frame = ($exo[0] - 1) - $exo[3];              # determinem la longitud de la sequencia
	                                   
	    
	    ##### DETERMINEM EL FRAME DE L'EXO #####

	    if ($frame % 3 == 0){                             # si la posicio d'inici del nostre exo es el de la primera        
		$exo[3] = 0;                                  # base del codo, el frame de l'exo sera 0.
	    }
	    
	    if ($frame % 3 == 1){                             # si la posicio d'inici del nostre exo es el de la segona  
		$exo[3] = 2;                                  # base del codo, el frame de l'exo sera 2.
	    }
	    
	    if ($frame % 3 == 2){                             # si la posicio d'inici del nostre exo es el de la primera  
		$exo[3] = 1;                                  # base del codo, el frame de l'exo sera 1.
	    }
	    

	    if (length ($seqexo) > 2){                        # si la longitud es suficient com per aplicar el hash de codons
		my $seq_exo = substr($seqexo, $exo[3]);       # definim la longitud de sequencia a examinar (en funcio del frame)
		my @codo = ($seq_exo =~ m/.../g);             # guardem la sequencia en un array dividida per codon.
	    

		my $r = 0;                    # inicialitzem a zero la variable que emmagatzema la puntuacio de cada prediccio.
		my $c = 0;                    # inicialitzem a zero el comptador de la segona iteracio.
		

		##### RESSEGUIM LA SEQUENCIA DE L'EXO PREDIT #####

		while ($c < scalar(@codo)) {                             # mentre hi hagi codons
		    $r = $r + log($codons{$codo[$c]}) - log(1/64);       # la puntuacio es la suma de la que trobem aplicant 
                                                                         # el biaix codificant sobre cada codo de la sequencia 
		    $c++;                                                # incrementem el comptador del tercer while.
		}
		
		$exo[2]= $exo[2]+$r;                                     # la puntuacio de l'exo es la puntuacio del biaix
                                                                         # codificant mes la de les senyals d'splicing.          
	
		if ($exo[2] > $llindar){                                 # si la puntuacio es superior a la que hem determinat 
		                                                         # com a minima per acceptar una prediccio.
		    $prediccions[$i][$n][2] = $exo[2];                   # emmagatzemem la nova puntuacio a la posicio que contenia 
                                                                         # la puntuacio de les senyals a @prediccions.
                                                                         # guardem el contingut del vector que hi ha a 
		    $resultats[$i][$n][0] = $exo[0];                     # @prediccions i guardem els diferents parametres que conte 
		    $resultats[$i][$n][1] = $exo[1];                     # al vector @resultats (inici, final, puntuacio i frame)
		    $resultats[$i][$n][2] = $exo[2];    
		    $resultats[$i][$n][3] = $exo[3];    
		    $resultats[$i][$n][4] = $exo[4];
		}

	    }
	    
	    $n++;                                                        # incrementem el comptador del segon while.  
	}
	
	$i++;                                                            # incrementem el comptador del primer while
    }

   return \@resultats;                                                   # retornem la referencia a l'array amb les prediccions fetes
                                                                         # amb la nova puntuacio
}
   



# FUNCIO 9 (junta): donat un array d'array d'arrays, emmagatzema tots els elements
# en un array d'arrays (treiem un nivell de complexitat).
#
# Parametres requerits: l'array que volguem arreglar. 


sub junta {

    my @array = @{$_[0]};                                   # emmagatzema el vector que volem arreglar 
    my $i = 0;                                              # inicialitzem el comptador de la iteracio a zero.
    my @resultats;                                          # emmagatzema l'array amb un nivell menys de complexitat.


    while ($i < @array){                                    # mentre hi hagi elements del primer nivell
	if ($array[$i]) {                                   # si existeix un element concret
	    
	    my $c = 0;                                      # inicialitzem el comptador de la iteracio a zero.
	    my @primer = @{$array[$i]};                     # definim a cada volta l'array amb les referencies
	    
	    while ($c < @primer){                           # mentre hi hagi elements a cada subarray
		if ($primer[$c]){                           # si existeix un element exo en una posicio concreta
		    my @segon = @{$primer[$c]};                      
		    $resultats[@resultats] = \@segon;       # guarda tots els elements seguits en @resultats 
		   
		}
		
		$c++;                                       # incrementem el comptador del segon while
	    }
	}

	$i++;                                               # incrementem el comptador del primer while
    }
    
    return \@resultats;                                     # retornem l'array arreglat.
}


###################### FI DEL PROGRAMA EXON-finding #####################################