annotate DEPENDENCIES/generic/include/boost/math/special_functions/detail/bernoulli_details.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents f46d142149f5
children
rev   line source
Chris@102 1 ///////////////////////////////////////////////////////////////////////////////
Chris@102 2 // Copyright 2013 John Maddock
Chris@102 3 // Distributed under the Boost
Chris@102 4 // Software License, Version 1.0. (See accompanying file
Chris@102 5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Chris@102 6
Chris@102 7 #ifndef BOOST_MATH_BERNOULLI_DETAIL_HPP
Chris@102 8 #define BOOST_MATH_BERNOULLI_DETAIL_HPP
Chris@102 9
Chris@102 10 #include <boost/config.hpp>
Chris@102 11 #include <boost/detail/lightweight_mutex.hpp>
Chris@102 12 #include <boost/utility/enable_if.hpp>
Chris@102 13 #include <boost/math/tools/toms748_solve.hpp>
Chris@102 14
Chris@102 15 #ifdef BOOST_HAS_THREADS
Chris@102 16
Chris@102 17 #ifndef BOOST_NO_CXX11_HDR_ATOMIC
Chris@102 18 # include <atomic>
Chris@102 19 # define BOOST_MATH_ATOMIC_NS std
Chris@102 20 #if ATOMIC_INT_LOCK_FREE == 2
Chris@102 21 typedef std::atomic<int> atomic_counter_type;
Chris@102 22 typedef int atomic_integer_type;
Chris@102 23 #elif ATOMIC_SHORT_LOCK_FREE == 2
Chris@102 24 typedef std::atomic<short> atomic_counter_type;
Chris@102 25 typedef short atomic_integer_type;
Chris@102 26 #elif ATOMIC_LONG_LOCK_FREE == 2
Chris@102 27 typedef std::atomic<long> atomic_counter_type;
Chris@102 28 typedef long atomic_integer_type;
Chris@102 29 #elif ATOMIC_LLONG_LOCK_FREE == 2
Chris@102 30 typedef std::atomic<long long> atomic_counter_type;
Chris@102 31 typedef long long atomic_integer_type;
Chris@102 32 #else
Chris@102 33 # define BOOST_MATH_NO_ATOMIC_INT
Chris@102 34 #endif
Chris@102 35
Chris@102 36 #else // BOOST_NO_CXX11_HDR_ATOMIC
Chris@102 37 //
Chris@102 38 // We need Boost.Atomic, but on any platform that supports auto-linking we do
Chris@102 39 // not need to link against a separate library:
Chris@102 40 //
Chris@102 41 #define BOOST_ATOMIC_NO_LIB
Chris@102 42 #include <boost/atomic.hpp>
Chris@102 43 # define BOOST_MATH_ATOMIC_NS boost
Chris@102 44
Chris@102 45 namespace boost{ namespace math{ namespace detail{
Chris@102 46
Chris@102 47 //
Chris@102 48 // We need a type to use as an atomic counter:
Chris@102 49 //
Chris@102 50 #if BOOST_ATOMIC_INT_LOCK_FREE == 2
Chris@102 51 typedef boost::atomic<int> atomic_counter_type;
Chris@102 52 typedef int atomic_integer_type;
Chris@102 53 #elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2
Chris@102 54 typedef boost::atomic<short> atomic_counter_type;
Chris@102 55 typedef short atomic_integer_type;
Chris@102 56 #elif BOOST_ATOMIC_LONG_LOCK_FREE == 2
Chris@102 57 typedef boost::atomic<long> atomic_counter_type;
Chris@102 58 typedef long atomic_integer_type;
Chris@102 59 #elif BOOST_ATOMIC_LLONG_LOCK_FREE == 2
Chris@102 60 typedef boost::atomic<long long> atomic_counter_type;
Chris@102 61 typedef long long atomic_integer_type;
Chris@102 62 #else
Chris@102 63 # define BOOST_MATH_NO_ATOMIC_INT
Chris@102 64 #endif
Chris@102 65
Chris@102 66 }}} // namespaces
Chris@102 67
Chris@102 68 #endif // BOOST_NO_CXX11_HDR_ATOMIC
Chris@102 69
Chris@102 70 #endif // BOOST_HAS_THREADS
Chris@102 71
Chris@102 72 namespace boost{ namespace math{ namespace detail{
Chris@102 73 //
Chris@102 74 // Asymptotic expansion for B2n due to
Chris@102 75 // Luschny LogB3 formula (http://www.luschny.de/math/primes/bernincl.html)
Chris@102 76 //
Chris@102 77 template <class T, class Policy>
Chris@102 78 T b2n_asymptotic(int n)
Chris@102 79 {
Chris@102 80 BOOST_MATH_STD_USING
Chris@102 81 const T nx = static_cast<T>(n);
Chris@102 82 const T nx2(nx * nx);
Chris@102 83
Chris@102 84 const T approximate_log_of_bernoulli_bn =
Chris@102 85 ((boost::math::constants::half<T>() + nx) * log(nx))
Chris@102 86 + ((boost::math::constants::half<T>() - nx) * log(boost::math::constants::pi<T>()))
Chris@102 87 + (((T(3) / 2) - nx) * boost::math::constants::ln_two<T>())
Chris@102 88 + ((nx * (T(2) - (nx2 * 7) * (1 + ((nx2 * 30) * ((nx2 * 12) - 1))))) / (((nx2 * nx2) * nx2) * 2520));
Chris@102 89 return ((n / 2) & 1 ? 1 : -1) * (approximate_log_of_bernoulli_bn > tools::log_max_value<T>()
Chris@102 90 ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, nx, Policy())
Chris@102 91 : static_cast<T>(exp(approximate_log_of_bernoulli_bn)));
Chris@102 92 }
Chris@102 93
Chris@102 94 template <class T, class Policy>
Chris@102 95 T t2n_asymptotic(int n)
Chris@102 96 {
Chris@102 97 BOOST_MATH_STD_USING
Chris@102 98 // Just get B2n and convert to a Tangent number:
Chris@102 99 T t2n = fabs(b2n_asymptotic<T, Policy>(2 * n)) / (2 * n);
Chris@102 100 T p2 = ldexp(T(1), n);
Chris@102 101 if(tools::max_value<T>() / p2 < t2n)
Chris@102 102 return policies::raise_overflow_error<T>("boost::math::tangent_t2n<%1%>(std::size_t)", 0, T(n), Policy());
Chris@102 103 t2n *= p2;
Chris@102 104 p2 -= 1;
Chris@102 105 if(tools::max_value<T>() / p2 < t2n)
Chris@102 106 return policies::raise_overflow_error<T>("boost::math::tangent_t2n<%1%>(std::size_t)", 0, Policy());
Chris@102 107 t2n *= p2;
Chris@102 108 return t2n;
Chris@102 109 }
Chris@102 110 //
Chris@102 111 // We need to know the approximate value of /n/ which will
Chris@102 112 // cause bernoulli_b2n<T>(n) to return infinity - this allows
Chris@102 113 // us to elude a great deal of runtime checking for values below
Chris@102 114 // n, and only perform the full overflow checks when we know that we're
Chris@102 115 // getting close to the point where our calculations will overflow.
Chris@102 116 // We use Luschny's LogB3 formula (http://www.luschny.de/math/primes/bernincl.html)
Chris@102 117 // to find the limit, and since we're dealing with the log of the Bernoulli numbers
Chris@102 118 // we need only perform the calculation at double precision and not with T
Chris@102 119 // (which may be a multiprecision type). The limit returned is within 1 of the true
Chris@102 120 // limit for all the types tested. Note that although the code below is basically
Chris@102 121 // the same as b2n_asymptotic above, it has been recast as a continuous real-valued
Chris@102 122 // function as this makes the root finding go smoother/faster. It also omits the
Chris@102 123 // sign of the Bernoulli number.
Chris@102 124 //
Chris@102 125 struct max_bernoulli_root_functor
Chris@102 126 {
Chris@102 127 max_bernoulli_root_functor(long long t) : target(static_cast<double>(t)) {}
Chris@102 128 double operator()(double n)
Chris@102 129 {
Chris@102 130 BOOST_MATH_STD_USING
Chris@102 131
Chris@102 132 // Luschny LogB3(n) formula.
Chris@102 133
Chris@102 134 const double nx2(n * n);
Chris@102 135
Chris@102 136 const double approximate_log_of_bernoulli_bn
Chris@102 137 = ((boost::math::constants::half<double>() + n) * log(n))
Chris@102 138 + ((boost::math::constants::half<double>() - n) * log(boost::math::constants::pi<double>()))
Chris@102 139 + (((double(3) / 2) - n) * boost::math::constants::ln_two<double>())
Chris@102 140 + ((n * (2 - (nx2 * 7) * (1 + ((nx2 * 30) * ((nx2 * 12) - 1))))) / (((nx2 * nx2) * nx2) * 2520));
Chris@102 141
Chris@102 142 return approximate_log_of_bernoulli_bn - target;
Chris@102 143 }
Chris@102 144 private:
Chris@102 145 double target;
Chris@102 146 };
Chris@102 147
Chris@102 148 template <class T, class Policy>
Chris@102 149 inline std::size_t find_bernoulli_overflow_limit(const mpl::false_&)
Chris@102 150 {
Chris@102 151 long long t = lltrunc(boost::math::tools::log_max_value<T>());
Chris@102 152 max_bernoulli_root_functor fun(t);
Chris@102 153 boost::math::tools::equal_floor tol;
Chris@102 154 boost::uintmax_t max_iter = boost::math::policies::get_max_root_iterations<Policy>();
Chris@102 155 return static_cast<std::size_t>(boost::math::tools::toms748_solve(fun, sqrt(double(t)), double(t), tol, max_iter).first) / 2;
Chris@102 156 }
Chris@102 157
Chris@102 158 template <class T, class Policy>
Chris@102 159 inline std::size_t find_bernoulli_overflow_limit(const mpl::true_&)
Chris@102 160 {
Chris@102 161 return max_bernoulli_index<bernoulli_imp_variant<T>::value>::value;
Chris@102 162 }
Chris@102 163
Chris@102 164 template <class T, class Policy>
Chris@102 165 std::size_t b2n_overflow_limit()
Chris@102 166 {
Chris@102 167 // This routine is called at program startup if it's called at all:
Chris@102 168 // that guarantees safe initialization of the static variable.
Chris@102 169 typedef mpl::bool_<(bernoulli_imp_variant<T>::value >= 1) && (bernoulli_imp_variant<T>::value <= 3)> tag_type;
Chris@102 170 static const std::size_t lim = find_bernoulli_overflow_limit<T, Policy>(tag_type());
Chris@102 171 return lim;
Chris@102 172 }
Chris@102 173
Chris@102 174 //
Chris@102 175 // The tangent numbers grow larger much more rapidly than the Bernoulli numbers do....
Chris@102 176 // so to compute the Bernoulli numbers from the tangent numbers, we need to avoid spurious
Chris@102 177 // overflow in the calculation, we can do this by scaling all the tangent number by some scale factor:
Chris@102 178 //
Chris@102 179 template <class T>
Chris@102 180 inline typename enable_if_c<std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::radix == 2), T>::type tangent_scale_factor()
Chris@102 181 {
Chris@102 182 BOOST_MATH_STD_USING
Chris@102 183 return ldexp(T(1), std::numeric_limits<T>::min_exponent + 5);
Chris@102 184 }
Chris@102 185 template <class T>
Chris@102 186 inline typename disable_if_c<std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::radix == 2), T>::type tangent_scale_factor()
Chris@102 187 {
Chris@102 188 return tools::min_value<T>() * 16;
Chris@102 189 }
Chris@102 190 //
Chris@102 191 // Initializer: ensure all our constants are initialized prior to the first call of main:
Chris@102 192 //
Chris@102 193 template <class T, class Policy>
Chris@102 194 struct bernoulli_initializer
Chris@102 195 {
Chris@102 196 struct init
Chris@102 197 {
Chris@102 198 init()
Chris@102 199 {
Chris@102 200 //
Chris@102 201 // We call twice, once to initialize our static table, and once to
Chris@102 202 // initialize our dymanic table:
Chris@102 203 //
Chris@102 204 boost::math::bernoulli_b2n<T>(2, Policy());
Chris@102 205 try{
Chris@102 206 boost::math::bernoulli_b2n<T>(max_bernoulli_b2n<T>::value + 1, Policy());
Chris@102 207 } catch(const std::overflow_error&){}
Chris@102 208 boost::math::tangent_t2n<T>(2, Policy());
Chris@102 209 }
Chris@102 210 void force_instantiate()const{}
Chris@102 211 };
Chris@102 212 static const init initializer;
Chris@102 213 static void force_instantiate()
Chris@102 214 {
Chris@102 215 initializer.force_instantiate();
Chris@102 216 }
Chris@102 217 };
Chris@102 218
Chris@102 219 template <class T, class Policy>
Chris@102 220 const typename bernoulli_initializer<T, Policy>::init bernoulli_initializer<T, Policy>::initializer;
Chris@102 221
Chris@102 222 //
Chris@102 223 // We need something to act as a cache for our calculated Bernoulli numbers. In order to
Chris@102 224 // ensure both fast access and thread safety, we need a stable table which may be extended
Chris@102 225 // in size, but which never reallocates: that way values already calculated may be accessed
Chris@102 226 // concurrently with another thread extending the table with new values.
Chris@102 227 //
Chris@102 228 // Very very simple vector class that will never allocate more than once, we could use
Chris@102 229 // boost::container::static_vector here, but that allocates on the stack, which may well
Chris@102 230 // cause issues for the amount of memory we want in the extreme case...
Chris@102 231 //
Chris@102 232 template <class T>
Chris@102 233 struct fixed_vector : private std::allocator<T>
Chris@102 234 {
Chris@102 235 typedef unsigned size_type;
Chris@102 236 typedef T* iterator;
Chris@102 237 typedef const T* const_iterator;
Chris@102 238 fixed_vector() : m_used(0)
Chris@102 239 {
Chris@102 240 std::size_t overflow_limit = 5 + b2n_overflow_limit<T, policies::policy<> >();
Chris@102 241 m_capacity = static_cast<unsigned>((std::min)(overflow_limit, static_cast<std::size_t>(100000u)));
Chris@102 242 m_data = this->allocate(m_capacity);
Chris@102 243 }
Chris@102 244 ~fixed_vector()
Chris@102 245 {
Chris@102 246 for(unsigned i = 0; i < m_used; ++i)
Chris@102 247 this->destroy(&m_data[i]);
Chris@102 248 this->deallocate(m_data, m_capacity);
Chris@102 249 }
Chris@102 250 T& operator[](unsigned n) { BOOST_ASSERT(n < m_used); return m_data[n]; }
Chris@102 251 const T& operator[](unsigned n)const { BOOST_ASSERT(n < m_used); return m_data[n]; }
Chris@102 252 unsigned size()const { return m_used; }
Chris@102 253 unsigned size() { return m_used; }
Chris@102 254 void resize(unsigned n, const T& val)
Chris@102 255 {
Chris@102 256 if(n > m_capacity)
Chris@102 257 throw std::runtime_error("Exhausted storage for Bernoulli numbers.");
Chris@102 258 for(unsigned i = m_used; i < n; ++i)
Chris@102 259 new (m_data + i) T(val);
Chris@102 260 m_used = n;
Chris@102 261 }
Chris@102 262 void resize(unsigned n) { resize(n, T()); }
Chris@102 263 T* begin() { return m_data; }
Chris@102 264 T* end() { return m_data + m_used; }
Chris@102 265 T* begin()const { return m_data; }
Chris@102 266 T* end()const { return m_data + m_used; }
Chris@102 267 unsigned capacity()const { return m_capacity; }
Chris@102 268 private:
Chris@102 269 T* m_data;
Chris@102 270 unsigned m_used, m_capacity;
Chris@102 271 };
Chris@102 272
Chris@102 273 template <class T, class Policy>
Chris@102 274 class bernoulli_numbers_cache
Chris@102 275 {
Chris@102 276 public:
Chris@102 277 bernoulli_numbers_cache() : m_overflow_limit((std::numeric_limits<std::size_t>::max)())
Chris@102 278 #if defined(BOOST_HAS_THREADS) && !defined(BOOST_MATH_NO_ATOMIC_INT)
Chris@102 279 , m_counter(0)
Chris@102 280 #endif
Chris@102 281 {}
Chris@102 282
Chris@102 283 typedef fixed_vector<T> container_type;
Chris@102 284
Chris@102 285 void tangent(std::size_t m)
Chris@102 286 {
Chris@102 287 static const std::size_t min_overflow_index = b2n_overflow_limit<T, Policy>() - 1;
Chris@102 288 tn.resize(static_cast<typename container_type::size_type>(m), T(0U));
Chris@102 289
Chris@102 290 BOOST_MATH_INSTRUMENT_VARIABLE(min_overflow_index);
Chris@102 291
Chris@102 292 std::size_t prev_size = m_intermediates.size();
Chris@102 293 m_intermediates.resize(m, T(0U));
Chris@102 294
Chris@102 295 if(prev_size == 0)
Chris@102 296 {
Chris@102 297 m_intermediates[1] = tangent_scale_factor<T>() /*T(1U)*/;
Chris@102 298 tn[0U] = T(0U);
Chris@102 299 tn[1U] = tangent_scale_factor<T>()/* T(1U)*/;
Chris@102 300 BOOST_MATH_INSTRUMENT_VARIABLE(tn[0]);
Chris@102 301 BOOST_MATH_INSTRUMENT_VARIABLE(tn[1]);
Chris@102 302 }
Chris@102 303
Chris@102 304 for(std::size_t i = std::max<size_t>(2, prev_size); i < m; i++)
Chris@102 305 {
Chris@102 306 bool overflow_check = false;
Chris@102 307 if(i >= min_overflow_index && (boost::math::tools::max_value<T>() / (i-1) < m_intermediates[1]) )
Chris@102 308 {
Chris@102 309 std::fill(tn.begin() + i, tn.end(), boost::math::tools::max_value<T>());
Chris@102 310 break;
Chris@102 311 }
Chris@102 312 m_intermediates[1] = m_intermediates[1] * (i-1);
Chris@102 313 for(std::size_t j = 2; j <= i; j++)
Chris@102 314 {
Chris@102 315 overflow_check =
Chris@102 316 (i >= min_overflow_index) && (
Chris@102 317 (boost::math::tools::max_value<T>() / (i - j) < m_intermediates[j])
Chris@102 318 || (boost::math::tools::max_value<T>() / (i - j + 2) < m_intermediates[j-1])
Chris@102 319 || (boost::math::tools::max_value<T>() - m_intermediates[j] * (i - j) < m_intermediates[j-1] * (i - j + 2))
Chris@102 320 || ((boost::math::isinf)(m_intermediates[j]))
Chris@102 321 );
Chris@102 322
Chris@102 323 if(overflow_check)
Chris@102 324 {
Chris@102 325 std::fill(tn.begin() + i, tn.end(), boost::math::tools::max_value<T>());
Chris@102 326 break;
Chris@102 327 }
Chris@102 328 m_intermediates[j] = m_intermediates[j] * (i - j) + m_intermediates[j-1] * (i - j + 2);
Chris@102 329 }
Chris@102 330 if(overflow_check)
Chris@102 331 break; // already filled the tn...
Chris@102 332 tn[static_cast<typename container_type::size_type>(i)] = m_intermediates[i];
Chris@102 333 BOOST_MATH_INSTRUMENT_VARIABLE(i);
Chris@102 334 BOOST_MATH_INSTRUMENT_VARIABLE(tn[static_cast<typename container_type::size_type>(i)]);
Chris@102 335 }
Chris@102 336 }
Chris@102 337
Chris@102 338 void tangent_numbers_series(const std::size_t m)
Chris@102 339 {
Chris@102 340 BOOST_MATH_STD_USING
Chris@102 341 static const std::size_t min_overflow_index = b2n_overflow_limit<T, Policy>() - 1;
Chris@102 342
Chris@102 343 typename container_type::size_type old_size = bn.size();
Chris@102 344
Chris@102 345 tangent(m);
Chris@102 346 bn.resize(static_cast<typename container_type::size_type>(m));
Chris@102 347
Chris@102 348 if(!old_size)
Chris@102 349 {
Chris@102 350 bn[0] = 1;
Chris@102 351 old_size = 1;
Chris@102 352 }
Chris@102 353
Chris@102 354 T power_two(ldexp(T(1), static_cast<int>(2 * old_size)));
Chris@102 355
Chris@102 356 for(std::size_t i = old_size; i < m; i++)
Chris@102 357 {
Chris@102 358 T b(static_cast<T>(i * 2));
Chris@102 359 //
Chris@102 360 // Not only do we need to take care to avoid spurious over/under flow in
Chris@102 361 // the calculation, but we also need to avoid overflow altogether in case
Chris@102 362 // we're calculating with a type where "bad things" happen in that case:
Chris@102 363 //
Chris@102 364 b = b / (power_two * tangent_scale_factor<T>());
Chris@102 365 b /= (power_two - 1);
Chris@102 366 bool overflow_check = (i >= min_overflow_index) && (tools::max_value<T>() / tn[static_cast<typename container_type::size_type>(i)] < b);
Chris@102 367 if(overflow_check)
Chris@102 368 {
Chris@102 369 m_overflow_limit = i;
Chris@102 370 while(i < m)
Chris@102 371 {
Chris@102 372 b = std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity() : tools::max_value<T>();
Chris@102 373 bn[static_cast<typename container_type::size_type>(i)] = ((i % 2U) ? b : T(-b));
Chris@102 374 ++i;
Chris@102 375 }
Chris@102 376 break;
Chris@102 377 }
Chris@102 378 else
Chris@102 379 {
Chris@102 380 b *= tn[static_cast<typename container_type::size_type>(i)];
Chris@102 381 }
Chris@102 382
Chris@102 383 power_two = ldexp(power_two, 2);
Chris@102 384
Chris@102 385 const bool b_neg = i % 2 == 0;
Chris@102 386
Chris@102 387 bn[static_cast<typename container_type::size_type>(i)] = ((!b_neg) ? b : T(-b));
Chris@102 388 }
Chris@102 389 }
Chris@102 390
Chris@102 391 template <class OutputIterator>
Chris@102 392 OutputIterator copy_bernoulli_numbers(OutputIterator out, std::size_t start, std::size_t n, const Policy& pol)
Chris@102 393 {
Chris@102 394 //
Chris@102 395 // There are basically 3 thread safety options:
Chris@102 396 //
Chris@102 397 // 1) There are no threads (BOOST_HAS_THREADS is not defined).
Chris@102 398 // 2) There are threads, but we do not have a true atomic integer type,
Chris@102 399 // in this case we just use a mutex to guard against race conditions.
Chris@102 400 // 3) There are threads, and we have an atomic integer: in this case we can
Chris@102 401 // use the double-checked locking pattern to avoid thread synchronisation
Chris@102 402 // when accessing values already in the cache.
Chris@102 403 //
Chris@102 404 // First off handle the common case for overflow and/or asymptotic expansion:
Chris@102 405 //
Chris@102 406 if(start + n > bn.capacity())
Chris@102 407 {
Chris@102 408 if(start < bn.capacity())
Chris@102 409 {
Chris@102 410 out = copy_bernoulli_numbers(out, start, bn.capacity() - start, pol);
Chris@102 411 n -= bn.capacity() - start;
Chris@102 412 start = static_cast<std::size_t>(bn.capacity());
Chris@102 413 }
Chris@102 414 if(start < b2n_overflow_limit<T, Policy>() + 2u)
Chris@102 415 {
Chris@102 416 for(; n; ++start, --n)
Chris@102 417 {
Chris@102 418 *out = b2n_asymptotic<T, Policy>(static_cast<typename container_type::size_type>(start * 2U));
Chris@102 419 ++out;
Chris@102 420 }
Chris@102 421 }
Chris@102 422 for(; n; ++start, --n)
Chris@102 423 {
Chris@102 424 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(start), pol);
Chris@102 425 ++out;
Chris@102 426 }
Chris@102 427 return out;
Chris@102 428 }
Chris@102 429 #if !defined(BOOST_HAS_THREADS)
Chris@102 430 //
Chris@102 431 // Single threaded code, very simple:
Chris@102 432 //
Chris@102 433 if(start + n >= bn.size())
Chris@102 434 {
Chris@102 435 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()));
Chris@102 436 tangent_numbers_series(new_size);
Chris@102 437 }
Chris@102 438
Chris@102 439 for(std::size_t i = (std::max)(max_bernoulli_b2n<T>::value + 1, start); i < start + n; ++i)
Chris@102 440 {
Chris@102 441 *out = (i >= m_overflow_limit) ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol) : bn[i];
Chris@102 442 ++out;
Chris@102 443 }
Chris@102 444 #elif defined(BOOST_MATH_NO_ATOMIC_INT)
Chris@102 445 //
Chris@102 446 // We need to grab a mutex every time we get here, for both readers and writers:
Chris@102 447 //
Chris@102 448 boost::detail::lightweight_mutex::scoped_lock l(m_mutex);
Chris@102 449 if(start + n >= bn.size())
Chris@102 450 {
Chris@102 451 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()));
Chris@102 452 tangent_numbers_series(new_size);
Chris@102 453 }
Chris@102 454
Chris@102 455 for(std::size_t i = (std::max)(max_bernoulli_b2n<T>::value + 1, start); i < start + n; ++i)
Chris@102 456 {
Chris@102 457 *out = (i >= m_overflow_limit) ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol) : bn[i];
Chris@102 458 ++out;
Chris@102 459 }
Chris@102 460
Chris@102 461 #else
Chris@102 462 //
Chris@102 463 // Double-checked locking pattern, lets us access cached already cached values
Chris@102 464 // without locking:
Chris@102 465 //
Chris@102 466 // Get the counter and see if we need to calculate more constants:
Chris@102 467 //
Chris@102 468 if(static_cast<std::size_t>(m_counter.load(BOOST_MATH_ATOMIC_NS::memory_order_consume)) < start + n)
Chris@102 469 {
Chris@102 470 boost::detail::lightweight_mutex::scoped_lock l(m_mutex);
Chris@102 471
Chris@102 472 if(static_cast<std::size_t>(m_counter.load(BOOST_MATH_ATOMIC_NS::memory_order_consume)) < start + n)
Chris@102 473 {
Chris@102 474 if(start + n >= bn.size())
Chris@102 475 {
Chris@102 476 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()));
Chris@102 477 tangent_numbers_series(new_size);
Chris@102 478 }
Chris@102 479 m_counter.store(static_cast<atomic_integer_type>(bn.size()), BOOST_MATH_ATOMIC_NS::memory_order_release);
Chris@102 480 }
Chris@102 481 }
Chris@102 482
Chris@102 483 for(std::size_t i = (std::max)(static_cast<std::size_t>(max_bernoulli_b2n<T>::value + 1), start); i < start + n; ++i)
Chris@102 484 {
Chris@102 485 *out = (i >= m_overflow_limit) ? policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol) : bn[static_cast<typename container_type::size_type>(i)];
Chris@102 486 ++out;
Chris@102 487 }
Chris@102 488
Chris@102 489 #endif
Chris@102 490 return out;
Chris@102 491 }
Chris@102 492
Chris@102 493 template <class OutputIterator>
Chris@102 494 OutputIterator copy_tangent_numbers(OutputIterator out, std::size_t start, std::size_t n, const Policy& pol)
Chris@102 495 {
Chris@102 496 //
Chris@102 497 // There are basically 3 thread safety options:
Chris@102 498 //
Chris@102 499 // 1) There are no threads (BOOST_HAS_THREADS is not defined).
Chris@102 500 // 2) There are threads, but we do not have a true atomic integer type,
Chris@102 501 // in this case we just use a mutex to guard against race conditions.
Chris@102 502 // 3) There are threads, and we have an atomic integer: in this case we can
Chris@102 503 // use the double-checked locking pattern to avoid thread synchronisation
Chris@102 504 // when accessing values already in the cache.
Chris@102 505 //
Chris@102 506 //
Chris@102 507 // First off handle the common case for overflow and/or asymptotic expansion:
Chris@102 508 //
Chris@102 509 if(start + n > bn.capacity())
Chris@102 510 {
Chris@102 511 if(start < bn.capacity())
Chris@102 512 {
Chris@102 513 out = copy_tangent_numbers(out, start, bn.capacity() - start, pol);
Chris@102 514 n -= bn.capacity() - start;
Chris@102 515 start = static_cast<std::size_t>(bn.capacity());
Chris@102 516 }
Chris@102 517 if(start < b2n_overflow_limit<T, Policy>() + 2u)
Chris@102 518 {
Chris@102 519 for(; n; ++start, --n)
Chris@102 520 {
Chris@102 521 *out = t2n_asymptotic<T, Policy>(static_cast<typename container_type::size_type>(start));
Chris@102 522 ++out;
Chris@102 523 }
Chris@102 524 }
Chris@102 525 for(; n; ++start, --n)
Chris@102 526 {
Chris@102 527 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(start), pol);
Chris@102 528 ++out;
Chris@102 529 }
Chris@102 530 return out;
Chris@102 531 }
Chris@102 532 #if !defined(BOOST_HAS_THREADS)
Chris@102 533 //
Chris@102 534 // Single threaded code, very simple:
Chris@102 535 //
Chris@102 536 if(start + n >= bn.size())
Chris@102 537 {
Chris@102 538 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()));
Chris@102 539 tangent_numbers_series(new_size);
Chris@102 540 }
Chris@102 541
Chris@102 542 for(std::size_t i = start; i < start + n; ++i)
Chris@102 543 {
Chris@102 544 if(i >= m_overflow_limit)
Chris@102 545 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 546 else
Chris@102 547 {
Chris@102 548 if(tools::max_value<T>() * tangent_scale_factor<T>() < tn[static_cast<typename container_type::size_type>(i)])
Chris@102 549 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 550 else
Chris@102 551 *out = tn[static_cast<typename container_type::size_type>(i)] / tangent_scale_factor<T>();
Chris@102 552 }
Chris@102 553 ++out;
Chris@102 554 }
Chris@102 555 #elif defined(BOOST_MATH_NO_ATOMIC_INT)
Chris@102 556 //
Chris@102 557 // We need to grab a mutex every time we get here, for both readers and writers:
Chris@102 558 //
Chris@102 559 boost::detail::lightweight_mutex::scoped_lock l(m_mutex);
Chris@102 560 if(start + n >= bn.size())
Chris@102 561 {
Chris@102 562 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()));
Chris@102 563 tangent_numbers_series(new_size);
Chris@102 564 }
Chris@102 565
Chris@102 566 for(std::size_t i = start; i < start + n; ++i)
Chris@102 567 {
Chris@102 568 if(i >= m_overflow_limit)
Chris@102 569 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 570 else
Chris@102 571 {
Chris@102 572 if(tools::max_value<T>() * tangent_scale_factor<T>() < tn[static_cast<typename container_type::size_type>(i)])
Chris@102 573 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 574 else
Chris@102 575 *out = tn[static_cast<typename container_type::size_type>(i)] / tangent_scale_factor<T>();
Chris@102 576 }
Chris@102 577 ++out;
Chris@102 578 }
Chris@102 579
Chris@102 580 #else
Chris@102 581 //
Chris@102 582 // Double-checked locking pattern, lets us access cached already cached values
Chris@102 583 // without locking:
Chris@102 584 //
Chris@102 585 // Get the counter and see if we need to calculate more constants:
Chris@102 586 //
Chris@102 587 if(static_cast<std::size_t>(m_counter.load(BOOST_MATH_ATOMIC_NS::memory_order_consume)) < start + n)
Chris@102 588 {
Chris@102 589 boost::detail::lightweight_mutex::scoped_lock l(m_mutex);
Chris@102 590
Chris@102 591 if(static_cast<std::size_t>(m_counter.load(BOOST_MATH_ATOMIC_NS::memory_order_consume)) < start + n)
Chris@102 592 {
Chris@102 593 if(start + n >= bn.size())
Chris@102 594 {
Chris@102 595 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()));
Chris@102 596 tangent_numbers_series(new_size);
Chris@102 597 }
Chris@102 598 m_counter.store(static_cast<atomic_integer_type>(bn.size()), BOOST_MATH_ATOMIC_NS::memory_order_release);
Chris@102 599 }
Chris@102 600 }
Chris@102 601
Chris@102 602 for(std::size_t i = start; i < start + n; ++i)
Chris@102 603 {
Chris@102 604 if(i >= m_overflow_limit)
Chris@102 605 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 606 else
Chris@102 607 {
Chris@102 608 if(tools::max_value<T>() * tangent_scale_factor<T>() < tn[static_cast<typename container_type::size_type>(i)])
Chris@102 609 *out = policies::raise_overflow_error<T>("boost::math::bernoulli_b2n<%1%>(std::size_t)", 0, T(i), pol);
Chris@102 610 else
Chris@102 611 *out = tn[static_cast<typename container_type::size_type>(i)] / tangent_scale_factor<T>();
Chris@102 612 }
Chris@102 613 ++out;
Chris@102 614 }
Chris@102 615
Chris@102 616 #endif
Chris@102 617 return out;
Chris@102 618 }
Chris@102 619
Chris@102 620 private:
Chris@102 621 //
Chris@102 622 // The caches for Bernoulli and tangent numbers, once allocated,
Chris@102 623 // these must NEVER EVER reallocate as it breaks our thread
Chris@102 624 // safety guarentees:
Chris@102 625 //
Chris@102 626 fixed_vector<T> bn, tn;
Chris@102 627 std::vector<T> m_intermediates;
Chris@102 628 // The value at which we know overflow has already occured for the Bn:
Chris@102 629 std::size_t m_overflow_limit;
Chris@102 630 #if !defined(BOOST_HAS_THREADS)
Chris@102 631 #elif defined(BOOST_MATH_NO_ATOMIC_INT)
Chris@102 632 boost::detail::lightweight_mutex m_mutex;
Chris@102 633 #else
Chris@102 634 boost::detail::lightweight_mutex m_mutex;
Chris@102 635 atomic_counter_type m_counter;
Chris@102 636 #endif
Chris@102 637 };
Chris@102 638
Chris@102 639 template <class T, class Policy>
Chris@102 640 inline bernoulli_numbers_cache<T, Policy>& get_bernoulli_numbers_cache()
Chris@102 641 {
Chris@102 642 //
Chris@102 643 // Force this function to be called at program startup so all the static variables
Chris@102 644 // get initailzed then (thread safety).
Chris@102 645 //
Chris@102 646 bernoulli_initializer<T, Policy>::force_instantiate();
Chris@102 647 static bernoulli_numbers_cache<T, Policy> data;
Chris@102 648 return data;
Chris@102 649 }
Chris@102 650
Chris@102 651 }}}
Chris@102 652
Chris@102 653 #endif // BOOST_MATH_BERNOULLI_DETAIL_HPP