262 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			262 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  Copyright (c) 2011 John Maddock | ||
|  | //  Use, modification and distribution are subject to the | ||
|  | //  Boost Software License, Version 1.0. (See accompanying file | ||
|  | //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | #ifndef BOOST_MATH_BESSEL_JN_SERIES_HPP | ||
|  | #define BOOST_MATH_BESSEL_JN_SERIES_HPP | ||
|  | 
 | ||
|  | #ifdef _MSC_VER | ||
|  | #pragma once | ||
|  | #endif | ||
|  | 
 | ||
|  | namespace boost { namespace math { namespace detail{ | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | struct bessel_j_small_z_series_term | ||
|  | { | ||
|  |    typedef T result_type; | ||
|  | 
 | ||
|  |    bessel_j_small_z_series_term(T v_, T x) | ||
|  |       : N(0), v(v_) | ||
|  |    { | ||
|  |       BOOST_MATH_STD_USING | ||
|  |       mult = x / 2; | ||
|  |       mult *= -mult; | ||
|  |       term = 1; | ||
|  |    } | ||
|  |    T operator()() | ||
|  |    { | ||
|  |       T r = term; | ||
|  |       ++N; | ||
|  |       term *= mult / (N * (N + v)); | ||
|  |       return r; | ||
|  |    } | ||
|  | private: | ||
|  |    unsigned N; | ||
|  |    T v; | ||
|  |    T mult; | ||
|  |    T term; | ||
|  | }; | ||
|  | // | ||
|  | // Series evaluation for BesselJ(v, z) as z -> 0. | ||
|  | // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/ | ||
|  | // Converges rapidly for all z << v. | ||
|  | // | ||
|  | template <class T, class Policy> | ||
|  | inline T bessel_j_small_z_series(T v, T x, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_MATH_STD_USING | ||
|  |    T prefix; | ||
|  |    if(v < max_factorial<T>::value) | ||
|  |    { | ||
|  |       prefix = pow(x / 2, v) / boost::math::tgamma(v+1, pol); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       prefix = v * log(x / 2) - boost::math::lgamma(v+1, pol); | ||
|  |       prefix = exp(prefix); | ||
|  |    } | ||
|  |    if(0 == prefix) | ||
|  |       return prefix; | ||
|  | 
 | ||
|  |    bessel_j_small_z_series_term<T, Policy> s(v, x); | ||
|  |    boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); | ||
|  | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) | ||
|  |    T zero = 0; | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); | ||
|  | #else | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter); | ||
|  | #endif | ||
|  |    policies::check_series_iterations<T>("boost::math::bessel_j_small_z_series<%1%>(%1%,%1%)", max_iter, pol); | ||
|  |    return prefix * result; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | struct bessel_y_small_z_series_term_a | ||
|  | { | ||
|  |    typedef T result_type; | ||
|  | 
 | ||
|  |    bessel_y_small_z_series_term_a(T v_, T x) | ||
|  |       : N(0), v(v_) | ||
|  |    { | ||
|  |       BOOST_MATH_STD_USING | ||
|  |       mult = x / 2; | ||
|  |       mult *= -mult; | ||
|  |       term = 1; | ||
|  |    } | ||
|  |    T operator()() | ||
|  |    { | ||
|  |       BOOST_MATH_STD_USING | ||
|  |       T r = term; | ||
|  |       ++N; | ||
|  |       term *= mult / (N * (N - v)); | ||
|  |       return r; | ||
|  |    } | ||
|  | private: | ||
|  |    unsigned N; | ||
|  |    T v; | ||
|  |    T mult; | ||
|  |    T term; | ||
|  | }; | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | struct bessel_y_small_z_series_term_b | ||
|  | { | ||
|  |    typedef T result_type; | ||
|  | 
 | ||
|  |    bessel_y_small_z_series_term_b(T v_, T x) | ||
|  |       : N(0), v(v_) | ||
|  |    { | ||
|  |       BOOST_MATH_STD_USING | ||
|  |       mult = x / 2; | ||
|  |       mult *= -mult; | ||
|  |       term = 1; | ||
|  |    } | ||
|  |    T operator()() | ||
|  |    { | ||
|  |       T r = term; | ||
|  |       ++N; | ||
|  |       term *= mult / (N * (N + v)); | ||
|  |       return r; | ||
|  |    } | ||
|  | private: | ||
|  |    unsigned N; | ||
|  |    T v; | ||
|  |    T mult; | ||
|  |    T term; | ||
|  | }; | ||
|  | // | ||
|  | // Series form for BesselY as z -> 0,  | ||
|  | // see: http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/ | ||
|  | // This series is only useful when the second term is small compared to the first | ||
|  | // otherwise we get catestrophic cancellation errors. | ||
|  | // | ||
|  | // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring: | ||
|  | // eps/2 * v^v(x/2)^-v > (x/2)^v or log(eps/2) > v log((x/2)^2/v) | ||
|  | // | ||
|  | template <class T, class Policy> | ||
|  | inline T bessel_y_small_z_series(T v, T x, T* pscale, const Policy& pol) | ||
|  | { | ||
|  |    BOOST_MATH_STD_USING | ||
|  |    static const char* function = "bessel_y_small_z_series<%1%>(%1%,%1%)"; | ||
|  |    T prefix; | ||
|  |    T gam; | ||
|  |    T p = log(x / 2); | ||
|  |    T scale = 1; | ||
|  |    bool need_logs = (v >= max_factorial<T>::value) || (tools::log_max_value<T>() / v < fabs(p)); | ||
|  |    if(!need_logs) | ||
|  |    { | ||
|  |       gam = boost::math::tgamma(v, pol); | ||
|  |       p = pow(x / 2, v); | ||
|  |       if(tools::max_value<T>() * p < gam) | ||
|  |       { | ||
|  |          scale /= gam; | ||
|  |          gam = 1; | ||
|  |          if(tools::max_value<T>() * p < gam) | ||
|  |          { | ||
|  |             return -policies::raise_overflow_error<T>(function, 0, pol); | ||
|  |          } | ||
|  |       } | ||
|  |       prefix = -gam / (constants::pi<T>() * p); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       gam = boost::math::lgamma(v, pol); | ||
|  |       p = v * p; | ||
|  |       prefix = gam - log(constants::pi<T>()) - p; | ||
|  |       if(tools::log_max_value<T>() < prefix) | ||
|  |       { | ||
|  |          prefix -= log(tools::max_value<T>() / 4); | ||
|  |          scale /= (tools::max_value<T>() / 4); | ||
|  |          if(tools::log_max_value<T>() < prefix) | ||
|  |          { | ||
|  |             return -policies::raise_overflow_error<T>(function, 0, pol); | ||
|  |          } | ||
|  |       } | ||
|  |       prefix = -exp(prefix); | ||
|  |    } | ||
|  |    bessel_y_small_z_series_term_a<T, Policy> s(v, x); | ||
|  |    boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); | ||
|  |    *pscale = scale; | ||
|  | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) | ||
|  |    T zero = 0; | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); | ||
|  | #else | ||
|  |    T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter); | ||
|  | #endif | ||
|  |    policies::check_series_iterations<T>("boost::math::bessel_y_small_z_series<%1%>(%1%,%1%)", max_iter, pol); | ||
|  |    result *= prefix; | ||
|  | 
 | ||
