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