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