lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [5467] Experimentally implement loading of xml tables (VS)


From: Greg Chicares
Subject: [lmi-commits] [5467] Experimentally implement loading of xml tables (VS)
Date: Sun, 27 May 2012 16:04:17 +0000

Revision: 5467
          http://svn.sv.gnu.org/viewvc/?view=rev&root=lmi&revision=5467
Author:   chicares
Date:     2012-05-27 16:04:17 +0000 (Sun, 27 May 2012)
Log Message:
-----------
Experimentally implement loading of xml tables (VS)

Modified Paths:
--------------
    lmi/trunk/actuarial_table.cpp
    lmi/trunk/actuarial_table.hpp
    lmi/trunk/actuarial_table_test.cpp
    lmi/trunk/objects.make

Modified: lmi/trunk/actuarial_table.cpp
===================================================================
--- lmi/trunk/actuarial_table.cpp       2012-05-26 12:29:33 UTC (rev 5466)
+++ lmi/trunk/actuarial_table.cpp       2012-05-27 16:04:17 UTC (rev 5467)
@@ -33,6 +33,8 @@
 #include "miscellany.hpp"
 #include "oecumenic_enumerations.hpp" // methuselah
 #include "path_utility.hpp" // fs::path inserter
+#include "value_cast.hpp"
+#include "xml_lmi.hpp"
 
 #include <boost/cstdint.hpp>
 #include <boost/filesystem/convenience.hpp>
@@ -40,11 +42,14 @@
 #include <boost/filesystem/path.hpp>
 #include <boost/static_assert.hpp>
 
+#include <xmlwrapp/nodes_view.h>
+
 #include <algorithm> // std::max(), std::min()
 #include <cctype>    // std::toupper()
 #include <climits>   // CHAR_BIT
 #include <ios>
 #include <istream>
+#include <iterator>  // std::distance()
 #include <limits>
 
 namespace
@@ -175,6 +180,322 @@
         }
 }
 
