107 changes: 107 additions & 0 deletions include/boost/random/traits.hpp
@@ -0,0 +1,107 @@
/* boost random/traits.hpp header file
*
* Copyright John Maddock 2015
* Distributed under 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)
*
* See http://www.boost.org for most recent version including documentation.
*
* These traits classes serve two purposes: they are designed to mostly
* work out of the box for multiprecision types (ie number types that are
* C++ class types and not integers or floats from type-traits point of view),
* they are also a potential point of specialization for user-defined
* number types.
*
* $Id$
*/

#ifndef BOOST_RANDOM_TRAITS_HPP
#define BOOST_RANDOM_TRAITS_HPP

#include <boost/type_traits/is_signed.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/mpl/bool.hpp>
#include <limits>

namespace boost {
namespace random {
namespace traits {
// \cond show_private
template <class T, bool intrinsic>
struct make_unsigned_imp
{
typedef typename boost::make_unsigned<T>::type type;
};
template <class T>
struct make_unsigned_imp<T, false>
{
BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_specialized);
BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_signed == false);
BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_integer == true);
typedef T type;
};
// \endcond
/** \brief Converts the argument type T to an unsigned type.
*
* This trait has a single member `type` which is the unsigned type corresponding to T.
* Note that
* if T is signed, then member `type` *should define a type with one more bit precision than T*. For built-in
* types this trait defaults to `boost::make_unsigned<T>::type`. For user defined types it simply asserts that
* the argument type T is an unsigned integer (using std::numeric_limits).
* User defined specializations may be provided for other cases.
*/
template <class T>
struct make_unsigned
// \cond show_private
: public make_unsigned_imp < T, boost::is_integral<T>::value >
// \endcond
{};
// \cond show_private
template <class T, bool intrinsic>
struct make_unsigned_or_unbounded_imp
{
typedef typename boost::make_unsigned<T>::type type;
};
template <class T>
struct make_unsigned_or_unbounded_imp<T, false>
{
BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_specialized);
BOOST_STATIC_ASSERT((std::numeric_limits<T>::is_signed == false) || (std::numeric_limits<T>::is_bounded == false));
BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_integer == true);
typedef T type;
};
// \endcond
/** \brief Converts the argument type T to either an unsigned type or an unbounded integer type.
*
* This trait has a single member `type` which is either the unsigned type corresponding to T or an unbounded
* integer type. This trait is used to generate types suitable for the calculation of a range: as a result
* if T is signed, then member `type` *should define a type with one more bit precision than T*. For built-in
* types this trait defaults to `boost::make_unsigned<T>::type`. For user defined types it simply asserts that
* the argument type T is either an unbounded integer, or an unsigned one (using std::numeric_limits).
* User defined specializations may be provided for other cases.
*/
template <class T>
struct make_unsigned_or_unbounded
// \cond show_private
: public make_unsigned_or_unbounded_imp < T, boost::is_integral<T>::value >
// \endcond
{};
/** \brief Traits class that indicates whether type T is an integer
*/
template <class T>
struct is_integral
: public mpl::bool_<boost::is_integral<T>::value || (std::numeric_limits<T>::is_integer)>
{};
/** \brief Traits class that indicates whether type T is a signed integer
*/
template <class T> struct is_signed
: public mpl::bool_ < boost::is_signed<T>::value || (std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed)>
{};

}
}
}

#endif
50 changes: 34 additions & 16 deletions include/boost/random/uniform_int_distribution.hpp
Expand Up @@ -28,9 +28,11 @@
#include <boost/random/detail/operators.hpp>
#include <boost/random/detail/uniform_int_float.hpp>
#include <boost/random/detail/signed_unsigned_tools.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/random/traits.hpp>
#include <boost/mpl/bool.hpp>
#ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
#include <boost/mpl/if.hpp>
#endif

