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