On Fri, 18 Jul 2025, 1nfocalypse wrote:

> Implements Philox Engine (P2075R6) and associated tests.
> 
> Additionally implements changes based on feedback from Patrick
> Palka and jwakely on v2.
> 
> Regarding the question posed by Patrick on why some things are
> defined inline and some out of line, things were either defined inline
> either due to being concise (i.e. max()), to keep parity with other
> generators (equality operator overload), or for functionality
> (istream/ostream operators). I particularly attempted defining the
> stream operators out of line because they are fairly obtrusive, however,
> I couldn't get functionality out of them out of line. I am happy to revisit 
> this
> if needed.

Yep, such hidden friends must be defined inside the class body.

> 
> Of further note, altering version.def via autogen led to a line
> in excess of 80 chars. Since it was the result of autogen, I let it lie. If 
> this
> was incorrect, I can adjust it.

That's fine, we don't care about line lengths of autogenerated files.

> 
> Otherwise, thank you for the review, and please let me know if further
> alterations are needed.

Nice, thanks!  A couple of more review comments added inline.

> 
> Built/tested on x86_64-linux-gnu.
> 
> 
> Subject: [PATCH] [PATCH v3] libstdc++: implement Philox Engine [PR119794]
> 
> Implemented changes according to feedback from Patrick Palka
> and jwakely.
> 
> Swapped out C++ version macro check for feature-check macro
> (__glibcxx_philox_engine).
> Modified version.def to support Philox Engine, and restricted
> architectures to those supporting __uint128_t.
> Implements LGW4143, LWG4153 errata for Philox Engine.
> Convoluted ios_base reference simplified.
> Template unpacking functions refactored into one.
> Uglified internal members in accordance with style.
> Utilized parens in equivalence operator overload.
> Fixed improper test call in constants.cc
> Removed license headers to comply with current test style guide.
> Fixed errors in seed function with sseq argument,
>  - off by one error
>  - incorrect number
> Removed extraneous == 1 from static_assert
> Braces omitted for single line if statements.

FWIW you can also omit braces for single-line while/for loops etc as per
our coding style.

> Removed extraneous std:: qualifiers.
> Optimized to rshift instead of division in mulhi.
> Corrected iterator arguments.
> Removed redundant neq overload.
> Fixed incorrect copyright statement (covered by removal).
> 
> Additionally altered PR60037-neg.cc to correctly suppress
> error (changed line number to remain correct).
> 
>       PR libstdc++/119794
> ---
>  libstdc++-v3/include/bits/random.h            | 299 ++++++++++++++++++
>  libstdc++-v3/include/bits/random.tcc          | 200 ++++++++++++
>  libstdc++-v3/include/bits/version.def         |   9 +
>  libstdc++-v3/include/bits/version.h           |  10 +
>  libstdc++-v3/include/std/random               |   4 +
>  .../testsuite/26_numerics/random/inequal.cc   |  49 +++
>  .../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 +-
>  21 files changed, 1003 insertions(+), 1 deletion(-)
>  create mode 100644 libstdc++-v3/testsuite/26_numerics/random/inequal.cc
>  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..182dcc45eb6 100644
> --- a/libstdc++-v3/include/bits/random.h
> +++ b/libstdc++-v3/include/bits/random.h
> @@ -1688,6 +1688,286 @@ _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 (equal(__x._M_x.begin(), __x._M_x.end(),
> +             __y._M_x.begin(), __y._M_x.end())
> +           && equal(__x._M_y.begin(), __x._M_y.end(),
> +             __y._M_y.begin(), __y._M_y.end())
> +           && 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>

Uppercase '_CharT', '_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>

Same

> +       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
> +       */
> +     _UIntType
> +     _M_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
> +       */
> +     _UIntType
> +     _M_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 +2022,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
>    typedef minstd_rand0 default_random_engine;
>  
> +// replace w/ appropriate feature guard, not version guard
> +#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..d1b1c6abff6 100644
> --- a/libstdc++-v3/include/bits/random.tcc
> +++ b/libstdc++-v3/include/bits/random.tcc
> @@ -518,6 +518,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        return __is;
>      }
>  
> +
> +

Accidental whitespace change?

