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