ellint_3.hpp 9.9 KB

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