diff options
| author | Martin Hořeňovský | 2023-12-08 22:01:36 +0100 |
|---|---|---|
| committer | Martin Hořeňovský | 2023-12-10 19:53:39 +0100 |
| commit | ed9d672b5ced5647d8420f3b4d040199a3281a2b (patch) | |
| tree | 4e97894ecb0ff8f8fb74e05ef3dba87c385b32ce /tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp | |
| parent | 04a829b0e13401049707c7c70e95c3cc0c73ba22 (diff) | |
| download | catch3-ed9d672b5ced5647d8420f3b4d040199a3281a2b.tar.gz | |
Add uniform_integer_distribution
Diffstat (limited to 'tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp')
| -rw-r--r-- | tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp | 385 |
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)); +} |