[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] valyuta/002 d137177 02/65: Currency
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] valyuta/002 d137177 02/65: Currency |
Date: |
Wed, 16 Sep 2020 16:55:11 -0400 (EDT) |
branch: valyuta/002
commit d137177b117c8cbeb35a052aaa9d77629035cfc4
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Currency
---
accountvalue.cpp | 59 +++++-----
basicvalues.cpp | 31 +++---
currency.hpp | 143 ++++++++++++++++++++++++
currency_test.cpp | 60 ++++++++++
death_benefits.cpp | 4 +-
gpt_specamt.cpp | 18 +--
group_values.cpp | 2 +-
ihs_acctval.cpp | 96 +++++++++-------
ihs_avdebug.cpp | 4 +-
ihs_avmly.cpp | 277 ++++++++++++++++++++++++++--------------------
ihs_avsolve.cpp | 52 ++++-----
ihs_avstrtgy.cpp | 48 ++++----
ihs_basicval.cpp | 186 ++++++++++++++++---------------
ledger_invariant_init.cpp | 9 +-
outlay.cpp | 40 ++++---
solve.cpp | 57 +++++-----
16 files changed, 682 insertions(+), 404 deletions(-)
diff --git a/accountvalue.cpp b/accountvalue.cpp
index eaf2dde..d457372 100644
--- a/accountvalue.cpp
+++ b/accountvalue.cpp
@@ -126,9 +126,9 @@ AccountValue::AccountValue(Input const& input)
/// Specified amount.
-double AccountValue::base_specamt(int year) const
+currency AccountValue::base_specamt(int year) const
{
- return InvariantValues().SpecAmt[year];
+ return currency(InvariantValues().SpecAmt[year]);
}
//============================================================================
@@ -139,7 +139,7 @@ std::shared_ptr<Ledger const>
AccountValue::ledger_from_av() const
}
//============================================================================
-double AccountValue::RunAV()
+currency AccountValue::RunAV()
{
InvariantValues().Init(this);
OverridingPmts = stored_pmts;
@@ -148,9 +148,9 @@ double AccountValue::RunAV()
}
//============================================================================
-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?
@@ -172,7 +172,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
@@ -184,7 +184,7 @@ double AccountValue::RunAllApplicableBases()
,mce_sep_full
);
- double z = 0.0;
+ currency z = currency(0);
if(Solving)
{
z = Solve();
@@ -210,7 +210,7 @@ double AccountValue::RunAllApplicableBases()
}
//============================================================================
-double AccountValue::RunOneCell(mcenum_run_basis TheBasis)
+currency AccountValue::RunOneCell(mcenum_run_basis TheBasis)
{
if(Solving)
{
@@ -331,8 +331,8 @@ void AccountValue::DoYear
YearsCorridorFactor = BasicValues::GetCorridorFactor()[Year];
- GrossPmts .assign(12, 0.0);
- NetPmts .assign(12, 0.0);
+ GrossPmts .assign(12, currency(0));
+ NetPmts .assign(12, currency(0));
// IHS !! Strategy here?
@@ -441,7 +441,7 @@ inline int AccountValue::MonthsToNextModalPmtDate() const
// Set specamt according to selected strategy, in every year.
void AccountValue::PerformSpecAmtStrategy()
{
- double SA = 0.0;
+ currency SA = currency(0);
switch(yare_input_.SpecifiedAmountStrategy[0])
{
case mce_sa_input_scalar:
@@ -522,7 +522,7 @@ void AccountValue::TxOptionChange()
}
// Nothing to do unless AV is positive.
- double AV = AVUnloaned + AVRegLn + AVPrfLn;
+ currency AV = AVUnloaned + AVRegLn + AVPrfLn;
if(AV <= 0.0)
{
return;
@@ -595,7 +595,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
@@ -694,7 +694,7 @@ void AccountValue::TxPmt()
GrossPmts[Month] = pmt;
if(0 == Year && 0 == Month)
{
- double TotalDumpin =
+ currency TotalDumpin =
Outlay_->dumpin()
+ Outlay_->external_1035_amount()
+ Outlay_->internal_1035_amount()
@@ -750,7 +750,8 @@ void AccountValue::TxSetBOMAV()
void AccountValue::TxSetDeathBft()
{
// Total account value is unloaned plus loaned.
- double AV = AVUnloaned + AVRegLn + AVPrfLn;
+ currency AV = AVUnloaned + AVRegLn + AVPrfLn;
+ currency corr = currency(round_death_benefit()(YearsCorridorFactor * AV));
// Set death benefit reflecting corridor and death benefit option.
switch(YearsDBOpt)
@@ -758,15 +759,15 @@ void AccountValue::TxSetDeathBft()
case mce_option1:
{
// Option 1: specamt, or corridor times AV if greater.
- deathbft = std::max(ActualSpecAmt, YearsCorridorFactor * AV);
+ deathbft = std::max(ActualSpecAmt, corr);
}
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
- (ActualSpecAmt + std::max(0.0, AV)
- ,YearsCorridorFactor * AV
+ (ActualSpecAmt + std::max(currency(0), AV)
+ ,corr
);
break;
case mce_rop: // fall through
@@ -810,7 +811,7 @@ void AccountValue::TxSetRiderDed()
AdbCharge = 0.0;
if(hasadb)
{
- AdbCharge = YearsAdbRate * std::min(500000.0, ActualSpecAmt);
+ AdbCharge = currency(YearsAdbRate * std::min<double>(500000.0,
ActualSpecAmt));
}
}
@@ -820,7 +821,7 @@ void AccountValue::TxDoMlyDed()
{
AVUnloaned -= CoiCharge + AdbCharge + WpCharge;
MlyDed = YearsMonthlyPolicyFee + CoiCharge + AdbCharge + WpCharge;
- mlydedtonextmodalpmtdate = MlyDed * MonthsToNextModalPmtDate();
+ mlydedtonextmodalpmtdate = currency(doubleize(MlyDed) *
MonthsToNextModalPmtDate());
}
/// Credit interest on account value.
@@ -832,7 +833,7 @@ void AccountValue::TxCreditInt()
if(0.0 < AVUnloaned)
{
// IHS !! Each interest increment is rounded separately in lmi.
- double z = round_interest_credit()(AVUnloaned * YearsGenAcctIntRate);
+ currency z = currency(round_interest_credit()(AVUnloaned *
YearsGenAcctIntRate));
AVUnloaned += z;
}
// Loaned account value cannot be negative.
@@ -857,8 +858,8 @@ void AccountValue::TxLoanInt()
AVRegLn += RegLnIntCred;
AVPrfLn += PrfLnIntCred;
- double RegLnIntAccrued = round_interest_credit()(RegLnBal *
YearsRegLnIntDueRate);
- double PrfLnIntAccrued = round_interest_credit()(PrfLnBal *
YearsPrfLnIntDueRate);
+ currency RegLnIntAccrued = currency(round_interest_credit()(RegLnBal *
YearsRegLnIntDueRate));
+ currency PrfLnIntAccrued = currency(round_interest_credit()(PrfLnBal *
YearsPrfLnIntDueRate));
RegLnBal += RegLnIntAccrued;
PrfLnBal += PrfLnIntAccrued;
@@ -899,7 +900,7 @@ void AccountValue::TxTakeWD()
// max loan: cannot become overloaned until end of policy year.
// However, lmi provides a variety of implementations instead of
// only one.
- double max_wd =
+ currency max_wd =
AVUnloaned
+ (AVRegLn + AVPrfLn)
- (RegLnBal + PrfLnBal)
@@ -949,7 +950,7 @@ void AccountValue::TxTakeWD()
}
// Deduct withdrawal fee.
- wd -= std::min(WDFee, wd * WDFeeRate);
+ wd -= std::min(WDFee, currency(wd * WDFeeRate));
// IHS !! This treats input WD as gross; it probably should be net. But
compare lmi.
InvariantValues().NetWD[Year] = wd;
@@ -987,7 +988,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(currency(0), MaxLoan);
MaxLoan = round_loan()(MaxLoan);
// IHS !! Preferred loan calculations would go here: implemented in lmi.
@@ -1064,9 +1065,9 @@ 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::IncrementBOM(int, int, double)
+ {return currency(0);}
+void AccountValue::IncrementEOM(int, int, currency, currency)
{return;}
void AccountValue::IncrementEOY(int)
{return;}
diff --git a/basicvalues.cpp b/basicvalues.cpp
index ed8022a..f13a8a3 100644
--- a/basicvalues.cpp
+++ b/basicvalues.cpp
@@ -119,9 +119,14 @@ void BasicValues::Init()
PremiumTax_ .reset(new premium_tax (PremiumTaxState_, database()));
Loads_ .reset(new Loads(database(), IsSubjectToIllustrationReg()));
- database().query_into(DB_MinSpecAmt, MinSpecAmt);
- database().query_into(DB_MinWd , MinWD );
- database().query_into(DB_WdFee , WDFee );
+// database().query_into(DB_MinSpecAmt, MinSpecAmt);
+// database().query_into(DB_MinWd , MinWD );
+// database().query_into(DB_WdFee , WDFee );
+// database().query_into(DB_WdFeeRate , WDFeeRate );
+ MinSpecAmt = database().query<int>(DB_MinSpecAmt);
+ MinWD = database().query<int>(DB_MinWd );
+ WDFee = database().query<int>(DB_WdFee );
+// WDFeeRate = database().query<int>(DB_WdFeeRate ); // no, this line looks
wrong
database().query_into(DB_WdFeeRate , WDFeeRate );
// The antediluvian branch leaves FundData_, StratifiedCharges_, and
@@ -137,20 +142,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
@@ -212,23 +217,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
@@ -286,7 +291,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 a3a3fc2..9b04fbb 100644
--- a/currency.hpp
+++ b/currency.hpp
@@ -24,6 +24,149 @@
#include "config.hpp"
+#include "bourn_cast.hpp"
+#include "round_to.hpp"
+
+#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
+{
+//#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;
+
+ public:
+ currency() = default;
+ currency(currency const&) = default;
+ ~currency() = default;
+
+ explicit currency(double d) {m_ = from_double(d);}
+
+ currency& operator=(currency const&) = default;
+ currency& operator=(double d) {m_ = from_double(d); return *this;}
+
+ operator double() const {return to_double();}
+
+ // Is this better, with 'const&'?
+// bool operator==(currency const& z) const {return z.m_ == m_;}
+
+ bool operator< (currency z) const {return m_ < z.m_;}
+ bool operator<=(currency z) const {return m_ <= z.m_;}
+ bool operator==(currency z) const {return m_ == z.m_;}
+ bool operator!=(currency z) const {return m_ != z.m_;}
+ bool operator> (currency z) const {return m_ > z.m_;}
+ bool operator>=(currency z) const {return m_ >= z.m_;}
+
+ bool operator< (double d) const {return to_double() < d;}
+ bool operator<=(double d) const {return to_double() <= d;}
+ bool operator==(double d) const {return to_double() == d;}
+ bool operator!=(double d) const {return to_double() != d;}
+ bool operator> (double d) const {return to_double() > d;}
+ bool operator>=(double d) const {return to_double() >= d;}
+
+ // 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;}
+ // NOPE!
+// currency& operator*=(currency z) {m_ *= z.m_; return *this;}
+
+ currency& operator+=(double z) {m_ += from_double(z); return *this;}
+ currency& operator-=(double z) {m_ -= from_double(z); return *this;}
+ // NOPE!
+// currency& operator*=(double z) {m_ *= from_double(z); return *this;}
+ // Check result range (and avoid multiplying by 100/100):
+// currency& operator*=(double z) {m_ = bourn_cast<data_type>(100.0 *
to_double() * z); return *this;}
+ // 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:
+// data_type from_double(double d) const {return bourn_cast<data_type>(100.0
* d);}
+ // Far too permissive:
+// data_type from_double(double d) const {return static_cast<data_type>(100.0
* d);}
+ // ...and a bit insidious:
+// data_type from_double(double d) const {return
static_cast<data_type>(100.000000000001 * d);}
+ // ...less bad:
+ data_type from_double(double d) const {return round(100.0 * d);}
+ double to_double() const {return bourn_cast<double>(m_) / 100.0;}
+
+ data_type round(double d) const
+ {
+ static round_to<double> const r(0, r_to_nearest);
+ return static_cast<data_type>(r(d));
+ }
+
+ data_type m_ = {0};
+};
+
+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.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)
+{
+// return os << c.m_ << ' ' << c.to_double();
+ 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); // This implementation seems surprising.
+}
+
+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 9920b13..83ba6cb 100644
--- a/currency_test.cpp
+++ b/currency_test.cpp
@@ -25,6 +25,8 @@
#include "test_tools.hpp"
+#include <limits>
+
class currency_test
{
public:
@@ -41,6 +43,64 @@ void currency_test::test()
void currency_test::test_something()
{
+ currency a0;
+ std::cout << a0 << std::endl;
+ BOOST_TEST(0.00 == a0.operator double());
+ BOOST_TEST( 0 == a0.m_);
+
+// Figure out what to do about this:
+// currency a1(3.14);
+
+ currency a1(3.25);
+ BOOST_TEST(3.25 == a1.operator double());
+ BOOST_TEST( 325 == a1.m_);
+ BOOST_TEST( 0 == a1 * a0);
+ a1 += a1;
+ BOOST_TEST(6.50 == a1.operator double());
+ BOOST_TEST( 650 == a1.m_);
+ 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_);
+
+ double d0 = 123.99999999999;
+ currency c0(d0);
+std::cout << c0 << " converted from 123.999..." << std::endl;
+ double d1 = 1.0 + std::numeric_limits<double>::epsilon();
+ currency c1(d1);
+std::cout << c1 << " converted from 1.0 + epsilon..." << std::endl;
+ double d2 = 1.0 - std::numeric_limits<double>::epsilon();
+ currency c2(d2);
+std::cout << c2 << " converted from 1.0 - epsilon..." << std::endl;
+
+ double big_num = 1.0e100;
+#pragma GCC diagnostic ignored "-Wfloat-conversion"
+ currency::data_type big_int0 = big_num;
+std::cout << "int0: " << big_int0 << std::endl;
+ currency::data_type big_int1 = 1.0 * big_num;
+std::cout << "int1: " << big_int1 << std::endl;
+ currency::data_type big_int2 = 10.0 * big_num;
+std::cout << "int2: " << big_int2 << std::endl;
+ currency::data_type big_inte = 100.0 * big_num;
+std::cout << "int3: " << big_inte << std::endl;
+ currency::data_type big_int4 = round(100.0 * big_num);
+std::cout << "int4: " << big_int4 << std::endl;
+
+ currency a3(big_num / 1000.0);
+std::cout << a3 << std::endl;
+std::cout << big_num << std::endl;
+std::cout << "rounded: " << round(big_num) << std::endl;
+ double too_big = std::numeric_limits<double>::max();
+ currency a4(too_big);
+std::cout << a4 << std::endl;
+std::cout << too_big << std::endl;
+std::cout << "rounded: " << round(too_big) << std::endl;
+std::cout << 100.0 * too_big << std::endl;
}
int test_main(int, char*[])
diff --git a/death_benefits.cpp b/death_benefits.cpp
index 08bfa31..ced6ac6 100644
--- a/death_benefits.cpp
+++ b/death_benefits.cpp
@@ -56,7 +56,7 @@ death_benefits::death_benefits
}
//============================================================================
-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
@@ -75,7 +75,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..434a8c0 100644
--- a/gpt_specamt.cpp
+++ b/gpt_specamt.cpp
@@ -28,10 +28,10 @@
#include "safely_dereference_as.hpp"
#include "zero.hpp"
-double gpt_specamt::CalculateGLPSpecAmt
+currency gpt_specamt::CalculateGLPSpecAmt
(BasicValues const& a_Values
,int a_Duration
- ,double a_Premium
+ ,currency a_Premium
,mcenum_dbopt_7702 a_DBOpt
)
{
@@ -46,10 +46,10 @@ double gpt_specamt::CalculateGLPSpecAmt
);
}
-double gpt_specamt::CalculateGSPSpecAmt
+currency gpt_specamt::CalculateGSPSpecAmt
(BasicValues const& a_Values
,int a_Duration
- ,double a_Premium
+ ,currency a_Premium
)
{
Irc7702 const& z(safely_dereference_as<Irc7702>(a_Values.Irc7702_.get()));
@@ -73,7 +73,7 @@ class FindSpecAmt
double const Premium;
double const NetPmtFactorTgt;
double const NetPmtFactorExc;
- double SpecAmt;
+ currency SpecAmt;
public:
FindSpecAmt
@@ -92,7 +92,7 @@ class FindSpecAmt
,Premium {a_Premium}
,NetPmtFactorTgt {a_NetPmtFactorTgt}
,NetPmtFactorExc {a_NetPmtFactorExc}
- ,SpecAmt {0.0}
+ ,SpecAmt {currency(0.0)}
{
}
double operator()(double a_Trial)
@@ -130,11 +130,11 @@ class FindSpecAmt
/// because it is typically used to set an input parameter, and
/// specamt is such a parameter whereas DB is not.
-double gpt_specamt::CalculateSpecAmt
+currency gpt_specamt::CalculateSpecAmt
(BasicValues const& a_Values
,EIOBasis a_EIOBasis
,int a_Duration
- ,double a_Premium
+ ,currency a_Premium
,double a_NetPmtFactorTgt
,double a_NetPmtFactorExc
)
@@ -166,5 +166,5 @@ double gpt_specamt::CalculateSpecAmt
,true
);
- return fsa.Get();
+ return currency(fsa.Get());
}
diff --git a/group_values.cpp b/group_values.cpp
index dbc55d6..ea8476c 100644
--- a/group_values.cpp
+++ b/group_values.cpp
@@ -418,7 +418,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);
// 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 decc40f..4d54db7 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -140,21 +140,21 @@ AccountValue::AccountValue(Input const& input)
/// Specified amount (disregarding any term or "supplemental" amount).
-double AccountValue::base_specamt(int year) const
+currency AccountValue::base_specamt(int year) const
{
- return InvariantValues().SpecAmt[year];
+ return currency(InvariantValues().SpecAmt[year]);
}
/// Specified amount of term rider.
-double AccountValue::term_specamt(int year) const
+currency AccountValue::term_specamt(int year) const
{
- return InvariantValues().TermSpecAmt[year];
+ return currency(InvariantValues().TermSpecAmt[year]);
}
/// Specified amount for 7702 (not 7702A).
-double AccountValue::specamt_for_7702(int year) const
+currency AccountValue::specamt_for_7702(int year) const
{
return
base_specamt(year)
@@ -164,7 +164,7 @@ double AccountValue::specamt_for_7702(int year) const
/// Specified amount for 7702A (not 7702).
-double AccountValue::specamt_for_7702A(int year) const
+currency AccountValue::specamt_for_7702A(int year) const
{
return
base_specamt(year)
@@ -180,7 +180,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
@@ -205,7 +205,7 @@ Then run other bases.
DebugPrintInit();
}
- double z = RunAllApplicableBases();
+ currency z = RunAllApplicableBases();
FinalizeLifeAllBases();
@@ -227,7 +227,7 @@ void AccountValue::SetGuarPrem()
}
//============================================================================
-double AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
+currency AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
{
if
( !BasicValues::IsSubjectToIllustrationReg()
@@ -240,7 +240,7 @@ double AccountValue::RunOneBasis(mcenum_run_basis a_Basis)
;
}
- double z = 0.0;
+ currency z(0);
if(Solving)
{
// Apparently this should never be done because Solve() is called in
@@ -263,9 +263,9 @@ 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;
+ currency z(0);
// TODO ?? Normally, running on the current basis determines the
// overriding values for all components of outlay--e.g., premiums,
@@ -296,7 +296,7 @@ double AccountValue::RunAllApplicableBases()
,yare_input_.SolveBeginYear
,yare_input_.SolveEndYear
,yare_input_.SolveTarget
- ,yare_input_.SolveTargetValue
+ ,currency(yare_input_.SolveTargetValue)
,yare_input_.SolveTargetYear
,yare_input_.SolveExpenseGeneralAccountBasis
,yare_input_.SolveSeparateAccountBasis
@@ -322,7 +322,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);
@@ -348,7 +348,7 @@ double AccountValue::RunOneCell(mcenum_run_basis a_Basis)
IncrementEOM
(year
,month
- ,SepAcctValueAfterDeduction * InforceLivesBoy()
+ ,currency(SepAcctValueAfterDeduction * InforceLivesBoy())
,CumPmts
);
}
@@ -401,7 +401,7 @@ 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));
// TAXATION !! Input::InforceAnnualTargetPremium should be used here.
double annual_target_premium = GetModalTgtPrem
@@ -578,15 +578,15 @@ void AccountValue::SetInitialValues()
CumPmts = InforceCumPmts;
TaxBasis = InforceTaxBasis;
- YearlyTaxBasis.assign(BasicValues::GetLength(), 0.0);
+ YearlyTaxBasis.assign(BasicValues::GetLength(), currency(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));
+ withdrawal_ullage_ .assign(BasicValues::GetLength(), currency(0));
NoLapseActive = true;
if(NoLapseDboLvlOnly && mce_option1 != DeathBfts_->dbopt()[0])
{
@@ -614,7 +614,7 @@ void AccountValue::SetInitialValues()
DcvWpCharge = 0.0;
HoneymoonActive = false;
- HoneymoonValue = -std::numeric_limits<double>::max();
+ HoneymoonValue = -std::numeric_limits<int>::max(); // yick
if(mce_gen_curr == GenBasis_)
{
HoneymoonActive = yare_input_.HoneymoonEndorsement;
@@ -680,7 +680,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
@@ -690,7 +690,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);
}
// Paranoid check.
@@ -739,10 +739,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)
@@ -762,8 +762,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(currency(0), assets_post_bom );
+ CumPmtsPostBom = std::max(currency(0), cum_pmts_post_bom);
DoMonthCR();
}
@@ -845,10 +845,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));
+ EeGrossPmts .assign(12, currency(0));
+ ErGrossPmts .assign(12, currency(0));
+ NetPmts .assign(12, currency(0));
InitializeSpecAmt();
}
@@ -1003,7 +1003,7 @@ void AccountValue::set_modal_min_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.
@@ -1016,14 +1016,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(currency(0), TotalAccountValue()));
}
//============================================================================
@@ -1063,6 +1063,17 @@ void AccountValue::SetClaims()
YearsGrossClaims = partial_mortality_qx()[Year] * DBReflectingCorr;
YearsAVRelOnDeath = partial_mortality_qx()[Year] *
TotalAccountValue();
YearsLoanRepaidOnDeath = partial_mortality_qx()[Year] * (RegLnBal +
PrfLnBal);
+#if 0
+ // presumably material_difference() isn't needed at all
+ YearsDeathProceeds = material_difference
+ (YearsGrossClaims
+ ,YearsLoanRepaidOnDeath
+ );
+ YearsNetClaims = material_difference
+ (YearsGrossClaims
+ ,YearsAVRelOnDeath
+ );
+#endif // 0
YearsDeathProceeds = material_difference
(YearsGrossClaims
,YearsLoanRepaidOnDeath
@@ -1100,9 +1111,10 @@ void AccountValue::SetProjectedCoiCharge()
TxSetDeathBft();
TxSetTermAmt();
+ // presumably material_difference() isn't needed at all? um...yes, it is
double this_years_terminal_naar = material_difference
- (DBReflectingCorr + TermDB
- ,TotalAccountValue()
+ (doubleize(DBReflectingCorr + TermDB)
+ ,doubleize(TotalAccountValue())
);
this_years_terminal_naar = std::max(0.0, this_years_terminal_naar);
double next_years_coi_rate = GetBandedCoiRates(GenBasis_, ActualSpecAmt)[1
+ Year];
@@ -1138,9 +1150,9 @@ void AccountValue::FinalizeYear()
{
VariantValues().TotalLoanBalance[Year] = RegLnBal + PrfLnBal;
- double total_av = TotalAccountValue();
- double surr_chg = SurrChg();
- double csv_net =
+ currency total_av = TotalAccountValue();
+ currency surr_chg = SurrChg();
+ currency csv_net =
total_av
- (RegLnBal + PrfLnBal)
// + ExpRatReserve // This would be added if it existed.
@@ -1168,7 +1180,7 @@ void AccountValue::FinalizeYear()
if(!Solving)
{
- csv_net = std::max(csv_net, 0.0);
+ csv_net = std::max(csv_net, currency(0));
}
if(Solving)
@@ -1177,7 +1189,7 @@ void AccountValue::FinalizeYear()
}
// 7702(f)(2)(A)
- double cv_7702 =
+ currency cv_7702 =
total_av
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if it existed.
diff --git a/ihs_avdebug.cpp b/ihs_avdebug.cpp
index 8893f04..bdc4af2 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 22b1cc5..63dd822 100644
--- a/ihs_avmly.cpp
+++ b/ihs_avmly.cpp
@@ -135,7 +135,7 @@ void AccountValue::DoMonthDR()
TxExch1035();
// TODO ?? TAXATION !! Is this where spec amt should be increased by GPT?
- double kludge_account_value = std::max(TotalAccountValue(),
HoneymoonValue);
+ currency kludge_account_value = std::max(TotalAccountValue(),
HoneymoonValue);
if(0 == Year && 0 == Month)
{
// This special case was apparently intended for 1035
@@ -151,14 +151,14 @@ void AccountValue::DoMonthDR()
// + std::max(0.0, ExpRatReserve) // This would be added if it
existed.
);
// TODO ?? TAXATION !! Use CashValueFor7702() instead?
- double max_necessary_premium = Irc7702A_->MaxNecessaryPremium
+ 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
@@ -196,24 +196,37 @@ void AccountValue::DoMonthDR()
);
}
- double gross_1035 = 0.0;
+ currency gross_1035 = currency(0);
if(0 == Year && 0 == Month)
{
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]
- ,gross_1035 + necessary_premium
+ double unnecessary_premium = material_difference // round?
+ (doubleize(GrossPmts[Month])
+ ,doubleize(gross_1035) + necessary_premium
);
+// NetMaxNecessaryPremium
+// GrossMaxNecessaryPremium
+ NecessaryPremium = necessary_premium;
+ UnnecessaryPremium = unnecessary_premium;
+ if(necessary_premium < 0.0 || unnecessary_premium < 0.0)
+ warning()
+ << necessary_premium << " necessary_premium\n"
+ << unnecessary_premium << " unnecessary_premium\n"
+ << Year << " Year\n"
+ << Month << " Month\n"
+// << z << " z\n"
+ << LMI_FLUSH
+ ;
// It is crucial to accept necessary premium before processing a
// material change, so that the correct DCV is used.
- TxRecognizePaymentFor7702A(necessary_premium, false);
- TxAcceptPayment(necessary_premium);
+ TxRecognizePaymentFor7702A(NecessaryPremium, false);
+ TxAcceptPayment(NecessaryPremium);
if(0.0 < unnecessary_premium)
{
Irc7702A_->InduceMaterialChange();
@@ -232,9 +245,9 @@ void AccountValue::DoMonthDR()
);
LMI_ASSERT(0.0 <= Dcv);
- UnnecessaryPremium = unnecessary_premium;
- TxRecognizePaymentFor7702A(unnecessary_premium, true);
- TxAcceptPayment(unnecessary_premium);
+// UnnecessaryPremium = unnecessary_premium; // moved up
+ TxRecognizePaymentFor7702A(UnnecessaryPremium, true);
+ TxAcceptPayment(UnnecessaryPremium);
TxTakeLoan();
TxLoanRepay();
@@ -261,7 +274,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,12 +286,12 @@ void AccountValue::process_payment(double payment)
LMI_ASSERT(0.0 <= EeGrossPmts[Month]);
LMI_ASSERT(0.0 <= ErGrossPmts[Month]);
- double gross_1035 = 0.0;
+ currency gross_1035 = currency(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_proportion = 0.0;
if(0.0 != gross_non_1035_pmts)
{
@@ -289,13 +302,13 @@ void AccountValue::process_payment(double payment)
// proportion, because that's the only way the proportion can be
// deduced from input. But it remains a net premium, so the
// net-premium rounding rule is appropriate.
- double er_net_pmt = round_net_premium()(er_proportion * payment);
+ currency er_net_pmt = currency(round_net_premium()(er_proportion *
payment));
// This is deliberately not rounded. The crucial invariant that
// must be preserved is that ee + er = total; but rounding both
// addends could break that invariant, e.g.:
// round(2.5) + round(2.5)
// might not produce 5.0 as desired.
- double ee_net_pmt = payment - er_net_pmt;
+ currency ee_net_pmt = payment - er_net_pmt;
switch(ee_premium_allocation_method)
{
@@ -331,7 +344,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)
{
increment = round_minutiae()(increment);
double genacct_increment = increment * GenAcctPaymentAllocation;
@@ -343,7 +356,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
)
{
@@ -365,7 +378,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)
{
@@ -384,7 +397,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)
{
@@ -413,7 +426,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)
{
decrement = round_minutiae()(decrement);
@@ -426,8 +439,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
@@ -477,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
)
{
@@ -536,7 +549,10 @@ void AccountValue::TxExch1035()
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, GrossPmts[Month]);
+ // Maybe this should return the modified value instead of altering
the argument.
+ double z = GrossPmts[Month];
+ 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
@@ -556,7 +572,7 @@ void AccountValue::TxExch1035()
// TODO ?? Assume for now that all 1035 exchanges represent ee payments.
EeGrossPmts[Month] = GrossPmts[Month];
- double amount_exempt_from_premium_tax = 0.0;
+ currency amount_exempt_from_premium_tax = currency(0);
if(WaivePmTxInt1035)
{
amount_exempt_from_premium_tax = Internal1035Amount;
@@ -588,7 +604,7 @@ void AccountValue::TxExch1035()
if(HoneymoonActive)
{
- HoneymoonValue += std::max(0.0, GrossPmts[Month]);
+ HoneymoonValue += std::max(currency(0), GrossPmts[Month]);
}
CumPmts += GrossPmts[Month];
@@ -602,7 +618,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,
@@ -622,7 +638,7 @@ void AccountValue::TxExch1035()
}
//============================================================================
-double AccountValue::CashValueFor7702() const
+currency AccountValue::CashValueFor7702() const
{
return std::max
(HoneymoonValue
@@ -653,12 +669,12 @@ double AccountValue::ActualMonthlyRate(double
monthly_rate) const
//============================================================================
// Rounded interest increment.
-double AccountValue::InterestCredited
- (double principal
+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)));
}
//============================================================================
@@ -686,7 +702,7 @@ 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
issuing_now
@@ -699,11 +715,11 @@ double AccountValue::minimum_specified_amount(bool
issuing_now, bool term_rider)
// 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)
{
delta = round_specamt()(delta);
double term_proportion = 0.0;
- double const old_total_specamt = ActualSpecAmt + TermSpecAmt;
+ currency const old_total_specamt = ActualSpecAmt + TermSpecAmt;
// Adjust term here only if it's formally a rider.
if(TermRiderActive && !TermIsNotRider)
{
@@ -793,14 +809,14 @@ void AccountValue::ChangeSpecAmtBy(double delta)
TxSetDeathBft();
}
-void AccountValue::ChangeSupplAmtBy(double delta)
+void AccountValue::ChangeSupplAmtBy(currency delta)
{
delta = round_specamt()(delta);
TermSpecAmt += delta;
TermSpecAmt = std::max
(TermSpecAmt
- ,0.0 // No minimum other than zero is defined.
+ ,currency(0) // No minimum other than zero is defined.
);
TermSpecAmt = round_specamt()(TermSpecAmt);
// At least for now, there is no effect on surrender charges.
@@ -891,11 +907,11 @@ void AccountValue::TxOptionChange()
{
if(mce_option2 == old_option)
{
- ChangeSpecAmtBy(std::max(0.0, TotalAccountValue()));
+ ChangeSpecAmtBy(std::max(currency(0),
TotalAccountValue()));
}
else if(mce_rop == old_option)
{
- ChangeSpecAmtBy(std::max(0.0, CumPmts));
+ ChangeSpecAmtBy(std::max(currency(0), CumPmts));
}
else if(mce_mdb == old_option)
{
@@ -922,7 +938,7 @@ void AccountValue::TxOptionChange()
case mce_option2:
if(OptChgCanDecrSA)
{
- ChangeSpecAmtBy(-std::max(0.0, TotalAccountValue()));
+ ChangeSpecAmtBy(currency(-std::max(currency(0),
TotalAccountValue())));
}
else
{
@@ -932,7 +948,7 @@ void AccountValue::TxOptionChange()
case mce_rop:
if(OptChgCanDecrSA)
{
- ChangeSpecAmtBy(-std::max(0.0, CumPmts));
+ ChangeSpecAmtBy(currency(-std::max(currency(0), CumPmts)));
}
else
{
@@ -942,7 +958,7 @@ void AccountValue::TxOptionChange()
case mce_mdb:
{
// Change spec amt by its additive inverse, making it 0.
- ChangeSpecAmtBy(-(ActualSpecAmt + TermSpecAmt));
+ ChangeSpecAmtBy(currency(-(ActualSpecAmt + TermSpecAmt)));
}
break;
}
@@ -966,7 +982,7 @@ void AccountValue::TxSpecAmtChange()
// > post-1035 exchange calculation of the seven pay will have a death benefit
// > amount to base its calculations on.
#if 1
- ChangeSpecAmtBy(0.0);
+ ChangeSpecAmtBy(currency(0));
#else // 0
// TODO ?? Changing specamt by zero is absurd. The following
// commented-out alternative seems to do the same thing as
@@ -1000,8 +1016,8 @@ void AccountValue::TxSpecAmtChange()
ChangeSupplAmtBy(DeathBfts_->supplamt()[Year] - TermSpecAmt);
}
- double const new_specamt = DeathBfts_->specamt()[Year];
- double const old_specamt = DeathBfts_->specamt()[Year - 1];
+ currency const new_specamt = DeathBfts_->specamt()[Year];
+ currency const old_specamt = DeathBfts_->specamt()[Year - 1];
// Nothing to do if no increase or decrease requested.
// TODO ?? new_specamt != ActualSpecAmt; the latter should be used.
@@ -1196,27 +1212,31 @@ void AccountValue::TxAscertainDesiredPayment()
LMI_ASSERT(materially_equal(GrossPmts[Month], EeGrossPmts[Month] +
ErGrossPmts[Month]));
- double eepmt = 0.0;
+ currency eepmt = currency(0);
if(ee_pay_this_month)
{
eepmt = PerformEePmtStrategy();
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, eepmt);
+ double z = eepmt;
+ Irc7702_->ProcessGptPmt(Year, z);
+ eepmt = z;
}
EeGrossPmts[Month] += eepmt;
GrossPmts [Month] += eepmt;
}
- double erpmt = 0.0;
+ currency erpmt = currency(0);
if(er_pay_this_month)
{
erpmt = PerformErPmtStrategy();
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, erpmt);
+ double z = erpmt;
+ Irc7702_->ProcessGptPmt(Year, z);
+ erpmt = z;
}
ErGrossPmts[Month] += erpmt;
GrossPmts [Month] += erpmt;
@@ -1230,7 +1250,9 @@ void AccountValue::TxAscertainDesiredPayment()
// Illustration-reg guaranteed premium ignores GPT limit.
if(!SolvingForGuarPremium)
{
- Irc7702_->ProcessGptPmt(Year, Dumpin);
+ double z = Dumpin;
+ Irc7702_->ProcessGptPmt(Year, z);
+ Dumpin = z;
}
EeGrossPmts[Month] += Dumpin;
GrossPmts [Month] += Dumpin;
@@ -1242,6 +1264,7 @@ void AccountValue::TxAscertainDesiredPayment()
//============================================================================
// TAXATION !! Should this be called for gpt? or, if it's called,
// should it assert that it has no effect?
+//void AccountValue::TxLimitPayment(currency a_maxpmt)
void AccountValue::TxLimitPayment(double a_maxpmt)
{
// Subtract premium load from gross premium yielding net premium.
@@ -1255,13 +1278,13 @@ void AccountValue::TxLimitPayment(double a_maxpmt)
if(mce_reduce_prem == yare_input_.AvoidMecMethod &&
!Irc7702A_->IsMecAlready())
{
- double gross_1035 = 0.0;
+ currency gross_1035 = currency(0);
if(0 == Year && 0 == Month)
{
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 = std::min<double>(gross_pmt_without_1035,
a_maxpmt);
// TODO ?? For now at least, reduce employee premium first.
progressively_limit
(EeGrossPmts[Month]
@@ -1299,7 +1322,7 @@ void AccountValue::TxLimitPayment(double a_maxpmt)
//============================================================================
void AccountValue::TxRecognizePaymentFor7702A
- (double a_pmt
+ (currency a_pmt
,bool a_this_payment_is_unnecessary
)
{
@@ -1312,7 +1335,7 @@ void AccountValue::TxRecognizePaymentFor7702A
// Policy issue date is always a modal payment date.
- double kludge_account_value = std::max(TotalAccountValue(),
HoneymoonValue);
+ currency kludge_account_value = std::max(TotalAccountValue(),
HoneymoonValue);
if(0 == Year && 0 == Month)
{
kludge_account_value = Dcv;
@@ -1328,7 +1351,7 @@ void AccountValue::TxRecognizePaymentFor7702A
// TODO ?? TAXATION !! Not correct yet--need to test pmt less deductible
WD; and
// shouldn't we deduct the *gross* WD? [Yes, if any fee is part of the
// WD, which it normally is.]
- double amount_paid_7702A = a_pmt;
+ currency amount_paid_7702A = a_pmt;
Irc7702A_->UpdatePmt7702A
(Dcv
,amount_paid_7702A
@@ -1341,18 +1364,26 @@ void AccountValue::TxRecognizePaymentFor7702A
}
//============================================================================
-void AccountValue::TxAcceptPayment(double a_pmt)
+void AccountValue::TxAcceptPayment(currency a_pmt)
{
if(0.0 == a_pmt)
{
return;
}
+ if(a_pmt < 0.0)
+ warning()
+ << a_pmt << " a_pmt\n"
+ << Year << " Year\n"
+ << Month << " Month\n"
+// << z << " z\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.
- double actual_load = GetPremLoad(a_pmt, 0.0);
- double net_pmt = a_pmt - actual_load;
+ currency actual_load = GetPremLoad(a_pmt, currency(0));
+ currency net_pmt = a_pmt - actual_load;
LMI_ASSERT(0.0 <= net_pmt);
NetPmts[Month] += net_pmt;
@@ -1364,12 +1395,12 @@ void AccountValue::TxAcceptPayment(double a_pmt)
process_payment(net_pmt);
- Dcv += std::max(0.0, net_pmt);
+ Dcv += std::max(currency(0), net_pmt);
LMI_ASSERT(0.0 <= Dcv);
if(HoneymoonActive)
{
- HoneymoonValue += std::max(0.0, a_pmt);
+ HoneymoonValue += std::max(currency(0), a_pmt);
}
CumPmts += a_pmt;
@@ -1403,12 +1434,12 @@ 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
+ ,currency a_portion_exempt_from_premium_tax
)
{
- double excess_portion;
+ currency excess_portion;
// All excess.
if(0.0 == UnusedTargetPrem)
{
@@ -1426,7 +1457,7 @@ double AccountValue::GetPremLoad
excess_portion = 0.0;
UnusedTargetPrem -= a_pmt;
}
- double target_portion = a_pmt - excess_portion;
+ currency target_portion = a_pmt - excess_portion;
premium_load_ =
target_portion * YearsPremLoadTgt
@@ -1466,13 +1497,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.
@@ -1502,11 +1533,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(-RequestedLoan, doubleize(RegLnBal) +
doubleize(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(-ActualLoan)));
+ LMI_ASSERT(0.0 == progressively_reduce(RegLnBal, PrfLnBal,
currency(-ActualLoan)));
// This seems wrong. If we're changing something that's invariant among
// bases, why do we change it for each basis?
@@ -1535,11 +1566,11 @@ void AccountValue::TxSetBOMAV()
(0 == Year && 0 == Month)
? std::max
(term_specamt(0) + base_specamt(0)
- ,NetPmts[0] * YearsCorridorFactor
+ ,currency(round_death_benefit()(NetPmts[0] *
YearsCorridorFactor))
)
: yare_input_.InforceSpecAmtLoadBase
;
- SpecAmtLoadBase = std::min(SpecAmtLoadBase, SpecAmtLoadLimit);
+ SpecAmtLoadBase = currency(std::min<double>(SpecAmtLoadBase,
SpecAmtLoadLimit));
}
// These assignments must happen every month.
@@ -1584,8 +1615,8 @@ void AccountValue::TxSetBOMAV()
///
/// TODO ?? TAXATION !! Should 7702 or 7702A processing be done here?
/// If so, then this code may be useful:
-/// double prior_db_7702A = DB7702A;
-/// double prior_sa_7702A = ActualSpecAmt;
+/// currency prior_db_7702A = DB7702A;
+/// currency prior_sa_7702A = ActualSpecAmt;
/// toward the beginning, and:
/// Irc7702A_->UpdateBft7702A(...);
/// LMI_ASSERT(0.0 <= Dcv);
@@ -1604,7 +1635,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(currency(0),
TotalAccountValue());
DB7702A = ActualSpecAmt;
}
break;
@@ -1613,8 +1644,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(currency(0), CumPmts);
+ DB7702A = ActualSpecAmt + std::max(currency(0), CumPmts);
}
break;
case mce_mdb:
@@ -1631,9 +1662,9 @@ void AccountValue::TxSetDeathBft()
// Surrender charges are generally ignored here, but any negative
// surrender charge must be subtracted, increasing the account value.
- double cash_value_for_corridor =
+ currency cash_value_for_corridor =
TotalAccountValue()
- - std::min(0.0, SurrChg())
+ - std::min(currency(0), SurrChg())
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if it existed.
;
@@ -1645,7 +1676,7 @@ void AccountValue::TxSetDeathBft()
DBReflectingCorr = std::max
(DBIgnoringCorr
- ,YearsCorridorFactor * std::max(0.0, cash_value_for_corridor)
+ ,currency(round_death_benefit()(YearsCorridorFactor *
std::max(currency(0), cash_value_for_corridor)))
);
DBReflectingCorr = round_death_benefit()(DBReflectingCorr);
LMI_ASSERT(0.0 <= DBReflectingCorr);
@@ -1654,12 +1685,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(currency(0), SurrChg())
+ GetRefundableSalesLoad()
// + std::max(0.0, ExpRatReserve) // This would be added if
it existed.
)
@@ -1694,7 +1725,7 @@ void AccountValue::TxSetTermAmt()
return;
}
- TermDB = std::max(0.0, TermSpecAmt + DBIgnoringCorr - DBReflectingCorr);
+ TermDB = std::max(currency(0), TermSpecAmt + DBIgnoringCorr -
DBReflectingCorr);
TermDB = round_death_benefit()(TermDB);
}
@@ -1750,17 +1781,17 @@ 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(0.0, doubleize(TotalAccountValue()))
);
NAAR = std::max(0.0, round_naar()(NAAR));
// TODO ?? This doesn't work. We need to reconsider the basic transactions.
-// double naar_forceout = std::max(0.0, NAAR - MaxNAAR);
+// currency 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.
@@ -1808,7 +1839,7 @@ void AccountValue::TxSetRiderDed()
AdbCharge = 0.0;
if(yare_input_.AccidentalDeathBenefit)
{
- AdbCharge = YearsAdbRate * std::min(ActualSpecAmt, AdbLimit);
+ AdbCharge = YearsAdbRate * std::min<double>(ActualSpecAmt, AdbLimit);
AdbCharge = round_rider_charges()(AdbCharge);
}
@@ -1847,7 +1878,7 @@ void AccountValue::TxSetRiderDed()
{
case oe_waiver_times_specamt:
{
- WpCharge = YearsWpRate * std::min(ActualSpecAmt, WpLimit);
+ WpCharge = YearsWpRate * std::min<double>(ActualSpecAmt,
WpLimit);
WpCharge = round_rider_charges()(WpCharge);
DcvWpCharge = WpCharge;
}
@@ -1949,7 +1980,7 @@ void AccountValue::TxTestHoneymoonForExpiration()
// experience rating reserve would affect the cash surrender value
// but not the honeymoon value.
//
- double csv_ignoring_loan =
+ currency csv_ignoring_loan =
TotalAccountValue()
- SurrChg()
+ GetRefundableSalesLoad()
@@ -1967,7 +1998,7 @@ void AccountValue::TxTestHoneymoonForExpiration()
if(HoneymoonValue <= 0.0 || HoneymoonValue < csv_ignoring_loan)
{
HoneymoonActive = false;
- HoneymoonValue = -std::numeric_limits<double>::max();
+ HoneymoonValue = -std::numeric_limits<int>::max(); // yick
}
}
@@ -2025,7 +2056,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)
{
@@ -2199,7 +2230,7 @@ void AccountValue::TxCreditInt()
// Loaned account value must not be negative.
LMI_ASSERT(0.0 <= AVRegLn && 0.0 <= AVPrfLn);
- double z = RegLnIntCred + PrfLnIntCred + SepAcctIntCred + GenAcctIntCred;
+ currency z = RegLnIntCred + PrfLnIntCred + SepAcctIntCred + GenAcctIntCred;
YearsTotalNetIntCredited += z;
YearsTotalGrossIntCredited += z + notional_sep_acct_charge;
}
@@ -2254,26 +2285,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(12.0 * MlyDed);
}
case mce_eighteen_times_last:
{
- return 18.0 * MlyDed;
+ return currency(18.0 * MlyDed);
}
case mce_to_next_anniversary:
{
- return MlyDed * (13 - Month);
+ return currency(MlyDed * (13.0 - Month));
}
case mce_to_next_modal_pmt_date:
{
- return MlyDed * (1 + MonthsToNextModalPmtDate());
+ return currency(MlyDed * (1.0 + MonthsToNextModalPmtDate()));
}
}
throw "Unreachable--silences a compiler diagnostic.";
@@ -2301,13 +2332,13 @@ void AccountValue::SetMaxWD()
+ (AVRegLn + AVPrfLn)
- (RegLnBal + PrfLnBal)
- anticipated_deduction(MaxWDDed_)
- - std::max(0.0, SurrChg())
+ - std::max(currency(0), SurrChg())
;
if(MaxWD < MinWD)
{
- MaxWD = 0.0;
+ MaxWD = currency(0);
}
- MaxWD = std::max(0.0, MaxWD);
+ MaxWD = std::max(currency(0), MaxWD);
}
/// Take a withdrawal.
@@ -2347,7 +2378,7 @@ void AccountValue::TxTakeWD()
if(Solving)
{
- withdrawal_ullage_[Year] = std::max(0.0, RequestedWD - MaxWD);
+ withdrawal_ullage_[Year] = std::max(currency(0), RequestedWD - MaxWD);
}
if(Solving || mce_run_gen_curr_sep_full == RunBasis_)
@@ -2463,8 +2494,8 @@ void AccountValue::TxTakeWD()
// the fee based on the requested proceeds and add that
// to the partial surrender amount.
- double av = TotalAccountValue();
- double csv = av - SurrChg_[Year];
+ currency av = TotalAccountValue();
+ currency csv = av - SurrChg_[Year];
LMI_ASSERT(0.0 <= SurrChg_[Year]);
if(csv <= 0.0)
{
@@ -2478,7 +2509,7 @@ void AccountValue::TxTakeWD()
return;
}
- GrossWD = round_withdrawal()(NetWD + std::min(WDFee, NetWD * WDFeeRate));
+ GrossWD = currency(round_withdrawal()(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
@@ -2486,7 +2517,7 @@ void AccountValue::TxTakeWD()
// distinct surrender-charge layers.
double surrchg_proportion = SurrChg_[Year] / csv;
- double non_free_wd = GrossWD;
+ currency non_free_wd = GrossWD;
if(0.0 != FreeWDProportion[Year])
{
// The free partial surrender amount is determined annually,
@@ -2496,7 +2527,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(0.0, doubleize(GrossWD) - free_wd);
}
double partial_surrchg = non_free_wd * surrchg_proportion;
GrossWD += round_withdrawal()(partial_surrchg);
@@ -2521,7 +2552,7 @@ void AccountValue::TxTakeWD()
// Do you really want 'face' here rather than specamt? --Yes
if(WdDecrSpecAmtDboLvl)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0) - GrossWD);
// Min AV after WD not directly implemented.
// If WD causes AV < min AV, do we:
// reduce the WD?
@@ -2541,7 +2572,7 @@ void AccountValue::TxTakeWD()
{
if(WdDecrSpecAmtDboInc)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0) - GrossWD);
}
else
{
@@ -2553,7 +2584,7 @@ void AccountValue::TxTakeWD()
{
if(WdDecrSpecAmtDboRop)
{
- ChangeSpecAmtBy(-GrossWD);
+ ChangeSpecAmtBy(currency(0) - GrossWD);
}
else
{
@@ -2599,11 +2630,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(currency(0), SurrChg())
;
// Illustrations generally permit loans only on anniversary.
@@ -2638,7 +2669,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
;
@@ -2648,9 +2679,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);
@@ -2681,7 +2712,7 @@ void AccountValue::TxTakeLoan()
return;
}
- double max_loan_increment = MaxLoan - (AVRegLn + AVPrfLn);
+ currency max_loan_increment = MaxLoan - (AVRegLn + AVPrfLn);
// When performing a solve, let it become overloaned--otherwise
// we'd introduce a discontinuity in the function for which we
@@ -2689,12 +2720,12 @@ void AccountValue::TxTakeLoan()
if(Solving)
{
ActualLoan = RequestedLoan;
- loan_ullage_[Year] = std::max(0.0, RequestedLoan - max_loan_increment);
+ loan_ullage_[Year] = std::max(currency(0), RequestedLoan -
max_loan_increment);
}
else
{
ActualLoan = std::min(max_loan_increment, RequestedLoan);
- ActualLoan = std::max(ActualLoan, 0.0);
+ ActualLoan = std::max(ActualLoan, currency(0));
// TODO ?? Shouldn't this happen in FinalizeMonth()?
InvariantValues().NewCashLoan[Year] = ActualLoan;
}
@@ -2767,14 +2798,14 @@ void AccountValue::TxTestLapse()
// is an actual balance-sheet item that is actually held in the
// certificate.
- double lapse_test_csv =
+ currency lapse_test_csv =
TotalAccountValue()
- (RegLnBal + PrfLnBal)
// + std::max(0.0, ExpRatReserve) // This would be added if it existed.
;
if(!LapseIgnoresSurrChg)
{
- lapse_test_csv -= std::max(0.0, SurrChg());
+ lapse_test_csv -= std::max(currency(0), SurrChg());
}
lapse_test_csv = std::max(lapse_test_csv, HoneymoonValue);
diff --git a/ihs_avsolve.cpp b/ihs_avsolve.cpp
index 4e04d98..2464be5 100644
--- a/ihs_avsolve.cpp
+++ b/ihs_avsolve.cpp
@@ -49,7 +49,7 @@ namespace
{
// TODO ?? Shouldn't this be a typedef for a SolveHelper member?
// As it stands, this would seem not to be reentrant.
- void (AccountValue::*solve_set_fn)(double);
+ void (AccountValue::*solve_set_fn)(currency);
} // Unnamed namespace.
class SolveHelper
@@ -60,9 +60,10 @@ class SolveHelper
:av {a_av}
{
}
+// double operator()(currency a_CandidateValue)
double operator()(double a_CandidateValue)
{
- return av.SolveTest(a_CandidateValue);
+ return av.SolveTest(currency(a_CandidateValue));
}
};
@@ -155,7 +156,7 @@ class SolveHelper
/// "Section 7B(2) does not preclude the illustrating of premiums
/// that exceed the guideline premiums in Section 7702 of the IRC."
-double AccountValue::SolveTest(double a_CandidateValue)
+currency AccountValue::SolveTest(currency a_CandidateValue)
{
(this->*solve_set_fn)(a_CandidateValue);
@@ -173,7 +174,7 @@ double AccountValue::SolveTest(double a_CandidateValue)
,0
);
LMI_ASSERT(0 <= no_lapse_dur);
- double most_negative_csv = 0.0;
+ currency most_negative_csv(0);
if(no_lapse_dur < SolveTargetDuration_)
{
most_negative_csv = *std::min_element
@@ -184,27 +185,27 @@ double AccountValue::SolveTest(double a_CandidateValue)
// AccountValue::Solve() asserts that SolveTargetDuration_ lies
// within appropriate bounds.
- double greatest_loan_ullage = *std::max_element
+ currency greatest_loan_ullage = *std::max_element
(loan_ullage_.begin()
,loan_ullage_.begin() + SolveTargetDuration_
);
- double greatest_withdrawal_ullage = *std::max_element
+ currency greatest_withdrawal_ullage = *std::max_element
(withdrawal_ullage_.begin()
,withdrawal_ullage_.begin() + SolveTargetDuration_
);
- double greatest_ullage = std::max
+ currency greatest_ullage = std::max
(greatest_loan_ullage
,greatest_withdrawal_ullage
);
- double worst_negative = std::min
+ currency worst_negative = std::min
(most_negative_csv
- ,-greatest_ullage
+ ,currency(-greatest_ullage) // really want a unary-negation operator
);
// SolveTargetDuration_ is in origin one. That's natural for loop
// counters and iterators--it's one past the end--but indexing
// must decrement it.
- double value = VariantValues().CSVNet[SolveTargetDuration_ - 1];
+ currency value = currency(VariantValues().CSVNet[SolveTargetDuration_ -
1]);
if(mce_solve_for_target_naar == SolveTarget_)
{
value =
@@ -231,14 +232,15 @@ double AccountValue::SolveTest(double a_CandidateValue)
if(mce_solve_for_non_mec == SolveTarget_)
{
- return 0.5 - InvariantValues().IsMec;
+// return 0.5 - InvariantValues().IsMec;
+ return currency(InvariantValues().IsMec ? -1.0 : 1.0); // Backport
first.
}
return value - SolveTargetCsv_;
}
//============================================================================
-void AccountValue::SolveSetSpecAmt(double a_CandidateValue)
+void AccountValue::SolveSetSpecAmt(currency a_CandidateValue)
{
// TODO ?? Does this change the surrchg when specamt changes?
DeathBfts_->set_specamt
@@ -249,48 +251,48 @@ void AccountValue::SolveSetSpecAmt(double
a_CandidateValue)
}
//============================================================================
-void AccountValue::SolveSetEePrem(double a_CandidateValue)
+void AccountValue::SolveSetEePrem(currency a_CandidateValue)
{
Outlay_->set_ee_modal_premiums(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
}
//============================================================================
-void AccountValue::SolveSetErPrem(double a_CandidateValue)
+void AccountValue::SolveSetErPrem(currency a_CandidateValue)
{
Outlay_->set_er_modal_premiums(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
}
//============================================================================
-void AccountValue::SolveSetLoan(double a_CandidateValue)
+void AccountValue::SolveSetLoan(currency a_CandidateValue)
{
Outlay_->set_new_cash_loans(a_CandidateValue, SolveBeginYear_,
SolveEndYear_);
}
//============================================================================
-void AccountValue::SolveSetWD(double a_CandidateValue)
+void AccountValue::SolveSetWD(currency a_CandidateValue)
{
Outlay_->set_withdrawals(a_CandidateValue, SolveBeginYear_, SolveEndYear_);
}
//============================================================================
-double AccountValue::SolveGuarPremium()
+currency 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, 0, BasicValues::GetLength());
+ Outlay_->set_er_modal_premiums(currency(0), 0, BasicValues::GetLength());
bool temp_solving = Solving;
Solving = true;
SolvingForGuarPremium = true;
// Run the solve using guaranteed assumptions.
- double guar_premium = Solve
+ currency guar_premium = Solve
(mce_solve_ee_prem
,0
,BasicValues::GetLength()
,mce_solve_for_endt
- ,0.0
+ ,currency(0)
,BasicValues::GetLength()
,mce_gen_guar
,mce_sep_full
@@ -305,12 +307,12 @@ double AccountValue::SolveGuarPremium()
}
//============================================================================
-double AccountValue::Solve
+currency AccountValue::Solve
(mcenum_solve_type a_SolveType
,int a_SolveBeginYear
,int a_SolveEndYear
,mcenum_solve_target a_SolveTarget
- ,double a_SolveTargetCsv
+ ,currency a_SolveTargetCsv
,int a_SolveTargetYear
,mcenum_gen_basis a_SolveGenBasis
,mcenum_sep_basis a_SolveSepBasis
@@ -442,6 +444,6 @@ double AccountValue::Solve
// are stored now, and values are regenerated downstream.
Solving = false;
- (this->*solve_set_fn)(solution.first);
- return solution.first;
+ (this->*solve_set_fn)(currency(solution.first));
+ return currency(solution.first);
}
diff --git a/ihs_avstrtgy.cpp b/ihs_avstrtgy.cpp
index 44374fd..10cc3c0 100644
--- a/ihs_avstrtgy.cpp
+++ b/ihs_avstrtgy.cpp
@@ -56,19 +56,21 @@
///
/// 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 = currency
+ // should there be currency::operator*=(int)?
+ (
Outlay_->ee_premium_modes ()[reference_year]
* Outlay_->ee_modal_premiums()[reference_year]
+ Outlay_->er_premium_modes ()[reference_year]
* Outlay_->er_modal_premiums()[reference_year]
- ;
+ );
switch(strategy)
{
case mce_sa_input_scalar:
@@ -127,12 +129,12 @@ void AccountValue::PerformSpecAmtStrategy()
// yare_input_.SpecifiedAmount means that the inforce warning
// appears only once, because the former is overwritten but the
// latter is not.
- double const inforce_specamt = DeathBfts_->specamt().at(InforceYear);
+ currency const inforce_specamt = DeathBfts_->specamt().at(InforceYear);
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,8 +146,8 @@ 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);
+ currency z = CalculateSpecAmtFromStrategy(j, 0, explicit_value,
strategy);
+ DeathBfts_->set_specamt(currency(round_specamt()(std::max(m, z))), j,
1 + j);
if
( j == InforceYear
&& yare_input_.EffectiveDate != yare_input_.InforceAsOfDate
@@ -172,22 +174,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); // 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);
}
}
/// 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
{
@@ -258,7 +260,7 @@ double AccountValue::DoPerformPmtStrategy
}
else
{
- double sa = ActualSpecAmt + TermSpecAmt;
+ currency sa = ActualSpecAmt + TermSpecAmt;
return GetModalMinPrem(Year, a_CurrentMode, sa);
}
}
@@ -266,27 +268,27 @@ double AccountValue::DoPerformPmtStrategy
case mce_pmt_target:
{
int const target_year = TgtPremFixedAtIssue ? 0 : Year;
- double sa = base_specamt(target_year);
+ currency sa = base_specamt(target_year);
return GetModalTgtPrem(Year, a_CurrentMode, sa);
}
case mce_pmt_mep:
{
- double sa = specamt_for_7702A(0);
+ currency sa = specamt_for_7702A(0);
return GetModalPremMaxNonMec(0, a_InitialMode, sa);
}
case mce_pmt_glp:
{
- double sa = specamt_for_7702(0);
+ currency sa = specamt_for_7702(0);
return GetModalPremGLP(0, a_InitialMode, sa, sa);
}
case mce_pmt_gsp:
{
- double sa = specamt_for_7702(0);
+ currency sa = specamt_for_7702(0);
return GetModalPremGSP(0, a_InitialMode, sa, sa);
}
case mce_pmt_corridor:
{
- double sa = specamt_for_7702(0);
+ currency sa = specamt_for_7702(0);
return GetModalPremCorridor(0, a_InitialMode, sa);
}
case mce_pmt_table:
@@ -304,7 +306,7 @@ double AccountValue::DoPerformPmtStrategy
/// Set employee payment according to selected strategy.
-double AccountValue::PerformEePmtStrategy() const
+currency AccountValue::PerformEePmtStrategy() const
{
return DoPerformPmtStrategy
(mce_solve_ee_prem
@@ -318,7 +320,7 @@ double AccountValue::PerformEePmtStrategy() const
/// Set employer payment according to selected strategy.
-double AccountValue::PerformErPmtStrategy() const
+currency AccountValue::PerformErPmtStrategy() const
{
return DoPerformPmtStrategy
(mce_solve_er_prem
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index 21aa86e..9c5e518 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -600,7 +600,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);
}
@@ -613,10 +613,11 @@ double BasicValues::GetAnnualTgtPrem(int a_year, double
a_specamt) const
void BasicValues::SetPermanentInvariants()
{
- database().query_into(DB_MinIssSpecAmt , MinIssSpecAmt);
- database().query_into(DB_MinIssBaseSpecAmt , MinIssBaseSpecAmt);
- database().query_into(DB_MinRenlSpecAmt , MinRenlSpecAmt);
- database().query_into(DB_MinRenlBaseSpecAmt , MinRenlBaseSpecAmt);
+ // maybe implement query_into<currency>
+ MinIssSpecAmt = database().query<int>(DB_MinIssSpecAmt);
+ MinIssBaseSpecAmt = database().query<int>(DB_MinIssBaseSpecAmt);
+ MinRenlSpecAmt = database().query<int>(DB_MinRenlSpecAmt);
+ MinRenlBaseSpecAmt = database().query<int>(DB_MinRenlBaseSpecAmt);
database().query_into(DB_NoLapseDboLvlOnly , NoLapseDboLvlOnly);
database().query_into(DB_NoLapseUnratedOnly , NoLapseUnratedOnly);
database().query_into(DB_DboChgCanIncrSpecAmt , OptChgCanIncrSA);
@@ -630,14 +631,18 @@ void BasicValues::SetPermanentInvariants()
database().query_into(DB_TermForcedConvAge , TermForcedConvAge);
database().query_into(DB_TermForcedConvDur , TermForcedConvDur);
database().query_into(DB_ExpSpecAmtLimit , ExpPerKLimit);
+// NO! ExpPerKLimit = database().query<int>(DB_ExpSpecAmtLimit);
database().query_into(DB_MinPremType , MinPremType);
database().query_into(DB_TgtPremType , TgtPremType);
database().query_into(DB_TgtPremFixedAtIssue , TgtPremFixedAtIssue);
- database().query_into(DB_TgtPremMonthlyPolFee , TgtPremMonthlyPolFee);
+ TgtPremMonthlyPolFee = database().query<int>(DB_TgtPremMonthlyPolFee);
// Assertion: see comments on GetModalPremTgtFromTable().
LMI_ASSERT(0.0 == TgtPremMonthlyPolFee || oe_modal_table == TgtPremType);
database().query_into(DB_CurrCoiTable0Limit , CurrCoiTable0Limit);
database().query_into(DB_CurrCoiTable1Limit , CurrCoiTable1Limit);
+// NO!
+// CurrCoiTable0Limit = database().query<int>(DB_CurrCoiTable0Limit );
+// CurrCoiTable1Limit = database().query<int>(DB_CurrCoiTable1Limit );
LMI_ASSERT(0.0 <= CurrCoiTable0Limit);
LMI_ASSERT(CurrCoiTable0Limit <= CurrCoiTable1Limit);
database().query_into(DB_CoiInforceReentry , CoiInforceReentry);
@@ -653,8 +658,11 @@ void BasicValues::SetPermanentInvariants()
database().query_into(DB_AdbLimit , AdbLimit);
database().query_into(DB_WpLimit , WpLimit);
database().query_into(DB_SpecAmtLoadLimit , SpecAmtLoadLimit);
- database().query_into(DB_MinWd , MinWD);
- database().query_into(DB_WdFee , WDFee);
+// AdbLimit = database().query<int>(DB_AdbLimit );
+// WpLimit = database().query<int>(DB_WpLimit );
+// SpecAmtLoadLimit = database().query<int>(DB_SpecAmtLoadLimit);
+ MinWD = database().query<int>(DB_MinWd );
+ WDFee = database().query<int>(DB_WdFee );
database().query_into(DB_WdFeeRate , WDFeeRate);
database().query_into(DB_AllowChangeToDbo2 , AllowChangeToDBO2);
database().query_into(DB_AllowSpecAmtIncr , AllowSAIncr);
@@ -910,10 +918,10 @@ double BasicValues::GetPartMortQ(int a_year) const
/// 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)
@@ -930,10 +938,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;
@@ -956,15 +964,15 @@ 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));
+ return currency(round_max_premium()(ldbl_eps_plus_one_times(temp *
a_specamt / a_mode)));
}
/// Calculate premium using a minimum-premium ratio.
@@ -974,17 +982,19 @@ 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()
- (ldbl_eps_plus_one_times
- (
- a_specamt * MortalityRates_->MinimumPremiumRates()[0]
- / a_mode
+ return currency(
+ round_max_premium()
+ (ldbl_eps_plus_one_times
+ (
+ a_specamt * MortalityRates_->MinimumPremiumRates()[0]
+ / a_mode
+ )
)
);
}
@@ -1012,39 +1022,39 @@ 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()
+ return currency(round_max_premium()
(ldbl_eps_plus_one_times
(
- ( TgtPremMonthlyPolFee * 12
+ ( TgtPremMonthlyPolFee * 12.0
+ (a_specamt * MortalityRates_->TargetPremiumRates()[0])
)
/ a_mode
)
- );
+ ));
}
/// 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()
+ return currency(round_gross_premium()
(
a_specamt
* MortalityRates_->GroupProxyRates()[a_year]
* a_table_multiplier
/ a_mode
- );
+ ));
}
/// Calculate premium using a corridor ratio.
@@ -1053,22 +1063,22 @@ 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));
+ return currency(round_max_premium()(ldbl_eps_plus_one_times((a_specamt /
temp) / a_mode)));
}
//============================================================================
-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
@@ -1086,15 +1096,15 @@ double BasicValues::GetModalPremGLP
// term rider, dumpin
z /= a_mode;
- return round_max_premium()(ldbl_eps_plus_one_times(z));
+ return currency(round_max_premium()(ldbl_eps_plus_one_times(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
@@ -1109,7 +1119,7 @@ double BasicValues::GetModalPremGSP
// term rider, dumpin
z /= a_mode;
- return round_max_premium()(ldbl_eps_plus_one_times(z));
+ return currency(round_max_premium()(ldbl_eps_plus_one_times(z)));
}
/// Calculate a monthly-deduction discount factor on the fly.
@@ -1181,10 +1191,12 @@ double BasicValues::mly_ded_discount_factor(int year,
mcenum_mode mode) const
/// employer typically pays the approximate monthly deductions, but
/// may also be used with any UL product when such a payment pattern
/// is desired.
+///
+/// Returns a pair of deliberately unrounded doubles.
std::pair<double,double> BasicValues::approx_mly_ded
- (int year
- ,double specamt
+ (int year
+ ,currency specamt
) const
{
double mly_ded = specamt * DBDiscountRate[year];
@@ -1193,7 +1205,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)
@@ -1211,7 +1223,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];
@@ -1225,7 +1237,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:
@@ -1261,9 +1273,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])
@@ -1281,7 +1293,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.
@@ -1302,7 +1314,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.
@@ -1316,7 +1328,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:
@@ -1338,10 +1350,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);
@@ -1349,16 +1361,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);
@@ -1367,8 +1379,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))
);
}
@@ -1389,10 +1401,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;
@@ -1410,14 +1422,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);
@@ -1444,12 +1456,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)
{
@@ -1458,11 +1470,11 @@ double BasicValues::GetModalSpecAmtMax(double
annualized_pmt) const
case oe_modal_nonmec:
return GetModalSpecAmtMinNonMec(annualized_pmt);
case oe_modal_table:
- return round_min_specamt()
+ return currency(round_min_specamt()
(
annualized_pmt
/ MortalityRates_->MinimumPremiumRates()[0]
- );
+ ));
}
throw "Unreachable--silences a compiler diagnostic.";
}
@@ -1473,7 +1485,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)
{
@@ -1482,11 +1494,11 @@ double BasicValues::GetModalSpecAmtTgt(double
annualized_pmt) const
case oe_modal_nonmec:
return GetModalSpecAmtMinNonMec(annualized_pmt);
case oe_modal_table:
- return round_min_specamt()
+ return currency(round_min_specamt()
(
- (annualized_pmt - TgtPremMonthlyPolFee * 12)
+ (annualized_pmt - TgtPremMonthlyPolFee * 12.0)
/ MortalityRates_->TargetPremiumRates()[0]
- );
+ ));
}
throw "Unreachable--silences a compiler diagnostic.";
}
@@ -1497,21 +1509,21 @@ 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]);
+ 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);
}
//============================================================================
-double BasicValues::GetModalSpecAmtGSP(double annualized_pmt) const
+currency BasicValues::GetModalSpecAmtGSP(currency annualized_pmt) const
{
return gpt_specamt::CalculateGSPSpecAmt(*this, 0, annualized_pmt);
}
@@ -1553,12 +1565,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.
@@ -1568,7 +1580,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]
@@ -1579,7 +1591,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
@@ -1589,13 +1601,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);
}
/// 'Unusual' banding is one particular approach we needed to model.
@@ -1606,7 +1618,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 a174184..103d149 100644
--- a/ledger_invariant_init.cpp
+++ b/ledger_invariant_init.cpp
@@ -27,6 +27,7 @@
#include "assert_lmi.hpp"
#include "basic_values.hpp"
#include "contains.hpp"
+#include "currency.hpp"
#include "database.hpp"
#include "dbnames.hpp"
#include "death_benefits.hpp"
@@ -103,7 +104,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;
@@ -113,7 +114,7 @@ void LedgerInvariant::Init(BasicValues const* b)
{
TermSpecAmt .assign(Length, 0.0);
}
- SpecAmt = b->DeathBfts_->specamt();
+ SpecAmt = doubleize(b->DeathBfts_->specamt());
// Forborne vectors.
@@ -722,7 +723,7 @@ void LedgerInvariant::ReInit(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;
@@ -732,7 +733,7 @@ void LedgerInvariant::ReInit(BasicValues const* b)
{
TermSpecAmt .assign(Length, 0.0);
}
- SpecAmt = b->DeathBfts_->specamt();
+ SpecAmt = doubleize(b->DeathBfts_->specamt());
InitBaseSpecAmt = b->DeathBfts_->specamt()[0];
InitTermSpecAmt = TermSpecAmt[0];
diff --git a/outlay.cpp b/outlay.cpp
index 9c1e422..9730287 100644
--- a/outlay.cpp
+++ b/outlay.cpp
@@ -34,56 +34,60 @@ modal_outlay::modal_outlay
,round_to<double> const& round_withdrawal
,round_to<double> const& round_loan
)
- :round_gross_premium_ {round_gross_premium}
- ,round_withdrawal_ {round_withdrawal }
- ,round_loan_ {round_loan }
- ,dumpin_ {round_gross_premium_(yi.Dumpin)
}
- ,external_1035_amount_
{round_gross_premium_(yi.External1035ExchangeAmount)}
- ,internal_1035_amount_
{round_gross_premium_(yi.Internal1035ExchangeAmount)}
- ,ee_modal_premiums_ {round_gross_premium_(yi.Payment)
}
+ :round_gross_premium_ { round_gross_premium}
+ ,round_withdrawal_ { round_withdrawal }
+ ,round_loan_ { round_loan }
+ ,dumpin_ { (round_gross_premium_(yi.Dumpin)
)}
+ ,external_1035_amount_ {
(round_gross_premium_(yi.External1035ExchangeAmount))}
+ ,internal_1035_amount_ {
(round_gross_premium_(yi.Internal1035ExchangeAmount))}
+ ,ee_modal_premiums_ {currencyize(round_gross_premium_(yi.Payment)
)}
,ee_premium_modes_ { yi.PaymentMode
}
- ,er_modal_premiums_ {round_gross_premium_(yi.CorporationPayment)
}
+ ,er_modal_premiums_
{currencyize(round_gross_premium_(yi.CorporationPayment) )}
,er_premium_modes_ { yi.CorporationPaymentMode
}
- ,withdrawals_ {round_withdrawal_ (yi.Withdrawal)
}
- ,new_cash_loans_ {round_loan_ (yi.NewLoan)
}
+ ,withdrawals_ {currencyize(round_withdrawal_ (yi.Withdrawal)
)}
+ ,new_cash_loans_ {currencyize(round_loan_ (yi.NewLoan)
)}
{
}
-void modal_outlay::set_external_1035_amount(double z)
+void modal_outlay::set_external_1035_amount(currency z)
{
external_1035_amount_ = round_gross_premium_(z);
}
-void modal_outlay::set_internal_1035_amount(double z)
+void modal_outlay::set_internal_1035_amount(currency z)
{
internal_1035_amount_ = round_gross_premium_(z);
}
-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)
{
z = round_gross_premium_(z);
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)
{
z = round_gross_premium_(z);
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_ = round_gross_premium_(z);
+// er_modal_premiums_ = round_gross_premium_(z);
+ // Temporarily disregard rounding, which remains necessary: the
+ // new value is currency, so it's rounded to cents somehow, but
+ // premiums could be rounded differently--say, up to dollars.
+ er_modal_premiums_ = z; // CURRENCY
}
-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)
{
z = round_withdrawal_(z);
std::fill_n(withdrawals_.begin() + from_year, to_year - from_year, 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)
{
z = round_loan_(z);
std::fill_n(new_cash_loans_.begin() + from_year, to_year - from_year, z);
diff --git a/solve.cpp b/solve.cpp
index f56d475..ec601da 100644
--- a/solve.cpp
+++ b/solve.cpp
@@ -61,7 +61,7 @@ namespace
//============================================================================
// This function isn't static and isn't in an unnamed namespace because
// that would make it difficult to grant it friendship.
-double SolveTest()
+currency SolveTest()
{
// Separate-account basis hardcoded because separate account not supported.
mcenum_run_basis temp;
@@ -79,12 +79,12 @@ double SolveTest()
// CSV at target duration
// lowest negative CSV through target duration
// amount of loan in excess of maximum loan through target duration
- double Negative = 0.0;
+ currency Negative(0);
// IHS !! Start counting only at end of no-lapse period--lmi does that
already.
for(int j = 0; j < ThatSolveTgtYear; ++j)
{
- Negative = std::min
+ Negative = std::min<double>
(Negative
,ConstThat->VariantValues().CSVNet[j]
// Ideally, it'd be this:
@@ -93,12 +93,12 @@ double SolveTest()
);
}
- double z = ConstThat->VariantValues().CSVNet[ThatSolveTgtYear - 1];
+ currency z = currency(ConstThat->VariantValues().CSVNet[ThatSolveTgtYear -
1]);
if(Negative < 0.0)
z = std::min(z, Negative);
// IHS !! If SolveTgtYr within no-lapse period...see lmi.
- double y = 0.0;
+ currency y(0);
switch(ThatSolveTarget)
{
case mce_solve_for_endt:
@@ -151,38 +151,42 @@ double SolveTest()
//============================================================================
inline static double SolveSpecAmt(double CandidateValue)
+//inline static double SolveSpecAmt(currency 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)
+//inline static double SolvePrem(currency 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)
+//inline static double SolveLoan(currency 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)
+//inline static double SolveWD(currency 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 +194,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 +204,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 +214,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,16 +224,16 @@ void AccountValue::SolveSetWDs
//============================================================================
void AccountValue::SolveSetLoanThenWD
- (double // Amt
- ,int // ThatSolveBegYear
- ,int // ThatSolveEndYear
+ (currency // Amt
+ ,int // ThatSolveBegYear
+ ,int // ThatSolveEndYear
)
{
// IHS !! Implemented in lmi.
}
//============================================================================
-double AccountValue::Solve()
+currency AccountValue::Solve()
{
That = this;
ThatSolveTargetValue = yare_input_.SolveTargetValue;
@@ -254,6 +258,7 @@ double AccountValue::Solve()
}
double(*SolveFn)(double) = nullptr;
+// double(*SolveFn)(currency) = nullptr;
double LowerBound = 0.0;
double UpperBound = 0.0;
root_bias Bias = bias_higher;
@@ -346,7 +351,7 @@ double AccountValue::Solve()
// generate or analyze account values. This global variable is a
// kludge, but so is 'That'; a function object is wanted instead.
only_set_values = !Solving;
- double actual_solution = Solution.first;
+ currency actual_solution = currency(Solution.first);
SolveFn(actual_solution);
return actual_solution;
- [lmi-commits] [lmi] valyuta/002 fb82290 63/65: Use maximal 64-bit rather than 32-bit integer value, (continued)
- [lmi-commits] [lmi] valyuta/002 fb82290 63/65: Use maximal 64-bit rather than 32-bit integer value, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 8fd00bb 34/65: rework operator*(), Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 bf1943b 03/65: reformat, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 7481c2b 09/65: discard--throwaway assertions, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 998fdf5 35/65: operator/(), Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 c105c78 29/65: Currency: implement binary operator-, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 9cf4517 46/65: c(), Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 9da6616 55/65: Round explicitly in a few more cases, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 0e8c429 12/65: rounding, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 794727e 22/65: Round when converting from double to currency, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 d137177 02/65: Currency,
Greg Chicares <=
- [lmi-commits] [lmi] valyuta/002 ba6a8d1 31/65: implement currency comparisons as free functions, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 28a5e54 39/65: make data_type public, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 e9f5447 30/65: round currency to currency? huh?, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 90773f3 47/65: currency() rather than currency(0.0), Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 5f00c5a 43/65: Add a commented-out idea, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 2dabfcf 62/65: Try to make something a little less arcane, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 9433c8e 65/65: Discuss timing, and philosophical and political questions, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 9bee994 19/65: use currency type, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 0bf83a4 18/65: augment, Greg Chicares, 2020/09/16
- [lmi-commits] [lmi] valyuta/002 615a24c 48/65: round.c(), Greg Chicares, 2020/09/16