#!/usr/bin/perl -w

use strict;

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

my $linia_aa;    # enregistra la primera linia que conte els aminoacids
my @vec_aa;      # enregistra els aminoacids llegits a $linia_aa ficant un per posicio
my @mat;         # enregistra la matriu de substitucio
my $ret1;        # enregista el primer parametre que retornen les funcions
my $ret2;        # enregista el segon parametre que retornen les funcions
my $ret3;        # enregista el tercer parametre que retornen les funcions

my %hash;              # enregistra la matriu de substitucio com a hash
my $contfilas = 0;     # ens permet indexar les files de les matriu
my $contcolumnas = 0;  # ens permet indexat les columnes de les matrius


my $matriuentrada = $ARGV[0];  # enregistra la matriu de substitucio per ser llegida
my $seq1entrada = $ARGV[1];    # enregistra la sequencia 1 per ser llegida
my $seq2entrada = $ARGV[2];    # enregistra la sequencia 2 per ser llegida
my $gappenalty = $ARGV[3];     # enregistra la penalitzacio d'obrir gap
my $gapextension = $ARGV[4];   # enregistra la penalitzacio d'extendre gap

my @seq1;                  # enregistra la sequencia 1 procesada
my $identificadorseq1;     # enregistra l'identificador de la sequencia 1

my @seq2;                  # enregistra la sequencia 2 procesada
my $identificadorseq2;     # enregistra l'identificador de la sequencia 2

my @matalineament;         # matriu tridimensional que enregistra la matriu d'alineament
                           #  [][][0] enregistra la matriu d'alineament
                           #  [][][1] enregistra les direccions per arribar a cada posicio
                           #  [][][2] enregistra la matriu d'alineament de gaps des de l'esquerra
                           #  [][][4] enregistra el nombre de gaps necesaris per arribar a la mateixa posicio a [][][2] 
                           #  [][][3] enregistra la matriu d'alineament de gaps des de dalt
                           #  [][][5] enregistra el nombre de gaps necesaris per arribar a la mateixa posicio a [][][2] 

my @alineadadireccio1;   # enregistra la primera sequencia en l'ordre correcte
my @alineadadireccio2;   # enregistra la segona sequencia en l'ordre correcte
my @simbolsdireccio;     # enregistra la sequencia de simbols d'identitat en l'ordre correcte
my $identitat;           # enregistra el valor del % d'identitat de l'alineament
my $score;               # enregistra el valor de l'score de l'alineament

my $espaisID19;       # enregistra els 9 primers caracters de la sequencia 1  
my $espaisID29;       # enregistra els 9 primers caracters de la sequencia 2
my @clustal1;         # enregistra la primera sequencia en 60 aa per cada fila de la matriu
my @clustal2;         # enregistra la segona sequencia en 60 aa per cada fila de la matriu
my @clustalsimbols;   # enregistra la sequencia de simbols d'identitat en 60 simbols per cada fila de la matriu
my $final;            # enregistra el numero de posicions que requereixen els tres vectors anteriors

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


### CRIDA LA FUNCIO DE LLEGIR LA MATRIU DE SUBSTITUCIO

($ret1,$ret2) = LlegirMatriuSubstitucio($matriuentrada);

@mat = @{$ret1};
@vec_aa = @{$ret2};



### CRIDA LA FUNCIO DE CREAR LA HASH

($ret1) = CrearHash(\@mat,\@vec_aa);

%hash = %{$ret1};



#### CRIDA LES FUNCIONS D'INTRODUIR LES SEQUENCIES

($ret1,$identificadorseq1) = IntroduirSeq1($seq1entrada);

@seq1 = @{$ret1};


($ret1,$identificadorseq2) = IntroduirSeq2($seq2entrada);

@seq2 = @{$ret1};



### CRIDA LA FUNCIO DE CREAR LA MATRIU D'ALINEAMENT

($ret1) = MatriuAlineament($gappenalty,$gapextension,\@seq1,\@seq2,\%hash);

@matalineament = @{$ret1};



### CRIDA LA FUNCIO PER LLEGIR LA MATRIU D'ALINEAMENT

($ret1,$ret2,$ret3,$identitat,$score) = LLegirMatriuAlineament(\@seq1,\@seq2,\@matalineament);

@alineadadireccio1 = @{$ret1};
@alineadadireccio2 = @{$ret2};
@simbolsdireccio = @{$ret3};

print "Identitat $identitat% \n";

print "Score $score \n\n\n";



### CRIDA LA FUNCIO PER IMPRIMIR LES SEQUENCIES ALINEADES

Imprimir(\@alineadadireccio1,\@alineadadireccio2,\@simbolsdireccio,$score,$identitat,$identificadorseq1,$identificadorseq2);




