On Fri, 11 Jul 2025 at 17:30, Patrick Palka <ppa...@redhat.com> wrote:
>
> Hi,
>
> On Thu, 22 May 2025, 1nfocalypse wrote:
>
> > Implements Philox Engine (P2075R6) and associated tests.
> >
> > v2 corrects a multiline comment left in error in serialize.cc, and 
> > additionally corrects a bug hidden by said comment, where the stream was 
> > given the output of 'y()' instead of 'y', causing state to be
> > incorrectly passed. Lastly, it fixes numerous whitespace issues found in 
> > the original patch. My apologies for not noticing prior to the submission 
> > of the original patch, which can now be disregarded.
> >
> > To reiterate from the original email, the template unpacking functions are 
> > placed in a private classifier prior to the public one due to an ordering 
> > bug, where in order to function correctly, they must be
> > placed prior to the bulk of the header. This is counter to the style 
> > recommendations, but I was unable to obtain functionality any other way. 
> > Additionally, while SIMD instructions are not utilized, and I do
> > not think that they would integrate well with how the generator's state is 
> > currently handled, some structure choices could be made that may make them 
> > of interest.
> >
> > Lastly, since word width can be specified, and thus atypical, maximum value 
> > is calculated via some bit manipulation rather than numeric_limits, since 
> > the specified width may differ from the width of the type
> > used.
> >
> > Built/tested on x86_64-linux-gnu.
>
> Sorry for the delay and thanks for your patience!  Some initial review
> comments below.
>
> >
> >  *  1nfocalypse
>
> >  >
> >  >
> >  Subject: [PATCH] [PATCH v2] libstdc++: implement Philox Engine [PR119794]
> >
> >  The template unpacking functions, while private, are placed prior
> >  to the public access specifier due to issues where the template
> >  pack could not be unpacked and used to populate the public member
> >  arrays without being declared beforehand.
> >
> >  Additionally, the tests implemented attempt to mirror the tests
> >  for other engines, when they apply. Changes to random
> >  provided cause for changing 'pr60037-neg.cc' because it suppresses
> >  an error by explicit line number. It should still be correctly
> >  suppressed in this patch. Lastly, v2 fixes an issue in
> >  'serialize.cc' for Philox, where a portion of the test was
> >  commented out, hiding a bug where 'y()' was passed to the
> >  stream instead of 'y'. Both have been remedied in this patch.
>
> This implements the changes to the original paper
> https://wg21.link/lwg4134
> https://wg21.link/lwg4153
> right?  Maybe we could make a note of that in the commit message.
>
> >
> >  Plus some whitespace fixes.
> >
> >       PR libstdc++/119794
> >
> >  libstdc++-v3/ChangeLog:
> >
> >       * include/bits/random.h: Add Philox Engine components.
> >       * include/bits/random.tcc: Implement Philox Engine components.
> >       * testsuite/26_numerics/random/pr60037-neg.cc: Alter line #.
> >       * testsuite/26_numerics/random/inequal.cc: New test.
> >       * testsuite/26_numerics/random/philox4x32.cc: New test.
> >       * testsuite/26_numerics/random/philox4x64.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/cons/
> >       119794.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/cons/
> >       copy.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/cons/
> >       default.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/cons/
> >       seed.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/cons/
> >       seed_seq.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/operators/
> >       equal.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/operators/
> >       inequal.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/operators/
> >       serialize.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/requirements/
> >       constants.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/requirements/
> >       constexpr_data.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/requirements/
> >       constexpr_functions.cc: New test.
> >       * testsuite/26_numerics/random/philox_engine/requirements/
> >       typedefs.cc: New test.
> >  ---
> >   libstdc++-v3/include/bits/random.h            | 340 ++++++++++++++++++
> >   libstdc++-v3/include/bits/random.tcc          | 201 +++++++++++
> >   .../testsuite/26_numerics/random/inequal.cc   |  49 +++
> >   .../26_numerics/random/philox4x32.cc          |  42 +++
> >   .../26_numerics/random/philox4x64.cc          |  42 +++
> >   .../random/philox_engine/cons/119794.cc       |  57 +++
> >   .../random/philox_engine/cons/copy.cc         |  44 +++
> >   .../random/philox_engine/cons/default.cc      |  46 +++
> >   .../random/philox_engine/cons/seed.cc         |  39 ++
> >   .../random/philox_engine/cons/seed_seq.cc     |  41 +++
> >   .../random/philox_engine/operators/equal.cc   |  49 +++
> >   .../random/philox_engine/operators/inequal.cc |  49 +++
> >   .../philox_engine/operators/serialize.cc      |  68 ++++
> >   .../philox_engine/requirements/constants.cc   |  45 +++
> >   .../requirements/constexpr_data.cc            |  69 ++++
> >   .../requirements/constexpr_functions.cc       |  60 ++++
> >   .../philox_engine/requirements/typedefs.cc    |  45 +++
> >   .../26_numerics/random/pr60037-neg.cc         |   2 +-
> >   18 files changed, 1287 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..5c675927515 100644
> >  --- a/libstdc++-v3/include/bits/random.h
> >  +++ b/libstdc++-v3/include/bits/random.h
> >  @@ -1688,6 +1688,328 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       { return !(__lhs == __rhs); }
> >   #endif
> >
> >  +#if __cplusplus > 202302L
>
> The implementation should more specifically be guarded by the
> corresponding feature-test macro __cpp_lib_philox_engine
> (defined to 202406L).
>
> The way this is done nowadays in libstdc++ is to add philox_engine to
> libstdc++-v3/include/bits/version.def and regenerate version.h via
> 'autogen version.def'.  This will define the internal feature-test macro
> __glibcxx_philox_engine that we can check here.
>
> In include/std/random, we need to do
>
> #define __glibcxx_want_philox_engine
> #include <bits/version.def>
>
> so that the __cpp_lib_philox_engine is provided by that header.
>
> >  +
> >  +  /**
> >  +   * @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)==1,
>
> Can you remove the ==1?
>
> >  +          "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.  */
> >  +    static constexpr
> >  +    std::array<_UIntType, __n / 2>
> >  +    __popMultArray()

