1 // (C) Copyright John Maddock 2015.
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)
8 #ifndef BOOST_NO_CXX11_HDR_TUPLE
10 #define BOOST_TEST_MAIN
11 #include <boost/test/unit_test.hpp>
12 #include <boost/test/tools/floating_point_comparison.hpp>
13 #include <boost/math/tools/roots.hpp>
14 #include <boost/test/results_collector.hpp>
15 #include <boost/test/unit_test.hpp>
16 #include <boost/math/special_functions/cbrt.hpp>
17 #include <boost/math/special_functions/beta.hpp>
21 #include "table_type.hpp"
23 // No derivatives - using TOMS748 internally.
24 struct cbrt_functor_noderiv
25 { // cube root of x using only function - no derivatives.
26 cbrt_functor_noderiv(double to_find_root_of) : a(to_find_root_of)
27 { // Constructor just stores value a to find root of.
29 double operator()(double x)
31 double fx = x*x*x - a; // Difference (estimate x^3 - a).
35 double a; // to be 'cube_rooted'.
36 }; // template <class T> struct cbrt_functor_noderiv
38 // Using 1st derivative only Newton-Raphson
39 struct cbrt_functor_deriv
40 { // Functor also returning 1st derviative.
41 cbrt_functor_deriv(double const& to_find_root_of) : a(to_find_root_of)
42 { // Constructor stores value a to find root of,
43 // for example: calling cbrt_functor_deriv<double>(x) to use to get cube root of x.
45 std::pair<double, double> operator()(double const& x)
46 { // Return both f(x) and f'(x).
47 double fx = x*x*x - a; // Difference (estimate x^3 - value).
48 double dx = 3 * x*x; // 1st derivative = 3x^2.
49 return std::make_pair(fx, dx); // 'return' both fx and dx.
52 double a; // to be 'cube_rooted'.
54 // Using 1st and 2nd derivatives with Halley algorithm.
55 struct cbrt_functor_2deriv
56 { // Functor returning both 1st and 2nd derivatives.
57 cbrt_functor_2deriv(double const& to_find_root_of) : a(to_find_root_of)
58 { // Constructor stores value a to find root of, for example:
59 // calling cbrt_functor_2deriv<double>(x) to get cube root of x,
61 std::tuple<double, double, double> operator()(double const& x)
62 { // Return both f(x) and f'(x) and f''(x).
63 double fx = x*x*x - a; // Difference (estimate x^3 - value).
64 double dx = 3 * x*x; // 1st derivative = 3x^2.
65 double d2x = 6 * x; // 2nd derivative = 6x.
66 return std::make_tuple(fx, dx, d2x); // 'return' fx, dx and d2x.
69 double a; // to be 'cube_rooted'.
72 template <class T, class Policy>
73 struct ibeta_roots_1 // for first order algorithms
75 ibeta_roots_1(T _a, T _b, T t, bool inv = false)
76 : a(_a), b(_b), target(t), invert(inv) {}
78 T operator()(const T& x)
80 return boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
87 template <class T, class Policy>
88 struct ibeta_roots_2 // for second order algorithms
90 ibeta_roots_2(T _a, T _b, T t, bool inv = false)
91 : a(_a), b(_b), target(t), invert(inv) {}
93 boost::math::tuple<T, T> operator()(const T& x)
95 typedef boost::math::lanczos::lanczos<T, Policy> S;
96 typedef typename S::type L;
97 T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
99 -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
100 : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
103 y = boost::math::tools::min_value<T>() * 8;
106 // make sure we don't have a zero derivative:
108 f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
110 return boost::math::make_tuple(f, f1);
117 template <class T, class Policy>
118 struct ibeta_roots_3 // for third order algorithms
120 ibeta_roots_3(T _a, T _b, T t, bool inv = false)
121 : a(_a), b(_b), target(t), invert(inv) {}
123 boost::math::tuple<T, T, T> operator()(const T& x)
125 typedef typename boost::math::lanczos::lanczos<T, Policy>::type L;
126 T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
128 -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
129 : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
132 y = boost::math::tools::min_value<T>() * 8;
134 T f2 = f1 * (-y * a + (b - 2) * x + 1) / (y * x);
138 // make sure we don't have a zero derivative:
140 f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
142 return boost::math::make_tuple(f, f1, f2);
150 BOOST_AUTO_TEST_CASE( test_main )
152 int newton_limits = static_cast<int>(std::numeric_limits<double>::digits * 0.6);
155 boost::uintmax_t iters;
161 double result = boost::math::cbrt(arg);
163 // Start with a really bad guess 5 times below the result:
168 std::pair<double, double> r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
169 BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
170 BOOST_CHECK_LE(iters, 14);
173 dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, guess / 2, result * 10, newton_limits, iters);
174 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
175 BOOST_CHECK_LE(iters, 12);
178 dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
179 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
180 BOOST_CHECK_LE(iters, 7);
183 dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
184 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
185 BOOST_CHECK_LE(iters, 11);
187 // Over again with a bad guess 5 times larger than the result:
191 r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
192 BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
193 BOOST_CHECK_LE(iters, 14);
196 dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
197 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
198 BOOST_CHECK_LE(iters, 12);
201 dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
202 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
203 BOOST_CHECK_LE(iters, 7);
206 dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
207 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
208 BOOST_CHECK_LE(iters, 11);
210 // A much better guess, 1% below result:
213 guess = result * 0.9;
214 r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
215 BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
216 BOOST_CHECK_LE(iters, 12);
219 dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
220 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
221 BOOST_CHECK_LE(iters, 5);
224 dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
225 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
226 BOOST_CHECK_LE(iters, 3);
229 dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
230 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
231 BOOST_CHECK_LE(iters, 4);
233 // A much better guess, 1% above result:
236 guess = result * 1.1;
237 r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
238 BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
239 BOOST_CHECK_LE(iters, 12);
242 dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
243 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
244 BOOST_CHECK_LE(iters, 5);
247 dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
248 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
249 BOOST_CHECK_LE(iters, 3);
252 dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
253 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
254 BOOST_CHECK_LE(iters, 4);
260 // Test ibeta as this triggers all the pathological cases!
267 # include "ibeta_small_data.ipp"
269 for (unsigned i = 0; i < ibeta_small_data.size(); ++i)
272 // These inverse tests are thrown off if the output of the
273 // incomplete beta is too close to 1: basically there is insuffient
274 // information left in the value we're using as input to the inverse
275 // to be able to get back to the original value.
277 if (ibeta_small_data[i][5] == 0)
280 dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
281 BOOST_CHECK_EQUAL(dr, 0.0);
282 BOOST_CHECK_LE(iters, 27);
284 dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
285 BOOST_CHECK_EQUAL(dr, 0.0);
286 BOOST_CHECK_LE(iters, 10);
288 else if ((1 - ibeta_small_data[i][5] > 0.001)
289 && (fabs(ibeta_small_data[i][5]) > 2 * boost::math::tools::min_value<double>()))
292 double result = ibeta_small_data[i][2];
293 dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
294 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 200);
295 #if defined(BOOST_MSVC) && (BOOST_MSVC == 1600)
296 BOOST_CHECK_LE(iters, 40);
298 BOOST_CHECK_LE(iters, 27);
301 result = ibeta_small_data[i][2];
302 dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
303 BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 200);
304 BOOST_CHECK_LE(iters, 40);
306 else if (1 == ibeta_small_data[i][5])
309 dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
310 BOOST_CHECK_EQUAL(dr, 1.0);
311 BOOST_CHECK_LE(iters, 27);
313 dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
314 BOOST_CHECK_EQUAL(dr, 1.0);
315 BOOST_CHECK_LE(iters, 10);
323 int main() { return 0; }