Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp
diff options
context:
space:
mode:
authorMartin Hořeňovský2023-12-08 22:01:36 +0100
committerMartin Hořeňovský2023-12-10 19:53:39 +0100
commited9d672b5ced5647d8420f3b4d040199a3281a2b (patch)
tree4e97894ecb0ff8f8fb74e05ef3dba87c385b32ce /tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp
parent04a829b0e13401049707c7c70e95c3cc0c73ba22 (diff)
downloadcatch3-ed9d672b5ced5647d8420f3b4d040199a3281a2b.tar.gz
Add uniform_integer_distribution
Diffstat (limited to 'tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp')
-rw-r--r--tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp385
1 files changed, 385 insertions, 0 deletions
diff --git a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp b/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp
index d432b445..5c17fb2b 100644
--- a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp
+++ b/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp
@@ -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));
+}