File indexing completed on 2025-01-18 09:39:58
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_MATH_BERNOULLI_DETAIL_HPP
0008 #define BOOST_MATH_BERNOULLI_DETAIL_HPP
0009
0010 #include <boost/math/tools/atomic.hpp>
0011 #include <boost/math/tools/toms748_solve.hpp>
0012 #include <boost/math/tools/cxx03_warn.hpp>
0013 #include <boost/math/tools/throw_exception.hpp>
0014 #include <boost/math/tools/config.hpp>
0015 #include <boost/math/special_functions/fpclassify.hpp>
0016 #include <vector>
0017 #include <type_traits>
0018
0019 #if defined(BOOST_HAS_THREADS) && !defined(BOOST_NO_CXX11_HDR_MUTEX) && !defined(BOOST_MATH_NO_ATOMIC_INT)
0020 #include <mutex>
0021 #else
0022 # define BOOST_MATH_BERNOULLI_NOTHREADS
0023 #endif
0024
0025 namespace boost{ namespace math{ namespace detail{
0026
0027
0028
0029
0030 template <class T, class Policy>
0031 T b2n_asymptotic(int n)
0032 {
0033 BOOST_MATH_STD_USING
0034 const auto nx = static_cast<T>(n);
0035 const T nx2(nx * nx);
0036
0037 const T approximate_log_of_bernoulli_bn =
0038 ((boost::math::constants::half<T>() + nx) * log(nx))
0039 + ((boost::math::constants::half<T>() - nx) * log(boost::math::constants::pi<T>()))
0040 + (((T(3) / 2) - nx) * boost::math::constants::ln_two<T>())
0041 + ((nx * (T(2) - (nx2 * 7) * (1 + ((nx2 * 30) * ((nx2 * 12) - 1))))) / (((nx2 * nx2) * nx2) * 2520));
0042 return ((n / 2) & 1 ? 1 : -1) * (approximate_log_of_bernoulli_bn > tools::log_max_value<T>()
0043 ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, nx, Policy())
0044 : static_cast<T>(exp(approximate_log_of_bernoulli_bn)));
0045 }
0046
0047 template <class T, class Policy>
0048 T t2n_asymptotic(int n)
0049 {
0050 BOOST_MATH_STD_USING
0051
0052 T t2n = fabs(b2n_asymptotic<T, Policy>(2 * n)) / (2 * n);
0053 T p2 = ldexp(T(1), n);
0054 if(tools::max_value<T>() / p2 < t2n)
0055 {
0056 return policies::raise_overflow_error<T>("boost::math::tangent_t2n<%1%>(std::size_t)", nullptr, T(n), Policy());
0057 }
0058 t2n *= p2;
0059 p2 -= 1;
0060 if(tools::max_value<T>() / p2 < t2n)
0061 {
0062 return policies::raise_overflow_error<T>("boost::math::tangent_t2n<%1%>(std::size_t)", nullptr, Policy());
0063 }
0064 t2n *= p2;
0065 return t2n;
0066 }
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082 struct max_bernoulli_root_functor
0083 {
0084 explicit max_bernoulli_root_functor(unsigned long long t) : target(static_cast<double>(t)) {}
0085 double operator()(double n) const
0086 {
0087 BOOST_MATH_STD_USING
0088
0089
0090
0091 const double nx2(n * n);
0092
0093 const double approximate_log_of_bernoulli_bn
0094 = ((boost::math::constants::half<double>() + n) * log(n))
0095 + ((boost::math::constants::half<double>() - n) * log(boost::math::constants::pi<double>()))
0096 + (((static_cast<double>(3) / 2) - n) * boost::math::constants::ln_two<double>())
0097 + ((n * (2 - (nx2 * 7) * (1 + ((nx2 * 30) * ((nx2 * 12) - 1))))) / (((nx2 * nx2) * nx2) * 2520));
0098
0099 return approximate_log_of_bernoulli_bn - target;
0100 }
0101 private:
0102 double target;
0103 };
0104
0105 template <class T, class Policy>
0106 inline std::size_t find_bernoulli_overflow_limit(const std::false_type&)
0107 {
0108
0109 static const auto max_result = static_cast<double>((std::numeric_limits<std::size_t>::max)() - 1000u);
0110
0111 unsigned long long t = lltrunc(boost::math::tools::log_max_value<T>());
0112 max_bernoulli_root_functor fun(t);
0113 boost::math::tools::equal_floor tol;
0114 std::uintmax_t max_iter = boost::math::policies::get_max_root_iterations<Policy>();
0115 double result = boost::math::tools::toms748_solve(fun, sqrt(static_cast<double>(t)), static_cast<double>(t), tol, max_iter).first / 2;
0116 if (result > max_result)
0117 {
0118 result = max_result;
0119 }
0120
0121 return static_cast<std::size_t>(result);
0122 }
0123
0124 template <class T, class Policy>
0125 inline std::size_t find_bernoulli_overflow_limit(const std::true_type&)
0126 {
0127 return max_bernoulli_index<bernoulli_imp_variant<T>::value>::value;
0128 }
0129
0130 template <class T, class Policy>
0131 std::size_t b2n_overflow_limit()
0132 {
0133
0134
0135 using tag_type = std::integral_constant<bool, (bernoulli_imp_variant<T>::value >= 1) && (bernoulli_imp_variant<T>::value <= 3)>;
0136 static const std::size_t lim = find_bernoulli_overflow_limit<T, Policy>(tag_type());
0137 return lim;
0138 }
0139
0140
0141
0142
0143
0144
0145 template <class T, typename std::enable_if<std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::radix == 2), bool>::type = true>
0146 inline T tangent_scale_factor()
0147 {
0148 BOOST_MATH_STD_USING
0149 return ldexp(T(1), std::numeric_limits<T>::min_exponent + 5);
0150 }
0151
0152 template <class T, typename std::enable_if<!std::numeric_limits<T>::is_specialized || !(std::numeric_limits<T>::radix == 2), bool>::type = true>
0153 inline T tangent_scale_factor()
0154 {
0155 return tools::min_value<T>() * 16;
0156 }
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168 template <class T>
0169 struct fixed_vector : private std::allocator<T>
0170 {
0171 using size_type = unsigned;
0172 using iterator = T*;
0173 using const_iterator = const T*;
0174 fixed_vector() : m_used(0)
0175 {
0176 std::size_t overflow_limit = 5 + b2n_overflow_limit<T, policies::policy<> >();
0177 m_capacity = static_cast<unsigned>((std::min)(overflow_limit, static_cast<std::size_t>(100000u)));
0178 m_data = this->allocate(m_capacity);
0179 }
0180 ~fixed_vector()
0181 {
0182 using allocator_type = std::allocator<T>;
0183 using allocator_traits = std::allocator_traits<allocator_type>;
0184 allocator_type& alloc = *this;
0185 for(unsigned i = 0; i < m_used; ++i)
0186 {
0187 allocator_traits::destroy(alloc, &m_data[i]);
0188 }
0189 allocator_traits::deallocate(alloc, m_data, m_capacity);
0190 }
0191 T& operator[](unsigned n) { BOOST_MATH_ASSERT(n < m_used); return m_data[n]; }
0192 const T& operator[](unsigned n)const { BOOST_MATH_ASSERT(n < m_used); return m_data[n]; }
0193 unsigned size()const { return m_used; }
0194 unsigned size() { return m_used; }
0195 bool resize(unsigned n, const T& val)
0196 {
0197 if(n > m_capacity)
0198 {
0199 #ifndef BOOST_NO_EXCEPTIONS
0200 BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Exhausted storage for Bernoulli numbers."));
0201 #else
0202 return false;
0203 #endif
0204 }
0205 for(unsigned i = m_used; i < n; ++i)
0206 new (m_data + i) T(val);
0207 m_used = n;
0208 return true;
0209 }
0210 bool resize(unsigned n) { return resize(n, T()); }
0211 T* begin() { return m_data; }
0212 T* end() { return m_data + m_used; }
0213 T* begin()const { return m_data; }
0214 T* end()const { return m_data + m_used; }
0215 unsigned capacity()const { return m_capacity; }
0216 void clear() { m_used = 0; }
0217 private:
0218 T* m_data;
0219 unsigned m_used {};
0220 unsigned m_capacity;
0221 };
0222
0223 template <class T, class Policy>
0224 class bernoulli_numbers_cache
0225 {
0226 public:
0227 bernoulli_numbers_cache() : m_overflow_limit((std::numeric_limits<std::size_t>::max)())
0228 , m_counter(0)
0229 , m_current_precision(boost::math::tools::digits<T>())
0230 {}
0231
0232 using container_type = fixed_vector<T>;
0233
0234 bool tangent(std::size_t m)
0235 {
0236 static const std::size_t min_overflow_index = b2n_overflow_limit<T, Policy>() - 1;
0237
0238 if (!tn.resize(static_cast<typename container_type::size_type>(m), T(0U)))
0239 {
0240 return false;
0241 }
0242
0243 BOOST_MATH_INSTRUMENT_VARIABLE(min_overflow_index);
0244
0245 std::size_t prev_size = m_intermediates.size();
0246 m_intermediates.resize(m, T(0U));
0247
0248 if(prev_size == 0)
0249 {
0250 m_intermediates[1] = tangent_scale_factor<T>() ;
0251 tn[0U] = T(0U);
0252 tn[1U] = tangent_scale_factor<T>();
0253 BOOST_MATH_INSTRUMENT_VARIABLE(tn[0]);
0254 BOOST_MATH_INSTRUMENT_VARIABLE(tn[1]);
0255 }
0256
0257 for(std::size_t i = std::max<size_t>(2, prev_size); i < m; i++)
0258 {
0259 bool overflow_check = false;
0260 if(i >= min_overflow_index && (boost::math::tools::max_value<T>() / (i-1) < m_intermediates[1]) )
0261 {
0262 std::fill(tn.begin() + i, tn.end(), boost::math::tools::max_value<T>());
0263 break;
0264 }
0265 m_intermediates[1] = m_intermediates[1] * (i-1);
0266 for(std::size_t j = 2; j <= i; j++)
0267 {
0268 overflow_check =
0269 (i >= min_overflow_index) && (
0270 (boost::math::tools::max_value<T>() / (i - j) < m_intermediates[j])
0271 || (boost::math::tools::max_value<T>() / (i - j + 2) < m_intermediates[j-1])
0272 || (boost::math::tools::max_value<T>() - m_intermediates[j] * (i - j) < m_intermediates[j-1] * (i - j + 2))
0273 || ((boost::math::isinf)(m_intermediates[j]))
0274 );
0275
0276 if(overflow_check)
0277 {
0278 std::fill(tn.begin() + i, tn.end(), boost::math::tools::max_value<T>());
0279 break;
0280 }
0281 m_intermediates[j] = m_intermediates[j] * (i - j) + m_intermediates[j-1] * (i - j + 2);
0282 }
0283 if(overflow_check)
0284 break;
0285 tn[static_cast<typename container_type::size_type>(i)] = m_intermediates[i];
0286 BOOST_MATH_INSTRUMENT_VARIABLE(i);
0287 BOOST_MATH_INSTRUMENT_VARIABLE(tn[static_cast<typename container_type::size_type>(i)]);
0288 }
0289 return true;
0290 }
0291
0292 bool tangent_numbers_series(const std::size_t m)
0293 {
0294 BOOST_MATH_STD_USING
0295 static const std::size_t min_overflow_index = b2n_overflow_limit<T, Policy>() - 1;
0296
0297 typename container_type::size_type old_size = bn.size();
0298
0299 if (!tangent(m))
0300 return false;
0301 if (!bn.resize(static_cast<typename container_type::size_type>(m)))
0302 return false;
0303
0304 if(!old_size)
0305 {
0306 bn[0] = 1;
0307 old_size = 1;
0308 }
0309
0310 T power_two(ldexp(T(1), static_cast<int>(2 * old_size)));
0311
0312 for(std::size_t i = old_size; i < m; i++)
0313 {
0314 T b(static_cast<T>(i * 2));
0315
0316
0317
0318
0319
0320 b = b / (power_two * tangent_scale_factor<T>());
0321 b /= (power_two - 1);
0322 bool overflow_check = (i >= min_overflow_index) && (tools::max_value<T>() / tn[static_cast<typename container_type::size_type>(i)] < b);
0323 if(overflow_check)
0324 {
0325 m_overflow_limit = i;
0326 while(i < m)
0327 {
0328 b = std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity() : tools::max_value<T>();
0329 bn[static_cast<typename container_type::size_type>(i)] = ((i % 2U) ? b : T(-b));
0330 ++i;
0331 }
0332 break;
0333 }
0334 else
0335 {
0336 b *= tn[static_cast<typename container_type::size_type>(i)];
0337 }
0338
0339 power_two = ldexp(power_two, 2);
0340
0341 const bool b_neg = i % 2 == 0;
0342
0343 bn[static_cast<typename container_type::size_type>(i)] = ((!b_neg) ? b : T(-b));
0344 }
0345 return true;
0346 }
0347
0348 template <class OutputIterator>
0349 OutputIterator copy_bernoulli_numbers(OutputIterator out, std::size_t start, std::size_t n, const Policy& pol)
0350 {
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363 if(start + n > bn.capacity())
0364 {
0365 if(start < bn.capacity())
0366 {
0367 out = copy_bernoulli_numbers(out, start, bn.capacity() - start, pol);
0368 n -= bn.capacity() - start;
0369 start = static_cast<std::size_t>(bn.capacity());
0370 }
0371 if(start < b2n_overflow_limit<T, Policy>() + 2u)
0372 {
0373 for(; n; ++start, --n)
0374 {
0375 *out = b2n_asymptotic<T, Policy>(static_cast<typename container_type::size_type>(start * 2U));
0376 ++out;
0377 }
0378 }
0379 for(; n; ++start, --n)
0380 {
0381 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(start), pol);
0382 ++out;
0383 }
0384 return out;
0385 }
0386
0387 #if defined(BOOST_HAS_THREADS) && defined(BOOST_MATH_BERNOULLI_NOTHREADS) && !defined(BOOST_MATH_BERNOULLI_UNTHREADED)
0388
0389 static_assert(sizeof(T) == 1, "Unsupported configuration: your platform appears to have either no atomic integers, or no std::mutex. If you are happy with thread-unsafe code, then you may define BOOST_MATH_BERNOULLI_UNTHREADED to suppress this error.");
0390 #elif defined(BOOST_MATH_BERNOULLI_NOTHREADS)
0391
0392
0393
0394 if(m_current_precision < boost::math::tools::digits<T>())
0395 {
0396 bn.clear();
0397 tn.clear();
0398 m_intermediates.clear();
0399 m_current_precision = boost::math::tools::digits<T>();
0400 }
0401 if(start + n >= bn.size())
0402 {
0403 std::size_t new_size = (std::min)((std::max)((std::max)(std::size_t(start + n), std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity()));
0404 if (!tangent_numbers_series(new_size))
0405 {
0406 return std::fill_n(out, n, policies::raise_evaluation_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol));
0407 }
0408 }
0409
0410 for(std::size_t i = (std::max)(std::size_t(max_bernoulli_b2n<T>::value + 1), start); i < start + n; ++i)
0411 {
0412 *out = (i >= m_overflow_limit) ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol) : bn[i];
0413 ++out;
0414 }
0415 #else
0416
0417
0418
0419
0420
0421
0422 if((static_cast<std::size_t>(m_counter.load(std::memory_order_consume)) < start + n)
0423 || (static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>()))
0424 {
0425 std::lock_guard<std::mutex> l(m_mutex);
0426
0427 if((static_cast<std::size_t>(m_counter.load(std::memory_order_consume)) < start + n)
0428 || (static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>()))
0429 {
0430 if(static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>())
0431 {
0432 bn.clear();
0433 tn.clear();
0434 m_intermediates.clear();
0435 m_counter.store(0, std::memory_order_release);
0436 m_current_precision = boost::math::tools::digits<T>();
0437 }
0438 if(start + n >= bn.size())
0439 {
0440 std::size_t new_size = (std::min)((std::max)((std::max)(std::size_t(start + n), std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity()));
0441 if (!tangent_numbers_series(new_size))
0442 return std::fill_n(out, n, policies::raise_evaluation_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(new_size), pol));
0443 }
0444 m_counter.store(static_cast<atomic_integer_type>(bn.size()), std::memory_order_release);
0445 }
0446 }
0447
0448 for(std::size_t i = (std::max)(static_cast<std::size_t>(max_bernoulli_b2n<T>::value + 1), start); i < start + n; ++i)
0449 {
0450 *out = (i >= m_overflow_limit) ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol) : bn[static_cast<typename container_type::size_type>(i)];
0451 ++out;
0452 }
0453
0454 #endif
0455 return out;
0456 }
0457
0458 template <class OutputIterator>
0459 OutputIterator copy_tangent_numbers(OutputIterator out, std::size_t start, std::size_t n, const Policy& pol)
0460 {
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474 if(start + n > bn.capacity())
0475 {
0476 if(start < bn.capacity())
0477 {
0478 out = copy_tangent_numbers(out, start, bn.capacity() - start, pol);
0479 n -= bn.capacity() - start;
0480 start = static_cast<std::size_t>(bn.capacity());
0481 }
0482 if(start < b2n_overflow_limit<T, Policy>() + 2u)
0483 {
0484 for(; n; ++start, --n)
0485 {
0486 *out = t2n_asymptotic<T, Policy>(static_cast<typename container_type::size_type>(start));
0487 ++out;
0488 }
0489 }
0490 for(; n; ++start, --n)
0491 {
0492 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(start), pol);
0493 ++out;
0494 }
0495 return out;
0496 }
0497
0498 #if defined(BOOST_MATH_BERNOULLI_NOTHREADS)
0499
0500
0501
0502 if(m_current_precision < boost::math::tools::digits<T>())
0503 {
0504 bn.clear();
0505 tn.clear();
0506 m_intermediates.clear();
0507 m_current_precision = boost::math::tools::digits<T>();
0508 }
0509 if(start + n >= bn.size())
0510 {
0511 std::size_t new_size = (std::min)((std::max)((std::max)(start + n, std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity()));
0512 if (!tangent_numbers_series(new_size))
0513 return std::fill_n(out, n, policies::raise_evaluation_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol));
0514 }
0515
0516 for(std::size_t i = start; i < start + n; ++i)
0517 {
0518 if(i >= m_overflow_limit)
0519 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol);
0520 else
0521 {
0522 if(tools::max_value<T>() * tangent_scale_factor<T>() < tn[static_cast<typename container_type::size_type>(i)])
0523 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol);
0524 else
0525 *out = tn[static_cast<typename container_type::size_type>(i)] / tangent_scale_factor<T>();
0526 }
0527 ++out;
0528 }
0529 #elif defined(BOOST_MATH_NO_ATOMIC_INT)
0530 static_assert(sizeof(T) == 1, "Unsupported configuration: your platform appears to have no atomic integers. If you are happy with thread-unsafe code, then you may define BOOST_MATH_BERNOULLI_UNTHREADED to suppress this error.");
0531 #else
0532
0533
0534
0535
0536
0537
0538 if((static_cast<std::size_t>(m_counter.load(std::memory_order_consume)) < start + n)
0539 || (static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>()))
0540 {
0541 std::lock_guard<std::mutex> l(m_mutex);
0542
0543 if((static_cast<std::size_t>(m_counter.load(std::memory_order_consume)) < start + n)
0544 || (static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>()))
0545 {
0546 if(static_cast<int>(m_current_precision.load(std::memory_order_consume)) < boost::math::tools::digits<T>())
0547 {
0548 bn.clear();
0549 tn.clear();
0550 m_intermediates.clear();
0551 m_counter.store(0, std::memory_order_release);
0552 m_current_precision = boost::math::tools::digits<T>();
0553 }
0554 if(start + n >= bn.size())
0555 {
0556 std::size_t new_size = (std::min)((std::max)((std::max)(start + n, std::size_t(bn.size() + 20)), std::size_t(50)), std::size_t(bn.capacity()));
0557 if (!tangent_numbers_series(new_size))
0558 return std::fill_n(out, n, policies::raise_evaluation_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", "Unable to allocate Bernoulli numbers cache for %1% values", T(start + n), pol));
0559 }
0560 m_counter.store(static_cast<atomic_integer_type>(bn.size()), std::memory_order_release);
0561 }
0562 }
0563
0564 for(std::size_t i = start; i < start + n; ++i)
0565 {
0566 if(i >= m_overflow_limit)
0567 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol);
0568 else
0569 {
0570 if(tools::max_value<T>() * tangent_scale_factor<T>() < tn[static_cast<typename container_type::size_type>(i)])
0571 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", nullptr, T(i), pol);
0572 else
0573 *out = tn[static_cast<typename container_type::size_type>(i)] / tangent_scale_factor<T>();
0574 }
0575 ++out;
0576 }
0577
0578 #endif
0579 return out;
0580 }
0581
0582 private:
0583
0584
0585
0586
0587
0588 fixed_vector<T> bn, tn;
0589 std::vector<T> m_intermediates;
0590
0591 std::size_t m_overflow_limit;
0592
0593 #if !defined(BOOST_MATH_BERNOULLI_NOTHREADS)
0594 std::mutex m_mutex;
0595 atomic_counter_type m_counter, m_current_precision;
0596 #else
0597 int m_counter;
0598 int m_current_precision;
0599 #endif
0600 };
0601
0602 template <class T, class Policy>
0603 inline typename std::enable_if<(std::numeric_limits<T>::digits == 0) || (std::numeric_limits<T>::digits >= INT_MAX), bernoulli_numbers_cache<T, Policy>&>::type get_bernoulli_numbers_cache()
0604 {
0605
0606
0607
0608
0609
0610 static
0611 #ifndef BOOST_MATH_NO_THREAD_LOCAL_WITH_NON_TRIVIAL_TYPES
0612 BOOST_MATH_THREAD_LOCAL
0613 #endif
0614 bernoulli_numbers_cache<T, Policy> data;
0615 return data;
0616 }
0617 template <class T, class Policy>
0618 inline typename std::enable_if<std::numeric_limits<T>::digits && (std::numeric_limits<T>::digits < INT_MAX), bernoulli_numbers_cache<T, Policy>&>::type get_bernoulli_numbers_cache()
0619 {
0620
0621
0622
0623 static bernoulli_numbers_cache<T, Policy> data;
0624 return data;
0625 }
0626
0627 }}}
0628
0629 #endif