diff --git a/libraries/random/xoshiro.h b/libraries/random/xoshiro.h
new file mode 100644
index 0000000..5bf4862
--- /dev/null
+++ b/libraries/random/xoshiro.h
@@ -0,0 +1,428 @@
+/* Code was taken from here:
+ https://prng.di.unimi.it
+ And wrapped as a bit generator for C++'s random module' */
+
+/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)
+
+To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.
+
+See . */
+#ifndef XOSHIRO_H
+#define XOSHIRO_H
+
+#include
+#include
+#include
+
+namespace Xoshiro {
+
+
+#if __cplusplus >= 202001L
+# define HAS_CPP20
+#endif
+
+static inline std::uint64_t rotl64(const std::uint64_t x, const int k) {
+ return (x << k) | (x >> (64 - k));
+}
+
+static inline std::uint32_t rotl32(const std::uint32_t x, const int k) {
+ return (x << k) | (x >> (32 - k));
+}
+
+/* these are in order to avoid gcc warnings about 'strict aliasing rules' */
+static inline std::uint32_t extract_32bits_from64_left(const std::uint64_t x)
+{
+ std::uint32_t out;
+ std::memcpy(&out, reinterpret_cast(&x), sizeof(std::uint32_t));
+ return out;
+}
+
+static inline std::uint32_t extract_32bits_from64_right(const std::uint64_t x)
+{
+ std::uint32_t out;
+ std::memcpy(&out, reinterpret_cast(&x) + 1, sizeof(std::uint32_t));
+ return out;
+}
+
+static inline void assign_32bits_to64_left(std::uint64_t assign_to, const std::uint32_t take_from)
+{
+ std::memcpy(reinterpret_cast(&assign_to), &take_from, sizeof(std::uint32_t));
+}
+
+static inline void assign_32bits_to64_right(std::uint64_t assign_to, const std::uint32_t take_from)
+{
+ std::memcpy(reinterpret_cast(&assign_to) + 1, &take_from, sizeof(std::uint32_t));
+}
+
+constexpr static const uint64_t JUMP_X256PP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };
+
+constexpr static const uint64_t LONG_JUMP_X256PP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };
+
+constexpr static const uint32_t JUMP_X128PP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b };
+
+constexpr static const uint32_t LONG_JUMP_X128PP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 };
+
+template
+static inline void jump_state(const int_t jump_table[4], rng_t &rng)
+{
+ int_t s0 = 0;
+ int_t s1 = 0;
+ int_t s2 = 0;
+ int_t s3 = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ for (int b = 0; b < 8*sizeof(int_t); b++)
+ {
+ if (jump_table[i] & ((int_t)1) << b)
+ {
+ s0 ^= rng.state[0];
+ s1 ^= rng.state[1];
+ s2 ^= rng.state[2];
+ s3 ^= rng.state[3];
+ }
+ rng();
+ }
+ }
+
+ rng.state[0] = s0;
+ rng.state[1] = s1;
+ rng.state[2] = s2;
+ rng.state[3] = s3;
+}
+
+/* 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. */
+static inline std::uint64_t splitmix64(const std::uint64_t seed)
+{
+ std::uint64_t z = (seed + 0x9e3779b97f4a7c15);
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+ return z ^ (z >> 31);
+}
+
+/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
+ 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.
+
+ For generating just floating-point numbers, xoshiro256+ is even faster.
+
+ The state must be seeded so that it is not everywhere zero. If you have
+ a 64-bit seed, we suggest to seed a splitmix64 generator and use its
+ output to fill s. */
+class Xoshiro256PP
+{
+public:
+ using result_type = std::uint64_t;
+ std::uint64_t state[4] = {0x3d23dce41c588f8c, 0x10c770bb8da027b0, 0xc7a4c5e87c63ba25, 0xa830f83239465a2e};
+
+ constexpr static result_type min()
+ {
+ return 0;
+ }
+
+ constexpr static result_type max()
+ {
+ return UINT64_MAX;
+ }
+
+ Xoshiro256PP() = default;
+
+ ~Xoshiro256PP() noexcept = default;
+
+ Xoshiro256PP(Xoshiro256PP &other) = default;
+
+ Xoshiro256PP& operator=(const Xoshiro256PP &other) = default;
+
+ Xoshiro256PP(Xoshiro256PP &&) noexcept = default;
+
+ Xoshiro256PP& operator=(Xoshiro256PP &&) noexcept = default;
+
+ void seed(const std::uint64_t seed)
+ {
+ this->state[0] = splitmix64(splitmix64(seed));
+ this->state[1] = splitmix64(this->state[0]);
+ this->state[2] = splitmix64(this->state[1]);
+ this->state[3] = splitmix64(this->state[2]);
+ }
+
+ void seed(const std::uint64_t seed[4])
+ {
+ std::memcpy(this->state, seed, 4*sizeof(std::uint64_t));
+ }
+
+ template
+ void seed(Sseq& seq)
+ {
+ seq.generate(reinterpret_cast(&this->state[0]),
+ reinterpret_cast(&this->state[0] + 4));
+ }
+
+ Xoshiro256PP(const std::uint64_t seed)
+ {
+ this->seed(seed);
+ }
+
+ Xoshiro256PP(const std::uint64_t seed[4])
+ {
+ this->seed(seed);
+ }
+
+ template
+ Xoshiro256PP(Sseq& seq)
+ {
+ this->seed(seq);
+ }
+
+ result_type operator()()
+ {
+ const std::uint64_t result = rotl64(this->state[0] + this->state[3], 23) + this->state[0];
+ const std::uint64_t t = this->state[1] << 17;
+ this->state[2] ^= this->state[0];
+ this->state[3] ^= this->state[1];
+ this->state[1] ^= this->state[2];
+ this->state[0] ^= this->state[3];
+ this->state[2] ^= t;
+ this->state[3] = rotl64(this->state[3], 45);
+ return result;
+ }
+
+ void discard(unsigned long long z)
+ {
+ for (unsigned long long ix = 0; ix < z; ix++)
+ this->operator()();
+ }
+
+ Xoshiro256PP jump()
+ {
+ Xoshiro256PP new_gen = *this;
+ jump_state(JUMP_X256PP, new_gen);
+ return new_gen;
+ }
+
+ Xoshiro256PP long_jump()
+ {
+ Xoshiro256PP new_gen = *this;
+ jump_state(LONG_JUMP_X256PP, new_gen);
+ return new_gen;
+ }
+
+ bool operator==(const Xoshiro256PP &rhs)
+ {
+ return std::memcmp(this->state, rhs.state, 4*sizeof(std::uint64_t));
+ }
+
+ #ifndef HAS_CPP20
+ bool operator!=(const Xoshiro256PP &rhs)
+ {
+ return !std::memcmp(this->state, rhs.state, 4*sizeof(std::uint64_t));
+ }
+ #endif
+
+ template< class CharT, class Traits >
+ friend std::basic_ostream&
+ operator<<(std::basic_ostream& ost, const Xoshiro256PP& e)
+ {
+ ost.write(reinterpret_cast(&e.state[0]), sizeof(std::uint64_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[1]), sizeof(std::uint64_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[2]), sizeof(std::uint64_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[3]), sizeof(std::uint64_t));
+ return ost;
+ }
+ template< class CharT, class Traits >
+ friend std::basic_istream&
+ operator>>(std::basic_istream& ist, Xoshiro256PP& e)
+ {
+ ist.read(reinterpret_cast(&e.state[0]), sizeof(std::uint64_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[1]), sizeof(std::uint64_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[2]), sizeof(std::uint64_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[3]), sizeof(std::uint64_t));
+ return ist;
+ }
+};
+
+/* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid
+ generators. It has excellent speed, a state size (128 bits) that is
+ large enough for mild parallelism, and it passes all tests we are aware
+ of.
+
+ For generating just single-precision (i.e., 32-bit) floating-point
+ numbers, xoshiro128+ is even faster.
+
+ The state must be seeded so that it is not everywhere zero. */
+class Xoshiro128PP
+{
+public:
+ using result_type = std::uint32_t;
+ std::uint32_t state[4] = {0x1c588f8c, 0x3d23dce4, 0x8da027b0, 0x10c770bb};
+
+ constexpr static result_type min()
+ {
+ return 0;
+ }
+
+ constexpr static result_type max()
+ {
+ return UINT32_MAX;
+ }
+
+ Xoshiro128PP() = default;
+
+ ~Xoshiro128PP() noexcept = default;
+
+ Xoshiro128PP(Xoshiro128PP &other) = default;
+
+ Xoshiro128PP& operator=(const Xoshiro128PP &other) = default;
+
+ Xoshiro128PP(Xoshiro128PP &&) noexcept = default;
+
+ Xoshiro128PP& operator=(Xoshiro128PP &&) noexcept = default;
+
+ void seed(const std::uint64_t seed)
+ {
+ const auto t1 = splitmix64(seed);
+ const auto t2 = splitmix64(t1);
+ this->state[0] = splitmix64(extract_32bits_from64_left(t1));
+ this->state[1] = splitmix64(extract_32bits_from64_right(t1));
+ this->state[2] = splitmix64(extract_32bits_from64_left(t2));
+ this->state[3] = splitmix64(extract_32bits_from64_right(t2));
+ }
+
+ void seed(const std::uint32_t seed)
+ {
+ std::uint64_t temp;
+ assign_32bits_to64_left(temp, seed);
+ assign_32bits_to64_right(temp, seed);
+ this->seed(temp);
+ }
+
+ void seed(const std::uint64_t seed[2])
+ {
+ std::memcpy(this->state, seed, 4*sizeof(std::uint32_t));
+ }
+
+ void seed(const std::uint32_t seed[4])
+ {
+ std::memcpy(this->state, seed, 4*sizeof(std::uint32_t));
+ }
+
+ template
+ void seed(Sseq& seq)
+ {
+ seq.generate(&this->state[0], &this->state[0] + 4);
+ }
+
+ Xoshiro128PP(const std::uint32_t seed)
+ {
+ this->seed(seed);
+ }
+
+ Xoshiro128PP(const std::uint64_t seed)
+ {
+ this->seed(seed);
+ }
+
+ Xoshiro128PP(const std::uint32_t seed[4])
+ {
+ this->seed(seed);
+ }
+
+ Xoshiro128PP(const std::uint64_t seed[2])
+ {
+ this->seed(seed);
+ }
+
+ template
+ Xoshiro128PP(Sseq& seq)
+ {
+ this->seed(seq);
+ }
+
+ result_type operator()()
+ {
+ const std::uint32_t result = rotl32(this->state[0] + this->state[3], 7) + this->state[0];
+ const std::uint32_t t = this->state[1] << 9;
+ this->state[2] ^= this->state[0];
+ this->state[3] ^= this->state[1];
+ this->state[1] ^= this->state[2];
+ this->state[0] ^= this->state[3];
+ this->state[2] ^= t;
+ this->state[3] = rotl32(this->state[3], 11);
+ return result;
+ }
+
+ Xoshiro128PP jump()
+ {
+ Xoshiro128PP new_gen = *this;
+ jump_state(JUMP_X128PP, new_gen);
+ return new_gen;
+ }
+
+ Xoshiro128PP long_jump()
+ {
+ Xoshiro128PP new_gen = *this;
+ jump_state(LONG_JUMP_X128PP, new_gen);
+ return new_gen;
+ }
+
+ void discard(unsigned long long z)
+ {
+ for (unsigned long long ix = 0; ix < z; ix++)
+ this->operator()();
+ }
+
+ bool operator==(const Xoshiro128PP &rhs)
+ {
+ return std::memcmp(this->state, rhs.state, 4*sizeof(std::uint32_t));
+ }
+
+ #ifndef HAS_CPP20
+ bool operator!=(const Xoshiro128PP &rhs)
+ {
+ return !std::memcmp(this->state, rhs.state, 4*sizeof(std::uint32_t));
+ }
+ #endif
+
+ template< class CharT, class Traits >
+ friend std::basic_ostream&
+ operator<<(std::basic_ostream& ost, const Xoshiro128PP& e)
+ {
+ ost.write(reinterpret_cast(&e.state[0]), sizeof(std::uint32_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[1]), sizeof(std::uint32_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[2]), sizeof(std::uint32_t));
+ ost.put(' ');
+ ost.write(reinterpret_cast(&e.state[3]), sizeof(std::uint32_t));
+ return ost;
+ }
+ template< class CharT, class Traits >
+ friend std::basic_istream&
+ operator>>(std::basic_istream& ist, Xoshiro128PP& e)
+ {
+ ist.read(reinterpret_cast(&e.state[0]), sizeof(std::uint32_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[1]), sizeof(std::uint32_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[2]), sizeof(std::uint32_t));
+ ist.get();
+ ist.read(reinterpret_cast(&e.state[3]), sizeof(std::uint32_t));
+ return ist;
+ }
+};
+
+}
+
+#endif