namespace boost {
namespace random {
Expand All @@ -50,10 +52,10 @@ T generate_uniform_int(
boost::mpl::true_ /** is_integral<Engine::result_type> */)
{
typedef T result_type;
typedef typename make_unsigned<T>::type range_type;
typedef typename boost::random::traits::make_unsigned_or_unbounded<T>::type range_type;
typedef typename Engine::result_type base_result;
// ranges are always unsigned
typedef typename make_unsigned<base_result>::type base_unsigned;
// ranges are always unsigned or unbounded
typedef typename boost::random::traits::make_unsigned_or_unbounded<base_result>::type base_unsigned;
const range_type range = random::detail::subtract<result_type>()(max_value, min_value);
const base_result bmin = (eng.min)();
const base_unsigned brange =
Expand Down Expand Up @@ -112,7 +114,7 @@ T generate_uniform_int(
// mult+mult*brange by (2), (3) (4)
// Therefore result+(eng()-bmin)*mult <
// mult*(brange+1) by (4)
result += static_cast<range_type>(random::detail::subtract<base_result>()(eng(), bmin) * mult);
result += static_cast<range_type>(static_cast<range_type>(random::detail::subtract<base_result>()(eng(), bmin)) * mult);

// equivalent to (mult * (brange+1)) == range+1, but avoids overflow.
if(mult * range_type(brange) == range - mult + 1) {
Expand Down Expand Up @@ -166,7 +168,7 @@ T generate_uniform_int(
static_cast<range_type>(0),
static_cast<range_type>(range/mult),
boost::mpl::true_());
if((std::numeric_limits<range_type>::max)() / mult < result_increment) {
if(std::numeric_limits<range_type>::is_bounded && ((std::numeric_limits<range_type>::max)() / mult < result_increment)) {
// The multiplcation would overflow. Reject immediately.
continue;
}
Expand All @@ -184,27 +186,43 @@ T generate_uniform_int(
return random::detail::add<range_type, result_type>()(result, min_value);
}
} else { // brange > range
base_unsigned bucket_size;
#ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
typedef typename mpl::if_c<
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;
#else
typedef base_unsigned mixed_range_type;
#endif

mixed_range_type bucket_size;
// it's safe to add 1 to range, as long as we cast it first,
// because we know that it is less than brange. However,
// we do need to be careful not to cause overflow by adding 1
// to brange.
// to brange. We use mixed_range_type throughout for mixed
// arithmetic between base_unsigned and range_type - in the case
// that range_type has more bits than base_unsigned it is always
// safe to use range_type for this albeit it may be more effient
// to use base_unsigned. The latter is a narrowing conversion though
// which may be disallowed if range_type is a multiprecision type
// and there are no explicit converison operators.

if(brange == (std::numeric_limits<base_unsigned>::max)()) {
bucket_size = brange / (static_cast<base_unsigned>(range)+1);
if(brange % (static_cast<base_unsigned>(range)+1) == static_cast<base_unsigned>(range)) {
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 = (brange+1) / (static_cast<base_unsigned>(range)+1);
bucket_size = static_cast<mixed_range_type>(brange + 1) / (static_cast<mixed_range_type>(range)+1);
}
for(;;) {
base_unsigned result =
mixed_range_type result =
random::detail::subtract<base_result>()(eng(), bmin);
result /= bucket_size;
// result and range are non-negative, and result is possibly larger
// than range, so the cast is safe
if(result <= static_cast<base_unsigned>(range))
return random::detail::add<base_unsigned, result_type>()(result, min_value);
if(result <= static_cast<mixed_range_type>(range))
return random::detail::add<mixed_range_type, result_type>()(result, min_value);
}
}
}
Expand All @@ -227,7 +245,7 @@ inline T generate_uniform_int(Engine& eng, T min_value, T max_value)
{
typedef typename Engine::result_type base_result;
return generate_uniform_int(eng, min_value, max_value,
boost::is_integral<base_result>());
boost::random::traits::is_integral<base_result>());
}

}
Expand Down
32 changes: 25 additions & 7 deletions include/boost/random/uniform_smallint.hpp
Expand Up @@ -30,6 +30,10 @@
#include <boost/detail/workaround.hpp>
#include <boost/mpl/bool.hpp>

#ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
#include <boost/mpl/if.hpp>
#endif

namespace boost {
namespace random {

Expand Down Expand Up @@ -194,7 +198,7 @@ class uniform_smallint
result_type operator()(Engine& eng) const
{
typedef typename Engine::result_type base_result;
return generate(eng, boost::is_integral<base_result>());
return generate(eng, boost::random::traits::is_integral<base_result>());
}

/** Returns a value uniformly distributed in the range [param.a(), param.b()]. */
Expand Down Expand Up @@ -238,28 +242,42 @@ class uniform_smallint
// equivalent to (eng() - eng.min()) % (_max - _min + 1) + _min,
// but guarantees no overflow.
typedef typename Engine::result_type base_result;
typedef typename boost::make_unsigned<base_result>::type base_unsigned;
typedef typename boost::make_unsigned<result_type>::type range_type;
typedef typename boost::random::traits::make_unsigned<base_result>::type base_unsigned;
typedef typename boost::random::traits::make_unsigned_or_unbounded<result_type>::type range_type;
#ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
typedef typename mpl::if_c<
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;
#else
typedef base_unsigned mixed_range_type;
#endif
range_type range = random::detail::subtract<result_type>()(_max, _min);
base_unsigned base_range =
random::detail::subtract<result_type>()((eng.max)(), (eng.min)());
random::detail::subtract<base_result>()((eng.max)(), (eng.min)());
base_unsigned val =
random::detail::subtract<base_result>()(eng(), (eng.min)());
if(range >= base_range) {
return boost::random::detail::add<range_type, result_type>()(
static_cast<range_type>(val), _min);
} else {
base_unsigned modulus = static_cast<base_unsigned>(range) + 1;
// This involves mixed arithmetic between the base generators range
// type, and the result_type's range type. mixed_range_type is
// normally the same as base_unsigned which is the most efficient
// option, but requires a narrowing explcit cast if result_type
// is a multiprecision type. If no such casts are available then use
// multiprecision arithmetic throughout instead.
mixed_range_type modulus = static_cast<mixed_range_type>(range)+1;
return boost::random::detail::add<range_type, result_type>()(
static_cast<range_type>(val % modulus), _min);
static_cast<mixed_range_type>(val) % modulus, _min);
}
}

template<class Engine>
result_type generate(Engine& eng, boost::mpl::false_) const
{
typedef typename Engine::result_type base_result;
typedef typename boost::make_unsigned<result_type>::type range_type;
typedef typename boost::random::traits::make_unsigned<result_type>::type range_type;
range_type range = random::detail::subtract<result_type>()(_max, _min);
base_result val = boost::uniform_01<base_result>()(eng);
// what is the worst that can possibly happen here?
Expand Down
223 changes: 223 additions & 0 deletions test/multiprecision_int_test.cpp
@@ -0,0 +1,223 @@

/* multiprecision_int_test.cpp
*
* Copyright John Maddock 2015
* Distributed under 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)
*
* $Id$
*
* Tests all integer related generators and distributions with multiprecision types:
* discard_block, independent_bits_engine, random_number_generator,
* xor_combine_engine, uniform_int_distribution, uniform_smallint.
*
* Not supported, but could be with more work (but probably not worth while):
* shuffle_order_engine, binomial_distribution, discrete_distribution, negative_binomial_distribution,
* poisson_distribution
*/

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include <boost/multiprecision/debug_adaptor.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/random/independent_bits.hpp>
#include <boost/random/discard_block.hpp>
#include <boost/random/xor_combine.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/random_number_generator.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_smallint.hpp>
#include <boost/random/discrete_distribution.hpp>
#include <sstream>

typedef boost::mpl::list <
boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::uint1024_t >,
boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::int1024_t >,
boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::checked_uint1024_t >,
boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::checked_int1024_t >,
boost::random::independent_bits_engine<boost::random::mt19937, 30000, boost::multiprecision::cpp_int >,
boost::random::discard_block_engine<boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::uint1024_t >, 20, 10>,
boost::random::discard_block_engine<boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::int1024_t >, 20, 10>,
boost::random::discard_block_engine<boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::checked_uint1024_t >, 20, 10>,
boost::random::discard_block_engine<boost::random::independent_bits_engine<boost::random::mt19937, 1024, boost::multiprecision::checked_int1024_t >, 20, 10>,
boost::random::discard_block_engine<boost::random::independent_bits_engine<boost::random::mt19937, 600, boost::multiprecision::cpp_int >, 20, 10>
> engines;

BOOST_AUTO_TEST_CASE_TEMPLATE(generator_test, engine_type, engines)
{
typedef typename engine_type::result_type test_type;

engine_type gen;
gen.seed();
test_type a = gen.min();
test_type b = gen.max();
BOOST_CHECK(a < b);
a = gen();
//
// This extracts 32-bit values for use in seeding other sequences,
// not really applicable here, and not functional for signed types anyway.
//gen.generate(&b, &b + 1);
gen.discard(20);

typename engine_type::base_type base(gen.base());

std::stringstream ss;
ss << gen;
engine_type gen2;
ss >> gen2;
BOOST_CHECK(gen == gen2);
gen2();
BOOST_CHECK(gen != gen2);
//
// construction and seeding:
//
engine_type gen3(0);
gen3.seed(2);
}

BOOST_AUTO_TEST_CASE(xor_combine_test)
{
//
// As above but with a few things missing which don't work - for example we have no
// way to drill down and get the seed-type of the underlying generator.
//
typedef boost::random::xor_combine_engine<boost::random::independent_bits_engine<boost::random::mt19937, 512, boost::multiprecision::uint1024_t >, 512, boost::random::independent_bits_engine<boost::random::mt19937, 512, boost::multiprecision::uint1024_t >, 10> engine_type;
typedef engine_type::result_type test_type;

engine_type gen;
gen.seed();
test_type a = gen.min();
test_type b = gen.max();
BOOST_CHECK(a < b);
a = gen();
#ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
gen.generate(&b, &b + 1);
#endif
gen.discard(20);

//typename engine_type::base_type base(gen.base());

std::stringstream ss;
ss << gen;
engine_type gen2;
ss >> gen2;
BOOST_CHECK(gen == gen2);
gen2();
BOOST_CHECK(gen != gen2);
//
// construction and seeding:
//
//engine_type gen3(0);
//gen3.seed(2);
}

typedef boost::mpl::list <
boost::random::random_number_generator<boost::random::mt19937, boost::multiprecision::cpp_int>,
boost::random::random_number_generator<boost::random::mt19937, boost::multiprecision::uint1024_t>,
boost::random::random_number_generator<boost::random::mt19937, boost::multiprecision::checked_uint1024_t>
> generators;


BOOST_AUTO_TEST_CASE_TEMPLATE(random_number_generator, generator_type, generators)
{
typedef typename generator_type::result_type result_type;
typedef typename generator_type::base_type base_type;

result_type lim = 1;
lim <<= 500;

base_type base;
generator_type gen(base);

for(unsigned i = 0; i < 100; ++i)
BOOST_CHECK(gen(lim) < lim);
}

typedef boost::mpl::list <
boost::random::uniform_int_distribution<boost::multiprecision::cpp_int>,
boost::random::uniform_int_distribution<boost::multiprecision::uint1024_t>,
boost::random::uniform_int_distribution<boost::multiprecision::checked_uint1024_t>,
boost::random::uniform_smallint<boost::multiprecision::cpp_int>,
boost::random::uniform_smallint<boost::multiprecision::uint1024_t>,
boost::random::uniform_smallint<boost::multiprecision::checked_uint1024_t>
> uniform_distributions;


BOOST_AUTO_TEST_CASE_TEMPLATE(distributions, distribution_type, uniform_distributions)
{
typedef typename distribution_type::result_type result_type;

result_type a = 20;
result_type b = 1;
b <<= 1000;

distribution_type d(a, b);
boost::random::mt19937 gen;

BOOST_CHECK_EQUAL(d.a(), a);
BOOST_CHECK_EQUAL(d.b(), b);
BOOST_CHECK_EQUAL((d.min)(), a);
BOOST_CHECK_EQUAL((d.max)(), b);

for(unsigned i = 0; i < 200; ++i)
{
result_type r = d(gen);
BOOST_CHECK(r <= b);
BOOST_CHECK(r >= a);
}

std::stringstream ss;
ss << d;
distribution_type d2;
ss >> d2;
BOOST_CHECK(d == d2);

boost::random::independent_bits_engine<boost::random::mt19937, std::numeric_limits<boost::multiprecision::uint1024_t>::digits, boost::multiprecision::uint1024_t > big_random;
for(unsigned i = 0; i < 200; ++i)
{
result_type r = d(big_random);
BOOST_CHECK(r <= b);
BOOST_CHECK(r >= a);
}
}

#ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS

typedef boost::mpl::list <
boost::random::discrete_distribution < boost::multiprecision::cpp_int, double>,
boost::random::discrete_distribution <unsigned int, boost::multiprecision::cpp_bin_float_100>
> other_distributions;


BOOST_AUTO_TEST_CASE_TEMPLATE(discrete_distributions, distribution_type, other_distributions)
{
typedef typename distribution_type::result_type result_type;
typedef typename distribution_type::input_type input_type;

input_type a[] = { 20, 30, 40, 50 };

distribution_type d(a, a + 4);
boost::random::mt19937 gen;

for(unsigned i = 0; i < 200; ++i)
{
result_type r = d(gen);
}

std::stringstream ss;
ss << std::setprecision(std::numeric_limits<input_type>::digits10 + 3) << d;
distribution_type d2;
ss >> d2;
BOOST_CHECK(d == d2);

boost::random::independent_bits_engine<boost::random::mt19937, std::numeric_limits<boost::multiprecision::uint1024_t>::digits, boost::multiprecision::uint1024_t > big_random;
for(unsigned i = 0; i < 200; ++i)
{
result_type r = d(big_random);
}
}

#endif