968 lines · plain
1// (C) Copyright John Maddock 2006.2// Use, modification and distribution are subject to the3// Boost Software License, Version 1.0. (See accompanying file4// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)5 6#ifndef BOOST_MATH_TOOLS_TEST_DATA_HPP7#define BOOST_MATH_TOOLS_TEST_DATA_HPP8 9#ifdef _MSC_VER10#pragma once11#endif12 13#include <boost/math/tools/config.hpp>14#include <boost/math/tools/assert.hpp>15#ifdef _MSC_VER16# pragma warning(push)17# pragma warning(disable: 4127 4701 4512)18# pragma warning(disable: 4130) // '==' : logical operation on address of string constant.19#endif20#include <boost/algorithm/string/trim.hpp>21#include <boost/lexical_cast.hpp>22#ifdef _MSC_VER23#pragma warning(pop)24#endif25#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_RANDOM29#include <random>30namespace random_ns = std;31#else32#include <boost/random.hpp>33namespace random_ns = boost::random;34#endif35#include <boost/math/tools/tuple.hpp>36#include <boost/math/tools/real_cast.hpp>37 38#include <set>39#include <vector>40#include <iostream>41 42#ifdef _MSC_VER43# pragma warning(push)44# pragma warning(disable: 4130) // '==' : logical operation on address of string constant.45// Used as a warning with BOOST_MATH_ASSERT46#endif47 48namespace boost{ namespace math{ namespace tools{49 50enum parameter_type51{52 random_in_range = 0,53 periodic_in_range = 1,54 power_series = 2,55 single_value = 3,56 plus_minus_value = 4,57 dummy_param = 0x8058};59 60parameter_type operator | (parameter_type a, parameter_type b)61{62 return static_cast<parameter_type>((int)a|(int)b);63}64parameter_type& operator |= (parameter_type& a, parameter_type b)65{66 a = static_cast<parameter_type>(a|b);67 return a;68}69 70//71// If type == random_in_range then72// z1 and r2 are the endpoints of the half open range and n1 is the number of points.73//74// If type == periodic_in_range then75// z1 and r2 are the endpoints of the half open range and n1 is the number of points.76//77// If type == power_series then78// n1 and n2 are the endpoints of the exponents (closed range) and z1 is the basis.79//80// If type == single_value then z1 contains the single value to add.81//82// If type == plus_minus_value then test at +-z183//84// If type & dummy_param then this data is ignored and not stored in the output, it85// is passed to the generator function however which can do with it as it sees fit.86//87template <class T>88struct parameter_info89{90 parameter_type type;91 T z1, z2;92 int n1, n2;93};94 95template <class T>96inline parameter_info<T> make_random_param(T start_range, T end_range, int n_points)97{98 parameter_info<T> result = { random_in_range, start_range, end_range, n_points, 0 };99 return result;100}101 102template <class T>103inline parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points)104{105 parameter_info<T> result = { periodic_in_range, start_range, end_range, n_points, 0 };106 return result;107}108 109template <class T>110inline parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent)111{112 parameter_info<T> result = { power_series, basis, 0, start_exponent, end_exponent };113 return result;114}115 116template <class T>117inline parameter_info<T> make_single_param(T val)118{119 parameter_info<T> result = { single_value, val };120 return result;121}122 123template <class T>124inline parameter_info<T> make_plus_minus_param(T val)125{126 parameter_info<T> result = { plus_minus_value, val };127 return result;128}129 130namespace detail{131 132template <class Seq, class Item, int N>133inline void unpack_and_append_tuple(Seq&,134 const Item&,135 const std::integral_constant<int, N>&,136 const std::false_type&)137{138 // termination condition nothing to do here139}140 141template <class Seq, class Item, int N>142inline void unpack_and_append_tuple(Seq& s,143 const Item& data,144 const std::integral_constant<int, N>&,145 const std::true_type&)146{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);150 s.push_back(val);151 152 typedef std::integral_constant<int, N+1> next_value;153 typedef std::integral_constant<bool, (boost::math::tuple_size<Item>::value > N+1)> terminate;154 155 unpack_and_append_tuple(s, data, next_value(), terminate());156}157 158template <class Seq, class Item>159inline void unpack_and_append(Seq& s, const Item& data, const std::true_type&)160{161 s.push_back(data);162}163 164template <class Seq, class Item>165inline void unpack_and_append(Seq& s, const Item& data, const std::false_type&)166{167 // Item had better be a tuple-like type or we've had it!!!!168 typedef std::integral_constant<int, 0> next_value;169 typedef std::integral_constant<bool, (boost::math::tuple_size<Item>::value > 0)> terminate;170 171 unpack_and_append_tuple(s, data, next_value(), terminate());172}173 174template <class Seq, class Item>175inline void unpack_and_append(Seq& s, const Item& data)176{177 typedef typename Seq::value_type value_type;178 unpack_and_append(s, data, ::std::is_convertible<Item, value_type>());179}180 181} // detail182 183template <class T>184class test_data185{186public:187 typedef std::vector<T> row_type;188 typedef row_type value_type;189private:190 typedef std::set<row_type> container_type;191public: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;198 199 // creation:200 test_data(){}201 template <class F>202 test_data(F func, const parameter_info<T>& arg1)203 {204 insert(func, arg1);205 }206 207 // insertion:208 template <class F>209 test_data& insert(F func, const parameter_info<T>& arg1)210 {211 // generate data for single argument functor F212 213 typedef typename std::set<T>::const_iterator it_type;214 215 std::set<T> points;216 create_test_points(points, arg1);217 it_type a = points.begin();218 it_type b = points.end();219 row_type row;220 while(a != b)221 {222 if((arg1.type & dummy_param) == 0)223 row.push_back(*a);224#ifndef BOOST_NO_EXCEPTIONS225 try{226#endif227 // domain_error exceptions from func are swallowed228 // and this data point is ignored:229 boost::math::tools::detail::unpack_and_append(row, func(*a));230 m_data.insert(row);231#ifndef BOOST_NO_EXCEPTIONS232 }233 catch(const std::domain_error&){}234#endif235 row.clear();236 ++a;237 }238 return *this;239 }240 241 template <class F>242 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2)243 {244 // generate data for 2-argument functor F245 246 typedef typename std::set<T>::const_iterator it_type;247 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();253 row_type row;254 while(a != b)255 {256 it_type c = points2.begin();257 it_type d = points2.end();258 while(c != d)259 {260 if((arg1.type & dummy_param) == 0)261 row.push_back(*a);262 if((arg2.type & dummy_param) == 0)263 row.push_back(*c);264#ifndef BOOST_NO_EXCEPTIONS265 try{266#endif267 // domain_error exceptions from func are swallowed268 // and this data point is ignored:269 detail::unpack_and_append(row, func(*a, *c));270 m_data.insert(row);271#ifndef BOOST_NO_EXCEPTIONS272 }273 catch(const std::domain_error&){}274#endif275 row.clear();276 ++c;277 }278 ++a;279 }280 return *this;281 }282 283 template <class F>284 test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3)285 {286 // generate data for 3-argument functor F287 288 typedef typename std::set<T>::const_iterator it_type;289 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();296 row_type row;297 while(a != b)298 {299 it_type c = points2.begin();300 it_type d = points2.end();301 while(c != d)302 {303 it_type e = points3.begin();304 it_type f = points3.end();305 while(e != f)306 {307 if((arg1.type & dummy_param) == 0)308 row.push_back(*a);309 if((arg2.type & dummy_param) == 0)310 row.push_back(*c);311 if((arg3.type & dummy_param) == 0)312 row.push_back(*e);313#ifndef BOOST_NO_EXCEPTIONS314 try{315#endif316 // domain_error exceptions from func are swallowed317 // and this data point is ignored:318 detail::unpack_and_append(row, func(*a, *c, *e));319 m_data.insert(row);320#ifndef BOOST_NO_EXCEPTIONS321 }322 catch(const std::domain_error&){}323#endif324 row.clear();325 ++e;326 }327 ++c;328 }329 ++a;330 }331 return *this;332 }333 334 template <class F>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)336 {337 // generate data for 4-argument functor F338 339 typedef typename std::set<T>::const_iterator it_type;340 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();348 row_type row;349 while(a != b)350 {351 it_type c = points2.begin();352 it_type d = points2.end();353 while(c != d)354 {355 it_type e = points3.begin();356 it_type f = points3.end();357 while(e != f)358 {359 it_type g = points4.begin();360 it_type h = points4.end();361 while (g != h)362 {363 if ((arg1.type & dummy_param) == 0)364 row.push_back(*a);365 if ((arg2.type & dummy_param) == 0)366 row.push_back(*c);367 if ((arg3.type & dummy_param) == 0)368 row.push_back(*e);369 if ((arg4.type & dummy_param) == 0)370 row.push_back(*g);371#ifndef BOOST_NO_EXCEPTIONS372 try {373#endif374 // domain_error exceptions from func are swallowed375 // and this data point is ignored:376 detail::unpack_and_append(row, func(*a, *c, *e, *g));377 m_data.insert(row);378#ifndef BOOST_NO_EXCEPTIONS379 }380 catch (const std::domain_error&) {}381#endif382 row.clear();383 ++g;384 }385 ++e;386 }387 ++c;388 }389 ++a;390 }391 return *this;392 }393 394 template <class F>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)396 {397 // generate data for 5-argument functor F398 399 typedef typename std::set<T>::const_iterator it_type;400 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();409 row_type row;410 while(a != b)411 {412 it_type c = points2.begin();413 it_type d = points2.end();414 while(c != d)415 {416 it_type e = points3.begin();417 it_type f = points3.end();418 while(e != f)419 {420 it_type g = points4.begin();421 it_type h = points4.end();422 while (g != h)423 {424 it_type i = points5.begin();425 it_type j = points5.end();426 while (i != j)427 {428 if ((arg1.type & dummy_param) == 0)429 row.push_back(*a);430 if ((arg2.type & dummy_param) == 0)431 row.push_back(*c);432 if ((arg3.type & dummy_param) == 0)433 row.push_back(*e);434 if ((arg4.type & dummy_param) == 0)435 row.push_back(*g);436 if ((arg5.type & dummy_param) == 0)437 row.push_back(*i);438#ifndef BOOST_NO_EXCEPTIONS439 try {440#endif441 // domain_error exceptions from func are swallowed442 // and this data point is ignored:443 detail::unpack_and_append(row, func(*a, *c, *e, *g, *i));444 m_data.insert(row);445#ifndef BOOST_NO_EXCEPTIONS446 }447 catch (const std::domain_error&) {}448#endif449 row.clear();450 ++i;451 }452 ++g;453 }454 ++e;455 }456 ++c;457 }458 ++a;459 }460 return *this;461 }462 463 void clear(){ m_data.clear(); }464 465 // access: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(); }476 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; }481 482private:483 void create_test_points(std::set<T>& points, const parameter_info<T>& arg1);484 std::set<row_type> m_data;485 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); }489};490 491//492// This code exists to bemuse the compiler's optimizer and force a493// truncation to float-precision only:494//495template <class T>496inline float test_data<T>::truncate_to_float(float const * pf)497{498 BOOST_MATH_STD_USING499 int expon;500 float f = floor(ldexp(frexp(*pf, &expon), 22));501 f = ldexp(f, expon - 22);502 return f;503 504 //extern_val = *pf;505 //return *pf;506}507 508template <class T>509float test_data<T>::extern_val = 0;510 511template <class T>512void test_data<T>::create_test_points(std::set<T>& points, const parameter_info<T>& arg1)513{514 BOOST_MATH_STD_USING515 //516 // Generate a set of test points as requested, try and generate points517 // at only float precision: otherwise when testing float versions of functions518 // there will be a rounding error in our input values which throws off the results519 // (Garbage in garbage out etc).520 //521 switch(arg1.type & 0x7F)522 {523 case random_in_range:524 {525 BOOST_MATH_ASSERT(arg1.z1 < arg1.z2);526 BOOST_MATH_ASSERT(arg1.n1 > 0);527 typedef float random_type;528 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));531 532 for(int i = 0; i < arg1.n1; ++i)533 {534 random_type r = ur_a(rnd);535 points.insert(truncate_to_float(r));536 }537 }538 break;539 case periodic_in_range:540 {541 BOOST_MATH_ASSERT(arg1.z1 < arg1.z2);542 BOOST_MATH_ASSERT(arg1.n1 > 0);543 float interval = real_cast<float>((arg1.z2 - arg1.z1) / arg1.n1);544 T val = arg1.z1;545 while(val < arg1.z2)546 {547 points.insert(truncate_to_float(real_cast<float>(val)));548 val += interval;549 }550 }551 break;552 case power_series:553 {554 BOOST_MATH_ASSERT(arg1.n1 < arg1.n2);555 556 typedef float random_type;557 typedef typename boost::mpl::if_<558 ::boost::is_floating_point<T>,559 T, long double>::type power_type;560 561 random_ns::mt19937 rnd;562 random_ns::uniform_real_distribution<random_type> ur_a(1.0, 2.0);563 564 for(int power = arg1.n1; power <= arg1.n2; ++power)565 {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)));569 }570 }571 break;572 case single_value:573 {574 points.insert(truncate_to_float(real_cast<float>(arg1.z1)));575 break;576 }577 case plus_minus_value:578 {579 points.insert(truncate_to_float(real_cast<float>(arg1.z1)));580 points.insert(truncate_to_float(-real_cast<float>(arg1.z1)));581 break;582 }583 default:584 BOOST_MATH_ASSERT(0 == "Invalid parameter_info object");585 // Assert will fail if get here.586 // Triggers warning 4130) // '==' : logical operation on address of string constant.587 }588}589 590//591// Prompt a user for information on a parameter range:592//593template <class T>594bool get_user_parameter_info(parameter_info<T>& info, const char* param_name)595{596#ifdef _MSC_VER597# pragma warning(push)598# pragma warning(disable: 4127)599#endif600 std::string line;601 do{602 std::cout << "What kind of distribution do you require for parameter " << param_name << "?\n"603 "Choices are:\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"607 "[Default=r]";608 609 std::getline(std::cin, line);610 boost::algorithm::trim(line);611 612 if(line == "r")613 {614 info.type = random_in_range;615 break;616 }617 else if(line == "p")618 {619 info.type = periodic_in_range;620 break;621 }622 else if(line == "e")623 {624 info.type = power_series;625 break;626 }627 else if(line == "")628 {629 info.type = random_in_range;630 break;631 }632 //633 // Ooops, not a valid input....634 //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);639 if(line == "n")640 return false;641 else if(line == "y")642 continue;643 std::cout << "Sorry don't recognise that either, giving up...\n\n";644 return false;645 }while(true);646 647 switch(info.type & ~dummy_param)648 {649 case random_in_range:650 case periodic_in_range:651 // get start and end points of range:652 do{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);657 if(line == "")658 {659 info.z1 = 0;660 break;661 }662#ifndef BOOST_NO_EXCEPTIONS663 try{664#endif665 info.z1 = boost::lexical_cast<T>(line);666 break;667#ifndef BOOST_NO_EXCEPTIONS668 }669 catch(const boost::bad_lexical_cast&)670 {671 std::cout << "Sorry, that was not valid input, try again [y/n]?";672 std::getline(std::cin, line);673 boost::algorithm::trim(line);674 if(line == "y")675 continue;676 if(line == "n")677 return false;678 std::cout << "Sorry don't recognise that either, giving up...\n\n";679 return false;680 }681#endif682 }while(true);683 do{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);687 if(line == "")688 {689 info.z2 = 1;690 }691 else692 {693#ifndef BOOST_NO_EXCEPTIONS694 try695 {696#endif697 info.z2 = boost::lexical_cast<T>(line);698#ifndef BOOST_NO_EXCEPTIONS699 }700 catch(const boost::bad_lexical_cast&)701 {702 std::cout << "Sorry, that was not valid input, try again [y/n]?";703 std::getline(std::cin, line);704 boost::algorithm::trim(line);705 if(line == "y")706 continue;707 if(line == "n")708 return false;709 std::cout << "Sorry don't recognise that either, giving up...\n\n";710 return false;711 }712#endif713 }714 if(info.z1 >= info.z2)715 {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);720 if(line == "y")721 continue;722 if(line == "n")723 return false;724 std::cout << "Sorry don't recognise that either, giving up...\n\n";725 return false;726 }727 break;728 }while(true);729 do{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_EXCEPTIONS735 try{736#endif737 info.n1 = boost::lexical_cast<int>(line);738 info.n2 = 0;739 if(info.n1 <= 0)740 {741 std::cout << "The number of points should be > 0\n"742 "try again [y/n]?";743 std::getline(std::cin, line);744 boost::algorithm::trim(line);745 if(line == "y")746 continue;747 if(line == "n")748 return false;749 std::cout << "Sorry don't recognise that either, giving up...\n\n";750 return false;751 }752 break;753#ifndef BOOST_NO_EXCEPTIONS754 }755 catch(const boost::bad_lexical_cast&)756 {757 std::cout << "Sorry, that was not valid input, try again [y/n]?";758 std::getline(std::cin, line);759 boost::algorithm::trim(line);760 if(line == "y")761 continue;762 if(line == "n")763 return false;764 std::cout << "Sorry don't recognise that either, giving up...\n\n";765 return false;766 }767#endif768 }while(true);769 break;770 case power_series:771 // get start and end points of range:772 info.z2 = 0;773 do{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);779 if(line == "")780 {781 info.z1 = 0;782 break;783 }784#ifndef BOOST_NO_EXCEPTIONS785 try{786#endif787 info.z1 = boost::lexical_cast<T>(line);788 break;789#ifndef BOOST_NO_EXCEPTIONS790 }791 catch(const boost::bad_lexical_cast&)792 {793 std::cout << "Sorry, that was not valid input, try again [y/n]?";794 std::getline(std::cin, line);795 boost::algorithm::trim(line);796 if(line == "y")797 continue;798 if(line == "n")799 return false;800 std::cout << "Sorry don't recognise that either, giving up...\n\n";801 return false;802 }803#endif804 }while(true);805 806 do{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_EXCEPTIONS813 try{814#endif815 info.n1 = boost::lexical_cast<int>(line);816 break;817#ifndef BOOST_NO_EXCEPTIONS818 }819 catch(const boost::bad_lexical_cast&)820 {821 std::cout << "Sorry, that was not valid input, try again [y/n]?";822 std::getline(std::cin, line);823 boost::algorithm::trim(line);824 if(line == "y")825 continue;826 if(line == "n")827 return false;828 std::cout << "Sorry don't recognise that either, giving up...\n\n";829 return false;830 }831#endif832 }while(true);833 834 do{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_EXCEPTIONS841 try{842#endif843 info.n2 = boost::lexical_cast<int>(line);844 break;845#ifndef BOOST_NO_EXCEPTIONS846 }847 catch(const boost::bad_lexical_cast&)848 {849 std::cout << "Sorry, that was not valid input, try again [y/n]?";850 std::getline(std::cin, line);851 boost::algorithm::trim(line);852 if(line == "y")853 continue;854 if(line == "n")855 return false;856 std::cout << "Sorry don't recognise that either, giving up...\n\n";857 return false;858 }859#endif860 }while(true);861 862 break;863 default:864 BOOST_MATH_ASSERT(0); // should never get here!!865 }866 867 return true;868#ifdef _MSC_VER869# pragma warning(pop)870#endif871}872 873template <class charT, class traits, class T>874inline std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,875 const test_data<T>& data)876{877 const charT defarg[] = { ',', ' ', '\0' };878 return write_csv(os, data, defarg);879}880 881template <class charT, class traits, class T>882std::basic_ostream<charT, traits>& write_csv(std::basic_ostream<charT, traits>& os,883 const test_data<T>& data,884 const charT* separator)885{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;889 it_type a, b;890 a = data.begin();891 b = data.end();892 while(a != b)893 {894 value_type_iterator x, y;895 bool sep = false;896 x = a->begin();897 y = a->end();898 while(x != y)899 {900 if(sep)901 os << separator;902 os << *x;903 sep = true;904 ++x;905 }906 os << std::endl;907 ++a;908 }909 return os;910}911 912template <class T>913std::ostream& write_code(std::ostream& os,914 const test_data<T>& data,915 const char* name)916{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;920 921 BOOST_MATH_ASSERT(os.good());922 923 it_type a, b;924 a = data.begin();925 b = data.end();926 if(a == b)927 return os;928 929 os << "#ifndef SC_\n# define SC_(x) static_cast<T>(BOOST_JOIN(x, L))\n#endif\n"930 " static const std::array<std::array<T, "931 << a->size() << ">, " << data.size() << "> " << name << " = {{\n";932 933 while(a != b)934 {935 if(a != data.begin())936 os << ", \n";937 938 value_type_iterator x, y;939 x = a->begin();940 y = a->end();941 os << " { ";942 while(x != y)943 {944 if(x != a->begin())945 os << ", ";946 os << "SC_(" << *x << ")";947 ++x;948 }949 os << " }";950 ++a;951 }952 os << "\n }};\n//#undef SC_\n\n";953 return os;954}955 956} // namespace tools957} // namespace math958} // namespace boost959 960#ifdef _MSC_VER961#pragma warning(pop)962#endif963 964 965#endif // BOOST_MATH_TOOLS_TEST_DATA_HPP966 967 968