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/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>
29 
30 // Elliptic integrals (complete and incomplete) of the third kind
31 // Carlson, Numerische Mathematik, vol 33, 1 (1979)
32 
33 namespace boost { namespace math {
34 
35 namespace detail{
36 
37 template <typename T, typename Policy>
38 T ellint_pi_imp(T v, T k, T vc, const Policy& pol);
39 
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)
43 {
44     // Note vc = 1-v presumably without cancellation error.
45     T value, x, y, z, p, t;
46 
47     BOOST_MATH_STD_USING
48     using namespace boost::math::tools;
49     using namespace boost::math::constants;
50 
51     static const char* function = "boost::math::ellint_3<%1%>(%1%,%1%,%1%)";
52 
53     if (abs(k) > 1)
54     {
55        return policies::raise_domain_error<T>(function,
56             "Got k = %1%, function requires |k| <= 1", k, pol);
57     }
58 
59     T sphi = sin(fabs(phi));
60 
61     if(v > 1 / (sphi * sphi))
62     {
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);
66     }
67 
68     // Special cases first:
69     if(v == 0)
70     {
71        // A&S 17.7.18 & 19
72        return (k == 0) ? phi : ellint_f_imp(phi, k, pol);
73     }
74     if(phi == constants::pi<T>() / 2)
75     {
76        // Have to filter this case out before the next
77        // special case, otherwise we might get an infinity from
78        // tan(phi).
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
81        // intent...
82        //
83        return ellint_pi_imp(v, k, vc, pol);
84     }
85     if(k == 0)
86     {
87        // A&S 17.7.20:
88        if(v < 1)
89        {
90           T vcr = sqrt(vc);
91           return atan(vcr * tan(phi)) / vcr;
92        }
93        else if(v == 1)
94        {
95           return tan(phi);
96        }
97        else
98        {
99           // v > 1:
100           T vcr = sqrt(-vc);
101           T arg = vcr * tan(phi);
102           return (boost::math::log1p(arg, pol) - boost::math::log1p(-arg, pol)) / (2 * vcr);
103        }
104     }
105 
106     if(v < 0)
107     {
108        //
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:
112        //
113        T k2 = k * k;
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);
119 
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));
124        return result;
125     }
126 #if 0  // disabled but retained for future reference: see below.
127     if(v > 1)
128     {
129        //
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.
137        //
138        // So we're stuck... the code is archived here in case
139        // some bright spart can figure out the fix.
140        //
141        T k2 = k * k;
142        T N = k2 / v;
143        T Nm1 = (v - k2) / v;
144        T p1 = sqrt((-vc) * (1 - k2 / v));
145        T delta = sqrt(1 - k2 * sphi * sphi);
146        //
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:
150        //
151        T result = -ellint_pi_imp(N, phi, k, Nm1);
152        result += ellint_f_imp(phi, k);
153        //
154        // This log term gives the complex result when
155        //     n > 1/sin^2(phi)
156        // However that case is dealt with as an error above,
157        // so we should always get a real result here:
158        //
159        result += log((delta + p1 * tan(phi)) / (delta - p1 * tan(phi))) / (2 * p1);
160        return result;
161     }
162 #endif
163 
164     // Carlson's algorithm works only for |phi| <= pi/2,
165     // use the integrand's periodicity to normalize phi
166     //
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:
170     //
171     if(fabs(phi) > 1 / tools::epsilon<T>())
172     {
173        if(v > 1)
174           return policies::raise_domain_error<T>(
175             function,
176             "Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
177        //
178        // Phi is so large that phi%pi is necessarily zero (or garbage),
179        // just return the second part of the duplication formula:
180        //
181        value = 2 * fabs(phi) * ellint_pi_imp(v, k, vc, pol) / constants::pi<T>();
182     }
183     else
184     {
185        T rphi = boost::math::tools::fmod_workaround(fabs(phi), T(constants::pi<T>() / 2));
186        T m = floor((2 * fabs(phi)) / constants::pi<T>());
187        int sign = 1;
188        if(boost::math::tools::fmod_workaround(m, T(2)) > 0.5)
189        {
190           m += 1;
191           sign = -1;
192           rphi = constants::pi<T>() / 2 - rphi;
193        }
194 #if 0
195        //
196        // This wasn't supported but is now... probably!
197        //
198        if((m > 0) && (v > 1))
199        {
200           //
201           // The region with v > 1 and phi outside [0, pi/2] is
202           // currently unsupported:
203           //
204           return policies::raise_domain_error<T>(
205             function,
206             "Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
207        }
208 #endif
209        T sinp = sin(rphi);
210        T cosp = cos(rphi);
211        x = cosp * cosp;
212        t = sinp * sinp;
213        y = 1 - k * k * t;
214        z = 1;
215        if(v * t < 0.5)
216            p = 1 - v * t;
217        else
218            p = x + vc * t;
219        value = sign * sinp * (ellint_rf_imp(x, y, z, pol) + v * t * ellint_rj_imp(x, y, z, p, pol) / 3);
220        if((m > 0) && (vc > 0))
221          value += m * ellint_pi_imp(v, k, vc, pol);
222     }
223 
224     if (phi < 0)
225     {
226         value = -value;    // odd function
227     }
228     return value;
229 }
230 
231 // Complete elliptic integral (Legendre form) of the third kind
232 template <typename T, typename Policy>
233 T ellint_pi_imp(T v, T k, T vc, const Policy& pol)
234 {
235     // Note arg vc = 1-v, possibly without cancellation errors
236     BOOST_MATH_STD_USING
237     using namespace boost::math::tools;
238 
239     static const char* function = "boost::math::ellint_pi<%1%>(%1%,%1%)";
240 
241     if (abs(k) >= 1)
242     {
243        return policies::raise_domain_error<T>(function,
244             "Got k = %1%, function requires |k| <= 1", k, pol);
245     }
246     if(vc <= 0)
247     {
248        // Result is complex:
249        return policies::raise_domain_error<T>(function,
250             "Got v = %1%, function requires v < 1", v, pol);
251     }
252 
253     if(v == 0)
254     {
255        return (k == 0) ? boost::math::constants::pi<T>() / 2 : ellint_k_imp(k, pol);
256     }
257 
258     if(v < 0)
259     {
260        T k2 = k * k;
261        T N = (k2 - v) / (1 - v);
262        T Nm1 = (1 - k2) / (1 - v);
263        T p2 = sqrt(-v * (k2 - v) / (1 - v));
264 
265        T result = boost::math::detail::ellint_pi_imp(N, k, Nm1, pol);
266 
267        result *= sqrt(Nm1 * (1 - k2 / N));
268        result += ellint_k_imp(k, pol) * k2 / p2;
269        result /= sqrt((1 - v) * (1 - k2 / v));
270        return result;
271     }
272 
273     T x = 0;
274     T y = 1 - k * k;
275     T z = 1;
276     T p = vc;
277     T value = ellint_rf_imp(x, y, z, pol) + v * ellint_rj_imp(x, y, z, p, pol) / 3;
278 
279     return value;
280 }
281 
282 template <class T1, class T2, class T3>
ellint_3(T1 k,T2 v,T3 phi,const mpl::false_ &)283 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const mpl::false_&)
284 {
285    return boost::math::ellint_3(k, v, phi, policies::policy<>());
286 }
287 
288 template <class T1, class T2, class Policy>
ellint_3(T1 k,T2 v,const Policy & pol,const mpl::true_ &)289 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const mpl::true_&)
290 {
291    typedef typename tools::promote_args<T1, T2>::type result_type;
292    typedef typename policies::evaluation<result_type, Policy>::type value_type;
293    return policies::checked_narrowing_cast<result_type, Policy>(
294       detail::ellint_pi_imp(
295          static_cast<value_type>(v),
296          static_cast<value_type>(k),
297          static_cast<value_type>(1-v),
298          pol), "boost::math::ellint_3<%1%>(%1%,%1%)");
299 }
300 
301 } // namespace detail
302 
303 template <class T1, class T2, class T3, class Policy>
ellint_3(T1 k,T2 v,T3 phi,const Policy & pol)304 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const Policy& pol)
305 {
306    typedef typename tools::promote_args<T1, T2, T3>::type result_type;
307    typedef typename policies::evaluation<result_type, Policy>::type value_type;
308    return policies::checked_narrowing_cast<result_type, Policy>(
309       detail::ellint_pi_imp(
310          static_cast<value_type>(v),
311          static_cast<value_type>(phi),
312          static_cast<value_type>(k),
313          static_cast<value_type>(1-v),
314          pol), "boost::math::ellint_3<%1%>(%1%,%1%,%1%)");
315 }
316 
317 template <class T1, class T2, class T3>
ellint_3(T1 k,T2 v,T3 phi)318 typename detail::ellint_3_result<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi)
319 {
320    typedef typename policies::is_policy<T3>::type tag_type;
321    return detail::ellint_3(k, v, phi, tag_type());
322 }
323 
324 template <class T1, class T2>
ellint_3(T1 k,T2 v)325 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v)
326 {
327    return ellint_3(k, v, policies::policy<>());
328 }
329 
330 }} // namespaces
331 
332 #endif // BOOST_MATH_ELLINT_3_HPP
333 
334