+actuarial_table::actuarial_table(std::string const& filename, int table_number)
+{
+    // SOA !! This is temporary code for API compatibility with 
soa_actuarial_table.
+    // It should be changed so that the constructor takes only a single
+    // argument, filename of the XML table file.
+    std::string xmlfile(filename, 0, filename.rfind('.'));
+    xmlfile += "_";
+    xmlfile += value_cast<std::string>(table_number);
+    xmlfile += ".xtable";
+    load_xml_table(xmlfile);
+}
+
+actuarial_table::~actuarial_table()
+{
+}
+
+void actuarial_table::load_xml_table(std::string const& filename)
+{
+    xml_lmi::dom_parser parser(filename);
+    xml::element root(parser.root_node("table"));
+
+    // SOA !! Implement loading of multi-dimensional tables as well.
+    // SOA !! Upgrade xmlwrapp:
+    // XMLWRAPP !! This should be const_iterator, but xmlwrapp < 0.7 lacks the
+    // necessary operator!= for comparing const and non-const iterators.
+    xml::node::iterator i;
+
+    if (root.end() != (i = root.find("aggregate")))
+        {
+        load_xml_aggregate_table(*i);
+        }
+    else if (root.end() != (i = root.find("duration")))
+        {
+        load_xml_duration_table(*i);
+        }
+    else if (root.end() != (i = root.find("select")))
+        {
+        load_xml_select_table(*i);
+        }
+    else if (root.end() != (i = root.find("select-and-ultimate")))
+        {
+        load_xml_select_and_ultimate_table(*i);
+        }
+    else
+        {
+        fatal_error()
+            << "Required data element not found."
+            << LMI_FLUSH
+            ;
+        }
+}
+
+void actuarial_table::load_xml_table_with_ages
+    (xml::element const& node
+    ,std::vector<double>& data
+    ,int& min_age
+    ,int& max_age
+    )
+{
+    xml::const_nodes_view const values = node.elements("value");
+
+    data.reserve(std::distance(values.begin(), values.end()));
+
+    min_age = max_age = -1;
+
+    typedef xml::const_nodes_view::const_iterator cnvi;
+    for(cnvi i = values.begin(); i != values.end(); ++i)
+        {
+        data.push_back(value_cast<double>(xml_lmi::get_content(*i)));
+        int age;
+        if(!xml_lmi::get_attr(*i, "age", age))
+            {
+            fatal_error()
+                << "XML <value> node doesn't have 'age' attribute."
+                << LMI_FLUSH
+                ;
+            }
+        if(-1 == min_age)
+            {
+            min_age = age;
+            }
+        else
+            {
+            if(max_age + 1 != age)
+                {
+                fatal_error()
+                    << "XML <value> node has 'age' attribute value '"
+                    << age
+                    << "' but '"
+                    << max_age + 1
+                    << "' was expected."
+                    << LMI_FLUSH
+                    ;
+                }
+            }
+        max_age = age;
+        }
+
+    LMI_ASSERT(data.size() == size_t(max_age - min_age + 1));
+}
+
+void actuarial_table::load_xml_aggregate_table(xml::element const& node)
+{
+    load_xml_table_with_ages
+        (node
+        ,data_
+        ,min_age_
+        ,max_age_
+        );
+
+    table_type_ = e_table_aggregate;
+}
+
+void actuarial_table::load_xml_duration_table(xml::element const& node)
+{
+    xml::const_nodes_view const values = node.elements("value");
+
+    data_.reserve(std::distance(values.begin(), values.end()));
+
+    typedef xml::const_nodes_view::const_iterator cnvi;
+    for(cnvi i = values.begin(); i != values.end(); ++i)
+        {
+        data_.push_back(value_cast<double>(xml_lmi::get_content(*i)));
+        }
+
+    table_type_ = e_table_duration;
+}
+
+void actuarial_table::load_xml_select_table(xml::element const& node)
+{
+    xml::const_nodes_view const rows = node.elements("row");
+
+    if(!xml_lmi::get_attr(node, "period", select_period_))
+        {
+        fatal_error()
+            << "XML <select> node doesn't have 'period' attribute."
+            << LMI_FLUSH
+            ;
+        }
+
+    data_.reserve(std::distance(rows.begin(), rows.end()) * select_period_);
+
+    min_age_ = max_age_ = -1;
+
+    typedef xml::const_nodes_view::const_iterator cnvi;
+    for(cnvi i = rows.begin(); i != rows.end(); ++i)
+        {
+        int age;
+        if(!xml_lmi::get_attr(*i, "age", age))
+            {
+            fatal_error()
+                << "XML <row> node doesn't have 'age' attribute."
+                << LMI_FLUSH
+                ;
+            }
+        if(-1 == min_age_)
+            {
+            min_age_ = age;
+            }
+        else
+            {
+            if(max_age_ + 1 != age)
+                {
+                fatal_error()
+                    << "XML <row> node has 'age' attribute value '"
+                    << age
+                    << "' but '"
+                    << max_age_ + 1
+                    << "' was expected."
+                    << LMI_FLUSH
+                    ;
+                }
+            }
+        max_age_ = age;
+
+        xml::const_nodes_view const values = i->elements("value");
+        if(select_period_ != std::distance(values.begin(), values.end()))
+            {
+            fatal_error()
+                << "XML <row> node has "
+                << std::distance(values.begin(), values.end())
+                << " values but select period is "
+                << select_period_
+                << "."
+                << LMI_FLUSH
+                ;
+            }
+
+        for(cnvi v = values.begin(); v != values.end(); ++v)
+            {
+            data_.push_back(value_cast<double>(xml_lmi::get_content(*v)));
+            }
+        }
+
+    max_select_age_ = max_age_;
+
+    LMI_ASSERT(data_.size() == size_t((max_age_ - min_age_ + 1) * 
select_period_));
+
+    // Use the same type for select and select & ultimate tables: selected
+    // table is just a special case of the latter where max_age_ ==
+    // max_select_age_ and the ultimate table is empty.
+    table_type_ = e_table_select_and_ultimate;
+}
+
+void actuarial_table::load_xml_select_and_ultimate_table(xml::element const& 
node)
+{
+    load_xml_select_table(*xml_lmi::retrieve_element(node, "select"));
+
+    int ultimate_min_age;
+    load_xml_table_with_ages
+        (*xml_lmi::retrieve_element(node, "ultimate")
+        ,ultimate_
+        ,ultimate_min_age
+        ,max_age_
+        );
+
+    if(ultimate_min_age != min_age_ + select_period_)
+        {
+        fatal_error()
+            << "Ultimate table should have min. age "
+            << min_age_
+            << ", but has "
+            << ultimate_min_age
+            << "."
+            << LMI_FLUSH
+            ;
+        }
+
+    table_type_ = e_table_select_and_ultimate;
+}
+
+std::vector<double> actuarial_table::specific_values
+    (int issue_age
+    ,int length
+    ) const
+{
+    if(table_type_ != e_table_duration)
+        {
+        // min_age_ and max_age_ are invalid for duration tables
+        LMI_ASSERT(min_age_ <= issue_age && issue_age <= max_age_);
+        LMI_ASSERT(0 <= length && length <= 1 + max_age_ - issue_age);
+        }
+
+    std::vector<double> v;
+    switch(table_type_)
+        {
+        case e_table_aggregate:
+            {
+            // Parenthesize the offsets--addition in C and C++ is
+            // in effect left associative:
+            //   data_.begin() + issue_age - min_age_
+            // means
+            //   (data_.begin() + issue_age) - min_age_
+            // but the subexpression
+            //   (data_.begin() + issue_age)
+            // is likely to return a past-the-end iterator, which
+            // libstdc++'s debug mode will dislike.
+            //
+            v = std::vector<double>
+                (data_.begin() + (issue_age - min_age_)
+                ,data_.begin() + (issue_age - min_age_ + length)
+                );
+            }
+            break;
+        case e_table_duration:
+            {
+            v = std::vector<double>
+                (data_.begin()
+                ,data_.begin() + length
+                );
+            }
+            break;
+        case e_table_select_and_ultimate:
+            {
+            v.resize(length);
+            std::vector<double>::iterator cursor = v.begin();
+
+            int row_to_start_at;
+
+            // Write select portion:
+            if(issue_age < max_select_age_ + select_period_)
+                {
+                row_to_start_at = (std::min(max_select_age_, issue_age) - 
min_age_);
+                int offset_in_row = std::max(0, issue_age - max_select_age_);
+                int k = offset_in_row
+                    +   row_to_start_at * select_period_
+                    ;
+                for(int i = offset_in_row; cursor != v.end() && i < 
select_period_; i++, k++)
+                    *(cursor++) = data_[k];
+                }
+            else
+                {
+                const int min_ultimate_age = min_age_ + select_period_;
+                row_to_start_at = issue_age - min_ultimate_age;
+                }
+
+            // And ultimate:
+            for(int k = row_to_start_at; cursor != v.end(); k++)
+                *(cursor++) = ultimate_[k];
+            }
+            break;
+
+        default:
+            {
+            fatal_error()
+                << "Table type '"
+                << table_type_
+                << "' not recognized: must be one of 'A', 'D', or 'S'."
+                << LMI_FLUSH
+                ;
+            }
+        }
+    LMI_ASSERT(v.size() == static_cast<unsigned int>(length));
+    return v;
+}
+
 soa_actuarial_table::soa_actuarial_table(std::string const& filename, int 
table_number)
     :filename_       (filename)
     ,table_number_   (table_number)
