#!/usr/bin/perl

use 5.040;

my $OPTION;
if (scalar @ARGV == 0){
    $OPTION = "help";
}else{
    $OPTION .= "$_ " for (@ARGV);
}

if ($OPTION !~ /noamort/i && $OPTION =~ /amort/i) { $OPTION = "amort"; }
my $OPTION2;
if ($OPTION =~ /h/i){
    say "";
    say "Usage: $0 [noamort] [tae]";
    say "";
    say "noamort = don't simulate anticiped amortizations (default)";
    say "amort = simulate extra amortizations where it cost more";
    say "tae = aditionally, tell what is the compound annual equivalent";
    say "Pedactic enough, it's only a TAE if approved by Central Bank";
    say "";
    exit -3;
}elsif ($OPTION =~ /tae/i){
    $OPTION2 = "TAE";
}

# MORTGAGE = Hipoteca

# Estrategia: simular de 0.1 a 0.9 el % dedicado a interés fijo ->
# préstamo francés
# simular EURIBOR alto mediano o bajo cada 6 meses
# simular margen de interés variable
# simular costes de amortización anticipada (igual para cada tipo)

# automatizar respuesta del cliente para amortizar la parte mala

# calcular rentabilidad final total del préstamo. Se puede suponer
# de 20 años, tiempo fijo.

# Estructura: 
# - Configuración del préstamo
# - Entorno, cada 6 meses
# - Azar, las amortizaciones: + probables al final
# (la amortización anticipada es azarosa, pero la decisión de la parte
# en la que se hace no: podría ser la más ventajosa según histórico)
# - Resultados: análisis conjunto de flujos de caja e interes único
# implícito
# El tema de las comisiones se divide en dos: las de antes de la firma
# que van a cargo del banco, pero se deben tener en cuenta en resultados
# (min. 1% sin variar el principal) y las de amortización inesperada que
# se deducen a lo amortizado y por tanto, seguro que afectan al resultado.

my @PPAL;
$PPAL[0] = 200_000;          # €

my $PERIODO_DE_CARENCIA = 0;

my $N = 20 * 12;             # meses de pago, se suele sumar 1 por el periodo 0
my $intf = 0.0425 / 12;        # interés fijo mensual
my $margen = 0.015 / 12;       # margen sobre variable mensual
my $puntoeuribor = 0.0025 /12;     # + - =
my ($comisiones_iniciales, $comision_amortizacion) = ( 0.012, 0.005 );
my $morosidad = 0.035;
# my $coste_depositos = 0.8 ;   # del euribor, se usará esto mejor
# duda de si deglosar en fijo y variable: va a ser que sí
# una solución de compromiso es poner 2 dimensiones [0][$N] total,
# [1][$N] tasa fija y [2][$N] tasa variable

my @cuota;
my @int_periodo;
my @amort;
my @amortacum;
my @cap_pend;
my @EURIBOR;    # semestres o años
#    my @coste = (0,0,0) x $N+1;    # supongo distinto el coste a tipo fijo (depósitos) que el variable (euribor)


