ellint_3.hpp 12 KB

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