<commit_before>#ifndef VSMC_RNG_PHILOX_HPP
#define VSMC_RNG_PHILOX_HPP

#include <vsmc/rng/common.hpp>

#if VSMC_USE_RANDOM123
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4521)
#endif // _MSC_VER
#include <Random123/philox.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#endif // VSMC_USE_RANDOM123

#define VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType) \
    VSMC_STATIC_ASSERT(                                                      \
            (cxx11::is_same<ResultType, uint32_t>::value ||                  \
             cxx11::is_same<ResultType, uint64_t>::value),                   \
            USE_PhiloxEngine_WITH_INTEGER_TYPE_OTHER_THAN_uint32_t_OR_uint64_t)

#define VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K) \
    VSMC_STATIC_ASSERT((K == 2 || K == 4),                                   \
            USE_PhiloxEngine_WITH_SIZE_OTHER_THAN_2_OR_4)

#define VSMC_STATIC_ASSERT_RNG_PHILOX_ROUND(R) \
    VSMC_STATIC_ASSERT((R <= 16),                                            \
            USE_PhiloxEngine_WITH_ROUNDS_LARGER_THAN_16)

#define VSMC_STATIC_ASSERT_RNG_PHILOX \
        VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType);             \
        VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K);                             \
        VSMC_STATIC_ASSERT_RNG_PHILOX_ROUND(R);

#define VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(T, I, val) \
    template <> struct PhiloxBumpkConstant < T, I > :                        \
        public cxx11::integral_constant< T, val > {};

#define VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(T, K, I, val) \
    template <> struct PhiloxRoundConstant < T, K, I > :                     \
        public cxx11::integral_constant< T, val > {};

namespace vsmc {

namespace internal {

#if VSMC_USE_RANDOM123
template <typename, std::size_t, std::size_t> struct PhiloxR123Trait;

template <std::size_t R>
struct PhiloxR123Trait<uint32_t, 2, R>
{typedef r123::Philox2x32_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint32_t, 4, R>
{typedef r123::Philox4x32_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint64_t, 2, R>
{typedef r123::Philox2x64_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint64_t, 4, R>
{typedef r123::Philox4x64_R<R> type;};
#endif

template <typename, std::size_t> struct PhiloxBumpkConstant;

VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint32_t, 0,
        static_cast<uint32_t>(0x9E3779B9))
VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint32_t, 1,
        static_cast<uint32_t>(0xBB67AE85))

VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint64_t, 0,
        UINT64_C(0x9E3779B97F4A7C15))
VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint64_t, 1,
        UINT64_C(0xBB67AE8584CAA73B))

template <typename, std::size_t, std::size_t> struct PhiloxRoundConstant;

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 2, 0,
        static_cast<uint32_t>(0xd256d193))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 4, 0,
        static_cast<uint32_t>(0xD2511F53))
VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 4, 1,
        static_cast<uint32_t>(0xCD9E8D57))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 2, 0,
        UINT64_C(0xD2B74407B1CE6E93))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 4, 0,
        UINT64_C(0xD2E7470EE14C6C93))
VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 4, 1,
        UINT64_C(0xCA5A826395121157))

template <typename ResultType, std::size_t, std::size_t N, bool = (N > 1)>
struct PhiloxBumpk {static void bumpk (ResultType *) {}};

template <typename ResultType, std::size_t N>
struct PhiloxBumpk<ResultType, 2, N, true>
{
    static void bumpk (ResultType *ks)
    {ks[0] += PhiloxBumpkConstant<ResultType, 0>::value;}
};

template <typename ResultType, std::size_t N>
struct PhiloxBumpk<ResultType, 4, N, true>
{
    static void bumpk (ResultType *ks)
    {
        ks[0] += PhiloxBumpkConstant<ResultType, 0>::value;
        ks[1] += PhiloxBumpkConstant<ResultType, 1>::value;
    }
};

template <std::size_t K, std::size_t N, typename ResultType>
void philox_hilo (ResultType b, ResultType &hi, ResultType &lo)
{
    const ResultType a = PhiloxRoundConstant<ResultType, K, N>::value;
    const unsigned whalf = RngUIntBits<ResultType>::value / 2;
    const ResultType lomask = (static_cast<ResultType>(1) << whalf) - 1;

    lo = a * b;

    ResultType ahi = a >> whalf;
    ResultType alo = a & lomask;
    ResultType bhi = b >> whalf;
    ResultType blo = b & lomask;

    ResultType ahbl = ahi * blo;
    ResultType albh = alo * bhi;

    ResultType ahbl_albh = ((ahbl & lomask) + (albh & lomask));

    hi = ahi * bhi + (ahbl >> whalf) + (albh >> whalf);
    hi += ahbl_albh >> whalf;
    hi += ((lo >> whalf) < (ahbl_albh & lomask));
}

template <typename ResultType, std::size_t, std::size_t N, bool = (N > 0)>
struct PhiloxRound {static void round (ResultType *, const ResultType *) {}};

