1 // Copyright (c) 2006 Xiaogang Zhang
2 // Copyright (c) 2006 John Maddock
3 // Use, modification and distribution are subject to the
4 // Boost Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 // XZ wrote the original of this file as part of the Google
9 // Summer of Code 2006. JM modified it to fit into the
10 // Boost.Math conceptual framework better, and to correctly
11 // handle the various corner cases.
14 #ifndef BOOST_MATH_ELLINT_3_HPP
15 #define BOOST_MATH_ELLINT_3_HPP
21 #include <boost/math/special_functions/ellint_rf.hpp>
22 #include <boost/math/special_functions/ellint_rj.hpp>
23 #include <boost/math/special_functions/ellint_1.hpp>
24 #include <boost/math/special_functions/ellint_2.hpp>
25 #include <boost/math/special_functions/log1p.hpp>
26 #include <boost/math/constants/constants.hpp>
27 #include <boost/math/policies/error_handling.hpp>
28 #include <boost/math/tools/workaround.hpp>
29 #include <boost/math/special_functions/round.hpp>
31 // Elliptic integrals (complete and incomplete) of the third kind
32 // Carlson, Numerische Mathematik, vol 33, 1 (1979)
34 namespace boost { namespace math {
38 template <typename T, typename Policy>
39 T ellint_pi_imp(T v, T k, T vc, const Policy& pol);
41 // Elliptic integral (Legendre form) of the third kind
42 template <typename T, typename Policy>
43 T ellint_pi_imp(T v, T phi, T k, T vc, const Policy& pol)
45 // Note vc = 1-v presumably without cancellation error.
46 T value, x, y, z, p, t;
49 using namespace boost::math::tools;
50 using namespace boost::math::constants;
52 static const char* function = "boost::math::ellint_3<%1%>(%1%,%1%,%1%)";
56 return policies::raise_domain_error<T>(function,
57 "Got k = %1%, function requires |k| <= 1", k, pol);
60 T sphi = sin(fabs(phi));
62 if(v > 1 / (sphi * sphi))
64 // Complex result is a domain error:
65 return policies::raise_domain_error<T>(function,
66 "Got v = %1%, but result is complex for v > 1 / sin^2(phi)", v, pol);
69 // Special cases first:
73 return (k == 0) ? phi : ellint_f_imp(phi, k, pol);
75 if(phi == constants::pi<T>() / 2)
77 // Have to filter this case out before the next
78 // special case, otherwise we might get an infinity from
80 // Also note that since we can't represent PI/2 exactly
81 // in a T, this is a bit of a guess as to the users true
84 return ellint_pi_imp(v, k, vc, pol);
92 return atan(vcr * tan(phi)) / vcr;
102 T arg = vcr * tan(phi);
103 return (boost::math::log1p(arg, pol) - boost::math::log1p(-arg, pol)) / (2 * vcr);
110 // If we don't shift to 0 <= v <= 1 we get
111 // cancellation errors later on. Use
112 // A&S 17.7.15/16 to shift to v > 0:
115 T N = (k2 - v) / (1 - v);
116 T Nm1 = (1 - k2) / (1 - v);
117 T p2 = sqrt(-v * (k2 - v) / (1 - v));
118 T delta = sqrt(1 - k2 * sphi * sphi);
119 T result = ellint_pi_imp(N, phi, k, Nm1, pol);
121 result *= sqrt(Nm1 * (1 - k2 / N));
122 result += ellint_f_imp(phi, k, pol) * k2 / p2;
123 result += atan((p2/2) * sin(2 * phi) / delta);
124 result /= sqrt((1 - v) * (1 - k2 / v));
127 #if 0 // disabled but retained for future reference: see below.
131 // If v > 1 we can use the identity in A&S 17.7.7/8
132 // to shift to 0 <= v <= 1. Unfortunately this
133 // identity appears only to function correctly when
134 // 0 <= phi <= pi/2, but it's when phi is outside that
135 // range that we really need it: That's when
136 // Carlson's formula fails, and what's more the periodicity
137 // reduction used below on phi doesn't work when v > 1.
139 // So we're stuck... the code is archived here in case
140 // some bright spart can figure out the fix.
144 T Nm1 = (v - k2) / v;
145 T p1 = sqrt((-vc) * (1 - k2 / v));
146 T delta = sqrt(1 - k2 * sphi * sphi);
148 // These next two terms have a large amount of cancellation
149 // so it's not clear if this relation is useable even if
150 // the issues with phi > pi/2 can be fixed:
152 T result = -ellint_pi_imp(N, phi, k, Nm1);
153 result += ellint_f_imp(phi, k);
155 // This log term gives the complex result when
157 // However that case is dealt with as an error above,
158 // so we should always get a real result here:
160 result += log((delta + p1 * tan(phi)) / (delta - p1 * tan(phi))) / (2 * p1);
165 // Carlson's algorithm works only for |phi| <= pi/2,
166 // use the integrand's periodicity to normalize phi
168 // Xiaogang's original code used a cast to long long here
169 // but that fails if T has more digits than a long long,
170 // so rewritten to use fmod instead:
172 if(fabs(phi) > 1 / tools::epsilon<T>())
175 return policies::raise_domain_error<T>(
177 "Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
179 // Phi is so large that phi%pi is necessarily zero (or garbage),
180 // just return the second part of the duplication formula:
182 value = 2 * fabs(phi) * ellint_pi_imp(v, k, vc, pol) / constants::pi<T>();
186 T rphi = boost::math::tools::fmod_workaround(T(fabs(phi)), T(constants::half_pi<T>()));
187 T m = boost::math::round((fabs(phi) - rphi) / constants::half_pi<T>());
189 if(boost::math::tools::fmod_workaround(m, T(2)) > 0.5)
193 rphi = constants::half_pi<T>() - rphi;
205 value = sign * sinp * (ellint_rf_imp(x, y, z, pol) + v * t * ellint_rj_imp(x, y, z, p, pol) / 3);
206 if((m > 0) && (vc > 0))
207 value += m * ellint_pi_imp(v, k, vc, pol);
212 value = -value; // odd function
217 // Complete elliptic integral (Legendre form) of the third kind
218 template <typename T, typename Policy>
219 T ellint_pi_imp(T v, T k, T vc, const Policy& pol)
221 // Note arg vc = 1-v, possibly without cancellation errors
223 using namespace boost::math::tools;
225 static const char* function = "boost::math::ellint_pi<%1%>(%1%,%1%)";
229 return policies::raise_domain_error<T>(function,
230 "Got k = %1%, function requires |k| <= 1", k, pol);
234 // Result is complex:
235 return policies::raise_domain_error<T>(function,
236 "Got v = %1%, function requires v < 1", v, pol);
241 return (k == 0) ? boost::math::constants::pi<T>() / 2 : ellint_k_imp(k, pol);
247 T N = (k2 - v) / (1 - v);
248 T Nm1 = (1 - k2) / (1 - v);
249 T p2 = sqrt(-v * (k2 - v) / (1 - v));
251 T result = boost::math::detail::ellint_pi_imp(N, k, Nm1, pol);
253 result *= sqrt(Nm1 * (1 - k2 / N));
254 result += ellint_k_imp(k, pol) * k2 / p2;
255 result /= sqrt((1 - v) * (1 - k2 / v));
263 T value = ellint_rf_imp(x, y, z, pol) + v * ellint_rj_imp(x, y, z, p, pol) / 3;
268 template <class T1, class T2, class T3>
269 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const mpl::false_&)
271 return boost::math::ellint_3(k, v, phi, policies::policy<>());
274 template <class T1, class T2, class Policy>
275 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const mpl::true_&)
277 typedef typename tools::promote_args<T1, T2>::type result_type;
278 typedef typename policies::evaluation<result_type, Policy>::type value_type;
279 return policies::checked_narrowing_cast<result_type, Policy>(
280 detail::ellint_pi_imp(
281 static_cast<value_type>(v),
282 static_cast<value_type>(k),
283 static_cast<value_type>(1-v),
284 pol), "boost::math::ellint_3<%1%>(%1%,%1%)");
287 } // namespace detail
289 template <class T1, class T2, class T3, class Policy>
290 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const Policy& pol)
292 typedef typename tools::promote_args<T1, T2, T3>::type result_type;
293 typedef typename policies::evaluation<result_type, Policy>::type value_type;
294 return policies::checked_narrowing_cast<result_type, Policy>(
295 detail::ellint_pi_imp(
296 static_cast<value_type>(v),
297 static_cast<value_type>(phi),
298 static_cast<value_type>(k),
299 static_cast<value_type>(1-v),
300 pol), "boost::math::ellint_3<%1%>(%1%,%1%,%1%)");
303 template <class T1, class T2, class T3>
304 typename detail::ellint_3_result<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi)
306 typedef typename policies::is_policy<T3>::type tag_type;
307 return detail::ellint_3(k, v, phi, tag_type());
310 template <class T1, class T2>
311 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v)
313 return ellint_3(k, v, policies::policy<>());
318 #endif // BOOST_MATH_ELLINT_3_HPP