213 lines
6.1 KiB
C++
213 lines
6.1 KiB
C++
///////////////////////////////////////////////////////////////
|
|
// Copyright Jens Maurer 2000-2021
|
|
// Copyright Steven Watanabe 2011-2021
|
|
// Copyright John Maddock 2015-2021
|
|
// Copyright Matt Borland 2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
//
|
|
// This is a C++11 compliant port of Boost.Random's implementation
|
|
// of uniform_int_distribution. See their comments for detailed
|
|
// descriptions
|
|
|
|
#ifndef BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
|
|
#define BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
|
|
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <boost/multiprecision/detail/standalone_config.hpp>
|
|
#include <boost/multiprecision/detail/assert.hpp>
|
|
#include <boost/multiprecision/traits/std_integer_traits.hpp>
|
|
|
|
namespace boost { namespace multiprecision {
|
|
|
|
namespace detail {
|
|
|
|
template <typename T, bool intrinsic>
|
|
struct make_unsigned_impl
|
|
{
|
|
using type = typename boost::multiprecision::detail::make_unsigned<T>::type;
|
|
};
|
|
|
|
template <typename T>
|
|
struct make_unsigned_impl<T, false>
|
|
{
|
|
using type = T;
|
|
};
|
|
|
|
template <typename T>
|
|
struct make_unsigned_mp
|
|
{
|
|
using type = typename make_unsigned_impl<T, boost::multiprecision::detail::is_integral<T>::value>::type;
|
|
};
|
|
|
|
template <typename Engine, typename T>
|
|
T generate_uniform_int (Engine& eng, T min_value, T max_value)
|
|
{
|
|
using range_type = typename boost::multiprecision::detail::make_unsigned_mp<T>::type;
|
|
using base_result = typename Engine::result_type;
|
|
using base_unsigned = typename boost::multiprecision::detail::make_unsigned_mp<base_result>::type;
|
|
|
|
const range_type range = max_value - min_value;
|
|
const base_result bmin = (eng.min)();
|
|
const base_unsigned brange = (eng.max)() - (eng.min)();
|
|
|
|
if(range == 0)
|
|
{
|
|
return min_value;
|
|
}
|
|
else if (brange < range)
|
|
{
|
|
for(;;)
|
|
{
|
|
range_type limit;
|
|
if(range == (std::numeric_limits<range_type>::max)())
|
|
{
|
|
limit = range / (range_type(brange) + 1);
|
|
if(range % (range_type(brange) + 1) == range_type(brange))
|
|
{
|
|
++limit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
limit = (range + 1) / (range_type(brange) + 1);
|
|
}
|
|
|
|
range_type result = 0;
|
|
range_type mult = 1;
|
|
|
|
while (mult <= limit)
|
|
{
|
|
result += static_cast<range_type>(static_cast<range_type>(eng() - bmin) * mult);
|
|
|
|
if(mult * range_type(brange) == range - mult + 1)
|
|
{
|
|
return(result);
|
|
}
|
|
|
|
mult *= range_type(brange)+range_type(1);
|
|
}
|
|
|
|
range_type result_increment = generate_uniform_int(eng, range_type(0), range_type(range/mult));
|
|
|
|
if(std::numeric_limits<range_type>::is_bounded && ((std::numeric_limits<range_type>::max)() / mult < result_increment))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result_increment *= mult;
|
|
result += result_increment;
|
|
|
|
if(result < result_increment)
|
|
{
|
|
continue;
|
|
}
|
|
if(result > range)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return result + min_value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using mixed_range_type =
|
|
typename std::conditional<std::numeric_limits<range_type>::is_specialized && std::numeric_limits<base_unsigned>::is_specialized &&
|
|
(std::numeric_limits<range_type>::digits >= std::numeric_limits<base_unsigned>::digits),
|
|
range_type, base_unsigned>::type;
|
|
|
|
mixed_range_type bucket_size;
|
|
|
|
if(brange == (std::numeric_limits<base_unsigned>::max)())
|
|
{
|
|
bucket_size = static_cast<mixed_range_type>(brange) / (static_cast<mixed_range_type>(range)+1);
|
|
if(static_cast<mixed_range_type>(brange) % (static_cast<mixed_range_type>(range)+1) == static_cast<mixed_range_type>(range))
|
|
{
|
|
++bucket_size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bucket_size = static_cast<mixed_range_type>(brange + 1) / (static_cast<mixed_range_type>(range)+1);
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
mixed_range_type result = eng() - bmin;
|
|
result /= bucket_size;
|
|
|
|
if(result <= static_cast<mixed_range_type>(range))
|
|
{
|
|
return result + min_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // Namespace detail
|
|
|
|
template <typename Integer = int>
|
|
class uniform_int_distribution
|
|
{
|
|
private:
|
|
Integer min_;
|
|
Integer max_;
|
|
|
|
public:
|
|
class param_type
|
|
{
|
|
private:
|
|
Integer min_;
|
|
Integer max_;
|
|
|
|
public:
|
|
explicit param_type(Integer min_val, Integer max_val) : min_ {min_val}, max_ {max_val}
|
|
{
|
|
BOOST_MP_ASSERT(min_ <= max_);
|
|
}
|
|
|
|
Integer a() const { return min_; }
|
|
Integer b() const { return max_; }
|
|
};
|
|
|
|
explicit uniform_int_distribution(Integer min_arg, Integer max_arg) : min_ {min_arg}, max_ {max_arg}
|
|
{
|
|
BOOST_MP_ASSERT(min_ <= max_);
|
|
}
|
|
|
|
explicit uniform_int_distribution(const param_type& param_arg) : min_ {param_arg.a()}, max_ {param_arg.b()} {}
|
|
|
|
Integer min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return min_; }
|
|
Integer max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return max_; }
|
|
|
|
Integer a() const { return min_; }
|
|
Integer b() const { return max_; }
|
|
|
|
param_type param() const { return param_type(min_, max_); }
|
|
|
|
void param(const param_type& param_arg)
|
|
{
|
|
min_ = param_arg.a();
|
|
max_ = param_arg.b();
|
|
}
|
|
|
|
template <typename Engine>
|
|
Integer operator() (Engine& eng) const
|
|
{
|
|
return detail::generate_uniform_int(eng, min_, max_);
|
|
}
|
|
|
|
template <typename Engine>
|
|
Integer operator() (Engine& eng, const param_type& param_arg) const
|
|
{
|
|
return detail::generate_uniform_int(eng, param_arg.a(), param_arg.b());
|
|
}
|
|
};
|
|
|
|
}} // Namespaces
|
|
|
|
#endif // BOOST_MP_UNIFORM_INT_DISTRIBUTION_HPP
|