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