1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::util::{float_close, WhenType, ATOL, RTOL};
use crate::{InterestPayment, Payment};
/// # Compute the payment against loan principal

/// ## Parameters
/// * `rate` : an interest rate compounded once per period
/// * `per` : the payment period to calculate the interest amount
/// * `nper` : number of compounding periods
/// * `pv` : a present value
/// * `fv` : a future value
/// * `when` : when payments are due [`WhenType`]. Defaults to `When::End`
///
/// ## Return:
/// * `ppmt`: the payment against loan principal
///
/// ## Example
/// ```rust
/// use rfinancial::*;
/// let ppmt = PrincipalPayment::from_tuple((0.1 / 12.0, 1, 24, 2000.0, 0.0, WhenType::End));
/// println!("{:#?}'s ppmt is {:?}", ppmt, ppmt.get());
/// ```

#[derive(Debug)]
pub struct PrincipalPayment {
    rate: f64,
    per: u32,
    nper: u32,
    pv: f64,
    fv: f64,
    when: WhenType,
}

impl PrincipalPayment {
    /// Instantiate a `PrincipalPayment` instance from a tuple of (`rate`, `per`, `nper`, `pv`, `fv` and `when`) in said order
    pub fn from_tuple(tup: (f64, u32, u32, f64, f64, WhenType)) -> Self {
        PrincipalPayment {
            rate: tup.0,
            per: tup.1,
            nper: tup.2,
            pv: tup.3,
            fv: tup.4,
            when: tup.5,
        }
    }

    fn ppmt(&self) -> Option<f64> {
        /*
            The total payment is made up of payment against principal plus interest.
            pmt = ppmt + ipmt
        */

        // total payment
        let total_pmt =
            Payment::from_tuple((self.rate, self.nper, self.pv, self.fv, self.when.clone())).get();
        // interest payment
        let ipmt = InterestPayment::from_tuple((
            self.rate,
            self.per,
            self.nper,
            self.pv,
            self.fv,
            self.when.clone(),
        ))
        .get();

        match ipmt {
            Some(value) => Some(total_pmt - value),
            None => None,
        }
    }

    /// Get the interet payment from an instance of `PrincipalPayment`
    pub fn get(&self) -> Option<f64> {
        self.ppmt()
    }
}

mod test {
    use super::*;

    #[test]
    fn test_ppmt_with_end() {
        let rate = 0.1 / 12.0;
        let per = 1;
        let nper = 60;
        let pv = 55000.0;
        let fv = 0.0;
        let when = WhenType::End;

        let ipmt = PrincipalPayment {
            rate,
            per,
            nper,
            pv,
            fv,
            when,
        };
        // npf.ppmt(0.1 / 12, 1, 60, 55000)
        // -710.254125786425
        let res = ipmt.get().unwrap();
        let tgt = -710.254125786425;
        assert!(
            float_close(res, tgt, RTOL, ATOL),
            "{:#?} v.s. {:#?}",
            res,
            tgt
        );
    }

    #[test]
    fn test_ppmt_with_begin() {
        let rate = 0.1 / 12.0;
        let per = 1;
        let nper = 60;
        let pv = 55000.0;
        let fv = 0.0;
        let when = WhenType::Begin;

        let ipmt = PrincipalPayment {
            rate,
            per,
            nper,
            pv,
            fv,
            when,
        };
        // npf.ppmt(0.1 / 12, 1, 60, 55000, 0, 'begin')
        // -1158.9297115237273
        let res = ipmt.get().unwrap();
        let tgt = -1158.9297115237273;
        assert!(
            float_close(res, tgt, RTOL, ATOL),
            "{:#?} v.s. {:#?}",
            res,
            tgt
        );
    }

    #[test]
    fn test_ppmt_zero_per() {
        let rate = 0.1 / 12.0;
        let per = 0;
        let nper = 24;
        let pv = 2000.0;
        let fv = 0.0;
        let when = WhenType::End;

        let ipmt = PrincipalPayment {
            rate,
            per,
            nper,
            pv,
            fv,
            when,
        };
        let res = ipmt.get();
        let tgt = None;
        assert_eq!(res, tgt, "{:#?} v.s. {:#?}", res, tgt);
    }
}