155 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2019 MariaDB Corporation Ab
 | |
|  *
 | |
|  * Use of this software is governed by the Business Source License included
 | |
|  * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | |
|  *
 | |
|  * Change Date: 2025-03-08
 | |
|  *
 | |
|  * On the date above, in accordance with the Business Source License, use
 | |
|  * of this software will be governed by version 2 or later of the General
 | |
|  * Public License.
 | |
|  */
 | |
| #pragma once
 | |
| 
 | |
| #include <maxbase/ccdefs.hh>
 | |
| 
 | |
| #include <random>
 | |
| #include <array>
 | |
| 
 | |
| namespace maxbase
 | |
| {
 | |
| 
 | |
| /**
 | |
|  * XorShiftRandom is an extremely fast all purpose random number generator.
 | |
|  *
 | |
|  * Uses xoshiro256** http://xoshiro.di.unimi.it written in 2018 by David Blackman
 | |
|  * and Sebastiano Vigna. Comment from the source code:
 | |
|  * This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
 | |
|  * excellent (sub-ns) speed, a state (256 bits) that is large enough for
 | |
|  * any parallel application, and it passes all tests we are aware of.
 | |
|  */
 | |
| class XorShiftRandom
 | |
| {
 | |
| public:
 | |
|     // Non-deterministic if seed == 0
 | |
|     explicit XorShiftRandom(uint64_t seed = 0);
 | |
|     uint64_t rand();
 | |
|     uint32_t rand32();
 | |
|     bool     rand_bool();
 | |
|     int64_t  b_to_e_co(int64_t b, int64_t e);
 | |
|     double   zero_to_one_co();
 | |
| private:
 | |
|     uint64_t rotl(const uint64_t x, int k);
 | |
|     std::array<uint64_t, 4> m_state;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * StdTwisterRandom is a class for random number generation using the C++ standard library.
 | |
|  * Uses the Mersenne Twister algorithms (mt19937 and mt19937_64).
 | |
|  */
 | |
| class StdTwisterRandom
 | |
| {
 | |
| public:
 | |
|     // Non-deterministic if seed == 0
 | |
|     explicit StdTwisterRandom(uint64_t seed = 0);
 | |
| 
 | |
|     uint64_t rand();
 | |
|     uint32_t rand32();
 | |
|     bool     rand_bool();
 | |
|     int64_t  b_to_e_co(int64_t b, int64_t e);
 | |
|     double   zero_to_one_co();
 | |
| 
 | |
|     // Borrow the mt19937_64 engine for other distributions.
 | |
|     std::mt19937_64& rnd_engine();
 | |
| private:
 | |
|     std::mt19937    m_twister_engine_32;
 | |
|     std::mt19937_64 m_twister_engine_64;
 | |
| };
 | |
| 
 | |
| // *********************************
 | |
| // inlined functions below this line
 | |
| 
 | |
| inline uint64_t XorShiftRandom::rotl(const uint64_t x, int k)
 | |
| {
 | |
|     return (x << k) | (x >> (64 - k));
 | |
| }
 | |
| 
 | |
| inline uint64_t XorShiftRandom::rand()
 | |
| {
 | |
|     // xoshiro256**
 | |
|     const uint64_t result_starstar = rotl(m_state[1] * 5, 7) * 9;
 | |
| 
 | |
|     const uint64_t t = m_state[1] << 17;
 | |
| 
 | |
|     m_state[2] ^= m_state[0];
 | |
|     m_state[3] ^= m_state[1];
 | |
|     m_state[1] ^= m_state[2];
 | |
|     m_state[0] ^= m_state[3];
 | |
| 
 | |
|     m_state[2] ^= t;
 | |
| 
 | |
|     m_state[3] = rotl(m_state[3], 45);
 | |
| 
 | |
|     return result_starstar;
 | |
| }
 | |
| 
 | |
| inline uint32_t XorShiftRandom::rand32()
 | |
| {
 | |
|     return rand() >> 32;    // shifting, although low bits are good
 | |
| }
 | |
| 
 | |
| inline bool XorShiftRandom::rand_bool()
 | |
| {
 | |
|     return std::signbit(int64_t(rand()));
 | |
| }
 | |
| 
 | |
| inline double XorShiftRandom::zero_to_one_co()
 | |
| {
 | |
|     uint64_t x = rand();
 | |
| 
 | |
|     // Turn uint64_t to a [0,1[ double (yes, it's standard and portable)
 | |
|     const union
 | |
|     {
 | |
|         uint64_t i;
 | |
|         double   d;
 | |
|     } u = {.i = UINT64_C(0x3FF) << 52 | x >> 12};
 | |
| 
 | |
|     return u.d - 1.0;
 | |
| }
 | |
| 
 | |
| inline int64_t XorShiftRandom::b_to_e_co(int64_t b, int64_t e)
 | |
| {
 | |
|     // With 64 bits mod bias does not happen in practise (a very, very large e-b would be needed).
 | |
|     // alternative: return b + int64_t(zero_to_one_exclusive()*(e-b));
 | |
|     return b + rand() % (e - b);
 | |
| }
 | |
| 
 | |
| inline uint64_t StdTwisterRandom::rand()
 | |
| {
 | |
|     return m_twister_engine_64();
 | |
| }
 | |
| 
 | |
| inline uint32_t StdTwisterRandom::rand32()
 | |
| {
 | |
|     return m_twister_engine_32();
 | |
| }
 | |
| 
 | |
| inline bool StdTwisterRandom::rand_bool()
 | |
| {
 | |
|     return std::signbit(int32_t(rand32()));
 | |
| }
 | |
| 
 | |
| inline double StdTwisterRandom::zero_to_one_co()
 | |
| {
 | |
|     std::uniform_real_distribution<double> zero_to_one {0, 1};
 | |
|     return zero_to_one(m_twister_engine_64);
 | |
| }
 | |
| 
 | |
| inline int64_t StdTwisterRandom::b_to_e_co(int64_t b, int64_t e)
 | |
| {
 | |
|     std::uniform_int_distribution<int64_t> dist {b, e - 1};
 | |
|     return dist(m_twister_engine_64);
 | |
| }
 | |
| }
 | 
