lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 72a51b5 4/5: Improve physical separation of c


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 72a51b5 4/5: Improve physical separation of concerns
Date: Fri, 5 Oct 2018 20:32:47 -0400 (EDT)

branch: master
commit 72a51b5296b1b29112448ec1f9ae8ca2053ececb
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Improve physical separation of concerns
    
    Split ledger_[in]variant implementations into two parts, isolating
    initialization that uses class BasicValues into standalone TUs.
---
 Makefile.am               |   2 +
 ledger_invariant.cpp      | 507 +-------------------------------------------
 ledger_invariant_init.cpp | 530 ++++++++++++++++++++++++++++++++++++++++++++++
 ledger_variant.cpp        |  99 +--------
 ledger_variant_init.cpp   | 118 +++++++++++
 objects.make              |   2 +
 6 files changed, 655 insertions(+), 603 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index bd2fcdb..0d74f88 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -341,10 +341,12 @@ liblmi_common_sources = \
     ledger_base.cpp \
     ledger_evaluator.cpp \
     ledger_invariant.cpp \
+    ledger_invariant_init.cpp \
     ledger_pdf.cpp \
     ledger_pdf_generator.cpp \
     ledger_text_formats.cpp \
     ledger_variant.cpp \
+    ledger_variant_init.cpp \
     ledger_xml_io.cpp \
     ledger_xsl.cpp \
     ledgervalues.cpp \
diff --git a/ledger_invariant.cpp b/ledger_invariant.cpp
index 4de177a..c20ba0e 100644
--- a/ledger_invariant.cpp
+++ b/ledger_invariant.cpp
@@ -23,33 +23,17 @@
 
 #include "ledger_invariant.hpp"
 
-#include "alert.hpp"
 #include "assert_lmi.hpp"
-#include "basic_values.hpp"
 #include "bourn_cast.hpp"
 #include "contains.hpp"
 #include "crc32.hpp"
-#include "database.hpp"
-#include "dbnames.hpp"
-#include "death_benefits.hpp"
 #include "financial.hpp"                // for CalculateIrrs()
-#include "fund_data.hpp"
-#include "input.hpp"
-#include "interest_rates.hpp"
 #include "ledger.hpp"                   // for CalculateIrrs()
 #include "ledger_variant.hpp"           // for CalculateIrrs()
-#include "loads.hpp"
 #include "mc_enum_aux.hpp"              // mc_e_vector_to_string_vector()
-#include "mc_enum_types_aux.hpp"        // mc_str()
-#include "miscellany.hpp"               // each_equal()
-#include "outlay.hpp"
-#include "premium_tax.hpp"
-#include "product_data.hpp"
-#include "ssize_lmi.hpp"
-
-#include <algorithm>
+
+#include <algorithm>                    // max(), min()
 #include <ostream>
-#include <stdexcept>
 
 //============================================================================
 LedgerInvariant::LedgerInvariant(int len)
@@ -461,493 +445,6 @@ void LedgerInvariant::Init()
 }
 
 //============================================================================
