diff options
author | Adam House <a@context.town> | 2024-06-21 08:23:29 -0500 |
---|---|---|
committer | Adam House <a@context.town> | 2024-06-21 08:23:29 -0500 |
commit | 828104b6e401b3e7b6f963960e4e3744fb855d5d (patch) | |
tree | a626702e86737fb28addd44cdfff51c7c991e321 |
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | lpd.zig | 87 |
4 files changed, 127 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1be49ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lpd +lpd.o @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Adam House + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..034231f --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# lpd + +simple **l**ump-sum **p**ay**d**own calculator + +`lpd` reports how many months remain on a given loan, based on the provided +principal balance, interest rate and monthly payment, then advises the payment +amounts that, if made now in addition to the ordinary monthly payments, would +reduce the loan term to the specified number of months. It will report up to +twelve of these. + +Because math is cool, we avoid recursion. + +## Installation & Usage + +Compile with [Zig](https://ziglang.org/), i.e. `zig build-exe lpd.zig`. + +Learn to use with `lpd -h`. @@ -0,0 +1,87 @@ +const std = @import("std"); +const math = std.math; + +// Calculates number of months to pay off a balance +fn n(b: f64, r: f64, p: f64) f64 { + if (r == 0) { + return @ceil(b / p); + } + const i = r / 12; + return @ceil(-(math.log(f64, math.e, 1 - (i * b / p))) / + math.log(f64, math.e, 1 + i)); +} + +// Calculates lump sum required to pay off a balance in specified time `t` +fn l(b: f64, r: f64, p: f64, t: f64) f64 { + if (r == 0) { + return @max(b - p * t, 0); + } + const i = r / 12; + return b - (p * (1 - math.pow(f64, 1 + i, -t)) / i); +} + +fn u() void { + std.debug.print("usage: lpd -b <balance> -p <payment> -r <rate>\n" ++ + " -b Principal balance\n" ++ + " -p Monthly payment\n" ++ + " -r Annual interest rate\n" ++ + " -h Show this help message\n", .{}); +} + +pub fn main() !void { + const a = try std.process.argsAlloc(std.heap.page_allocator); + defer std.process.argsFree(std.heap.page_allocator, a); + + var b: f64 = 0; + var p: f64 = 0; + var r: f64 = 0; + + var i: usize = 1; + while (i < a.len) : (i += 1) { + const f = a[i]; + if (std.mem.eql(u8, f, "-h")) { + u(); + return; + } else if (i + 1 < a.len) { + const v = std.fmt.parseFloat(f64, a[i + 1]) catch { + u(); + return; + }; + if (std.mem.eql(u8, f, "-b")) { + b = v; + } else if (std.mem.eql(u8, f, "-p")) { + p = v; + } else if (std.mem.eql(u8, f, "-r")) { + r = v / 100; + } else { + u(); + return; + } + i += 1; + } else { + u(); + return; + } + } + + if (b == 0 or p == 0) { + u(); + return; + } + + if (p <= r / 12 * b) { + std.debug.print("payment must cover interest\n", .{}); + return; + } + + const m = n(b, r, p); + try std.io.getStdOut().writer().print("{d} remain\n", .{m}); + + const min = @max(m - 12, 0); + var j = m - 1; + while (j >= min) : (j -= 1) { + const s = l(b, r, p, j); + try std.io.getStdOut().writer() + .print("pay ${d:.2} for {d}\n", .{ s, j }); + } +} |