#################################  LLEGIR MATRIU DE SUBSTITUCIO  #################################
#                                                                                                #
########################  AGAFEM EL FITXER DE LA MATRIU DE SUBSTITUCIÓ I EL PASEM A MATRIU       #
#                                                                                                #
##################################################################################################


sub LlegirMatriuSubstitucio {

my $i;                                  # ens permet indexar les files de la matriu @mat

open(MAT,$matriuentrada);

$linia_aa = <MAT>;                      # llegim la primera linia amb els aminoacids
chomp($linia_aa);                       # treiem el canvi de linia
@vec_aa = ($linia_aa =~ m/[A-Z]/g);     # fiquem els aminoacids en el vector @vec_aa

$i=0;                                   # comencarem a enregistrar la matriu a partir de la fila 0
while (<MAT>) {
    my @v = ($_ =~ m/[\d\-]+/g);        # llegim cada linia de la matriu i fiquem els valors
                                        # corresponents a cada columna
    my $j = 0;                             

    while ($j < scalar(@v)) {
	$mat[$i][$j] = $v[$j];
	$j = $j + 1;
    } 

    $i = $i + 1;                          # incrementem la fila

}

close(MAT);

return (\@mat,\@vec_aa);

}

### RETORNA la matriu de substitucio i un vector amb els aa corresponents a la matriu  
#####################################################################################




#################################  CREAR HASH  #########################################
#                                                                                      #
########################  AGAFEM LA MATRIU DE SUBTITUCIO I LA ENREGISTREM COM A HASH   #
#                                                                                      #
########################################################################################


sub CrearHash {

$contfilas = 0;        # ens permet indexar les files de la hash
$contcolumnas = 0;     # ens permet indexar les columnes de la la hash

while ($contfilas < scalar(@vec_aa)){             # llegim totes les posicions de la matriu @mat i fiquem
                                                  # els valors en una hash

while ($contcolumnas < scalar(@vec_aa)){
    
    $hash{$vec_aa[$contcolumnas]}{$vec_aa[$contfilas]} = $mat[$contcolumnas][$contfilas];

    $contcolumnas = $contcolumnas + 1 }     # incrementem la columna

$contcolumnas = 0;                          # tornem el contador de columnes a 0

$contfilas = $contfilas + 1; }              # incrementem la columna

return \%hash;

}

### RETORNA la matriu de substitucio en una hash
################################################




#################################  ENTRAR SEQUENCIES  #################################
#                                                                                     #
########################  ENTRAR SEQUENCIES i PROCESARLES                             #
#                                                                                     #
#######################################################################################


