diff --git a/include/random.h b/include/random.h index 5b88db1f5..86995e36c 100644 --- a/include/random.h +++ b/include/random.h @@ -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 diff --git a/src/random.c b/src/random.c index 14983e776..345d585c2 100644 --- a/src/random.c +++ b/src/random.c @@ -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); diff --git a/test/random.c b/test/random.c index 39f587c06..ef364de27 100644 --- a/test/random.c +++ b/test/random.c @@ -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;