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