blob: 190e100ac80bb36c247c98a24af3650d1349ed89 [file] [log] [blame]
Jeff Thompson86b6d642013-10-17 15:01:56 -07001/* boost random/uniform_smallint.hpp header file
2 *
3 * Copyright Jens Maurer 2000-2001
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 * See http://www.boost.org for most recent version including documentation.
9 *
10 * $Id: uniform_smallint.hpp 71018 2011-04-05 21:27:52Z steven_watanabe $
11 *
12 * Revision history
13 * 2001-04-08 added min<max assertion (N. Becker)
14 * 2001-02-18 moved to individual header files
15 */
16
17#ifndef NDNBOOST_RANDOM_UNIFORM_SMALLINT_HPP
18#define NDNBOOST_RANDOM_UNIFORM_SMALLINT_HPP
19
20#include <istream>
21#include <iosfwd>
22#include <ndnboost/assert.hpp>
23#include <ndnboost/config.hpp>
24#include <ndnboost/limits.hpp>
25#include <ndnboost/type_traits/is_integral.hpp>
26#include <ndnboost/random/detail/config.hpp>
27#include <ndnboost/random/detail/operators.hpp>
28#include <ndnboost/random/detail/signed_unsigned_tools.hpp>
29#include <ndnboost/random/uniform_01.hpp>
30#include <ndnboost/detail/workaround.hpp>
31
32namespace ndnboost {
33namespace random {
34
35// uniform integer distribution on a small range [min, max]
36
37/**
38 * The distribution function uniform_smallint models a \random_distribution.
39 * On each invocation, it returns a random integer value uniformly distributed
40 * in the set of integer numbers {min, min+1, min+2, ..., max}. It assumes
41 * that the desired range (max-min+1) is small compared to the range of the
42 * underlying source of random numbers and thus makes no attempt to limit
43 * quantization errors.
44 *
45 * Let \f$r_{\mathtt{out}} = (\mbox{max}-\mbox{min}+1)\f$ the desired range of
46 * integer numbers, and
47 * let \f$r_{\mathtt{base}}\f$ be the range of the underlying source of random
48 * numbers. Then, for the uniform distribution, the theoretical probability
49 * for any number i in the range \f$r_{\mathtt{out}}\f$ will be
50 * \f$\displaystyle p_{\mathtt{out}}(i) = \frac{1}{r_{\mathtt{out}}}\f$.
51 * Likewise, assume a uniform distribution on \f$r_{\mathtt{base}}\f$ for
52 * the underlying source of random numbers, i.e.
53 * \f$\displaystyle p_{\mathtt{base}}(i) = \frac{1}{r_{\mathtt{base}}}\f$.
54 * Let \f$p_{\mathtt{out\_s}}(i)\f$ denote the random
55 * distribution generated by @c uniform_smallint. Then the sum over all
56 * i in \f$r_{\mathtt{out}}\f$ of
57 * \f$\displaystyle
58 * \left(\frac{p_{\mathtt{out\_s}}(i)}{p_{\mathtt{out}}(i)} - 1\right)^2\f$
59 * shall not exceed
60 * \f$\displaystyle \frac{r_{\mathtt{out}}}{r_{\mathtt{base}}^2}
61 * (r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
62 * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$.
63 *
64 * The template parameter IntType shall denote an integer-like value type.
65 *
66 * @xmlnote
67 * The property above is the square sum of the relative differences
68 * in probabilities between the desired uniform distribution
69 * \f$p_{\mathtt{out}}(i)\f$ and the generated distribution
70 * \f$p_{\mathtt{out\_s}}(i)\f$.
71 * The property can be fulfilled with the calculation
72 * \f$(\mbox{base\_rng} \mbox{ mod } r_{\mathtt{out}})\f$, as follows:
73 * Let \f$r = r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}}\f$.
74 * The base distribution on \f$r_{\mathtt{base}}\f$ is folded onto the
75 * range \f$r_{\mathtt{out}}\f$. The numbers i < r have assigned
76 * \f$\displaystyle
77 * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor+1\f$
78 * numbers of the base distribution, the rest has only \f$\displaystyle
79 * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor\f$.
80 * Therefore,
81 * \f$\displaystyle p_{\mathtt{out\_s}}(i) =
82 * \left(\left\lfloor\frac{r_{\mathtt{base}}}
83 * {r_{\mathtt{out}}}\right\rfloor+1\right) /
84 * r_{\mathtt{base}}\f$ for i < r and \f$\displaystyle p_{\mathtt{out\_s}}(i) =
85 * \left\lfloor\frac{r_{\mathtt{base}}}
86 * {r_{\mathtt{out}}}\right\rfloor/r_{\mathtt{base}}\f$ otherwise.
87 * Substituting this in the
88 * above sum formula leads to the desired result.
89 * @endxmlnote
90 *
91 * Note: The upper bound for
92 * \f$(r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
93 * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$ is
94 * \f$\displaystyle \frac{r_{\mathtt{out}}^2}{4}\f$. Regarding the upper bound
95 * for the square sum of the relative quantization error of
96 * \f$\displaystyle \frac{r_\mathtt{out}^3}{4r_{\mathtt{base}}^2}\f$, it
97 * seems wise to either choose \f$r_{\mathtt{base}}\f$ so that
98 * \f$r_{\mathtt{base}} > 10r_{\mathtt{out}}^2\f$ or ensure that
99 * \f$r_{\mathtt{base}}\f$ is
100 * divisible by \f$r_{\mathtt{out}}\f$.
101 */
102template<class IntType = int>
103class uniform_smallint
104{
105public:
106 typedef IntType input_type;
107 typedef IntType result_type;
108
109 class param_type
110 {
111 public:
112
113 typedef uniform_smallint distribution_type;
114
115 /** constructs the parameters of a @c uniform_smallint distribution. */
116 param_type(IntType min_arg = 0, IntType max_arg = 9)
117 : _min(min_arg), _max(max_arg)
118 {
119 NDNBOOST_ASSERT(_min <= _max);
120 }
121
122 /** Returns the minimum value. */
123 IntType a() const { return _min; }
124 /** Returns the maximum value. */
125 IntType b() const { return _max; }
126
127
128 /** Writes the parameters to a @c std::ostream. */
129 NDNBOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
130 {
131 os << parm._min << " " << parm._max;
132 return os;
133 }
134
135 /** Reads the parameters from a @c std::istream. */
136 NDNBOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
137 {
138 is >> parm._min >> std::ws >> parm._max;
139 return is;
140 }
141
142 /** Returns true if the two sets of parameters are equal. */
143 NDNBOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
144 { return lhs._min == rhs._min && lhs._max == rhs._max; }
145
146 /** Returns true if the two sets of parameters are different. */
147 NDNBOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
148
149 private:
150 IntType _min;
151 IntType _max;
152 };
153
154 /**
155 * Constructs a @c uniform_smallint. @c min and @c max are the
156 * lower and upper bounds of the output range, respectively.
157 */
158 explicit uniform_smallint(IntType min_arg = 0, IntType max_arg = 9)
159 : _min(min_arg), _max(max_arg) {}
160
161 /**
162 * Constructs a @c uniform_smallint from its parameters.
163 */
164 explicit uniform_smallint(const param_type& parm)
165 : _min(parm.a()), _max(parm.b()) {}
166
167 /** Returns the minimum value of the distribution. */
168 result_type a() const { return _min; }
169 /** Returns the maximum value of the distribution. */
170 result_type b() const { return _max; }
171 /** Returns the minimum value of the distribution. */
172 result_type min NDNBOOST_PREVENT_MACRO_SUBSTITUTION () const { return _min; }
173 /** Returns the maximum value of the distribution. */
174 result_type max NDNBOOST_PREVENT_MACRO_SUBSTITUTION () const { return _max; }
175
176 /** Returns the parameters of the distribution. */
177 param_type param() const { return param_type(_min, _max); }
178 /** Sets the parameters of the distribution. */
179 void param(const param_type& parm)
180 {
181 _min = parm.a();
182 _max = parm.b();
183 }
184
185 /**
186 * Effects: Subsequent uses of the distribution do not depend
187 * on values produced by any engine prior to invoking reset.
188 */
189 void reset() { }
190
191 /** Returns a value uniformly distributed in the range [min(), max()]. */
192 template<class Engine>
193 result_type operator()(Engine& eng) const
194 {
195 typedef typename Engine::result_type base_result;
196 return generate(eng, ndnboost::is_integral<base_result>());
197 }
198
199 /** Returns a value uniformly distributed in the range [param.a(), param.b()]. */
200 template<class Engine>
201 result_type operator()(Engine& eng, const param_type& parm) const
202 { return uniform_smallint(parm)(eng); }
203
204 /** Writes the distribution to a @c std::ostream. */
205 NDNBOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, uniform_smallint, ud)
206 {
207 os << ud._min << " " << ud._max;
208 return os;
209 }
210
211 /** Reads the distribution from a @c std::istream. */
212 NDNBOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, uniform_smallint, ud)
213 {
214 is >> ud._min >> std::ws >> ud._max;
215 return is;
216 }
217
218 /**
219 * Returns true if the two distributions will produce identical
220 * sequences of values given equal generators.
221 */
222 NDNBOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(uniform_smallint, lhs, rhs)
223 { return lhs._min == rhs._min && lhs._max == rhs._max; }
224
225 /**
226 * Returns true if the two distributions may produce different
227 * sequences of values given equal generators.
228 */
229 NDNBOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(uniform_smallint)
230
231private:
232
233 // \cond show_private
234 template<class Engine>
235 result_type generate(Engine& eng, ndnboost::mpl::true_) const
236 {
237 // equivalent to (eng() - eng.min()) % (_max - _min + 1) + _min,
238 // but guarantees no overflow.
239 typedef typename Engine::result_type base_result;
240 typedef typename ndnboost::make_unsigned<base_result>::type base_unsigned;
241 typedef typename ndnboost::make_unsigned<result_type>::type range_type;
242 range_type range = random::detail::subtract<result_type>()(_max, _min);
243 base_unsigned base_range =
244 random::detail::subtract<result_type>()((eng.max)(), (eng.min)());
245 base_unsigned val =
246 random::detail::subtract<base_result>()(eng(), (eng.min)());
247 if(range >= base_range) {
248 return ndnboost::random::detail::add<range_type, result_type>()(
249 static_cast<range_type>(val), _min);
250 } else {
251 base_unsigned modulus = static_cast<base_unsigned>(range) + 1;
252 return ndnboost::random::detail::add<range_type, result_type>()(
253 static_cast<range_type>(val % modulus), _min);
254 }
255 }
256
257 template<class Engine>
258 result_type generate(Engine& eng, ndnboost::mpl::false_) const
259 {
260 typedef typename Engine::result_type base_result;
261 typedef typename ndnboost::make_unsigned<result_type>::type range_type;
262 range_type range = random::detail::subtract<result_type>()(_max, _min);
263 base_result val = ndnboost::uniform_01<base_result>()(eng);
264 // what is the worst that can possibly happen here?
265 // base_result may not be able to represent all the values in [0, range]
266 // exactly. If this happens, it will cause round off error and we
267 // won't be able to produce all the values in the range. We don't
268 // care about this because the user has already told us not to by
269 // using uniform_smallint. However, we do need to be careful
270 // to clamp the result, or floating point rounding can produce
271 // an out of range result.
272 range_type offset = static_cast<range_type>(val * (static_cast<base_result>(range) + 1));
273 if(offset > range) return _max;
274 return ndnboost::random::detail::add<range_type, result_type>()(offset , _min);
275 }
276 // \endcond
277
278 result_type _min;
279 result_type _max;
280};
281
282} // namespace random
283
284using random::uniform_smallint;
285
286} // namespace ndnboost
287
288#endif // NDNBOOST_RANDOM_UNIFORM_SMALLINT_HPP