]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/math/special_functions/ellint_3.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / math / special_functions / ellint_3.hpp
CommitLineData
7c673cae
FG
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)
6//
7// History:
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.
12//
13
14#ifndef BOOST_MATH_ELLINT_3_HPP
15#define BOOST_MATH_ELLINT_3_HPP
16
17#ifdef _MSC_VER
18#pragma once
19#endif
20
21#include <boost/math/special_functions/math_fwd.hpp>
22#include <boost/math/special_functions/ellint_rf.hpp>
23#include <boost/math/special_functions/ellint_rj.hpp>
24#include <boost/math/special_functions/ellint_1.hpp>
25#include <boost/math/special_functions/ellint_2.hpp>
26#include <boost/math/special_functions/log1p.hpp>
27#include <boost/math/special_functions/atanh.hpp>
28#include <boost/math/constants/constants.hpp>
29#include <boost/math/policies/error_handling.hpp>
30#include <boost/math/tools/workaround.hpp>
31#include <boost/math/special_functions/round.hpp>
32
33// Elliptic integrals (complete and incomplete) of the third kind
34// Carlson, Numerische Mathematik, vol 33, 1 (1979)
35
36namespace boost { namespace math {
37
38namespace detail{
39
40template <typename T, typename Policy>
41T ellint_pi_imp(T v, T k, T vc, const Policy& pol);
42
43// Elliptic integral (Legendre form) of the third kind
44template <typename T, typename Policy>
45T ellint_pi_imp(T v, T phi, T k, T vc, const Policy& pol)
46{
47 // Note vc = 1-v presumably without cancellation error.
48 BOOST_MATH_STD_USING
49
50 static const char* function = "boost::math::ellint_3<%1%>(%1%,%1%,%1%)";
51
7c673cae
FG
52
53 T sphi = sin(fabs(phi));
54 T result = 0;
55
92f5a8d4
TL
56 if (k * k * sphi * sphi > 1)
57 {
58 return policies::raise_domain_error<T>(function,
59 "Got k = %1%, function requires |k| <= 1", k, pol);
60 }
7c673cae
FG
61 // Special cases first:
62 if(v == 0)
63 {
64 // A&S 17.7.18 & 19
65 return (k == 0) ? phi : ellint_f_imp(phi, k, pol);
66 }
67 if((v > 0) && (1 / v < (sphi * sphi)))
68 {
69 // Complex result is a domain error:
70 return policies::raise_domain_error<T>(function,
71 "Got v = %1%, but result is complex for v > 1 / sin^2(phi)", v, pol);
72 }
73
74 if(v == 1)
75 {
92f5a8d4
TL
76 if (k == 0)
77 return tan(phi);
78
7c673cae
FG
79 // http://functions.wolfram.com/08.06.03.0008.01
80 T m = k * k;
81 result = sqrt(1 - m * sphi * sphi) * tan(phi) - ellint_e_imp(phi, k, pol);
82 result /= 1 - m;
83 result += ellint_f_imp(phi, k, pol);
84 return result;
85 }
86 if(phi == constants::half_pi<T>())
87 {
88 // Have to filter this case out before the next
89 // special case, otherwise we might get an infinity from
90 // tan(phi).
91 // Also note that since we can't represent PI/2 exactly
92 // in a T, this is a bit of a guess as to the users true
93 // intent...
94 //
95 return ellint_pi_imp(v, k, vc, pol);
96 }
97 if((phi > constants::half_pi<T>()) || (phi < 0))
98 {
99 // Carlson's algorithm works only for |phi| <= pi/2,
100 // use the integrand's periodicity to normalize phi
101 //
102 // Xiaogang's original code used a cast to long long here
103 // but that fails if T has more digits than a long long,
104 // so rewritten to use fmod instead:
105 //
106 // See http://functions.wolfram.com/08.06.16.0002.01
107 //
108 if(fabs(phi) > 1 / tools::epsilon<T>())
109 {
110 if(v > 1)
111 return policies::raise_domain_error<T>(
112 function,
113 "Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
114 //
115 // Phi is so large that phi%pi is necessarily zero (or garbage),
116 // just return the second part of the duplication formula:
117 //
118 result = 2 * fabs(phi) * ellint_pi_imp(v, k, vc, pol) / constants::pi<T>();
119 }
120 else
121 {
122 T rphi = boost::math::tools::fmod_workaround(T(fabs(phi)), T(constants::half_pi<T>()));
123 T m = boost::math::round((fabs(phi) - rphi) / constants::half_pi<T>());
124 int sign = 1;
125 if((m != 0) && (k >= 1))
126 {
127 return policies::raise_domain_error<T>(function, "Got k=1 and phi=%1% but the result is complex in that domain", phi, pol);
128 }
129 if(boost::math::tools::fmod_workaround(m, T(2)) > 0.5)
130 {
131 m += 1;
132 sign = -1;
133 rphi = constants::half_pi<T>() - rphi;
134 }
135 result = sign * ellint_pi_imp(v, rphi, k, vc, pol);
136 if((m > 0) && (vc > 0))
137 result += m * ellint_pi_imp(v, k, vc, pol);
138 }
139 return phi < 0 ? T(-result) : result;
140 }
141 if(k == 0)
142 {
143 // A&S 17.7.20:
144 if(v < 1)
145 {
146 T vcr = sqrt(vc);
147 return atan(vcr * tan(phi)) / vcr;
148 }
7c673cae
FG
149 else
150 {
151 // v > 1:
152 T vcr = sqrt(-vc);
153 T arg = vcr * tan(phi);
154 return (boost::math::log1p(arg, pol) - boost::math::log1p(-arg, pol)) / (2 * vcr);
155 }
156 }
92f5a8d4 157 if((v < 0) && fabs(k) <= 1)
7c673cae
FG
158 {
159 //
160 // If we don't shift to 0 <= v <= 1 we get
161 // cancellation errors later on. Use
162 // A&S 17.7.15/16 to shift to v > 0.
163 //
164 // Mathematica simplifies the expressions
165 // given in A&S as follows (with thanks to
166 // Rocco Romeo for figuring these out!):
167 //
168 // V = (k2 - n)/(1 - n)
169 // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[Sqrt[(1 - V)*(1 - k2 / V)] / Sqrt[((1 - n)*(1 - k2 / n))]]]
170 // Result: ((-1 + k2) n) / ((-1 + n) (-k2 + n))
171 //
172 // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[k2 / (Sqrt[-n*(k2 - n) / (1 - n)] * Sqrt[(1 - n)*(1 - k2 / n)])]]
173 // Result : k2 / (k2 - n)
174 //
175 // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[Sqrt[1 / ((1 - n)*(1 - k2 / n))]]]
176 // Result : Sqrt[n / ((k2 - n) (-1 + n))]
177 //
178 T k2 = k * k;
179 T N = (k2 - v) / (1 - v);
180 T Nm1 = (1 - k2) / (1 - v);
181 T p2 = -v * N;
182 T t;
183 if(p2 <= tools::min_value<T>())
184 p2 = sqrt(-v) * sqrt(N);
185 else
186 p2 = sqrt(p2);
187 T delta = sqrt(1 - k2 * sphi * sphi);
188 if(N > k2)
189 {
190 result = ellint_pi_imp(N, phi, k, Nm1, pol);
191 result *= v / (v - 1);
192 result *= (k2 - 1) / (v - k2);
193 }
194
195 if(k != 0)
196 {
197 t = ellint_f_imp(phi, k, pol);
198 t *= k2 / (k2 - v);
199 result += t;
200 }
201 t = v / ((k2 - v) * (v - 1));
202 if(t > tools::min_value<T>())
203 {
204 result += atan((p2 / 2) * sin(2 * phi) / delta) * sqrt(t);
205 }
206 else
207 {
208 result += atan((p2 / 2) * sin(2 * phi) / delta) * sqrt(fabs(1 / (k2 - v))) * sqrt(fabs(v / (v - 1)));
209 }
210 return result;
211 }
212 if(k == 1)
213 {
214 // See http://functions.wolfram.com/08.06.03.0013.01
215 result = sqrt(v) * atanh(sqrt(v) * sin(phi)) - log(1 / cos(phi) + tan(phi));
216 result /= v - 1;
217 return result;
218 }
219#if 0 // disabled but retained for future reference: see below.
220 if(v > 1)
221 {
222 //
223 // If v > 1 we can use the identity in A&S 17.7.7/8
224 // to shift to 0 <= v <= 1. In contrast to previous
225 // revisions of this header, this identity does now work
226 // but appears not to produce better error rates in
227 // practice. Archived here for future reference...
228 //
229 T k2 = k * k;
230 T N = k2 / v;
231 T Nm1 = (v - k2) / v;
232 T p1 = sqrt((-vc) * (1 - k2 / v));
233 T delta = sqrt(1 - k2 * sphi * sphi);
234 //
235 // These next two terms have a large amount of cancellation
236 // so it's not clear if this relation is useable even if
237 // the issues with phi > pi/2 can be fixed:
238 //
239 result = -ellint_pi_imp(N, phi, k, Nm1, pol);
240 result += ellint_f_imp(phi, k, pol);
241 //
242 // This log term gives the complex result when
243 // n > 1/sin^2(phi)
244 // However that case is dealt with as an error above,
245 // so we should always get a real result here:
246 //
247 result += log((delta + p1 * tan(phi)) / (delta - p1 * tan(phi))) / (2 * p1);
248 return result;
249 }
250#endif
251 //
252 // Carlson's algorithm works only for |phi| <= pi/2,
253 // by the time we get here phi should already have been
254 // normalised above.
255 //
256 BOOST_ASSERT(fabs(phi) < constants::half_pi<T>());
257 BOOST_ASSERT(phi >= 0);
258 T x, y, z, p, t;
259 T cosp = cos(phi);
260 x = cosp * cosp;
261 t = sphi * sphi;
262 y = 1 - k * k * t;
263 z = 1;
264 if(v * t < 0.5)
265 p = 1 - v * t;
266 else
267 p = x + vc * t;
268 result = sphi * (ellint_rf_imp(x, y, z, pol) + v * t * ellint_rj_imp(x, y, z, p, pol) / 3);
269
270 return result;
271}
272
273// Complete elliptic integral (Legendre form) of the third kind
274template <typename T, typename Policy>
275T ellint_pi_imp(T v, T k, T vc, const Policy& pol)
276{
277 // Note arg vc = 1-v, possibly without cancellation errors
278 BOOST_MATH_STD_USING
279 using namespace boost::math::tools;
280
281 static const char* function = "boost::math::ellint_pi<%1%>(%1%,%1%)";
282
283 if (abs(k) >= 1)
284 {
285 return policies::raise_domain_error<T>(function,
286 "Got k = %1%, function requires |k| <= 1", k, pol);
287 }
288 if(vc <= 0)
289 {
290 // Result is complex:
291 return policies::raise_domain_error<T>(function,
292 "Got v = %1%, function requires v < 1", v, pol);
293 }
294
295 if(v == 0)
296 {
297 return (k == 0) ? boost::math::constants::pi<T>() / 2 : ellint_k_imp(k, pol);
298 }
299
300 if(v < 0)
301 {
302 // Apply A&S 17.7.17:
303 T k2 = k * k;
304 T N = (k2 - v) / (1 - v);
305 T Nm1 = (1 - k2) / (1 - v);
306 T result = 0;
307 result = boost::math::detail::ellint_pi_imp(N, k, Nm1, pol);
308 // This next part is split in two to avoid spurious over/underflow:
309 result *= -v / (1 - v);
310 result *= (1 - k2) / (k2 - v);
311 result += ellint_k_imp(k, pol) * k2 / (k2 - v);
312 return result;
313 }
314
315 T x = 0;
316 T y = 1 - k * k;
317 T z = 1;
318 T p = vc;
319 T value = ellint_rf_imp(x, y, z, pol) + v * ellint_rj_imp(x, y, z, p, pol) / 3;
320
321 return value;
322}
323
324template <class T1, class T2, class T3>
325inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const mpl::false_&)
326{
327 return boost::math::ellint_3(k, v, phi, policies::policy<>());
328}
329
330template <class T1, class T2, class Policy>
331inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const mpl::true_&)
332{
333 typedef typename tools::promote_args<T1, T2>::type result_type;
334 typedef typename policies::evaluation<result_type, Policy>::type value_type;
335 return policies::checked_narrowing_cast<result_type, Policy>(
336 detail::ellint_pi_imp(
337 static_cast<value_type>(v),
338 static_cast<value_type>(k),
339 static_cast<value_type>(1-v),
340 pol), "boost::math::ellint_3<%1%>(%1%,%1%)");
341}
342
343} // namespace detail
344
345template <class T1, class T2, class T3, class Policy>
346inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const Policy& pol)
347{
348 typedef typename tools::promote_args<T1, T2, T3>::type result_type;
349 typedef typename policies::evaluation<result_type, Policy>::type value_type;
350 return policies::checked_narrowing_cast<result_type, Policy>(
351 detail::ellint_pi_imp(
352 static_cast<value_type>(v),
353 static_cast<value_type>(phi),
354 static_cast<value_type>(k),
355 static_cast<value_type>(1-v),
356 pol), "boost::math::ellint_3<%1%>(%1%,%1%,%1%)");
357}
358
359template <class T1, class T2, class T3>
360typename detail::ellint_3_result<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi)
361{
362 typedef typename policies::is_policy<T3>::type tag_type;
363 return detail::ellint_3(k, v, phi, tag_type());
364}
365
366template <class T1, class T2>
367inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v)
368{
369 return ellint_3(k, v, policies::policy<>());
370}
371
372}} // namespaces
373
374#endif // BOOST_MATH_ELLINT_3_HPP
375