initial commit from pause master
authorNathan Wagner <nw@hydaspes.if.org>
Wed, 9 Jul 2014 09:23:14 +0000 (09:23 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Wed, 9 Jul 2014 09:23:14 +0000 (09:23 +0000)
13 files changed:
.cvsignore [new file with mode: 0644]
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
META.yml [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
examples/htmlamtable [new file with mode: 0755]
lib/Finance/Amortization.pm [new file with mode: 0644]
t/00-load.t [new file with mode: 0644]
t/basic.t [new file with mode: 0644]
t/boilerplate.t [new file with mode: 0644]
t/pod-coverage.t [new file with mode: 0644]
t/pod.t [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..19b4806
--- /dev/null
@@ -0,0 +1,10 @@
+blib*
+Makefile
+Makefile.old
+Build
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+Finance-Amortization-*
+cover_db
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..4e7a0a6
--- /dev/null
+++ b/Changes
@@ -0,0 +1,28 @@
+0.5
+
+Changes to improve "kwalitee" rating on cpants.
+
+0.4
+
+Fixed doc bug in BUGS section.  BUGS now notes that there is a test script,
+which should be better.
+
+Fixed documentation of new() constructor.  It now matches the behavior.
+
+Added TODO about locale dependent rounding.
+
+0.3
+
+Added t directory and a test program.
+
+Added precision field to round results.
+
+Fixed some bugs.
+
+0.2
+
+Version number bump to get around PAUSE naming issue.
+
+0.1
+
+Initial release.
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..aab0b30
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,13 @@
+.cvsignore
+Changes
+examples/htmlamtable
+lib/Finance/Amortization.pm
+Makefile.PL
+MANIFEST                       This list of files
+META.yml
+README
+t/00-load.t
+t/basic.t
+t/boilerplate.t
+t/pod-coverage.t
+t/pod.t
diff --git a/META.yml b/META.yml
new file mode 100644 (file)
index 0000000..2d9b4c5
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,14 @@
+--- #YAML:1.0
+name:                Finance-Amortization
+version:             0.5
+abstract:            Simple Amortization Schedules
+license:             ~
+generated_by:        ExtUtils::MakeMaker version 6.32
+distribution_type:   module
+requires:     
+    Test::More:                    0
+meta-spec:
+    url:     http://module-build.sourceforge.net/META-spec-v1.2.html
+    version: 1.2
+author:
+    - Nathan Wagner <wagner@cpan.org>
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..01782c6
--- /dev/null
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME                => 'Finance::Amortization',
+    AUTHOR              => 'Nathan Wagner <wagner@cpan.org>',
+    VERSION_FROM        => 'lib/Finance/Amortization.pm',
+    ABSTRACT_FROM       => 'lib/Finance/Amortization.pm',
+    PL_FILES            => {},
+    PREREQ_PM => {
+        'Test::More' => 0,
+    },
+    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean               => { FILES => 'Finance-Amortization-*' },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b8436c8
--- /dev/null
+++ b/README
@@ -0,0 +1,131 @@
+Finance::Amortization(3\bU)\bser Contributed Perl Documentati\bFo\bin\bnance::Amortization(3)
+
+
+
+N\bNA\bAM\bME\bE
+       Finance::Amortization - Simple Amortization Schedules
+
+S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS
+       use Finance::Amortization
+
+       # make a new schedule
+
+       $amortization = new Finance::Amortization(principal => 100000, rate =
+       0.06/12,      periods = 360);
+
+       # get the balance after a the twelveth period
+
+       $balance = $amortization->balance(12)
+
+       # get the interest paid during the twelfth period
+
+       $interest = $amortization->interest(12);
+
+D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
+       Finance::Amortization is a simple object oriented interface to an amor-
+       tization table.  Pass in the principal to be amortized, the number of
+       payments to be made, and the interest rate per payment.  It will calcu-
+       late the rest on demand, and provides a few methods to ask for the
+       state of the table after a given number of periods.
+
+       Finance::Amortization is written in pure perl and does not depend on
+       any other modules.  It exports no functions; all access is via methods
+       called on an amortization object.  (Except for _\bn_\be_\bw_\b(_\b), of course.)
+
+       _\bn_\be_\bw_\b(_\b)
+
+       $am = Finance::Amortization->new(principal => 0, rate => 0, periods =>
+       0,      compounding => 12, precision => 2);
+
+       Creates a new amortization object.  Calling interface is hash style.
+       The fields principal, rate, and periods are available, all defaulting
+       to zero.
+
+       Compounding is a parameter which sets how many periods the rate is com-
+       pounded over.  Thus, if each amortization period is one month, setting
+       compounding to 12 (the default), will make the rate an annual rate.
+       That is, the interest rate per period is the rate specified, divided by
+       the compounding.
+
+       So, to get an amortization for 30 years on 200000, with a 6% annual
+       rate, you would call new(principal => 200000, periods => 12*30, rate =>
+       0.06), the compounding will default to 12, and so the rate will work
+       out right for monthly payments.
+
+       precision is used to specify the number of decimal places to round to
+       when returning answers.  It defaults to 2, which is appropriate for US
+       currency and many others.
+
+       _\br_\ba_\bt_\be_\b(_\b)
+
+       $rate_per_period = $am->_\br_\ba_\bt_\be_\b(_\b)
+
+       returns the interest rate per period.  Ignores any arguments.
+
+       _\bp_\br_\bi_\bn_\bc_\bi_\bp_\ba_\bl_\b(_\b)
+
+       $initial_value = $am->_\bp_\br_\bi_\bn_\bc_\bi_\bp_\ba_\bl_\b(_\b)
+
+       returns the initial principal being amortized.  Ignores any arguments.
+
+       _\bp_\be_\br_\bi_\bo_\bd_\bs_\b(_\b)
+
+       $number_of_periods = $am->_\bp_\be_\br_\bi_\bo_\bd_\bs_\b(_\b)
+
+       returns the number of periods in which the principal is being amor-
+       tized.  Ignores any arguments.
+
+       _\bp_\ba_\by_\bm_\be_\bn_\bt_\b(_\b)
+
+       $pmt = $am->_\bp_\ba_\by_\bm_\be_\bn_\bt_\b(_\b)
+
+       returns the payment per period.  This method will cache the value the
+       first time it is called.
+
+       b\bba\bal\bla\ban\bnc\bce\be(\b(n\bn)\b)
+
+       $balance = $am->balance(12);
+
+       Returns the balance of the amortization after the period given in the
+       argument
+
+       i\bin\bnt\bte\ber\bre\bes\bst\bt(\b(n\bn)\b)
+
+       $interest = $am->interest(12);
+
+       Returns the interest paid in the period given in the argument
+
+B\bBU\bUG\bGS\bS
+       This module uses perl's floating point for financial calculations.
+       This may introduce inaccuracies and/or make this module unsuitable for
+       serious financial applications.
+
+T\bTO\bOD\bDO\bO
+       Use Math::BigRat for the calculations.
+
+       Provide amortizers for present value, future value, annuities, etc.
+
+       Allow for caching calculated values.
+
+       Provide output methods and converters to various table modules.
+       HTML::Table, Text::Table, and Data::Table come to mind.
+
+       Write better test scripts.
+
+       Better checking for errors and out of range input.  Return undef in
+       these cases.
+
+       Use a locale dependent value to set an appropriate default for preci-
+       sion in the _\bn_\be_\bw_\b(_\b) method.
+
+L\bLI\bIC\bCE\bEN\bNS\bSE\bE
+       None.  This entire module is in the public domain.
+
+A\bAU\bUT\bTH\bHO\bOR\bR
+       Nathan Wagner <nw@hydaspes.if.org>
+
+       This entire module is written by me and placed into the public domain.
+
+
+
+perl v5.8.6                       2007-05-27          Finance::Amortization(3)
diff --git a/examples/htmlamtable b/examples/htmlamtable
new file mode 100755 (executable)
index 0000000..4b27998
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib '../blib/lib';
+
+use Finance::Amortization;
+use HTML::Table;
+
+# this program generates a 30 year amortization table on a $200,000
+# loan at 7%
+
+my $am = Finance::Amortization->new(principal => 200000, rate => .07,
+        periods => 30*12, compounding => 12, precision => 2);
+
+
+#balance, interest, payment
+
+my @cols = qw(Period Payment Interest Principal Balance);
+
+my $table = HTML::Table->new(0, 5);
+
+$table->addRow(@cols);
+$table->setRowHead(1);
+
+my $pmt = $am->payment;
+
+for(1 .. 30*12) {
+       my ($int, $bal) = ($am->interest($_),$am->balance($_));
+       $table->addRow($_, $pmt, $int, $pmt-$int, $bal);
+}
+
+$table->print;
diff --git a/lib/Finance/Amortization.pm b/lib/Finance/Amortization.pm
new file mode 100644 (file)
index 0000000..f752f66
--- /dev/null
@@ -0,0 +1,250 @@
+package Finance::Amortization;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.5';
+
+=head1 NAME
+
+Finance::Amortization - Simple Amortization Schedules
+
+=head1 SYNOPSIS
+
+use Finance::Amortization
+
+# make a new schedule
+
+$amortization = new Finance::Amortization(principal => 100000, rate = 0.06/12,
+       periods = 360);
+
+# get the balance after a the twelveth period
+
+$balance = $amortization->balance(12)
+
+# get the interest paid during the twelfth period
+
+$interest = $amortization->interest(12);
+
+=head1 DESCRIPTION
+
+Finance::Amortization is a simple object oriented interface to an
+amortization table.  Pass in the principal to be amortized, the number
+of payments to be made, and the interest rate per payment.  It will
+calculate the rest on demand, and provides a few methods to ask
+for the state of the table after a given number of periods.
+
+Finance::Amortization is written in pure perl and does not depend
+on any other modules.  It exports no functions; all access is via
+methods called on an amortization object.  (Except for new(), of course.)
+
+=cut
+
+=head2 new()
+
+$am = Finance::Amortization->new(principal => 0, rate => 0, periods => 0,
+       compounding => 12, precision => 2);
+
+Creates a new amortization object.  Calling interface is hash style.
+The fields principal, rate, and periods are available, all defaulting
+to zero.
+
+Compounding is a parameter which sets how many periods the rate is compounded
+over.  Thus, if each amortization period is one month, setting compounding
+to 12 (the default), will make the rate an annual rate.  That is, the
+interest rate per period is the rate specified, divided by the compounding.
+
+So, to get an amortization for 30 years on 200000, with a 6% annual rate,
+you would call new(principal => 200000, periods => 12*30, rate => 0.06),
+the compounding will default to 12, and so the rate will work out right
+for monthly payments.
+
+precision is used to specify the number of decimal places to round to
+when returning answers.  It defaults to 2, which is appropriate for
+US currency and many others.
+
+=cut
+
+sub new {
+       my $pkg = shift;
+       # bless package variables
+       my %conf = (
+               principal => 0.00,
+               rate => 0.00,
+               compounding => 12,
+               precision => 2, # how many decimals to round
+               @_
+       );
+       if (!defined $conf{'periods'}) {
+               $conf{'periods'} = $conf{'length'} * $conf{'compounding'};
+       }
+       if (defined($conf{'compounding'})) {
+               $conf{'rate'} /= $conf{'compounding'};
+       }
+
+       bless {
+               %conf
+       }, $pkg;
+}
+
+=head2 rate()
+
+$rate_per_period = $am->rate()
+
+returns the interest rate per period.  Ignores any arguments.
+
+=cut
+
+sub rate {
+       my $am = shift;
+       return $am->{'rate'};
+}      
+
+=head2 principal()
+
+$initial_value = $am->principal()
+
+returns the initial principal being amortized.  Ignores any arguments.
+
+=cut
+
+sub principal {
+       my $am = shift;
+       return sprintf('%.*f', $am->{'precision'}, $am->{'principal'});
+}      
+
+=head2 periods()
+
+$number_of_periods = $am->periods()
+
+returns the number of periods in which the principal is being amortized.
+Ignores any arguments.
+
+=cut
+
+sub periods {
+       my $am = shift;
+       return $am->{'periods'};
+}      
+
+#P = r*L*(1+r)^n/{(1+r)^n - 1}
+
+=head2 payment()
+
+$pmt = $am->payment()
+
+returns the payment per period.  This method will cache the value the
+first time it is called.
+
+=cut
+
+sub payment {
+       my $am = shift;
+
+       if ($am->{'payment'}) {
+               return $am->{'payment'}
+       }
+
+       my $r = $am->rate;
+       my $r1 = $r + 1;
+       my $n = $am->periods();
+       my $p = $am->principal;
+
+       if ($r == 0) {
+               return $am->{'payment'} = $p / $n;
+       }
+
+       $am->{'payment'} = sprintf('%.2f', $r * $p * $r1**$n / ($r1**$n-1));
+}
+
+=head2 balance(n)
+
+$balance = $am->balance(12);
+
+Returns the balance of the amortization after the period given in the
+argument
+
+=cut
+
+sub balance {
+       my $am = shift;
+       my $period = shift;
+       return $am->principal() if $period == 0;
+
+       return 0 if ($period < 1 or $period > $am->periods);
+
+       my $rate = $am->rate;
+       my $rate1 = $rate + 1;
+       my $periods = $am->periods();
+       my $principal = $am->principal;
+       my $pmt = $am->payment();
+
+       return sprintf('%.*f', $am->{'precision'},
+                $principal*$rate1**$period-$pmt*($rate1**$period - 1)/$rate);
+
+}
+
+=head2 interest(n)
+
+$interest = $am->interest(12);
+
+Returns the interest paid in the period given in the argument
+
+=cut
+
+sub interest {
+       my $am = shift;
+       my $period = shift;
+
+       return 0 if ($period < 1 or $period > $am->periods);
+
+       my $rate = $am->rate;
+
+       return sprintf('%.*f', $am->{'precision'},
+               $rate * $am->balance($period - 1));
+}
+
+=head1 BUGS
+
+This module uses perl's floating point for financial calculations.  This
+may introduce inaccuracies and/or make this module unsuitable for serious
+financial applications.
+
+Please report any bugs or feature requests to
+C<bug-finance-amortization at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Finance-Amortization>.
+
+=head1 TODO
+
+Use Math::BigRat for the calculations.
+
+Provide amortizers for present value, future value, annuities, etc.
+
+Allow for caching calculated values.
+
+Provide output methods and converters to various table modules.
+HTML::Table, Text::Table, and Data::Table come to mind.
+
+Write better test scripts.
+
+Better checking for errors and out of range input.  Return undef
+in these cases.
+
+Use a locale dependent value to set an appropriate default for precision
+in the new() method.
+
+=head1 LICENSE
+
+None.  This entire module is in the public domain.
+
+=head1 AUTHOR
+
+Nathan Wagner <nw@hydaspes.if.org>
+
+This entire module is written by me and placed into the public domain.
+
+=cut
+
+1;
+
+__END__
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644 (file)
index 0000000..2116354
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+       use_ok( 'Finance::Amortization' );
+}
+
+diag( "Testing Finance::Amortization $Finance::Amortization::VERSION, Perl $], $^X" );
diff --git a/t/basic.t b/t/basic.t
new file mode 100644 (file)
index 0000000..bcf874b
--- /dev/null
+++ b/t/basic.t
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use Test::More tests => 7;
+
+BEGIN { use_ok('Finance::Amortization') }
+
+my $am = Finance::Amortization->new(principal => 10000, rate => 0.12,
+       periods => 12 * 5);
+
+isa_ok($am, 'Finance::Amortization');
+
+is($am->periods(), 60, 'periods');
+is($am->principal, '10000.00', 'principal');
+is($am->rate, 0.01, 'rate');
+is($am->balance(1), 9877.56, 'balance 1');
+is($am->interest(1), '100.00', 'interest 1');
+
diff --git a/t/boilerplate.t b/t/boilerplate.t
new file mode 100644 (file)
index 0000000..fa70ec2
--- /dev/null
@@ -0,0 +1,48 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+sub not_in_file_ok {
+    my ($filename, %regex) = @_;
+    open my $fh, "<", $filename
+        or die "couldn't open $filename for reading: $!";
+
+    my %violated;
+
+    while (my $line = <$fh>) {
+        while (my ($desc, $regex) = each %regex) {
+            if ($line =~ $regex) {
+                push @{$violated{$desc}||=[]}, $.;
+            }
+        }
+    }
+
+    if (%violated) {
+        fail("$filename contains boilerplate text");
+        diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
+    } else {
+        pass("$filename contains no boilerplate text");
+    }
+}
+
+not_in_file_ok(README =>
+    "The README is used..."       => qr/The README is used/,
+    "'version information here'"  => qr/to provide version information/,
+);
+
+not_in_file_ok(Changes =>
+    "placeholder date/time"       => qr(Date/time)
+);
+
+sub module_boilerplate_ok {
+    my ($module) = @_;
+    not_in_file_ok($module =>
+        'the great new $MODULENAME'   => qr/ - The great new /,
+        'boilerplate description'     => qr/Quick summary of what the module/,
+        'stub function definition'    => qr/function[12]/,
+    );
+}
+
+module_boilerplate_ok('lib/Finance/Amortization.pm');
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644 (file)
index 0000000..703f91d
--- /dev/null
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+all_pod_coverage_ok();
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..976d7cd
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+all_pod_files_ok();