In our naming convention this should be _S_pop_mult_array()

> >  +    {
> >  +      if constexpr (__n == 4)
> >  +      {
>
> We typically elide the braces for single-statement if branches.
>
> >  +        return
> >  +          std::array<_UIntType, __n / 2> {__consts...[0], __consts...[2]};

The "std::" on std::array is not needed. The surrounding code in
<random> is a bit inconsistent about this, but that's because it was
ported from <tr1/random> where everything was in namespace std::tr1
and so the std:: qualification made it clear where things came from.
For <random> which is all in std, we don't need that. Especially not
in new code.


> >  +      } else
> >  +      {
> >  +        return std::array<_UIntType, __n / 2> { __consts...[0] };
> >  +      }
> >  +    }
> >  +
> >  +    /** @brief an internal unpacking function for %philox_engine.  */
> >  +    static constexpr
> >  +    std::array<_UIntType, __n / 2>
> >  +    __popConstArray()

_S_pop_const_array()

> >  +    {
> >  +      if constexpr (__n == 4)
> >  +      {
> >  +        return
> >  +          std::array<_UIntType, __n / 2> {__consts...[1], __consts...[3]};
> >  +      } else
> >  +      {
> >  +        return std::array<_UIntType, __n / 2> {__consts...[1] };
> >  +      }
> >  +    }
> >  +
> >  +      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 std::array<result_type, __n / 2> multipliers =
> >  +       philox_engine::__popMultArray();
> >  +    static constexpr std::array<result_type, __n / 2> round_consts =
> >  +       philox_engine::__popConstArray();

Since you're creating static data members for these anyway, would it
make more sense for them to just be variable templates instead of
calling

Can we just use a single function for both of these?

template<size_t _Ind0, size_t _Ind1>
static constexpr array<result_type, __n/2>
_S_populate_array()
{
  if constexpr (__n == 4)
    return {__consts...[_Ind0], __consts[_Ind1]};
  else
    return {__consts...[_Ind0]};
}

then:

    static constexpr std::array<result_type, __n / 2> multipliers
       = _S_populate_array<0, 2>();
    static constexpr std::array<result_type, __n / 2> round_consts
       = _S_populate_array<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 std::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
