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
/*
* Copyright Sebastiano Vigna 2015.
* Copyright Matt Borland 2022.
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
*/
#ifndef BOOST_RANDOM_SPLITMIX64_HPP
#define BOOST_RANDOM_SPLITMIX64_HPP
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <array>
#include <string>
#include <ios>
#include <type_traits>
namespace boost { namespace random {
/**
* This is a fixed-increment version of Java 8's SplittableRandom generator
* See http://dx.doi.org/10.1145/2714064.2660195 and
* http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
* It is a very fast generator passing BigCrush, and it can be useful if
* for some reason you absolutely want 64 bits of state; otherwise, we
* rather suggest to use a xoroshiro128+ (for moderately parallel
* computations) or xorshift1024* (for massively parallel computations)
* generator.
*/
class splitmix64
{
private:
std::uint64_t state_;
inline std::uint64_t concatenate(std::uint32_t word1, std::uint32_t word2) noexcept
{
return static_cast<std::uint64_t>(word1) << 32 | word2;
}
public:
using result_type = std::uint64_t;
using seed_type = std::uint64_t;
// Required for old Boost.Random concept
static constexpr bool has_fixed_range {false};
/** Seeds the generator with the default seed. */
void seed(result_type value = 0) noexcept
{
if (value == 0)
{
state_ = UINT64_C(0xA164B43C8F634A13);
}
else
{
state_ = value;
}
}
/**
* Seeds the generator with 32-bit values produced by @c seq.generate().
*/
template <typename Sseq, typename std::enable_if<!std::is_convertible<Sseq, std::uint64_t>::value, bool>::type = true>
void seed(Sseq& seq)
{
std::array<std::uint32_t, 2> seeds;
seq.generate(seeds.begin(), seeds.end());
state_ = concatenate(seeds[0], seeds[1]);
}
/**
* Seeds the generator with 64-bit values produced by @c seq.generate().
*/
template <typename Sseq, typename std::enable_if<!std::is_convertible<Sseq, splitmix64>::value, bool>::type = true>
explicit splitmix64(Sseq& seq)
{
seed(seq);
}
/** Seeds the generator with a user provided seed. */
template <typename T, typename std::enable_if<std::is_convertible<T, std::uint64_t>::value, bool>::type = true>
void seed(T value = 0) noexcept
{
seed(static_cast<std::uint64_t>(value));
}
/** Seeds the generator with a user provided seed. */
explicit splitmix64(std::uint64_t state = 0) noexcept
{
seed(state);
}
splitmix64(const splitmix64& other) = default;
splitmix64& operator=(const splitmix64& other) = default;
/** Returns the next value of the generator. */
inline result_type next() noexcept
{
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);
}
/** Returns the next value of the generator. */
inline result_type operator()() noexcept
{
return next();
}
/** Advances the state of the generator by @c z. */
inline void discard(std::uint64_t z) noexcept
{
for (std::uint64_t i {}; i < z; ++i)
{
next();
}
}
/**
* Returns true if the two generators will produce identical
* sequences of values.
*/
inline friend bool operator==(const splitmix64& lhs, const splitmix64& rhs) noexcept
{
return lhs.state_ == rhs.state_;
}
/**
* Returns true if the two generators will produce different
* sequences of values.
*/
inline friend bool operator!=(const splitmix64& lhs, const splitmix64& rhs) noexcept
{
return !(lhs == rhs);
}
/** Writes a @c splitmix64 to a @c std::ostream. */
template <typename CharT, typename Traits>
inline friend std::basic_ostream<CharT,Traits>& operator<<(std::basic_ostream<CharT,Traits>& ost,
const splitmix64& e)
{
ost << e.state_;
return ost;
}
/** Writes a @c splitmix64 to a @c std::istream. */
template <typename CharT, typename Traits>
inline friend std::basic_istream<CharT,Traits>& operator>>(std::basic_istream<CharT,Traits>& ist,
splitmix64& e)
{
std::string sstate;
CharT val;
while (ist >> val)
{
if (std::isdigit(val))
{
sstate.push_back(val);
}
}
e.state_ = std::strtoull(sstate.c_str(), nullptr, 10);
return ist;
}
/** Fills a range with random values */
template <typename FIter>
inline void generate(FIter first, FIter last) noexcept
{
while (first != last)
{
*first++ = next();
}
}
/**
* Returns the largest value that the @c splitmix64
* can produce.
*/
static constexpr result_type (max)() noexcept
{
return (std::numeric_limits<std::uint64_t>::max)();
}
/**
* Returns the smallest value that the @c splitmix64
* can produce.
*/
static constexpr result_type (min)() noexcept
{
return (std::numeric_limits<std::uint64_t>::min)();
}
};
}} // Namespace boost::random
#endif // BOOST_RANDOM_SPLITMIX64_HPP