| 
							- #pragma once
 - #include <common.hpp>
 - #include <random>
 - #include <vector>
 - 
 - 
 - namespace rack {
 - /** Random number generation */
 - namespace random {
 - 
 - 
 - /** xoroshiro128+. Very fast, not-cryptographic random number generator.
 - From https://prng.di.unimi.it/
 - Example:
 - 
 - 	std::random_device rd;
 - 	random::Xoroshiro128Plus rng(rd(), rd());
 - 	uint64_t r = rng();
 - 
 - 	std::uniform_real_distribution<float> uniform(0.f, 1.f);
 - 	float r = uniform(rng);
 - 
 - 	std::normal_distribution<> normal(0.0, 1.0);
 - 	double r = normal(rng);
 - */
 - struct Xoroshiro128Plus {
 - 	uint64_t state[2] = {};
 - 
 - 	void seed(uint64_t s0, uint64_t s1) {
 - 		state[0] = s0;
 - 		state[1] = s1;
 - 		// A bad seed will give a bad first result, so shift the state
 - 		operator()();
 - 	}
 - 
 - 	bool isSeeded() {
 - 		return state[0] || state[1];
 - 	}
 - 
 - 	static uint64_t rotl(uint64_t x, int k) {
 - 		return (x << k) | (x >> (64 - k));
 - 	}
 - 
 - 	uint64_t operator()() {
 - 		uint64_t s0 = state[0];
 - 		uint64_t s1 = state[1];
 - 		uint64_t result = s0 + s1;
 - 
 - 		s1 ^= s0;
 - 		state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
 - 		state[1] = rotl(s1, 36);
 - 
 - 		return result;
 - 	}
 - 	constexpr uint64_t min() const {
 - 		return 0;
 - 	}
 - 	constexpr uint64_t max() const {
 - 		return UINT64_MAX;
 - 	}
 - };
 - 
 - 
 - // Simple thread-local API
 - 
 - /** Initializes the thread-local RNG state.
 - Must call when creating a thread, otherwise random state is undefined (might always return 0).
 - */
 - void init();
 - 
 - /** Returns the thread-local generator.
 - */
 - Xoroshiro128Plus& local();
 - 
 - template <typename T>
 - T get() {
 - 	// Call operator()() and cast by default
 - 	return local()();
 - }
 - 
 - template <>
 - inline uint32_t get() {
 - 	// Take top 32 bits which has better randomness properties.
 - 	return get<uint64_t>() >> 32;
 - }
 - 
 - template <>
 - inline uint16_t get() {
 - 	return get<uint64_t>() >> 48;
 - }
 - 
 - template <>
 - inline uint8_t get() {
 - 	return get<uint64_t>() >> 56;
 - }
 - 
 - template <>
 - inline bool get() {
 - 	return get<uint64_t>() >> 63;
 - }
 - 
 - template <>
 - inline float get() {
 - 	// The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1).
 - 	return get<uint32_t>() * 2.32830629e-10f;
 - }
 - 
 - template <>
 - inline double get() {
 - 	return get<uint64_t>() * 5.421010862427522e-20;
 - }
 - 
 - 
 - /** Returns a uniform random uint64_t from 0 to UINT64_MAX */
 - inline uint64_t u64() {return get<uint64_t>();}
 - /** Returns a uniform random uint32_t from 0 to UINT32_MAX */
 - inline uint32_t u32() {return get<uint32_t>();}
 - /** Returns a uniform random float in the interval [0.0, 1.0) */
 - inline float uniform() {return get<float>();}
 - 
 - /** Returns a normal random number with mean 0 and standard deviation 1 */
 - inline float normal() {
 - 	// Box-Muller transform
 - 	float radius = std::sqrt(-2.f * std::log(1.f - get<float>()));
 - 	float theta = 2.f * M_PI * get<float>();
 - 	return radius * std::sin(theta);
 - 
 - 	// // Central Limit Theorem
 - 	// const int n = 8;
 - 	// float sum = 0.0;
 - 	// for (int i = 0; i < n; i++) {
 - 	// 	sum += get<float>();
 - 	// }
 - 	// return (sum - n / 2.f) / std::sqrt(n / 12.f);
 - }
 - 
 - /** Fills an array with random bytes. */
 - inline void buffer(uint8_t* out, size_t len) {
 - 	Xoroshiro128Plus& rng = local();
 - 	for (size_t i = 0; i < len; i += 4) {
 - 		uint64_t r = rng();
 - 		out[i] = r;
 - 		if (i + 1 < len)
 - 			out[i + 1] = r >> 8;
 - 		if (i + 2 < len)
 - 			out[i + 2] = r >> 16;
 - 		if (i + 3 < len)
 - 			out[i + 3] = r >> 24;
 - 	}
 - }
 - 
 - /** Creates a vector of random bytes. */
 - inline std::vector<uint8_t> vector(size_t len) {
 - 	std::vector<uint8_t> v(len);
 - 	buffer(v.data(), len);
 - 	return v;
 - }
 - 
 - 
 - } // namespace random
 - } // namespace rack
 
 
  |