xlsxfin/
lib.rs

1pub fn pmt(rate: f64, nper: i64, pv: i64, fv: i64, payment_flag: bool) -> f64 {
2    if nper == 0 {
3        return 0.0;
4    }
5
6    let nper_f64: f64 = nper as f64;
7    let pv_f64: f64 = pv as f64;
8    let fv_f64: f64 = fv as f64;
9
10    if rate == 0.0 {
11        return -(pv_f64 + fv_f64) / nper_f64;
12    }
13
14    let pvif = (1.0 + rate).powf(nper_f64);
15    let pmt = (rate / (pvif - 1.0)) * -(pv_f64 * pvif + fv_f64);
16
17    if !payment_flag {
18        return pmt;
19    }
20    pmt / (1.0 + rate)
21}
22
23#[cfg(test)]
24mod tests_pmt {
25    use super::*;
26
27    #[derive(Debug)]
28    struct TestArgs {
29        rate: f64,
30        nper: i64,
31        pv: i64,
32        fv: i64,
33        payment_flag: bool,
34    }
35
36    struct TestData {
37        args: TestArgs,
38        expected: f64,
39    }
40
41    #[test]
42    fn test_nper_is_0() {
43        let actual = pmt(0.3, 0, 100_000, 0, false);
44        assert_eq!(actual, 0.0);
45    }
46
47    #[test]
48    fn test_rate_is_0() {
49        let test_cases: [TestData; 4] = [
50            TestData {
51                args: TestArgs {
52                    rate: 0.0,
53                    nper: 36,
54                    pv: 100_000,
55                    fv: 0,
56                    payment_flag: false,
57                },
58                expected: -2_777.777777777778,
59            },
60            TestData {
61                args: TestArgs {
62                    rate: 0.0,
63                    nper: 36,
64                    pv: 100_000,
65                    fv: 0,
66                    payment_flag: true,
67                },
68                expected: -2_777.777777777778,
69            },
70            TestData {
71                args: TestArgs {
72                    rate: 0.0,
73                    nper: 36,
74                    pv: 100_000,
75                    fv: 1_000,
76                    payment_flag: false,
77                },
78                expected: -2805.5555555555557,
79            },
80            TestData {
81                args: TestArgs {
82                    rate: 0.0,
83                    nper: 36,
84                    pv: 100_000,
85                    fv: 1_000,
86                    payment_flag: true,
87                },
88                expected: -2805.5555555555557,
89            },
90        ];
91        for t in &test_cases {
92            let actual = pmt(
93                t.args.rate,
94                t.args.nper,
95                t.args.pv,
96                t.args.fv,
97                t.args.payment_flag,
98            );
99            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
100        }
101    }
102
103    #[test]
104    fn test_rate_is_over_0() {
105        let test_cases: [TestData; 4] = [
106            TestData {
107                args: TestArgs {
108                    rate: 0.3,
109                    nper: 36,
110                    pv: 100_000,
111                    fv: 0,
112                    payment_flag: false,
113                },
114                expected: -30_002.37243823623,
115            },
116            TestData {
117                args: TestArgs {
118                    rate: 0.3,
119                    nper: 36,
120                    pv: 100_000,
121                    fv: 0,
122                    payment_flag: true,
123                },
124                expected: -23078.748029412483,
125            },
126            TestData {
127                args: TestArgs {
128                    rate: 0.3,
129                    nper: 36,
130                    pv: 100_000,
131                    fv: 1_000,
132                    payment_flag: false,
133                },
134                expected: -30_002.396162618596,
135            },
136            TestData {
137                args: TestArgs {
138                    rate: 0.3,
139                    nper: 36,
140                    pv: 100_000,
141                    fv: 1_000,
142                    payment_flag: true,
143                },
144                expected: -23078.76627893738,
145            },
146        ];
147        for t in &test_cases {
148            let actual = pmt(
149                t.args.rate,
150                t.args.nper,
151                t.args.pv,
152                t.args.fv,
153                t.args.payment_flag,
154            );
155            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
156        }
157    }
158}
159
160pub fn ipmt(rate: f64, per: i64, nper: i64, pv: i64, fv: i64, payment_flag: bool) -> f64 {
161    if nper == 0 {
162        return 0.0;
163    }
164
165    if per == 0 {
166        return 0.0;
167    }
168
169    if rate < 0.0 {
170        return 0.0;
171    }
172
173    let pmt = pmt(rate, nper, pv, fv, false);
174    let per_sub_1_f64 = (per - 1) as f64;
175
176    let n = if rate.abs() > 0.5 {
177        (1.0 + rate).powf(per_sub_1_f64)
178    } else {
179        (per_sub_1_f64 * (1.0 + rate).ln()).exp()
180    };
181
182    let m = (per_sub_1_f64 * (1.0 + rate).ln()).exp() - 1.0;
183
184    let ip = -((pv as f64) * n * rate + pmt * m);
185    if !payment_flag {
186        return ip;
187    }
188    ip / (1.0 + rate)
189}
190
191#[cfg(test)]
192mod tests_ipmt {
193    use super::*;
194
195    #[derive(Debug)]
196    struct TestArgs {
197        rate: f64,
198        per: i64,
199        nper: i64,
200        pv: i64,
201        fv: i64,
202        payment_flag: bool,
203    }
204
205    struct TestData {
206        args: TestArgs,
207        expected: f64,
208    }
209
210    #[test]
211    fn test_per_is_0() {
212        let actual = ipmt(0.3, 0, 36, 100_000, 0, false);
213        assert_eq!(actual, 0.0);
214    }
215
216    #[test]
217    fn test_nper_is_0() {
218        let actual = ipmt(0.3, 3, 0, 100_000, 0, false);
219        assert_eq!(actual, 0.0);
220    }
221
222    #[test]
223    fn test_rate_less_than_0() {
224        let actual = ipmt(-0.1, 3, 36, 100_000, 0, false);
225        assert_eq!(actual, 0.0);
226    }
227
228    #[test]
229    fn test_rate_is_over_0() {
230        let test_cases: [TestData; 8] = [
231            TestData {
232                args: TestArgs {
233                    rate: 0.1,
234                    per: 2,
235                    nper: 36,
236                    pv: 800_000,
237                    fv: 0,
238                    payment_flag: false,
239                },
240                expected: -79_732.55489453014,
241            },
242            TestData {
243                args: TestArgs {
244                    rate: 0.1,
245                    per: 2,
246                    nper: 36,
247                    pv: 800_000,
248                    fv: 0,
249                    payment_flag: true,
250                },
251                expected: -72_484.14081320922,
252            },
253            TestData {
254                args: TestArgs {
255                    rate: 0.1,
256                    per: 2,
257                    nper: 36,
258                    pv: 800_000,
259                    fv: 1_000,
260                    payment_flag: false,
261                },
262                expected: -79_732.22058814831,
263            },
264            TestData {
265                args: TestArgs {
266                    rate: 0.1,
267                    per: 2,
268                    nper: 36,
269                    pv: 800_000,
270                    fv: 1_000,
271                    payment_flag: true,
272                },
273                expected: -72_483.83689831664,
274            },
275            TestData {
276                args: TestArgs {
277                    rate: 0.6,
278                    per: 2,
279                    nper: 36,
280                    pv: 800_000,
281                    fv: 0,
282                    payment_flag: false,
283                },
284                expected: -479_999.9870856327,
285            },
286            TestData {
287                args: TestArgs {
288                    rate: 0.6,
289                    per: 2,
290                    nper: 36,
291                    pv: 800_000,
292                    fv: 0,
293                    payment_flag: true,
294                },
295                expected: -299_999.99192852044,
296            },
297            TestData {
298                args: TestArgs {
299                    rate: 0.6,
300                    per: 2,
301                    nper: 36,
302                    pv: 800_000,
303                    fv: 1_000,
304                    payment_flag: false,
305                },
306                expected: -479_999.9870694897,
307            },
308            TestData {
309                args: TestArgs {
310                    rate: 0.6,
311                    per: 2,
312                    nper: 36,
313                    pv: 800_000,
314                    fv: 1_000,
315                    payment_flag: true,
316                },
317                expected: -299_999.9919184311,
318            },
319        ];
320        for t in &test_cases {
321            let actual = ipmt(
322                t.args.rate,
323                t.args.per,
324                t.args.nper,
325                t.args.pv,
326                t.args.fv,
327                t.args.payment_flag,
328            );
329            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
330        }
331    }
332}
333
334pub fn fv(rate: f64, nper: i64, pmt: f64, pv: i64, payment_flag: bool) -> f64 {
335    let pv_f64 = pv as f64;
336    let nper_f64 = nper as f64;
337
338    if rate == 0.0 {
339        return -(pv_f64 + pmt * nper_f64);
340    }
341
342    let term = (1.0 + rate).powf(nper_f64);
343    if payment_flag {
344        return -(pv_f64 * term + (pmt * (1.0 + rate) * (term - 1.0)) / rate);
345    }
346    -(pv_f64 * term + (pmt * (term - 1.0)) / rate)
347}
348
349#[cfg(test)]
350mod tests_fv {
351    use super::*;
352
353    #[derive(Debug)]
354    struct TestArgs {
355        rate: f64,
356        nper: i64,
357        pmt: f64,
358        pv: i64,
359        payment_flag: bool,
360    }
361
362    struct TestData {
363        args: TestArgs,
364        expected: f64,
365    }
366
367    #[test]
368    fn test_rate_is_0() {
369        let test_cases: [TestData; 4] = [
370            TestData {
371                args: TestArgs {
372                    rate: 0.0,
373                    nper: 12,
374                    pmt: 10_000.0,
375                    pv: 0,
376                    payment_flag: false,
377                },
378                expected: -120_000.0,
379            },
380            TestData {
381                args: TestArgs {
382                    rate: 0.0,
383                    nper: 12,
384                    pmt: 10_000.0,
385                    pv: 0,
386                    payment_flag: true,
387                },
388                expected: -120_000.0,
389            },
390            TestData {
391                args: TestArgs {
392                    rate: 0.0,
393                    nper: 12,
394                    pmt: 10_000.0,
395                    pv: 1_000,
396                    payment_flag: false,
397                },
398                expected: -121_000.0,
399            },
400            TestData {
401                args: TestArgs {
402                    rate: 0.0,
403                    nper: 12,
404                    pmt: 10_000.0,
405                    pv: 1_000,
406                    payment_flag: true,
407                },
408                expected: -121_000.0,
409            },
410        ];
411        for t in &test_cases {
412            let actual = fv(
413                t.args.rate,
414                t.args.nper,
415                t.args.pmt,
416                t.args.pv,
417                t.args.payment_flag,
418            );
419            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
420        }
421    }
422
423    #[test]
424    fn test_rate_is_over_0() {
425        let test_cases: [TestData; 4] = [
426            TestData {
427                args: TestArgs {
428                    rate: 0.1,
429                    nper: 12,
430                    pmt: 10_000.0,
431                    pv: 0,
432                    payment_flag: false,
433                },
434                expected: -213_842.83767210032,
435            },
436            TestData {
437                args: TestArgs {
438                    rate: 0.1,
439                    nper: 12,
440                    pmt: 10_000.0,
441                    pv: 0,
442                    payment_flag: true,
443                },
444                expected: -235_227.12143931031,
445            },
446            TestData {
447                args: TestArgs {
448                    rate: 0.1,
449                    nper: 12,
450                    pmt: 10_000.0,
451                    pv: 1_000,
452                    payment_flag: false,
453                },
454                expected: -216_981.26604882133,
455            },
456            TestData {
457                args: TestArgs {
458                    rate: 0.1,
459                    nper: 12,
460                    pmt: 10_000.0,
461                    pv: 1_000,
462                    payment_flag: true,
463                },
464                expected: -238_365.54981603133,
465            },
466        ];
467        for t in &test_cases {
468            let actual = fv(
469                t.args.rate,
470                t.args.nper,
471                t.args.pmt,
472                t.args.pv,
473                t.args.payment_flag,
474            );
475            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
476        }
477    }
478}
479
480pub fn ppmt(rate: f64, per: i64, nper: i64, pv: i64, fv: i64, payment_flag: bool) -> f64 {
481    if per < 1 || per > nper {
482        return 0.0;
483    }
484    let pmt = pmt(rate, nper, pv, fv, payment_flag);
485    let ipmt = ipmt(rate, per, nper, pv, fv, payment_flag);
486    pmt - ipmt
487}
488
489#[cfg(test)]
490mod tests_ppmt {
491    use super::*;
492
493    #[derive(Debug)]
494    struct TestArgs {
495        rate: f64,
496        per: i64,
497        nper: i64,
498        pv: i64,
499        fv: i64,
500        payment_flag: bool,
501    }
502
503    struct TestData {
504        args: TestArgs,
505        expected: f64,
506    }
507
508    #[test]
509    fn test_per_less_than_1() {
510        let test_cases: [TestData; 2] = [
511            TestData {
512                args: TestArgs {
513                    rate: 0.1,
514                    per: 0,
515                    nper: 10,
516                    pv: 800_000,
517                    fv: 0,
518                    payment_flag: false,
519                },
520                expected: 0.0,
521            },
522            TestData {
523                args: TestArgs {
524                    rate: 0.1,
525                    per: -1,
526                    nper: 10,
527                    pv: 800_000,
528                    fv: 0,
529                    payment_flag: false,
530                },
531                expected: 0.0,
532            },
533        ];
534        for t in &test_cases {
535            let actual = ppmt(
536                t.args.rate,
537                t.args.per,
538                t.args.nper,
539                t.args.pv,
540                t.args.fv,
541                t.args.payment_flag,
542            );
543            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
544        }
545    }
546
547    #[test]
548    fn test_per_ge_nper_plus_1() {
549        let test_cases: [TestData; 2] = [
550            TestData {
551                args: TestArgs {
552                    rate: 0.1,
553                    per: 11,
554                    nper: 10,
555                    pv: 800_000,
556                    fv: 0,
557                    payment_flag: false,
558                },
559                expected: 0.0,
560            },
561            TestData {
562                args: TestArgs {
563                    rate: 0.1,
564                    per: 15,
565                    nper: 10,
566                    pv: 800_000,
567                    fv: 0,
568                    payment_flag: false,
569                },
570                expected: 0.0,
571            },
572        ];
573        for t in &test_cases {
574            let actual = ppmt(
575                t.args.rate,
576                t.args.per,
577                t.args.nper,
578                t.args.pv,
579                t.args.fv,
580                t.args.payment_flag,
581            );
582            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
583        }
584    }
585
586    #[test]
587    fn test_per_ge_1_and_per_lt_nper_plus_1() {
588        let test_cases: [TestData; 4] = [
589            TestData {
590                args: TestArgs {
591                    rate: 0.1,
592                    per: 12,
593                    nper: 36,
594                    pv: 800_000,
595                    fv: 0,
596                    payment_flag: false,
597                },
598                expected: -7_630.520983834242,
599            },
600            TestData {
601                args: TestArgs {
602                    rate: 0.1,
603                    per: 12,
604                    nper: 36,
605                    pv: 800_000,
606                    fv: 1_000,
607                    payment_flag: false,
608                },
609                expected: -7_640.059135064032,
610            },
611            TestData {
612                args: TestArgs {
613                    rate: 0.1,
614                    per: 12,
615                    nper: 36,
616                    pv: 800_000,
617                    fv: 0,
618                    payment_flag: true,
619                },
620                expected: -6_936.837258031126,
621            },
622            TestData {
623                args: TestArgs {
624                    rate: 0.1,
625                    per: 12,
626                    nper: 36,
627                    pv: 800_000,
628                    fv: 1_000,
629                    payment_flag: true,
630                },
631                expected: -6_945.50830460366,
632            },
633        ];
634        for t in &test_cases {
635            let actual = ppmt(
636                t.args.rate,
637                t.args.per,
638                t.args.nper,
639                t.args.pv,
640                t.args.fv,
641                t.args.payment_flag,
642            );
643            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
644        }
645    }
646}
647
648pub fn cumipmt(rate: f64, nper: i64, pv: i64, start: i64, end: i64, payment_flag: bool) -> f64 {
649    if rate <= 0.0 || nper <= 0 || pv <= 0 {
650        return 0.0;
651    }
652
653    if start < 1 || end < 1 || start > end {
654        return 0.0;
655    }
656
657    let pmt = pmt(rate, nper, pv, 0, payment_flag);
658    let mut interest = 0.0;
659    let mut mut_start = start;
660    if start == 1 {
661        if !payment_flag {
662            interest = -pv as f64;
663        }
664        mut_start += 1;
665    }
666    for i in mut_start..end + 1 {
667        interest += if payment_flag {
668            fv(rate, i - 2, pmt, pv, true) - pmt
669        } else {
670            fv(rate, i - 1, pmt, pv, false)
671        };
672    }
673    interest * rate
674}
675
676#[cfg(test)]
677mod tests_cumipmt {
678    use super::*;
679
680    #[derive(Debug)]
681    struct TestArgs {
682        rate: f64,
683        nper: i64,
684        pv: i64,
685        start: i64,
686        end: i64,
687        payment_flag: bool,
688    }
689
690    struct TestData {
691        args: TestArgs,
692        expected: f64,
693    }
694
695    #[test]
696    fn test_rate_le_0() {
697        let test_cases: [TestData; 2] = [
698            TestData {
699                args: TestArgs {
700                    rate: 0.0,
701                    nper: 36,
702                    pv: 800_000,
703                    start: 6,
704                    end: 12,
705                    payment_flag: false,
706                },
707                expected: 0.0,
708            },
709            TestData {
710                args: TestArgs {
711                    rate: -1.0,
712                    nper: 36,
713                    pv: 800_000,
714                    start: 6,
715                    end: 12,
716                    payment_flag: false,
717                },
718                expected: 0.0,
719            },
720        ];
721        for t in &test_cases {
722            let actual = cumipmt(
723                t.args.rate,
724                t.args.nper,
725                t.args.pv,
726                t.args.start,
727                t.args.end,
728                t.args.payment_flag,
729            );
730            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
731        }
732    }
733
734    #[test]
735    fn test_nper_le_0() {
736        let test_cases: [TestData; 2] = [
737            TestData {
738                args: TestArgs {
739                    rate: 0.1,
740                    nper: 0,
741                    pv: 800_000,
742                    start: 6,
743                    end: 12,
744                    payment_flag: false,
745                },
746                expected: 0.0,
747            },
748            TestData {
749                args: TestArgs {
750                    rate: 0.1,
751                    nper: -1,
752                    pv: 800_000,
753                    start: 6,
754                    end: 12,
755                    payment_flag: false,
756                },
757                expected: 0.0,
758            },
759        ];
760        for t in &test_cases {
761            let actual = cumipmt(
762                t.args.rate,
763                t.args.nper,
764                t.args.pv,
765                t.args.start,
766                t.args.end,
767                t.args.payment_flag,
768            );
769            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
770        }
771    }
772
773    #[test]
774    fn test_pv_le_0() {
775        let test_cases: [TestData; 2] = [
776            TestData {
777                args: TestArgs {
778                    rate: 0.1,
779                    nper: 36,
780                    pv: 0,
781                    start: 6,
782                    end: 12,
783                    payment_flag: false,
784                },
785                expected: 0.0,
786            },
787            TestData {
788                args: TestArgs {
789                    rate: 0.1,
790                    nper: 36,
791                    pv: -1,
792                    start: 6,
793                    end: 12,
794                    payment_flag: false,
795                },
796                expected: 0.0,
797            },
798        ];
799        for t in &test_cases {
800            let actual = cumipmt(
801                t.args.rate,
802                t.args.nper,
803                t.args.pv,
804                t.args.start,
805                t.args.end,
806                t.args.payment_flag,
807            );
808            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
809        }
810    }
811
812    #[test]
813    fn test_start_or_end_is_an_invalid_value() {
814        let test_cases: [TestData; 3] = [
815            TestData {
816                args: TestArgs {
817                    rate: 0.1,
818                    nper: 36,
819                    pv: 800_000,
820                    start: 0,
821                    end: 12,
822                    payment_flag: false,
823                },
824                expected: 0.0,
825            },
826            TestData {
827                args: TestArgs {
828                    rate: 0.1,
829                    nper: 36,
830                    pv: 800_000,
831                    start: 1,
832                    end: 0,
833                    payment_flag: false,
834                },
835                expected: 0.0,
836            },
837            TestData {
838                args: TestArgs {
839                    rate: 0.1,
840                    nper: 36,
841                    pv: 800_000,
842                    start: 10,
843                    end: 9,
844                    payment_flag: false,
845                },
846                expected: 0.0,
847            },
848        ];
849        for t in &test_cases {
850            let actual = cumipmt(
851                t.args.rate,
852                t.args.nper,
853                t.args.pv,
854                t.args.start,
855                t.args.end,
856                t.args.payment_flag,
857            );
858            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
859        }
860    }
861
862    #[test]
863    fn test_calculate() {
864        let test_cases: [TestData; 4] = [
865            TestData {
866                args: TestArgs {
867                    rate: 0.1,
868                    nper: 36,
869                    pv: 800_000,
870                    start: 6,
871                    end: 12,
872                    payment_flag: true,
873                },
874                expected: -488_961.5711288557,
875            },
876            TestData {
877                args: TestArgs {
878                    rate: 0.1,
879                    nper: 36,
880                    pv: 800_000,
881                    start: 6,
882                    end: 12,
883                    payment_flag: false,
884                },
885                expected: -537_857.7282417413,
886            },
887            TestData {
888                args: TestArgs {
889                    rate: 0.1,
890                    nper: 36,
891                    pv: 800_000,
892                    start: 1,
893                    end: 12,
894                    payment_flag: true,
895                },
896                expected: -777_183.8112556307,
897            },
898            TestData {
899                args: TestArgs {
900                    rate: 0.1,
901                    nper: 36,
902                    pv: 800_000,
903                    start: 1,
904                    end: 12,
905                    payment_flag: false,
906                },
907                expected: -934_902.1923811939,
908            },
909        ];
910        for t in &test_cases {
911            let actual = cumipmt(
912                t.args.rate,
913                t.args.nper,
914                t.args.pv,
915                t.args.start,
916                t.args.end,
917                t.args.payment_flag,
918            );
919            assert_eq!(actual, t.expected, "args: {:#?}", t.args);
920        }
921    }
922}