]> git.donarmstrong.com Git - rsem.git/blob - boost/math/special_functions/ellint_3.hpp
Updated boost to v1.55.0
[rsem.git] / boost / math / special_functions / ellint_3.hpp
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 #include <boost/math/special_functions/round.hpp>
30
31 // Elliptic integrals (complete and incomplete) of the third kind
32 // Carlson, Numerische Mathematik, vol 33, 1 (1979)
33
34 namespace boost { namespace math { 
35    
36 namespace detail{
37
38 template <typename T, typename Policy>
39 T ellint_pi_imp(T v, T k, T vc, const Policy& pol);
40
41 // Elliptic integral (Legendre form) of the third kind
42 template <typename T, typename Policy>
43 T ellint_pi_imp(T v, T phi, T k, T vc, const Policy& pol)
44 {
45     // Note vc = 1-v presumably without cancellation error.
46     T value, x, y, z, p, t;
47
48     BOOST_MATH_STD_USING
49     using namespace boost::math::tools;
50     using namespace boost::math::constants;
51
52     static const char* function = "boost::math::ellint_3<%1%>(%1%,%1%,%1%)";
53
54     if (abs(k) > 1)
55     {
56        return policies::raise_domain_error<T>(function,
57             "Got k = %1%, function requires |k| <= 1", k, pol);
58     }
59
60     T sphi = sin(fabs(phi));
61
62     if(v > 1 / (sphi * sphi))
63     {
64         // Complex result is a domain error:
65        return policies::raise_domain_error<T>(function,
66             "Got v = %1%, but result is complex for v > 1 / sin^2(phi)", v, pol);
67     }
68
69     // Special cases first:
70     if(v == 0)
71     {
72        // A&S 17.7.18 & 19
73        return (k == 0) ? phi : ellint_f_imp(phi, k, pol);
74     }
75     if(phi == constants::pi<T>() / 2)
76     {
77        // Have to filter this case out before the next
78        // special case, otherwise we might get an infinity from
79        // tan(phi).
80        // Also note that since we can't represent PI/2 exactly
81        // in a T, this is a bit of a guess as to the users true
82        // intent...
83        //
84        return ellint_pi_imp(v, k, vc, pol);
85     }
86     if(k == 0)
87     {
88        // A&S 17.7.20:
89        if(v < 1)
90        {
91           T vcr = sqrt(vc);
92           return atan(vcr * tan(phi)) / vcr;
93        }
94        else if(v == 1)
95        {
96           return tan(phi);
97        }
98        else
99        {
100           // v > 1:
101           T vcr = sqrt(-vc);
102           T arg = vcr * tan(phi);
103           return (boost::math::log1p(arg, pol) - boost::math::log1p(-arg, pol)) / (2 * vcr);
104        }
105     }
106
107     if(v < 0)
108     {
109        //
110        // If we don't shift to 0 <= v <= 1 we get
111        // cancellation errors later on.  Use
112        // A&S 17.7.15/16 to shift to v > 0:
113        //
114        T k2 = k * k;
115        T N = (k2 - v) / (1 - v);
116        T Nm1 = (1 - k2) / (1 - v);
117        T p2 = sqrt(-v * (k2 - v) / (1 - v));
118        T delta = sqrt(1 - k2 * sphi * sphi);
119        T result = ellint_pi_imp(N, phi, k, Nm1, pol);
120
121        result *= sqrt(Nm1 * (1 - k2 / N));
122        result += ellint_f_imp(phi, k, pol) * k2 / p2;
123        result += atan((p2/2) * sin(2 * phi) / delta);
124        result /= sqrt((1 - v) * (1 - k2 / v));
125        return result;
126     }
127 #if 0  // disabled but retained for future reference: see below.
128     if(v > 1)
129     {
130        //
131        // If v > 1 we can use the identity in A&S 17.7.7/8
132        // to shift to 0 <= v <= 1.  Unfortunately this
133        // identity appears only to function correctly when
134        // 0 <= phi <= pi/2, but it's when phi is outside that
135        // range that we really need it: That's when
136        // Carlson's formula fails, and what's more the periodicity
137        // reduction used below on phi doesn't work when v > 1.
138        //
139        // So we're stuck... the code is archived here in case
140        // some bright spart can figure out the fix.
141        //
142        T k2 = k * k;
143        T N = k2 / v;
144        T Nm1 = (v - k2) / v;
145        T p1 = sqrt((-vc) * (1 - k2 / v));
146        T delta = sqrt(1 - k2 * sphi * sphi);
147        //
148        // These next two terms have a large amount of cancellation
149        // so it's not clear if this relation is useable even if
150        // the issues with phi > pi/2 can be fixed:
151        //
152        T result = -ellint_pi_imp(N, phi, k, Nm1);
153        result += ellint_f_imp(phi, k);
154        //
155        // This log term gives the complex result when
156        //     n > 1/sin^2(phi)
157        // However that case is dealt with as an error above, 
158        // so we should always get a real result here:
159        //
160        result += log((delta + p1 * tan(phi)) / (delta - p1 * tan(phi))) / (2 * p1);
161        return result;
162     }
163 #endif
164
165     // Carlson's algorithm works only for |phi| <= pi/2,
166     // use the integrand's periodicity to normalize phi
167     //
168     // Xiaogang's original code used a cast to long long here
169     // but that fails if T has more digits than a long long,
170     // so rewritten to use fmod instead:
171     //
172     if(fabs(phi) > 1 / tools::epsilon<T>())
173     {
174        if(v > 1)
175           return policies::raise_domain_error<T>(
176             function,
177             "Got v = %1%, but this is only supported for 0 <= phi <= pi/2", v, pol);
178        //  
179        // Phi is so large that phi%pi is necessarily zero (or garbage),
180        // just return the second part of the duplication formula:
181        //
182        value = 2 * fabs(phi) * ellint_pi_imp(v, k, vc, pol) / constants::pi<T>();
183     }
184     else
185     {
186        T rphi = boost::math::tools::fmod_workaround(T(fabs(phi)), T(constants::half_pi<T>()));
187        T m = boost::math::round((fabs(phi) - rphi) / constants::half_pi<T>());
188        int sign = 1;
189        if(boost::math::tools::fmod_workaround(m, T(2)) > 0.5)
190        {
191           m += 1;
192           sign = -1;
193           rphi = constants::half_pi<T>() - rphi;
194        }
195        T sinp = sin(rphi);
196        T cosp = cos(rphi);
197        x = cosp * cosp;
198        t = sinp * sinp;
199        y = 1 - k * k * t;
200        z = 1;
201        if(v * t < 0.5)
202            p = 1 - v * t;
203        else
204            p = x + vc * t;
205        value = sign * sinp * (ellint_rf_imp(x, y, z, pol) + v * t * ellint_rj_imp(x, y, z, p, pol) / 3);
206        if((m > 0) && (vc > 0))
207          value += m * ellint_pi_imp(v, k, vc, pol);
208     }
209
210     if (phi < 0)
211     {
212         value = -value;    // odd function
213     }
214     return value;
215 }
216
217 // Complete elliptic integral (Legendre form) of the third kind
218 template <typename T, typename Policy>
219 T ellint_pi_imp(T v, T k, T vc, const Policy& pol)
220 {
221     // Note arg vc = 1-v, possibly without cancellation errors
222     BOOST_MATH_STD_USING
223     using namespace boost::math::tools;
224
225     static const char* function = "boost::math::ellint_pi<%1%>(%1%,%1%)";
226
227     if (abs(k) >= 1)
228     {
229        return policies::raise_domain_error<T>(function,
230             "Got k = %1%, function requires |k| <= 1", k, pol);
231     }
232     if(vc <= 0)
233     {
234        // Result is complex:
235        return policies::raise_domain_error<T>(function,
236             "Got v = %1%, function requires v < 1", v, pol);
237     }
238
239     if(v == 0)
240     {
241        return (k == 0) ? boost::math::constants::pi<T>() / 2 : ellint_k_imp(k, pol);
242     }
243
244     if(v < 0)
245     {
246        T k2 = k * k;
247        T N = (k2 - v) / (1 - v);
248        T Nm1 = (1 - k2) / (1 - v);
249        T p2 = sqrt(-v * (k2 - v) / (1 - v));
250
251        T result = boost::math::detail::ellint_pi_imp(N, k, Nm1, pol);
252
253        result *= sqrt(Nm1 * (1 - k2 / N));
254        result += ellint_k_imp(k, pol) * k2 / p2;
255        result /= sqrt((1 - v) * (1 - k2 / v));
256        return result;
257     }
258
259     T x = 0;
260     T y = 1 - k * k;
261     T z = 1;
262     T p = vc;
263     T value = ellint_rf_imp(x, y, z, pol) + v * ellint_rj_imp(x, y, z, p, pol) / 3;
264
265     return value;
266 }
267
268 template <class T1, class T2, class T3>
269 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const mpl::false_&)
270 {
271    return boost::math::ellint_3(k, v, phi, policies::policy<>());
272 }
273
274 template <class T1, class T2, class Policy>
275 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v, const Policy& pol, const mpl::true_&)
276 {
277    typedef typename tools::promote_args<T1, T2>::type result_type;
278    typedef typename policies::evaluation<result_type, Policy>::type value_type;
279    return policies::checked_narrowing_cast<result_type, Policy>(
280       detail::ellint_pi_imp(
281          static_cast<value_type>(v), 
282          static_cast<value_type>(k),
283          static_cast<value_type>(1-v),
284          pol), "boost::math::ellint_3<%1%>(%1%,%1%)");
285 }
286
287 } // namespace detail
288
289 template <class T1, class T2, class T3, class Policy>
290 inline typename tools::promote_args<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi, const Policy& pol)
291 {
292    typedef typename tools::promote_args<T1, T2, T3>::type result_type;
293    typedef typename policies::evaluation<result_type, Policy>::type value_type;
294    return policies::checked_narrowing_cast<result_type, Policy>(
295       detail::ellint_pi_imp(
296          static_cast<value_type>(v), 
297          static_cast<value_type>(phi), 
298          static_cast<value_type>(k),
299          static_cast<value_type>(1-v),
300          pol), "boost::math::ellint_3<%1%>(%1%,%1%,%1%)");
301 }
302
303 template <class T1, class T2, class T3>
304 typename detail::ellint_3_result<T1, T2, T3>::type ellint_3(T1 k, T2 v, T3 phi)
305 {
306    typedef typename policies::is_policy<T3>::type tag_type;
307    return detail::ellint_3(k, v, phi, tag_type());
308 }
309
310 template <class T1, class T2>
311 inline typename tools::promote_args<T1, T2>::type ellint_3(T1 k, T2 v)
312 {
313    return ellint_3(k, v, policies::policy<>());
314 }
315
316 }} // namespaces
317
318 #endif // BOOST_MATH_ELLINT_3_HPP
319