annotate DEPENDENCIES/generic/include/boost/math/special_functions/ellint_3.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
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