2023-02-20 21:06:01 +01:00
|
|
|
#include "global.h"
|
|
|
|
#include "test.h"
|
|
|
|
#include "random.h"
|
|
|
|
|
2023-07-05 19:46:40 +02:00
|
|
|
// We expect each element to have an indexSum of 3.5 * 1024.
|
|
|
|
// Therefore the maximum error is 8*3584, or 28672.
|
|
|
|
#define SHUFFLE_TEST_IMPL \
|
|
|
|
u32 i, j, error; \
|
|
|
|
u16 indexSum[7]; \
|
|
|
|
memset(indexSum, 0, sizeof(indexSum)); \
|
|
|
|
for (i = 0; i < 1024; i++) \
|
|
|
|
{ \
|
|
|
|
Shuffle(array, ARRAY_COUNT(array), sizeof(array[0])); \
|
|
|
|
for (j = 0; j < ARRAY_COUNT(array); j++) \
|
|
|
|
indexSum[array[j]] += j; \
|
|
|
|
} \
|
|
|
|
error = 0; \
|
|
|
|
for (i = 0; i < ARRAY_COUNT(indexSum); i++) \
|
|
|
|
error += abs(3584 - indexSum[i]); \
|
|
|
|
EXPECT_LT(error, (int)(28672 * 0.025));
|
|
|
|
|
|
|
|
TEST("Shuffle randomizes the array [Shuffle8]")
|
|
|
|
{
|
|
|
|
u8 array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
|
|
SHUFFLE_TEST_IMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST("Shuffle randomizes the array [Shuffle16]")
|
|
|
|
{
|
|
|
|
u16 array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
|
|
SHUFFLE_TEST_IMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST("Shuffle randomizes the array [Shuffle32]")
|
|
|
|
{
|
|
|
|
u32 array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
|
|
SHUFFLE_TEST_IMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST("Shuffle randomizes the array [Shuffle64]")
|
|
|
|
{
|
|
|
|
u64 array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
|
|
SHUFFLE_TEST_IMPL;
|
|
|
|
}
|
|
|
|
|
2023-02-20 21:06:01 +01:00
|
|
|
TEST("RandomUniform generates lo..hi")
|
|
|
|
{
|
|
|
|
u32 lo, hi, i;
|
|
|
|
PARAMETRIZE { lo = 0; hi = 1; }
|
|
|
|
PARAMETRIZE { lo = 0; hi = 2; }
|
|
|
|
PARAMETRIZE { lo = 0; hi = 3; }
|
|
|
|
PARAMETRIZE { lo = 2; hi = 4; }
|
|
|
|
for (i = 0; i < 1024; i++)
|
|
|
|
{
|
|
|
|
u32 r = RandomUniformDefault(RNG_NONE, lo, hi);
|
|
|
|
EXPECT(lo <= r && r <= hi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-19 17:44:21 +02:00
|
|
|
static bool32 InvalidEven(u32 n)
|
|
|
|
{
|
|
|
|
return n % 2 == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST("RandomUniformExcept generates lo..hi")
|
|
|
|
{
|
|
|
|
u32 lo, hi, i;
|
|
|
|
PARAMETRIZE { lo = 0; hi = 1; }
|
|
|
|
PARAMETRIZE { lo = 0; hi = 2; }
|
|
|
|
PARAMETRIZE { lo = 0; hi = 3; }
|
|
|
|
PARAMETRIZE { lo = 2; hi = 4; }
|
|
|
|
for (i = 0; i < 1024; i++)
|
|
|
|
{
|
|
|
|
u32 r = RandomUniformExceptDefault(RNG_NONE, lo, hi, InvalidEven);
|
|
|
|
EXPECT(lo <= r && r <= hi && r % 2 != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-20 21:06:01 +01:00
|
|
|
TEST("RandomWeighted generates 0..n-1")
|
|
|
|
{
|
|
|
|
u32 n, sum, i;
|
|
|
|
static const u8 ws[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
|
|
|
|
PARAMETRIZE { n = 1; }
|
|
|
|
PARAMETRIZE { n = 2; }
|
|
|
|
PARAMETRIZE { n = 3; }
|
|
|
|
PARAMETRIZE { n = 4; }
|
|
|
|
ASSUME(n <= ARRAY_COUNT(ws));
|
|
|
|
for (i = 0, sum = 0; i < n; i++)
|
|
|
|
sum += ws[i];
|
|
|
|
for (i = 0; i < 1024; i++)
|
|
|
|
{
|
|
|
|
u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, n, ws);
|
|
|
|
EXPECT(0 <= r && r < n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-27 18:32:16 +02:00
|
|
|
TEST("RandomElement generates an element")
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
static const u8 es[4] = { 1, 2, 4, 8 };
|
|
|
|
for (i = 0; i < 1024; i++)
|
|
|
|
{
|
|
|
|
u32 e = *(const u8 *)RandomElementArrayDefault(RNG_NONE, es, sizeof(es[0]), ARRAY_COUNT(es));
|
|
|
|
EXPECT(e == 1 || e == 2 || e == 4 || e == 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-20 21:06:01 +01:00
|
|
|
TEST("RandomUniform generates uniform distribution")
|
|
|
|
{
|
|
|
|
u32 i, error;
|
|
|
|
u16 distribution[4];
|
|
|
|
|
|
|
|
memset(distribution, 0, sizeof(distribution));
|
|
|
|
for (i = 0; i < 4096; i++)
|
|
|
|
{
|
2023-03-27 18:32:16 +02:00
|
|
|
u32 r = RandomUniformDefault(RNG_NONE, 0, ARRAY_COUNT(distribution) - 1);
|
2023-02-20 21:06:01 +01:00
|
|
|
EXPECT(0 <= r && r < ARRAY_COUNT(distribution));
|
|
|
|
distribution[r]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(distribution); i++)
|
|
|
|
error += abs(UQ_4_12(0.25) - distribution[i]);
|
|
|
|
|
|
|
|
EXPECT_LT(error, UQ_4_12(0.025));
|
|
|
|
}
|
|
|
|
|
2023-07-19 17:44:21 +02:00
|
|
|
TEST("RandomUniformExcept generates uniform distribution")
|
|
|
|
{
|
|
|
|
u32 i, error;
|
|
|
|
u16 distribution[4];
|
|
|
|
|
|
|
|
memset(distribution, 0, sizeof(distribution));
|
|
|
|
for (i = 0; i < 4096; i++)
|
|
|
|
{
|
|
|
|
u32 r = RandomUniformExceptDefault(RNG_NONE, 0, ARRAY_COUNT(distribution) - 1, InvalidEven);
|
|
|
|
EXPECT(0 <= r && r < ARRAY_COUNT(distribution));
|
|
|
|
distribution[r]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(distribution); i++)
|
|
|
|
{
|
|
|
|
if (i % 2 != 0)
|
|
|
|
error += abs(UQ_4_12(0.5) - distribution[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_LT(error, UQ_4_12(0.05));
|
|
|
|
}
|
|
|
|
|
2023-02-20 21:06:01 +01:00
|
|
|
TEST("RandomWeighted generates distribution in proportion to the weights")
|
|
|
|
{
|
|
|
|
u32 i, sum, error;
|
|
|
|
static const u8 ws[4] = { 1, 2, 2, 3 };
|
|
|
|
u16 distribution[ARRAY_COUNT(ws)];
|
|
|
|
|
|
|
|
for (i = 0, sum = 0; i < ARRAY_COUNT(ws); i++)
|
|
|
|
sum += ws[i];
|
|
|
|
|
|
|
|
memset(distribution, 0, sizeof(distribution));
|
|
|
|
for (i = 0; i < 4096; i++)
|
|
|
|
{
|
|
|
|
u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, ARRAY_COUNT(ws), ws);
|
|
|
|
EXPECT(0 <= r && r < ARRAY_COUNT(ws));
|
|
|
|
distribution[r]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
error += abs(UQ_4_12(0.125) - distribution[0]);
|
|
|
|
error += abs(UQ_4_12(0.250) - distribution[1]);
|
|
|
|
error += abs(UQ_4_12(0.250) - distribution[2]);
|
|
|
|
error += abs(UQ_4_12(0.375) - distribution[3]);
|
|
|
|
|
|
|
|
EXPECT_LT(error, UQ_4_12(0.025));
|
|
|
|
}
|
2023-03-27 18:32:16 +02:00
|
|
|
|
|
|
|
TEST("RandomElement generates a uniform distribution")
|
|
|
|
{
|
|
|
|
u32 i, error;
|
|
|
|
static const u8 es[4] = { 1, 2, 4, 8 };
|
|
|
|
u16 distribution[9];
|
|
|
|
|
|
|
|
memset(distribution, 0, sizeof(distribution));
|
|
|
|
for (i = 0; i < 4096; i++)
|
|
|
|
{
|
|
|
|
u32 e = *(const u8 *)RandomElementArrayDefault(RNG_NONE, es, sizeof(es[0]), ARRAY_COUNT(es));
|
|
|
|
distribution[e]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(es); i++)
|
|
|
|
error += abs(UQ_4_12(0.25) - distribution[es[i]]);
|
|
|
|
|
|
|
|
EXPECT_LT(error, UQ_4_12(0.025));
|
|
|
|
}
|
2023-07-24 13:51:43 +02:00
|
|
|
|
|
|
|
TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
struct Benchmark mulBenchmark, modBenchmark;
|
|
|
|
u32 mulSum = 0, modSum = 0;
|
|
|
|
|
|
|
|
BENCHMARK(&mulBenchmark)
|
|
|
|
{
|
|
|
|
mulSum += RandomUniformDefault(RNG_NONE, 0, 1);
|
|
|
|
mulSum += RandomUniformDefault(RNG_NONE, 0, 2);
|
|
|
|
mulSum += RandomUniformDefault(RNG_NONE, 0, 3);
|
|
|
|
mulSum += RandomUniformDefault(RNG_NONE, 0, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
BENCHMARK(&modBenchmark)
|
|
|
|
{
|
|
|
|
modSum += Random() % 2;
|
|
|
|
modSum += Random() % 3;
|
|
|
|
modSum += Random() % 4;
|
|
|
|
modSum += Random() % 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_FASTER(mulBenchmark, modBenchmark);
|
|
|
|
|
|
|
|
// Reference mulSum/modSum to prevent optimization.
|
|
|
|
// These numbers are different because multiplication and modulus
|
|
|
|
// have subtly different biases (so subtle that it's irrelevant for
|
|
|
|
// our purposes).
|
|
|
|
EXPECT_EQ(mulSum, 3);
|
|
|
|
EXPECT_EQ(modSum, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
struct Benchmark mulBenchmark, modBenchmark;
|
|
|
|
u32 mulSum = 0, modSum = 0;
|
|
|
|
|
|
|
|
BENCHMARK(&mulBenchmark)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
mulSum += RandomUniformDefault(RNG_NONE, 0, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
BENCHMARK(&modBenchmark)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
modSum += Random() % (i + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_FASTER(mulBenchmark, modBenchmark);
|
|
|
|
|
|
|
|
// Reference mulSum/modSum to prevent optimization.
|
|
|
|
EXPECT_EQ(mulSum, 232);
|
|
|
|
EXPECT_EQ(modSum, 249);
|
|
|
|
}
|