lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] valyuta/005 d5c50ee 1/5: Resolve more gratuitous inc


From: Greg Chicares
Subject: [lmi-commits] [lmi] valyuta/005 d5c50ee 1/5: Resolve more gratuitous incompatibilities with master
Date: Thu, 28 Jan 2021 17:53:33 -0500 (EST)

branch: valyuta/005
commit d5c50ee53a0cbf27cbc4099cc4c662cf2a7a8eea
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Resolve more gratuitous incompatibilities with master
---
 accountvalue.cpp |  35 +++++----
 basicvalues.cpp  |  11 +--
 ihs_acctval.cpp  |  55 ++++-----------
 ihs_avmly.cpp    | 211 ++++++++++++++++++++++++-------------------------------
 ihs_avsolve.cpp  |  19 ++---
 ihs_basicval.cpp |  26 +++----
 solve.cpp        |  10 +--
 7 files changed, 149 insertions(+), 218 deletions(-)

diff --git a/accountvalue.cpp b/accountvalue.cpp
index 4ee32d4..7787cc6 100644
--- a/accountvalue.cpp
+++ b/accountvalue.cpp
@@ -486,8 +486,6 @@ void AccountValue::PerformSpecAmtStrategy()
             }
         }
 
-    SA = round_specamt().c(SA); // already rounded
-
     for(int j = 0; j < BasicValues::GetLength(); ++j)
         {
         InvariantValues().SpecAmt[j] = dblize(SA);
@@ -545,7 +543,8 @@ void AccountValue::TxOptionChange()
             alarum() << "Case " << YearsDBOpt << " not found." << LMI_FLUSH;
             }
         }
-    ActualSpecAmt = round_specamt().c(ActualSpecAmt); // already rounded
+    // AV normally rounded to cents, but specamt perhaps to dollars.
+    ActualSpecAmt = round_specamt().c(ActualSpecAmt);
 
     // Carry the new spec amt forward into all future years.
     for(int j = Year; j < BasicValues::GetLength(); ++j)
@@ -758,10 +757,7 @@ void AccountValue::TxSetDeathBft()
         case mce_option2:
             // Option 2: specamt plus AV, or corridor times AV if greater.
             // Negative AV doesn't decrease death benefit.
-            deathbft = std::max
-                (ActualSpecAmt + std::max(C0, AV)
-                ,corr
-                );
+            deathbft = std::max(ActualSpecAmt + std::max(C0, AV), corr);
             break;
         case mce_rop: // fall through
         case mce_mdb: // fall through
@@ -771,7 +767,7 @@ void AccountValue::TxSetDeathBft()
             }
         }
 
-    deathbft = round_death_benefit().c(deathbft); // already rounded
+    deathbft = round_death_benefit().c(deathbft);
 
     // SOMEDAY !! Accumulate average death benefit for profit testing here.
 }
@@ -783,8 +779,7 @@ void AccountValue::TxSetCoiCharge()
     TxSetDeathBft();
 
     // Negative AV doesn't increase NAAR.
-    NAAR = deathbft * mlyguarv - dblize(AVUnloaned + AVRegLn + AVPrfLn);
-    NAAR = round_naar()(NAAR);
+    NAAR = round_naar()(deathbft * mlyguarv - dblize(AVUnloaned + AVRegLn + 
AVPrfLn));
 
     CoiCharge = round_coi_charge().c(NAAR * YearsCoiRate0);
 }
@@ -797,8 +792,7 @@ void AccountValue::TxSetRiderDed()
     if(haswp)
         {
         WpCharge = round_rider_charges().c
-            (
-                YearsWpRate
+            (   YearsWpRate
             *   (CoiCharge + YearsMonthlyPolicyFee + AdbCharge)
             );
         }
@@ -806,7 +800,11 @@ void AccountValue::TxSetRiderDed()
     AdbCharge = C0;
     if(hasadb)
         {
-        AdbCharge = round_rider_charges().c(YearsAdbRate * std::min(500000.0, 
dblize(ActualSpecAmt)));
+        AdbCharge = round_rider_charges().c
+            ( YearsAdbRate
+            // IHS !! Icky manifest constant--lmi uses a database entity.
+            * std::min(from_cents(50000000), ActualSpecAmt)
+            );
         }
 }
 
@@ -828,8 +826,7 @@ void AccountValue::TxCreditInt()
     if(C0 < AVUnloaned)
         {
         // IHS !! Each interest increment is rounded separately in lmi.
-        currency z = round_interest_credit().c(AVUnloaned * 
YearsGenAcctIntRate);
-        AVUnloaned += z;
+        AVUnloaned += round_interest_credit().c(AVUnloaned * 
YearsGenAcctIntRate);
         }
     // Loaned account value cannot be negative.
     LMI_ASSERT(C0 <= AVRegLn + AVPrfLn);
@@ -920,7 +917,7 @@ void AccountValue::TxTakeWD()
 // IHS !!            ActualSpecAmt = std::min(ActualSpecAmt, deathbft - wd);
             ActualSpecAmt -= wd;
             ActualSpecAmt = std::max(ActualSpecAmt, MinSpecAmt);
-            ActualSpecAmt = round_specamt().c(ActualSpecAmt); // already 
rounded
+            ActualSpecAmt = round_specamt().c(ActualSpecAmt);
             // IHS !! If WD causes AV < min AV, do we:
             //   reduce the WD?
             //   lapse the policy?
@@ -972,11 +969,11 @@ void AccountValue::TxTakeLoan()
     // If maximum exceeded...limit it.
     // IHS !! For solves, the lmi branch uses an 'ullage' concept.
     double max_loan =
-          dblize(AVUnloaned) * 0.9    // IHS !! Icky manifest constant--lmi 
uses a database entity.
+          AVUnloaned * 0.9 // IHS !! Icky manifest constant--lmi uses a 
database entity.
         // - surrchg
         + dblize(AVRegLn + AVPrfLn)
