diff mbox series

[v5] Implement Philox Engine [PR119794]

Message ID eiJT-ZyFg6a6ZtG8pK90L5mP4ewSB5wCGx3qYrxWYqyZdtssjmjdTpf_otRL1LuZQ1e2nJpY5nlpC7HMw5QVQxPsMk4UMfKjOC_N2j9p05Y=@protonmail.com
State New
Headers show
Series [v5] Implement Philox Engine [PR119794] | expand

Commit Message

1nfocalypse July 23, 2025, 4:18 p.m. UTC
Implements Philox Engine (P2075R6) and associated tests.

Implements additional feedback from v4 from Patrick Palka.

While fixing the carry bit propagation, I noted an issue for the
limb carry reset as well, which has been fixed, plus I snagged
some accidental whitespace that survived in random.h and
some extra curly braces that made it into v4.

Otherwise, with regards to the v4 feedback, yes, a fallback for
targets that don't support __uint128_t would be very nice, and
I think doing that as an independent patch is fitting given
Random123 itself demands 128 bit integer support for Philox.

If any further changes need to be made, please let me know.

Built/tested on x86_64-pc-linux-gnu.

Comments

Patrick Palka July 29, 2025, 8:10 p.m. UTC | #1
On Wed, 23 Jul 2025, 1nfocalypse wrote:

> Implements Philox Engine (P2075R6) and associated tests.
> 
> Implements additional feedback from v4 from Patrick Palka.
> 
> While fixing the carry bit propagation, I noted an issue for the
> limb carry reset as well, which has been fixed, plus I snagged
> some accidental whitespace that survived in random.h and
> some extra curly braces that made it into v4.
> 
> Otherwise, with regards to the v4 feedback, yes, a fallback for
> targets that don't support __uint128_t  would be very nice, and
> I think doing that as an independent patch is fitting given
> Random123 itself demands 128 bit integer support for Philox.
> 
> If any further changes need to be made, please let me know.

Thanks!  Just a few more changes requested to operator>>/<< and to
seed_seq.cc, besides that looks good I think.

> 
> Built/tested on x86_64-pc-linux-gnu.
> 
> 

> Subject: [PATCH] [PATCH v5] libstdc++: Implement Philox Engine [PR119794]
> 
> Implemented additional changes according to feedback from Patrick Palka.
> Plus, caught some accidental whitespace introduced in random.h.
> 
> mulhi, mullo changed to static member functions, and renamed to reflect
> removed extraneous {} remaining in the istream overload
> if (__n == 4) -> if constexpr (__n == 4)
> Fixed carry bug where overflow occurs before carry bit propagation
> Fixed limb logic in the 4x64 case
> Also modified pr60037-neg.cc again to accurately reflect line number.
> 
> Conforms with errata LWG4143, LWF4153 for Philox Engine.
> 
> 	PR libstdc++/119794
> ---
>  libstdc++-v3/include/bits/random.h            | 292 ++++++++++++++++++
>  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     |  22 ++
>  .../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         |   2 +-
>  20 files changed, 937 insertions(+), 1 deletion(-)
>  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..8e5fa6a4fe8 100644
> --- a/libstdc++-v3/include/bits/random.h
> +++ b/libstdc++-v3/include/bits/random.h
> @@ -1688,6 +1688,280 @@ _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)
> +	      {
> +		using __ios_base = ios_base;

This alias seems redundant?  We can just refer to ios_base:: directly.
Same in operator>>.

I think we need to include <iomanip> from this header as well, for sake
of the IWYU (include what you use) principle.

> +		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(' '));
> +		auto __it = __x._M_k.begin();
> +		while (__it != __x._M_k.end())
> +		{
> +		  __os << *__it << ' ';
> +		  ++__it;
> +		}

Here you're using iterator-based looping over _M_k and _M_k, and
in operator>> you're using index-based looping.  Can we make these
functions more consistent?  Perhaps a range-based for loop would
be most idiomatic actually.

