Annuity calculation/process.pl

From Organic Design wiki
#!/usr/bin/perl -w
use strict;
use CGI::Lite;
# 1) Setup
print "Content-type: text/html\n\n";

my $Lite = new CGI::Lite;
my %form_data = $Lite -> parse_form_data($ENV{'REQUEST_METHOD'});

if(exists $form_data{debug})
{
print "=============== DEBUG ==============<br />";
    foreach( keys %form_data)
    {
	print "$_ => $form_data{$_}<br />";
    }
print "====================================<br />";
}


my ($i, $ip, $n, $p, $C,$roundup)=(0.0685,12, 25, 52, 106000,0);

($form_data{"i"}) && ($i = $form_data{i});
($form_data{"ip"}) && ($i = &ip_to_i($form_data{"i"}, $form_data{"ip"}));

($form_data{"n"}) && ($n = $form_data{n});
($form_data{"p"}) && ($p = $form_data{p});
($form_data{"C"}) && ($C = $form_data{C});
($form_data{"roundup"}) && ($roundup = $form_data{"roundup"});

my %period_name =
    (
     12 => "month",
     26 => "fortnight",
     52 => "week",
     );

my $period;
($period_name{$p}) ? ($period = $period_name{$p}) : ($period = "period");


my $table = &repayment_schedule(i=>$i, n=>$n, p=>$p, C=>$C, roundup => $roundup);

#print $table;

print qq(<html bgcolor="white">\n);
&print_table($table);
print qq(</html>);

sub print_table
{
    my $table = shift;
    my @seq = 0 .. $#{$table->[2]};
    my $append=0;
    my ($interest_sum, $capital_sum, $payments_sum) = (0,0,0);
# Calculate totals
    foreach my $i (@seq)
    {
	$interest_sum += $table->[1]->[$i];
	$capital_sum  += $table->[2]->[$i];
	$payments_sum += $table->[3];
    }
    
    $interest_sum = sprintf("%10.2f", $interest_sum);
    $capital_sum = sprintf("%10.2f", $capital_sum);
    $payments_sum = sprintf("%10.2f", $payments_sum);

    if(scalar @seq >20)
    {
	my $end = $#seq;
	@seq = (@seq[0..9], @seq[($end-9) ..$end]);
	$append=1;
    }

    $i = sprintf("%1.5f", $i);
    my $i_12 = sprintf("%1.5f",&i_to_ip(i=>$i,p=>12));
    my $i_26 = sprintf("%1.5f",&i_to_ip(i=>$i,p=>26));
my $i_52 = sprintf("%1.5f",&i_to_ip(i=>$i,p=>52));

    print qq(<table bgcolor="#EEEEEE" border=0 cellspacing=0 cellpadding=2><tr>
<td>i_52 (weekly)</td>
<td> &nbsp; </td>
<td>i_26 (fortnightly)</td>
<td>  &nbsp; </td>
<td>i_12 (monthly)</td>
<td>  &nbsp; </td>
<td>i (effective) </td>
</tr>
<tr>
<td>$i_52</td>
<td> < </td>
<td>$i_26</td>
<td> < </td>
<td>$i_12</td>
<td> < </td>
<td>$i</td>

</tr></table>);
	     
# Printing details
    print qq(term=$n \(yrs\)<br /> payments=$p \(${period}ly\) <br /> Capital=$C <br />);


    print qq(<table border=0 cellspacing=0 cellpadding=2>\n);
    print qq(<tr bgcolor="#EEEEEE"><td>nth payment</br>in arrears</td>
	     <td>Loan outstanding at </br> start of nth $period</td><td>
	     Interest due at </br> end of nth $period</td><td>
	     Capital repaid at </br> end of nth $period</td><td>
	     Repayment each </br>nth $period</td><tr>\n);
    my $color="#FFFFFF";
    foreach my $i (@seq)
    {
format STDOUT = 
<tr align="right" bgcolor="@||||||"><td> @########## </td><td> @#########.## </td><td> @#########.## </td><td>@#########.## </td><td> @#########.##</td></tr>
$color, $i+1, $table->[0]->[$i], $table->[1]->[$i],  $table->[2]->[$i], $table->[3]
.

	if($color eq "#FFFFFF")
	{
    write;
    $color = "#EEEEEE";
}else
{
    write;
    $color="#FFFFFF";
};
    

        if($append && $i==9)
        { 
	    print qq(<tr align="right" bgcolor="#FFFFFF"><td>...&nbsp;&nbsp;&nbsp;</td><td>...&nbsp;&nbsp;&nbsp;</td><td>...&nbsp;&nbsp;&nbsp;</td><td>...&nbsp;&nbsp;&nbsp;</td><td>...&nbsp;&nbsp;&nbsp;</td><tr>\n);
        } 
    }
printf qq(<tr align="right" bgcolor="#FFFFFF"><td align="center" colspan=2>Totals</td><td>$interest_sum</td><td>$capital_sum</td><td>$payments_sum</td></tr>);


print qq(</table>\n);
# foreach(@{$table->[2]}){$a+=$_}

}

sub i_to_ip
{
    my %params = (
		  i => 0.0685,
		  p => 52,
		  @_,
		  );

    my $i  = $params{"i"};
    my $p  = $params{"p"};
    
    my $ip = (((1+$i)**(1/$p))-1)*$p;
    return $ip;
}

sub Anp
{
    my %params = (
		  i => 0.0685,
		  n => 25,
		  p => 52,
		  @_,
		  );

    my $i  = $params{"i"};
    my $n  = $params{"n"};
    my $p  = $params{"p"};
    my $v = 1/(1+$i);

    my $ip = &i_to_ip(i=>$i,p=>$p);
    my $Anp = (1-$v**$n)/$ip;
    return $Anp
}

sub repayment_schedule
{
    my %params = (
		  i=>0.0685,
		  n=>25,
		  p=>52,
		  C=>106000,
		  roundup => 0,
		  @_,
		  );
    
    my $i = $params{"i"};
    my $n = $params{"n"};
    my $p = $params{"p"};
    my $C = $params{"C"};
    my $roundup = $params{"roundup"};

    my $ip  = &i_to_ip(i=>$i, p=>$p);
    my $Anp = &Anp(i=>$i, n=>$n, p=>$p);

    my $np = $n * $p;
    my $payment = $C/($Anp*$p);
    
    if($roundup)
    {
	$payment = &roundup($payment);
    }
    my @loan = ($C);
    my @interest;
    my @capital;
    my ($loan_sum, $interest_sum, $capital_sum) = (0,0,0);

    foreach(1 .. $np)
    {
	push(@interest, $loan[-1] * $ip/$p);
	push(@capital, $payment - $loan[-1] * $ip/$p);
	push(@loan, $loan[-1] * (1+$ip/$p) - $payment);
    }
        return [\@loan, \@interest, \@capital, $payment];
}


sub roundup
{
    my $value = shift;
    $value =~ m/(\d+\.\d{2})(\d)/;
    if($2)
    {
	$value = $1+0.01;
    }
    return $value;
}

sub ip_to_i
{
    my $ip = shift;
    my $p  = shift;
    my $i  = (1 + $ip/$p)**$p -1;
    return $i;
}