Shuffle implementation (#3097)

This commit is contained in:
Martin Griffin 2023-07-05 18:46:40 +01:00 committed by GitHub
parent 862602233d
commit eab4e32e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 0 deletions

View File

@ -20,6 +20,22 @@ u16 Random2(void);
void SeedRng(u16 seed);
void SeedRng2(u16 seed);
void Shuffle8(void *data, size_t n);
void Shuffle16(void *data, size_t n);
void Shuffle32(void *data, size_t n);
void ShuffleN(void *data, size_t n, size_t size);
static inline void Shuffle(void *data, size_t n, size_t size)
{
switch (size)
{
case 1: Shuffle8(data, n); break;
case 2: Shuffle16(data, n); break;
case 4: Shuffle32(data, n); break;
default: ShuffleN(data, n, size); break;
}
}
/* Structured random number generator.
* Instead of the caller converting bits from Random() to a meaningful
* value, the caller provides metadata that is used to return the

View File

@ -32,6 +32,48 @@ u16 Random2(void)
return gRng2Value >> 16;
}
#define SHUFFLE_IMPL \
u32 tmp; \
--n; \
while (n > 1) \
{ \
int j = Random() % (n+1); \
SWAP(data[n], data[j], tmp); \
--n; \
}
void Shuffle8(void *data_, size_t n)
{
u8 *data = data_;
SHUFFLE_IMPL;
}
void Shuffle16(void *data_, size_t n)
{
u16 *data = data_;
SHUFFLE_IMPL;
}
void Shuffle32(void *data_, size_t n)
{
u32 *data = data_;
SHUFFLE_IMPL;
}
void ShuffleN(void *data, size_t n, size_t size)
{
void *tmp = alloca(size);
--n;
while (n > 1)
{
int j = Random() % (n+1);
memcpy(tmp, (u8 *)data + n*size, size); // tmp = data[n];
memcpy((u8 *)data + n*size, (u8 *)data + j*size, size); // data[n] = data[j];
memcpy((u8 *)data + j*size, tmp, size); // data[j] = tmp;
--n;
}
}
__attribute__((weak, alias("RandomUniformDefault")))
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi);

View File

@ -2,6 +2,47 @@
#include "test.h"
#include "random.h"
// 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;
}
TEST("RandomUniform generates lo..hi")
{
u32 lo, hi, i;