@@ -523,7 +844,7 @@
     std::vector<double> v;
     switch(table_type_)
         {
-        case 'A':
+        case e_table_aggregate:
             {
             // Parenthesize the offsets--addition in C and C++ is
             // in effect left associative:
@@ -541,7 +862,7 @@
                 );
             }
             break;
-        case 'D':
+        case e_table_duration:
             {
             v = std::vector<double>
                 (data_.begin()
@@ -549,7 +870,7 @@
                 );
             }
             break;
-        case 'S':
+        case e_table_select_and_ultimate:
             {
             int const stride = 1 + select_period_;
             int k =
@@ -591,7 +912,7 @@
     ,int                length
     )
 {
-    soa_actuarial_table z(table_filename, table_number);
+    actuarial_table z(table_filename, table_number);
     return z.values(issue_age, length);
 }
 
@@ -605,7 +926,7 @@
     ,int                      reset_duration
     )
 {
-    soa_actuarial_table z(table_filename, table_number);
+    actuarial_table z(table_filename, table_number);
     return z.values_elaborated
         (issue_age
         ,length

Modified: lmi/trunk/actuarial_table.hpp
===================================================================
--- lmi/trunk/actuarial_table.hpp       2012-05-26 12:29:33 UTC (rev 5466)
+++ lmi/trunk/actuarial_table.hpp       2012-05-27 16:04:17 UTC (rev 5467)
@@ -28,6 +28,7 @@
 
 #include "obstruct_slicing.hpp"
 #include "uncopyable_lmi.hpp"
+#include "xml_lmi_fwd.hpp"
 
 #include <iosfwd>
 #include <string>
@@ -115,6 +116,15 @@
     ,e_reenter_upon_rate_reset     = 2
     };
 
+/// Types of actuarial tables.
+
+enum e_table_type
+    {e_table_invalid               = -1
+    ,e_table_aggregate             = 'A'
+    ,e_table_duration              = 'D'
+    ,e_table_select_and_ultimate   = 'S'
+    };
+
 /// Base class for actuarial tables, both XML and binary.
 /// SOA !! This is only temporary, merge with xml_actuarial_table into
 /// single class once we remove binary SOA format support
@@ -151,6 +161,45 @@
     int  max_select_age_ ;
 };
 
