<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("{} with {}", 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
1118template<auto S, std::size_t w = 0>
1119struct star {
1120 constexpr auto operator()(const auto& state) const { return S * state[w]; }
1121
1122 static constexpr auto type_string() { return std::format("star<{:x},{}>", S, w); }
1123};
1124
1135template<auto S, auto R, auto T, std::size_t w = 0>
1137 constexpr auto operator()(const auto& state) const { return T * std::rotl(S * state[w], R); }
1138
1139 static constexpr auto type_string() { return std::format("star_star<{:x},{},{:x},{}>", S, R, T, w); }
1140};
1141
1149template<std::size_t w0 = 0, std::size_t w1 = 1>
1150struct plus {
1151 constexpr auto operator()(const auto& state) const { return state[w0] + state[w1]; }
1152
1153 static constexpr auto type_string() { return std::format("plus<{},{}>", w0, w1); }
1154};
1155
1161template<auto R, std::size_t w0, std::size_t w1>
1163 constexpr auto operator()(const auto& state) const { return std::rotl(state[w0] + state[w1], R) + state[w0]; }
1164
1165 static constexpr auto type_string() { return std::format("plus_plus<{},{},{}>", R, w0, w1); }
1166};
1167
1168// --------------------------------------------------------------------------------------------------------------------
1169// Type aliases for all 17 generators discussed in the Black & Vigna paper
1170// --------------------------------------------------------------------------------------------------------------------
1171
1172// clang-format off
1173// The analysed versions of the xoshiro engine with specific choices for A & B:
1174using xoshiro_4x32 = xoshiro<4, uint32_t, 9, 11>;
1175using xoshiro_4x64 = xoshiro<4, uint64_t, 17, 45>;
1176using xoshiro_8x64 = xoshiro<8, uint64_t, 11, 21>;
1177
1178// The analysed versions of the xoshiro engine with specific choices for A, B & C:
1179using xoroshiro_2x32 = xoroshiro<2, uint32_t, 26, 9, 13>;
1180using xoroshiro_2x64 = xoroshiro<2, uint64_t, 24, 16, 37>;
1181using xoroshiro_2x64b = xoroshiro<2, uint64_t, 49, 21, 28>; // Alternative for 2x64 case
1182using xoroshiro_16x64 = xoroshiro<16, uint64_t, 25, 27, 36>;
1183
1184// The analyzed versions of the xoshiro generators:
1185using xoshiro_4x32_plus = generator<xoshiro_4x32, plus<0, 3>>;
1186using xoshiro_4x32_plus_plus = generator<xoshiro_4x32, plus_plus<7, 0, 3>>;
1187using xoshiro_4x32_star_star = generator<xoshiro_4x32, star_star<5, 7, 9, 1>>;
1188using xoshiro_4x64_plus = generator<xoshiro_4x64, plus<0, 3>>;
1189using xoshiro_4x64_plus_plus = generator<xoshiro_4x64, plus_plus<23, 0, 3>>;
1190using xoshiro_4x64_star_star = generator<xoshiro_4x64, star_star<5, 7, 9, 1>>;
1191using xoshiro_8x64_plus = generator<xoshiro_8x64, plus<2, 0>>;
1192using xoshiro_8x64_plus_plus = generator<xoshiro_8x64, plus_plus<17, 2, 0>>;
1193using xoshiro_8x64_star_star = generator<xoshiro_8x64, star_star<5, 7, 9, 1>>;
1194
1195// The analysed versions of the xoroshiro generators:
1196using xoroshiro_2x32_star = generator<xoroshiro_2x32, star<0x9E3779BB, 0>>;
1197using xoroshiro_2x32_star_star = generator<xoroshiro_2x32, star_star<0x9E3779BB, 5, 5, 0>>;
1198using xoroshiro_2x64_plus = generator<xoroshiro_2x64, plus<0, 1>>;
1199using xoroshiro_2x64_plus_plus = generator<xoroshiro_2x64b, plus_plus<17, 0, 1>>;
1200using xoroshiro_2x64_star_star = generator<xoroshiro_2x64, star_star<5, 7, 9, 0>>;
1201using xoroshiro_16x64_plus_plus = generator<xoroshiro_16x64, plus_plus<23, 15, 0>>;
1202using xoroshiro_16x64_star = generator<xoroshiro_16x64, star<0x9e3779b97f4a7c13, 0>>;
1203using xoroshiro_16x64_star_star = generator<xoroshiro_16x64, star_star<5, 7, 9, 0>>;
1204// clang-format on
1205
1207using rng32 = xoshiro_4x32_star_star;
1208
1210using rng64 = xoshiro_4x64_star_star;
1211
1213using rng = rng64;
1214
1215// --------------------------------------------------------------------------------------------------------------------
1216// Partitions of random number stream:
1217//
1218// For parallel processing applications it can be useful to split a single random number stream into a number of
1219// non-overlapping "partitions". Then different computational threads can get their "own set" of random numbers without
1220// worrying about stream overlaps etc.
1221//
1222// The partitions need to be very large so this only works for States where we can jump far ahead in an efficient way.
1223// --------------------------------------------------------------------------------------------------------------------
1224
1232template<typename RNG>
1234public:
1243 partition(const RNG& rng, std::size_t n_partitions) : m_rng{rng} {
1244 // Make sure the requested number of partitions makes sense -- silently fix any issues.
1245 if (n_partitions == 0) n_partitions = 1;
1246
1247 // How many bits of state in the RNG type?
1248 auto n_bits = RNG::bit_count();
1249
1250 // The period of the generator is `2^n_bits` so each partition ideally has size `2^n_bits / n_partitions`.
1251 // That number will probably overflow a std::size_t so we must instead keep everything in log 2 form.
1252 // First we find the smallest `n` such that `2^n >= n_partitions - 1`.
1253 // Note if `n_partitions` is 128 the following gives `n = 7` and does the same if `n = 100`.
1254 auto n = static_cast<std::size_t>(std::bit_width(n_partitions - 1));
1255
1256 // We will create `2^n` partitions which is probably more than needed but the wastage is negligible.
1257 // To create those `2^n` partitions we must be able to jump ahead `2^(n_bits - n)` steps many times.
1258 auto power_2 = n_bits - n;
1259
1260 // Precompute the jump coefficients to advance the generator `2^power_2` steps i.e. along to the next partition.
1261 m_jump_coefficients = xso::jump_coefficients<typename RNG::state_type>(power_2, true);
1262 }
1263
1266 RNG next() {
1267 // We already have a pre-baked generator seeded at the right spot ready to go.
1268 RNG result = m_rng;
1269
1270 // Prep for the next call by jumping the parent copy once more along its stream.
1271 m_rng.jump(m_jump_coefficients);
1272
1273 // And return the pre-baked one ...
1274 return result;
1275 }
1276
1277private:
1278 using array_type = typename RNG::array_type;
1279
1280 RNG m_rng;
1281 array_type m_jump_coefficients;
1282};
1283
1284// --------------------------------------------------------------------------------------------------------------------
1285// Non-member functions for computing jump polynomials:
1286// --------------------------------------------------------------------------------------------------------------------
1287
1301template<typename State>
1302constexpr auto
1303jump_coefficients(std::size_t n, bool n_is_log2) {
1304
1305 // Retrieve the low order coefficients of characteristic polynomial --- this can throw if not precomputed.
1306 std::array<typename State::word_type, State::word_count()> char_coefficients;
1307 State::characteristic_coefficients(char_coefficients.begin());
1308
1309 // The jump polynomial is x^J mod c(x) -- that computation expects to be handed p(x) where c(x) = x^n + p(x).
1310 return xso::reduce(char_coefficients, n, n_is_log2);
1311}
1312
1313// --------------------------------------------------------------------------------------------------------------------
1314// We have some "internal" functions that reduce x^J mod c(x) --- needed for the jump polynomial computations.
1315// These re-implement a more general reduction method in the gf2::BitPolynomial class (https://nessan/github.io/gf2/).
1316// Repeated here to make `xoshiro.h` self-contained.
1317//
1318// Not part of the public API but used for the jump calculations in the generator class.
1319// --------------------------------------------------------------------------------------------------------------------
1320
1321// Function to "riffle" a `src` word into two other words `lo` and `hi` containing the bits from `src`
1322// interleaved with zeros.
1323//
1324// So the 8-bit word `abcdefgh` fills `lo` as `a0b0c0d0` and `hi` as `e0f0g0h0`.
1325//
1326// # Example
1327// ```
1328// std::uint8_t src = 0b1111'1111;
1329// std::uint8_t lo, hi;
1330// xso::riffle(src, lo, hi);
1331// assert_eq(lo, 0b01010101, "lo is {:08b}", lo);
1332// assert_eq(hi, 0b01010101, "hi is {:08b}", hi);
1333// ```
1334// *NOTE:** This function is "public" for testing purposes only -- it is not part of the normal interface.
1335template<std::unsigned_integral word_type>
1336constexpr void
1337riffle(word_type src, word_type& lo, word_type& hi) {
1338 // Constants
1339 constexpr std::size_t bits_per_word = std::numeric_limits<word_type>::digits;
1340 constexpr std::size_t half_bits = bits_per_word / 2;
1341 constexpr word_type one{1};
1342 constexpr word_type ones = std::numeric_limits<word_type>::max();
1343
1344 // Split the src into lo and hi halves.
1345 lo = src & (ones >> half_bits);
1346 hi = src >> half_bits;
1347
1348 // Some magic to interleave the respective halves with zeros.
1349 for (auto i = bits_per_word / 4; i > 0; i /= 2) {
1350 word_type div = word_type(one << i) | one;
1351 word_type msk = ones / div;
1352 lo = (lo ^ (lo << i)) & msk;
1353 hi = (hi ^ (hi << i)) & msk;
1354 }
1355}
1356
1357// Function that riffles a `src` array of unsigneds into two others `lo` and `hi` which will be filled with the
1358// bits from `src` interleaved with zeros.
1359//
1360// We treat `lo` and `hi` as contiguous storage and fill the elements of `lo` first and then `hi`.
1361// This allows us to optionally reuse `src` for the output array `lo` -- the call `riffle(src, src, hi)` will work.
1362//
1363// # Example
1364// ```
1365// std::array<std::uint8_t, 2> src = {0b1111'1111, 0b1111'1111};
1366// std::array<std::uint8_t, 2> lo, hi;
1367// xso::riffle(src, lo, hi);
1368// assert_eq(lo, (std::array<std::uint8_t, 2>{0b01010101, 0b01010101}));
1369// assert_eq(hi, (std::array<std::uint8_t, 2>{0b01010101, 0b01010101}));
1370// ```
1371// *NOTE:** This function is "public" for testing purposes only -- it is not part of the normal interface.
1372template<std::unsigned_integral word_type, std::size_t N>
1373static constexpr void
1374riffle(const std::array<word_type, N>& src, std::array<word_type, N>& lo, std::array<word_type, N>& hi) {
1375 // We will riffle each word in src into two other words x & y
1376 word_type x, y;
1377
1378 // We work through `src` in reverse order -- this allows the reuse of `src` for `lo`!
1379 // Treating [lo|hi] as contiguous storage, first working back through hi and then back through lo.
1380 for (std::size_t i = N; i-- > 0;) {
1381 riffle(src[i], x, y);
1382 if (2 * i + 1 > N) {
1383 // Both x & y go in hi -- note that if 2i + 1 - N > 0 then 2i - N is >= 0.
1384 hi[2 * i - N] = x;
1385 hi[2 * i + 1 - N] = y;
1386 } else if (2 * i + 1 == N) {
1387 // Straddling situation where y goes in the first word of hi and x in the last word of lo.
1388 lo[N - 1] = x;
1389 hi[0] = y;
1390 } else {
1391 // Need to pop both x & y into the lo array.
1392 lo[2 * i] = x;
1393 lo[2 * i + 1] = y;
1394 }
1395 }
1396}
1397
1398// 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.
1399//
1400// 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.
1401// We are passed the n coefficients of p(x) packed into an array of unsigned integers.
1402// We return the coefficients of r(x) = x^e mod c(x) packed into an array of unsigned integers in the same format.
1403//
1404// # Note
1405// Any linting tool you use will (reasonably) complain that the complexity of this method is too high!
1406// The method is a direct re-implementation of the `gf2::BitPolynomial::reduce` method which is also complex but
1407// easier to understand as it is properly factored. The version here is is less general as it assumes that the
1408// degree of p(x) is a multiple of 32.
1409//
1410// *NOTE:** This method "public" for testing purposes only -- it is not part of the normal interface.
1411template<std::unsigned_integral word_type, std::size_t N>
1412constexpr auto
1413reduce(const std::array<word_type, N>& p, std::size_t J, bool J_is_log2) {
1414
1415 // We will return the coefficients of r(x) = x^e mod c(x) packed into an array of unsigned integers of this type.
1416 using array_type = std::array<word_type, N>;
1417
1418 // Constant we use to indicate "no such position"/"not found" and a couple of others.
1419 constexpr auto npos = static_cast<std::size_t>(-1);
1420 constexpr word_type one{1};
1421
1422 // 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:
1423 constexpr std::size_t bits_per_word = std::numeric_limits<word_type>::digits;
1424 constexpr std::size_t n = N * bits_per_word;
1425
1426 // lambda: Returns the index of the word that holds p_i.
1427 auto word = [=](std::size_t i) { return i / bits_per_word; };
1428
1429 // lambda: Returns the bit location of p_i inside the word that holds it.
1430 auto bit = [=](std::size_t i) { return i % bits_per_word; };
1431
1432 // lambda: Returns a mask that isolates p_i within the word that holds it.
1433 auto mask = [=](std::size_t i) { return word_type{one << bit(i)}; };
1434
1435 // lambda: Returns true if poly_i is 1.
1436 auto test = [=](const auto& poly, std::size_t i) -> bool { return poly[word(i)] & mask(i); };
1437
1438 // lambda: Sets poly_i to 1.
1439 auto set = [=](auto& poly, std::size_t i) { poly[word(i)] |= mask(i); };
1440
1441 // lambda: Returns the index for the least significant set bit in the argument or `npos` if none set.
1442 auto lsb = [=](word_type w) { return w == 0 ? npos : static_cast<std::size_t>(std::countr_zero(w)); };
1443
1444 // lambda: Returns the index for the most significant set bit in the argument or `npos` if none set.
1445 auto msb = [=](word_type w) { return static_cast<std::size_t>(std::bit_width(w) - 1); };
1446
1447 // lambda: Returns the first set coefficient in poly or `npos` if the coefficients are all zero.
1448 auto first_set = [=](const auto& poly) {
1449 for (auto i = 0uz; i < N; ++i)
1450 if (poly[i] != 0) return i * bits_per_word + lsb(poly[i]);
1451 return npos;
1452 };
1453
1454 // lambda: Returns the final set coefficient in poly or `npos` if the coefficients are all zero.
1455 auto final_set = [=](const auto& poly) {
1456 std::size_t i = N;
1457 while (i--)
1458 if (poly[i] != 0) return i * bits_per_word + msb(poly[i]);
1459 return npos;
1460 };
1461
1462 // lambda: Returns true if the highest coefficient in `poly` is set (then poly is said to be monic).
1463 auto monic = [=](const auto& poly) {
1464 constexpr std::size_t complement = bits_per_word - 1;
1465 constexpr auto final_bit_mask = word_type{one << complement};
1466 return poly[N - 1] & final_bit_mask;
1467 };
1468
1469 // lambda: Computes lhs <- lhs + rhs which in GF(2) is equivalent to lhs <- lhs^rhs.
1470 auto add = [=](auto& lhs, const auto& rhs) {
1471 for (auto i = 0uz; i < N; ++i) lhs[i] ^= rhs[i];
1472 };
1473
1474 // lambda: Performs a one place shift on the polynomial coefficients stored poly.
1475 // Shift is to the the right if you think the elements are in vector order [p0,p1,p2,p3] -> [0,p0,p1,p2].
1476 // Shift is to the left when you think in bit order [b3,b2,b1,b0] -> [b2,b1,b0,0].
1477 auto shift = [=](auto& poly) {
1478 constexpr std::size_t complement = bits_per_word - 1;
1479 for (std::size_t i = N - 1; i > 0; --i) {
1480 auto l = static_cast<word_type>(poly[i] << 1);
1481 auto r = static_cast<word_type>(poly[i - 1] >> complement);
1482 poly[i] = l | r;
1483 }
1484 poly[0] <<= 1;
1485 };
1486
1487 // lambda: If degree[poly] < n, this performs poly(x) <- x*poly(x) mod c(x) where c(x) = x^n + p(x).
1488 auto times_x_step = [&](auto& poly) {
1489 bool add_p = monic(poly);
1490 shift(poly);
1491 if (add_p) add(poly, p);
1492 };
1493
1494 // We precompute x^{n + i} mod c(x) for i = 0, ..., n-1 starting from the known x^n mod c(x) = p.
1495 // Each x^{n + i} mod c(x) is a word-vector of coefficients and we put the lot into a std::vector.
1496 std::vector<array_type> power_mod(n);
1497 power_mod[0] = p;
1498 for (std::size_t i = 1; i < n; ++i) {
1499 power_mod[i] = power_mod[i - 1];
1500 times_x_step(power_mod[i]);
1501 }
1502
1503 // Some work space we use below.
1504 array_type hi;
1505
1506 // lambda: If degree[poly] < n, performs: poly(x) <- poly(x)^2 mod c(x) where as usual c(x) = x^n + p(x).
1507 auto square_step = [&](auto& poly) {
1508 // Compute poly(x)^2 -- in GF(2) this means interspersing all the coefficients with zeros.
1509 // We actually riffle poly directly into two arrays lo & hi so that poly(x)^2 = lo(x) + x^n hi(x).
1510 // This only works because we assume n some multiple of N (i.e. all bits in poly matter).
1511 // NOTE: Our riffle method above for arrays lets us reuse the poly array for lo.
1512 riffle(poly, poly, hi);
1513
1514 // poly(x)^2 mod c(x) now is poly(x) + x^n hi(x) mod c(x) as degree[poly] < n.
1515 // Add the x^n hi(x) mod c(x) term-by-term noting that at most every second term in hi(x) is 1.
1516 auto hi_first = first_set(hi);
1517 if (hi_first != npos) {
1518 auto hi_final = final_set(hi);
1519 for (std::size_t i = hi_first; i <= hi_final; i += 2)
1520 if (test(hi, i)) add(poly, power_mod[i]);
1521 }
1522 };
1523
1524 // Initialize our return value to all zeros.
1525 array_type result;
1526 result.fill(0);
1527
1528 // Case: e = 2^J -- we just do J squaring steps starting from r(x) = x to get to x^(2^J) mod c(x).
1529 if (J_is_log2) {
1530 set(result, 1);
1531 for (std::size_t j = 0; j < J; ++j) square_step(result);
1532 return result;
1533 }
1534
1535 // Case e = J < n: Then x^J mod c(x) = x^J so we can set the appropriate coefficient in r and return.
1536 if (J < n) {
1537 set(result, J);
1538 return result;
1539 }
1540
1541 // Case e = J = n: Then x^J mod c(x) = p(x).
1542 if (J == n) return p;
1543
1544 // Case e = J > n: We use a square & multiply algorithm:
1545 // Note that if e.g. J = 0b00010111 then std::bit_floor(J) = 0b00010000.
1546 std::size_t J_bit = std::bit_floor(J);
1547
1548 // Start with r(x) = x mod c(x) which takes care of the most significant binary digit in J.
1549 set(result, 1);
1550 J_bit >>= 1;
1551
1552 // And off we go ...
1553 while (J_bit) {
1554
1555 // Always do a square step and then a times_x step if necessary (i.e. if current bit in J is set).
1556 square_step(result);
1557 if (J & J_bit) times_x_step(result);
1558
1559 // On to the next bit position in n.
1560 J_bit >>= 1;
1561 }
1562 return result;
1563}
1564
1565#ifdef GF2
1566
1567// --------------------------------------------------------------------------------------------------------------------
1568// Non-member functions that are only defined if we have access to the `gf2` library.
1569// See https://nessan.github.io/gf2/ for tools for working with polynomials and matrices over GF(2).
1570// --------------------------------------------------------------------------------------------------------------------
1571
1581template<typename State>
1582auto
1584
1585 using word_type = typename State::word_type;
1586
1587 // Some constants for our state size.
1588 constexpr auto n_words = State::word_count();
1589 constexpr auto n_bits = State::bit_count();
1590
1591 // The transition matrix will be a square n_bits x n_bits matrix over GF(2).
1592 gf2::BitMatrix<word_type> result{n_bits, n_bits};
1593
1594 // Some work-space in word and bit space
1595 gf2::BitVector<word_type> bits{n_bits};
1596 std::array<word_type, n_words> words;
1597
1598 // Create an State instance we will use to step through unit states.
1599 State state;
1600
1601 // We get the columns of the transition matrix by looking at the action of `step()` on all the unit states.
1602 for (std::size_t k = 0; k < n_bits; ++k) {
1603
1604 // Create the k'th unit state (i.e. the state just has the k'th bit set and all others are zero)
1605 bits.set_all(false);
1606 bits.set(k, true);
1607
1608 // Translate that unit bit-vector into an array of words.
1609 bits.to_words(words.begin());
1610
1611 // Seed the state from those words.
1612 state.seed(words.cbegin(), words.cend());
1613
1614 // Advance the state by one step.
1615 state.step();
1616
1617 // Translate the new state to bit-space.
1618 for (auto i = 0uz; i < n_words; ++i) bits.set_word(i, state[i]);
1619
1620 // Store those bits into column `k` of the transition matrix.
1621 // NOTE: Columnar access for a gf2::BitMatrix must be done element by element.
1622 for (auto i = 0uz; i < n_bits; ++i) result(i, k) = bits[i];
1623 }
1624
1625 return result;
1626}
1627
1632template<typename State>
1633auto
1636 return T.characteristic_polynomial();
1637}
1638
1646template<typename State>
1647auto
1648jump_polynomial(gf2::BitPolynomial<typename State::word_type> const& c, std::size_t N, bool N_is_log2 = false) {
1649 return c.reduce_x_to_the(N, N_is_log2);
1650}
1651
1652#endif
1653
1654} // namespace xso
1655
1656// --------------------------------------------------------------------------------------------------------------------
1657// Formatting and output stream support for our generators:
1658// --------------------------------------------------------------------------------------------------------------------
1659
1660// A concept that matches any type that has an accessible `type_string()` class `method.
1661template<typename T>
1662concept has_type_string_class_method = requires {
1663 { T::type_string() } -> std::convertible_to<std::string>;
1664};
1665
1669template<has_type_string_class_method T>
1670struct std::formatter<T> {
1671
1673 constexpr auto parse(const std::format_parse_context& ctx) {
1674 auto it = ctx.begin();
1675 assert(it == ctx.end() || *it == '}');
1676 return it;
1677 }
1678
1680 template<class FormatContext>
1681 auto format(const T&, FormatContext& ctx) const {
1682 return std::format_to(ctx.out(), "{}", T::type_string());
1683 }
1684};
1685
1689template<has_type_string_class_method T>
1690std::ostream&
1691operator<<(std::ostream& s, const T&) {
1692 s << T::type_string();
1693 return s;
1694}
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:1243
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:1266
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:1681
constexpr auto parse(const std::format_parse_context &ctx)
Parse the format specifier – currently only handle the default empty specifier.
Definition xoshiro.h:1673
The plus_plus scrambler is passed a state array an returns rotl(state[w0] + state[w1],...
Definition xoshiro.h:1162
The plus scrambler is passed a state array an returns state[w0] + state[w1].
Definition xoshiro.h:1150
The star_star scrambler is passed a state array an returns T * rotl(S * state[w], R) where:
Definition xoshiro.h:1136
The star scrambler is passed a state array and will return S * state[w] where:
Definition xoshiro.h:1119
auto transition_matrix()
Returns the transition matrix for a state/generator type as a gf2::BitMatrix.
Definition xoshiro.h:1583
xoshiro_4x32_star_star rng32
The recommended 32-bit output generator – used as xso::rng32.
Definition xoshiro.h:1207
std::ostream & operator<<(std::ostream &s, const T &)
The usual output stream operator for an xso::generator, State, or Scrambler.
Definition xoshiro.h:1691
rng64 rng
The recommended default generator for most usage – used as xso::rng.
Definition xoshiro.h:1213
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:1303
xoshiro_4x64_star_star rng64
The recommended 64-bit output generator – used as xso::rng64.
Definition xoshiro.h:1210
auto characteristic_polynomial()
Returns the characteristic polynomial for our state's transition matrix as a gf2::BitPolynomial.
Definition xoshiro.h:1634
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:1648