diff --git a/LendingHelpers__test_8cpp_source.html b/LendingHelpers__test_8cpp_source.html
index 3082de5d6f..2dc7b80bae 100644
--- a/LendingHelpers__test_8cpp_source.html
+++ b/LendingHelpers__test_8cpp_source.html
@@ -695,576 +695,564 @@ $(document).ready(function() { init_codefold(0); });
592 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
593 Number const overpaymentAmount{50};
-
+ 595 auto const overpaymentComponents = computeOverpaymentComponents(
-
+
599 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
- 601 Number const periodicPayment = loanProperites.periodicPayment;
-
- 603 auto const ret = tryOverpayment(
-
-
- 606 overpaymentComponents,
- 607 loanProperites.loanState,
-
-
-
-
-
+ 601 auto const ret = tryOverpayment(
+
+
+ 604 overpaymentComponents,
+ 605 loanProperties.loanState,
+ 606 loanProperties.periodicPayment,
+
+
+
+
+
+
-
-
- 616 auto const& [actualPaymentParts, newLoanProperties] = *ret;
- 617 auto const& newState = newLoanProperties.loanState;
-
-
-
- 621 actualPaymentParts.valueChange == 0,
- 622 " valueChange mismatch: expected 0, got " +
to_string(actualPaymentParts.valueChange));
-
-
- 625 actualPaymentParts.feePaid == 0,
- 626 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
-
-
- 629 actualPaymentParts.interestPaid == 0,
- 630 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
-
-
- 633 actualPaymentParts.principalPaid == overpaymentAmount,
- 634 " principalPaid mismatch: expected " +
to_string(overpaymentAmount) +
", got " +
- 635 to_string(actualPaymentParts.principalPaid));
-
-
-
- 639 loanProperites.loanState.interestDue - newState.interestDue == 0,
- 640 " interest change mismatch: expected 0, got " +
- 641 to_string(loanProperites.loanState.interestDue - newState.interestDue));
-
-
- 644 loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
- 645 " management fee change mismatch: expected 0, got " +
- 646 to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
-
-
- 649 actualPaymentParts.principalPaid ==
- 650 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
- 651 " principalPaid mismatch: expected " +
- 652 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
- 653 to_string(actualPaymentParts.principalPaid));
-
+ 614 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+ 615 auto const& newState = newLoanProperties.loanState;
+
+
+
+ 619 actualPaymentParts.valueChange == 0,
+ 620 " valueChange mismatch: expected 0, got " +
to_string(actualPaymentParts.valueChange));
+
+
+ 623 actualPaymentParts.feePaid == 0,
+ 624 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
+
+
+ 627 actualPaymentParts.interestPaid == 0,
+ 628 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+ 631 actualPaymentParts.principalPaid == overpaymentAmount,
+ 632 " principalPaid mismatch: expected " +
to_string(overpaymentAmount) +
", got " +
+ 633 to_string(actualPaymentParts.principalPaid));
+
+
+
+ 637 loanProperties.loanState.interestDue - newState.interestDue == 0,
+ 638 " interest change mismatch: expected 0, got " +
+ 639 to_string(loanProperties.loanState.interestDue - newState.interestDue));
+
+
+ 642 loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
+ 643 " management fee change mismatch: expected 0, got " +
+ 644 to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
+
+
+ 647 actualPaymentParts.principalPaid ==
+ 648 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+ 649 " principalPaid mismatch: expected " +
+ 650 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+ 651 to_string(actualPaymentParts.principalPaid));
+
-
-
-
-
-
-
659 testcase(
"tryOverpayment - No Interest With Overpayment Fee");
-
-
-
-
-
-
665 Account const issuer{
"issuer"};
-
-
-
-
-
670 Number const loanPrincipal{1'000};
-
-
-
673 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
-
-
-
-
-
-
-
-
-
-
-
684 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
-
-
686 Number const periodicPayment = loanProperites.periodicPayment;
-
-
688 auto const ret = tryOverpayment(
-
-
-
691 overpaymentComponents,
-
692 loanProperites.loanState,
-
-
-
-
-
-
-
-
-
701 auto const& [actualPaymentParts, newLoanProperties] = *ret;
-
702 auto const& newState = newLoanProperties.loanState;
-
-
+
+
+
+
+
+
657 testcase(
"tryOverpayment - No Interest With Overpayment Fee");
+
+
+
+
+
+
663 Account const issuer{
"issuer"};
+
+
+
+
+
668 Number const loanPrincipal{1'000};
+
+
+
671 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
673 auto const overpaymentComponents = computeOverpaymentComponents(
+
+
+
+
+
+
+
+
+
682 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
+
+
684 auto const ret = tryOverpayment(
+
+
+
687 overpaymentComponents,
+
688 loanProperties.loanState,
+
689 loanProperties.periodicPayment,
+
+
+
+
+
+
+
+
697 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+
698 auto const& newState = newLoanProperties.loanState;
+
+
+
+
702 actualPaymentParts.valueChange == 0,
+
703 " valueChange mismatch: expected 0, got " +
to_string(actualPaymentParts.valueChange));
+
-
706 actualPaymentParts.valueChange == 0,
-
707 " valueChange mismatch: expected 0, got " +
to_string(actualPaymentParts.valueChange));
+
706 actualPaymentParts.feePaid == 5,
+
707 " feePaid mismatch: expected 5, got " +
to_string(actualPaymentParts.feePaid));
-
710 actualPaymentParts.feePaid == 5,
-
711 " feePaid mismatch: expected 5, got " +
to_string(actualPaymentParts.feePaid));
+
710 actualPaymentParts.principalPaid == 45,
+
711 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
-
714 actualPaymentParts.principalPaid == 45,
-
715 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
+
714 actualPaymentParts.interestPaid == 0,
+
715 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
-
-
718 actualPaymentParts.interestPaid == 0,
-
719 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
-
-
-
-
-
724 loanProperites.loanState.interestDue - newState.interestDue == 0,
-
725 " interest change mismatch: expected 0, got " +
-
726 to_string(loanProperites.loanState.interestDue - newState.interestDue));
-
-
-
-
730 loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
-
731 " management fee change mismatch: expected 0, got " +
-
732 to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
-
-
-
735 actualPaymentParts.principalPaid ==
-
736 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
-
737 " principalPaid mismatch: expected " +
-
738 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
-
739 to_string(actualPaymentParts.principalPaid));
-
+
+
+
+
720 loanProperties.loanState.interestDue - newState.interestDue == 0,
+
721 " interest change mismatch: expected 0, got " +
+
722 to_string(loanProperties.loanState.interestDue - newState.interestDue));
+
+
+
+
726 loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
+
727 " management fee change mismatch: expected 0, got " +
+
728 to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
+
+
+
731 actualPaymentParts.principalPaid ==
+
732 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+
733 " principalPaid mismatch: expected " +
+
734 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+
735 to_string(actualPaymentParts.principalPaid));
+
-
-
-
-
-
-
745 testcase(
"tryOverpayment - Loan Interest, No Overpayment Fees");
-
-
-
-
-
-
751 Account const issuer{
"issuer"};
-
-
-
-
-
756 Number const loanPrincipal{1'000};
-
-
-
759 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
-
-
-
-
-
-
-
-
-
-
-
770 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
-
-
772 Number const periodicPayment = loanProperites.periodicPayment;
-
-
774 auto const ret = tryOverpayment(
-
-
-
777 overpaymentComponents,
-
778 loanProperites.loanState,
-
-
-
-
-
-
-
-
-
787 auto const& [actualPaymentParts, newLoanProperties] = *ret;
-
788 auto const& newState = newLoanProperties.loanState;
-
-
-
-
+
+
+
+
+
+
741 testcase(
"tryOverpayment - Loan Interest, No Overpayment Fees");
+
+
+
+
+
+
747 Account const issuer{
"issuer"};
+
+
+
+
+
752 Number const loanPrincipal{1'000};
+
+
+
755 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
757 auto const overpaymentComponents = computeOverpaymentComponents(
+
+
+
+
+
+
+
+
+
766 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
+
+
768 auto const ret = tryOverpayment(
+
+
+
771 overpaymentComponents,
+
772 loanProperties.loanState,
+
773 loanProperties.periodicPayment,
+
+
+
+
+
+
+
+
781 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+
782 auto const& newState = newLoanProperties.loanState;
+
+
+
+
+
+
788 (actualPaymentParts.valueChange ==
Number{-228802, -5}),
+
789 " valueChange mismatch: expected " +
to_string(
Number{-228802, -5}) +
", got " +
+
790 to_string(actualPaymentParts.valueChange));
+
+
-
794 (actualPaymentParts.valueChange ==
Number{-228802, -5}),
-
795 " valueChange mismatch: expected " +
to_string(
Number{-228802, -5}) +
", got " +
-
796 to_string(actualPaymentParts.valueChange));
-
-
-
-
800 actualPaymentParts.feePaid == 0,
-
801 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
-
-
-
804 actualPaymentParts.principalPaid == 50,
-
805 " principalPaid mismatch: expected 50, got `" +
to_string(actualPaymentParts.principalPaid));
-
-
-
-
809 actualPaymentParts.interestPaid == 0,
-
810 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
-
-
-
-
814 actualPaymentParts.principalPaid ==
-
815 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
-
816 " principalPaid mismatch: expected " +
-
817 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
-
818 to_string(actualPaymentParts.principalPaid));
+
794 actualPaymentParts.feePaid == 0,
+
795 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
+
+
+
798 actualPaymentParts.principalPaid == 50,
+
799 " principalPaid mismatch: expected 50, got `" +
to_string(actualPaymentParts.principalPaid));
+
+
+
+
803 actualPaymentParts.interestPaid == 0,
+
804 " interestPaid mismatch: expected 0, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+
+
808 actualPaymentParts.principalPaid ==
+
809 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+
810 " principalPaid mismatch: expected " +
+
811 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+
812 to_string(actualPaymentParts.principalPaid));
+
+
+
815 actualPaymentParts.valueChange == newState.interestDue - loanProperties.loanState.interestDue,
+
816 " valueChange mismatch: expected " +
+
817 to_string(newState.interestDue - loanProperties.loanState.interestDue) +
", got " +
+
818 to_string(actualPaymentParts.valueChange));
-
-
821 actualPaymentParts.valueChange == newState.interestDue - loanProperites.loanState.interestDue,
-
822 " valueChange mismatch: expected " +
-
823 to_string(newState.interestDue - loanProperites.loanState.interestDue) +
", got " +
-
824 to_string(actualPaymentParts.valueChange));
-
-
-
-
828 loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
-
829 " management fee change mismatch: expected 0, got " +
-
830 to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
-
+
+
+
822 loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
+
823 " management fee change mismatch: expected 0, got " +
+
824 to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
+
-
-
-
-
-
-
836 testcase(
"tryOverpayment - Loan Interest, Overpayment Interest, No Fee");
-
-
-
-
-
-
842 Account const issuer{
"issuer"};
-
-
-
-
-
847 Number const loanPrincipal{1'000};
-
-
-
850 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
-
-
-
-
-
-
-
-
-
-
-
861 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
-
-
863 Number const periodicPayment = loanProperites.periodicPayment;
-
-
865 auto const ret = tryOverpayment(
-
-
-
868 overpaymentComponents,
-
869 loanProperites.loanState,
-
-
-
-
-
-
-
-
-
878 auto const& [actualPaymentParts, newLoanProperties] = *ret;
-
879 auto const& newState = newLoanProperties.loanState;
-
-
-
-
-
884 actualPaymentParts.interestPaid == 5,
-
885 " interestPaid mismatch: expected 5, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+
+
+
+
830 testcase(
"tryOverpayment - Loan Interest, Overpayment Interest, No Fee");
+
+
+
+
+
+
836 Account const issuer{
"issuer"};
+
+
+
+
+
841 Number const loanPrincipal{1'000};
+
+
+
844 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
846 auto const overpaymentComponents = computeOverpaymentComponents(
+
+
+
+
+
+
+
+
+
855 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
+
+
857 auto const ret = tryOverpayment(
+
+
+
860 overpaymentComponents,
+
861 loanProperties.loanState,
+
862 loanProperties.periodicPayment,
+
+
+
+
+
+
+
+
870 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+
871 auto const& newState = newLoanProperties.loanState;
+
+
+
+
+
876 actualPaymentParts.interestPaid == 5,
+
877 " interestPaid mismatch: expected 5, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+
+
+
882 (actualPaymentParts.valueChange ==
Number{-205922, -5} + actualPaymentParts.interestPaid),
+
883 " valueChange mismatch: expected " +
+
884 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid) +
", got " +
+
885 to_string(actualPaymentParts.valueChange));
-
-
-
-
890 (actualPaymentParts.valueChange ==
Number{-205922, -5} + actualPaymentParts.interestPaid),
-
891 " valueChange mismatch: expected " +
-
892 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid) +
", got " +
-
893 to_string(actualPaymentParts.valueChange));
-
-
-
-
897 actualPaymentParts.feePaid == 0,
-
898 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
-
-
-
901 actualPaymentParts.principalPaid == 45,
-
902 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
+
+
+
889 actualPaymentParts.feePaid == 0,
+
890 " feePaid mismatch: expected 0, got " +
to_string(actualPaymentParts.feePaid));
+
+
+
893 actualPaymentParts.principalPaid == 45,
+
894 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
+
+
+
+
898 actualPaymentParts.principalPaid ==
+
899 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+
900 " principalPaid mismatch: expected " +
+
901 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+
902 to_string(actualPaymentParts.principalPaid));
-
-
-
906 actualPaymentParts.principalPaid ==
-
907 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
-
908 " principalPaid mismatch: expected " +
-
909 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
-
910 to_string(actualPaymentParts.principalPaid));
-
-
-
-
-
915 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
-
916 newState.interestDue - loanProperites.loanState.interestDue,
-
917 " valueChange mismatch: expected " +
-
-
919 newState.interestDue - loanProperites.loanState.interestDue + actualPaymentParts.interestPaid) +
-
920 ", got " +
to_string(actualPaymentParts.valueChange));
-
-
-
-
924 loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
-
925 " management fee change mismatch: expected 0, got " +
-
926 to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
-
+
+
+
+
907 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
+
908 newState.interestDue - loanProperties.loanState.interestDue,
+
909 " valueChange mismatch: expected " +
+
+
911 newState.interestDue - loanProperties.loanState.interestDue + actualPaymentParts.interestPaid) +
+
912 ", got " +
to_string(actualPaymentParts.valueChange));
+
+
+
+
916 loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
+
917 " management fee change mismatch: expected 0, got " +
+
918 to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
+
-
-
-
-
-
-
-
933 "tryOverpayment - Loan Interest and Fee, Overpayment Interest, No "
-
-
-
-
-
-
-
940 Account const issuer{
"issuer"};
-
-
-
-
-
945 Number const loanPrincipal{1'000};
-
-
-
948 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
+
+
+
+
+
925 "tryOverpayment - Loan Interest and Fee, Overpayment Interest, No "
+
+
+
+
+
+
+
932 Account const issuer{
"issuer"};
+
+
+
+
+
937 Number const loanPrincipal{1'000};
+
+
+
940 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
942 auto const overpaymentComponents = computeOverpaymentComponents(
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
959 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
-
-
961 Number const periodicPayment = loanProperites.periodicPayment;
-
-
963 auto const ret = tryOverpayment(
-
-
-
966 overpaymentComponents,
-
967 loanProperites.loanState,
-
-
-
-
-
-
-
-
-
976 auto const& [actualPaymentParts, newLoanProperties] = *ret;
-
977 auto const& newState = newLoanProperties.loanState;
-
-
-
-
-
-
-
984 (actualPaymentParts.interestPaid ==
Number{45, -1}),
-
985 " interestPaid mismatch: expected 4.5, got " +
to_string(actualPaymentParts.interestPaid));
-
-
-
-
-
990 (actualPaymentParts.valueChange ==
Number{-18533, -4} + actualPaymentParts.interestPaid),
-
991 " valueChange mismatch: expected " +
to_string(
Number{-18533, -4} + actualPaymentParts.interestPaid) +
-
992 ", got " +
to_string(actualPaymentParts.valueChange));
+
+
951 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
+
+
953 auto const ret = tryOverpayment(
+
+
+
956 overpaymentComponents,
+
957 loanProperties.loanState,
+
958 loanProperties.periodicPayment,
+
+
+
+
+
+
+
+
966 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+
967 auto const& newState = newLoanProperties.loanState;
+
+
+
+
+
+
+
974 (actualPaymentParts.interestPaid ==
Number{45, -1}),
+
975 " interestPaid mismatch: expected 4.5, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+
+
+
980 (actualPaymentParts.valueChange ==
Number{-18533, -4} + actualPaymentParts.interestPaid),
+
981 " valueChange mismatch: expected " +
to_string(
Number{-18533, -4} + actualPaymentParts.interestPaid) +
+
982 ", got " +
to_string(actualPaymentParts.valueChange));
+
+
+
+
+
987 (actualPaymentParts.feePaid ==
Number{5, -1}),
+
988 " feePaid mismatch: expected 0.5, got " +
to_string(actualPaymentParts.feePaid));
+
+
+
991 actualPaymentParts.principalPaid == 45,
+
992 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
-
-
-
-
997 (actualPaymentParts.feePaid ==
Number{5, -1}),
-
998 " feePaid mismatch: expected 0.5, got " +
to_string(actualPaymentParts.feePaid));
-
-
-
1001 actualPaymentParts.principalPaid == 45,
-
1002 " principalPaid mismatch: expected 45, got `" +
to_string(actualPaymentParts.principalPaid));
-
-
-
-
1006 actualPaymentParts.principalPaid ==
-
1007 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
-
1008 " principalPaid mismatch: expected " +
-
1009 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
-
1010 to_string(actualPaymentParts.principalPaid));
-
-
-
-
-
1015 (newState.managementFeeDue - loanProperites.loanState.managementFeeDue ==
Number{-20592, -5}),
-
1016 " management fee change mismatch: expected " +
to_string(
Number{-20592, -5}) +
", got " +
-
1017 to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
-
-
-
1020 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
-
1021 newState.interestDue - loanProperites.loanState.interestDue,
-
1022 " valueChange mismatch: expected " +
-
1023 to_string(newState.interestDue - loanProperites.loanState.interestDue) +
", got " +
-
1024 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
-
+
+
+
996 actualPaymentParts.principalPaid ==
+
997 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+
998 " principalPaid mismatch: expected " +
+
999 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+
1000 to_string(actualPaymentParts.principalPaid));
+
+
+
+
+
1005 (newState.managementFeeDue - loanProperties.loanState.managementFeeDue ==
Number{-20592, -5}),
+
1006 " management fee change mismatch: expected " +
to_string(
Number{-20592, -5}) +
", got " +
+
1007 to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
+
+
+
1010 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
+
1011 newState.interestDue - loanProperties.loanState.interestDue,
+
1012 " valueChange mismatch: expected " +
+
1013 to_string(newState.interestDue - loanProperties.loanState.interestDue) +
", got " +
+
1014 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
+
-
-
-
-
-
-
1030 testcase(
"tryOverpayment - Loan Interest, Fee, Overpayment Interest, Fee");
-
-
1032 using namespace jtx;
-
-
-
-
1036 Account const issuer{
"issuer"};
-
-
-
-
-
1041 Number const loanPrincipal{1'000};
-
-
-
1044 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
-
-
-
-
-
-
-
-
-
-
-
1055 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
-
-
1057 Number const periodicPayment = loanProperites.periodicPayment;
-
-
1059 auto const ret = tryOverpayment(
-
-
-
1062 overpaymentComponents,
-
1063 loanProperites.loanState,
-
-
-
-
-
-
-
-
-
1072 auto const& [actualPaymentParts, newLoanProperties] = *ret;
-
1073 auto const& newState = newLoanProperties.loanState;
-
-
-
-
-
-
-
1080 (actualPaymentParts.interestPaid ==
Number{45, -1}),
-
1081 " interestPaid mismatch: expected 4.5, got " +
to_string(actualPaymentParts.interestPaid));
-
-
-
-
-
1086 (actualPaymentParts.valueChange ==
Number{-164737, -5} + actualPaymentParts.interestPaid),
-
1087 " valueChange mismatch: expected " +
to_string(
Number{-164737, -5} + actualPaymentParts.interestPaid) +
-
1088 ", got " +
to_string(actualPaymentParts.valueChange));
+
+
+
+
+
+
1020 testcase(
"tryOverpayment - Loan Interest, Fee, Overpayment Interest, Fee");
+
+
1022 using namespace jtx;
+
+
+
+
1026 Account const issuer{
"issuer"};
+
+
+
+
+
1031 Number const loanPrincipal{1'000};
+
+
+
1034 auto const periodicRate =
loanPeriodicRate(loanInterestRate, paymentInterval);
+
+
1036 auto const overpaymentComponents = computeOverpaymentComponents(
+
+
+
+
+
+
+
+
+
1045 asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
+
+
1047 auto const ret = tryOverpayment(
+
+
+
1050 overpaymentComponents,
+
1051 loanProperties.loanState,
+
1052 loanProperties.periodicPayment,
+
+
+
+
+
+
+
+
1060 auto const& [actualPaymentParts, newLoanProperties] = *ret;
+
1061 auto const& newState = newLoanProperties.loanState;
+
+
+
+
+
+
+
1068 (actualPaymentParts.interestPaid ==
Number{45, -1}),
+
1069 " interestPaid mismatch: expected 4.5, got " +
to_string(actualPaymentParts.interestPaid));
+
+
+
+
+
1074 (actualPaymentParts.valueChange ==
Number{-164737, -5} + actualPaymentParts.interestPaid),
+
1075 " valueChange mismatch: expected " +
to_string(
Number{-164737, -5} + actualPaymentParts.interestPaid) +
+
1076 ", got " +
to_string(actualPaymentParts.valueChange));
+
+
+
+
+
1081 (actualPaymentParts.feePaid ==
Number{55, -1}),
+
1082 " feePaid mismatch: expected 5.5, got " +
to_string(actualPaymentParts.feePaid));
+
+
+
1085 actualPaymentParts.principalPaid == 40,
+
1086 " principalPaid mismatch: expected 40, got `" +
to_string(actualPaymentParts.principalPaid));
+
+
-
-
-
-
1093 (actualPaymentParts.feePaid ==
Number{55, -1}),
-
1094 " feePaid mismatch: expected 5.5, got " +
to_string(actualPaymentParts.feePaid));
-
-
-
1097 actualPaymentParts.principalPaid == 40,
-
1098 " principalPaid mismatch: expected 40, got `" +
to_string(actualPaymentParts.principalPaid));
-
-
-
-
-
1103 actualPaymentParts.principalPaid ==
-
1104 loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
-
1105 " principalPaid mismatch: expected " +
-
1106 to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
-
1107 to_string(actualPaymentParts.principalPaid));
-
-
-
-
-
1112 (newState.managementFeeDue - loanProperites.loanState.managementFeeDue ==
Number{-18304, -5}),
-
1113 " management fee change mismatch: expected " +
to_string(
Number{-18304, -5}) +
", got " +
-
1114 to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
-
-
-
1117 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
-
1118 newState.interestDue - loanProperites.loanState.interestDue,
-
1119 " valueChange mismatch: expected " +
-
1120 to_string(newState.interestDue - loanProperites.loanState.interestDue) +
", got " +
-
1121 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
-
+
+
1091 actualPaymentParts.principalPaid ==
+
1092 loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
+
1093 " principalPaid mismatch: expected " +
+
1094 to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) +
", got " +
+
1095 to_string(actualPaymentParts.principalPaid));
+
+
+
+
+
1100 (newState.managementFeeDue - loanProperties.loanState.managementFeeDue ==
Number{-18304, -5}),
+
1101 " management fee change mismatch: expected " +
to_string(
Number{-18304, -5}) +
", got " +
+
1102 to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
+
+
+
1105 actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
+
1106 newState.interestDue - loanProperties.loanState.interestDue,
+
1107 " valueChange mismatch: expected " +
+
1108 to_string(newState.interestDue - loanProperties.loanState.interestDue) +
", got " +
+
1109 to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1147BEAST_DEFINE_TESTSUITE(LendingHelpers, app,
xrpl);
-
-
-
+
1135BEAST_DEFINE_TESTSUITE(LendingHelpers, app,
xrpl);
+
+
+
testcase_t testcase
Memberspace for declaring test cases.
@@ -1273,17 +1261,17 @@ $(document).ready(function() { init_codefold(0); });
void testComputePaymentFactor()
void testComputeRaisedRate()
void testLoanPrincipalFromPeriodicPayment()
-
void testTryOverpaymentLoanInterestNoOverpaymentFees()
+
void testTryOverpaymentLoanInterestNoOverpaymentFees()
void testComputeInterestAndFeeParts()
-
void testTryOverpaymentNoInterestOverpaymentFee()
+
void testTryOverpaymentNoInterestOverpaymentFee()
void testTryOverpaymentNoInterestNoFee()
-
void testTryOverpaymentLoanInterestFeeOverpaymentInterestNoFee()
-
void testTryOverpaymentLoanInterestFeeOverpaymentInterestFee()
+
void testTryOverpaymentLoanInterestFeeOverpaymentInterestNoFee()
+
void testTryOverpaymentLoanInterestFeeOverpaymentInterestFee()
void testComputeFullPaymentInterest()
void testLoanPeriodicPayment()
void testComputeOverpaymentComponents()
-
void testTryOverpaymentLoanInterestOverpaymentInterest()
-
void run() override
Runs the suite.
+
void testTryOverpaymentLoanInterestOverpaymentInterest()
+
void run() override
Runs the suite.
void testLoanAccruedInterest()
void testLoanLatePaymentInterest()
Immutable cryptographic account descriptor.
@@ -1302,7 +1290,6 @@ $(document).ready(function() { init_codefold(0); });
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
-
diff --git a/Manifest_8cpp_source.html b/Manifest_8cpp_source.html
index 0b5e958128..75cd1f2a88 100644
--- a/Manifest_8cpp_source.html
+++ b/Manifest_8cpp_source.html
@@ -577,107 +577,111 @@ $(document).ready(function() { init_codefold(0); });
461 map_.emplace(std::move(masterKey), std::move(m));
-
-
-
-
-
-
467 if (
auto stream =
j_.
info())
-
-
-
-
-
-
-
-
475 iter->second = std::move(m);
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
471 if (
auto stream =
j_.
info())
+
+
+
+
+
+
+
+
479 iter->second = std::move(m);
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
497 load(dbCon, dbTable);
-
-
499 if (!configManifest.
empty())
-
-
-
-
-
504 JLOG(
j_.
error()) <<
"Malformed validator_token in config";
-
-
-
-
-
-
510 JLOG(
j_.
warn()) <<
"Configured manifest revokes public key";
-
-
-
-
-
515 JLOG(
j_.
error()) <<
"Manifest in config was rejected";
-
-
-
-
-
520 if (!configRevocation.
empty())
-
-
-
-
524 configRevocation.
cbegin(),
-
525 configRevocation.
cend(),
-
-
-
-
529 for (
auto const& line : configRevocation)
-
530 revocationStr += boost::algorithm::trim_copy(line);
-
-
-
-
-
-
536 JLOG(
j_.
error()) <<
"Invalid validator key revocation in config";
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
501 load(dbCon, dbTable);
+
+
503 if (!configManifest.
empty())
+
+
+
+
+
508 JLOG(
j_.
error()) <<
"Malformed validator_token in config";
+
+
+
+
+
+
514 JLOG(
j_.
warn()) <<
"Configured manifest revokes public key";
+
+
+
+
+
519 JLOG(
j_.
error()) <<
"Manifest in config was rejected";
+
+
+
+
+
524 if (!configRevocation.
empty())
+
+
+
+
528 configRevocation.
cbegin(),
+
529 configRevocation.
cend(),
+
+
+
+
533 for (
auto const& line : configRevocation)
+
534 revocationStr += boost::algorithm::trim_copy(line);
+
+
+
+
+
+
540 JLOG(
j_.
error()) <<
"Invalid validator key revocation in config";
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
+
@@ -695,7 +699,7 @@ $(document).ready(function() { init_codefold(0); });
LockedSociSession checkoutDb()
std::atomic< std::uint32_t > seq_
-
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
+
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
std::optional< std::string > getDomain(PublicKey const &pk) const
Returns domain claimed by a given public key.
@@ -704,7 +708,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns manifest corresponding to a given public key.
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
diff --git a/Manifest_8h_source.html b/Manifest_8h_source.html
index a8168f34d2..91b5bba39d 100644
--- a/Manifest_8h_source.html
+++ b/Manifest_8h_source.html
@@ -395,7 +395,7 @@ $(document).ready(function() { init_codefold(0); });
std::atomic< std::uint32_t > seq_
void for_each_manifest(PreFun &&pf, EachFun &&f) const
Invokes the callback once for every populated manifest.
-
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
+
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
ManifestCache(beast::Journal j=beast::Journal(beast::Journal::getNullSink()))
@@ -406,7 +406,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns manifest corresponding to a given public key.
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
diff --git a/Manifest__test_8cpp_source.html b/Manifest__test_8cpp_source.html
index 334035a027..85a3e670e3 100644
--- a/Manifest__test_8cpp_source.html
+++ b/Manifest__test_8cpp_source.html
@@ -950,52 +950,57 @@ $(document).ready(function() { init_codefold(0); });
-
-
-
-
-
+
+
+
832 BEAST_EXPECT(cache.
sequence() > seq0);
+
+
-
-
-
-
-
-
841 BEAST_EXPECT(!cache.
revoked(pk_a));
-
842 BEAST_EXPECT(s_aMax.revoked());
-
-
-
-
-
847 BEAST_EXPECT(cache.
revoked(pk_a));
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
836 BEAST_EXPECT(cache.
sequence() == seq1);
+
+
+
+
+
+
+
+
+
+
846 BEAST_EXPECT(!cache.
revoked(pk_a));
+
847 BEAST_EXPECT(s_aMax.revoked());
+
+
+
+
+
852 BEAST_EXPECT(cache.
revoked(pk_a));
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -1003,11 +1008,12 @@ $(document).ready(function() { init_codefold(0); });
testcase_t testcase
Memberspace for declaring test cases.
void fail(String const &reason, char const *file, int line)
Record a failure.
Remembers manifests with the highest sequence number.
-
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
+
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
+
std::uint32_t sequence() const
A monotonically increasing number used to detect new manifests.
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
diff --git a/classxrpl_1_1ManifestCache.html b/classxrpl_1_1ManifestCache.html
index a0c8371dac..2fe6ccbb90 100644
--- a/classxrpl_1_1ManifestCache.html
+++ b/classxrpl_1_1ManifestCache.html
@@ -484,7 +484,7 @@ Private Attributes
- Thread Safety
May be called concurrently
-
Definition at line 491 of file Manifest.cpp.
+
Definition at line 495 of file Manifest.cpp.
@@ -525,7 +525,7 @@ Private Attributes
- Thread Safety
May be called concurrently
-
Definition at line 484 of file Manifest.cpp.
+
Definition at line 488 of file Manifest.cpp.
@@ -572,7 +572,7 @@ Private Attributes
- Thread Safety
May be called concurrently
-Definition at line 545 of file Manifest.cpp.
+Definition at line 549 of file Manifest.cpp.
diff --git a/classxrpl_1_1test_1_1LendingHelpers__test.html b/classxrpl_1_1test_1_1LendingHelpers__test.html
index 155e8e7751..19c29ba2ad 100644
--- a/classxrpl_1_1test_1_1LendingHelpers__test.html
+++ b/classxrpl_1_1test_1_1LendingHelpers__test.html
@@ -565,7 +565,7 @@ Private Attributes
@@ -592,7 +592,7 @@ Private Attributes
@@ -619,7 +619,7 @@ Private Attributes
@@ -646,7 +646,7 @@ Private Attributes
@@ -673,7 +673,7 @@ Private Attributes
@@ -704,7 +704,7 @@ Private Attributes
Implements beast::unit_test::suite.
-Definition at line 1126 of file LendingHelpers_test.cpp.
+Definition at line 1114 of file LendingHelpers_test.cpp.