sub IntroduirSeq1 {

my $entrada;               # permet manipular linea per linea la sequencia introduida
my $dolarseq1 = '-';       # enregistrem les linies llegides de la primera sequencia amb un gap a l'inici


open(SEQUENCIAUNO,$seq1entrada);

$identificadorseq1 = <SEQUENCIAUNO>;    # enregistrem la primera linea introduida de la primera sequencia
                                        # que es la que conte l'identificador
chomp($identificadorseq1);

while (<SEQUENCIAUNO>) {                # llegim totes les linies de la primera sequencia i les concatenem

    $entrada = $_;
    chomp ($entrada);
    $dolarseq1 = $dolarseq1.$entrada;
}

$dolarseq1 = uc($dolarseq1);            # pasem els caracters introduits a majuscules

close(SEQUENCIAUNO);

@seq1= split(//,$dolarseq1);            # pasem la sequencia enregistrasa a $dolarseq1 al vector seq1

return (\@seq1,$identificadorseq1);

}

#####

sub IntroduirSeq2 {

my $entrada;               # permet manipular linea per linea la sequencia introduida
my $dolarseq2 = '-';       # enregistrem les linies llegides de la segona sequencia amb un gap a l'inici

open(SEQUENCIADOS,$seq2entrada);


$identificadorseq2 = <SEQUENCIADOS>;     # enregistrem la primera linea introduida de la segona sequencia
                                         # que es la que conte l'identificador
chomp($identificadorseq2);

while (<SEQUENCIADOS>) {                 # llegim totes les linies de la segona sequencia i les concatenem

    $entrada = $_;
    chomp ($entrada);
    $dolarseq2 = $dolarseq2.$entrada;
}

$dolarseq2 = uc($dolarseq2);             # pasem els caracters introduits a majuscules

close(SEQUENCIADOS);

my @seq2= split(//,$dolarseq2);          # pasem la sequencia enregistrasa a $dolarseq1 al vector seq1

return (\@seq2,$identificadorseq2);

}

### La primera funcio RETORNA un vector amb la sequencia 1 amb un gap a la posicio [0] (@seq1)
### La segona funcio RETORNA un vector amb la sequencia 2 amb un gap a la posicio [0] (@seq2)
##############################################################################################




#################################  MATRIU D'ALINEAMENT  #################################
#                                                                                       #
########################  CREACIO DE LA MATRIU D'ALINEAMNET                             #
#                                                                                       #
#########################################################################################


sub MatriuAlineament {

my $control = 0;    # dona prioritat a alinear en diagonal sobre esquerra o dalt, i a esquerra sobre dalt 
my $dalt;           # guarda el valor d'alinear l'aa de la sequencia 2 amb un inici de gap
my $dalt2;          # guarda el valor d'alinear l'aa de la sequencia 2 amb l'extensio d'un gap
my $diagonal;       # guarda el valor d'alinear l'aa de la sequencia 1 amb el corresponent de la sequencia 2
my $esquerra;       # guarda el valor d'alinear l'aa de la sequencia 1 amb un inici de gap
my $esquerra2;      # guarda el valor d'alinear l'aa de la sequencia 1 amb l'extensio d'un gap
my $contriz;        # es igual a 1 si el valor d'extendre el gap d'esquerra es mes gran al d'iniciar-lo
                    # es igual a 0 si el valor d'extendre el gap d'esquerra es menor al d'iniciar-lo
my $contrdalt;      # es igual a 1 si el valor d'extendre el gap de dalt es mes gran al d'iniciar-lo
                    # es igual a 0 si el valor d'extendre el gap de dalt es menor al d'iniciar-lo
my $max;            # guarda el valor maxim entre $diagonal $esquerra i $dalt
my $contesquerra;   # contador de la posicio de la sequencia 1
my $contdalt;       # contador de la posicio de la sequencia 2

($ret1) = MatriuAlineamentPrimeraLinea($gappenalty,$gapextension,\@seq1,\@seq2); ### CRIDA LA FUNCIO PER CREAR
                                                                                 # LA PRIMERA LINEA DE LA MATRIU
@matalineament = @{$ret1};


$contfilas = 1;        # ens permet indexar les files de @matalineament
$contcolumnas = 1;     # ens permet indexar les columnes de @matalineament

     
while ($contfilas < scalar(@seq2)) {       # mira per totes les posicions de la matriu de alineament quin
                                           # valor entre $diagonal $esquerra $esquerra2 $dalt i $dalt2 es mes gran 
    $contcolumnas = 1;

while ($contcolumnas < scalar(@seq1)) {
  
 
    $esquerra = $matalineament[$contcolumnas - 1][$contfilas][0] + $gappenalty + $gapextension;
    
    $esquerra2 = $matalineament[$contcolumnas][$contfilas][2];
    
    if ($esquerra2 >= $esquerra) {         # determina quin valor es el mes gran i el guarda com a $esquerra

	$esquerra = $esquerra2;

	$contriz = 1;

    } else {

    $contriz = 0;

    }

   
    $dalt = $matalineament[$contcolumnas][$contfilas - 1][0] + $gappenalty + $gapextension;
        
    $dalt2 = $matalineament[$contcolumnas][$contfilas][3]; 
 
    if ($dalt2 >= $dalt) {                 # determina quin valor es el mes gran i el guarda com a $dalt
    
        $dalt = $dalt2; 

        $contrdalt = 1;

    } else { 

    $contrdalt = 0;

    }


    $diagonal = $matalineament[$contcolumnas - 1][$contfilas - 1][0] + $hash{$seq1[$contcolumnas]}{$seq2[$contfilas]};

    $max = FuncioMax($esquerra, $dalt, $diagonal);   ### CRIDA A LA FUNCIO MAX

 
    if ($diagonal == $max) {     # si $diagonal es el valor mes alt aquest valor s'enregistra a @matalineament[][][0]
	                         # a @matalineament[][][1] s'enregistra el cami per arribar 'diagonal'
	                         # a @matalineament[+1][][2] s'enregistra el valor anterior + la penalitzacio
                                 # d'extendre el gap des de l'esquerra, i a [][][4] contem un gap mes
	                         # a @matalineament[][+1][+3] s'enregistra el valor anterior + la penalitzacio
                                 # d'extendre el gap des de dalt, i a [][][5] contem un gap mes

	$matalineament[$contcolumnas][$contfilas][0] = $diagonal;
	$matalineament[$contcolumnas][$contfilas][1] = 'diagonal';
	
	$matalineament[$contcolumnas + 1][$contfilas][2] = $matalineament[$contcolumnas][$contfilas][2] + $gapextension;
	$matalineament[$contcolumnas][$contfilas + 1][3] = $matalineament[$contcolumnas][$contfilas][3] + $gapextension;

	$matalineament[$contcolumnas][$contfilas][4] = $matalineament[$contcolumnas - 1][$contfilas][4] + 1;
        $matalineament[$contcolumnas][$contfilas][5] = $matalineament[$contcolumnas][$contfilas - 1][5] + 1;

	$control = 1;            # evita que es reconeguin $esquerra o $dalt com a maxims
    }



    if ($esquerra == $max && $control != 1) {   # si $esquerra es el valor mes alt aquest valor s'enregistra a @matalineament[][][0]
	                                        # a @matalineament[][][1] s'enregistra el cami per arribar 'esquerra'

	$matalineament[$contcolumnas][$contfilas][0] = $esquerra;
		
	$control = 1;            # evita que es reconegui $dalt com a maxim

	if ($contriz == 1) {     # si el valor d'alinear l'aa de la sequencia 1 amb l'extensio d'un gap es mes gran
	                         # al valor d'iniciar aquest gap, a @matalineament[+1][][2] s'enregistra el valor anterior
                                 # + la penalitzacio d'extendre el gap des de l'esquerra, i a [][][4] contem un gap mes
                                 # canviem el cami a @matalineament[][][1] per 'esquerra' fins que trobem l'inici
                                 # del gap

            $matalineament[$contcolumnas][$contfilas][1] = 'esquerra';
            $matalineament[$contcolumnas + 1][$contfilas][2] = $matalineament[$contcolumnas][$contfilas][2] + $gapextension;
            $matalineament[$contcolumnas][$contfilas][4] = $matalineament[$contcolumnas - 1][$contfilas][4] + 1;


        } else {                 # si el valor d'alinear l'aa de la sequencia 1 amb l'extensio d'un gap es menor
	                         # al valor d'iniciar aquest gap, a @matalineament[+1][][2] s'enregistra el valor
                                 # corresponent a @matalineament[][][0] + la penalitzacio d'extendre el gap
                                 # com iniciem un gap,a [][][4] donem valor de 0

            $matalineament[$contcolumnas][$contfilas][1] = 'esquerra'; 
	    $matalineament[$contcolumnas + 1][$contfilas][2] = $esquerra + $gapextension;
            $matalineament[$contcolumnas][$contfilas][4] = 0;
	}
	
	$matalineament[$contcolumnas][$contfilas + 1][3] = $matalineament[$contcolumnas][$contfilas][3] + $gapextension; 
        $matalineament[$contcolumnas][$contfilas][5] = $matalineament[$contcolumnas][$contfilas - 1][5] + 1;
                                           # a @matalineament[][+1][+3] s'enregistra el valor anterior + la penalitzacio
                                           # d'extendre el gap des de dalt, i a [][][5] contem un gap mes
    }



    if ($dalt == $max && $control != 1) {   # si $dalt es el valor mes alt aquest valor s'enregistra a @matalineament[][][0]
	                                    # a @matalineament[][][1] s'enregistra el cami per arribar 'dalt'

	$matalineament[$contcolumnas][$contfilas][0] = $dalt;
	
	if ($contrdalt == 1) {     # si el valor d'alinear l'aa de la sequencia 2 amb l'extensio d'un gap es mes gran
	                           # al valor d'iniciar aquest gap, a @matalineament[][+1][3] s'enregistra el valor anterior
                                   # + la penalitzacio d'extendre el gap des de dalt, i a [][][5] contem un gap mes
                                   # canviem el cami a @matalineament[][][1] per 'dalt' fins que trobem l'inici
                                   # del gap

            $matalineament[$contcolumnas][$contfilas][1] = 'dalt';
	    $matalineament[$contcolumnas][$contfilas + 1][3] = $matalineament[$contcolumnas][$contfilas][3] + $gapextension;
            $matalineament[$contcolumnas][$contfilas][5] = $matalineament[$contcolumnas][$contfilas - 1][5] + 1;
   
	
	} else {                 # si el valor d'alinear l'aa de la sequencia 2 amb l'extensio d'un gap es menor
	                         # al valor d'iniciar aquest gap, a @matalineament[][+1][3] s'enregistra el valor
                                 # corresponent a @matalineament[][][0] + la penalitzacio d'extendre el gap
	                         # com iniciem un gap,a [][][5] donem valor de 0

	    $matalineament[$contcolumnas][$contfilas][1] = 'dalt';
	    $matalineament[$contcolumnas][$contfilas + 1][3] = $dalt + $gapextension;
            $matalineament[$contcolumnas][$contfilas][5] = 0; 
	}

	$matalineament[$contcolumnas + 1][$contfilas][2] = $matalineament[$contcolumnas][$contfilas][2] + $gapextension;
        $matalineament[$contcolumnas][$contfilas][4] = $matalineament[$contcolumnas - 1][$contfilas][4] + 1;

                                           # a @matalineament[+1][][2] s'enregistra el valor anterior + la penalitzacio
                                           # d'extendre el gap des de dalt, i a [][][5] contem un gap mes	
    }

    $control = 0;                          # retorna $control a 0 per permetre que $esquerra i $dalt puguin ser
                                           # reconeguts com a maxims

    $contcolumnas = $contcolumnas + 1;     # incrementem la columna

}
 
    $contfilas = $contfilas + 1;           # incrementem la fila
}

$matalineament[1][0][5] = 0;
$matalineament[0][1][4] = 0;
                             # canviem a 0 aquests valors, que per facilitar la generacio de les respectives matrius 
                             # havien pres un valor de -1, pero els pertoca 0 pq a aquestes posicions de la matriu
                             # [][][1] figura 'dalt' i 'esquerra' respectivament

return (\@matalineament);

}

### RETORNA la matriu d'alineament
##################################

#################################  PRIMERA FILA DE LA MATRIU D'ALINEAMENT  #################################

# aquesta funcio omple la linia i la columna 0 de la matriu @matalineament[][][0] , [][][1] , [][][4] i [][][5]
# omple les linies i columnes 0 i la columna 1 de la matriu @matalineament[][][2]
# omple les linies i columnes 0 i la fila 1 de la matriu @matalineament[][][3]

# les posicions de la matriu que omple aquesta funcio son independents de les sequencies introduides
# i seran el punt de partida per omplir la totalitat de la matriu

# la linea i la columna 0 son comunes a les matrius [][][0], [][][2] i [][][3]
# la columna 1 de @matalineament[][][2] es construeix afegint les puntuacions d'obrir un gap
# al valor de la columna anterior 
# la fila 1 de @matalineament[][][3] es construeix afegint les puntuacions d'obrir un gap
# al valor de la fila anterior 

# tal com s'indica a la declaracio de variables la matriu te la seguent estructura:
      #  [][][0] enregistra la matriu d'alineament
      #  [][][1] enregistra les direccions per arribar a cada posicio
      #  [][][2] enregistra la matriu d'alineament de gaps des de l'esquerra
      #  [][][4] enregistra el nombre de gaps necesaris per arribar a la mateixa posicio a [][][2] 
      #  [][][3] enregistra la matriu d'alineament de gaps des de dalt
      #  [][][5] enregistra el nombre de gaps necesaris per arribar a la mateixa posicio a [][][2] 

sub MatriuAlineamentPrimeraLinea {

$matalineament[0][0][0] = 0;         # es igual a 0 ja que correspon a la puntuacio d'alinear un gap amb un gap
$matalineament[0][0][1] = 'final';   # ens indicara que em acabat de llegir la matriu 
$matalineament[0][0][2] = 0;         # correspon a la puntuacio d'alinear un gap amb un gap
$matalineament[0][0][3] = 0;         # correspon a la puntuacio d'alinear un gap amb un gap

my $contcolumnas = 2;                # permet indexar les columnes
my $contfilas = 2;                   # permet indexar les files

$matalineament[1][0][0] = $matalineament[0][0][0] + $gapextension + $gappenalty; 
$matalineament[1][0][1] = 'esquerra';
$matalineament[1][0][2] = $matalineament[1][0][0];
$matalineament[1][0][3] = $matalineament[1][0][0];
$matalineament[1][0][5] = - 1;       # pren valor de -1 per facilitar la construccio de la matriu, posteriorment el canviarem a 0 

$matalineament[0][1][0] = $matalineament[0][0][0] + $gapextension + $gappenalty;
$matalineament[0][1][1] = 'dalt';    
$matalineament[0][1][2] = $matalineament[0][1][0];
$matalineament[0][1][3] = $matalineament[0][1][0];
$matalineament[0][1][4] = -1;        # pren valor de -1 per facilitar la construccio de la matriu, posteriorment el canviarem a 0

$matalineament[1][1][2] = $matalineament[0][1][2] + $gapextension + $gappenalty;
$matalineament[1][1][3] = $matalineament[1][0][3] + $gapextension + $gappenalty;


while ($contcolumnas < scalar(@seq1)) {    # recorre totes les columnes omplint la fila 0 i la fila 0 i la 1 de [][][3]

    $matalineament[$contcolumnas][0][0] = $matalineament[$contcolumnas - 1][0][0] + $gapextension;
    $matalineament[$contcolumnas][0][1] = 'esquerra';
    $matalineament[$contcolumnas][0][4] = $contcolumnas - 1;
    $matalineament[$contcolumnas][0][2] = $matalineament[$contcolumnas][0][0];
    $matalineament[$contcolumnas][0][3] = $matalineament[$contcolumnas][0][0];

    $matalineament[$contcolumnas][1][3] = $matalineament[$contcolumnas - 1][1][3] + $gapextension;
    $matalineament[$contcolumnas][0][5] = -1;

    $contcolumnas = $contcolumnas + 1;
}


while ($contfilas < scalar(@seq2)) {      # recorre totes les files omplint la columna 0 i la columna 0 i la 1 de [][][2]

    $matalineament[0][$contfilas][0] = $matalineament[0][$contfilas - 1][0] + $gapextension;
    $matalineament[0][$contfilas][1] = 'dalt';
    $matalineament[0][$contfilas][5] = $contfilas - 1;
    $matalineament[0][$contfilas][2] = $matalineament[0][$contfilas][0];
    $matalineament[0][$contfilas][3] = $matalineament[0][$contfilas][0];
    
    $matalineament[1][$contfilas][2] = $matalineament[1][$contfilas - 1][2] + $gapextension;
    $matalineament[0][$contfilas][4] = -1;

    $contfilas = $contfilas + 1;
}

return (\@matalineament);

}

### RETORNA la matriu d'alineament omplerta en les posicions independents de la sequencia
#########################################################################################

#################################  FUNCIO MAX  #################################

# aquesta funcio determina quin es el valor mes gran entre $diagonal $esquerra i $dalt
# i per tant, determina quin es el cami optim per omplir la matriu d'alineament 

sub FuncioMax {

  my $i = 0;
  my $max = -99999999999;
  
  while ($i < 3) {            # guarda a $max el valor maxims dels entrats

    if ($_[$i] >= $max) {
    $max = $_[$i];
    
    }

    $i = $i + 1;

  }

  return $max;

}

### RETORNA el valor maxim entre $diagonal $esquerra i $dalt
############################################################




#################################  LLEGIR MATRIU D'ALINEAMENT  ###################################
#                                                                                                #
########################  FER VECTORS DE SEQ ALINEADES i SIMBOLS CONFORME TORNE CAP ARRERE       #
#                                                                                                #
##################################################################################################


sub LLegirMatriuAlineament {

my @seqalineada1;           # enregistra la primera sequencia alineada
my @seqalineada2;           # enregistra la segona sequencia alineada
my @simbols;                # enregistra la sequencia de simbols d'identitat
my $contseqalineada = 0;    # conta la posicio dels vectors de les sequencies que estem alineant
my $estrellitas = 0;        # conta el numero de coincidencies entre les sequencies alineades 
my $controlsalt;            # retorna a l'inici del "while"

$contcolumnas = scalar(@seq1) - 1;  
$contfilas = scalar(@seq2) - 1;      

my $contseq2 = $contfilas;    # permet indexar les files de @matalineament
my $contseq1 = $contcolumnas; # permet indexar les columnes de @matalineament
my $congap;                   # contador del numero de gaps
my $consal;                   # permet guardar el valor de les matrius [][][4] i [][][5] de repeticio dels gaps

while ($matalineament[$contcolumnas][$contfilas][1] ne 'final') {   # recorre la matriu d'alineament des de l'ultima posicio
                                                                    # fins arribar a la casella [0][0], i per cada posicio
                                                                    # agafa el cami indicat com a optim en la matriu [][][1]

    $controlsalt = 0;                                               # retorna el control a 0 perque pugui explorar totes les posibilitats

    if ($matalineament[$contcolumnas][$contfilas][1] eq 'diagonal') {

	$seqalineada1[$contseqalineada] = $seq1[$contseq1];  # asigna a @seqalineada1 l'aa corresponent a la posicio de la matriu 
	$seqalineada2[$contseqalineada] = $seq2[$contseq2];  # asigna a @seqalineada2 l'aa corresponent a la posicio de la matriu 

	if ($seq1[$contseq1] eq $seq2[$contseq2]) {          # comproba si els dos aa son igual
	
	    $simbols[$contseqalineada] = '*';                # asigna a @simbols una coincidencia
	    $estrellitas = $estrellitas + 1;                 # conta les coincidencies


	} else { 

	    if ($hash{$seq1[$contseq1]}{$seq2[$contseq2]} >= 0) {    # comproba si el valor d'alinear els dos aa es
		                                                     # superior a 0

		$simbols[$contseqalineada] = ':';                    # asigna a @simbols ':'

	    } else {

		$simbols[$contseqalineada] = ' ';                    # asigna a @simbols un espai en blanc
	    }
	}
	
	$contseq1 = $contseq1 - 1;                 # saltem l' aa de la sequencia 1 que ha estat alineat
	$contseq2 = $contseq2 - 1;                 # saltem l' aa de la sequencia 2 que ha estat alineat

	$contcolumnas = $contcolumnas - 1;         # mou una columna cap a l'esquerra la posicio a la matriu
	$contfilas = $contfilas - 1;               # mou una fila cap a dalt la posicio a la matriu
	$contseqalineada = $contseqalineada + 1;   # mou la pocisio dels vectors on enregistrem les sequencies 
	$controlsalt = 1;                          # evita entrar en els dos "if" seguents
    }   


    if ($matalineament[$contcolumnas][$contfilas][1] eq 'esquerra' && $controlsalt != 1){

	$congap = -1;
        $consal = $matalineament[$contcolumnas][$contfilas][4];  # guarda el numero de vegades -1 que s'ha de posar gap
        $controlsalt = 1;                                        # evita entrar en el "if" seguent
	
	while ($congap < $consal) {                              # alinea amb tants gaps com indica la matriu [][][4]

	    $seqalineada1[$contseqalineada] = $seq1[$contseq1];  # asigna a @seqalineada1 l'aa corresponent a la posicio de la matriu 
	    $seqalineada2[$contseqalineada] = '-';               # asigna a @seqalineada2 un gap

	    $contseq1 = $contseq1 - 1;                           # saltem l' aa de la sequencia 1 que ha estat alineat
	    $simbols[$contseqalineada] = ' ';                    # asigna a @simbols un espai en blanc

	    $contcolumnas = $contcolumnas - 1;                   # mou una columna cap a l'esquerra la posicio a la matriu
	    $contseqalineada = $contseqalineada + 1;             # mou la pocisio dels vectors on enregistrem les sequencies 

	    $congap = $congap + 1;

	}
    }

 
 if ($matalineament[$contcolumnas][$contfilas][1] eq 'dalt' && $controlsalt != 1){

	$congap = -1;
        $consal = $matalineament[$contcolumnas][$contfilas][5];  # guarda el numero de vegades -1 que s'ha de posar gap
        
	while ($congap < $consal) {                              # alinea amb tants gaps com indica la matriu [][][5]

	    $seqalineada1[$contseqalineada] = '-';               # asigna a @seqalineada1 un gap
	    $seqalineada2[$contseqalineada] = $seq2[$contseq2];  # asigna a @seqalineada2 l'aa corresponent a la posicio de la matriu 
	
	    $contseq2 = $contseq2 - 1;                           # saltem l' aa de la sequencia 2 que ha estat alineat
	    $simbols[$contseqalineada] = ' ';                    # asigna a @simbols un espai en blanc

	    $contfilas = $contfilas - 1;                         # mou una fila cap a dalt la posicio a la matriu
	    $contseqalineada = $contseqalineada + 1;             # mou la pocisio dels vectors on enregistrem les sequencies

	    $congap = $congap + 1;

	}
    }

}


my @alineadadireccio1 =  reverse(@seqalineada1);  # enregistra la sequencia 1 alineada en l'ordre correcte
my @alineadadireccio2 =  reverse(@seqalineada2);  # enregistra la sequencia 2 alineada en l'ordre correcte
my @simbolsdireccio =  reverse(@simbols);         # enregistra la sequencia de simbols d'identitats alineada en l'ordre correcte

my $identitat = $estrellitas / scalar(@simbols) * 100;                       # enregistra el % d'identitat

my $score = $matalineament[scalar(@seq1 - 1)][scalar(@seq2 - 1)][0];   # enregistra l'score

return (\@alineadadireccio1,\@alineadadireccio2,\@simbolsdireccio,$identitat,$score)

}

### RETORNA les dues sequencies alineades i la sequencia amb els simbols d'identitat
### Tambe RETORNA el % d'identitat i l'score de l'alineament
####################################################################################




#################################  IMPRIMIR  ###################################
#                                                                              #
########################  IMPRIMIX LES SEQUENCIES EN FORMAT CLUSTAL            #
#                                                                              #
################################################################################


sub Imprimir {

my $espaisclustal = '     ';   #enregistra els espais que el format clustal requereix entre l'identificador i la sequencia

($espaisID19,$espaisID29) = Identificadors ($identificadorseq1,$identificadorseq2);              ### CRIDA LA FUNCIO IDENTIFICADORS

($ret1,$ret2,$ret3,$final) = Clustal(\@alineadadireccio1,\@alineadadireccio2,\@simbolsdireccio); ### CRIDA LA FUNCIO CLUSTAL

@clustal1 = @{$ret1};
@clustal2 = @{$ret2};
@clustalsimbols = @{$ret3};

my $con = 0;    # permet indexar les files de les matrius clustal
my $con2 = 0;   # permet indexar les columnes de les matrius clustal

while ($con <= $final) {

    print "$espaisID19 $espaisclustal";            # imprimix l'dentificador de la sequencia 1

    $con2 = 0;                                     # retorna el contador 2 a 0

    while ($con2 < 60) {                           # recorre i imprimix la fila corresponent de la matriu de la sequencia 1

	if (!$clustal1[$con2][$con]) {} else {

	    print $clustal1[$con2][$con];
	}
	
	$con2 = $con2 + 1;                         # mou la columna

    }

    print "\n";                                    # imprimix un canvi de linia

    print "$espaisID29 $espaisclustal";            # imprimix l'dentificador de la sequencia 2

    $con2 = 0;                                     # retorna el contador 2 a 0

    while ($con2 < 60) {                           # recorre i imprimix la fila corresponent de la matriu de la sequencia 2

	if (!$clustal2[$con2][$con]) {} else {

	    print $clustal2[$con2][$con];
	
	}

	$con2 = $con2 + 1;                         # mou la columna

    }

    print "\n";                                    # imprimix un canvi de linia

    print '               ';                       # imprimeix espais al lloc corresponent a l'identificador de la sequencia

    $con2 = 0;                                     # retorna el contador 2 a 0

    while ($con2 < 60) {                           # recorre i imprimix la fila corresponent de la matriu de simbols

	if (!$clustalsimbols[$con2][$con]) {} else {

	    print $clustalsimbols[$con2][$con];
	
	}

	$con2 = $con2 + 1;                         # mou la columna

    }

print "\n\n";                                      # imprimix dos canvis de linia

$con = $con + 1;                                   # mou la fila

}

}

### No RETORNA res, la finalitat d'aquesta funcio es imprimir els resultats de l'alineament
###########################################################################################




#################################  FUNCIO IDENTIFICADORS  #################################

# aquesta funcio enregistra els identificador de les sequencies en variables amb 9 digits

sub Identificadors {

my @vectorindt1= split(//,$identificadorseq1);  # pasa l'identificador de la sequencia 1 a un vector
my $con = 1;                                    # permet indexar el vector

while ($con < 10) {                             # concatena els 9 primers valors del vector a $espaisID19
                                                # si l'identificador te menys de 9 digits, omple els restants amb ceros per
                                                # evitar la desconfiguracio del format

    if (!$vectorindt1[$con]) {

    $espaisID19 = $espaisID19.'0';

    } else {

    $espaisID19 = $espaisID19.$vectorindt1[$con];

    }

    $con = $con + 1;

}

my @vectorindt2= split(//,$identificadorseq2);  # pasa l'identificador de la sequencia 1 a un vector
$con = 1;                                       # permet indexar el vector

while ($con < 10) {                             # concatena els 9 primers valors del vector a $espaisID29
                                                # si l'identificador te menys de 9 digits, omple els restants amb ceros per
                                                # evitar la desconfiguracio del format
     if (!$vectorindt2[$con]) {

    $espaisID29 = $espaisID29.'0';

    } else {

    $espaisID29 = $espaisID29.$vectorindt2[$con];

    }

    $con = $con + 1;

}

return ($espaisID19,$espaisID29);

}

### RETORNA dues variables ($espaisID19,$espaisID29) amb 9 digits de l'identificador corresponent
#################################################################################################

#################################  FUNCIO CLUSTAL  #################################

# enregistra les sequencies en grups de 60 aa o simbols

sub Clustal {

my $con = 0;   # permet indexar les files de la matriu clustal
my $con2 = 0;  # permet indexar les columnes de la matriu clustal
my $con3 = 0;  # permet indexat la posicio del vector de les sequencies alineades

$final = (scalar(@alineadadireccio1)/60);   # calcula el numero de files requerides per enregistrar la sequencia


while ($con <= $final) {                    # enregistra la primera sequencia en una matriu de 60 
                                            # columnes amb un aa per posicio
    $con2 = 0;

    while ($con2 < 60) {

	$clustal1[$con2][$con] = $alineadadireccio1[$con3];

	$con2 = $con2 + 1;                  # mou una columna

	$con3 = $con3 + 1;                  # mou la posicio del vector de la sequencia alineada
    }

    $con = $con + 1;                        # mou la fila

}

$con = 0;
$con2 = 0;
$con3 = 0;                                  # tornem a 0 els contadors

while ($con <= $final) {                    # enregistra la segona sequencia en una matriu de 60
                                            # columnes amb un aa per posicio
    $con2 = 0;

    while ($con2 < 60) {

	$clustal2[$con2][$con] = $alineadadireccio2[$con3];

	$con2 = $con2 + 1;                  # mou una columna

	$con3 = $con3 + 1;                  # mou la posicio del vector de la sequencia alineada
    }

    $con = $con + 1;                        # mou la fila

}

$con = 0;
$con2 = 0;
$con3 = 0;                                  # tornem a 0 els contadors

while ($con <= $final) {                    # enregistra la segona de simbols d'identitat en una matriu de 60
                                            # columnes amb un simbol per posicio
    $con2 = 0;

    while ($con2 < 60) {

	$clustalsimbols[$con2][$con] = $simbolsdireccio[$con3];

	$con2 = $con2 + 1;                  # mou una columna

	$con3 = $con3 + 1;                  # mou la posicio del vector de la sequencia alineada
    }

    $con = $con + 1;                        # mou la fila

}

return (\@clustal1,\@clustal2,\@clustalsimbols,$final);

}