-        - dblize(RegLnBal) * (std::pow((1.0 + YearsRegLnIntDueRate), 12 - 
Month) - 1.0)
-        - dblize(PrfLnBal) * (std::pow((1.0 + YearsPrfLnIntDueRate), 12 - 
Month) - 1.0)
+        - RegLnBal * (std::pow((1.0 + YearsRegLnIntDueRate), 12 - Month) - 1.0)
+        - PrfLnBal * (std::pow((1.0 + YearsPrfLnIntDueRate), 12 - Month) - 1.0)
         - dblize(mlydedtonextmodalpmtdate)
         ;
     // Interest adjustment: d upper n where n is # months remaining in year.
diff --git a/basicvalues.cpp b/basicvalues.cpp
index 05eee6e..08a2d1b 100644
--- a/basicvalues.cpp
+++ b/basicvalues.cpp
@@ -119,14 +119,9 @@ void BasicValues::Init()
     PremiumTax_    .reset(new premium_tax    (PremiumTaxState_, database()));
     Loads_         .reset(new Loads(database(), IsSubjectToIllustrationReg()));
 
-//  database().query_into(DB_MinSpecAmt, MinSpecAmt);
-//  database().query_into(DB_MinWd     , MinWD     );
-//  database().query_into(DB_WdFee     , WDFee     );
-//  database().query_into(DB_WdFeeRate , WDFeeRate );
-    MinSpecAmt = round_specamt   ().c(database().query<int>(DB_MinSpecAmt));
-    MinWD      = round_withdrawal().c(database().query<int>(DB_MinWd     ));
-    WDFee      = round_withdrawal().c(database().query<int>(DB_WdFee     ));
-//  WDFeeRate  = database().query<int>(DB_WdFeeRate ); // no, this line looks 
wrong
+    MinSpecAmt = round_specamt   ().c(database().query<double>(DB_MinSpecAmt));
+    MinWD      = round_withdrawal().c(database().query<double>(DB_MinWd     ));
+    WDFee      = round_withdrawal().c(database().query<double>(DB_WdFee     ));
     database().query_into(DB_WdFeeRate , WDFeeRate );
 
 // The antediluvian branch leaves FundData_, StratifiedCharges_, and
diff --git a/ihs_acctval.cpp b/ihs_acctval.cpp
index ac3aa29..bc747de 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -256,8 +256,6 @@ void AccountValue::SetGuarPrem()
         {
         GuarPremium = SolveGuarPremium();
         }
-    // bignum: the largest integer convertible to and from double.
-    LMI_ASSERT(GuarPremium < from_cents(1LL << 53));
     ledger_->SetGuarPremium(dblize(GuarPremium));
 }
 
@@ -428,20 +426,20 @@ void AccountValue::InitializeLife(mcenum_run_basis 
a_Basis)
     SurrChg_.assign(BasicValues::GetLength(), C0);
 
     // TAXATION !! Input::InforceAnnualTargetPremium should be used here.
-    double annual_target_premium = dblize(GetModalTgtPrem
+    currency annual_target_premium = GetModalTgtPrem
         (0
         ,mce_annual
         ,base_specamt(0)
-        ));
-    double sa = dblize(specamt_for_7702(0));
+        );
+    currency sa = specamt_for_7702(0);
 
     // It is at best superfluous to do this for every basis.
     // TAXATION !! Don't do that then.
     Irc7702_->Initialize7702
-        (sa
-        ,sa
+        (dblize(sa)
+        ,dblize(sa)
         ,effective_dbopt_7702(DeathBfts_->dbopt()[0], Effective7702DboRop)
-        ,annual_target_premium
+        ,dblize(annual_target_premium)
         );
 
     InvariantValues().InitGLP = Irc7702_->RoundedGLP();
@@ -647,16 +645,13 @@ void AccountValue::SetInitialValues()
     DcvWpCharge                 = 0.0;
 
     HoneymoonActive             = false;
-    // In 'master', this is
-    //   -std::numeric_limits<double>::max()
-    // which is the identity element for std::max(). Here, it's nearly
-    // the equivalent for currency::data_type; dividing it by 101 is a
-    // casual defense against commuting between dollars and cents.
+    // Identity element for std::max(), disregarding -INF and NaN.
+    // CURRENCY !! support infinities?
 #if defined USE_CURRENCY_CLASS
-    HoneymoonValue = 
from_cents(-std::numeric_limits<currency::data_type>::max() / 101);
+    HoneymoonValue = 
-from_cents(std::numeric_limits<currency::data_type>::max());
 #else  // !defined USE_CURRENCY_CLASS
     HoneymoonValue = -std::numeric_limits<double>::max();
-#endif // ! defined USE_CURRENCY_CLASS
+#endif // !defined USE_CURRENCY_CLASS
     if(mce_gen_curr == GenBasis_)
         {
         HoneymoonActive = yare_input_.HoneymoonEndorsement;
@@ -989,7 +984,7 @@ void AccountValue::set_list_bill_premium()
             );
         InvariantValues().EeListBillPremium = dblize(z.first);
         InvariantValues().ErListBillPremium = dblize(z.second);
-        InvariantValues().ListBillPremium = dblize(z.first) + dblize(z.second);
+        InvariantValues().ListBillPremium = dblize(z.first + z.second);
         }
 }
 
@@ -1027,7 +1022,7 @@ void AccountValue::set_modal_min_premium()
             );
         InvariantValues().EeModalMinimumPremium[Year] = dblize(z.first);
         InvariantValues().ErModalMinimumPremium[Year] = dblize(z.second);
-        InvariantValues().ModalMinimumPremium[Year] = dblize(z.first) + 
dblize(z.second);
+        InvariantValues().ModalMinimumPremium[Year] = dblize(z.first + 
z.second);
         }
 }
 
@@ -1105,17 +1100,6 @@ void AccountValue::SetClaims()
     YearsGrossClaims       = partial_mortality_qx()[Year] * DBReflectingCorr;
     YearsAVRelOnDeath      = partial_mortality_qx()[Year] * 