> >  +        {
>
> Please use parens here instead.  It should be formatted as
>
>   return (std::equal(...)
>           && ...
>           && __x._M_ == __y._M_i);
>
> >  +          std::equal(__x.__x.begin(), __x.__x.end(),
> >  +                    __y.__x.begin(), __y.__x.end()) &&
> >  +          std::equal(__x.__y.begin(), __x.__y.end(),
> >  +                    __y.__y.begin(), __y.__y.end()) &&
> >  +          std::equal(__x.__k.begin(), __x.__k.end(),
> >  +                    __y.__k.begin(), __y.__k.end()) &&
> >  +          __x.__i == __y.__i
> >  +        };
> >  +    }
> >  +
> >  +
> >  +    _UIntType
> >  +    operator()();
> >  +
> >  +    /** @brief discards __z numbers
> >  +      *
> >  +      * @params __z number of iterations to discard
> >  +      */
> >  +    void
> >  +    discard(unsigned long long __z);
> >  +
> >  +    // Note: hidden friend seems best, suspect a better way of doing 
> > things
> >  +    // but unable to find it

Yes, a hidden friend is what we want here.

> >  +    /** @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 =
> >  +              typename basic_ostream<_charT, _traits>::ios_base;

Isn't that just ... std::ios_base? or simply 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.__k.begin();
> >  +            while (__it != __x.__k.end())
> >  +            {
> >  +              __os << *__it << ' ';
> >  +              ++__it;
> >  +            }
> >  +            auto __it2 = __x.__x.begin();
> >  +            while (__it2 != __x.__x.end())
> >  +            {
> >  +              __os << *__it2 << ' ';
> >  +              ++__it2;
> >  +            }
> >  +            __os << __x.__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 =
> >  +              typename basic_istream<_charT, _traits>::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.__k.size(); ++__j)
> >  +            {
> >  +              __is >> __x.__k[__j];
> >  +            }
> >  +            for (size_t __j = 0; __j < __x.__x.size(); ++__j)
> >  +            {
> >  +              __is >> __x.__x[__j];
> >  +            }
> >  +            std::array<_UIntType, __n> __tmpCtr = __x.__x;
> >  +            unsigned char __setIndex = 0;
> >  +            for (size_t __j = 0; __j < __x.__x.size(); ++__j)
> >  +            {
> >  +              if (__x.__x[__j] > 0)
> >  +              {
> >  +                __setIndex = __j;
> >  +                break;
> >  +              }
> >  +            }
> >  +            for (size_t __j = 0; __j <= __setIndex; ++__j)
> >  +            {
> >  +              if (__j != __setIndex)
> >  +              {
> >  +                __x.__x[__j] = max();
> >  +              } else
> >  +              {
> >  +                --__x.__x[__j];
> >  +              }
> >  +            }
> >  +            __x.__philox();
> >  +            __x.__x = __tmpCtr;
> >  +            __is >> __x.__i;
> >  +            __is.flags(__flags);
> >  +            return __is;
> >  +          }
> >  +      private:
> >  +    // private state variables
> >  +    std::array<_UIntType, __n> __x;
> >  +    std::array<_UIntType, __n / 2> __k;
> >  +    std::array<_UIntType, __n> __y;
> >  +    unsigned long long __i = 0;
>
> In libstdc++, internal non-static data members are conventionally
> uglified as _M_foo.  Internal static members are uglified as _S_foo.

And private member functions use the same convention.

>
> >  +
> >  +    /** @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
> >  +    __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
> >  +    __mullo(_UIntType __a, _UIntType __b); // (A*B)%2^W
> >  +
> >  +    /** @brief an R-round SP-net followed by a Feistel Network for
> >  +      * %philox_engine
> >  +      */
> >  +    void
> >  +    __philox();
> >  +
> >  +    /** @brief an internal transition function for the %philox_engine.  */
> >  +    void
> >  +    __transition();
> >  +    };
> >  +
> >  +  /**
> >  +   * Compares two %philox_engine random number generator objects
> >  +   * of the same type for inequality.
> >  +   *
> >  +   * @param __lhs A %philox_engine random number generator object.
> >  +   * @param __rhs Another %philox_engine random number generator
> >  +   *                  object.
> >  +   *
> >  +   * @returns true if the streams would be different, false otherwise.
> >  +   */
> >  +  template<class _UIntType, size_t __w,
> >  +       size_t __n, size_t __r,
> >  +       _UIntType... __consts>
> >  +  inline bool
> >  +  operator!=(const std::philox_engine<_UIntType, __w,
> >  +         __n, __r, __consts...>& __lhs,
> >  +         const std::philox_engine<_UIntType, __w,
> >  +         __n, __r, __consts...>& __rhs)
> >  +  { return !(__lhs == __rhs); }

