Imported Upstream version 1.51.0
[platform/upstream/boost.git] / boost / math / tools / test_data.hpp
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)
5
6 #ifndef BOOST_MATH_TOOLS_TEST_DATA_HPP
7 #define BOOST_MATH_TOOLS_TEST_DATA_HPP
8
9 #ifdef _MSC_VER
10 #pragma once
11 #endif
12
13 #include <boost/math/tools/config.hpp>
14 #include <boost/assert.hpp>
15 #ifdef BOOST_MSVC
16 #  pragma warning(push)
17 #  pragma warning(disable: 4127 4701 4512)
18 #  pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
19 #endif
20 #include <boost/algorithm/string/trim.hpp>
21 #include <boost/lexical_cast.hpp>
22 #ifdef BOOST_MSVC
23 #pragma warning(pop)
24 #endif
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>
31
32 #include <set>
33 #include <vector>
34 #include <iostream>
35
36 #ifdef BOOST_MSVC
37 #  pragma warning(push)
38 #  pragma warning(disable: 4130) // '==' : logical operation on address of string constant.
39 // Used as a warning with BOOST_ASSERT
40 #endif
41
42 namespace boost{ namespace math{ namespace tools{
43
44 enum parameter_type
45 {
46    random_in_range = 0,
47    periodic_in_range = 1,
48    power_series = 2,
49    dummy_param = 0x80
50 };
51
52 parameter_type operator | (parameter_type a, parameter_type b)
53 {
54    return static_cast<parameter_type>((int)a|(int)b);
55 }
56 parameter_type& operator |= (parameter_type& a, parameter_type b)
57 {
58    a = static_cast<parameter_type>(a|b);
59    return a;
60 }
61
62 //
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.
65 //
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.
68 //
69 // If type == power_series then
70 // n1 and n2 are the endpoints of the exponents (closed range) and z1 is the basis.
71 //
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.
74 //
75 template <class T>
76 struct parameter_info
77 {
78    parameter_type type;
79    T z1, z2;
80    int n1, n2;
81 };
82
83 template <class T>
84 inline parameter_info<T> make_random_param(T start_range, T end_range, int n_points)
85 {
86    parameter_info<T> result = { random_in_range, start_range, end_range, n_points, 0 };
87    return result;
88 }
89
90 template <class T>
91 inline parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points)
92 {
93    parameter_info<T> result = { periodic_in_range, start_range, end_range, n_points, 0 };
94    return result;
95 }
96
97 template <class T>
98 inline parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent)
99 {
100    parameter_info<T> result = { power_series, basis, 0, start_exponent, end_exponent };
101    return result;
102 }
103
104 namespace detail{
105
106 template <class Seq, class Item, int N>
107 inline void unpack_and_append_tuple(Seq& s,
108                                     const Item& data,
109                                     const boost::integral_constant<int, N>&,
110                                     const boost::false_type&)
111 {
112    // termimation condition nothing to do here
113 }
114
115 template <class Seq, class Item, int N>
116 inline void unpack_and_append_tuple(Seq& s,
117                                     const Item& data,
118                                     const boost::integral_constant<int, N>&,
119                                     const boost::true_type&)
120 {
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);
124    s.push_back(val);
125
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;
128
129    unpack_and_append_tuple(s, data, next_value(), terminate());
130 }
131
132 template <class Seq, class Item>
133 inline void unpack_and_append(Seq& s, const Item& data, const boost::true_type&)
134 {
135    s.push_back(data);
136 }
137
138 template <class Seq, class Item>
139 inline void unpack_and_append(Seq& s, const Item& data, const boost::false_type&)
140 {
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;
144
145    unpack_and_append_tuple(s, data, next_value(), terminate());
146 }
147
148 template <class Seq, class Item>
149 inline void unpack_and_append(Seq& s, const Item& data)
150 {
151    typedef typename Seq::value_type value_type;
152    unpack_and_append(s, data, ::boost::is_convertible<Item, value_type>());
153 }
154
155 } // detail
156
157 template <class T>
158 class test_data
159 {
160 public:
161    typedef std::vector<T> row_type;
162    typedef row_type value_type;
163 private:
164    typedef std::set<row_type> container_type;
165 public:
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;
172
173    // creation:
174    test_data(){}
175    template <class F>
176    test_data(F func, const parameter_info<T>& arg1)
177    {
178       insert(func, arg1);
179    }
180
181    // insertion:
182    template <class F>
183    test_data& insert(F func, const parameter_info<T>& arg1)
184    {
185       // generate data for single argument functor F
186
187       typedef typename std::set<T>::const_iterator it_type;
188
189       std::set<T> points;
190       create_test_points(points, arg1);
191       it_type a = points.begin();
192       it_type b = points.end();
193       row_type row;
194       while(a != b)
195       {
196          if((arg1.type & dummy_param) == 0)
197             row.push_back(*a);
198          try{
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));
202             m_data.insert(row);
203          }
204          catch(const std::domain_error&){}
205          row.clear();
206          ++a;
207       }
208       return *this;
209    }
210
211    template <class F>
212    test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2)
213    {
214       // generate data for 2-argument functor F
215
216       typedef typename std::set<T>::const_iterator it_type;
217
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();
223       row_type row;
224       while(a != b)
225       {
226          it_type c = points2.begin();
227          it_type d = points2.end();
228          while(c != d)
229          {
230             if((arg1.type & dummy_param) == 0)
231                row.push_back(*a);
232             if((arg2.type & dummy_param) == 0)
233                row.push_back(*c);
234             try{
235                // domain_error exceptions from func are swallowed
236                // and this data point is ignored:
237                detail::unpack_and_append(row, func(*a, *c));
238                m_data.insert(row);
239             }
240             catch(const std::domain_error&){}
241             row.clear();
242             ++c;
243          }
244          ++a;
245       }
246       return *this;
247    }
248
249    template <class F>
250    test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3)
251    {
252       // generate data for 3-argument functor F
253
254       typedef typename std::set<T>::const_iterator it_type;
255
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();
262       row_type row;
263       while(a != b)
264       {
265          it_type c = points2.begin();
266          it_type d = points2.end();
267          while(c != d)
268          {
269             it_type e = points3.begin();
270             it_type f = points3.end();
271             while(e != f)
272             {
273                if((arg1.type & dummy_param) == 0)
274                   row.push_back(*a);
275                if((arg2.type & dummy_param) == 0)
276                   row.push_back(*c);
277                if((arg3.type & dummy_param) == 0)
278                   row.push_back(*e);
279                try{
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));
283                   m_data.insert(row);
284                }
285                catch(const std::domain_error&){}
286                row.clear();
287                ++e;
288             }
289             ++c;
290          }
291          ++a;
292       }
293       return *this;
294    }
295
296    void clear(){ m_data.clear(); }
297
298    // access:
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(); }
309
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; }
314
315 private:
316    void create_test_points(std::set<T>& points, const parameter_info<T>& arg1);
317    std::set<row_type> m_data;
318
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); }
322 };
323
324 //
325 // This code exists to bemuse the compiler's optimizer and force a
326 // truncation to float-precision only:
327 //
328 template <class T>
329 inline float test_data<T>::truncate_to_float(float const * pf)
330 {
331    BOOST_MATH_STD_USING
332    int expon;
333    float f = floor(ldexp(frexp(*pf, &expon), 22));
334    f = ldexp(f, expon - 22);
335    return f;
336
337    //extern_val = *pf;
338    //return *pf;
339 }
340
341 template <class T>
342 float test_data<T>::extern_val = 0;
343
344 template <class T>
345 void test_data<T>::create_test_points(std::set<T>& points, const parameter_info<T>& arg1)
346 {
347    BOOST_MATH_STD_USING
348    //
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).
353    //
354    switch(arg1.type & 0x7F)
355    {
356    case random_in_range:
357       {
358          BOOST_ASSERT(arg1.z1 < arg1.z2);
359          BOOST_ASSERT(arg1.n1 > 0);
360          typedef float random_type;
361
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);
365
366          for(int i = 0; i < arg1.n1; ++i)
367          {
368             random_type r = gen();
369             points.insert(truncate_to_float(r));
370          }
371      }
372       break;
373    case periodic_in_range:
374       {
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);
378          T val = arg1.z1;
379          while(val < arg1.z2)
380          {
381             points.insert(truncate_to_float(real_cast<float>(val)));
382             val += interval;
383          }
384       }
385       break;
386    case power_series:
387       {
388          BOOST_ASSERT(arg1.n1 < arg1.n2);
389
390          typedef float random_type;
391          typedef typename boost::mpl::if_<
392             ::boost::is_floating_point<T>,
393             T, long double>::type power_type;
394
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);
398
399          for(int power = arg1.n1; power <= arg1.n2; ++power)
400          {
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)));
404          }
405       }
406       break;
407    default:
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.
411    }
412 }
413
414 //
415 // Prompt a user for information on a parameter range:
416 //
417 template <class T>
418 bool get_user_parameter_info(parameter_info<T>& info, const char* param_name)
419 {
420 #ifdef BOOST_MSVC
421 #  pragma warning(push)
422 #  pragma warning(disable: 4127)
423 #endif
424    std::string line;
425    do{
426       std::cout << "What kind of distribution do you require for parameter " << param_name << "?\n"
427          "Choices are:\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"
431          "[Default=r]";
432
433       std::getline(std::cin, line);
434       boost::algorithm::trim(line);
435
436       if(line == "r")
437       {
438          info.type = random_in_range;
439          break;
440       }
441       else if(line == "p")
442       {
443          info.type = periodic_in_range;
444          break;
445       }
446       else if(line == "e")
447       {
448          info.type = power_series;
449          break;
450       }
451       else if(line == "")
452       {
453          info.type = random_in_range;
454          break;
455       }
456       //
457       // Ooops, not a valid input....
458       //
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);
463       if(line == "n")
464          return false;
465       else if(line == "y")
466          continue;
467       std::cout << "Sorry don't recognise that either, giving up...\n\n";
468       return false;
469    }while(true);
470
471    switch(info.type & ~dummy_param)
472    {
473    case random_in_range:
474    case periodic_in_range:
475       // get start and end points of range:
476       do{
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);
481          if(line == "")
482          {
483             info.z1 = 0;
484             break;
485          }
486          try{
487             info.z1 = boost::lexical_cast<T>(line);
488             break;
489          }
490          catch(const boost::bad_lexical_cast&)
491          {
492             std::cout << "Sorry, that was not valid input, try again [y/n]?";
493             std::getline(std::cin, line);
494             boost::algorithm::trim(line);
495             if(line == "y")
496                continue;
497             if(line == "n")
498                return false;
499             std::cout << "Sorry don't recognise that either, giving up...\n\n";
500             return false;
501          }
502       }while(true);
503       do{
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);
507          if(line == "")
508          {
509             info.z2 = 1;
510          }
511          else
512          {
513             try
514             {
515                info.z2 = boost::lexical_cast<T>(line);
516             }
517             catch(const boost::bad_lexical_cast&)
518             {
519                std::cout << "Sorry, that was not valid input, try again [y/n]?";
520                std::getline(std::cin, line);
521                boost::algorithm::trim(line);
522                if(line == "y")
523                   continue;
524                if(line == "n")
525                   return false;
526                std::cout << "Sorry don't recognise that either, giving up...\n\n";
527                return false;
528             }
529          }
530          if(info.z1 >= info.z2)
531          {
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);
536             if(line == "y")
537                continue;
538             if(line == "n")
539                return false;
540             std::cout << "Sorry don't recognise that either, giving up...\n\n";
541             return false;
542          }
543          break;
544       }while(true);
545       do{
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);
550          try{
551             info.n1 = boost::lexical_cast<int>(line);
552             info.n2 = 0;
553             if(info.n1 <= 0)
554             {
555                std::cout << "The number of points should be > 0\n"
556                   "try again [y/n]?";
557                std::getline(std::cin, line);
558                boost::algorithm::trim(line);
559                if(line == "y")
560                   continue;
561                if(line == "n")
562                   return false;
563                std::cout << "Sorry don't recognise that either, giving up...\n\n";
564                return false;
565             }
566             break;
567          }
568          catch(const boost::bad_lexical_cast&)
569          {
570             std::cout << "Sorry, that was not valid input, try again [y/n]?";
571             std::getline(std::cin, line);
572             boost::algorithm::trim(line);
573             if(line == "y")
574                continue;
575             if(line == "n")
576                return false;
577             std::cout << "Sorry don't recognise that either, giving up...\n\n";
578             return false;
579          }
580       }while(true);
581       break;
582    case power_series:
583       // get start and end points of range:
584       info.z2 = 0;
585       do{
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);
591          if(line == "")
592          {
593             info.z1 = 0;
594             break;
595          }
596          try{
597             info.z1 = boost::lexical_cast<T>(line);
598             break;
599          }
600          catch(const boost::bad_lexical_cast&)
601          {
602             std::cout << "Sorry, that was not valid input, try again [y/n]?";
603             std::getline(std::cin, line);
604             boost::algorithm::trim(line);
605             if(line == "y")
606                continue;
607             if(line == "n")
608                return false;
609             std::cout << "Sorry don't recognise that either, giving up...\n\n";
610             return false;
611          }
612       }while(true);
613
614       do{
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);
620          try{
621             info.n1 = boost::lexical_cast<int>(line);
622             break;
623          }
624          catch(const boost::bad_lexical_cast&)
625          {
626             std::cout << "Sorry, that was not valid input, try again [y/n]?";
627             std::getline(std::cin, line);
628             boost::algorithm::trim(line);
629             if(line == "y")
630                continue;
631             if(line == "n")
632                return false;
633             std::cout << "Sorry don't recognise that either, giving up...\n\n";
634             return false;
635          }
636       }while(true);
637
638       do{
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);
644          try{
645             info.n2 = boost::lexical_cast<int>(line);
646             break;
647          }
648          catch(const boost::bad_lexical_cast&)
649          {
650             std::cout << "Sorry, that was not valid input, try again [y/n]?";
651             std::getline(std::cin, line);
652             boost::algorithm::trim(line);
653             if(line == "y")
654                continue;
655             if(line == "n")
656                return false;
657             std::cout << "Sorry don't recognise that either, giving up...\n\n";
658             return false;
659          }
660       }while(true);
661
662       break;
663    default:
664       BOOST_ASSERT(0); // should never get here!!
665    }
666
667    return true;
668 #ifdef BOOST_MSVC
669 #  pragma warning(pop)
670 #endif
671 }
672
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)
676 {
677    const charT defarg[] = { ',', ' ', '\0' };
678    return write_csv(os, data, defarg);
679 }
680
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)
685 {
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;
689    it_type a, b;
690    a = data.begin();
691    b = data.end();
692    while(a != b)
693    {
694       value_type_iterator x, y;
695       bool sep = false;
696       x = a->begin();
697       y = a->end();
698       while(x != y)
699       {
700          if(sep)
701             os << separator;
702          os << *x;
703          sep = true;
704          ++x;
705       }
706       os << std::endl;
707       ++a;
708    }
709    return os;
710 }
711
712 template <class T>
713 std::ostream& write_code(std::ostream& os,
714                          const test_data<T>& data,
715                          const char* name)
716 {
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;
720
721    BOOST_ASSERT(os.good());
722
723    it_type a, b;
724    a = data.begin();
725    b = data.end();
726    if(a == b)
727       return os;
728
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";
732
733    while(a != b)
734    {
735       if(a != data.begin())
736          os << ", \n";
737
738       value_type_iterator x, y;
739       x = a->begin();
740       y = a->end();
741       os << "      { ";
742       while(x != y)
743       {
744          if(x != a->begin())
745             os << ", ";
746          os << "SC_(" << *x << ")";
747          ++x;
748       }
749       os << " }";
750       ++a;
751    }
752    os << "\n   }};\n//#undef SC_\n\n";
753    return os;
754 }
755
756 } // namespace tools
757 } // namespace math
758 } // namespace boost
759
760 #ifdef BOOST_MSVC
761 #pragma warning(pop)
762 #endif
763
764
765 #endif // BOOST_MATH_TOOLS_TEST_DATA_HPP
766
767