-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
Copy pathconstexpr_random.h
438 lines (384 loc) · 15.1 KB
/
constexpr_random.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef TEST_SUPPORT_CONSTEXPR_RANDOM_H
#define TEST_SUPPORT_CONSTEXPR_RANDOM_H
#include <climits>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>
#include <type_traits>
#include <utility>
#include "test_macros.h"
namespace support {
namespace detail {
template <class>
struct is_valid_integer_type_for_random : std::false_type {};
template <>
struct is_valid_integer_type_for_random<std::int8_t> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<short> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<int> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<long> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<long long> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<std::uint8_t> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<unsigned short> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<unsigned int> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<unsigned long> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<unsigned long long> : std::true_type {};
#ifndef TEST_HAS_NO_INT128
template <>
struct is_valid_integer_type_for_random<__int128_t> : std::true_type {};
template <>
struct is_valid_integer_type_for_random<__uint128_t> : std::true_type {};
#endif // TEST_HAS_NO_INT128
template <class, class = void>
struct is_valid_urng : std::false_type {};
template <class Gen>
struct is_valid_urng<
Gen,
typename std::enable_if<std::is_unsigned<typename Gen::result_type>::value &&
std::is_same<decltype(std::declval<Gen&>()()), typename Gen::result_type>::value>::type>
: std::true_type {};
template <class UIntType, UIntType N, std::size_t R>
struct meta_log2_imp;
template <unsigned long long N, std::size_t R>
struct meta_log2_imp<unsigned long long, N, R> {
static const std::size_t value =
N & ((unsigned long long)(1) << R) ? R : meta_log2_imp<unsigned long long, N, R - 1>::value;
};
template <unsigned long long N>
struct meta_log2_imp<unsigned long long, N, 0> {
static const std::size_t value = 0;
};
template <size_t R>
struct meta_log2_imp<unsigned long long, 0, R> {
static const std::size_t value = R + 1;
};
#ifndef TEST_HAS_NO_INT128
template <__uint128_t N, std::size_t R>
struct meta_log2_imp<__uint128_t, N, R> {
static const size_t value =
(N >> 64) ? (64 + meta_log2_imp<unsigned long long, (N >> 64), 63>::value)
: meta_log2_imp<unsigned long long, N, 63>::value;
};
#endif // TEST_HAS_NO_INT128
template <class UIntType, UIntType N>
struct meta_log2 {
static const size_t value = meta_log2_imp<
#ifndef TEST_HAS_NO_INT128
typename std::conditional<sizeof(UIntType) <= sizeof(unsigned long long), unsigned long long, __uint128_t>::type,
#else
unsigned long long,
#endif // TEST_HAS_NO_INT128
N,
sizeof(UIntType) * CHAR_BIT - 1 >::value;
};
#ifdef TEST_COMPILER_MSVC
template <int Width, class T, typename std::enable_if<(Width <= 1), int>::type = 0>
TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT {
return static_cast<int>(~n) & 1;
}
template <int Width, class T, typename std::enable_if<(Width > 1), int>::type = 0>
TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT {
return n >= (static_cast<T>(1) << (Width / 2))
? detail::countl_zero_div_conq<Width / 2>(n >> (Width / 2))
: detail::countl_zero_div_conq<Width / 2>(n) + Width / 2;
}
#endif
template <class T, typename std::enable_if<std::is_same<T, unsigned int>::value, int>::type = 0>
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
#ifdef TEST_COMPILER_MSVC
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
#else
return __builtin_clz(n);
#endif
}
template <class T, typename std::enable_if<std::is_same<T, unsigned long>::value, int>::type = 0>
TEST_CONSTEXPR_CXX14 int countl_zero(T n) TEST_NOEXCEPT {
#ifdef TEST_COMPILER_MSVC
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
#else
return __builtin_clzl(n);
#endif
}
template <class T, typename std::enable_if<std::is_same<T, unsigned long long>::value, int>::type = 0>
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
#ifdef TEST_COMPILER_MSVC
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
#else
return __builtin_clzll(n);
#endif
}
#ifndef TEST_HAS_NO_INT128
template <class T, typename std::enable_if<std::is_same<T, __uint128_t>::value, int>::type = 0>
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
return n > std::numeric_limits<std::uint64_t>::max()
? detail::countl_zero(static_cast<std::uint64_t>(n >> 64))
: detail::countl_zero(static_cast<std::uint64_t>(n)) + 64;
}
#endif // TEST_HAS_NO_INT128
template <class T,
typename std::enable_if<std::is_same<T, unsigned char>::value || std::is_same<T, unsigned short>::value,
int>::type = 0>
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
return detail::countl_zero(static_cast<unsigned int>(n)) -
(std::numeric_limits<unsigned int>::digits - std::numeric_limits<T>::digits);
}
template <class Engine, class UIntType>
class independent_bits_engine {
public:
typedef UIntType result_type;
private:
typedef typename Engine::result_type engine_result_type;
typedef
typename std::conditional<sizeof(engine_result_type) <= sizeof(result_type), result_type, engine_result_type>::
type working_result_type;
Engine& eng_;
std::size_t width_;
std::size_t wid0_;
std::size_t round_count_all_;
std::size_t round_count_regular_;
working_result_type y0_;
working_result_type y1_;
engine_result_type mask0_;
engine_result_type mask1_;
#if TEST_STD_VER >= 11
static constexpr working_result_type rp = Engine::max() - Engine::min() + working_result_type(1);
#else
static const working_result_type rp = Engine::max_value - Engine::min_value + working_result_type(1);
#endif
static TEST_CONSTEXPR const std::size_t rp_log2 = meta_log2<working_result_type, rp>::value;
static TEST_CONSTEXPR const std::size_t w_digits = std::numeric_limits<working_result_type>::digits;
static TEST_CONSTEXPR const std::size_t e_digits = std::numeric_limits<engine_result_type>::digits;
public:
// constructors and seeding functions
TEST_CONSTEXPR_CXX14 independent_bits_engine(Engine& eng, std::size_t width)
: eng_(eng),
width_(width),
wid0_(),
round_count_all_(),
round_count_regular_(),
y0_(),
y1_(),
mask0_(),
mask1_() {
round_count_all_ = width_ / rp_log2 + (width_ % rp_log2 != 0);
wid0_ = width_ / round_count_all_;
if TEST_CONSTEXPR_CXX17 (rp == 0) {
y0_ = rp;
} else {
if (wid0_ < w_digits)
y0_ = (rp >> wid0_) << wid0_;
else
y0_ = 0;
}
if (rp - y0_ > y0_ / round_count_all_) {
++round_count_all_;
wid0_ = width_ / round_count_all_;
if (wid0_ < w_digits)
y0_ = (rp >> wid0_) << wid0_;
else
y0_ = 0;
}
round_count_regular_ = round_count_all_ - width_ % round_count_all_;
if (wid0_ < w_digits - 1)
y1_ = (rp >> (wid0_ + 1)) << (wid0_ + 1);
else
y1_ = 0;
mask0_ = wid0_ > 0 ? static_cast<engine_result_type>(engine_result_type(~0) >> (e_digits - wid0_))
: engine_result_type(0);
mask1_ = wid0_ < e_digits - 1 ? static_cast<engine_result_type>(engine_result_type(~0) >> (e_digits - (wid0_ + 1)))
: engine_result_type(~0);
}
// generating functions
TEST_CONSTEXPR_CXX14 result_type operator()() { return generate(std::integral_constant<bool, (rp != 0)>()); }
private:
TEST_CONSTEXPR_CXX14 result_type generate(std::false_type) { return static_cast<result_type>(eng_() & mask0_); }
TEST_CONSTEXPR_CXX14 result_type generate(std::true_type) {
const std::size_t r_digits = std::numeric_limits<result_type>::digits;
result_type result = 0;
for (std::size_t k = 0; k < round_count_regular_; ++k) {
engine_result_type eng_result = 0;
do {
eng_result = static_cast<engine_result_type>(eng_() - Engine::min());
} while (eng_result >= y0_);
if (wid0_ < r_digits)
result <<= wid0_;
else
result = 0;
result += eng_result & mask0_;
}
for (std::size_t k = round_count_regular_; k < round_count_all_; ++k) {
engine_result_type eng_result = 0;
do {
eng_result = static_cast<engine_result_type>(eng_() - Engine::min());
} while (eng_result >= y1_);
if (wid0_ < r_digits - 1)
result <<= wid0_ + 1;
else
result = 0;
result += eng_result & mask1_;
}
return result;
}
};
} // namespace detail
template <class IntType = int>
class uniform_int_distribution {
static_assert(detail::is_valid_integer_type_for_random<IntType>::value, "IntType must be a supported integer type");
public:
// types
typedef IntType result_type;
class param_type {
result_type a_;
result_type b_;
public:
typedef uniform_int_distribution distribution_type;
#if TEST_STD_VER >= 11
constexpr param_type() : param_type(0) {}
#else
param_type() : a_(0), b_(std::numeric_limits<result_type>::max()) {}
#endif
TEST_CONSTEXPR explicit param_type(result_type ax, result_type bx = std::numeric_limits<result_type>::max())
: a_(ax), b_(bx) {}
TEST_CONSTEXPR result_type a() const { return a_; }
TEST_CONSTEXPR result_type b() const { return b_; }
#if TEST_STD_VER >= 20
friend bool operator==(const param_type&, const param_type&) = default;
#else
TEST_CONSTEXPR friend bool operator==(const param_type& lhs, const param_type& rhs) {
return lhs.a_ == rhs.a_ && lhs.b_ == rhs.b_;
}
TEST_CONSTEXPR friend bool operator!=(const param_type& lhs, const param_type& rhs) { return !(lhs == rhs); }
#endif
};
private:
param_type param_;
public:
// constructors and reset functions
#if TEST_STD_VER >= 11
uniform_int_distribution() = default;
#else
uniform_int_distribution() {}
#endif
TEST_CONSTEXPR explicit uniform_int_distribution(result_type ax,
result_type bx = std::numeric_limits<result_type>::max())
: param_(ax, bx) {}
TEST_CONSTEXPR explicit uniform_int_distribution(const param_type& param) : param_(param) {}
TEST_CONSTEXPR_CXX14 void reset() {}
// generating functions
template <class URNG>
TEST_CONSTEXPR_CXX14 result_type operator()(URNG& gen) {
return (*this)(gen, param_);
}
#if TEST_HAS_FEATURE(no_sanitize) && !defined(TEST_COMPILER_GCC)
# define TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK __attribute__((__no_sanitize__("unsigned-integer-overflow")))
#else
# define TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
#endif
template <class URNG>
TEST_CONSTEXPR_CXX14 result_type operator()(URNG& gen, const param_type& param)
TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK {
static_assert(detail::is_valid_urng<URNG>::value, "invalid uniform random bit generator used");
typedef typename std::conditional<sizeof(result_type) <= sizeof(std::uint32_t),
std::uint32_t,
typename std::make_unsigned<result_type>::type>::type UIntType;
const UIntType rp = UIntType(param.b()) - UIntType(param.a()) + UIntType(1);
if (rp == 1)
return param.a();
const std::size_t ur_digits = std::numeric_limits<UIntType>::digits;
typedef detail::independent_bits_engine<URNG, UIntType> Eng;
if (rp == 0)
return static_cast<result_type>(Eng(gen, ur_digits)());
std::size_t width = ur_digits - detail::countl_zero(rp) - 1;
if ((rp & (std::numeric_limits<UIntType>::max() >> (ur_digits - width))) != 0)
++width;
Eng eng(gen, width);
UIntType eng_result = 0;
do {
eng_result = eng();
} while (eng_result >= rp);
return static_cast<result_type>(eng_result + param.a());
}
#undef TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
// property functions
TEST_CONSTEXPR result_type a() const { return param_.a(); }
TEST_CONSTEXPR result_type b() const { return param_.b(); }
TEST_CONSTEXPR param_type param() const { return param_; }
TEST_CONSTEXPR_CXX14 void param(const param_type& param) { param_ = param; }
TEST_CONSTEXPR result_type min() const { return a(); }
TEST_CONSTEXPR result_type max() const { return b(); }
#if TEST_STD_VER >= 20
friend bool operator==(const uniform_int_distribution&, const uniform_int_distribution&) = default;
#else
TEST_CONSTEXPR friend bool operator==(const uniform_int_distribution& lhs, const uniform_int_distribution& rhs) {
return lhs.param_ == rhs.param_;
}
TEST_CONSTEXPR friend bool operator!=(const uniform_int_distribution& lhs, const uniform_int_distribution& rhs) {
return !(lhs == rhs);
}
#endif
};
class simple_random_generator { // A linear congruential generator, using the constants used by MS UCRT.
private:
std::uint32_t status_;
public:
typedef std::uint16_t result_type;
static TEST_CONSTEXPR result_type min() TEST_NOEXCEPT { return 0; }
static TEST_CONSTEXPR result_type max() TEST_NOEXCEPT { return static_cast<result_type>(-1); }
#if TEST_STD_VER < 11
static const result_type min_value = 0;
static const result_type max_value = static_cast<result_type>(-1);
#endif
static TEST_CONSTEXPR const result_type default_seed = 19937;
#if TEST_STD_VER >= 11
constexpr simple_random_generator() noexcept : simple_random_generator(default_seed) {}
#else
simple_random_generator() throw() : status_(default_seed) {}
#endif
TEST_CONSTEXPR explicit simple_random_generator(std::uint16_t s) TEST_NOEXCEPT : status_(s) {}
TEST_CONSTEXPR_CXX14 result_type operator()() TEST_NOEXCEPT {
status_ = status_ * 214013u + 2531011u;
return static_cast<result_type>(status_ >> 16);
}
};
template <class RandomAccessIterator, class UniformRandomNumberGenerator>
TEST_CONSTEXPR_CXX14 void
#if TEST_STD_VER >= 11
shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator&& gen)
#else
shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator& gen)
#endif
{
typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef uniform_int_distribution<ptrdiff_t> dist;
typedef typename dist::param_type param_type;
RandomAccessIterator last_iter = last;
difference_type diff = last_iter - first;
if (diff > 1) {
dist uid;
for (--last_iter, (void)--diff; first < last; ++first, (void)--diff) {
difference_type index = uid(gen, param_type(0, diff));
if (index != difference_type(0)) {
using std::swap;
swap(*first, *(first + index));
}
}
}
}
} // namespace support
#endif // TEST_SUPPORT_CONSTEXPR_RANDOM_H