TotalAccountValue();
     YearsLoanRepaidOnDeath = partial_mortality_qx()[Year] * (RegLnBal + 
PrfLnBal);
-#if 0
-    // presumably material_difference() isn't needed at all
-    YearsDeathProceeds = material_difference
-        (YearsGrossClaims
-        ,YearsLoanRepaidOnDeath
-        );
-    YearsNetClaims = material_difference
-        (YearsGrossClaims
-        ,YearsAVRelOnDeath
-        );
-#endif // 0
     YearsDeathProceeds = material_difference
         (YearsGrossClaims
         ,YearsLoanRepaidOnDeath
@@ -1153,9 +1137,9 @@ void AccountValue::SetProjectedCoiCharge()
 
     TxSetDeathBft();
     TxSetTermAmt();
-    // presumably material_difference() isn't needed at all? um...yes, it is
+    // CURRENCY !! presumably material_difference() isn't needed at all? 
um...yes, it is
     double this_years_terminal_naar = material_difference
-        (dblize((DBReflectingCorr + TermDB))
+        (dblize(DBReflectingCorr + TermDB)
         ,dblize(TotalAccountValue())
         );
     this_years_terminal_naar = std::max(0.0, this_years_terminal_naar);
@@ -1310,15 +1294,6 @@ void AccountValue::FinalizeYear()
         + YearsTotalGrossIntCredited
         - YearsTotalNetIntCredited
         ;
-#if 0 // not needed--already currency
-    double notional_sep_acct_charge =
-          dblize(YearsTotalSepAcctLoad)
-        + material_difference
-            (YearsTotalGrossIntCredited
-            ,YearsTotalNetIntCredited
-            )
-        ;
-#endif // 0
     VariantValues().SepAcctCharges    [Year] = 
dblize(notional_sep_acct_charge);
 
     // Record dynamic interest rate in ledger object.
@@ -1383,7 +1358,6 @@ void AccountValue::FinalizeYear()
             {
             InvariantValues().InitPrem = InvariantValues().GrossPmt[Year];
             }
-#if 1
         LMI_ASSERT
             (materially_equal
                 (   InvariantValues().GrossPmt  [Year]
@@ -1391,7 +1365,6 @@ void AccountValue::FinalizeYear()
                 +   InvariantValues().ErGrossPmt[Year]
                 )
             );
-#endif // 0
         InvariantValues().Outlay[Year] =
                 InvariantValues().GrossPmt   [Year]
             -   InvariantValues().NetWD      [Year]
diff --git a/ihs_avmly.cpp b/ihs_avmly.cpp
index a0f88a9..70444f7 100644
--- a/ihs_avmly.cpp
+++ b/ihs_avmly.cpp
@@ -151,7 +151,6 @@ void AccountValue::DoMonthDR()
 //          + std::max(0.0, ExpRatReserve) // This would be added if it 
existed.
         );
 // TODO ?? TAXATION !! Use CashValueFor7702() instead?
-    // Not already rounded by class Irc7702A.
     double max_necessary_premium = Irc7702A_->MaxNecessaryPremium
         (Dcv
         ,dblize(AnnualTargetPrem)
@@ -161,7 +160,7 @@ void AccountValue::DoMonthDR()
         );
 // TAXATION !! Should round here, but need to investigate regressions.
 //  max_necessary_premium = round_max_premium()(max_necessary_premium);
-    // Already rounded by class Irc7702A.
+    // CURRENCY !! already rounded by class Irc7702A--appropriately?
     double max_non_mec_premium = Irc7702A_->MaxNonMecPremium
         (Dcv
         ,dblize(AnnualTargetPrem)
@@ -191,7 +190,7 @@ void AccountValue::DoMonthDR()
         {
         Irc7702A_->UpdatePmt7702A
             (Dcv
-            ,-dblize(NetWD) // TAXATION !! This should be gross, not net.
+            ,dblize(-NetWD) // TAXATION !! This should be gross, not net.
             ,false
             ,dblize(AnnualTargetPrem)
             ,YearsTotLoadTgtLowestPremtax
@@ -205,34 +204,22 @@ void AccountValue::DoMonthDR()
         {
         gross_1035 = External1035Amount + Internal1035Amount;
         }
-    double necessary_premium = std::min // round?
-        (material_difference // round?
+    // CURRENCY !! currency is immune to catastrophic cancellation
+    double necessary_premium = std::min
+        // CURRENCY !! Wouldn't simple subtraction do, for currency?
+        (material_difference
             (dblize(GrossPmts[Month])
             ,dblize(gross_1035)
             )
         ,max_necessary_premium
         );
-    double unnecessary_premium = material_difference // round?
+    // CURRENCY !! Wouldn't simple subtraction do, for currency?
+    double unnecessary_premium = material_difference
         (dblize(GrossPmts[Month])
         ,dblize(gross_1035) + necessary_premium
         );
-//  NetMaxNecessaryPremium
-//  GrossMaxNecessaryPremium
     NecessaryPremium   = round_minutiae().c(necessary_premium  );
     UnnecessaryPremium = round_minutiae().c(unnecessary_premium);
-    if(necessary_premium < 0.0 || unnecessary_premium < 0.0)
-        warning()
-//          << GrossPmts[Month] << " GrossPmts[Month]\n"
-//          << gross_1035 << " gross_1035\n"
-//          << GrossPmts[Month] - gross_1035 << " GrossPmts[Month] - 
gross_1035\n"
-            << necessary_premium << " necessary_premium\n"
-            << unnecessary_premium << " unnecessary_premium\n"
-            << Year << " Year\n"
-            << Month << " Month\n"
-//          << z << " z\n"
-            << LMI_FLUSH
-            ;
-
     // It is crucial to accept necessary premium before processing a
     // material change, so that the correct DCV is used.
     TxRecognizePaymentFor7702A(NecessaryPremium, false);
@@ -255,7 +242,6 @@ void AccountValue::DoMonthDR()
         );
     LMI_ASSERT(0.0 <= Dcv);
 
-//  UnnecessaryPremium = unnecessary_premium; // moved up
     TxRecognizePaymentFor7702A(UnnecessaryPremium, true);
     TxAcceptPayment(UnnecessaryPremium);
 
@@ -305,6 +291,7 @@ void AccountValue::process_payment(currency payment)
     double er_proportion = 0.0;
     if(C0 != gross_non_1035_pmts)
         {
+        // CURRENCY !! more efficient: currency / currency --> double
         er_proportion = ErGrossPmts[Month] / dblize(gross_non_1035_pmts);
         }
 
@@ -356,7 +343,7 @@ void AccountValue::process_payment(currency payment)
 // portions of unloaned account value according to input allocations.
 void AccountValue::IncrementAVProportionally(currency increment)
 {
-    increment = round_minutiae().c(increment); // already rounded?
+    increment = round_minutiae().c(increment); // CURRENCY !! already rounded?
     currency genacct_increment = round_minutiae().c(increment * 
GenAcctPaymentAllocation);
     AVGenAcct += genacct_increment;
     AVSepAcct += increment - genacct_increment;
@@ -441,10 +428,13 @@ void AccountValue::process_distribution(currency 
decrement)
 
 void AccountValue::DecrementAVProportionally(currency decrement)
 {
-    decrement = round_minutiae().c(decrement); // already rounded?
+    decrement = round_minutiae().c(decrement); // CURRENCY !! already rounded?
 
-//  if(materially_equal(decrement, AVGenAcct + AVSepAcct))
+#if defined USE_CURRENCY_CLASS
     if(decrement == AVGenAcct + AVSepAcct)
+#else  // !defined USE_CURRENCY_CLASS
+    if(materially_equal(decrement, AVGenAcct + AVSepAcct))
+#endif // !defined USE_CURRENCY_CLASS
         {
         AVGenAcct = C0;
         AVSepAcct = C0;
@@ -453,8 +443,9 @@ void AccountValue::DecrementAVProportionally(currency 
decrement)
 
     double general_account_proportion  = 0.0;
     double separate_account_proportion = 0.0;
-    double general_account_nonnegative_assets  = std::max(0.0, 
dblize(AVGenAcct));
-    double separate_account_nonnegative_assets = std::max(0.0, 
dblize(AVSepAcct));
+    // CURRENCY !! more efficient: currency / currency --> double
+    double general_account_nonnegative_assets  = dblize(std::max(C0, 
AVGenAcct));
+    double separate_account_nonnegative_assets = dblize(std::max(C0, 
AVSepAcct));
     if
         (  0.0 == general_account_nonnegative_assets
         && 0.0 == separate_account_nonnegative_assets
@@ -511,8 +502,11 @@ void AccountValue::DecrementAVProgressively
     ,oenum_increment_account_preference preferred_account
     )
 {
-//  if(materially_equal(decrement, AVGenAcct + AVSepAcct))
+#if defined USE_CURRENCY_CLASS
     if(decrement == AVGenAcct + AVSepAcct)
+#else  // !defined USE_CURRENCY_CLASS
+    if(materially_equal(decrement, AVGenAcct + AVSepAcct))
+#endif // !defined USE_CURRENCY_CLASS
         {
         AVGenAcct = C0;
         AVSepAcct = C0;
@@ -567,7 +561,7 @@ void AccountValue::TxExch1035()
         // Illustration-reg guaranteed premium ignores GPT limit.
         if(!SolvingForGuarPremium)
             {
-            // Maybe this should return the modified value instead of altering 
the argument.
+            // CURRENCY !! return modified value instead of altering argument
             double z = dblize(GrossPmts[Month]);
             Irc7702_->ProcessGptPmt(Year, z);
             GrossPmts[Month] = round_gross_premium().c(z);
@@ -627,8 +621,7 @@ void AccountValue::TxExch1035()
 
     CumPmts += GrossPmts[Month];
     TaxBasis += round_minutiae().c
-        (
-          yare_input_.External1035ExchangeTaxBasis
+        ( yare_input_.External1035ExchangeTaxBasis
         + yare_input_.Internal1035ExchangeTaxBasis
         );
 
@@ -731,7 +724,7 @@ currency AccountValue::minimum_specified_amount(bool 
issuing_now, bool term_ride
 // Make sure ActualSpecAmt is never less than minimum specamt.
 void AccountValue::ChangeSpecAmtBy(currency delta)
 {
-    delta = round_specamt().c(delta); // already rounded?
+    delta = round_specamt().c(delta); // CURRENCY !! already rounded?
     double term_proportion = 0.0;
     currency const old_total_specamt = ActualSpecAmt + TermSpecAmt;
     // Adjust term here only if it's formally a rider.
@@ -746,6 +739,7 @@ void AccountValue::ChangeSpecAmtBy(currency delta)
                 break;
             case mce_adjust_both:
                 {
+                // CURRENCY !! more efficient: currency / currency --> double
                 term_proportion = TermSpecAmt / dblize(old_total_specamt);
                 }
                 break;
@@ -789,11 +783,9 @@ void AccountValue::ChangeSpecAmtBy(currency delta)
 
     // If specamt would be reduced below the minimum (e.g., by a large
     // withdrawal), then force it to the minimum.
-    ActualSpecAmt =
-        (std::max
-            (ActualSpecAmt
-            ,minimum_specified_amount(0 == Year && 0 == Month, TermRiderActive)
-            )
+    ActualSpecAmt = std::max
+        (ActualSpecAmt
+        ,minimum_specified_amount(0 == Year && 0 == Month, TermRiderActive)
         );
 
     // Carry the new specamt forward into all future years.
@@ -824,16 +816,14 @@ void AccountValue::ChangeSpecAmtBy(currency delta)
 
 void AccountValue::ChangeSupplAmtBy(currency delta)
 {
-    delta = round_specamt().c(delta); // already rounded?
+    delta = round_specamt().c(delta); // CURRENCY !! already rounded?
     TermSpecAmt += delta;
 
-    TermSpecAmt = std::max
-        (TermSpecAmt
-        ,C0 // No minimum other than zero is defined.
-        );
-    // At least for now, there is no effect on surrender charges.
+    // No minimum other than zero is defined.
+    TermSpecAmt = std::max(TermSpecAmt, C0);
 
     // Carry the new supplemental amount forward into all future years.
+    // At least for now, there is no effect on surrender charges.
     for(int j = Year; j < BasicValues::GetLength(); ++j)
         {
         InvariantValues().TermSpecAmt[j] = dblize(TermSpecAmt);
@@ -1008,7 +998,7 @@ void AccountValue::TxSpecAmtChange()
                 (ActualSpecAmt
                 ,minimum_specified_amount(0 == Year && 0 == Month, 
TermRiderActive)
                 );
-            ActualSpecAmt = round_specamt().c(ActualSpecAmt); // already 
rounded?
+            ActualSpecAmt = round_specamt().c(ActualSpecAmt); // CURRENCY !! 
already rounded?
             InvariantValues().SpecAmt[j] = ActualSpecAmt;
             if(!TermIsNotRider)
                 {
@@ -1128,10 +1118,15 @@ void AccountValue::TxTestGPT()
     // TAXATION !! This assumes the term rider can be treated as death benefit;
     // use 'TermIsDbFor7702'.
     bool adj_event =
-            (
-                OldSA != ActualSpecAmt + TermSpecAmt
+#if defined USE_CURRENCY_CLASS
+            (   OldSA != ActualSpecAmt + TermSpecAmt
             &&  OldDB != DBReflectingCorr + TermDB
             )
+#else  // !defined USE_CURRENCY_CLASS
+            (   !materially_equal(OldSA, ActualSpecAmt + TermSpecAmt)
+            &&  !materially_equal(OldDB, DBReflectingCorr + TermDB)
+            )
+#endif // !defined USE_CURRENCY_CLASS
         ||  old_dbopt != new_dbopt
         ;
     if(adj_event)
@@ -1152,7 +1147,7 @@ void AccountValue::TxTestGPT()
             );
         }
 
-    // Already rounded by class Irc7702.
+    // CURRENCY !! already rounded by class Irc7702--appropriately?
     GptForceout = round_minutiae().c(Irc7702_->Forceout());
     // TODO ?? TAXATION !! On other bases, nothing is forced out, and payments 
aren't limited.
     process_distribution(GptForceout);
@@ -1223,16 +1218,6 @@ void AccountValue::TxAscertainDesiredPayment()
         return;
         }
 
-    if(GrossPmts[Month] != EeGrossPmts[Month] + ErGrossPmts[Month])
-        warning()
-            << GrossPmts[Month] << " GrossPmts[Month]\n"
-            << EeGrossPmts[Month] + ErGrossPmts[Month] << " EeGrossPmts[Month] 
+ ErGrossPmts[Month]\n"
-            << EeGrossPmts[Month] << " EeGrossPmts[Month]\n"
-            << ErGrossPmts[Month] << " ErGrossPmts[Month]\n"
-            << Year << " Year\n"
-            << Month << " Month\n"
-            << LMI_FLUSH
-            ;
     assert_pmts_add_up(__FILE__, __LINE__, Month);
 
     currency eepmt = C0;
@@ -1242,6 +1227,7 @@ void AccountValue::TxAscertainDesiredPayment()
         // Illustration-reg guaranteed premium ignores GPT limit.
         if(!SolvingForGuarPremium)
             {
+            // CURRENCY !! return modified value instead of altering argument
             double z = dblize(eepmt);
             Irc7702_->ProcessGptPmt(Year, z);
             eepmt = round_gross_premium().c(z);
@@ -1257,6 +1243,7 @@ void AccountValue::TxAscertainDesiredPayment()
         // Illustration-reg guaranteed premium ignores GPT limit.
         if(!SolvingForGuarPremium)
             {
+            // CURRENCY !! return modified value instead of altering argument
             double z = dblize(erpmt);
             Irc7702_->ProcessGptPmt(Year, z);
             erpmt = round_gross_premium().c(z);
@@ -1265,18 +1252,10 @@ void AccountValue::TxAscertainDesiredPayment()
         GrossPmts  [Month] += erpmt;
         }
 
-    if(GrossPmts[Month] != EeGrossPmts[Month] + ErGrossPmts[Month])
-        warning()
-            << GrossPmts[Month] << " GrossPmts[Month]\n"
-            << EeGrossPmts[Month] + ErGrossPmts[Month] << " EeGrossPmts[Month] 
+ ErGrossPmts[Month]\n"
-            << EeGrossPmts[Month] << " EeGrossPmts[Month]\n"
-            << ErGrossPmts[Month] << " ErGrossPmts[Month]\n"
-            << Year << " Year\n"
-            << Month << " Month\n"
-            << LMI_FLUSH
-            ;
     assert_pmts_add_up(__FILE__, __LINE__, Month);
-    // bignum: the largest integer convertible to and from double.
+    // CURRENCY !! is this useful?
+    // bignum: the largest integer convertible to and from double
+    // such that incrementing it by one loses that property.
     LMI_ASSERT(GrossPmts[Month] < from_cents(1LL << 53));
 
     if(0 == Year && 0 == Month)
@@ -1284,6 +1263,7 @@ void AccountValue::TxAscertainDesiredPayment()
         // Illustration-reg guaranteed premium ignores GPT limit.
         if(!SolvingForGuarPremium)
             {
+            // CURRENCY !! return modified value instead of altering argument
             double z = dblize(Dumpin);
             Irc7702_->ProcessGptPmt(Year, z);
             Dumpin = round_gross_premium().c(z);
@@ -1322,6 +1302,8 @@ void AccountValue::TxLimitPayment(double a_maxpmt)
             gross_1035 = External1035Amount + Internal1035Amount;
             }
         currency gross_pmt_without_1035 = GrossPmts[Month] - gross_1035;
+        // CURRENCY !! support infinities?
+//      gross_pmt_without_1035 = std::min(gross_pmt_without_1035, a_maxpmt);
         gross_pmt_without_1035 = 
round_gross_premium().c(std::min(dblize(gross_pmt_without_1035), a_maxpmt));
         // TODO ?? For now at least, reduce employee premium first.
         progressively_limit
@@ -1404,14 +1386,6 @@ void AccountValue::TxAcceptPayment(currency a_pmt)
         return;
         }
 
-    if(a_pmt < C0)
-        warning()
-            << a_pmt << " a_pmt\n"
-            << Year << " Year\n"
-            << Month << " Month\n"
-//          << z << " z\n"
-            << LMI_FLUSH
-            ;
     LMI_ASSERT(C0 <= a_pmt);
     // Internal 1035 exchanges may be exempt from premium tax; they're
     // handled elsewhere, so here the exempt amount is always zero.
@@ -1520,7 +1494,6 @@ currency AccountValue::GetPremLoad
         ;
     LMI_ASSERT(0.0 <= sum_of_separate_loads);
 
-#if 1
     double total_load =
           target_portion * YearsTotLoadTgt
         + excess_portion * YearsTotLoadExc
@@ -1530,7 +1503,7 @@ currency AccountValue::GetPremLoad
         (   PremiumTax_->is_tiered()
         ||  materially_equal(total_load, sum_of_separate_loads)
         );
-#endif // 0
+
     return round_net_premium().c(sum_of_separate_loads);
 }
 
@@ -1597,18 +1570,16 @@ void AccountValue::TxSetBOMAV()
             LMI_ASSERT(C0 == term_specamt(0));
             }
         LMI_ASSERT(yare_input_.InforceSpecAmtLoadBase <= SpecAmtLoadLimit);
-        SpecAmtLoadBase = round_specamt().c
-            (
-            std::min
-                (   SpecAmtLoadLimit
-                ,   (0 == Year && 0 == Month)
-                    ? dblize(std::max
-                        (term_specamt(0) + base_specamt(0)
-                        ,round_death_benefit().c(NetPmts[0] * 
YearsCorridorFactor)
-                        ))
-                    : yare_input_.InforceSpecAmtLoadBase
+        SpecAmtLoadBase =
+            (0 == Year && 0 == Month)
+            ? std::max
+                (term_specamt(0) + base_specamt(0)
+                ,round_specamt().c(NetPmts[0] * YearsCorridorFactor)
                 )
-            );
+            : round_specamt().c(yare_input_.InforceSpecAmtLoadBase)
+            ;
+        // CURRENCY !! support infinities?
+        SpecAmtLoadBase = round_specamt().c(std::min(dblize(SpecAmtLoadBase), 
SpecAmtLoadLimit));
         }
 
     // These assignments must happen every month.
@@ -1723,7 +1694,7 @@ void AccountValue::TxSetDeathBft()
     DB7702A = DBReflectingCorr + TermDB;
 
     DcvDeathBft = std::max
-        (dblize(DBIgnoringCorr)
+        (   dblize(DBIgnoringCorr)
         ,   (
                 YearsCorridorFactor
             *   (   Dcv
@@ -1945,11 +1916,13 @@ void AccountValue::TxSetRiderDed()
                     YearsWpRate
                     *   (
                             DcvCoiCharge
-                        +   dblize(MonthsPolicyFees)
-                        +   dblize(SpecAmtLoad)
-                        +   dblize(AdbCharge)
-                        +   dblize(SpouseRiderCharge)
-                        +   dblize(ChildRiderCharge)
+                        +   dblize
+                            ( MonthsPolicyFees
+                            + SpecAmtLoad
+                            + AdbCharge
+                            + SpouseRiderCharge
+                            + ChildRiderCharge
+                            )
                         +   DcvTermCharge
                         );
                 }
@@ -2040,16 +2013,12 @@ void AccountValue::TxTestHoneymoonForExpiration()
     if(HoneymoonValue <= C0 || HoneymoonValue < csv_ignoring_loan)
         {
         HoneymoonActive = false;
-        // In 'master', this is
-        //   -std::numeric_limits<double>::max()
-        // which is the identity element for std::max(). Here, it's nearly
-        // the equivalent for currency::data_type; dividing it by 101 is a
-        // casual defense against commuting between dollars and cents.
 #if defined USE_CURRENCY_CLASS
-        HoneymoonValue  = 
from_cents(-std::numeric_limits<currency::data_type>::max() / 101);
+        // CURRENCY !! support infinities?
+        HoneymoonValue = 
-from_cents(std::numeric_limits<currency::data_type>::max());
 #else  // !defined USE_CURRENCY_CLASS
         HoneymoonValue = -std::numeric_limits<double>::max();
-#endif // ! defined USE_CURRENCY_CLASS
+#endif // !defined USE_CURRENCY_CLASS
         }
 }
 
@@ -2063,6 +2032,7 @@ void AccountValue::TxTakeSepAcctLoad()
 {
     if(SepAcctLoadIsDynamic)
         {
+        // CURRENCY !! should class stratified_charges use currency?
         double stratified_load = StratifiedCharges_->stratified_sepacct_load
             (GenBasis_
             ,dblize(AssetsPostBom)
@@ -2098,7 +2068,7 @@ void AccountValue::TxTakeSepAcctLoad()
         }
 
     SepAcctLoad = round_interest_credit().c(YearsSepAcctLoadRate * AVSepAcct);
-    // Does this seem right? Mightn't it take a sepacct load from the genacct?
+    // CURRENCY !! Does this seem right? Mightn't it take a sepacct load from 
the genacct?
     process_deduction(SepAcctLoad);
     YearsTotalSepAcctLoad += SepAcctLoad;
     Dcv -= dblize(SepAcctLoad);
@@ -2356,20 +2326,21 @@ currency AccountValue::anticipated_deduction
 
 void AccountValue::SetMaxWD()
 {
-    double max_wd =
-          AVGenAcct * MaxWdGenAcctValMult
-        + AVSepAcct * MaxWdSepAcctValMult
-        + dblize(AVRegLn  + AVPrfLn)
-        - dblize(RegLnBal + PrfLnBal)
-        - dblize(anticipated_deduction(MaxWDDed_))
-        - dblize(std::max(C0, SurrChg()))
+    MaxWD =
+          round_withdrawal().c
+            ( AVGenAcct * MaxWdGenAcctValMult
+            + AVSepAcct * MaxWdSepAcctValMult
+            )
+        + (AVRegLn  + AVPrfLn)
+        - (RegLnBal + PrfLnBal)
+        - anticipated_deduction(MaxWDDed_)
+        - std::max(C0, SurrChg())
         ;
-    if(max_wd < dblize(MinWD))
+    if(MaxWD < MinWD)
         {
-        max_wd = 0.0;
+        MaxWD = C0;
         }
-    max_wd = std::max(0.0, max_wd);
-    MaxWD = round_withdrawal().c(max_wd);
+    MaxWD = std::max(C0, MaxWD);
 }
 
 /// Take a withdrawal.
@@ -2540,13 +2511,14 @@ void AccountValue::TxTakeWD()
         return;
         }
 
-    GrossWD = round_withdrawal().c(dblize(NetWD) + std::min(dblize(WDFee), 
NetWD * WDFeeRate));
+    GrossWD = NetWD + std::min(WDFee, round_withdrawal().c(NetWD * WDFeeRate));
 
     // Free partial surrenders: for instance, the first 20% of account
     // value might be withdrawn each policy year free of surrender
     // charge. This would become more complicated if we maintained
     // distinct surrender-charge layers.
 
+    // CURRENCY !! more efficient: currency / currency --> double
     double surrchg_proportion = SurrChg_[Year] / dblize(csv);
     currency non_free_wd = GrossWD;
     if(0.0 != FreeWDProportion[Year])
@@ -2646,7 +2618,7 @@ void AccountValue::TxTakeWD()
             {
             // TODO ?? TAXATION !! What if reference argument
             // 'premiums_paid_increment' is modified?
-            double premiums_paid_increment = 0.0 - dblize(GrossWD);
+            double premiums_paid_increment = -dblize(GrossWD);
             Irc7702_->ProcessGptPmt(Year, premiums_paid_increment);
             }
         }
@@ -2848,8 +2820,11 @@ void AccountValue::TxTestLapse()
             (       NoLapseMinAge <= Year + BasicValues::GetIssueAge()
                 &&  NoLapseMinDur <= Year
             ||      CumPmts < CumNoLapsePrem
-//              &&  !materially_equal(CumPmts, CumNoLapsePrem)
-                &&  CumPmts != CumNoLapsePrem
+#if defined USE_CURRENCY_CLASS
+            // x<y --> x<>y for x,y integral
+#else  // !defined USE_CURRENCY_CLASS
+                &&  !materially_equal(CumPmts, CumNoLapsePrem)
+#endif // !defined USE_CURRENCY_CLASS
             )
             {
             NoLapseActive = false;
diff --git a/ihs_avsolve.cpp b/ihs_avsolve.cpp
index ec57dd6..1f2f18d 100644
--- a/ihs_avsolve.cpp
+++ b/ihs_avsolve.cpp
@@ -69,7 +69,7 @@ class SolveHelper
     // or at least to make this function take a 'currency' argument.
     double operator()(double a_CandidateValue)
         {
-// CURRENCY !! Consider using zero<currency> instead of double.
+// CURRENCY !! Consider using decimal_root<currency> instead of double.
         return dblize(av.SolveTest(av.round_minutiae().c(a_CandidateValue)));
         }
 };
@@ -221,7 +221,6 @@ currency AccountValue::SolveTest(currency a_CandidateValue)
     // counters and iterators--it's one past the end--but indexing
     // must decrement it.
     // CURRENCY !! Cents in ledger will make rounding unnecessary.
-//  currency value {VariantValues().CSVNet[SolveTargetDuration_ - 1]};
     currency value = 
round_minutiae().c(VariantValues().CSVNet[SolveTargetDuration_ - 1]);
     if(mce_solve_for_target_naar == SolveTarget_)
         {
@@ -251,8 +250,8 @@ currency AccountValue::SolveTest(currency a_CandidateValue)
 
     if(mce_solve_for_non_mec == SolveTarget_)
         {
-        static const currency C1 = from_cents(100); // One hundred cents.
-        return InvariantValues().IsMec ? -C1 : C1;
+        static const currency C100 = from_cents(100); // one dollar
+        return InvariantValues().IsMec ? -C100 : C100;
         }
 
     return value - SolveTargetCsv_;
@@ -379,11 +378,13 @@ currency AccountValue::Solve
             // Generally, base and term are independent, and it is
             // the base specamt that's being solved for here, so set
             // the minimum as though there were no term.
-            lower_bound = dblize(minimum_specified_amount
-                (  0 == SolveBeginYear_
-                && yare_input_.EffectiveDate == yare_input_.InforceAsOfDate
-                ,false
-                ));
+            lower_bound = dblize
+                (minimum_specified_amount
+                    (  0 == SolveBeginYear_
+                    && yare_input_.EffectiveDate == yare_input_.InforceAsOfDate
+                    ,false
+                    )
+                );
             }
             break;
         case mce_solve_ee_prem:
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index b63d368..2ff24a5 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -567,11 +567,10 @@ currency BasicValues::GetAnnualTgtPrem(int a_year, 
currency a_specamt) const
 
 void BasicValues::SetPermanentInvariants()
 {
-    // maybe implement query_into<currency>
-    MinIssSpecAmt      = 
round_specamt().c(database().query<int>(DB_MinIssSpecAmt     ));
-    MinIssBaseSpecAmt  = 
round_specamt().c(database().query<int>(DB_MinIssBaseSpecAmt ));
-    MinRenlSpecAmt     = 
round_specamt().c(database().query<int>(DB_MinRenlSpecAmt    ));
-    MinRenlBaseSpecAmt = 
round_specamt().c(database().query<int>(DB_MinRenlBaseSpecAmt));
+    MinIssSpecAmt      = 
round_specamt().c(database().query<double>(DB_MinIssSpecAmt     ));
+    MinIssBaseSpecAmt  = 
round_specamt().c(database().query<double>(DB_MinIssBaseSpecAmt ));
+    MinRenlSpecAmt     = 
round_specamt().c(database().query<double>(DB_MinRenlSpecAmt    ));
+    MinRenlBaseSpecAmt = 
round_specamt().c(database().query<double>(DB_MinRenlBaseSpecAmt));
     database().query_into(DB_NoLapseDboLvlOnly    , NoLapseDboLvlOnly);
     database().query_into(DB_NoLapseUnratedOnly   , NoLapseUnratedOnly);
     database().query_into(DB_DboChgCanIncrSpecAmt , OptChgCanIncrSA);
@@ -585,18 +584,14 @@ void BasicValues::SetPermanentInvariants()
     database().query_into(DB_TermForcedConvAge    , TermForcedConvAge);
     database().query_into(DB_TermForcedConvDur    , TermForcedConvDur);
     database().query_into(DB_ExpSpecAmtLimit      , ExpPerKLimit);
-// NO! ExpPerKLimit = database().query<int>(DB_ExpSpecAmtLimit);
     database().query_into(DB_MinPremType          , MinPremType);
     database().query_into(DB_TgtPremType          , TgtPremType);
     database().query_into(DB_TgtPremFixedAtIssue  , TgtPremFixedAtIssue);
-    TgtPremMonthlyPolFee = 
round_gross_premium().c(database().query<int>(DB_TgtPremMonthlyPolFee));
+    TgtPremMonthlyPolFee = 
round_gross_premium().c(database().query<double>(DB_TgtPremMonthlyPolFee));
     // Assertion: see comments on GetModalPremTgtFromTable().
     LMI_ASSERT(C0 == TgtPremMonthlyPolFee || oe_modal_table == TgtPremType);
     database().query_into(DB_CurrCoiTable0Limit   , CurrCoiTable0Limit);
     database().query_into(DB_CurrCoiTable1Limit   , CurrCoiTable1Limit);
-// NO!
-//  CurrCoiTable0Limit   = database().query<int>(DB_CurrCoiTable0Limit  );
-//  CurrCoiTable1Limit   = database().query<int>(DB_CurrCoiTable1Limit  );
     LMI_ASSERT(0.0                <= CurrCoiTable0Limit);
     LMI_ASSERT(CurrCoiTable0Limit <= CurrCoiTable1Limit);
     database().query_into(DB_CoiInforceReentry    , CoiInforceReentry);
@@ -612,11 +607,8 @@ void BasicValues::SetPermanentInvariants()
     database().query_into(DB_AdbLimit             , AdbLimit);
     database().query_into(DB_WpLimit              , WpLimit);
     database().query_into(DB_SpecAmtLoadLimit     , SpecAmtLoadLimit);
-//  AdbLimit         = database().query<int>(DB_AdbLimit        );
-//  WpLimit          = database().query<int>(DB_WpLimit         );
-//  SpecAmtLoadLimit = database().query<int>(DB_SpecAmtLoadLimit);
-    MinWD            = round_withdrawal().c(database().query<int>(DB_MinWd));
-    WDFee            = round_withdrawal().c(database().query<int>(DB_WdFee));
+    MinWD = round_withdrawal().c(database().query<double>(DB_MinWd));
+    WDFee = round_withdrawal().c(database().query<double>(DB_WdFee));
     database().query_into(DB_WdFeeRate            , WDFeeRate);
     database().query_into(DB_AllowChangeToDbo2    , AllowChangeToDBO2);
     database().query_into(DB_AllowSpecAmtIncr     , AllowSAIncr);
@@ -1157,6 +1149,10 @@ std::pair<double,double> BasicValues::approx_mly_ded
     if(yare_input_.AccidentalDeathBenefit)
         {
         double const r = MortalityRates_->AdbRates()[year];
+        // CURRENCY !! Here and elsewhere in this file, consider
+        // letting currency objects assume infinite values--so that
+        // 'AdbLimit' could be of currency type, and dblize() would
+        // not be needed.
         mly_ded += r * std::min(dblize(specamt), AdbLimit);
         }
 
diff --git a/solve.cpp b/solve.cpp
index 124d906..3216232 100644
--- a/solve.cpp
+++ b/solve.cpp
@@ -98,8 +98,7 @@ currency SolveTest()
             );
         }
 
-// CURRENCY !! Cents in ledger will make rounding unnecessary.
-//  currency z {ConstThat->VariantValues().CSVNet[ThatSolveTgtYear - 1]};
+    // CURRENCY !! Cents in ledger will make rounding unnecessary.
     currency z = 
round_to_cents.c(ConstThat->VariantValues().CSVNet[ThatSolveTgtYear - 1]);
     if(Negative < C0)
         z = std::min(z, Negative);
@@ -158,7 +157,6 @@ currency SolveTest()
 
 //============================================================================
 inline static double SolveSpecAmt(double CandidateValue)
-//inline static double SolveSpecAmt(currency CandidateValue)
 {
 // IHS !! Change surrchg when SA changes?
     That->SolveSetSpecAmt(round_to_cents.c(CandidateValue), ThatSolveBegYear, 
ThatSolveEndYear);
@@ -167,7 +165,6 @@ inline static double SolveSpecAmt(double CandidateValue)
 
 //============================================================================
 inline static double SolvePrem(double CandidateValue)
-//inline static double SolvePrem(currency CandidateValue)
 {
     That->SolveSetPmts(round_to_cents.c(CandidateValue), ThatSolveBegYear, 
ThatSolveEndYear);
     return only_set_values ? 0.0 : dblize(SolveTest());
@@ -175,7 +172,6 @@ inline static double SolvePrem(double CandidateValue)
 
 //============================================================================
 inline static double SolveLoan(double CandidateValue)
-//inline static double SolveLoan(currency CandidateValue)
 {
     That->SolveSetLoans(round_to_cents.c(CandidateValue), ThatSolveBegYear, 
ThatSolveEndYear);
     return only_set_values ? 0.0 : dblize(SolveTest());
@@ -183,7 +179,6 @@ inline static double SolveLoan(double CandidateValue)
 
 //============================================================================
 inline static double SolveWD(double CandidateValue)
-//inline static double SolveWD(currency CandidateValue)
 {
     That->SolveSetWDs(round_to_cents.c(CandidateValue), ThatSolveBegYear, 
ThatSolveEndYear);
     return only_set_values ? 0.0 : dblize(SolveTest());
@@ -265,7 +260,6 @@ currency AccountValue::Solve()
         }
 
     double(*SolveFn)(double)     = nullptr;
-//  double(*SolveFn)(currency)   = nullptr;
     double           LowerBound  = 0.0;
     double           UpperBound  = 0.0;
     root_bias        Bias        = bias_higher;
@@ -358,8 +352,8 @@ currency AccountValue::Solve()
     // generate or analyze account values. This global variable is a
     // kludge, but so is 'That'; a function object is wanted instead.
     only_set_values = !Solving;
-    currency const solution_cents = round_to_cents.c(Solution.first);
 
+    currency const solution_cents = round_to_cents.c(Solution.first);
     SolveFn(dblize(solution_cents));
     return solution_cents;
 }



reply via email to

[Prev in Thread] Current Thread [Next in Thread]