1 
2 ///////////////////////////////////////////////////////////////////////////////
3 // Copyright Christopher Kormanyos 2013 - 2014.
4 // Copyright John Maddock 2013.
5 // Distributed under the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt
7 // or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // This work is based on an earlier work:
10 // "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
11 // in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469
12 //
13 
14 #include <algorithm>
15 #include <cstdint>
16 #include <deque>
17 #include <functional>
18 #include <iostream>
19 #include <limits>
20 #include <numeric>
21 #include <vector>
22 #include <boost/math/constants/constants.hpp>
23 #include <boost/noncopyable.hpp>
24 
25 //#define USE_CPP_BIN_FLOAT
26 #define USE_CPP_DEC_FLOAT
27 //#define USE_MPFR
28 
29 #if !defined(DIGIT_COUNT)
30 #define DIGIT_COUNT 100
31 #endif
32 
33 #if !defined(BOOST_NO_CXX11_HDR_CHRONO)
34   #include <chrono>
35   #define STD_CHRONO std::chrono
36 #else
37   #include <boost/chrono.hpp>
38   #define STD_CHRONO boost::chrono
39 #endif
40 
41 #if defined(USE_CPP_BIN_FLOAT)
42   #include <boost/multiprecision/cpp_bin_float.hpp>
43   typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<DIGIT_COUNT + 10> > mp_type;
44 #elif defined(USE_CPP_DEC_FLOAT)
45   #include <boost/multiprecision/cpp_dec_float.hpp>
46   typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<DIGIT_COUNT + 10> > mp_type;
47 #elif defined(USE_MPFR)
48   #include <boost/multiprecision/mpfr.hpp>
49   typedef boost::multiprecision::number<boost::multiprecision::mpfr_float_backend<DIGIT_COUNT + 10> > mp_type;
50 #else
51   #error no multiprecision floating type is defined
52 #endif
53 
54 template <class clock_type>
55 struct stopwatch
56 {
57 public:
58   typedef typename clock_type::duration duration_type;
59 
stopwatchstopwatch60   stopwatch() : m_start(clock_type::now()) { }
61 
stopwatchstopwatch62   stopwatch(const stopwatch& other) : m_start(other.m_start) { }
63 
operator =stopwatch64   stopwatch& operator=(const stopwatch& other)
65   {
66     m_start = other.m_start;
67     return *this;
68   }
69 
~stopwatchstopwatch70   ~stopwatch() { }
71 
elapsedstopwatch72   duration_type elapsed() const
73   {
74     return (clock_type::now() - m_start);
75   }
76 
resetstopwatch77   void reset()
78   {
79     m_start = clock_type::now();
80   }
81 
82 private:
83   typename clock_type::time_point m_start;
84 };
85 
86 namespace my_math
87 {
88   template<class T> T chebyshev_t(const std::int32_t n, const T& x);
89 
90   template<class T> T chebyshev_t(const std::uint32_t n, const T& x, std::vector<T>* vp);
91 
isneg(const T & x)92   template<class T> bool isneg(const T& x) { return (x < T(0)); }
93 
zero()94   template<class T> const T& zero() { static const T value_zero(0); return value_zero; }
one()95   template<class T> const T& one () { static const T value_one (1); return value_one; }
two()96   template<class T> const T& two () { static const T value_two (2); return value_two; }
97 }
98 
99 namespace orthogonal_polynomial_series
100 {
orthogonal_polynomial_template(const T & x,const std::uint32_t n,std::vector<T> * const vp=static_cast<std::vector<T> * > (0u))101   template<typename T> static inline T orthogonal_polynomial_template(const T& x, const std::uint32_t n, std::vector<T>* const vp = static_cast<std::vector<T>*>(0u))
102   {
103     // Compute the value of an orthogonal chebyshev polinomial.
104     // Use stable upward recursion.
105 
106     if(vp != nullptr)
107     {
108       vp->clear();
109       vp->reserve(static_cast<std::size_t>(n + 1u));
110     }
111 
112     T y0 = my_math::one<T>();
113 
114     if(vp != nullptr) { vp->push_back(y0); }
115 
116     if(n == static_cast<std::uint32_t>(0u))
117     {
118       return y0;
119     }
120 
121     T y1 = x;
122 
123     if(vp != nullptr) { vp->push_back(y1); }
124 
125     if(n == static_cast<std::uint32_t>(1u))
126     {
127       return y1;
128     }
129 
130     T a = my_math::two <T>();
131     T b = my_math::zero<T>();
132     T c = my_math::one <T>();
133 
134     T yk;
135 
136     // Calculate higher orders using the recurrence relation.
137     // The direction of stability is upward recursion.
138     for(std::int32_t k = static_cast<std::int32_t>(2); k <= static_cast<std::int32_t>(n); ++k)
139     {
140       yk = (((a * x) + b) * y1) - (c * y0);
141 
142       y0 = y1;
143       y1 = yk;
144 
145       if(vp != nullptr) { vp->push_back(yk); }
146     }
147 
148     return yk;
149   }
150 }
151 
chebyshev_t(const std::int32_t n,const T & x)152 template<class T> T my_math::chebyshev_t(const std::int32_t n, const T& x)
153 {
154   if(my_math::isneg(x))
155   {
156     const bool b_negate = ((n % static_cast<std::int32_t>(2)) != static_cast<std::int32_t>(0));
157 
158     const T y = chebyshev_t(n, -x);
159 
160     return (!b_negate ? y : -y);
161   }
162 
163   if(n < static_cast<std::int32_t>(0))
164   {
165     const std::int32_t nn = static_cast<std::int32_t>(-n);
166 
167     return chebyshev_t(nn, x);
168   }
169   else
170   {
171     return orthogonal_polynomial_series::orthogonal_polynomial_template(x, static_cast<std::uint32_t>(n));
172   }
173 }
174 
chebyshev_t(const std::uint32_t n,const T & x,std::vector<T> * const vp)175 template<class T> T my_math::chebyshev_t(const std::uint32_t n, const T& x, std::vector<T>* const vp) { return orthogonal_polynomial_series::orthogonal_polynomial_template(x, static_cast<std::int32_t>(n),  vp); }
176 
177 namespace util
178 {
digit_scale()179   template <class T> float digit_scale()
180   {
181     const int d = ((std::max)(std::numeric_limits<T>::digits10, 15));
182     return static_cast<float>(d) / 300.0F;
183   }
184 }
185 
186 namespace examples
187 {
188   namespace nr_006
189   {
190     template<typename T> class hypergeometric_pfq_base : private boost::noncopyable
191     {
192     public:
~hypergeometric_pfq_base()193       virtual ~hypergeometric_pfq_base() { }
194 
195       virtual void ccoef() const = 0;
196 
series() const197       virtual T series() const
198       {
199         using my_math::chebyshev_t;
200 
201         // Compute the Chebyshev coefficients.
202         // Get the values of the shifted Chebyshev polynomials.
203         std::vector<T> chebyshev_t_shifted_values;
204         const T z_shifted = ((Z / W) * static_cast<std::int32_t>(2)) - static_cast<std::int32_t>(1);
205 
206         chebyshev_t(static_cast<std::uint32_t>(C.size()),
207                     z_shifted,
208                     &chebyshev_t_shifted_values);
209 
210         // Luke: C     ---------- COMPUTE SCALE FACTOR                       ----------
211         // Luke: C
212         // Luke: C     ---------- SCALE THE COEFFICIENTS                     ----------
213         // Luke: C
214 
215         // The coefficient scaling is preformed after the Chebyshev summation,
216         // and it is carried out with a single division operation.
217         bool b_neg = false;
218 
219         const T scale = std::accumulate(C.begin(),
220                                         C.end(),
221                                         T(0),
222                                         [&b_neg](T& scale_sum, const T& ck) -> T
223                                         {
224                                           ((!b_neg) ? (scale_sum += ck) : (scale_sum -= ck));
225                                           b_neg = (!b_neg);
226                                           return scale_sum;
227                                         });
228 
229         // Compute the result of the series expansion using unscaled coefficients.
230         const T sum = std::inner_product(C.begin(),
231                                          C.end(),
232                                          chebyshev_t_shifted_values.begin(),
233                                          T(0));
234 
235         // Return the properly scaled result.
236         return sum / scale;
237       }
238 
239     protected:
240       const   T             Z;
241       const   T             W;
242       mutable std::deque<T> C;
243 
hypergeometric_pfq_base(const T & z,const T & w)244       hypergeometric_pfq_base(const T& z,
245                               const T& w) : Z(z),
246                                             W(w),
247                                             C(0u) { }
248 
N() const249       virtual std::int32_t N() const { return static_cast<std::int32_t>(util::digit_scale<T>() * 500.0F); }
250     };
251 
252     template<typename T> class ccoef4_hypergeometric_0f1 : public hypergeometric_pfq_base<T>
253     {
254     public:
ccoef4_hypergeometric_0f1(const T & c,const T & z,const T & w)255       ccoef4_hypergeometric_0f1(const T& c,
256                                 const T& z,
257                                 const T& w) : hypergeometric_pfq_base<T>(z, w),
258                                               CP(c) { }
259 
~ccoef4_hypergeometric_0f1()260       virtual ~ccoef4_hypergeometric_0f1() { }
261 
ccoef() const262       virtual void ccoef() const
263       {
264         // See Luke 1977 page 80.
265         const std::int32_t N1 = static_cast<std::int32_t>(this->N() + static_cast<std::int32_t>(1));
266         const std::int32_t N2 = static_cast<std::int32_t>(this->N() + static_cast<std::int32_t>(2));
267 
268         // Luke: C     ---------- START COMPUTING COEFFICIENTS USING         ----------
269         // Luke: C     ---------- BACKWARD RECURRENCE SCHEME                 ----------
270         // Luke: C
271         T A3(0);
272         T A2(0);
273         T A1(boost::math::tools::root_epsilon<T>());
274 
275         hypergeometric_pfq_base<T>::C.resize(1u, A1);
276 
277         std::int32_t X1 = N2;
278 
279         T C1 = T(1) - CP;
280 
281         const T Z1 = T(4) / hypergeometric_pfq_base<T>::W;
282 
283         for(std::int32_t k = static_cast<std::int32_t>(0); k < N1; ++k)
284         {
285           const T DIVFAC = T(1) / X1;
286 
287           --X1;
288 
289           // The terms have been slightly re-arranged resulting in lower complexity.
290           // Parentheses have been added to avoid reliance on operator precedence.
291           const T term =   (A2 - ((A3 * DIVFAC) * X1))
292                          + ((A2 * X1) * ((1 + (C1 + X1)) * Z1))
293                          + ((A1 * X1) * ((DIVFAC - (C1 * Z1)) + (X1 * Z1)));
294 
295           hypergeometric_pfq_base<T>::C.push_front(term);
296 
297           A3 = A2;
298           A2 = A1;
299           A1 = hypergeometric_pfq_base<T>::C.front();
300         }
301 
302         hypergeometric_pfq_base<T>::C.front() /= static_cast<std::int32_t>(2);
303       }
304 
305     private:
306       const T CP;
307     };
308 
309     template<typename T> class ccoef1_hypergeometric_1f0 : public hypergeometric_pfq_base<T>
310     {
311     public:
ccoef1_hypergeometric_1f0(const T & a,const T & z,const T & w)312       ccoef1_hypergeometric_1f0(const T& a,
313                                 const T& z,
314                                 const T& w) : hypergeometric_pfq_base<T>(z, w),
315                                               AP(a) { }
316 
~ccoef1_hypergeometric_1f0()317       virtual ~ccoef1_hypergeometric_1f0() { }
318 
ccoef() const319       virtual void ccoef() const
320       {
321         // See Luke 1977 page 67.
322         const std::int32_t N1 = static_cast<std::int32_t>(N() + static_cast<std::int32_t>(1));
323         const std::int32_t N2 = static_cast<std::int32_t>(N() + static_cast<std::int32_t>(2));
324 
325         // Luke: C     ---------- START COMPUTING COEFFICIENTS USING         ----------
326         // Luke: C     ---------- BACKWARD RECURRENCE SCHEME                 ----------
327         // Luke: C
328         T A2(0);
329         T A1(boost::math::tools::root_epsilon<T>());
330 
331         hypergeometric_pfq_base<T>::C.resize(1u, A1);
332 
333         std::int32_t X1 = N2;
334 
335         T V1 = T(1) - AP;
336 
337         // Here, we have corrected what appears to be an error in Luke's code.
338 
339         // Luke's original code listing has:
340         //  AFAC = 2 + FOUR/W
341         // But it appears as though the correct form is:
342         //  AFAC = 2 - FOUR/W.
343 
344         const T AFAC = 2 - (T(4) / hypergeometric_pfq_base<T>::W);
345 
346         for(std::int32_t k = static_cast<std::int32_t>(0); k < N1; ++k)
347         {
348           --X1;
349 
350           // The terms have been slightly re-arranged resulting in lower complexity.
351           // Parentheses have been added to avoid reliance on operator precedence.
352           const T term = -(((X1 * AFAC) * A1) + ((X1 + V1) * A2)) / (X1 - V1);
353 
354           hypergeometric_pfq_base<T>::C.push_front(term);
355 
356           A2 = A1;
357           A1 = hypergeometric_pfq_base<T>::C.front();
358         }
359 
360         hypergeometric_pfq_base<T>::C.front() /= static_cast<std::int32_t>(2);
361       }
362 
363     private:
364       const T AP;
365 
N() const366       virtual std::int32_t N() const { return static_cast<std::int32_t>(util::digit_scale<T>() * 1600.0F); }
367     };
368 
369     template<typename T> class ccoef3_hypergeometric_1f1 : public hypergeometric_pfq_base<T>
370     {
371     public:
ccoef3_hypergeometric_1f1(const T & a,const T & c,const T & z,const T & w)372       ccoef3_hypergeometric_1f1(const T& a,
373                                 const T& c,
374                                 const T& z,
375                                 const T& w) : hypergeometric_pfq_base<T>(z, w),
376                                               AP(a),
377                                               CP(c) { }
378 
~ccoef3_hypergeometric_1f1()379       virtual ~ccoef3_hypergeometric_1f1() { }
380 
ccoef() const381       virtual void ccoef() const
382       {
383         // See Luke 1977 page 74.
384         const std::int32_t N1 = static_cast<std::int32_t>(this->N() + static_cast<std::int32_t>(1));
385         const std::int32_t N2 = static_cast<std::int32_t>(this->N() + static_cast<std::int32_t>(2));
386 
387         // Luke: C     ---------- START COMPUTING COEFFICIENTS USING         ----------
388         // Luke: C     ---------- BACKWARD RECURRENCE SCHEME                 ----------
389         // Luke: C
390         T A3(0);
391         T A2(0);
392         T A1(boost::math::tools::root_epsilon<T>());
393 
394         hypergeometric_pfq_base<T>::C.resize(1u, A1);
395 
396         std::int32_t X  = N1;
397         std::int32_t X1 = N2;
398 
399         T XA  =  X + AP;
400         T X3A = (X + 3) - AP;
401 
402         const T Z1 = T(4) / hypergeometric_pfq_base<T>::W;
403 
404         for(std::int32_t k = static_cast<std::int32_t>(0); k < N1; ++k)
405         {
406           --X;
407           --X1;
408           --XA;
409           --X3A;
410 
411           const T X3A_over_X2 = X3A / static_cast<std::int32_t>(X + 2);
412 
413           // The terms have been slightly re-arranged resulting in lower complexity.
414           // Parentheses have been added to avoid reliance on operator precedence.
415           const T PART1 =  A1 * (((X + CP) * Z1) - X3A_over_X2);
416           const T PART2 =  A2 * (Z1 * ((X + 3) - CP) + (XA / X1));
417           const T PART3 =  A3 * X3A_over_X2;
418 
419           const T term = (((PART1 + PART2) + PART3) * X1) / XA;
420 
421           hypergeometric_pfq_base<T>::C.push_front(term);
422 
423           A3 = A2;
424           A2 = A1;
425           A1 = hypergeometric_pfq_base<T>::C.front();
426         }
427 
428         hypergeometric_pfq_base<T>::C.front() /= static_cast<std::int32_t>(2);
429       }
430 
431     private:
432       const T AP;
433       const T CP;
434     };
435 
436     template<typename T> class ccoef6_hypergeometric_1f2 : public hypergeometric_pfq_base<T>
437     {
438     public:
ccoef6_hypergeometric_1f2(const T & a,const T & b,const T & c,const T & z,const T & w)439       ccoef6_hypergeometric_1f2(const T& a,
440                                 const T& b,
441                                 const T& c,
442                                 const T& z,
443                                 const T& w) : hypergeometric_pfq_base<T>(z, w),
444                                               AP(a),
445                                               BP(b),
446                                               CP(c) { }
447 
~ccoef6_hypergeometric_1f2()448       virtual ~ccoef6_hypergeometric_1f2() { }
449 
ccoef() const450       virtual void ccoef() const
451       {
452         // See Luke 1977 page 85.
453         const std::int32_t N1 = static_cast<std::int32_t>(this->N() + static_cast<std::int32_t>(1));
454 
455         // Luke: C     ---------- START COMPUTING COEFFICIENTS USING         ----------
456         // Luke: C     ---------- BACKWARD RECURRENCE SCHEME                 ----------
457         // Luke: C
458         T A4(0);
459         T A3(0);
460         T A2(0);
461         T A1(boost::math::tools::root_epsilon<T>());
462 
463         hypergeometric_pfq_base<T>::C.resize(1u, A1);
464 
465         std::int32_t X  = N1;
466         T            PP = X + AP;
467 
468         const T Z1 = T(4) / hypergeometric_pfq_base<T>::W;
469 
470         for(std::int32_t k = static_cast<std::int32_t>(0); k < N1; ++k)
471         {
472           --X;
473           --PP;
474 
475           const std::int32_t TWO_X    = static_cast<std::int32_t>(X * 2);
476           const std::int32_t X_PLUS_1 = static_cast<std::int32_t>(X + 1);
477           const std::int32_t X_PLUS_3 = static_cast<std::int32_t>(X + 3);
478           const std::int32_t X_PLUS_4 = static_cast<std::int32_t>(X + 4);
479 
480           const T QQ = T(TWO_X + 3) / static_cast<std::int32_t>(TWO_X + static_cast<std::int32_t>(5));
481           const T SS = (X + BP) * (X + CP);
482 
483           // The terms have been slightly re-arranged resulting in lower complexity.
484           // Parentheses have been added to avoid reliance on operator precedence.
485           const T PART1 =   A1 * (((PP - (QQ * (PP + 1))) * 2) + (SS * Z1));
486           const T PART2 =  (A2 * (X + 2)) * ((((TWO_X + 1) * PP) / X_PLUS_1) - ((QQ * 4) * (PP + 1)) + (((TWO_X + 3) * (PP + 2)) / X_PLUS_3) + ((Z1 * 2) * (SS - (QQ * (X_PLUS_1 + BP)) * (X_PLUS_1 + CP))));
487           const T PART3 =   A3 * ((((X_PLUS_3 - AP) - (QQ * (X_PLUS_4 - AP))) * 2) + (((QQ * Z1) * (X_PLUS_4 - BP)) * (X_PLUS_4 - CP)));
488           const T PART4 = ((A4 * QQ) * (X_PLUS_4 - AP)) / X_PLUS_3;
489 
490           const T term = (((PART1 - PART2) + (PART3 - PART4)) * X_PLUS_1) / PP;
491 
492           hypergeometric_pfq_base<T>::C.push_front(term);
493 
494           A4 = A3;
495           A3 = A2;
496           A2 = A1;
497           A1 = hypergeometric_pfq_base<T>::C.front();
498         }
499 
500         hypergeometric_pfq_base<T>::C.front() /= static_cast<std::int32_t>(2);
501       }
502 
503     private:
504       const T AP;
505       const T BP;
506       const T CP;
507     };
508 
509     template<typename T> class ccoef2_hypergeometric_2f1 : public hypergeometric_pfq_base<T>
510     {
511     public:
ccoef2_hypergeometric_2f1(const T & a,const T & b,const T & c,const T & z,const T & w)512       ccoef2_hypergeometric_2f1(const T& a,
513                                 const T& b,
514                                 const T& c,
515                                 const T& z,
516                                 const T& w) : hypergeometric_pfq_base<T>(z, w),
517                                               AP(a),
518                                               BP(b),
519                                               CP(c) { }
520 
~ccoef2_hypergeometric_2f1()521       virtual ~ccoef2_hypergeometric_2f1() { }
522 
ccoef() const523       virtual void ccoef() const
524       {
525         // See Luke 1977 page 59.
526         const std::int32_t N1 = static_cast<std::int32_t>(N() + static_cast<std::int32_t>(1));
527         const std::int32_t N2 = static_cast<std::int32_t>(N() + static_cast<std::int32_t>(2));
528 
529         // Luke: C     ---------- START COMPUTING COEFFICIENTS USING         ----------
530         // Luke: C     ---------- BACKWARD RECURRENCE SCHEME                 ----------
531         // Luke: C
532         T A3(0);
533         T A2(0);
534         T A1(boost::math::tools::root_epsilon<T>());
535 
536         hypergeometric_pfq_base<T>::C.resize(1u, A1);
537 
538         std::int32_t X  = N1;
539         std::int32_t X1 = N2;
540         std::int32_t X3 = static_cast<std::int32_t>((X * 2) + 3);
541 
542         T X3A = (X + 3) - AP;
543         T X3B = (X + 3) - BP;
544 
545         const T Z1 = T(4) / hypergeometric_pfq_base<T>::W;
546 
547         for(std::int32_t k = static_cast<std::int32_t>(0); k < N1; ++k)
548         {
549           --X;
550           --X1;
551           --X3A;
552           --X3B;
553           X3 -= 2;
554 
555           const std::int32_t X_PLUS_2 = static_cast<std::int32_t>(X + 2);
556 
557           const T XAB = T(1) / ((X + AP) * (X + BP));
558 
559           // The terms have been slightly re-arranged resulting in lower complexity.
560           // Parentheses have been added to avoid reliance on operator precedence.
561           const T PART1 = (A1 * X1) * (2 - (((AP + X1) * (BP + X1)) * ((T(X3) / X_PLUS_2) * XAB)) + ((CP + X) * (XAB * Z1)));
562           const T PART2 = (A2 * XAB) * ((X3A * X3B) - (X3 * ((X3A + X3B) - 1)) + (((3 - CP) + X) * (X1 * Z1)));
563           const T PART3 = (A3 * X1) * (X3A / X_PLUS_2) * (X3B * XAB);
564 
565           const T term = (PART1 + PART2) - PART3;
566 
567           hypergeometric_pfq_base<T>::C.push_front(term);
568 
569           A3 = A2;
570           A2 = A1;
571           A1 = hypergeometric_pfq_base<T>::C.front();
572         }
573 
574         hypergeometric_pfq_base<T>::C.front() /= static_cast<std::int32_t>(2);
575       }
576 
577     private:
578       const T AP;
579       const T BP;
580       const T CP;
581 
N() const582       virtual std::int32_t N() const { return static_cast<std::int32_t>(util::digit_scale<T>() * 1600.0F); }
583     };
584 
585     template<class T> T luke_ccoef4_hypergeometric_0f1(const T& a, const T& x);
586     template<class T> T luke_ccoef1_hypergeometric_1f0(const T& a, const T& x);
587     template<class T> T luke_ccoef3_hypergeometric_1f1(const T& a, const T& b, const T& x);
588     template<class T> T luke_ccoef6_hypergeometric_1f2(const T& a, const T& b, const T& c, const T& x);
589     template<class T> T luke_ccoef2_hypergeometric_2f1(const T& a, const T& b, const T& c, const T& x);
590   }
591 }
592 
593 template<class T>
luke_ccoef4_hypergeometric_0f1(const T & a,const T & x)594 T examples::nr_006::luke_ccoef4_hypergeometric_0f1(const T& a, const T& x)
595 {
596   const ccoef4_hypergeometric_0f1<T> hypergeometric_0f1_object(a, x, T(-20));
597 
598   hypergeometric_0f1_object.ccoef();
599 
600   return hypergeometric_0f1_object.series();
601 }
602 
603 template<class T>
luke_ccoef1_hypergeometric_1f0(const T & a,const T & x)604 T examples::nr_006::luke_ccoef1_hypergeometric_1f0(const T& a, const T& x)
605 {
606   const ccoef1_hypergeometric_1f0<T> hypergeometric_1f0_object(a, x, T(-20));
607 
608   hypergeometric_1f0_object.ccoef();
609 
610   return hypergeometric_1f0_object.series();
611 }
612 
613 template<class T>
luke_ccoef3_hypergeometric_1f1(const T & a,const T & b,const T & x)614 T examples::nr_006::luke_ccoef3_hypergeometric_1f1(const T& a, const T& b, const T& x)
615 {
616   const ccoef3_hypergeometric_1f1<T> hypergeometric_1f1_object(a, b, x, T(-20));
617 
618   hypergeometric_1f1_object.ccoef();
619 
620   return hypergeometric_1f1_object.series();
621 }
622 
623 template<class T>
luke_ccoef6_hypergeometric_1f2(const T & a,const T & b,const T & c,const T & x)624 T examples::nr_006::luke_ccoef6_hypergeometric_1f2(const T& a, const T& b, const T& c, const T& x)
625 {
626   const ccoef6_hypergeometric_1f2<T> hypergeometric_1f2_object(a, b, c, x, T(-20));
627 
628   hypergeometric_1f2_object.ccoef();
629 
630   return hypergeometric_1f2_object.series();
631 }
632 
633 template<class T>
luke_ccoef2_hypergeometric_2f1(const T & a,const T & b,const T & c,const T & x)634 T examples::nr_006::luke_ccoef2_hypergeometric_2f1(const T& a, const T& b, const T& c, const T& x)
635 {
636   const ccoef2_hypergeometric_2f1<T> hypergeometric_2f1_object(a, b, c, x, T(-20));
637 
638   hypergeometric_2f1_object.ccoef();
639 
640   return hypergeometric_2f1_object.series();
641 }
642 
main()643 int main()
644 {
645   stopwatch<STD_CHRONO::high_resolution_clock> my_stopwatch;
646   float total_time = 0.0F;
647 
648   std::vector<mp_type> hypergeometric_0f1_results(20U);
649   std::vector<mp_type> hypergeometric_1f0_results(20U);
650   std::vector<mp_type> hypergeometric_1f1_results(20U);
651   std::vector<mp_type> hypergeometric_2f1_results(20U);
652   std::vector<mp_type> hypergeometric_1f2_results(20U);
653 
654   const mp_type a(mp_type(3) / 7);
655   const mp_type b(mp_type(2) / 3);
656   const mp_type c(mp_type(1) / 4);
657 
658   std::int_least16_t i;
659 
660   std::cout << "test hypergeometric_0f1." << std::endl;
661   i = 1U;
662   my_stopwatch.reset();
663 
664   // Generate a table of values of Hypergeometric0F1.
665   // Compare with the Mathematica command:
666   // Table[N[HypergeometricPFQ[{}, {3/7}, -(i*EulerGamma)], 100], {i, 1, 20, 1}]
667   std::for_each(hypergeometric_0f1_results.begin(),
668                 hypergeometric_0f1_results.end(),
669                 [&i, &a](mp_type& new_value)
670                 {
671                   const mp_type x(-(boost::math::constants::euler<mp_type>() * i));
672 
673                   new_value = examples::nr_006::luke_ccoef4_hypergeometric_0f1(a, x);
674 
675                   ++i;
676                 });
677 
678   total_time += STD_CHRONO::duration_cast<STD_CHRONO::duration<float> >(my_stopwatch.elapsed()).count();
679 
680   // Print the values of Hypergeometric0F1.
681   std::for_each(hypergeometric_0f1_results.begin(),
682                 hypergeometric_0f1_results.end(),
683                 [](const mp_type& h)
684                 {
685                   std::cout << std::setprecision(DIGIT_COUNT) << h << std::endl;
686                 });
687 
688   std::cout << "test hypergeometric_1f0." << std::endl;
689   i = 1U;
690   my_stopwatch.reset();
691 
692   // Generate a table of values of Hypergeometric1F0.
693   // Compare with the Mathematica command:
694   // Table[N[HypergeometricPFQ[{3/7}, {}, -(i*EulerGamma)], 100], {i, 1, 20, 1}]
695   std::for_each(hypergeometric_1f0_results.begin(),
696                 hypergeometric_1f0_results.end(),
697                 [&i, &a](mp_type& new_value)
698                 {
699                   const mp_type x(-(boost::math::constants::euler<mp_type>() * i));
700 
701                   new_value = examples::nr_006::luke_ccoef1_hypergeometric_1f0(a, x);
702 
703                   ++i;
704                 });
705 
706   total_time += STD_CHRONO::duration_cast<STD_CHRONO::duration<float> >(my_stopwatch.elapsed()).count();
707 
708   // Print the values of Hypergeometric1F0.
709   std::for_each(hypergeometric_1f0_results.begin(),
710                 hypergeometric_1f0_results.end(),
711                 [](const mp_type& h)
712                 {
713                   std::cout << std::setprecision(DIGIT_COUNT) << h << std::endl;
714                 });
715 
716   std::cout << "test hypergeometric_1f1." << std::endl;
717   i = 1U;
718   my_stopwatch.reset();
719 
720   // Generate a table of values of Hypergeometric1F1.
721   // Compare with the Mathematica command:
722   // Table[N[HypergeometricPFQ[{3/7}, {2/3}, -(i*EulerGamma)], 100], {i, 1, 20, 1}]
723   std::for_each(hypergeometric_1f1_results.begin(),
724                 hypergeometric_1f1_results.end(),
725                 [&i, &a, &b](mp_type& new_value)
726                 {
727                   const mp_type x(-(boost::math::constants::euler<mp_type>() * i));
728 
729                   new_value = examples::nr_006::luke_ccoef3_hypergeometric_1f1(a, b, x);
730 
731                   ++i;
732                 });
733 
734   total_time += STD_CHRONO::duration_cast<STD_CHRONO::duration<float> >(my_stopwatch.elapsed()).count();
735 
736   // Print the values of Hypergeometric1F1.
737   std::for_each(hypergeometric_1f1_results.begin(),
738                 hypergeometric_1f1_results.end(),
739                 [](const mp_type& h)
740                 {
741                   std::cout << std::setprecision(DIGIT_COUNT) << h << std::endl;
742                 });
743 
744   std::cout << "test hypergeometric_1f2." << std::endl;
745   i = 1U;
746   my_stopwatch.reset();
747 
748   // Generate a table of values of Hypergeometric1F2.
749   // Compare with the Mathematica command:
750   // Table[N[HypergeometricPFQ[{3/7}, {2/3, 1/4}, -(i*EulerGamma)], 100], {i, 1, 20, 1}]
751   std::for_each(hypergeometric_1f2_results.begin(),
752                 hypergeometric_1f2_results.end(),
753                 [&i, &a, &b, &c](mp_type& new_value)
754                 {
755                   const mp_type x(-(boost::math::constants::euler<mp_type>() * i));
756 
757                   new_value = examples::nr_006::luke_ccoef6_hypergeometric_1f2(a, b, c, x);
758 
759                   ++i;
760                 });
761 
762   total_time += STD_CHRONO::duration_cast<STD_CHRONO::duration<float> >(my_stopwatch.elapsed()).count();
763 
764   // Print the values of Hypergeometric1F2.
765   std::for_each(hypergeometric_1f2_results.begin(),
766                 hypergeometric_1f2_results.end(),
767                 [](const mp_type& h)
768                 {
769                   std::cout << std::setprecision(DIGIT_COUNT) << h << std::endl;
770                 });
771 
772   std::cout << "test hypergeometric_2f1." << std::endl;
773   i = 1U;
774   my_stopwatch.reset();
775 
776   // Generate a table of values of Hypergeometric2F1.
777   // Compare with the Mathematica command:
778   // Table[N[HypergeometricPFQ[{3/7, 2/3}, {1/4}, -(i * EulerGamma)], 100], {i, 1, 20, 1}]
779   std::for_each(hypergeometric_2f1_results.begin(),
780                 hypergeometric_2f1_results.end(),
781                 [&i, &a, &b, &c](mp_type& new_value)
782                 {
783                   const mp_type x(-(boost::math::constants::euler<mp_type>() * i));
784 
785                   new_value = examples::nr_006::luke_ccoef2_hypergeometric_2f1(a, b, c, x);
786 
787                   ++i;
788                 });
789 
790   total_time += STD_CHRONO::duration_cast<STD_CHRONO::duration<float> >(my_stopwatch.elapsed()).count();
791 
792   // Print the values of Hypergeometric2F1.
793   std::for_each(hypergeometric_2f1_results.begin(),
794                 hypergeometric_2f1_results.end(),
795                 [](const mp_type& h)
796                 {
797                   std::cout << std::setprecision(DIGIT_COUNT) << h << std::endl;
798                 });
799 
800   std::cout << "Total execution time = " << std::setprecision(3) << total_time << "s" << std::endl;
801 }
802