while (1){
    my $alpha = rand() ;     # rand() o 0.5 o intervalo
    $PPAL[1] = $alpha * $PPAL[0];
    $PPAL[2] = (1-$alpha) * $PPAL[0];
    
    @cuota =();
    @int_periodo =();
    @amort =();
    @amortacum =();
    @cap_pend =();
    @EURIBOR =();    # semestres o años
#    my @coste = (0,0,0) x $N+1;    # supongo distinto el coste a tipo fijo (depósitos) que el variable (euribor)
    
    $cuota[1][0] = cuotafija();
    $amortacum[1][0] = 0;
    $cap_pend[1][0] = $PPAL[1];
#    $coste[1][0] = 0.667;          # 2/3
    $amortacum[2][0] = 0;
    $cap_pend[2][0] = $PPAL[2];
    $EURIBOR[0] = 0.03 / 12;       # IMPORTANTE - Sept 2024
#    $coste[2][0] = $EURIBOR[0];
    my @flujo = (0) x ($N+1);  # normalmente será @{$cuota[0]} descontado y sumado -$PPAL
                              # con un 'unshift @flujo_gral, -$PPAL;' 0j0 a los índices
    $flujo[0] = -($PPAL[0] + $comisiones_iniciales * $PPAL[0]); # + $coste[1][0] * $PPAL[1] + $coste[2][0] * $PPAL[2]); 
    
    my $flag = 1;
    for my $i ( 1 .. $N ){

	$cuota[1][$i] = $cuota[1][0];
	$int_periodo[1][$i] = $intf * $cap_pend[1][$i-1];
	$amort[1][$i] = $cuota[1][$i] - $int_periodo[1][$i];
	$amortacum[1][$i] = $amortacum[1][$i-1] + $amort[1][$i];
	$cap_pend[1][$i] = $cap_pend[1][$i-1] - $amort[1][$i];
    
	if ($i % 12 == 0) {
	    my $j = int(rand()*3);                            
	    my $correccion = ($j > 1) ? $puntoeuribor : -$puntoeuribor;
	    $correccion = 0 if ($j == 0);
	    if ($EURIBOR[$i-1] <= 0) { 
		$EURIBOR[$i] = $EURIBOR[$i-1] +$puntoeuribor;
	    }elsif ($EURIBOR[$i-1] >= 0.05){
		$EURIBOR[$i] = $EURIBOR[$i-1] -$puntoeuribor;
	    }else{
		$EURIBOR[$i] = $EURIBOR[$i-1] + $correccion;
	    }
	}else{
	    $EURIBOR[$i] = $EURIBOR[$i-1];   # mes normal
	}
	
	if ( $cap_pend[2][$i-1] > 0 ){
	    $int_periodo[2][$i] = ($EURIBOR[$i] + $margen) * $cap_pend[2][$i-1];
	    
	    $amort[2][$i] = $PPAL[2]/$N;
	    $cuota[2][$i] = $int_periodo[2][$i] + $amort[2][$i];
	    
	}else{
	    $cuota[2][$i] = 0;
	    $int_periodo[2][$i] = 0;
	    $amort[2][$i] = 0;
	}
	#    $amortacum[2][$i] y $cap_pend[2][$i] ya lo tendrán en cuenta
	$amortacum[2][$i] = $amort[2][$i] + $amortacum[2][$i-1];
	if ($cap_pend[2][$i-1] > $amort[2][$i]){
	    $cap_pend[2][$i] = $cap_pend[2][$i-1] - $amort[2][$i]; 
	}else{
	    $cap_pend[2][$i] = 0;
	}
	goto noamort if $OPTION !~ /amort/;
	
	if ($i > $N/3 && $i % 24 == 0){        # amort anticipada, adicional
	    my $ahorro =  1+int(rand()*11) * 1000;                            # Esto está por debajo de lo esperado: puede interesar amortizar de [1]
#	    my $cual = (rand()>=0.5) ? 1 : 2;   # Esto se debe sustituir por un sistema de decisión mejor, según MAX int_periodo[x][i] / PPAL[x] -> devolver x
	    
	    my $cual = decide ($i-1);
	    
	    if ($cap_pend[$cual][$i-1] > $amort[$cual][$i] + $ahorro){
		$cuota[$cual][$i] += $ahorro * (1+$comision_amortizacion);        # La amort anticipada ES un flujo de caja, por lo que sí puedo sumarlo a la cuota ($i)
		$amort[$cual][$i] += $ahorro;
	    }else{
		$ahorro = $cap_pend[$cual][$i-1];
		$cuota[$cual][$i] += $ahorro;                                     # No considero la comisión de CANCELACION
		$amort[$cual][$i] = $ahorro;
	    }
	}
	
noamort:	
	
	$flujo[$i] = $cuota[1][$i] + $cuota[2][$i];

	# Con sin morosidad debe de ser opcional
#	if (int($i * $morosidad) >= $flag){
#	    $flujo[$i] = 0;                                   # Complicado: distinguir si la morosidad es solo de amortización o también de intereses 
#	    $flag++;
#	}
	last if (abs($amortacum[1][$i] + $amortacum[2][$i] -$PPAL[0]) <= 1.0e-6); 
	last if (abs($cap_pend[1][$i]) <= 1.0e-6 && abs($cap_pend[2][$i]) <= 1.0e-6);
    }
    
    my $tipo = busca_int(@flujo);
    printf "\nEl tipo real de lo dual (alpha = $alpha) es = %.6f %%\n",100*$tipo;
    if (defined $OPTION2 && $OPTION2){
	if ($OPTION2 eq "TAE"){
	    $tipo /= 12;
	    my $TAE = ((1+$tipo)**12)-1;
	    say "Y a interés compuesto supone                             ", sprintf("%.6f",100*$TAE), " %\n";
	}
    }
    sleep 1;
}

exit 2;

sub decide {
    my $j = shift;
    my $sum1 = 0;
    my $sum2 = 0;
    for my $z ( 1 .. $j ){
	$sum1 += $int_periodo[1][$z];
	$sum2 += $int_periodo[2][$z];
    }
    if ($sum1 / $PPAL[1] > $sum2 / $PPAL[2]){
	return 1;
    }else{
	return 2;
    }
    # Aquí no se tiene en cuenta de que puede no ser la mejor idea 
    # amortizar anticipadamente debido a que la comisión es alta.
}

sub cuotafija {
    my $factor = (1- (1+$intf)**(-$N) ) / $intf;
    return $PPAL[1] / $factor;
}

sub busca_int {
    my @f = @_;       # el flujo de caja, lo que importa
    my $r = 0 ;       # el tipo, que intenta ser anual, pero a interés simple
    my $err = 1.0e-6;
    my $sum;
    my $c;
    do {
	$sum = $c = 0;
	for my $k (@f){
	    $sum += $k * ( (1+$r)**(-$c) );
#	    $sum = $k if $c==0;
	    $c++;
	}
	print "\r$0: error absoluto $sum   ";
	if ($sum < 0){
	    $r -= rand() * abs($sum) *1.0e-9;
	}elsif ($sum > 0){
	    $r += rand() * abs($sum) *1.0e-9;
	}else{
	    return $r*12;
	}    
    }until (abs($sum) < $err);
    return $r*12;
}