-void LedgerInvariant::Init(BasicValues const* b)
-{
-    // Zero-initialize almost everything.
-    Init();
-
-    irr_precision_ = b->round_irr().decimals();
-
-// TODO ?? These names are confusing. EePmt and ErPmt are *input* values.
-// If they're entered as $1000 for all years, then they have that value
-// every year, even after lapse. Variables whose names end in -'GrossPmt'
-// hold the results of transaction processing, e.g. $0 after lapse.
-// EePmt and ErPmt are used e.g. in premium-strategy calculations.
-
-    EePmt           = b->Outlay_->ee_modal_premiums();
-    ErPmt           = b->Outlay_->er_modal_premiums();
-//  TgtPrem         =
-//  GrossPmt        =
-//  EeGrossPmt      =
-//  ErGrossPmt      =
-    // These must be set dynamically because they may be changed,
-    // e.g. to respect guideline limits.
-//    External1035Amount;
-//    Internal1035Amount;
-//    Dumpin               =
-
-    InforceUnloanedAV =
-          b->yare_input_.InforceGeneralAccountValue
-        + b->yare_input_.InforceSeparateAccountValue
-        ;
-    InforceTaxBasis      = b->yare_input_.InforceTaxBasis           ;
-
-    // Certain data members, including but almost certainly not
-    // limited to these, should not be initialized to any non-zero
-    // value here. Actual values are inserted in account-value
-    // processing, subject to various restrictions that often cause
-    // them to differ from input values. Notably, values need to be
-    // zero after lapse.
-//    NetWD                 =
-//    NewCashLoan           =
-//    GptForceout           =
-//    NaarForceout          =
-//    ModalMinimumPremium   =
-//    EeModalMinimumPremium =
-//    ErModalMinimumPremium =
-//    ProducerCompensation  =
-
-    HasSupplSpecAmt = false;
-    if(b->yare_input_.TermRider)
-        {
-        TermSpecAmt     .assign(Length, b->yare_input_.TermRiderAmount);
-        }
-    else if(b->Database_->Query(DB_TermIsNotRider))
-        {
-        TermSpecAmt      = b->DeathBfts_->supplamt();
-        if(!each_equal(TermSpecAmt, 0.0))
-            {
-            HasSupplSpecAmt = true;
-            }
-        }
-    else
-        {
-        TermSpecAmt     .assign(Length, 0.0);
-        }
-    SpecAmt         = b->DeathBfts_->specamt();
-    for(int j = 0; j < Length; ++j)
-        {
-        EeMode[j] = b->Outlay_->ee_premium_modes()[j];
-        ErMode[j] = b->Outlay_->er_premium_modes()[j];
-        DBOpt [j] = b->DeathBfts_->dbopt()[j];
-        }
-
-    IndvTaxBracket       = b->yare_input_.TaxBracket                ;
-    CorpTaxBracket       = b->yare_input_.CorporationTaxBracket     ;
-    Salary               = b->yare_input_.ProjectedSalary           ;
-    AnnualFlatExtra      = b->yare_input_.FlatExtra                 ;
-    HoneymoonValueSpread = b->yare_input_.HoneymoonValueSpread      ;
-    AddonMonthlyFee      = b->yare_input_.ExtraMonthlyCustodialFee  ;
-    AddonCompOnAssets    = b->yare_input_.ExtraCompensationOnAssets ;
-    AddonCompOnPremium   = b->yare_input_.ExtraCompensationOnPremium;
-    CorridorFactor       = b->GetCorridorFactor();
-    AnnLoanDueRate       = b->InterestRates_->RegLnDueRate
-        (mce_gen_curr
-        ,mce_annual_rate
-        );
-    InitAnnLoanDueRate   = AnnLoanDueRate[0];
-    CurrMandE            = b->InterestRates_->MAndERate(mce_gen_curr);
-    TotalIMF             = b->InterestRates_->InvestmentManagementFee();
-    RefundableSalesLoad  = b->Loads_->refundable_sales_load_proportion();
-
-    std::vector<double> coimult;
-    b->Database_->Query(coimult, DB_CurrCoiMultiplier);
-    CurrentCoiMultiplier =
-          coimult                            [b->yare_input_.InforceYear]
-        * b->yare_input_.CurrentCoiMultiplier[b->yare_input_.InforceYear]
-        * b->yare_input_.CountryCoiMultiplier
-        ;
-
-    CountryIso3166Abbrev = mc_str(b->yare_input_.Country);
-    Comments             = b->yare_input_.Comments;
-
-    FundNumbers           .resize(0);
-    FundNames             .resize(0);
-    FundAllocs            .resize(0);
-    FundAllocations       .resize(0);
-
-    // The antediluvian branch has a null FundData_ object.
-    int number_of_funds(0);
-    if(b->FundData_)
-        {
-        number_of_funds = b->FundData_->GetNumberOfFunds();
-        }
-
-//    enum{NumberOfFunds = 30}; // DEPRECATED
-    int const NumberOfFunds = 30; // DEPRECATED
-    int expected_number_of_funds = std::max(number_of_funds, NumberOfFunds);
-    std::vector<double> v(b->yare_input_.FundAllocations);
-    if(lmi::ssize(v) < expected_number_of_funds)
-        {
-        v.insert(v.end(), expected_number_of_funds - v.size(), 0.0);
-        }
-
-    for(int j = 0; j < number_of_funds; ++j)
-        {
-        FundNumbers.push_back(j);
-        FundNames.push_back(b->FundData_->GetFundInfo(j).LongName());
-
-        // TODO ?? InputParms::NumberOfFunds is defectively hardcocded
-        // as thirty as this is written, but we need to support a product
-        // with more than thirty funds. The input routines respect that
-        // hardcoded limit and are difficult to change, so funds after
-        // the thirtieth cannot be selected individually; but if the
-        // rule 'equal initial fund allocations' is chosen instead of
-        // specifying individual allocations, then the average fund fee
-        // is calculated reflecting all funds, even past the thirtieth:
-        // thus, calculations are correct for any input, and the defect
-        // in the program itself is just that some legitimate inputs are
-        // not allowed, though the output spreadsheet has its own
-        // hardcoded limit (due to space), which is a separate defect.
-        // Here we pass a zero allocation to the output spreadsheet for
-        // all funds past the thirtieth, which is correct because no
-        // nonzero allocation can be selected. That's correct even if
-        // the 'equal initial allocations' rule is chosen, in which
-        // case the allocations are not explicitly shown, but are instead
-        // stated in words to be equal--because the spreadsheet layout
-        // otherwise shows allocations as integer percentages, and
-        // something like '.3333333...' would overflow the space available.
-        //
-        // As of 2008, most of the foregoing is no longer applicable,
-        // except for the hardcoded limit, which is copied above.
-        FundAllocs     .push_back(static_cast<int>(v[j]));
-        FundAllocations.push_back(0.01 * v[j]);
-        }
-
-    GenAcctAllocation = 1.0 - premium_allocation_to_sepacct(b->yare_input_);
-
-    SplitFundAllocation =
-            (0.0 != GenAcctAllocation && 1.0 != GenAcctAllocation)
-        ||
-            (  0.0 != b->yare_input_.InforceGeneralAccountValue
-            && 0.0 != b->yare_input_.InforceSeparateAccountValue
-            )
-        ;
-
-    NoLapseAlwaysActive     = b->Database_->Query(DB_NoLapseAlwaysActive);
-    NoLapseMinDur           = b->Database_->Query(DB_NoLapseMinDur);
-    NoLapseMinAge           = b->Database_->Query(DB_NoLapseMinAge);
-    Has1035ExchCharge       = b->Database_->Query(DB_Has1035ExchCharge);
-
-    // SOMEDAY !! Things indexed with '[0]' should probably use inforce year 
instead.
-    InitBaseSpecAmt         = b->DeathBfts_->specamt()[0];
-    InitTermSpecAmt         = TermSpecAmt[0];
-    ChildRiderAmount        = b->yare_input_.ChildRiderAmount;
-    SpouseRiderAmount       = b->yare_input_.SpouseRiderAmount;
-
-//  InitPrem                = 0;
-//  GuarPrem                = 0;
-//  InitSevenPayPrem        =
-//  InitTgtPrem             =
-//  InitMinPrem             =
-//  ListBillPremium         =
-//  EeListBillPremium       =
-//  ErListBillPremium       =
-//  ModalMinimumDumpin      =
-
-    MaleProportion          = b->yare_input_.MaleProportion;
-    NonsmokerProportion     = b->yare_input_.NonsmokerProportion;
-    PartMortTableMult       = b->yare_input_.PartialMortalityMultiplier;
-
-    // Assert this because the illustration currently prints a scalar
-    // guaranteed max, assuming that it's the same for all years.
-    std::vector<double> const& guar_m_and_e_rate = b->InterestRates_->MAndERate
-        (mce_gen_guar
-        );
-    LMI_ASSERT(each_equal(guar_m_and_e_rate, guar_m_and_e_rate.front()));
-    GuarMaxMandE            = guar_m_and_e_rate[0];
-    InitDacTaxRate          = 
b->Loads_->dac_tax_load()[b->yare_input_.InforceYear];
-    InitPremTaxRate         = b->PremiumTax_->maximum_load_rate();
-//  GenderDistinct          = 0;
-    GenderBlended           = b->yare_input_.BlendGender;
-//  SmokerDistinct          = 0;
-    SmokerBlended           = b->yare_input_.BlendSmoking;
-
-    SubstdTable             = b->yare_input_.SubstandardTable;
-
-    Age                     = b->yare_input_.IssueAge;
-    RetAge                  = b->yare_input_.RetirementAge;
-    EndtAge                 = b->yare_input_.IssueAge + b->GetLength();
-    GroupIndivSelection     = b->Database_->Query(DB_GroupIndivSelection);
-    UseExperienceRating     = b->yare_input_.UseExperienceRating;
-    UsePartialMort          = b->yare_input_.UsePartialMortality;
-    AvgFund                 = b->yare_input_.UseAverageOfAllFunds;
-    CustomFund              = b->yare_input_.OverrideFundManagementFee;
-
-    HasWP                   = b->yare_input_.WaiverOfPremiumBenefit;
-    HasADD                  = b->yare_input_.AccidentalDeathBenefit;
-    HasTerm                 = b->yare_input_.TermRider;
-    HasChildRider           = b->yare_input_.ChildRider;
-    HasSpouseRider          = b->yare_input_.SpouseRider;
-    SpouseIssueAge          = b->yare_input_.SpouseIssueAge;
-
-    HasHoneymoon            = b->yare_input_.HoneymoonEndorsement;
-    PostHoneymoonSpread     = b->yare_input_.PostHoneymoonSpread;
-    SplitMinPrem            = b->Database_->Query(DB_SplitMinPrem);
-    AllowDbo3               = b->Database_->Query(DB_AllowDbo3);
-
-    // These are reassigned below based on product data if available.
-    std::string dbo_name_option1 = mc_str(mce_option1);
-    std::string dbo_name_option2 = mc_str(mce_option2);
-    std::string dbo_name_rop     = mc_str(mce_rop    );
-    std::string dbo_name_mdb     = mc_str(mce_mdb    ); // DBO3 !! reconsider
-
-    // The antediluvian branch has a null ProductData_ object.
-    if(b->ProductData_)
-        {
-        product_data const& p = *b->ProductData_;
-        // Accommodate one alternative policy-form name.
-        // DATABASE !! It would be much better, of course, to let all
-        // strings in class product_data vary across the same axes as
-        // database_entity objects.
-        bool alt_form = b->Database_->Query(DB_UsePolicyFormAlt);
-        dbo_name_option1               = p.datum("DboNameLevel"                
   );
-        dbo_name_option2               = p.datum("DboNameIncreasing"           
   );
-        dbo_name_rop                   = p.datum("DboNameReturnOfPremium"      
   );
-//      dbo_name_mdb                   = // DBO3 !! reconsider
-        PolicyForm = p.datum(alt_form ? "PolicyFormAlternative" : 
"PolicyForm");
-        PolicyMktgName                 = p.datum("PolicyMktgName"              
   );
-        PolicyLegalName                = p.datum("PolicyLegalName"             
   );
-        InsCoShortName                 = p.datum("InsCoShortName"              
   );
-        InsCoName                      = p.datum("InsCoName"                   
   );
-        InsCoAddr                      = p.datum("InsCoAddr"                   
   );
-        InsCoStreet                    = p.datum("InsCoStreet"                 
   );
-        InsCoPhone                     = p.datum("InsCoPhone"                  
   );
-        MainUnderwriter                = p.datum("MainUnderwriter"             
   );
-        MainUnderwriterAddress         = p.datum("MainUnderwriterAddress"      
   );
-        CoUnderwriter                  = p.datum("CoUnderwriter"               
   );
-        CoUnderwriterAddress           = p.datum("CoUnderwriterAddress"        
   );
-
-        AvName                         = p.datum("AvName"                      
   );
-        CsvName                        = p.datum("CsvName"                     
   );
-        CsvHeaderName                  = p.datum("CsvHeaderName"               
   );
-        NoLapseProvisionName           = p.datum("NoLapseProvisionName"        
   );
-        ContractName                   = p.datum("ContractName"                
   );
-
-        AccountValueFootnote           = p.datum("AccountValueFootnote"        
   );
-        AttainedAgeFootnote            = p.datum("AttainedAgeFootnote"         
   );
-        CashSurrValueFootnote          = p.datum("CashSurrValueFootnote"       
   );
-        DeathBenefitFootnote           = p.datum("DeathBenefitFootnote"        
   );
-        InitialPremiumFootnote         = p.datum("InitialPremiumFootnote"      
   );
-        NetPremiumFootnote             = p.datum("NetPremiumFootnote"          
   );
-        GrossPremiumFootnote           = p.datum("GrossPremiumFootnote"        
   );
-        OutlayFootnote                 = p.datum("OutlayFootnote"              
   );
-        PolicyYearFootnote             = p.datum("PolicyYearFootnote"          
   );
-
-        ADDTerseName                   = p.datum("ADDTerseName"                
   );
-        InsurabilityTerseName          = p.datum("InsurabilityTerseName"       
   );
-        ChildTerseName                 = p.datum("ChildTerseName"              
   );
-        SpouseTerseName                = p.datum("SpouseTerseName"             
   );
-        TermTerseName                  = p.datum("TermTerseName"               
   );
-        WaiverTerseName                = p.datum("WaiverTerseName"             
   );
-        AccelBftRiderTerseName         = p.datum("AccelBftRiderTerseName"      
   );
-        OverloanRiderTerseName         = p.datum("OverloanRiderTerseName"      
   );
-
-        ADDFootnote                    = p.datum("ADDFootnote"                 
   );
-        ChildFootnote                  = p.datum("ChildFootnote"               
   );
-        SpouseFootnote                 = p.datum("SpouseFootnote"              
   );
-        TermFootnote                   = p.datum("TermFootnote"                
   );
-        WaiverFootnote                 = p.datum("WaiverFootnote"              
   );
-        AccelBftRiderFootnote          = p.datum("AccelBftRiderFootnote"       
   );
-        OverloanRiderFootnote          = p.datum("OverloanRiderFootnote"       
   );
-
-        GroupQuoteShortProductName     = p.datum("GroupQuoteShortProductName"  
   );
-        GroupQuoteIsNotAnOffer         = p.datum("GroupQuoteIsNotAnOffer"      
   );
-        GroupQuoteRidersFooter         = p.datum("GroupQuoteRidersFooter"      
   );
-        GroupQuotePolicyFormId         = p.datum("GroupQuotePolicyFormId"      
   );
-        GroupQuoteStateVariations      = p.datum("GroupQuoteStateVariations"   
   );
-        GroupQuoteProspectus           = p.datum("GroupQuoteProspectus"        
   );
-        GroupQuoteUnderwriter          = p.datum("GroupQuoteUnderwriter"       
   );
-        GroupQuoteBrokerDealer         = p.datum("GroupQuoteBrokerDealer"      
   );
-        GroupQuoteRubricMandatory      = p.datum("GroupQuoteRubricMandatory"   
   );
-        GroupQuoteRubricVoluntary      = p.datum("GroupQuoteRubricVoluntary"   
   );
-        GroupQuoteRubricFusion         = p.datum("GroupQuoteRubricFusion"      
   );
-        GroupQuoteFooterMandatory      = p.datum("GroupQuoteFooterMandatory"   
   );
-        GroupQuoteFooterVoluntary      = p.datum("GroupQuoteFooterVoluntary"   
   );
-        GroupQuoteFooterFusion         = p.datum("GroupQuoteFooterFusion"      
   );
-
-        MinimumPremiumFootnote         = p.datum("MinimumPremiumFootnote"      
   );
-        PremAllocationFootnote         = p.datum("PremAllocationFootnote"      
   );
-
-        InterestDisclaimer             = p.datum("InterestDisclaimer"          
   );
-        GuarMortalityFootnote          = p.datum("GuarMortalityFootnote"       
   );
-        ProductDescription             = p.datum("ProductDescription"          
   );
-        StableValueFootnote            = p.datum("StableValueFootnote"         
   );
-        NoVanishPremiumFootnote        = p.datum("NoVanishPremiumFootnote"     
   );
-        RejectPremiumFootnote          = p.datum("RejectPremiumFootnote"       
   );
-        ExpRatingFootnote              = p.datum("ExpRatingFootnote"           
   );
-        MortalityBlendFootnote         = p.datum("MortalityBlendFootnote"      
   );
-        HypotheticalRatesFootnote      = p.datum("HypotheticalRatesFootnote"   
   );
-        SalesLoadRefundFootnote        = p.datum("SalesLoadRefundFootnote"     
   );
-        NoLapseFootnote                = p.datum("NoLapseFootnote"             
   );
-        MarketValueAdjFootnote         = p.datum("MarketValueAdjFootnote"      
   );
-        ExchangeChargeFootnote0        = p.datum("ExchangeChargeFootnote0"     
   );
-        CurrentValuesFootnote          = p.datum("CurrentValuesFootnote"       
   );
-        DBOption1Footnote              = p.datum("DBOption1Footnote"           
   );
-        DBOption2Footnote              = p.datum("DBOption2Footnote"           
   );
-        ExpRatRiskChargeFootnote       = p.datum("ExpRatRiskChargeFootnote"    
   );
-        ExchangeChargeFootnote1        = p.datum("ExchangeChargeFootnote1"     
   );
-        FlexiblePremiumFootnote        = p.datum("FlexiblePremiumFootnote"     
   );
-        GuaranteedValuesFootnote       = p.datum("GuaranteedValuesFootnote"    
   );
-        CreditingRateFootnote          = p.datum("CreditingRateFootnote"       
   );
-        GrossRateFootnote              = p.datum("GrossRateFootnote"           
   );
-        NetRateFootnote                = p.datum("NetRateFootnote"             
   );
-        MecFootnote                    = p.datum("MecFootnote"                 
   );
-        GptFootnote                    = p.datum("GptFootnote"                 
   );
-        MidpointValuesFootnote         = p.datum("MidpointValuesFootnote"      
   );
-        SinglePremiumFootnote          = p.datum("SinglePremiumFootnote"       
   );
-        MonthlyChargesFootnote         = p.datum("MonthlyChargesFootnote"      
   );
-        UltCreditingRateFootnote       = p.datum("UltCreditingRateFootnote"    
   );
-        MaxNaarFootnote                = p.datum("MaxNaarFootnote"             
   );
-        PremTaxSurrChgFootnote         = p.datum("PremTaxSurrChgFootnote"      
   );
-        PolicyFeeFootnote              = p.datum("PolicyFeeFootnote"           
   );
-        AssetChargeFootnote            = p.datum("AssetChargeFootnote"         
   );
-        InvestmentIncomeFootnote       = p.datum("InvestmentIncomeFootnote"    
   );
-        IrrDbFootnote                  = p.datum("IrrDbFootnote"               
   );
-        IrrCsvFootnote                 = p.datum("IrrCsvFootnote"              
   );
-        MortalityChargesFootnote       = p.datum("MortalityChargesFootnote"    
   );
-        LoanAndWithdrawalFootnote      = p.datum("LoanAndWithdrawalFootnote"   
   );
-        LoanFootnote                   = p.datum("LoanFootnote"                
   );
-        ImprimaturPresale              = p.datum("ImprimaturPresale"           
   );
-        ImprimaturPresaleComposite     = p.datum("ImprimaturPresaleComposite"  
   );
-        ImprimaturInforce              = p.datum("ImprimaturInforce"           
   );
-        ImprimaturInforceComposite     = p.datum("ImprimaturInforceComposite"  
   );
-        StateMarketingImprimatur       = p.datum("StateMarketingImprimatur"    
   );
-        InforceNonGuaranteedFootnote0  = 
p.datum("InforceNonGuaranteedFootnote0"  );
-        InforceNonGuaranteedFootnote1  = 
p.datum("InforceNonGuaranteedFootnote1"  );
-        InforceNonGuaranteedFootnote2  = 
p.datum("InforceNonGuaranteedFootnote2"  );
-        InforceNonGuaranteedFootnote3  = 
p.datum("InforceNonGuaranteedFootnote3"  );
-        NonGuaranteedFootnote          = p.datum("NonGuaranteedFootnote"       
   );
-        MonthlyChargesPaymentFootnote  = 
p.datum("MonthlyChargesPaymentFootnote"  );
-        SurrenderFootnote              = p.datum("SurrenderFootnote"           
   );
-        PortabilityFootnote            = p.datum("PortabilityFootnote"         
   );
-        FundRateFootnote               = p.datum("FundRateFootnote"            
   );
-        FundRateFootnote0              = p.datum("FundRateFootnote0"           
   );
-        FundRateFootnote1              = p.datum("FundRateFootnote1"           
   );
-        IssuingCompanyFootnote         = p.datum("IssuingCompanyFootnote"      
   );
-        SubsidiaryFootnote             = p.datum("SubsidiaryFootnote"          
   );
-        PlacementAgentFootnote         = p.datum("PlacementAgentFootnote"      
   );
-        MarketingNameFootnote          = p.datum("MarketingNameFootnote"       
   );
-        }
-
-    ProductName             = b->yare_input_.ProductName;
-    ProducerName            = b->yare_input_.AgentName;
-
-    std::string agent_city     = b->yare_input_.AgentCity;
-    std::string agent_state    = mc_str(b->yare_input_.AgentState);
-    std::string agent_zip_code = b->yare_input_.AgentZipCode;
-    std::string agent_city_etc(agent_city + ", " + agent_state);
-    if(!agent_zip_code.empty())
-        {
-        agent_city_etc += " ";
-        }
-    agent_city_etc += agent_zip_code;
-
-    ProducerStreet          = b->yare_input_.AgentAddress;
-    ProducerCity            = agent_city_etc;
-    CorpName                = b->yare_input_.CorporationName;
-
-    MasterContractNumber    = b->yare_input_.MasterContractNumber;
-    ContractNumber          = b->yare_input_.ContractNumber;
-
-    Insured1                = b->yare_input_.InsuredName;
-    Gender                  = mc_str(b->yare_input_.Gender);
-    UWType                  = mc_str(b->yare_input_.GroupUnderwritingType);
-
-    // This could be factored out if it ever needs to be reused.
-    //
-    // DATABASE !! It would make sense to handle it in the product
-    // database if class product_data is rewritten to encompass
-    // variation across axes (as class DBDictionary does).
-    //
-    oenum_smoking_or_tobacco smoke_or_tobacco =
-        static_cast<oenum_smoking_or_tobacco>
-            (static_cast<int>(b->Database_->Query(DB_SmokeOrTobacco))
-            );
-    if(oe_tobacco_nontobacco == smoke_or_tobacco)
-        {
-        switch(b->yare_input_.Smoking)
-            {
-            case mce_smoker:    Smoker =    "Tobacco"; break;
-            case mce_nonsmoker: Smoker = "Nontobacco"; break;
-            case mce_unismoke:  Smoker = "Unitobacco"; break;
-            }
-        }
-    else if(oe_smoker_nonsmoker == smoke_or_tobacco)
-        {
-        Smoker = mc_str(b->yare_input_.Smoking);
-        }
-    else
-        {
-        throw std::logic_error("Unknown oe_smoker_nonsmoker convention.");
-        }
-
-    UWClass                 = mc_str(b->yare_input_.UnderwritingClass);
-    SubstandardTable        = mc_str(b->yare_input_.SubstandardTable);
-
-    EffDate                 = calendar_date(b->yare_input_.EffectiveDate  
).str();
-    EffDateJdn              = calendar_date(b->yare_input_.EffectiveDate  
).julian_day_number();
-    DateOfBirth             = calendar_date(b->yare_input_.DateOfBirth    
).str();
-    DateOfBirthJdn          = calendar_date(b->yare_input_.DateOfBirth    
).julian_day_number();
-    ListBillDate            = calendar_date(b->yare_input_.ListBillDate   
).str();
-    ListBillDateJdn         = calendar_date(b->yare_input_.ListBillDate   
).julian_day_number();
-    InforceAsOfDate         = 
calendar_date(b->yare_input_.InforceAsOfDate).str();
-    InforceAsOfDateJdn      = 
calendar_date(b->yare_input_.InforceAsOfDate).julian_day_number();
-    InitErMode              = mc_str(b->Outlay_->er_premium_modes()[0]);
-
-    mcenum_dbopt const init_dbo = b->DeathBfts_->dbopt()[0];
-    InitDBOpt =
-         (mce_option1 == init_dbo) ? dbo_name_option1
-        :(mce_option2 == init_dbo) ? dbo_name_option2
-        :(mce_rop     == init_dbo) ? dbo_name_rop
-        :(mce_mdb     == init_dbo) ? dbo_name_mdb
-        :throw std::logic_error("Unrecognized initial death benefit option.")
-        ;
-
-    DefnLifeIns             = mc_str(b->yare_input_.DefinitionOfLifeInsurance);
-    DefnMaterialChange      = 
mc_str(b->yare_input_.DefinitionOfMaterialChange);
-    AvoidMec                = mc_str(b->yare_input_.AvoidMecMethod);
-    PartMortTableName       = "1983 GAM"; // TODO ?? Hardcoded.
-    StatePostalAbbrev       = mc_str(b->GetStateOfJurisdiction());
-    PremiumTaxState         = mc_str(b->GetPremiumTaxState());
-
-    IsInforce = b->yare_input_.EffectiveDate != b->yare_input_.InforceAsOfDate;
-
-    // This test is probably redundant because it is already performed
-    // in class Input. But it's difficult to prove that it is actually
-    // redundant and will always remain so, while repeating it here
-    // costs little and gives a stronger guarantee that illustrations
-    // that would violate this rule cannot be produced.
-    if(IsInforce && (0 == b->yare_input_.InforceYear && 0 == 
b->yare_input_.InforceMonth))
-        {
-        alarum()
-            << "Inforce illustrations not permitted during month of issue."
-            << LMI_FLUSH
-            ;
-        }
-
-    WriteTsvFile = contains(b->yare_input_.Comments, "idiosyncrasyY");
-
-    SupplementalReport         = b->yare_input_.CreateSupplementalReport;
-    SupplementalReportColumn00 = 
mc_str(b->yare_input_.SupplementalReportColumn00);
-    SupplementalReportColumn01 = 
mc_str(b->yare_input_.SupplementalReportColumn01);
-    SupplementalReportColumn02 = 
mc_str(b->yare_input_.SupplementalReportColumn02);
-    SupplementalReportColumn03 = 
mc_str(b->yare_input_.SupplementalReportColumn03);
-    SupplementalReportColumn04 = 
mc_str(b->yare_input_.SupplementalReportColumn04);
-    SupplementalReportColumn05 = 
mc_str(b->yare_input_.SupplementalReportColumn05);
-    SupplementalReportColumn06 = 
mc_str(b->yare_input_.SupplementalReportColumn06);
-    SupplementalReportColumn07 = 
mc_str(b->yare_input_.SupplementalReportColumn07);
-    SupplementalReportColumn08 = 
mc_str(b->yare_input_.SupplementalReportColumn08);
-    SupplementalReportColumn09 = 
mc_str(b->yare_input_.SupplementalReportColumn09);
-    SupplementalReportColumn10 = 
mc_str(b->yare_input_.SupplementalReportColumn10);
-    SupplementalReportColumn11 = 
mc_str(b->yare_input_.SupplementalReportColumn11);
-
-    // irr_initialized_ is deliberately not set here: it's not
-    // encompassed by 'FullyInitialized'.
-    FullyInitialized = true;
-}
-
-//============================================================================
 LedgerInvariant& LedgerInvariant::PlusEq(LedgerInvariant const& a_Addend)
 {
     LedgerBase::PlusEq(a_Addend, a_Addend.InforceLives);
diff --git a/ledger_invariant_init.cpp b/ledger_invariant_init.cpp
new file mode 100644
index 0000000..0cdfda5
--- /dev/null
+++ b/ledger_invariant_init.cpp
@@ -0,0 +1,530 @@
+// Ledger data that do not vary by basis--initialization.
+//
+// Copyright (C) 1998, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 
2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "ledger_invariant.hpp"
+
+#include "alert.hpp"
+#include "assert_lmi.hpp"
+#include "basic_values.hpp"
+#include "contains.hpp"
+#include "database.hpp"
+#include "dbnames.hpp"
+#include "death_benefits.hpp"
+#include "fund_data.hpp"
+#include "interest_rates.hpp"
+#include "loads.hpp"
+#include "mc_enum_types_aux.hpp"        // mc_str()
+#include "miscellany.hpp"               // each_equal()
+#include "outlay.hpp"
+#include "premium_tax.hpp"
+#include "product_data.hpp"
+#include "ssize_lmi.hpp"
+
+#include <algorithm>                    // max()
+#include <stdexcept>
+
+void LedgerInvariant::Init(BasicValues const* b)
+{
+    // Zero-initialize almost everything.
+    Init();
+
+    irr_precision_ = b->round_irr().decimals();
+
+// TODO ?? These names are confusing. EePmt and ErPmt are *input* values.
+// If they're entered as $1000 for all years, then they have that value
+// every year, even after lapse. Variables whose names end in -'GrossPmt'
+// hold the results of transaction processing, e.g. $0 after lapse.
+// EePmt and ErPmt are used e.g. in premium-strategy calculations.
+
+    EePmt           = b->Outlay_->ee_modal_premiums();
+    ErPmt           = b->Outlay_->er_modal_premiums();
+//  TgtPrem         =
+//  GrossPmt        =
+//  EeGrossPmt      =
+//  ErGrossPmt      =
+    // These must be set dynamically because they may be changed,
+    // e.g. to respect guideline limits.
+//    External1035Amount;
+//    Internal1035Amount;
+//    Dumpin               =
+
+    InforceUnloanedAV =
+          b->yare_input_.InforceGeneralAccountValue
+        + b->yare_input_.InforceSeparateAccountValue
+        ;
+    InforceTaxBasis      = b->yare_input_.InforceTaxBasis           ;
+
+    // Certain data members, including but almost certainly not
+    // limited to these, should not be initialized to any non-zero
+    // value here. Actual values are inserted in account-value
+    // processing, subject to various restrictions that often cause
+    // them to differ from input values. Notably, values need to be
+    // zero after lapse.
+//    NetWD                 =
+//    NewCashLoan           =
+//    GptForceout           =
+//    NaarForceout          =
+//    ModalMinimumPremium   =
+//    EeModalMinimumPremium =
+//    ErModalMinimumPremium =
+//    ProducerCompensation  =
+
+    HasSupplSpecAmt = false;
+    if(b->yare_input_.TermRider)
+        {
+        TermSpecAmt     .assign(Length, b->yare_input_.TermRiderAmount);
+        }
+    else if(b->Database_->Query(DB_TermIsNotRider))
+        {
+        TermSpecAmt      = b->DeathBfts_->supplamt();
+        if(!each_equal(TermSpecAmt, 0.0))
+            {
+            HasSupplSpecAmt = true;
+            }
+        }
+    else
+        {
+        TermSpecAmt     .assign(Length, 0.0);
+        }
+    SpecAmt         = b->DeathBfts_->specamt();
+    for(int j = 0; j < Length; ++j)
+        {
+        EeMode[j] = b->Outlay_->ee_premium_modes()[j];
+        ErMode[j] = b->Outlay_->er_premium_modes()[j];
+        DBOpt [j] = b->DeathBfts_->dbopt()[j];
+        }
+
+    IndvTaxBracket       = b->yare_input_.TaxBracket                ;
+    CorpTaxBracket       = b->yare_input_.CorporationTaxBracket     ;
+    Salary               = b->yare_input_.ProjectedSalary           ;
+    AnnualFlatExtra      = b->yare_input_.FlatExtra                 ;
+    HoneymoonValueSpread = b->yare_input_.HoneymoonValueSpread      ;
+    AddonMonthlyFee      = b->yare_input_.ExtraMonthlyCustodialFee  ;
+    AddonCompOnAssets    = b->yare_input_.ExtraCompensationOnAssets ;
+    AddonCompOnPremium   = b->yare_input_.ExtraCompensationOnPremium;
+    CorridorFactor       = b->GetCorridorFactor();
+    AnnLoanDueRate       = b->InterestRates_->RegLnDueRate
+        (mce_gen_curr
+        ,mce_annual_rate
+        );
+    InitAnnLoanDueRate   = AnnLoanDueRate[0];
+    CurrMandE            = b->InterestRates_->MAndERate(mce_gen_curr);
+    TotalIMF             = b->InterestRates_->InvestmentManagementFee();
+    RefundableSalesLoad  = b->Loads_->refundable_sales_load_proportion();
+
+    std::vector<double> coimult;
+    b->Database_->Query(coimult, DB_CurrCoiMultiplier);
+    CurrentCoiMultiplier =
+          coimult                            [b->yare_input_.InforceYear]
+        * b->yare_input_.CurrentCoiMultiplier[b->yare_input_.InforceYear]
+        * b->yare_input_.CountryCoiMultiplier
+        ;
+
+    CountryIso3166Abbrev = mc_str(b->yare_input_.Country);
+    Comments             = b->yare_input_.Comments;
+
+    FundNumbers           .resize(0);
+    FundNames             .resize(0);
+    FundAllocs            .resize(0);
+    FundAllocations       .resize(0);
+
+    // The antediluvian branch has a null FundData_ object.
+    int number_of_funds(0);
+    if(b->FundData_)
+        {
+        number_of_funds = b->FundData_->GetNumberOfFunds();
+        }
+
+//    enum{NumberOfFunds = 30}; // DEPRECATED
+    int const NumberOfFunds = 30; // DEPRECATED
+    int expected_number_of_funds = std::max(number_of_funds, NumberOfFunds);
+    std::vector<double> v(b->yare_input_.FundAllocations);
+    if(lmi::ssize(v) < expected_number_of_funds)
+        {
+        v.insert(v.end(), expected_number_of_funds - v.size(), 0.0);
+        }
+
+    for(int j = 0; j < number_of_funds; ++j)
+        {
+        FundNumbers.push_back(j);
+        FundNames.push_back(b->FundData_->GetFundInfo(j).LongName());
+
+        // TODO ?? InputParms::NumberOfFunds is defectively hardcocded
+        // as thirty as this is written, but we need to support a product
+        // with more than thirty funds. The input routines respect that
+        // hardcoded limit and are difficult to change, so funds after
+        // the thirtieth cannot be selected individually; but if the
+        // rule 'equal initial fund allocations' is chosen instead of
+        // specifying individual allocations, then the average fund fee
+        // is calculated reflecting all funds, even past the thirtieth:
+        // thus, calculations are correct for any input, and the defect
+        // in the program itself is just that some legitimate inputs are
+        // not allowed, though the output spreadsheet has its own
+        // hardcoded limit (due to space), which is a separate defect.
+        // Here we pass a zero allocation to the output spreadsheet for
+        // all funds past the thirtieth, which is correct because no
+        // nonzero allocation can be selected. That's correct even if
+        // the 'equal initial allocations' rule is chosen, in which
+        // case the allocations are not explicitly shown, but are instead
+        // stated in words to be equal--because the spreadsheet layout
+        // otherwise shows allocations as integer percentages, and
+        // something like '.3333333...' would overflow the space available.
+        //
+        // As of 2008, most of the foregoing is no longer applicable,
+        // except for the hardcoded limit, which is copied above.
+        FundAllocs     .push_back(static_cast<int>(v[j]));
+        FundAllocations.push_back(0.01 * v[j]);
+        }
+
+    GenAcctAllocation = 1.0 - premium_allocation_to_sepacct(b->yare_input_);
+
+    SplitFundAllocation =
+            (0.0 != GenAcctAllocation && 1.0 != GenAcctAllocation)
+        ||
+            (  0.0 != b->yare_input_.InforceGeneralAccountValue
+            && 0.0 != b->yare_input_.InforceSeparateAccountValue
+            )
+        ;
+
+    NoLapseAlwaysActive     = b->Database_->Query(DB_NoLapseAlwaysActive);
+    NoLapseMinDur           = b->Database_->Query(DB_NoLapseMinDur);
+    NoLapseMinAge           = b->Database_->Query(DB_NoLapseMinAge);
+    Has1035ExchCharge       = b->Database_->Query(DB_Has1035ExchCharge);
+
+    // SOMEDAY !! Things indexed with '[0]' should probably use inforce year 
instead.
+    InitBaseSpecAmt         = b->DeathBfts_->specamt()[0];
+    InitTermSpecAmt         = TermSpecAmt[0];
+    ChildRiderAmount        = b->yare_input_.ChildRiderAmount;
+    SpouseRiderAmount       = b->yare_input_.SpouseRiderAmount;
+
+//  InitPrem                = 0;
+//  GuarPrem                = 0;
+//  InitSevenPayPrem        =
+//  InitTgtPrem             =
+//  InitMinPrem             =
+//  ListBillPremium         =
+//  EeListBillPremium       =
+//  ErListBillPremium       =
+//  ModalMinimumDumpin      =
+
+    MaleProportion          = b->yare_input_.MaleProportion;
+    NonsmokerProportion     = b->yare_input_.NonsmokerProportion;
+    PartMortTableMult       = b->yare_input_.PartialMortalityMultiplier;
+
+    // Assert this because the illustration currently prints a scalar
+    // guaranteed max, assuming that it's the same for all years.
+    std::vector<double> const& guar_m_and_e_rate = b->InterestRates_->MAndERate
+        (mce_gen_guar
+        );
+    LMI_ASSERT(each_equal(guar_m_and_e_rate, guar_m_and_e_rate.front()));
+    GuarMaxMandE            = guar_m_and_e_rate[0];
+    InitDacTaxRate          = 
b->Loads_->dac_tax_load()[b->yare_input_.InforceYear];
+    InitPremTaxRate         = b->PremiumTax_->maximum_load_rate();
+//  GenderDistinct          = 0;
+    GenderBlended           = b->yare_input_.BlendGender;
+//  SmokerDistinct          = 0;
+    SmokerBlended           = b->yare_input_.BlendSmoking;
+
+    SubstdTable             = b->yare_input_.SubstandardTable;
+
+    Age                     = b->yare_input_.IssueAge;
+    RetAge                  = b->yare_input_.RetirementAge;
+    EndtAge                 = b->yare_input_.IssueAge + b->GetLength();
+    GroupIndivSelection     = b->Database_->Query(DB_GroupIndivSelection);
+    UseExperienceRating     = b->yare_input_.UseExperienceRating;
+    UsePartialMort          = b->yare_input_.UsePartialMortality;
+    AvgFund                 = b->yare_input_.UseAverageOfAllFunds;
+    CustomFund              = b->yare_input_.OverrideFundManagementFee;
+
+    HasWP                   = b->yare_input_.WaiverOfPremiumBenefit;
+    HasADD                  = b->yare_input_.AccidentalDeathBenefit;
+    HasTerm                 = b->yare_input_.TermRider;
+    HasChildRider           = b->yare_input_.ChildRider;
+    HasSpouseRider          = b->yare_input_.SpouseRider;
+    SpouseIssueAge          = b->yare_input_.SpouseIssueAge;
+
+    HasHoneymoon            = b->yare_input_.HoneymoonEndorsement;
+    PostHoneymoonSpread     = b->yare_input_.PostHoneymoonSpread;
+    SplitMinPrem            = b->Database_->Query(DB_SplitMinPrem);
+    AllowDbo3               = b->Database_->Query(DB_AllowDbo3);
+
+    // These are reassigned below based on product data if available.
+    std::string dbo_name_option1 = mc_str(mce_option1);
+    std::string dbo_name_option2 = mc_str(mce_option2);
+    std::string dbo_name_rop     = mc_str(mce_rop    );
+    std::string dbo_name_mdb     = mc_str(mce_mdb    ); // DBO3 !! reconsider
+
+    // The antediluvian branch has a null ProductData_ object.
+    if(b->ProductData_)
+        {
+        product_data const& p = *b->ProductData_;
+        // Accommodate one alternative policy-form name.
+        // DATABASE !! It would be much better, of course, to let all
+        // strings in class product_data vary across the same axes as
+        // database_entity objects.
+        bool alt_form = b->Database_->Query(DB_UsePolicyFormAlt);
+        dbo_name_option1               = p.datum("DboNameLevel"                
   );
+        dbo_name_option2               = p.datum("DboNameIncreasing"           
   );
+        dbo_name_rop                   = p.datum("DboNameReturnOfPremium"      
   );
+//      dbo_name_mdb                   = // DBO3 !! reconsider
+        PolicyForm = p.datum(alt_form ? "PolicyFormAlternative" : 
"PolicyForm");
+        PolicyMktgName                 = p.datum("PolicyMktgName"              
   );
+        PolicyLegalName                = p.datum("PolicyLegalName"             
   );
+        InsCoShortName                 = p.datum("InsCoShortName"              
   );
+        InsCoName                      = p.datum("InsCoName"                   
   );
+        InsCoAddr                      = p.datum("InsCoAddr"                   
   );
+        InsCoStreet                    = p.datum("InsCoStreet"                 
   );
+        InsCoPhone                     = p.datum("InsCoPhone"                  
   );
+        MainUnderwriter                = p.datum("MainUnderwriter"             
   );
+        MainUnderwriterAddress         = p.datum("MainUnderwriterAddress"      
   );
+        CoUnderwriter                  = p.datum("CoUnderwriter"               
   );
+        CoUnderwriterAddress           = p.datum("CoUnderwriterAddress"        
   );
+
+        AvName                         = p.datum("AvName"                      
   );
+        CsvName                        = p.datum("CsvName"                     
   );
+        CsvHeaderName                  = p.datum("CsvHeaderName"               
   );
+        NoLapseProvisionName           = p.datum("NoLapseProvisionName"        
   );
+        ContractName                   = p.datum("ContractName"                
   );
+
+        AccountValueFootnote           = p.datum("AccountValueFootnote"        
   );
+        AttainedAgeFootnote            = p.datum("AttainedAgeFootnote"         
   );
+        CashSurrValueFootnote          = p.datum("CashSurrValueFootnote"       
   );
+        DeathBenefitFootnote           = p.datum("DeathBenefitFootnote"        
   );
+        InitialPremiumFootnote         = p.datum("InitialPremiumFootnote"      
   );
+        NetPremiumFootnote             = p.datum("NetPremiumFootnote"          
   );
+        GrossPremiumFootnote           = p.datum("GrossPremiumFootnote"        
   );
+        OutlayFootnote                 = p.datum("OutlayFootnote"              
   );
+        PolicyYearFootnote             = p.datum("PolicyYearFootnote"          
   );
+
+        ADDTerseName                   = p.datum("ADDTerseName"                
   );
+        InsurabilityTerseName          = p.datum("InsurabilityTerseName"       
   );
+        ChildTerseName                 = p.datum("ChildTerseName"              
   );
+        SpouseTerseName                = p.datum("SpouseTerseName"             
   );
+        TermTerseName                  = p.datum("TermTerseName"               
   );
+        WaiverTerseName                = p.datum("WaiverTerseName"             
   );
+        AccelBftRiderTerseName         = p.datum("AccelBftRiderTerseName"      
   );
+        OverloanRiderTerseName         = p.datum("OverloanRiderTerseName"      
   );
+
+        ADDFootnote                    = p.datum("ADDFootnote"                 
   );
+        ChildFootnote                  = p.datum("ChildFootnote"               
   );
+        SpouseFootnote                 = p.datum("SpouseFootnote"              
   );
+        TermFootnote                   = p.datum("TermFootnote"                
   );
+        WaiverFootnote                 = p.datum("WaiverFootnote"              
   );
+        AccelBftRiderFootnote          = p.datum("AccelBftRiderFootnote"       
   );
+        OverloanRiderFootnote          = p.datum("OverloanRiderFootnote"       
   );
+
+        GroupQuoteShortProductName     = p.datum("GroupQuoteShortProductName"  
   );
+        GroupQuoteIsNotAnOffer         = p.datum("GroupQuoteIsNotAnOffer"      
   );
+        GroupQuoteRidersFooter         = p.datum("GroupQuoteRidersFooter"      
   );
+        GroupQuotePolicyFormId         = p.datum("GroupQuotePolicyFormId"      
   );
+        GroupQuoteStateVariations      = p.datum("GroupQuoteStateVariations"   
   );
+        GroupQuoteProspectus           = p.datum("GroupQuoteProspectus"        
   );
+        GroupQuoteUnderwriter          = p.datum("GroupQuoteUnderwriter"       
   );
+        GroupQuoteBrokerDealer         = p.datum("GroupQuoteBrokerDealer"      
   );
+        GroupQuoteRubricMandatory      = p.datum("GroupQuoteRubricMandatory"   
   );
+        GroupQuoteRubricVoluntary      = p.datum("GroupQuoteRubricVoluntary"   
   );
+        GroupQuoteRubricFusion         = p.datum("GroupQuoteRubricFusion"      
   );
+        GroupQuoteFooterMandatory      = p.datum("GroupQuoteFooterMandatory"   
   );
+        GroupQuoteFooterVoluntary      = p.datum("GroupQuoteFooterVoluntary"   
   );
+        GroupQuoteFooterFusion         = p.datum("GroupQuoteFooterFusion"      
   );
+
+        MinimumPremiumFootnote         = p.datum("MinimumPremiumFootnote"      
   );
+        PremAllocationFootnote         = p.datum("PremAllocationFootnote"      
   );
+
+        InterestDisclaimer             = p.datum("InterestDisclaimer"          
   );
+        GuarMortalityFootnote          = p.datum("GuarMortalityFootnote"       
   );
+        ProductDescription             = p.datum("ProductDescription"          
   );
+        StableValueFootnote            = p.datum("StableValueFootnote"         
   );
+        NoVanishPremiumFootnote        = p.datum("NoVanishPremiumFootnote"     
   );
+        RejectPremiumFootnote          = p.datum("RejectPremiumFootnote"       
   );
+        ExpRatingFootnote              = p.datum("ExpRatingFootnote"           
   );
+        MortalityBlendFootnote         = p.datum("MortalityBlendFootnote"      
   );
+        HypotheticalRatesFootnote      = p.datum("HypotheticalRatesFootnote"   
   );
+        SalesLoadRefundFootnote        = p.datum("SalesLoadRefundFootnote"     
   );
+        NoLapseFootnote                = p.datum("NoLapseFootnote"             
   );
+        MarketValueAdjFootnote         = p.datum("MarketValueAdjFootnote"      
   );
+        ExchangeChargeFootnote0        = p.datum("ExchangeChargeFootnote0"     
   );
+        CurrentValuesFootnote          = p.datum("CurrentValuesFootnote"       
   );
+        DBOption1Footnote              = p.datum("DBOption1Footnote"           
   );
+        DBOption2Footnote              = p.datum("DBOption2Footnote"           
   );
+        ExpRatRiskChargeFootnote       = p.datum("ExpRatRiskChargeFootnote"    
   );
+        ExchangeChargeFootnote1        = p.datum("ExchangeChargeFootnote1"     
   );
+        FlexiblePremiumFootnote        = p.datum("FlexiblePremiumFootnote"     
   );
+        GuaranteedValuesFootnote       = p.datum("GuaranteedValuesFootnote"    
   );
+        CreditingRateFootnote          = p.datum("CreditingRateFootnote"       
   );
+        GrossRateFootnote              = p.datum("GrossRateFootnote"           
   );
+        NetRateFootnote                = p.datum("NetRateFootnote"             
   );
+        MecFootnote                    = p.datum("MecFootnote"                 
   );
+        GptFootnote                    = p.datum("GptFootnote"                 
   );
+        MidpointValuesFootnote         = p.datum("MidpointValuesFootnote"      
   );
+        SinglePremiumFootnote          = p.datum("SinglePremiumFootnote"       
   );
+        MonthlyChargesFootnote         = p.datum("MonthlyChargesFootnote"      
   );
+        UltCreditingRateFootnote       = p.datum("UltCreditingRateFootnote"    
   );
+        MaxNaarFootnote                = p.datum("MaxNaarFootnote"             
   );
+        PremTaxSurrChgFootnote         = p.datum("PremTaxSurrChgFootnote"      
   );
+        PolicyFeeFootnote              = p.datum("PolicyFeeFootnote"           
   );
+        AssetChargeFootnote            = p.datum("AssetChargeFootnote"         
   );
+        InvestmentIncomeFootnote       = p.datum("InvestmentIncomeFootnote"    
   );
+        IrrDbFootnote                  = p.datum("IrrDbFootnote"               
   );
+        IrrCsvFootnote                 = p.datum("IrrCsvFootnote"              
   );
+        MortalityChargesFootnote       = p.datum("MortalityChargesFootnote"    
   );
+        LoanAndWithdrawalFootnote      = p.datum("LoanAndWithdrawalFootnote"   
   );
+        LoanFootnote                   = p.datum("LoanFootnote"                
   );
+        ImprimaturPresale              = p.datum("ImprimaturPresale"           
   );
+        ImprimaturPresaleComposite     = p.datum("ImprimaturPresaleComposite"  
   );
+        ImprimaturInforce              = p.datum("ImprimaturInforce"           
   );
+        ImprimaturInforceComposite     = p.datum("ImprimaturInforceComposite"  
   );
+        StateMarketingImprimatur       = p.datum("StateMarketingImprimatur"    
   );
+        InforceNonGuaranteedFootnote0  = 
p.datum("InforceNonGuaranteedFootnote0"  );
+        InforceNonGuaranteedFootnote1  = 
p.datum("InforceNonGuaranteedFootnote1"  );
+        InforceNonGuaranteedFootnote2  = 
p.datum("InforceNonGuaranteedFootnote2"  );
+        InforceNonGuaranteedFootnote3  = 
p.datum("InforceNonGuaranteedFootnote3"  );
+        NonGuaranteedFootnote          = p.datum("NonGuaranteedFootnote"       
   );
+        MonthlyChargesPaymentFootnote  = 
p.datum("MonthlyChargesPaymentFootnote"  );
+        SurrenderFootnote              = p.datum("SurrenderFootnote"           
   );
+        PortabilityFootnote            = p.datum("PortabilityFootnote"         
   );
+        FundRateFootnote               = p.datum("FundRateFootnote"            
   );
+        FundRateFootnote0              = p.datum("FundRateFootnote0"           
   );
+        FundRateFootnote1              = p.datum("FundRateFootnote1"           
   );
+        IssuingCompanyFootnote         = p.datum("IssuingCompanyFootnote"      
   );
+        SubsidiaryFootnote             = p.datum("SubsidiaryFootnote"          
   );
+        PlacementAgentFootnote         = p.datum("PlacementAgentFootnote"      
   );
+        MarketingNameFootnote          = p.datum("MarketingNameFootnote"       
   );
+        }
+
+    ProductName             = b->yare_input_.ProductName;
+    ProducerName            = b->yare_input_.AgentName;
+
+    std::string agent_city     = b->yare_input_.AgentCity;
+    std::string agent_state    = mc_str(b->yare_input_.AgentState);
+    std::string agent_zip_code = b->yare_input_.AgentZipCode;
+    std::string agent_city_etc(agent_city + ", " + agent_state);
+    if(!agent_zip_code.empty())
+        {
+        agent_city_etc += " ";
+        }
+    agent_city_etc += agent_zip_code;
+
+    ProducerStreet          = b->yare_input_.AgentAddress;
+    ProducerCity            = agent_city_etc;
+    CorpName                = b->yare_input_.CorporationName;
+
+    MasterContractNumber    = b->yare_input_.MasterContractNumber;
+    ContractNumber          = b->yare_input_.ContractNumber;
+
+    Insured1                = b->yare_input_.InsuredName;
+    Gender                  = mc_str(b->yare_input_.Gender);
+    UWType                  = mc_str(b->yare_input_.GroupUnderwritingType);
+
+    // This could be factored out if it ever needs to be reused.
+    //
+    // DATABASE !! It would make sense to handle it in the product
+    // database if class product_data is rewritten to encompass
+    // variation across axes (as class DBDictionary does).
+    //
+    oenum_smoking_or_tobacco smoke_or_tobacco =
+        static_cast<oenum_smoking_or_tobacco>
+            (static_cast<int>(b->Database_->Query(DB_SmokeOrTobacco))
+            );
+    if(oe_tobacco_nontobacco == smoke_or_tobacco)
+        {
+        switch(b->yare_input_.Smoking)
+            {
+            case mce_smoker:    Smoker =    "Tobacco"; break;
+            case mce_nonsmoker: Smoker = "Nontobacco"; break;
+            case mce_unismoke:  Smoker = "Unitobacco"; break;
+            }
+        }
+    else if(oe_smoker_nonsmoker == smoke_or_tobacco)
+        {
+        Smoker = mc_str(b->yare_input_.Smoking);
+        }
+    else
+        {
+        throw std::logic_error("Unknown oe_smoker_nonsmoker convention.");
+        }
+
+    UWClass                 = mc_str(b->yare_input_.UnderwritingClass);
+    SubstandardTable        = mc_str(b->yare_input_.SubstandardTable);
+
+    EffDate                 = calendar_date(b->yare_input_.EffectiveDate  
).str();
+    EffDateJdn              = calendar_date(b->yare_input_.EffectiveDate  
).julian_day_number();
+    DateOfBirth             = calendar_date(b->yare_input_.DateOfBirth    
).str();
+    DateOfBirthJdn          = calendar_date(b->yare_input_.DateOfBirth    
).julian_day_number();
+    ListBillDate            = calendar_date(b->yare_input_.ListBillDate   
).str();
+    ListBillDateJdn         = calendar_date(b->yare_input_.ListBillDate   
).julian_day_number();
+    InforceAsOfDate         = 
calendar_date(b->yare_input_.InforceAsOfDate).str();
+    InforceAsOfDateJdn      = 
calendar_date(b->yare_input_.InforceAsOfDate).julian_day_number();
+    InitErMode              = mc_str(b->Outlay_->er_premium_modes()[0]);
+
+    mcenum_dbopt const init_dbo = b->DeathBfts_->dbopt()[0];
+    InitDBOpt =
+         (mce_option1 == init_dbo) ? dbo_name_option1
+        :(mce_option2 == init_dbo) ? dbo_name_option2
+        :(mce_rop     == init_dbo) ? dbo_name_rop
+        :(mce_mdb     == init_dbo) ? dbo_name_mdb
+        :throw std::logic_error("Unrecognized initial death benefit option.")
+        ;
+
+    DefnLifeIns             = mc_str(b->yare_input_.DefinitionOfLifeInsurance);
+    DefnMaterialChange      = 
mc_str(b->yare_input_.DefinitionOfMaterialChange);
+    AvoidMec                = mc_str(b->yare_input_.AvoidMecMethod);
+    PartMortTableName       = "1983 GAM"; // TODO ?? Hardcoded.
+    StatePostalAbbrev       = mc_str(b->GetStateOfJurisdiction());
+    PremiumTaxState         = mc_str(b->GetPremiumTaxState());
+
+    IsInforce = b->yare_input_.EffectiveDate != b->yare_input_.InforceAsOfDate;
+
+    // This test is probably redundant because it is already performed
+    // in class Input. But it's difficult to prove that it is actually
+    // redundant and will always remain so, while repeating it here
+    // costs little and gives a stronger guarantee that illustrations
+    // that would violate this rule cannot be produced.
+    if(IsInforce && (0 == b->yare_input_.InforceYear && 0 == 
b->yare_input_.InforceMonth))
+        {
+        alarum()
+            << "Inforce illustrations not permitted during month of issue."
+            << LMI_FLUSH
+            ;
+        }
+
+    WriteTsvFile = contains(b->yare_input_.Comments, "idiosyncrasyY");
+
+    SupplementalReport         = b->yare_input_.CreateSupplementalReport;
+    SupplementalReportColumn00 = 
mc_str(b->yare_input_.SupplementalReportColumn00);
+    SupplementalReportColumn01 = 
mc_str(b->yare_input_.SupplementalReportColumn01);
+    SupplementalReportColumn02 = 
mc_str(b->yare_input_.SupplementalReportColumn02);
+    SupplementalReportColumn03 = 
mc_str(b->yare_input_.SupplementalReportColumn03);
+    SupplementalReportColumn04 = 
mc_str(b->yare_input_.SupplementalReportColumn04);
+    SupplementalReportColumn05 = 
mc_str(b->yare_input_.SupplementalReportColumn05);
+    SupplementalReportColumn06 = 
mc_str(b->yare_input_.SupplementalReportColumn06);
+    SupplementalReportColumn07 = 
mc_str(b->yare_input_.SupplementalReportColumn07);
+    SupplementalReportColumn08 = 
mc_str(b->yare_input_.SupplementalReportColumn08);
+    SupplementalReportColumn09 = 
mc_str(b->yare_input_.SupplementalReportColumn09);
+    SupplementalReportColumn10 = 
mc_str(b->yare_input_.SupplementalReportColumn10);
+    SupplementalReportColumn11 = 
mc_str(b->yare_input_.SupplementalReportColumn11);
+
+    // irr_initialized_ is deliberately not set here: it's not
+    // encompassed by 'FullyInitialized'.
+    FullyInitialized = true;
+}
diff --git a/ledger_variant.cpp b/ledger_variant.cpp
index 52b04be..23f73f1 100644
--- a/ledger_variant.cpp
+++ b/ledger_variant.cpp
@@ -24,15 +24,9 @@
 #include "ledger_variant.hpp"
 
 #include "assert_lmi.hpp"
-#include "basic_values.hpp"
-#include "database.hpp"                 // Used only for initial loan rate.
-#include "dbnames.hpp"                  // Used only for initial loan rate.
-#include "interest_rates.hpp"
-#include "loads.hpp"
 #include "mc_enum_types_aux.hpp"        // mc_str()
-#include "outlay.hpp"
 
-#include <algorithm>
+#include <algorithm>                    // max()
 #include <ostream>
 
 //============================================================================
@@ -187,97 +181,6 @@ void LedgerVariant::Init()
 }
 
 //============================================================================
