lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 88138c0 1/2: Add and unit-test input-sequence


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 88138c0 1/2: Add and unit-test input-sequence canonical form [396]
Date: Fri, 24 Feb 2017 22:53:51 -0500 (EST)

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

    Add and unit-test input-sequence canonical form [396]
    
    Updated the unit-test expected results by mechanically transforming
      value [begin, end);
    to
      value end;
---
 input_sequence.cpp      | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
 input_sequence.hpp      |   2 +
 input_sequence_test.cpp |  48 ++++++++-----------
 3 files changed, 145 insertions(+), 28 deletions(-)

diff --git a/input_sequence.cpp b/input_sequence.cpp
index a79b842..162451c 100644
--- a/input_sequence.cpp
+++ b/input_sequence.cpp
@@ -238,6 +238,129 @@ void InputSequence::initialize_from_vector(std::vector<T> 
const& v)
 
 InputSequence::~InputSequence() = default;
 
+/// Canonical form of an input sequence
+///
+/// Consider the set S of all strings that are well formed wrt the
+/// grammar defined by class SequenceParser. Some are equivalent:
+/// e.g., "1 [0, maturity)" and "1" mean the same thing. Choosing one
+/// member of each equivalence class defines a canonical form.
+///
+/// The canonical form need not be a string if a better representation
+/// can be found. For example, it might appear obvious that any member
+/// of S can be transformed into this member of the present class:
+///   std::vector<ValueInterval> intervals_;
+/// yet that is not necessarily the case: this sequence
+///   0, @65; 10000
+/// is well formed, but (as this is written in 2017-02) would not be
+/// permitted for a 70-year-old. Even if that obstacle is overcome,
+/// a std::vector<ValueInterval> seems a poor choice because it is
+/// intricate and unwieldy: it would be uncouth to serialize that into
+/// xml or ask humans to deal with it. A simple, compact, readable
+/// string is wanted instead.
+///
+/// Another unsuccessful candidate for the canonical form would have
+/// specified left-closed and right-open intervals in full. Thus,
+///   0, retirement; 10000, #10; 0
+/// would have been canonicalized as
+///   0 [0, retirement); 10000 [retirement, #10); 0 [?, maturity)
+/// But then the last interval must begin at "retirement + 10", which
+/// is not allowed by the grammar and therefore cannot be canonical.
+/// At first, it had seemed possible to work around this by changing
+/// the parser, to forbid
+///   e_number_of_years == ValueInterval.begin_mode
+/// by replacing that mode with the most recent differing begin_mode:
+/// thus, {"@50" + "#10"} and {"5" + "#10"} would become "@60" and 15
+/// respectively. However, that experiment failed because "retirement"
+/// is neither an age nor a duration. It might be deemed to signify an
+/// age, but that would lose the variable nature of the retirement-age
+/// ctor argument, inaptly treating the case above as equivalent to
+///   0, retirement; 10000, @C; 0
+/// where C is 75 for retirement at age 65, 72...at age 62, and so on.
+/// Specifying the original string
+///   0, retirement; 10000, #10; 0
+/// at the group level, for a census with differing retirement ages,
+/// encompasses that variation; no canonicalization that loses that
+/// advantage is acceptable.
+
+std::string InputSequence::canonical_form() const
+{
+    std::ostringstream oss;
+    for(auto const& interval_i : intervals_)
+        {
+        if(interval_i.value_is_keyword)
+            {
+            oss << interval_i.value_keyword;
+            }
+        else
+            {
+            oss << value_cast<std::string>(interval_i.value_number);
+            }
+
+        if(1 == intervals_.size())
+            {
+            break;
+            }
+
+        std::string s;
+        switch(interval_i.end_mode)
+            {
+            case e_invalid_mode:
+                {
+                fatal_error() << "Invalid mode." << LMI_FLUSH;
+                }
+                break;
+            case e_duration:
+                {
+                int const z = interval_i.end_duration;
+                s = " " + value_cast<std::string>(z);
+                }
+                break;
+            case e_attained_age:
+                {
+                int const z = interval_i.end_duration + issue_age_;
+                s = " @" + value_cast<std::string>(z);
+                }
+                break;
+            case e_number_of_years:
+                {
+                int const z = interval_i.end_duration - 
interval_i.begin_duration;
+                s = " #" + value_cast<std::string>(z);
+                }
+                break;
+            case e_inception:
+                {
+                fatal_error() << "Interval ended at inception." << LMI_FLUSH;
+                }
+                break;
+            case e_inforce:
+                {
+                fatal_error() << "'e_inforce' not implemented." << LMI_FLUSH;
+                }
+                break;
+            case e_retirement:
+                {
+                s = " retirement";
+                }
+                break;
+            case e_maturity:
+                {
+                s = " maturity"; // Generally omitted.
+                }
+                break;
+            }
+
+        if(interval_i.end_duration != years_to_maturity_)
+            {
+            oss << s << "; ";
+            }
+        else
+            {
+            ; // Do nothing.
+            }
+        }
+    return oss.str();
+}
+
 std::vector<double> const& InputSequence::linear_number_representation() const
 {
     return number_result_;
diff --git a/input_sequence.hpp b/input_sequence.hpp
index c2b3a83..ba586e5 100644
--- a/input_sequence.hpp
+++ b/input_sequence.hpp
@@ -161,6 +161,8 @@ class LMI_SO InputSequence
 
     ~InputSequence();
 
+    std::string canonical_form() const;
+
     std::vector<double>      const& linear_number_representation()  const;
     std::vector<std::string> const& linear_keyword_representation() const;
 
diff --git a/input_sequence_test.cpp b/input_sequence_test.cpp
index 9ceacaa..57ea721 100644
--- a/input_sequence_test.cpp
+++ b/input_sequence_test.cpp
@@ -77,7 +77,7 @@ void check
             std::cout << std::endl;
             }
 