+/// Read actuarial table from XML file.
+
+class actuarial_table
+    :        public  actuarial_table_base
+    ,        private lmi::uncopyable <actuarial_table>
+    ,virtual private obstruct_slicing<actuarial_table>
+{
+  public:
+    actuarial_table(std::string const& filename, int table_number);
+    ~actuarial_table();
+
+  protected:
+    std::vector<double> specific_values(int issue_age, int length) const;
+
+  private:
+    void load_xml_table                    (std::string const& filename);
+    void load_xml_aggregate_table          (xml::element const& node);
+    void load_xml_duration_table           (xml::element const& node);
+    void load_xml_select_table             (xml::element const& node);
+    void load_xml_select_and_ultimate_table(xml::element const& node);
+    void load_xml_table_with_ages          (xml::element const& node
+                                            ,std::vector<double>& data
+                                            ,int& min_age
+                                            ,int& max_age
+                                            );
+
+    // Table data. For 1D tables (e_table_aggregate and e_table_duration), this
+    // is the vector of values from min_age_ to max_age_.
+    // For e_table_select_and_ultimate, the content is organized by rows, with
+    // select_period_ entries per row, with rows ranging from min_age_ to
+    // max_select_age_.
+    std::vector<double> data_;
+
+    // For e_table_select_and_ultimate, this vector contains the ultimate
+    // column. The first value, ultimate_[0], is for min_age_+select_period_,
+    // the last is for max_select_age_.
+    std::vector<double> ultimate_;
+};
+
 /// Read a table from a database in the binary format designed by the
 /// Society of Actuaries (SOA) and used for the tables SOA publishes.
 ///
@@ -197,8 +246,6 @@
     std::streampos table_offset_;
 };
 
-typedef soa_actuarial_table actuarial_table;
-
 /// Convenience function: read particular values from a table stored
 /// in the SOA table-manager format.
 

Modified: lmi/trunk/actuarial_table_test.cpp
===================================================================
--- lmi/trunk/actuarial_table_test.cpp  2012-05-26 12:29:33 UTC (rev 5466)
+++ lmi/trunk/actuarial_table_test.cpp  2012-05-27 16:04:17 UTC (rev 5467)
@@ -249,14 +249,14 @@
         (actuarial_table("nonexistent", 0)
         ,std::runtime_error
         ,"There is no table number 0 in file 'nonexistent'."
-        );
+        ); // SOA !! Revise for xml.
 
     BOOST_TEST_THROW
         (actuarial_table("nonexistent", 1)
         ,std::runtime_error
         ,"File 'nonexistent.ndx' is required but could not be found."
          " Try reinstalling."
-        );
+        ); // SOA !! Revise for xml.
 
     std::ifstream ifs((qx_cso + ".ndx").c_str(), ios_in_binary());
     std::ofstream ofs("eraseme.ndx", ios_out_trunc_binary());
@@ -267,7 +267,7 @@
         ,std::runtime_error
         ,"File 'eraseme.dat' is required but could not be found."
          " Try reinstalling."
-        );
+        ); // SOA !! Revise for xml.
     BOOST_TEST(0 == std::remove("eraseme.ndx"));
 
     actuarial_table z(qx_ins, 256);

Modified: lmi/trunk/objects.make
===================================================================
--- lmi/trunk/objects.make      2012-05-26 12:29:33 UTC (rev 5466)
+++ lmi/trunk/objects.make      2012-05-27 16:04:17 UTC (rev 5467)
@@ -540,9 +540,11 @@
 actuarial_table_test$(EXEEXT): \
   $(boost_filesystem_objects) \
   $(common_test_objects) \
+  $(xmlwrapp_objects) \
   actuarial_table.o \
   actuarial_table_test.o \
   timer.o \
+  xml_lmi.o \
 
 alert_test$(EXEEXT): \
   $(common_test_objects) \




reply via email to

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