#ifndef BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP
#define BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP
#include <boost/random/detail/qrng_base.hpp>
#include <boost/core/bit.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <functional>
#include <algorithm>
#include <boost/type_traits/conditional.hpp>
#include <boost/integer/integer_mask.hpp>
namespace boost {
namespace random {
namespace qrng_detail {
template<class T> static int lsb( T x )
{
if( x == 0 )
{
BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::lsb: argument is 0" ) );
}
return boost::core::countr_zero( x );
}
template<class T> static int msb( T x )
{
if( x == 0 )
{
BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::msb: argument is 0" ) );
}
return std::numeric_limits<T>::digits - 1 - boost::core::countl_zero( x );
}
template<typename LatticeT>
class gray_coded_qrng
: public qrng_base<
gray_coded_qrng<LatticeT>
, LatticeT
, typename LatticeT::value_type
>
{
public:
typedef typename LatticeT::value_type result_type;
typedef result_type size_type;
private:
typedef gray_coded_qrng<LatticeT> self_t;
typedef qrng_base<self_t, LatticeT, size_type> base_t;
friend class qrng_base<self_t, LatticeT, size_type>;
struct check_nothing {
inline static void bit_pos(unsigned) {}
inline static void code_size(size_type) {}
};
struct check_bit_range {
static void raise_bit_count() {
boost::throw_exception( std::range_error("gray_coded_qrng: bit_count") );
}
inline static void bit_pos(unsigned bit_pos) {
if (bit_pos >= LatticeT::bit_count)
raise_bit_count();
}
inline static void code_size(size_type code) {
if (code > (self_t::max)())
raise_bit_count();
}
};
BOOST_STATIC_ASSERT(LatticeT::bit_count <= std::numeric_limits<size_type>::digits);
typedef typename conditional<
((LatticeT::bit_count) < std::numeric_limits<size_type>::digits)
, check_bit_range
, check_nothing
>::type check_bit_range_t;
public:
static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION ()
{ return 0; }
static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION ()
{ return low_bits_mask_t<LatticeT::bit_count>::sig_bits; }
explicit gray_coded_qrng(std::size_t dimension)
: base_t(dimension)
{}
void seed()
{
set_zero_state();
update_quasi(0);
base_t::reset_seq(0);
}
void seed(const size_type init)
{
if (init != this->curr_seq())
{
check_seed_sign(init);
size_type seq_code = init + 1;
if (BOOST_UNLIKELY(!(init < seq_code)))
boost::throw_exception( std::range_error("gray_coded_qrng: seed") );
seq_code ^= (seq_code >> 1);
check_bit_range_t::code_size(seq_code);
set_zero_state();
for (unsigned r = 0; seq_code != 0; ++r, seq_code >>= 1)
{
if (seq_code & static_cast<size_type>(1))
update_quasi(r);
}
}
base_t::reset_seq(init);
}
private:
void compute_seq(size_type seq)
{
unsigned r = qrng_detail::lsb(static_cast<size_type>(seq ^ (self_t::max)()));
check_bit_range_t::bit_pos(r);
update_quasi(r);
}
void update_quasi(unsigned r)
{
std::transform(this->state_begin(), this->state_end(),
this->lattice.iter_at(r * this->dimension()), this->state_begin(),
std::bit_xor<result_type>());
}
void set_zero_state()
{
std::fill(this->state_begin(), this->state_end(), result_type ());
}
};
}
} }
#endif