lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master f1cd162 1/3: Implement and unit-test a set of


From: Greg Chicares
Subject: [lmi-commits] [lmi] master f1cd162 1/3: Implement and unit-test a set of DBO rules
Date: Tue, 17 Dec 2019 13:02:13 -0500 (EST)

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

    Implement and unit-test a set of DBO rules
    
    For the time being, one set of DBO rules is hardcoded. Eventually, rules
    will be taken from the product database.
---
 Makefile.am        |  11 +++++
 dbo_rules.cpp      | 123 ++++++++++++++++++++++++++++++++++++++++++++++
 dbo_rules.hpp      |  37 ++++++++++++++
 dbo_rules_test.cpp | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 objects.make       |  10 ++++
 5 files changed, 321 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index fce017e..b470bba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -99,6 +99,7 @@ TESTS = \
     test_contains \
     test_crc32 \
     test_currency \
+    test_dbo_rules \
     test_expression_template_0 \
     test_fenv_lmi \
     test_file_command \
@@ -681,6 +682,16 @@ test_currency_SOURCES = \
   timer.cpp
 test_currency_CXXFLAGS = $(AM_CXXFLAGS)
 
+test_dbo_rules_SOURCES = \
+  $(common_test_objects) \
+  dbo_rules.cpp \
+  dbo_rules_test.cpp \
+  facets.cpp \
+  mc_enum.cpp \
+  mc_enum_types.cpp \
+  timer.cpp
+test_dbo_rules_CXXFLAGS = $(AM_CXXFLAGS)
+
 test_expression_template_0_SOURCES = \
   $(common_test_objects) \
   expression_template_0_test.cpp \
