| Message ID | __gvHmoZJoVR8laX9y6KMbxVSabmG2VrdICXKc_W8vt88mif3yG6g7FTXXVLJXfBfAL3_ZaWZ-BMweDStBvhrnaq9AxHb35TWOBY5fU78X4=@protonmail.com |
|---|---|
| State | New |
| Headers | show |
| Series | [v6] libstdc++: Implement Philox Engine (PR119794) | expand |
On Tue, 5 Aug 2025, 1nfocalypse wrote: > Implements Philox Engine (P2075R6) and associated tests. > > Implements additional feedback from v5 from Patrick Palka. > Also cut some trailing whitespace from the limb propagation > fix in v5. > > Apologies for the delay from v5 - I had to finish writing some > CTF challenges for DEFCON this week. Thank you for the > feedback, and please let me know if anything further needs > to be changed. > > Built/tested on x86_64-pc-linux-gnu. This version LGTM! A ChangeLog entry in the commit message is missing (https://gcc.gnu.org/codingconventions.html#ChangeLogs) but I'm happy to take care of that while pushing, it'll consist of mostly boilerplate for a new feature like this. > > > Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) > > Implemented additional changes from Patrick Palka. > Also removed some trailing whitespace in random.tcc. > > replaced alias of __ios_base with ios_base > included <iomanip> in random.h > altered line number in pr60037-neg.cc to be accurate > Refactored iterator/index loops to be range-based for > in istream/ostream operator overloads > include <random> in philox_engine/cons/seed_seq.cc > Conforms with errata LWG4143, LWG4153 for Philox Engine. > > PR libstdc++/119794 > --- > libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ > libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ > libstdc++-v3/include/bits/version.def | 9 + > libstdc++-v3/include/bits/version.h | 10 + > libstdc++-v3/include/std/random | 3 + > .../26_numerics/random/philox4x32.cc | 23 ++ > .../26_numerics/random/philox4x64.cc | 23 ++ > .../random/philox_engine/cons/119794.cc | 39 +++ > .../random/philox_engine/cons/copy.cc | 25 ++ > .../random/philox_engine/cons/default.cc | 27 ++ > .../random/philox_engine/cons/seed.cc | 20 ++ > .../random/philox_engine/cons/seed_seq.cc | 24 ++ > .../random/philox_engine/operators/equal.cc | 30 ++ > .../random/philox_engine/operators/inequal.cc | 30 ++ > .../philox_engine/operators/serialize.cc | 49 +++ > .../philox_engine/requirements/constants.cc | 26 ++ > .../requirements/constexpr_data.cc | 50 ++++ > .../requirements/constexpr_functions.cc | 41 +++ > .../philox_engine/requirements/typedefs.cc | 26 ++ > .../26_numerics/random/pr60037-neg.cc | 4 +- > 20 files changed, 931 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > > diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h > index 1fdaf51934f..e74b7bd75c0 100644 > --- a/libstdc++-v3/include/bits/random.h > +++ b/libstdc++-v3/include/bits/random.h > @@ -33,6 +33,7 @@ > > #include <vector> > #include <bits/uniform_int_dist.h> > +#include <iomanip> > > namespace std _GLIBCXX_VISIBILITY(default) > { > @@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { return !(__lhs == __rhs); } > #endif > > +#if __cpp_lib_philox_engine > + > + /** > + * @brief: A discrete pseudorandom number generator based off of weakened > + * cryptographic primitives. > + * > + * This algorithm was intended to be used for highly parallel random number > + * generation, and is capable of immensely long periods. It provides "Crush- > + * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" > + * test, demonstrating significant apparent entropy. It is not intended for > + * cryptographic use and should not be used for such, despite being based on > + * cryptographic primitives. > + * > + * The two four-word definitions are likely the best use for this algorithm, > + * and are given below as defaults. > + * > + * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and > + * David Shaw as a product of D.E. Shaw Research. > + * > + * @tparam __w Word size > + * @tparam __n Buffer size > + * @tparam __r Rounds > + * @tparam __consts Multiplication and round constant pack, ordered as > + * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} > + * > + * @headerfile random > + * @since C++26 > + */ > + template<class _UIntType, size_t __w, > + size_t __n, size_t __r, > + _UIntType... __consts> > + class philox_engine > + { > + static_assert(__n == 2 || __n == 4, > + "template argument N must be either 2 or 4"); > + static_assert(sizeof...(__consts) == __n, > + "length of consts array must match specified N"); > + static_assert(0 < __r, "a number of rounds must be specified"); > + static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), > + "specified bitlength must match input type"); > + template<typename _Sseq> > + using _If_seed_seq > + = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; > + > + private: > + // the ordering here is essential to functionality. > + /** @brief an internal unpacking function for %philox_engine. */ > + template <size_t __ind0, size_t __ind1> > + static constexpr > + array<_UIntType, __n / 2> > + _S_popArray() > + { > + if constexpr (__n == 4) > + return {__consts...[__ind0], __consts...[__ind1]}; > + else > + return {__consts...[__ind0]}; > + } > + > + public: > + /** Type of template param. */ > + using result_type = _UIntType; > + // public members > + static constexpr size_t word_size = __w; > + static constexpr size_t word_count = __n; > + static constexpr size_t round_count = __r; > + static constexpr array<result_type, __n / 2> multipliers = > + philox_engine::_S_popArray<0,2>(); > + static constexpr array<result_type, __n / 2> round_consts = > + philox_engine::_S_popArray<1,3>(); > + > + /** @brief returns the minimum value possible. */ > + static constexpr result_type > + min() > + { return 0; } > + > + /** @brief returns the maximum value possible. */ > + static constexpr result_type > + max() > + { > + return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); > + } > + // default key value > + static constexpr result_type default_seed = 20111115u; > + > + // constructors > + philox_engine() > + : philox_engine(default_seed) > + {} > + > + explicit > + philox_engine(result_type __value); > + > + /** @brief seed sequence constructor for %philox_engine > + * > + * @params __q the seed sequence > + */ > + template<typename _Sseq, typename = _If_seed_seq<_Sseq>> > + explicit > + philox_engine(_Sseq& __q) > + { > + seed(__q); > + } > + > + void > + seed(result_type value = default_seed); > + > + /** @brief seeds %philox_engine by seed sequence > + * > + * @params __q the seed sequence > + */ > + template<typename _Sseq> > + _If_seed_seq<_Sseq> > + seed(_Sseq& __q); > + > + /** @brief sets the internal counter "cleartext" > + * > + * @params __counter std::array of len N > + */ > + void > + set_counter(const array<result_type, __n>& __counter); > + > + /** @brief compares two %philox_engine objects > + * > + * @params __x A %philox_engine object > + * @params __y A %philox_engine object > + * > + * @returns true if the objects will produce an identical stream, false > + * otherwise > + */ > + friend bool > + operator==(const philox_engine& __x, const philox_engine& __y) > + { > + return (std::equal(__x._M_x.begin(), __x._M_x.end(), > + __y._M_x.begin(), __y._M_x.end()) > + && std::equal(__x._M_y.begin(), __x._M_y.end(), > + __y._M_y.begin(), __y._M_y.end()) > + && std::equal(__x._M_k.begin(), __x._M_k.end(), > + __y._M_k.begin(), __y._M_k.end()) > + && __x._M_i == __y._M_i); > + } > + > + /** @brief outputs a single w-bit number and handles state advancement > + * > + * @returns return_type > + */ > + _UIntType > + operator()(); > + > + /** @brief discards __z numbers > + * > + * @params __z number of iterations to discard > + */ > + void > + discard(unsigned long long __z); > + > + /** @brief outputs the state of the generator > + * > + * @param __os An output stream. > + * @param __x A %philox_engine object reference > + * > + * @returns the state of the Philox Engine in __os > + */ > + template<typename _CharT, typename _Traits> > + friend basic_ostream<_CharT, _Traits>& > + operator<<(basic_ostream<_CharT, _Traits>& __os, > + const philox_engine& __x) > + { > + const typename ios_base::fmtflags __flags = __os.flags(); > + const _CharT __fill = __os.fill(); > + __os.flags(ios_base::dec | ios_base::left); > + __os.fill(__os.widen(' ')); > + for (auto &__subkey : __x._M_k) > + __os << __subkey << ' '; > + for (auto &__ctr : __x._M_x) > + __os << __ctr << ' '; > + __os << __x._M_i; > + __os.flags(__flags); > + __os.fill(__fill); > + return __os; > + } > + > + /** @brief takes input to set the state of the %philox_engine object > + * > + * @param __is An input stream. > + * @param __x A %philox_engine object reference > + * > + * @returns %philox_engine object is set with values from instream > + */ > + template <typename _CharT, typename _Traits> > + friend basic_istream<_CharT, _Traits>& > + operator>>(basic_istream<_CharT, _Traits>& __is, > + philox_engine& __x) > + { > + const typename ios_base::fmtflags __flags = __is.flags(); > + __is.flags(ios_base::dec | ios_base::skipws); > + for (auto &__subkey : __x._M_k) > + __is >> __subkey; > + for (auto &__ctr : __x._M_x) > + __is >> __ctr; > + array<_UIntType, __n> __tmpCtr = __x._M_x; > + unsigned char __setIndex = 0; > + for (size_t __j = 0; __j < __x._M_x.size(); ++__j) > + { > + if (__x._M_x[__j] > 0) > + { > + __setIndex = __j; > + break; > + } > + } > + for (size_t __j = 0; __j <= __setIndex; ++__j) > + { > + if (__j != __setIndex) > + __x._M_x[__j] = max(); > + else > + --__x._M_x[__j]; > + } > + __x._M_philox(); > + __x._M_x = __tmpCtr; > + __is >> __x._M_i; > + __is.flags(__flags); > + return __is; > + } > + private: > + // private state variables > + array<_UIntType, __n> _M_x; > + array<_UIntType, __n / 2> _M_k; > + array<_UIntType, __n> _M_y; > + unsigned long long _M_i = 0; > + > + /** @brief Takes the high values of the product of __a, __b > + * > + * @params __a an unsigned integer > + * @params __b an unsigned integer > + * > + * @returns an unsigned integer of at most bitlength W > + */ > + static > + _UIntType > + _S_mulhi(_UIntType __a, _UIntType __b); // (A*B)/2^W > + > + /** @brief Takes the low values of the product of __a, __b > + * > + * @params __a an unsigned integer > + * @params __b an unsigned integer > + * > + * @returns an unsigned integer of at most bitlength W > + */ > + static > + _UIntType > + _S_mullo(_UIntType __a, _UIntType __b); // (A*B)%2^W > + > + /** @brief an R-round substitution/Feistel Network hybrid for > + * %philox_engine > + */ > + void > + _M_philox(); > + > + /** @brief an internal transition function for the %philox_engine. */ > + void > + _M_transition(); > + }; > + > +#endif > + > /** > * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller. > */ > @@ -1742,6 +2007,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > typedef minstd_rand0 default_random_engine; > > +#if __cpp_lib_philox_engine > + > + typedef philox_engine< > + uint_fast32_t, > + 32, 4, 10, > + 0xCD9E8D57, 0x9E3779B9, > + 0xD2511F53, 0xBB67AE85> philox4x32; > + > + /** > + * Alternative Philox instance (64 bit) > + */ > + typedef philox_engine< > + uint_fast64_t, > + 64, 4, 10, > + 0xCA5A826395121157, 0x9E3779B97F4A7C15, > + 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B> philox4x64; > +#endif > + > /** > * A standard interface to a platform-specific non-deterministic > * random number generator (if any are available). > diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc > index 53ccacb2e38..53f302da800 100644 > --- a/libstdc++-v3/include/bits/random.tcc > +++ b/libstdc++-v3/include/bits/random.tcc > @@ -907,6 +907,197 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return __is; > } > > +#if __cpp_lib_philox_engine > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + _UIntType > + philox_engine<_UIntType, > + __w, __n, __r, __consts...>::_S_mulhi(_UIntType __a, _UIntType __b) > + { > + const __uint128_t __num = > + static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b); > + return static_cast<_UIntType>((__num >> __w) & max()); > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + _UIntType > + philox_engine<_UIntType, > + __w, __n, __r, __consts...>::_S_mullo(_UIntType __a, _UIntType __b) > + { > + return static_cast<_UIntType>((__a * __b) & max()); > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + void > + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_transition() > + { > + ++_M_i; > + if (_M_i == __n) > + { > + _M_philox(); > + if constexpr (__n == 4) > + { > + __uint128_t __uh = > + (static_cast<__uint128_t>(_M_x[1]) << __w) > + | (static_cast<__uint128_t>(_M_x[0]) + 1); > + __uint128_t __lh = > + ((static_cast<__uint128_t>(_M_x[3]) << __w) > + | (_M_x[2])); > + __uint128_t __bigMask = > + (static_cast<__uint128_t>(1) << ((2 * __w) - 1)) > + | ((static_cast<__uint128_t>(1) << ((2 * __w) - 1)) - 1); > + if ((__uh & __bigMask) == 0) > + { > + ++__lh; > + __uh = 0; > + } > + _M_x[0] = __uh & max(); > + _M_x[1] = (__uh >> (__w)) & max(); > + _M_x[2] = __lh & max(); > + _M_x[3] = (__lh >> (__w)) & max(); > + } else > + { > + __uint128_t __num = > + (static_cast<__uint128_t>(_M_x[1]) << __w) > + | (static_cast<__uint128_t>(_M_x[0]) + 1); > + _M_x[0] = __num & max(); > + _M_x[1] = (__num >> __w) & max(); > + } > + _M_i = 0; > + } > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + void > + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() > + { > + array<_UIntType, __n> __outputSeq{}; > + for (size_t __j = 0; __j < __n; ++__j) > + __outputSeq[__j] = _M_x[__j]; > + for (unsigned long __j = 0; __j < __r; ++__j) > + { > + array<_UIntType, __n> __intermedSeq{}; > + if constexpr (__n == 4) > + { > + __intermedSeq[0] = __outputSeq[2]; > + __intermedSeq[1] = __outputSeq[1]; > + __intermedSeq[2] = __outputSeq[0]; > + __intermedSeq[3] = __outputSeq[3]; > + } else > + { > + __intermedSeq[0] = __outputSeq[0]; > + __intermedSeq[1] = __outputSeq[1]; > + } > + for (unsigned long __k = 0; __k < (__n/2); ++__k) > + { > + __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) > + ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) > + ^ __intermedSeq[2*__k+1]; > + > + __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], > + multipliers[__k]); > + } > + } > + for (unsigned long __j = 0; __j < __n; ++__j) > + _M_y[__j] = __outputSeq[__j]; > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + philox_engine<_UIntType, > + __w, __n, __r, __consts...>::philox_engine(result_type __value) > + { > + std::fill(_M_x.begin(), _M_x.end(), 0); > + std::fill(_M_k.begin(), _M_k.end(), 0); > + std::fill(_M_y.begin(), _M_y.end(), 0); > + _M_k[0] = __value & max(); > + _M_i = __n - 1; > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + void > + philox_engine<_UIntType, > + __w, __n, __r, __consts...>::seed(result_type __value) > + { > + std::fill(_M_x.begin(), _M_x.end(), 0); > + std::fill(_M_k.begin(), _M_k.end(), 0); > + std::fill(_M_y.begin(), _M_y.end(), 0); > + _M_k[0] = __value & max(); > + _M_i = __n - 1; > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + void > + philox_engine<_UIntType, __w, > + __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) > + { > + for (unsigned long long __j = 0; __j < __n; ++__j) > + _M_x[__j] = __counter[__n - 1 - __j] & max(); > + _M_i = __n - 1; > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + template<typename _Sseq> > + auto > + philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) > + -> _If_seed_seq<_Sseq> > + { > + std::fill(_M_k.begin(), _M_k.end(), 0); > + const unsigned long long __p = 1 + ((__w - 1)/ 32); > + uint_least32_t __tmpArr[(__n / 2) * __p]; > + __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); > + for (unsigned long long __k = 0; __k < (__n/2); ++__k) > + { > + unsigned long long __precalc = 0; > + for (unsigned long long __j = 0; __j < __p; ++__j) > + { > + unsigned long long __multiplicand = (1ull << (32 * __j)); > + __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); > + } > + _M_k[__k] = __precalc; > + } > + std::fill(_M_x.begin(), _M_x.end(), 0); > + std::fill(_M_y.begin(), _M_y.end(), 0); > + _M_i = __n - 1; > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + void > + philox_engine<_UIntType, > + __w, __n, __r, __consts...>::discard(unsigned long long __z) > + { > + for (unsigned long long __j = 0; __j < __z; ++__j) > + _M_transition(); > + } > + > + template<class _UIntType, > + size_t __w, size_t __n, > + size_t __r, _UIntType... __consts> > + _UIntType > + philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() > + { > + _M_transition(); > + return _M_y[_M_i]; > + } > + > +#endif > > template<typename _IntType, typename _CharT, typename _Traits> > std::basic_ostream<_CharT, _Traits>& > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index dbe2cb8f175..4c21fa187c8 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -2069,6 +2069,15 @@ ftms = { > }; > }; > > +ftms = { > + name = philox_engine; > + values = { > + v = 202406; > + cxxmin = 26; > + extra_cond = "__SIZEOF_INT128__"; > + }; > +}; > + > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 7bb6016df68..75a9946adce 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2318,4 +2318,14 @@ > #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ > #undef __glibcxx_want_constexpr_exceptions > > +#if !defined(__cpp_lib_philox_engine) > +# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) > +# define __glibcxx_philox_engine 202406L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) > +# define __cpp_lib_philox_engine 202406L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ > +#undef __glibcxx_want_philox_engine > + > #undef __glibcxx_want_all > diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random > index 0e058a04bd9..8a02ade4b75 100644 > --- a/libstdc++-v3/include/std/random > +++ b/libstdc++-v3/include/std/random > @@ -39,6 +39,9 @@ > # include <bits/c++0x_warning.h> > #else > > +#define __glibcxx_want_philox_engine > +#include <bits/version.h> > + > #include <cmath> > #include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t > #include <cstdlib> > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > new file mode 100644 > index 00000000000..d5a8ca078ef > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > @@ -0,0 +1,23 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_hooks.h> > + > +void > +test01() > +{ > + std::philox4x32 a; > + a.discard(9999); > + > + VERIFY( a() == 1955073260 ); > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > new file mode 100644 > index 00000000000..131f094cb28 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > @@ -0,0 +1,23 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_hooks.h> > + > +void > +test01() > +{ > + std::philox4x64 a; > + a.discard(9999); > + > + VERIFY( a() == 3409172418970261260 ); > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > new file mode 100644 > index 00000000000..c3a5a0eb754 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > @@ -0,0 +1,39 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +#include <random> > +#include <testsuite_hooks.h> > + > +int f(int x) > +{ > + std::seed_seq sq(&x, &x + 1); > + auto rnd = std::philox4x32(sq); > + return std::uniform_int_distribution<int>()(rnd); > +} > + > +int g(int x) > +{ > + std::seed_seq sq(&x, &x + 1); > + auto rnd = std::philox4x32(); > + rnd.seed(sq); > + return std::uniform_int_distribution<int>()(rnd); > +} > + > +void test01() > +{ > + const int f1 = f(0); > + const int f2 = f(0); > + > + const int g1 = g(0); > + const int g2 = g(0); > + > + VERIFY( f1 == f2 ); > + VERIFY( g1 == g2 ); > + VERIFY( f1 == g1 ); > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > new file mode 100644 > index 00000000000..4f61928a157 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > @@ -0,0 +1,25 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > + > +void > +test01() > +{ > + > + std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); > + > + const auto f(e); > + auto g(f); > + g = g; // Suppress unused warning > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > new file mode 100644 > index 00000000000..9f9ae94db0f > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > @@ -0,0 +1,27 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_hooks.h> > + > +void > +test01() > +{ > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> philox4x32nullkey(0); > + > + VERIFY( philox4x32nullkey.min() == 0 ); > + VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); > + VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > new file mode 100644 > index 00000000000..5cb914f4b45 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > @@ -0,0 +1,20 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +#include <random> > + > +void > +test01() > +{ > + unsigned long seed = 2; > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> philox4x32seeded(seed); > +} > + > +int main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > new file mode 100644 > index 00000000000..7d9e3e6540d > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > @@ -0,0 +1,24 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > + > +void > +test01() > +{ > + std::seed_seq sseq{ 1, 2, 3, 4 }; > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> philox4x32sseq(sseq); > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > new file mode 100644 > index 00000000000..4f62bfbbd88 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > @@ -0,0 +1,30 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_hooks.h> > + > +void > +test01() > +{ > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> x, y; > + > + VERIFY ( x == y); > + x.discard(100); > + y.discard(100); > + > + VERIFY (x == y); > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > new file mode 100644 > index 00000000000..86d757db904 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > @@ -0,0 +1,30 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_hooks.h> > + > +void > +test01() > +{ > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> x, y; > + > + VERIFY ( !(x != y) ); > + x.discard(100); > + y.discard(100); > + > + VERIFY ( !(x != y) ); > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > new file mode 100644 > index 00000000000..bec4b172512 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > @@ -0,0 +1,49 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <sstream> > +#include <random> > +#include <testsuite_hooks.h> > +#include <iostream> > + > +void > +test01() > +{ > + std::stringstream str; > + std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> x, y; > + > + x(); > + str << x; > + > + VERIFY ( !(x == y) ); > + str >> y; > + VERIFY ( x == y ); > + for (unsigned long i = 0; i < 100; ++i) > + { > + VERIFY (x() == y()); > + } > + str.clear(); > + str << y; > + x(); > + x(); > + x(); > + str >> x; > + VERIFY ( x == y ); > + for (unsigned long i = 0; i < 1000; ++i) > + { > + VERIFY (x() == y()); > + } > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > new file mode 100644 > index 00000000000..c242056e0a4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > @@ -0,0 +1,26 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > + > +void test01() > +{ > + std::philox4x32 philox; > + const void* p = &philox.word_size; > + p = &philox.word_count; > + p = &philox.round_count; > + p = &philox.multipliers; > + p = &philox.round_consts; > + p = &philox.default_seed; > + p = p; // Suppress unused warning. > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > new file mode 100644 > index 00000000000..5be0108c88c > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > @@ -0,0 +1,50 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_common_types.h> > + > +namespace __gnu_test > +{ > + struct constexpr_member_data > + { > + template<typename _Ttesttype> > + void > + operator()() > + { > + struct _Concept > + { > + void __constraint() > + { > + constexpr auto v1 __attribute__((unused)) > + = _Ttesttype::word_size; > + constexpr auto v2 __attribute__((unused)) > + = _Ttesttype::word_count; > + constexpr auto v3 __attribute__((unused)) > + = _Ttesttype::round_count; > + constexpr auto v4 __attribute__((unused)) > + = _Ttesttype::multipliers; > + constexpr auto v5 __attribute__((unused)) > + = _Ttesttype::round_consts; > + constexpr auto v6 __attribute__((unused)) > + = _Ttesttype::default_seed; > + } > + }; > + > + _Concept c; > + c.__constraint(); > + } > + }; > +}; > + > +int > +main() > +{ > + __gnu_test::constexpr_member_data test; > + typedef std::philox4x32 type; > + test.operator()<type>(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > new file mode 100644 > index 00000000000..eb61d15568a > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > @@ -0,0 +1,41 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > +#include <random> > +#include <testsuite_common_types.h> > + > +namespace __gnu_test > +{ > + struct constexpr_member_functions > + { > + template<typename _Ttesttype> > + void > + operator()() > + { > + struct _Concept > + { > + void __constraint() > + { > + constexpr auto v1 __attribute__((unused)) > + = _Ttesttype::min(); > + constexpr auto v2 __attribute__((unused)) > + = _Ttesttype::max(); > + } > + }; > + _Concept c; > + c.__constraint(); > + } > + }; > +} > + > +int > +main() > +{ > + __gnu_test::constexpr_member_functions test; > + typedef std::philox4x32 type; > + test.operator()<type>(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > new file mode 100644 > index 00000000000..b368ee74106 > --- /dev/null > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > @@ -0,0 +1,26 @@ > +// { dg-do run { target c++26 } } > +// { dg-require-cstdint "" } > + > +// 29.5.4 Random Number Engine Class Templates > +// 29.5.4.5 Class Template philox_engine > + > + > +#include <random> > + > +void > +test01() > +{ > + typedef std::philox_engine<std::uint_fast32_t, > + 32, 4, 10, 0xCD9E8D57, > + 0x9E3779B9, 0xD2511F53, > + 0xBB67AE85> testType; > + > + typedef testType::result_type result_type; > +} > + > +int > +main() > +{ > + test01(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > index 0afba654152..13a0b8947b0 100644 > --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > @@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); > auto x = std::generate_canonical<std::size_t, > std::numeric_limits<std::size_t>::digits>(urng); > > -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } > +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } > > -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } > +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } > -- > 2.49.0 >
Awesome! Thanks for taking care of the ChangeLog entry, and my apologies for missing that. I'll start trying to get support for targets not supporting __uint128_t. Thanks for the feedback throughout as well! On Tuesday, August 19th, 2025 at 8:42 AM, Patrick Palka <ppalka@redhat.com> wrote: > On Tue, 5 Aug 2025, 1nfocalypse wrote: > > > Implements Philox Engine (P2075R6) and associated tests. > > > > Implements additional feedback from v5 from Patrick Palka. > > Also cut some trailing whitespace from the limb propagation > > fix in v5. > > > > Apologies for the delay from v5 - I had to finish writing some > > CTF challenges for DEFCON this week. Thank you for the > > feedback, and please let me know if anything further needs > > to be changed. > > > > Built/tested on x86_64-pc-linux-gnu. > > > This version LGTM! A ChangeLog entry in the commit message is missing > (https://gcc.gnu.org/codingconventions.html#ChangeLogs) but I'm > happy to take care of that while pushing, it'll consist of mostly > boilerplate for a new feature like this. > > > Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) > > > > Implemented additional changes from Patrick Palka. > > Also removed some trailing whitespace in random.tcc. > > > > replaced alias of __ios_base with ios_base > > included <iomanip> in random.h > > altered line number in pr60037-neg.cc to be accurate > > Refactored iterator/index loops to be range-based for > > in istream/ostream operator overloads > > include <random> in philox_engine/cons/seed_seq.cc > > Conforms with errata LWG4143, LWG4153 for Philox Engine. > > > > PR libstdc++/119794 > > --- > > libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ > > libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ > > libstdc++-v3/include/bits/version.def | 9 + > > libstdc++-v3/include/bits/version.h | 10 + > > libstdc++-v3/include/std/random | 3 + > > .../26_numerics/random/philox4x32.cc | 23 ++ > > .../26_numerics/random/philox4x64.cc | 23 ++ > > .../random/philox_engine/cons/119794.cc | 39 +++ > > .../random/philox_engine/cons/copy.cc | 25 ++ > > .../random/philox_engine/cons/default.cc | 27 ++ > > .../random/philox_engine/cons/seed.cc | 20 ++ > > .../random/philox_engine/cons/seed_seq.cc | 24 ++ > > .../random/philox_engine/operators/equal.cc | 30 ++ > > .../random/philox_engine/operators/inequal.cc | 30 ++ > > .../philox_engine/operators/serialize.cc | 49 +++ > > .../philox_engine/requirements/constants.cc | 26 ++ > > .../requirements/constexpr_data.cc | 50 ++++ > > .../requirements/constexpr_functions.cc | 41 +++ > > .../philox_engine/requirements/typedefs.cc | 26 ++ > > .../26_numerics/random/pr60037-neg.cc | 4 +- > > 20 files changed, 931 insertions(+), 2 deletions(-) > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > > > > diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h > > index 1fdaf51934f..e74b7bd75c0 100644 > > --- a/libstdc++-v3/include/bits/random.h > > +++ b/libstdc++-v3/include/bits/random.h > > @@ -33,6 +33,7 @@ > > > > #include <vector> > > #include <bits/uniform_int_dist.h> > > +#include <iomanip> > > > > namespace std _GLIBCXX_VISIBILITY(default) > > { > > @@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > { return !(__lhs == __rhs); } > > #endif > > > > +#if __cpp_lib_philox_engine > > + > > + /** > > + * @brief: A discrete pseudorandom number generator based off of weakened > > + * cryptographic primitives. > > + * > > + * This algorithm was intended to be used for highly parallel random number > > + * generation, and is capable of immensely long periods. It provides "Crush- > > + * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" > > + * test, demonstrating significant apparent entropy. It is not intended for > > + * cryptographic use and should not be used for such, despite being based on > > + * cryptographic primitives. > > + * > > + * The two four-word definitions are likely the best use for this algorithm, > > + * and are given below as defaults. > > + * > > + * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and > > + * David Shaw as a product of D.E. Shaw Research. > > + * > > + * @tparam __w Word size > > + * @tparam n Buffer size > > + * @tparam r Rounds > > + * @tparam consts Multiplication and round constant pack, ordered as > > + * M{0}, C{0}, M{1}, C{1}, ... , M{N/2-1}, C{N/2-1} > > + * > > + * @headerfile random > > + * @since C++26 > > + / > > + template<class _UIntType, size_t __w, > > + size_t __n, size_t __r, > > + _UIntType... __consts> > > + class philox_engine > > + { > > + static_assert(__n == 2 || __n == 4, > > + "template argument N must be either 2 or 4"); > > + static_assert(sizeof...(__consts) == __n, > > + "length of consts array must match specified N"); > > + static_assert(0 < __r, "a number of rounds must be specified"); > > + static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), > > + "specified bitlength must match input type"); > > + template<typename _Sseq> > > + using _If_seed_seq > > + = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; > > + > > + private: > > + // the ordering here is essential to functionality. > > + /* @brief an internal unpacking function for %philox_engine. / > > + template <size_t __ind0, size_t __ind1> > > + static constexpr > > + array<_UIntType, __n / 2> > > + _S_popArray() > > + { > > + if constexpr (__n == 4) > > + return {__consts...[__ind0], __consts...[__ind1]}; > > + else > > + return {__consts...[__ind0]}; > > + } > > + > > + public: > > + /* Type of template param. / > > + using result_type = _UIntType; > > + // public members > > + static constexpr size_t word_size = __w; > > + static constexpr size_t word_count = __n; > > + static constexpr size_t round_count = __r; > > + static constexpr array<result_type, __n / 2> multipliers = > > + philox_engine::_S_popArray<0,2>(); > > + static constexpr array<result_type, __n / 2> round_consts = > > + philox_engine::_S_popArray<1,3>(); > > + > > + /* @brief returns the minimum value possible. / > > + static constexpr result_type > > + min() > > + { return 0; } > > + > > + /* @brief returns the maximum value possible. / > > + static constexpr result_type > > + max() > > + { > > + return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); > > + } > > + // default key value > > + static constexpr result_type default_seed = 20111115u; > > + > > + // constructors > > + philox_engine() > > + : philox_engine(default_seed) > > + {} > > + > > + explicit > > + philox_engine(result_type __value); > > + > > + /* @brief seed sequence constructor for %philox_engine > > + * > > + * @params __q the seed sequence > > + / > > + template<typename _Sseq, typename = _If_seed_seq<_Sseq>> > > + explicit > > + philox_engine(_Sseq& __q) > > + { > > + seed(__q); > > + } > > + > > + void > > + seed(result_type value = default_seed); > > + > > + /* @brief seeds %philox_engine by seed sequence > > + * > > + * @params __q the seed sequence > > + / > > + template<typename _Sseq> > > + _If_seed_seq<_Sseq> > > + seed(_Sseq& __q); > > + > > + /* @brief sets the internal counter "cleartext" > > + * > > + * @params __counter std::array of len N > > + / > > + void > > + set_counter(const array<result_type, __n>& __counter); > > + > > + /* @brief compares two %philox_engine objects > > + * > > + * @params __x A %philox_engine object > > + * @params __y A %philox_engine object > > + * > > + * @returns true if the objects will produce an identical stream, false > > + * otherwise > > + / > > + friend bool > > + operator==(const philox_engine& __x, const philox_engine& __y) > > + { > > + return (std::equal(__x._M_x.begin(), __x._M_x.end(), > > + __y._M_x.begin(), __y._M_x.end()) > > + && std::equal(__x._M_y.begin(), __x._M_y.end(), > > + __y._M_y.begin(), __y._M_y.end()) > > + && std::equal(__x._M_k.begin(), __x._M_k.end(), > > + __y._M_k.begin(), __y._M_k.end()) > > + && __x._M_i == __y._M_i); > > + } > > + > > + /* @brief outputs a single w-bit number and handles state advancement > > + * > > + * @returns return_type > > + / > > + _UIntType > > + operator()(); > > + > > + /* @brief discards __z numbers > > + * > > + * @params __z number of iterations to discard > > + / > > + void > > + discard(unsigned long long __z); > > + > > + /* @brief outputs the state of the generator > > + * > > + * @param __os An output stream. > > + * @param __x A %philox_engine object reference > > + * > > + * @returns the state of the Philox Engine in __os > > + / > > + template<typename _CharT, typename _Traits> > > + friend basic_ostream<_CharT, _Traits>& > > + operator<<(basic_ostream<_CharT, _Traits>& __os, > > + const philox_engine& __x) > > + { > > + const typename ios_base::fmtflags __flags = __os.flags(); > > + const _CharT __fill = __os.fill(); > > + __os.flags(ios_base::dec | ios_base::left); > > + __os.fill(__os.widen(' ')); > > + for (auto &__subkey : __x._M_k) > > + __os << __subkey << ' '; > > + for (auto &__ctr : __x._M_x) > > + __os << __ctr << ' '; > > + __os << __x._M_i; > > + __os.flags(__flags); > > + __os.fill(__fill); > > + return __os; > > + } > > + > > + /* @brief takes input to set the state of the %philox_engine object > > + * > > + * @param __is An input stream. > > + * @param __x A %philox_engine object reference > > + * > > + * @returns %philox_engine object is set with values from instream > > + / > > + template <typename _CharT, typename _Traits> > > + friend basic_istream<_CharT, _Traits>& > > + operator>>(basic_istream<_CharT, _Traits>& __is, > > + philox_engine& __x) > > + { > > + const typename ios_base::fmtflags __flags = __is.flags(); > > + __is.flags(ios_base::dec | ios_base::skipws); > > + for (auto &__subkey : __x._M_k) > > + __is >> __subkey; > > + for (auto &__ctr : __x._M_x) > > + __is >> __ctr; > > + array<_UIntType, __n> __tmpCtr = __x._M_x; > > + unsigned char __setIndex = 0; > > + for (size_t __j = 0; __j < __x._M_x.size(); ++__j) > > + { > > + if (__x._M_x[__j] > 0) > > + { > > + __setIndex = __j; > > + break; > > + } > > + } > > + for (size_t __j = 0; __j <= __setIndex; ++__j) > > + { > > + if (__j != __setIndex) > > + __x._M_x[__j] = max(); > > + else > > + --__x._M_x[__j]; > > + } > > + __x._M_philox(); > > + __x._M_x = __tmpCtr; > > + __is >> __x._M_i; > > + __is.flags(__flags); > > + return __is; > > + } > > + private: > > + // private state variables > > + array<_UIntType, __n> _M_x; > > + array<_UIntType, __n / 2> _M_k; > > + array<_UIntType, __n> _M_y; > > + unsigned long long _M_i = 0; > > + > > + /* @brief Takes the high values of the product of __a, __b > > + * > > + * @params __a an unsigned integer > > + * @params __b an unsigned integer > > + * > > + * @returns an unsigned integer of at most bitlength W > > + / > > + static > > + _UIntType > > + _S_mulhi(_UIntType __a, _UIntType __b); // (AB)/2^W > > + > > + /** @brief Takes the low values of the product of __a, __b > > + * > > + * @params __a an unsigned integer > > + * @params __b an unsigned integer > > + * > > + * @returns an unsigned integer of at most bitlength W > > + / > > + static > > + _UIntType > > + _S_mullo(_UIntType __a, _UIntType __b); // (AB)%2^W > > + > > + /** @brief an R-round substitution/Feistel Network hybrid for > > + * %philox_engine > > + / > > + void > > + _M_philox(); > > + > > + /* @brief an internal transition function for the %philox_engine. / > > + void > > + _M_transition(); > > + }; > > + > > +#endif > > + > > /* > > * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller. > > */ > > @@ -1742,6 +2007,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > typedef minstd_rand0 default_random_engine; > > > > +#if __cpp_lib_philox_engine > > + > > + typedef philox_engine< > > + uint_fast32_t, > > + 32, 4, 10, > > + 0xCD9E8D57, 0x9E3779B9, > > + 0xD2511F53, 0xBB67AE85> philox4x32; > > + > > + /** > > + * Alternative Philox instance (64 bit) > > + / > > + typedef philox_engine< > > + uint_fast64_t, > > + 64, 4, 10, > > + 0xCA5A826395121157, 0x9E3779B97F4A7C15, > > + 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B> philox4x64; > > +#endif > > + > > /* > > * A standard interface to a platform-specific non-deterministic > > * random number generator (if any are available). > > diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc > > index 53ccacb2e38..53f302da800 100644 > > --- a/libstdc++-v3/include/bits/random.tcc > > +++ b/libstdc++-v3/include/bits/random.tcc > > @@ -907,6 +907,197 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > return __is; > > } > > > > +#if __cpp_lib_philox_engine > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + _UIntType > > + philox_engine<_UIntType, > > + __w, __n, __r, __consts...>::_S_mulhi(_UIntType __a, _UIntType __b) > > + { > > + const __uint128_t __num = > > + static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b); > > + return static_cast<_UIntType>((__num >> __w) & max()); > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + _UIntType > > + philox_engine<_UIntType, > > + __w, __n, __r, __consts...>::_S_mullo(_UIntType __a, _UIntType __b) > > + { > > + return static_cast<_UIntType>((__a * __b) & max()); > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + void > > + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_transition() > > + { > > + ++_M_i; > > + if (_M_i == __n) > > + { > > + _M_philox(); > > + if constexpr (__n == 4) > > + { > > + __uint128_t __uh = > > + (static_cast<__uint128_t>(_M_x[1]) << __w) > > + | (static_cast<__uint128_t>(_M_x[0]) + 1); > > + __uint128_t __lh = > > + ((static_cast<__uint128_t>(_M_x[3]) << __w) > > + | (_M_x[2])); > > + __uint128_t __bigMask = > > + (static_cast<__uint128_t>(1) << ((2 * __w) - 1)) > > + | ((static_cast<__uint128_t>(1) << ((2 * __w) - 1)) - 1); > > + if ((__uh & __bigMask) == 0) > > + { > > + ++__lh; > > + __uh = 0; > > + } > > + _M_x[0] = __uh & max(); > > + _M_x[1] = (__uh >> (__w)) & max(); > > + _M_x[2] = __lh & max(); > > + _M_x[3] = (__lh >> (__w)) & max(); > > + } else > > + { > > + __uint128_t __num = > > + (static_cast<__uint128_t>(_M_x[1]) << __w) > > + | (static_cast<__uint128_t>(_M_x[0]) + 1); > > + _M_x[0] = __num & max(); > > + _M_x[1] = (__num >> __w) & max(); > > + } > > + _M_i = 0; > > + } > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + void > > + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() > > + { > > + array<_UIntType, __n> __outputSeq{}; > > + for (size_t __j = 0; __j < __n; ++__j) > > + __outputSeq[__j] = _M_x[__j]; > > + for (unsigned long __j = 0; __j < __r; ++__j) > > + { > > + array<_UIntType, __n> __intermedSeq{}; > > + if constexpr (__n == 4) > > + { > > + __intermedSeq[0] = __outputSeq[2]; > > + __intermedSeq[1] = __outputSeq[1]; > > + __intermedSeq[2] = __outputSeq[0]; > > + __intermedSeq[3] = __outputSeq[3]; > > + } else > > + { > > + __intermedSeq[0] = __outputSeq[0]; > > + __intermedSeq[1] = __outputSeq[1]; > > + } > > + for (unsigned long __k = 0; __k < (__n/2); ++__k) > > + { > > + __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) > > + ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) > > + ^ __intermedSeq[2*__k+1]; > > + > > + __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], > > + multipliers[__k]); > > + } > > + } > > + for (unsigned long __j = 0; __j < __n; ++__j) > > + _M_y[__j] = __outputSeq[__j]; > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + philox_engine<_UIntType, > > + __w, __n, __r, __consts...>::philox_engine(result_type __value) > > + { > > + std::fill(_M_x.begin(), _M_x.end(), 0); > > + std::fill(_M_k.begin(), _M_k.end(), 0); > > + std::fill(_M_y.begin(), _M_y.end(), 0); > > + _M_k[0] = __value & max(); > > + _M_i = __n - 1; > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + void > > + philox_engine<_UIntType, > > + __w, __n, __r, __consts...>::seed(result_type __value) > > + { > > + std::fill(_M_x.begin(), _M_x.end(), 0); > > + std::fill(_M_k.begin(), _M_k.end(), 0); > > + std::fill(_M_y.begin(), _M_y.end(), 0); > > + _M_k[0] = __value & max(); > > + _M_i = __n - 1; > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + void > > + philox_engine<_UIntType, __w, > > + __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) > > + { > > + for (unsigned long long __j = 0; __j < __n; ++__j) > > + _M_x[__j] = __counter[__n - 1 - __j] & max(); > > + _M_i = __n - 1; > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + template<typename _Sseq> > > + auto > > + philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) > > + -> _If_seed_seq<_Sseq> > > + { > > + std::fill(_M_k.begin(), _M_k.end(), 0); > > + const unsigned long long __p = 1 + ((__w - 1)/ 32); > > + uint_least32_t __tmpArr[(__n / 2) * __p]; > > + __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) __p)); > > + for (unsigned long long __k = 0; __k < (__n/2); ++__k) > > + { > > + unsigned long long __precalc = 0; > > + for (unsigned long long __j = 0; __j < __p; ++__j) > > + { > > + unsigned long long __multiplicand = (1ull << (32 * __j)); > > + __precalc += (__tmpArr[__k__p + __j] * __multiplicand) & max(); > > + } > > + _M_k[__k] = __precalc; > > + } > > + std::fill(_M_x.begin(), _M_x.end(), 0); > > + std::fill(_M_y.begin(), _M_y.end(), 0); > > + _M_i = __n - 1; > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + void > > + philox_engine<_UIntType, > > + __w, __n, __r, __consts...>::discard(unsigned long long __z) > > + { > > + for (unsigned long long __j = 0; __j < __z; ++__j) > > + _M_transition(); > > + } > > + > > + template<class _UIntType, > > + size_t __w, size_t __n, > > + size_t __r, _UIntType... __consts> > > + _UIntType > > + philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() > > + { > > + _M_transition(); > > + return _M_y[_M_i]; > > + } > > + > > +#endif > > > > template<typename _IntType, typename _CharT, typename _Traits> > > std::basic_ostream<_CharT, _Traits>& > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > > index dbe2cb8f175..4c21fa187c8 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -2069,6 +2069,15 @@ ftms = { > > }; > > }; > > > > +ftms = { > > + name = philox_engine; > > + values = { > > + v = 202406; > > + cxxmin = 26; > > + extra_cond = "SIZEOF_INT128"; > > + }; > > +}; > > + > > // Standard test specifications. > > stds[97] = ">= 199711L"; > > stds[03] = ">= 199711L"; > > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > > index 7bb6016df68..75a9946adce 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -2318,4 +2318,14 @@ > > #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ > > #undef __glibcxx_want_constexpr_exceptions > > > > +#if !defined(__cpp_lib_philox_engine) > > +# if (__cplusplus > 202302L) && (SIZEOF_INT128) > > +# define __glibcxx_philox_engine 202406L > > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) > > +# define __cpp_lib_philox_engine 202406L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ > > +#undef __glibcxx_want_philox_engine > > + > > #undef __glibcxx_want_all > > diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random > > index 0e058a04bd9..8a02ade4b75 100644 > > --- a/libstdc++-v3/include/std/random > > +++ b/libstdc++-v3/include/std/random > > @@ -39,6 +39,9 @@ > > # include <bits/c++0x_warning.h> > > #else > > > > +#define __glibcxx_want_philox_engine > > +#include <bits/version.h> > > + > > #include <cmath> > > #include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t > > #include <cstdlib> > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > > new file mode 100644 > > index 00000000000..d5a8ca078ef > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > > @@ -0,0 +1,23 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +void > > +test01() > > +{ > > + std::philox4x32 a; > > + a.discard(9999); > > + > > + VERIFY( a() == 1955073260 ); > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > > new file mode 100644 > > index 00000000000..131f094cb28 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > > @@ -0,0 +1,23 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +void > > +test01() > > +{ > > + std::philox4x64 a; > > + a.discard(9999); > > + > > + VERIFY( a() == 3409172418970261260 ); > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > > new file mode 100644 > > index 00000000000..c3a5a0eb754 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > > @@ -0,0 +1,39 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +int f(int x) > > +{ > > + std::seed_seq sq(&x, &x + 1); > > + auto rnd = std::philox4x32(sq); > > + return std::uniform_int_distribution<int>()(rnd); > > +} > > + > > +int g(int x) > > +{ > > + std::seed_seq sq(&x, &x + 1); > > + auto rnd = std::philox4x32(); > > + rnd.seed(sq); > > + return std::uniform_int_distribution<int>()(rnd); > > +} > > + > > +void test01() > > +{ > > + const int f1 = f(0); > > + const int f2 = f(0); > > + > > + const int g1 = g(0); > > + const int g2 = g(0); > > + > > + VERIFY( f1 == f2 ); > > + VERIFY( g1 == g2 ); > > + VERIFY( f1 == g1 ); > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > > new file mode 100644 > > index 00000000000..4f61928a157 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > > @@ -0,0 +1,25 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > + > > +void > > +test01() > > +{ > > + > > + std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); > > + > > + const auto f(e); > > + auto g(f); > > + g = g; // Suppress unused warning > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > > new file mode 100644 > > index 00000000000..9f9ae94db0f > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > > @@ -0,0 +1,27 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +void > > +test01() > > +{ > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> philox4x32nullkey(0); > > + > > + VERIFY( philox4x32nullkey.min() == 0 ); > > + VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); > > + VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > > new file mode 100644 > > index 00000000000..5cb914f4b45 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > > @@ -0,0 +1,20 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +#include <random> > > + > > +void > > +test01() > > +{ > > + unsigned long seed = 2; > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> philox4x32seeded(seed); > > +} > > + > > +int main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > > new file mode 100644 > > index 00000000000..7d9e3e6540d > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > > @@ -0,0 +1,24 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > + > > +void > > +test01() > > +{ > > + std::seed_seq sseq{ 1, 2, 3, 4 }; > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> philox4x32sseq(sseq); > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > > new file mode 100644 > > index 00000000000..4f62bfbbd88 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > > @@ -0,0 +1,30 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +void > > +test01() > > +{ > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> x, y; > > + > > + VERIFY ( x == y); > > + x.discard(100); > > + y.discard(100); > > + > > + VERIFY (x == y); > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > > new file mode 100644 > > index 00000000000..86d757db904 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > > @@ -0,0 +1,30 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_hooks.h> > > + > > +void > > +test01() > > +{ > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> x, y; > > + > > + VERIFY ( !(x != y) ); > > + x.discard(100); > > + y.discard(100); > > + > > + VERIFY ( !(x != y) ); > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > > new file mode 100644 > > index 00000000000..bec4b172512 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > > @@ -0,0 +1,49 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <sstream> > > +#include <random> > > +#include <testsuite_hooks.h> > > +#include <iostream> > > + > > +void > > +test01() > > +{ > > + std::stringstream str; > > + std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> x, y; > > + > > + x(); > > + str << x; > > + > > + VERIFY ( !(x == y) ); > > + str >> y; > > + VERIFY ( x == y ); > > + for (unsigned long i = 0; i < 100; ++i) > > + { > > + VERIFY (x() == y()); > > + } > > + str.clear(); > > + str << y; > > + x(); > > + x(); > > + x(); > > + str >> x; > > + VERIFY ( x == y ); > > + for (unsigned long i = 0; i < 1000; ++i) > > + { > > + VERIFY (x() == y()); > > + } > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > > new file mode 100644 > > index 00000000000..c242056e0a4 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > > @@ -0,0 +1,26 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > + > > +void test01() > > +{ > > + std::philox4x32 philox; > > + const void* p = &philox.word_size; > > + p = &philox.word_count; > > + p = &philox.round_count; > > + p = &philox.multipliers; > > + p = &philox.round_consts; > > + p = &philox.default_seed; > > + p = p; // Suppress unused warning. > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > > new file mode 100644 > > index 00000000000..5be0108c88c > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > > @@ -0,0 +1,50 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_common_types.h> > > + > > +namespace __gnu_test > > +{ > > + struct constexpr_member_data > > + { > > + template<typename _Ttesttype> > > + void > > + operator()() > > + { > > + struct _Concept > > + { > > + void __constraint() > > + { > > + constexpr auto v1 attribute((unused)) > > + = _Ttesttype::word_size; > > + constexpr auto v2 attribute((unused)) > > + = _Ttesttype::word_count; > > + constexpr auto v3 attribute((unused)) > > + = _Ttesttype::round_count; > > + constexpr auto v4 attribute((unused)) > > + = _Ttesttype::multipliers; > > + constexpr auto v5 attribute((unused)) > > + = _Ttesttype::round_consts; > > + constexpr auto v6 attribute((unused)) > > + = _Ttesttype::default_seed; > > + } > > + }; > > + > > + _Concept c; > > + c.__constraint(); > > + } > > + }; > > +}; > > + > > +int > > +main() > > +{ > > + __gnu_test::constexpr_member_data test; > > + typedef std::philox4x32 type; > > + test.operator()<type>(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > > new file mode 100644 > > index 00000000000..eb61d15568a > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > > @@ -0,0 +1,41 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > +#include <random> > > +#include <testsuite_common_types.h> > > + > > +namespace __gnu_test > > +{ > > + struct constexpr_member_functions > > + { > > + template<typename _Ttesttype> > > + void > > + operator()() > > + { > > + struct _Concept > > + { > > + void __constraint() > > + { > > + constexpr auto v1 attribute((unused)) > > + = _Ttesttype::min(); > > + constexpr auto v2 attribute((unused)) > > + = _Ttesttype::max(); > > + } > > + }; > > + _Concept c; > > + c.__constraint(); > > + } > > + }; > > +} > > + > > +int > > +main() > > +{ > > + __gnu_test::constexpr_member_functions test; > > + typedef std::philox4x32 type; > > + test.operator()<type>(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > > new file mode 100644 > > index 00000000000..b368ee74106 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > > @@ -0,0 +1,26 @@ > > +// { dg-do run { target c++26 } } > > +// { dg-require-cstdint "" } > > + > > +// 29.5.4 Random Number Engine Class Templates > > +// 29.5.4.5 Class Template philox_engine > > + > > + > > +#include <random> > > + > > +void > > +test01() > > +{ > > + typedef std::philox_engine<std::uint_fast32_t, > > + 32, 4, 10, 0xCD9E8D57, > > + 0x9E3779B9, 0xD2511F53, > > + 0xBB67AE85> testType; > > + > > + typedef testType::result_type result_type; > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + return 0; > > +} > > diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > > index 0afba654152..13a0b8947b0 100644 > > --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > > +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > > @@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); > > auto x = std::generate_canonical<std::size_t, > > std::numeric_limitsstd::size_t::digits>(urng); > > > > -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target --* } 270 } > > +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target --* } 271 } > > > > -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target --* } 3357 } > > +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target --* } 3548 } > > -- > > 2.49.0
On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: >Implements Philox Engine (P2075R6) and associated tests. > >Implements additional feedback from v5 from Patrick Palka. >Also cut some trailing whitespace from the limb propagation >fix in v5. > >Apologies for the delay from v5 - I had to finish writing some >CTF challenges for DEFCON this week. Thank you for the >feedback, and please let me know if anything further needs >to be changed. > >Built/tested on x86_64-pc-linux-gnu. >From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 >From: 1nfocalypse <1nfocalypse@protonmail.com> >Date: Tue, 5 Aug 2025 01:37:18 +0000 >Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) > >Implemented additional changes from Patrick Palka. >Also removed some trailing whitespace in random.tcc. > >replaced alias of __ios_base with ios_base >included <iomanip> in random.h >altered line number in pr60037-neg.cc to be accurate >Refactored iterator/index loops to be range-based for >in istream/ostream operator overloads >include <random> in philox_engine/cons/seed_seq.cc >Conforms with errata LWG4143, LWG4153 for Philox Engine. > > PR libstdc++/119794 >--- > libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ > libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ > libstdc++-v3/include/bits/version.def | 9 + > libstdc++-v3/include/bits/version.h | 10 + > libstdc++-v3/include/std/random | 3 + > .../26_numerics/random/philox4x32.cc | 23 ++ > .../26_numerics/random/philox4x64.cc | 23 ++ > .../random/philox_engine/cons/119794.cc | 39 +++ > .../random/philox_engine/cons/copy.cc | 25 ++ > .../random/philox_engine/cons/default.cc | 27 ++ > .../random/philox_engine/cons/seed.cc | 20 ++ > .../random/philox_engine/cons/seed_seq.cc | 24 ++ > .../random/philox_engine/operators/equal.cc | 30 ++ > .../random/philox_engine/operators/inequal.cc | 30 ++ > .../philox_engine/operators/serialize.cc | 49 +++ > .../philox_engine/requirements/constants.cc | 26 ++ > .../requirements/constexpr_data.cc | 50 ++++ > .../requirements/constexpr_functions.cc | 41 +++ > .../philox_engine/requirements/typedefs.cc | 26 ++ > .../26_numerics/random/pr60037-neg.cc | 4 +- > 20 files changed, 931 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h >index 1fdaf51934f..e74b7bd75c0 100644 >--- a/libstdc++-v3/include/bits/random.h >+++ b/libstdc++-v3/include/bits/random.h >@@ -33,6 +33,7 @@ > > #include <vector> > #include <bits/uniform_int_dist.h> >+#include <iomanip> > > namespace std _GLIBCXX_VISIBILITY(default) > { >@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { return !(__lhs == __rhs); } > #endif > >+#if __cpp_lib_philox_engine This should check __glibcxx_philox_engine (just in case this code is moved to a separate header that ends up being included somewhere withou all of <random>). >+ /** >+ * @brief: A discrete pseudorandom number generator based off of weakened >+ * cryptographic primitives. Can we say "based on" instead of "based off of" which is an informal American English term that sounds wrong to me. I'm also not sure what this is telling me as a non-export user. Would it be clearer to talk about it having weak cryptographic properties, rather than being based on weakened cryptographic primitives? >+ * >+ * This algorithm was intended to be used for highly parallel random number "was intended to be used" sounds like it refers to something in the past that is no longer true. Maybe "was designed to be used" instead? >+ * generation, and is capable of immensely long periods. It provides "Crush- I was going to ask whether immensely is a bit hyperbolic and could be just "very long periods (e.g. ...)" but since we're talking at least 2^128 I think "immensely" is fine :-) >+ * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" >+ * test, demonstrating significant apparent entropy. It is not intended for >+ * cryptographic use and should not be used for such, despite being based on >+ * cryptographic primitives. >+ * >+ * The two four-word definitions are likely the best use for this algorithm, >+ * and are given below as defaults. Can we refer to those as aliases or typedefs rather than definitions? Or specializations? Maybe even say it like this: * The typedefs `philox4x32` and `philox4x64` are provided as * suitable defaults for most use cases, providing high-quality * random numbers at reasonable performance. >+ * >+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and >+ * David Shaw as a product of D.E. Shaw Research. >+ * >+ * @tparam __w Word size >+ * @tparam __n Buffer size >+ * @tparam __r Rounds >+ * @tparam __consts Multiplication and round constant pack, ordered as >+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} >+ * >+ * @headerfile random >+ * @since C++26 >+ */ >+ template<class _UIntType, size_t __w, Our convention in libstdc++ code is to type 'typename' for template parameters instead of 'class' >+ size_t __n, size_t __r, >+ _UIntType... __consts> >+ class philox_engine >+ { >+ static_assert(__n == 2 || __n == 4, >+ "template argument N must be either 2 or 4"); >+ static_assert(sizeof...(__consts) == __n, >+ "length of consts array must match specified N"); >+ static_assert(0 < __r, "a number of rounds must be specified"); >+ static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), >+ "specified bitlength must match input type"); The static assert messages all use the preferred "must" form, thanks! >+ template<typename _Sseq> >+ using _If_seed_seq >+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; >+ >+ private: This access specifier is redundant because everything above it is already private. >+ // the ordering here is essential to functionality. Does this refer to the ordering of the _S_popArray member in the class, because it needs to be declared before it's used? If so, I think we can drop the comment. If somebody fails to notice that when reading the class definition, they'd find out pretty quickly if they tried to reorder it and got errors. The style recommendations to put private internal members last is not a hard requirement, for cases like this where the order is significant putting them first is obviously fine (since you have no choice! :-) >+ /** @brief an internal unpacking function for %philox_engine. */ We don't need a doxygen comment for private member functions, they won't get included in the docs anyway. >+ template <size_t __ind0, size_t __ind1> >+ static constexpr >+ array<_UIntType, __n / 2> >+ _S_popArray() >+ { >+ if constexpr (__n == 4) >+ return {__consts...[__ind0], __consts...[__ind1]}; >+ else >+ return {__consts...[__ind0]}; >+ } >+ >+ public: >+ /** Type of template param. */ >+ using result_type = _UIntType; >+ // public members >+ static constexpr size_t word_size = __w; >+ static constexpr size_t word_count = __n; >+ static constexpr size_t round_count = __r; >+ static constexpr array<result_type, __n / 2> multipliers = >+ philox_engine::_S_popArray<0,2>(); >+ static constexpr array<result_type, __n / 2> round_consts = >+ philox_engine::_S_popArray<1,3>(); >+ >+ /** @brief returns the minimum value possible. */ >+ static constexpr result_type >+ min() >+ { return 0; } >+ >+ /** @brief returns the maximum value possible. */ >+ static constexpr result_type >+ max() >+ { >+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); >+ } >+ // default key value >+ static constexpr result_type default_seed = 20111115u; I wonder if we have a defect in the C++26 draft here, because 20111115u won't fit in a 16-bit int. We fixed that for std::subtract_with_carry_engine via: https://cplusplus.github.io/LWG/issue3809 and then fixed what I broke via: https://cplusplus.github.io/LWG/issue4014 Since the value of default_seed is only used as the argument to the default ctor (or the equivalent of calling seed() with no argument) and the ctor reduces value by mod 2^w, I think we could define the default seed as 20111115u & max(). But let's leave it for now, I'll raise this with LWG. >+ >+ // constructors >+ philox_engine() >+ : philox_engine(default_seed) >+ {} >+ >+ explicit >+ philox_engine(result_type __value); >+ >+ /** @brief seed sequence constructor for %philox_engine >+ * >+ * @params __q the seed sequence >+ */ >+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> >+ explicit >+ philox_engine(_Sseq& __q) >+ { >+ seed(__q); >+ } >+ >+ void >+ seed(result_type value = default_seed); >+ >+ /** @brief seeds %philox_engine by seed sequence >+ * >+ * @params __q the seed sequence >+ */ >+ template<typename _Sseq> >+ _If_seed_seq<_Sseq> >+ seed(_Sseq& __q); >+ >+ /** @brief sets the internal counter "cleartext" >+ * >+ * @params __counter std::array of len N >+ */ >+ void >+ set_counter(const array<result_type, __n>& __counter); >+ >+ /** @brief compares two %philox_engine objects >+ * >+ * @params __x A %philox_engine object >+ * @params __y A %philox_engine object >+ * >+ * @returns true if the objects will produce an identical stream, false >+ * otherwise >+ */ >+ friend bool >+ operator==(const philox_engine& __x, const philox_engine& __y) >+ { >+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), >+ __y._M_x.begin(), __y._M_x.end()) >+ && std::equal(__x._M_y.begin(), __x._M_y.end(), >+ __y._M_y.begin(), __y._M_y.end()) >+ && std::equal(__x._M_k.begin(), __x._M_k.end(), >+ __y._M_k.begin(), __y._M_k.end()) >+ && __x._M_i == __y._M_i); >+ } >+ >+ /** @brief outputs a single w-bit number and handles state advancement >+ * >+ * @returns return_type >+ */ >+ _UIntType >+ operator()(); >+ >+ /** @brief discards __z numbers >+ * >+ * @params __z number of iterations to discard >+ */ >+ void >+ discard(unsigned long long __z); >+ >+ /** @brief outputs the state of the generator >+ * >+ * @param __os An output stream. >+ * @param __x A %philox_engine object reference >+ * >+ * @returns the state of the Philox Engine in __os >+ */ >+ template<typename _CharT, typename _Traits> >+ friend basic_ostream<_CharT, _Traits>& >+ operator<<(basic_ostream<_CharT, _Traits>& __os, >+ const philox_engine& __x) >+ { The "operator<<" and "{" lines should be lined up with "friend" >+ const typename ios_base::fmtflags __flags = __os.flags(); >+ const _CharT __fill = __os.fill(); >+ __os.flags(ios_base::dec | ios_base::left); >+ __os.fill(__os.widen(' ')); >+ for (auto &__subkey : __x._M_k) auto& rather than putting the space before the & please (and below as well). >+ __os << __subkey << ' '; >+ for (auto &__ctr : __x._M_x) >+ __os << __ctr << ' '; Inserting ' ' will get widened if __os is a wide stream, but since you've already called __os.widen(' ') above you could save the result of that in a local variable and then insert that character instead of ' '. That will avoid calling widen again on every iteration of the loop. >+ __os << __x._M_i; >+ __os.flags(__flags); If writing to the stream throws an exception we won't restore the flags and fill character, but I think that's consistent with all our operator<< overloads in <random>. >+ __os.fill(__fill); >+ return __os; >+ } >+ >+ /** @brief takes input to set the state of the %philox_engine object >+ * >+ * @param __is An input stream. >+ * @param __x A %philox_engine object reference >+ * >+ * @returns %philox_engine object is set with values from instream >+ */ >+ template <typename _CharT, typename _Traits> >+ friend basic_istream<_CharT, _Traits>& >+ operator>>(basic_istream<_CharT, _Traits>& __is, >+ philox_engine& __x) >+ { Same comment about aligning to "friend". >+ const typename ios_base::fmtflags __flags = __is.flags(); >+ __is.flags(ios_base::dec | ios_base::skipws); >+ for (auto &__subkey : __x._M_k) >+ __is >> __subkey; >+ for (auto &__ctr : __x._M_x) >+ __is >> __ctr; >+ array<_UIntType, __n> __tmpCtr = __x._M_x; >+ unsigned char __setIndex = 0; >+ for (size_t __j = 0; __j < __x._M_x.size(); ++__j) >+ { >+ if (__x._M_x[__j] > 0) >+ { >+ __setIndex = __j; >+ break; >+ } >+ } >+ for (size_t __j = 0; __j <= __setIndex; ++__j) >+ { >+ if (__j != __setIndex) >+ __x._M_x[__j] = max(); >+ else >+ --__x._M_x[__j]; >+ } >+ __x._M_philox(); >+ __x._M_x = __tmpCtr; >+ __is >> __x._M_i; >+ __is.flags(__flags); >+ return __is; >+ } >+ private: >+ // private state variables >+ array<_UIntType, __n> _M_x; >+ array<_UIntType, __n / 2> _M_k; >+ array<_UIntType, __n> _M_y; >+ unsigned long long _M_i = 0; >+ >+ /** @brief Takes the high values of the product of __a, __b >+ * >+ * @params __a an unsigned integer >+ * @params __b an unsigned integer >+ * >+ * @returns an unsigned integer of at most bitlength W The constant 'w' is lowercase in the spec (and in our template parameter, __w). But we don't need Doxygen comments here, it can just be a simple non-Doxygen comment to explain what's going on (and so no need to say that params of type _UIntType are unsigned integers). Something like: // The high w-bit word of the product of a and b. >+ */ >+ static >+ _UIntType >+ _S_mulhi(_UIntType __a, _UIntType __b); // (A*B)/2^W I think I'd prefer (a*b)/2^w here, i.e. all lowercase. >+ >+ /** @brief Takes the low values of the product of __a, __b >+ * >+ * @params __a an unsigned integer >+ * @params __b an unsigned integer >+ * >+ * @returns an unsigned integer of at most bitlength W >+ */ // The low w-bit word of the product of a and b. >+ static >+ _UIntType >+ _S_mullo(_UIntType __a, _UIntType __b); // (A*B)%2^W >+ >+ /** @brief an R-round substitution/Feistel Network hybrid for >+ * %philox_engine >+ */ Simplify this too: // An R-round substitution/Feistel Network hybrid. >+ void >+ _M_philox(); >+ >+ /** @brief an internal transition function for the %philox_engine. */ And I think there's no need for any comment here, it doesn't seem to add anything that philox_engine::_M_transition() doesn't already say. >+ void >+ _M_transition(); >+ }; >+ >+#endif >+ > /** > * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller. > */ >@@ -1742,6 +2007,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > typedef minstd_rand0 default_random_engine; > >+#if __cpp_lib_philox_engine Use __glibcxx_philox_engine again >+ >+ typedef philox_engine< >+ uint_fast32_t, >+ 32, 4, 10, >+ 0xCD9E8D57, 0x9E3779B9, >+ 0xD2511F53, 0xBB67AE85> philox4x32; >+ >+ /** >+ * Alternative Philox instance (64 bit) >+ */ >+ typedef philox_engine< >+ uint_fast64_t, >+ 64, 4, 10, >+ 0xCA5A826395121157, 0x9E3779B97F4A7C15, >+ 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B> philox4x64; >+#endif >+ > /** > * A standard interface to a platform-specific non-deterministic > * random number generator (if any are available). >diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc >index 53ccacb2e38..53f302da800 100644 >--- a/libstdc++-v3/include/bits/random.tcc >+++ b/libstdc++-v3/include/bits/random.tcc >@@ -907,6 +907,197 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return __is; > } > >+#if __cpp_lib_philox_engine >+ >+ template<class _UIntType, 'typename' here again and the definitions below >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ _UIntType >+ philox_engine<_UIntType, >+ __w, __n, __r, __consts...>::_S_mulhi(_UIntType __a, _UIntType __b) >+ { >+ const __uint128_t __num = >+ static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b); >+ return static_cast<_UIntType>((__num >> __w) & max()); >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ _UIntType >+ philox_engine<_UIntType, >+ __w, __n, __r, __consts...>::_S_mullo(_UIntType __a, _UIntType __b) >+ { >+ return static_cast<_UIntType>((__a * __b) & max()); >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ void >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_transition() >+ { >+ ++_M_i; >+ if (_M_i == __n) >+ { >+ _M_philox(); >+ if constexpr (__n == 4) >+ { >+ __uint128_t __uh = >+ (static_cast<__uint128_t>(_M_x[1]) << __w) >+ | (static_cast<__uint128_t>(_M_x[0]) + 1); >+ __uint128_t __lh = >+ ((static_cast<__uint128_t>(_M_x[3]) << __w) >+ | (_M_x[2])); >+ __uint128_t __bigMask = >+ (static_cast<__uint128_t>(1) << ((2 * __w) - 1)) >+ | ((static_cast<__uint128_t>(1) << ((2 * __w) - 1)) - 1); >+ if ((__uh & __bigMask) == 0) >+ { >+ ++__lh; >+ __uh = 0; >+ } >+ _M_x[0] = __uh & max(); >+ _M_x[1] = (__uh >> (__w)) & max(); >+ _M_x[2] = __lh & max(); >+ _M_x[3] = (__lh >> (__w)) & max(); >+ } else >+ { >+ __uint128_t __num = >+ (static_cast<__uint128_t>(_M_x[1]) << __w) >+ | (static_cast<__uint128_t>(_M_x[0]) + 1); >+ _M_x[0] = __num & max(); >+ _M_x[1] = (__num >> __w) & max(); >+ } >+ _M_i = 0; >+ } >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ void >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() >+ { >+ array<_UIntType, __n> __outputSeq{}; >+ for (size_t __j = 0; __j < __n; ++__j) >+ __outputSeq[__j] = _M_x[__j]; >+ for (unsigned long __j = 0; __j < __r; ++__j) >+ { >+ array<_UIntType, __n> __intermedSeq{}; >+ if constexpr (__n == 4) >+ { >+ __intermedSeq[0] = __outputSeq[2]; >+ __intermedSeq[1] = __outputSeq[1]; >+ __intermedSeq[2] = __outputSeq[0]; >+ __intermedSeq[3] = __outputSeq[3]; >+ } else >+ { >+ __intermedSeq[0] = __outputSeq[0]; >+ __intermedSeq[1] = __outputSeq[1]; >+ } >+ for (unsigned long __k = 0; __k < (__n/2); ++__k) >+ { >+ __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) >+ ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) >+ ^ __intermedSeq[2*__k+1]; >+ >+ __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], >+ multipliers[__k]); >+ } >+ } >+ for (unsigned long __j = 0; __j < __n; ++__j) >+ _M_y[__j] = __outputSeq[__j]; >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ philox_engine<_UIntType, >+ __w, __n, __r, __consts...>::philox_engine(result_type __value) >+ { >+ std::fill(_M_x.begin(), _M_x.end(), 0); >+ std::fill(_M_k.begin(), _M_k.end(), 0); >+ std::fill(_M_y.begin(), _M_y.end(), 0); Can we just initialize these in a mem-initializer-list? : _M_x{}, _M_k{}, _M_y{} The compiler is more likely to optimize that well. >+ _M_k[0] = __value & max(); >+ _M_i = __n - 1; >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ void >+ philox_engine<_UIntType, >+ __w, __n, __r, __consts...>::seed(result_type __value) >+ { >+ std::fill(_M_x.begin(), _M_x.end(), 0); This can use std::array::fill, i.e. _M_x.fill(0); Or would this whole function be simpler as: *this = philox_engine(__value); ? >+ std::fill(_M_k.begin(), _M_k.end(), 0); >+ std::fill(_M_y.begin(), _M_y.end(), 0); >+ _M_k[0] = __value & max(); >+ _M_i = __n - 1; >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ void >+ philox_engine<_UIntType, __w, >+ __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) >+ { >+ for (unsigned long long __j = 0; __j < __n; ++__j) >+ _M_x[__j] = __counter[__n - 1 - __j] & max(); >+ _M_i = __n - 1; >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ template<typename _Sseq> >+ auto >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) >+ -> _If_seed_seq<_Sseq> >+ { >+ std::fill(_M_k.begin(), _M_k.end(), 0); >+ const unsigned long long __p = 1 + ((__w - 1)/ 32); >+ uint_least32_t __tmpArr[(__n / 2) * __p]; >+ __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); >+ for (unsigned long long __k = 0; __k < (__n/2); ++__k) >+ { >+ unsigned long long __precalc = 0; >+ for (unsigned long long __j = 0; __j < __p; ++__j) >+ { >+ unsigned long long __multiplicand = (1ull << (32 * __j)); >+ __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); >+ } >+ _M_k[__k] = __precalc; >+ } >+ std::fill(_M_x.begin(), _M_x.end(), 0); >+ std::fill(_M_y.begin(), _M_y.end(), 0); >+ _M_i = __n - 1; >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ void >+ philox_engine<_UIntType, >+ __w, __n, __r, __consts...>::discard(unsigned long long __z) >+ { >+ for (unsigned long long __j = 0; __j < __z; ++__j) >+ _M_transition(); >+ } >+ >+ template<class _UIntType, >+ size_t __w, size_t __n, >+ size_t __r, _UIntType... __consts> >+ _UIntType >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() >+ { >+ _M_transition(); >+ return _M_y[_M_i]; >+ } >+ >+#endif > > template<typename _IntType, typename _CharT, typename _Traits> > std::basic_ostream<_CharT, _Traits>& >diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def >index dbe2cb8f175..4c21fa187c8 100644 >--- a/libstdc++-v3/include/bits/version.def >+++ b/libstdc++-v3/include/bits/version.def >@@ -2069,6 +2069,15 @@ ftms = { > }; > }; > >+ftms = { >+ name = philox_engine; >+ values = { >+ v = 202406; >+ cxxmin = 26; >+ extra_cond = "__SIZEOF_INT128__"; >+ }; >+}; >+ > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; >diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h >index 7bb6016df68..75a9946adce 100644 >--- a/libstdc++-v3/include/bits/version.h >+++ b/libstdc++-v3/include/bits/version.h >@@ -2318,4 +2318,14 @@ > #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ > #undef __glibcxx_want_constexpr_exceptions > >+#if !defined(__cpp_lib_philox_engine) >+# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) >+# define __glibcxx_philox_engine 202406L >+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) >+# define __cpp_lib_philox_engine 202406L >+# endif >+# endif >+#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ >+#undef __glibcxx_want_philox_engine >+ > #undef __glibcxx_want_all >diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random >index 0e058a04bd9..8a02ade4b75 100644 >--- a/libstdc++-v3/include/std/random >+++ b/libstdc++-v3/include/std/random >@@ -39,6 +39,9 @@ > # include <bits/c++0x_warning.h> > #else > >+#define __glibcxx_want_philox_engine >+#include <bits/version.h> >+ > #include <cmath> > #include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t > #include <cstdlib> >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >new file mode 100644 >index 00000000000..d5a8ca078ef >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >@@ -0,0 +1,23 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine Please identify the document where these references are from, because 29.5.4 might be a different subclause in C++29, i.e. >+// N5014 29.5.4 Random Number Engine Class Templates >+// N5014 29.5.4.5 Class Template philox_engine (Ideally we'd say "C++26 29.5.4 ..." but we don't know if that subclause number will be correct in the final standard.) >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+void >+test01() >+{ >+ std::philox4x32 a; >+ a.discard(9999); >+ >+ VERIFY( a() == 1955073260 ); >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >new file mode 100644 >index 00000000000..131f094cb28 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >@@ -0,0 +1,23 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+void >+test01() >+{ >+ std::philox4x64 a; >+ a.discard(9999); >+ >+ VERIFY( a() == 3409172418970261260 ); >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >new file mode 100644 >index 00000000000..c3a5a0eb754 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >@@ -0,0 +1,39 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+int f(int x) >+{ >+ std::seed_seq sq(&x, &x + 1); >+ auto rnd = std::philox4x32(sq); >+ return std::uniform_int_distribution<int>()(rnd); >+} >+ >+int g(int x) >+{ >+ std::seed_seq sq(&x, &x + 1); >+ auto rnd = std::philox4x32(); >+ rnd.seed(sq); >+ return std::uniform_int_distribution<int>()(rnd); >+} >+ >+void test01() >+{ >+ const int f1 = f(0); >+ const int f2 = f(0); >+ >+ const int g1 = g(0); >+ const int g2 = g(0); >+ >+ VERIFY( f1 == f2 ); >+ VERIFY( g1 == g2 ); >+ VERIFY( f1 == g1 ); >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >new file mode 100644 >index 00000000000..4f61928a157 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >@@ -0,0 +1,25 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+ >+void >+test01() >+{ >+ >+ std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); >+ >+ const auto f(e); >+ auto g(f); >+ g = g; // Suppress unused warning >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >new file mode 100644 >index 00000000000..9f9ae94db0f >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >@@ -0,0 +1,27 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+void >+test01() >+{ >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> philox4x32nullkey(0); >+ >+ VERIFY( philox4x32nullkey.min() == 0 ); >+ VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); >+ VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >new file mode 100644 >index 00000000000..5cb914f4b45 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >@@ -0,0 +1,20 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+#include <random> >+ >+void >+test01() >+{ >+ unsigned long seed = 2; >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> philox4x32seeded(seed); >+} >+ >+int main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >new file mode 100644 >index 00000000000..7d9e3e6540d >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >@@ -0,0 +1,24 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+ >+void >+test01() >+{ >+ std::seed_seq sseq{ 1, 2, 3, 4 }; >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> philox4x32sseq(sseq); >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >new file mode 100644 >index 00000000000..4f62bfbbd88 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >@@ -0,0 +1,30 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+void >+test01() >+{ >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> x, y; >+ >+ VERIFY ( x == y); >+ x.discard(100); >+ y.discard(100); >+ >+ VERIFY (x == y); >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >new file mode 100644 >index 00000000000..86d757db904 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >@@ -0,0 +1,30 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_hooks.h> >+ >+void >+test01() >+{ >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> x, y; >+ >+ VERIFY ( !(x != y) ); >+ x.discard(100); >+ y.discard(100); >+ >+ VERIFY ( !(x != y) ); >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >new file mode 100644 >index 00000000000..bec4b172512 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >@@ -0,0 +1,49 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <sstream> >+#include <random> >+#include <testsuite_hooks.h> >+#include <iostream> >+ >+void >+test01() >+{ >+ std::stringstream str; >+ std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> x, y; >+ >+ x(); >+ str << x; >+ >+ VERIFY ( !(x == y) ); >+ str >> y; >+ VERIFY ( x == y ); >+ for (unsigned long i = 0; i < 100; ++i) >+ { >+ VERIFY (x() == y()); >+ } >+ str.clear(); >+ str << y; >+ x(); >+ x(); >+ x(); >+ str >> x; >+ VERIFY ( x == y ); >+ for (unsigned long i = 0; i < 1000; ++i) >+ { >+ VERIFY (x() == y()); >+ } >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >new file mode 100644 >index 00000000000..c242056e0a4 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >@@ -0,0 +1,26 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+ >+void test01() >+{ >+ std::philox4x32 philox; >+ const void* p = &philox.word_size; >+ p = &philox.word_count; >+ p = &philox.round_count; >+ p = &philox.multipliers; >+ p = &philox.round_consts; >+ p = &philox.default_seed; >+ p = p; // Suppress unused warning. >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >new file mode 100644 >index 00000000000..5be0108c88c >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >@@ -0,0 +1,50 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_common_types.h> >+ >+namespace __gnu_test >+{ >+ struct constexpr_member_data >+ { >+ template<typename _Ttesttype> >+ void >+ operator()() >+ { >+ struct _Concept >+ { >+ void __constraint() >+ { >+ constexpr auto v1 __attribute__((unused)) >+ = _Ttesttype::word_size; >+ constexpr auto v2 __attribute__((unused)) >+ = _Ttesttype::word_count; >+ constexpr auto v3 __attribute__((unused)) >+ = _Ttesttype::round_count; >+ constexpr auto v4 __attribute__((unused)) >+ = _Ttesttype::multipliers; >+ constexpr auto v5 __attribute__((unused)) >+ = _Ttesttype::round_consts; >+ constexpr auto v6 __attribute__((unused)) >+ = _Ttesttype::default_seed; >+ } >+ }; >+ >+ _Concept c; >+ c.__constraint(); >+ } >+ }; >+}; >+ >+int >+main() >+{ >+ __gnu_test::constexpr_member_data test; >+ typedef std::philox4x32 type; >+ test.operator()<type>(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >new file mode 100644 >index 00000000000..eb61d15568a >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >@@ -0,0 +1,41 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+#include <random> >+#include <testsuite_common_types.h> >+ >+namespace __gnu_test >+{ >+ struct constexpr_member_functions >+ { >+ template<typename _Ttesttype> >+ void >+ operator()() >+ { >+ struct _Concept >+ { >+ void __constraint() >+ { >+ constexpr auto v1 __attribute__((unused)) >+ = _Ttesttype::min(); >+ constexpr auto v2 __attribute__((unused)) >+ = _Ttesttype::max(); >+ } >+ }; >+ _Concept c; >+ c.__constraint(); >+ } >+ }; >+} >+ >+int >+main() >+{ >+ __gnu_test::constexpr_member_functions test; >+ typedef std::philox4x32 type; >+ test.operator()<type>(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >new file mode 100644 >index 00000000000..b368ee74106 >--- /dev/null >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >@@ -0,0 +1,26 @@ >+// { dg-do run { target c++26 } } >+// { dg-require-cstdint "" } >+ >+// 29.5.4 Random Number Engine Class Templates >+// 29.5.4.5 Class Template philox_engine >+ >+ >+#include <random> >+ >+void >+test01() >+{ >+ typedef std::philox_engine<std::uint_fast32_t, >+ 32, 4, 10, 0xCD9E8D57, >+ 0x9E3779B9, 0xD2511F53, >+ 0xBB67AE85> testType; >+ >+ typedef testType::result_type result_type; >+} >+ >+int >+main() >+{ >+ test01(); >+ return 0; >+} >diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >index 0afba654152..13a0b8947b0 100644 >--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); > auto x = std::generate_canonical<std::size_t, > std::numeric_limits<std::size_t>::digits>(urng); > >-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } >+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } > >-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } >+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } >-- >2.49.0 >
Since these are all minor style issues and changes to comments, I'm happy to make the changes myself before pushing the patch. But first, does anybody have any comments on my comments? :-) On Mon, 8 Sept 2025 at 15:57, Jonathan Wakely <jwakely@redhat.com> wrote: > > On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: > >Implements Philox Engine (P2075R6) and associated tests. > > > >Implements additional feedback from v5 from Patrick Palka. > >Also cut some trailing whitespace from the limb propagation > >fix in v5. > > > >Apologies for the delay from v5 - I had to finish writing some > >CTF challenges for DEFCON this week. Thank you for the > >feedback, and please let me know if anything further needs > >to be changed. > > > >Built/tested on x86_64-pc-linux-gnu. > > >From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 > >From: 1nfocalypse <1nfocalypse@protonmail.com> > >Date: Tue, 5 Aug 2025 01:37:18 +0000 > >Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) > > > >Implemented additional changes from Patrick Palka. > >Also removed some trailing whitespace in random.tcc. > > > >replaced alias of __ios_base with ios_base > >included <iomanip> in random.h > >altered line number in pr60037-neg.cc to be accurate > >Refactored iterator/index loops to be range-based for > >in istream/ostream operator overloads > >include <random> in philox_engine/cons/seed_seq.cc > >Conforms with errata LWG4143, LWG4153 for Philox Engine. > > > > PR libstdc++/119794 > >--- > > libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ > > libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ > > libstdc++-v3/include/bits/version.def | 9 + > > libstdc++-v3/include/bits/version.h | 10 + > > libstdc++-v3/include/std/random | 3 + > > .../26_numerics/random/philox4x32.cc | 23 ++ > > .../26_numerics/random/philox4x64.cc | 23 ++ > > .../random/philox_engine/cons/119794.cc | 39 +++ > > .../random/philox_engine/cons/copy.cc | 25 ++ > > .../random/philox_engine/cons/default.cc | 27 ++ > > .../random/philox_engine/cons/seed.cc | 20 ++ > > .../random/philox_engine/cons/seed_seq.cc | 24 ++ > > .../random/philox_engine/operators/equal.cc | 30 ++ > > .../random/philox_engine/operators/inequal.cc | 30 ++ > > .../philox_engine/operators/serialize.cc | 49 +++ > > .../philox_engine/requirements/constants.cc | 26 ++ > > .../requirements/constexpr_data.cc | 50 ++++ > > .../requirements/constexpr_functions.cc | 41 +++ > > .../philox_engine/requirements/typedefs.cc | 26 ++ > > .../26_numerics/random/pr60037-neg.cc | 4 +- > > 20 files changed, 931 insertions(+), 2 deletions(-) > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > > create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > > > >diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h > >index 1fdaf51934f..e74b7bd75c0 100644 > >--- a/libstdc++-v3/include/bits/random.h > >+++ b/libstdc++-v3/include/bits/random.h > >@@ -33,6 +33,7 @@ > > > > #include <vector> > > #include <bits/uniform_int_dist.h> > >+#include <iomanip> > > > > namespace std _GLIBCXX_VISIBILITY(default) > > { > >@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > { return !(__lhs == __rhs); } > > #endif > > > >+#if __cpp_lib_philox_engine > > This should check __glibcxx_philox_engine (just in case this code is > moved to a separate header that ends up being included somewhere > withou all of <random>). > > >+ /** > >+ * @brief: A discrete pseudorandom number generator based off of weakened > >+ * cryptographic primitives. > > Can we say "based on" instead of "based off of" which is an informal > American English term that sounds wrong to me. > > I'm also not sure what this is telling me as a non-export user. Would > it be clearer to talk about it having weak cryptographic properties, > rather than being based on weakened cryptographic primitives? > > >+ * > >+ * This algorithm was intended to be used for highly parallel random number > > "was intended to be used" sounds like it refers to something in the > past that is no longer true. Maybe "was designed to be used" instead? > > >+ * generation, and is capable of immensely long periods. It provides "Crush- > > I was going to ask whether immensely is a bit hyperbolic and could be > just "very long periods (e.g. ...)" but since we're talking at least > 2^128 I think "immensely" is fine :-) > > >+ * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" > >+ * test, demonstrating significant apparent entropy. It is not intended for > >+ * cryptographic use and should not be used for such, despite being based on > >+ * cryptographic primitives. > >+ * > >+ * The two four-word definitions are likely the best use for this algorithm, > >+ * and are given below as defaults. > > Can we refer to those as aliases or typedefs rather than definitions? > Or specializations? > > Maybe even say it like this: > > * The typedefs `philox4x32` and `philox4x64` are provided as > * suitable defaults for most use cases, providing high-quality > * random numbers at reasonable performance. > > > >+ * > >+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and > >+ * David Shaw as a product of D.E. Shaw Research. > >+ * > >+ * @tparam __w Word size > >+ * @tparam __n Buffer size > >+ * @tparam __r Rounds > >+ * @tparam __consts Multiplication and round constant pack, ordered as > >+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} > >+ * > >+ * @headerfile random > >+ * @since C++26 > >+ */ > >+ template<class _UIntType, size_t __w, > > Our convention in libstdc++ code is to type 'typename' for template > parameters instead of 'class' > > >+ size_t __n, size_t __r, > >+ _UIntType... __consts> > >+ class philox_engine > >+ { > >+ static_assert(__n == 2 || __n == 4, > >+ "template argument N must be either 2 or 4"); > >+ static_assert(sizeof...(__consts) == __n, > >+ "length of consts array must match specified N"); > >+ static_assert(0 < __r, "a number of rounds must be specified"); > >+ static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), > >+ "specified bitlength must match input type"); > > The static assert messages all use the preferred "must" form, thanks! > > >+ template<typename _Sseq> > >+ using _If_seed_seq > >+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; > >+ > >+ private: > > This access specifier is redundant because everything above it is > already private. > > >+ // the ordering here is essential to functionality. > > Does this refer to the ordering of the _S_popArray member in the > class, because it needs to be declared before it's used? > > If so, I think we can drop the comment. If somebody fails to notice > that when reading the class definition, they'd find out pretty quickly > if they tried to reorder it and got errors. > > The style recommendations to put private internal members last is not > a hard requirement, for cases like this where the order is significant > putting them first is obviously fine (since you have no choice! :-) > > >+ /** @brief an internal unpacking function for %philox_engine. */ > > We don't need a doxygen comment for private member functions, they > won't get included in the docs anyway. > > >+ template <size_t __ind0, size_t __ind1> > >+ static constexpr > >+ array<_UIntType, __n / 2> > >+ _S_popArray() > >+ { > >+ if constexpr (__n == 4) > >+ return {__consts...[__ind0], __consts...[__ind1]}; > >+ else > >+ return {__consts...[__ind0]}; > >+ } > >+ > >+ public: > >+ /** Type of template param. */ > >+ using result_type = _UIntType; > >+ // public members > >+ static constexpr size_t word_size = __w; > >+ static constexpr size_t word_count = __n; > >+ static constexpr size_t round_count = __r; > >+ static constexpr array<result_type, __n / 2> multipliers = > >+ philox_engine::_S_popArray<0,2>(); > >+ static constexpr array<result_type, __n / 2> round_consts = > >+ philox_engine::_S_popArray<1,3>(); > >+ > >+ /** @brief returns the minimum value possible. */ > >+ static constexpr result_type > >+ min() > >+ { return 0; } > >+ > >+ /** @brief returns the maximum value possible. */ > >+ static constexpr result_type > >+ max() > >+ { > >+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); > >+ } > >+ // default key value > >+ static constexpr result_type default_seed = 20111115u; > > I wonder if we have a defect in the C++26 draft here, because > 20111115u won't fit in a 16-bit int. We fixed that for > std::subtract_with_carry_engine via: > https://cplusplus.github.io/LWG/issue3809 > and then fixed what I broke via: > https://cplusplus.github.io/LWG/issue4014 > > Since the value of default_seed is only used as the argument to the > default ctor (or the equivalent of calling seed() with no argument) > and the ctor reduces value by mod 2^w, I think we could define the > default seed as 20111115u & max(). > > But let's leave it for now, I'll raise this with LWG. > > >+ > >+ // constructors > >+ philox_engine() > >+ : philox_engine(default_seed) > >+ {} > >+ > >+ explicit > >+ philox_engine(result_type __value); > >+ > >+ /** @brief seed sequence constructor for %philox_engine > >+ * > >+ * @params __q the seed sequence > >+ */ > >+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> > >+ explicit > >+ philox_engine(_Sseq& __q) > >+ { > >+ seed(__q); > >+ } > >+ > >+ void > >+ seed(result_type value = default_seed); > >+ > >+ /** @brief seeds %philox_engine by seed sequence > >+ * > >+ * @params __q the seed sequence > >+ */ > >+ template<typename _Sseq> > >+ _If_seed_seq<_Sseq> > >+ seed(_Sseq& __q); > >+ > >+ /** @brief sets the internal counter "cleartext" > >+ * > >+ * @params __counter std::array of len N > >+ */ > >+ void > >+ set_counter(const array<result_type, __n>& __counter); > >+ > >+ /** @brief compares two %philox_engine objects > >+ * > >+ * @params __x A %philox_engine object > >+ * @params __y A %philox_engine object > >+ * > >+ * @returns true if the objects will produce an identical stream, false > >+ * otherwise > >+ */ > >+ friend bool > >+ operator==(const philox_engine& __x, const philox_engine& __y) > >+ { > >+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), > >+ __y._M_x.begin(), __y._M_x.end()) > >+ && std::equal(__x._M_y.begin(), __x._M_y.end(), > >+ __y._M_y.begin(), __y._M_y.end()) > >+ && std::equal(__x._M_k.begin(), __x._M_k.end(), > >+ __y._M_k.begin(), __y._M_k.end()) > >+ && __x._M_i == __y._M_i); > >+ } > >+ > >+ /** @brief outputs a single w-bit number and handles state advancement > >+ * > >+ * @returns return_type > >+ */ > >+ _UIntType > >+ operator()(); > >+ > >+ /** @brief discards __z numbers > >+ * > >+ * @params __z number of iterations to discard > >+ */ > >+ void > >+ discard(unsigned long long __z); > >+ > >+ /** @brief outputs the state of the generator > >+ * > >+ * @param __os An output stream. > >+ * @param __x A %philox_engine object reference > >+ * > >+ * @returns the state of the Philox Engine in __os > >+ */ > >+ template<typename _CharT, typename _Traits> > >+ friend basic_ostream<_CharT, _Traits>& > >+ operator<<(basic_ostream<_CharT, _Traits>& __os, > >+ const philox_engine& __x) > >+ { > > The "operator<<" and "{" lines should be lined up with "friend" > > >+ const typename ios_base::fmtflags __flags = __os.flags(); > >+ const _CharT __fill = __os.fill(); > >+ __os.flags(ios_base::dec | ios_base::left); > >+ __os.fill(__os.widen(' ')); > >+ for (auto &__subkey : __x._M_k) > > auto& rather than putting the space before the & please (and below as > well). > > >+ __os << __subkey << ' '; > >+ for (auto &__ctr : __x._M_x) > >+ __os << __ctr << ' '; > > Inserting ' ' will get widened if __os is a wide stream, but since > you've already called __os.widen(' ') above you could save the result > of that in a local variable and then insert that character instead of > ' '. That will avoid calling widen again on every iteration of the > loop. > > >+ __os << __x._M_i; > >+ __os.flags(__flags); > > If writing to the stream throws an exception we won't restore the > flags and fill character, but I think that's consistent with all our > operator<< overloads in <random>. > > >+ __os.fill(__fill); > >+ return __os; > >+ } > >+ > >+ /** @brief takes input to set the state of the %philox_engine object > >+ * > >+ * @param __is An input stream. > >+ * @param __x A %philox_engine object reference > >+ * > >+ * @returns %philox_engine object is set with values from instream > >+ */ > >+ template <typename _CharT, typename _Traits> > >+ friend basic_istream<_CharT, _Traits>& > >+ operator>>(basic_istream<_CharT, _Traits>& __is, > >+ philox_engine& __x) > >+ { > > Same comment about aligning to "friend". > > >+ const typename ios_base::fmtflags __flags = __is.flags(); > >+ __is.flags(ios_base::dec | ios_base::skipws); > >+ for (auto &__subkey : __x._M_k) > >+ __is >> __subkey; > >+ for (auto &__ctr : __x._M_x) > >+ __is >> __ctr; > >+ array<_UIntType, __n> __tmpCtr = __x._M_x; > >+ unsigned char __setIndex = 0; > >+ for (size_t __j = 0; __j < __x._M_x.size(); ++__j) > >+ { > >+ if (__x._M_x[__j] > 0) > >+ { > >+ __setIndex = __j; > >+ break; > >+ } > >+ } > >+ for (size_t __j = 0; __j <= __setIndex; ++__j) > >+ { > >+ if (__j != __setIndex) > >+ __x._M_x[__j] = max(); > >+ else > >+ --__x._M_x[__j]; > >+ } > >+ __x._M_philox(); > >+ __x._M_x = __tmpCtr; > >+ __is >> __x._M_i; > >+ __is.flags(__flags); > >+ return __is; > >+ } > >+ private: > >+ // private state variables > >+ array<_UIntType, __n> _M_x; > >+ array<_UIntType, __n / 2> _M_k; > >+ array<_UIntType, __n> _M_y; > >+ unsigned long long _M_i = 0; > >+ > >+ /** @brief Takes the high values of the product of __a, __b > >+ * > >+ * @params __a an unsigned integer > >+ * @params __b an unsigned integer > >+ * > >+ * @returns an unsigned integer of at most bitlength W > > The constant 'w' is lowercase in the spec (and in our template > parameter, __w). But we don't need Doxygen comments here, it can just > be a simple non-Doxygen comment to explain what's going on (and so no > need to say that params of type _UIntType are unsigned integers). > > Something like: > > // The high w-bit word of the product of a and b. > > >+ */ > >+ static > >+ _UIntType > >+ _S_mulhi(_UIntType __a, _UIntType __b); // (A*B)/2^W > > I think I'd prefer (a*b)/2^w here, i.e. all lowercase. > > >+ > >+ /** @brief Takes the low values of the product of __a, __b > >+ * > >+ * @params __a an unsigned integer > >+ * @params __b an unsigned integer > >+ * > >+ * @returns an unsigned integer of at most bitlength W > >+ */ > > // The low w-bit word of the product of a and b. > > >+ static > >+ _UIntType > >+ _S_mullo(_UIntType __a, _UIntType __b); // (A*B)%2^W > >+ > >+ /** @brief an R-round substitution/Feistel Network hybrid for > >+ * %philox_engine > >+ */ > > Simplify this too: > > // An R-round substitution/Feistel Network hybrid. > > >+ void > >+ _M_philox(); > >+ > >+ /** @brief an internal transition function for the %philox_engine. */ > > And I think there's no need for any comment here, it doesn't seem to > add anything that philox_engine::_M_transition() doesn't already say. > > >+ void > >+ _M_transition(); > >+ }; > >+ > >+#endif > >+ > > /** > > * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller. > > */ > >@@ -1742,6 +2007,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > typedef minstd_rand0 default_random_engine; > > > >+#if __cpp_lib_philox_engine > > Use __glibcxx_philox_engine again > > >+ > >+ typedef philox_engine< > >+ uint_fast32_t, > >+ 32, 4, 10, > >+ 0xCD9E8D57, 0x9E3779B9, > >+ 0xD2511F53, 0xBB67AE85> philox4x32; > >+ > >+ /** > >+ * Alternative Philox instance (64 bit) > >+ */ > >+ typedef philox_engine< > >+ uint_fast64_t, > >+ 64, 4, 10, > >+ 0xCA5A826395121157, 0x9E3779B97F4A7C15, > >+ 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B> philox4x64; > >+#endif > >+ > > /** > > * A standard interface to a platform-specific non-deterministic > > * random number generator (if any are available). > >diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc > >index 53ccacb2e38..53f302da800 100644 > >--- a/libstdc++-v3/include/bits/random.tcc > >+++ b/libstdc++-v3/include/bits/random.tcc > >@@ -907,6 +907,197 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > return __is; > > } > > > >+#if __cpp_lib_philox_engine > >+ > >+ template<class _UIntType, > > 'typename' here again and the definitions below > > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ _UIntType > >+ philox_engine<_UIntType, > >+ __w, __n, __r, __consts...>::_S_mulhi(_UIntType __a, _UIntType __b) > >+ { > >+ const __uint128_t __num = > >+ static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b); > >+ return static_cast<_UIntType>((__num >> __w) & max()); > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ _UIntType > >+ philox_engine<_UIntType, > >+ __w, __n, __r, __consts...>::_S_mullo(_UIntType __a, _UIntType __b) > >+ { > >+ return static_cast<_UIntType>((__a * __b) & max()); > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ void > >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_transition() > >+ { > >+ ++_M_i; > >+ if (_M_i == __n) > >+ { > >+ _M_philox(); > >+ if constexpr (__n == 4) > >+ { > >+ __uint128_t __uh = > >+ (static_cast<__uint128_t>(_M_x[1]) << __w) > >+ | (static_cast<__uint128_t>(_M_x[0]) + 1); > >+ __uint128_t __lh = > >+ ((static_cast<__uint128_t>(_M_x[3]) << __w) > >+ | (_M_x[2])); > >+ __uint128_t __bigMask = > >+ (static_cast<__uint128_t>(1) << ((2 * __w) - 1)) > >+ | ((static_cast<__uint128_t>(1) << ((2 * __w) - 1)) - 1); > >+ if ((__uh & __bigMask) == 0) > >+ { > >+ ++__lh; > >+ __uh = 0; > >+ } > >+ _M_x[0] = __uh & max(); > >+ _M_x[1] = (__uh >> (__w)) & max(); > >+ _M_x[2] = __lh & max(); > >+ _M_x[3] = (__lh >> (__w)) & max(); > >+ } else > >+ { > >+ __uint128_t __num = > >+ (static_cast<__uint128_t>(_M_x[1]) << __w) > >+ | (static_cast<__uint128_t>(_M_x[0]) + 1); > >+ _M_x[0] = __num & max(); > >+ _M_x[1] = (__num >> __w) & max(); > >+ } > >+ _M_i = 0; > >+ } > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ void > >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() > >+ { > >+ array<_UIntType, __n> __outputSeq{}; > >+ for (size_t __j = 0; __j < __n; ++__j) > >+ __outputSeq[__j] = _M_x[__j]; > >+ for (unsigned long __j = 0; __j < __r; ++__j) > >+ { > >+ array<_UIntType, __n> __intermedSeq{}; > >+ if constexpr (__n == 4) > >+ { > >+ __intermedSeq[0] = __outputSeq[2]; > >+ __intermedSeq[1] = __outputSeq[1]; > >+ __intermedSeq[2] = __outputSeq[0]; > >+ __intermedSeq[3] = __outputSeq[3]; > >+ } else > >+ { > >+ __intermedSeq[0] = __outputSeq[0]; > >+ __intermedSeq[1] = __outputSeq[1]; > >+ } > >+ for (unsigned long __k = 0; __k < (__n/2); ++__k) > >+ { > >+ __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) > >+ ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) > >+ ^ __intermedSeq[2*__k+1]; > >+ > >+ __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], > >+ multipliers[__k]); > >+ } > >+ } > >+ for (unsigned long __j = 0; __j < __n; ++__j) > >+ _M_y[__j] = __outputSeq[__j]; > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ philox_engine<_UIntType, > >+ __w, __n, __r, __consts...>::philox_engine(result_type __value) > >+ { > >+ std::fill(_M_x.begin(), _M_x.end(), 0); > >+ std::fill(_M_k.begin(), _M_k.end(), 0); > >+ std::fill(_M_y.begin(), _M_y.end(), 0); > > Can we just initialize these in a mem-initializer-list? > > : _M_x{}, _M_k{}, _M_y{} > > The compiler is more likely to optimize that well. > > >+ _M_k[0] = __value & max(); > >+ _M_i = __n - 1; > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ void > >+ philox_engine<_UIntType, > >+ __w, __n, __r, __consts...>::seed(result_type __value) > >+ { > >+ std::fill(_M_x.begin(), _M_x.end(), 0); > > This can use std::array::fill, i.e. _M_x.fill(0); > > Or would this whole function be simpler as: > > *this = philox_engine(__value); > > ? > > >+ std::fill(_M_k.begin(), _M_k.end(), 0); > >+ std::fill(_M_y.begin(), _M_y.end(), 0); > >+ _M_k[0] = __value & max(); > >+ _M_i = __n - 1; > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ void > >+ philox_engine<_UIntType, __w, > >+ __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) > >+ { > >+ for (unsigned long long __j = 0; __j < __n; ++__j) > >+ _M_x[__j] = __counter[__n - 1 - __j] & max(); > >+ _M_i = __n - 1; > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ template<typename _Sseq> > >+ auto > >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) > >+ -> _If_seed_seq<_Sseq> > >+ { > >+ std::fill(_M_k.begin(), _M_k.end(), 0); > >+ const unsigned long long __p = 1 + ((__w - 1)/ 32); > >+ uint_least32_t __tmpArr[(__n / 2) * __p]; > >+ __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); > >+ for (unsigned long long __k = 0; __k < (__n/2); ++__k) > >+ { > >+ unsigned long long __precalc = 0; > >+ for (unsigned long long __j = 0; __j < __p; ++__j) > >+ { > >+ unsigned long long __multiplicand = (1ull << (32 * __j)); > >+ __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); > >+ } > >+ _M_k[__k] = __precalc; > >+ } > >+ std::fill(_M_x.begin(), _M_x.end(), 0); > >+ std::fill(_M_y.begin(), _M_y.end(), 0); > >+ _M_i = __n - 1; > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ void > >+ philox_engine<_UIntType, > >+ __w, __n, __r, __consts...>::discard(unsigned long long __z) > >+ { > >+ for (unsigned long long __j = 0; __j < __z; ++__j) > >+ _M_transition(); > >+ } > >+ > >+ template<class _UIntType, > >+ size_t __w, size_t __n, > >+ size_t __r, _UIntType... __consts> > >+ _UIntType > >+ philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() > >+ { > >+ _M_transition(); > >+ return _M_y[_M_i]; > >+ } > >+ > >+#endif > > > > template<typename _IntType, typename _CharT, typename _Traits> > > std::basic_ostream<_CharT, _Traits>& > >diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > >index dbe2cb8f175..4c21fa187c8 100644 > >--- a/libstdc++-v3/include/bits/version.def > >+++ b/libstdc++-v3/include/bits/version.def > >@@ -2069,6 +2069,15 @@ ftms = { > > }; > > }; > > > >+ftms = { > >+ name = philox_engine; > >+ values = { > >+ v = 202406; > >+ cxxmin = 26; > >+ extra_cond = "__SIZEOF_INT128__"; > >+ }; > >+}; > >+ > > // Standard test specifications. > > stds[97] = ">= 199711L"; > > stds[03] = ">= 199711L"; > >diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > >index 7bb6016df68..75a9946adce 100644 > >--- a/libstdc++-v3/include/bits/version.h > >+++ b/libstdc++-v3/include/bits/version.h > >@@ -2318,4 +2318,14 @@ > > #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ > > #undef __glibcxx_want_constexpr_exceptions > > > >+#if !defined(__cpp_lib_philox_engine) > >+# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) > >+# define __glibcxx_philox_engine 202406L > >+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) > >+# define __cpp_lib_philox_engine 202406L > >+# endif > >+# endif > >+#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ > >+#undef __glibcxx_want_philox_engine > >+ > > #undef __glibcxx_want_all > >diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random > >index 0e058a04bd9..8a02ade4b75 100644 > >--- a/libstdc++-v3/include/std/random > >+++ b/libstdc++-v3/include/std/random > >@@ -39,6 +39,9 @@ > > # include <bits/c++0x_warning.h> > > #else > > > >+#define __glibcxx_want_philox_engine > >+#include <bits/version.h> > >+ > > #include <cmath> > > #include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t > > #include <cstdlib> > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > >new file mode 100644 > >index 00000000000..d5a8ca078ef > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > >@@ -0,0 +1,23 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > > Please identify the document where these references are from, because > 29.5.4 might be a different subclause in C++29, i.e. > > >+// N5014 29.5.4 Random Number Engine Class Templates > >+// N5014 29.5.4.5 Class Template philox_engine > > (Ideally we'd say "C++26 29.5.4 ..." but we don't know if that > subclause number will be correct in the final standard.) > > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+void > >+test01() > >+{ > >+ std::philox4x32 a; > >+ a.discard(9999); > >+ > >+ VERIFY( a() == 1955073260 ); > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > >new file mode 100644 > >index 00000000000..131f094cb28 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > >@@ -0,0 +1,23 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+void > >+test01() > >+{ > >+ std::philox4x64 a; > >+ a.discard(9999); > >+ > >+ VERIFY( a() == 3409172418970261260 ); > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > >new file mode 100644 > >index 00000000000..c3a5a0eb754 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > >@@ -0,0 +1,39 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+int f(int x) > >+{ > >+ std::seed_seq sq(&x, &x + 1); > >+ auto rnd = std::philox4x32(sq); > >+ return std::uniform_int_distribution<int>()(rnd); > >+} > >+ > >+int g(int x) > >+{ > >+ std::seed_seq sq(&x, &x + 1); > >+ auto rnd = std::philox4x32(); > >+ rnd.seed(sq); > >+ return std::uniform_int_distribution<int>()(rnd); > >+} > >+ > >+void test01() > >+{ > >+ const int f1 = f(0); > >+ const int f2 = f(0); > >+ > >+ const int g1 = g(0); > >+ const int g2 = g(0); > >+ > >+ VERIFY( f1 == f2 ); > >+ VERIFY( g1 == g2 ); > >+ VERIFY( f1 == g1 ); > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > >new file mode 100644 > >index 00000000000..4f61928a157 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > >@@ -0,0 +1,25 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+ > >+void > >+test01() > >+{ > >+ > >+ std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); > >+ > >+ const auto f(e); > >+ auto g(f); > >+ g = g; // Suppress unused warning > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > >new file mode 100644 > >index 00000000000..9f9ae94db0f > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > >@@ -0,0 +1,27 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+void > >+test01() > >+{ > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> philox4x32nullkey(0); > >+ > >+ VERIFY( philox4x32nullkey.min() == 0 ); > >+ VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); > >+ VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > >new file mode 100644 > >index 00000000000..5cb914f4b45 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > >@@ -0,0 +1,20 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+#include <random> > >+ > >+void > >+test01() > >+{ > >+ unsigned long seed = 2; > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> philox4x32seeded(seed); > >+} > >+ > >+int main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > >new file mode 100644 > >index 00000000000..7d9e3e6540d > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > >@@ -0,0 +1,24 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+ > >+void > >+test01() > >+{ > >+ std::seed_seq sseq{ 1, 2, 3, 4 }; > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> philox4x32sseq(sseq); > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > >new file mode 100644 > >index 00000000000..4f62bfbbd88 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > >@@ -0,0 +1,30 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+void > >+test01() > >+{ > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> x, y; > >+ > >+ VERIFY ( x == y); > >+ x.discard(100); > >+ y.discard(100); > >+ > >+ VERIFY (x == y); > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > >new file mode 100644 > >index 00000000000..86d757db904 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > >@@ -0,0 +1,30 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_hooks.h> > >+ > >+void > >+test01() > >+{ > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> x, y; > >+ > >+ VERIFY ( !(x != y) ); > >+ x.discard(100); > >+ y.discard(100); > >+ > >+ VERIFY ( !(x != y) ); > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > >new file mode 100644 > >index 00000000000..bec4b172512 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > >@@ -0,0 +1,49 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <sstream> > >+#include <random> > >+#include <testsuite_hooks.h> > >+#include <iostream> > >+ > >+void > >+test01() > >+{ > >+ std::stringstream str; > >+ std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> x, y; > >+ > >+ x(); > >+ str << x; > >+ > >+ VERIFY ( !(x == y) ); > >+ str >> y; > >+ VERIFY ( x == y ); > >+ for (unsigned long i = 0; i < 100; ++i) > >+ { > >+ VERIFY (x() == y()); > >+ } > >+ str.clear(); > >+ str << y; > >+ x(); > >+ x(); > >+ x(); > >+ str >> x; > >+ VERIFY ( x == y ); > >+ for (unsigned long i = 0; i < 1000; ++i) > >+ { > >+ VERIFY (x() == y()); > >+ } > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > >new file mode 100644 > >index 00000000000..c242056e0a4 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > >@@ -0,0 +1,26 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+ > >+void test01() > >+{ > >+ std::philox4x32 philox; > >+ const void* p = &philox.word_size; > >+ p = &philox.word_count; > >+ p = &philox.round_count; > >+ p = &philox.multipliers; > >+ p = &philox.round_consts; > >+ p = &philox.default_seed; > >+ p = p; // Suppress unused warning. > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > >new file mode 100644 > >index 00000000000..5be0108c88c > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > >@@ -0,0 +1,50 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_common_types.h> > >+ > >+namespace __gnu_test > >+{ > >+ struct constexpr_member_data > >+ { > >+ template<typename _Ttesttype> > >+ void > >+ operator()() > >+ { > >+ struct _Concept > >+ { > >+ void __constraint() > >+ { > >+ constexpr auto v1 __attribute__((unused)) > >+ = _Ttesttype::word_size; > >+ constexpr auto v2 __attribute__((unused)) > >+ = _Ttesttype::word_count; > >+ constexpr auto v3 __attribute__((unused)) > >+ = _Ttesttype::round_count; > >+ constexpr auto v4 __attribute__((unused)) > >+ = _Ttesttype::multipliers; > >+ constexpr auto v5 __attribute__((unused)) > >+ = _Ttesttype::round_consts; > >+ constexpr auto v6 __attribute__((unused)) > >+ = _Ttesttype::default_seed; > >+ } > >+ }; > >+ > >+ _Concept c; > >+ c.__constraint(); > >+ } > >+ }; > >+}; > >+ > >+int > >+main() > >+{ > >+ __gnu_test::constexpr_member_data test; > >+ typedef std::philox4x32 type; > >+ test.operator()<type>(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > >new file mode 100644 > >index 00000000000..eb61d15568a > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > >@@ -0,0 +1,41 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+#include <random> > >+#include <testsuite_common_types.h> > >+ > >+namespace __gnu_test > >+{ > >+ struct constexpr_member_functions > >+ { > >+ template<typename _Ttesttype> > >+ void > >+ operator()() > >+ { > >+ struct _Concept > >+ { > >+ void __constraint() > >+ { > >+ constexpr auto v1 __attribute__((unused)) > >+ = _Ttesttype::min(); > >+ constexpr auto v2 __attribute__((unused)) > >+ = _Ttesttype::max(); > >+ } > >+ }; > >+ _Concept c; > >+ c.__constraint(); > >+ } > >+ }; > >+} > >+ > >+int > >+main() > >+{ > >+ __gnu_test::constexpr_member_functions test; > >+ typedef std::philox4x32 type; > >+ test.operator()<type>(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >new file mode 100644 > >index 00000000000..b368ee74106 > >--- /dev/null > >+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >@@ -0,0 +1,26 @@ > >+// { dg-do run { target c++26 } } > >+// { dg-require-cstdint "" } > >+ > >+// 29.5.4 Random Number Engine Class Templates > >+// 29.5.4.5 Class Template philox_engine > >+ > >+ > >+#include <random> > >+ > >+void > >+test01() > >+{ > >+ typedef std::philox_engine<std::uint_fast32_t, > >+ 32, 4, 10, 0xCD9E8D57, > >+ 0x9E3779B9, 0xD2511F53, > >+ 0xBB67AE85> testType; > >+ > >+ typedef testType::result_type result_type; > >+} > >+ > >+int > >+main() > >+{ > >+ test01(); > >+ return 0; > >+} > >diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >index 0afba654152..13a0b8947b0 100644 > >--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); > > auto x = std::generate_canonical<std::size_t, > > std::numeric_limits<std::size_t>::digits>(urng); > > > >-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } > >+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } > > > >-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } > >+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } > >-- > >2.49.0 > > >
On Mon, 08 Sep 2025 at 15:55 +0100, Jonathan Wakely wrote: >On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: >>Implements Philox Engine (P2075R6) and associated tests. >> >>Implements additional feedback from v5 from Patrick Palka. >>Also cut some trailing whitespace from the limb propagation >>fix in v5. >> >>Apologies for the delay from v5 - I had to finish writing some >>CTF challenges for DEFCON this week. Thank you for the >>feedback, and please let me know if anything further needs >>to be changed. >> >>Built/tested on x86_64-pc-linux-gnu. > >>From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 >>From: 1nfocalypse <1nfocalypse@protonmail.com> >>Date: Tue, 5 Aug 2025 01:37:18 +0000 >>Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) >> >>Implemented additional changes from Patrick Palka. >>Also removed some trailing whitespace in random.tcc. >> >>replaced alias of __ios_base with ios_base >>included <iomanip> in random.h >>altered line number in pr60037-neg.cc to be accurate >>Refactored iterator/index loops to be range-based for >>in istream/ostream operator overloads >>include <random> in philox_engine/cons/seed_seq.cc >>Conforms with errata LWG4143, LWG4153 for Philox Engine. >> >> PR libstdc++/119794 >>--- >>libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ >>libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ >>libstdc++-v3/include/bits/version.def | 9 + >>libstdc++-v3/include/bits/version.h | 10 + >>libstdc++-v3/include/std/random | 3 + >>.../26_numerics/random/philox4x32.cc | 23 ++ >>.../26_numerics/random/philox4x64.cc | 23 ++ >>.../random/philox_engine/cons/119794.cc | 39 +++ >>.../random/philox_engine/cons/copy.cc | 25 ++ >>.../random/philox_engine/cons/default.cc | 27 ++ >>.../random/philox_engine/cons/seed.cc | 20 ++ >>.../random/philox_engine/cons/seed_seq.cc | 24 ++ >>.../random/philox_engine/operators/equal.cc | 30 ++ >>.../random/philox_engine/operators/inequal.cc | 30 ++ >>.../philox_engine/operators/serialize.cc | 49 +++ >>.../philox_engine/requirements/constants.cc | 26 ++ >>.../requirements/constexpr_data.cc | 50 ++++ >>.../requirements/constexpr_functions.cc | 41 +++ >>.../philox_engine/requirements/typedefs.cc | 26 ++ >>.../26_numerics/random/pr60037-neg.cc | 4 +- >>20 files changed, 931 insertions(+), 2 deletions(-) >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >> >>diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h >>index 1fdaf51934f..e74b7bd75c0 100644 >>--- a/libstdc++-v3/include/bits/random.h >>+++ b/libstdc++-v3/include/bits/random.h >>@@ -33,6 +33,7 @@ >> >>#include <vector> >>#include <bits/uniform_int_dist.h> >>+#include <iomanip> >> >>namespace std _GLIBCXX_VISIBILITY(default) >>{ >>@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> { return !(__lhs == __rhs); } >>#endif >> >>+#if __cpp_lib_philox_engine > >This should check __glibcxx_philox_engine (just in case this code is >moved to a separate header that ends up being included somewhere >withou all of <random>). > >>+ /** >>+ * @brief: A discrete pseudorandom number generator based off of weakened No semi-colon after @brief >>+ * cryptographic primitives. > >Can we say "based on" instead of "based off of" which is an informal >American English term that sounds wrong to me. > >I'm also not sure what this is telling me as a non-export user. Would >it be clearer to talk about it having weak cryptographic properties, >rather than being based on weakened cryptographic primitives? > >>+ * >>+ * This algorithm was intended to be used for highly parallel random number > >"was intended to be used" sounds like it refers to something in the >past that is no longer true. Maybe "was designed to be used" instead? > >>+ * generation, and is capable of immensely long periods. It provides "Crush- > >I was going to ask whether immensely is a bit hyperbolic and could be >just "very long periods (e.g. ...)" but since we're talking at least >2^128 I think "immensely" is fine :-) > >>+ * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" >>+ * test, demonstrating significant apparent entropy. It is not intended for >>+ * cryptographic use and should not be used for such, despite being based on >>+ * cryptographic primitives. >>+ * >>+ * The two four-word definitions are likely the best use for this algorithm, >>+ * and are given below as defaults. > >Can we refer to those as aliases or typedefs rather than definitions? >Or specializations? > >Maybe even say it like this: > > * The typedefs `philox4x32` and `philox4x64` are provided as > * suitable defaults for most use cases, providing high-quality > * random numbers at reasonable performance. > > >>+ * >>+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and >>+ * David Shaw as a product of D.E. Shaw Research. >>+ * >>+ * @tparam __w Word size >>+ * @tparam __n Buffer size >>+ * @tparam __r Rounds >>+ * @tparam __consts Multiplication and round constant pack, ordered as >>+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} >>+ * >>+ * @headerfile random >>+ * @since C++26 >>+ */ >>+ template<class _UIntType, size_t __w, > >Our convention in libstdc++ code is to type 'typename' for template >parameters instead of 'class' > >>+ size_t __n, size_t __r, >>+ _UIntType... __consts> >>+ class philox_engine >>+ { >>+ static_assert(__n == 2 || __n == 4, >>+ "template argument N must be either 2 or 4"); >>+ static_assert(sizeof...(__consts) == __n, >>+ "length of consts array must match specified N"); >>+ static_assert(0 < __r, "a number of rounds must be specified"); >>+ static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), >>+ "specified bitlength must match input type"); > >The static assert messages all use the preferred "must" form, thanks! > >>+ template<typename _Sseq> >>+ using _If_seed_seq >>+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; >>+ >>+ private: > >This access specifier is redundant because everything above it is >already private. > >>+ // the ordering here is essential to functionality. > >Does this refer to the ordering of the _S_popArray member in the >class, because it needs to be declared before it's used? > >If so, I think we can drop the comment. If somebody fails to notice >that when reading the class definition, they'd find out pretty quickly >if they tried to reorder it and got errors. > >The style recommendations to put private internal members last is not >a hard requirement, for cases like this where the order is significant >putting them first is obviously fine (since you have no choice! :-) > >>+ /** @brief an internal unpacking function for %philox_engine. */ > >We don't need a doxygen comment for private member functions, they >won't get included in the docs anyway. > >>+ template <size_t __ind0, size_t __ind1> >>+ static constexpr >>+ array<_UIntType, __n / 2> >>+ _S_popArray() >>+ { >>+ if constexpr (__n == 4) >>+ return {__consts...[__ind0], __consts...[__ind1]}; >>+ else >>+ return {__consts...[__ind0]}; >>+ } >>+ >>+ public: >>+ /** Type of template param. */ >>+ using result_type = _UIntType; >>+ // public members >>+ static constexpr size_t word_size = __w; >>+ static constexpr size_t word_count = __n; >>+ static constexpr size_t round_count = __r; >>+ static constexpr array<result_type, __n / 2> multipliers = >>+ philox_engine::_S_popArray<0,2>(); >>+ static constexpr array<result_type, __n / 2> round_consts = >>+ philox_engine::_S_popArray<1,3>(); >>+ >>+ /** @brief returns the minimum value possible. */ >>+ static constexpr result_type >>+ min() >>+ { return 0; } >>+ >>+ /** @brief returns the maximum value possible. */ >>+ static constexpr result_type >>+ max() >>+ { >>+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); >>+ } >>+ // default key value >>+ static constexpr result_type default_seed = 20111115u; > >I wonder if we have a defect in the C++26 draft here, because >20111115u won't fit in a 16-bit int. We fixed that for >std::subtract_with_carry_engine via: >https://cplusplus.github.io/LWG/issue3809 >and then fixed what I broke via: >https://cplusplus.github.io/LWG/issue4014 > >Since the value of default_seed is only used as the argument to the >default ctor (or the equivalent of calling seed() with no argument) >and the ctor reduces value by mod 2^w, I think we could define the >default seed as 20111115u & max(). > >But let's leave it for now, I'll raise this with LWG. > >>+ >>+ // constructors >>+ philox_engine() >>+ : philox_engine(default_seed) >>+ {} >>+ >>+ explicit >>+ philox_engine(result_type __value); >>+ >>+ /** @brief seed sequence constructor for %philox_engine >>+ * >>+ * @params __q the seed sequence This should be @param singular, not plural. (Doxygen allows either @return or @returns, but only @param) >>+ */ >>+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> >>+ explicit >>+ philox_engine(_Sseq& __q) >>+ { >>+ seed(__q); >>+ } >>+ >>+ void >>+ seed(result_type value = default_seed); >>+ >>+ /** @brief seeds %philox_engine by seed sequence >>+ * >>+ * @params __q the seed sequence >>+ */ >>+ template<typename _Sseq> >>+ _If_seed_seq<_Sseq> >>+ seed(_Sseq& __q); >>+ >>+ /** @brief sets the internal counter "cleartext" >>+ * >>+ * @params __counter std::array of len N >>+ */ >>+ void >>+ set_counter(const array<result_type, __n>& __counter); >>+ >>+ /** @brief compares two %philox_engine objects >>+ * >>+ * @params __x A %philox_engine object >>+ * @params __y A %philox_engine object >>+ * >>+ * @returns true if the objects will produce an identical stream, false >>+ * otherwise >>+ */ >>+ friend bool >>+ operator==(const philox_engine& __x, const philox_engine& __y) >>+ { >>+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), >>+ __y._M_x.begin(), __y._M_x.end()) >>+ && std::equal(__x._M_y.begin(), __x._M_y.end(), >>+ __y._M_y.begin(), __y._M_y.end()) >>+ && std::equal(__x._M_k.begin(), __x._M_k.end(), >>+ __y._M_k.begin(), __y._M_k.end()) >>+ && __x._M_i == __y._M_i); I was going to suggest that this could use operator== for std::array and be: return __x._M_x == __y._M_x && __x._M_y == __x._M_y && __x._M_k == __y._M_k && __x._M_i == __y._M_i; but then I realised that's exactly what the compiler would do for: friend bool operator==(const philox_engine&, const philox_engine&) = default; Is there any reason not to just define it as defaulted? If we added new internal non-static data members then we'd need to define operator== explicitly, so that the new members were ignored ... but are we likely to do that? The entire state is represented by the existing data members so I don't see why we'd need to add others.
On Mon, 08 Sep 2025 at 15:55 +0100, Jonathan Wakely wrote: >On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: >>Implements Philox Engine (P2075R6) and associated tests. >> >>Implements additional feedback from v5 from Patrick Palka. >>Also cut some trailing whitespace from the limb propagation >>fix in v5. >> >>Apologies for the delay from v5 - I had to finish writing some >>CTF challenges for DEFCON this week. Thank you for the >>feedback, and please let me know if anything further needs >>to be changed. >> >>Built/tested on x86_64-pc-linux-gnu. > >>From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 >>From: 1nfocalypse <1nfocalypse@protonmail.com> >>Date: Tue, 5 Aug 2025 01:37:18 +0000 >>Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) >> >>Implemented additional changes from Patrick Palka. >>Also removed some trailing whitespace in random.tcc. >> >>replaced alias of __ios_base with ios_base >>included <iomanip> in random.h >>altered line number in pr60037-neg.cc to be accurate >>Refactored iterator/index loops to be range-based for >>in istream/ostream operator overloads >>include <random> in philox_engine/cons/seed_seq.cc >>Conforms with errata LWG4143, LWG4153 for Philox Engine. >> >> PR libstdc++/119794 >>--- >>libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ >>libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ >>libstdc++-v3/include/bits/version.def | 9 + >>libstdc++-v3/include/bits/version.h | 10 + >>libstdc++-v3/include/std/random | 3 + >>.../26_numerics/random/philox4x32.cc | 23 ++ >>.../26_numerics/random/philox4x64.cc | 23 ++ >>.../random/philox_engine/cons/119794.cc | 39 +++ >>.../random/philox_engine/cons/copy.cc | 25 ++ >>.../random/philox_engine/cons/default.cc | 27 ++ >>.../random/philox_engine/cons/seed.cc | 20 ++ >>.../random/philox_engine/cons/seed_seq.cc | 24 ++ >>.../random/philox_engine/operators/equal.cc | 30 ++ >>.../random/philox_engine/operators/inequal.cc | 30 ++ >>.../philox_engine/operators/serialize.cc | 49 +++ >>.../philox_engine/requirements/constants.cc | 26 ++ >>.../requirements/constexpr_data.cc | 50 ++++ >>.../requirements/constexpr_functions.cc | 41 +++ >>.../philox_engine/requirements/typedefs.cc | 26 ++ >>.../26_numerics/random/pr60037-neg.cc | 4 +- >>20 files changed, 931 insertions(+), 2 deletions(-) >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >> >>diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h >>index 1fdaf51934f..e74b7bd75c0 100644 >>--- a/libstdc++-v3/include/bits/random.h >>+++ b/libstdc++-v3/include/bits/random.h >>@@ -33,6 +33,7 @@ >> >>#include <vector> >>#include <bits/uniform_int_dist.h> >>+#include <iomanip> >> >>namespace std _GLIBCXX_VISIBILITY(default) >>{ >>@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> { return !(__lhs == __rhs); } >>#endif >> >>+#if __cpp_lib_philox_engine > >This should check __glibcxx_philox_engine (just in case this code is >moved to a separate header that ends up being included somewhere >withou all of <random>). > >>+ /** >>+ * @brief: A discrete pseudorandom number generator based off of weakened >>+ * cryptographic primitives. > >Can we say "based on" instead of "based off of" which is an informal >American English term that sounds wrong to me. > >I'm also not sure what this is telling me as a non-export user. Would >it be clearer to talk about it having weak cryptographic properties, >rather than being based on weakened cryptographic primitives? > >>+ * >>+ * This algorithm was intended to be used for highly parallel random number > >"was intended to be used" sounds like it refers to something in the >past that is no longer true. Maybe "was designed to be used" instead? > >>+ * generation, and is capable of immensely long periods. It provides "Crush- > >I was going to ask whether immensely is a bit hyperbolic and could be >just "very long periods (e.g. ...)" but since we're talking at least >2^128 I think "immensely" is fine :-) > >>+ * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" >>+ * test, demonstrating significant apparent entropy. It is not intended for >>+ * cryptographic use and should not be used for such, despite being based on >>+ * cryptographic primitives. >>+ * >>+ * The two four-word definitions are likely the best use for this algorithm, >>+ * and are given below as defaults. > >Can we refer to those as aliases or typedefs rather than definitions? >Or specializations? > >Maybe even say it like this: > > * The typedefs `philox4x32` and `philox4x64` are provided as > * suitable defaults for most use cases, providing high-quality > * random numbers at reasonable performance. > > >>+ * >>+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and >>+ * David Shaw as a product of D.E. Shaw Research. >>+ * >>+ * @tparam __w Word size >>+ * @tparam __n Buffer size >>+ * @tparam __r Rounds >>+ * @tparam __consts Multiplication and round constant pack, ordered as >>+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} >>+ * >>+ * @headerfile random >>+ * @since C++26 >>+ */ >>+ template<class _UIntType, size_t __w, > >Our convention in libstdc++ code is to type 'typename' for template >parameters instead of 'class' > >>+ size_t __n, size_t __r, >>+ _UIntType... __consts> >>+ class philox_engine >>+ { >>+ static_assert(__n == 2 || __n == 4, >>+ "template argument N must be either 2 or 4"); >>+ static_assert(sizeof...(__consts) == __n, >>+ "length of consts array must match specified N"); >>+ static_assert(0 < __r, "a number of rounds must be specified"); >>+ static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), >>+ "specified bitlength must match input type"); > >The static assert messages all use the preferred "must" form, thanks! > >>+ template<typename _Sseq> >>+ using _If_seed_seq >>+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; >>+ >>+ private: > >This access specifier is redundant because everything above it is >already private. > >>+ // the ordering here is essential to functionality. > >Does this refer to the ordering of the _S_popArray member in the >class, because it needs to be declared before it's used? > >If so, I think we can drop the comment. If somebody fails to notice >that when reading the class definition, they'd find out pretty quickly >if they tried to reorder it and got errors. > >The style recommendations to put private internal members last is not >a hard requirement, for cases like this where the order is significant >putting them first is obviously fine (since you have no choice! :-) > >>+ /** @brief an internal unpacking function for %philox_engine. */ > >We don't need a doxygen comment for private member functions, they >won't get included in the docs anyway. > >>+ template <size_t __ind0, size_t __ind1> >>+ static constexpr >>+ array<_UIntType, __n / 2> >>+ _S_popArray() >>+ { >>+ if constexpr (__n == 4) >>+ return {__consts...[__ind0], __consts...[__ind1]}; >>+ else >>+ return {__consts...[__ind0]}; >>+ } >>+ >>+ public: >>+ /** Type of template param. */ >>+ using result_type = _UIntType; >>+ // public members >>+ static constexpr size_t word_size = __w; >>+ static constexpr size_t word_count = __n; >>+ static constexpr size_t round_count = __r; >>+ static constexpr array<result_type, __n / 2> multipliers = >>+ philox_engine::_S_popArray<0,2>(); >>+ static constexpr array<result_type, __n / 2> round_consts = >>+ philox_engine::_S_popArray<1,3>(); >>+ >>+ /** @brief returns the minimum value possible. */ >>+ static constexpr result_type >>+ min() >>+ { return 0; } >>+ >>+ /** @brief returns the maximum value possible. */ >>+ static constexpr result_type >>+ max() >>+ { >>+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); >>+ } >>+ // default key value >>+ static constexpr result_type default_seed = 20111115u; > >I wonder if we have a defect in the C++26 draft here, because >20111115u won't fit in a 16-bit int. We fixed that for >std::subtract_with_carry_engine via: >https://cplusplus.github.io/LWG/issue3809 >and then fixed what I broke via: >https://cplusplus.github.io/LWG/issue4014 > >Since the value of default_seed is only used as the argument to the >default ctor (or the equivalent of calling seed() with no argument) >and the ctor reduces value by mod 2^w, I think we could define the >default seed as 20111115u & max(). > >But let's leave it for now, I'll raise this with LWG. > >>+ >>+ // constructors >>+ philox_engine() >>+ : philox_engine(default_seed) >>+ {} >>+ >>+ explicit >>+ philox_engine(result_type __value); >>+ >>+ /** @brief seed sequence constructor for %philox_engine >>+ * >>+ * @params __q the seed sequence >>+ */ >>+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> >>+ explicit >>+ philox_engine(_Sseq& __q) >>+ { >>+ seed(__q); >>+ } >>+ >>+ void >>+ seed(result_type value = default_seed); >>+ >>+ /** @brief seeds %philox_engine by seed sequence >>+ * >>+ * @params __q the seed sequence >>+ */ >>+ template<typename _Sseq> >>+ _If_seed_seq<_Sseq> >>+ seed(_Sseq& __q); >>+ >>+ /** @brief sets the internal counter "cleartext" >>+ * >>+ * @params __counter std::array of len N >>+ */ >>+ void >>+ set_counter(const array<result_type, __n>& __counter); >>+ >>+ /** @brief compares two %philox_engine objects >>+ * >>+ * @params __x A %philox_engine object >>+ * @params __y A %philox_engine object >>+ * >>+ * @returns true if the objects will produce an identical stream, false >>+ * otherwise >>+ */ >>+ friend bool >>+ operator==(const philox_engine& __x, const philox_engine& __y) >>+ { >>+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), >>+ __y._M_x.begin(), __y._M_x.end()) >>+ && std::equal(__x._M_y.begin(), __x._M_y.end(), >>+ __y._M_y.begin(), __y._M_y.end()) >>+ && std::equal(__x._M_k.begin(), __x._M_k.end(), >>+ __y._M_k.begin(), __y._M_k.end()) >>+ && __x._M_i == __y._M_i); >>+ } >>+ >>+ /** @brief outputs a single w-bit number and handles state advancement >>+ * >>+ * @returns return_type >>+ */ >>+ _UIntType >>+ operator()(); >>+ >>+ /** @brief discards __z numbers >>+ * >>+ * @params __z number of iterations to discard >>+ */ >>+ void >>+ discard(unsigned long long __z); >>+ >>+ /** @brief outputs the state of the generator >>+ * >>+ * @param __os An output stream. >>+ * @param __x A %philox_engine object reference >>+ * >>+ * @returns the state of the Philox Engine in __os >>+ */ >>+ template<typename _CharT, typename _Traits> >>+ friend basic_ostream<_CharT, _Traits>& >>+ operator<<(basic_ostream<_CharT, _Traits>& __os, >>+ const philox_engine& __x) >>+ { > >The "operator<<" and "{" lines should be lined up with "friend" > >>+ const typename ios_base::fmtflags __flags = __os.flags(); >>+ const _CharT __fill = __os.fill(); >>+ __os.flags(ios_base::dec | ios_base::left); >>+ __os.fill(__os.widen(' ')); >>+ for (auto &__subkey : __x._M_k) > >auto& rather than putting the space before the & please (and below as >well). > >>+ __os << __subkey << ' '; >>+ for (auto &__ctr : __x._M_x) >>+ __os << __ctr << ' '; > >Inserting ' ' will get widened if __os is a wide stream, but since >you've already called __os.widen(' ') above you could save the result >of that in a local variable and then insert that character instead of >' '. That will avoid calling widen again on every iteration of the >loop. > >>+ __os << __x._M_i; >>+ __os.flags(__flags); > >If writing to the stream throws an exception we won't restore the >flags and fill character, but I think that's consistent with all our >operator<< overloads in <random>. > >>+ __os.fill(__fill); >>+ return __os; >>+ } >>+ >>+ /** @brief takes input to set the state of the %philox_engine object >>+ * >>+ * @param __is An input stream. >>+ * @param __x A %philox_engine object reference >>+ * >>+ * @returns %philox_engine object is set with values from instream >>+ */ >>+ template <typename _CharT, typename _Traits> >>+ friend basic_istream<_CharT, _Traits>& >>+ operator>>(basic_istream<_CharT, _Traits>& __is, >>+ philox_engine& __x) >>+ { > >Same comment about aligning to "friend". > >>+ const typename ios_base::fmtflags __flags = __is.flags(); >>+ __is.flags(ios_base::dec | ios_base::skipws); >>+ for (auto &__subkey : __x._M_k) >>+ __is >> __subkey; >>+ for (auto &__ctr : __x._M_x) >>+ __is >> __ctr; >>+ array<_UIntType, __n> __tmpCtr = __x._M_x; >>+ unsigned char __setIndex = 0; >>+ for (size_t __j = 0; __j < __x._M_x.size(); ++__j) >>+ { >>+ if (__x._M_x[__j] > 0) >>+ { >>+ __setIndex = __j; >>+ break; >>+ } >>+ } >>+ for (size_t __j = 0; __j <= __setIndex; ++__j) >>+ { >>+ if (__j != __setIndex) >>+ __x._M_x[__j] = max(); >>+ else >>+ --__x._M_x[__j]; >>+ } >>+ __x._M_philox(); >>+ __x._M_x = __tmpCtr; >>+ __is >> __x._M_i; >>+ __is.flags(__flags); >>+ return __is; >>+ } >>+ private: >>+ // private state variables >>+ array<_UIntType, __n> _M_x; >>+ array<_UIntType, __n / 2> _M_k; >>+ array<_UIntType, __n> _M_y; >>+ unsigned long long _M_i = 0; Why is this unsigned long long? The maximum value of i is 4, isn't it? >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ void >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() >>+ { >>+ array<_UIntType, __n> __outputSeq{}; >>+ for (size_t __j = 0; __j < __n; ++__j) >>+ __outputSeq[__j] = _M_x[__j]; >>+ for (unsigned long __j = 0; __j < __r; ++__j) Should __j be size_t? Otherwise on Windows it would be 32-bit, but __r is size_t which is 64-bit, and so __j < __r might never be true. >>+ { >>+ array<_UIntType, __n> __intermedSeq{}; >>+ if constexpr (__n == 4) >>+ { >>+ __intermedSeq[0] = __outputSeq[2]; >>+ __intermedSeq[1] = __outputSeq[1]; >>+ __intermedSeq[2] = __outputSeq[0]; >>+ __intermedSeq[3] = __outputSeq[3]; >>+ } else >>+ { >>+ __intermedSeq[0] = __outputSeq[0]; >>+ __intermedSeq[1] = __outputSeq[1]; >>+ } >>+ for (unsigned long __k = 0; __k < (__n/2); ++__k) __k should be the same type as __n, although this loop never runs more than twice, right? >>+ { >>+ __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) >>+ ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) >>+ ^ __intermedSeq[2*__k+1]; >>+ >>+ __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], >>+ multipliers[__k]); >>+ } >>+ } >>+ for (unsigned long __j = 0; __j < __n; ++__j) >>+ _M_y[__j] = __outputSeq[__j]; __j should be the same type as __n, but can this be simply: _M_y = __outputSeq; ? >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ philox_engine<_UIntType, >>+ __w, __n, __r, __consts...>::philox_engine(result_type __value) >>+ { >>+ std::fill(_M_x.begin(), _M_x.end(), 0); >>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>+ std::fill(_M_y.begin(), _M_y.end(), 0); > >Can we just initialize these in a mem-initializer-list? > > : _M_x{}, _M_k{}, _M_y{} > >The compiler is more likely to optimize that well. > >>+ _M_k[0] = __value & max(); >>+ _M_i = __n - 1; >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ void >>+ philox_engine<_UIntType, >>+ __w, __n, __r, __consts...>::seed(result_type __value) >>+ { >>+ std::fill(_M_x.begin(), _M_x.end(), 0); > >This can use std::array::fill, i.e. _M_x.fill(0); > >Or would this whole function be simpler as: > > *this = philox_engine(__value); > >? > >>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>+ std::fill(_M_y.begin(), _M_y.end(), 0); >>+ _M_k[0] = __value & max(); >>+ _M_i = __n - 1; >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ void >>+ philox_engine<_UIntType, __w, >>+ __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) >>+ { >>+ for (unsigned long long __j = 0; __j < __n; ++__j) __j should be the same type as __n >>+ _M_x[__j] = __counter[__n - 1 - __j] & max(); >>+ _M_i = __n - 1; >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ template<typename _Sseq> >>+ auto >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) >>+ -> _If_seed_seq<_Sseq> >>+ { >>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>+ const unsigned long long __p = 1 + ((__w - 1)/ 32); >>+ uint_least32_t __tmpArr[(__n / 2) * __p]; >>+ __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); >>+ for (unsigned long long __k = 0; __k < (__n/2); ++__k) >>+ { >>+ unsigned long long __precalc = 0; >>+ for (unsigned long long __j = 0; __j < __p; ++__j) >>+ { >>+ unsigned long long __multiplicand = (1ull << (32 * __j)); >>+ __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); >>+ } Do any of the variables above need to be unsigned long long? >>+ _M_k[__k] = __precalc; >>+ } >>+ std::fill(_M_x.begin(), _M_x.end(), 0); >>+ std::fill(_M_y.begin(), _M_y.end(), 0); >>+ _M_i = __n - 1; >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ void >>+ philox_engine<_UIntType, >>+ __w, __n, __r, __consts...>::discard(unsigned long long __z) >>+ { >>+ for (unsigned long long __j = 0; __j < __z; ++__j) >>+ _M_transition(); >>+ } >>+ >>+ template<class _UIntType, >>+ size_t __w, size_t __n, >>+ size_t __r, _UIntType... __consts> >>+ _UIntType >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() >>+ { >>+ _M_transition(); >>+ return _M_y[_M_i]; >>+ } >>+ >>+#endif >> >> template<typename _IntType, typename _CharT, typename _Traits> >> std::basic_ostream<_CharT, _Traits>& >>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def >>index dbe2cb8f175..4c21fa187c8 100644 >>--- a/libstdc++-v3/include/bits/version.def >>+++ b/libstdc++-v3/include/bits/version.def >>@@ -2069,6 +2069,15 @@ ftms = { >> }; >>}; >> >>+ftms = { >>+ name = philox_engine; >>+ values = { >>+ v = 202406; >>+ cxxmin = 26; >>+ extra_cond = "__SIZEOF_INT128__"; >>+ }; >>+}; >>+ >>// Standard test specifications. >>stds[97] = ">= 199711L"; >>stds[03] = ">= 199711L"; >>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h >>index 7bb6016df68..75a9946adce 100644 >>--- a/libstdc++-v3/include/bits/version.h >>+++ b/libstdc++-v3/include/bits/version.h >>@@ -2318,4 +2318,14 @@ >>#endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ >>#undef __glibcxx_want_constexpr_exceptions >> >>+#if !defined(__cpp_lib_philox_engine) >>+# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) >>+# define __glibcxx_philox_engine 202406L >>+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) >>+# define __cpp_lib_philox_engine 202406L >>+# endif >>+# endif >>+#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ >>+#undef __glibcxx_want_philox_engine >>+ >>#undef __glibcxx_want_all >>diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random >>index 0e058a04bd9..8a02ade4b75 100644 >>--- a/libstdc++-v3/include/std/random >>+++ b/libstdc++-v3/include/std/random >>@@ -39,6 +39,9 @@ >># include <bits/c++0x_warning.h> >>#else >> >>+#define __glibcxx_want_philox_engine >>+#include <bits/version.h> >>+ >>#include <cmath> >>#include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t >>#include <cstdlib> >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>new file mode 100644 >>index 00000000000..d5a8ca078ef >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>@@ -0,0 +1,23 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine > >Please identify the document where these references are from, because >29.5.4 might be a different subclause in C++29, i.e. > >>+// N5014 29.5.4 Random Number Engine Class Templates >>+// N5014 29.5.4.5 Class Template philox_engine > >(Ideally we'd say "C++26 29.5.4 ..." but we don't know if that >subclause number will be correct in the final standard.) > >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+void >>+test01() >>+{ >>+ std::philox4x32 a; >>+ a.discard(9999); >>+ >>+ VERIFY( a() == 1955073260 ); >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>new file mode 100644 >>index 00000000000..131f094cb28 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>@@ -0,0 +1,23 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+void >>+test01() >>+{ >>+ std::philox4x64 a; >>+ a.discard(9999); >>+ >>+ VERIFY( a() == 3409172418970261260 ); >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>new file mode 100644 >>index 00000000000..c3a5a0eb754 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>@@ -0,0 +1,39 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+int f(int x) >>+{ >>+ std::seed_seq sq(&x, &x + 1); >>+ auto rnd = std::philox4x32(sq); >>+ return std::uniform_int_distribution<int>()(rnd); >>+} >>+ >>+int g(int x) >>+{ >>+ std::seed_seq sq(&x, &x + 1); >>+ auto rnd = std::philox4x32(); >>+ rnd.seed(sq); >>+ return std::uniform_int_distribution<int>()(rnd); >>+} >>+ >>+void test01() >>+{ >>+ const int f1 = f(0); >>+ const int f2 = f(0); >>+ >>+ const int g1 = g(0); >>+ const int g2 = g(0); >>+ >>+ VERIFY( f1 == f2 ); >>+ VERIFY( g1 == g2 ); >>+ VERIFY( f1 == g1 ); >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>new file mode 100644 >>index 00000000000..4f61928a157 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>@@ -0,0 +1,25 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+ >>+void >>+test01() >>+{ >>+ >>+ std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); >>+ >>+ const auto f(e); >>+ auto g(f); >>+ g = g; // Suppress unused warning >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>new file mode 100644 >>index 00000000000..9f9ae94db0f >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>@@ -0,0 +1,27 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+void >>+test01() >>+{ >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> philox4x32nullkey(0); >>+ >>+ VERIFY( philox4x32nullkey.min() == 0 ); >>+ VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); >>+ VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>new file mode 100644 >>index 00000000000..5cb914f4b45 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>@@ -0,0 +1,20 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+#include <random> >>+ >>+void >>+test01() >>+{ >>+ unsigned long seed = 2; >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> philox4x32seeded(seed); >>+} >>+ >>+int main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>new file mode 100644 >>index 00000000000..7d9e3e6540d >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>@@ -0,0 +1,24 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+ >>+void >>+test01() >>+{ >>+ std::seed_seq sseq{ 1, 2, 3, 4 }; >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> philox4x32sseq(sseq); >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>new file mode 100644 >>index 00000000000..4f62bfbbd88 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>@@ -0,0 +1,30 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+void >>+test01() >>+{ >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> x, y; >>+ >>+ VERIFY ( x == y); >>+ x.discard(100); >>+ y.discard(100); >>+ >>+ VERIFY (x == y); >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>new file mode 100644 >>index 00000000000..86d757db904 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>@@ -0,0 +1,30 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_hooks.h> >>+ >>+void >>+test01() >>+{ >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> x, y; >>+ >>+ VERIFY ( !(x != y) ); >>+ x.discard(100); >>+ y.discard(100); >>+ >>+ VERIFY ( !(x != y) ); >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>new file mode 100644 >>index 00000000000..bec4b172512 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>@@ -0,0 +1,49 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <sstream> >>+#include <random> >>+#include <testsuite_hooks.h> >>+#include <iostream> >>+ >>+void >>+test01() >>+{ >>+ std::stringstream str; >>+ std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> x, y; >>+ >>+ x(); >>+ str << x; >>+ >>+ VERIFY ( !(x == y) ); >>+ str >> y; >>+ VERIFY ( x == y ); >>+ for (unsigned long i = 0; i < 100; ++i) >>+ { >>+ VERIFY (x() == y()); >>+ } >>+ str.clear(); >>+ str << y; >>+ x(); >>+ x(); >>+ x(); >>+ str >> x; >>+ VERIFY ( x == y ); >>+ for (unsigned long i = 0; i < 1000; ++i) >>+ { >>+ VERIFY (x() == y()); >>+ } >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>new file mode 100644 >>index 00000000000..c242056e0a4 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>@@ -0,0 +1,26 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+ >>+void test01() >>+{ >>+ std::philox4x32 philox; >>+ const void* p = &philox.word_size; >>+ p = &philox.word_count; >>+ p = &philox.round_count; >>+ p = &philox.multipliers; >>+ p = &philox.round_consts; >>+ p = &philox.default_seed; >>+ p = p; // Suppress unused warning. >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>new file mode 100644 >>index 00000000000..5be0108c88c >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>@@ -0,0 +1,50 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_common_types.h> >>+ >>+namespace __gnu_test >>+{ >>+ struct constexpr_member_data >>+ { >>+ template<typename _Ttesttype> >>+ void >>+ operator()() >>+ { >>+ struct _Concept >>+ { >>+ void __constraint() >>+ { >>+ constexpr auto v1 __attribute__((unused)) >>+ = _Ttesttype::word_size; >>+ constexpr auto v2 __attribute__((unused)) >>+ = _Ttesttype::word_count; >>+ constexpr auto v3 __attribute__((unused)) >>+ = _Ttesttype::round_count; >>+ constexpr auto v4 __attribute__((unused)) >>+ = _Ttesttype::multipliers; >>+ constexpr auto v5 __attribute__((unused)) >>+ = _Ttesttype::round_consts; >>+ constexpr auto v6 __attribute__((unused)) >>+ = _Ttesttype::default_seed; >>+ } >>+ }; >>+ >>+ _Concept c; >>+ c.__constraint(); >>+ } >>+ }; >>+}; >>+ >>+int >>+main() >>+{ >>+ __gnu_test::constexpr_member_data test; >>+ typedef std::philox4x32 type; >>+ test.operator()<type>(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>new file mode 100644 >>index 00000000000..eb61d15568a >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>@@ -0,0 +1,41 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+#include <random> >>+#include <testsuite_common_types.h> >>+ >>+namespace __gnu_test >>+{ >>+ struct constexpr_member_functions >>+ { >>+ template<typename _Ttesttype> >>+ void >>+ operator()() >>+ { >>+ struct _Concept >>+ { >>+ void __constraint() >>+ { >>+ constexpr auto v1 __attribute__((unused)) >>+ = _Ttesttype::min(); >>+ constexpr auto v2 __attribute__((unused)) >>+ = _Ttesttype::max(); >>+ } >>+ }; >>+ _Concept c; >>+ c.__constraint(); >>+ } >>+ }; >>+} >>+ >>+int >>+main() >>+{ >>+ __gnu_test::constexpr_member_functions test; >>+ typedef std::philox4x32 type; >>+ test.operator()<type>(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >>new file mode 100644 >>index 00000000000..b368ee74106 >>--- /dev/null >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >>@@ -0,0 +1,26 @@ >>+// { dg-do run { target c++26 } } >>+// { dg-require-cstdint "" } >>+ >>+// 29.5.4 Random Number Engine Class Templates >>+// 29.5.4.5 Class Template philox_engine >>+ >>+ >>+#include <random> >>+ >>+void >>+test01() >>+{ >>+ typedef std::philox_engine<std::uint_fast32_t, >>+ 32, 4, 10, 0xCD9E8D57, >>+ 0x9E3779B9, 0xD2511F53, >>+ 0xBB67AE85> testType; >>+ >>+ typedef testType::result_type result_type; >>+} >>+ >>+int >>+main() >>+{ >>+ test01(); >>+ return 0; >>+} >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>index 0afba654152..13a0b8947b0 100644 >>--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); >>auto x = std::generate_canonical<std::size_t, >> std::numeric_limits<std::size_t>::digits>(urng); >> >>-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } >>+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } >> >>-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } >>+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } >>-- >>2.49.0 >> >
All the new tests fail on 32-bit targets because they don't have
__uint128_t so the philox engines aren't defined.
We should add this to them all:
// { dg-require-cpp-feature-test __cpp_lib_philox_engine }
On Mon, 8 Sept 2025 at 18:18, Jonathan Wakely <jwakely@redhat.com> wrote: > > All the new tests fail on 32-bit targets because they don't have > __uint128_t so the philox engines aren't defined. > > We should add this to them all: > > // { dg-require-cpp-feature-test __cpp_lib_philox_engine } And we don't need dg-require-cstdint now, that's historical.
Some of the tests use { dg-do run } but don't actually do anything,
they just refer to some typedefs. Those should be { dg-do compile }
instead of run.
I've pushed your philox implementation to trunk now, thanks very much for adding it to GCC! I also pushed some minor refactoring, and would still like to address the (unnecessary?) uses of 64-bit types pointed out in the mail below. And we need to make it use uint64_t for the calculations when the result type only has 32-bits, and eventually emulate 128-bit arithmetic to support 64-bit result types on targets without int128. But they can all be done later. On Mon, 8 Sept 2025, 16:46 Jonathan Wakely, <jwakely@redhat.com> wrote: > On Mon, 08 Sep 2025 at 15:55 +0100, Jonathan Wakely wrote: > >On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: > >>Implements Philox Engine (P2075R6) and associated tests. > >> > >>Implements additional feedback from v5 from Patrick Palka. > >>Also cut some trailing whitespace from the limb propagation > >>fix in v5. > >> > >>Apologies for the delay from v5 - I had to finish writing some > >>CTF challenges for DEFCON this week. Thank you for the > >>feedback, and please let me know if anything further needs > >>to be changed. > >> > >>Built/tested on x86_64-pc-linux-gnu. > > > >>From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 > >>From: 1nfocalypse <1nfocalypse@protonmail.com> > >>Date: Tue, 5 Aug 2025 01:37:18 +0000 > >>Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) > >> > >>Implemented additional changes from Patrick Palka. > >>Also removed some trailing whitespace in random.tcc. > >> > >>replaced alias of __ios_base with ios_base > >>included <iomanip> in random.h > >>altered line number in pr60037-neg.cc to be accurate > >>Refactored iterator/index loops to be range-based for > >>in istream/ostream operator overloads > >>include <random> in philox_engine/cons/seed_seq.cc > >>Conforms with errata LWG4143, LWG4153 for Philox Engine. > >> > >> PR libstdc++/119794 > >>--- > >>libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ > >>libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ > >>libstdc++-v3/include/bits/version.def | 9 + > >>libstdc++-v3/include/bits/version.h | 10 + > >>libstdc++-v3/include/std/random | 3 + > >>.../26_numerics/random/philox4x32.cc | 23 ++ > >>.../26_numerics/random/philox4x64.cc | 23 ++ > >>.../random/philox_engine/cons/119794.cc | 39 +++ > >>.../random/philox_engine/cons/copy.cc | 25 ++ > >>.../random/philox_engine/cons/default.cc | 27 ++ > >>.../random/philox_engine/cons/seed.cc | 20 ++ > >>.../random/philox_engine/cons/seed_seq.cc | 24 ++ > >>.../random/philox_engine/operators/equal.cc | 30 ++ > >>.../random/philox_engine/operators/inequal.cc | 30 ++ > >>.../philox_engine/operators/serialize.cc | 49 +++ > >>.../philox_engine/requirements/constants.cc | 26 ++ > >>.../requirements/constexpr_data.cc | 50 ++++ > >>.../requirements/constexpr_functions.cc | 41 +++ > >>.../philox_engine/requirements/typedefs.cc | 26 ++ > >>.../26_numerics/random/pr60037-neg.cc | 4 +- > >>20 files changed, 931 insertions(+), 2 deletions(-) > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > >>create mode 100644 > libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >> > >>diff --git a/libstdc++-v3/include/bits/random.h > b/libstdc++-v3/include/bits/random.h > >>index 1fdaf51934f..e74b7bd75c0 100644 > >>--- a/libstdc++-v3/include/bits/random.h > >>+++ b/libstdc++-v3/include/bits/random.h > >>@@ -33,6 +33,7 @@ > >> > >>#include <vector> > >>#include <bits/uniform_int_dist.h> > >>+#include <iomanip> > >> > >>namespace std _GLIBCXX_VISIBILITY(default) > >>{ > >>@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> { return !(__lhs == __rhs); } > >>#endif > >> > >>+#if __cpp_lib_philox_engine > > > >This should check __glibcxx_philox_engine (just in case this code is > >moved to a separate header that ends up being included somewhere > >withou all of <random>). > > > >>+ /** > >>+ * @brief: A discrete pseudorandom number generator based off of > weakened > >>+ * cryptographic primitives. > > > >Can we say "based on" instead of "based off of" which is an informal > >American English term that sounds wrong to me. > > > >I'm also not sure what this is telling me as a non-export user. Would > >it be clearer to talk about it having weak cryptographic properties, > >rather than being based on weakened cryptographic primitives? > > > >>+ * > >>+ * This algorithm was intended to be used for highly parallel random > number > > > >"was intended to be used" sounds like it refers to something in the > >past that is no longer true. Maybe "was designed to be used" instead? > > > >>+ * generation, and is capable of immensely long periods. It provides > "Crush- > > > >I was going to ask whether immensely is a bit hyperbolic and could be > >just "very long periods (e.g. ...)" but since we're talking at least > >2^128 I think "immensely" is fine :-) > > > >>+ * resistance", denoting an ability to pass the TestU01 Suite's "Big > Crush" > >>+ * test, demonstrating significant apparent entropy. It is not > intended for > >>+ * cryptographic use and should not be used for such, despite being > based on > >>+ * cryptographic primitives. > >>+ * > >>+ * The two four-word definitions are likely the best use for this > algorithm, > >>+ * and are given below as defaults. > > > >Can we refer to those as aliases or typedefs rather than definitions? > >Or specializations? > > > >Maybe even say it like this: > > > > * The typedefs `philox4x32` and `philox4x64` are provided as > > * suitable defaults for most use cases, providing high-quality > > * random numbers at reasonable performance. > > > > > >>+ * > >>+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, > and > >>+ * David Shaw as a product of D.E. Shaw Research. > >>+ * > >>+ * @tparam __w Word size > >>+ * @tparam __n Buffer size > >>+ * @tparam __r Rounds > >>+ * @tparam __consts Multiplication and round constant pack, > ordered as > >>+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , > M_{N/2-1}, C_{N/2-1} > >>+ * > >>+ * @headerfile random > >>+ * @since C++26 > >>+ */ > >>+ template<class _UIntType, size_t __w, > > > >Our convention in libstdc++ code is to type 'typename' for template > >parameters instead of 'class' > > > >>+ size_t __n, size_t __r, > >>+ _UIntType... __consts> > >>+ class philox_engine > >>+ { > >>+ static_assert(__n == 2 || __n == 4, > >>+ "template argument N must be either 2 or 4"); > >>+ static_assert(sizeof...(__consts) == __n, > >>+ "length of consts array must match specified N"); > >>+ static_assert(0 < __r, "a number of rounds must be specified"); > >>+ static_assert((0 < __w && __w <= > numeric_limits<_UIntType>::digits), > >>+ "specified bitlength must match input type"); > > > >The static assert messages all use the preferred "must" form, thanks! > > > >>+ template<typename _Sseq> > >>+ using _If_seed_seq > >>+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; > >>+ > >>+ private: > > > >This access specifier is redundant because everything above it is > >already private. > > > >>+ // the ordering here is essential to functionality. > > > >Does this refer to the ordering of the _S_popArray member in the > >class, because it needs to be declared before it's used? > > > >If so, I think we can drop the comment. If somebody fails to notice > >that when reading the class definition, they'd find out pretty quickly > >if they tried to reorder it and got errors. > > > >The style recommendations to put private internal members last is not > >a hard requirement, for cases like this where the order is significant > >putting them first is obviously fine (since you have no choice! :-) > > > >>+ /** @brief an internal unpacking function for %philox_engine. */ > > > >We don't need a doxygen comment for private member functions, they > >won't get included in the docs anyway. > > > >>+ template <size_t __ind0, size_t __ind1> > >>+ static constexpr > >>+ array<_UIntType, __n / 2> > >>+ _S_popArray() > >>+ { > >>+ if constexpr (__n == 4) > >>+ return {__consts...[__ind0], __consts...[__ind1]}; > >>+ else > >>+ return {__consts...[__ind0]}; > >>+ } > >>+ > >>+ public: > >>+ /** Type of template param. */ > >>+ using result_type = _UIntType; > >>+ // public members > >>+ static constexpr size_t word_size = __w; > >>+ static constexpr size_t word_count = __n; > >>+ static constexpr size_t round_count = __r; > >>+ static constexpr array<result_type, __n / 2> multipliers = > >>+ philox_engine::_S_popArray<0,2>(); > >>+ static constexpr array<result_type, __n / 2> round_consts = > >>+ philox_engine::_S_popArray<1,3>(); > >>+ > >>+ /** @brief returns the minimum value possible. */ > >>+ static constexpr result_type > >>+ min() > >>+ { return 0; } > >>+ > >>+ /** @brief returns the maximum value possible. */ > >>+ static constexpr result_type > >>+ max() > >>+ { > >>+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); > >>+ } > >>+ // default key value > >>+ static constexpr result_type default_seed = 20111115u; > > > >I wonder if we have a defect in the C++26 draft here, because > >20111115u won't fit in a 16-bit int. We fixed that for > >std::subtract_with_carry_engine via: > >https://cplusplus.github.io/LWG/issue3809 > >and then fixed what I broke via: > >https://cplusplus.github.io/LWG/issue4014 > > > >Since the value of default_seed is only used as the argument to the > >default ctor (or the equivalent of calling seed() with no argument) > >and the ctor reduces value by mod 2^w, I think we could define the > >default seed as 20111115u & max(). > > > >But let's leave it for now, I'll raise this with LWG. > > > >>+ > >>+ // constructors > >>+ philox_engine() > >>+ : philox_engine(default_seed) > >>+ {} > >>+ > >>+ explicit > >>+ philox_engine(result_type __value); > >>+ > >>+ /** @brief seed sequence constructor for %philox_engine > >>+ * > >>+ * @params __q the seed sequence > >>+ */ > >>+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> > >>+ explicit > >>+ philox_engine(_Sseq& __q) > >>+ { > >>+ seed(__q); > >>+ } > >>+ > >>+ void > >>+ seed(result_type value = default_seed); > >>+ > >>+ /** @brief seeds %philox_engine by seed sequence > >>+ * > >>+ * @params __q the seed sequence > >>+ */ > >>+ template<typename _Sseq> > >>+ _If_seed_seq<_Sseq> > >>+ seed(_Sseq& __q); > >>+ > >>+ /** @brief sets the internal counter "cleartext" > >>+ * > >>+ * @params __counter std::array of len N > >>+ */ > >>+ void > >>+ set_counter(const array<result_type, __n>& __counter); > >>+ > >>+ /** @brief compares two %philox_engine objects > >>+ * > >>+ * @params __x A %philox_engine object > >>+ * @params __y A %philox_engine object > >>+ * > >>+ * @returns true if the objects will produce an identical stream, > false > >>+ * otherwise > >>+ */ > >>+ friend bool > >>+ operator==(const philox_engine& __x, const philox_engine& __y) > >>+ { > >>+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), > >>+ __y._M_x.begin(), __y._M_x.end()) > >>+ && std::equal(__x._M_y.begin(), __x._M_y.end(), > >>+ __y._M_y.begin(), __y._M_y.end()) > >>+ && std::equal(__x._M_k.begin(), __x._M_k.end(), > >>+ __y._M_k.begin(), __y._M_k.end()) > >>+ && __x._M_i == __y._M_i); > >>+ } > >>+ > >>+ /** @brief outputs a single w-bit number and handles state > advancement > >>+ * > >>+ * @returns return_type > >>+ */ > >>+ _UIntType > >>+ operator()(); > >>+ > >>+ /** @brief discards __z numbers > >>+ * > >>+ * @params __z number of iterations to discard > >>+ */ > >>+ void > >>+ discard(unsigned long long __z); > >>+ > >>+ /** @brief outputs the state of the generator > >>+ * > >>+ * @param __os An output stream. > >>+ * @param __x A %philox_engine object reference > >>+ * > >>+ * @returns the state of the Philox Engine in __os > >>+ */ > >>+ template<typename _CharT, typename _Traits> > >>+ friend basic_ostream<_CharT, _Traits>& > >>+ operator<<(basic_ostream<_CharT, _Traits>& __os, > >>+ const philox_engine& __x) > >>+ { > > > >The "operator<<" and "{" lines should be lined up with "friend" > > > >>+ const typename ios_base::fmtflags __flags = __os.flags(); > >>+ const _CharT __fill = __os.fill(); > >>+ __os.flags(ios_base::dec | ios_base::left); > >>+ __os.fill(__os.widen(' ')); > >>+ for (auto &__subkey : __x._M_k) > > > >auto& rather than putting the space before the & please (and below as > >well). > > > >>+ __os << __subkey << ' '; > >>+ for (auto &__ctr : __x._M_x) > >>+ __os << __ctr << ' '; > > > >Inserting ' ' will get widened if __os is a wide stream, but since > >you've already called __os.widen(' ') above you could save the result > >of that in a local variable and then insert that character instead of > >' '. That will avoid calling widen again on every iteration of the > >loop. > > > >>+ __os << __x._M_i; > >>+ __os.flags(__flags); > > > >If writing to the stream throws an exception we won't restore the > >flags and fill character, but I think that's consistent with all our > >operator<< overloads in <random>. > > > >>+ __os.fill(__fill); > >>+ return __os; > >>+ } > >>+ > >>+ /** @brief takes input to set the state of the %philox_engine > object > >>+ * > >>+ * @param __is An input stream. > >>+ * @param __x A %philox_engine object reference > >>+ * > >>+ * @returns %philox_engine object is set with values from instream > >>+ */ > >>+ template <typename _CharT, typename _Traits> > >>+ friend basic_istream<_CharT, _Traits>& > >>+ operator>>(basic_istream<_CharT, _Traits>& __is, > >>+ philox_engine& __x) > >>+ { > > > >Same comment about aligning to "friend". > > > >>+ const typename ios_base::fmtflags __flags = __is.flags(); > >>+ __is.flags(ios_base::dec | ios_base::skipws); > >>+ for (auto &__subkey : __x._M_k) > >>+ __is >> __subkey; > >>+ for (auto &__ctr : __x._M_x) > >>+ __is >> __ctr; > >>+ array<_UIntType, __n> __tmpCtr = __x._M_x; > >>+ unsigned char __setIndex = 0; > >>+ for (size_t __j = 0; __j < __x._M_x.size(); ++__j) > >>+ { > >>+ if (__x._M_x[__j] > 0) > >>+ { > >>+ __setIndex = __j; > >>+ break; > >>+ } > >>+ } > >>+ for (size_t __j = 0; __j <= __setIndex; ++__j) > >>+ { > >>+ if (__j != __setIndex) > >>+ __x._M_x[__j] = max(); > >>+ else > >>+ --__x._M_x[__j]; > >>+ } > >>+ __x._M_philox(); > >>+ __x._M_x = __tmpCtr; > >>+ __is >> __x._M_i; > >>+ __is.flags(__flags); > >>+ return __is; > >>+ } > >>+ private: > >>+ // private state variables > >>+ array<_UIntType, __n> _M_x; > >>+ array<_UIntType, __n / 2> _M_k; > >>+ array<_UIntType, __n> _M_y; > >>+ unsigned long long _M_i = 0; > > Why is this unsigned long long? The maximum value of i is 4, isn't it? > > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ void > >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() > >>+ { > >>+ array<_UIntType, __n> __outputSeq{}; > >>+ for (size_t __j = 0; __j < __n; ++__j) > >>+ __outputSeq[__j] = _M_x[__j]; > >>+ for (unsigned long __j = 0; __j < __r; ++__j) > > Should __j be size_t? > > Otherwise on Windows it would be 32-bit, but __r is size_t which is > 64-bit, and so __j < __r might never be true. > > >>+ { > >>+ array<_UIntType, __n> __intermedSeq{}; > >>+ if constexpr (__n == 4) > >>+ { > >>+ __intermedSeq[0] = __outputSeq[2]; > >>+ __intermedSeq[1] = __outputSeq[1]; > >>+ __intermedSeq[2] = __outputSeq[0]; > >>+ __intermedSeq[3] = __outputSeq[3]; > >>+ } else > >>+ { > >>+ __intermedSeq[0] = __outputSeq[0]; > >>+ __intermedSeq[1] = __outputSeq[1]; > >>+ } > >>+ for (unsigned long __k = 0; __k < (__n/2); ++__k) > > __k should be the same type as __n, although this loop never runs > more than twice, right? > > >>+ { > >>+ __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], > multipliers[__k]) > >>+ ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) > >>+ ^ __intermedSeq[2*__k+1]; > >>+ > >>+ __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], > >>+ multipliers[__k]); > >>+ } > >>+ } > >>+ for (unsigned long __j = 0; __j < __n; ++__j) > >>+ _M_y[__j] = __outputSeq[__j]; > > __j should be the same type as __n, but can this be simply: > > _M_y = __outputSeq; > > ? > > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ philox_engine<_UIntType, > >>+ __w, __n, __r, __consts...>::philox_engine(result_type __value) > >>+ { > >>+ std::fill(_M_x.begin(), _M_x.end(), 0); > >>+ std::fill(_M_k.begin(), _M_k.end(), 0); > >>+ std::fill(_M_y.begin(), _M_y.end(), 0); > > > >Can we just initialize these in a mem-initializer-list? > > > > : _M_x{}, _M_k{}, _M_y{} > > > >The compiler is more likely to optimize that well. > > > >>+ _M_k[0] = __value & max(); > >>+ _M_i = __n - 1; > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ void > >>+ philox_engine<_UIntType, > >>+ __w, __n, __r, __consts...>::seed(result_type __value) > >>+ { > >>+ std::fill(_M_x.begin(), _M_x.end(), 0); > > > >This can use std::array::fill, i.e. _M_x.fill(0); > > > >Or would this whole function be simpler as: > > > > *this = philox_engine(__value); > > > >? > > > >>+ std::fill(_M_k.begin(), _M_k.end(), 0); > >>+ std::fill(_M_y.begin(), _M_y.end(), 0); > >>+ _M_k[0] = __value & max(); > >>+ _M_i = __n - 1; > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ void > >>+ philox_engine<_UIntType, __w, > >>+ __n, __r, __consts...>::set_counter(const array<result_type, __n>& > __counter) > >>+ { > >>+ for (unsigned long long __j = 0; __j < __n; ++__j) > > __j should be the same type as __n > > >>+ _M_x[__j] = __counter[__n - 1 - __j] & max(); > >>+ _M_i = __n - 1; > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ template<typename _Sseq> > >>+ auto > >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) > >>+ -> _If_seed_seq<_Sseq> > >>+ { > >>+ std::fill(_M_k.begin(), _M_k.end(), 0); > >>+ const unsigned long long __p = 1 + ((__w - 1)/ 32); > >>+ uint_least32_t __tmpArr[(__n / 2) * __p]; > >>+ __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); > >>+ for (unsigned long long __k = 0; __k < (__n/2); ++__k) > >>+ { > >>+ unsigned long long __precalc = 0; > >>+ for (unsigned long long __j = 0; __j < __p; ++__j) > >>+ { > >>+ unsigned long long __multiplicand = (1ull << (32 * __j)); > >>+ __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); > >>+ } > > Do any of the variables above need to be unsigned long long? > > >>+ _M_k[__k] = __precalc; > >>+ } > >>+ std::fill(_M_x.begin(), _M_x.end(), 0); > >>+ std::fill(_M_y.begin(), _M_y.end(), 0); > >>+ _M_i = __n - 1; > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ void > >>+ philox_engine<_UIntType, > >>+ __w, __n, __r, __consts...>::discard(unsigned long long __z) > >>+ { > >>+ for (unsigned long long __j = 0; __j < __z; ++__j) > >>+ _M_transition(); > >>+ } > >>+ > >>+ template<class _UIntType, > >>+ size_t __w, size_t __n, > >>+ size_t __r, _UIntType... __consts> > >>+ _UIntType > >>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() > >>+ { > >>+ _M_transition(); > >>+ return _M_y[_M_i]; > >>+ } > >>+ > >>+#endif > >> > >> template<typename _IntType, typename _CharT, typename _Traits> > >> std::basic_ostream<_CharT, _Traits>& > >>diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > >>index dbe2cb8f175..4c21fa187c8 100644 > >>--- a/libstdc++-v3/include/bits/version.def > >>+++ b/libstdc++-v3/include/bits/version.def > >>@@ -2069,6 +2069,15 @@ ftms = { > >> }; > >>}; > >> > >>+ftms = { > >>+ name = philox_engine; > >>+ values = { > >>+ v = 202406; > >>+ cxxmin = 26; > >>+ extra_cond = "__SIZEOF_INT128__"; > >>+ }; > >>+}; > >>+ > >>// Standard test specifications. > >>stds[97] = ">= 199711L"; > >>stds[03] = ">= 199711L"; > >>diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > >>index 7bb6016df68..75a9946adce 100644 > >>--- a/libstdc++-v3/include/bits/version.h > >>+++ b/libstdc++-v3/include/bits/version.h > >>@@ -2318,4 +2318,14 @@ > >>#endif /* !defined(__cpp_lib_constexpr_exceptions) && > defined(__glibcxx_want_constexpr_exceptions) */ > >>#undef __glibcxx_want_constexpr_exceptions > >> > >>+#if !defined(__cpp_lib_philox_engine) > >>+# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) > >>+# define __glibcxx_philox_engine 202406L > >>+# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_philox_engine) > >>+# define __cpp_lib_philox_engine 202406L > >>+# endif > >>+# endif > >>+#endif /* !defined(__cpp_lib_philox_engine) && > defined(__glibcxx_want_philox_engine) */ > >>+#undef __glibcxx_want_philox_engine > >>+ > >>#undef __glibcxx_want_all > >>diff --git a/libstdc++-v3/include/std/random > b/libstdc++-v3/include/std/random > >>index 0e058a04bd9..8a02ade4b75 100644 > >>--- a/libstdc++-v3/include/std/random > >>+++ b/libstdc++-v3/include/std/random > >>@@ -39,6 +39,9 @@ > >># include <bits/c++0x_warning.h> > >>#else > >> > >>+#define __glibcxx_want_philox_engine > >>+#include <bits/version.h> > >>+ > >>#include <cmath> > >>#include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t > >>#include <cstdlib> > >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > >>new file mode 100644 > >>index 00000000000..d5a8ca078ef > >>--- /dev/null > >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc > >>@@ -0,0 +1,23 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > > > >Please identify the document where these references are from, because > >29.5.4 might be a different subclause in C++29, i.e. > > > >>+// N5014 29.5.4 Random Number Engine Class Templates > >>+// N5014 29.5.4.5 Class Template philox_engine > > > >(Ideally we'd say "C++26 29.5.4 ..." but we don't know if that > >subclause number will be correct in the final standard.) > > > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::philox4x32 a; > >>+ a.discard(9999); > >>+ > >>+ VERIFY( a() == 1955073260 ); > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > >>new file mode 100644 > >>index 00000000000..131f094cb28 > >>--- /dev/null > >>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc > >>@@ -0,0 +1,23 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::philox4x64 a; > >>+ a.discard(9999); > >>+ > >>+ VERIFY( a() == 3409172418970261260 ); > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > >>new file mode 100644 > >>index 00000000000..c3a5a0eb754 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc > >>@@ -0,0 +1,39 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+int f(int x) > >>+{ > >>+ std::seed_seq sq(&x, &x + 1); > >>+ auto rnd = std::philox4x32(sq); > >>+ return std::uniform_int_distribution<int>()(rnd); > >>+} > >>+ > >>+int g(int x) > >>+{ > >>+ std::seed_seq sq(&x, &x + 1); > >>+ auto rnd = std::philox4x32(); > >>+ rnd.seed(sq); > >>+ return std::uniform_int_distribution<int>()(rnd); > >>+} > >>+ > >>+void test01() > >>+{ > >>+ const int f1 = f(0); > >>+ const int f2 = f(0); > >>+ > >>+ const int g1 = g(0); > >>+ const int g2 = g(0); > >>+ > >>+ VERIFY( f1 == f2 ); > >>+ VERIFY( g1 == g2 ); > >>+ VERIFY( f1 == g1 ); > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > >>new file mode 100644 > >>index 00000000000..4f61928a157 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc > >>@@ -0,0 +1,25 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+ > >>+void > >>+test01() > >>+{ > >>+ > >>+ std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); > >>+ > >>+ const auto f(e); > >>+ auto g(f); > >>+ g = g; // Suppress unused warning > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > >>new file mode 100644 > >>index 00000000000..9f9ae94db0f > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc > >>@@ -0,0 +1,27 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> philox4x32nullkey(0); > >>+ > >>+ VERIFY( philox4x32nullkey.min() == 0 ); > >>+ VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); > >>+ VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > >>new file mode 100644 > >>index 00000000000..5cb914f4b45 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc > >>@@ -0,0 +1,20 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+#include <random> > >>+ > >>+void > >>+test01() > >>+{ > >>+ unsigned long seed = 2; > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> philox4x32seeded(seed); > >>+} > >>+ > >>+int main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > >>new file mode 100644 > >>index 00000000000..7d9e3e6540d > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc > >>@@ -0,0 +1,24 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::seed_seq sseq{ 1, 2, 3, 4 }; > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> philox4x32sseq(sseq); > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > >>new file mode 100644 > >>index 00000000000..4f62bfbbd88 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc > >>@@ -0,0 +1,30 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> x, y; > >>+ > >>+ VERIFY ( x == y); > >>+ x.discard(100); > >>+ y.discard(100); > >>+ > >>+ VERIFY (x == y); > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > >>new file mode 100644 > >>index 00000000000..86d757db904 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc > >>@@ -0,0 +1,30 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> x, y; > >>+ > >>+ VERIFY ( !(x != y) ); > >>+ x.discard(100); > >>+ y.discard(100); > >>+ > >>+ VERIFY ( !(x != y) ); > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > >>new file mode 100644 > >>index 00000000000..bec4b172512 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc > >>@@ -0,0 +1,49 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <sstream> > >>+#include <random> > >>+#include <testsuite_hooks.h> > >>+#include <iostream> > >>+ > >>+void > >>+test01() > >>+{ > >>+ std::stringstream str; > >>+ std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> x, y; > >>+ > >>+ x(); > >>+ str << x; > >>+ > >>+ VERIFY ( !(x == y) ); > >>+ str >> y; > >>+ VERIFY ( x == y ); > >>+ for (unsigned long i = 0; i < 100; ++i) > >>+ { > >>+ VERIFY (x() == y()); > >>+ } > >>+ str.clear(); > >>+ str << y; > >>+ x(); > >>+ x(); > >>+ x(); > >>+ str >> x; > >>+ VERIFY ( x == y ); > >>+ for (unsigned long i = 0; i < 1000; ++i) > >>+ { > >>+ VERIFY (x() == y()); > >>+ } > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > >>new file mode 100644 > >>index 00000000000..c242056e0a4 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc > >>@@ -0,0 +1,26 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+ > >>+void test01() > >>+{ > >>+ std::philox4x32 philox; > >>+ const void* p = &philox.word_size; > >>+ p = &philox.word_count; > >>+ p = &philox.round_count; > >>+ p = &philox.multipliers; > >>+ p = &philox.round_consts; > >>+ p = &philox.default_seed; > >>+ p = p; // Suppress unused warning. > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > >>new file mode 100644 > >>index 00000000000..5be0108c88c > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc > >>@@ -0,0 +1,50 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_common_types.h> > >>+ > >>+namespace __gnu_test > >>+{ > >>+ struct constexpr_member_data > >>+ { > >>+ template<typename _Ttesttype> > >>+ void > >>+ operator()() > >>+ { > >>+ struct _Concept > >>+ { > >>+ void __constraint() > >>+ { > >>+ constexpr auto v1 __attribute__((unused)) > >>+ = _Ttesttype::word_size; > >>+ constexpr auto v2 __attribute__((unused)) > >>+ = _Ttesttype::word_count; > >>+ constexpr auto v3 __attribute__((unused)) > >>+ = _Ttesttype::round_count; > >>+ constexpr auto v4 __attribute__((unused)) > >>+ = _Ttesttype::multipliers; > >>+ constexpr auto v5 __attribute__((unused)) > >>+ = _Ttesttype::round_consts; > >>+ constexpr auto v6 __attribute__((unused)) > >>+ = _Ttesttype::default_seed; > >>+ } > >>+ }; > >>+ > >>+ _Concept c; > >>+ c.__constraint(); > >>+ } > >>+ }; > >>+}; > >>+ > >>+int > >>+main() > >>+{ > >>+ __gnu_test::constexpr_member_data test; > >>+ typedef std::philox4x32 type; > >>+ test.operator()<type>(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > >>new file mode 100644 > >>index 00000000000..eb61d15568a > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc > >>@@ -0,0 +1,41 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+#include <random> > >>+#include <testsuite_common_types.h> > >>+ > >>+namespace __gnu_test > >>+{ > >>+ struct constexpr_member_functions > >>+ { > >>+ template<typename _Ttesttype> > >>+ void > >>+ operator()() > >>+ { > >>+ struct _Concept > >>+ { > >>+ void __constraint() > >>+ { > >>+ constexpr auto v1 __attribute__((unused)) > >>+ = _Ttesttype::min(); > >>+ constexpr auto v2 __attribute__((unused)) > >>+ = _Ttesttype::max(); > >>+ } > >>+ }; > >>+ _Concept c; > >>+ c.__constraint(); > >>+ } > >>+ }; > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ __gnu_test::constexpr_member_functions test; > >>+ typedef std::philox4x32 type; > >>+ test.operator()<type>(); > >>+ return 0; > >>+} > >>diff --git > a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >>new file mode 100644 > >>index 00000000000..b368ee74106 > >>--- /dev/null > >>+++ > b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc > >>@@ -0,0 +1,26 @@ > >>+// { dg-do run { target c++26 } } > >>+// { dg-require-cstdint "" } > >>+ > >>+// 29.5.4 Random Number Engine Class Templates > >>+// 29.5.4.5 Class Template philox_engine > >>+ > >>+ > >>+#include <random> > >>+ > >>+void > >>+test01() > >>+{ > >>+ typedef std::philox_engine<std::uint_fast32_t, > >>+ 32, 4, 10, 0xCD9E8D57, > >>+ 0x9E3779B9, 0xD2511F53, > >>+ 0xBB67AE85> testType; > >>+ > >>+ typedef testType::result_type result_type; > >>+} > >>+ > >>+int > >>+main() > >>+{ > >>+ test01(); > >>+ return 0; > >>+} > >>diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >>index 0afba654152..13a0b8947b0 100644 > >>--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >>+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc > >>@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> > aurng(urng); > >>auto x = std::generate_canonical<std::size_t, > >> std::numeric_limits<std::size_t>::digits>(urng); > >> > >>-// { dg-error "static assertion failed: template argument must be a > floating point type" "" { target *-*-* } 270 } > >>+// { dg-error "static assertion failed: template argument must be a > floating point type" "" { target *-*-* } 271 } > >> > >>-// { dg-error "static assertion failed: template argument must be a > floating point type" "" { target *-*-* } 3357 } > >>+// { dg-error "static assertion failed: template argument must be a > floating point type" "" { target *-*-* } 3548 } > >>-- > >>2.49.0 > >> > > > >
Hi, Of course, thank you for your comments! I apologize for missing them - things have gotten a little busy lately, and the chain slipped by. Thank you for the comment and style changes. I am currently drafting out a portable unsigned 128-bit type for detail on Patrick's request, which I'm currently testing out a draft of on my end. Currently the holdup is division and remainder (Knuth's Algorithm D), but initial testing seems promising for everything else. Of course, more testing is required, but I hope to get a prototype to him soon to look at for further considerations before integrating within libstdc++ and submitting a patch. As far as the unnecessary uses of ull, _M_i can be narrowed safely, and so can __j. I slipped _M_i and the type of the template argument in that latter definition - it not only can be narrowed down, but should be. Furthermore, yes, __k should be size_t. Your next question on seed(_Sseq& __q), the types of __p, __k, and __j can be narrowed, but __precalc and __multiplicand must remain the same in order to support the 64-bit variant of the engine. If I missed one of your points of concern or if you have further questions regarding the portable variant of the type, please let me know and I'd be happy to answer. Regards, 1nfocalypse On Tuesday, September 23rd, 2025 at 10:07 AM, Jonathan Wakely <jwakely.gcc@gmail.com> wrote: > I've pushed your philox implementation to trunk now, thanks very much for adding it to GCC! > > I also pushed some minor refactoring, and would still like to address the (unnecessary?) uses of 64-bit types pointed out in the mail below. And we need to make it use uint64_t for the calculations when the result type only has 32-bits, and eventually emulate 128-bit arithmetic to support 64-bit result types on targets without int128. But they can all be done later. > > On Mon, 8 Sept 2025, 16:46 Jonathan Wakely, <jwakely@redhat.com> wrote: > >> On Mon, 08 Sep 2025 at 15:55 +0100, Jonathan Wakely wrote: >>>On Tue, 05 Aug 2025 at 09:21 +0000, 1nfocalypse wrote: >>>>Implements Philox Engine (P2075R6) and associated tests. >>>> >>>>Implements additional feedback from v5 from Patrick Palka. >>>>Also cut some trailing whitespace from the limb propagation >>>>fix in v5. >>>> >>>>Apologies for the delay from v5 - I had to finish writing some >>>>CTF challenges for DEFCON this week. Thank you for the >>>>feedback, and please let me know if anything further needs >>>>to be changed. >>>> >>>>Built/tested on x86_64-pc-linux-gnu. >>> >>>>From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 >>>>From: 1nfocalypse <1nfocalypse@protonmail.com> >>>>Date: Tue, 5 Aug 2025 01:37:18 +0000 >>>>Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) >>>> >>>>Implemented additional changes from Patrick Palka. >>>>Also removed some trailing whitespace in random.tcc. >>>> >>>>replaced alias of __ios_base with ios_base >>>>included <iomanip> in random.h >>>>altered line number in pr60037-neg.cc to be accurate >>>>Refactored iterator/index loops to be range-based for >>>>in istream/ostream operator overloads >>>>include <random> in philox_engine/cons/seed_seq.cc >>>>Conforms with errata LWG4143, LWG4153 for Philox Engine. >>>> >>>> PR libstdc++/119794 >>>>--- >>>>libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ >>>>libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ >>>>libstdc++-v3/include/bits/version.def | 9 + >>>>libstdc++-v3/include/bits/version.h | 10 + >>>>libstdc++-v3/include/std/random | 3 + >>>>.../26_numerics/random/philox4x32.cc | 23 ++ >>>>.../26_numerics/random/philox4x64.cc | 23 ++ >>>>.../random/philox_engine/cons/119794.cc | 39 +++ >>>>.../random/philox_engine/cons/copy.cc | 25 ++ >>>>.../random/philox_engine/cons/default.cc | 27 ++ >>>>.../random/philox_engine/cons/seed.cc | 20 ++ >>>>.../random/philox_engine/cons/seed_seq.cc | 24 ++ >>>>.../random/philox_engine/operators/equal.cc | 30 ++ >>>>.../random/philox_engine/operators/inequal.cc | 30 ++ >>>>.../philox_engine/operators/serialize.cc | 49 +++ >>>>.../philox_engine/requirements/constants.cc | 26 ++ >>>>.../requirements/constexpr_data.cc | 50 ++++ >>>>.../requirements/constexpr_functions.cc | 41 +++ >>>>.../philox_engine/requirements/typedefs.cc | 26 ++ >>>>.../26_numerics/random/pr60037-neg.cc | 4 +- >>>>20 files changed, 931 insertions(+), 2 deletions(-) >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>>>create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >>>> >>>>diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h >>>>index 1fdaf51934f..e74b7bd75c0 100644 >>>>--- a/libstdc++-v3/include/bits/random.h >>>>+++ b/libstdc++-v3/include/bits/random.h >>>>@@ -33,6 +33,7 @@ >>>> >>>>#include <vector> >>>>#include <bits/uniform_int_dist.h> >>>>+#include <iomanip> >>>> >>>>namespace std _GLIBCXX_VISIBILITY(default) >>>>{ >>>>@@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>>> { return !(__lhs == __rhs); } >>>>#endif >>>> >>>>+#if __cpp_lib_philox_engine >>> >>>This should check __glibcxx_philox_engine (just in case this code is >>>moved to a separate header that ends up being included somewhere >>>withou all of <random>). >>> >>>>+ /** >>>>+ * @brief: A discrete pseudorandom number generator based off of weakened >>>>+ * cryptographic primitives. >>> >>>Can we say "based on" instead of "based off of" which is an informal >>>American English term that sounds wrong to me. >>> >>>I'm also not sure what this is telling me as a non-export user. Would >>>it be clearer to talk about it having weak cryptographic properties, >>>rather than being based on weakened cryptographic primitives? >>> >>>>+ * >>>>+ * This algorithm was intended to be used for highly parallel random number >>> >>>"was intended to be used" sounds like it refers to something in the >>>past that is no longer true. Maybe "was designed to be used" instead? >>> >>>>+ * generation, and is capable of immensely long periods. It provides "Crush- >>> >>>I was going to ask whether immensely is a bit hyperbolic and could be >>>just "very long periods (e.g. ...)" but since we're talking at least >>>2^128 I think "immensely" is fine :-) >>> >>>>+ * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" >>>>+ * test, demonstrating significant apparent entropy. It is not intended for >>>>+ * cryptographic use and should not be used for such, despite being based on >>>>+ * cryptographic primitives. >>>>+ * >>>>+ * The two four-word definitions are likely the best use for this algorithm, >>>>+ * and are given below as defaults. >>> >>>Can we refer to those as aliases or typedefs rather than definitions? >>>Or specializations? >>> >>>Maybe even say it like this: >>> >>> * The typedefs `philox4x32` and `philox4x64` are provided as >>> * suitable defaults for most use cases, providing high-quality >>> * random numbers at reasonable performance. >>> >>> >>>>+ * >>>>+ * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and >>>>+ * David Shaw as a product of D.E. Shaw Research. >>>>+ * >>>>+ * @tparam __w Word size >>>>+ * @tparam __n Buffer size >>>>+ * @tparam __r Rounds >>>>+ * @tparam __consts Multiplication and round constant pack, ordered as >>>>+ * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} >>>>+ * >>>>+ * @headerfile random >>>>+ * @since C++26 >>>>+ */ >>>>+ template<class _UIntType, size_t __w, >>> >>>Our convention in libstdc++ code is to type 'typename' for template >>>parameters instead of 'class' >>> >>>>+ size_t __n, size_t __r, >>>>+ _UIntType... __consts> >>>>+ class philox_engine >>>>+ { >>>>+ static_assert(__n == 2 || __n == 4, >>>>+ "template argument N must be either 2 or 4"); >>>>+ static_assert(sizeof...(__consts) == __n, >>>>+ "length of consts array must match specified N"); >>>>+ static_assert(0 < __r, "a number of rounds must be specified"); >>>>+ static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), >>>>+ "specified bitlength must match input type"); >>> >>>The static assert messages all use the preferred "must" form, thanks! >>> >>>>+ template<typename _Sseq> >>>>+ using _If_seed_seq >>>>+ = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; >>>>+ >>>>+ private: >>> >>>This access specifier is redundant because everything above it is >>>already private. >>> >>>>+ // the ordering here is essential to functionality. >>> >>>Does this refer to the ordering of the _S_popArray member in the >>>class, because it needs to be declared before it's used? >>> >>>If so, I think we can drop the comment. If somebody fails to notice >>>that when reading the class definition, they'd find out pretty quickly >>>if they tried to reorder it and got errors. >>> >>>The style recommendations to put private internal members last is not >>>a hard requirement, for cases like this where the order is significant >>>putting them first is obviously fine (since you have no choice! :-) >>> >>>>+ /** @brief an internal unpacking function for %philox_engine. */ >>> >>>We don't need a doxygen comment for private member functions, they >>>won't get included in the docs anyway. >>> >>>>+ template <size_t __ind0, size_t __ind1> >>>>+ static constexpr >>>>+ array<_UIntType, __n / 2> >>>>+ _S_popArray() >>>>+ { >>>>+ if constexpr (__n == 4) >>>>+ return {__consts...[__ind0], __consts...[__ind1]}; >>>>+ else >>>>+ return {__consts...[__ind0]}; >>>>+ } >>>>+ >>>>+ public: >>>>+ /** Type of template param. */ >>>>+ using result_type = _UIntType; >>>>+ // public members >>>>+ static constexpr size_t word_size = __w; >>>>+ static constexpr size_t word_count = __n; >>>>+ static constexpr size_t round_count = __r; >>>>+ static constexpr array<result_type, __n / 2> multipliers = >>>>+ philox_engine::_S_popArray<0,2>(); >>>>+ static constexpr array<result_type, __n / 2> round_consts = >>>>+ philox_engine::_S_popArray<1,3>(); >>>>+ >>>>+ /** @brief returns the minimum value possible. */ >>>>+ static constexpr result_type >>>>+ min() >>>>+ { return 0; } >>>>+ >>>>+ /** @brief returns the maximum value possible. */ >>>>+ static constexpr result_type >>>>+ max() >>>>+ { >>>>+ return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); >>>>+ } >>>>+ // default key value >>>>+ static constexpr result_type default_seed = 20111115u; >>> >>>I wonder if we have a defect in the C++26 draft here, because >>>20111115u won't fit in a 16-bit int. We fixed that for >>>std::subtract_with_carry_engine via: >>>https://cplusplus.github.io/LWG/issue3809 >>>and then fixed what I broke via: >>>https://cplusplus.github.io/LWG/issue4014 >>> >>>Since the value of default_seed is only used as the argument to the >>>default ctor (or the equivalent of calling seed() with no argument) >>>and the ctor reduces value by mod 2^w, I think we could define the >>>default seed as 20111115u & max(). >>> >>>But let's leave it for now, I'll raise this with LWG. >>> >>>>+ >>>>+ // constructors >>>>+ philox_engine() >>>>+ : philox_engine(default_seed) >>>>+ {} >>>>+ >>>>+ explicit >>>>+ philox_engine(result_type __value); >>>>+ >>>>+ /** @brief seed sequence constructor for %philox_engine >>>>+ * >>>>+ * @params __q the seed sequence >>>>+ */ >>>>+ template<typename _Sseq, typename = _If_seed_seq<_Sseq>> >>>>+ explicit >>>>+ philox_engine(_Sseq& __q) >>>>+ { >>>>+ seed(__q); >>>>+ } >>>>+ >>>>+ void >>>>+ seed(result_type value = default_seed); >>>>+ >>>>+ /** @brief seeds %philox_engine by seed sequence >>>>+ * >>>>+ * @params __q the seed sequence >>>>+ */ >>>>+ template<typename _Sseq> >>>>+ _If_seed_seq<_Sseq> >>>>+ seed(_Sseq& __q); >>>>+ >>>>+ /** @brief sets the internal counter "cleartext" >>>>+ * >>>>+ * @params __counter std::array of len N >>>>+ */ >>>>+ void >>>>+ set_counter(const array<result_type, __n>& __counter); >>>>+ >>>>+ /** @brief compares two %philox_engine objects >>>>+ * >>>>+ * @params __x A %philox_engine object >>>>+ * @params __y A %philox_engine object >>>>+ * >>>>+ * @returns true if the objects will produce an identical stream, false >>>>+ * otherwise >>>>+ */ >>>>+ friend bool >>>>+ operator==(const philox_engine& __x, const philox_engine& __y) >>>>+ { >>>>+ return (std::equal(__x._M_x.begin(), __x._M_x.end(), >>>>+ __y._M_x.begin(), __y._M_x.end()) >>>>+ && std::equal(__x._M_y.begin(), __x._M_y.end(), >>>>+ __y._M_y.begin(), __y._M_y.end()) >>>>+ && std::equal(__x._M_k.begin(), __x._M_k.end(), >>>>+ __y._M_k.begin(), __y._M_k.end()) >>>>+ && __x._M_i == __y._M_i); >>>>+ } >>>>+ >>>>+ /** @brief outputs a single w-bit number and handles state advancement >>>>+ * >>>>+ * @returns return_type >>>>+ */ >>>>+ _UIntType >>>>+ operator()(); >>>>+ >>>>+ /** @brief discards __z numbers >>>>+ * >>>>+ * @params __z number of iterations to discard >>>>+ */ >>>>+ void >>>>+ discard(unsigned long long __z); >>>>+ >>>>+ /** @brief outputs the state of the generator >>>>+ * >>>>+ * @param __os An output stream. >>>>+ * @param __x A %philox_engine object reference >>>>+ * >>>>+ * @returns the state of the Philox Engine in __os >>>>+ */ >>>>+ template<typename _CharT, typename _Traits> >>>>+ friend basic_ostream<_CharT, _Traits>& >>>>+ operator<<(basic_ostream<_CharT, _Traits>& __os, >>>>+ const philox_engine& __x) >>>>+ { >>> >>>The "operator<<" and "{" lines should be lined up with "friend" >>> >>>>+ const typename ios_base::fmtflags __flags = __os.flags(); >>>>+ const _CharT __fill = __os.fill(); >>>>+ __os.flags(ios_base::dec | ios_base::left); >>>>+ __os.fill(__os.widen(' ')); >>>>+ for (auto &__subkey : __x._M_k) >>> >>>auto& rather than putting the space before the & please (and below as >>>well). >>> >>>>+ __os << __subkey << ' '; >>>>+ for (auto &__ctr : __x._M_x) >>>>+ __os << __ctr << ' '; >>> >>>Inserting ' ' will get widened if __os is a wide stream, but since >>>you've already called __os.widen(' ') above you could save the result >>>of that in a local variable and then insert that character instead of >>>' '. That will avoid calling widen again on every iteration of the >>>loop. >>> >>>>+ __os << __x._M_i; >>>>+ __os.flags(__flags); >>> >>>If writing to the stream throws an exception we won't restore the >>>flags and fill character, but I think that's consistent with all our >>>operator<< overloads in <random>. >>> >>>>+ __os.fill(__fill); >>>>+ return __os; >>>>+ } >>>>+ >>>>+ /** @brief takes input to set the state of the %philox_engine object >>>>+ * >>>>+ * @param __is An input stream. >>>>+ * @param __x A %philox_engine object reference >>>>+ * >>>>+ * @returns %philox_engine object is set with values from instream >>>>+ */ >>>>+ template <typename _CharT, typename _Traits> >>>>+ friend basic_istream<_CharT, _Traits>& >>>>+ operator>>(basic_istream<_CharT, _Traits>& __is, >>>>+ philox_engine& __x) >>>>+ { >>> >>>Same comment about aligning to "friend". >>> >>>>+ const typename ios_base::fmtflags __flags = __is.flags(); >>>>+ __is.flags(ios_base::dec | ios_base::skipws); >>>>+ for (auto &__subkey : __x._M_k) >>>>+ __is >> __subkey; >>>>+ for (auto &__ctr : __x._M_x) >>>>+ __is >> __ctr; >>>>+ array<_UIntType, __n> __tmpCtr = __x._M_x; >>>>+ unsigned char __setIndex = 0; >>>>+ for (size_t __j = 0; __j < __x._M_x.size(); ++__j) >>>>+ { >>>>+ if (__x._M_x[__j] > 0) >>>>+ { >>>>+ __setIndex = __j; >>>>+ break; >>>>+ } >>>>+ } >>>>+ for (size_t __j = 0; __j <= __setIndex; ++__j) >>>>+ { >>>>+ if (__j != __setIndex) >>>>+ __x._M_x[__j] = max(); >>>>+ else >>>>+ --__x._M_x[__j]; >>>>+ } >>>>+ __x._M_philox(); >>>>+ __x._M_x = __tmpCtr; >>>>+ __is >> __x._M_i; >>>>+ __is.flags(__flags); >>>>+ return __is; >>>>+ } >>>>+ private: >>>>+ // private state variables >>>>+ array<_UIntType, __n> _M_x; >>>>+ array<_UIntType, __n / 2> _M_k; >>>>+ array<_UIntType, __n> _M_y; >>>>+ unsigned long long _M_i = 0; >> >> Why is this unsigned long long? The maximum value of i is 4, isn't it? >> >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ void >>>>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() >>>>+ { >>>>+ array<_UIntType, __n> __outputSeq{}; >>>>+ for (size_t __j = 0; __j < __n; ++__j) >>>>+ __outputSeq[__j] = _M_x[__j]; >>>>+ for (unsigned long __j = 0; __j < __r; ++__j) >> >> Should __j be size_t? >> >> Otherwise on Windows it would be 32-bit, but __r is size_t which is >> 64-bit, and so __j < __r might never be true. >> >>>>+ { >>>>+ array<_UIntType, __n> __intermedSeq{}; >>>>+ if constexpr (__n == 4) >>>>+ { >>>>+ __intermedSeq[0] = __outputSeq[2]; >>>>+ __intermedSeq[1] = __outputSeq[1]; >>>>+ __intermedSeq[2] = __outputSeq[0]; >>>>+ __intermedSeq[3] = __outputSeq[3]; >>>>+ } else >>>>+ { >>>>+ __intermedSeq[0] = __outputSeq[0]; >>>>+ __intermedSeq[1] = __outputSeq[1]; >>>>+ } >>>>+ for (unsigned long __k = 0; __k < (__n/2); ++__k) >> >> __k should be the same type as __n, although this loop never runs >> more than twice, right? >> >>>>+ { >>>>+ __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) >>>>+ ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) >>>>+ ^ __intermedSeq[2*__k+1]; >>>>+ >>>>+ __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], >>>>+ multipliers[__k]); >>>>+ } >>>>+ } >>>>+ for (unsigned long __j = 0; __j < __n; ++__j) >>>>+ _M_y[__j] = __outputSeq[__j]; >> >> __j should be the same type as __n, but can this be simply: >> >> _M_y = __outputSeq; >> >> ? >> >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ philox_engine<_UIntType, >>>>+ __w, __n, __r, __consts...>::philox_engine(result_type __value) >>>>+ { >>>>+ std::fill(_M_x.begin(), _M_x.end(), 0); >>>>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>>>+ std::fill(_M_y.begin(), _M_y.end(), 0); >>> >>>Can we just initialize these in a mem-initializer-list? >>> >>> : _M_x{}, _M_k{}, _M_y{} >>> >>>The compiler is more likely to optimize that well. >>> >>>>+ _M_k[0] = __value & max(); >>>>+ _M_i = __n - 1; >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ void >>>>+ philox_engine<_UIntType, >>>>+ __w, __n, __r, __consts...>::seed(result_type __value) >>>>+ { >>>>+ std::fill(_M_x.begin(), _M_x.end(), 0); >>> >>>This can use std::array::fill, i.e. _M_x.fill(0); >>> >>>Or would this whole function be simpler as: >>> >>> *this = philox_engine(__value); >>> >>>? >>> >>>>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>>>+ std::fill(_M_y.begin(), _M_y.end(), 0); >>>>+ _M_k[0] = __value & max(); >>>>+ _M_i = __n - 1; >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ void >>>>+ philox_engine<_UIntType, __w, >>>>+ __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) >>>>+ { >>>>+ for (unsigned long long __j = 0; __j < __n; ++__j) >> >> __j should be the same type as __n >> >>>>+ _M_x[__j] = __counter[__n - 1 - __j] & max(); >>>>+ _M_i = __n - 1; >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ template<typename _Sseq> >>>>+ auto >>>>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) >>>>+ -> _If_seed_seq<_Sseq> >>>>+ { >>>>+ std::fill(_M_k.begin(), _M_k.end(), 0); >>>>+ const unsigned long long __p = 1 + ((__w - 1)/ 32); >>>>+ uint_least32_t __tmpArr[(__n / 2) * __p]; >>>>+ __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); >>>>+ for (unsigned long long __k = 0; __k < (__n/2); ++__k) >>>>+ { >>>>+ unsigned long long __precalc = 0; >>>>+ for (unsigned long long __j = 0; __j < __p; ++__j) >>>>+ { >>>>+ unsigned long long __multiplicand = (1ull << (32 * __j)); >>>>+ __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); >>>>+ } >> >> Do any of the variables above need to be unsigned long long? >> >>>>+ _M_k[__k] = __precalc; >>>>+ } >>>>+ std::fill(_M_x.begin(), _M_x.end(), 0); >>>>+ std::fill(_M_y.begin(), _M_y.end(), 0); >>>>+ _M_i = __n - 1; >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ void >>>>+ philox_engine<_UIntType, >>>>+ __w, __n, __r, __consts...>::discard(unsigned long long __z) >>>>+ { >>>>+ for (unsigned long long __j = 0; __j < __z; ++__j) >>>>+ _M_transition(); >>>>+ } >>>>+ >>>>+ template<class _UIntType, >>>>+ size_t __w, size_t __n, >>>>+ size_t __r, _UIntType... __consts> >>>>+ _UIntType >>>>+ philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() >>>>+ { >>>>+ _M_transition(); >>>>+ return _M_y[_M_i]; >>>>+ } >>>>+ >>>>+#endif >>>> >>>> template<typename _IntType, typename _CharT, typename _Traits> >>>> std::basic_ostream<_CharT, _Traits>& >>>>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def >>>>index dbe2cb8f175..4c21fa187c8 100644 >>>>--- a/libstdc++-v3/include/bits/version.def >>>>+++ b/libstdc++-v3/include/bits/version.def >>>>@@ -2069,6 +2069,15 @@ ftms = { >>>> }; >>>>}; >>>> >>>>+ftms = { >>>>+ name = philox_engine; >>>>+ values = { >>>>+ v = 202406; >>>>+ cxxmin = 26; >>>>+ extra_cond = "__SIZEOF_INT128__"; >>>>+ }; >>>>+}; >>>>+ >>>>// Standard test specifications. >>>>stds[97] = ">= 199711L"; >>>>stds[03] = ">= 199711L"; >>>>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h >>>>index 7bb6016df68..75a9946adce 100644 >>>>--- a/libstdc++-v3/include/bits/version.h >>>>+++ b/libstdc++-v3/include/bits/version.h >>>>@@ -2318,4 +2318,14 @@ >>>>#endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ >>>>#undef __glibcxx_want_constexpr_exceptions >>>> >>>>+#if !defined(__cpp_lib_philox_engine) >>>>+# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) >>>>+# define __glibcxx_philox_engine 202406L >>>>+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) >>>>+# define __cpp_lib_philox_engine 202406L >>>>+# endif >>>>+# endif >>>>+#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ >>>>+#undef __glibcxx_want_philox_engine >>>>+ >>>>#undef __glibcxx_want_all >>>>diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random >>>>index 0e058a04bd9..8a02ade4b75 100644 >>>>--- a/libstdc++-v3/include/std/random >>>>+++ b/libstdc++-v3/include/std/random >>>>@@ -39,6 +39,9 @@ >>>># include <bits/c++0x_warning.h> >>>>#else >>>> >>>>+#define __glibcxx_want_philox_engine >>>>+#include <bits/version.h> >>>>+ >>>>#include <cmath> >>>>#include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t >>>>#include <cstdlib> >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>>>new file mode 100644 >>>>index 00000000000..d5a8ca078ef >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc >>>>@@ -0,0 +1,23 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>> >>>Please identify the document where these references are from, because >>>29.5.4 might be a different subclause in C++29, i.e. >>> >>>>+// N5014 29.5.4 Random Number Engine Class Templates >>>>+// N5014 29.5.4.5 Class Template philox_engine >>> >>>(Ideally we'd say "C++26 29.5.4 ..." but we don't know if that >>>subclause number will be correct in the final standard.) >>> >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::philox4x32 a; >>>>+ a.discard(9999); >>>>+ >>>>+ VERIFY( a() == 1955073260 ); >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>>>new file mode 100644 >>>>index 00000000000..131f094cb28 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc >>>>@@ -0,0 +1,23 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::philox4x64 a; >>>>+ a.discard(9999); >>>>+ >>>>+ VERIFY( a() == 3409172418970261260 ); >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>>>new file mode 100644 >>>>index 00000000000..c3a5a0eb754 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc >>>>@@ -0,0 +1,39 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+int f(int x) >>>>+{ >>>>+ std::seed_seq sq(&x, &x + 1); >>>>+ auto rnd = std::philox4x32(sq); >>>>+ return std::uniform_int_distribution<int>()(rnd); >>>>+} >>>>+ >>>>+int g(int x) >>>>+{ >>>>+ std::seed_seq sq(&x, &x + 1); >>>>+ auto rnd = std::philox4x32(); >>>>+ rnd.seed(sq); >>>>+ return std::uniform_int_distribution<int>()(rnd); >>>>+} >>>>+ >>>>+void test01() >>>>+{ >>>>+ const int f1 = f(0); >>>>+ const int f2 = f(0); >>>>+ >>>>+ const int g1 = g(0); >>>>+ const int g2 = g(0); >>>>+ >>>>+ VERIFY( f1 == f2 ); >>>>+ VERIFY( g1 == g2 ); >>>>+ VERIFY( f1 == g1 ); >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>>>new file mode 100644 >>>>index 00000000000..4f61928a157 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc >>>>@@ -0,0 +1,25 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ >>>>+ std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); >>>>+ >>>>+ const auto f(e); >>>>+ auto g(f); >>>>+ g = g; // Suppress unused warning >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>>>new file mode 100644 >>>>index 00000000000..9f9ae94db0f >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc >>>>@@ -0,0 +1,27 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> philox4x32nullkey(0); >>>>+ >>>>+ VERIFY( philox4x32nullkey.min() == 0 ); >>>>+ VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); >>>>+ VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>>>new file mode 100644 >>>>index 00000000000..5cb914f4b45 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc >>>>@@ -0,0 +1,20 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+#include <random> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ unsigned long seed = 2; >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> philox4x32seeded(seed); >>>>+} >>>>+ >>>>+int main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>>>new file mode 100644 >>>>index 00000000000..7d9e3e6540d >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc >>>>@@ -0,0 +1,24 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::seed_seq sseq{ 1, 2, 3, 4 }; >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> philox4x32sseq(sseq); >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>>>new file mode 100644 >>>>index 00000000000..4f62bfbbd88 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc >>>>@@ -0,0 +1,30 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> x, y; >>>>+ >>>>+ VERIFY ( x == y); >>>>+ x.discard(100); >>>>+ y.discard(100); >>>>+ >>>>+ VERIFY (x == y); >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>>>new file mode 100644 >>>>index 00000000000..86d757db904 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc >>>>@@ -0,0 +1,30 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> x, y; >>>>+ >>>>+ VERIFY ( !(x != y) ); >>>>+ x.discard(100); >>>>+ y.discard(100); >>>>+ >>>>+ VERIFY ( !(x != y) ); >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>>>new file mode 100644 >>>>index 00000000000..bec4b172512 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc >>>>@@ -0,0 +1,49 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <sstream> >>>>+#include <random> >>>>+#include <testsuite_hooks.h> >>>>+#include <iostream> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ std::stringstream str; >>>>+ std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> x, y; >>>>+ >>>>+ x(); >>>>+ str << x; >>>>+ >>>>+ VERIFY ( !(x == y) ); >>>>+ str >> y; >>>>+ VERIFY ( x == y ); >>>>+ for (unsigned long i = 0; i < 100; ++i) >>>>+ { >>>>+ VERIFY (x() == y()); >>>>+ } >>>>+ str.clear(); >>>>+ str << y; >>>>+ x(); >>>>+ x(); >>>>+ x(); >>>>+ str >> x; >>>>+ VERIFY ( x == y ); >>>>+ for (unsigned long i = 0; i < 1000; ++i) >>>>+ { >>>>+ VERIFY (x() == y()); >>>>+ } >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>>>new file mode 100644 >>>>index 00000000000..c242056e0a4 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc >>>>@@ -0,0 +1,26 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+ >>>>+void test01() >>>>+{ >>>>+ std::philox4x32 philox; >>>>+ const void* p = &philox.word_size; >>>>+ p = &philox.word_count; >>>>+ p = &philox.round_count; >>>>+ p = &philox.multipliers; >>>>+ p = &philox.round_consts; >>>>+ p = &philox.default_seed; >>>>+ p = p; // Suppress unused warning. >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>>>new file mode 100644 >>>>index 00000000000..5be0108c88c >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc >>>>@@ -0,0 +1,50 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_common_types.h> >>>>+ >>>>+namespace __gnu_test >>>>+{ >>>>+ struct constexpr_member_data >>>>+ { >>>>+ template<typename _Ttesttype> >>>>+ void >>>>+ operator()() >>>>+ { >>>>+ struct _Concept >>>>+ { >>>>+ void __constraint() >>>>+ { >>>>+ constexpr auto v1 __attribute__((unused)) >>>>+ = _Ttesttype::word_size; >>>>+ constexpr auto v2 __attribute__((unused)) >>>>+ = _Ttesttype::word_count; >>>>+ constexpr auto v3 __attribute__((unused)) >>>>+ = _Ttesttype::round_count; >>>>+ constexpr auto v4 __attribute__((unused)) >>>>+ = _Ttesttype::multipliers; >>>>+ constexpr auto v5 __attribute__((unused)) >>>>+ = _Ttesttype::round_consts; >>>>+ constexpr auto v6 __attribute__((unused)) >>>>+ = _Ttesttype::default_seed; >>>>+ } >>>>+ }; >>>>+ >>>>+ _Concept c; >>>>+ c.__constraint(); >>>>+ } >>>>+ }; >>>>+}; >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ __gnu_test::constexpr_member_data test; >>>>+ typedef std::philox4x32 type; >>>>+ test.operator()<type>(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>>>new file mode 100644 >>>>index 00000000000..eb61d15568a >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc >>>>@@ -0,0 +1,41 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+#include <random> >>>>+#include <testsuite_common_types.h> >>>>+ >>>>+namespace __gnu_test >>>>+{ >>>>+ struct constexpr_member_functions >>>>+ { >>>>+ template<typename _Ttesttype> >>>>+ void >>>>+ operator()() >>>>+ { >>>>+ struct _Concept >>>>+ { >>>>+ void __constraint() >>>>+ { >>>>+ constexpr auto v1 __attribute__((unused)) >>>>+ = _Ttesttype::min(); >>>>+ constexpr auto v2 __attribute__((unused)) >>>>+ = _Ttesttype::max(); >>>>+ } >>>>+ }; >>>>+ _Concept c; >>>>+ c.__constraint(); >>>>+ } >>>>+ }; >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ __gnu_test::constexpr_member_functions test; >>>>+ typedef std::philox4x32 type; >>>>+ test.operator()<type>(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >>>>new file mode 100644 >>>>index 00000000000..b368ee74106 >>>>--- /dev/null >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc >>>>@@ -0,0 +1,26 @@ >>>>+// { dg-do run { target c++26 } } >>>>+// { dg-require-cstdint "" } >>>>+ >>>>+// 29.5.4 Random Number Engine Class Templates >>>>+// 29.5.4.5 Class Template philox_engine >>>>+ >>>>+ >>>>+#include <random> >>>>+ >>>>+void >>>>+test01() >>>>+{ >>>>+ typedef std::philox_engine<std::uint_fast32_t, >>>>+ 32, 4, 10, 0xCD9E8D57, >>>>+ 0x9E3779B9, 0xD2511F53, >>>>+ 0xBB67AE85> testType; >>>>+ >>>>+ typedef testType::result_type result_type; >>>>+} >>>>+ >>>>+int >>>>+main() >>>>+{ >>>>+ test01(); >>>>+ return 0; >>>>+} >>>>diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>>>index 0afba654152..13a0b8947b0 100644 >>>>--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>>>+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc >>>>@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); >>>>auto x = std::generate_canonical<std::size_t, >>>> std::numeric_limits<std::size_t>::digits>(urng); >>>> >>>>-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } >>>>+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } >>>> >>>>-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } >>>>+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } >>>>-- >>>>2.49.0 >>>> >>>
From 23ffd0ca595c27e8721d6d73ac746293aace7771 Mon Sep 17 00:00:00 2001 From: 1nfocalypse <1nfocalypse@protonmail.com> Date: Tue, 5 Aug 2025 01:37:18 +0000 Subject: [PATCH] [PATCH v6] libstdc++: Implement Philox Engine (PR119794) Implemented additional changes from Patrick Palka. Also removed some trailing whitespace in random.tcc. replaced alias of __ios_base with ios_base included <iomanip> in random.h altered line number in pr60037-neg.cc to be accurate Refactored iterator/index loops to be range-based for in istream/ostream operator overloads include <random> in philox_engine/cons/seed_seq.cc Conforms with errata LWG4143, LWG4153 for Philox Engine. PR libstdc++/119794 --- libstdc++-v3/include/bits/random.h | 283 ++++++++++++++++++ libstdc++-v3/include/bits/random.tcc | 191 ++++++++++++ libstdc++-v3/include/bits/version.def | 9 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/random | 3 + .../26_numerics/random/philox4x32.cc | 23 ++ .../26_numerics/random/philox4x64.cc | 23 ++ .../random/philox_engine/cons/119794.cc | 39 +++ .../random/philox_engine/cons/copy.cc | 25 ++ .../random/philox_engine/cons/default.cc | 27 ++ .../random/philox_engine/cons/seed.cc | 20 ++ .../random/philox_engine/cons/seed_seq.cc | 24 ++ .../random/philox_engine/operators/equal.cc | 30 ++ .../random/philox_engine/operators/inequal.cc | 30 ++ .../philox_engine/operators/serialize.cc | 49 +++ .../philox_engine/requirements/constants.cc | 26 ++ .../requirements/constexpr_data.cc | 50 ++++ .../requirements/constexpr_functions.cc | 41 +++ .../philox_engine/requirements/typedefs.cc | 26 ++ .../26_numerics/random/pr60037-neg.cc | 4 +- 20 files changed, 931 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h index 1fdaf51934f..e74b7bd75c0 100644 --- a/libstdc++-v3/include/bits/random.h +++ b/libstdc++-v3/include/bits/random.h @@ -33,6 +33,7 @@ #include <vector> #include <bits/uniform_int_dist.h> +#include <iomanip> namespace std _GLIBCXX_VISIBILITY(default) { @@ -1688,6 +1689,270 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !(__lhs == __rhs); } #endif +#if __cpp_lib_philox_engine + + /** + * @brief: A discrete pseudorandom number generator based off of weakened + * cryptographic primitives. + * + * This algorithm was intended to be used for highly parallel random number + * generation, and is capable of immensely long periods. It provides "Crush- + * resistance", denoting an ability to pass the TestU01 Suite's "Big Crush" + * test, demonstrating significant apparent entropy. It is not intended for + * cryptographic use and should not be used for such, despite being based on + * cryptographic primitives. + * + * The two four-word definitions are likely the best use for this algorithm, + * and are given below as defaults. + * + * This algorithm was created by John Salmon, Mark Moraes, Ron Dror, and + * David Shaw as a product of D.E. Shaw Research. + * + * @tparam __w Word size + * @tparam __n Buffer size + * @tparam __r Rounds + * @tparam __consts Multiplication and round constant pack, ordered as + * M_{0}, C_{0}, M_{1}, C_{1}, ... , M_{N/2-1}, C_{N/2-1} + * + * @headerfile random + * @since C++26 + */ + template<class _UIntType, size_t __w, + size_t __n, size_t __r, + _UIntType... __consts> + class philox_engine + { + static_assert(__n == 2 || __n == 4, + "template argument N must be either 2 or 4"); + static_assert(sizeof...(__consts) == __n, + "length of consts array must match specified N"); + static_assert(0 < __r, "a number of rounds must be specified"); + static_assert((0 < __w && __w <= numeric_limits<_UIntType>::digits), + "specified bitlength must match input type"); + template<typename _Sseq> + using _If_seed_seq + = __detail::_If_seed_seq_for<_Sseq, philox_engine, _UIntType>; + + private: + // the ordering here is essential to functionality. + /** @brief an internal unpacking function for %philox_engine. */ + template <size_t __ind0, size_t __ind1> + static constexpr + array<_UIntType, __n / 2> + _S_popArray() + { + if constexpr (__n == 4) + return {__consts...[__ind0], __consts...[__ind1]}; + else + return {__consts...[__ind0]}; + } + + public: + /** Type of template param. */ + using result_type = _UIntType; + // public members + static constexpr size_t word_size = __w; + static constexpr size_t word_count = __n; + static constexpr size_t round_count = __r; + static constexpr array<result_type, __n / 2> multipliers = + philox_engine::_S_popArray<0,2>(); + static constexpr array<result_type, __n / 2> round_consts = + philox_engine::_S_popArray<1,3>(); + + /** @brief returns the minimum value possible. */ + static constexpr result_type + min() + { return 0; } + + /** @brief returns the maximum value possible. */ + static constexpr result_type + max() + { + return ((1ull << (__w - 1)) | ((1ull << (__w - 1)) - 1)); + } + // default key value + static constexpr result_type default_seed = 20111115u; + + // constructors + philox_engine() + : philox_engine(default_seed) + {} + + explicit + philox_engine(result_type __value); + + /** @brief seed sequence constructor for %philox_engine + * + * @params __q the seed sequence + */ + template<typename _Sseq, typename = _If_seed_seq<_Sseq>> + explicit + philox_engine(_Sseq& __q) + { + seed(__q); + } + + void + seed(result_type value = default_seed); + + /** @brief seeds %philox_engine by seed sequence + * + * @params __q the seed sequence + */ + template<typename _Sseq> + _If_seed_seq<_Sseq> + seed(_Sseq& __q); + + /** @brief sets the internal counter "cleartext" + * + * @params __counter std::array of len N + */ + void + set_counter(const array<result_type, __n>& __counter); + + /** @brief compares two %philox_engine objects + * + * @params __x A %philox_engine object + * @params __y A %philox_engine object + * + * @returns true if the objects will produce an identical stream, false + * otherwise + */ + friend bool + operator==(const philox_engine& __x, const philox_engine& __y) + { + return (std::equal(__x._M_x.begin(), __x._M_x.end(), + __y._M_x.begin(), __y._M_x.end()) + && std::equal(__x._M_y.begin(), __x._M_y.end(), + __y._M_y.begin(), __y._M_y.end()) + && std::equal(__x._M_k.begin(), __x._M_k.end(), + __y._M_k.begin(), __y._M_k.end()) + && __x._M_i == __y._M_i); + } + + /** @brief outputs a single w-bit number and handles state advancement + * + * @returns return_type + */ + _UIntType + operator()(); + + /** @brief discards __z numbers + * + * @params __z number of iterations to discard + */ + void + discard(unsigned long long __z); + + /** @brief outputs the state of the generator + * + * @param __os An output stream. + * @param __x A %philox_engine object reference + * + * @returns the state of the Philox Engine in __os + */ + template<typename _CharT, typename _Traits> + friend basic_ostream<_CharT, _Traits>& + operator<<(basic_ostream<_CharT, _Traits>& __os, + const philox_engine& __x) + { + const typename ios_base::fmtflags __flags = __os.flags(); + const _CharT __fill = __os.fill(); + __os.flags(ios_base::dec | ios_base::left); + __os.fill(__os.widen(' ')); + for (auto &__subkey : __x._M_k) + __os << __subkey << ' '; + for (auto &__ctr : __x._M_x) + __os << __ctr << ' '; + __os << __x._M_i; + __os.flags(__flags); + __os.fill(__fill); + return __os; + } + + /** @brief takes input to set the state of the %philox_engine object + * + * @param __is An input stream. + * @param __x A %philox_engine object reference + * + * @returns %philox_engine object is set with values from instream + */ + template <typename _CharT, typename _Traits> + friend basic_istream<_CharT, _Traits>& + operator>>(basic_istream<_CharT, _Traits>& __is, + philox_engine& __x) + { + const typename ios_base::fmtflags __flags = __is.flags(); + __is.flags(ios_base::dec | ios_base::skipws); + for (auto &__subkey : __x._M_k) + __is >> __subkey; + for (auto &__ctr : __x._M_x) + __is >> __ctr; + array<_UIntType, __n> __tmpCtr = __x._M_x; + unsigned char __setIndex = 0; + for (size_t __j = 0; __j < __x._M_x.size(); ++__j) + { + if (__x._M_x[__j] > 0) + { + __setIndex = __j; + break; + } + } + for (size_t __j = 0; __j <= __setIndex; ++__j) + { + if (__j != __setIndex) + __x._M_x[__j] = max(); + else + --__x._M_x[__j]; + } + __x._M_philox(); + __x._M_x = __tmpCtr; + __is >> __x._M_i; + __is.flags(__flags); + return __is; + } + private: + // private state variables + array<_UIntType, __n> _M_x; + array<_UIntType, __n / 2> _M_k; + array<_UIntType, __n> _M_y; + unsigned long long _M_i = 0; + + /** @brief Takes the high values of the product of __a, __b + * + * @params __a an unsigned integer + * @params __b an unsigned integer + * + * @returns an unsigned integer of at most bitlength W + */ + static + _UIntType + _S_mulhi(_UIntType __a, _UIntType __b); // (A*B)/2^W + + /** @brief Takes the low values of the product of __a, __b + * + * @params __a an unsigned integer + * @params __b an unsigned integer + * + * @returns an unsigned integer of at most bitlength W + */ + static + _UIntType + _S_mullo(_UIntType __a, _UIntType __b); // (A*B)%2^W + + /** @brief an R-round substitution/Feistel Network hybrid for + * %philox_engine + */ + void + _M_philox(); + + /** @brief an internal transition function for the %philox_engine. */ + void + _M_transition(); + }; + +#endif + /** * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller. */ @@ -1742,6 +2007,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef minstd_rand0 default_random_engine; +#if __cpp_lib_philox_engine + + typedef philox_engine< + uint_fast32_t, + 32, 4, 10, + 0xCD9E8D57, 0x9E3779B9, + 0xD2511F53, 0xBB67AE85> philox4x32; + + /** + * Alternative Philox instance (64 bit) + */ + typedef philox_engine< + uint_fast64_t, + 64, 4, 10, + 0xCA5A826395121157, 0x9E3779B97F4A7C15, + 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B> philox4x64; +#endif + /** * A standard interface to a platform-specific non-deterministic * random number generator (if any are available). diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc index 53ccacb2e38..53f302da800 100644 --- a/libstdc++-v3/include/bits/random.tcc +++ b/libstdc++-v3/include/bits/random.tcc @@ -907,6 +907,197 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __is; } +#if __cpp_lib_philox_engine + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + _UIntType + philox_engine<_UIntType, + __w, __n, __r, __consts...>::_S_mulhi(_UIntType __a, _UIntType __b) + { + const __uint128_t __num = + static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b); + return static_cast<_UIntType>((__num >> __w) & max()); + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + _UIntType + philox_engine<_UIntType, + __w, __n, __r, __consts...>::_S_mullo(_UIntType __a, _UIntType __b) + { + return static_cast<_UIntType>((__a * __b) & max()); + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + void + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_transition() + { + ++_M_i; + if (_M_i == __n) + { + _M_philox(); + if constexpr (__n == 4) + { + __uint128_t __uh = + (static_cast<__uint128_t>(_M_x[1]) << __w) + | (static_cast<__uint128_t>(_M_x[0]) + 1); + __uint128_t __lh = + ((static_cast<__uint128_t>(_M_x[3]) << __w) + | (_M_x[2])); + __uint128_t __bigMask = + (static_cast<__uint128_t>(1) << ((2 * __w) - 1)) + | ((static_cast<__uint128_t>(1) << ((2 * __w) - 1)) - 1); + if ((__uh & __bigMask) == 0) + { + ++__lh; + __uh = 0; + } + _M_x[0] = __uh & max(); + _M_x[1] = (__uh >> (__w)) & max(); + _M_x[2] = __lh & max(); + _M_x[3] = (__lh >> (__w)) & max(); + } else + { + __uint128_t __num = + (static_cast<__uint128_t>(_M_x[1]) << __w) + | (static_cast<__uint128_t>(_M_x[0]) + 1); + _M_x[0] = __num & max(); + _M_x[1] = (__num >> __w) & max(); + } + _M_i = 0; + } + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + void + philox_engine<_UIntType, __w, __n, __r, __consts...>::_M_philox() + { + array<_UIntType, __n> __outputSeq{}; + for (size_t __j = 0; __j < __n; ++__j) + __outputSeq[__j] = _M_x[__j]; + for (unsigned long __j = 0; __j < __r; ++__j) + { + array<_UIntType, __n> __intermedSeq{}; + if constexpr (__n == 4) + { + __intermedSeq[0] = __outputSeq[2]; + __intermedSeq[1] = __outputSeq[1]; + __intermedSeq[2] = __outputSeq[0]; + __intermedSeq[3] = __outputSeq[3]; + } else + { + __intermedSeq[0] = __outputSeq[0]; + __intermedSeq[1] = __outputSeq[1]; + } + for (unsigned long __k = 0; __k < (__n/2); ++__k) + { + __outputSeq[2*__k]= _S_mulhi(__intermedSeq[2*__k], multipliers[__k]) + ^ (((_M_k[__k] + (__j * round_consts[__k])) & max())) + ^ __intermedSeq[2*__k+1]; + + __outputSeq[(2*__k)+1]= _S_mullo(__intermedSeq[2*__k], + multipliers[__k]); + } + } + for (unsigned long __j = 0; __j < __n; ++__j) + _M_y[__j] = __outputSeq[__j]; + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + philox_engine<_UIntType, + __w, __n, __r, __consts...>::philox_engine(result_type __value) + { + std::fill(_M_x.begin(), _M_x.end(), 0); + std::fill(_M_k.begin(), _M_k.end(), 0); + std::fill(_M_y.begin(), _M_y.end(), 0); + _M_k[0] = __value & max(); + _M_i = __n - 1; + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + void + philox_engine<_UIntType, + __w, __n, __r, __consts...>::seed(result_type __value) + { + std::fill(_M_x.begin(), _M_x.end(), 0); + std::fill(_M_k.begin(), _M_k.end(), 0); + std::fill(_M_y.begin(), _M_y.end(), 0); + _M_k[0] = __value & max(); + _M_i = __n - 1; + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + void + philox_engine<_UIntType, __w, + __n, __r, __consts...>::set_counter(const array<result_type, __n>& __counter) + { + for (unsigned long long __j = 0; __j < __n; ++__j) + _M_x[__j] = __counter[__n - 1 - __j] & max(); + _M_i = __n - 1; + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + template<typename _Sseq> + auto + philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& __q) + -> _If_seed_seq<_Sseq> + { + std::fill(_M_k.begin(), _M_k.end(), 0); + const unsigned long long __p = 1 + ((__w - 1)/ 32); + uint_least32_t __tmpArr[(__n / 2) * __p]; + __q.generate(__tmpArr + 0, __tmpArr + ((__n / 2) *__p)); + for (unsigned long long __k = 0; __k < (__n/2); ++__k) + { + unsigned long long __precalc = 0; + for (unsigned long long __j = 0; __j < __p; ++__j) + { + unsigned long long __multiplicand = (1ull << (32 * __j)); + __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & max(); + } + _M_k[__k] = __precalc; + } + std::fill(_M_x.begin(), _M_x.end(), 0); + std::fill(_M_y.begin(), _M_y.end(), 0); + _M_i = __n - 1; + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + void + philox_engine<_UIntType, + __w, __n, __r, __consts...>::discard(unsigned long long __z) + { + for (unsigned long long __j = 0; __j < __z; ++__j) + _M_transition(); + } + + template<class _UIntType, + size_t __w, size_t __n, + size_t __r, _UIntType... __consts> + _UIntType + philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()() + { + _M_transition(); + return _M_y[_M_i]; + } + +#endif template<typename _IntType, typename _CharT, typename _Traits> std::basic_ostream<_CharT, _Traits>& diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index dbe2cb8f175..4c21fa187c8 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -2069,6 +2069,15 @@ ftms = { }; }; +ftms = { + name = philox_engine; + values = { + v = 202406; + cxxmin = 26; + extra_cond = "__SIZEOF_INT128__"; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 7bb6016df68..75a9946adce 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2318,4 +2318,14 @@ #endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */ #undef __glibcxx_want_constexpr_exceptions +#if !defined(__cpp_lib_philox_engine) +# if (__cplusplus > 202302L) && (__SIZEOF_INT128__) +# define __glibcxx_philox_engine 202406L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_philox_engine) +# define __cpp_lib_philox_engine 202406L +# endif +# endif +#endif /* !defined(__cpp_lib_philox_engine) && defined(__glibcxx_want_philox_engine) */ +#undef __glibcxx_want_philox_engine + #undef __glibcxx_want_all diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random index 0e058a04bd9..8a02ade4b75 100644 --- a/libstdc++-v3/include/std/random +++ b/libstdc++-v3/include/std/random @@ -39,6 +39,9 @@ # include <bits/c++0x_warning.h> #else +#define __glibcxx_want_philox_engine +#include <bits/version.h> + #include <cmath> #include <cstdint> // For uint_fast32_t, uint_fast64_t, uint_least32_t #include <cstdlib> diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc new file mode 100644 index 00000000000..d5a8ca078ef --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc @@ -0,0 +1,23 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox4x32 a; + a.discard(9999); + + VERIFY( a() == 1955073260 ); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc new file mode 100644 index 00000000000..131f094cb28 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc @@ -0,0 +1,23 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox4x64 a; + a.discard(9999); + + VERIFY( a() == 3409172418970261260 ); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc new file mode 100644 index 00000000000..c3a5a0eb754 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/119794.cc @@ -0,0 +1,39 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +#include <random> +#include <testsuite_hooks.h> + +int f(int x) +{ + std::seed_seq sq(&x, &x + 1); + auto rnd = std::philox4x32(sq); + return std::uniform_int_distribution<int>()(rnd); +} + +int g(int x) +{ + std::seed_seq sq(&x, &x + 1); + auto rnd = std::philox4x32(); + rnd.seed(sq); + return std::uniform_int_distribution<int>()(rnd); +} + +void test01() +{ + const int f1 = f(0); + const int f2 = f(0); + + const int g1 = g(0); + const int g2 = g(0); + + VERIFY( f1 == f2 ); + VERIFY( g1 == g2 ); + VERIFY( f1 == g1 ); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc new file mode 100644 index 00000000000..4f61928a157 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc @@ -0,0 +1,25 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> + +void +test01() +{ + + std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(1ul); + + const auto f(e); + auto g(f); + g = g; // Suppress unused warning +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc new file mode 100644 index 00000000000..9f9ae94db0f --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> philox4x32nullkey(0); + + VERIFY( philox4x32nullkey.min() == 0 ); + VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); + VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc new file mode 100644 index 00000000000..5cb914f4b45 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc @@ -0,0 +1,20 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +#include <random> + +void +test01() +{ + unsigned long seed = 2; + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> philox4x32seeded(seed); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc new file mode 100644 index 00000000000..7d9e3e6540d --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc @@ -0,0 +1,24 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> + +void +test01() +{ + std::seed_seq sseq{ 1, 2, 3, 4 }; + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> philox4x32sseq(sseq); +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc new file mode 100644 index 00000000000..4f62bfbbd88 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc @@ -0,0 +1,30 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> x, y; + + VERIFY ( x == y); + x.discard(100); + y.discard(100); + + VERIFY (x == y); +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc new file mode 100644 index 00000000000..86d757db904 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/inequal.cc @@ -0,0 +1,30 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> x, y; + + VERIFY ( !(x != y) ); + x.discard(100); + y.discard(100); + + VERIFY ( !(x != y) ); +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc new file mode 100644 index 00000000000..bec4b172512 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc @@ -0,0 +1,49 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <sstream> +#include <random> +#include <testsuite_hooks.h> +#include <iostream> + +void +test01() +{ + std::stringstream str; + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> x, y; + + x(); + str << x; + + VERIFY ( !(x == y) ); + str >> y; + VERIFY ( x == y ); + for (unsigned long i = 0; i < 100; ++i) + { + VERIFY (x() == y()); + } + str.clear(); + str << y; + x(); + x(); + x(); + str >> x; + VERIFY ( x == y ); + for (unsigned long i = 0; i < 1000; ++i) + { + VERIFY (x() == y()); + } +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc new file mode 100644 index 00000000000..c242056e0a4 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc @@ -0,0 +1,26 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> + +void test01() +{ + std::philox4x32 philox; + const void* p = &philox.word_size; + p = &philox.word_count; + p = &philox.round_count; + p = &philox.multipliers; + p = &philox.round_consts; + p = &philox.default_seed; + p = p; // Suppress unused warning. +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc new file mode 100644 index 00000000000..5be0108c88c --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_data.cc @@ -0,0 +1,50 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_common_types.h> + +namespace __gnu_test +{ + struct constexpr_member_data + { + template<typename _Ttesttype> + void + operator()() + { + struct _Concept + { + void __constraint() + { + constexpr auto v1 __attribute__((unused)) + = _Ttesttype::word_size; + constexpr auto v2 __attribute__((unused)) + = _Ttesttype::word_count; + constexpr auto v3 __attribute__((unused)) + = _Ttesttype::round_count; + constexpr auto v4 __attribute__((unused)) + = _Ttesttype::multipliers; + constexpr auto v5 __attribute__((unused)) + = _Ttesttype::round_consts; + constexpr auto v6 __attribute__((unused)) + = _Ttesttype::default_seed; + } + }; + + _Concept c; + c.__constraint(); + } + }; +}; + +int +main() +{ + __gnu_test::constexpr_member_data test; + typedef std::philox4x32 type; + test.operator()<type>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc new file mode 100644 index 00000000000..eb61d15568a --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constexpr_functions.cc @@ -0,0 +1,41 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_common_types.h> + +namespace __gnu_test +{ + struct constexpr_member_functions + { + template<typename _Ttesttype> + void + operator()() + { + struct _Concept + { + void __constraint() + { + constexpr auto v1 __attribute__((unused)) + = _Ttesttype::min(); + constexpr auto v2 __attribute__((unused)) + = _Ttesttype::max(); + } + }; + _Concept c; + c.__constraint(); + } + }; +} + +int +main() +{ + __gnu_test::constexpr_member_functions test; + typedef std::philox4x32 type; + test.operator()<type>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc new file mode 100644 index 00000000000..b368ee74106 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc @@ -0,0 +1,26 @@ +// { dg-do run { target c++26 } } +// { dg-require-cstdint "" } + +// 29.5.4 Random Number Engine Class Templates +// 29.5.4.5 Class Template philox_engine + + +#include <random> + +void +test01() +{ + typedef std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> testType; + + typedef testType::result_type result_type; +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc index 0afba654152..13a0b8947b0 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc @@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); auto x = std::generate_canonical<std::size_t, std::numeric_limits<std::size_t>::digits>(urng); -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3357 } +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 } -- 2.49.0