Isn't this redundant in C++23? We only need operator==


> >  +
> >  +#endif
> >  +
> >     /**
> >      * The classic Minimum Standard rand0 of Lewis, Goodman, and Miller.
> >      */
> >  @@ -1742,6 +2064,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >     typedef minstd_rand0 default_random_engine;
> >
> >  +#if __cplusplus > 202302L
> >  +
> >  +  typedef philox_engine<
> >  +    uint_fast32_t,
> >  +    32, 4, 10,
> >  +    0xCD9E8D57, 0x9E3779B9,
> >  +    0xD2511F53, 0xBB67AE85> philox4x32;
> >  +
> >  +  /**
> >  +   * Alternative Philox instance (64 bit)
> >  +   */
> >  +  typedef philox_engine<
> >  +    std::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..faba3b6d4b5 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;
> >       }
> >
> >  +
> >  +
> >   #if ! __cpp_inline_variables
> >     template<typename _UIntType, size_t __w, size_t __s, size_t __r>
> >       constexpr size_t
> >  @@ -907,6 +909,205 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >         return __is;
> >       }
> >
> >  +#if __cplusplus > 202302L
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  _UIntType
> >  +  std::philox_engine<_UIntType,
>
> I believe std:: prefix here is unnecessary since we're already inside
> the std namespace.  Why are some member functions defined inline and
> some out-of-line?
>
> >  +  __w, __n, __r, __consts...>::__mulhi(_UIntType __a, _UIntType __b)
> >  +  {
> >  +    const __uint128_t __num =
>
> Since not all targets provide a 128-bit integer type, I think we need to
> disable philox_engine on targets lacking it.  The easiest way would be
> to add __SIZEOF_INT128__ to extra_conds in the corresponding version.def
> entry.
>
> >  +    static_cast<__uint128_t>(__a) * static_cast<__uint128_t>(__b);
> >  +    const __uint128_t __div = (static_cast<__uint128_t>(1) << __w);
> >  +    return static_cast<_UIntType>((__num / __div) & this->max());
>
> Can we just right-shift by __w instead of dividing by 1 << __w?
>
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  _UIntType
> >  +  std::philox_engine<_UIntType,
> >  +  __w, __n, __r, __consts...>::__mullo(_UIntType __a, _UIntType __b)
> >  +  {
> >  +    return ((__a * __b) & max());
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  void
> >  +  std::philox_engine<_UIntType, __w, __n, __r, 
> > __consts...>::__transition()
> >  +  {
> >  +    ++__i;
> >  +    if (__i == __n)
> >  +    {
> >  +      __philox();
> >  +      if (__n == 4)
> >  +      {
> >  +    __uint128_t __uh =
> >  +    {
> >  +      (static_cast<__uint128_t>(this->__x[1]) << __w)
> >  +            | ((this->__x[0]) + 1)
> >  +    };
> >  +    __uint128_t __lh =
> >  +    {
> >  +      ((static_cast<__uint128_t>(this->__x[3]) << __w)
> >  +            | (this->__x[2]))
> >  +    };
> >  +    if (!(__uh & this->max()))
> >  +    {
> >  +      ++__lh;
> >  +      __uh = 0;
> >  +    }
> >  +    this->__x[0] = __uh & this->max();
> >  +    this->__x[1] = (__uh >> (__w)) & this->max();
> >  +    this->__x[2] = __lh & this->max();
> >  +    this->__x[3] = (__lh >> (__w)) & this->max();
> >  +      } else
> >  +      {
> >  +    __uint128_t __num = ((this->__x[1] << __w)) | ((this->__x[0]) + 1);
> >  +    this->__x[0] = __num & this->max();
> >  +    this->__x[1] = (__num >> __w) & this->max();
> >  +      }
> >  +      __i = 0;
> >  +    }
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  void
> >  +  std::philox_engine<_UIntType, __w, __n, __r, __consts...>::__philox()
> >  +  {
> >  +    std::array<_UIntType, __n> __outputSeq{};
> >  +    for (size_t __j = 0; __j < __n; ++__j)
> >  +    {
> >  +      __outputSeq[__j] = __x[__j];
> >  +    }
> >  +    for (unsigned long __j = 0; __j < __r; ++__j)
> >  +    {
> >  +      std::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]= __mulhi(__intermedSeq[2*__k], multipliers[__k])
> >  +      ^ (((this->__k[__k] + (__j * round_consts[__k])) & this->max()))
> >  +      ^ __intermedSeq[2*__k+1];
> >  +
> >  +    __outputSeq[(2*__k)+1]= __mullo(__intermedSeq[2*__k],
> >  +      multipliers[__k]);
> >  +      }
> >  +    }
> >  +    for (unsigned long __j = 0; __j < __n; ++__j)
> >  +    {
> >  +      __y[__j] = __outputSeq[__j];
> >  +    }
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  std::philox_engine<_UIntType,
> >  +    __w, __n, __r, __consts...>::philox_engine(result_type __value)
> >  +  {
> >  +    std::fill(__x.begin(), __x.end(), 0);
> >  +    std::fill(__k.begin(), __k.end(), 0);
> >  +    std::fill(__y.begin(), __y.end(), 0);
> >  +    __k[0] = __value & this->max();
> >  +    __i = __n - 1;
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  void
> >  +  std::philox_engine<_UIntType,
> >  +  __w, __n, __r, __consts...>::seed(result_type __value)
> >  +  {
> >  +    std::fill(__x.begin(), __x.end(), 0);
> >  +    std::fill(__k.begin(), __k.end(), 0);
> >  +    std::fill(__y.begin(), __y.end(), 0);
> >  +    __k[0] = __value & this->max();
> >  +    __i = __n - 1;
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  void
> >  +  std::philox_engine<_UIntType, __w,
> >  +  __n, __r, __consts...>::set_counter(const array<result_type, __n>& 
> > __counter)
> >  +  {
> >  +    for (unsigned long long __j = 0; __j < __n; ++__j)
> >  +    {
> >  +      __x[__j] = __counter[__n - 1 - __j] & this->max();
> >  +    }
> >  +    __i = __n - 1;
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  template<typename _Sseq>
> >  +  auto
> >  +  std::philox_engine<_UIntType, __w, __n, __r, __consts...>::seed(_Sseq& 
> > __q)
> >  +  -> _If_seed_seq<_Sseq>
> >  +  {
> >  +    std::fill(__k.begin(), __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) - 1; ++__k)
>
> this->__k has n/2 elements, but this loop is up to n/2 - 1?
>
> >  +    {
> >  +      unsigned long long __precalc = 0;
> >  +      for (unsigned long long __j = 0; __j < __p; ++__j)
> >  +      {
> >  +    unsigned long long __multiplicand = (1ull << (32 * __j - 1));
>
> I believe this left shift is UB when __j is zero (and so the shift
> amount is huge).
>
> >  +    __precalc += (__tmpArr[__k*__p + __j] * __multiplicand) & this->max();
> >  +      }
> >  +      this->__k[__k] = __precalc;
> >  +    }
> >  +    std::fill(__x.begin(), __x.end(), 0);
> >  +    std::fill(__y.begin(), __y.begin(), 0);
>
> __y.begin(), __y.end() rather?
>
> >  +    __i = __n - 1;
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  void
> >  +  std::philox_engine<_UIntType,
> >  +    __w, __n, __r, __consts...>::discard(unsigned long long __z)
> >  +  {
> >  +    for (unsigned long long __j = 0; __j < __z; ++__j)
> >  +    {
> >  +      __transition();
> >  +    }
> >  +  }
> >  +
> >  +  template<class _UIntType,
> >  +    size_t __w, size_t __n,
> >  +    size_t __r, _UIntType... __consts>
> >  +  _UIntType
> >  +  std::philox_engine<_UIntType, __w, __n, __r, __consts...>::operator()()
> >  +  {
> >  +    __transition();
> >  +    return __y[__i];
> >  +  }
> >  +
> >  +#endif
> >
> >     template<typename _IntType, typename _CharT, typename _Traits>
> >       std::basic_ostream<_CharT, _Traits>&
> >  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..081c908a3eb
> >  --- /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.

2034? Huh?


> >  +//
> >  +// 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>
>
> Nowadays we don't add license headers to tests.

See https://gcc.gnu.org/onlinedocs/libstdc++/manual/test.html#test.new_tests


Reply via email to