mirror of
https://github.com/catchorg/Catch2.git
synced 2025-08-01 21:05:39 +02:00
Add uniform_integer_distribution
This commit is contained in:
@@ -12,7 +12,9 @@
|
||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||
#include <catch2/internal/catch_random_seed_generation.hpp>
|
||||
#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
|
||||
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_range_equals.hpp>
|
||||
|
||||
TEST_CASE("Our PCG implementation provides expected results for known seeds", "[rng]") {
|
||||
Catch::SimplePcg32 rng;
|
||||
@@ -104,3 +106,386 @@ TEST_CASE( "fillBitsFrom - shortening and stretching", "[rng][approvals]" ) {
|
||||
REQUIRE( stretched == 0xccbe'5f04'a424'a486 );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("uniform_integer_distribution can return the bounds", "[rng][distribution]") {
|
||||
Catch::uniform_integer_distribution<int32_t> dist( -10, 10 );
|
||||
REQUIRE( dist.a() == -10 );
|
||||
REQUIRE( dist.b() == 10 );
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
static void CheckReturnValue(Catch::uniform_integer_distribution<T>& dist,
|
||||
Catch::SimplePcg32& rng,
|
||||
T target) {
|
||||
REQUIRE( dist.a() == dist.b() );
|
||||
for (int i = 0; i < 1'000; ++i) {
|
||||
REQUIRE( dist( rng ) == target );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle unit ranges",
|
||||
"[rng][distribution][approvals]",
|
||||
unsigned char,
|
||||
signed char,
|
||||
char,
|
||||
uint8_t,
|
||||
int8_t,
|
||||
uint16_t,
|
||||
int16_t,
|
||||
uint32_t,
|
||||
int32_t,
|
||||
uint64_t,
|
||||
int64_t ) {
|
||||
// We want random seed to sample different parts of the rng state,
|
||||
// the output is predetermined anyway
|
||||
std::random_device rd;
|
||||
auto seed = rd();
|
||||
CAPTURE( seed );
|
||||
Catch::SimplePcg32 pcg( seed );
|
||||
|
||||
// We check unitary ranges of 3 different values, min for type, max for type,
|
||||
// some value inbetween just to make sure
|
||||
SECTION("lowest value") {
|
||||
constexpr auto lowest = std::numeric_limits<TestType>::min();
|
||||
Catch::uniform_integer_distribution<TestType> dist( lowest, lowest );
|
||||
CheckReturnValue( dist, pcg, lowest );
|
||||
}
|
||||
SECTION( "highest value" ) {
|
||||
constexpr auto highest = std::numeric_limits<TestType>::max();
|
||||
Catch::uniform_integer_distribution<TestType> dist( highest, highest );
|
||||
CheckReturnValue( dist, pcg, highest );
|
||||
}
|
||||
SECTION( "some value" ) {
|
||||
constexpr auto some = TestType( 42 );
|
||||
Catch::uniform_integer_distribution<TestType> dist( some, some );
|
||||
CheckReturnValue( dist, pcg, some );
|
||||
}
|
||||
}
|
||||
|
||||
// Bool needs its own test because it doesn't have a valid "third" value
|
||||
TEST_CASE( "uniform_integer_distribution can handle boolean unit ranges",
|
||||
"[rng][distribution][approvals]" ) {
|
||||
// We want random seed to sample different parts of the rng state,
|
||||
// the output is predetermined anyway
|
||||
std::random_device rd;
|
||||
auto seed = rd();
|
||||
CAPTURE( seed );
|
||||
Catch::SimplePcg32 pcg( seed );
|
||||
|
||||
// We check unitary ranges of 3 different values, min for type, max for
|
||||
// type, some value inbetween just to make sure
|
||||
SECTION( "lowest value" ) {
|
||||
Catch::uniform_integer_distribution<bool> dist( false, false );
|
||||
CheckReturnValue( dist, pcg, false );
|
||||
}
|
||||
SECTION( "highest value" ) {
|
||||
Catch::uniform_integer_distribution<bool> dist( true, true );
|
||||
CheckReturnValue( dist, pcg, true );
|
||||
}
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle full width ranges",
|
||||
"[rng][distribution][approvals]",
|
||||
unsigned char,
|
||||
signed char,
|
||||
char,
|
||||
uint8_t,
|
||||
int8_t,
|
||||
uint16_t,
|
||||
int16_t,
|
||||
uint32_t,
|
||||
int32_t,
|
||||
uint64_t,
|
||||
int64_t ) {
|
||||
// We want random seed to sample different parts of the rng state,
|
||||
// the output is predetermined anyway
|
||||
std::random_device rd;
|
||||
auto seed = rd();
|
||||
CAPTURE( seed );
|
||||
Catch::SimplePcg32 pcg( seed );
|
||||
|
||||
constexpr auto lowest = std::numeric_limits<TestType>::min();
|
||||
constexpr auto highest = std::numeric_limits<TestType>::max();
|
||||
Catch::uniform_integer_distribution<TestType> dist( lowest, highest );
|
||||
STATIC_REQUIRE( std::is_same<TestType, decltype( dist( pcg ) )>::value );
|
||||
|
||||
// We need to do bit operations on the results, so we will have to
|
||||
// cast them to unsigned type.
|
||||
using BitType = std::make_unsigned_t<TestType>;
|
||||
BitType ORs = 0;
|
||||
BitType ANDs = BitType(-1);
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto bits = static_cast<BitType>( dist( pcg ) );
|
||||
ORs |= bits;
|
||||
ANDs &= bits;
|
||||
}
|
||||
// Assuming both our RNG and distribution are unbiased, asking for
|
||||
// the full range should essentially give us random bit generator.
|
||||
// Over long run, OR of all the generated values should have all
|
||||
// bits set to 1, while AND should have all bits set to 0.
|
||||
// The chance of this test failing for unbiased pipeline is
|
||||
// 1 / 2**iters, which for 100 iterations is astronomical.
|
||||
REQUIRE( ORs == BitType( -1 ) );
|
||||
REQUIRE( ANDs == 0 );
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
struct uniform_integer_test_params;
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<bool> {
|
||||
static constexpr bool lowest = false;
|
||||
static constexpr bool highest = true;
|
||||
// This seems weird, but it is an artifact of the specific seed
|
||||
static constexpr bool expected[] = { true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<char> {
|
||||
static constexpr char lowest = 32;
|
||||
static constexpr char highest = 126;
|
||||
static constexpr char expected[] = { 'k',
|
||||
'\\',
|
||||
'Z',
|
||||
'X',
|
||||
'`',
|
||||
'Q',
|
||||
';',
|
||||
'o',
|
||||
']',
|
||||
'T',
|
||||
'v',
|
||||
'p',
|
||||
':',
|
||||
'S',
|
||||
't' };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<uint8_t> {
|
||||
static constexpr uint8_t lowest = 3;
|
||||
static constexpr uint8_t highest = 123;
|
||||
static constexpr uint8_t expected[] = { 'c',
|
||||
'P',
|
||||
'M',
|
||||
'J',
|
||||
'U',
|
||||
'A',
|
||||
'%',
|
||||
'h',
|
||||
'Q',
|
||||
'F',
|
||||
'q',
|
||||
'i',
|
||||
'$',
|
||||
'E',
|
||||
'o' };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<int8_t> {
|
||||
static constexpr int8_t lowest = -27;
|
||||
static constexpr int8_t highest = 73;
|
||||
static constexpr int8_t expected[] = { '5',
|
||||
'%',
|
||||
'#',
|
||||
' ',
|
||||
'*',
|
||||
25,
|
||||
2,
|
||||
'9',
|
||||
'&',
|
||||
29,
|
||||
'A',
|
||||
':',
|
||||
1,
|
||||
28,
|
||||
'?' };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<uint16_t> {
|
||||
static constexpr uint16_t lowest = 123;
|
||||
static constexpr uint16_t highest = 33333;
|
||||
static constexpr uint16_t expected[] = { 26684,
|
||||
21417,
|
||||
20658,
|
||||
19791,
|
||||
22896,
|
||||
17433,
|
||||
9806,
|
||||
27948,
|
||||
21767,
|
||||
18588,
|
||||
30556,
|
||||
28244,
|
||||
9439,
|
||||
18293,
|
||||
29949 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<int16_t> {
|
||||
static constexpr int16_t lowest = -17222;
|
||||
static constexpr int16_t highest = 17222;
|
||||
static constexpr int16_t expected[] = { 10326,
|
||||
4863,
|
||||
4076,
|
||||
3177,
|
||||
6397,
|
||||
731,
|
||||
-7179,
|
||||
11637,
|
||||
5226,
|
||||
1929,
|
||||
14342,
|
||||
11944,
|
||||
-7560,
|
||||
1623,
|
||||
13712 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<uint32_t> {
|
||||
static constexpr uint32_t lowest = 17222;
|
||||
static constexpr uint32_t highest = 234234;
|
||||
static constexpr uint32_t expected[] = { 190784,
|
||||
156367,
|
||||
151409,
|
||||
145743,
|
||||
166032,
|
||||
130337,
|
||||
80501,
|
||||
199046,
|
||||
158654,
|
||||
137883,
|
||||
216091,
|
||||
200981,
|
||||
78099,
|
||||
135954,
|
||||
212120 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<int32_t> {
|
||||
static constexpr int32_t lowest = -237272;
|
||||
static constexpr int32_t highest = 234234;
|
||||
static constexpr int32_t expected[] = { 139829,
|
||||
65050,
|
||||
54278,
|
||||
41969,
|
||||
86051,
|
||||
8494,
|
||||
-99785,
|
||||
157781,
|
||||
70021,
|
||||
24890,
|
||||
194815,
|
||||
161985,
|
||||
-105004,
|
||||
20699,
|
||||
186186 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<uint64_t> {
|
||||
static constexpr uint64_t lowest = 1234;
|
||||
static constexpr uint64_t highest = 1234567890;
|
||||
static constexpr uint64_t expected[] = { 987382749,
|
||||
763380386,
|
||||
846572137,
|
||||
359990258,
|
||||
804599765,
|
||||
1131353566,
|
||||
346324913,
|
||||
1108760730,
|
||||
1141693933,
|
||||
856999148,
|
||||
879390623,
|
||||
1149485521,
|
||||
900556586,
|
||||
952385958,
|
||||
807916408 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct uniform_integer_test_params<int64_t> {
|
||||
static constexpr int64_t lowest = -1234567890;
|
||||
static constexpr int64_t highest = 1234567890;
|
||||
static constexpr int64_t expected[] = { 740197113,
|
||||
292191940,
|
||||
458575608,
|
||||
-514589122,
|
||||
374630781,
|
||||
1028139036,
|
||||
-541919840,
|
||||
982953318,
|
||||
1048819790,
|
||||
479429651,
|
||||
524212647,
|
||||
1064402981,
|
||||
566544615,
|
||||
670203462,
|
||||
381264073 };
|
||||
};
|
||||
|
||||
// We need these definitions for C++14 and earlier, but
|
||||
// GCC will complain about them in newer C++ standards
|
||||
#if __cplusplus <= 201402L
|
||||
constexpr bool uniform_integer_test_params<bool>::expected[];
|
||||
constexpr char uniform_integer_test_params<char>::expected[];
|
||||
constexpr uint8_t uniform_integer_test_params<uint8_t>::expected[];
|
||||
constexpr int8_t uniform_integer_test_params<int8_t>::expected[];
|
||||
constexpr uint16_t uniform_integer_test_params<uint16_t>::expected[];
|
||||
constexpr int16_t uniform_integer_test_params<int16_t>::expected[];
|
||||
constexpr uint32_t uniform_integer_test_params<uint32_t>::expected[];
|
||||
constexpr int32_t uniform_integer_test_params<int32_t>::expected[];
|
||||
constexpr uint64_t uniform_integer_test_params<uint64_t>::expected[];
|
||||
constexpr int64_t uniform_integer_test_params<int64_t>::expected[];
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE( "uniform_integer_distribution is reproducible",
|
||||
"[rng][distribution][approvals]",
|
||||
bool,
|
||||
char,
|
||||
uint8_t,
|
||||
int8_t,
|
||||
uint16_t,
|
||||
int16_t,
|
||||
uint32_t,
|
||||
int32_t,
|
||||
uint64_t,
|
||||
int64_t) {
|
||||
Catch::SimplePcg32 pcg( 0xaabb'ccdd );
|
||||
|
||||
constexpr auto lowest = uniform_integer_test_params<TestType>::lowest;
|
||||
constexpr auto highest = uniform_integer_test_params<TestType>::highest;
|
||||
Catch::uniform_integer_distribution<TestType> dist(lowest, highest);
|
||||
|
||||
constexpr auto iters = 15;
|
||||
std::array<TestType, iters> generated;
|
||||
for (int i = 0; i < iters; ++i) {
|
||||
generated[i] = dist( pcg );
|
||||
}
|
||||
|
||||
REQUIRE_THAT(generated, Catch::Matchers::RangeEquals(uniform_integer_test_params<TestType>::expected));
|
||||
}
|
||||
|
Reference in New Issue
Block a user