[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master b677016 2/6: Add a 7702i unit test
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master b677016 2/6: Add a 7702i unit test |
Date: |
Tue, 16 Mar 2021 20:20:10 -0400 (EDT) |
branch: master
commit b677016dc0685b91fd7aff411da9cc31e350c42b
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Add a 7702i unit test
To achieve a favorable physical design, with the fewest possible
objects linked for the unit test, split the i7702 implementation into
two files; the new one is named with "_init" because of its similarity
to other '*_init.cpp' files.
---
Makefile.am | 8 ++++
i7702.cpp | 120 +---------------------------------------------
i7702_init.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
i7702_test.cpp | 78 ++++++++++++++++++++++++++++++
objects.make | 7 +++
5 files changed, 241 insertions(+), 119 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 5757a0f..4eaab31 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -116,6 +116,7 @@ TESTS = \
test_global_settings \
test_gpt \
test_handle_exceptions \
+ test_i7702 \
test_ieee754 \
test_input_seq \
test_input \
@@ -417,6 +418,7 @@ liblmi_la_SOURCES = \
gpt_state.cpp \
gpt_xml_document.cpp \
i7702.cpp \
+ i7702_init.cpp \
ihs_acctval.cpp \
ihs_avdebug.cpp \
ihs_avmly.cpp \
@@ -800,6 +802,12 @@ test_handle_exceptions_SOURCES = \
handle_exceptions_test.cpp
test_handle_exceptions_CXXFLAGS = $(AM_CXXFLAGS)
+test_i7702_SOURCES = \
+ $(common_test_objects) \
+ i7702.cpp \
+ i7702_test.cpp
+test_i7702_CXXFLAGS = $(AM_CXXFLAGS)
+
test_ieee754_SOURCES = \
$(common_test_objects) \
ieee754_test.cpp
diff --git a/i7702.cpp b/i7702.cpp
index 7d660fe..13fd513 100644
--- a/i7702.cpp
+++ b/i7702.cpp
@@ -24,14 +24,10 @@
#include "i7702.hpp"
#include "assert_lmi.hpp"
-#include "contains.hpp" // 7702 !! pyx
-#include "database.hpp"
#include "et_vector.hpp"
-#include "global_settings.hpp" // 7702 !! pyx
#include "math_functions.hpp"
-#include "miscellany.hpp" // each_equal(), minmax
+#include "miscellany.hpp" // each_equal()
#include "ssize_lmi.hpp"
-#include "stratified_charges.hpp"
#include <iostream> // 7702 !! pyx
@@ -247,120 +243,6 @@ i7702::i7702
initialize();
}
-i7702::i7702
- (product_database const& database
- ,stratified_charges const& stratified
- )
- :length_ {database.length()}
- ,trace_ {contains(global_settings::instance().pyx(), "show_7702i")}
- ,A0_ {}
- ,A1_ {}
- ,Bgen_ (length_)
- ,Bsep_ (length_)
- ,Bflr_ (length_)
- ,Bvlr_ (length_)
- ,Cgen_ (length_)
- ,Csep_ (length_)
- ,Cflr_ (length_)
- ,Cvlr_ (length_)
- ,Dgen_ (length_)
- ,Dsep_ (length_)
- ,Dflr_ (length_)
- ,Dvlr_ (length_)
- ,Em_ (length_)
- ,use_gen_ (length_)
- ,use_sep_ (length_)
- ,use_flr_ (length_)
- ,use_vlr_ (length_)
- ,ic_usual_ (length_)
- ,ic_glp_ (length_)
- ,ic_gsp_ (length_)
- ,ig_usual_ (length_)
- ,ig_glp_ (length_)
- ,ig_gsp_ (length_)
-{
- // 7702 !! Should 'C*' members be scalar--first year only?
-
- std::vector<double> const zero(length_);
-
- database.query_into(DB_AllowGenAcct , use_gen_);
- database.query_into(DB_AllowSepAcct , use_sep_);
- database.query_into(DB_AllowFixedLoan, use_flr_);
- database.query_into(DB_AllowVlr , use_vlr_);
-
- if(database.query<bool>(DB_IgnoreLoanRateFor7702))
- {
- use_flr_ = zero;
- use_vlr_ = zero;
- }
-
- // 7702 !! Assert that all use_* are boolean.
- // 7702 !! Assert (use_gen_ || use_sep_) for each duration.
-
- // 7702 !! Alternatively, specify A0_ and delta, then calculate A1_?
- A0_ = database.query<double>(DB_AnnInterestRate7702);
- A1_ = 0.02 + A0_;
-
- database.query_into(DB_GuarInt, Bgen_);
-
- std::vector<double> fixed_loan_rate;
- database.query_into(DB_FixedLoanRate, fixed_loan_rate);
- // This isn't the actual rate--lmi doesn't yet implement VLR.
- std::vector<double> variable_loan_rate;
- database.query_into(DB_MaxVlrRate, variable_loan_rate);
- std::vector<double> guar_loan_spread;
- database.query_into(DB_GuarRegLoanSpread, guar_loan_spread);
- assign(Bflr_, fixed_loan_rate - guar_loan_spread);
- assign(Bvlr_, variable_loan_rate - guar_loan_spread);
-
- // If lmi someday implements VLR, then the current VLR rate on
- // the issue date constitutes a short-term guarantee that must be
- // reflected in the 7702 interest rates (excluding the GLP rate).
-
- // It just so happens that these loads are zero for all products
- // lmi supports, except for separate-account-only products. The
- // logic will soon be reworked to make that irrelevant.
- // 7702 !! DB_CurrAcctValLoad is sepacct only: change its name
- database.query_into(DB_CurrAcctValLoad, Dsep_);
- Dsep_ += stratified.minimum_tiered_sepacct_load_for_7702();
-
- // Eckley's 'ig' represents the interest rate by which death
- // benefit is discounted for calculating mortality charges,
- // as seen in his formula (1):
- // [0V + P - Q(1/(1 + ig) - OV - P)] (1 + ic) = 1V
- // where it is the monthly (i upper 12 over 12) equivalent of
- // the annual 'Bgen_' rate above. Specifying a discount based on
- // any other rate is presumably an error.
- //
- // In lmi's product database, DB_GuarInt is i. DB_NaarDiscount is
- // (i upper 12)/12, rounded iff the contract specifies a rounded
- // numerical value. An exception is thrown if the absolute value
- // of the quantization error exceeds a small (though arbitrary)
- // tolerance.
- //
- // However, if the contract applies no such discount, then 'ig'
- // must be zero for formula (1) to apply. As of 2021-02, lmi
- // supports one ancient product that seems to have no such
- // discount. This is so extraordinary that it doesn't merit
- // a special database flag. Instead, the discount is deemed
- // to be absent iff the contractual discount according to the
- // product database is uniformly zero.
-
- database.query_into(DB_NaarDiscount, Em_);
- bool const no_naar_discount = zero == Em_;
- std::vector<double> theoretical_naar_discount(length_);
- theoretical_naar_discount +=
- apply_unary(i_upper_12_over_12_from_i<double>(), Bgen_);
-
- std::vector<double> diff(length_);
- diff += fabs(Em_ - theoretical_naar_discount);
- minmax<double> const mm(diff);
- constexpr double tolerance {0.0000001};
- LMI_ASSERT(no_naar_discount || mm < tolerance);
-
- initialize();
-}
-
void i7702::initialize()
{
// max(A0, B, C)
diff --git a/i7702_init.cpp b/i7702_init.cpp
new file mode 100644
index 0000000..c7c3501
--- /dev/null
+++ b/i7702_init.cpp
@@ -0,0 +1,147 @@
+// 7702 (and 7702A) interest rates--initialization.
+//
+// Copyright (C) 2020, 2021 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// https://savannah.nongnu.org/projects/lmi
+// email: <gchicares@sbcglobal.net>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "i7702.hpp"
+
+#include "assert_lmi.hpp"
+#include "contains.hpp" // 7702 !! pyx
+#include "database.hpp"
+#include "et_vector.hpp"
+#include "global_settings.hpp" // 7702 !! pyx
+#include "math_functions.hpp"
+#include "miscellany.hpp" // minmax
+#include "stratified_charges.hpp"
+
+i7702::i7702
+ (product_database const& database
+ ,stratified_charges const& stratified
+ )
+ :length_ {database.length()}
+ ,trace_ {contains(global_settings::instance().pyx(), "show_7702i")}
+ ,A0_ {}
+ ,A1_ {}
+ ,Bgen_ (length_)
+ ,Bsep_ (length_)
+ ,Bflr_ (length_)
+ ,Bvlr_ (length_)
+ ,Cgen_ (length_)
+ ,Csep_ (length_)
+ ,Cflr_ (length_)
+ ,Cvlr_ (length_)
+ ,Dgen_ (length_)
+ ,Dsep_ (length_)
+ ,Dflr_ (length_)
+ ,Dvlr_ (length_)
+ ,Em_ (length_)
+ ,use_gen_ (length_)
+ ,use_sep_ (length_)
+ ,use_flr_ (length_)
+ ,use_vlr_ (length_)
+ ,ic_usual_ (length_)
+ ,ic_glp_ (length_)
+ ,ic_gsp_ (length_)
+ ,ig_usual_ (length_)
+ ,ig_glp_ (length_)
+ ,ig_gsp_ (length_)
+{
+ // 7702 !! Should 'C*' members be scalar--first year only?
+
+ std::vector<double> const zero(length_);
+
+ database.query_into(DB_AllowGenAcct , use_gen_);
+ database.query_into(DB_AllowSepAcct , use_sep_);
+ database.query_into(DB_AllowFixedLoan, use_flr_);
+ database.query_into(DB_AllowVlr , use_vlr_);
+
+ if(database.query<bool>(DB_IgnoreLoanRateFor7702))
+ {
+ use_flr_ = zero;
+ use_vlr_ = zero;
+ }
+
+ // 7702 !! Assert that all use_* are boolean.
+ // 7702 !! Assert (use_gen_ || use_sep_) for each duration.
+
+ // 7702 !! Alternatively, specify A0_ and delta, then calculate A1_?
+ A0_ = database.query<double>(DB_AnnInterestRate7702);
+ A1_ = 0.02 + A0_;
+
+ database.query_into(DB_GuarInt, Bgen_);
+
+ std::vector<double> fixed_loan_rate;
+ database.query_into(DB_FixedLoanRate, fixed_loan_rate);
+ // This isn't the actual rate--lmi doesn't yet implement VLR.
+ std::vector<double> variable_loan_rate;
+ database.query_into(DB_MaxVlrRate, variable_loan_rate);
+ std::vector<double> guar_loan_spread;
+ database.query_into(DB_GuarRegLoanSpread, guar_loan_spread);
+ assign(Bflr_, fixed_loan_rate - guar_loan_spread);
+ assign(Bvlr_, variable_loan_rate - guar_loan_spread);
+
+ // If lmi someday implements VLR, then the current VLR rate on
+ // the issue date constitutes a short-term guarantee that must be
+ // reflected in the 7702 interest rates (excluding the GLP rate).
+
+ // It just so happens that these loads are zero for all products
+ // lmi supports, except for separate-account-only products. The
+ // logic will soon be reworked to make that irrelevant.
+ // 7702 !! DB_CurrAcctValLoad is sepacct only: change its name
+ database.query_into(DB_CurrAcctValLoad, Dsep_);
+ Dsep_ += stratified.minimum_tiered_sepacct_load_for_7702();
+
+ // Eckley's 'ig' represents the interest rate by which death
+ // benefit is discounted for calculating mortality charges,
+ // as seen in his formula (1):
+ // [0V + P - Q(1/(1 + ig) - OV - P)] (1 + ic) = 1V
+ // where it is the monthly (i upper 12 over 12) equivalent of
+ // the annual 'Bgen_' rate above. Specifying a discount based on
+ // any other rate is presumably an error.
+ //
+ // In lmi's product database, DB_GuarInt is i. DB_NaarDiscount is
+ // (i upper 12)/12, rounded iff the contract specifies a rounded
+ // numerical value. An exception is thrown if the absolute value
+ // of the quantization error exceeds a small (though arbitrary)
+ // tolerance.
+ //
+ // However, if the contract applies no such discount, then 'ig'
+ // must be zero for formula (1) to apply. As of 2021-02, lmi
+ // supports one ancient product that seems to have no such
+ // discount. This is so extraordinary that it doesn't merit
+ // a special database flag. Instead, the discount is deemed
+ // to be absent iff the contractual discount according to the
+ // product database is uniformly zero.
+
+ database.query_into(DB_NaarDiscount, Em_);
+ bool const no_naar_discount = zero == Em_;
+ std::vector<double> theoretical_naar_discount(length_);
+ theoretical_naar_discount +=
+ apply_unary(i_upper_12_over_12_from_i<double>(), Bgen_);
+
+ std::vector<double> diff(length_);
+ diff += fabs(Em_ - theoretical_naar_discount);
+ minmax<double> const mm(diff);
+ constexpr double tolerance {0.0000001};
+ LMI_ASSERT(no_naar_discount || mm < tolerance);
+
+ initialize();
+}
diff --git a/i7702_test.cpp b/i7702_test.cpp
new file mode 100644
index 0000000..37cd760
--- /dev/null
+++ b/i7702_test.cpp
@@ -0,0 +1,78 @@
+// 7702 (and 7702A) interest rates--unit test.
+//
+// Copyright (C) 2021 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// https://savannah.nongnu.org/projects/lmi
+// email: <gchicares@sbcglobal.net>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "i7702.hpp"
+
+#include "materially_equal.hpp"
+#include "test_tools.hpp"
+
+class i7702_test
+{
+ public:
+ static void test()
+ {
+ test0();
+ test1();
+ }
+
+ private:
+ static void test0();
+ static void test1();
+};
+
+void i7702_test::test0()
+{
+ i7702 z
+ {1 // length
+ ,0.04 // A0
+ ,0.06 // A1
+ ,{0.03} // Bgen
+ ,{0.00} // Bsep
+ ,{0.02} // Bflr
+ ,{0.02} // Bvlr
+ ,{0.00} // Cgen
+ ,{0.00} // Csep
+ ,{0.00} // Cflr
+ ,{0.00} // Cvlr
+ ,{0.00} // Dgen
+ ,{0.00} // Dsep
+ ,{0.00} // Dflr
+ ,{0.00} // Dvlr
+ ,{0.0032737} // Em
+ ,{1.0} // use_gen
+ ,{1.0} // use_sep
+ ,{1.0} // use_flr
+ ,{1.0} // use_vlr
+ };
+ LMI_TEST(materially_equal(0.0032737, z.ig_usual()[0], 0.0000125));
+}
+
+void i7702_test::test1()
+{
+}
+
+int test_main(int, char*[])
+{
+ i7702_test::test();
+ return EXIT_SUCCESS;
+}
diff --git a/objects.make b/objects.make
index dd982f2..1aa392f 100644
--- a/objects.make
+++ b/objects.make
@@ -283,6 +283,7 @@ lmi_common_objects := \
gpt_state.o \
gpt_xml_document.o \
i7702.o \
+ i7702_init.o \
ihs_acctval.o \
ihs_avdebug.o \
ihs_avmly.o \
@@ -430,6 +431,7 @@ unit_test_targets := \
global_settings_test \
gpt_test \
handle_exceptions_test \
+ i7702_test \
ieee754_test \
input_sequence_test \
input_test \
@@ -684,6 +686,11 @@ handle_exceptions_test$(EXEEXT): \
$(common_test_objects) \
handle_exceptions_test.o \
+i7702_test$(EXEEXT): \
+ $(common_test_objects) \
+ i7702.o \
+ i7702_test.o \
+
ieee754_test$(EXEEXT): \
$(common_test_objects) \
ieee754_test.o \
- [lmi-commits] [lmi] master updated (60d683a -> f2a0fce), Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master e52df17 1/6: Establish a data member to control i7702 trace, Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master 468d06c 3/6: Record speed measurements, Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master bcd3f13 5/6: Improve i7702 trace, Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master f2a0fce 6/6: Fix defect introduced 20050114T1947Z: wrong DCV NAAR discount, Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master 790cfc3 4/6: Expunge DB_BonusInt, Greg Chicares, 2021/03/16
- [lmi-commits] [lmi] master b677016 2/6: Add a 7702i unit test,
Greg Chicares <=