123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- // (C) Copyright John Maddock 2006.
- // Use, modification and distribution are subject to the
- // Boost Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_MATH_TOOLS_TEST_DATA_HPP
- #define BOOST_MATH_TOOLS_TEST_DATA_HPP
- #ifdef _MSC_VER
- #pragma once
- #endif
- #include <boost/math/tools/config.hpp>
- #include <boost/assert.hpp>
- #ifdef BOOST_MSVC
- # pragma warning(push)
- # pragma warning(disable: 4127 4701 4512)
- # pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
- #endif
- #include <boost/algorithm/string/trim.hpp>
- #include <boost/lexical_cast.hpp>
- #ifdef BOOST_MSVC
- #pragma warning(pop)
- #endif
- #include <boost/type_traits/is_floating_point.hpp>
- #include <boost/type_traits/is_convertible.hpp>
- #include <boost/type_traits/integral_constant.hpp>
- #include <boost/tr1/random.hpp>
- #include <boost/math/tools/tuple.hpp>
- #include <boost/math/tools/real_cast.hpp>
- #include <set>
- #include <vector>
- #include <iostream>
- #ifdef BOOST_MSVC
- # pragma warning(push)
- # pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
- // Used as a warning with BOOST_ASSERT
- #endif
- namespace boost{ namespace math{ namespace tools{
- enum parameter_type
- {
- random_in_range = 0,
- periodic_in_range = 1,
- power_series = 2,
- dummy_param = 0x80
- };
- parameter_type operator | (parameter_type a, parameter_type b)
- {
- return static_cast<parameter_type>((int)a|(int)b);
- }
- parameter_type& operator |= (parameter_type& a, parameter_type b)
- {
- a = static_cast<parameter_type>(a|b);
- return a;
- }
- //
- // If type == random_in_range then
- // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
- //
- // If type == periodic_in_range then
- // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
- //
- // If type == power_series then
- // n1 and n2 are the endpoints of the exponents (closed range) and z1 is the basis.
- //
- // If type & dummy_param then this data is ignored and not stored in the output, it
- // is passed to the generator function however which can do with it as it sees fit.
- //
- template <class T>
- struct parameter_info
- {
- parameter_type type;
- T z1, z2;
- int n1, n2;
- };
- template <class T>
- inline parameter_info<T> make_random_param(T start_range, T end_range, int n_points)
- {
- parameter_info<T> result = { random_in_range, start_range, end_range, n_points, 0 };
- return result;
- }
- template <class T>
- inline parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points)
- {
- parameter_info<T> result = { periodic_in_range, start_range, end_range, n_points, 0 };
- return result;
- }
- template <class T>
- inline parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent)
- {
- parameter_info<T> result = { power_series, basis, 0, start_exponent, end_exponent };
- return result;
- }
- namespace detail{
- template <class Seq, class Item, int N>
- inline void unpack_and_append_tuple(Seq& s,
- const Item& data,
- const boost::integral_constant<int, N>&,
- const boost::false_type&)
- {
- // termimation condition nothing to do here
- }
- template <class Seq, class Item, int N>
- inline void unpack_and_append_tuple(Seq& s,
- const Item& data,
- const boost::integral_constant<int, N>&,
- const boost::true_type&)
- {
- // extract the N'th element, append, and recurse:
- typedef typename Seq::value_type value_type;
- value_type val = boost::math::get<N>(data);
- s.push_back(val);
- typedef boost::integral_constant<int, N+1> next_value;
- typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > N+1)> terminate;
- unpack_and_append_tuple(s, data, next_value(), terminate());
- }
- template <class Seq, class Item>
- inline void unpack_and_append(Seq& s, const Item& data, const boost::true_type&)
- {
- s.push_back(data);
- }
- template <class Seq, class Item>
- inline void unpack_and_append(Seq& s, const Item& data, const boost::false_type&)
- {
- // Item had better be a tuple-like type or we've had it!!!!
- typedef boost::integral_constant<int, 0> next_value;
- typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > 0)> terminate;
- unpack_and_append_tuple(s, data, next_value(), terminate());
- }
- template <class Seq, class Item>
- inline void unpack_and_append(Seq& s, const Item& data)
- {
- typedef typename Seq::value_type value_type;
- unpack_and_append(s, data, ::boost::is_convertible<Item, value_type>());
- }
- } // detail
- template <class T>
- class test_data
- {
- public:
- typedef std::vector<T> row_type;
- typedef row_type value_type;
- private:
- typedef std::set<row_type> container_type;
- public:
- typedef typename container_type::reference reference;
- typedef typename container_type::const_reference const_reference;
- typedef typename container_type::iterator iterator;
- typedef typename container_type::const_iterator const_iterator;
- typedef typename container_type::difference_type difference_type;
- typedef typename container_type::size_type size_type;
- // creation:
- test_data(){}
- template <class F>
- test_data(F func, const parameter_info<T>& arg1)
- {
- insert(func, arg1);
- }
- // insertion:
- template <class F>
- test_data& insert(F func, const parameter_info<T>& arg1)
- {
- // generate data for single argument functor F
- typedef typename std::set<T>::const_iterator it_type;
- std::set<T> points;
- create_test_points(points, arg1);
- it_type a = points.begin();
- it_type b = points.end();
- row_type row;
- while(a != b)
- {
- if((arg1.type & dummy_param) == 0)
- row.push_back(*a);
- try{
- // domain_error exceptions from func are swallowed
- // and this data point is ignored:
- boost::math::tools::detail::unpack_and_append(row, func(*a));
- m_data.insert(row);
- }
- catch(const std::domain_error&){}
- row.clear();
- ++a;
- }
- return *this;
- }
- template <class F>
- test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2)
- {
- // generate data for 2-argument functor F
- typedef typename std::set<T>::const_iterator it_type;
- std::set<T> points1, points2;
- create_test_points(points1, arg1);
- create_test_points(points2, arg2);
- it_type a = points1.begin();
- it_type b = points1.end();
- row_type row;
- while(a != b)
- {
- it_type c = points2.begin();
- it_type d = points2.end();
- while(c != d)
- {
- if((arg1.type & dummy_param) == 0)
- row.push_back(*a);
- if((arg2.type & dummy_param) == 0)
- row.push_back(*c);
- try{
- // domain_error exceptions from func are swallowed
- // and this data point is ignored:
- detail::unpack_and_append(row, func(*a, *c));
- m_data.insert(row);
- }
- catch(const std::domain_error&){}
- row.clear();
- ++c;
- }
- ++a;
- }
- return *this;
- }
- template <class F>
- test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3)
- {
- // generate data for 3-argument functor F
- typedef typename std::set<T>::const_iterator it_type;
- std::set<T> points1, points2, points3;
- create_test_points(points1, arg1);
- create_test_points(points2, arg2);
- create_test_points(points3, arg3);
- it_type a = points1.begin();
- it_type b = points1.end();
- row_type row;
- while(a != b)
- {
- it_type c = points2.begin();
- it_type d = points2.end();
- while(c != d)
- {
- it_type e = points3.begin();
- it_type f = points3.end();
- while(e != f)
- {
- if((arg1.type & dummy_param) == 0)
- row.push_back(*a);
- if((arg2.type & dummy_param) == 0)
- row.push_back(*c);
- if((arg3.type & dummy_param) == 0)
- row.push_back(*e);
- try{
- // domain_error exceptions from func are swallowed
- // and this data point is ignored:
- detail::unpack_and_append(row, func(*a, *c, *e));
- m_data.insert(row);
- }
- catch(const std::domain_error&){}
- row.clear();
- ++e;
- }
- ++c;
- }
- ++a;
- }
- return *this;
- }
- void clear(){ m_data.clear(); }
- // access:
- iterator begin() { return m_data.begin(); }
- iterator end() { return m_data.end(); }
- const_iterator begin()const { return m_data.begin(); }
- const_iterator end()const { return m_data.end(); }
- bool operator==(const test_data& d)const{ return m_data == d.m_data; }
- bool operator!=(const test_data& d)const{ return m_data != d.m_data; }
- void swap(test_data& other){ m_data.swap(other.m_data); }
- size_type size()const{ return m_data.size(); }
- size_type max_size()const{ return m_data.max_size(); }
- bool empty()const{ return m_data.empty(); }
- bool operator < (const test_data& dat)const{ return m_data < dat.m_data; }
- bool operator <= (const test_data& dat)const{ return m_data <= dat.m_data; }
- bool operator > (const test_data& dat)const{ return m_data > dat.m_data; }
- bool operator >= (const test_data& dat)const{ return m_data >= dat.m_data; }
- private:
- void create_test_points(std::set<T>& points, const parameter_info<T>& arg1);
- std::set<row_type> m_data;
- static float extern_val;
- static float truncate_to_float(float const * pf);
- static float truncate_to_float(float c){ return truncate_to_float(&c); }
- };
- //
- // This code exists to bemuse the compiler's optimizer and force a
- // truncation to float-precision only:
- //
- template <class T>
- inline float test_data<T>::truncate_to_float(float const * pf)
- {
- BOOST_MATH_STD_USING
- int expon;
- float f = floor(ldexp(frexp(*pf, &expon), 22));
- f = ldexp(f, expon - 22);
- return f;
- //extern_val = *pf;
- //return *pf;
- }
- template <class T>
- float test_data<T>::extern_val = 0;
- template <class T>
- void test_data<T>::create_test_points(std::set<T>& points, const parameter_info<T>& arg1)
- {
- BOOST_MATH_STD_USING
- //
- // Generate a set of test points as requested, try and generate points
- // at only float precision: otherwise when testing float versions of functions
- // there will be a rounding error in our input values which throws off the results
- // (Garbage in garbage out etc).
- //
- switch(arg1.type & 0x7F)
- {
- case random_in_range:
- {
- BOOST_ASSERT(arg1.z1 < arg1.z2);
- BOOST_ASSERT(arg1.n1 > 0);
- typedef float random_type;
- std::tr1::mt19937 rnd;
- std::tr1::uniform_real<random_type> ur_a(real_cast<random_type>(arg1.z1), real_cast<random_type>(arg1.z2));
- std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_real<random_type> > gen(rnd, ur_a);
- for(int i = 0; i < arg1.n1; ++i)
- {
- random_type r = gen();
- points.insert(truncate_to_float(r));
- }
- }
- break;
- case periodic_in_range:
- {
- BOOST_ASSERT(arg1.z1 < arg1.z2);
- BOOST_ASSERT(arg1.n1 > 0);
- float interval = real_cast<float>((arg1.z2 - arg1.z1) / arg1.n1);
- T val = arg1.z1;
- while(val < arg1.z2)
- {
- points.insert(truncate_to_float(real_cast<float>(val)));
- val += interval;
- }
- }
- break;
- case power_series:
- {
- BOOST_ASSERT(arg1.n1 < arg1.n2);
- typedef float random_type;
- typedef typename boost::mpl::if_<
- ::boost::is_floating_point<T>,
- T, long double>::type power_type;
- std::tr1::mt19937 rnd;
- std::tr1::uniform_real<random_type> ur_a(1.0, 2.0);
- std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_real<random_type> > gen(rnd, ur_a);
- for(int power = arg1.n1; power <= arg1.n2; ++power)
- {
- random_type r = gen();
- power_type p = ldexp(static_cast<power_type>(r), power);
- points.insert(truncate_to_float(real_cast<float>(arg1.z1 + p)));
- }
- }
- break;
- default:
- BOOST_ASSERT(0 == "Invalid parameter_info object");
- // Assert will fail if get here.
- // Triggers warning 4130) // '==' : logical operation on address of string constant.
- }
- }
- //
- // Prompt a user for information on a parameter range:
- //
- template <class T>
- bool get_user_parameter_info(parameter_info<T>& info, const char* param_name)
- {
- #ifdef BOOST_MSVC
- # pragma warning(push)
- # pragma warning(disable: 4127)
- #endif
- std::string line;
- do{
- std::cout << "What kind of distribution do you require for parameter " << param_name << "?\n"
- "Choices are:\n"
- " r Random values in a half open range\n"
- " p Evenly spaced periodic values in a half open range\n"
- " e Exponential power series at a particular point: a + 2^b for some range of b\n"
- "[Default=r]";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "r")
- {
- info.type = random_in_range;
- break;
- }
- else if(line == "p")
- {
- info.type = periodic_in_range;
- break;
- }
- else if(line == "e")
- {
- info.type = power_series;
- break;
- }
- else if(line == "")
- {
- info.type = random_in_range;
- break;
- }
- //
- // Ooops, not a valid input....
- //
- std::cout << "Sorry don't recognise \"" << line << "\" as a valid input\n"
- "do you want to try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "n")
- return false;
- else if(line == "y")
- continue;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }while(true);
- switch(info.type & ~dummy_param)
- {
- case random_in_range:
- case periodic_in_range:
- // get start and end points of range:
- do{
- std::cout << "Data will be in the half open range a <= x < b,\n"
- "enter value for the start point fo the range [default=0]:";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "")
- {
- info.z1 = 0;
- break;
- }
- try{
- info.z1 = boost::lexical_cast<T>(line);
- break;
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }while(true);
- do{
- std::cout << "Enter value for the end point fo the range [default=1]:";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "")
- {
- info.z2 = 1;
- }
- else
- {
- try
- {
- info.z2 = boost::lexical_cast<T>(line);
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }
- if(info.z1 >= info.z2)
- {
- std::cout << "The end point of the range was <= the start point\n"
- "try a different value for the endpoint [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- break;
- }while(true);
- do{
- // get the number of points:
- std::cout << "How many data points do you want?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- try{
- info.n1 = boost::lexical_cast<int>(line);
- info.n2 = 0;
- if(info.n1 <= 0)
- {
- std::cout << "The number of points should be > 0\n"
- "try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- break;
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }while(true);
- break;
- case power_series:
- // get start and end points of range:
- info.z2 = 0;
- do{
- std::cout << "Data will be in the form a + r*2^b\n"
- "for random value r,\n"
- "enter value for the point a [default=0]:";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "")
- {
- info.z1 = 0;
- break;
- }
- try{
- info.z1 = boost::lexical_cast<T>(line);
- break;
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }while(true);
- do{
- std::cout << "Data will be in the form a + r*2^b\n"
- "for random value r,\n"
- "enter value for the starting exponent b:";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- try{
- info.n1 = boost::lexical_cast<int>(line);
- break;
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }while(true);
- do{
- std::cout << "Data will be in the form a + r*2^b\n"
- "for random value r,\n"
- "enter value for the ending exponent b:";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- try{
- info.n2 = boost::lexical_cast<int>(line);
- break;
- }
- catch(const boost::bad_lexical_cast&)
- {
- std::cout << "Sorry, that was not valid input, try again [y/n]?";
- std::getline(std::cin, line);
- boost::algorithm::trim(line);
- if(line == "y")
- continue;
- if(line == "n")
- return false;
- std::cout << "Sorry don't recognise that either, giving up...\n\n";
- return false;
- }
- }while(true);
- break;
- default:
- BOOST_ASSERT(0); // should never get here!!
- }
- return true;
- #ifdef BOOST_MSVC
- # pragma warning(pop)
- #endif
- }
- template <class charT, class traits, class T>
- inline std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
- const test_data<T>& data)
- {
- const charT defarg[] = { ',', ' ', '\0' };
- return write_csv(os, data, defarg);
- }
- template <class charT, class traits, class T>
- std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
- const test_data<T>& data,
- const charT* separator)
- {
- typedef typename test_data<T>::const_iterator it_type;
- typedef typename test_data<T>::value_type value_type;
- typedef typename value_type::const_iterator value_type_iterator;
- it_type a, b;
- a = data.begin();
- b = data.end();
- while(a != b)
- {
- value_type_iterator x, y;
- bool sep = false;
- x = a->begin();
- y = a->end();
- while(x != y)
- {
- if(sep)
- os << separator;
- os << *x;
- sep = true;
- ++x;
- }
- os << std::endl;
- ++a;
- }
- return os;
- }
- template <class T>
- std::ostream& write_code(std::ostream& os,
- const test_data<T>& data,
- const char* name)
- {
- typedef typename test_data<T>::const_iterator it_type;
- typedef typename test_data<T>::value_type value_type;
- typedef typename value_type::const_iterator value_type_iterator;
- BOOST_ASSERT(os.good());
- it_type a, b;
- a = data.begin();
- b = data.end();
- if(a == b)
- return os;
- os << "#ifndef SC_\n# define SC_(x) static_cast<T>(BOOST_JOIN(x, L))\n#endif\n"
- " static const boost::array<boost::array<T, "
- << a->size() << ">, " << data.size() << "> " << name << " = {{\n";
- while(a != b)
- {
- if(a != data.begin())
- os << ", \n";
- value_type_iterator x, y;
- x = a->begin();
- y = a->end();
- os << " { ";
- while(x != y)
- {
- if(x != a->begin())
- os << ", ";
- os << "SC_(" << *x << ")";
- ++x;
- }
- os << " }";
- ++a;
- }
- os << "\n }};\n//#undef SC_\n\n";
- return os;
- }
- } // namespace tools
- } // namespace math
- } // namespace boost
- #ifdef BOOST_MSVC
- #pragma warning(pop)
- #endif
- #endif // BOOST_MATH_TOOLS_TEST_DATA_HPP
|