blob: 3949f8352bbcc2bdcb614fe1452d00c8763a13b8 [file] [log] [blame]
// (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 << "#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