-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathrng.h
185 lines (152 loc) · 3.92 KB
/
rng.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
#pragma once
//
// The code in this file is free and unencumbered software released
// into the public domain.
//
// <http://creativecommons.org/publicdomain/zero/1.0/>.
//
#ifndef RNG_H
#define RNG_H
#include <cstdint>
#include <random>
#include <thread>
#if defined(_MSC_VER)
/* Microsoft C/C++-compatible compiler
* Adaptive implementations
*/
#include <intrin.h>
#define _rdtsc() __rdtsc()
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
/* GCC-compatible compiler, targeting x86/x86-64
* NO implementation needed
*/
#include <x86intrin.h>
#elif defined(__GNUC__) && defined(__ARM_NEON__)
/* GCC-compatible compiler, targeting ARM with NEON
* Adaptive implementations
*/
#include <arm_neon.h>
#elif defined(__GNUC__) && defined(__IWMMXT__)
/* GCC-compatible compiler, targeting ARM with WMMX
* Adaptive implementations
*/
#include <mmintrin.h>
#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
/* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX
* Adaptive implementations
*/
#include <altivec.h>
#elif defined(__GNUC__) && defined(__SPE__)
/* GCC-compatible compiler, targeting PowerPC with SPE
* Adaptive implementations
*/
#include <spe.h>
#endif
namespace rng {
//
// Bitwise circular left shift.
//
static inline std::uint64_t
rotl(const std::uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}
//
// A simple random seed generator based on the entropy coming from the
// system thread/process scheduler. This is rather slow but seeds are
// normally generated very infrequently.
//
struct tsc_seed
{
using result_type = std::uint64_t;
result_type operator()()
{
std::uint64_t base = _rdtsc();
std::uint64_t seed = base & 0xff;
for (int i = 1; i < 8; i++) {
std::this_thread::yield();
seed |= ((_rdtsc() - base) & 0xff) << (i << 3);
}
return seed;
}
};
//
// A random seed generator based on std::random_device.
//
struct random_device_seed
{
using result_type = std::uint64_t;
result_type operator()()
{
std::random_device rd;
if (sizeof(result_type) > sizeof(std::random_device::result_type))
return rd() | (result_type{rd()} << 32);
else
return rd();
}
};
//
// A random number generator with 64-bit internal state. It is based on
// the code from here: http://xoroshiro.di.unimi.it/splitmix64.c
//
struct rng64
{
using result_type = std::uint64_t;
std::uint64_t state;
rng64(std::uint64_t seed = 1) : state{seed} {}
result_type operator()()
{
std::uint64_t z = (state += UINT64_C(0x9E3779B97F4A7C15));
z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);
return z ^ (z >> 31);
}
};
//
// A random number generator with 128-bit internal state. It is based on
// the code from here: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
//
struct rng128
{
using result_type = std::uint64_t;
std::uint64_t state[2];
rng128(std::uint64_t seed[2]) : state{seed[0], seed[1]} {}
rng128(std::uint64_t s0, std::uint64_t s1) : state{s0, s1} {}
rng128(std::uint64_t seed = 1)
{
rng64 seeder(seed);
state[0] = seeder();
state[1] = seeder();
}
result_type operator()()
{
const uint64_t s0 = state[0];
uint64_t s1 = state[1];
const uint64_t value = s0 + s1;
s1 ^= s0;
state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
state[1] = rotl(s1, 36);
return value;
}
// This is the jump function for the generator. It is equivalent
// to 2 ^ 64 calls to next(); it can be used to generate 2 ^ 64
// non-overlapping subsequences for parallel computations.
void jump()
{
static const std::uint64_t j[] = {0xbeac0467eba5facb, 0xd86b048b86aa9922};
std::uint64_t s0 = 0, s1 = 0;
for (std::size_t i = 0; i < sizeof j / sizeof j[0]; i++) {
for (int b = 0; b < 64; b++) {
if ((j[i] & UINT64_C(1) << b) != 0) {
s0 ^= state[0];
s1 ^= state[1];
}
operator()();
}
}
state[0] = s0;
state[1] = s1;
}
};
} // namespace rng
#endif // RNG_H