| 1 | // Copyright (c) 2011 John Maddock |
| 2 | // Use, modification and distribution are subject to the |
| 3 | // Boost Software License, Version 1.0. (See accompanying file |
| 4 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 5 | |
| 6 | #ifndef BOOST_MATH_BESSEL_JN_SERIES_HPP |
| 7 | #define BOOST_MATH_BESSEL_JN_SERIES_HPP |
| 8 | |
| 9 | #ifdef _MSC_VER |
| 10 | #pragma once |
| 11 | #endif |
| 12 | |
| 13 | namespace boost { namespace math { namespace detail{ |
| 14 | |
| 15 | template <class T, class Policy> |
| 16 | struct bessel_j_small_z_series_term |
| 17 | { |
| 18 | typedef T result_type; |
| 19 | |
| 20 | bessel_j_small_z_series_term(T v_, T x) |
| 21 | : N(0), v(v_) |
| 22 | { |
| 23 | BOOST_MATH_STD_USING |
| 24 | mult = x / 2; |
| 25 | mult *= -mult; |
| 26 | term = 1; |
| 27 | } |
| 28 | T operator()() |
| 29 | { |
| 30 | T r = term; |
| 31 | ++N; |
| 32 | term *= mult / (N * (N + v)); |
| 33 | return r; |
| 34 | } |
| 35 | private: |
| 36 | unsigned N; |
| 37 | T v; |
| 38 | T mult; |
| 39 | T term; |
| 40 | }; |
| 41 | // |
| 42 | // Series evaluation for BesselJ(v, z) as z -> 0. |
| 43 | // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/ |
| 44 | // Converges rapidly for all z << v. |
| 45 | // |
| 46 | template <class T, class Policy> |
| 47 | inline T bessel_j_small_z_series(T v, T x, const Policy& pol) |
| 48 | { |
| 49 | BOOST_MATH_STD_USING |
| 50 | T prefix; |
| 51 | if(v < max_factorial<T>::value) |
| 52 | { |
| 53 | prefix = pow(x / 2, v) / boost::math::tgamma(v+1, pol); |
| 54 | } |
| 55 | else |
| 56 | { |
| 57 | prefix = v * log(x / 2) - boost::math::lgamma(v+1, pol); |
| 58 | prefix = exp(prefix); |
| 59 | } |
| 60 | if(0 == prefix) |
| 61 | return prefix; |
| 62 | |
| 63 | bessel_j_small_z_series_term<T, Policy> s(v, x); |
| 64 | boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); |
| 65 | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
| 66 | T zero = 0; |
| 67 | T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); |
| 68 | #else |
| 69 | T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter); |
| 70 | #endif |
| 71 | policies::check_series_iterations<T>("boost::math::bessel_j_small_z_series<%1%>(%1%,%1%)" , max_iter, pol); |
| 72 | return prefix * result; |
| 73 | } |
| 74 | |
| 75 | template <class T, class Policy> |
| 76 | struct bessel_y_small_z_series_term_a |
| 77 | { |
| 78 | typedef T result_type; |
| 79 | |
| 80 | bessel_y_small_z_series_term_a(T v_, T x) |
| 81 | : N(0), v(v_) |
| 82 | { |
| 83 | BOOST_MATH_STD_USING |
| 84 | mult = x / 2; |
| 85 | mult *= -mult; |
| 86 | term = 1; |
| 87 | } |
| 88 | T operator()() |
| 89 | { |
| 90 | BOOST_MATH_STD_USING |
| 91 | T r = term; |
| 92 | ++N; |
| 93 | term *= mult / (N * (N - v)); |
| 94 | return r; |
| 95 | } |
| 96 | private: |
| 97 | unsigned N; |
| 98 | T v; |
| 99 | T mult; |
| 100 | T term; |
| 101 | }; |
| 102 | |
| 103 | template <class T, class Policy> |
| 104 | struct bessel_y_small_z_series_term_b |
| 105 | { |
| 106 | typedef T result_type; |
| 107 | |
| 108 | bessel_y_small_z_series_term_b(T v_, T x) |
| 109 | : N(0), v(v_) |
| 110 | { |
| 111 | BOOST_MATH_STD_USING |
| 112 | mult = x / 2; |
| 113 | mult *= -mult; |
| 114 | term = 1; |
| 115 | } |
| 116 | T operator()() |
| 117 | { |
| 118 | T r = term; |
| 119 | ++N; |
| 120 | term *= mult / (N * (N + v)); |
| 121 | return r; |
| 122 | } |
| 123 | private: |
| 124 | unsigned N; |
| 125 | T v; |
| 126 | T mult; |
| 127 | T term; |
| 128 | }; |
| 129 | // |
| 130 | // Series form for BesselY as z -> 0, |
| 131 | // see: http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/ |
| 132 | // This series is only useful when the second term is small compared to the first |
| 133 | // otherwise we get catastrophic cancellation errors. |
| 134 | // |
| 135 | // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring: |
| 136 | // eps/2 * v^v(x/2)^-v > (x/2)^v or log(eps/2) > v log((x/2)^2/v) |
| 137 | // |
| 138 | template <class T, class Policy> |
| 139 | inline T bessel_y_small_z_series(T v, T x, T* pscale, const Policy& pol) |
| 140 | { |
| 141 | BOOST_MATH_STD_USING |
| 142 | static const char* function = "bessel_y_small_z_series<%1%>(%1%,%1%)" ; |
| 143 | T prefix; |
| 144 | T gam; |
| 145 | T p = log(x / 2); |
| 146 | T scale = 1; |
| 147 | bool need_logs = (v >= max_factorial<T>::value) || (tools::log_max_value<T>() / v < fabs(p)); |
| 148 | if(!need_logs) |
| 149 | { |
| 150 | gam = boost::math::tgamma(v, pol); |
| 151 | p = pow(x / 2, v); |
| 152 | if(tools::max_value<T>() * p < gam) |
| 153 | { |
| 154 | scale /= gam; |
| 155 | gam = 1; |
| 156 | if(tools::max_value<T>() * p < gam) |
| 157 | { |
| 158 | return -policies::raise_overflow_error<T>(function, 0, pol); |
| 159 | } |
| 160 | } |
| 161 | prefix = -gam / (constants::pi<T>() * p); |
| 162 | } |
| 163 | else |
| 164 | { |
| 165 | gam = boost::math::lgamma(v, pol); |
| 166 | p = v * p; |
| 167 | prefix = gam - log(constants::pi<T>()) - p; |
| 168 | if(tools::log_max_value<T>() < prefix) |
| 169 | { |
| 170 | prefix -= log(tools::max_value<T>() / 4); |
| 171 | scale /= (tools::max_value<T>() / 4); |
| 172 | if(tools::log_max_value<T>() < prefix) |
| 173 | { |
| 174 | return -policies::raise_overflow_error<T>(function, 0, pol); |
| 175 | } |
| 176 | } |
| 177 | prefix = -exp(prefix); |
| 178 | } |
| 179 | bessel_y_small_z_series_term_a<T, Policy> s(v, x); |
| 180 | boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>(); |
| 181 | *pscale = scale; |
| 182 | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
| 183 | T zero = 0; |
| 184 | T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); |
| 185 | #else |
| 186 | T result = boost::math::tools::sum_series(s, boost::math::policies::get_epsilon<T, Policy>(), max_iter); |
| 187 | #endif |
| 188 | policies::check_series_iterations<T>("boost::math::bessel_y_small_z_series<%1%>(%1%,%1%)" , max_iter, pol); |
| 189 | result *= prefix; |
| 190 | |
| 191 | if(!need_logs) |
| 192 | { |
| 193 | prefix = boost::math::tgamma(-v, pol) * boost::math::cos_pi(v) * p / constants::pi<T>(); |
| 194 | } |
| 195 | else |
| 196 | { |
| 197 | int sgn; |
| 198 | prefix = boost::math::lgamma(-v, &sgn, pol) + p; |
| 199 | prefix = exp(prefix) * sgn / constants::pi<T>(); |
| 200 | } |
| 201 | bessel_y_small_z_series_term_b<T, Policy> s2(v, x); |
| 202 | max_iter = policies::get_max_series_iterations<Policy>(); |
| 203 | #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
| 204 | T b = boost::math::tools::sum_series(s2, boost::math::policies::get_epsilon<T, Policy>(), max_iter, zero); |
| 205 | #else |
| 206 | T b = boost::math::tools::sum_series(s2, boost::math::policies::get_epsilon<T, Policy>(), max_iter); |
| 207 | #endif |
| 208 | result -= scale * prefix * b; |
| 209 | return result; |
| 210 | } |
| 211 | |
| 212 | template <class T, class Policy> |
| 213 | T bessel_yn_small_z(int n, T z, T* scale, const Policy& pol) |
| 214 | { |
| 215 | // |
| 216 | // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/02/ |
| 217 | // |
| 218 | // Note that when called we assume that x < epsilon and n is a positive integer. |
| 219 | // |
| 220 | BOOST_MATH_STD_USING |
| 221 | BOOST_ASSERT(n >= 0); |
| 222 | BOOST_ASSERT((z < policies::get_epsilon<T, Policy>())); |
| 223 | |
| 224 | if(n == 0) |
| 225 | { |
| 226 | return (2 / constants::pi<T>()) * (log(z / 2) + constants::euler<T>()); |
| 227 | } |
| 228 | else if(n == 1) |
| 229 | { |
| 230 | return (z / constants::pi<T>()) * log(z / 2) |
| 231 | - 2 / (constants::pi<T>() * z) |
| 232 | - (z / (2 * constants::pi<T>())) * (1 - 2 * constants::euler<T>()); |
| 233 | } |
| 234 | else if(n == 2) |
| 235 | { |
| 236 | return (z * z) / (4 * constants::pi<T>()) * log(z / 2) |
| 237 | - (4 / (constants::pi<T>() * z * z)) |
| 238 | - ((z * z) / (8 * constants::pi<T>())) * (T(3)/2 - 2 * constants::euler<T>()); |
| 239 | } |
| 240 | else |
| 241 | { |
| 242 | T p = pow(z / 2, n); |
| 243 | T result = -((boost::math::factorial<T>(n - 1) / constants::pi<T>())); |
| 244 | if(p * tools::max_value<T>() < result) |
| 245 | { |
| 246 | T div = tools::max_value<T>() / 8; |
| 247 | result /= div; |
| 248 | *scale /= div; |
| 249 | if(p * tools::max_value<T>() < result) |
| 250 | { |
| 251 | return -policies::raise_overflow_error<T>("bessel_yn_small_z<%1%>(%1%,%1%)" , 0, pol); |
| 252 | } |
| 253 | } |
| 254 | return result / p; |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | }}} // namespaces |
| 259 | |
| 260 | #endif // BOOST_MATH_BESSEL_JN_SERIES_HPP |
| 261 | |
| 262 | |