>  #if ! __cpp_inline_variables
>    template<typename _UIntType, size_t __w, size_t __s, size_t __r>
>      constexpr size_t
> @@ -907,6 +909,204 @@ _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...>::_M_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) & this->max());
> +  }
> +
> +  template<class _UIntType,
> +     size_t __w, size_t __n,
> +     size_t __r, _UIntType... __consts>
> +  _UIntType
> +  philox_engine<_UIntType,
> +  __w, __n, __r, __consts...>::_M_mullo(_UIntType __a, _UIntType __b)
> +  {
> +    return ((__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 (__n == 4)
> +      {
> +     __uint128_t __uh =
> +     {
> +       (static_cast<__uint128_t>(this->_M_x[1]) << __w)
> +             | ((this->_M_x[0]) + 1)

Can you remove the unnecessary this-> qualifier and the
outer {}?

> +     };
> +     __uint128_t __lh =
> +     {
> +       ((static_cast<__uint128_t>(this->_M_x[3]) << __w)
> +             | (this->_M_x[2]))
> +     };
> +     if (!(__uh & this->max()))
> +     {
> +       ++__lh;
> +       __uh = 0;
> +     }
> +     this->_M_x[0] = __uh & this->max();
> +     this->_M_x[1] = (__uh >> (__w)) & this->max();
> +     this->_M_x[2] = __lh & this->max();
> +     this->_M_x[3] = (__lh >> (__w)) & this->max();
> +      } else
> +      {
> +     __uint128_t __num = ((this->_M_x[1] << __w)) | ((this->_M_x[0]) + 1);
> +     this->_M_x[0] = __num & this->max();
> +     this->_M_x[1] = (__num >> __w) & this->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 (__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]= _M_mulhi(__intermedSeq[2*__k], multipliers[__k])
> +       ^ (((this->_M_k[__k] + (__j * round_consts[__k])) & this->max()))
> +       ^ __intermedSeq[2*__k+1];
> +
> +     __outputSeq[(2*__k)+1]= _M_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)
> +  {
> +    fill(_M_x.begin(), _M_x.end(), 0);

Can you please call std::fill fully qualified, as well as other
namespace-scope std functions, so that we don't have to worry/wonder
about unintentional ADL?

> +    fill(_M_k.begin(), _M_k.end(), 0);
> +    fill(_M_y.begin(), _M_y.end(), 0);
> +    _M_k[0] = __value & this->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)
> +  {
> +    fill(_M_x.begin(), _M_x.end(), 0);
> +    fill(_M_k.begin(), _M_k.end(), 0);
> +    fill(_M_y.begin(), _M_y.end(), 0);
> +    _M_k[0] = __value & this->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] & this->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>
> +  {
> +    fill(_M_k.begin(), _M_k.end(), 0);
> +    const unsigned long long __p = 1 + ((__w - 1/ 32));

The redundant parens seems suspicious, should this be (__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) & this->max();
> +      }
> +      this->_M_k[__k] = __precalc;
> +    }
> +    fill(_M_x.begin(), _M_x.end(), 0);
> +    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 cf0672b4822..32eedb89bcf 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -2059,6 +2059,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 c01ddf14dd5..f90280b4dce 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2309,4 +2309,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..4e2915c9d9a 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>
> @@ -51,6 +54,7 @@
>  #include <bits/opt_random.h>
>  #include <bits/random.tcc>
>  
> +

Another accidentaly whitespace change.

>  #endif // C++11
>  
>  #endif // _GLIBCXX_RANDOM
> diff --git a/libstdc++-v3/testsuite/26_numerics/random/inequal.cc 
> b/libstdc++-v3/testsuite/26_numerics/random/inequal.cc
> new file mode 100644
> index 00000000000..c8974449675
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/random/inequal.cc
> @@ -0,0 +1,49 @@
> +// { dg-do run { target c++26 } }
> +// { dg-require-cstdint "" }
> +//
> +// 2025-04-09   1nfocalypse <1nfocaly...@protonmail.com>
> +//
> +// Copyright (C) 2025-2034 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +//
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +//
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3. If not see
> +// https://www.gnu.org/licenses>
> +
> +// 29.5.4 Random Number Engine Class Templates
> +// 29.5.4.5 Class Template philox_engine
> +

Can you remove this copyright header too?

> +#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/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 );

I naively tried verifying this result against numpy's philox4x32 impl, but I'm
getting a different answer:

$ cat philox.py
import numpy as np

bit_generator = np.random.Philox(seed=20111115)
rng = np.random.Generator(bit_generator)

num_sequence = rng.integers(
    low=0,
    high=2**32,
    size=10000,
    dtype=np.uint32
)

print(num_sequence[-1])

$ python philox.py
3330218339

> +}
> +
> +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..649e7020c86 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 *-*-* } 3557 }
> -- 
> 2.49.0
> 

Reply via email to