]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | /* boost random/detail/gray_coded_qrng.hpp header file |
2 | * | |
3 | * Copyright Justinas Vygintas Daugmaudis 2010-2018 | |
4 | * Distributed under the Boost Software License, Version 1.0. (See | |
5 | * accompanying file LICENSE_1_0.txt or copy at | |
6 | * http://www.boost.org/LICENSE_1_0.txt) | |
7 | */ | |
8 | ||
9 | #ifndef BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP | |
10 | #define BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP | |
11 | ||
12 | #include <boost/random/detail/qrng_base.hpp> | |
13 | ||
1e59de90 TL |
14 | #include <boost/core/bit.hpp> // lsb |
15 | #include <boost/throw_exception.hpp> | |
16 | #include <stdexcept> | |
92f5a8d4 TL |
17 | |
18 | #include <functional> // bit_xor | |
1e59de90 | 19 | #include <algorithm> |
92f5a8d4 | 20 | |
20effc67 | 21 | #include <boost/type_traits/conditional.hpp> |
92f5a8d4 TL |
22 | |
23 | #include <boost/integer/integer_mask.hpp> | |
24 | ||
25 | //!\file | |
26 | //!Describes the gray-coded quasi-random number generator base class template. | |
27 | ||
28 | namespace boost { | |
29 | namespace random { | |
30 | ||
31 | namespace qrng_detail { | |
32 | ||
1e59de90 TL |
33 | template<class T> static int lsb( T x ) |
34 | { | |
35 | if( x == 0 ) | |
36 | { | |
37 | BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::lsb: argument is 0" ) ); | |
38 | } | |
39 | ||
40 | return boost::core::countr_zero( x ); | |
41 | } | |
42 | ||
43 | template<class T> static int msb( T x ) | |
44 | { | |
45 | if( x == 0 ) | |
46 | { | |
47 | BOOST_THROW_EXCEPTION( std::range_error( "qrng_detail::msb: argument is 0" ) ); | |
48 | } | |
49 | ||
50 | return std::numeric_limits<T>::digits - 1 - boost::core::countl_zero( x ); | |
51 | } | |
52 | ||
92f5a8d4 TL |
53 | template<typename LatticeT> |
54 | class gray_coded_qrng | |
55 | : public qrng_base< | |
56 | gray_coded_qrng<LatticeT> | |
57 | , LatticeT | |
58 | , typename LatticeT::value_type | |
59 | > | |
60 | { | |
61 | public: | |
62 | typedef typename LatticeT::value_type result_type; | |
63 | typedef result_type size_type; | |
64 | ||
65 | private: | |
66 | typedef gray_coded_qrng<LatticeT> self_t; | |
67 | typedef qrng_base<self_t, LatticeT, size_type> base_t; | |
68 | ||
69 | // The base needs to access modifying member f-ns, and we | |
70 | // don't want these functions to be available for the public use | |
71 | friend class qrng_base<self_t, LatticeT, size_type>; | |
72 | ||
73 | // Respect lattice bit_count here | |
74 | struct check_nothing { | |
75 | inline static void bit_pos(unsigned) {} | |
76 | inline static void code_size(size_type) {} | |
77 | }; | |
78 | struct check_bit_range { | |
79 | static void raise_bit_count() { | |
80 | boost::throw_exception( std::range_error("gray_coded_qrng: bit_count") ); | |
81 | } | |
82 | inline static void bit_pos(unsigned bit_pos) { | |
83 | if (bit_pos >= LatticeT::bit_count) | |
84 | raise_bit_count(); | |
85 | } | |
86 | inline static void code_size(size_type code) { | |
87 | if (code > (self_t::max)()) | |
88 | raise_bit_count(); | |
89 | } | |
90 | }; | |
91 | ||
92 | // We only want to check whether bit pos is outside the range if given bit_count | |
93 | // is narrower than the size_type, otherwise checks compile to nothing. | |
94 | BOOST_STATIC_ASSERT(LatticeT::bit_count <= std::numeric_limits<size_type>::digits); | |
95 | ||
20effc67 | 96 | typedef typename conditional< |
92f5a8d4 TL |
97 | ((LatticeT::bit_count) < std::numeric_limits<size_type>::digits) |
98 | , check_bit_range | |
99 | , check_nothing | |
100 | >::type check_bit_range_t; | |
101 | ||
102 | public: | |
103 | //!Returns: Tight lower bound on the set of values returned by operator(). | |
104 | //! | |
105 | //!Throws: nothing. | |
106 | static BOOST_CONSTEXPR result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () | |
107 | { return 0; } | |
108 | ||
109 | //!Returns: Tight upper bound on the set of values returned by operator(). | |
110 | //! | |
111 | //!Throws: nothing. | |
112 | static BOOST_CONSTEXPR result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () | |
113 | { return low_bits_mask_t<LatticeT::bit_count>::sig_bits; } | |
114 | ||
115 | explicit gray_coded_qrng(std::size_t dimension) | |
116 | : base_t(dimension) | |
117 | {} | |
118 | ||
119 | // default copy c-tor is fine | |
120 | ||
121 | // default assignment operator is fine | |
122 | ||
123 | void seed() | |
124 | { | |
125 | set_zero_state(); | |
126 | update_quasi(0); | |
127 | base_t::reset_seq(0); | |
128 | } | |
129 | ||
130 | void seed(const size_type init) | |
131 | { | |
132 | if (init != this->curr_seq()) | |
133 | { | |
134 | // We don't want negative seeds. | |
135 | check_seed_sign(init); | |
136 | ||
1e59de90 | 137 | size_type seq_code = init + 1; |
92f5a8d4 TL |
138 | if (BOOST_UNLIKELY(!(init < seq_code))) |
139 | boost::throw_exception( std::range_error("gray_coded_qrng: seed") ); | |
140 | ||
141 | seq_code ^= (seq_code >> 1); | |
142 | // Fail if we see that seq_code is outside bit range. | |
143 | // We do that before we even touch engine state. | |
144 | check_bit_range_t::code_size(seq_code); | |
145 | ||
146 | set_zero_state(); | |
147 | for (unsigned r = 0; seq_code != 0; ++r, seq_code >>= 1) | |
148 | { | |
149 | if (seq_code & static_cast<size_type>(1)) | |
150 | update_quasi(r); | |
151 | } | |
152 | } | |
153 | // Everything went well, set the new seq count | |
154 | base_t::reset_seq(init); | |
155 | } | |
156 | ||
157 | private: | |
1e59de90 | 158 | |
92f5a8d4 TL |
159 | void compute_seq(size_type seq) |
160 | { | |
161 | // Find the position of the least-significant zero in sequence count. | |
162 | // This is the bit that changes in the Gray-code representation as | |
163 | // the count is advanced. | |
164 | // Xor'ing with max() has the effect of flipping all the bits in seq, | |
165 | // except for the sign bit. | |
1e59de90 | 166 | unsigned r = qrng_detail::lsb(static_cast<size_type>(seq ^ (self_t::max)())); |
92f5a8d4 TL |
167 | check_bit_range_t::bit_pos(r); |
168 | update_quasi(r); | |
169 | } | |
170 | ||
171 | void update_quasi(unsigned r) | |
172 | { | |
173 | // Calculate the next state. | |
174 | std::transform(this->state_begin(), this->state_end(), | |
175 | this->lattice.iter_at(r * this->dimension()), this->state_begin(), | |
176 | std::bit_xor<result_type>()); | |
177 | } | |
178 | ||
179 | void set_zero_state() | |
180 | { | |
181 | std::fill(this->state_begin(), this->state_end(), result_type /*zero*/ ()); | |
182 | } | |
183 | }; | |
184 | ||
185 | } // namespace qrng_detail | |
186 | ||
187 | } // namespace random | |
188 | } // namespace boost | |
189 | ||
190 | #endif // BOOST_RANDOM_DETAIL_GRAY_CODED_QRNG_HPP |