1#[inline]
3pub fn cal_pv(val: f64, rate: f64, step: f64) -> f64 {
4 assert!(rate > -1.0, "Discount rate must be greater than -100%");
5 assert!(step >= 0.0, "Step should generally be non-negative");
6
7 val / (1.0 + rate).powf(step)
8}
9
10pub fn cal_pv_from_cf(cf: &[f64], rate: f64) -> f64 {
11 if rate <= -1.0 {
12 return f64::NAN;
13 }
14
15 let discount_multiplier = 1.0 / (1.0 + rate);
16 let mut current_discount = 1.0;
17
18 let mut total_pv = 0.0;
19 for &val in cf {
20 total_pv += val * current_discount;
21 current_discount *= discount_multiplier;
22 }
23
24 total_pv
25}
26
27pub fn pv_unispread(t_pv: f64, t_steps: i32, rate: f64, w_init: bool) -> f64 {
39 assert!(t_steps >= 0, "Steps must be non-negative");
40 if t_steps < 0 || rate <= -1.0 {
41 return f64::NAN;
42 }
43
44 let start_idx = if w_init { 0 } else { 1 };
45
46 if start_idx > t_steps {
47 return f64::NAN;
48 }
49
50 let base = 1.0 + rate;
51 let mut current_factor = base.powi(start_idx);
52 let mut fact = 0.0;
53
54 for _ in start_idx..=t_steps {
55 fact += current_factor;
56 current_factor *= base;
57 }
58
59 t_pv / fact
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_cal_pv_with_std_inputs_returns_expected_val() {
68 let out = cal_pv(100.00, 0.12, 1.0);
69 assert!(
70 (out - 89.2857).abs() < 0.1,
71 "Result: {out}, Expected: 89.2857",
72 );
73 }
74
75 #[test]
76 fn test_cal_pv_from_cf_with_std_inputs_returns_expected_val() {
77 let _in: [f64; 2] = [100.00, 100.00];
78 let out = cal_pv_from_cf(&_in, 0.12);
79 assert!(
80 (out - 189.2857).abs() < 0.1,
81 "Result: {out}, Expected: 189.2857",
82 );
83 }
84
85 #[test]
86 fn test_pv_unispread_with_init_returns_expected_val() {
87 let out = pv_unispread(100.00, 2, 0.12, true);
88 assert!(
89 (out - 29.6348).abs() < 0.1,
90 "Result: {out}, Expected: 29.6348",
91 );
92 }
93
94 #[test]
95 fn test_pv_unispread_without_init_returns_expected_val() {
96 let out = pv_unispread(100.00, 2, 0.12, false);
97 assert!(
98 (out - 42.1159).abs() < 0.1,
99 "Result: {out}, Expected: 42.1159",
100 );
101 }
102
103 #[test]
104 #[should_panic(expected = "Discount rate must be greater than -100%")]
105 fn test_cal_pv_with_invalid_rate_panics() {
106 let _ = cal_pv(100.0, -1.5, 1.0);
107 }
108
109 #[test]
110 #[should_panic(expected = "Step should generally be non-negative")]
111 fn test_cal_pv_with_negative_step_panics() {
112 let _ = cal_pv(100.0, 0.12, -1.0);
113 }
114
115 #[test]
116 fn test_cal_pv_from_cf_with_invalid_rate_returns_nan() {
117 let _in: [f64; 2] = [100.00, 100.00];
118 let out = cal_pv_from_cf(&_in, -1.5);
119 assert!(out.is_nan());
120 }
121
122 #[test]
123 fn test_pv_unispread_with_invalid_rate_returns_nan() {
124 let out = pv_unispread(100.0, 2, -1.5, true);
125 assert!(out.is_nan());
126 }
127
128 #[test]
129 #[should_panic(expected = "Steps must be non-negative")]
130 fn test_pv_unispread_with_negative_steps_panics() {
131 let _ = pv_unispread(100.0, -2, 0.12, true);
132 }
133
134 #[test]
135 fn test_pv_unispread_with_start_idx_greater_than_t_steps_returns_nan() {
136 let out = pv_unispread(100.0, 0, 0.12, false);
138 assert!(out.is_nan());
139 }
140}