Flow 1.0.0
Flow project: Full implementation reference.
random.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22#include <boost/random.hpp>
23#include <limits>
24
25namespace flow::util
26{
27
28/**
29 * Base class for Rnd_gen_uniform_range and Rnd_gen_uniform_range_mt for various aliases and similar, so
30 * template arguments need not be involved.
31 */
33{
34public:
35 // Types.
36
37 /**
38 * The random generator engine; it is public for the reason explained in Usability section of the
39 * Rnd_gen_uniform_range doc header.
40 */
41 using Random_generator = boost::random::mt19937;
42};
43
44/**
45 * Simple, non-thread-safe uniform-range random number generator. This merely wraps the perfectly nice, advanced
46 * random number generation facilities for this common, simple use case.
47 *
48 * ### Thread safety ###
49 * The random number generation itself is not safe for concurrent invocation on the same `this`.
50 * Use Rnd_gen_uniform_range_mt if this is not acceptable.
51 *
52 * ### Usability, performance discussion ###
53 * This class is hopefully appealingly simple; but please note that if you are using N=2+ instances of it for N
54 * different uniform ranges (let alone other distributions), you could instead use boost.random directly: namely
55 * institute a single RNG and then generate values against various distributions from that shared RNG. In particular,
56 * this would require only one seed (arguably increases code simplicity in that respect); and might be faster
57 * since there is one raw RNG engine instead of N (as would be the case if one used N `Rnd_gen_uniform_range`s).
58 *
59 * If one chooses to do as suggested in the previous paragraph, Rnd_gen_uniform_range_base::Random_generator is a good
60 * choice for the RNG itself (but of course one is free to use another one, if this one's mathematical/perf properties
61 * are not acceptable).
62 *
63 * @tparam range_t
64 * Type of values generated; must be suitable for `boost::random::uniform_int_distribution`.
65 *
66 * @todo Rnd_gen_uniform_range and Rnd_gen_uniform_range_mt, on reflection, are very similar to
67 * `boost::random::random_number_generator`, and should be written to conform to it, so as to be usable in
68 * `std::random_shuffle()`, and also to gain its appealing extra features without losing any of the already-available
69 * simplicity. Note, however, that `std::random_shuffle()` is deprecated (and gone in C++17) in favor of
70 * `std::shuffle()`, which is available from C++11 on, and which works directly with a formal URNG
71 * such as Rnd_gen_uniform_range_base::Random_generator. So this to-do is less about that and more about gaining
72 * those other features while being suitable for `random_shuffle()`, which some code does still use out there.
73 *
74 * @internal
75 *
76 * Regarding the above to-do: Here is how `boost::random::random_number_generator` differs from this class and
77 * the implications:
78 * - It accepts a URNG as a template arg. We should do that too while defaulting to
79 * Rnd_gen_uniform_range_base::Random_generator.
80 * - It accepts a URNG reference as a ctor arg. We should do that too while also providing a ctor that causes us to
81 * create our own, like now; in the latter case we would pass the ctor `seed` arg into it. In the former case
82 * it should invoke `URNG.seed()` to set the seed while requiring that URNG is a
83 * boost.random PseudoRandomNumberGenerator (so that it can take `.seed()`). Do note that taking a seed this way
84 * is an added feature we have.
85 * - Its `operator()` accepts N and returns a value in [0, N - 1], whereas ours takes no args and returns
86 * something in the range [min, max] given in ctor. We must (in order to conform to what
87 * `std::random_shuffle()` expects) provide what they provide; but we should also feature two more `operator()`
88 * overloads:
89 * - No-args: Same as now.
90 * - Two args min, max: Return from [min, max].
91 * - It is interesting that its implementation as of Boost 1.76.0 (at least) creates a new equivalent of
92 * #m_seq in *each* `operator()` call. Is that slower than our impl? However, since no one is screaming about
93 * its slowness, maybe we should conclude the way they do it is fine; and it does have an advantage:
94 * one can implement the non-zero-arg `operator()` trivially. Perhaps we should do what they do for the
95 * non-zero-arg overloads; while doing what we do for the zero-arg one.
96 *
97 * Lastly we also provide Rnd_gen_uniform_range_mt, which is a feature they lack, and we can trivially extend it
98 * to cover all of the above suggested changes.
99 */
100template<typename range_t>
103 private boost::noncopyable
104{
105public:
106 // Constuctors/destructor.
107
108 /**
109 * Begins the random number generation sequence. Current time is used as the seed.
110 *
111 * @param min
112 * Generated numbers will be in range `[min, max]`.
113 * @param max
114 * See `min`.
115 */
116 Rnd_gen_uniform_range(range_t min = 0, range_t max = std::numeric_limits<range_t>::max());
117
118 /**
119 * Begins the random number generation sequence. Seed is to be given as an argument.
120 *
121 * @param min
122 * Generated numbers will be in range `[min, max]`.
123 * @param max
124 * See `min`.
125 * @param seed
126 * Seed. Supply the same value to yield the same sequence repeatedly.
127 */
128 Rnd_gen_uniform_range(uint32_t seed, range_t min, range_t max);
129
130 // Methods.
131
132 /**
133 * Returns the next pseudo-random number in `[min, max]` provided in constructor.
134 * Not thread-safe for a given `this`.
135 *
136 * @return Ditto.
137 */
138 range_t operator()();
139
140private:
141 // Data.
142
143 /// Random number generator engine.
145 /// Uniform-distribution sequence in `[min, max]` backed by #m_gen.
146 boost::random::uniform_int_distribution<range_t> m_seq;
147}; // class Rnd_gen_uniform_range
148
149/**
150 * Identical to Rnd_gen_uniform_range but safe for concurrent RNG given a single object.
151 *
152 * @tparam range_t
153 * Same as for Rnd_gen_uniform_range.
154 */
155template<typename range_t>
157 private Rnd_gen_uniform_range<range_t>
158{
159public:
160 // Constructors/destructor.
161
162 // Delegate the exact constructors from Rnd_gen_uniform_range.
164
165 // Methods.
166
167 /**
168 * Returns the next pseudo-random number in `[min, max]` provided in constructor.
169 * Thread-safe for a given `this`.
170 *
171 * @return Ditto.
172 */
173 range_t operator()();
174
175private:
176 // Data.
177
178 /// Mutex that protects against concurrency-based corruption.
180};
181
182// Template implementations.
183
184template<typename range_t>
185Rnd_gen_uniform_range<range_t>::Rnd_gen_uniform_range(uint32_t seed, range_t min, range_t max) :
186 m_gen(static_cast<typename decltype(m_gen)::result_type>(seed)),
187 m_seq(min, max)
188{
189 // Nothing else.
190}
191
192template<typename range_t>
194{
195 return m_seq(m_gen);
196}
197
198template<typename range_t>
200 Rnd_gen_uniform_range(static_cast<unsigned int>(flow::util::time_since_posix_epoch().count()), min, max)
201{
202 // Nothing else.
203}
204
205template<typename range_t>
207{
208 Lock_guard<decltype(m_mutex)> lock(m_mutex);
209 return static_cast<Rnd_gen_uniform_range<range_t>&>(*this)();
210}
211
212} // namespace flow::util
Base class for Rnd_gen_uniform_range and Rnd_gen_uniform_range_mt for various aliases and similar,...
Definition: random.hpp:33
boost::random::mt19937 Random_generator
The random generator engine; it is public for the reason explained in Usability section of the Rnd_ge...
Definition: random.hpp:41
Identical to Rnd_gen_uniform_range but safe for concurrent RNG given a single object.
Definition: random.hpp:158
flow::util::Mutex_non_recursive m_mutex
Mutex that protects against concurrency-based corruption.
Definition: random.hpp:179
range_t operator()()
Returns the next pseudo-random number in [min, max] provided in constructor.
Definition: random.hpp:206
Simple, non-thread-safe uniform-range random number generator.
Definition: random.hpp:104
Random_generator m_gen
Random number generator engine.
Definition: random.hpp:144
range_t operator()()
Returns the next pseudo-random number in [min, max] provided in constructor.
Definition: random.hpp:193
Rnd_gen_uniform_range(uint32_t seed, range_t min, range_t max)
Begins the random number generation sequence.
Definition: random.hpp:185
Rnd_gen_uniform_range(range_t min=0, range_t max=std::numeric_limits< range_t >::max())
Begins the random number generation sequence.
Definition: random.hpp:199
boost::random::uniform_int_distribution< range_t > m_seq
Uniform-distribution sequence in [min, max] backed by m_gen.
Definition: random.hpp:146
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
Definition: basic_blob.hpp:29
boost::unique_lock< Mutex > Lock_guard
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of tha...
Definition: util_fwd.hpp:265
boost::chrono::microseconds time_since_posix_epoch()
Get the current POSIX (Unix) time as a duration from the Epoch time point.
Definition: util.cpp:147
boost::mutex Mutex_non_recursive
Short-hand for non-reentrant, exclusive mutex. ("Reentrant" = one can lock an already-locked-in-that-...
Definition: util_fwd.hpp:215
Catch-all namespace for the Flow project: A collection of various production-quality modules written ...
Definition: async_fwd.hpp:75