<tt>xoshiro
Loading...
Searching...
No Matches
xoshiro.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2023 Nessan Fitzmaurice <nessan.fitzmaurice@me.com>
3// SPDX-License-Identifier: MIT
4
8
9#include <algorithm>
10#include <array>
11#include <bit>
12#include <cassert>
13#include <chrono>
14#include <concepts>
15#include <cstdint>
16#include <format>
17#include <iterator>
18#include <limits>
19#include <ostream>
20#include <random>
21#include <ranges>
22#include <stdexcept>
23#include <string>
24#include <type_traits>
25#include <vector>
26
27// If the `gf2` library is available, we provide extra methods---primarily for analysing new RNG variants.
28// For normal use, this is not required & the single-header xoshiro.h works fine without it.
29#ifdef GF2
30 #include <gf2/gf2.h>
31#endif
32
33namespace xso {
34
35// --------------------------------------------------------------------------------------------------------------------
36// A C++ concept that lets us distinguish standard distribution types from other types.
37// --------------------------------------------------------------------------------------------------------------------
38
45template<typename Dist>
46concept Distribution = requires { typename Dist::param_type; };
47
48// --------------------------------------------------------------------------------------------------------------------
49// Forward declarations of methods defined later in this file.
50// --------------------------------------------------------------------------------------------------------------------
51template<typename State>
52constexpr auto jump_coefficients(std::size_t J, bool J_is_log2 = false);
53
54template<std::unsigned_integral word_type, std::size_t N>
55constexpr auto reduce(const std::array<word_type, N>& p, std::size_t J, bool J_is_log2 = false);
56
57// --------------------------------------------------------------------------------------------------------------------
58// The xoshiro/xoroshiro pseudorandom number generator class template.
59// --------------------------------------------------------------------------------------------------------------------
60
68template<typename State, typename Scrambler>
69class generator {
70public:
73
75 using state_type = State;
76
78 using scrambler_type = Scrambler;
79
81 using word_type = typename State::word_type;
82
84 using array_type = typename State::array_type;
85
92 static constexpr std::size_t word_count() { return State::word_count(); }
93
100 static constexpr std::size_t bit_count() { return State::bit_count(); }
101
114 static constexpr auto type_string() { return std::format("{}{}", State::type_string(), Scrambler::type_string()); }
115
119
126
136 static constexpr result_type min() noexcept { return 0; }
137
147 static constexpr result_type max() noexcept { return std::numeric_limits<result_type>::max(); }
148
152
162 constexpr generator() { seed(); }
163
174 explicit constexpr generator(word_type s) { seed(s); }
175
193 template<std::input_iterator Iter>
194 requires std::convertible_to<std::iter_value_t<Iter>, word_type>
195 explicit constexpr generator(Iter b, Iter e) {
196 seed(b, e);
197 }
198
202
216 constexpr void seed() {
217 // We will use std::random_device as the principal source of entropy.
218 std::random_device dev;
219
220 // Fill a full seed array with calls to dev() -- may need a couple of dev() calls to fill one word of state.
221 array_type full_state;
222 if constexpr (sizeof(word_type) <= sizeof(std::random_device::result_type)) {
223 for (auto& word : full_state) word = static_cast<word_type>(dev());
224 } else {
225 for (auto& word : full_state) word = static_cast<word_type>(static_cast<uint64_t>(dev()) << 32 | dev());
226 }
227
228 // However, `std::random_device` may be poor so add data from a call to a high resolution clock for first word.
229 using clock_type = std::chrono::high_resolution_clock;
230 auto ticks = static_cast<std::uint64_t>(clock_type::now().time_since_epoch().count());
231
232 // From call to call, time ticks only changes in the low order bits -- better scramble things a bit!
233 ticks = murmur_scramble64(ticks);
234
235 // Fold the scrambled ticks variable into the first seed word.
236 full_state[0] ^= static_cast<word_type>(ticks);
237
238 // Seed the state from our full "high quality" seed array.
239 m_state.seed(full_state.cbegin(), full_state.cend());
240 }
241
254 constexpr void seed(word_type seed) {
255 // Scramble the bits in the single seed we were given.
256 auto sm64_state = murmur_scramble64(seed);
257
258 // Use SplitMix64 to at least put some values in all the state words & seed the state.
259 array_type full_state;
260 for (auto& word : full_state) word = static_cast<word_type>(split_mix64(sm64_state));
261 m_state.seed(full_state.cbegin(), full_state.cend());
262 }
263
283 template<std::input_iterator Src>
284 requires std::convertible_to<std::iter_value_t<Src>, word_type>
285 constexpr void seed(Src b, Src e) {
286 m_state.seed(b, e);
287 }
288
292
294 constexpr void step() { m_state.step(); }
295
303 result_type result = m_scrambler(m_state);
304 step();
305 return result;
306 }
307
311
313 constexpr word_type operator[](std::size_t i) const { return m_state[i]; }
314
319 template<std::output_iterator<word_type> Dst>
320 constexpr void get_state(Dst dst) const {
321 m_state.get_state(dst);
322 }
323
327
342 template<std::integral T>
343 constexpr T sample(T a, T b) {
344 return std::uniform_int_distribution<T>{a, b}(*this);
345 }
346
361 template<std::floating_point T>
362 constexpr T sample(T a, T b) {
363 return std::uniform_real_distribution<T>{a, b}(*this);
364 }
365
380 template<std::integral T>
381 constexpr T index(T len) {
382 return sample(T{0}, len - 1);
383 }
384
401 template<std::input_iterator T>
402 constexpr auto sample(T b, T e) {
403 // Edge case?
404 auto len = std::distance(b, e);
405 if (len < 2) return *b;
406
407 // Pick an index inside the iteration at random & return the corresponding value.
408 auto i = index(len);
409 std::advance(b, i);
410 return *b;
411 }
412
431 template<typename Container>
432 requires std::ranges::input_range<const Container> && std::ranges::common_range<const Container>
433 constexpr auto sample(const Container& container) {
434 return sample(std::ranges::cbegin(container), std::ranges::cend(container));
435 }
436
459 template<std::input_iterator Src, std::output_iterator<std::iter_value_t<Src>> Dst>
460 constexpr Dst sample(Src b, Src e, Dst dst, std::size_t n) {
461 return std::sample(b, e, dst, n, *this);
462 }
463
488 template<typename Src, std::output_iterator<std::ranges::range_value_t<const Src>> Dst>
489 requires std::ranges::input_range<const Src> && std::ranges::common_range<const Src>
490 constexpr auto sample(const Src& src, Dst dst, std::size_t n) {
491 return sample(std::ranges::cbegin(src), std::ranges::cend(src), dst, n);
492 }
493
508 constexpr auto sample(Distribution auto& dist) { return dist(*this); }
509
529 template<typename Dst>
530 constexpr Dst sample(Distribution auto& dist, Dst dst, std::size_t n) {
531 while (n-- != 0) *dst++ = dist(*this);
532 return dst;
533 }
534
548 constexpr std::size_t roll(std::size_t n_sides = 6) { return sample(1uz, n_sides); }
549
561 constexpr bool flip(double p = 0.5) { return std::bernoulli_distribution{p}(*this); }
562
566
580 template<std::random_access_iterator Iter>
581 constexpr void shuffle(Iter b, Iter e) {
582 std::shuffle(b, e, *this);
583 }
584
598 template<typename Container>
599 requires std::ranges::random_access_range<Container> && std::ranges::common_range<Container>
600 constexpr void shuffle(Container& container) {
601 return shuffle(std::begin(container), std::end(container));
602 }
603
607
614 void discard(std::uint64_t z) {
615 for (std::uint64_t i = 0; i < z; ++i) step();
616 }
617
621
642 constexpr auto jump(std::size_t n, bool n_is_log2 = false) {
643 // Get the jump polynomial coefficients for this jump
644 auto jump_coeffs = xso::jump_coefficients<state_type>(n, n_is_log2);
645
646 // Perform the jump using those coefficients
647 jump(jump_coeffs);
648
649 // Return the jump polynomial coefficients to the caller for possible reuse later.
650 return jump_coeffs;
651 }
652
671 constexpr void jump(const array_type& jump_coefficients) {
672
673 // Constant
674 constexpr std::size_t bits_per_word = std::numeric_limits<word_type>::digits;
675
676 // lambda: `is_set(word, b)` returns true if bit `b` in `word` is set.
677 auto is_set = [=](const word_type word, std::size_t b) -> bool {
678 return word & static_cast<word_type>(word_type{1} << b);
679 };
680
681 // If the current state is `s` and the state's transition matrix is T then the jump we want is s <- (T^n).s.
682 // Cayley-Hamilton is invoked to equate T^n with a small polynomial sum r(T) where r(x) = x^n mod c(x)
683 // and c(x) is the characteristic polynomial for T. Thus (T^n).s == r(T).s which is doable.
684 //
685 // The `jump_coefficients` argument is assumed to have the coefficients of r(x) stored compactly in words.
686 // The next loop computes the sum r(T).s = r_0.s + r_1.s^1 + ... + r_{n-1} s^{n-1}.
687 // We compute s^{i+1} = T.s^i using the step() method (so s <- T.s <- (T^2).s by iteratively stepping s
688 // forward).
689 array_type sum;
690 sum.fill(0);
691 for (auto i = 0uz; i < word_count(); ++i) {
692
693 // Iterate over the bits in each coefficient word -- the bits are the coefficients of r(x).
694 word_type r_word = jump_coefficients[i];
695 for (auto b = 0uz; b < bits_per_word; ++b) {
696 if (is_set(r_word, b)) {
697 // This coefficient in r(x) is one, so contributes to the sum ...
698 for (auto w = 0uz; w < word_count(); ++w) sum[w] ^= m_state[w];
699 }
700 // Compute the next state s^{i+1} by calling the step() method (same as s <- T.s).
701 m_state.step();
702 }
703 }
704
705 // Store the computed jumped state back as our current state so we are ready to proceed from there ...
706 m_state.seed(sum.cbegin(), sum.cend());
707 }
708
727 template<std::output_iterator<word_type> Dst>
728 static constexpr void characteristic_coefficients(Dst dst) {
729 State::characteristic_coefficients(dst);
730 }
731
748 static constexpr auto characteristic_coefficients() {
749 array_type result;
750 State::characteristic_coefficients(result.begin());
751 return result;
752 }
753
754#ifdef GF2
755
759
763 void jump(gf2::BitPolynomial<word_type> const& jump_poly) {
764 array_type sum;
765
766 // Computing [r_0 + r_1 T + ... + r_{m-1} T^{m-1}].s where s is the current state and r is the jump polynomial.
767 // T is the state's transition matrix so we can compute s^{i+1} = T.s^i using the step() method.
768 sum.fill(0);
769 for (auto i = 0uz; i < jump_poly.size(); ++i) {
770 if (jump_poly[i])
771 for (std::size_t w = 0; w < word_count(); ++w) sum[w] ^= m_state[w];
772 m_state.step();
773 }
774
775 // Perform the computed jump by reseeding the state from the computed sum ...
776 m_state.seed(sum.cbegin(), sum.cend());
777 }
778#endif
780
781private:
782 State m_state;
783 Scrambler m_scrambler;
784
785 // Class method that is an implementation of the SplitMix64 random number generator -- a simple generator with 64
786 // bits of state.
787 //
788 // Used here to help seed our more complex generators from a single 64-bit seed value.
789 static constexpr std::uint64_t split_mix64(std::uint64_t& state) {
790 std::uint64_t z = (state += 0x9e3779b97f4a7c15);
791 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
792 z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
793 z = (z ^ (z >> 31));
794 return z;
795 };
796
797 // Class method that returns a 64-bit word that is a scrambled version of the input 64-bit value.
798 //
799 // This is based on the MurmurHash3 finalizer function.
800 static constexpr std::uint64_t murmur_scramble64(std::uint64_t x) {
801 x ^= x >> 33;
802 x *= 0xff51afd7ed558ccdL;
803 x ^= x >> 33;
804 x *= 0xc4ceb9fe1a85ec53L;
805 x ^= x >> 33;
806 return x;
807 }
808
809 // Class method that returns a 32-bit word that is a scrambled version of the input 32-bit value.
810 //
811 // This is based on the MurmurHash3 mix function.
812 static constexpr std::uint32_t murmur_scramble32(std::uint32_t x) {
813 x *= 0xcc9e2d51;
814 x = (x << 15) | (x >> 17);
815 x *= 0x1b873593;
816 return x;
817 }
818};
819
820// --------------------------------------------------------------------------------------------------------------------
821// The Xoshiro state:
822// --------------------------------------------------------------------------------------------------------------------
823
828template<std::size_t N, std::unsigned_integral T, std::uint8_t A, std::uint8_t B>
829class xoshiro {
830public:
832 using word_type = T;
833
835 using array_type = std::array<T, N>;
836
838 static constexpr std::size_t word_count() { return N; }
839
841 static constexpr std::size_t bit_count() { return N * std::numeric_limits<T>::digits; }
842
844 static constexpr auto type_string() {
845 return std::format("xoshiro<{}x{},{},{}>", N, std::numeric_limits<T>::digits, A, B);
846 }
847
849 constexpr T operator[](std::size_t i) const { return m_state[i]; }
850
855 template<std::output_iterator<word_type> Dst>
856 constexpr void get_state(Dst dst) const {
857 std::copy(m_state.cbegin(), m_state.cend(), dst);
858 }
859
865 template<std::input_iterator Src>
866 requires std::convertible_to<std::iter_value_t<Src>, word_type>
867 constexpr void seed(Src b, Src e) {
868 std::copy(b, e, m_state.begin());
869 }
870
877 constexpr void step() {
878 if constexpr (N == 4) {
879 auto tmp = m_state[1] << A;
880 m_state[2] ^= m_state[0];
881 m_state[3] ^= m_state[1];
882 m_state[1] ^= m_state[2];
883 m_state[0] ^= m_state[3];
884 m_state[2] ^= tmp;
885 m_state[3] = std::rotl(m_state[3], B);
886 } else if constexpr (N == 8) {
887 auto tmp = m_state[1] << A;
888 m_state[2] ^= m_state[0];
889 m_state[5] ^= m_state[1];
890 m_state[1] ^= m_state[2];
891 m_state[7] ^= m_state[3];
892 m_state[3] ^= m_state[4];
893 m_state[4] ^= m_state[5];
894 m_state[0] ^= m_state[6];
895 m_state[6] ^= m_state[7];
896 m_state[6] ^= tmp;
897 m_state[7] = std::rotl(m_state[7], B);
898 } else {
899 // Get a useful'ish error message by pumping a deliberately false condition into static_assert(...).
900 static_assert(N < 0, "No xoshiro step() implementation for this number of words of state!");
901 }
902 }
903
912 template<std::output_iterator<word_type> Dst>
913 static constexpr void characteristic_coefficients(Dst dst) {
914 // In practice we have precomputed the p(x) polynomial for just a few xoshiro's with specific parameters.
915 if constexpr (std::is_same_v<T, uint32_t> && N == 4 && A == 9 && B == 11) {
916 std::array<T, N> p = {0xde18fc01, 0x1b489db6, 0x6254b1, 0xfc65a2};
917 std::copy(p.cbegin(), p.cend(), dst);
918 } else if constexpr (std::is_same_v<T, uint64_t> && N == 4 && A == 17 && B == 45) {
919 std::array<T, N> p = {0x9d116f2bb0f0f001, 0x280002bcefd1a5e, 0x4b4edcf26259f85, 0x3c03c3f3ecb19};
920 std::copy(p.cbegin(), p.cend(), dst);
921 } else if constexpr (std::is_same_v<T, uint64_t> && N == 8 && A == 11 && B == 21) {
922 std::array<T, N> p = {0xcf3cff0c00000001, 0x7fdc78d886f00c63, 0xf05e63fca6d7b781, 0x7a67058e7bbab6f0,
923 0xf11eef832e32518f, 0x51ba7c47edc758ad, 0x8f2d27268ce4b20b, 0x500055d8b77f};
924 std::copy(p.cbegin(), p.cend(), dst);
925 } else {
926 throw std::invalid_argument("xoshiro characteristic polynomial not pre-computed for given parameters!");
927 }
928 }
929
939 static constexpr auto characteristic_coefficients() {
940 array_type result;
941 characteristic_coefficients(result.begin());
942 return result;
943 }
944
945private:
946 std::array<T, N> m_state = {1};
947};
948
949// --------------------------------------------------------------------------------------------------------------------
950// The Xoroshiro State:
951// --------------------------------------------------------------------------------------------------------------------
952
957template<std::size_t N, std::unsigned_integral T, std::uint8_t A, std::uint8_t B, std::uint8_t C>
959public:
961 using word_type = T;
962
964 using array_type = std::array<T, N>;
965
967 static constexpr std::size_t word_count() { return N; }
968
970 static constexpr std::size_t bit_count() { return N * std::numeric_limits<T>::digits; }
971
973 static constexpr auto type_string() {
974 return std::format("xoroshiro<{}x{},{},{},{}>", N, std::numeric_limits<T>::digits, A, B, C);
975 }
976
980 constexpr T operator[](std::size_t i) const { return m_state[(i + m_final + 1) % N]; }
981
988 template<std::output_iterator<word_type> Dst>
989 constexpr void get_state(Dst dst) const {
990 for (auto i = 0uz; i < N; ++i, ++dst) *dst = operator[](i);
991 }
992
998 template<std::input_iterator Src>
999 requires std::convertible_to<std::iter_value_t<Src>, word_type>
1000 constexpr void seed(Src b, Src e) {
1001 std::copy(b, e, m_state.begin());
1002
1003 // Reset the ring buffer final index
1004 m_final = N - 1;
1005 }
1006
1008 constexpr void step() {
1009 // Depending on the size of `N` we either do an explicit or implicit array shuffle of the state array.
1010 if constexpr (N == 2)
1011 simple_step();
1012 else
1013 clever_step();
1014 }
1015
1024 template<std::output_iterator<word_type> Dst>
1025 static constexpr void characteristic_coefficients(Dst dst) {
1026 if constexpr (std::is_same_v<T, uint32_t> && N == 2 && A == 26 && B == 9 && C == 13) {
1027 std::array<T, N> p = {0x6e2286c1, 0x53be9da};
1028 std::copy(p.cbegin(), p.cend(), dst);
1029 } else if constexpr (std::is_same_v<T, uint64_t> && N == 2 && A == 24 && B == 16 && C == 37) {
1030 std::array<T, N> p = {0x95b8f76579aa001, 0x8828e513b43d5};
1031 std::copy(p.cbegin(), p.cend(), dst);
1032 } else if constexpr (std::is_same_v<T, uint64_t> && N == 2 && A == 49 && B == 21 && C == 28) {
1033 std::array<T, N> p = {0x8dae70779760b081, 0x31bcf2f855d6e5};
1034 std::copy(p.cbegin(), p.cend(), dst);
1035 } else if constexpr (std::is_same_v<T, uint64_t> && N == 16 && A == 25 && B == 27 && C == 36) {
1036 std::array<T, N> p = {0x5cfeb8cc48ddb211, 0xb73e379d035a06dd, 0x17d5100a20a0350e, 0x7550223f68f98cac,
1037 0x29d373b5c5ed3459, 0x3689b412ef70de48, 0xa1d3b6ee079a7cc6, 0x9bf0b669abd100f8,
1038 0x955c84e105f60997, 0x6ca140c61889cddd, 0xabaf68c5fc3a0e4a, 0xa46134526b83adc5,
1039 0x710704d05683d63, 0x580d080b44b606a2, 0x8040a0580158a1, 0x800081};
1040 std::copy(p.cbegin(), p.cend(), dst);
1041 } else {
1042 throw std::invalid_argument("xoroshiro characteristic polynomial not pre-computed for given parameters!");
1043 }
1044 }
1045
1055 static constexpr auto characteristic_coefficients() {
1056 array_type result;
1057 characteristic_coefficients(result.begin());
1058 return result;
1059 }
1060
1061private:
1062 std::array<T, N> m_state = {1}; // The state is an array of words -- should never be all zeros!
1063 std::size_t m_final = N - 1; // Current location of the final word of state.
1064
1067 constexpr void simple_step() {
1068 // Capture the current values in the first and final words of state
1069 T s0 = m_state[0];
1070 T s1 = m_state[N - 1];
1071
1072 // Shift most of the words of state down one slot
1073 // Note: It could help to unroll this loop at least once for larger N but the shuffle indices method is
1074 // better
1075 for (auto i = 0uz; i < N - 2; ++i) m_state[i] = m_state[i + 1];
1076
1077 // Update the first and final words of state
1078 s1 ^= s0;
1079 m_state[N - 2] = std::rotl(s0, A) ^ (s1 << B) ^ s1;
1080 m_state[N - 1] = std::rotl(s1, C);
1081 }
1082
1085 constexpr void clever_step() {
1086 // Which indices point to the current final & first words of state
1087 std::size_t i_final = m_final;
1088 std::size_t i_first = (m_final + 1) % N;
1089
1090 // Capture the current values in the final & first words of state
1091 T s_final = m_state[i_final];
1092 T s_first = m_state[i_first];
1093
1094 // Update the values for the final & first words of state
1095 s_final ^= s_first;
1096 m_state[i_final] = std::rotl(s_first, A) ^ (s_final << B) ^ s_final;
1097 m_state[i_first] = std::rotl(s_final, C);
1098
1099 // Step the index of the final word of state -- this shuffles the state array down a slot.
1100 m_final = i_first;
1101 }
1102};
1103
1104// --------------------------------------------------------------------------------------------------------------------
1105// The Scramblers:
1106//
1107// A Scrambler is a functor that is passed a State and returns a single unsigned output word.
1108// --------------------------------------------------------------------------------------------------------------------
1109
1116template<auto S, std::size_t w>
1117struct star {
1118 constexpr auto operator()(const auto& state) const { return S * state[w]; }
1119
1120 static constexpr auto type_string() { return std::format("star<{:x},{}>", S, w); }
1121};
1122
1132template<auto S, auto R, auto T, std::size_t w>
1134 constexpr auto operator()(const auto& state) const { return T * std::rotl(S * state[w], R); }
1135
1136 static constexpr auto type_string() { return std::format("star_star<{:x},{},{}>", S, R, w); }
1137};
1138
1145template<std::size_t w0, std::size_t w1>
1146struct plus {
1147 constexpr auto operator()(const auto& state) const { return state[w0] + state[w1]; }
1148
1149 static constexpr auto type_string() { return std::format("plus<{},{}>", w0, w1); }
1150};
1151
1159template<auto R, std::size_t w0, std::size_t w1>
1161 constexpr auto operator()(const auto& state) const { return std::rotl(state[w0] + state[w1], R) + state[w0]; }
1162
1163 static constexpr auto type_string() { return std::format("plus_plus<{},{},{}>", R, w0, w1); }
1164};
1165
1166// --------------------------------------------------------------------------------------------------------------------
1167// Type aliases for all 17 generators discussed in the Black & Vigna paper
1168// --------------------------------------------------------------------------------------------------------------------
1169
1170// clang-format off
1171// The analysed versions of the xoshiro engine with specific choices for A & B:
1172using xoshiro_4x32 = xoshiro<4, uint32_t, 9, 11>;
1173using xoshiro_4x64 = xoshiro<4, uint64_t, 17, 45>;
1174using xoshiro_8x64 = xoshiro<8, uint64_t, 11, 21>;
1175
1176// The analysed versions of the xoshiro engine with specific choices for A, B & C:
1177using xoroshiro_2x32 = xoroshiro<2, uint32_t, 26, 9, 13>;
1178using xoroshiro_2x64 = xoroshiro<2, uint64_t, 24, 16, 37>;
1179using xoroshiro_2x64b = xoroshiro<2, uint64_t, 49, 21, 28>; // Alternative for 2x64 case
1180using xoroshiro_16x64 = xoroshiro<16, uint64_t, 25, 27, 36>;
1181
1182// The analyzed versions of the xoshiro generators:
1183using xoshiro_4x32_plus = generator<xoshiro_4x32, plus<0, 3>>;
1184using xoshiro_4x32_plus_plus = generator<xoshiro_4x32, plus_plus<7, 0, 3>>;
1185using xoshiro_4x32_star_star = generator<xoshiro_4x32, star_star<5, 7, 9, 1>>;
1186using xoshiro_4x64_plus = generator<xoshiro_4x64, plus<0, 3>>;
1187using xoshiro_4x64_plus_plus = generator<xoshiro_4x64, plus_plus<23, 0, 3>>;
1188using xoshiro_4x64_star_star = generator<xoshiro_4x64, star_star<5, 7, 9, 1>>;
1189using xoshiro_8x64_plus = generator<xoshiro_8x64, plus<2, 0>>;
1190using xoshiro_8x64_plus_plus = generator<xoshiro_8x64, plus_plus<17, 2, 0>>;
1191using xoshiro_8x64_star_star = generator<xoshiro_8x64, star_star<5, 7, 9, 1>>;
1192
1193// The analysed versions of the xoroshiro generators:
1194using xoroshiro_2x32_star = generator<xoroshiro_2x32, star<0x9E3779BB, 0>>;
1195using xoroshiro_2x32_star_star = generator<xoroshiro_2x32, star_star<0x9E3779BBu, 5, 5, 0>>;
1196using xoroshiro_2x64_plus = generator<xoroshiro_2x64, plus<0, 1>>;
1197using xoroshiro_2x64_plus_plus = generator<xoroshiro_2x64b, plus_plus<17, 0, 1>>;
1198using xoroshiro_2x64_star_star = generator<xoroshiro_2x64, star_star<5, 7, 9, 0>>;
1199using xoroshiro_16x64_plus_plus = generator<xoroshiro_16x64, plus_plus<23, 15, 0>>;
1200using xoroshiro_16x64_star = generator<xoroshiro_16x64, star<0x9e3779b97f4a7c13, 0>>;
1201using xoroshiro_16x64_star_star = generator<xoroshiro_16x64, star_star<5, 7, 9, 0>>;
1202// clang-format on
1203
1205using rng32 = xoshiro_4x32_star_star;
1206
1208using rng64 = xoshiro_4x64_star_star;
1209
1211using rng = rng64;
1212
1213// --------------------------------------------------------------------------------------------------------------------
1214// Partitions of random number stream:
1215//
1216// For parallel processing applications it can be useful to split a single random number stream into a number of
1217// non-overlapping "partitions". Then different computational threads can get their "own set" of random numbers without
1218// worrying about stream overlaps etc.
1219//
1220// The partitions need to be very large so this only works for States where we can jump far ahead in an efficient way.
1221// --------------------------------------------------------------------------------------------------------------------
1222
1230template<typename RNG>
1232public:
1241 partition(const RNG& rng, std::size_t n_partitions) : m_rng{rng} {
1242 // Make sure the requested number of partitions makes sense -- silently fix any issues.
1243 if (n_partitions == 0) n_partitions = 1;
1244
1245 // How many bits of state in the RNG type?
1246 auto n_bits = RNG::bit_count();
1247
1248 // The period of the generator is `2^n_bits` so each partition ideally has size `2^n_bits / n_partitions`.
1249 // That number will probably overflow a std::size_t so we must instead keep everything in log 2 form.
1250 // First we find the smallest `n` such that `2^n >= n_partitions - 1`.
1251 // Note if `n_partitions` is 128 the following gives `n = 7` and does the same if `n = 100`.
1252 auto n = static_cast<std::size_t>(std::bit_width(n_partitions - 1));
1253
1254 // We will create `2^n` partitions which is probably more than needed but the wastage is negligible.
1255 // To create those `2^n` partitions we must be able to jump ahead `2^(n_bits - n)` steps many times.
1256 auto power_2 = n_bits - n;
1257
1258 // Precompute the jump coefficients to advance the generator `2^power_2` steps i.e. along to the next partition.
1259 m_jump_coefficients = xso::jump_coefficients<typename RNG::state_type>(power_2, true);
1260 }
1261
1264 RNG next() {
1265 // We already have a pre-baked generator seeded at the right spot ready to go.
1266 RNG result = m_rng;
1267
1268 // Prep for the next call by jumping the parent copy once more along its stream.
1269 m_rng.jump(m_jump_coefficients);
1270
1271 // And return the pre-baked one ...
1272 return result;
1273 }
1274
1275private:
1276 using array_type = typename RNG::array_type;
1277
1278 RNG m_rng;
1279 array_type m_jump_coefficients;
1280};
1281
1282// --------------------------------------------------------------------------------------------------------------------
1283// Non-member functions for computing jump polynomials:
1284// --------------------------------------------------------------------------------------------------------------------
1285
1299template<typename State>
1300constexpr auto
1301jump_coefficients(std::size_t n, bool n_is_log2) {
1302
1303 // Retrieve the low order coefficients of characteristic polynomial --- this can throw if not precomputed.
1304 std::array<typename State::word_type, State::word_count()> char_coefficients;
1305 State::characteristic_coefficients(char_coefficients.begin());
1306
1307 // The jump polynomial is x^J mod c(x) -- that computation expects to be handed p(x) where c(x) = x^n + p(x).
1308 return xso::reduce(char_coefficients, n, n_is_log2);
1309}
1310
1311// --------------------------------------------------------------------------------------------------------------------
1312// We have some "internal" functions that reduce x^J mod c(x) --- needed for the jump polynomial computations.
1313// These re-implement a more general reduction method in the gf2::BitPolynomial class (https://nessan/github.io/gf2/).
1314// Repeated here to make `xoshiro.h` self-contained.
1315//
1316// Not part of the public API but used for the jump calculations in the generator class.
1317// --------------------------------------------------------------------------------------------------------------------
1318
1319// Function to "riffle" a `src` word into two other words `lo` and `hi` containing the bits from `src`
1320// interleaved with zeros.
1321//
1322// So the 8-bit word `abcdefgh` fills `lo` as `a0b0c0d0` and `hi` as `e0f0g0h0`.
1323//
1324// # Example
1325// ```
1326// std::uint8_t src = 0b1111'1111;
1327// std::uint8_t lo, hi;
1328// xso::riffle(src, lo, hi);
1329// assert_eq(lo, 0b01010101, "lo is {:08b}", lo);
1330// assert_eq(hi, 0b01010101, "hi is {:08b}", hi);
1331// ```
1332// *NOTE:** This function is "public" for testing purposes only -- it is not part of the normal interface.
1333template<std::unsigned_integral word_type>
1334constexpr void
1335riffle(word_type src, word_type& lo, word_type& hi) {
1336 // Constants
1337 constexpr std::size_t bits_per_word = std::numeric_limits<word_type>::digits;
1338 constexpr std::size_t half_bits = bits_per_word / 2;
1339 constexpr word_type one{1};
1340 constexpr word_type ones = std::numeric_limits<word_type>::max();
1341
1342 // Split the src into lo and hi halves.
1343 lo = src & (ones >> half_bits);
1344 hi = src >> half_bits;
1345
1346 // Some magic to interleave the respective halves with zeros.
1347 for (auto i = bits_per_word / 4; i > 0; i /= 2) {
1348 word_type div = word_type(one << i) | one;
1349 word_type msk = ones / div;
1350 lo = (lo ^ (lo << i)) & msk;
1351 hi = (hi ^ (hi << i)) & msk;
1352 }
1353}
1354
1355// Function that riffles a `src` array of unsigneds into two others `lo` and `hi` which will be filled with the
1356// bits from `src` interleaved with zeros.
1357//
1358// We treat `lo` and `hi` as contiguous storage and fill the elements of `lo` first and then `hi`.
1359// This allows us to optionally reuse `src` for the output array `lo` -- the call `riffle(src, src, hi)` will work.
1360//
1361// # Example
1362// ```
1363// std::array<std::uint8_t, 2> src = {0b1111'1111, 0b1111'1111};
1364// std::array<std::uint8_t, 2> lo, hi;
1365// xso::riffle(src, lo, hi);
1366// assert_eq(lo, (std::array<std::uint8_t, 2>{0b01010101, 0b01010101}));
1367// assert_eq(hi, (std::array<std::uint8_t, 2>{0b01010101, 0b01010101}));
1368// ```
1369// *NOTE:** This function is "public" for testing purposes only -- it is not part of the normal interface.
1370template<std::unsigned_integral word_type, std::size_t N>
1371static constexpr void
1372riffle(const std::array<word_type, N>& src, std::array<word_type, N>& lo, std::array<word_type, N>& hi) {
1373 // We will riffle each word in src into two other words x & y
1374 word_type x, y;
1375
1376 // We work through `src` in reverse order -- this allows the reuse of `src` for `lo`!
1377 // Treating [lo|hi] as contiguous storage, first working back through hi and then back through lo.
1378 for (std::size_t i = N; i-- > 0;) {
1379 riffle(src[i], x, y);
1380 if (2 * i + 1 > N) {
1381 // Both x & y go in hi -- note that if 2i + 1 - N > 0 then 2i - N is >= 0.
1382 hi[2 * i - N] = x;
1383 hi[2 * i + 1 - N] = y;
1384 } else if (2 * i + 1 == N) {
1385 // Straddling situation where y goes in the first word of hi and x in the last word of lo.
1386 lo[N - 1] = x;
1387 hi[0] = y;
1388 } else {
1389 // Need to pop both x & y into the lo array.
1390 lo[2 * i] = x;
1391 lo[2 * i + 1] = y;
1392 }
1393 }
1394}
1395
1396// Function that computes r(x) = x^e mod c(x) in GF(2) where e = J or 2^J and J is an unsigned integer argument.
1397//
1398// The polynomial c(x) is monic of degree n so c(x) = x^n + p(x) where p(x) is a polynomial of degree less than n.
1399// We are passed the n coefficients of p(x) packed into an array of unsigned integers.
1400// We return the coefficients of r(x) = x^e mod c(x) packed into an array of unsigned integers in the same format.
1401//
1402// # Note
1403// Any linting tool you use will (reasonably) complain that the complexity of this method is too high!
1404// The method is a direct re-implementation of the `gf2::BitPolynomial::reduce` method which is also complex but
1405// easier to understand as it is properly factored. The version here is is less general as it assumes that the
1406// degree of p(x) is a multiple of 32.
1407//
1408// *NOTE:** This method "public" for testing purposes only -- it is not part of the normal interface.
1409template<std::unsigned_integral word_type, std::size_t N>
1410constexpr auto
1411reduce(const std::array<word_type, N>& p, std::size_t J, bool J_is_log2) {
1412
1413 // We will return the coefficients of r(x) = x^e mod c(x) packed into an array of unsigned integers of this type.
1414 using array_type = std::array<word_type, N>;
1415
1416 // Constant we use to indicate "no such position"/"not found" and a couple of others.
1417 constexpr auto npos = static_cast<std::size_t>(-1);
1418 constexpr word_type one{1};
1419
1420 // The polynomial c(x) = x^n + p(x) where p(x) = p_0 + p_1 x + ... + p_{n-1} x^{n-1} and n is:
1421 constexpr std::size_t bits_per_word = std::numeric_limits<word_type>::digits;
1422 constexpr std::size_t n = N * bits_per_word;
1423
1424 // lambda: Returns the index of the word that holds p_i.
1425 auto word = [=](std::size_t i) { return i / bits_per_word; };
1426
1427 // lambda: Returns the bit location of p_i inside the word that holds it.
1428 auto bit = [=](std::size_t i) { return i % bits_per_word; };
1429
1430 // lambda: Returns a mask that isolates p_i within the word that holds it.
1431 auto mask = [=](std::size_t i) { return word_type{one << bit(i)}; };
1432
1433 // lambda: Returns true if poly_i is 1.
1434 auto test = [=](const auto& poly, std::size_t i) -> bool { return poly[word(i)] & mask(i); };
1435
1436 // lambda: Sets poly_i to 1.
1437 auto set = [=](auto& poly, std::size_t i) { poly[word(i)] |= mask(i); };
1438
1439 // lambda: Returns the index for the least significant set bit in the argument or `npos` if none set.
1440 auto lsb = [=](word_type w) { return w == 0 ? npos : static_cast<std::size_t>(std::countr_zero(w)); };
1441
1442 // lambda: Returns the index for the most significant set bit in the argument or `npos` if none set.
1443 auto msb = [=](word_type w) { return static_cast<std::size_t>(std::bit_width(w) - 1); };
1444
1445 // lambda: Returns the first set coefficient in poly or `npos` if the coefficients are all zero.
1446 auto first_set = [=](const auto& poly) {
1447 for (auto i = 0uz; i < N; ++i)
1448 if (poly[i] != 0) return i * bits_per_word + lsb(poly[i]);
1449 return npos;
1450 };
1451
1452 // lambda: Returns the final set coefficient in poly or `npos` if the coefficients are all zero.
1453 auto final_set = [=](const auto& poly) {
1454 std::size_t i = N;
1455 while (i--)
1456 if (poly[i] != 0) return i * bits_per_word + msb(poly[i]);
1457 return npos;
1458 };
1459
1460 // lambda: Returns true if the highest coefficient in `poly` is set (then poly is said to be monic).
1461 auto monic = [=](const auto& poly) {
1462 constexpr std::size_t complement = bits_per_word - 1;
1463 constexpr auto final_bit_mask = word_type{one << complement};
1464 return poly[N - 1] & final_bit_mask;
1465 };
1466
1467 // lambda: Computes lhs <- lhs + rhs which in GF(2) is equivalent to lhs <- lhs^rhs.
1468 auto add = [=](auto& lhs, const auto& rhs) {
1469 for (auto i = 0uz; i < N; ++i) lhs[i] ^= rhs[i];
1470 };
1471
1472 // lambda: Performs a one place shift on the polynomial coefficients stored poly.
1473 // Shift is to the the right if you think the elements are in vector order [p0,p1,p2,p3] -> [0,p0,p1,p2].
1474 // Shift is to the left when you think in bit order [b3,b2,b1,b0] -> [b2,b1,b0,0].
1475 auto shift = [=](auto& poly) {
1476 constexpr std::size_t complement = bits_per_word - 1;
1477 for (std::size_t i = N - 1; i > 0; --i) {
1478 auto l = static_cast<word_type>(poly[i] << 1);
1479 auto r = static_cast<word_type>(poly[i - 1] >> complement);
1480 poly[i] = l | r;
1481 }
1482 poly[0] <<= 1;
1483 };
1484
1485 // lambda: If degree[poly] < n, this performs poly(x) <- x*poly(x) mod c(x) where c(x) = x^n + p(x).
1486 auto times_x_step = [&](auto& poly) {
1487 bool add_p = monic(poly);
1488 shift(poly);
1489 if (add_p) add(poly, p);
1490 };
1491
1492 // We precompute x^{n + i} mod c(x) for i = 0, ..., n-1 starting from the known x^n mod c(x) = p.
1493 // Each x^{n + i} mod c(x) is a word-vector of coefficients and we put the lot into a std::vector.
1494 std::vector<array_type> power_mod(n);
1495 power_mod[0] = p;
1496 for (std::size_t i = 1; i < n; ++i) {
1497 power_mod[i] = power_mod[i - 1];
1498 times_x_step(power_mod[i]);
1499 }
1500
1501 // Some work space we use below.
1502 array_type hi;
1503
1504 // lambda: If degree[poly] < n, performs: poly(x) <- poly(x)^2 mod c(x) where as usual c(x) = x^n + p(x).
1505 auto square_step = [&](auto& poly) {
1506 // Compute poly(x)^2 -- in GF(2) this means interspersing all the coefficients with zeros.
1507 // We actually riffle poly directly into two arrays lo & hi so that poly(x)^2 = lo(x) + x^n hi(x).
1508 // This only works because we assume n some multiple of N (i.e. all bits in poly matter).
1509 // NOTE: Our riffle method above for arrays lets us reuse the poly array for lo.
1510 riffle(poly, poly, hi);
1511
1512 // poly(x)^2 mod c(x) now is poly(x) + x^n hi(x) mod c(x) as degree[poly] < n.
1513 // Add the x^n hi(x) mod c(x) term-by-term noting that at most every second term in hi(x) is 1.
1514 auto hi_first = first_set(hi);
1515 if (hi_first != npos) {
1516 auto hi_final = final_set(hi);
1517 for (std::size_t i = hi_first; i <= hi_final; i += 2)
1518 if (test(hi, i)) add(poly, power_mod[i]);
1519 }
1520 };
1521
1522 // Initialize our return value to all zeros.
1523 array_type result;
1524 result.fill(0);
1525
1526 // Case: e = 2^J -- we just do J squaring steps starting from r(x) = x to get to x^(2^J) mod c(x).
1527 if (J_is_log2) {
1528 set(result, 1);
1529 for (std::size_t j = 0; j < J; ++j) square_step(result);
1530 return result;
1531 }
1532
1533 // Case e = J < n: Then x^J mod c(x) = x^J so we can set the appropriate coefficient in r and return.
1534 if (J < n) {
1535 set(result, J);
1536 return result;
1537 }
1538
1539 // Case e = J = n: Then x^J mod c(x) = p(x).
1540 if (J == n) return p;
1541
1542 // Case e = J > n: We use a square & multiply algorithm:
1543 // Note that if e.g. J = 0b00010111 then std::bit_floor(J) = 0b00010000.
1544 std::size_t J_bit = std::bit_floor(J);
1545
1546 // Start with r(x) = x mod c(x) which takes care of the most significant binary digit in J.
1547 set(result, 1);
1548 J_bit >>= 1;
1549
1550 // And off we go ...
1551 while (J_bit) {
1552
1553 // Always do a square step and then a times_x step if necessary (i.e. if current bit in J is set).
1554 square_step(result);
1555 if (J & J_bit) times_x_step(result);
1556
1557 // On to the next bit position in n.
1558 J_bit >>= 1;
1559 }
1560 return result;
1561}
1562
1563#ifdef GF2
1564
1565// --------------------------------------------------------------------------------------------------------------------
1566// Non-member functions that are only defined if we have access to the `gf2` library.
1567// See https://nessan.github.io/gf2/ for tools for working with polynomials and matrices over GF(2).
1568// --------------------------------------------------------------------------------------------------------------------
1569
1579template<typename State>
1580auto
1582
1583 using word_type = typename State::word_type;
1584
1585 // Some constants for our state size.
1586 constexpr auto n_words = State::word_count();
1587 constexpr auto n_bits = State::bit_count();
1588
1589 // The transition matrix will be a square n_bits x n_bits matrix over GF(2).
1590 gf2::BitMatrix<word_type> result{n_bits, n_bits};
1591
1592 // Some work-space in word and bit space
1593 gf2::BitVector<word_type> bits{n_bits};
1594 std::array<word_type, n_words> words;
1595
1596 // Create an State instance we will use to step through unit states.
1597 State state;
1598
1599 // We get the columns of the transition matrix by looking at the action of `step()` on all the unit states.
1600 for (std::size_t k = 0; k < n_bits; ++k) {
1601
1602 // Create the k'th unit state (i.e. the state just has the k'th bit set and all others are zero)
1603 bits.set_all(false);
1604 bits.set(k, true);
1605
1606 // Translate that unit bit-vector into an array of words.
1607 bits.to_words(words.begin());
1608
1609 // Seed the state from those words.
1610 state.seed(words.cbegin(), words.cend());
1611
1612 // Advance the state by one step.
1613 state.step();
1614
1615 // Translate the new state to bit-space.
1616 for (auto i = 0uz; i < n_words; ++i) bits.set_word(i, state[i]);
1617
1618 // Store those bits into column `k` of the transition matrix.
1619 // NOTE: Columnar access for a gf2::BitMatrix must be done element by element.
1620 for (auto i = 0uz; i < n_bits; ++i) result(i, k) = bits[i];
1621 }
1622
1623 return result;
1624}
1625
1630template<typename State>
1631auto
1634 return T.characteristic_polynomial();
1635}
1636
1644template<typename State>
1645auto
1646jump_polynomial(gf2::BitPolynomial<typename State::word_type> const& c, std::size_t N, bool N_is_log2 = false) {
1647 return c.reduce_x_to_the(N, N_is_log2);
1648}
1649
1650#endif
1651
1652} // namespace xso
1653
1654// --------------------------------------------------------------------------------------------------------------------
1655// Formatting and output stream support for our generators:
1656// --------------------------------------------------------------------------------------------------------------------
1657
1658// A concept that matches any type that has an accessible `type_string()` class `method.
1659template<typename T>
1660concept has_type_string_class_method = requires {
1661 { T::type_string() } -> std::convertible_to<std::string>;
1662};
1663
1667template<has_type_string_class_method T>
1668struct std::formatter<T> {
1669
1671 constexpr auto parse(const std::format_parse_context& ctx) {
1672 auto it = ctx.begin();
1673 assert(it == ctx.end() || *it == '}');
1674 return it;
1675 }
1676
1678 template<class FormatContext>
1679 auto format(const T&, FormatContext& ctx) const {
1680 return std::format_to(ctx.out(), "{}", T::type_string());
1681 }
1682};
1683
1687template<has_type_string_class_method T>
1688std::ostream&
1689operator<<(std::ostream& s, const T&) {
1690 s << T::type_string();
1691 return s;
1692}
A pseudorandom number generator combining a State and a Scrambler.
Definition xoshiro.h:69
static constexpr auto type_string()
Class method that returns a name for this type of generator — combining State and Scrambler names.
Definition xoshiro.h:114
static constexpr result_type max() noexcept
Returns the largest value this generator can produce.
Definition xoshiro.h:147
constexpr word_type operator[](std::size_t i) const
Returns the i'th state word.
Definition xoshiro.h:313
constexpr void jump(const array_type &jump_coefficients)
Efficiently jumps the generator's state forward by J steps where J can be huge (e....
Definition xoshiro.h:671
typename State::array_type array_type
A convenience container type to hold the full state of this generator, jump polynomial coefficients,...
Definition xoshiro.h:84
constexpr void step()
Advances the state by one step.
Definition xoshiro.h:294
static constexpr result_type min() noexcept
Returns the smallest value this generator can produce.
Definition xoshiro.h:136
State state_type
The State used for by generator type – one of the two state classes defined below.
Definition xoshiro.h:75
constexpr bool flip(double p=0.5)
Sampling method that flips a coin, where the probability of getting true is p (defaults to 0....
Definition xoshiro.h:561
constexpr std::size_t roll(std::size_t n_sides=6)
Sampling method that rolls a dice with an arbitrary number of sides (defaults to the usual 6).
Definition xoshiro.h:548
constexpr void seed(Src b, Src e)
Seeds the generator from an iteration of unsigned words which are all copied into the state.
Definition xoshiro.h:285
constexpr Dst sample(Src b, Src e, Dst dst, std::size_t n)
Sampling method that picks n elements without replacement from an iteration [b, e) and puts them in d...
Definition xoshiro.h:460
constexpr void shuffle(Container &container)
This method shuffles all the elements of a container.
Definition xoshiro.h:600
constexpr generator()
The default constructor seeds the full underlying state randomly.
Definition xoshiro.h:162
static constexpr std::size_t word_count()
Class method that returns the number of words of state for this type of generator.
Definition xoshiro.h:92
constexpr Dst sample(Distribution auto &dist, Dst dst, std::size_t n)
Sampling method that takes n samples from a distribution and puts them into a destination iterator.
Definition xoshiro.h:530
constexpr void seed(word_type seed)
Seeds the generator quickly but probably not well from a single unsigned integer value.
Definition xoshiro.h:254
constexpr auto sample(const Src &src, Dst dst, std::size_t n)
Sampling method that picks n elements without replacement from a container and puts them in dst.
Definition xoshiro.h:490
static constexpr std::size_t bit_count()
Class method that returns the number of bits of state for this type of generator.
Definition xoshiro.h:100
static constexpr auto characteristic_coefficients()
Class method that returns the state's precomputed characteristic polynomial coefficients packed into ...
Definition xoshiro.h:748
void discard(std::uint64_t z)
Discards the next z iterations in the random number sequence.
Definition xoshiro.h:614
constexpr T index(T len)
Sampling method that returns a single index from a uniform distribution over [0, len-1].
Definition xoshiro.h:381
constexpr void seed()
Seeds the full state to random starting values.
Definition xoshiro.h:216
constexpr generator(word_type s)
Construct a generator quickly but not well from a single unsigned integer value.
Definition xoshiro.h:174
constexpr result_type operator()()
Returns the next random value from the generator which is a result_type unsigned integer.
Definition xoshiro.h:302
constexpr generator(Iter b, Iter e)
Construct and seed from an iteration of unsigned words which are all copied into the state.
Definition xoshiro.h:195
typename State::word_type word_type
This generator type packs its state into words of this type (in practice, 32 or 64 bit unsigneds).
Definition xoshiro.h:81
constexpr auto jump(std::size_t n, bool n_is_log2=false)
Jumps the generator's state forward by J steps where J is either n or 2^n to accommodate huge jumps t...
Definition xoshiro.h:642
Scrambler scrambler_type
The Scrambler used for by generator type – one of the scrambler classes defined below.
Definition xoshiro.h:78
constexpr T sample(T a, T b)
Sampling method that returns a single integer value from a uniform distribution over [a,...
Definition xoshiro.h:343
constexpr auto sample(Distribution auto &dist)
Sampling method that returns a single random variate drawn from a distribution.
Definition xoshiro.h:508
static constexpr void characteristic_coefficients(Dst dst)
Class method that fills a destination iterator with the state's precomputed characteristic polynomial...
Definition xoshiro.h:728
constexpr void get_state(Dst dst) const
Copies the whole state into the destination iterator dst.
Definition xoshiro.h:320
constexpr void shuffle(Iter b, Iter e)
This method shuffles the elements in an iteration.
Definition xoshiro.h:581
constexpr auto sample(T b, T e)
Sampling method that returns a single value from an iteration – all elements are equally likely to be...
Definition xoshiro.h:402
constexpr auto sample(const Container &container)
Sampling method that returns a single value from a container – all elements are equally likely to be ...
Definition xoshiro.h:433
word_type result_type
The unsigned integer type returned by the generator's operator()() method.
Definition xoshiro.h:125
void jump(gf2::BitPolynomial< word_type > const &jump_poly)
Jumps a state/generator ahead in its random number stream by J steps.
Definition xoshiro.h:763
partition(const RNG &rng, std::size_t n_partitions)
Constructs a partition for the passed parent random number generator rng.
Definition xoshiro.h:1241
RNG next()
Returns the next generator that is seeded at the start of the next sub-stream of the parent random nu...
Definition xoshiro.h:1264
The state for the Xoroshiro family of pseudorandom number generators.
Definition xoshiro.h:958
static constexpr auto type_string()
Class method that returns a name for this state.
Definition xoshiro.h:973
constexpr void get_state(Dst dst) const
Copies the whole state into the destination iterator dst.
Definition xoshiro.h:989
constexpr void step()
Advance the state by one step.
Definition xoshiro.h:1008
T word_type
The type of the words of state.
Definition xoshiro.h:961
constexpr T operator[](std::size_t i) const
Returns the i'th state word.
Definition xoshiro.h:980
static constexpr std::size_t bit_count()
Class method that returns the number of bits of state.
Definition xoshiro.h:970
static constexpr auto characteristic_coefficients()
Class method that returns the state's precomputed characteristic polynomial coefficients packed into ...
Definition xoshiro.h:1055
static constexpr std::size_t word_count()
Class method that returns the number of words in the underlying state.
Definition xoshiro.h:967
constexpr void seed(Src b, Src e)
Sets the state from an iteration of words.
Definition xoshiro.h:1000
static constexpr void characteristic_coefficients(Dst dst)
Class method that fills a destination iterator with our precomputed characteristic polynomial coeffic...
Definition xoshiro.h:1025
std::array< T, N > array_type
A convenience container type to hold the full state of this generator, jump polynomial coefficients,...
Definition xoshiro.h:964
The state for the Xoshiro family of pseudorandom number generators.
Definition xoshiro.h:829
constexpr T operator[](std::size_t i) const
Returns the i'th state word.
Definition xoshiro.h:849
constexpr void step()
Advance the state by one step.
Definition xoshiro.h:877
static constexpr auto characteristic_coefficients()
Class method that returns the state's precomputed characteristic polynomial coefficients packed into ...
Definition xoshiro.h:939
static constexpr std::size_t word_count()
Class method that returns the number of words in the underlying state.
Definition xoshiro.h:838
static constexpr std::size_t bit_count()
Class method that returns the number of bits of state.
Definition xoshiro.h:841
T word_type
The state is stored as an array of N words of type T.
Definition xoshiro.h:832
static constexpr void characteristic_coefficients(Dst dst)
Class method that fills a destination iterator with our precomputed characteristic polynomial coeffic...
Definition xoshiro.h:913
std::array< T, N > array_type
A convenience container type to hold the full state of this generator, jump polynomial coefficients,...
Definition xoshiro.h:835
static constexpr auto type_string()
Class method that returns a name for this type of state.
Definition xoshiro.h:844
constexpr void get_state(Dst dst) const
Copies the whole state into the destination iterator dst.
Definition xoshiro.h:856
constexpr void seed(Src b, Src e)
Sets the state from an iteration of words.
Definition xoshiro.h:867
A C++ concept that lets us distinguish standard distribution types from other types.
Definition xoshiro.h:46
auto format(const T &, FormatContext &ctx) const
Push out a formatted xso::generator using its type_string() method.
Definition xoshiro.h:1679
constexpr auto parse(const std::format_parse_context &ctx)
Parse the format specifier – currently only handle the default empty specifier.
Definition xoshiro.h:1671
The plus_plus functor can be passed a state array and will return the sum of two state words rotated ...
Definition xoshiro.h:1160
The plus functor can be passed a state array and will return the sum of two state words.
Definition xoshiro.h:1146
The star_star functor multiplies a state word by a constant, rotates the result, then multiplies by a...
Definition xoshiro.h:1133
The star functor multiplies a state word by a constant.
Definition xoshiro.h:1117
auto transition_matrix()
Returns the transition matrix for a state/generator type as a gf2::BitMatrix.
Definition xoshiro.h:1581
xoshiro_4x32_star_star rng32
The recommended 32-bit output generator – used as xso::rng32.
Definition xoshiro.h:1205
std::ostream & operator<<(std::ostream &s, const T &)
The usual output stream operator for an xso::generator, State, or Scrambler.
Definition xoshiro.h:1689
rng64 rng
The recommended default generator for most usage – used as xso::rng.
Definition xoshiro.h:1211
constexpr auto jump_coefficients(std::size_t J, bool J_is_log2=false)
Returns the coefficients of the jump polynomial that can be used to jump a generator/state ahead by ...
Definition xoshiro.h:1301
xoshiro_4x64_star_star rng64
The recommended 64-bit output generator – used as xso::rng64.
Definition xoshiro.h:1208
auto characteristic_polynomial()
Returns the characteristic polynomial for our state's transition matrix as a gf2::BitPolynomial.
Definition xoshiro.h:1632
auto jump_polynomial(gf2::BitPolynomial< typename State::word_type > const &c, std::size_t N, bool N_is_log2=false)
Returns a jump polynomial that moves the generator type J steps ahead in its random number stream.
Definition xoshiro.h:1646