diff --git a/dbo_rules.cpp b/dbo_rules.cpp
new file mode 100644
index 0000000..94bc6d4
--- /dev/null
+++ b/dbo_rules.cpp
@@ -0,0 +1,123 @@
+// Death benefit option (DBO) rules.
+//
+// Copyright (C) 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 "dbo_rules.hpp"
+
+#include "alert.hpp"
+#include "mc_enum.hpp"
+#include "ssize_lmi.hpp"
+
+#include <array>
+
+namespace
+{
+/// Permitted transitions among death benefit options.
+///
+/// Rows: "from" state; columns: "to" state.
+///
+/// The "zero" state is a special "from"-only state that is not part
+/// of the mcenum_dbopt enumeration. It represents an empty state
+/// before the policy comes into existence. Thus, the "zero" row
+/// specifies the states available upon policy issue. In this example,
+/// "ROP" is entirely forbidden.
+///
+/// It's simplest to view this as a partitioned matrix (below), the
+/// "zero" row being a special case. The square submatrix's main
+/// diagonal could just as well be all 1's: "ROP" --> "ROP" is a
+/// transition from a forbidden state to the same state, so it can
+/// never arise anyway. But it's preferable to forbid whatever is
+/// never permitted, even if it is impossible.
+///
+/// This (augmented) matrix implies this graph:
+///
+///          +---+
+///   +----> |MDB| <--+--+
+///   |      +---+    ^  ^
+///   |        ^      |  |
+///   |        |      |  |
+/// +---+    +---+ >--+  |
+/// | 0 | -> | A |       |
+/// +---+    +---+ <--+  |
+///   |        ^      ^  |
+///   |        |      |  |
+///   |      +---+ >--+  |
+///   +----> | B |       |
+///          +---+ >-----+
+///
+/// C++ WG21 defect 1270 suggests eliding the outermost superfluous
+/// braces, but g++ 8.x doesn't implement that. The innermost sets
+/// of superfluous braces are written deliberately so that the
+/// initializer cannot match
+///   std::array<std::array<bool,5>,4>
+/// whose indices look like the correct ones in the declaration
+///   bool M1[5][4]
+/// but actually need to be specified in a different order.
+
+std::array<std::array<bool,4>,5> M1 =
+    // A   B  ROP MDB
+    {{{1,  1,  0,  1,}, // "zero" state before issue
+    // -------------- // partitioned: square matrix follows...
+      {1,  0,  0,  1,}, // A
+      {1,  1,  0,  1,}, // B
+      {0,  0,  0,  0,}, // ROP
+      {0,  0,  0,  1,}, // MDB
+     }};
+} // Unnamed namespace.
+
+bool dbo_at_issue_is_allowed(mce_dbopt z)
+{
+    return M1[0][z.ordinal()];
+}
+
+bool dbo_transition_is_allowed(mce_dbopt from, mce_dbopt to)
+{
+    return M1[1 + from.ordinal()][to.ordinal()];
+}
+
+// The return value is fairly useless, and may later be removed.
+
+bool dbo_sequence_is_allowed(std::vector<mce_dbopt> v)
+{
+    if(0 == lmi::ssize(v))
+        {
+        alarum() << "DBO must not be empty." << LMI_FLUSH;
+        return false;
+        }
+    if(!dbo_at_issue_is_allowed(v[0]))
+        {
+        alarum() << "Forbidden initial DBO '" << v[0] << "'." << LMI_FLUSH;
+        return false;
+        }
+    for(int j = 1; j < lmi::ssize(v); ++j)
+        if(!dbo_transition_is_allowed(v[j - 1], v[j]))
+            {
+            alarum()
+                << "Forbidden DBO change from '" << v[j - 1]
+                << "' to '" << v[j]
+                << "' after " << j << " years."
+                << LMI_FLUSH
+                ;
+            return false;
+            }
+    return true;
+}
diff --git a/dbo_rules.hpp b/dbo_rules.hpp
new file mode 100644
index 0000000..589dcff
--- /dev/null
+++ b/dbo_rules.hpp
@@ -0,0 +1,37 @@
+// Death benefit option (DBO) rules.
+//
+// Copyright (C) 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 dbo_rules_hpp
+#define dbo_rules_hpp
+
+#include "config.hpp"
+
+#include "pchfile.hpp"
+
+#include "mc_enum_types.hpp"
+
+#include <vector>
+
+bool dbo_at_issue_is_allowed(mce_dbopt);
+bool dbo_transition_is_allowed(mce_dbopt from, mce_dbopt to);
+bool dbo_sequence_is_allowed(std::vector<mce_dbopt>);
+
+#endif // dbo_rules_hpp
diff --git a/dbo_rules_test.cpp b/dbo_rules_test.cpp
new file mode 100644
index 0000000..d23417f
--- /dev/null
+++ b/dbo_rules_test.cpp
@@ -0,0 +1,140 @@
+// Death benefit option (DBO) rules--unit test.
+//
+// Copyright (C) 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 "dbo_rules.hpp"
+
+#include "mc_enum.hpp"
+#include "mc_enum_types.hpp"
+#include "test_tools.hpp"
+#include "timer.hpp"
+
+#include <vector>
+
+int test_main(int, char*[])
+{
+    BOOST_TEST( dbo_at_issue_is_allowed(mce_dbopt("A"  )));
+    BOOST_TEST( dbo_at_issue_is_allowed(mce_dbopt("B"  )));
+    BOOST_TEST(!dbo_at_issue_is_allowed(mce_dbopt("ROP")));
+    BOOST_TEST( dbo_at_issue_is_allowed(mce_dbopt("MDB")));
+
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("A"  ), mce_dbopt("A"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("A"  ), mce_dbopt("B"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("A"  ), mce_dbopt("ROP")));
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("A"  ), mce_dbopt("MDB")));
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("B"  ), mce_dbopt("A"  )));
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("B"  ), mce_dbopt("B"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("B"  ), mce_dbopt("ROP")));
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("B"  ), mce_dbopt("MDB")));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("ROP"), mce_dbopt("A"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("ROP"), mce_dbopt("B"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("ROP"), mce_dbopt("ROP")));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("ROP"), mce_dbopt("MDB")));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("MDB"), mce_dbopt("A"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("MDB"), mce_dbopt("B"  )));
+    BOOST_TEST(!dbo_transition_is_allowed(mce_dbopt("MDB"), mce_dbopt("ROP")));
+    BOOST_TEST( dbo_transition_is_allowed(mce_dbopt("MDB"), mce_dbopt("MDB")));
+
+    {
+    std::vector<mce_dbopt> v = {};
+    BOOST_TEST_THROW
+        (dbo_sequence_is_allowed(v)
+        ,std::runtime_error
+        ,"DBO must not be empty."
+        );
+    }
+
+    {
+    std::vector<mce_dbopt> v = {mce_dbopt("MDB")};
+    BOOST_TEST(dbo_sequence_is_allowed(v));
+    }
+
+    {
+    std::vector<mce_dbopt> v = {mce_dbopt("ROP")};
+    BOOST_TEST_THROW
+        (dbo_sequence_is_allowed(v)
+        ,std::runtime_error
+        ,"Forbidden initial DBO 'ROP'."
+        );
+    }
+
+    {
+    std::vector<mce_dbopt> v =
+        {mce_dbopt("B")
+        ,mce_dbopt("A")
+        ,mce_dbopt("MDB")
+        };
+    BOOST_TEST(dbo_sequence_is_allowed(v));
+    }
+
+    {
+    std::vector<mce_dbopt> v =
+        {mce_dbopt("A")
+        ,mce_dbopt("B")
+        };
+    BOOST_TEST_THROW
+        (dbo_sequence_is_allowed(v)
+        ,std::runtime_error
+        ,"Forbidden DBO change from 'A' to 'B' after 1 years."
+        );
+    }
+
+    {
+    std::vector<mce_dbopt> v =
+        {mce_dbopt("B")
+        ,mce_dbopt("B")
+        ,mce_dbopt("B")
+        ,mce_dbopt("A")
+        ,mce_dbopt("A")
+        ,mce_dbopt("MDB")
+        ,mce_dbopt("MDB")
+        ,mce_dbopt("ROP")
+        ,mce_dbopt("MDB")
+        };
+    BOOST_TEST_THROW
+        (dbo_sequence_is_allowed(v)
+        ,std::runtime_error
+        ,"Forbidden DBO change from 'MDB' to 'ROP' after 7 years."
+        );
+    }
+
+    {
+    std::vector<mce_dbopt> v =
+        {mce_dbopt("B")
+        ,mce_dbopt("B")
+        ,mce_dbopt("B")
+        ,mce_dbopt("A")
+        ,mce_dbopt("A")
+        ,mce_dbopt("MDB")
+        };
+    v.resize(100, mce_dbopt("MDB"));
+    auto f = [&] {dbo_sequence_is_allowed(v);};
+    std::cout
+        << "\n  Speed test..."
+        << "\n  DBO changes: " << TimeAnAliquot(f)
+        << std::endl
+        ;
+    (dbo_sequence_is_allowed(v));
+    }
+
+    return 0;
+}
diff --git a/objects.make b/objects.make
index d9172ed..46d0d2c 100644
--- a/objects.make
+++ b/objects.make
@@ -421,6 +421,7 @@ unit_test_targets := \
   contains_test \
   crc32_test \
   currency_test \
+  dbo_rules_test \
   expression_template_0_test \
   fenv_lmi_test \
   file_command_test \
@@ -606,6 +607,15 @@ currency_test$(EXEEXT): \
   currency_test.o \
   timer.o \
 
+dbo_rules_test$(EXEEXT): \
+  $(common_test_objects) \
+  dbo_rules.o \
+  dbo_rules_test.o \
+  facets.o \
+  mc_enum.o \
+  mc_enum_types.o \
+  timer.o \
+
 expression_template_0_test$(EXEEXT): \
   $(common_test_objects) \
   expression_template_0_test.o \



reply via email to

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