mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 07:36:08 +00:00

Drive-by: - Adds `constexpr_random.h` for pseudo-randomizing or shuffling in tests for constant evaluation.
439 lines
15 KiB
C++
439 lines
15 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 <climits>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "test_macros.h"
|
|
|
|
namespace support {
|
|
|
|
namespace detail {
|
|
|
|
template <class>
|
|
struct is_valid_integer_type_for_random : std::false_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<std::int8_t> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<short> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<int> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<long> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<long long> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<std::uint8_t> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<unsigned short> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<unsigned int> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<unsigned long> : std::true_type {};
|
|
template <>
|
|
struct is_valid_integer_type_for_random<unsigned long long> : 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 <class, class = void>
|
|
struct is_valid_urng : std::false_type {};
|
|
template <class Gen>
|
|
struct is_valid_urng<
|
|
Gen,
|
|
typename std::enable_if<std::is_unsigned<typename Gen::result_type>::value &&
|
|
std::is_same<decltype(std::declval<Gen&>()()), typename Gen::result_type>::value>::type>
|
|
: std::true_type {};
|
|
|
|
template <class UIntType, UIntType N, std::size_t R>
|
|
struct meta_log2_imp;
|
|
|
|
template <unsigned long long N, std::size_t R>
|
|
struct meta_log2_imp<unsigned long long, N, R> {
|
|
static const std::size_t value =
|
|
N & ((unsigned long long)(1) << R) ? R : meta_log2_imp<unsigned long long, N, R - 1>::value;
|
|
};
|
|
|
|
template <unsigned long long N>
|
|
struct meta_log2_imp<unsigned long long, N, 0> {
|
|
static const std::size_t value = 0;
|
|
};
|
|
|
|
template <size_t R>
|
|
struct meta_log2_imp<unsigned long long, 0, R> {
|
|
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<unsigned long long, (N >> 64), 63>::value)
|
|
: meta_log2_imp<unsigned long long, N, 63>::value;
|
|
};
|
|
#endif // TEST_HAS_NO_INT128
|
|
|
|
template <class UIntType, UIntType N>
|
|
struct meta_log2 {
|
|
static const size_t value = meta_log2_imp<
|
|
#ifndef TEST_HAS_NO_INT128
|
|
typename std::conditional<sizeof(UIntType) <= sizeof(unsigned long long), unsigned long long, __uint128_t>::type,
|
|
#else
|
|
unsigned long long,
|
|
#endif // TEST_HAS_NO_INT128
|
|
N,
|
|
sizeof(UIntType) * CHAR_BIT - 1 >::value;
|
|
};
|
|
|
|
#ifdef TEST_COMPILER_MSVC
|
|
template <int Width, class T, typename std::enable_if<(Width <= 1), int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT {
|
|
return static_cast<int>(~n) & 1;
|
|
}
|
|
|
|
template <int Width, class T, typename std::enable_if<(Width > 1), int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero_div_conq(T n) TEST_NOEXCEPT {
|
|
return n >= (static_cast<T>(1) << (Width / 2))
|
|
? detail::countl_zero_div_conq<Width / 2>(n >> (Width / 2))
|
|
: detail::countl_zero_div_conq<Width / 2>(n) + Width / 2;
|
|
}
|
|
#endif
|
|
|
|
template <class T, typename std::enable_if<std::is_same<T, unsigned int>::value, int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
|
|
#ifdef TEST_COMPILER_MSVC
|
|
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
|
|
#else
|
|
return __builtin_clz(n);
|
|
#endif
|
|
}
|
|
|
|
template <class T, typename std::enable_if<std::is_same<T, unsigned long>::value, int>::type = 0>
|
|
TEST_CONSTEXPR_CXX14 int countl_zero(T n) TEST_NOEXCEPT {
|
|
#ifdef TEST_COMPILER_MSVC
|
|
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
|
|
#else
|
|
return __builtin_clzl(n);
|
|
#endif
|
|
}
|
|
|
|
template <class T, typename std::enable_if<std::is_same<T, unsigned long long>::value, int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
|
|
#ifdef TEST_COMPILER_MSVC
|
|
return detail::countl_zero_div_conq<std::numeric_limits<T>::digits>(n);
|
|
#else
|
|
return __builtin_clzll(n);
|
|
#endif
|
|
}
|
|
|
|
#ifndef TEST_HAS_NO_INT128
|
|
template <class T, typename std::enable_if<std::is_same<T, __uint128_t>::value, int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
|
|
return n > std::numeric_limits<std::uint64_t>::max()
|
|
? detail::countl_zero(static_cast<std::uint64_t>(n >> 64))
|
|
: detail::countl_zero(static_cast<std::uint64_t>(n)) + 64;
|
|
}
|
|
#endif // TEST_HAS_NO_INT128
|
|
|
|
template <class T,
|
|
typename std::enable_if<std::is_same<T, unsigned char>::value || std::is_same<T, unsigned short>::value,
|
|
int>::type = 0>
|
|
TEST_CONSTEXPR int countl_zero(T n) TEST_NOEXCEPT {
|
|
return detail::countl_zero(static_cast<unsigned int>(n)) -
|
|
(std::numeric_limits<unsigned int>::digits - std::numeric_limits<T>::digits);
|
|
}
|
|
|
|
template <class Engine, class UIntType>
|
|
class independent_bits_engine {
|
|
public:
|
|
typedef UIntType result_type;
|
|
|
|
private:
|
|
typedef typename Engine::result_type engine_result_type;
|
|
typedef
|
|
typename std::conditional<sizeof(engine_result_type) <= sizeof(result_type), result_type, engine_result_type>::
|
|
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<working_result_type, rp>::value;
|
|
static TEST_CONSTEXPR const std::size_t w_digits = std::numeric_limits<working_result_type>::digits;
|
|
static TEST_CONSTEXPR const std::size_t e_digits = std::numeric_limits<engine_result_type>::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>(engine_result_type(~0) >> (e_digits - wid0_))
|
|
: engine_result_type(0);
|
|
mask1_ = wid0_ < e_digits - 1 ? static_cast<engine_result_type>(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<bool, (rp != 0)>()); }
|
|
|
|
private:
|
|
TEST_CONSTEXPR_CXX14 result_type generate(std::false_type) { return static_cast<result_type>(eng_() & mask0_); }
|
|
|
|
TEST_CONSTEXPR_CXX14 result_type generate(std::true_type) {
|
|
const std::size_t r_digits = std::numeric_limits<result_type>::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<engine_result_type>(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<engine_result_type>(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 IntType = int>
|
|
class uniform_int_distribution {
|
|
static_assert(detail::is_valid_integer_type_for_random<IntType>::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<result_type>::max()) {}
|
|
#endif
|
|
TEST_CONSTEXPR explicit param_type(result_type ax, result_type bx = std::numeric_limits<result_type>::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<result_type>::max())
|
|
: param_(ax, bx) {}
|
|
TEST_CONSTEXPR explicit uniform_int_distribution(const param_type& param) : param_(param) {}
|
|
TEST_CONSTEXPR_CXX14 void reset() {}
|
|
|
|
// generating functions
|
|
template <class URNG>
|
|
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 <class URNG>
|
|
TEST_CONSTEXPR_CXX14 result_type operator()(URNG& gen, const param_type& param)
|
|
TEST_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK {
|
|
static_assert(detail::is_valid_urng<URNG>::value, "invalid uniform random bit generator used");
|
|
typedef typename std::conditional<sizeof(result_type) <= sizeof(std::uint32_t),
|
|
std::uint32_t,
|
|
typename std::make_unsigned<result_type>::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<UIntType>::digits;
|
|
typedef detail::independent_bits_engine<URNG, UIntType> Eng;
|
|
if (rp == 0)
|
|
return static_cast<result_type>(Eng(gen, ur_digits)());
|
|
std::size_t width = ur_digits - detail::countl_zero(rp) - 1;
|
|
if ((rp & (std::numeric_limits<UIntType>::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<result_type>(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<result_type>(-1); }
|
|
#if TEST_STD_VER < 11
|
|
static const result_type min_value = 0;
|
|
static const result_type max_value = static_cast<result_type>(-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<result_type>(status_ >> 16);
|
|
}
|
|
};
|
|
|
|
template <class RandomAccessIterator, class UniformRandomNumberGenerator>
|
|
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<RandomAccessIterator>::difference_type difference_type;
|
|
typedef uniform_int_distribution<ptrdiff_t> 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
|