1 // 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 #define BOOST_ENABLE_ASSERT_HANDLER
7 #define BOOST_MATH_MAX_SERIES_ITERATION_POLICY INT_MAX
8 // for consistent behaviour across compilers/platforms:
9 #define BOOST_MATH_PROMOTE_DOUBLE_POLICY false
10 // overflow to infinity is OK, we treat these as zero error as long as the sign is correct!
11 #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
15 #include <boost/multiprecision/mpfr.hpp>
16 #include <boost/multiprecision/cpp_bin_float.hpp>
17 #include <boost/math/special_functions/hypergeometric_1F1.hpp>
18 #include <boost/math/special_functions/hypergeometric_pFq.hpp>
19 #include <boost/math/special_functions/relative_difference.hpp>
21 #include <boost/random.hpp>
24 #include <boost/iostreams/tee.hpp>
25 #include <boost/iostreams/stream.hpp>
27 using boost::multiprecision::mpfr_float
;
31 // We convert assertions into exceptions, so we can log them and continue:
33 void assertion_failed(char const * expr
, char const *, char const * file
, long line
)
35 std::ostringstream oss
;
36 oss
<< file
<< ":" << line
<< " Assertion failed: " << expr
;
37 throw std::runtime_error(oss
.str());
42 typedef boost::multiprecision::cpp_bin_float_quad test_type
;
49 test_type a_start
, a_end
;
50 test_type b_start
, b_end
;
51 test_type a_mult
, b_mult
;
53 std::cout
<< "Enter range for paramater a: ";
54 std::cin
>> a_start
>> a_end
;
55 std::cout
<< "Enter range for paramater b: ";
56 std::cin
>> b_start
>> b_end
;
57 std::cout
<< "Enter multiplier for a parameter: ";
59 std::cout
<< "Enter multiplier for b parameter: ";
62 double error_limit
= 200;
63 double time_limit
= 10.0;
65 for (test_type a
= a_start
; a
< a_end
; a_start
< 0 ? a
/= a_mult
: a
*= a_mult
)
67 for (test_type b
= b_start
; b
< b_end
; b_start
< 0 ? b
/= b_mult
: b
*= b_mult
)
70 test_type last_good
= 0;
73 for (test_type z
= 1; z
< 1e10
; z
*= z_mult
, z_mult
*= 2)
75 // std::cout << "z = " << z << std::endl;
76 boost::uintmax_t max_iter
= 1000;
77 test_type calc
= boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients
<test_type
>(a
, b
, z
), std::numeric_limits
<test_type
>::epsilon() * 2, max_iter
);
78 test_type reference
= (test_type
)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a
) }, { mpfr_float(b
) }, mpfr_float(z
), 50, time_limit
) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a
+ 1) }, { mpfr_float(b
+ 1) }, mpfr_float(z
), std::numeric_limits
<test_type
>::digits10
* 2, time_limit
));
79 double err
= (double)boost::math::epsilon_difference(reference
, calc
);
81 if (err
< error_limit
)
92 catch (const std::exception
& e
)
94 std::cout
<< "Unexpected exception: " << e
.what() << std::endl
;
95 std::cout
<< "For a = " << a
<< " b = " << b
<< " z = " << bad
* z_mult
/ 2 << std::endl
;
99 z_limit
= 1; // Any z is large enough
100 else if (0 == last_good
)
101 z_limit
= std::numeric_limits
<test_type
> ::infinity();
105 // At this stage last_good and bad should bracket the edge of the domain, bisect to narrow things down:
107 z_limit
= last_good
== 0 ? 0 : boost::math::tools::bisect([&a
, b
, error_limit
, time_limit
](test_type z
)
109 boost::uintmax_t max_iter
= 1000;
110 test_type calc
= boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients
<test_type
>(a
, b
, z
), std::numeric_limits
<test_type
>::epsilon() * 2, max_iter
);
111 test_type reference
= (test_type
)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a
) }, { mpfr_float(b
) }, mpfr_float(z
), 50, time_limit
+ 20) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a
+ 1) }, { mpfr_float(b
+ 1) }, mpfr_float(z
), std::numeric_limits
<test_type
>::digits10
* 2, time_limit
+ 20));
112 test_type err
= boost::math::epsilon_difference(reference
, calc
);
113 return err
< error_limit
? 1 : -1;
114 }, bad
, last_good
, boost::math::tools::equal_floor()).first
;
115 z_limit
= floor(z_limit
+ 2); // Give ourselves some headroom!
117 // std::cout << "z_limit = " << z_limit << std::endl;
119 // Now over again for backwards recurrence domain at the same points:
121 bad
= z_limit
> 1e10
? 1e10
: z_limit
;
124 for (test_type z
= bad
; z
> 1; z
/= z_mult
, z_mult
*= 2)
126 // std::cout << "z = " << z << std::endl;
128 boost::uintmax_t max_iter
= 1000;
129 test_type calc
= boost::math::tools::function_ratio_from_backwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients
<test_type
>(a
, b
, z
), std::numeric_limits
<test_type
>::epsilon() * 2, max_iter
);
130 test_type reference
= (test_type
)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a
) }, { mpfr_float(b
) }, mpfr_float(z
), 50, time_limit
) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a
- 1) }, { mpfr_float(b
- 1) }, mpfr_float(z
), std::numeric_limits
<test_type
>::digits10
* 2, time_limit
));
131 test_type err
= boost::math::epsilon_difference(reference
, calc
);
133 if (err
< error_limit
)
143 catch (const std::exception
& e
)
146 std::cout
<< "Unexpected exception: " << e
.what() << std::endl
;
147 std::cout
<< "For a = " << a
<< " b = " << b
<< " z = " << z
<< std::endl
;
150 test_type lower_z_limit
;
153 else if (last_good
>= bad
)
155 boost::uintmax_t max_iter
= 1000;
157 test_type calc
= boost::math::tools::function_ratio_from_forwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients
<test_type
>(a
, b
, z
), std::numeric_limits
<test_type
>::epsilon() * 2, max_iter
);
158 test_type reference
= (test_type
)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a
) }, { mpfr_float(b
) }, mpfr_float(z
), 50, time_limit
) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a
+ 1) }, { mpfr_float(b
+ 1) }, mpfr_float(z
), std::numeric_limits
<test_type
>::digits10
* 2, time_limit
));
159 test_type err
= boost::math::epsilon_difference(reference
, calc
);
160 if (err
< error_limit
)
162 lower_z_limit
= bad
; // Both forwards and backwards iteration work!!!
165 throw std::runtime_error("Internal logic failed!");
170 // At this stage last_good and bad should bracket the edge of the domain, bisect to narrow things down:
172 lower_z_limit
= last_good
== 0 ? 0 : boost::math::tools::bisect([&a
, b
, error_limit
, time_limit
](test_type z
)
174 boost::uintmax_t max_iter
= 1000;
175 test_type calc
= boost::math::tools::function_ratio_from_backwards_recurrence(boost::math::detail::hypergeometric_1F1_recurrence_a_and_b_coefficients
<test_type
>(a
, b
, z
), std::numeric_limits
<test_type
>::epsilon() * 2, max_iter
);
176 test_type reference
= (test_type
)(boost::math::hypergeometric_pFq_precision({ mpfr_float(a
) }, { mpfr_float(b
) }, mpfr_float(z
), 50, time_limit
+ 20) / boost::math::hypergeometric_pFq_precision({ mpfr_float(a
- 1) }, { mpfr_float(b
- 1) }, mpfr_float(z
), std::numeric_limits
<test_type
>::digits10
* 2, time_limit
+ 20));
177 test_type err
= boost::math::epsilon_difference(reference
, calc
);
178 return err
< error_limit
? 1 : -1;
179 }, last_good
, bad
, boost::math::tools::equal_floor()).first
;
180 z_limit
= ceil(z_limit
- 2); // Give ourselves some headroom!
183 std::cout
<< std::setprecision(std::numeric_limits
<test_type
>::max_digits10
) << "{ " << a
<< ", " << b
<< ", " << lower_z_limit
<< ", " << z_limit
<< "}," << std::endl
;
187 catch (const std::exception
& e
)
189 std::cout
<< "Unexpected exception: " << e
.what() << std::endl
;