1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
16 #define ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
17
18 #include <cstddef>
19 #include <cstdint>
20 #include <limits>
21 #include <type_traits>
22
23 #include "absl/base/config.h"
24 #include "absl/meta/type_traits.h"
25
26 namespace absl {
27 ABSL_NAMESPACE_BEGIN
28 namespace random_internal {
29 // Returns true if the input value is zero or a power of two. Useful for
30 // determining if the range of output values in a URBG
31 template <typename UIntType>
IsPowerOfTwoOrZero(UIntType n)32 constexpr bool IsPowerOfTwoOrZero(UIntType n) {
33 return (n == 0) || ((n & (n - 1)) == 0);
34 }
35
36 // Computes the length of the range of values producible by the URBG, or returns
37 // zero if that would encompass the entire range of representable values in
38 // URBG::result_type.
39 template <typename URBG>
RangeSize()40 constexpr typename URBG::result_type RangeSize() {
41 using result_type = typename URBG::result_type;
42 static_assert((URBG::max)() != (URBG::min)(), "URBG range cannot be 0.");
43 return ((URBG::max)() == (std::numeric_limits<result_type>::max)() &&
44 (URBG::min)() == std::numeric_limits<result_type>::lowest())
45 ? result_type{0}
46 : ((URBG::max)() - (URBG::min)() + result_type{1});
47 }
48
49 // Computes the floor of the log. (i.e., std::floor(std::log2(N));
50 template <typename UIntType>
IntegerLog2(UIntType n)51 constexpr UIntType IntegerLog2(UIntType n) {
52 return (n <= 1) ? 0 : 1 + IntegerLog2(n >> 1);
53 }
54
55 // Returns the number of bits of randomness returned through
56 // `PowerOfTwoVariate(urbg)`.
57 template <typename URBG>
NumBits()58 constexpr size_t NumBits() {
59 return RangeSize<URBG>() == 0
60 ? std::numeric_limits<typename URBG::result_type>::digits
61 : IntegerLog2(RangeSize<URBG>());
62 }
63
64 // Given a shift value `n`, constructs a mask with exactly the low `n` bits set.
65 // If `n == 0`, all bits are set.
66 template <typename UIntType>
MaskFromShift(size_t n)67 constexpr UIntType MaskFromShift(size_t n) {
68 return ((n % std::numeric_limits<UIntType>::digits) == 0)
69 ? ~UIntType{0}
70 : (UIntType{1} << n) - UIntType{1};
71 }
72
73 // Tags used to dispatch FastUniformBits::generate to the simple or more complex
74 // entropy extraction algorithm.
75 struct SimplifiedLoopTag {};
76 struct RejectionLoopTag {};
77
78 // FastUniformBits implements a fast path to acquire uniform independent bits
79 // from a type which conforms to the [rand.req.urbg] concept.
80 // Parameterized by:
81 // `UIntType`: the result (output) type
82 //
83 // The std::independent_bits_engine [rand.adapt.ibits] adaptor can be
84 // instantiated from an existing generator through a copy or a move. It does
85 // not, however, facilitate the production of pseudorandom bits from an un-owned
86 // generator that will outlive the std::independent_bits_engine instance.
87 template <typename UIntType = uint64_t>
88 class FastUniformBits {
89 public:
90 using result_type = UIntType;
91
result_type(min)92 static constexpr result_type(min)() { return 0; }
result_type(max)93 static constexpr result_type(max)() {
94 return (std::numeric_limits<result_type>::max)();
95 }
96
97 template <typename URBG>
98 result_type operator()(URBG& g); // NOLINT(runtime/references)
99
100 private:
101 static_assert(std::is_unsigned<UIntType>::value,
102 "Class-template FastUniformBits<> must be parameterized using "
103 "an unsigned type.");
104
105 // Generate() generates a random value, dispatched on whether
106 // the underlying URBG must use rejection sampling to generate a value,
107 // or whether a simplified loop will suffice.
108 template <typename URBG>
109 result_type Generate(URBG& g, // NOLINT(runtime/references)
110 SimplifiedLoopTag);
111
112 template <typename URBG>
113 result_type Generate(URBG& g, // NOLINT(runtime/references)
114 RejectionLoopTag);
115 };
116
117 template <typename UIntType>
118 template <typename URBG>
119 typename FastUniformBits<UIntType>::result_type
operator()120 FastUniformBits<UIntType>::operator()(URBG& g) { // NOLINT(runtime/references)
121 // kRangeMask is the mask used when sampling variates from the URBG when the
122 // width of the URBG range is not a power of 2.
123 // Y = (2 ^ kRange) - 1
124 static_assert((URBG::max)() > (URBG::min)(),
125 "URBG::max and URBG::min may not be equal.");
126
127 using tag = absl::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()),
128 SimplifiedLoopTag, RejectionLoopTag>;
129 return Generate(g, tag{});
130 }
131
132 template <typename UIntType>
133 template <typename URBG>
134 typename FastUniformBits<UIntType>::result_type
Generate(URBG & g,SimplifiedLoopTag)135 FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
136 SimplifiedLoopTag) {
137 // The simplified version of FastUniformBits works only on URBGs that have
138 // a range that is a power of 2. In this case we simply loop and shift without
139 // attempting to balance the bits across calls.
140 static_assert(IsPowerOfTwoOrZero(RangeSize<URBG>()),
141 "incorrect Generate tag for URBG instance");
142
143 static constexpr size_t kResultBits =
144 std::numeric_limits<result_type>::digits;
145 static constexpr size_t kUrbgBits = NumBits<URBG>();
146 static constexpr size_t kIters =
147 (kResultBits / kUrbgBits) + (kResultBits % kUrbgBits != 0);
148 static constexpr size_t kShift = (kIters == 1) ? 0 : kUrbgBits;
149 static constexpr auto kMin = (URBG::min)();
150
151 result_type r = static_cast<result_type>(g() - kMin);
152 for (size_t n = 1; n < kIters; ++n) {
153 r = (r << kShift) + static_cast<result_type>(g() - kMin);
154 }
155 return r;
156 }
157
158 template <typename UIntType>
159 template <typename URBG>
160 typename FastUniformBits<UIntType>::result_type
Generate(URBG & g,RejectionLoopTag)161 FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
162 RejectionLoopTag) {
163 static_assert(!IsPowerOfTwoOrZero(RangeSize<URBG>()),
164 "incorrect Generate tag for URBG instance");
165 using urbg_result_type = typename URBG::result_type;
166
167 // See [rand.adapt.ibits] for more details on the constants calculated below.
168 //
169 // It is preferable to use roughly the same number of bits from each generator
170 // call, however this is only possible when the number of bits provided by the
171 // URBG is a divisor of the number of bits in `result_type`. In all other
172 // cases, the number of bits used cannot always be the same, but it can be
173 // guaranteed to be off by at most 1. Thus we run two loops, one with a
174 // smaller bit-width size (`kSmallWidth`) and one with a larger width size
175 // (satisfying `kLargeWidth == kSmallWidth + 1`). The loops are run
176 // `kSmallIters` and `kLargeIters` times respectively such
177 // that
178 //
179 // `kResultBits == kSmallIters * kSmallBits
180 // + kLargeIters * kLargeBits`
181 //
182 // where `kResultBits` is the total number of bits in `result_type`.
183 //
184 static constexpr size_t kResultBits =
185 std::numeric_limits<result_type>::digits; // w
186 static constexpr urbg_result_type kUrbgRange = RangeSize<URBG>(); // R
187 static constexpr size_t kUrbgBits = NumBits<URBG>(); // m
188
189 // compute the initial estimate of the bits used.
190 // [rand.adapt.ibits] 2 (c)
191 static constexpr size_t kA = // ceil(w/m)
192 (kResultBits / kUrbgBits) + ((kResultBits % kUrbgBits) != 0); // n'
193
194 static constexpr size_t kABits = kResultBits / kA; // w0'
195 static constexpr urbg_result_type kARejection =
196 ((kUrbgRange >> kABits) << kABits); // y0'
197
198 // refine the selection to reduce the rejection frequency.
199 static constexpr size_t kTotalIters =
200 ((kUrbgRange - kARejection) <= (kARejection / kA)) ? kA : (kA + 1); // n
201
202 // [rand.adapt.ibits] 2 (b)
203 static constexpr size_t kSmallIters =
204 kTotalIters - (kResultBits % kTotalIters); // n0
205 static constexpr size_t kSmallBits = kResultBits / kTotalIters; // w0
206 static constexpr urbg_result_type kSmallRejection =
207 ((kUrbgRange >> kSmallBits) << kSmallBits); // y0
208
209 static constexpr size_t kLargeBits = kSmallBits + 1; // w0+1
210 static constexpr urbg_result_type kLargeRejection =
211 ((kUrbgRange >> kLargeBits) << kLargeBits); // y1
212
213 //
214 // Because `kLargeBits == kSmallBits + 1`, it follows that
215 //
216 // `kResultBits == kSmallIters * kSmallBits + kLargeIters`
217 //
218 // and therefore
219 //
220 // `kLargeIters == kTotalWidth % kSmallWidth`
221 //
222 // Intuitively, each iteration with the large width accounts for one unit
223 // of the remainder when `kTotalWidth` is divided by `kSmallWidth`. As
224 // mentioned above, if the URBG width is a divisor of `kTotalWidth`, then
225 // there would be no need for any large iterations (i.e., one loop would
226 // suffice), and indeed, in this case, `kLargeIters` would be zero.
227 static_assert(kResultBits == kSmallIters * kSmallBits +
228 (kTotalIters - kSmallIters) * kLargeBits,
229 "Error in looping constant calculations.");
230
231 // The small shift is essentially small bits, but due to the potential
232 // of generating a smaller result_type from a larger urbg type, the actual
233 // shift might be 0.
234 static constexpr size_t kSmallShift = kSmallBits % kResultBits;
235 static constexpr auto kSmallMask =
236 MaskFromShift<urbg_result_type>(kSmallShift);
237 static constexpr size_t kLargeShift = kLargeBits % kResultBits;
238 static constexpr auto kLargeMask =
239 MaskFromShift<urbg_result_type>(kLargeShift);
240
241 static constexpr auto kMin = (URBG::min)();
242
243 result_type s = 0;
244 for (size_t n = 0; n < kSmallIters; ++n) {
245 urbg_result_type v;
246 do {
247 v = g() - kMin;
248 } while (v >= kSmallRejection);
249
250 s = (s << kSmallShift) + static_cast<result_type>(v & kSmallMask);
251 }
252
253 for (size_t n = kSmallIters; n < kTotalIters; ++n) {
254 urbg_result_type v;
255 do {
256 v = g() - kMin;
257 } while (v >= kLargeRejection);
258
259 s = (s << kLargeShift) + static_cast<result_type>(v & kLargeMask);
260 }
261 return s;
262 }
263
264 } // namespace random_internal
265 ABSL_NAMESPACE_END
266 } // namespace absl
267
268 #endif // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
269