Skip to content

Commit c76b47d

Browse files
committed
random: adds new random generator
1 parent fa50209 commit c76b47d

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

lib/include/irritator/random.hpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright (c) 2025 INRAE Distributed under the Boost Software License,
2+
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
3+
// http://www.boost.org/LICENSE_1_0.txt)
4+
5+
#ifndef ORG_VLEPROJECT_IRRITATOR_RANDOM_HPP
6+
#define ORG_VLEPROJECT_IRRITATOR_RANDOM_HPP
7+
8+
#include <array>
9+
#include <bit>
10+
#include <concepts>
11+
#include <cstdint>
12+
#include <limits>
13+
14+
#ifdef _MSC_VER
15+
#include <intrin.h>
16+
#endif
17+
18+
/**
19+
* A Pseudo Random Generator based on Salmon et al. (Random123).
20+
*
21+
* The interface of @c philox_64 in fully compatible with the standard random
22+
* header.
23+
*/
24+
class philox_64
25+
{
26+
public:
27+
using result_type = uint64_t;
28+
29+
static constexpr uint64_t PHILOX_M0 = 0xD2B74407B1CADAC9ULL;
30+
static constexpr uint64_t PHILOX_W0 = 0x9E3779B97F4A7C15ULL;
31+
static constexpr int ROUNDS = 10;
32+
33+
/**
34+
* @brief Ctor
35+
* @param seed Global simulation @c seed.
36+
* @param index Unique identifiant for model
37+
* @param step First step
38+
*/
39+
constexpr Philox64(const uint64_t seed,
40+
const uint64_t index,
41+
const uint64_t step = 0) noexcept
42+
: m_key({ seed })
43+
, m_counter({ index, step })
44+
, m_buffer_pos(2) // To force buffer computation
45+
{}
46+
47+
/** Build the next random number. Defined by the STL requirements for random
48+
* number generators. */
49+
constexpr result_type operator()() noexcept
50+
{
51+
if (m_buffer_pos >= 2) {
52+
refill_buffer();
53+
}
54+
returnm_buffer[m_buffer_pos++];
55+
}
56+
57+
/** Discard @c z random numbers. Useful to jump ahead in the random
58+
* sequence. */
59+
constexpr void discard(const uint64_t z) noexcept
60+
{
61+
m_counter[1] += z;
62+
m_buffer_pos = 2; // Invalid the buffer.
63+
}
64+
65+
/** A state setter to give full random access for example to replay
66+
* simulation for example @c index is the @c model_id and @c step is the
67+
* number of the random. */
68+
constexpr void set_state(const uint64_t index, const uint64_t step) noexcept
69+
{
70+
m_counter[0] = index;
71+
m_counter[1] = step;
72+
m_buffer_pos = 2; // Invalid the buffer.
73+
}
74+
75+
/** Give the minimum integer of the random generator. Defined by the STL
76+
* requirements for random number generators. */
77+
static constexpr result_type min() noexcept
78+
{
79+
return std::numeric_limits<result_type>::min();
80+
}
81+
82+
/** Give the maximun integer of the random generator. Defined by the STL
83+
* requirements for random number generators. */
84+
static constexpr result_type max() noexcept
85+
{
86+
return std::numeric_limits<result_type>::max();
87+
}
88+
89+
private:
90+
std::array<uint64_t, 1> m_key; /**< Global seed. */
91+
std::array<uint64_t, 2>
92+
m_counter; /**< m_counter[0] the model_id and m_counter[1] the step. */
93+
std::array<uint64_t, 2>
94+
m_buffer; /**< Buffer to store result and next result. */
95+
int m_buffer_pos;
96+
97+
// A 128-bit multiplication (high and low parts)
98+
static constexpr void mulhilo(uint64_t a,
99+
uint64_t b,
100+
uint64_t& lo,
101+
uint64_t& hi) noexcept
102+
{
103+
#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
104+
__uint128_t product =
105+
static_cast<__uint128_t>(a) * static_cast<__uint128_t>(b);
106+
lo = static_cast<uint64_t>(product);
107+
hi = static_cast<uint64_t>(product >> 64);
108+
#elif defined(_MSC_VER) && defined(_WIN64)
109+
lo = _umul128(a, b, &hi);
110+
#else
111+
/* First calculate all of the cross products. */
112+
uint64_t lo_lo = (lhs & 0xffffffff) * (rhs & 0xffffffff);
113+
uint64_t hi_lo = (lhs >> 32) * (rhs & 0xffffffff);
114+
uint64_t lo_hi = (lhs & 0xffffffff) * (rhs >> 32);
115+
uint64_t hi_hi = (lhs >> 32) * (rhs >> 32);
116+
117+
/* Now add the products together. These will never overflow. */
118+
uint64_t cross = (lo_lo >> 32) + (hi_lo & 0xffffffff) + lo_hi;
119+
uint64_t upper = (hi_lo >> 32) + (cross >> 32) + hi_hi;
120+
121+
hi = upper;
122+
lo = (cross << 32) | (lo_lo & 0xffffffff);
123+
#endif
124+
}
125+
126+
constexpr void refill_buffer() noexcept
127+
{
128+
uint64_t ctr0 = m_counter[0];
129+
uint64_t ctr1 = m_counter[1];
130+
uint64_t key0 = m_key[0];
131+
132+
for (int i = 0; i < ROUNDS; ++i) {
133+
uint64_t hi, lo;
134+
mulhilo(PHILOX_M0, ctr0, lo, hi);
135+
136+
ctr0 = hi ^ key0 ^ ctr1;
137+
ctr1 = lo;
138+
139+
key0 += PHILOX_W0;
140+
}
141+
142+
m_buffer[0] = ctr0;
143+
m_buffer[1] = ctr1;
144+
145+
// Finaly increase the counter and reset the buffer position.
146+
m_counter[1]++;
147+
m_buffer_pos = 0;
148+
}
149+
};

0 commit comments

Comments
 (0)