-        std::string const& f = seq.mathematical_representation();
+        std::string const& f = seq.canonical_form();
         bool const bf = g == f;
         if(!bf)
             {
@@ -87,13 +87,7 @@ void check
             std::cout << std::endl;
             }
 
-//#define TEST_REPRESENTATION
-#if defined TEST_REPRESENTATION
         INVOKE_BOOST_TEST(bv && bs && bf, file, line);
-#else  // !defined TEST_REPRESENTATION
-        // Don't make 'bf' fail the test yet.
-        INVOKE_BOOST_TEST(bv && bs /* && bf */, file, line);
-#endif // !defined TEST_REPRESENTATION
         }
     catch(std::exception const& x)
         {
@@ -178,7 +172,7 @@ int test_main(int, char*[])
     std::string const e("1 3; 7 5;0");
     census += e + "\t\t\t\n";
     // '1 3; 7 5; 0'
-    std::string const g("1 [0, 3); 7 [3, 5); 0 [5, maturity)");
+    std::string const g("1 3; 7 5; 0");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -190,7 +184,7 @@ int test_main(int, char*[])
     std::string const e("1; 2; 3");
     census += e + "\t\t\t\n";
     // '1 1; 2 2; 3'
-    std::string const g("1 [0, 1); 2 [1, 2); 3 [2, maturity)");
+    std::string const g("1 1; 2 2; 3");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -201,7 +195,7 @@ int test_main(int, char*[])
     std::string const e("1 3; 3 6; 5 9; 7");
     census += e + "\t\t\t\n";
     // '1 3; 3 6; 5 9; 7'
-    std::string const g("1 [0, 3); 3 [3, 6); 5 [6, 9); 7 [9, maturity)");
+    std::string const g("1 3; 3 6; 5 9; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -212,7 +206,7 @@ int test_main(int, char*[])
     std::string const e("1 @93; 3 @96; 5 @99; 7");
     census += e + "\t\t\t\n";
     // '1 @93; 3 @96; 5 @99; 7'
-    std::string const g("1 [0, @93); 3 address@hidden, @96); 5 address@hidden, 
@99); 7 address@hidden, maturity)");
+    std::string const g("1 @93; 3 @96; 5 @99; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -223,7 +217,7 @@ int test_main(int, char*[])
     std::string const e("1 #3; 3 #3; 5 #3; 7");
     census += e + "\t\t\t\n";
     // '1 #3; 3 #3; 5 #3; 7'
-    std::string const g("1 [0, #3); 3 [3, #3); 5 [6, #3); 7 [9, maturity)");
+    std::string const g("1 #3; 3 #3; 5 #3; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -236,7 +230,7 @@ int test_main(int, char*[])
     std::string const e("1 [0, 2); 3 [2, 5); 5 [5, 6); 7");
     census += e + "\t\t\t\n";
     // '1 2; 3 5; 5 6; 7'
-    std::string const g("1 [0, 2); 3 [2, 5); 5 [5, 6); 7 [6, maturity)");
+    std::string const g("1 2; 3 5; 5 6; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -248,7 +242,7 @@ int test_main(int, char*[])
     census += e + "\t\t\t\n";
     // '1 1; 1 3; 3 6; 5 7; 7'
     // Should the first two intervals be combined?
-    std::string const g("1 [0, 1); 1 [1, 3); 3 [3, 6); 5 [6, 7); 7 [7, 
maturity)");
+    std::string const g("1 1; 1 3; 3 6; 5 7; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -259,7 +253,7 @@ int test_main(int, char*[])
     std::string const e("1 [0, 4); 2 5; 3 #1; 4 @97; 5");
     census += e + "\t\t\t\n";
     // '1 4; 2 5; 3 #1; 4 @97; 5'
-    std::string const g("1 [0, 4); 2 [4, 5); 3 [5, #1); 4 [6, @97); 5 
address@hidden, maturity)");
+    std::string const g("1 4; 2 5; 3 #1; 4 @97; 5");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -270,7 +264,7 @@ int test_main(int, char*[])
     std::string const e("1 [0, 1); 3 [1, 2); 5 (1, 2]; 7");
     census += e + "\t\t\t\n";
     // '1 1; 3 2; 5 3; 7'
-    std::string const g("1 [0, 1); 3 [1, 2); 5 [2, 3); 7 [3, maturity)");
+    std::string const g("1 1; 3 2; 5 3; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -330,7 +324,7 @@ int test_main(int, char*[])
     std::string const e("1 [1, 2); 3 [3, 3]; 5 (4, 5]; 7");
     census += e + "\t\t\t\n";
     // '0 1; 1 2; 0 3; 3 4; 0 5; 5 6; 7'
-    std::string const g("0 [0, 1); 1 [1, 2); 0 [2, 3); 3 [3, 4); 0 [4, 5); 5 
[5, 6); 7 [6, maturity)");
+    std::string const g("0 1; 1 2; 0 3; 3 4; 0 5; 5 6; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -340,7 +334,7 @@ int test_main(int, char*[])
     double const d[n] = {0, 1, 1, 3, 3, 5, 5, 7, 7};
     std::string const e("0; 1 (0, 8]; 3 (2, 7]; 5 (4, 6]; 7");
     // census: invalid expression cannot be pasted into GUI
-    std::string const g("0 [0, 1); 1 [1, 9); 3 [3, 8); 5 [5, 7); 7 [7, 
maturity)");
+    std::string const g("0 1; 1 9; 3 8; 5 7; 7");
     char const* m =
         "Interval [ 9, 3 ) is improper: it ends before it begins."
         ;
@@ -372,7 +366,7 @@ int test_main(int, char*[])
     std::string const e("12 [1, @92); 27 address@hidden, @93]; 1 (@94, #1]; 
7");
     census += e + "\t\t\t\n";
     // '0 1; 12 @92; 0 @93; 27 @94; 0 @95; 1 #1; 7'
-    std::string const g("0 [0, 1); 12 [1, @92); 0 address@hidden, @93); 27 
address@hidden, @94); 0 address@hidden, @95); 1 address@hidden, #1); 7 
address@hidden, maturity)");
+    std::string const g("0 1; 12 @92; 0 @93; 27 @94; 0 @95; 1 #1; 7");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
@@ -385,12 +379,10 @@ int test_main(int, char*[])
     std::string const e("12.25 [1,@92); 27.875 address@hidden,@93]; 
1.0625(@94,#1]; 7.5");
     census += e + "\t\t\t\n";
     // '0 1; 12.25 @92; 0 @93; 27.875 @94; 0 @95; 1.0625 #1; 7.5'
-    std::string const g("0 [0, 1); 12.25 [1, @92); 0 address@hidden, @93); 
27.875 address@hidden, @94); 0 address@hidden, @95); 1.0625 address@hidden, 
#1); 7.5 address@hidden, maturity)");
+    std::string const g("0 1; 12.25 @92; 0 @93; 27.875 @94; 0 @95; 1.0625 #1; 
7.5");
     check(__FILE__, __LINE__, n, d, e, g);
     }
 
-    // TODO ?? Test against canonical representation once we define that.
-
     // Test construction from numeric vector.
     {
     std::vector<double> const v{1, 1, 1, 2, 2};
@@ -434,7 +426,7 @@ int test_main(int, char*[])
     std::string const e("p[0, 2); rrr [2, 4);q[4, 6);");
     census += "glp[0, 2); target [2, 4);gsp[4, 6);\t\t\t\n";
     // 'glp 2; target 4; gsp'
-    std::string const g("p [0, 2); rrr [2, 4); q [4, maturity)");
+    std::string const g("p 2; rrr 4; q");
     strvec const k{"not_used", "p", "q", "r", "rr", "rrr"};
     check(__FILE__, __LINE__, n, d, e, g, "", k, c);
     // Toggle keywords-only switch on: same result.
@@ -454,7 +446,7 @@ int test_main(int, char*[])
     std::string const e("1 [0, 2); keyword_00 [2, 4); 5 [4, 6); 7");
     census += "1 [0, 2); corridor [2, 4); 5 [4, 6); 7\t\t\t\n";
     // '1 2; corridor 4; 5 6; 7'
-    std::string const g("1 [0, 2); keyword_00 [2, 4); 5 [4, 6); 7 [6, 
maturity)");
+    std::string const g("1 2; keyword_00 4; 5 6; 7");
     strvec const k{"keyword_00"};
     check(__FILE__, __LINE__, n, d, e, g, "", k, c);
     }
@@ -517,7 +509,7 @@ int test_main(int, char*[])
     std::string const e("q [0, 2); p [4, maturity)");
     census += "\tquarterly [0, 2); monthly [4, maturity)\t\t\n";
     // 'quarterly 2; annual 4; monthly'
-    std::string const g("q [0, 2); z [2, 4); p [4, maturity)");
+    std::string const g("q 2; z 4; p");
     strvec const k{"p", "q", "z"};
     bool const o = true;
     std::string w("z");
@@ -560,7 +552,7 @@ int test_main(int, char*[])
     std::string const e("q [1, 3); p [3, maturity)");
     census += "\tquarterly [1, 3); monthly [3, maturity)\t\t\n";
     // 'annual 1; quarterly 3; monthly'
-    std::string const g("z [0, 1); q [1, 3); p [3, maturity)");
+    std::string const g("z 1; q 3; p");
     strvec const k{"p", "q", "z"};
     bool const o = true;
     std::string w("z");
@@ -579,7 +571,7 @@ int test_main(int, char*[])
     std::string const e("q [1, 3); p [3, maturity)");
     census += "sevenpay [1, 3); glp [3, maturity)\t\t\t\n";
     // '0 1; sevenpay 3; glp'
-    std::string const g("0 [0, 1); q [1, 3); p [3, maturity)");
+    std::string const g("0 1; q 3; p");
     strvec const k{"p", "q", "z"};
     check(__FILE__, __LINE__, n, d, e, g, "", k, c);
     }
@@ -591,7 +583,7 @@ int test_main(int, char*[])
     std::string const e("7, retirement; 4, maturity");
     census += e + "\t\t\t\n";
     // '7 retirement; 4'
-    std::string const g("7 [0, retirement); 4 [retirement, maturity)");
+    std::string const g("7 retirement; 4");
     check(__FILE__, __LINE__, n, d, e, g);
     InputSequence const seq(e, 10, 90, 95, 0, 2002);
     std::vector<ValueInterval> const& i(seq.interval_representation());



reply via email to

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