legendre_function.tcc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Special functions -*- C++ -*-
  2. // Copyright (C) 2006-2018 Free Software Foundation, Inc.
  3. //
  4. // This file is part of the GNU ISO C++ Library. This library is free
  5. // software; you can redistribute it and/or modify it under the
  6. // terms of the GNU General Public License as published by the
  7. // Free Software Foundation; either version 3, or (at your option)
  8. // any later version.
  9. //
  10. // This library is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // Under Section 7 of GPL version 3, you are granted additional
  16. // permissions described in the GCC Runtime Library Exception, version
  17. // 3.1, as published by the Free Software Foundation.
  18. // You should have received a copy of the GNU General Public License and
  19. // a copy of the GCC Runtime Library Exception along with this program;
  20. // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  21. // <http://www.gnu.org/licenses/>.
  22. /** @file tr1/legendre_function.tcc
  23. * This is an internal header file, included by other library headers.
  24. * Do not attempt to use it directly. @headername{tr1/cmath}
  25. */
  26. //
  27. // ISO C++ 14882 TR1: 5.2 Special functions
  28. //
  29. // Written by Edward Smith-Rowland based on:
  30. // (1) Handbook of Mathematical Functions,
  31. // ed. Milton Abramowitz and Irene A. Stegun,
  32. // Dover Publications,
  33. // Section 8, pp. 331-341
  34. // (2) The Gnu Scientific Library, http://www.gnu.org/software/gsl
  35. // (3) Numerical Recipes in C, by W. H. Press, S. A. Teukolsky,
  36. // W. T. Vetterling, B. P. Flannery, Cambridge University Press (1992),
  37. // 2nd ed, pp. 252-254
  38. #ifndef _GLIBCXX_TR1_LEGENDRE_FUNCTION_TCC
  39. #define _GLIBCXX_TR1_LEGENDRE_FUNCTION_TCC 1
  40. #include "special_function_util.h"
  41. namespace std _GLIBCXX_VISIBILITY(default)
  42. {
  43. _GLIBCXX_BEGIN_NAMESPACE_VERSION
  44. #if _GLIBCXX_USE_STD_SPEC_FUNCS
  45. # define _GLIBCXX_MATH_NS ::std
  46. #elif defined(_GLIBCXX_TR1_CMATH)
  47. namespace tr1
  48. {
  49. # define _GLIBCXX_MATH_NS ::std::tr1
  50. #else
  51. # error do not include this header directly, use <cmath> or <tr1/cmath>
  52. #endif
  53. // [5.2] Special functions
  54. // Implementation-space details.
  55. namespace __detail
  56. {
  57. /**
  58. * @brief Return the Legendre polynomial by recursion on order
  59. * @f$ l @f$.
  60. *
  61. * The Legendre function of @f$ l @f$ and @f$ x @f$,
  62. * @f$ P_l(x) @f$, is defined by:
  63. * @f[
  64. * P_l(x) = \frac{1}{2^l l!}\frac{d^l}{dx^l}(x^2 - 1)^{l}
  65. * @f]
  66. *
  67. * @param l The order of the Legendre polynomial. @f$l >= 0@f$.
  68. * @param x The argument of the Legendre polynomial. @f$|x| <= 1@f$.
  69. */
  70. template<typename _Tp>
  71. _Tp
  72. __poly_legendre_p(unsigned int __l, _Tp __x)
  73. {
  74. if ((__x < _Tp(-1)) || (__x > _Tp(+1)))
  75. std::__throw_domain_error(__N("Argument out of range"
  76. " in __poly_legendre_p."));
  77. else if (__isnan(__x))
  78. return std::numeric_limits<_Tp>::quiet_NaN();
  79. else if (__x == +_Tp(1))
  80. return +_Tp(1);
  81. else if (__x == -_Tp(1))
  82. return (__l % 2 == 1 ? -_Tp(1) : +_Tp(1));
  83. else
  84. {
  85. _Tp __p_lm2 = _Tp(1);
  86. if (__l == 0)
  87. return __p_lm2;
  88. _Tp __p_lm1 = __x;
  89. if (__l == 1)
  90. return __p_lm1;
  91. _Tp __p_l = 0;
  92. for (unsigned int __ll = 2; __ll <= __l; ++__ll)
  93. {
  94. // This arrangement is supposed to be better for roundoff
  95. // protection, Arfken, 2nd Ed, Eq 12.17a.
  96. __p_l = _Tp(2) * __x * __p_lm1 - __p_lm2
  97. - (__x * __p_lm1 - __p_lm2) / _Tp(__ll);
  98. __p_lm2 = __p_lm1;
  99. __p_lm1 = __p_l;
  100. }
  101. return __p_l;
  102. }
  103. }
  104. /**
  105. * @brief Return the associated Legendre function by recursion
  106. * on @f$ l @f$.
  107. *
  108. * The associated Legendre function is derived from the Legendre function
  109. * @f$ P_l(x) @f$ by the Rodrigues formula:
  110. * @f[
  111. * P_l^m(x) = (1 - x^2)^{m/2}\frac{d^m}{dx^m}P_l(x)
  112. * @f]
  113. *
  114. * @param l The order of the associated Legendre function.
  115. * @f$ l >= 0 @f$.
  116. * @param m The order of the associated Legendre function.
  117. * @f$ m <= l @f$.
  118. * @param x The argument of the associated Legendre function.
  119. * @f$ |x| <= 1 @f$.
  120. */
  121. template<typename _Tp>
  122. _Tp
  123. __assoc_legendre_p(unsigned int __l, unsigned int __m, _Tp __x)
  124. {
  125. if (__x < _Tp(-1) || __x > _Tp(+1))
  126. std::__throw_domain_error(__N("Argument out of range"
  127. " in __assoc_legendre_p."));
  128. else if (__m > __l)
  129. std::__throw_domain_error(__N("Degree out of range"
  130. " in __assoc_legendre_p."));
  131. else if (__isnan(__x))
  132. return std::numeric_limits<_Tp>::quiet_NaN();
  133. else if (__m == 0)
  134. return __poly_legendre_p(__l, __x);
  135. else
  136. {
  137. _Tp __p_mm = _Tp(1);
  138. if (__m > 0)
  139. {
  140. // Two square roots seem more accurate more of the time
  141. // than just one.
  142. _Tp __root = std::sqrt(_Tp(1) - __x) * std::sqrt(_Tp(1) + __x);
  143. _Tp __fact = _Tp(1);
  144. for (unsigned int __i = 1; __i <= __m; ++__i)
  145. {
  146. __p_mm *= -__fact * __root;
  147. __fact += _Tp(2);
  148. }
  149. }
  150. if (__l == __m)
  151. return __p_mm;
  152. _Tp __p_mp1m = _Tp(2 * __m + 1) * __x * __p_mm;
  153. if (__l == __m + 1)
  154. return __p_mp1m;
  155. _Tp __p_lm2m = __p_mm;
  156. _Tp __P_lm1m = __p_mp1m;
  157. _Tp __p_lm = _Tp(0);
  158. for (unsigned int __j = __m + 2; __j <= __l; ++__j)
  159. {
  160. __p_lm = (_Tp(2 * __j - 1) * __x * __P_lm1m
  161. - _Tp(__j + __m - 1) * __p_lm2m) / _Tp(__j - __m);
  162. __p_lm2m = __P_lm1m;
  163. __P_lm1m = __p_lm;
  164. }
  165. return __p_lm;
  166. }
  167. }
  168. /**
  169. * @brief Return the spherical associated Legendre function.
  170. *
  171. * The spherical associated Legendre function of @f$ l @f$, @f$ m @f$,
  172. * and @f$ \theta @f$ is defined as @f$ Y_l^m(\theta,0) @f$ where
  173. * @f[
  174. * Y_l^m(\theta,\phi) = (-1)^m[\frac{(2l+1)}{4\pi}
  175. * \frac{(l-m)!}{(l+m)!}]
  176. * P_l^m(\cos\theta) \exp^{im\phi}
  177. * @f]
  178. * is the spherical harmonic function and @f$ P_l^m(x) @f$ is the
  179. * associated Legendre function.
  180. *
  181. * This function differs from the associated Legendre function by
  182. * argument (@f$x = \cos(\theta)@f$) and by a normalization factor
  183. * but this factor is rather large for large @f$ l @f$ and @f$ m @f$
  184. * and so this function is stable for larger differences of @f$ l @f$
  185. * and @f$ m @f$.
  186. *
  187. * @param l The order of the spherical associated Legendre function.
  188. * @f$ l >= 0 @f$.
  189. * @param m The order of the spherical associated Legendre function.
  190. * @f$ m <= l @f$.
  191. * @param theta The radian angle argument of the spherical associated
  192. * Legendre function.
  193. */
  194. template <typename _Tp>
  195. _Tp
  196. __sph_legendre(unsigned int __l, unsigned int __m, _Tp __theta)
  197. {
  198. if (__isnan(__theta))
  199. return std::numeric_limits<_Tp>::quiet_NaN();
  200. const _Tp __x = std::cos(__theta);
  201. if (__l < __m)
  202. {
  203. std::__throw_domain_error(__N("Bad argument "
  204. "in __sph_legendre."));
  205. }
  206. else if (__m == 0)
  207. {
  208. _Tp __P = __poly_legendre_p(__l, __x);
  209. _Tp __fact = std::sqrt(_Tp(2 * __l + 1)
  210. / (_Tp(4) * __numeric_constants<_Tp>::__pi()));
  211. __P *= __fact;
  212. return __P;
  213. }
  214. else if (__x == _Tp(1) || __x == -_Tp(1))
  215. {
  216. // m > 0 here
  217. return _Tp(0);
  218. }
  219. else
  220. {
  221. // m > 0 and |x| < 1 here
  222. // Starting value for recursion.
  223. // Y_m^m(x) = sqrt( (2m+1)/(4pi m) gamma(m+1/2)/gamma(m) )
  224. // (-1)^m (1-x^2)^(m/2) / pi^(1/4)
  225. const _Tp __sgn = ( __m % 2 == 1 ? -_Tp(1) : _Tp(1));
  226. const _Tp __y_mp1m_factor = __x * std::sqrt(_Tp(2 * __m + 3));
  227. #if _GLIBCXX_USE_C99_MATH_TR1
  228. const _Tp __lncirc = _GLIBCXX_MATH_NS::log1p(-__x * __x);
  229. #else
  230. const _Tp __lncirc = std::log(_Tp(1) - __x * __x);
  231. #endif
  232. // Gamma(m+1/2) / Gamma(m)
  233. #if _GLIBCXX_USE_C99_MATH_TR1
  234. const _Tp __lnpoch = _GLIBCXX_MATH_NS::lgamma(_Tp(__m + _Tp(0.5L)))
  235. - _GLIBCXX_MATH_NS::lgamma(_Tp(__m));
  236. #else
  237. const _Tp __lnpoch = __log_gamma(_Tp(__m + _Tp(0.5L)))
  238. - __log_gamma(_Tp(__m));
  239. #endif
  240. const _Tp __lnpre_val =
  241. -_Tp(0.25L) * __numeric_constants<_Tp>::__lnpi()
  242. + _Tp(0.5L) * (__lnpoch + __m * __lncirc);
  243. _Tp __sr = std::sqrt((_Tp(2) + _Tp(1) / __m)
  244. / (_Tp(4) * __numeric_constants<_Tp>::__pi()));
  245. _Tp __y_mm = __sgn * __sr * std::exp(__lnpre_val);
  246. _Tp __y_mp1m = __y_mp1m_factor * __y_mm;
  247. if (__l == __m)
  248. {
  249. return __y_mm;
  250. }
  251. else if (__l == __m + 1)
  252. {
  253. return __y_mp1m;
  254. }
  255. else
  256. {
  257. _Tp __y_lm = _Tp(0);
  258. // Compute Y_l^m, l > m+1, upward recursion on l.
  259. for ( int __ll = __m + 2; __ll <= __l; ++__ll)
  260. {
  261. const _Tp __rat1 = _Tp(__ll - __m) / _Tp(__ll + __m);
  262. const _Tp __rat2 = _Tp(__ll - __m - 1) / _Tp(__ll + __m - 1);
  263. const _Tp __fact1 = std::sqrt(__rat1 * _Tp(2 * __ll + 1)
  264. * _Tp(2 * __ll - 1));
  265. const _Tp __fact2 = std::sqrt(__rat1 * __rat2 * _Tp(2 * __ll + 1)
  266. / _Tp(2 * __ll - 3));
  267. __y_lm = (__x * __y_mp1m * __fact1
  268. - (__ll + __m - 1) * __y_mm * __fact2) / _Tp(__ll - __m);
  269. __y_mm = __y_mp1m;
  270. __y_mp1m = __y_lm;
  271. }
  272. return __y_lm;
  273. }
  274. }
  275. }
  276. } // namespace __detail
  277. #undef _GLIBCXX_MATH_NS
  278. #if ! _GLIBCXX_USE_STD_SPEC_FUNCS && defined(_GLIBCXX_TR1_CMATH)
  279. } // namespace tr1
  280. #endif
  281. _GLIBCXX_END_NAMESPACE_VERSION
  282. }
  283. #endif // _GLIBCXX_TR1_LEGENDRE_FUNCTION_TCC