//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef TEST_SUPPORT_CONSTEXPR_RANDOM_H #define TEST_SUPPORT_CONSTEXPR_RANDOM_H #include #include #include #include #include #include #include #include "test_macros.h" namespace support { namespace detail { template struct is_valid_integer_type_for_random : std::false_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; template <> struct is_valid_integer_type_for_random : std::true_type {}; #ifndef TEST_HAS_NO_INT128 template <> struct is_valid_integer_type_for_random<__int128_t> : std::true_type {}; template <> struct is_valid_integer_type_for_random<__uint128_t> : std::true_type {}; #endif // TEST_HAS_NO_INT128 template struct is_valid_urng : std::false_type {}; template struct is_valid_urng< Gen, typename std::enable_if::value && std::is_same()()), typename Gen::result_type>::value>::type> : std::true_type {}; template struct meta_log2_imp; template struct meta_log2_imp { static const std::size_t value = N & ((unsigned long long)(1) << R) ? R : meta_log2_imp::value; }; template struct meta_log2_imp { static const std::size_t value = 0; }; template struct meta_log2_imp { static const std::size_t value = R + 1; }; #ifndef TEST_HAS_NO_INT128 template <__uint128_t N, std::size_t R> struct meta_log2_imp<__uint128_t, N, R> { static const size_t value = (N >> 64) ? (64 + meta_log2_imp> 64), 63>::value) : meta_log2_imp::value; }; #endif // TEST_HAS_NO_INT128 template struct meta_log2 { static const size_t value = meta_log2_imp< #ifndef TEST_HAS_NO_INT128 typename std::conditional::type, #else unsigned long long, #endif // TEST_HAS_NO_INT128 N, sizeof(UIntType) * CHAR_BIT - 1 >::value; }; #ifdef TEST_COMPILER_MSVC template ::type = 0> TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT { return static_cast(~n) & 1; } template 1), int>::type = 0> TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT { return n >= (static_cast(1) << (Width / 2)) ? detail::countl_zero_div_conq(n >> (Width / 2)) : detail::countl_zero_div_conq(n) + Width / 2; } #endif template ::value, int>::type = 0> TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT { #ifdef TEST_COMPILER_MSVC return detail::countl_zero_div_conq::digits>(n); #else return __builtin_clz(n); #endif } template ::value, int>::type = 0> TEST_CONSTEXPR_CXX14 int countl_zero(T n) TEST_NOEXCEPT { #ifdef TEST_COMPILER_MSVC return detail::countl_zero_div_conq::digits>(n); #else return __builtin_clzl(n); #endif } template ::value, int>::type = 0> TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT { #ifdef TEST_COMPILER_MSVC return detail::countl_zero_div_conq::digits>(n); #else return __builtin_clzll(n); #endif } #ifndef TEST_HAS_NO_INT128 template ::value, int>::type = 0> TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT { return n > std::numeric_limits::max() ? detail::countl_zero(static_cast(n >> 64)) : detail::countl_zero(static_cast(n)) + 64; } #endif // TEST_HAS_NO_INT128 template ::value || std::is_same::value, int>::type = 0> TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT { return detail::countl_zero(static_cast(n)) - (std::numeric_limits::digits - std::numeric_limits::digits); } template class independent_bits_engine { public: typedef UIntType result_type; private: typedef typename Engine::result_type engine_result_type; typedef typename std::conditional:: type working_result_type; Engine& eng_; std::size_t width_; std::size_t wid0_; std::size_t round_count_all_; std::size_t round_count_regular_; working_result_type y0_; working_result_type y1_; engine_result_type mask0_; engine_result_type mask1_; #if TEST_STD_VER >= 11 static constexpr working_result_type rp = Engine::max() - Engine::min() + working_result_type(1); #else static const working_result_type rp = Engine::max_value - Engine::min_value + working_result_type(1); #endif static TEST_CONSTEXPR const std::size_t rp_log2 = meta_log2::value; static TEST_CONSTEXPR const std::size_t w_digits = std::numeric_limits::digits; static TEST_CONSTEXPR const std::size_t e_digits = std::numeric_limits::digits; public: // constructors and seeding functions TEST_CONSTEXPR_CXX14 independent_bits_engine(Engine& eng, std::size_t width) : eng_(eng), width_(width), wid0_(), round_count_all_(), round_count_regular_(), y0_(), y1_(), mask0_(), mask1_() { round_count_all_ = width_ / rp_log2 + (width_ % rp_log2 != 0); wid0_ = width_ / round_count_all_; if TEST_CONSTEXPR_CXX17 (rp == 0) { y0_ = rp; } else { if (wid0_ < w_digits) y0_ = (rp >> wid0_) << wid0_; else y0_ = 0; } if (rp - y0_ > y0_ / round_count_all_) { ++round_count_all_; wid0_ = width_ / round_count_all_; if (wid0_ < w_digits) y0_ = (rp >> wid0_) << wid0_; else y0_ = 0; } round_count_regular_ = round_count_all_ - width_ % round_count_all_; if (wid0_ < w_digits - 1) y1_ = (rp >> (wid0_ + 1)) << (wid0_ + 1); else y1_ = 0; mask0_ = wid0_ > 0 ? static_cast(engine_result_type(~0) >> (e_digits - wid0_)) : engine_result_type(0); mask1_ = wid0_ < e_digits - 1 ? static_cast(engine_result_type(~0) >> (e_digits - (wid0_ + 1))) : engine_result_type(~0); } // generating functions TEST_CONSTEXPR_CXX14 result_type operator()() { return generate(std::integral_constant()); } private: TEST_CONSTEXPR_CXX14 result_type generate(std::false_type) { return static_cast(eng_() & mask0_); } TEST_CONSTEXPR_CXX14 result_type generate(std::true_type) { const std::size_t r_digits = std::numeric_limits::digits; result_type result = 0; for (std::size_t k = 0; k < round_count_regular_; ++k) { engine_result_type eng_result = 0; do { eng_result = static_cast(eng_() - Engine::min()); } while (eng_result >= y0_); if (wid0_ < r_digits) result <<= wid0_; else result = 0; result += eng_result & mask0_; } for (std::size_t k = round_count_regular_; k < round_count_all_; ++k) { engine_result_type eng_result = 0; do { eng_result = static_cast(eng_() - Engine::min()); } while (eng_result >= y1_); if (wid0_ < r_digits - 1) result <<= wid0_ + 1; else result = 0; result += eng_result & mask1_; } return result; } }; } // namespace detail template class uniform_int_distribution { static_assert(detail::is_valid_integer_type_for_random::value, "IntType must be a supported integer type"); public: // types typedef IntType result_type; class param_type { result_type a_; result_type b_; public: typedef uniform_int_distribution distribution_type; #if TEST_STD_VER >= 11 constexpr param_type() : param_type(0) {} #else param_type() : a_(0), b_(std::numeric_limits::max()) {} #endif TEST_CONSTEXPR explicit param_type(result_type ax, result_type bx = std::numeric_limits::max()) : a_(ax), b_(bx) {} TEST_CONSTEXPR result_type a() const { return a_; } TEST_CONSTEXPR result_type b() const { return b_; } #if TEST_STD_VER >= 20 friend bool operator==(const param_type&, const param_type&) = default; #else TEST_CONSTEXPR friend bool operator==(const param_type& lhs, const param_type& rhs) { return lhs.a_ == rhs.a_ && lhs.b_ == rhs.b_; } TEST_CONSTEXPR friend bool operator!=(const param_type& lhs, const param_type& rhs) { return !(lhs == rhs); } #endif }; private: param_type param_; public: // constructors and reset functions #if TEST_STD_VER >= 11 uniform_int_distribution() = default; #else uniform_int_distribution() {} #endif TEST_CONSTEXPR explicit uniform_int_distribution(result_type ax, result_type bx = std::numeric_limits::max()) : param_(ax, bx) {} TEST_CONSTEXPR explicit uniform_int_distribution(const param_type& param) : param_(param) {} TEST_CONSTEXPR_CXX14 void reset() {} // generating functions template TEST_CONSTEXPR_CXX14 result_type operator()(URNG& gen) { return (*this)(gen, param_); } #if TEST_HAS_FEATURE(no_sanitize) && !defined(TEST_COMPILER_GCC) # define TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK __attribute__((__no_sanitize__("unsigned-integer-overflow"))) #else # define TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK #endif template TEST_CONSTEXPR_CXX14 result_type operator()(URNG& gen, const param_type& param) TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK { static_assert(detail::is_valid_urng::value, "invalid uniform random bit generator used"); typedef typename std::conditional::type>::type UIntType; const UIntType rp = UIntType(param.b()) - UIntType(param.a()) + UIntType(1); if (rp == 1) return param.a(); const std::size_t ur_digits = std::numeric_limits::digits; typedef detail::independent_bits_engine Eng; if (rp == 0) return static_cast(Eng(gen, ur_digits)()); std::size_t width = ur_digits - detail::countl_zero(rp) - 1; if ((rp & (std::numeric_limits::max() >> (ur_digits - width))) != 0) ++width; Eng eng(gen, width); UIntType eng_result = 0; do { eng_result = eng(); } while (eng_result >= rp); return static_cast(eng_result + param.a()); } #undef TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK // property functions TEST_CONSTEXPR result_type a() const { return param_.a(); } TEST_CONSTEXPR result_type b() const { return param_.b(); } TEST_CONSTEXPR param_type param() const { return param_; } TEST_CONSTEXPR_CXX14 void param(const param_type& param) { param_ = param; } TEST_CONSTEXPR result_type min() const { return a(); } TEST_CONSTEXPR result_type max() const { return b(); } #if TEST_STD_VER >= 20 friend bool operator==(const uniform_int_distribution&, const uniform_int_distribution&) = default; #else TEST_CONSTEXPR friend bool operator==(const uniform_int_distribution& lhs, const uniform_int_distribution& rhs) { return lhs.param_ == rhs.param_; } TEST_CONSTEXPR friend bool operator!=(const uniform_int_distribution& lhs, const uniform_int_distribution& rhs) { return !(lhs == rhs); } #endif }; class simple_random_generator { // A linear congruential generator, using the constants used by MS UCRT. private: std::uint32_t status_; public: typedef std::uint16_t result_type; static TEST_CONSTEXPR result_type min() TEST_NOEXCEPT { return 0; } static TEST_CONSTEXPR result_type max() TEST_NOEXCEPT { return static_cast(-1); } #if TEST_STD_VER < 11 static const result_type min_value = 0; static const result_type max_value = static_cast(-1); #endif static TEST_CONSTEXPR const result_type default_seed = 19937; #if TEST_STD_VER >= 11 constexpr simple_random_generator() noexcept : simple_random_generator(default_seed) {} #else simple_random_generator() throw() : status_(default_seed) {} #endif TEST_CONSTEXPR explicit simple_random_generator(std::uint16_t s) TEST_NOEXCEPT : status_(s) {} TEST_CONSTEXPR_CXX14 result_type operator()() TEST_NOEXCEPT { status_ = status_ * 214013u + 2531011u; return static_cast(status_ >> 16); } }; template TEST_CONSTEXPR_CXX14 void #if TEST_STD_VER >= 11 shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator&& gen) #else shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator& gen) #endif { typedef typename std::iterator_traits::difference_type difference_type; typedef uniform_int_distribution dist; typedef typename dist::param_type param_type; RandomAccessIterator last_iter = last; difference_type diff = last_iter - first; if (diff > 1) { dist uid; for (--last_iter, (void)--diff; first < last; ++first, (void)--diff) { difference_type index = uid(gen, param_type(0, diff)); if (index != difference_type(0)) { using std::swap; swap(*first, *(first + index)); } } } } } // namespace support #endif // TEST_SUPPORT_CONSTEXPR_RANDOM_H