template <typename ResultType, std::size_t N>
struct PhiloxRound<ResultType, 2, N, true>
{
    static void round (ResultType *state, const ResultType *ks)
    {
        ResultType hi = 0;
        ResultType lo = 0;
        philox_hilo<2, 0>(state[0], hi, lo);
        state[0] = hi^ks[0]^state[1];
        state[1] = lo;
    }
};

template <typename ResultType, std::size_t N>
struct PhiloxRound<ResultType, 4, N, true>
{
    static void round (ResultType *state, const ResultType *ks)
    {
        ResultType hi0 = 0;
        ResultType hi1 = 0;
        ResultType lo0 = 0;
        ResultType lo1 = 0;
        philox_hilo<4, 0>(state[0], hi0, lo0);
        philox_hilo<4, 1>(state[2], hi1, lo1);
        state[0] = hi1^state[1]^ks[0];
        state[1] = lo1;
        state[2] = hi0^state[3]^ks[1];
        state[3] = lo0;
    }
};

} // namespace vsmc::internal

template <typename ResultType, std::size_t K, std::size_t R = 10>
class PhiloxEngine
{
    public :

    typedef ResultType result_type;
    typedef StaticVector<ResultType, K / 2> key_type;
    typedef StaticVector<ResultType, K> ctr_type;

    explicit PhiloxEngine (result_type s = 0) : remain_(0)
    {
        VSMC_STATIC_ASSERT_RNG_PHILOX;
        seed(s);
    }

    template <typename SeedSeq>
    explicit PhiloxEngine (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, ResultType>::value>::type * =<commit_msg>doc Philox<commit_after>#ifndef VSMC_RNG_PHILOX_HPP
#define VSMC_RNG_PHILOX_HPP

#include <vsmc/rng/common.hpp>

#if VSMC_USE_RANDOM123
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4521)
#endif // _MSC_VER
#include <Random123/philox.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#endif // VSMC_USE_RANDOM123

#define VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType) \
    VSMC_STATIC_ASSERT(                                                      \
            (cxx11::is_same<ResultType, uint32_t>::value ||                  \
             cxx11::is_same<ResultType, uint64_t>::value),                   \
            USE_PhiloxEngine_WITH_INTEGER_TYPE_OTHER_THAN_uint32_t_OR_uint64_t)

#define VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K) \
    VSMC_STATIC_ASSERT((K == 2 || K == 4),                                   \
            USE_PhiloxEngine_WITH_SIZE_OTHER_THAN_2_OR_4)

#define VSMC_STATIC_ASSERT_RNG_PHILOX_ROUND(R) \
    VSMC_STATIC_ASSERT((R <= 16),                                            \
            USE_PhiloxEngine_WITH_ROUNDS_LARGER_THAN_16)

#define VSMC_STATIC_ASSERT_RNG_PHILOX \
        VSMC_STATIC_ASSERT_RNG_PHILOX_RESULT_TYPE(ResultType);             \
        VSMC_STATIC_ASSERT_RNG_PHILOX_SIZE(K);                             \
        VSMC_STATIC_ASSERT_RNG_PHILOX_ROUND(R);

#define VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(T, I, val) \
    template <> struct PhiloxBumpkConstant < T, I > :                        \
        public cxx11::integral_constant< T, val > {};

#define VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(T, K, I, val) \
    template <> struct PhiloxRoundConstant < T, K, I > :                     \
        public cxx11::integral_constant< T, val > {};

namespace vsmc {

namespace internal {

#if VSMC_USE_RANDOM123
template <typename, std::size_t, std::size_t> struct PhiloxR123Trait;

template <std::size_t R>
struct PhiloxR123Trait<uint32_t, 2, R>
{typedef r123::Philox2x32_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint32_t, 4, R>
{typedef r123::Philox4x32_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint64_t, 2, R>
{typedef r123::Philox2x64_R<R> type;};

template <std::size_t R>
struct PhiloxR123Trait<uint64_t, 4, R>
{typedef r123::Philox4x64_R<R> type;};
#endif

template <typename, std::size_t> struct PhiloxBumpkConstant;

VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint32_t, 0,
        static_cast<uint32_t>(0x9E3779B9))
VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint32_t, 1,
        static_cast<uint32_t>(0xBB67AE85))

VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint64_t, 0,
        UINT64_C(0x9E3779B97F4A7C15))
VSMC_DEFINE_RNG_PHILOX_BUMPK_CONSTANT(uint64_t, 1,
        UINT64_C(0xBB67AE8584CAA73B))

template <typename, std::size_t, std::size_t> struct PhiloxRoundConstant;

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 2, 0,
        static_cast<uint32_t>(0xd256d193))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 4, 0,
        static_cast<uint32_t>(0xD2511F53))
VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint32_t, 4, 1,
        static_cast<uint32_t>(0xCD9E8D57))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 2, 0,
        UINT64_C(0xD2B74407B1CE6E93))

VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 4, 0,
        UINT64_C(0xD2E7470EE14C6C93))
VSMC_DEFINE_RNG_PHILOX_ROUND_CONSTANT(uint64_t, 4, 1,
        UINT64_C(0xCA5A826395121157))

