1use crate::{
2 get_f64, get_u32, get_when, Error, InterestPayment, ParaMap, Payment, Result, WhenType,
3};
4#[derive(Debug)]
25pub struct PrincipalPayment {
26 rate: f64,
27 per: u32,
28 nper: u32,
29 pv: f64,
30 fv: f64,
31 when: WhenType,
32}
33
34impl PrincipalPayment {
35 pub fn from_tuple(tup: (f64, u32, u32, f64, f64, WhenType)) -> Self {
37 PrincipalPayment {
38 rate: tup.0,
39 per: tup.1,
40 nper: tup.2,
41 pv: tup.3,
42 fv: tup.4,
43 when: tup.5,
44 }
45 }
46
47 pub fn from_map(map: ParaMap) -> Result<Self> {
50 let op = |err: Error| {
51 Error::OtherError(format!(
52 "Failed construct an instance of `PrincipalPayment` from: `{:?}` <- {}",
53 map, err
54 ))
55 };
56
57 let rate = get_f64(&map, "rate").map_err(|err| op(err))?;
58 let per = get_u32(&map, "per").map_err(|err| op(err))?;
59 let nper = get_u32(&map, "nper").map_err(|err| op(err))?;
60 let pv = get_f64(&map, "pv").map_err(|err| op(err))?;
61 let fv = get_f64(&map, "fv").map_err(|err| op(err))?;
62 let when = get_when(&map, "when").map_err(|err| op(err))?;
63 Ok(PrincipalPayment {
64 rate,
65 per,
66 nper,
67 pv,
68 fv,
69 when,
70 })
71 }
72
73 fn ppmt(&self) -> Result<Option<f64>> {
74 let total_pmt =
81 Payment::from_tuple((self.rate, self.nper, self.pv, self.fv, self.when.clone()))
82 .get()?;
83 let ipmt = InterestPayment::from_tuple((
85 self.rate,
86 self.per,
87 self.nper,
88 self.pv,
89 self.fv,
90 self.when.clone(),
91 ))
92 .get()?;
93
94 let ppmt = match ipmt {
95 Some(value) => Some(total_pmt - value),
96 None => None,
97 };
98
99 Ok(ppmt)
100 }
101
102 pub fn get(&self) -> Result<Option<f64>> {
104 self.ppmt()
105 }
106}
107
108#[allow(unused_imports)]
109#[cfg(test)]
110mod tests {
111 use crate::*;
112
113 #[test]
114 fn test_ppmt_from_tuple() {
115 let ppmt = PrincipalPayment::from_tuple((0.1 / 12.0, 1, 60, 55000.0, 0.0, WhenType::End));
116 let res = ppmt.get().unwrap().unwrap();
119 let tgt = -710.254125786425;
120 assert!(
121 float_close(res, tgt, RTOL, ATOL),
122 "{:#?} v.s. {:#?}",
123 res,
124 tgt
125 );
126 }
127
128 #[test]
129 fn test_ppmt_from_map() {
130 let mut map = ParaMap::new();
131 map.insert("rate".into(), ParaType::F64(0.1 / 12.0));
132 map.insert("per".into(), ParaType::U32(1));
133 map.insert("nper".into(), ParaType::U32(60));
134 map.insert("pv".into(), ParaType::F64(55000.0));
135 map.insert("fv".into(), ParaType::F64(0.0));
136 map.insert("when".into(), ParaType::When(WhenType::End));
137 let ppmt = PrincipalPayment::from_map(map).unwrap();
138 let res = ppmt.get().unwrap().unwrap();
141 let tgt = -710.254125786425;
142 assert!(
143 float_close(res, tgt, RTOL, ATOL),
144 "{:#?} v.s. {:#?}",
145 res,
146 tgt
147 );
148 }
149
150 #[test]
151 fn test_ppmt_with_end() {
152 let rate = 0.1 / 12.0;
153 let per = 1;
154 let nper = 60;
155 let pv = 55000.0;
156 let fv = 0.0;
157 let when = WhenType::End;
158
159 let ppmt = PrincipalPayment {
160 rate,
161 per,
162 nper,
163 pv,
164 fv,
165 when,
166 };
167 let res = ppmt.get().unwrap().unwrap();
170 let tgt = -710.254125786425;
171 assert!(
172 float_close(res, tgt, RTOL, ATOL),
173 "{:#?} v.s. {:#?}",
174 res,
175 tgt
176 );
177 }
178
179 #[test]
180 fn test_ppmt_with_begin() {
181 let rate = 0.1 / 12.0;
182 let per = 1;
183 let nper = 60;
184 let pv = 55000.0;
185 let fv = 0.0;
186 let when = WhenType::Begin;
187
188 let ppmt = PrincipalPayment {
189 rate,
190 per,
191 nper,
192 pv,
193 fv,
194 when,
195 };
196 let res = ppmt.get().unwrap().unwrap();
199 let tgt = -1158.9297115237273;
200 assert!(
201 float_close(res, tgt, RTOL, ATOL),
202 "{:#?} v.s. {:#?}",
203 res,
204 tgt
205 );
206 }
207
208 #[test]
209 fn test_ppmt_zero_per() {
210 let rate = 0.1 / 12.0;
211 let per = 0;
212 let nper = 24;
213 let pv = 2000.0;
214 let fv = 0.0;
215 let when = WhenType::End;
216
217 let ppmt = PrincipalPayment {
218 rate,
219 per,
220 nper,
221 pv,
222 fv,
223 when,
224 };
225 let res = ppmt.get().unwrap();
226 let tgt = None;
227 assert_eq!(res, tgt, "{:#?} v.s. {:#?}", res, tgt);
228 }
229
230 #[test]
231 fn test_ppmt_err() {
232 let mut map = ParaMap::new();
233 map.insert("Rate".into(), ParaType::F64(0.1 / 12.0));
234 map.insert("per".into(), ParaType::U32(1));
235 map.insert("nper".into(), ParaType::U32(60));
236 map.insert("pv".into(), ParaType::F64(55000.0));
237 map.insert("fv".into(), ParaType::F64(0.0));
238 map.insert("when".into(), ParaType::When(WhenType::End));
239 let ppmt = PrincipalPayment::from_map(map);
240 let cond = ppmt.is_err();
241 assert!(cond);
242 }
243}