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}