[Top][All Lists]
[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) \
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [5467] Experimentally implement loading of xml tables (VS),
Greg Chicares <=