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 
53    T sphi = sin(fabs(phi));
54    T result = 0;
55 
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    }
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       if (k == 0)
77          return tan(phi);
78 
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       }
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    }
157    if((v < 0) && fabs(k) <= 1)
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), pol) - 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_MATH_ASSERT(fabs(phi) < constants::half_pi<T>());
257    BOOST_MATH_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
274 template <typename T, typename Policy>
275 T 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 
324 template <class T1, class T2, class T3>
ellint_3(T1 k,T2 v,T3 phi,const std::false_type &)325 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const std::false_type&)
326 {
327    return boost::math::ellint_3(k, v, phi, policies::policy<>());
328 }
329 
330 template <class T1, class T2, class Policy>
ellint_3(T1 k,T2 v,const Policy & pol,const std::true_type &)331 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const std::true_type&)
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 
345 template <class T1, class T2, class T3, class Policy>
ellint_3(T1 k,T2 v,T3 phi,const Policy & pol)346 inline 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 
359 template <class T1, class T2, class T3>
ellint_3(T1 k,T2 v,T3 phi)360 typename 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 
366 template <class T1, class T2>
ellint_3(T1 k,T2 v)367 inline 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 
376