Browse Source

Port of random

Kajetan Johannes Hammerle 1 month ago
parent
commit
9b457a6474
7 changed files with 204 additions and 14 deletions
  1. 13 13
      CMakeLists.txt
  2. 18 0
      include/core/utils/Random.h
  3. 1 0
      include/core/utils/Types.h
  4. 61 0
      src/Random.c
  5. 1 1
      test/Main.c
  6. 6 0
      test/modules/BufferTests.c
  7. 104 0
      test/modules/RandomTests.c

+ 13 - 13
CMakeLists.txt

@@ -4,30 +4,31 @@ project(core)
 set(CMAKE_C_STANDARD 23)
 
 set(SRC
+    "src/Buffer.c"
     "src/Logger.c"
+    "src/Random.c"
     "src/Utility.c"
-    "src/Buffer.c"
-    #"src/Clock.cpp"
-    #"src/Random.cpp"
+    #"src/ArrayString.cpp"
     #"src/BitArray.cpp"
-    #"src/Vector.cpp"
-    #"src/Quaternion.cpp"
-    #"src/Matrix.cpp"
     #"src/Box.cpp"
-    #"src/Plane.cpp"
+    #"src/Clock.cpp"
+    #"src/FileReader.cpp"
     #"src/Frustum.cpp"
-    #"src/View.cpp"
-    #"src/Thread.cpp"
+    #"src/Matrix.cpp"
     #"src/Mutex.cpp"
+    #"src/Plane.cpp"
+    #"src/Quaternion.cpp"
     #"src/SpinLock.cpp"
-    #"src/FileReader.cpp"
-    #"src/ArrayString.cpp"
+    #"src/Thread.cpp"
+    #"src/Vector.cpp"
+    #"src/View.cpp"
 )
 
 set(SRC_TESTS
     "test/Main.c"
     "test/Test.c"
     "test/modules/BufferTests.c"
+    "test/modules/RandomTests.c"
     "test/modules/UtilityTests.c"
     #"test/modules/ArrayListTests.cpp"
     #"test/modules/ArrayStringTests.cpp"
@@ -49,7 +50,6 @@ set(SRC_TESTS
     #"test/modules/MatrixTests.cpp"
     #"test/modules/PlaneTests.cpp"
     #"test/modules/QuaternionTests.cpp"
-    #"test/modules/RandomTests.cpp"
     #"test/modules/RingBufferTests.cpp"
     #"test/modules/StackTests.cpp"
     #"test/modules/ThreadTests.cpp"
@@ -172,6 +172,7 @@ target_sources(core PUBLIC
         ./include/core/utils/Check.h
         ./include/core/utils/Error.h
         ./include/core/utils/Logger.h
+        ./include/core/utils/Random.h
         ./include/core/utils/Types.h
         ./include/core/utils/Utility.h
 #        ./include/core/data/Array.hpp
@@ -201,7 +202,6 @@ target_sources(core PUBLIC
 #        ./include/core/utils/Clock.hpp
 #        ./include/core/utils/Color.hpp
 #        ./include/core/utils/HashCode.hpp
-#        ./include/core/utils/Random.hpp
 )
 install(TARGETS core FILE_SET HEADERS)
 

+ 18 - 0
include/core/utils/Random.h

@@ -0,0 +1,18 @@
+#ifndef CORE_RANDOM_H
+#define CORE_RANDOM_H
+
+#include "core/utils/Types.h"
+
+typedef struct {
+    u32 data[25];
+    size_t index;
+} CoreRandom;
+
+void coreInitRandom(CoreRandom* r, u32 seed);
+u32 coreRandomU32(CoreRandom* r, u32 min, u32 exclusiveMax);
+i32 coreRandomI32(CoreRandom* r, i32 min, i32 exclusiveMax);
+size_t coreRandomSize(CoreRandom* r, size_t min, size_t exclusiveMax);
+bool coreRandomBool(CoreRandom* r);
+float coreRandomFloat(CoreRandom* r);
+
+#endif

+ 1 - 0
include/core/utils/Types.h

@@ -12,5 +12,6 @@ typedef uint64_t u64;
 typedef uint32_t u32;
 typedef uint16_t u16;
 typedef uint8_t u8;
+#define CORE_ARRAY_LENGTH(array) (sizeof(array) / sizeof(*array))
 
 #endif

+ 61 - 0
src/Random.c

@@ -0,0 +1,61 @@
+#include "core/utils/Random.h"
+
+static const size_t M = 7;
+
+void coreInitRandom(CoreRandom* r, u32 seed) {
+    for(size_t i = 0; i < CORE_ARRAY_LENGTH(r->data); i++) {
+        r->data[i] = seed;
+        seed = seed * 7 + 31;
+    }
+}
+
+static void update(CoreRandom* r) {
+    static const u32 map[2] = {0, 0x8EBFD028};
+    static const size_t LENGTH = CORE_ARRAY_LENGTH(r->data);
+    for(size_t i = 0; i < LENGTH - M; i++) {
+        r->data[i] = r->data[i + M] ^ (r->data[i] >> 1) ^ map[r->data[i] & 1];
+    }
+    for(size_t i = LENGTH - M; i < LENGTH; i++) {
+        r->data[i] =
+            r->data[i + (M - LENGTH)] ^ (r->data[i] >> 1) ^ map[r->data[i] & 1];
+    }
+    r->index = 0;
+}
+
+#define CORE_LIMIT(value, min, max) (min + value % (max - min))
+
+static u32 next(CoreRandom* r) {
+    if(r->index >= CORE_ARRAY_LENGTH(r->data)) {
+        update(r);
+    }
+    u32 u = r->data[r->index++];
+    u ^= (u << 7) & 0x2B5B2500;
+    u ^= (u << 15) & 0xDB8B0000;
+    u ^= (u >> 16);
+    return u;
+}
+
+u32 coreRandomU32(CoreRandom* r, u32 min, u32 exclusiveMax) {
+    u32 u = next(r);
+    return CORE_LIMIT(u, min, exclusiveMax);
+}
+
+i32 coreRandomI32(CoreRandom* r, i32 min, i32 exclusiveMax) {
+    i32 i = (i32)(next(r) >> 1);
+    return CORE_LIMIT(i, min, exclusiveMax);
+}
+
+size_t coreRandomSize(CoreRandom* r, size_t min, size_t exclusiveMax) {
+    size_t s = next(r);
+    return CORE_LIMIT(s, min, exclusiveMax);
+}
+
+bool coreRandomBool(CoreRandom* r) {
+    return next(r) & 1;
+}
+
+float coreRandomFloat(CoreRandom* r) {
+    u32 u = next(r);
+    float f = (float)u / (float)0xFFFFFFF;
+    return f >= 1.0f ? coreRandomFloat(r) : f;
+}

+ 1 - 1
test/Main.c

@@ -50,7 +50,7 @@ int main(int argAmount, const char** args) {
     // coreTestMatrix();
     // coreTestPlane();
     // coreTestQuaternion();
-    // coreTestRandom(light);
+    coreTestRandom(light);
     // coreTestRingBuffer();
     // coreTestStack(light);
     // coreTestThread();

+ 6 - 0
test/modules/BufferTests.c

@@ -19,5 +19,11 @@ void coreTestBuffer(bool light) {
         coreAddBufferData(&buffer, &d);
     }
     CORE_TEST_SIZE(SIZE_TYPES * limit, buffer.size);
+
+    coreClearBuffer(&buffer);
+    long l = 20;
+    coreAddBufferData(&buffer, &l);
+    CORE_TEST_SIZE(sizeof(l), buffer.size);
+
     coreDestroyBuffer(&buffer);
 }

+ 104 - 0
test/modules/RandomTests.c

@@ -0,0 +1,104 @@
+#include "../Tests.h"
+#include "core/utils/Random.h"
+
+static void testAverage(bool light) {
+    int limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 553);
+    float sum = 0;
+    for(int i = 0; i < limit; i++) {
+        u32 u = coreRandomU32(&r, 2, 11);
+        sum += (float)u;
+    }
+    sum /= (float)limit;
+    CORE_TEST_FLOAT(6.0f, sum, 0.01f);
+}
+
+static void testCoin(bool light) {
+    int limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 5533);
+    int c[2] = {0, 0};
+    for(int i = 0; i < limit; i++) {
+        c[coreRandomBool(&r)]++;
+    }
+    CORE_TEST_FLOAT(0.0f, (float)(c[0] - c[1]) / (float)limit, 0.003f);
+}
+
+static void testDistribution(bool light) {
+    size_t limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 553);
+    int c[102] = {0};
+    for(size_t i = 0; i < limit; i++) {
+        c[coreRandomSize(&r, 1, 101)]++;
+    }
+    CORE_TEST_INT(0, c[0]);
+    CORE_TEST_INT(0, c[101]);
+    for(size_t i = 1; i < 101; i++) {
+        CORE_TEST_FLOAT(0.01f, (float)c[i] / (float)limit, 0.001f);
+    }
+}
+
+static void testFloatAverage(bool light) {
+    int limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 553);
+    float sum = 0;
+    for(int i = 0; i < limit; i++) {
+        sum += coreRandomFloat(&r);
+    }
+    sum /= (float)limit;
+    CORE_TEST_FLOAT(0.5f, sum, 0.001f);
+}
+
+static void testFloatCoin(bool light) {
+    int limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 5534);
+    int c[2] = {0, 0};
+    for(int i = 0; i < limit; i++) {
+        c[coreRandomFloat(&r) < 0.5f]++;
+    }
+    CORE_TEST_FLOAT(0.0f, (float)(c[0] - c[1]) / (float)limit, 0.003f);
+}
+
+static void testFloatDistribution(bool light) {
+    int limit = light ? 100000 : 1000000;
+    CoreRandom r;
+    coreInitRandom(&r, 553);
+    int c[102] = {0};
+    for(int i = 0; i < limit; i++) {
+        c[(size_t)(1.0f + coreRandomFloat(&r) * 100.0f)]++;
+    }
+    CORE_TEST_INT(0, c[0]);
+    CORE_TEST_INT(0, c[101]);
+    for(size_t i = 1; i < 101; i++) {
+        CORE_TEST_FLOAT(0.01f, (float)c[i] / (float)limit, 0.003f);
+    }
+}
+
+static void testRandomI32() {
+    CoreRandom r;
+    coreInitRandom(&r, 56346);
+    int c[7] = {0};
+    for(int i = 0; i < 10000; i++) {
+        i32 index = coreRandomI32(&r, -2, 3) + 3;
+        if(CORE_TEST_TRUE(index >= 0 && index < 7)) {
+            c[index]++;
+        }
+    }
+    for(size_t i = 0; i < 7; i++) {
+        CORE_TEST_BOOL(i != 0 && i != 6, c[i] > 0);
+    }
+}
+
+void coreTestRandom(bool light) {
+    testAverage(light);
+    testCoin(light);
+    testDistribution(light);
+    testFloatAverage(light);
+    testFloatCoin(light);
+    testFloatDistribution(light);
+    testRandomI32();
+}