template <typename ResultType, std::size_t, std::size_t N, bool = (N > 1)>
struct PhiloxBumpk {static void bumpk (ResultType *) {}};

template <typename ResultType, std::size_t N>
struct PhiloxBumpk<ResultType, 2, N, true>
{
    static void bumpk (ResultType *ks)
    {ks[0] += PhiloxBumpkConstant<ResultType, 0>::value;}
};

template <typename ResultType, std::size_t N>
struct PhiloxBumpk<ResultType, 4, N, true>
{
    static void bumpk (ResultType *ks)
    {
        ks[0] += PhiloxBumpkConstant<ResultType, 0>::value;
        ks[1] += PhiloxBumpkConstant<ResultType, 1>::value;
    }
};

template <std::size_t K, std::size_t N, typename ResultType>
void philox_hilo (ResultType b, ResultType &hi, ResultType &lo)
{
    const ResultType a = PhiloxRoundConstant<ResultType, K, N>::value;
    const unsigned whalf = RngUIntBits<ResultType>::value / 2;
    const ResultType lomask = (static_cast<ResultType>(1) << whalf) - 1;

    lo = a * b;

    ResultType ahi = a >> whalf;
    ResultType alo = a & lomask;
    ResultType bhi = b >> whalf;
    ResultType blo = b & lomask;

    ResultType ahbl = ahi * blo;
    ResultType albh = alo * bhi;

    ResultType ahbl_albh = ((ahbl & lomask) + (albh & lomask));

    hi = ahi * bhi + (ahbl >> whalf) + (albh >> whalf);
    hi += ahbl_albh >> whalf;
    hi += ((lo >> whalf) < (ahbl_albh & lomask));
}

template <typename ResultType, std::size_t, std::size_t N, bool = (N > 0)>
struct PhiloxRound {static void round (ResultType *, const ResultType *) {}};

template <typename ResultType, std::size_t N>
struct PhiloxRound<ResultType, 2, N, true>
{
    static void round (ResultType *state, const ResultType *ks)
    {
        ResultType hi = 0;
        ResultType lo = 0;
        philox_hilo<2, 0>(state[0], hi, lo);
        state[0] = hi^ks[0]^state[1];
        state[1] = lo;
    }
};

template <typename ResultType, std::size_t N>
struct PhiloxRound<ResultType, 4, N, true>
{
    static void round (ResultType *state, const ResultType *ks)
    {
        ResultType hi0 = 0;
        ResultType hi1 = 0;
        ResultType lo0 = 0;
        ResultType lo1 = 0;
        philox_hilo<4, 0>(state[0], hi0, lo0);
        philox_hilo<4, 1>(state[2], hi1, lo1);
        state[0] = hi1^state[1]^ks[0];
        state[1] = lo1;
        state[2] = hi0^state[3]^ks[1];
        state[3] = lo0;
    }
};

} // namespace vsmc::internal

/// \brief Philox RNG engine reimplemented
/// \ingroup R123RNG
///
/// \details
/// This is a reimplementation of the algorithm Philox as described in
/// [Parallel Random Numbers: As Easy as 1, 2, 3][r123paper] and implemented in
/// [Random123][r123lib]. Currently it is much slower than the original. The
/// original implementation use some platform dependent assembly or intrinsics
/// to optimize the performance. This implementation use standard C++. So it is
/// more portable. But whenever possible, the original should be used.
///
/// [r123paper]:http://sc11.supercomputing.org/schedule/event_detail.php?evid=pap274
/// [r123lib]: https://www.deshawresearch.com/resources_random123.html
///
/// The implementation is almost identical to the original. Compared to
/// `r123:Engine<Philox2x32>` etc., when using the default constructor of the
/// one with a single seed, the output shall be exactly the same for the first
/// \f$2^n\f$ iterations, where \f$n\f$ is the number of bits (32 or 64).
/// Further iterations may produce different results, as vSMC increment the
/// counter slightly differently, but it still cover the same range and has the
/// same period as the original.
template <typename ResultType, std::size_t K, std::size_t R = 10>
class PhiloxEngine
{
    public :

    typedef ResultType result_type;
    typedef StaticVector<ResultType, K / 2> key_type;
    typedef StaticVector<ResultType, K> ctr_type;

    explicit PhiloxEngine (result_type s = 0) : remain_(0)
    {
        VSMC_STATIC_ASSERT_RNG_PHILOX;
        seed(s);
    }

    template <typename SeedSeq>
    explicit PhiloxEngine (SeedSeq &seq, typename cxx11::enable_if<
            !internal::is_seed_sequence<SeedSeq, ResultType>::value>::type * =
{'fraction_non_alphanumeric': 0.11437718277066357, 'fraction_numerical': 0.0459837019790454, 'mean_word_length': 3.496238141969251, 'pattern_counts': {'":': 0, '<': 124, '<?xml version=': 0, '>': 144, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2983338', 'n_tokens_mistral': 5671, 'n_tokens_neox': 5199, 'n_words': 1054}