use super::super::*;
use crate::types::Value;
fn approx(a: Value, b: f64, tol: f64) -> bool {
if let Value::Number(n) = a { (n - b).abs() < tol } else { false }
}
#[test]
fn ipmt_first_period_interest() {
let args = [
Value::Number(0.1 / 12.0),
Value::Number(1.0),
Value::Number(12.0),
Value::Number(10000.0),
];
let result = ipmt_fn(&args);
assert!(approx(result.clone(), -83.3333, 1e-4), "got {:?}", result);
}
#[test]
fn ipmt_result_is_negative() {
let args = [
Value::Number(0.1 / 12.0),
Value::Number(1.0),
Value::Number(12.0),
Value::Number(10000.0),
];
if let Value::Number(n) = ipmt_fn(&args) {
assert!(n < 0.0, "interest payment should be negative, got {}", n);
} else {
panic!("expected Number");
}
}
#[test]
fn sln_basic() {
let args = [
Value::Number(10000.0),
Value::Number(1000.0),
Value::Number(5.0),
];
assert!(approx(sln_fn(&args), 1800.0, 1e-9));
}
#[test]
fn sln_zero_salvage() {
let args = [
Value::Number(6000.0),
Value::Number(0.0),
Value::Number(3.0),
];
assert!(approx(sln_fn(&args), 2000.0, 1e-9));
}
#[test]
fn fvschedule_two_rates() {
let args = [
Value::Number(1000.0),
Value::Array(vec![Value::Number(0.05), Value::Number(0.05)]),
];
assert!(approx(fvschedule_fn(&args), 1102.5, 1e-4));
}
#[test]
fn fvschedule_single_rate() {
let args = [
Value::Number(500.0),
Value::Array(vec![Value::Number(0.1)]),
];
assert!(approx(fvschedule_fn(&args), 550.0, 1e-9));
}
#[test]
fn mirr_positive_return() {
let args = [
Value::Array(vec![
Value::Number(-1000.0),
Value::Number(300.0),
Value::Number(400.0),
Value::Number(500.0),
]),
Value::Number(0.1),
Value::Number(0.12),
];
let result = mirr_fn(&args);
assert!(approx(result.clone(), 0.09816, 1e-4), "got {:?}", result);
}
#[test]
fn ppmt_period_1_interest_and_principal() {
let args = [
Value::Number(0.1 / 12.0),
Value::Number(1.0),
Value::Number(12.0),
Value::Number(10000.0),
];
let result = ppmt_fn(&args);
assert!(approx(result.clone(), -795.8255, 1e-3), "got {:?}", result);
}
#[test]
fn ppmt_zero_rate_equal_payments() {
for per in [1.0, 6.0, 12.0] {
let args = [
Value::Number(0.0),
Value::Number(per),
Value::Number(12.0),
Value::Number(12000.0),
];
let result = ppmt_fn(&args);
assert!(approx(result.clone(), -1000.0, 1e-9), "per={} got {:?}", per, result);
}
}
#[test]
fn ppmt_plus_ipmt_equals_pmt() {
let rate = Value::Number(0.1 / 12.0);
let nper = Value::Number(12.0);
let pv = Value::Number(10000.0);
for per in [1.0_f64, 5.0, 12.0] {
let period = Value::Number(per);
let pmt_args = [rate.clone(), period.clone(), nper.clone(), pv.clone()];
let ppmt = match ppmt_fn(&pmt_args) { Value::Number(n) => n, v => panic!("ppmt@per={}: {:?}", per, v) };
let ipmt = match ipmt_fn(&pmt_args) { Value::Number(n) => n, v => panic!("ipmt@per={}: {:?}", per, v) };
let total = ppmt + ipmt;
assert!((total - (-879.1588)).abs() < 1e-3, "per={} ppmt+ipmt={} want -879.1588", per, total);
}
}
#[test]
fn xirr_exact_one_year_ten_percent() {
let args = [
Value::Array(vec![Value::Number(-1000.0), Value::Number(1100.0)]),
Value::Array(vec![Value::Number(44927.0), Value::Number(45292.0)]),
];
let result = xirr_fn(&args);
assert!(approx(result.clone(), 0.1, 1e-6), "got {:?}", result);
}
#[test]
fn xirr_two_year_ten_percent() {
let args = [
Value::Array(vec![Value::Number(-1000.0), Value::Number(1210.0)]),
Value::Array(vec![Value::Number(44927.0), Value::Number(45657.0)]),
];
let result = xirr_fn(&args);
assert!(approx(result.clone(), 0.1, 1e-6), "got {:?}", result);
}
#[test]
fn xirr_with_explicit_guess() {
let args = [
Value::Array(vec![Value::Number(-1000.0), Value::Number(1100.0)]),
Value::Array(vec![Value::Number(44927.0), Value::Number(45292.0)]),
Value::Number(0.05),
];
let result = xirr_fn(&args);
assert!(approx(result.clone(), 0.1, 1e-6), "got {:?}", result);
}
#[test]
fn xnpv_break_even() {
let args = [
Value::Number(0.1),
Value::Array(vec![Value::Number(-1000.0), Value::Number(1100.0)]),
Value::Array(vec![Value::Number(44927.0), Value::Number(45292.0)]),
];
assert!(approx(xnpv_fn(&args), 0.0, 1e-4));
}
#[test]
fn xnpv_positive_npv() {
let args = [
Value::Number(0.1),
Value::Array(vec![Value::Number(-1000.0), Value::Number(1200.0)]),
Value::Array(vec![Value::Number(44927.0), Value::Number(45292.0)]),
];
assert!(approx(xnpv_fn(&args), 90.909, 1e-3));
}