> +		auto __it2 = __x._M_x.begin();
> +		while (__it2 != __x._M_x.end())
> +		{
> +		  __os << *__it2 << ' ';
> +		  ++__it2;
> +		}
> +		__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)
> +	      {
> +		using __ios_base = ios_base;
> +		const typename __ios_base::fmtflags __flags = __is.flags();
> +		__is.flags(__ios_base::dec | __ios_base::skipws);
> +		for (size_t __j = 0; __j < __x._M_k.size(); ++__j)
> +		  __is >> __x._M_k[__j];
> +		for (size_t __j = 0; __j < __x._M_x.size(); ++__j)
> +		  __is >> __x._M_x[__j];
> +		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 +2016,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..73225dad6a5 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..033667616f9
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc
> @@ -0,0 +1,22 @@
> +// { dg-do run { target c++26 } }
> +// { dg-require-cstdint "" }


Missing #include <random>

> +
> +// 29.5.4 Random Number Engine Class Templates
> +// 29.5.4.5 Class Template philox_engine
> +
> +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..91c4762b83a 100644
> --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
> +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
> @@ -12,4 +12,4 @@ auto x = std::generate_canonical<std::size_t,
>  
>  // { 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 *-*-* } 3357 }
> +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 }
> -- 
> 2.49.0
>
Jonathan Wakely Sept. 8, 2025, 4:22 p.m. UTC | #2
On Wed, 23 Jul 2025 at 17:19, 1nfocalypse <1nfocalypse@protonmail.com> wrote:
>
> Implements Philox Engine (P2075R6) and associated tests.
>
> Implements additional feedback from v4 from Patrick Palka.
>
> While fixing the carry bit propagation, I noted an issue for the
> limb carry reset as well, which has been fixed, plus I snagged
> some accidental whitespace that survived in random.h and
> some extra curly braces that made it into v4.
>
> Otherwise, with regards to the v4 feedback, yes, a fallback for
> targets that don't support __uint128_t  would be very nice, and
> I think doing that as an independent patch is fitting given
> Random123 itself demands 128 bit integer support for Philox.

It looks like Random123 supports philox4x32 without 128-bit integers,
just not philox4x64.

(And unfortunately the standard requires both to work even for 16-bit
microcontrollers!)

_Select_uint_least_t in bits/random.h should be used to select the
type for the intermediate results, so that we don't use int128 for
philox4x32 which doesn't require more than uint64_t. With a little bit
of work, the fallback impl of _Select_uint_least_t::type for 32-bit
targets could support the required 128-bit arithmetic for targets
without __int128 support.

>
> If any further changes need to be made, please let me know.
>
> Built/tested on x86_64-pc-linux-gnu.
diff mbox series

Patch

From 6afcd874c5a4d6059f6ed3409f7f4c08bf6ee0b9 Mon Sep 17 00:00:00 2001
From: 1nfocalypse <1nfocalypse@protonmail.com>
Date: Wed, 23 Jul 2025 07:46:42 +0000
Subject: [PATCH] [PATCH v5] libstdc++: Implement Philox Engine [PR119794]

Implemented additional changes according to feedback from Patrick Palka.
Plus, caught some accidental whitespace introduced in random.h.

mulhi, mullo changed to static member functions, and renamed to reflect
removed extraneous {} remaining in the istream overload
if (__n == 4) -> if constexpr (__n == 4)
Fixed carry bug where overflow occurs before carry bit propagation
Fixed limb logic in the 4x64 case
Also modified pr60037-neg.cc again to accurately reflect line number.

Conforms with errata LWG4143, LWF4153 for Philox Engine.

	PR libstdc++/119794
---
 libstdc++-v3/include/bits/random.h            | 292 ++++++++++++++++++
 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     |  22 ++
 .../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         |   2 +-
 20 files changed, 937 insertions(+), 1 deletion(-)
 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..8e5fa6a4fe8 100644
--- a/libstdc++-v3/include/bits/random.h
+++ b/libstdc++-v3/include/bits/random.h
@@ -1688,6 +1688,280 @@  _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)
+	      {
+		using __ios_base = ios_base;
+		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(' '));
+		auto __it = __x._M_k.begin();
+		while (__it != __x._M_k.end())
+		{
+		  __os << *__it << ' ';
+		  ++__it;
+		}
+		auto __it2 = __x._M_x.begin();
+		while (__it2 != __x._M_x.end())
+		{
+		  __os << *__it2 << ' ';
+		  ++__it2;
+		}
+		__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)
+	      {
+		using __ios_base = ios_base;
+		const typename __ios_base::fmtflags __flags = __is.flags();
+		__is.flags(__ios_base::dec | __ios_base::skipws);
+		for (size_t __j = 0; __j < __x._M_k.size(); ++__j)
+		  __is >> __x._M_k[__j];
+		for (size_t __j = 0; __j < __x._M_x.size(); ++__j)
+		  __is >> __x._M_x[__j];
+		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 +2016,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..73225dad6a5 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..033667616f9
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed_seq.cc
@@ -0,0 +1,22 @@ 
+// { 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
+
+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..91c4762b83a 100644
--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
@@ -12,4 +12,4 @@  auto x = std::generate_canonical<std::size_t,
 
 // { 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 *-*-* } 3357 }
+// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3548 }
-- 
2.49.0