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 #ifndef BOOST_NO_CXX11_HDR_RANDOM
30 namespace random_ns = std;
32 #include <boost/random.hpp>
33 namespace random_ns = boost::random;
35 #include <boost/math/tools/tuple.hpp>
36 #include <boost/math/tools/real_cast.hpp>
43 # pragma warning(push)
44 # pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
45 // Used as a warning with BOOST_ASSERT
48 namespace boost{ namespace math{ namespace tools{
53 periodic_in_range = 1,
60 parameter_type operator | (parameter_type a, parameter_type b)
62 return static_cast<parameter_type>((int)a|(int)b);
64 parameter_type& operator |= (parameter_type& a, parameter_type b)
66 a = static_cast<parameter_type>(a|b);
71 // If type == random_in_range then
72 // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
74 // If type == periodic_in_range then
75 // z1 and r2 are the endpoints of the half open range and n1 is the number of points.
77 // If type == power_series then
78 // n1 and n2 are the endpoints of the exponents (closed range) and z1 is the basis.
80 // If type == single_value then z1 contains the single value to add.
82 // If type == plus_minus_value then test at +-z1
84 // If type & dummy_param then this data is ignored and not stored in the output, it
85 // is passed to the generator function however which can do with it as it sees fit.
96 inline parameter_info<T> make_random_param(T start_range, T end_range, int n_points)
98 parameter_info<T> result = { random_in_range, start_range, end_range, n_points, 0 };
103 inline parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points)
105 parameter_info<T> result = { periodic_in_range, start_range, end_range, n_points, 0 };
110 inline parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent)
112 parameter_info<T> result = { power_series, basis, 0, start_exponent, end_exponent };
117 inline parameter_info<T> make_single_param(T val)
119 parameter_info<T> result = { single_value, val };
124 inline parameter_info<T> make_plus_minus_param(T val)
126 parameter_info<T> result = { plus_minus_value, val };
132 template <class Seq, class Item, int N>
133 inline void unpack_and_append_tuple(Seq&,
135 const boost::integral_constant<int, N>&,
136 const boost::false_type&)
138 // termimation condition nothing to do here
141 template <class Seq, class Item, int N>
142 inline void unpack_and_append_tuple(Seq& s,
144 const boost::integral_constant<int, N>&,
145 const boost::true_type&)
147 // extract the N'th element, append, and recurse:
148 typedef typename Seq::value_type value_type;
149 value_type val = boost::math::get<N>(data);
152 typedef boost::integral_constant<int, N+1> next_value;
153 typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > N+1)> terminate;
155 unpack_and_append_tuple(s, data, next_value(), terminate());
158 template <class Seq, class Item>
159 inline void unpack_and_append(Seq& s, const Item& data, const boost::true_type&)
164 template <class Seq, class Item>
165 inline void unpack_and_append(Seq& s, const Item& data, const boost::false_type&)
167 // Item had better be a tuple-like type or we've had it!!!!
168 typedef boost::integral_constant<int, 0> next_value;
169 typedef boost::integral_constant<bool, (boost::math::tuple_size<Item>::value > 0)> terminate;
171 unpack_and_append_tuple(s, data, next_value(), terminate());
174 template <class Seq, class Item>
175 inline void unpack_and_append(Seq& s, const Item& data)
177 typedef typename Seq::value_type value_type;
178 unpack_and_append(s, data, ::boost::is_convertible<Item, value_type>());
187 typedef std::vector<T> row_type;
188 typedef row_type value_type;
190 typedef std::set<row_type> container_type;
192 typedef typename container_type::reference reference;
193 typedef typename container_type::const_reference const_reference;
194 typedef typename container_type::iterator iterator;
195 typedef typename container_type::const_iterator const_iterator;
196 typedef typename container_type::difference_type difference_type;
197 typedef typename container_type::size_type size_type;
202 test_data(F func, const parameter_info<T>& arg1)
209 test_data& insert(F func, const parameter_info<T>& arg1)
211 // generate data for single argument functor F
213 typedef typename std::set<T>::const_iterator it_type;
216 create_test_points(points, arg1);
217 it_type a = points.begin();
218 it_type b = points.end();
222 if((arg1.type & dummy_param) == 0)
224 #ifndef BOOST_NO_EXCEPTIONS
227 // domain_error exceptions from func are swallowed
228 // and this data point is ignored:
229 boost::math::tools::detail::unpack_and_append(row, func(*a));
231 #ifndef BOOST_NO_EXCEPTIONS
233 catch(const std::domain_error&){}
242 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2)
244 // generate data for 2-argument functor F
246 typedef typename std::set<T>::const_iterator it_type;
248 std::set<T> points1, points2;
249 create_test_points(points1, arg1);
250 create_test_points(points2, arg2);
251 it_type a = points1.begin();
252 it_type b = points1.end();
256 it_type c = points2.begin();
257 it_type d = points2.end();
260 if((arg1.type & dummy_param) == 0)
262 if((arg2.type & dummy_param) == 0)
264 #ifndef BOOST_NO_EXCEPTIONS
267 // domain_error exceptions from func are swallowed
268 // and this data point is ignored:
269 detail::unpack_and_append(row, func(*a, *c));
271 #ifndef BOOST_NO_EXCEPTIONS
273 catch(const std::domain_error&){}
284 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3)
286 // generate data for 3-argument functor F
288 typedef typename std::set<T>::const_iterator it_type;
290 std::set<T> points1, points2, points3;
291 create_test_points(points1, arg1);
292 create_test_points(points2, arg2);
293 create_test_points(points3, arg3);
294 it_type a = points1.begin();
295 it_type b = points1.end();
299 it_type c = points2.begin();
300 it_type d = points2.end();
303 it_type e = points3.begin();
304 it_type f = points3.end();
307 if((arg1.type & dummy_param) == 0)
309 if((arg2.type & dummy_param) == 0)
311 if((arg3.type & dummy_param) == 0)
313 #ifndef BOOST_NO_EXCEPTIONS
316 // domain_error exceptions from func are swallowed
317 // and this data point is ignored:
318 detail::unpack_and_append(row, func(*a, *c, *e));
320 #ifndef BOOST_NO_EXCEPTIONS
322 catch(const std::domain_error&){}
335 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3, const parameter_info<T>& arg4)
337 // generate data for 4-argument functor F
339 typedef typename std::set<T>::const_iterator it_type;
341 std::set<T> points1, points2, points3, points4;
342 create_test_points(points1, arg1);
343 create_test_points(points2, arg2);
344 create_test_points(points3, arg3);
345 create_test_points(points4, arg4);
346 it_type a = points1.begin();
347 it_type b = points1.end();
351 it_type c = points2.begin();
352 it_type d = points2.end();
355 it_type e = points3.begin();
356 it_type f = points3.end();
359 it_type g = points4.begin();
360 it_type h = points4.end();
363 if ((arg1.type & dummy_param) == 0)
365 if ((arg2.type & dummy_param) == 0)
367 if ((arg3.type & dummy_param) == 0)
369 if ((arg4.type & dummy_param) == 0)
371 #ifndef BOOST_NO_EXCEPTIONS
374 // domain_error exceptions from func are swallowed
375 // and this data point is ignored:
376 detail::unpack_and_append(row, func(*a, *c, *e, *g));
378 #ifndef BOOST_NO_EXCEPTIONS
380 catch (const std::domain_error&) {}
395 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3, const parameter_info<T>& arg4, const parameter_info<T>& arg5)
397 // generate data for 5-argument functor F
399 typedef typename std::set<T>::const_iterator it_type;
401 std::set<T> points1, points2, points3, points4, points5;
402 create_test_points(points1, arg1);
403 create_test_points(points2, arg2);
404 create_test_points(points3, arg3);
405 create_test_points(points4, arg4);
406 create_test_points(points5, arg5);
407 it_type a = points1.begin();
408 it_type b = points1.end();
412 it_type c = points2.begin();
413 it_type d = points2.end();
416 it_type e = points3.begin();
417 it_type f = points3.end();
420 it_type g = points4.begin();
421 it_type h = points4.end();
424 it_type i = points5.begin();
425 it_type j = points5.end();
428 if ((arg1.type & dummy_param) == 0)
430 if ((arg2.type & dummy_param) == 0)
432 if ((arg3.type & dummy_param) == 0)
434 if ((arg4.type & dummy_param) == 0)
436 if ((arg5.type & dummy_param) == 0)
438 #ifndef BOOST_NO_EXCEPTIONS
441 // domain_error exceptions from func are swallowed
442 // and this data point is ignored:
443 detail::unpack_and_append(row, func(*a, *c, *e, *g, *i));
445 #ifndef BOOST_NO_EXCEPTIONS
447 catch (const std::domain_error&) {}
463 void clear(){ m_data.clear(); }
466 iterator begin() { return m_data.begin(); }
467 iterator end() { return m_data.end(); }
468 const_iterator begin()const { return m_data.begin(); }
469 const_iterator end()const { return m_data.end(); }
470 bool operator==(const test_data& d)const{ return m_data == d.m_data; }
471 bool operator!=(const test_data& d)const{ return m_data != d.m_data; }
472 void swap(test_data& other){ m_data.swap(other.m_data); }
473 size_type size()const{ return m_data.size(); }
474 size_type max_size()const{ return m_data.max_size(); }
475 bool empty()const{ return m_data.empty(); }
477 bool operator < (const test_data& dat)const{ return m_data < dat.m_data; }
478 bool operator <= (const test_data& dat)const{ return m_data <= dat.m_data; }
479 bool operator > (const test_data& dat)const{ return m_data > dat.m_data; }
480 bool operator >= (const test_data& dat)const{ return m_data >= dat.m_data; }
483 void create_test_points(std::set<T>& points, const parameter_info<T>& arg1);
484 std::set<row_type> m_data;
486 static float extern_val;
487 static float truncate_to_float(float const * pf);
488 static float truncate_to_float(float c){ return truncate_to_float(&c); }
492 // This code exists to bemuse the compiler's optimizer and force a
493 // truncation to float-precision only:
496 inline float test_data<T>::truncate_to_float(float const * pf)
500 float f = floor(ldexp(frexp(*pf, &expon), 22));
501 f = ldexp(f, expon - 22);
509 float test_data<T>::extern_val = 0;
512 void test_data<T>::create_test_points(std::set<T>& points, const parameter_info<T>& arg1)
516 // Generate a set of test points as requested, try and generate points
517 // at only float precision: otherwise when testing float versions of functions
518 // there will be a rounding error in our input values which throws off the results
519 // (Garbage in garbage out etc).
521 switch(arg1.type & 0x7F)
523 case random_in_range:
525 BOOST_ASSERT(arg1.z1 < arg1.z2);
526 BOOST_ASSERT(arg1.n1 > 0);
527 typedef float random_type;
529 random_ns::mt19937 rnd;
530 random_ns::uniform_real_distribution<random_type> ur_a(real_cast<random_type>(arg1.z1), real_cast<random_type>(arg1.z2));
532 for(int i = 0; i < arg1.n1; ++i)
534 random_type r = ur_a(rnd);
535 points.insert(truncate_to_float(r));
539 case periodic_in_range:
541 BOOST_ASSERT(arg1.z1 < arg1.z2);
542 BOOST_ASSERT(arg1.n1 > 0);
543 float interval = real_cast<float>((arg1.z2 - arg1.z1) / arg1.n1);
547 points.insert(truncate_to_float(real_cast<float>(val)));
554 BOOST_ASSERT(arg1.n1 < arg1.n2);
556 typedef float random_type;
557 typedef typename boost::mpl::if_<
558 ::boost::is_floating_point<T>,
559 T, long double>::type power_type;
561 random_ns::mt19937 rnd;
562 random_ns::uniform_real_distribution<random_type> ur_a(1.0, 2.0);
564 for(int power = arg1.n1; power <= arg1.n2; ++power)
566 random_type r = ur_a(rnd);
567 power_type p = ldexp(static_cast<power_type>(r), power);
568 points.insert(truncate_to_float(real_cast<float>(arg1.z1 + p)));
574 points.insert(truncate_to_float(real_cast<float>(arg1.z1)));
577 case plus_minus_value:
579 points.insert(truncate_to_float(real_cast<float>(arg1.z1)));
580 points.insert(truncate_to_float(-real_cast<float>(arg1.z1)));
584 BOOST_ASSERT(0 == "Invalid parameter_info object");
585 // Assert will fail if get here.
586 // Triggers warning 4130) // '==' : logical operation on address of string constant.
591 // Prompt a user for information on a parameter range:
594 bool get_user_parameter_info(parameter_info<T>& info, const char* param_name)
597 # pragma warning(push)
598 # pragma warning(disable: 4127)
602 std::cout << "What kind of distribution do you require for parameter " << param_name << "?\n"
604 " r Random values in a half open range\n"
605 " p Evenly spaced periodic values in a half open range\n"
606 " e Exponential power series at a particular point: a + 2^b for some range of b\n"
609 std::getline(std::cin, line);
610 boost::algorithm::trim(line);
614 info.type = random_in_range;
619 info.type = periodic_in_range;
624 info.type = power_series;
629 info.type = random_in_range;
633 // Ooops, not a valid input....
635 std::cout << "Sorry don't recognise \"" << line << "\" as a valid input\n"
636 "do you want to try again [y/n]?";
637 std::getline(std::cin, line);
638 boost::algorithm::trim(line);
643 std::cout << "Sorry don't recognise that either, giving up...\n\n";
647 switch(info.type & ~dummy_param)
649 case random_in_range:
650 case periodic_in_range:
651 // get start and end points of range:
653 std::cout << "Data will be in the half open range a <= x < b,\n"
654 "enter value for the start point fo the range [default=0]:";
655 std::getline(std::cin, line);
656 boost::algorithm::trim(line);
662 #ifndef BOOST_NO_EXCEPTIONS
665 info.z1 = boost::lexical_cast<T>(line);
667 #ifndef BOOST_NO_EXCEPTIONS
669 catch(const boost::bad_lexical_cast&)
671 std::cout << "Sorry, that was not valid input, try again [y/n]?";
672 std::getline(std::cin, line);
673 boost::algorithm::trim(line);
678 std::cout << "Sorry don't recognise that either, giving up...\n\n";
684 std::cout << "Enter value for the end point fo the range [default=1]:";
685 std::getline(std::cin, line);
686 boost::algorithm::trim(line);
693 #ifndef BOOST_NO_EXCEPTIONS
697 info.z2 = boost::lexical_cast<T>(line);
698 #ifndef BOOST_NO_EXCEPTIONS
700 catch(const boost::bad_lexical_cast&)
702 std::cout << "Sorry, that was not valid input, try again [y/n]?";
703 std::getline(std::cin, line);
704 boost::algorithm::trim(line);
709 std::cout << "Sorry don't recognise that either, giving up...\n\n";
714 if(info.z1 >= info.z2)
716 std::cout << "The end point of the range was <= the start point\n"
717 "try a different value for the endpoint [y/n]?";
718 std::getline(std::cin, line);
719 boost::algorithm::trim(line);
724 std::cout << "Sorry don't recognise that either, giving up...\n\n";
730 // get the number of points:
731 std::cout << "How many data points do you want?";
732 std::getline(std::cin, line);
733 boost::algorithm::trim(line);
734 #ifndef BOOST_NO_EXCEPTIONS
737 info.n1 = boost::lexical_cast<int>(line);
741 std::cout << "The number of points should be > 0\n"
743 std::getline(std::cin, line);
744 boost::algorithm::trim(line);
749 std::cout << "Sorry don't recognise that either, giving up...\n\n";
753 #ifndef BOOST_NO_EXCEPTIONS
755 catch(const boost::bad_lexical_cast&)
757 std::cout << "Sorry, that was not valid input, try again [y/n]?";
758 std::getline(std::cin, line);
759 boost::algorithm::trim(line);
764 std::cout << "Sorry don't recognise that either, giving up...\n\n";
771 // get start and end points of range:
774 std::cout << "Data will be in the form a + r*2^b\n"
775 "for random value r,\n"
776 "enter value for the point a [default=0]:";
777 std::getline(std::cin, line);
778 boost::algorithm::trim(line);
784 #ifndef BOOST_NO_EXCEPTIONS
787 info.z1 = boost::lexical_cast<T>(line);
789 #ifndef BOOST_NO_EXCEPTIONS
791 catch(const boost::bad_lexical_cast&)
793 std::cout << "Sorry, that was not valid input, try again [y/n]?";
794 std::getline(std::cin, line);
795 boost::algorithm::trim(line);
800 std::cout << "Sorry don't recognise that either, giving up...\n\n";
807 std::cout << "Data will be in the form a + r*2^b\n"
808 "for random value r,\n"
809 "enter value for the starting exponent b:";
810 std::getline(std::cin, line);
811 boost::algorithm::trim(line);
812 #ifndef BOOST_NO_EXCEPTIONS
815 info.n1 = boost::lexical_cast<int>(line);
817 #ifndef BOOST_NO_EXCEPTIONS
819 catch(const boost::bad_lexical_cast&)
821 std::cout << "Sorry, that was not valid input, try again [y/n]?";
822 std::getline(std::cin, line);
823 boost::algorithm::trim(line);
828 std::cout << "Sorry don't recognise that either, giving up...\n\n";
835 std::cout << "Data will be in the form a + r*2^b\n"
836 "for random value r,\n"
837 "enter value for the ending exponent b:";
838 std::getline(std::cin, line);
839 boost::algorithm::trim(line);
840 #ifndef BOOST_NO_EXCEPTIONS
843 info.n2 = boost::lexical_cast<int>(line);
845 #ifndef BOOST_NO_EXCEPTIONS
847 catch(const boost::bad_lexical_cast&)
849 std::cout << "Sorry, that was not valid input, try again [y/n]?";
850 std::getline(std::cin, line);
851 boost::algorithm::trim(line);
856 std::cout << "Sorry don't recognise that either, giving up...\n\n";
864 BOOST_ASSERT(0); // should never get here!!
869 # pragma warning(pop)
873 template <class charT, class traits, class T>
874 inline std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
875 const test_data<T>& data)
877 const charT defarg[] = { ',', ' ', '\0' };
878 return write_csv(os, data, defarg);
881 template <class charT, class traits, class T>
882 std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,
883 const test_data<T>& data,
884 const charT* separator)
886 typedef typename test_data<T>::const_iterator it_type;
887 typedef typename test_data<T>::value_type value_type;
888 typedef typename value_type::const_iterator value_type_iterator;
894 value_type_iterator x, y;
913 std::ostream& write_code(std::ostream& os,
914 const test_data<T>& data,
917 typedef typename test_data<T>::const_iterator it_type;
918 typedef typename test_data<T>::value_type value_type;
919 typedef typename value_type::const_iterator value_type_iterator;
921 BOOST_ASSERT(os.good());
929 os << "#ifndef SC_\n# define SC_(x) static_cast<T>(BOOST_JOIN(x, L))\n#endif\n"
930 " static const boost::array<boost::array<T, "
931 << a->size() << ">, " << data.size() << "> " << name << " = {{\n";
935 if(a != data.begin())
938 value_type_iterator x, y;
946 os << "SC_(" << *x << ")";
952 os << "\n }};\n//#undef SC_\n\n";
965 #endif // BOOST_MATH_TOOLS_TEST_DATA_HPP