| // (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/tr1/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 = std::tr1::get<N>(data); |
| s.push_back(val); |
| |
| typedef boost::integral_constant<int, N+1> next_value; |
| typedef boost::integral_constant<bool, (std::tr1::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, (std::tr1::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 << "#define SC_(x) static_cast<T>(BOOST_JOIN(x, L))\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 |
| |
| |