[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] valyuta/001 504f88c 3/4: See README.branch
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] valyuta/001 504f88c 3/4: See README.branch |
Date: |
Sat, 22 Aug 2020 11:37:10 -0400 (EDT) |
branch: valyuta/001
commit 504f88c6cc1e925ef280deaffe2697a44029df73
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
See README.branch
The two earlier commits on this branch set up some code changes that
wouldn't compile. This commit rectifies them, often in a cavalier
manner. Do read 'README.branch'.
---
README.branch | 131 +++++++++++++++++++++++
account_value.hpp | 61 ++++++-----
accountvalue.cpp | 54 +++++-----
basic_values.hpp | 36 ++++---
basicvalues.cpp | 20 ++--
currency.hpp | 58 ++++++++++-
currency_test.cpp | 6 ++
death_benefits.cpp | 4 +-
gpt_specamt.cpp | 2 +-
group_values.cpp | 3 +-
ihs_acctval.cpp | 90 ++++++++--------
ihs_avdebug.cpp | 4 +-
ihs_avmly.cpp | 260 ++++++++++++++++++++++++++++------------------
ihs_avsolve.cpp | 14 +--
ihs_avstrtgy.cpp | 62 +++++------
ihs_basicval.cpp | 176 +++++++++++++++++--------------
ledger_invariant_init.cpp | 8 +-
outlay.cpp | 18 ++--
solve.cpp | 40 +++----
19 files changed, 663 insertions(+), 384 deletions(-)
diff --git a/README.branch b/README.branch
new file mode 100644
index 0000000..118cf9f
--- /dev/null
+++ b/README.branch
@@ -0,0 +1,131 @@
+// README for this branch.
+//
+// Copyright (C) 2020 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// https://savannah.nongnu.org/projects/lmi
+// email: <gchicares@sbcglobal.net>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+[based on 'README.branch' from branch valyuta/000]
+
+This branch explores replacing 'double' with a currency class. Several
+dozen data members of class AccountValue were changed to the new type.
+Those members were chosen as representative, for experimentation;
+many others should also be of currency type.
+
+The currency class is a coarse exploratory attempt.
+
+Various code changes were made:
+ - some explicit type conversions;
+ - std::max<double>, with template parameter explicitly specified, to
+ make it work with one double and one currency argument;
+ - an intermediate variable (max_loan) of type double, to hold some
+ calculations that are necessarily floating point before posting the
+ final result to the (currency) MaxLoan variable;
+
+For branch valyuta/000:
+ - similarly, restructuring of the NAAR calculation.
+but for this branch, that calculation wasn't restructured. It's not
+clear which way is better.
+
+The regression test does not pass. Some variables have never been
+rounded, though they are certainly integral numbers of cents in the
+problem domain:
+ AVSepAcct especially; also AVGenAcct sometimes
+and those are defects that should be fixed in the trunk. On branch
+valyuta/000, these were also noted:
+ DacTaxLoad, PremTaxLoad
+ SpecAmtLoad
+ RiderCharges
+ GrossPmt (for testdeck 'sample')
+but some of those defects have since been fixed in origin/master,
+while others (DacTaxLoad, PremTaxLoad) are deliberately not rounded
+(but total premium load is rounded).
+
+Future directions:
+
+The ledger class too often contains values that are plainly intended
+to represent an integral number of cents, but whose values look like
+ 17696.499999999996362
+where a compiler might choose either closest representable neighbor,
+the choice depending on the phase of the moon; such false positives
+make regression testing too laborious. One idea is to "de-round" these
+values, but that would be to let the tail wag the dog.
+
+It seems far better to use a fixed-point class for values that are
+supposed to be an integral number of cents. The underlying type can
+be either integer or floating point; that's an implementation detail
+that needn't be resolved now. The crucial idea is that the internal
+representation should be cents; users of course expect to see dollars
+and cents, but that's a View concern--the Model wants to use cents.
+With a fixed-point class, a value such as 17696.499999999996362 that
+had actually been rounded would be stored as 1769650 exactly, and
+printed as 17696.50 (i.e., with an explicit decimal point) for
+regression testing (and of course for end-user report generation).
+
+This coarse implementation runs slowly:
+
+ * branch valyuta/000
+
+/opt/lmi/bin[0]$wine ./lmi_cli_shared.exe --accept --data_path=/opt/lmi/data
--selftest
+Test solve speed: 1.805e-01 s mean; 180125 us least of 6 runs
+
+versus about 89000 us for production, i.e., half as fast. Numeric
+conversions probably account for most of that overhead.
+
+ * branch valyuta/001 [this branch]
+
+Test solve speed: 3.644e-01 s mean; 363663 us least of 3 runs
+
+...even with
+ using data_type = double;
+in 'currency.hpp'. That's much worse than the older branch because
+many more variables have been changed to 'currency' on this branch.
+
+With
+ using data_type = std::int64_t;
+it's a little faster:
+
+Test solve speed: 3.394e-01 s mean; 338108 us least of 3 runs
+
+That 25000 us improvement is small relative to the enormous overhead
+of this experimental work; but if that overhead can be substantially
+eliminated, the integer-math improvement is about fifteen percent of
+the speed measured in production.
+
+With
+ using currency = double;
+it's about as fast as production:
+
+Test solve speed: 9.283e-02 s mean; 92595 us least of 11 runs
+
+Regression testing finds many discrepancies. With
+ using data_type = double;
+a couple of testcases are off by a couple hundred dollars, about
+a dozen more are off by ten to a hundred, and about three dozen
+others by one to ten dollars; about five hundred differ by at least
+one cent, and seven hundred by less than one cent. It is unknown
+which of these regressions are actually errors; certainly some of
+the touchstone values (like non-integral-cents account values) are
+defects that should be fixed on master. Discrepancies in regression
+testing are about the same with
+ using data_type = std::int64_t;
+OTOH, with
+ using currency = double;
+only about a hundred testcases differ at all, of which about seventy
+are off by one cent or more, yet one testcase is off by about 1e+14
+due to an insane 'KFactor', apparently arising from catastrophic
+cancellation of a minute 'ProjectedCoi'.
diff --git a/account_value.hpp b/account_value.hpp
index 11dafbc..519d6d6 100644
--- a/account_value.hpp
+++ b/account_value.hpp
@@ -148,11 +148,11 @@ class LMI_SO AccountValue final
,int a_InforceMonth = 0
);
- void SolveSetSpecAmt (currency a_CandidateValue);
- void SolveSetEePrem (currency a_CandidateValue);
- void SolveSetErPrem (currency a_CandidateValue);
- void SolveSetLoan (currency a_CandidateValue);
- void SolveSetWD (currency a_CandidateValue);
+ void SolveSetSpecAmt (double a_CandidateValue);
+ void SolveSetEePrem (double a_CandidateValue);
+ void SolveSetErPrem (double a_CandidateValue);
+ void SolveSetLoan (double a_CandidateValue);
+ void SolveSetWD (double a_CandidateValue);
void DebugPrint ();
@@ -190,21 +190,21 @@ class LMI_SO AccountValue final
bool PrecedesInforceDuration(int year, int month);
- currency Solve(); // Antediluvian.
- currency Solve
+ double Solve(); // Antediluvian.
+ double Solve
(mcenum_solve_type a_SolveType
,int a_SolveBeginYear
,int a_SolveEndYear
,mcenum_solve_target a_SolveTarget
- ,currency a_SolveTargetCsv
+ ,double a_SolveTargetCsv
,int a_SolveTargetYear
,mcenum_gen_basis a_SolveGenBasis
,mcenum_sep_basis a_SolveSepBasis
);
- currency SolveTest (currency a_CandidateValue);
+ double SolveTest (double a_CandidateValue);
- currency SolveGuarPremium ();
+ double SolveGuarPremium ();
double GetPartMortQ (int year) const;
@@ -236,13 +236,13 @@ class LMI_SO AccountValue final
void TxTestGPT ();
void TxPmt(); // Antediluvian.
void TxAscertainDesiredPayment ();
- void TxLimitPayment (currency a_maxpmt);
+ void TxLimitPayment (double a_maxpmt);
void TxRecognizePaymentFor7702A
(currency a_pmt
,bool a_this_payment_is_unnecessary
);
void TxAcceptPayment (currency payment);
- double GetPremLoad
+ currency GetPremLoad
(currency a_pmt
,double a_portion_exempt_from_premium_tax
);
@@ -271,14 +271,14 @@ class LMI_SO AccountValue final
// Reflects optional daily interest accounting.
double ActualMonthlyRate (double monthly_rate) const;
currency InterestCredited
- (double currency
- ,double monthly_rate
+ (currency principal
+ ,double monthly_rate
) const;
bool IsModalPmtDate (mcenum_mode) const;
bool IsModalPmtDate (); // Antediluvian.
int MonthsToNextModalPmtDate() const;
- currency anticipated_deduction (mcenum_anticipated_deduction);
+ currency anticipated_deduction (mcenum_anticipated_deduction);
currency minimum_specified_amount(bool issuing_now, bool term_rider) const;
void ChangeSpecAmtBy (currency delta);
@@ -422,8 +422,8 @@ class LMI_SO AccountValue final
currency NetMaxNecessaryPremium;
currency GrossMaxNecessaryPremium;
- currency NecessaryPremium;
- currency UnnecessaryPremium;
+ currency NecessaryPremium {currency(0.0)};
+ currency UnnecessaryPremium {currency(0.0)};
// 7702A CVAT deemed cash value.
double Dcv;
@@ -555,22 +555,29 @@ class LMI_SO AccountValue final
currency RequestedLoan;
currency RequestedWD;
- currency AdbCharge;
- currency SpouseRiderCharge;
- currency ChildRiderCharge;
- currency WpCharge;
- currency TermCharge;
+// These really ought to be rounded individually:
+// currency AdbCharge;
+// currency SpouseRiderCharge;
+// currency ChildRiderCharge;
+// currency WpCharge;
+// currency TermCharge;
+
+ double AdbCharge;
+ double SpouseRiderCharge;
+ double ChildRiderCharge;
+ double WpCharge;
+ double TermCharge;
currency MlyDed;
currency mlydedtonextmodalpmtdate; // Antediluvian.
currency YearsTotalCoiCharge;
currency YearsTotalRiderCharges;
- currency YearsAVRelOnDeath;
- currency YearsLoanRepaidOnDeath;
- currency YearsGrossClaims;
- currency YearsDeathProceeds;
- currency YearsNetClaims;
+ double YearsAVRelOnDeath;
+ double YearsLoanRepaidOnDeath;
+ double YearsGrossClaims;
+ double YearsDeathProceeds;
+ double YearsNetClaims;
double YearsTotalNetIntCredited;
double YearsTotalGrossIntCredited;
double YearsTotalLoanIntAccrued;
diff --git a/accountvalue.cpp b/accountvalue.cpp
index d4afefb..5ce5039 100644
--- a/accountvalue.cpp
+++ b/accountvalue.cpp
@@ -131,18 +131,18 @@ std::shared_ptr<Ledger const>
AccountValue::ledger_from_av() const
}
//============================================================================
-double AccountValue::RunAV()
+currency AccountValue::RunAV()
{
InvariantValues().Init(this);
- OverridingPmts = InvariantValues().EePmt;
+ OverridingPmts = currencyize(InvariantValues().EePmt);
Solving = mce_solve_none != yare_input_.SolveType;
return RunAllApplicableBases();
}
//============================================================================
-double AccountValue::RunOneBasis(mcenum_run_basis TheBasis)
+currency AccountValue::RunOneBasis(mcenum_run_basis TheBasis)
{
- double z;
+ currency z;
if(Solving)
{
// IHS !! Isn't this unreachable?
@@ -164,7 +164,7 @@ double AccountValue::RunOneBasis(mcenum_run_basis TheBasis)
// if running all bases
// run all bases
//
-double AccountValue::RunAllApplicableBases()
+currency AccountValue::RunAllApplicableBases()
{
// set pmts, specamt, surrchg
@@ -176,11 +176,11 @@ double AccountValue::RunAllApplicableBases()
,mce_sep_full
);
- double z = 0.0;
+ currency z(0.0);
if(Solving)
{
z = Solve();
- OverridingPmts = InvariantValues().EePmt;
+ OverridingPmts = currencyize(InvariantValues().EePmt);
ledger_->SetOneLedgerVariant(run_basis, VariantValues());
Solving = false;
}
@@ -202,7 +202,7 @@ double AccountValue::RunAllApplicableBases()
}
//============================================================================
-double AccountValue::RunOneCell(mcenum_run_basis TheBasis)
+currency AccountValue::RunOneCell(mcenum_run_basis TheBasis)
{
if(Solving)
{
@@ -323,8 +323,8 @@ void AccountValue::DoYear
YearsCorridorFactor = BasicValues::GetCorridorFactor()[Year];
- GrossPmts .assign(12, 0.0);
- NetPmts .assign(12, 0.0);
+ GrossPmts .assign(12, currency(0.0));
+ NetPmts .assign(12, currency(0.0));
// IHS !! Strategy here?
@@ -443,12 +443,12 @@ void AccountValue::PerformSpecAmtStrategy()
break;
case mce_sa_maximum:
{
- SA = GetModalMaxSpecAmt(InvariantValues().EeMode[0].value(),
InvariantValues().EePmt[0]);
+ SA = GetModalMaxSpecAmt(InvariantValues().EeMode[0].value(),
currency(InvariantValues().EePmt[0]));
}
break;
case mce_sa_target:
{
- SA = GetModalTgtSpecAmt(InvariantValues().EeMode[0].value(),
InvariantValues().EePmt[0]);
+ SA = GetModalTgtSpecAmt(InvariantValues().EeMode[0].value(),
currency(InvariantValues().EePmt[0]));
}
break;
case mce_sa_mep:
@@ -533,7 +533,7 @@ void AccountValue::TxOptionChange()
{
// Option 2: decrease spec amt by AV, but not below min spec amt.
ActualSpecAmt -= AV;
- ActualSpecAmt = std::max(ActualSpecAmt, MinSpecAmt);
+ ActualSpecAmt = std::max(ActualSpecAmt, currency(MinSpecAmt));
// An alternative is to lapse the policy.
}
break;
@@ -576,7 +576,7 @@ void AccountValue::TxSpecAmtChange()
}
// Change specified amount.
- ActualSpecAmt = std::max(MinSpecAmt, DeathBfts_->specamt()[Year]);
+ ActualSpecAmt = std::max(currency(MinSpecAmt),
DeathBfts_->specamt()[Year]);
// Carry the new spec amt forward into all future years.
for(int j = Year; j < BasicValues::GetLength(); ++j)
@@ -587,7 +587,7 @@ void AccountValue::TxSpecAmtChange()
//============================================================================
// Set payment according to selected strategy, in each non-solve year.
-void AccountValue::PerformPmtStrategy(double* a_Pmt)
+void AccountValue::PerformPmtStrategy(currency* a_Pmt)
{
// Don't override premium during solve period.
if
@@ -750,13 +750,13 @@ void AccountValue::TxSetDeathBft()
case mce_option1:
{
// Option 1: specamt, or corridor times AV if greater.
- deathbft = std::max(ActualSpecAmt, YearsCorridorFactor * AV);
+ deathbft = std::max<double>(ActualSpecAmt, YearsCorridorFactor *
AV);
}
break;
case mce_option2:
// Option 2: specamt plus AV, or corridor times AV if greater.
// Negative AV doesn't decrease death benefit.
- deathbft = std::max
+ deathbft = std::max<double>
(ActualSpecAmt + std::max(0.0, AV)
,YearsCorridorFactor * AV
);
@@ -802,7 +802,7 @@ void AccountValue::TxSetRiderDed()
AdbCharge = 0.0;
if(hasadb)
{
- AdbCharge = YearsAdbRate * std::min(500000.0, ActualSpecAmt);
+ AdbCharge = YearsAdbRate * std::min<double>(500000.0, ActualSpecAmt);
}
}
@@ -812,7 +812,7 @@ void AccountValue::TxDoMlyDed()
{
AVUnloaned -= CoiCharge + AdbCharge + WpCharge;
MlyDed = YearsMonthlyPolicyFee + CoiCharge + AdbCharge + WpCharge;
- mlydedtonextmodalpmtdate = MlyDed * MonthsToNextModalPmtDate();
+ mlydedtonextmodalpmtdate = doubleize(MlyDed) * MonthsToNextModalPmtDate();
// bad solution
}
/// Credit interest on account value.
@@ -915,7 +915,7 @@ void AccountValue::TxTakeWD()
// lapse the policy?
// IHS !! ActualSpecAmt = std::min(ActualSpecAmt, deathbft - wd);
ActualSpecAmt -= wd;
- ActualSpecAmt = std::max(ActualSpecAmt, MinSpecAmt);
+ ActualSpecAmt = std::max(ActualSpecAmt, currency(MinSpecAmt));
ActualSpecAmt = round_specamt()(ActualSpecAmt);
// IHS !! If WD causes AV < min AV, do we:
// reduce the WD?
@@ -941,7 +941,7 @@ void AccountValue::TxTakeWD()
}
// Deduct withdrawal fee.
- wd -= std::min(WDFee, wd * WDFeeRate);
+ wd -= std::min<double>(WDFee, wd * WDFeeRate);
// IHS !! This treats input WD as gross; it probably should be net. But
compare lmi.
InvariantValues().NetWD[Year] = wd;
@@ -979,7 +979,7 @@ void AccountValue::TxTakeLoan()
double IntAdj = std::pow((1.0 + YearsRegLnIntDueRate), 12 - Month);
IntAdj = (IntAdj - 1.0) / IntAdj;
MaxLoan *= 1.0 - IntAdj;
- MaxLoan = std::max(0.0, MaxLoan);
+ MaxLoan = std::max<double>(0.0, MaxLoan);
MaxLoan = round_loan()(MaxLoan);
// IHS !! Preferred loan calculations would go here: implemented in lmi.
@@ -1054,11 +1054,11 @@ double AccountValue::GetCurtateNetCoiChargeInforce()
const
{return 0.0;}
double AccountValue::GetProjectedCoiChargeInforce() const
{return 0.0;}
-double AccountValue::GetSepAcctAssetsInforce() const
- {return 0.0;}
-double AccountValue::IncrementBOM(int, int, double)
- {return 0.0;}
-void AccountValue::IncrementEOM(int, int, double, double)
+currency AccountValue::GetSepAcctAssetsInforce() const
+ {return currency(0.0);}
+currency AccountValue::IncrementBOM(int, int, double)
+ {return currency(0.0);}
+void AccountValue::IncrementEOM(int, int, currency, currency)
{return;}
void AccountValue::IncrementEOY(int)
{return;}
diff --git a/basic_values.hpp b/basic_values.hpp
index 0048256..521591d 100644
--- a/basic_values.hpp
+++ b/basic_values.hpp
@@ -304,13 +304,15 @@ class LMI_SO BasicValues
mcenum_defn_life_ins DefnLifeIns_;
mcenum_defn_material_change DefnMaterialChange_;
mcenum_dbopt_7702 Effective7702DboRop;
- currency MaxNAAR;
+ // Things like this are notionally currency, but they're cached
+ // values from a database of double-only values.
+ double /*currency*/ MaxNAAR;
int EndtAge;
- currency MinSpecAmt; // Antediluvian.
- currency MinIssSpecAmt;
- currency MinIssBaseSpecAmt;
- currency MinRenlSpecAmt;
- currency MinRenlBaseSpecAmt;
+ double /*currency*/ MinSpecAmt; // Antediluvian.
+ double /*currency*/ MinIssSpecAmt;
+ double /*currency*/ MinIssBaseSpecAmt;
+ double /*currency*/ MinRenlSpecAmt;
+ double /*currency*/ MinRenlBaseSpecAmt;
bool NoLapseDboLvlOnly;
bool NoLapseUnratedOnly;
bool OptChgCanIncrSA;
@@ -325,13 +327,13 @@ class LMI_SO BasicValues
int TermForcedConvDur;
bool TermIsDbFor7702;
bool TermIsDbFor7702A;
- currency ExpPerKLimit;
+ double /*currency*/ ExpPerKLimit;
oenum_modal_prem_type MinPremType;
oenum_modal_prem_type TgtPremType;
bool TgtPremFixedAtIssue;
- currency TgtPremMonthlyPolFee;
- currency CurrCoiTable0Limit;
- currency CurrCoiTable1Limit;
+ double /*currency*/ TgtPremMonthlyPolFee;
+ double /*currency*/ CurrCoiTable0Limit;
+ double /*currency*/ CurrCoiTable1Limit;
e_actuarial_table_method CoiInforceReentry;
mcenum_anticipated_deduction MaxWDDed_;
double MaxWdGenAcctValMult;
@@ -349,11 +351,11 @@ class LMI_SO BasicValues
bool SurrChgOnDecr;
std::vector<double> FreeWDProportion;
- currency AdbLimit;
- currency WpLimit;
- currency SpecAmtLoadLimit;
- currency MinWD;
- currency WDFee;
+ double /*currency*/ AdbLimit;
+ double /*currency*/ WpLimit;
+ double /*currency*/ SpecAmtLoadLimit;
+ double /*currency*/ MinWD;
+ double /*currency*/ WDFee;
double WDFeeRate;
bool AllowChangeToDBO2;
@@ -383,11 +385,11 @@ class LMI_SO BasicValues
BasicValues& operator=(BasicValues const&) = delete;
double mly_ded_discount_factor(int year, mcenum_mode mode) const;
- std::pair<currency,currency> approx_mly_ded
+ std::pair<double,double> approx_mly_ded
(int year
,currency specamt
) const;
- std::pair<currency,currency> approx_mly_ded_ex
+ std::pair<double,double> approx_mly_ded_ex
(int year
,currency specamt
,currency termamt
diff --git a/basicvalues.cpp b/basicvalues.cpp
index d93a0e0..4cb3cc7 100644
--- a/basicvalues.cpp
+++ b/basicvalues.cpp
@@ -136,20 +136,20 @@ double BasicValues::InvestmentManagementFee() const
//============================================================================
// IHS !! Simply calls the target-premium routine for now--see lmi.
-double BasicValues::GetModalMinPrem
+currency BasicValues::GetModalMinPrem
(int a_year
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
return GetModalTgtPrem(a_year, a_mode, a_specamt);
}
//============================================================================
-double BasicValues::GetModalTgtPrem
+currency BasicValues::GetModalTgtPrem
(int a_year
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
// IHS !! Simplistic. Ignores table ratings, flat extras, and
@@ -211,23 +211,23 @@ double BasicValues::GetModalTgtPrem
// IHS !! Parameterized in lmi.
static round_to<double> const round_it(2, r_upward);
- return round_it(z);
+ return currency(round_it(z));
}
//============================================================================
// Simply calls the target-specamt routine for now.
-double BasicValues::GetModalMaxSpecAmt
+currency BasicValues::GetModalMaxSpecAmt
(mcenum_mode a_mode
- ,double a_pmt
+ ,currency a_pmt
) const
{
return GetModalTgtSpecAmt(a_mode, a_pmt);
}
//============================================================================
-double BasicValues::GetModalTgtSpecAmt
+currency BasicValues::GetModalTgtSpecAmt
(mcenum_mode a_mode
- ,double a_pmt
+ ,currency a_pmt
) const
{
// IHS !! Factor out the (defectively simplistic) code this
@@ -285,7 +285,7 @@ double BasicValues::GetModalTgtSpecAmt
// IHS !! Parameterized in lmi.
static round_to<double> const round_it(0, r_downward);
- return round_it(z);
+ return currency(round_it(z));
}
//============================================================================
diff --git a/currency.hpp b/currency.hpp
index 9dea3e8..ff738b0 100644
--- a/currency.hpp
+++ b/currency.hpp
@@ -29,10 +29,28 @@
#include <cstdint> // int64_t
#include <iostream> // ostream
+#include <vector>
+#define USE_CURRENCY_CLASS
+
+#if !defined USE_CURRENCY_CLASS
+using currency = double;
+
+#if defined __GNUC__
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif // defined __GNUC__
+#endif // !defined USE_CURRENCY_CLASS
+
+#if defined USE_CURRENCY_CLASS
class currency
{
- using data_type = std::int64_t;
+#if defined __GNUC__
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif // defined __GNUC__
+ using data_type = double;
+// using data_type = long double;
+// using data_type = std::int64_t;
+
friend std::ostream& operator<<(std::ostream&, currency const&);
friend class currency_test;
@@ -68,6 +86,8 @@ class currency
// Is this the ideal signature for this operator?
// currency operator-() const {return currency(-m_);}
// currency& operator-() {m_ = -m_; return *this;}
+// Dangerous--demonstrably makes calculations wrong, but hard to see why
+// currency operator-() const {return currency(bourn_cast<double>(-m_));}
currency& operator+=(currency z) {m_ += z.m_; return *this;}
currency& operator-=(currency z) {m_ -= z.m_; return *this;}
@@ -83,6 +103,8 @@ class currency
// Far too permissive:
// currency& operator*=(double z) {m_ = static_cast<data_type>(100.0 *
to_double() * z); return *this;}
double operator*=(double z) {return to_double() * z;}
+// Dangerous--can somehow take the place of operator*(double)
+// currency const& operator*=(int z) {m_ *= z; return *this;}
private:
// Want something just slightly more permissive:
@@ -107,7 +129,10 @@ class currency
inline currency operator+(currency lhs, double rhs) {return lhs +=
currency(rhs);}
inline currency operator-(currency lhs, double rhs) {return lhs -=
currency(rhs);}
//inline currency operator*(currency lhs, double rhs) {return lhs *=
currency(rhs);}
-inline double operator*(currency lhs, double rhs) {return lhs *=
currency(rhs);}
+////inline double operator*(currency lhs, double rhs) {return lhs *=
currency(rhs);}
+inline double operator*(currency lhs, double rhs) {return lhs.operator
double() * rhs;}
+//inline currency operator*(currency lhs, int rhs) {return lhs *= rhs;}
+//inline currency operator*(int lhs, currency rhs) {return rhs *= lhs;}
inline std::ostream& operator<<(std::ostream& os, currency const& c)
{
@@ -115,4 +140,33 @@ inline std::ostream& operator<<(std::ostream& os, currency
const& c)
return os << c.to_double();
}
+#endif // defined USE_CURRENCY_CLASS
+
+// Sloppy.
+inline currency requantize(double z) {return currency(z);}
+
+inline std::vector<currency> currencyize(std::vector<double> const& z)
+{
+ std::vector<currency> r;
+ r.reserve(z.size());
+ for(auto const& i : z)
+ r.push_back(currency(i));
+ return r;
+}
+
+inline double doubleize(currency const& z)
+{
+ return currency(z);
+}
+
+inline std::vector<double> doubleize(std::vector<currency> const& z)
+{
+ std::vector<double> r;
+ r.reserve(z.size());
+ for(auto const& i : z)
+// r.push_back(i.operator double()); // no need to convert explicitly
+ r.push_back(i);
+ return r;
+}
+
#endif // currency_hpp
diff --git a/currency_test.cpp b/currency_test.cpp
index e7af97f..06cad93 100644
--- a/currency_test.cpp
+++ b/currency_test.cpp
@@ -59,6 +59,12 @@ void currency_test::test_something()
a1 *= a0;
BOOST_TEST(0.00 == a1.operator double());
BOOST_TEST( 0 == a1.m_);
+// Not sure what this should do, if anything, but it prints "6.5".
+std::cout << a1 << std::endl;
+
+ currency a2 = currency(0.0) - a1;
+ BOOST_TEST(-6.50 == a2.operator double());
+ BOOST_TEST( -650 == a2.m_);
}
int test_main(int, char*[])
diff --git a/death_benefits.cpp b/death_benefits.cpp
index 55d9bdb..ac1b01b 100644
--- a/death_benefits.cpp
+++ b/death_benefits.cpp
@@ -51,7 +51,7 @@ death_benefits::death_benefits(int length, yare_input const&
yi)
}
//============================================================================
-void death_benefits::set_specamt(double z, int from_year, int to_year)
+void death_benefits::set_specamt(currency z, int from_year, int to_year)
{
#if 0
// Something like this would seem preferable, but it gives
@@ -69,7 +69,7 @@ void death_benefits::set_specamt(double z, int from_year, int
to_year)
}
//============================================================================
-void death_benefits::set_supplamt(double z, int from_year, int to_year)
+void death_benefits::set_supplamt(currency z, int from_year, int to_year)
{
#if 0
// Something like this would seem preferable, but it gives
diff --git a/gpt_specamt.cpp b/gpt_specamt.cpp
index 8e803a3..cca9384 100644
--- a/gpt_specamt.cpp
+++ b/gpt_specamt.cpp
@@ -107,7 +107,7 @@ class FindSpecAmt
,a_Trial
,NetPmtFactorTgt
,NetPmtFactorExc
- ,Values_.GetAnnualTgtPrem(Duration, SpecAmt)
+ ,Values_.GetAnnualTgtPrem(Duration, currency(SpecAmt))
)
- Premium
;
diff --git a/group_values.cpp b/group_values.cpp
index dbc55d6..81987cb 100644
--- a/group_values.cpp
+++ b/group_values.cpp
@@ -28,6 +28,7 @@
#include "assert_lmi.hpp"
#include "configurable_settings.hpp"
#include "contains.hpp"
+#include "currency.hpp"
#include "emit_ledger.hpp"
#include "fenv_guard.hpp"
#include "input.hpp"
@@ -418,7 +419,7 @@ census_run_result run_census_in_parallel::operator()
;
for(int month = inforce_month; month < 12; ++month)
{
- double assets = 0.0;
+ currency assets = currency(0.0);
// Get total case assets prior to interest crediting because
// those assets may determine the M&E charge.
diff --git a/ihs_acctval.cpp b/ihs_acctval.cpp
index 35bb946..fb499a5 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -187,7 +187,7 @@ std::shared_ptr<Ledger const>
AccountValue::ledger_from_av() const
}
//============================================================================
-double AccountValue::RunAV()
+currency AccountValue::RunAV()
{
/*
First run current, for solves and strategies. This determines
@@ -212,7 +212,7 @@ Then run other bases.
DebugPrintInit();
}
- double z = RunAllApplicableBases();
+ currency z = RunAllApplicableBases();
FinalizeLifeAllBases();
@@ -234,7 +234,7 @@ void AccountValue::SetGuarPrem()
}
//============================================================================
-double AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
+currency AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
{
if
( !BasicValues::IsSubjectToIllustrationReg()
@@ -247,7 +247,7 @@ double AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
;
}
- double z = 0.0;
+ currency z(0.0);
if(Solving)
{
// Apparently this should never be done because Solve() is called in
@@ -270,7 +270,7 @@ double AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
// if running all bases
// run all bases
//
-double AccountValue::RunAllApplicableBases()
+currency AccountValue::RunAllApplicableBases()
{
double z = 0.0;
@@ -317,7 +317,7 @@ double AccountValue::RunAllApplicableBases()
{
RunOneBasis(b);
}
- return z;
+ return currency(z); // dubious
}
//============================================================================
@@ -329,7 +329,7 @@ double AccountValue::RunAllApplicableBases()
/// which isn't necessary anyway because all the functions it calls
/// contain such a condition.
-double AccountValue::RunOneCell(mcenum_run_basis a_Basis)
+currency AccountValue::RunOneCell(mcenum_run_basis a_Basis)
{
InitializeLife(a_Basis);
@@ -355,7 +355,7 @@ double AccountValue::RunOneCell(mcenum_run_basis a_Basis)
IncrementEOM
(year
,month
- ,SepAcctValueAfterDeduction * InforceLivesBoy()
+ ,currency(SepAcctValueAfterDeduction * InforceLivesBoy()) //
conversion is dubious
,CumPmts
);
}
@@ -431,13 +431,13 @@ void AccountValue::InitializeLife(mcenum_run_basis
a_Basis)
// TODO ?? TAXATION !! Shouldn't we increase initial SA if contract in
corridor at issue?
OldDB = OldSA;
- SurrChg_.assign(BasicValues::GetLength(), 0.0);
+ SurrChg_.assign(BasicValues::GetLength(), currency(0.0));
// TAXATION !! Input::InforceAnnualTargetPremium should be used here.
double annual_target_premium = GetModalTgtPrem
(0
,mce_annual
- ,InvariantValues().SpecAmt[0]
+ ,requantize(InvariantValues().SpecAmt[0])
);
double sa =
InvariantValues().SpecAmt [0]
@@ -620,15 +620,15 @@ void AccountValue::SetInitialValues()
CumPmts = InforceCumPmts;
TaxBasis = InforceTaxBasis;
- YearlyTaxBasis.assign(BasicValues::GetLength(), 0.0);
+ YearlyTaxBasis.assign(BasicValues::GetLength(), currency(0.0));
MlyNoLapsePrem = 0.0;
CumNoLapsePrem = InforceCumNoLapsePrem;
// Initialize all elements of this vector to 'false'. Then, when
// the no-lapse criteria fail to be met, future values are right.
YearlyNoLapseActive.assign(BasicValues::GetLength(), false);
- loan_ullage_ .assign(BasicValues::GetLength(), 0.0);
- withdrawal_ullage_ .assign(BasicValues::GetLength(), 0.0);
+ loan_ullage_ .assign(BasicValues::GetLength(), currency(0.0));
+ withdrawal_ullage_ .assign(BasicValues::GetLength(), currency(0.0));
NoLapseActive = true;
if(NoLapseDboLvlOnly && mce_option1 != DeathBfts_->dbopt()[0])
{
@@ -731,7 +731,7 @@ void AccountValue::SetInitialValues()
//============================================================================
// Process monthly transactions up to but excluding interest credit
-double AccountValue::IncrementBOM
+currency AccountValue::IncrementBOM
(int year
,int month
,double a_case_k_factor
@@ -741,7 +741,7 @@ double AccountValue::IncrementBOM
{
// Return value is total assets. After the policy has lapsed or
// matured, there are no assets.
- return 0.0;
+ return currency(0.0);
}
// Paranoid check.
@@ -790,10 +790,10 @@ double AccountValue::IncrementBOM
//============================================================================
// Credit interest and process all subsequent monthly transactions
void AccountValue::IncrementEOM
- (int year
- ,int month
- ,double assets_post_bom
- ,double cum_pmts_post_bom
+ (int year
+ ,int month
+ ,currency assets_post_bom
+ ,currency cum_pmts_post_bom
)
{
if(ItLapsed || BasicValues::GetLength() <= Year)
@@ -813,8 +813,8 @@ void AccountValue::IncrementEOM
// Save arguments, constraining their values to be nonnegative,
// for calculating banded and tiered quantities.
- AssetsPostBom = std::max(0.0, assets_post_bom );
- CumPmtsPostBom = std::max(0.0, cum_pmts_post_bom);
+ AssetsPostBom = std::max<double>(0.0, assets_post_bom );
+ CumPmtsPostBom = std::max<double>(0.0, cum_pmts_post_bom);
DoMonthCR();
}
@@ -896,10 +896,10 @@ void AccountValue::InitializeYear()
// value depends on the maximum loan, so it cannot be known here.
ActualLoan = 0.0;
- GrossPmts .assign(12, 0.0);
- EeGrossPmts .assign(12, 0.0);
- ErGrossPmts .assign(12, 0.0);
- NetPmts .assign(12, 0.0);
+ GrossPmts .assign(12, currency(0.0));
+ EeGrossPmts .assign(12, currency(0.0));
+ ErGrossPmts .assign(12, currency(0.0));
+ NetPmts .assign(12, currency(0.0));
InitializeSpecAmt();
}
@@ -929,10 +929,10 @@ void AccountValue::InitializeSpecAmt()
InvariantValues().ModalMinimumPremium[Year] = GetModalMinPrem
(Year
,InvariantValues().ErMode[Year].value()
- ,InvariantValues().SpecAmt[Year]
+ ,requantize(InvariantValues().SpecAmt[Year])
);
InvariantValues().ErModalMinimumPremium[Year] =
- InvariantValues().ModalMinimumPremium[Year]
+ requantize(InvariantValues().ModalMinimumPremium[Year])
;
}
else
@@ -940,8 +940,8 @@ void AccountValue::InitializeSpecAmt()
auto const z = GetModalPremMlyDedEx
(Year
,InvariantValues().ErMode[Year].value()
- ,InvariantValues().SpecAmt[Year]
- ,InvariantValues().TermSpecAmt[Year]
+ ,requantize(InvariantValues().SpecAmt[Year])
+ ,requantize(InvariantValues().TermSpecAmt[Year])
);
InvariantValues().EeModalMinimumPremium[Year] = z.first;
InvariantValues().ErModalMinimumPremium[Year] = z.second;
@@ -970,12 +970,12 @@ void AccountValue::InitializeSpecAmt()
MlyNoLapsePrem = GetModalMinPrem
(target_year
,mce_monthly
- ,InvariantValues().SpecAmt[target_year]
+ ,requantize(InvariantValues().SpecAmt[target_year])
);
UnusedTargetPrem = GetModalTgtPrem
(target_year
,mce_annual
- ,InvariantValues().SpecAmt[target_year]
+ ,requantize(InvariantValues().SpecAmt[target_year])
);
AnnualTargetPrem = UnusedTargetPrem;
@@ -1018,7 +1018,7 @@ void AccountValue::set_list_bill_premium()
InvariantValues().ListBillPremium = GetListBillPremMlyDed
(Year
,InvariantValues().ErMode[Year].value()
- ,InvariantValues().SpecAmt[Year]
+ ,requantize(InvariantValues().SpecAmt[Year])
);
InvariantValues().ErListBillPremium =
InvariantValues().ListBillPremium
@@ -1029,8 +1029,8 @@ void AccountValue::set_list_bill_premium()
auto const z = GetListBillPremMlyDedEx
(Year
,InvariantValues().ErMode[Year].value()
- ,InvariantValues().SpecAmt[Year]
- ,InvariantValues().TermSpecAmt[Year]
+ ,requantize(InvariantValues().SpecAmt[Year])
+ ,requantize(InvariantValues().TermSpecAmt[Year])
);
InvariantValues().EeListBillPremium = z.first;
InvariantValues().ErListBillPremium = z.second;
@@ -1052,7 +1052,7 @@ void AccountValue::set_list_bill_premium()
///
/// SOMEDAY !! Table support and UL model reg formulas should be added.
-double AccountValue::SurrChg() const
+currency AccountValue::SurrChg() const
{
LMI_ASSERT(0.0 <= SurrChg_[Year]);
// For the nonce, CSVBoost() is netted against surrender charge.
@@ -1065,14 +1065,14 @@ double AccountValue::SurrChg() const
///
/// Probably the input field should be expunged.
-double AccountValue::CSVBoost() const
+currency AccountValue::CSVBoost() const
{
double const z =
CashValueEnhMult[Year]
+ yare_input_.CashValueEnhancementRate[Year]
;
LMI_ASSERT(0.0 <= z);
- return z * std::max(0.0, TotalAccountValue());
+ return currency(z * std::max<double>(0.0, TotalAccountValue())); //
round...or expunge
}
//============================================================================
@@ -1149,10 +1149,16 @@ void AccountValue::SetProjectedCoiCharge()
TxSetDeathBft();
TxSetTermAmt();
+#if 0 // no need for material_difference()
double this_years_terminal_naar = material_difference
(DBReflectingCorr + TermDB
,TotalAccountValue()
);
+#endif //
+ double this_years_terminal_naar =
+ DBReflectingCorr + TermDB
+ - TotalAccountValue()
+ ;
this_years_terminal_naar = std::max(0.0, this_years_terminal_naar);
double next_years_coi_rate = GetBandedCoiRates(GenBasis_, ActualSpecAmt)[1
+ Year];
@@ -1213,7 +1219,7 @@ void AccountValue::FinalizeYear()
csv_net -= surr_chg;
}
- csv_net = std::max(csv_net, HoneymoonValue);
+ csv_net = std::max<double>(csv_net, HoneymoonValue);
if(!Solving)
{
@@ -1239,7 +1245,7 @@ void AccountValue::FinalizeYear()
{
cv_7702 -= surr_chg;
}
- cv_7702 = std::max(cv_7702, HoneymoonValue);
+ cv_7702 = std::max<double>(cv_7702, HoneymoonValue);
VariantValues().AcctVal [Year] = total_av;
VariantValues().AVGenAcct [Year] = AVGenAcct + AVRegLn + AVPrfLn;
@@ -1504,14 +1510,14 @@ double AccountValue::GetPartMortQ(int a_year) const
}
//============================================================================
-double AccountValue::GetSepAcctAssetsInforce() const
+currency AccountValue::GetSepAcctAssetsInforce() const
{
if(ItLapsed || BasicValues::GetLength() <= Year)
{
- return 0.0;
+ return currency(0.0);
}
- return SepAcctValueAfterDeduction * InvariantValues().InforceLives[Year];
+ return currency(SepAcctValueAfterDeduction *
InvariantValues().InforceLives[Year]); // round this
}
//============================================================================
diff --git a/ihs_avdebug.cpp b/ihs_avdebug.cpp
index 425d8d0..bf6490a 100644
--- a/ihs_avdebug.cpp
+++ b/ihs_avdebug.cpp
@@ -413,7 +413,7 @@ void AccountValue::DebugPrint()
SetMonthlyDetail(eCumNoLapsePrem ,CumNoLapsePrem );
SetMonthlyDetail(eNoLapseActive ,NoLapseActive );
SetMonthlyDetail(eEOMAV ,TotalAccountValue() );
- SetMonthlyDetail(eHMValue ,std::max(HoneymoonValue, 0.0) );
+ SetMonthlyDetail(eHMValue ,std::max<double>(HoneymoonValue,
0.0) );
SetMonthlyDetail(eSurrChg ,SurrChg() );
// TODO ?? Unfortunately duplicated from AccountValue::FinalizeYear().
@@ -425,7 +425,7 @@ void AccountValue::DebugPrint()
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if it
existed.
;
- csv_net = std::max(HoneymoonValue, csv_net);
+ csv_net = std::max<double>(HoneymoonValue, csv_net);
SetMonthlyDetail(eEOMCSVNet ,csv_net );
SetMonthlyDetail(eEOMCV7702 ,CashValueFor7702() );
diff --git a/ihs_avmly.cpp b/ihs_avmly.cpp
index 049d515..4827009 100644
--- a/ihs_avmly.cpp
+++ b/ihs_avmly.cpp
@@ -144,21 +144,24 @@ void AccountValue::DoMonthDR()
// LMI_ASSERT(kludge_account_value == Dcv);
kludge_account_value = Dcv;
}
- kludge_account_value = std::max
+ kludge_account_value = std::max<double>
(HoneymoonValue
, kludge_account_value
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if it
existed.
);
// TODO ?? TAXATION !! Use CashValueFor7702() instead?
- double max_necessary_premium = Irc7702A_->MaxNecessaryPremium
+ // These two functions return DBL_MAX in some circumstances;
+ // that gets translated to a negative currency amount, which
+ // is most infelicitous.
+ double max_necessary_premium = Irc7702A_->MaxNecessaryPremium // round
(Dcv
,AnnualTargetPrem
,YearsTotLoadTgtLowestPremtax
,YearsTotLoadExcLowestPremtax
,kludge_account_value
);
- double max_non_mec_premium = Irc7702A_->MaxNonMecPremium
+ double max_non_mec_premium = Irc7702A_->MaxNonMecPremium // round
(Dcv
,AnnualTargetPrem
,YearsTotLoadTgtLowestPremtax
@@ -201,19 +204,42 @@ void AccountValue::DoMonthDR()
{
gross_1035 = External1035Amount + Internal1035Amount;
}
- double necessary_premium = std::min
+ double necessary_premium = std::min<double> // round
(GrossPmts[Month] - gross_1035
,max_necessary_premium
);
- double unnecessary_premium = material_difference
- (GrossPmts[Month]
+ double unnecessary_premium = material_difference // round
+ (doubleize(GrossPmts[Month])
,gross_1035 + necessary_premium
);
+ NecessaryPremium = necessary_premium; // or round here?
+ UnnecessaryPremium = unnecessary_premium; // or round here?
+
+ if(NecessaryPremium < 0.0 || UnnecessaryPremium < 0.0 || necessary_premium
< 0.0 || unnecessary_premium < 0.0)
+ warning()
+ << NecessaryPremium << " NecessaryPremium\n"
+ << UnnecessaryPremium << " UnnecessaryPremium\n"
+ << necessary_premium << " necessary_premium\n"
+ << unnecessary_premium << " unnecessary_premium\n"
+ << gross_1035 << " gross_1035\n"
+ << GrossPmts[Month] - gross_1035 << " GrossPmts[Month] - gross_1035\n"
+ << GrossPmts[Month] << " GrossPmts[Month]\n"
+ << doubleize(GrossPmts[Month]) << " doubleize(GrossPmts[Month])\n"
+ << kludge_account_value << " kludge_account_value\n"
+ << max_necessary_premium << " max_necessary_premium\n"
+ << max_non_mec_premium << " max_non_mec_premium\n"
+ << LMI_FLUSH
+ ;
// It is crucial to accept necessary premium before processing a
// material change, so that the correct DCV is used.
- TxRecognizePaymentFor7702A(necessary_premium, false);
- TxAcceptPayment(necessary_premium);
+ TxRecognizePaymentFor7702A(NecessaryPremium, false);
+ if(NecessaryPremium < 0.0)
+ warning()
+ << NecessaryPremium << " NecessaryPremium\n"
+ << LMI_FLUSH
+ ;
+ TxAcceptPayment(NecessaryPremium);
if(0.0 < unnecessary_premium)
{
Irc7702A_->InduceMaterialChange();
@@ -232,9 +258,15 @@ void AccountValue::DoMonthDR()
);
LMI_ASSERT(0.0 <= Dcv);
- UnnecessaryPremium = unnecessary_premium;
- TxRecognizePaymentFor7702A(unnecessary_premium, true);
- TxAcceptPayment(unnecessary_premium);
+// moved up
+// UnnecessaryPremium = unnecessary_premium; // or round here?
+ TxRecognizePaymentFor7702A(UnnecessaryPremium, true);
+ if(UnnecessaryPremium < 0.0)
+ warning()
+ << UnnecessaryPremium << " UnnecessaryPremium\n"
+ << LMI_FLUSH
+ ;
+ TxAcceptPayment(UnnecessaryPremium);
TxTakeLoan();
TxLoanRepay();
@@ -261,7 +293,7 @@ void AccountValue::DoMonthCR()
//============================================================================
// Apportion all payments among accounts.
-void AccountValue::process_payment(double payment)
+void AccountValue::process_payment(currency payment)
{
// Apply ee and er net payments according to database rules.
// Net payments were already aggregated, then split between
@@ -273,21 +305,21 @@ void AccountValue::process_payment(double payment)
LMI_ASSERT(0.0 <= EeGrossPmts[Month]);
LMI_ASSERT(0.0 <= ErGrossPmts[Month]);
- double net_pmt = payment;
+ currency net_pmt = payment;
- double gross_1035 = 0.0;
+ currency gross_1035(0.0);
if(0 == Year && 0 == Month)
{
gross_1035 = External1035Amount + Internal1035Amount;
}
- double gross_non_1035_pmts = GrossPmts[Month] - gross_1035;
+ currency gross_non_1035_pmts = GrossPmts[Month] - gross_1035;
double er_ratio = 0.0;
if(0.0 != gross_non_1035_pmts)
{
er_ratio = ErGrossPmts[Month] / gross_non_1035_pmts;
}
- double er_net_pmt = er_ratio * net_pmt;
- double ee_net_pmt = net_pmt - er_net_pmt;
+ currency er_net_pmt = currency(round_gross_premium()(er_ratio * net_pmt));
+ currency ee_net_pmt = net_pmt - er_net_pmt;
switch(ee_premium_allocation_method)
{
@@ -323,7 +355,7 @@ void AccountValue::process_payment(double payment)
//============================================================================
// Prorate increments to account value between separate- and general-account
// portions of unloaned account value according to input allocations.
-void AccountValue::IncrementAVProportionally(double increment)
+void AccountValue::IncrementAVProportionally(currency increment)
{
AVGenAcct += increment * GenAcctPaymentAllocation;
AVSepAcct += increment * SepAcctPaymentAllocation;
@@ -332,7 +364,7 @@ void AccountValue::IncrementAVProportionally(double
increment)
//============================================================================
// Apply increments to account value to the preferred account.
void AccountValue::IncrementAVPreferentially
- (double increment
+ (currency increment
,oenum_increment_account_preference preferred_account
)
{
@@ -354,7 +386,7 @@ void AccountValue::IncrementAVPreferentially
/// Apportion all charges to be deducted from account value among
/// accounts.
-void AccountValue::process_deduction(double decrement)
+void AccountValue::process_deduction(currency decrement)
{
switch(deduction_method)
{
@@ -373,7 +405,7 @@ void AccountValue::process_deduction(double decrement)
/// Apportion all distributions from account value among accounts.
-void AccountValue::process_distribution(double decrement)
+void AccountValue::process_distribution(currency decrement)
{
switch(distribution_method)
{
@@ -402,7 +434,7 @@ void AccountValue::process_distribution(double decrement)
/// Otherwise, unloaned account value might have a minuscule negative
/// value due to catastrophic cancellation, improperly causing lapse.
-void AccountValue::DecrementAVProportionally(double decrement)
+void AccountValue::DecrementAVProportionally(currency decrement)
{
if(materially_equal(decrement, AVGenAcct + AVSepAcct))
{
@@ -413,8 +445,8 @@ void AccountValue::DecrementAVProportionally(double
decrement)
double general_account_proportion = 0.0;
double separate_account_proportion = 0.0;
- double general_account_nonnegative_assets = std::max(0.0, AVGenAcct);
- double separate_account_nonnegative_assets = std::max(0.0, AVSepAcct);
+ double general_account_nonnegative_assets = std::max<double>(0.0,
AVGenAcct);
+ double separate_account_nonnegative_assets = std::max<double>(0.0,
AVSepAcct);
if
( 0.0 == general_account_nonnegative_assets
&& 0.0 == separate_account_nonnegative_assets
@@ -458,7 +490,7 @@ void AccountValue::DecrementAVProportionally(double
decrement)
/// value due to catastrophic cancellation, improperly causing lapse.
void AccountValue::DecrementAVProgressively
- (double decrement
+ (currency decrement
,oenum_increment_account_preference preferred_account
)
{
@@ -517,7 +549,9 @@ void AccountValue::TxExch1035()
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, GrossPmts[Month]);
+ double z = doubleize(GrossPmts[Month]); // yicky workaround
+ Irc7702_->ProcessGptPmt(Year, z);
+ GrossPmts[Month] = z;
}
// Limit external 1035 first, then internal, as necessary to avoid
// exceeding the guideline limit. This is what the customer would
@@ -569,7 +603,7 @@ void AccountValue::TxExch1035()
if(HoneymoonActive)
{
- HoneymoonValue += std::max(0.0, GrossPmts[Month]);
+ HoneymoonValue += std::max<double>(0.0, GrossPmts[Month]);
}
CumPmts += GrossPmts[Month];
@@ -583,7 +617,7 @@ void AccountValue::TxExch1035()
// Immediately after a 1035 exchange, DCV should be
// the 1035 amount reduced by any premium-based loads,
// but only for the current rate basis.
- LMI_ASSERT(materially_equal(Dcv, NetPmts[Month]));
+ LMI_ASSERT(materially_equal(Dcv, doubleize(NetPmts[Month])));
// The initial seven-pay premium shown on the illustration
// must be its value immediately after any 1035 exchange,
@@ -603,7 +637,7 @@ void AccountValue::TxExch1035()
}
//============================================================================
-double AccountValue::CashValueFor7702() const
+currency AccountValue::CashValueFor7702() const
{
return std::max
(HoneymoonValue
@@ -634,12 +668,12 @@ double AccountValue::ActualMonthlyRate(double
monthly_rate) const
//============================================================================
// Rounded interest increment.
-double AccountValue::InterestCredited
- (double principal
- ,double monthly_rate
+currency AccountValue::InterestCredited
+ (currency principal
+ ,double monthly_rate
) const
{
- return round_interest_credit()(principal *
ActualMonthlyRate(monthly_rate));
+ return currency(round_interest_credit()(principal *
ActualMonthlyRate(monthly_rate)));
}
//============================================================================
@@ -667,20 +701,21 @@ int AccountValue::MonthsToNextModalPmtDate() const
/// Argument 'term_rider' indicates whether a term rider is to be
/// taken into account, as that affects the base-policy minimum.
-double AccountValue::minimum_specified_amount(bool issuing_now, bool
term_rider) const
+currency AccountValue::minimum_specified_amount(bool issuing_now, bool
term_rider) const
{
- return
+ return currency
+ (
issuing_now
? (term_rider ? MinIssBaseSpecAmt : MinIssSpecAmt )
: (term_rider ? MinRenlBaseSpecAmt : MinRenlSpecAmt)
- ;
+ );
}
//============================================================================
// All changes to SA must be handled here.
// Proportionately reduce base and term SA if term rider present.
// Make sure ActualSpecAmt is never less than minimum specamt.
-void AccountValue::ChangeSpecAmtBy(double delta)
+void AccountValue::ChangeSpecAmtBy(currency delta)
{
double ProportionAppliedToTerm = 0.0;
// Adjust term here only if it's formally a rider.
@@ -726,7 +761,7 @@ void AccountValue::ChangeSpecAmtBy(double delta)
if(TermRiderActive)
{
TermSpecAmt =
- std::max(TermSpecAmt + ActualSpecAmt, MinRenlSpecAmt)
+ std::max<double>(TermSpecAmt + ActualSpecAmt,
MinRenlSpecAmt)
- ActualSpecAmt
;
}
@@ -772,11 +807,11 @@ void AccountValue::ChangeSpecAmtBy(double delta)
TxSetDeathBft();
}
-void AccountValue::ChangeSupplAmtBy(double delta)
+void AccountValue::ChangeSupplAmtBy(currency delta)
{
TermSpecAmt += delta;
- TermSpecAmt = std::max
+ TermSpecAmt = std::max<double>
(TermSpecAmt
,0.0 // No minimum other than zero is defined.
);
@@ -869,11 +904,11 @@ void AccountValue::TxOptionChange()
{
if(mce_option2 == old_option)
{
- ChangeSpecAmtBy(std::max(0.0, TotalAccountValue()));
+ ChangeSpecAmtBy(std::max(currency(0.0),
TotalAccountValue()));
}
else if(mce_rop == old_option)
{
- ChangeSpecAmtBy(std::max(0.0, CumPmts));
+ ChangeSpecAmtBy(std::max(currency(0.0), CumPmts));
}
else if(mce_mdb == old_option)
{
@@ -900,7 +935,7 @@ void AccountValue::TxOptionChange()
case mce_option2:
if(OptChgCanDecrSA)
{
- ChangeSpecAmtBy(-std::max(0.0, TotalAccountValue()));
+ ChangeSpecAmtBy(currency(0.0) - std::max(currency(0.0),
TotalAccountValue()));
}
else
{
@@ -910,7 +945,7 @@ void AccountValue::TxOptionChange()
case mce_rop:
if(OptChgCanDecrSA)
{
- ChangeSpecAmtBy(-std::max(0.0, CumPmts));
+ ChangeSpecAmtBy(currency(0.0) - std::max(currency(0.0),
CumPmts));
}
else
{
@@ -920,7 +955,7 @@ void AccountValue::TxOptionChange()
case mce_mdb:
{
// Change spec amt by its additive inverse, making it 0.
- ChangeSpecAmtBy(-(ActualSpecAmt + TermSpecAmt));
+ ChangeSpecAmtBy(currency(0.0) - (ActualSpecAmt + TermSpecAmt));
}
break;
}
@@ -956,7 +991,7 @@ void AccountValue::TxSpecAmtChange()
// to be called only when the spec amt changes; calling it with an argument
// of zero is a rather bizarre concept.
- ChangeSpecAmtBy(0.0);
+ ChangeSpecAmtBy(currency(0.0));
return;
}
@@ -1199,7 +1234,9 @@ void AccountValue::TxAscertainDesiredPayment()
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, Dumpin);
+ double dumpin = doubleize(Dumpin); // yicky workaround
+ Irc7702_->ProcessGptPmt(Year, dumpin);
+ Dumpin = currency(dumpin);
}
EeGrossPmts[Month] += Dumpin;
GrossPmts [Month] += Dumpin;
@@ -1229,8 +1266,8 @@ void AccountValue::TxLimitPayment(double a_maxpmt)
{
gross_1035 = External1035Amount + Internal1035Amount;
}
- double gross_pmt_without_1035 = GrossPmts[Month] - gross_1035;
- gross_pmt_without_1035 = std::min(gross_pmt_without_1035, a_maxpmt);
+ currency gross_pmt_without_1035 = GrossPmts[Month] - gross_1035;
+ gross_pmt_without_1035 =
currency(std::min(doubleize(gross_pmt_without_1035), a_maxpmt));
// TODO ?? For now at least, reduce employee premium first.
progressively_limit
(EeGrossPmts[Month]
@@ -1268,8 +1305,8 @@ void AccountValue::TxLimitPayment(double a_maxpmt)
//============================================================================
void AccountValue::TxRecognizePaymentFor7702A
- (double a_pmt
- ,bool a_this_payment_is_unnecessary
+ (currency const a_pmt
+ ,bool a_this_payment_is_unnecessary
)
{
if(0.0 == a_pmt)
@@ -1286,7 +1323,7 @@ void AccountValue::TxRecognizePaymentFor7702A
{
kludge_account_value = Dcv;
}
- kludge_account_value = std::max
+ kludge_account_value = std::max<double>
(HoneymoonValue
, kludge_account_value
+ GetRefundableSalesLoad()
@@ -1310,13 +1347,18 @@ void AccountValue::TxRecognizePaymentFor7702A
}
//============================================================================
-void AccountValue::TxAcceptPayment(double a_pmt)
+void AccountValue::TxAcceptPayment(currency const a_pmt)
{
if(0.0 == a_pmt)
{
return;
}
+ if(a_pmt < 0.0)
+ warning()
+ << a_pmt << " a_pmt\n"
+ << LMI_FLUSH
+ ;
LMI_ASSERT(0.0 <= a_pmt);
// Internal 1035 exchanges may be exempt from premium tax; they're
// handled elsewhere, so here the exempt amount is always zero.
@@ -1331,14 +1373,14 @@ void AccountValue::TxAcceptPayment(double a_pmt)
// automatic increases are not adjustable events, and the SA increase
// due to a payment in this case might be considered automatic by IRS.
- process_payment(net_pmt);
+ process_payment(currency(round_net_premium()(net_pmt))); // round more
carefully above
Dcv += std::max(0.0, net_pmt);
LMI_ASSERT(0.0 <= Dcv);
if(HoneymoonActive)
{
- HoneymoonValue += std::max(0.0, a_pmt);
+ HoneymoonValue += std::max<double>(0.0, a_pmt);
}
CumPmts += a_pmt;
@@ -1372,9 +1414,9 @@ void AccountValue::TxAcceptPayment(double a_pmt)
/// calculation doesn't require too many adjustments, in particular
/// when tiered premium tax is passed through as a load.
-double AccountValue::GetPremLoad
- (double a_pmt
- ,double a_portion_exempt_from_premium_tax
+currency AccountValue::GetPremLoad
+ (currency a_pmt
+ ,double a_portion_exempt_from_premium_tax
)
{
double excess_portion;
@@ -1435,13 +1477,13 @@ double AccountValue::GetPremLoad
|| materially_equal(total_load, sum_of_separate_loads)
);
- return round_net_premium()(sum_of_separate_loads);
+ return currency(round_net_premium()(sum_of_separate_loads));
}
//============================================================================
-double AccountValue::GetRefundableSalesLoad() const
+currency AccountValue::GetRefundableSalesLoad() const
{
- return CumulativeSalesLoad * YearsSalesLoadRefundRate;
+ return currency(CumulativeSalesLoad * YearsSalesLoadRefundRate);
#if 0
// CURRENCY !! Assertions such as these are desirable, but adding
// them now would cause regression artifacts.
@@ -1471,11 +1513,11 @@ void AccountValue::TxLoanRepay()
// TODO ?? This idiom seems too cute. And it can return -0.0 .
// Maximum repayment is total debt.
- ActualLoan = -std::min(-RequestedLoan, RegLnBal + PrfLnBal);
+ ActualLoan = -std::min<double>(-RequestedLoan, RegLnBal + PrfLnBal);
process_distribution(ActualLoan);
- LMI_ASSERT(0.0 == progressively_reduce(AVRegLn , AVPrfLn , -ActualLoan));
- LMI_ASSERT(0.0 == progressively_reduce(RegLnBal, PrfLnBal, -ActualLoan));
+ LMI_ASSERT(0.0 == progressively_reduce(AVRegLn , AVPrfLn , currency(0.0) -
ActualLoan));
+ LMI_ASSERT(0.0 == progressively_reduce(RegLnBal, PrfLnBal, currency(0.0) -
ActualLoan));
// This seems wrong. If we're changing something that's invariant among
// bases, why do we change it for each basis?
@@ -1508,7 +1550,7 @@ void AccountValue::TxSetBOMAV()
)
: yare_input_.InforceSpecAmtLoadBase
;
- SpecAmtLoadBase = std::min(SpecAmtLoadBase, SpecAmtLoadLimit);
+ SpecAmtLoadBase = std::min<double>(SpecAmtLoadBase, SpecAmtLoadLimit);
}
// These assignments must happen every month.
@@ -1573,7 +1615,7 @@ void AccountValue::TxSetDeathBft()
case mce_option2:
{
// Negative AV doesn't decrease death benefit.
- DBIgnoringCorr = ActualSpecAmt + std::max(0.0,
TotalAccountValue());
+ DBIgnoringCorr = ActualSpecAmt + std::max<double>(0.0,
TotalAccountValue());
DB7702A = ActualSpecAmt;
}
break;
@@ -1582,8 +1624,8 @@ void AccountValue::TxSetDeathBft()
// SA + sum of premiums less withdrawals, but not < SA;
// i.e., ignore 'CumPmts' if it is less than zero, as it
// easily can be, e.g., if WDs are not limited to basis.
- DBIgnoringCorr = ActualSpecAmt + std::max(0.0, CumPmts);
- DB7702A = ActualSpecAmt + std::max(0.0, CumPmts);
+ DBIgnoringCorr = ActualSpecAmt + std::max<double>(0.0, CumPmts);
+ DB7702A = ActualSpecAmt + std::max<double>(0.0, CumPmts);
}
break;
case mce_mdb:
@@ -1602,19 +1644,19 @@ void AccountValue::TxSetDeathBft()
// surrender charge must be subtracted, increasing the account value.
double cash_value_for_corridor =
TotalAccountValue()
- - std::min(0.0, SurrChg())
+ - std::min<double>(0.0, SurrChg())
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if it existed.
;
- cash_value_for_corridor = std::max
+ cash_value_for_corridor = std::max<double>
(cash_value_for_corridor
,HoneymoonValue
);
- DBReflectingCorr = std::max
+ DBReflectingCorr = std::max<double>
(DBIgnoringCorr
- ,YearsCorridorFactor * std::max(0.0, cash_value_for_corridor)
+ ,YearsCorridorFactor * std::max<double>(0.0, cash_value_for_corridor)
);
DBReflectingCorr = round_death_benefit()(DBReflectingCorr);
LMI_ASSERT(0.0 <= DBReflectingCorr);
@@ -1623,12 +1665,12 @@ void AccountValue::TxSetDeathBft()
// TAXATION !! Use DB_Irc7702BftIsSpecAmt
DB7702A = DBReflectingCorr + TermDB;
- DcvDeathBft = std::max
+ DcvDeathBft = std::max<double>
(DBIgnoringCorr
, (
YearsCorridorFactor
* ( Dcv
- - std::min(0.0, SurrChg())
+ - std::min<double>(0.0, SurrChg())
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if
it existed.
)
@@ -1663,7 +1705,7 @@ void AccountValue::TxSetTermAmt()
return;
}
- TermDB = std::max(0.0, TermSpecAmt + DBIgnoringCorr - DBReflectingCorr);
+ TermDB = std::max<double>(0.0, TermSpecAmt + DBIgnoringCorr -
DBReflectingCorr);
TermDB = round_death_benefit()(TermDB);
}
@@ -1719,17 +1761,29 @@ void AccountValue::TxSetCoiCharge()
// the account value by deducting a negative mortality charge.
NAAR = material_difference
(DBReflectingCorr * DBDiscountRate[Year]
- ,std::max(0.0, TotalAccountValue())
+ ,std::max<double>(0.0, TotalAccountValue())
);
NAAR = std::max(0.0, round_naar()(NAAR));
+#if 0
+ warning()
+ << std::setprecision(21)
+ << NAAR << " NAAR\n"
+ << DBReflectingCorr << " DBReflectingCorr\n"
+ << DBDiscountRate[Year] << " DBDiscountRate[Year]\n"
+ << DBReflectingCorr * DBDiscountRate[Year] << " DBReflectingCorr *
DBDiscountRate[Year]\n"
+ << TotalAccountValue() << " TotalAccountValue()\n"
+ << std::max<double>(0.0, TotalAccountValue()) << "
std::max<double>(0.0, TotalAccountValue())\n"
+ << LMI_FLUSH
+ ;
+#endif // 0
// TODO ?? This doesn't work. We need to reconsider the basic transactions.
// double naar_forceout = std::max(0.0, NAAR - MaxNAAR);
// process_distribution(naar_forceout);
// TAXATION !! Should this be handled at the same time as GPT forceouts?
DcvNaar = material_difference
- (std::max(DcvDeathBft, DBIgnoringCorr) * DBDiscountRate[Year]
+ (std::max<double>(DcvDeathBft, DBIgnoringCorr) * DBDiscountRate[Year]
,std::max(0.0, Dcv)
);
// DCV need not be rounded.
@@ -1777,7 +1831,7 @@ void AccountValue::TxSetRiderDed()
AdbCharge = 0.0;
if(yare_input_.AccidentalDeathBenefit)
{
- AdbCharge = YearsAdbRate * std::min(ActualSpecAmt, AdbLimit);
+ AdbCharge = YearsAdbRate * std::min<double>(ActualSpecAmt, AdbLimit);
}
SpouseRiderCharge = 0.0;
@@ -1811,7 +1865,7 @@ void AccountValue::TxSetRiderDed()
{
case oe_waiver_times_specamt:
{
- WpCharge = YearsWpRate * std::min(ActualSpecAmt, WpLimit);
+ WpCharge = YearsWpRate * std::min<double>(ActualSpecAmt,
WpLimit);
DcvWpCharge = WpCharge;
}
break;
@@ -1985,7 +2039,7 @@ void AccountValue::TxTakeSepAcctLoad()
//============================================================================
// When the M&E charge depends on each month's case total assets, the
// interest rate is no longer an annual invariant. Set it monthly here.
-void AccountValue::ApplyDynamicMandE(double assets)
+void AccountValue::ApplyDynamicMandE(currency assets)
{
if(!MandEIsDynamic)
{
@@ -2214,26 +2268,26 @@ void AccountValue::TxLoanInt()
// actual future deductions--particularly in the month of issue, when
// it is zero.
//
-double AccountValue::anticipated_deduction
+currency AccountValue::anticipated_deduction
(mcenum_anticipated_deduction method)
{
switch(method)
{
case mce_twelve_times_last:
{
- return 12.0 * MlyDed;
+ return currency(MlyDed * double(12));
}
case mce_eighteen_times_last:
{
- return 18.0 * MlyDed;
+ return currency(MlyDed * double(18));
}
case mce_to_next_anniversary:
{
- return MlyDed * (13 - Month);
+ return currency(MlyDed * double(13 - Month));
}
case mce_to_next_modal_pmt_date:
{
- return MlyDed * (1 + MonthsToNextModalPmtDate());
+ return currency(MlyDed * double(1 + MonthsToNextModalPmtDate()));
}
}
throw "Unreachable--silences a compiler diagnostic.";
@@ -2261,13 +2315,13 @@ void AccountValue::SetMaxWD()
+ (AVRegLn + AVPrfLn)
- (RegLnBal + PrfLnBal)
- anticipated_deduction(MaxWDDed_)
- - std::max(0.0, SurrChg())
+ - std::max<double>(0.0, SurrChg())
;
if(MaxWD < MinWD)
{
MaxWD = 0.0;
}
- MaxWD = std::max(0.0, MaxWD);
+ MaxWD = std::max<double>(0.0, MaxWD);
}
/// Take a withdrawal.
@@ -2307,7 +2361,7 @@ void AccountValue::TxTakeWD()
if(Solving)
{
- withdrawal_ullage_[Year] = std::max(0.0, RequestedWD - MaxWD);
+ withdrawal_ullage_[Year] = std::max<double>(0.0, RequestedWD - MaxWD);
}
if(Solving || mce_run_gen_curr_sep_full == RunBasis_)
@@ -2438,7 +2492,7 @@ void AccountValue::TxTakeWD()
return;
}
- GrossWD = NetWD + std::min(WDFee, NetWD * WDFeeRate);
+ GrossWD = NetWD + std::min<double>(WDFee, NetWD * WDFeeRate);
// Free partial surrenders: for instance, the first 20% of account
// value might be withdrawn each policy year free of surrender
@@ -2456,7 +2510,7 @@ void AccountValue::TxTakeWD()
LMI_ASSERT(AVPrfLn == PrfLnBal);
LMI_ASSERT(av == AVGenAcct + AVSepAcct);
double free_wd = FreeWDProportion[Year] * av;
- non_free_wd = std::max(0.0, GrossWD - free_wd);
+ non_free_wd = std::max<double>(0.0, GrossWD - free_wd);
}
double partial_surrchg = non_free_wd * surrchg_proportion;
GrossWD += partial_surrchg;
@@ -2481,7 +2535,7 @@ void AccountValue::TxTakeWD()
// Do you really want 'face' here rather than specamt? --Yes
if(WdDecrSpecAmtDboLvl)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0.0) - GrossWD);
// Min AV after WD not directly implemented.
// If WD causes AV < min AV, do we:
// reduce the WD?
@@ -2501,7 +2555,7 @@ void AccountValue::TxTakeWD()
{
if(WdDecrSpecAmtDboInc)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0.0) - GrossWD);
}
else
{
@@ -2513,7 +2567,7 @@ void AccountValue::TxTakeWD()
{
if(WdDecrSpecAmtDboRop)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0.0) - GrossWD);
}
else
{
@@ -2559,11 +2613,11 @@ void AccountValue::TxTakeWD()
// Calculate maximum permissible total loan (not increment).
void AccountValue::SetMaxLoan()
{
- MaxLoan =
+ double max_loan =
(AVGenAcct + AVSepAcct) * MaxLoanAVMult
+ (AVRegLn + AVPrfLn)
- anticipated_deduction(MaxLoanDed_)
- - std::max(0.0, SurrChg())
+ - std::max<double>(0.0, SurrChg())
;
// Illustrations generally permit loans only on anniversary.
@@ -2598,7 +2652,7 @@ void AccountValue::SetMaxLoan()
// the end of the policy year--but does not guarantee that, e.g.
// because the specified amount may change between anniversaries,
// even on illustrations.
- MaxLoan -=
+ max_loan -=
RegLnBal * reg_loan_factor
+ PrfLnBal * prf_loan_factor
;
@@ -2608,9 +2662,9 @@ void AccountValue::SetMaxLoan()
// plausible but unasserted assumption that that factor is more
// liberal than the preferred-loan factor?
//
- MaxLoan *= 1.0 - (reg_loan_factor) / (1.0 + reg_loan_factor);
+ max_loan *= 1.0 - (reg_loan_factor) / (1.0 + reg_loan_factor);
- MaxLoan = round_loan()(MaxLoan);
+ MaxLoan = round_loan()(max_loan);
// I do not think we want a MaxLoan < current level of indebtedness.
MaxLoan = std::max((AVRegLn + AVPrfLn), MaxLoan);
@@ -2649,12 +2703,12 @@ void AccountValue::TxTakeLoan()
if(Solving)
{
ActualLoan = RequestedLoan;
- loan_ullage_[Year] = std::max(0.0, RequestedLoan - max_loan_increment);
+ loan_ullage_[Year] = std::max<double>(0.0, RequestedLoan -
max_loan_increment);
}
else
{
- ActualLoan = std::min(max_loan_increment, RequestedLoan);
- ActualLoan = std::max(ActualLoan, 0.0);
+ ActualLoan = std::min<double>(max_loan_increment, RequestedLoan);
+ ActualLoan = std::max<double>(ActualLoan, 0.0);
// TODO ?? Shouldn't this happen in FinalizeMonth()?
InvariantValues().NewCashLoan[Year] = ActualLoan;
}
@@ -2734,9 +2788,9 @@ void AccountValue::TxTestLapse()
;
if(!LapseIgnoresSurrChg)
{
- lapse_test_csv -= std::max(0.0, SurrChg());
+ lapse_test_csv -= std::max<double>(0.0, SurrChg());
}
- lapse_test_csv = std::max(lapse_test_csv, HoneymoonValue);
+ lapse_test_csv = std::max<double>(lapse_test_csv, HoneymoonValue);
// Perform no-lapse test.
if(NoLapseActive && !NoLapseAlwaysActive)
diff --git a/ihs_avsolve.cpp b/ihs_avsolve.cpp
index 15411f4..0fc0325 100644
--- a/ihs_avsolve.cpp
+++ b/ihs_avsolve.cpp
@@ -242,7 +242,7 @@ void AccountValue::SolveSetSpecAmt(double a_CandidateValue)
{
// TODO ?? Does this change the surrchg when specamt changes?
DeathBfts_->set_specamt
- (a_CandidateValue
+ (currency(a_CandidateValue)
,SolveBeginYear_
,SolveEndYear_
);
@@ -251,35 +251,35 @@ void AccountValue::SolveSetSpecAmt(double
a_CandidateValue)
//============================================================================
void AccountValue::SolveSetEePrem(double a_CandidateValue)
{
- Outlay_->set_ee_modal_premiums(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
+ Outlay_->set_ee_modal_premiums(currency(a_CandidateValue),
SolveBeginYear_, SolveEndYear_);
}
//============================================================================
void AccountValue::SolveSetErPrem(double a_CandidateValue)
{
- Outlay_->set_er_modal_premiums(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
+ Outlay_->set_er_modal_premiums(currency(a_CandidateValue),
SolveBeginYear_, SolveEndYear_);
}
//============================================================================
void AccountValue::SolveSetLoan(double a_CandidateValue)
{
- Outlay_->set_new_cash_loans(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
+ Outlay_->set_new_cash_loans(currency(a_CandidateValue), SolveBeginYear_,
SolveEndYear_);
}
//============================================================================
void AccountValue::SolveSetWD(double a_CandidateValue)
{
- Outlay_->set_withdrawals(a_CandidateValue, SolveBeginYear_, SolveEndYear_);
+ Outlay_->set_withdrawals(currency(a_CandidateValue), SolveBeginYear_,
SolveEndYear_);
}
//============================================================================
double AccountValue::SolveGuarPremium()
{
// Store original er premiums for later restoration.
- std::vector<double> stored = Outlay_->er_modal_premiums();
+ std::vector<currency> stored = Outlay_->er_modal_premiums();
// Zero out er premiums and solve for ee premiums only.
Outlay_->set_er_modal_premiums
- (0.0
+ (currency(0.0)
,0
,static_cast<int>(InvariantValues().EndtAge - InvariantValues().Age)
);
diff --git a/ihs_avstrtgy.cpp b/ihs_avstrtgy.cpp
index e748777..1e26a4e 100644
--- a/ihs_avstrtgy.cpp
+++ b/ihs_avstrtgy.cpp
@@ -55,19 +55,19 @@
///
/// No minimum is imposed here; see PerformSpecAmtStrategy().
-double AccountValue::CalculateSpecAmtFromStrategy
+currency AccountValue::CalculateSpecAmtFromStrategy
(int actual_year
,int reference_year
- ,double explicit_value
+ ,currency explicit_value
,mcenum_sa_strategy strategy
) const
{
- double annualized_pmt =
+ currency annualized_pmt(
InvariantValues().EeMode[reference_year].value()
* InvariantValues().EePmt [reference_year]
+ InvariantValues().ErMode[reference_year].value()
* InvariantValues().ErPmt [reference_year]
- ;
+ );
switch(strategy)
{
case mce_sa_input_scalar:
@@ -130,8 +130,8 @@ void AccountValue::PerformSpecAmtStrategy()
for(int j = 0; j < BasicValues::Length; ++j)
{
bool t = yare_input_.TermRider && 0.0 != yare_input_.TermRiderAmount;
- double m = minimum_specified_amount(0 == j, t);
- double explicit_value = DeathBfts_->specamt()[j];
+ currency m = minimum_specified_amount(0 == j, t);
+ currency explicit_value = DeathBfts_->specamt()[j];
mcenum_sa_strategy strategy = yare_input_.SpecifiedAmountStrategy[j];
// Don't override a specamt that's being solved for.
if
@@ -144,7 +144,7 @@ void AccountValue::PerformSpecAmtStrategy()
strategy = mce_sa_input_scalar;
}
double z = CalculateSpecAmtFromStrategy(j, 0, explicit_value,
strategy);
- DeathBfts_->set_specamt(round_specamt()(std::max(m, z)), j, 1 + j);
+ DeathBfts_->set_specamt(currency(round_specamt()(std::max<double>(m,
z))), j, 1 + j); // rounding
if
( j == InforceYear
&& yare_input_.EffectiveDate != yare_input_.InforceAsOfDate
@@ -171,22 +171,22 @@ void AccountValue::PerformSupplAmtStrategy()
{
for(int j = 0; j < BasicValues::Length; ++j)
{
- double m = 0.0; // No minimum other than zero is defined.
- double explicit_value = DeathBfts_->supplamt()[j];
+ currency m = currency(0.0); // No minimum other than zero is defined.
+ currency explicit_value = DeathBfts_->supplamt()[j];
mcenum_sa_strategy strategy =
yare_input_.SupplementalAmountStrategy[j];
- double z = CalculateSpecAmtFromStrategy(j, 0, explicit_value,
strategy);
- DeathBfts_->set_supplamt(round_specamt()(std::max(m, z)), j, 1 + j);
+ currency z = CalculateSpecAmtFromStrategy(j, 0, explicit_value,
strategy);
+ DeathBfts_->set_supplamt(currency(round_specamt()(std::max(m, z))), j,
1 + j); // rounding
}
}
/// Set payment according to selected strategy in a non-solve year.
-double AccountValue::DoPerformPmtStrategy
+currency AccountValue::DoPerformPmtStrategy
(mcenum_solve_type a_SolveForWhichPrem
,mcenum_mode a_CurrentMode
,mcenum_mode a_InitialMode
,double a_TblMult
- ,std::vector<double> const& a_PmtVector
+ ,std::vector<currency> const& a_PmtVector
,std::vector<mcenum_pmt_strategy> const& a_StrategyVector
) const
{
@@ -257,7 +257,7 @@ double AccountValue::DoPerformPmtStrategy
}
else
{
- double sa = ActualSpecAmt + TermSpecAmt;
+ currency sa = ActualSpecAmt + TermSpecAmt;
return GetModalMinPrem(Year, a_CurrentMode, sa);
}
}
@@ -265,38 +265,38 @@ double AccountValue::DoPerformPmtStrategy
case mce_pmt_target:
{
int const target_year = TgtPremFixedAtIssue ? 0 : Year;
- double sa = InvariantValues().SpecAmt[target_year];
+ currency sa = requantize(InvariantValues().SpecAmt[target_year]);
return GetModalTgtPrem(Year, a_CurrentMode, sa);
}
case mce_pmt_mep:
{
- double sa =
- InvariantValues().SpecAmt [0]
- + (TermIsDbFor7702A ? InvariantValues().TermSpecAmt[0] : 0.0)
+ currency sa =
+ requantize(InvariantValues().SpecAmt
[0])
+ + (TermIsDbFor7702A ?
requantize(InvariantValues().TermSpecAmt[0]) : currency(0.0))
;
return GetModalPremMaxNonMec(0, a_InitialMode, sa);
}
case mce_pmt_glp:
{
- double sa =
- InvariantValues().SpecAmt [0]
- + (TermIsDbFor7702 ? InvariantValues().TermSpecAmt[0] : 0.0)
+ currency sa =
+ requantize(InvariantValues().SpecAmt
[0])
+ + (TermIsDbFor7702 ?
requantize(InvariantValues().TermSpecAmt[0]) : currency(0.0))
;
return GetModalPremGLP(0, a_InitialMode, sa, sa);
}
case mce_pmt_gsp:
{
- double sa =
- InvariantValues().SpecAmt [0]
- + (TermIsDbFor7702 ? InvariantValues().TermSpecAmt[0] : 0.0)
+ currency sa =
+ requantize(InvariantValues().SpecAmt
[0])
+ + (TermIsDbFor7702 ?
requantize(InvariantValues().TermSpecAmt[0]) : currency(0.0))
;
return GetModalPremGSP(0, a_InitialMode, sa, sa);
}
case mce_pmt_corridor:
{
- double sa =
- InvariantValues().SpecAmt [0]
- + (TermIsDbFor7702 ? InvariantValues().TermSpecAmt[0] : 0.0)
+ currency sa =
+ requantize(InvariantValues().SpecAmt
[0])
+ + (TermIsDbFor7702 ?
requantize(InvariantValues().TermSpecAmt[0]) : currency(0.0))
;
return GetModalPremCorridor(0, a_InitialMode, sa);
}
@@ -315,28 +315,28 @@ double AccountValue::DoPerformPmtStrategy
/// Set employee payment according to selected strategy.
-double AccountValue::PerformEePmtStrategy() const
+currency AccountValue::PerformEePmtStrategy() const
{
return DoPerformPmtStrategy
(mce_solve_ee_prem
,InvariantValues().EeMode[Year].value()
,InvariantValues().EeMode[0] .value()
,yare_input_.InsuredPremiumTableFactor
- ,InvariantValues().EePmt
+ ,currencyize(InvariantValues().EePmt)
,yare_input_.PaymentStrategy
);
}
/// Set employer payment according to selected strategy.
-double AccountValue::PerformErPmtStrategy() const
+currency AccountValue::PerformErPmtStrategy() const
{
return DoPerformPmtStrategy
(mce_solve_er_prem
,InvariantValues().ErMode[Year].value()
,InvariantValues().ErMode[0] .value()
,yare_input_.CorporationPremiumTableFactor
- ,InvariantValues().ErPmt
+ ,currencyize(InvariantValues().ErPmt)
,yare_input_.CorporationPaymentStrategy
);
}
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index cec8a0a..0d9a853 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -596,7 +596,7 @@ void BasicValues::Init7702A()
/// Public function used for GPT specamt calculation.
-double BasicValues::GetAnnualTgtPrem(int a_year, double a_specamt) const
+currency BasicValues::GetAnnualTgtPrem(int a_year, currency a_specamt) const
{
return GetModalTgtPrem(a_year, mce_annual, a_specamt);
}
@@ -843,10 +843,10 @@ void BasicValues::SetMaxSurvivalDur()
/// value would otherwise be negative), or
/// - keeps account value nonnegative (preventing lapse directly).
-double BasicValues::GetModalMinPrem
+currency BasicValues::GetModalMinPrem
(int a_year
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
switch(MinPremType)
@@ -863,10 +863,10 @@ double BasicValues::GetModalMinPrem
/// Ascertain modal payment for a target-premium strategy.
-double BasicValues::GetModalTgtPrem
+currency BasicValues::GetModalTgtPrem
(int a_year
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
int const target_year = TgtPremFixedAtIssue ? 0 : a_year;
@@ -889,15 +889,16 @@ double BasicValues::GetModalTgtPrem
/// and specified amount. Thus, arguments should represent initial
/// premium and mode.
-double BasicValues::GetModalPremMaxNonMec
+currency BasicValues::GetModalPremMaxNonMec
(int // a_year // Unused.
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
// TAXATION !! No table available if 7PP calculated from first principles.
double temp = MortalityRates_->SevenPayRates()[0];
- return round_max_premium()(ldbl_eps_plus_one_times(temp * a_specamt /
a_mode));
+ double z = round_max_premium()(ldbl_eps_plus_one_times(temp * a_specamt /
a_mode));
+ return currency(z);
}
/// Calculate premium using a minimum-premium ratio.
@@ -907,19 +908,20 @@ double BasicValues::GetModalPremMaxNonMec
/// the initial specified amount may also be fixed at issue, but that
/// choice is left to the caller.
-double BasicValues::GetModalPremMinFromTable
+currency BasicValues::GetModalPremMinFromTable
(int // a_year // Unused.
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
- return round_max_premium()
+ double z = round_max_premium()
(ldbl_eps_plus_one_times
(
- a_specamt * MortalityRates_->MinimumPremiumRates()[0]
+ doubleize(a_specamt) *
MortalityRates_->MinimumPremiumRates()[0]
/ a_mode
)
);
+ return currency(z);
}
/// Calculate premium using a target-premium ratio.
@@ -945,13 +947,13 @@ double BasicValues::GetModalPremMinFromTable
/// asserted to be zero--upstream, so that it'll signal an error even
/// if a target strategy isn't used.
-double BasicValues::GetModalPremTgtFromTable
+currency BasicValues::GetModalPremTgtFromTable
(int // a_year // Unused.
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
- return round_max_premium()
+ double z = round_max_premium()
(ldbl_eps_plus_one_times
(
( TgtPremMonthlyPolFee * 12
@@ -960,24 +962,26 @@ double BasicValues::GetModalPremTgtFromTable
/ a_mode
)
);
+ return currency(z);
}
/// Calculate premium using a tabular proxy for group insurance.
-double BasicValues::GetModalPremProxyTable
+currency BasicValues::GetModalPremProxyTable
(int a_year
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
,double a_table_multiplier
) const
{
- return round_gross_premium()
+ double z = round_gross_premium()
(
a_specamt
* MortalityRates_->GroupProxyRates()[a_year]
* a_table_multiplier
/ a_mode
);
+ return currency(z);
}
/// Calculate premium using a corridor ratio.
@@ -986,22 +990,23 @@ double BasicValues::GetModalPremProxyTable
/// strategy makes sense only at issue. Thus, arguments should
/// represent initial specified amount and mode.
-double BasicValues::GetModalPremCorridor
+currency BasicValues::GetModalPremCorridor
(int // a_year // Unused.
,mcenum_mode a_mode
- ,double a_specamt
+ ,currency a_specamt
) const
{
double temp = GetCorridorFactor()[0];
- return round_max_premium()(ldbl_eps_plus_one_times((a_specamt / temp) /
a_mode));
+ double z = round_max_premium()(ldbl_eps_plus_one_times((a_specamt / temp)
/ a_mode));
+ return currency(z);
}
//============================================================================
-double BasicValues::GetModalPremGLP
+currency BasicValues::GetModalPremGLP
(int a_duration
,mcenum_mode a_mode
- ,double a_bft_amt
- ,double a_specamt
+ ,currency a_bft_amt
+ ,currency a_specamt
) const
{
// TAXATION !! Use GetAnnualTgtPrem() to get target here if needed
@@ -1019,15 +1024,16 @@ double BasicValues::GetModalPremGLP
// term rider, dumpin
z /= a_mode;
- return round_max_premium()(ldbl_eps_plus_one_times(z));
+ z = round_max_premium()(ldbl_eps_plus_one_times(z));
+ return currency(z);
}
//============================================================================
-double BasicValues::GetModalPremGSP
+currency BasicValues::GetModalPremGSP
(int a_duration
,mcenum_mode a_mode
- ,double a_bft_amt
- ,double a_specamt
+ ,currency a_bft_amt
+ ,currency a_specamt
) const
{
double z = Irc7702_->CalculateGSP
@@ -1042,7 +1048,8 @@ double BasicValues::GetModalPremGSP
// term rider, dumpin
z /= a_mode;
- return round_max_premium()(ldbl_eps_plus_one_times(z));
+ z = round_max_premium()(ldbl_eps_plus_one_times(z));
+ return currency(z);
}
/// Calculate a monthly-deduction discount factor on the fly.
@@ -1116,8 +1123,8 @@ double BasicValues::mly_ded_discount_factor(int year,
mcenum_mode mode) const
/// is desired.
std::pair<double,double> BasicValues::approx_mly_ded
- (int year
- ,double specamt
+ (int year
+ ,currency specamt
) const
{
double mly_ded = specamt * DBDiscountRate[year];
@@ -1126,7 +1133,7 @@ std::pair<double,double> BasicValues::approx_mly_ded
if(yare_input_.AccidentalDeathBenefit)
{
double const r = MortalityRates_->AdbRates()[year];
- mly_ded += r * std::min(specamt, AdbLimit);
+ mly_ded += r * std::min<double>(specamt, AdbLimit);
}
if(yare_input_.SpouseRider)
@@ -1144,7 +1151,7 @@ std::pair<double,double> BasicValues::approx_mly_ded
if(true) // Written thus for parallelism and to keep 'r' local.
{
double const r = Loads_->specified_amount_load(mce_gen_curr)[year];
- mly_ded += r * std::min(specamt, SpecAmtLoadLimit);
+ mly_ded += r * std::min<double>(specamt, SpecAmtLoadLimit);
}
mly_ded += Loads_->monthly_policy_fee(mce_gen_curr)[year];
@@ -1158,7 +1165,7 @@ std::pair<double,double> BasicValues::approx_mly_ded
{
case oe_waiver_times_specamt:
{
- mly_ded += r * std::min(specamt, WpLimit);
+ mly_ded += r * std::min<double>(specamt, WpLimit);
}
break;
case oe_waiver_times_deductions:
@@ -1171,6 +1178,9 @@ std::pair<double,double> BasicValues::approx_mly_ded
}
double const load =
Loads_->target_premium_load_maximum_premium_tax()[year];
+ // Don't round!
+// mly_ded /= round_minutiae()(1.0 - load);
+// ann_ded /= round_minutiae()(1.0 - load);
mly_ded /= 1.0 - load;
ann_ded /= 1.0 - load;
@@ -1194,9 +1204,9 @@ std::pair<double,double> BasicValues::approx_mly_ded
/// between ee and er would not be wanted.
std::pair<double,double> BasicValues::approx_mly_ded_ex
- (int year
- ,double specamt
- ,double termamt
+ (int year
+ ,currency specamt
+ ,currency termamt
) const
{
if(0.0 != Loads_->annual_policy_fee(mce_gen_curr)[year])
@@ -1214,7 +1224,7 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
if(yare_input_.AccidentalDeathBenefit)
{
double const r = MortalityRates_->AdbRates()[year];
- er_ded += r * std::min(specamt, AdbLimit);
+ er_ded += r * std::min<double>(specamt, AdbLimit);
}
// Paid by ee.
@@ -1235,7 +1245,7 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
if(true) // Written thus for parallelism and to keep 'r' local.
{
double const r = Loads_->specified_amount_load(mce_gen_curr)[year];
- er_ded += r * std::min(specamt, SpecAmtLoadLimit);
+ er_ded += r * std::min<double>(specamt, SpecAmtLoadLimit);
}
// Paid by er.
@@ -1249,7 +1259,7 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
case oe_waiver_times_specamt:
{
// Paid by er. (In this case, WP excludes term.)
- er_ded += r * std::min(specamt, WpLimit);
+ er_ded += r * std::min<double>(specamt, WpLimit);
}
break;
case oe_waiver_times_deductions:
@@ -1263,6 +1273,9 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
}
double const load =
Loads_->target_premium_load_maximum_premium_tax()[year];
+ // Don't round!
+// ee_ded /= round_minutiae()(1.0 - load);
+// er_ded /= round_minutiae()(1.0 - load);
ee_ded /= 1.0 - load;
er_ded /= 1.0 - load;
@@ -1271,10 +1284,10 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
/// Determine an approximate "pay as you go" modal premium.
-double BasicValues::GetModalPremMlyDed
+currency BasicValues::GetModalPremMlyDed
(int year
,mcenum_mode mode
- ,double specamt
+ ,currency specamt
) const
{
auto const deductions = approx_mly_ded(year, specamt);
@@ -1282,16 +1295,16 @@ double BasicValues::GetModalPremMlyDed
double const mly_ded = deductions.second;
double const v12 = mly_ded_discount_factor(year, mode);
double const annuity = (1.0 - std::pow(v12, 12.0 / mode)) / (1.0 - v12);
- return round_min_premium()(ann_ded + mly_ded * annuity);
+ return currency(round_min_premium()(ann_ded + mly_ded * annuity));
}
/// Determine approximate ee and er "pay as you go" modal premiums.
-std::pair<double,double> BasicValues::GetModalPremMlyDedEx
+std::pair<currency,currency> BasicValues::GetModalPremMlyDedEx
(int year
,mcenum_mode mode
- ,double specamt
- ,double termamt
+ ,currency specamt
+ ,currency termamt
) const
{
auto const deductions = approx_mly_ded_ex(year, specamt, termamt);
@@ -1300,8 +1313,8 @@ std::pair<double,double> BasicValues::GetModalPremMlyDedEx
double const v12 = DBDiscountRate[year];
double const annuity = (1.0 - std::pow(v12, 12.0 / mode)) / (1.0 - v12);
return std::make_pair
- (round_min_premium()(ee_ded * annuity)
- ,round_min_premium()(er_ded * annuity)
+ (currency(round_min_premium()(ee_ded * annuity))
+ ,currency(round_min_premium()(er_ded * annuity))
);
}
@@ -1322,10 +1335,10 @@ std::pair<double,double>
BasicValues::GetModalPremMlyDedEx
/// are extraordinary, and occur only on products for which list bills
/// would not be wanted.
-double BasicValues::GetListBillPremMlyDed
+currency BasicValues::GetListBillPremMlyDed
(int year
,mcenum_mode mode
- ,double specamt
+ ,currency specamt
) const
{
double const p0 = approx_mly_ded(year, specamt).second;
@@ -1343,14 +1356,14 @@ double BasicValues::GetListBillPremMlyDed
,yare_input_.ListBillDate
,mly_ded_discount_factor(year, mode)
);
- return round_min_premium()(z);
+ return currency(round_min_premium()(z));
}
-std::pair<double,double> BasicValues::GetListBillPremMlyDedEx
+std::pair<currency,currency> BasicValues::GetListBillPremMlyDedEx
(int year
,mcenum_mode mode
- ,double specamt
- ,double termamt
+ ,currency specamt
+ ,currency termamt
) const
{
auto const p0 = approx_mly_ded_ex(year, specamt, termamt);
@@ -1377,12 +1390,12 @@ std::pair<double,double>
BasicValues::GetListBillPremMlyDedEx
,DBDiscountRate[year]
);
return std::make_pair
- (round_min_premium()(ee_prem)
- ,round_min_premium()(er_prem)
+ (currency(round_min_premium()(ee_prem))
+ ,currency(round_min_premium()(er_prem))
);
}
-double BasicValues::GetModalSpecAmtMax(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtMax(currency annualized_pmt) const
{
switch(MinPremType)
{
@@ -1391,10 +1404,12 @@ double BasicValues::GetModalSpecAmtMax(double
annualized_pmt) const
case oe_modal_nonmec:
return GetModalSpecAmtMinNonMec(annualized_pmt);
case oe_modal_table:
- return round_min_specamt()
- (
- annualized_pmt
- / MortalityRates_->MinimumPremiumRates()[0]
+ return currency
+ (round_min_specamt()
+ (
+ annualized_pmt
+ / MortalityRates_->MinimumPremiumRates()[0]
+ )
);
}
throw "Unreachable--silences a compiler diagnostic.";
@@ -1406,7 +1421,7 @@ double BasicValues::GetModalSpecAmtMax(double
annualized_pmt) const
/// duration. It's taken to include 'TgtPremMonthlyPolFee', to make
/// this function the inverse of GetModalPremTgtFromTable(), q.v.
-double BasicValues::GetModalSpecAmtTgt(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtTgt(currency annualized_pmt) const
{
switch(TgtPremType)
{
@@ -1415,10 +1430,12 @@ double BasicValues::GetModalSpecAmtTgt(double
annualized_pmt) const
case oe_modal_nonmec:
return GetModalSpecAmtMinNonMec(annualized_pmt);
case oe_modal_table:
- return round_min_specamt()
- (
- (annualized_pmt - TgtPremMonthlyPolFee * 12)
- / MortalityRates_->TargetPremiumRates()[0]
+ return currency
+ (round_min_specamt()
+ (
+ (annualized_pmt - TgtPremMonthlyPolFee * 12)
+ / MortalityRates_->TargetPremiumRates()[0]
+ )
);
}
throw "Unreachable--silences a compiler diagnostic.";
@@ -1430,23 +1447,24 @@ double BasicValues::GetModalSpecAmtTgt(double
annualized_pmt) const
/// changes dramatically complicate the relationship between premium
/// and specified amount.
-double BasicValues::GetModalSpecAmtMinNonMec(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtMinNonMec(currency annualized_pmt) const
{
// TAXATION !! No table available if 7PP calculated from first principles.
- return round_min_specamt()(annualized_pmt /
MortalityRates_->SevenPayRates()[0]);
+ // shouldn't this be rounded?
+ return currency(round_min_specamt()(annualized_pmt /
MortalityRates_->SevenPayRates()[0]));
}
//============================================================================
-double BasicValues::GetModalSpecAmtGLP(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtGLP(currency annualized_pmt) const
{
mcenum_dbopt_7702 const z = effective_dbopt_7702(DeathBfts_->dbopt()[0],
Effective7702DboRop);
- return gpt_specamt::CalculateGLPSpecAmt(*this, 0, annualized_pmt, z);
+ return currency(gpt_specamt::CalculateGLPSpecAmt(*this, 0, annualized_pmt,
z));
}
//============================================================================
-double BasicValues::GetModalSpecAmtGSP(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtGSP(currency annualized_pmt) const
{
- return gpt_specamt::CalculateGSPSpecAmt(*this, 0, annualized_pmt);
+ return currency(gpt_specamt::CalculateGSPSpecAmt(*this, 0,
annualized_pmt));
}
/// Calculate specified amount using a corridor ratio.
@@ -1486,12 +1504,12 @@ double BasicValues::GetModalSpecAmtGSP(double
annualized_pmt) const
/// integral cents, this implementation cannot guarantee to give the
/// desired answer in every case.
-double BasicValues::GetModalSpecAmtCorridor(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtCorridor(currency annualized_pmt) const
{
int const k = round_corridor_factor().decimals();
double const s = nonstd::power(10, k);
double const z = std::round(s * GetCorridorFactor()[0]);
- return round_min_specamt()((z * annualized_pmt) / s);
+ return currency(round_min_specamt()((z * annualized_pmt) / s));
}
/// Calculate specified amount based on salary.
@@ -1501,7 +1519,7 @@ double BasicValues::GetModalSpecAmtCorridor(double
annualized_pmt) const
/// sufficiently large, then specamt would be negative, which cannot
/// make any sense.
-double BasicValues::GetModalSpecAmtSalary(int a_year) const
+currency BasicValues::GetModalSpecAmtSalary(int a_year) const
{
double z =
yare_input_.ProjectedSalary[a_year]
@@ -1512,7 +1530,7 @@ double BasicValues::GetModalSpecAmtSalary(int a_year)
const
z = std::min(z, yare_input_.SalarySpecifiedAmountCap);
}
z -= yare_input_.SalarySpecifiedAmountOffset;
- return round_min_specamt()(std::max(0.0, z));
+ return currency(round_min_specamt()(std::max(0.0, z)));
}
/// In general, strategies linking specamt and premium commute. The
@@ -1522,13 +1540,13 @@ double BasicValues::GetModalSpecAmtSalary(int a_year)
const
/// calling this function elicits an error message. SOMEDAY !! It
/// would be better to disable this strategy in the GUI.
-double BasicValues::GetModalSpecAmtMlyDed(double, mcenum_mode) const
+currency BasicValues::GetModalSpecAmtMlyDed(currency, mcenum_mode) const
{
alarum()
<< "No maximum specified amount is defined for this product."
<< LMI_FLUSH
;
- return 0.0;
+ return currency(0.0);
}
/// 'Unusual' banding is one particular approach we needed to model.
@@ -1539,7 +1557,7 @@ double BasicValues::GetModalSpecAmtMlyDed(double,
mcenum_mode) const
std::vector<double> const& BasicValues::GetBandedCoiRates
(mcenum_gen_basis rate_basis
- ,double a_specamt
+ ,currency a_specamt
) const
{
if(UseUnusualCOIBanding && mce_gen_guar != rate_basis)
diff --git a/ledger_invariant_init.cpp b/ledger_invariant_init.cpp
index 289283b..a53b7bb 100644
--- a/ledger_invariant_init.cpp
+++ b/ledger_invariant_init.cpp
@@ -101,7 +101,7 @@ void LedgerInvariant::Init(BasicValues const* b)
}
else if(b->database().query<bool>(DB_TermIsNotRider))
{
- TermSpecAmt = b->DeathBfts_->supplamt();
+ TermSpecAmt = doubleize(b->DeathBfts_->supplamt());
if(!each_equal(TermSpecAmt, 0.0))
{
HasSupplSpecAmt = true;
@@ -111,7 +111,7 @@ void LedgerInvariant::Init(BasicValues const* b)
{
TermSpecAmt .assign(Length, 0.0);
}
- SpecAmt = b->DeathBfts_->specamt();
+ SpecAmt = doubleize(b->DeathBfts_->specamt());
// Forborne vectors.
@@ -675,8 +675,8 @@ void LedgerInvariant::Init(BasicValues const* b)
// premium-strategy calculations. Use E[er]GrossPmt for illustrations:
// they're *output* values that result from transaction processing.
- EePmt = b->Outlay_->ee_modal_premiums();
- ErPmt = b->Outlay_->er_modal_premiums();
+ EePmt = doubleize(b->Outlay_->ee_modal_premiums());
+ ErPmt = doubleize(b->Outlay_->er_modal_premiums());
// Special-case strings.
diff --git a/outlay.cpp b/outlay.cpp
index 66e32e3..e3974f9 100644
--- a/outlay.cpp
+++ b/outlay.cpp
@@ -32,37 +32,37 @@ modal_outlay::modal_outlay(yare_input const& yi)
:dumpin_ {yi.Dumpin}
,external_1035_amount_ {yi.External1035ExchangeAmount}
,internal_1035_amount_ {yi.Internal1035ExchangeAmount}
- ,ee_modal_premiums_ {yi.Payment}
+ ,ee_modal_premiums_ (currencyize(yi.Payment))
,ee_premium_modes_ {yi.PaymentMode}
- ,er_modal_premiums_ {yi.CorporationPayment}
+ ,er_modal_premiums_ (currencyize(yi.CorporationPayment))
,er_premium_modes_ {yi.CorporationPaymentMode}
- ,new_cash_loans_ {yi.NewLoan}
- ,withdrawals_ {yi.Withdrawal}
+ ,new_cash_loans_ (currencyize(yi.NewLoan))
+ ,withdrawals_ (currencyize(yi.Withdrawal))
{
}
-void modal_outlay::set_ee_modal_premiums(double z, int from_year, int to_year)
+void modal_outlay::set_ee_modal_premiums(currency z, int from_year, int
to_year)
{
std::fill_n(ee_modal_premiums_.begin() + from_year, to_year - from_year,
z);
}
-void modal_outlay::set_er_modal_premiums(double z, int from_year, int to_year)
+void modal_outlay::set_er_modal_premiums(currency z, int from_year, int
to_year)
{
std::fill_n(er_modal_premiums_.begin() + from_year, to_year - from_year,
z);
}
-void modal_outlay::set_er_modal_premiums(std::vector<double> const& z)
+void modal_outlay::set_er_modal_premiums(std::vector<currency> const& z)
{
LMI_ASSERT(z.size() == er_modal_premiums_.size());
er_modal_premiums_ = z;
}
-void modal_outlay::set_new_cash_loans(double z, int from_year, int to_year)
+void modal_outlay::set_new_cash_loans(currency z, int from_year, int to_year)
{
std::fill_n(new_cash_loans_.begin() + from_year, to_year - from_year, z);
}
-void modal_outlay::set_withdrawals(double z, int from_year, int to_year)
+void modal_outlay::set_withdrawals(currency z, int from_year, int to_year)
{
std::fill_n(withdrawals_.begin() + from_year, to_year - from_year, z);
}
diff --git a/solve.cpp b/solve.cpp
index 8b27ee1..e941e0e 100644
--- a/solve.cpp
+++ b/solve.cpp
@@ -153,36 +153,36 @@ double SolveTest()
inline static double SolveSpecAmt(double CandidateValue)
{
// IHS !! Change surrchg when SA changes?
- That->SolveSetSpecAmt(CandidateValue, ThatSolveBegYear, ThatSolveEndYear);
+ That->SolveSetSpecAmt(currency(CandidateValue), ThatSolveBegYear,
ThatSolveEndYear);
return only_set_values ? 0.0 : SolveTest();
}
//============================================================================
inline static double SolvePrem(double CandidateValue)
{
- That->SolveSetPmts(CandidateValue, ThatSolveBegYear, ThatSolveEndYear);
+ That->SolveSetPmts(currency(CandidateValue), ThatSolveBegYear,
ThatSolveEndYear);
return only_set_values ? 0.0 : SolveTest();
}
//============================================================================
inline static double SolveLoan(double CandidateValue)
{
- That->SolveSetLoans(CandidateValue, ThatSolveBegYear, ThatSolveEndYear);
+ That->SolveSetLoans(currency(CandidateValue), ThatSolveBegYear,
ThatSolveEndYear);
return only_set_values ? 0.0 : SolveTest();
}
//============================================================================
inline static double SolveWD(double CandidateValue)
{
- That->SolveSetWDs(CandidateValue, ThatSolveBegYear, ThatSolveEndYear);
+ That->SolveSetWDs(currency(CandidateValue), ThatSolveBegYear,
ThatSolveEndYear);
return only_set_values ? 0.0 : SolveTest();
}
//============================================================================
void AccountValue::SolveSetPmts
- (double a_Pmt
- ,int ThatSolveBegYear
- ,int ThatSolveEndYear
+ (currency a_Pmt
+ ,int ThatSolveBegYear
+ ,int ThatSolveEndYear
)
{
Outlay_->set_ee_modal_premiums(a_Pmt, ThatSolveBegYear, ThatSolveEndYear);
@@ -190,9 +190,9 @@ void AccountValue::SolveSetPmts
//============================================================================
void AccountValue::SolveSetSpecAmt
- (double a_Bft
- ,int ThatSolveBegYear
- ,int ThatSolveEndYear
+ (currency a_Bft
+ ,int ThatSolveBegYear
+ ,int ThatSolveEndYear
)
{
DeathBfts_->set_specamt(a_Bft, ThatSolveBegYear, ThatSolveEndYear);
@@ -200,9 +200,9 @@ void AccountValue::SolveSetSpecAmt
//============================================================================
void AccountValue::SolveSetLoans
- (double a_Loan
- ,int ThatSolveBegYear
- ,int ThatSolveEndYear
+ (currency a_Loan
+ ,int ThatSolveBegYear
+ ,int ThatSolveEndYear
)
{
Outlay_->set_new_cash_loans(a_Loan, ThatSolveBegYear, ThatSolveEndYear);
@@ -210,9 +210,9 @@ void AccountValue::SolveSetLoans
//============================================================================
void AccountValue::SolveSetWDs
- (double a_WD
- ,int ThatSolveBegYear
- ,int ThatSolveEndYear
+ (currency a_WD
+ ,int ThatSolveBegYear
+ ,int ThatSolveEndYear
)
{
Outlay_->set_withdrawals(a_WD, ThatSolveBegYear, ThatSolveEndYear);
@@ -220,9 +220,9 @@ void AccountValue::SolveSetWDs
//============================================================================
void AccountValue::SolveSetLoanThenWD
- (double // Amt
- ,int // ThatSolveBegYear
- ,int // ThatSolveEndYear
+ (currency // Amt
+ ,int // ThatSolveBegYear
+ ,int // ThatSolveEndYear
)
{
// IHS !! Implemented in lmi.
@@ -348,6 +348,6 @@ double AccountValue::Solve()
only_set_values = !Solving;
double actual_solution = Solution.first;
- SolveFn(actual_solution);
+ SolveFn(currency(actual_solution));
return actual_solution;
}