lmi-commits
[Top][All Lists]
Advanced

[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;



reply via email to

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