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 });
}
}