lmi-commits
[Top][All Lists]
Advanced

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



reply via email to

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