1 // (C) Copyright John Maddock 2006.
2 // Use, modification and distribution are subject to the
3 // Boost Software License, Version 1.0. (See accompanying file
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 #ifndef BOOST_MATH_TOOLS_TEST_DATA_HPP
7 #define BOOST_MATH_TOOLS_TEST_DATA_HPP
13 #include <boost/math/tools/config.hpp>
14 #include <boost/assert.hpp>
16 # pragma warning(push)
17 # pragma warning(disable: 4127 4701 4512)
18 # pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
20 #include <boost/algorithm/string/trim.hpp>
21 #include <boost/lexical_cast.hpp>
25 #include <boost/type_traits/is_floating_point.hpp>
26 #include <boost/type_traits/is_convertible.hpp>
27 #include <boost/type_traits/integral_constant.hpp>
28 #include <boost/tr1/random.hpp>
29 #include <boost/math/tools/tuple.hpp>
30 #include <boost/math/tools/real_cast.hpp>
37 # pragma warning(push)
38 # pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
39 // Used as a warning with BOOST_ASSERT
42 namespace boost{ namespace math{ namespace tools{
47 periodic_in_range = 1,
52 parameter_type operator | (parameter_type a, parameter_type b)
54 return static_cast<parameter_type>((int)a|(int)b);
56 parameter_type& operator |= (parameter_type& a, parameter_type b)
58 a = static_cast<parameter_type>(a|b);
63 // If type == random_in_range then
64 // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
66 // If type == periodic_in_range then
67 // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
69 // If type == power_series then
70 // n1 and n2 are the endpoints of the exponents (closed range) and z1 is the basis.
72 // If type & dummy_param then this data is ignored and not stored in the output, it
73 // is passed to the generator function however which can do with it as it sees fit.
84 inline parameter_info<T> make_random_param(T start_range, T end_range, int n_points)
86 parameter_info<T> result = { random_in_range, start_range, end_range, n_points, 0 };
91 inline parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points)
93 parameter_info<T> result = { periodic_in_range, start_range, end_range, n_points, 0 };
98 inline parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent)
100 parameter_info<T> result = { power_series, basis, 0, start_exponent, end_exponent };
106 template <class Seq, class Item, int N>
107 inline void unpack_and_append_tuple(Seq& s,
109 const boost::integral_constant<int, N>&,
110 const boost::false_type&)
112 // termimation condition nothing to do here
115 template <class Seq, class Item, int N>
116 inline void unpack_and_append_tuple(Seq& s,
118 const boost::integral_constant<int, N>&,
119 const boost::true_type&)
121 // extract the N'th element, append, and recurse:
122 typedef typename Seq::value_type value_type;
123 value_type val = boost::math::get<N>(data);
126 typedef boost::integral_constant<int, N+1> next_value;
127 typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > N+1)> terminate;
129 unpack_and_append_tuple(s, data, next_value(), terminate());
132 template <class Seq, class Item>
133 inline void unpack_and_append(Seq& s, const Item& data, const boost::true_type&)
138 template <class Seq, class Item>
139 inline void unpack_and_append(Seq& s, const Item& data, const boost::false_type&)
141 // Item had better be a tuple-like type or we've had it!!!!
142 typedef boost::integral_constant<int, 0> next_value;
143 typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > 0)> terminate;
145 unpack_and_append_tuple(s, data, next_value(), terminate());
148 template <class Seq, class Item>
149 inline void unpack_and_append(Seq& s, const Item& data)
151 typedef typename Seq::value_type value_type;
152 unpack_and_append(s, data, ::boost::is_convertible<Item, value_type>());
161 typedef std::vector<T> row_type;
162 typedef row_type value_type;
164 typedef std::set<row_type> container_type;
166 typedef typename container_type::reference reference;
167 typedef typename container_type::const_reference const_reference;
168 typedef typename container_type::iterator iterator;
169 typedef typename container_type::const_iterator const_iterator;
170 typedef typename container_type::difference_type difference_type;
171 typedef typename container_type::size_type size_type;
176 test_data(F func, const parameter_info<T>& arg1)
183 test_data& insert(F func, const parameter_info<T>& arg1)
185 // generate data for single argument functor F
187 typedef typename std::set<T>::const_iterator it_type;
190 create_test_points(points, arg1);
191 it_type a = points.begin();
192 it_type b = points.end();
196 if((arg1.type & dummy_param) == 0)
199 // domain_error exceptions from func are swallowed
200 // and this data point is ignored:
201 boost::math::tools::detail::unpack_and_append(row, func(*a));
204 catch(const std::domain_error&){}
212 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2)
214 // generate data for 2-argument functor F
216 typedef typename std::set<T>::const_iterator it_type;
218 std::set<T> points1, points2;
219 create_test_points(points1, arg1);
220 create_test_points(points2, arg2);
221 it_type a = points1.begin();
222 it_type b = points1.end();
226 it_type c = points2.begin();
227 it_type d = points2.end();
230 if((arg1.type & dummy_param) == 0)
232 if((arg2.type & dummy_param) == 0)
235 // domain_error exceptions from func are swallowed
236 // and this data point is ignored:
237 detail::unpack_and_append(row, func(*a, *c));
240 catch(const std::domain_error&){}
250 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3)
252 // generate data for 3-argument functor F
254 typedef typename std::set<T>::const_iterator it_type;
256 std::set<T> points1, points2, points3;
257 create_test_points(points1, arg1);
258 create_test_points(points2, arg2);
259 create_test_points(points3, arg3);
260 it_type a = points1.begin();
261 it_type b = points1.end();
265 it_type c = points2.begin();
266 it_type d = points2.end();
269 it_type e = points3.begin();
270 it_type f = points3.end();
273 if((arg1.type & dummy_param) == 0)
275 if((arg2.type & dummy_param) == 0)
277 if((arg3.type & dummy_param) == 0)
280 // domain_error exceptions from func are swallowed
281 // and this data point is ignored:
282 detail::unpack_and_append(row, func(*a, *c, *e));
285 catch(const std::domain_error&){}
296 void clear(){ m_data.clear(); }
299 iterator begin() { return m_data.begin(); }
300 iterator end() { return m_data.end(); }
301 const_iterator begin()const { return m_data.begin(); }
302 const_iterator end()const { return m_data.end(); }
303 bool operator==(const test_data& d)const{ return m_data == d.m_data; }
304 bool operator!=(const test_data& d)const{ return m_data != d.m_data; }
305 void swap(test_data& other){ m_data.swap(other.m_data); }
306 size_type size()const{ return m_data.size(); }
307 size_type max_size()const{ return m_data.max_size(); }
308 bool empty()const{ return m_data.empty(); }
310 bool operator < (const test_data& dat)const{ return m_data < dat.m_data; }
311 bool operator <= (const test_data& dat)const{ return m_data <= dat.m_data; }
312 bool operator > (const test_data& dat)const{ return m_data > dat.m_data; }
313 bool operator >= (const test_data& dat)const{ return m_data >= dat.m_data; }
316 void create_test_points(std::set<T>& points, const parameter_info<T>& arg1);
317 std::set<row_type> m_data;
319 static float extern_val;
320 static float truncate_to_float(float const * pf);
321 static float truncate_to_float(float c){ return truncate_to_float(&c); }
325 // This code exists to bemuse the compiler's optimizer and force a
326 // truncation to float-precision only:
329 inline float test_data<T>::truncate_to_float(float const * pf)
333 float f = floor(ldexp(frexp(*pf, &expon), 22));
334 f = ldexp(f, expon - 22);
342 float test_data<T>::extern_val = 0;
345 void test_data<T>::create_test_points(std::set<T>& points, const parameter_info<T>& arg1)
349 // Generate a set of test points as requested, try and generate points
350 // at only float precision: otherwise when testing float versions of functions
351 // there will be a rounding error in our input values which throws off the results
352 // (Garbage in garbage out etc).
354 switch(arg1.type & 0x7F)
356 case random_in_range:
358 BOOST_ASSERT(arg1.z1 < arg1.z2);
359 BOOST_ASSERT(arg1.n1 > 0);
360 typedef float random_type;
362 std::tr1::mt19937 rnd;
363 std::tr1::uniform_real<random_type> ur_a(real_cast<random_type>(arg1.z1), real_cast<random_type>(arg1.z2));
364 std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_real<random_type> > gen(rnd, ur_a);
366 for(int i = 0; i < arg1.n1; ++i)
368 random_type r = gen();
369 points.insert(truncate_to_float(r));
373 case periodic_in_range:
375 BOOST_ASSERT(arg1.z1 < arg1.z2);
376 BOOST_ASSERT(arg1.n1 > 0);
377 float interval = real_cast<float>((arg1.z2 - arg1.z1) / arg1.n1);
381 points.insert(truncate_to_float(real_cast<float>(val)));
388 BOOST_ASSERT(arg1.n1 < arg1.n2);
390 typedef float random_type;
391 typedef typename boost::mpl::if_<
392 ::boost::is_floating_point<T>,
393 T, long double>::type power_type;
395 std::tr1::mt19937 rnd;
396 std::tr1::uniform_real<random_type> ur_a(1.0, 2.0);
397 std::tr1::variate_generator<std::tr1::mt19937, std::tr1::uniform_real<random_type> > gen(rnd, ur_a);
399 for(int power = arg1.n1; power <= arg1.n2; ++power)
401 random_type r = gen();
402 power_type p = ldexp(static_cast<power_type>(r), power);
403 points.insert(truncate_to_float(real_cast<float>(arg1.z1 + p)));
408 BOOST_ASSERT(0 == "Invalid parameter_info object");
409 // Assert will fail if get here.
410 // Triggers warning 4130) // '==' : logical operation on address of string constant.
415 // Prompt a user for information on a parameter range:
418 bool get_user_parameter_info(parameter_info<T>& info, const char* param_name)
421 # pragma warning(push)
422 # pragma warning(disable: 4127)
426 std::cout << "What kind of distribution do you require for parameter " << param_name << "?\n"
428 " r Random values in a half open range\n"
429 " p Evenly spaced periodic values in a half open range\n"
430 " e Exponential power series at a particular point: a + 2^b for some range of b\n"
433 std::getline(std::cin, line);
434 boost::algorithm::trim(line);
438 info.type = random_in_range;
443 info.type = periodic_in_range;
448 info.type = power_series;
453 info.type = random_in_range;
457 // Ooops, not a valid input....
459 std::cout << "Sorry don't recognise \"" << line << "\" as a valid input\n"
460 "do you want to try again [y/n]?";
461 std::getline(std::cin, line);
462 boost::algorithm::trim(line);
467 std::cout << "Sorry don't recognise that either, giving up...\n\n";
471 switch(info.type & ~dummy_param)
473 case random_in_range:
474 case periodic_in_range:
475 // get start and end points of range:
477 std::cout << "Data will be in the half open range a <= x < b,\n"
478 "enter value for the start point fo the range [default=0]:";
479 std::getline(std::cin, line);
480 boost::algorithm::trim(line);
487 info.z1 = boost::lexical_cast<T>(line);
490 catch(const boost::bad_lexical_cast&)
492 std::cout << "Sorry, that was not valid input, try again [y/n]?";
493 std::getline(std::cin, line);
494 boost::algorithm::trim(line);
499 std::cout << "Sorry don't recognise that either, giving up...\n\n";
504 std::cout << "Enter value for the end point fo the range [default=1]:";
505 std::getline(std::cin, line);
506 boost::algorithm::trim(line);
515 info.z2 = boost::lexical_cast<T>(line);
517 catch(const boost::bad_lexical_cast&)
519 std::cout << "Sorry, that was not valid input, try again [y/n]?";
520 std::getline(std::cin, line);
521 boost::algorithm::trim(line);
526 std::cout << "Sorry don't recognise that either, giving up...\n\n";
530 if(info.z1 >= info.z2)
532 std::cout << "The end point of the range was <= the start point\n"
533 "try a different value for the endpoint [y/n]?";
534 std::getline(std::cin, line);
535 boost::algorithm::trim(line);
540 std::cout << "Sorry don't recognise that either, giving up...\n\n";
546 // get the number of points:
547 std::cout << "How many data points do you want?";
548 std::getline(std::cin, line);
549 boost::algorithm::trim(line);
551 info.n1 = boost::lexical_cast<int>(line);
555 std::cout << "The number of points should be > 0\n"
557 std::getline(std::cin, line);
558 boost::algorithm::trim(line);
563 std::cout << "Sorry don't recognise that either, giving up...\n\n";
568 catch(const boost::bad_lexical_cast&)
570 std::cout << "Sorry, that was not valid input, try again [y/n]?";
571 std::getline(std::cin, line);
572 boost::algorithm::trim(line);
577 std::cout << "Sorry don't recognise that either, giving up...\n\n";
583 // get start and end points of range:
586 std::cout << "Data will be in the form a + r*2^b\n"
587 "for random value r,\n"
588 "enter value for the point a [default=0]:";
589 std::getline(std::cin, line);
590 boost::algorithm::trim(line);
597 info.z1 = boost::lexical_cast<T>(line);
600 catch(const boost::bad_lexical_cast&)
602 std::cout << "Sorry, that was not valid input, try again [y/n]?";
603 std::getline(std::cin, line);
604 boost::algorithm::trim(line);
609 std::cout << "Sorry don't recognise that either, giving up...\n\n";
615 std::cout << "Data will be in the form a + r*2^b\n"
616 "for random value r,\n"
617 "enter value for the starting exponent b:";
618 std::getline(std::cin, line);
619 boost::algorithm::trim(line);
621 info.n1 = boost::lexical_cast<int>(line);
624 catch(const boost::bad_lexical_cast&)
626 std::cout << "Sorry, that was not valid input, try again [y/n]?";
627 std::getline(std::cin, line);
628 boost::algorithm::trim(line);
633 std::cout << "Sorry don't recognise that either, giving up...\n\n";
639 std::cout << "Data will be in the form a + r*2^b\n"
640 "for random value r,\n"
641 "enter value for the ending exponent b:";
642 std::getline(std::cin, line);
643 boost::algorithm::trim(line);
645 info.n2 = boost::lexical_cast<int>(line);
648 catch(const boost::bad_lexical_cast&)
650 std::cout << "Sorry, that was not valid input, try again [y/n]?";
651 std::getline(std::cin, line);
652 boost::algorithm::trim(line);
657 std::cout << "Sorry don't recognise that either, giving up...\n\n";
664 BOOST_ASSERT(0); // should never get here!!
669 # pragma warning(pop)
673 template <class charT, class traits, class T>
674 inline std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
675 const test_data<T>& data)
677 const charT defarg[] = { ',', ' ', '\0' };
678 return write_csv(os, data, defarg);
681 template <class charT, class traits, class T>
682 std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
683 const test_data<T>& data,
684 const charT* separator)
686 typedef typename test_data<T>::const_iterator it_type;
687 typedef typename test_data<T>::value_type value_type;
688 typedef typename value_type::const_iterator value_type_iterator;
694 value_type_iterator x, y;
713 std::ostream& write_code(std::ostream& os,
714 const test_data<T>& data,
717 typedef typename test_data<T>::const_iterator it_type;
718 typedef typename test_data<T>::value_type value_type;
719 typedef typename value_type::const_iterator value_type_iterator;
721 BOOST_ASSERT(os.good());
729 os << "#ifndef SC_\n# define SC_(x) static_cast<T>(BOOST_JOIN(x, L))\n#endif\n"
730 " static const boost::array<boost::array<T, "
731 << a->size() << ">, " << data.size() << "> " << name << " = {{\n";
735 if(a != data.begin())
738 value_type_iterator x, y;
746 os << "SC_(" << *x << ")";
752 os << "\n }};\n//#undef SC_\n\n";
765 #endif // BOOST_MATH_TOOLS_TEST_DATA_HPP