[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] odd/unify_products 405d654: Unify code to generate '
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] odd/unify_products 405d654: Unify code to generate '.database' and '.policy' files |
Date: |
Mon, 8 Jul 2019 17:17:49 -0400 (EDT) |
branch: odd/unify_products
commit 405d6548e6f1e4f35e8dd9e179841c0826f2d4a9
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Unify code to generate '.database' and '.policy' files
This experimental commit demonstrates a technique that probably won't
be used in production.
Inspiration: The similarity of these snippets...
// dbdict.cpp
class sample : public DBDictionary {public: sample();};
class sample2finra : public sample {public: sample2finra();};
// product_data.cpp
class sample : public product_data {public: sample();};
class sample2 : public sample {public: sample2();};
class sample2finra : public sample2 {public: sample2finra();};
...suggests merging these two nearly-identical hierarchies, as is done
in new file 'products.hpp'.
Analysis: The new implementation of sample2finra::sample2finra() in
'products.cpp' shows how attractive such a merger can be, given a set
of products that differ only slightly. However, proprietary products
more often have dozens of differences, and combining two implementations
like
sample::sample() // 281 lines in 'dbdict.cpp'
sample::sample() // 78 lines in 'product_data.cpp'
into a single 359-line function is a serious pessimization. Inheriting
from multiple bases
class sample : public DBDictionary, public product_data {...};
is not a concern because unit tests show that the default ctors are
fast; all that matters is simplicity and comprehensibility.
A template alternative
template<typename T> class sample : public T {...};
is unattractive because every derived class would need to be a template,
but compact declarations like
class sample2finra : public sample2 {public: sample2finra();};
class sample2prosp : public sample2 {public: sample2prosp();};
class sample2gpp : public sample2 {public: sample2gpp ();};
are much easier to read, especially for extensive, tangled hierarchies
of proprietary products.
---
objects.make | 1 +
products.cpp | 632 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
products.hpp | 41 ++++
3 files changed, 674 insertions(+)
diff --git a/objects.make b/objects.make
index d9172ed..90e620a 100644
--- a/objects.make
+++ b/objects.make
@@ -1166,4 +1166,5 @@ product_files$(EXEEXT): \
my_proem.o \
my_rnd.o \
my_tier.o \
+ products.o \
liblmi$(SHREXT) \
diff --git a/products.cpp b/products.cpp
new file mode 100644
index 0000000..face6a3
--- /dev/null
+++ b/products.cpp
@@ -0,0 +1,632 @@
+// Product parameters.
+//
+// Copyright (C) 1998, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 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 "products.hpp"
+
+#include "dbnames.hpp"
+#include "premium_tax.hpp" //
premium_tax_rates_for_life_insurance()
+
+/// The 'sample' product DWISOTT. Its values, where specified at all
+/// (rather than defaulted to empty strings), are intended to be
+/// plausible, if perhaps whimsical.
+
+sample::sample()
+{
+ // Numeric data.
+
+ Add({DB_MinIssAge , 15});
+ Add({DB_MaxIssAge , 70});
+ Add({DB_MaxIncrAge , 99});
+ Add({DB_AllowFullUw , true});
+ Add({DB_AllowParamedUw , true});
+ Add({DB_AllowNonmedUw , true});
+ Add({DB_AllowSimpUw , true});
+ Add({DB_AllowGuarUw , true});
+ Add({DB_SmokeOrTobacco , oe_tobacco_nontobacco});
+ Add({DB_AllowPreferredClass , true});
+ Add({DB_AllowUltraPrefClass , false});
+
+ // Forbid substandard table ratings with simplified or guaranteed issue.
+ int uw_dims[e_number_of_axes] = {1, 1, 1, 1, 5, 1, 1};
+ // med para nonmed SI GI
+ double allow_substd_table[] = {true, true, true, false, false};
+ Add({DB_AllowSubstdTable, e_number_of_axes, uw_dims, allow_substd_table});
+
+ Add({DB_AllowFlatExtras , true});
+ Add({DB_AllowRatedWp , false});
+ Add({DB_AllowRatedAdb , false});
+ Add({DB_AllowRatedTerm , true});
+ Add({DB_AllowRetirees , true});
+ Add({DB_AllowUnisex , true});
+ Add({DB_AllowSexDistinct , true});
+ Add({DB_AllowUnismoke , true});
+ Add({DB_AllowSmokeDistinct , true});
+ Add({DB_StateApproved , true});
+ Add({DB_AllowStateXX , true});
+ Add({DB_AllowForeign , true});
+ Add({DB_GroupIndivSelection , false});
+ Add({DB_Allowable , true});
+ Add({DB_AllowCvat , true});
+ Add({DB_AllowGpt , true});
+ Add({DB_AllowNo7702 , false});
+ Add({DB_CorridorWhence , oe_7702_corr_from_table});
+ Add({DB_Irc7702NspWhence , oe_7702_nsp_reciprocal_cvat_corridor});
+ Add({DB_SevenPayWhence , oe_7702_7pp_from_table});
+ Add({DB_Irc7702QWhence , oe_7702_q_external_table});
+
+ // This is just a sample product, so make do with plausible
+ // all-male seven-pay premiums, and use GPT corridor factors for
+ // CVAT. 'Irc7702NspWhence' specifies that NSP is calculated as
+ // the reciprocal of CVAT corridor, so no NSP table is needed.
+ Add({DB_CorridorTable , 7});
+ Add({DB_Irc7702NspTable , 0});
+ Add({DB_SevenPayTable , 10});
+
+ Add({DB_CsoEra , oe_1980cso});
+ // Following IRS Notice 88-128, use only the male and female
+ // tables with no smoker distinction, and a unisex table where
+ // required by state law.
+ //
+ // US 1980 CSO age last, not smoker distinct. Unisex = table D.
+ // Male uses table E, which is correct, as opposed to table F,
+ // which contains a numerical error but was adopted by NAIC.
+ int dims311[e_number_of_axes] = {3, 1, 1, 1, 1, 1, 1}; // gender
+ double T7702q[3] = {35, 41, 107,}; // Female, male, unisex.
+ Add({DB_Irc7702QTable, e_number_of_axes, dims311, T7702q});
+ Add({DB_Irc7702QAxisGender , true});
+ Add({DB_Irc7702QAxisSmoking , false});
+
+ Add({DB_CvatMatChangeDefn ,
mce_earlier_of_increase_or_unnecessary_premium});
+ Add({DB_GptMatChangeDefn , 0});
+ Add({DB_Irc7702BftIsSpecAmt , 0});
+
+ // US 1980 CSO age last; unisex = table D.
+ // Male uses table E, which is correct, as opposed to table F,
+ // which contains a numerical error but was adopted by NAIC.
+ int dims313[e_number_of_axes] = {3, 1, 3, 1, 1, 1, 1}; // gender, smoker
+ double TgCOI[9] =
+ {
+ 39, 37, 35, // female: sm ns us
+ 45, 57, 41, // male: sm ns us
+ 111, 109, 107, // unisex: sm ns us
+ };
+ Add({DB_GuarCoiTable, e_number_of_axes, dims313, TgCOI});
+
+ Add({DB_GuarCoiIsAnnual , true});
+
+ // For now at least, just use (a multiple of) guaranteed COI rates
+ // as current.
+ Add({DB_CurrCoiTable, e_number_of_axes, dims313, TgCOI});
+
+ Add({DB_CurrCoiIsAnnual , true});
+
+ double coimult[9] =
+ {
+ 0.40, 0.30, 0.35, // female: sm ns us
+ 0.60, 0.50, 0.55, // male: sm ns us
+ 0.50, 0.40, 0.45, // unisex: sm ns us
+ };
+ Add({DB_CurrCoiMultiplier, e_number_of_axes, dims313, coimult});
+
+ Add({DB_MdptCoiIsAnnual , true});
+ Add({DB_UseNyCoiFloor , 0.0});
+ Add({DB_GuarCoiCeiling , false});
+ Add({DB_CoiGuarIsMin , false});
+ Add({DB_AllowMortBlendSex , true});
+ Add({DB_AllowMortBlendSmoke , true});
+ Add({DB_GuarInt , 0.03});
+ Add({DB_NaarDiscount , 0.00246627});
+ Add({DB_GuarMandE , 0.009});
+ Add({DB_CurrIntSpread , 0.01});
+ Add({DB_CurrMandE , 0.009});
+ Add({DB_GenAcctIntBonus , 0.0});
+ Add({DB_BonusInt , 0.0});
+ Add({DB_IntFloor , 0.0});
+ Add({DB_AllowGenAcct , true});
+ Add({DB_AllowSepAcct , true});
+ Add({DB_AllowGenAcctEarnRate, true});
+ Add({DB_AllowSepAcctNetRate , true});
+ Add({DB_MaxGenAcctRate , 0.06});
+ Add({DB_MaxSepAcctRate , 0.12});
+ Add({DB_SepAcctSpreadMethod , mce_spread_is_effective_annual});
+ Add({DB_IntSpreadMode , mce_spread_daily});
+ Add({DB_DynamicMandE , false});
+ Add({DB_AllowAmortPremLoad , false});
+ Add({DB_LoadAmortFundCharge , 0.0030});
+ Add({DB_AllowImfOverride , false});
+ Add({DB_AssetChargeType , oe_asset_charge_spread});
+ Add({DB_StableValFundCharge , 0.0});
+ Add({DB_GuarFundAdminChg , 0.0});
+ Add({DB_CurrFundAdminChg , 0.0});
+ Add({DB_FundCharge , 0.0});
+ Add({DB_GuarMonthlyPolFee , 8.00});
+ Add({DB_GuarAnnualPolFee , 0.0});
+ Add({DB_GuarPremLoadTgt , 0.07});
+ Add({DB_GuarPremLoadExc , 0.04});
+ Add({DB_GuarPremLoadTgtRfd , 0.00});
+ Add({DB_GuarPremLoadExcRfd , 0.00});
+ Add({DB_GuarSpecAmtLoad , 0.0});
+ Add({DB_GuarAcctValLoad , 0.0});
+ Add({DB_CurrMonthlyPolFee , 5.00});
+ Add({DB_CurrAnnualPolFee , 0.0});
+ Add({DB_CurrPremLoadTgt , 0.05});
+ Add({DB_CurrPremLoadExc , 0.02});
+ Add({DB_CurrPremLoadTgtRfd , 0.00});
+ Add({DB_CurrPremLoadExcRfd , 0.00});
+ Add({DB_CurrSpecAmtLoad , 0.0});
+ Add({DB_CurrAcctValLoad , 0.0});
+ Add({DB_TgtPremMonthlyPolFee, 0.0});
+ Add({DB_LoadRfdProportion , 0.0});
+ Add({DB_SpecAmtLoadLimit , 10000000.0});
+ Add({DB_DynamicSepAcctLoad , false});
+ Add({DB_DacTaxFundCharge , 0.0});
+ Add({DB_DacTaxPremLoad , 0.01});
+ Add({DB_PremTaxFundCharge , 0.0});
+
+ // Pass through premium tax.
+ int ptd[e_number_of_axes] = {1, 1, 1, 1, 1, e_max_dim_state, 1};
+ std::vector<int> premium_tax_dimensions(ptd, ptd + e_number_of_axes);
+ Add({DB_PremTaxLoad, premium_tax_dimensions,
premium_tax_rates_for_life_insurance()});
+
+ Add({DB_WaivePremTaxInt1035 , true});
+ Add({DB_PremTaxAmortPeriod , 0});
+ Add({DB_PremTaxAmortIntRate , 0.0});
+
+ Add({DB_PremTaxRate, premium_tax_dimensions,
premium_tax_rates_for_life_insurance()});
+
+ Add({DB_PremTaxState , oe_ee_state});
+ Add({DB_AllowSpecAmtIncr , true});
+ Add({DB_MinSpecAmtIncr , 0.0});
+ Add({DB_EnforceNaarLimit , true});
+ Add({DB_MinSpecAmt , 100000.0});
+ Add({DB_MinIssSpecAmt , 50000.0});
+ Add({DB_MinIssBaseSpecAmt , 50000.0});
+ Add({DB_MinRenlSpecAmt , 50000.0});
+ Add({DB_MinRenlBaseSpecAmt , 50000.0});
+ Add({DB_AllowDboLvl , true});
+ Add({DB_AllowDboInc , true});
+ Add({DB_AllowDboRop , true});
+ Add({DB_AllowDboMdb , true});
+ Add({DB_DboLvlChangeToWhat , 0b1111});
+ Add({DB_DboLvlChangeMethod , 0b1111});
+ Add({DB_DboIncChangeToWhat , 0b1111});
+ Add({DB_DboIncChangeMethod , 0b1111});
+ Add({DB_DboRopChangeToWhat , 0b1111});
+ Add({DB_DboRopChangeMethod , 0b1111});
+ Add({DB_DboMdbChangeToWhat , 0b1111});
+ Add({DB_DboMdbChangeMethod , 0b1111});
+ Add({DB_AllowChangeToDbo2 , true});
+ Add({DB_DboChgCanIncrSpecAmt, true});
+ Add({DB_DboChgCanDecrSpecAmt, true});
+ Add({DB_AllowExtEndt , true});
+ Add({DB_AllowTerm , true});
+
+ int dims143[e_number_of_axes] = {1, 4, 3, 1, 1, 1, 1}; // uw_class, smoker
+ double TtCOI[12] =
+ {
+ 3, 2, 1, // pref: sm ns us
+ 6, 5, 4, // std: sm ns us
+ 6, 5, 4, // rated: sm ns us [same as std]
+ 0, 0, 0, // ultra: sm ns us [zero: error message--no ultrapref class]
+ };
+ Add({DB_GuarTermTable, e_number_of_axes, dims143, TtCOI});
+ Add({DB_TermTable , e_number_of_axes, dims143, TtCOI});
+
+ Add({DB_TermMinIssAge , 15});
+ Add({DB_TermMaxIssAge , 65});
+ Add({DB_TermForcedConvAge , 70});
+ Add({DB_TermForcedConvDur , 10});
+ Add({DB_MaxTermProportion , 0.0});
+ Add({DB_AllowWp , true});
+ Add({DB_WpTable , 8});
+ Add({DB_WpMinIssAge , 18});
+ Add({DB_WpMaxIssAge , 64});
+ Add({DB_AllowAdb , true});
+ Add({DB_AdbTable , 708}); // 70-75 US ADB experience
+ Add({DB_AdbMinIssAge , 15});
+ Add({DB_AdbMaxIssAge , 70});
+ Add({DB_AdbLimit , 1000000.0});
+ Add({DB_AllowSpouseRider , true});
+ Add({DB_SpouseRiderMinAmt , 10000.0});
+ Add({DB_SpouseRiderMaxAmt , 1000000.0});
+ Add({DB_SpouseRiderMinIssAge, 20});
+ Add({DB_SpouseRiderMaxIssAge, 65});
+ Add({DB_SpouseRiderGuarTable, 305}); // arbitrarily use 1960 CSG
+ Add({DB_SpouseRiderTable , 305}); // arbitrarily use 1960 CSG
+ Add({DB_AllowChildRider , true});
+ Add({DB_ChildRiderMinAmt , 25000}); // for testing, min==max
+ Add({DB_ChildRiderMaxAmt , 25000}); // for testing, min==max
+ Add({DB_ChildRiderTable , 305}); // arbitrarily use 1960 CSG
+ Add({DB_AllowWd , true});
+ Add({DB_WdFee , 25.0});
+ Add({DB_WdFeeRate , 0.02});
+ Add({DB_MinWd , 100.0});
+ Add({DB_MaxWdDed , mce_to_next_anniversary});
+ Add({DB_WdDecrSpecAmtDboLvl , true});
+ Add({DB_WdDecrSpecAmtDboInc , true});
+ Add({DB_WdDecrSpecAmtDboRop , true});
+ Add({DB_FirstWdMonth , 0.0});
+ Add({DB_AllowLoan , true});
+ Add({DB_AllowPrefLoan , false});
+ Add({DB_AllowFixedLoan , true});
+ Add({DB_AllowVlr , true});
+ Add({DB_FixedLoanRate , 0.06});
+ Add({DB_MinVlrRate , 0.04});
+ Add({DB_MaxLoanAcctValMult , 1.0});
+ Add({DB_MaxLoanDed , mce_to_next_anniversary});
+ Add({DB_GuarPrefLoanSpread , 0.0});
+ Add({DB_GuarRegLoanSpread , 0.04});
+ Add({DB_CurrPrefLoanSpread , 0.0});
+ Add({DB_CurrRegLoanSpread , 0.02});
+ Add({DB_FirstLoanMonth , 0.0});
+ Add({DB_MinPremType , oe_monthly_deduction});
+ Add({DB_TgtPremType , oe_modal_nonmec});
+ Add({DB_TgtPremTable , 10}); // use seven-pay as target
+ Add({DB_TgtPremFixedAtIssue , false});
+ Add({DB_TgtPremIgnoreSubstd , true});
+ Add({DB_MinPmt , 0.0});
+ Add({DB_NoLapseMinDur , 0.0});
+ Add({DB_NoLapseMinAge , 0.0});
+ Add({DB_NoLapseUnratedOnly , false});
+ Add({DB_NoLapseDboLvlOnly , false});
+ Add({DB_NoLapseAlwaysActive , false});
+ Add({DB_AllowHoneymoon , true});
+ Add({DB_AllowExtraAssetComp , true});
+ Add({DB_AllowExtraPremComp , true});
+ Add({DB_AllowExpRating , false});
+ Add({DB_AllowExpRating , true});
+ Add({DB_ExpRatStdDevMult , 0.0});
+ Add({DB_ExpRatIbnrMult , 0.0});
+ Add({DB_ExpRatIbnrMult , 6.0});
+ Add({DB_ExpRatCoiRetention , 0.0});
+ Add({DB_ExpRatRiskCoiMult , 0});
+ Add({DB_ExpRatAmortPeriod , 4.0});
+ Add({DB_LedgerType , mce_ill_reg});
+ Add({DB_AgeLastOrNearest , oe_age_last_birthday});
+ Add({DB_MaturityAge , 100});
+ Add({DB_GroupProxyRateTable , 305}); // 1960 CSG (gender-indistinct)
+
+ // 1983 GAM; unisex=male because no unisex table was published.
+ double T83Gam[3] = {825, 826, 826,}; // f, m, u
+ Add({DB_PartialMortTable, e_number_of_axes, dims311, T83Gam});
+
+ // Use alternative policy form name in states beginning with "K".
+ std::vector<double> alt_form(e_max_dim_state);
+ alt_form[mce_s_KS] = true;
+ alt_form[mce_s_KY] = true;
+ Add({DB_UsePolicyFormAlt, premium_tax_dimensions, alt_form});
+
+ // String data.
+
+ // Names of lmi product files.
+ item("DatabaseFilename") = glossed_string("sample.database");
+ item("FundFilename") = glossed_string("sample.funds");
+ item("RoundingFilename") = glossed_string("sample.rounding");
+ item("TierFilename") = glossed_string("sample.strata");
+
+ // Base names of mortality-table databases.
+ item("CvatCorridorFilename") = glossed_string("sample");
+ item("Irc7702NspFilename") = glossed_string("sample");
+ item("CurrCOIFilename") = glossed_string("qx_cso");
+ item("GuarCOIFilename") = glossed_string("qx_cso");
+ item("WPFilename") = glossed_string("sample");
+ item("ADDFilename") = glossed_string("qx_ins", "Specimen
gloss.");
+ item("ChildRiderFilename") = glossed_string("qx_ins");
+ item("CurrSpouseRiderFilename") = glossed_string("qx_ins");
+ item("GuarSpouseRiderFilename") = glossed_string("qx_ins");
+ item("CurrTermFilename") = glossed_string("sample");
+ item("GuarTermFilename") = glossed_string("sample");
+ item("GroupProxyFilename") = glossed_string("qx_ins");
+ item("SevenPayFilename") = glossed_string("sample");
+ item("TgtPremFilename") = glossed_string("sample");
+ item("Irc7702QFilename") = glossed_string("qx_cso");
+ item("PartialMortalityFilename") = glossed_string("qx_ann");
+ item("SubstdTblMultFilename") = glossed_string("sample");
+ item("CurrSpecAmtLoadFilename") = glossed_string("sample");
+ item("GuarSpecAmtLoadFilename") = glossed_string("sample");
+
+ // Other data that affect calculations.
+ item("InsCoDomicile") = glossed_string("WI");
+
+ // Substitutable strings.
+ item("PolicyForm") = glossed_string("UL32768-NY");
+ item("PolicyFormAlternative") = glossed_string("UL32768-X");
+ item("PolicyMktgName") = glossed_string("UL Supreme");
+ item("PolicyLegalName") = glossed_string("Flexible Premium
Adjustable Life Insurance Policy");
+ item("InsCoShortName") = glossed_string("Superior Life");
+ item("InsCoName") = glossed_string("Superior Life
Insurance Company");
+ item("InsCoAddr") = glossed_string("Superior, WI 12345");
+ item("InsCoStreet") = glossed_string("246 Main Street");
+ item("InsCoPhone") = glossed_string("(800) 555-1212");
+ item("MainUnderwriter") = glossed_string("Superior Securities");
+ item("MainUnderwriterAddress") = glossed_string("246-M Main Street,
Superior, WI 12345");
+ item("CoUnderwriter") = glossed_string("Superior Investors");
+ item("CoUnderwriterAddress") = glossed_string("246-C Main Street,
Superior, WI 12345");
+ item("AvName") = glossed_string("Account");
+ item("CsvName") = glossed_string("Cash Surrender");
+ item("CsvHeaderName") = glossed_string("Cash Surr");
+ item("NoLapseProvisionName") = glossed_string("No-lapse Provision");
+ item("ContractName") = glossed_string("contract"); //
Alternatively, "policy" or "certificate".
+ item("DboNameLevel") = glossed_string("A");
+ item("DboNameIncreasing") = glossed_string("B");
+ item("DboNameReturnOfPremium") = glossed_string("ROP");
+ item("DboNameMinDeathBenefit") = glossed_string("MDB");
+ item("MarketingNameFootnote") = glossed_string("Policy form
UL32768-NY is marketed as 'UL Supreme'.");
+
+ item("ADDTerseName") = glossed_string("Accident");
+ item("InsurabilityTerseName") = glossed_string("Insurability");
+ item("ChildTerseName") = glossed_string("Child");
+ item("SpouseTerseName") = glossed_string("Spouse");
+ item("TermTerseName") = glossed_string("Term");
+ item("WaiverTerseName") = glossed_string("Waiver");
+ item("AccelBftRiderTerseName") = glossed_string("Acceleration");
+ item("OverloanRiderTerseName") = glossed_string("Overloan");
+
+ item("GroupQuoteShortProductName") = glossed_string("UL SUPREMEĀ®");
+ item("GroupQuoteIsNotAnOffer") = glossed_string("This is not an offer
of insurance.");
+ item("GroupQuoteRidersFooter") = glossed_string("Available riders:
accident and waiver.");
+ item("GroupQuotePolicyFormId") = glossed_string("Policy form
UL32768-NY is a flexible premium contract.");
+ item("GroupQuoteStateVariations") = glossed_string("Not available in all
states.");
+ item("GroupQuoteProspectus") = glossed_string("Read the prospectus
carefully.");
+ item("GroupQuoteUnderwriter") = glossed_string("Securities
underwritten by Superior Securities.");
+ item("GroupQuoteBrokerDealer") = glossed_string("Securities offered
through Superior Brokerage.");
+ item("GroupQuoteRubricMandatory") = glossed_string("Mandatory");
+ item("GroupQuoteRubricVoluntary") = glossed_string("Voluntary");
+ item("GroupQuoteRubricFusion") = glossed_string("Fusion");
+ item("GroupQuoteFooterMandatory") = glossed_string("The employer pays all
premiums.");
+ item("GroupQuoteFooterVoluntary") = glossed_string("The employee pays all
premiums.");
+ item("GroupQuoteFooterFusion") = glossed_string("The employer and
employee pay their respective premiums.");
+}
+
+/// The 'sample2*' products are designed to facilitate testing.
+/// There is one for each supported ledger type:
+/// sample2naic mce_ill_reg
+/// sample2finra mce_finra
+/// sample2prosp mce_prospectus_abeyed ['emit_test_data' only]
+/// sample2gpp mce_group_private_placement
+/// sample2ipp mce_individual_private_placement
+/// and one for exotica:
+/// sample2xyz mce_finra
+///
+/// "*Filename" members are names of actual lmi product files, or
+/// basenames of mortality-table databases, and their values must
+/// nominate actual files. Member 'InsCoDomicile' is used to
+/// determine retaliatory premium-tax rates, and must be a two-letter
+/// USPS abbreviation. All other members represent text that is used
+/// for report formatting; in order to make 'sample2*' more useful for
+/// developing and testing reports, each has a nonempty value that is
+/// its member name enclosed in braces ("{}"). Braces aren't otherwise
+/// used in values, so any output substring like "{contract}" here:
+/// "This {contract} provides valuable protection"
+/// necessarily represents a substitutable value, while everything
+/// else in a report is just literal text.
+
+sample2::sample2()
+{
+ // Numeric data: none, deliberately.
+
+ // String data.
+
+ for(auto const& i : product_data::member_names())
+ {
+ product_data::operator[](i) = '{' + i + '}';
+ }
+
+ // Names of lmi product files.
+ item("DatabaseFilename") = glossed_string("sample.database");
+ item("FundFilename") = glossed_string("sample.funds");
+ item("RoundingFilename") = glossed_string("sample.rounding");
+ item("TierFilename") = glossed_string("sample.strata");
+
+ // Base names of mortality-table databases.
+ item("CvatCorridorFilename") = glossed_string("sample");
+ item("Irc7702NspFilename") = glossed_string("sample");
+ item("CurrCOIFilename") = glossed_string("qx_cso");
+ item("GuarCOIFilename") = glossed_string("qx_cso");
+ item("WPFilename") = glossed_string("sample");
+ item("ADDFilename") = glossed_string("qx_ins", "Specimen
gloss.");
+ item("ChildRiderFilename") = glossed_string("qx_ins");
+ item("CurrSpouseRiderFilename") = glossed_string("qx_ins");
+ item("GuarSpouseRiderFilename") = glossed_string("qx_ins");
+ item("CurrTermFilename") = glossed_string("sample");
+ item("GuarTermFilename") = glossed_string("sample");
+ item("GroupProxyFilename") = glossed_string("qx_ins");
+ item("SevenPayFilename") = glossed_string("sample");
+ item("TgtPremFilename") = glossed_string("sample");
+ item("Irc7702QFilename") = glossed_string("qx_cso");
+ item("PartialMortalityFilename") = glossed_string("qx_ann");
+ item("SubstdTblMultFilename") = glossed_string("sample");
+ item("CurrSpecAmtLoadFilename") = glossed_string("sample");
+ item("GuarSpecAmtLoadFilename") = glossed_string("sample");
+
+ // Other data that affect calculations.
+ item("InsCoDomicile") = glossed_string("WI");
+}
+
+sample2naic::sample2naic()
+{
+ // Deliberately empty implementation.
+}
+
+sample2finra::sample2finra()
+{
+ // Numeric data.
+
+ Add({DB_LedgerType , mce_finra});
+
+ // String data.
+
+ item("DatabaseFilename") =
glossed_string("sample2finra.database");
+}
+
+sample2prosp::sample2prosp()
+{
+ // Numeric data.
+
+ Add({DB_LedgerType , mce_prospectus_abeyed});
+
+ // String data.
+
+ item("DatabaseFilename") =
glossed_string("sample2prosp.database");
+}
+
+sample2gpp::sample2gpp()
+{
+ // Numeric data.
+
+ Add({DB_LedgerType , mce_group_private_placement});
+
+ // String data.
+
+ item("DatabaseFilename") = glossed_string("sample2gpp.database");
+}
+
+/// This specimen product
+/// https://lists.nongnu.org/archive/html/lmi/2018-09/msg00039.html
+/// | has deliberately overlong footnotes
+/// for pagination testing.
+
+sample2ipp::sample2ipp()
+{
+ // Numeric data.
+
+ Add({DB_LedgerType , mce_individual_private_placement});
+
+ // String data.
+
+ item("DatabaseFilename") = glossed_string("sample2ipp.database");
+ item("IrrDbFootnote") = glossed_string
+ ("The \"Red Death\" had long devastated the country. No pestilence"
+ " had ever been so fatal, or so hideous. Blood was its Avatar and"
+ " its seal--the redness and the horror of blood. There were sharp"
+ " pains, and sudden dizziness, and then profuse bleeding at the"
+ " pores, with dissolution. The scarlet stains upon the body and"
+ " especially upon the face of the victim, were the pest ban which"
+ " shut him out from the aid and from the sympathy of his fellow-men."
+ " And the whole seizure, progress and termination of the disease,"
+ " were the incidents of half an hour."
+ );
+ item("IrrCsvFootnote") = glossed_string
+ ("But the Prince Prospero was happy and dauntless and sagacious. When"
+ " his dominions were half depopulated, he summoned to his presence a"
+ " thousand hale and light-hearted friends from among the knights and"
+ " dames of his court, and with these retired to the deep seclusion"
+ " of one of his castellated abbeys. This was an extensive and"
+ " magnificent structure, the creation of the prince's own eccentric"
+ " yet august taste. A strong and lofty wall girdled it in. This wall"
+ " had gates of iron. The courtiers, having entered, brought furnaces"
+ " and massy hammers and welded the bolts. They resolved to leave"
+ " means neither of ingress nor egress to the sudden impulses of"
+ " despair or of frenzy from within. The abbey was amply provisioned."
+ " With such precautions the courtiers might bid defiance to
contagion."
+ " The external world could take care of itself. In the meantime it"
+ " was folly to grieve, or to think. The prince had provided all the"
+ " appliances of pleasure. There were buffoons, there were"
+ " improvisatori, there were ballet-dancers, there were musicians,"
+ " there was Beauty, there was wine. All these and security were"
+ " within. Without was the \"Red Death\"."
+ );
+ item("MortalityChargesFootnote") = glossed_string
+ ("It was towards the close of the fifth or sixth month of his"
+ " seclusion, and while the pestilence raged most furiously abroad,"
+ " that the Prince Prospero entertained his thousand friends at a"
+ " masked ball of the most unusual magnificence."
+ );
+ item("PolicyYearFootnote") = glossed_string
+ ("It was a voluptuous scene, that masquerade. But first let me tell"
+ " of the rooms in which it was held. These were seven--an imperial"
+ " suite. In many palaces, however, such suites form a long and"
+ " straight vista, while the folding doors slide back nearly to the"
+ " walls on either hand, so that the view of the whole extent is"
+ " scarcely impeded. Here the case was very different, as might have"
+ " been expected from the duke's love of the _bizarre_. The apartments"
+ " were so irregularly disposed that the vision embraced but little"
+ " more than one at a time. There was a sharp turn at every twenty or"
+ " thirty yards, and at each turn a novel effect. To the right and"
+ " left, in the middle of each wall, a tall and narrow Gothic window"
+ " looked out upon a closed corridor which pursued the windings of the"
+ " suite. These windows were of stained glass whose color varied in"
+ " accordance with the prevailing hue of the decorations of the"
+ " chamber into which it opened. That at the eastern extremity was"
+ " hung, for example in blue--and vividly blue were its windows. The"
+ " second chamber was purple in its ornaments and tapestries, and here"
+ " the panes were purple. The third was green throughout, and so were"
+ " the casements. The fourth was furnished and lighted with
orange--the"
+ " fifth with white--the sixth with violet. The seventh apartment was"
+ " closely shrouded in black velvet tapestries that hung all over the"
+ " ceiling and down the walls, falling in heavy folds upon a carpet of"
+ " the same material and hue. But in this chamber only, the color of"
+ " the windows failed to correspond with the decorations. The panes"
+ " here were scarlet--a deep blood color. Now in no one of the seven"
+ " apartments was there any lamp or candelabrum, amid the profusion of"
+ " golden ornaments that lay scattered to and fro or depended from the"
+ " roof. There was no light of any kind emanating from lamp or candle"
+ " within the suite of chambers. But in the corridors that followed
the"
+ " suite, there stood, opposite to each window, a heavy tripod,
bearing"
+ " a brazier of fire, that projected its rays through the tinted glass"
+ " and so glaringly illumined the room. And thus were produced a"
+ " multitude of gaudy and fantastic appearances. But in the western or"
+ " black chamber the effect of the fire-light that streamed upon the"
+ " dark hangings through the blood-tinted panes, was ghastly in the"
+ " extreme, and produced so wild a look upon the countenances of those"
+ " who entered, that there were few of the company bold enough to set"
+ " foot within its precincts at all."
+ );
+}
+
+sample2xyz::sample2xyz()
+{
+ // Numeric data.
+
+ // Exotica.
+#if 0
+ // US 1980 CSO age last, not gender distinct. Unisex = table D.
+ // This deviation from the 'sample' family should necessitate
+ // different 7pp and corridor tables. Enable this deliberate
+ // inconsistency as an optional test of the product verifier.
+ int dims113[e_number_of_axes] = {1, 1, 3, 1, 1, 1, 1}; // smoking
+ double T7702q[3] = {111, 109, 107,}; // Smoker, nonsmoker, unismoke.
+ Add({DB_Irc7702QTable, e_number_of_axes, dims113, T7702q});
+ Add({DB_Irc7702QAxisGender , false});
+ Add({DB_Irc7702QAxisSmoking , true});
+#endif // 0
+ // Arguably the most complex ledger type.
+ Add({DB_LedgerType , mce_finra});
+ // Certain group-quote columns are available only when these two
+ // entities are 'true':
+ Add({DB_SplitMinPrem , true});
+ Add({DB_TermIsNotRider , true});
+ // Certain illustration columns are controlled by this:
+ Add({DB_ErNotionallyPaysTerm, true});
+ Add({DB_TxCallsGuarUwSubstd , true});
+ // This fixed loan rate varies by duration.
+ int dims_1111113[e_number_of_axes] = {1, 1, 1, 1, 1, 1, 3};
+ double loanrate[3] = {0.06, 0.05, 0.04};
+ Add({DB_FixedLoanRate, e_number_of_axes, dims_1111113, loanrate});
+ double cv_enh[3] = {0.10, 0.05, 0.00};
+ Add({DB_CashValueEnhMult, e_number_of_axes, dims_1111113, cv_enh});
+
+ // String data.
+
+ item("DatabaseFilename") = glossed_string("sample2xyz.database");
+}
diff --git a/products.hpp b/products.hpp
new file mode 100644
index 0000000..b5d265d
--- /dev/null
+++ b/products.hpp
@@ -0,0 +1,41 @@
+// Product parameters.
+//
+// Copyright (C) 1998, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 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
+
+#ifndef products_hpp
+#define products_hpp
+
+#include "config.hpp"
+
+#include "dbdict.hpp"
+#include "product_data.hpp"
+
+class sample : public DBDictionary, public product_data {public: sample();};
+
+class sample2 : public sample {public: sample2();};
+
+class sample2naic : public sample2 {public: sample2naic ();};
+class sample2finra : public sample2 {public: sample2finra();};
+class sample2prosp : public sample2 {public: sample2prosp();};
+class sample2gpp : public sample2 {public: sample2gpp ();};
+class sample2ipp : public sample2 {public: sample2ipp ();};
+class sample2xyz : public sample2 {public: sample2xyz ();};
+
+#endif // products_hpp