|  |    if(!need_logs) | ||
|  |    { | ||
|  |       prefix = boost::math::tgamma(-v, pol) * boost::math::cos_pi(v) * p / constants::pi<T>(); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       int sgn; | ||
|  |       prefix = boost::math::lgamma(-v, &sgn, pol) + p; | ||
|  |       prefix = exp(prefix) * sgn / constants::pi<T>(); | ||
|  |    } | ||
|  |    bessel_y_small_z_series_term_b<T, Policy> s2(v, x); | ||
|  |    max_iter = policies::get_max_series_iterations<Policy>(); | ||
|  | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) | ||
|  |    T b = boost::math::tools::sum_series(s2, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); | ||
|  | #else | ||
|  |    T b = boost::math::tools::sum_series(s2, boost::math::policies::get_epsilon<T, Policy>(), max_iter); | ||
|  | #endif | ||
|  |    result -= scale * prefix * b; | ||
|  |    return result; | ||
|  | } | ||
|  | 
 | ||
|  | template <class T, class Policy> | ||
|  | T bessel_yn_small_z(int n, T z, T* scale, const Policy& pol) | ||
|  | { | ||
|  |    // | ||
|  |    // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/02/ | ||
|  |    // | ||
|  |    // Note that when called we assume that x < epsilon and n is a positive integer. | ||
|  |    // | ||
|  |    BOOST_MATH_STD_USING | ||
|  |    BOOST_ASSERT(n >= 0); | ||
|  |    BOOST_ASSERT((z < policies::get_epsilon<T, Policy>())); | ||
|  | 
 | ||
|  |    if(n == 0) | ||
|  |    { | ||
|  |       return (2 / constants::pi<T>()) * (log(z / 2) +  constants::euler<T>()); | ||
|  |    } | ||
|  |    else if(n == 1) | ||
|  |    { | ||
|  |       return (z / constants::pi<T>()) * log(z / 2)  | ||
|  |          - 2 / (constants::pi<T>() * z)  | ||
|  |          - (z / (2 * constants::pi<T>())) * (1 - 2 * constants::euler<T>()); | ||
|  |    } | ||
|  |    else if(n == 2) | ||
|  |    { | ||
|  |       return (z * z) / (4 * constants::pi<T>()) * log(z / 2)  | ||
|  |          - (4 / (constants::pi<T>() * z * z))  | ||
|  |          - ((z * z) / (8 * constants::pi<T>())) * (T(3)/2 - 2 * constants::euler<T>()); | ||
|  |    } | ||
|  |    else | ||
|  |    { | ||
|  |       T p = pow(z / 2, n); | ||
|  |       T result = -((boost::math::factorial<T>(n - 1) / constants::pi<T>())); | ||
|  |       if(p * tools::max_value<T>() < result) | ||
|  |       { | ||
|  |          T div = tools::max_value<T>() / 8; | ||
|  |          result /= div; | ||
|  |          *scale /= div; | ||
|  |          if(p * tools::max_value<T>() < result) | ||
|  |          { | ||
|  |             return -policies::raise_overflow_error<T>("bessel_yn_small_z<%1%>(%1%,%1%)", 0, pol); | ||
|  |          } | ||
|  |       } | ||
|  |       return result / p; | ||
|  |    } | ||
|  | } | ||
|  | 
 | ||
|  | }}} // namespaces | ||
|  | 
 | ||
|  | #endif // BOOST_MATH_BESSEL_JN_SERIES_HPP | ||
|  | 
 |