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>
30
// Elliptic integrals (complete and incomplete) of the third kind
31
// Carlson, Numerische Mathematik, vol 33, 1 (1979)
33
namespace boost { namespace math {
37
template <typename T, typename Policy>
38
T ellint_pi_imp(T v, T k, T vc, const Policy& pol);
40
// Elliptic integral (Legendre form) of the third kind
41
template <typename T, typename Policy>
42
T ellint_pi_imp(T v, T phi, T k, T vc, const Policy& pol)
44
// Note vc = 1-v presumably without cancellation error.
45
T value, x, y, z, p, t;
48
using namespace boost::math::tools;
49
using namespace boost::math::constants;
51
static const char* function = "boost::math::ellint_3<%1%>(%1%,%1%,%1%)";
55
return policies::raise_domain_error<T>(function,
56
"Got k = %1%, function requires |k| <= 1", k, pol);
59
T sphi = sin(fabs(phi));
61
if(v > 1 / (sphi * sphi))
63
// Complex result is a domain error:
64
return policies::raise_domain_error<T>(function,
65
"Got v = %1%, but result is complex for v > 1 / sin^2(phi)", v, pol);
68
// Special cases first:
72
return (k == 0) ? phi : ellint_f_imp(phi, k, pol);
74
if(phi == constants::pi<T>() / 2)
76
// Have to filter this case out before the next
77
// special case, otherwise we might get an infinity from
79
// Also note that since we can't represent PI/2 exactly
80
// in a T, this is a bit of a guess as to the users true
83
return ellint_pi_imp(v, k, vc, pol);
91
return atan(vcr * tan(phi)) / vcr;
101
T arg = vcr * tan(phi);
102
return (boost::math::log1p(arg, pol) - boost::math::log1p(-arg, pol)) / (2 * vcr);
109
// If we don't shift to 0 <= v <= 1 we get
110
// cancellation errors later on. Use
111
// A&S 17.7.15/16 to shift to v > 0:
114
T N = (k2 - v) / (1 - v);
115
T Nm1 = (1 - k2) / (1 - v);
116
T p2 = sqrt(-v * (k2 - v) / (1 - v));
117
T delta = sqrt(1 - k2 * sphi * sphi);
118
T result = ellint_pi_imp(N, phi, k, Nm1, pol);
120
result *= sqrt(Nm1 * (1 - k2 / N));
121
result += ellint_f_imp(phi, k, pol) * k2 / p2;
122
result += atan((p2/2) * sin(2 * phi) / delta);
123
result /= sqrt((1 - v) * (1 - k2 / v));
126
#if 0 // disabled but retained for future reference: see below.
130
// If v > 1 we can use the identity in A&S 17.7.7/8
131
// to shift to 0 <= v <= 1. Unfortunately this
132
// identity appears only to function correctly when
133
// 0 <= phi <= pi/2, but it's when phi is outside that
134
// range that we really need it: That's when
135
// Carlson's formula fails, and what's more the periodicity
136
// reduction used below on phi doesn't work when v > 1.
138
// So we're stuck... the code is archived here in case
139
// some bright spart can figure out the fix.
143
T Nm1 = (v - k2) / v;
144
T p1 = sqrt((-vc) * (1 - k2 / v));
145
T delta = sqrt(1 - k2 * sphi * sphi);
147
// These next two terms have a large amount of cancellation
148
// so it's not clear if this relation is useable even if
149
// the issues with phi > pi/2 can be fixed:
151
T result = -ellint_pi_imp(N, phi, k, Nm1);
152
result += ellint_f_imp(phi, k);
154
// This log term gives the complex result when
156
// However that case is dealt with as an error above,
157
// so we should always get a real result here:
159
result += log((delta + p1 * tan(phi)) / (delta - p1 * tan(phi))) / (2 * p1);
164
// Carlson's algorithm works only for |phi| <= pi/2,
165
// use the integrand's periodicity to normalize phi
167
// Xiaogang's original code used a cast to long long here
168
// but that fails if T has more digits than a long long,
169
// so rewritten to use fmod instead:
171
if(fabs(phi) > 1 / tools::epsilon<T>())
174
return policies::raise_domain_error<T>(
176
"Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
178
// Phi is so large that phi%pi is necessarily zero (or garbage),
179
// just return the second part of the duplication formula:
181
value = 2 * fabs(phi) * ellint_pi_imp(v, k, vc, pol) / constants::pi<T>();
185
T rphi = boost::math::tools::fmod_workaround(fabs(phi), constants::pi<T>() / 2);
186
T m = floor((2 * fabs(phi)) / constants::pi<T>());
188
if(boost::math::tools::fmod_workaround(m, T(2)) > 0.5)
192
rphi = constants::pi<T>() / 2 - rphi;
195
if((m > 0) && (v > 1))
198
// The region with v > 1 and phi outside [0, pi/2] is
199
// currently unsupported:
201
return policies::raise_domain_error<T>(
203
"Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
215
value = sign * sinp * (ellint_rf_imp(x, y, z, pol) + v * t * ellint_rj_imp(x, y, z, p, pol) / 3);
217
value += m * ellint_pi_imp(v, k, vc, pol);
222
value = -value; // odd function
227
// Complete elliptic integral (Legendre form) of the third kind
228
template <typename T, typename Policy>
229
T ellint_pi_imp(T v, T k, T vc, const Policy& pol)
231
// Note arg vc = 1-v, possibly without cancellation errors
233
using namespace boost::math::tools;
235
static const char* function = "boost::math::ellint_pi<%1%>(%1%,%1%)";
239
return policies::raise_domain_error<T>(function,
240
"Got k = %1%, function requires |k| <= 1", k, pol);
244
// Result is complex:
245
return policies::raise_domain_error<T>(function,
246
"Got v = %1%, function requires v < 1", v, pol);
251
return (k == 0) ? boost::math::constants::pi<T>() / 2 : ellint_k_imp(k, pol);
257
T N = (k2 - v) / (1 - v);
258
T Nm1 = (1 - k2) / (1 - v);
259
T p2 = sqrt(-v * (k2 - v) / (1 - v));
261
T result = boost::math::detail::ellint_pi_imp(N, k, Nm1, pol);
263
result *= sqrt(Nm1 * (1 - k2 / N));
264
result += ellint_k_imp(k, pol) * k2 / p2;
265
result /= sqrt((1 - v) * (1 - k2 / v));
273
T value = ellint_rf_imp(x, y, z, pol) + v * ellint_rj_imp(x, y, z, p, pol) / 3;
278
template <class T1, class T2, class T3>
279
inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const mpl::false_&)
281
return boost::math::ellint_3(k, v, phi, policies::policy<>());
284
template <class T1, class T2, class Policy>
285
inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const mpl::true_&)
287
typedef typename tools::promote_args<T1, T2>::type result_type;
288
typedef typename policies::evaluation<result_type, Policy>::type value_type;
289
return policies::checked_narrowing_cast<result_type, Policy>(
290
detail::ellint_pi_imp(
291
static_cast<value_type>(v),
292
static_cast<value_type>(k),
293
static_cast<value_type>(1-v),
294
pol), "boost::math::ellint_3<%1%>(%1%,%1%)");
297
} // namespace detail
299
template <class T1, class T2, class T3, class Policy>
300
inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const Policy& pol)
302
typedef typename tools::promote_args<T1, T2, T3>::type result_type;
303
typedef typename policies::evaluation<result_type, Policy>::type value_type;
304
return policies::checked_narrowing_cast<result_type, Policy>(
305
detail::ellint_pi_imp(
306
static_cast<value_type>(v),
307
static_cast<value_type>(phi),
308
static_cast<value_type>(k),
309
static_cast<value_type>(1-v),
310
pol), "boost::math::ellint_3<%1%>(%1%,%1%,%1%)");
313
template <class T1, class T2, class T3>
314
typename detail::ellint_3_result<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi)
316
typedef typename policies::is_policy<T3>::type tag_type;
317
return detail::ellint_3(k, v, phi, tag_type());
320
template <class T1, class T2>
321
inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v)
323
return ellint_3(k, v, policies::policy<>());
328
#endif // BOOST_MATH_ELLINT_3_HPP