-void LedgerVariant::Init
-    (BasicValues const& bv
-    ,mcenum_gen_basis   gen_basis
-    ,mcenum_sep_basis   sep_basis
-    )
-{
-    Init(); // Zero out (almost) everything to start.
-
-    GenBasis_ = gen_basis;
-    SepBasis_ = sep_basis;
-
-//  EOYDeathBft     =
-//  AcctVal         =
-//  CSVNet          =
-//  CV7702          =
-//  COICharge       =
-//  RiderCharges    =
-//  ExpenseCharges  =
-    MlySAIntRate               = bv.InterestRates_->SepAcctNetRate
-        (SepBasis_
-        ,GenBasis_
-        ,mce_monthly_rate
-        );
-    MlyGAIntRate               = bv.InterestRates_->GenAcctNetRate
-        (GenBasis_
-        ,mce_monthly_rate
-        );
-    MlyHoneymoonValueRate      = bv.InterestRates_->HoneymoonValueRate
-        (GenBasis_
-        ,mce_monthly_rate
-        );
-    MlyPostHoneymoonRate       = bv.InterestRates_->PostHoneymoonGenAcctRate
-        (GenBasis_
-        ,mce_monthly_rate
-        );
-    AnnSAIntRate               = bv.InterestRates_->SepAcctNetRate
-        (SepBasis_
-        ,GenBasis_
-        ,mce_annual_rate
-        );
-    AnnGAIntRate               = bv.InterestRates_->GenAcctNetRate
-        (GenBasis_
-        ,mce_annual_rate
-        );
-    AnnHoneymoonValueRate      = bv.InterestRates_->HoneymoonValueRate
-        (GenBasis_
-        ,mce_annual_rate
-        );
-    AnnPostHoneymoonRate       = bv.InterestRates_->PostHoneymoonGenAcctRate
-        (GenBasis_
-        ,mce_annual_rate
-        );
-
-//  PrefLoanBalance =
-//  TotalLoanBalance=
-//  AvgDeathBft     =
-//  SurrChg         =
-//  TermPurchased   =
-//  BaseDeathBft    =
-//  ProjectedCoiCharge =
-//  KFactor         =
-
-    InitAnnLoanCredRate = bv.InterestRates_->RegLnCredRate
-        (GenBasis_
-        ,mce_annual_rate
-        )[0];
-
-    InitAnnGenAcctInt = bv.InterestRates_->GenAcctNetRate
-        (GenBasis_
-        ,mce_annual_rate
-        )
-        [0]
-        ;
-
-    InitAnnSepAcctGrossInt = bv.InterestRates_->SepAcctGrossRate(SepBasis_)[0];
-
-    InitAnnSepAcctNetInt = bv.InterestRates_->SepAcctNetRate
-        (SepBasis_
-        ,GenBasis_
-        ,mce_annual_rate
-        )
-        [0]
-        ;
-
-    InitTgtPremHiLoadRate = 
bv.Loads_->target_premium_load_maximum_premium_tax()[bv.yare_input_.InforceYear];
-    InitMlyPolFee         = bv.Loads_->monthly_policy_fee(GenBasis_)           
 [bv.yare_input_.InforceYear];
-
-    FullyInitialized = true;
-}
-
-//============================================================================
 void LedgerVariant::set_run_basis(mcenum_run_basis b)
 {
     set_cloven_bases_from_run_basis(b, GenBasis_, SepBasis_);
diff --git a/ledger_variant_init.cpp b/ledger_variant_init.cpp
new file mode 100644
index 0000000..36b4c95
--- /dev/null
+++ b/ledger_variant_init.cpp
@@ -0,0 +1,118 @@
+// Ledger data that vary by basis--initialization.
+//
+// Copyright (C) 1998, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 
2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "ledger_variant.hpp"
+
+#include "basic_values.hpp"
+#include "interest_rates.hpp"
+#include "loads.hpp"
+
+void LedgerVariant::Init
+    (BasicValues const& bv
+    ,mcenum_gen_basis   gen_basis
+    ,mcenum_sep_basis   sep_basis
+    )
+{
+    Init(); // Zero out (almost) everything to start.
+
+    GenBasis_ = gen_basis;
+    SepBasis_ = sep_basis;
+
+//  EOYDeathBft     =
+//  AcctVal         =
+//  CSVNet          =
+//  CV7702          =
+//  COICharge       =
+//  RiderCharges    =
+//  ExpenseCharges  =
+    MlySAIntRate               = bv.InterestRates_->SepAcctNetRate
+        (SepBasis_
+        ,GenBasis_
+        ,mce_monthly_rate
+        );
+    MlyGAIntRate               = bv.InterestRates_->GenAcctNetRate
+        (GenBasis_
+        ,mce_monthly_rate
+        );
+    MlyHoneymoonValueRate      = bv.InterestRates_->HoneymoonValueRate
+        (GenBasis_
+        ,mce_monthly_rate
+        );
+    MlyPostHoneymoonRate       = bv.InterestRates_->PostHoneymoonGenAcctRate
+        (GenBasis_
+        ,mce_monthly_rate
+        );
+    AnnSAIntRate               = bv.InterestRates_->SepAcctNetRate
+        (SepBasis_
+        ,GenBasis_
+        ,mce_annual_rate
+        );
+    AnnGAIntRate               = bv.InterestRates_->GenAcctNetRate
+        (GenBasis_
+        ,mce_annual_rate
+        );
+    AnnHoneymoonValueRate      = bv.InterestRates_->HoneymoonValueRate
+        (GenBasis_
+        ,mce_annual_rate
+        );
+    AnnPostHoneymoonRate       = bv.InterestRates_->PostHoneymoonGenAcctRate
+        (GenBasis_
+        ,mce_annual_rate
+        );
+
+//  PrefLoanBalance =
+//  TotalLoanBalance=
+//  AvgDeathBft     =
+//  SurrChg         =
+//  TermPurchased   =
+//  BaseDeathBft    =
+//  ProjectedCoiCharge =
+//  KFactor         =
+
+    InitAnnLoanCredRate = bv.InterestRates_->RegLnCredRate
+        (GenBasis_
+        ,mce_annual_rate
+        )[0];
+
+    InitAnnGenAcctInt = bv.InterestRates_->GenAcctNetRate
+        (GenBasis_
+        ,mce_annual_rate
+        )
+        [0]
+        ;
+
+    InitAnnSepAcctGrossInt = bv.InterestRates_->SepAcctGrossRate(SepBasis_)[0];
+
+    InitAnnSepAcctNetInt = bv.InterestRates_->SepAcctNetRate
+        (SepBasis_
+        ,GenBasis_
+        ,mce_annual_rate
+        )
+        [0]
+        ;
+
+    InitTgtPremHiLoadRate = 
bv.Loads_->target_premium_load_maximum_premium_tax()[bv.yare_input_.InforceYear];
+    InitMlyPolFee         = bv.Loads_->monthly_policy_fee(GenBasis_)           
 [bv.yare_input_.InforceYear];
+
+    FullyInitialized = true;
+}
diff --git a/objects.make b/objects.make
index 964b70e..20fb150 100644
--- a/objects.make
+++ b/objects.make
@@ -225,10 +225,12 @@ common_common_objects := \
   ledger_base.o \
   ledger_evaluator.o \
   ledger_invariant.o \
+  ledger_invariant_init.o \
   ledger_pdf.o \
   ledger_pdf_generator.o \
   ledger_text_formats.o \
   ledger_variant.o \
+  ledger_variant_init.o \
   ledger_xml_io.o \
   ledger_xsl.o \
   ledgervalues.o \



reply via email to

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