浏览代码

more stuff moved from gaming core, limit system headers to cpp files

Kajetan Johannes Hammerle 1 年之前
父节点
当前提交
071914301a
共有 28 个文件被更改,包括 843 次插入138 次删除
  1. 1 4
      data/BitArray.cpp
  2. 56 0
      math/Box.cpp
  3. 34 0
      math/Box.h
  4. 13 5
      math/Math.cpp
  5. 14 10
      math/Math.h
  6. 125 0
      math/Matrix.cpp
  7. 53 0
      math/Matrix.h
  8. 3 2
      math/Quaternion.cpp
  9. 1 1
      math/Quaternion.h
  10. 2 3
      math/Vector.cpp
  11. 3 3
      math/Vector.h
  12. 5 0
      meson.build
  13. 5 1
      test/Main.cpp
  14. 1 1
      test/Test.cpp
  15. 62 0
      tests/BoxTests.cpp
  16. 8 0
      tests/BoxTests.h
  17. 59 28
      tests/MathTests.cpp
  18. 204 0
      tests/MatrixTests.cpp
  19. 8 0
      tests/MatrixTests.h
  20. 6 0
      tests/UtilityTests.cpp
  21. 13 13
      tests/VectorTests.cpp
  22. 21 28
      utils/ArrayString.h
  23. 3 3
      utils/HashCode.cpp
  24. 1 1
      utils/HashCode.h
  25. 7 13
      utils/Logger.h
  26. 0 15
      utils/Types.h
  27. 45 1
      utils/Utility.cpp
  28. 90 6
      utils/Utility.h

+ 1 - 4
data/BitArray.cpp

@@ -1,8 +1,5 @@
 #include "data/BitArray.h"
 
-#include <stdio.h>
-#include <string.h>
-
 #include "math/Math.h"
 #include "utils/Utility.h"
 
@@ -143,7 +140,7 @@ check_return bool Core::BitArray::resize(int newLength, int newBits) {
     if(newData == nullptr) {
         return true;
     }
-    memset(newData, 0, static_cast<size_t>(arrayLength) * sizeof(int));
+    Core::memorySet(newData, 0, arrayLength * CORE_SIZE(int));
 
     int end = Math::min(length, newLength);
     for(int i = 0; i < end; i++) {

+ 56 - 0
math/Box.cpp

@@ -0,0 +1,56 @@
+#include "math/Box.h"
+
+Core::Box::Box(const Vector3& min_, const Vector3& max_)
+    : min(min_), max(max_) {
+}
+
+Core::Box::Box(const Vector3& size) {
+    for(int i = 0; i < 3; i++) {
+        if(size[i] < 0.0f) {
+            min[i] = size[i];
+        } else {
+            max[i] = size[i];
+        }
+    }
+}
+
+Core::Box Core::Box::offset(const Vector3& offset) const {
+    return Box(min + offset, max + offset);
+}
+
+bool Core::Box::collidesWith(const Box& other) const {
+    return max[0] > other.min[0] && min[0] < other.max[0] &&
+           max[1] > other.min[1] && min[1] < other.max[1] &&
+           max[2] > other.min[2] && min[2] < other.max[2];
+}
+
+Core::Box Core::Box::expand(const Vector3& offset) const {
+    Vector3 add(offset[0] > 0.0f ? offset[0] : 0.0f,
+                offset[1] > 0.0f ? offset[1] : 0.0f,
+                offset[2] > 0.0f ? offset[2] : 0.0f);
+    Vector3 sub(offset[0] < 0.0f ? offset[0] : 0.0f,
+                offset[1] < 0.0f ? offset[1] : 0.0f,
+                offset[2] < 0.0f ? offset[2] : 0.0f);
+    return Box(min + sub, max + add);
+}
+
+Core::Box Core::Box::grow(const Vector3& growth) const {
+    Vector3 half = growth * 0.5f;
+    Vector3 nMin = min - half;
+    Vector3 nMax = max + half;
+    for(int i = 0; i < 3; i++) {
+        if(nMin[i] > nMax[i]) {
+            nMin[i] = (min[i] + max[i]) * 0.5f;
+            nMax[i] = nMin[i];
+        }
+    }
+    return Box(nMin, nMax);
+}
+
+const Core::Vector3& Core::Box::getMin() const {
+    return min;
+}
+
+const Core::Vector3& Core::Box::getMax() const {
+    return max;
+}

+ 34 - 0
math/Box.h

@@ -0,0 +1,34 @@
+#ifndef CORE_BOX_H
+#define CORE_BOX_H
+
+#include "math/Vector.h"
+#include "utils/ArrayString.h"
+
+namespace Core {
+    class Box final {
+        Vector3 min;
+        Vector3 max;
+
+        Box(const Vector3& min, const Vector3& max);
+
+    public:
+        Box(const Vector3& size);
+
+        Box offset(const Vector3& offset) const;
+        bool collidesWith(const Box& other) const;
+        Box expand(const Vector3& offset) const;
+        Box grow(const Vector3& growth) const;
+
+        const Vector3& getMin() const;
+        const Vector3& getMax() const;
+
+        // returns true on error
+        template<int L>
+        check_return bool toString(ArrayString<L>& s) const {
+            return s.append("Box(") || s.append(min) || s.append(", ") ||
+                   s.append(max) || s.append(")");
+        }
+    };
+}
+
+#endif

+ 13 - 5
math/Math.cpp

@@ -1,8 +1,16 @@
 #include "math/Math.h"
 
-#ifndef _GNU_SOURCE
-void sincosf(float a, float* s, float* c) {
-    *s = sinf(a);
-    *c = cosf(a);
+#include <math.h>
+
+void Core::Math::sinCos(float a, float& s, float& c) {
+#ifdef _GNU_SOURCE
+    sincosf(a, &s, &c);
+#else
+    s = sinf(a);
+    c = cosf(a);
+#endif
 }
-#endif
+
+float Core::Math::squareRoot(float f) {
+    return sqrtf(f);
+}

+ 14 - 10
math/Math.h

@@ -1,18 +1,8 @@
 #ifndef CORE_MATH_H
 #define CORE_MATH_H
 
-#include <math.h>
-
 #include "utils/Utility.h"
 
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
-#ifndef _GNU_SOURCE
-void sincosf(float a, float* s, float* c);
-#endif
-
 namespace Core::Math {
     template<typename T>
     T interpolate(const T& a, const T& b, float f) {
@@ -58,6 +48,20 @@ namespace Core::Math {
         const T& high = max(borderA, borderB);
         return max(low, min(high, t));
     }
+
+    constexpr float PI = 3.14159265358979323846f;
+
+    void sinCos(float a, float& s, float& c);
+
+    constexpr float radianToDegree(float radians) {
+        return radians * (180.0f / PI);
+    }
+
+    constexpr float degreeToRadian(float degrees) {
+        return degrees * (PI / 180.0f);
+    }
+
+    float squareRoot(float f);
 }
 
 #endif

+ 125 - 0
math/Matrix.cpp

@@ -0,0 +1,125 @@
+#include "math/Matrix.h"
+
+#include "math/Math.h"
+
+Core::Matrix::Matrix() {
+    unit();
+}
+
+Core::Matrix& Core::Matrix::unit() {
+    return translateTo(Vector3(0.0f, 0.0f, 0.0f));
+}
+
+Core::Matrix& Core::Matrix::set(int index, const Vector4& v) {
+    data[index] = v;
+    return *this;
+}
+
+Core::Matrix Core::Matrix::transpose() {
+    Matrix m;
+    for(int x = 0; x < 4; x++) {
+        for(int y = 0; y < 4; y++) {
+            m.data[x][y] = data[y][x];
+        }
+    }
+    return m;
+}
+
+const float* Core::Matrix::getValues() const {
+    return &(data[0][0]);
+}
+
+Core::Matrix& Core::Matrix::operator*=(const Matrix& m) {
+    data[0] = data[0][0] * m.data[0] + data[0][1] * m.data[1] +
+              data[0][2] * m.data[2] + data[0][3] * m.data[3];
+    data[1] = data[1][0] * m.data[0] + data[1][1] * m.data[1] +
+              data[1][2] * m.data[2] + data[1][3] * m.data[3];
+    data[2] = data[2][0] * m.data[0] + data[2][1] * m.data[1] +
+              data[2][2] * m.data[2] + data[2][3] * m.data[3];
+    data[3] = data[3][0] * m.data[0] + data[3][1] * m.data[1] +
+              data[3][2] * m.data[2] + data[3][3] * m.data[3];
+    return *this;
+}
+
+Core::Matrix Core::Matrix::operator*(const Matrix& other) const {
+    Matrix m = *this;
+    m *= other;
+    return m;
+}
+
+Core::Vector3 Core::Matrix::operator*(const Vector3& v) const {
+    Vector4 v4(v[0], v[1], v[2], 1.0f);
+    return Vector3(data[0].dot(v4), data[1].dot(v4), data[2].dot(v4)) *
+           (1.0f / data[3].dot(v4));
+}
+
+Core::Matrix& Core::Matrix::scale(const Vector3& v) {
+    data[0] *= v[0];
+    data[1] *= v[1];
+    data[2] *= v[2];
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::scale(float s) {
+    return scale(Vector3(s, s, s));
+}
+
+Core::Matrix& Core::Matrix::translate(const Vector3& v) {
+    return translateX(v[0]).translateY(v[1]).translateZ(v[2]);
+}
+
+Core::Matrix& Core::Matrix::translateX(float tx) {
+    data[0] += data[3] * tx;
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::translateY(float ty) {
+    data[1] += data[3] * ty;
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::translateZ(float tz) {
+    data[2] += data[3] * tz;
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::translateTo(const Vector3& v) {
+    data[0] = Vector4(1.0f, 0.0f, 0.0f, v[0]);
+    data[1] = Vector4(0.0f, 1.0f, 0.0f, v[1]);
+    data[2] = Vector4(0.0f, 0.0f, 1.0f, v[2]);
+    data[3] = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::rotate(float degrees, int a, int b) {
+    float sin = 0.0f;
+    float cos = 0.0f;
+    Math::sinCos(Core::Math::degreeToRadian(degrees), sin, cos);
+    Vector4 v = data[a];
+    data[a] = cos * data[a] - sin * data[b];
+    data[b] = sin * v + cos * data[b];
+    return *this;
+}
+
+Core::Matrix& Core::Matrix::rotateX(float degrees) {
+    return rotate(degrees, 1, 2);
+}
+
+Core::Matrix& Core::Matrix::rotateY(float degrees) {
+    return rotate(-degrees, 0, 2);
+}
+
+Core::Matrix& Core::Matrix::rotateZ(float degrees) {
+    return rotate(degrees, 0, 1);
+}
+
+Core::Matrix& Core::Matrix::rotate(const Quaternion& q) {
+    Vector3 a = q * Vector3(data[0][0], data[1][0], data[2][0]);
+    Vector3 b = q * Vector3(data[0][1], data[1][1], data[2][1]);
+    Vector3 c = q * Vector3(data[0][2], data[1][2], data[2][2]);
+    Vector3 d = q * Vector3(data[0][3], data[1][3], data[2][3]);
+    set(0, Vector4(a[0], b[0], c[0], d[0]));
+    set(1, Vector4(a[1], b[1], c[1], d[1]));
+    set(2, Vector4(a[2], b[2], c[2], d[2]));
+    return *this;
+}

+ 53 - 0
math/Matrix.h

@@ -0,0 +1,53 @@
+#ifndef CORE_MATRIX_H
+#define CORE_MATRIX_H
+
+#include "math/Quaternion.h"
+#include "utils/ArrayString.h"
+
+namespace Core {
+    class Matrix final {
+        Vector4 data[4];
+
+    public:
+        Matrix();
+
+        Matrix& unit();
+
+        Matrix& set(int index, const Vector4& v);
+
+        Matrix transpose();
+
+        const float* getValues() const;
+
+        Matrix& operator*=(const Matrix& other);
+        Matrix operator*(const Matrix& other) const;
+        Vector3 operator*(const Vector3& v) const;
+
+        Matrix& scale(const Vector3& v);
+        Matrix& scale(float f);
+
+        Matrix& translate(const Vector3& v);
+        Matrix& translateX(float tx);
+        Matrix& translateY(float ty);
+        Matrix& translateZ(float tz);
+        Matrix& translateTo(const Vector3& v);
+
+        Matrix& rotateX(float degrees);
+        Matrix& rotateY(float degrees);
+        Matrix& rotateZ(float degrees);
+        Matrix& rotate(const Quaternion& q);
+
+        // returns true on error
+        template<int L>
+        check_return bool toString(ArrayString<L>& s) const {
+            return s.append('[') || s.append(data[0]) || s.append(", ") ||
+                   s.append(data[1]) || s.append(", ") || s.append(data[2]) ||
+                   s.append(", ") || s.append(data[3]) || s.append("]");
+        }
+
+    private:
+        Matrix& rotate(float degrees, int a, int b);
+    };
+}
+
+#endif

+ 3 - 2
math/Quaternion.cpp

@@ -6,7 +6,7 @@ Core::Quaternion::Quaternion() : w(1.0f) {
 Core::Quaternion::Quaternion(const Vector3& axis, float angle) : xyz(axis) {
     xyz.normalize();
     float factor = 0.0f;
-    sincosf(angle * (static_cast<float>(M_PI) / 360.0f), &factor, &w);
+    Core::Math::sinCos(Core::Math::degreeToRadian(angle) * 0.5f, factor, w);
     xyz *= factor;
 }
 
@@ -15,7 +15,8 @@ Core::Quaternion Core::Quaternion::lerp(float f,
     Quaternion q;
     q.xyz = xyz * (1.0f - f) + other.xyz * f;
     q.w = w * (1.0f - f) + other.w * f;
-    float iLength = 1.0f / sqrtf(q.xyz.squareLength() + q.w * q.w);
+    float iLength =
+        1.0f / Core::Math::squareRoot(q.xyz.squareLength() + q.w * q.w);
     q.xyz *= iLength;
     q.w *= iLength;
     return q;

+ 1 - 1
math/Quaternion.h

@@ -5,7 +5,7 @@
 #include "utils/ArrayString.h"
 
 namespace Core {
-    class Quaternion {
+    class Quaternion final {
         Vector3 xyz;
         float w;
 

+ 2 - 3
math/Vector.cpp

@@ -4,12 +4,11 @@ template<>
 Core::Vector3& Core::Vector3::setAngles(float lengthAngle, float widthAngle) {
     float sWidth = 0.0f;
     float cWidth = 0.0f;
-    sincosf(widthAngle * (static_cast<float>(M_PI) / 180.0f), &sWidth, &cWidth);
+    Core::Math::sinCos(Math::degreeToRadian(widthAngle), sWidth, cWidth);
 
     float sLength = 0.0f;
     float cLength = 0.0f;
-    sincosf(lengthAngle * (static_cast<float>(M_PI) / 180.0f), &sLength,
-            &cLength);
+    Core::Math::sinCos(Math::degreeToRadian(lengthAngle), sLength, cLength);
 
     return *this = Vector3(cWidth * cLength, sWidth, -sLength * cWidth);
 }

+ 3 - 3
math/Vector.h

@@ -1,5 +1,5 @@
-#ifndef VECTOR_H
-#define VECTOR_H
+#ifndef CORE_VECTOR_H
+#define CORE_VECTOR_H
 
 #include "math/Math.h"
 #include "utils/ArrayString.h"
@@ -137,7 +137,7 @@ namespace Core {
         }
 
         float length() const {
-            return sqrtf(squareLength());
+            return Math::squareRoot(squareLength());
         }
 
         Vector& normalize() {

+ 5 - 0
meson.build

@@ -5,8 +5,11 @@ src = [
     'utils/Utility.cpp',
     'utils/HashCode.cpp',
     'data/BitArray.cpp',
+    'math/Math.cpp',
     'math/Vector.cpp',
     'math/Quaternion.cpp',
+    'math/Matrix.cpp',
+    'math/Box.cpp',
 ]
 
 src_tests = [
@@ -27,6 +30,8 @@ src_tests = [
     'tests/ComponentsTests.cpp',
     'tests/VectorTests.cpp',
     'tests/QuaternionTests.cpp',
+    'tests/MatrixTests.cpp',
+    'tests/BoxTests.cpp',
 ]
 
 compiler = meson.get_compiler('cpp')

+ 5 - 1
test/Main.cpp

@@ -5,11 +5,13 @@
 #include "tests/ArrayStringTests.h"
 #include "tests/ArrayTests.h"
 #include "tests/BitArrayTests.h"
+#include "tests/BoxTests.h"
 #include "tests/ComponentsTests.h"
 #include "tests/HashMapTests.h"
 #include "tests/LinkedListTests.h"
 #include "tests/ListTests.h"
 #include "tests/MathTests.h"
+#include "tests/MatrixTests.h"
 #include "tests/QuaternionTests.h"
 #include "tests/RingBufferTests.h"
 #include "tests/StackTests.h"
@@ -28,11 +30,13 @@ int main() {
     Core::ArrayStringTests::test();
     Core::ArrayTests::test();
     Core::BitArrayTests::test();
+    Core::BoxTests::test();
     Core::ComponentsTests::test();
     Core::HashMapTests::test();
     Core::LinkedListTests::test();
     Core::ListTests::test();
     Core::MathTests::test();
+    Core::MatrixTests::test();
     Core::QuaternionTests::test();
     Core::RingBufferTests::test();
     Core::StackTests::test();
@@ -44,7 +48,7 @@ int main() {
 
     unsigned int data = 0x1234;
     Core::setExitHandler(onExit, &data);
-    Core::exitWithHandler(0);
+    CORE_EXIT(0);
 
     return 0;
 }

+ 1 - 1
test/Test.cpp

@@ -21,7 +21,7 @@ void Core::Test::Internal::checkFloat(const char* file, int line, float wanted,
     result->tests++;
     float diff = wanted - actual;
     diff = diff < 0.0f ? -diff : diff;
-    if(diff < error) {
+    if(diff <= error) {
         result->successTests++;
     } else {
         CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted,

+ 62 - 0
tests/BoxTests.cpp

@@ -0,0 +1,62 @@
+#include "tests/BoxTests.h"
+
+#include "math/Box.h"
+#include "test/Test.h"
+
+static void testInit() {
+    Core::Box box(Core::Vector3(1.0f, 2.0f, 3.0f));
+    CORE_TEST_STRING("Box([0.00, 0.00, 0.00], [1.00, 2.00, 3.00])", box);
+    CORE_TEST_STRING("[0.00, 0.00, 0.00]", box.getMin());
+    CORE_TEST_STRING("[1.00, 2.00, 3.00]", box.getMax());
+
+    box = Core::Box(Core::Vector3(-1.0f, -2.0f, -3.0f));
+    CORE_TEST_STRING("Box([-1.00, -2.00, -3.00], [0.00, 0.00, 0.00])", box);
+    CORE_TEST_STRING("[-1.00, -2.00, -3.00]", box.getMin());
+    CORE_TEST_STRING("[0.00, 0.00, 0.00]", box.getMax());
+}
+
+static void testOffset() {
+    Core::Box box(Core::Vector3(1.0f, 2.0f, 3.0f));
+    CORE_TEST_STRING("Box([7.00, -4.00, 6.00], [8.00, -2.00, 9.00])",
+                     box.offset(Core::Vector3(7.0f, -4.0f, 6.0f)));
+}
+
+static void testCollidesWith() {
+    Core::Box boxA(Core::Vector3(1.0f, 2.0f, 3.0f));
+    Core::Box boxB(Core::Vector3(-1.0f, -2.0f, -3.0f));
+    Core::Box boxC(Core::Vector3(2.0f, 2.0f, 2.0f));
+    boxC = boxC.offset(Core::Vector3(-1.0f, -1.0f, -1.0f));
+
+    CORE_TEST_TRUE(boxC.collidesWith(boxA));
+    CORE_TEST_TRUE(boxC.collidesWith(boxB));
+    CORE_TEST_TRUE(boxA.collidesWith(boxC));
+    CORE_TEST_TRUE(boxB.collidesWith(boxC));
+    CORE_TEST_FALSE(boxA.collidesWith(boxB));
+    CORE_TEST_FALSE(boxB.collidesWith(boxA));
+}
+
+static void testExpand() {
+    Core::Box box(Core::Vector3(1.0f, 2.0f, 3.0f));
+    CORE_TEST_STRING("Box([0.00, -4.00, 0.00], [8.00, 2.00, 9.00])",
+                     box.expand(Core::Vector3(7.0f, -4.0f, 6.0f)));
+    CORE_TEST_STRING("Box([-7.00, 0.00, -6.00], [1.00, 6.00, 3.00])",
+                     box.expand(Core::Vector3(-7.0f, 4.0f, -6.0f)));
+}
+
+static void testGrow() {
+    Core::Box box(Core::Vector3(1.0f, 2.0f, 3.0f));
+    CORE_TEST_STRING("Box([-2.00, -1.00, -3.00], [3.00, 3.00, 6.00])",
+                     box.grow(Core::Vector3(4.0f, 2.0f, 6.0f)));
+    CORE_TEST_STRING("Box([0.50, 1.00, 1.50], [0.50, 1.00, 1.50])",
+                     box.grow(Core::Vector3(-4.0f, -2.0f, -6.0f)));
+    CORE_TEST_STRING("Box([0.05, 1.00, 0.50], [0.95, 1.00, 2.50])",
+                     box.grow(Core::Vector3(-0.1f, -4.0f, -1.0f)));
+}
+
+void Core::BoxTests::test() {
+    testInit();
+    testOffset();
+    testCollidesWith();
+    testExpand();
+    testGrow();
+}

+ 8 - 0
tests/BoxTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_BOX_TESTS_H
+#define CORE_BOX_TESTS_H
+
+namespace Core::BoxTests {
+    void test();
+}
+
+#endif

+ 59 - 28
tests/MathTests.cpp

@@ -1,51 +1,79 @@
 #include "tests/MathTests.h"
 
+#include <math.h>
+
 #include "math/Math.h"
 #include "test/Test.h"
 
 constexpr float eps = 0.0001f;
 
+namespace CMath = Core::Math;
+
 static void testInterpolate() {
-    CORE_TEST_FLOAT(7.5f, Core::Math::interpolate(5.0f, 10.0f, 0.5f), eps);
-    CORE_TEST_FLOAT(-2.0, Core::Math::interpolate(-10.0f, 10.0f, 0.4f), eps);
-    CORE_TEST_FLOAT(10.0f, Core::Math::interpolate(-3.0f, 10.0f, 1.0f), eps);
-    CORE_TEST_FLOAT(7.0f, Core::Math::interpolate(7.0f, 10.0f, 0.0f), eps);
-    CORE_TEST_FLOAT(6.0f, Core::Math::interpolate(0.0f, 10.0f, 0.6f), eps);
+    CORE_TEST_FLOAT(7.5f, CMath::interpolate(5.0f, 10.0f, 0.5f), eps);
+    CORE_TEST_FLOAT(-2.0, CMath::interpolate(-10.0f, 10.0f, 0.4f), eps);
+    CORE_TEST_FLOAT(10.0f, CMath::interpolate(-3.0f, 10.0f, 1.0f), eps);
+    CORE_TEST_FLOAT(7.0f, CMath::interpolate(7.0f, 10.0f, 0.0f), eps);
+    CORE_TEST_FLOAT(6.0f, CMath::interpolate(0.0f, 10.0f, 0.6f), eps);
 }
 
 static void testRoundUpLog2() {
-    CORE_TEST_EQUAL(0, Core::Math::roundUpLog2(-5));
-    CORE_TEST_EQUAL(0, Core::Math::roundUpLog2(0));
-    CORE_TEST_EQUAL(1, Core::Math::roundUpLog2(1));
-    CORE_TEST_EQUAL(1, Core::Math::roundUpLog2(2));
-    CORE_TEST_EQUAL(2, Core::Math::roundUpLog2(3));
-    CORE_TEST_EQUAL(2, Core::Math::roundUpLog2(4));
-    CORE_TEST_EQUAL(3, Core::Math::roundUpLog2(5));
-    CORE_TEST_EQUAL(4, Core::Math::roundUpLog2(10));
-    CORE_TEST_EQUAL(5, Core::Math::roundUpLog2(20));
-    CORE_TEST_EQUAL(16, Core::Math::roundUpLog2(35345));
-    CORE_TEST_EQUAL(31, Core::Math::roundUpLog2(0x7FFFFFFF));
+    CORE_TEST_EQUAL(0, CMath::roundUpLog2(-5));
+    CORE_TEST_EQUAL(0, CMath::roundUpLog2(0));
+    CORE_TEST_EQUAL(1, CMath::roundUpLog2(1));
+    CORE_TEST_EQUAL(1, CMath::roundUpLog2(2));
+    CORE_TEST_EQUAL(2, CMath::roundUpLog2(3));
+    CORE_TEST_EQUAL(2, CMath::roundUpLog2(4));
+    CORE_TEST_EQUAL(3, CMath::roundUpLog2(5));
+    CORE_TEST_EQUAL(4, CMath::roundUpLog2(10));
+    CORE_TEST_EQUAL(5, CMath::roundUpLog2(20));
+    CORE_TEST_EQUAL(16, CMath::roundUpLog2(35345));
+    CORE_TEST_EQUAL(31, CMath::roundUpLog2(0x7FFFFFFF));
 }
 
 static void testMin() {
-    CORE_TEST_EQUAL(-5, Core::Math::min(-5));
-    CORE_TEST_EQUAL(-10, Core::Math::min(-5, 4, 3, 2, -10));
-    CORE_TEST_EQUAL(4, Core::Math::min(5, 20, 4, 30));
+    CORE_TEST_EQUAL(-5, CMath::min(-5));
+    CORE_TEST_EQUAL(-10, CMath::min(-5, 4, 3, 2, -10));
+    CORE_TEST_EQUAL(4, CMath::min(5, 20, 4, 30));
 }
 
 static void testMax() {
-    CORE_TEST_EQUAL(-5, Core::Math::max(-5));
-    CORE_TEST_EQUAL(4, Core::Math::max(-5, 4, 3, 2, -10));
-    CORE_TEST_EQUAL(30, Core::Math::max(5, 20, 4, 30));
+    CORE_TEST_EQUAL(-5, CMath::max(-5));
+    CORE_TEST_EQUAL(4, CMath::max(-5, 4, 3, 2, -10));
+    CORE_TEST_EQUAL(30, CMath::max(5, 20, 4, 30));
 }
 
 static void testClamp() {
-    CORE_TEST_EQUAL(5, Core::Math::clamp(1, 5, 10));
-    CORE_TEST_EQUAL(7, Core::Math::clamp(7, 5, 10));
-    CORE_TEST_EQUAL(10, Core::Math::clamp(20, 5, 10));
-    CORE_TEST_EQUAL(5, Core::Math::clamp(1, 10, 5));
-    CORE_TEST_EQUAL(7, Core::Math::clamp(7, 10, 5));
-    CORE_TEST_EQUAL(10, Core::Math::clamp(20, 10, 5));
+    CORE_TEST_EQUAL(5, CMath::clamp(1, 5, 10));
+    CORE_TEST_EQUAL(7, CMath::clamp(7, 5, 10));
+    CORE_TEST_EQUAL(10, CMath::clamp(20, 5, 10));
+    CORE_TEST_EQUAL(5, CMath::clamp(1, 10, 5));
+    CORE_TEST_EQUAL(7, CMath::clamp(7, 10, 5));
+    CORE_TEST_EQUAL(10, CMath::clamp(20, 10, 5));
+}
+
+static void testSinCos() {
+    for(float f = -10.0f; f < 10.0f; f += 0.1f) {
+        float rSin = 0.0f;
+        float rCos = 0.0f;
+        CMath::sinCos(f, rSin, rCos);
+        CORE_TEST_FLOAT(sinf(f), rSin, eps);
+        CORE_TEST_FLOAT(cosf(f), rCos, eps);
+    }
+}
+
+static void testRadianToDegree() {
+    CORE_TEST_FLOAT(45.0f, CMath::radianToDegree(CMath::PI * 0.25f), eps);
+    CORE_TEST_FLOAT(90.0f, CMath::radianToDegree(CMath::PI * 0.5f), eps);
+    CORE_TEST_FLOAT(180.0f, CMath::radianToDegree(CMath::PI), eps);
+    CORE_TEST_FLOAT(360.0f, CMath::radianToDegree(CMath::PI * 2.0f), eps);
+}
+
+static void testDegreeToRadian() {
+    CORE_TEST_FLOAT(CMath::PI * 0.25f, CMath::degreeToRadian(45.0f), eps);
+    CORE_TEST_FLOAT(CMath::PI * 0.5f, CMath::degreeToRadian(90.0f), eps);
+    CORE_TEST_FLOAT(CMath::PI, CMath::degreeToRadian(180.0f), eps);
+    CORE_TEST_FLOAT(CMath::PI * 2.0f, CMath::degreeToRadian(360.0f), eps);
 }
 
 void Core::MathTests::test() {
@@ -54,4 +82,7 @@ void Core::MathTests::test() {
     testMin();
     testMax();
     testClamp();
+    testSinCos();
+    testRadianToDegree();
+    testDegreeToRadian();
 }

+ 204 - 0
tests/MatrixTests.cpp

@@ -0,0 +1,204 @@
+#include "tests/MatrixTests.h"
+
+#include "math/Matrix.h"
+#include "test/Test.h"
+
+const float eps = 0.0001f;
+
+template<int N, typename T>
+static void compareVectors(const Core::Vector<N, T>& wanted,
+                           const Core::Vector<N, T>& actual) {
+    for(int i = 0; i < N; i++) {
+        CORE_TEST_FLOAT(static_cast<float>(wanted[i]),
+                        static_cast<float>(actual[i]), eps);
+    }
+}
+
+static void testInit() {
+    Core::Matrix m;
+    const float* data = m.getValues();
+    for(int i = 0; i < 16; i++) {
+        int x = i % 4;
+        int y = i / 4;
+        CORE_TEST_FLOAT(x == y, data[i], 0.0f);
+    }
+}
+
+static void testTranspose() {
+    Core::Matrix m;
+    m.set(0, Core::Vector4(1.0f, 2.0f, 3.0f, 4.0f));
+    m.set(1, Core::Vector4(5.0f, 6.0f, 7.0f, 8.0f));
+    m.set(2, Core::Vector4(9.0f, 10.0f, 11.0f, 12.0f));
+    m.set(3, Core::Vector4(13.0f, 14.0f, 15.0f, 16.0f));
+    Core::Matrix t = m.transpose();
+    Core::Matrix m2 = t.transpose();
+
+    const float* mp = m.getValues();
+    const float* tp = t.getValues();
+    for(int x = 0; x < 4; x++) {
+        for(int y = 0; y < 4; y++) {
+            CORE_TEST_FLOAT(mp[y * 4 + x], tp[x * 4 + y], 0.0f);
+        }
+    }
+    const float* mp2 = m2.getValues();
+    for(int i = 0; i < 16; i++) {
+        CORE_TEST_FLOAT(mp[i], mp2[i], 0.0f);
+    }
+}
+
+static void testScale() {
+    Core::Matrix m;
+    m.scale(Core::Vector3(2.0f, 3.0f, 4.0f));
+    compareVectors(Core::Vector3(-8.0f, 18.0f, 28.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testUniformScale() {
+    Core::Matrix m;
+    m.scale(2.0f);
+    compareVectors(Core::Vector3(-8.0f, 12.0f, 14.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testTranslateX() {
+    Core::Matrix m;
+    m.translateX(5.0f);
+    compareVectors(Core::Vector3(1.0f, 6.0f, 7.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testTranslateY() {
+    Core::Matrix m;
+    m.translateY(6.0f);
+    compareVectors(Core::Vector3(-4.0f, 12.0f, 7.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testTranslateZ() {
+    Core::Matrix m;
+    m.translateZ(7.0f);
+    compareVectors(Core::Vector3(-4.0f, 6.0f, 14.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testTranslate() {
+    Core::Matrix m;
+    m.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+    compareVectors(Core::Vector3(-3.0f, 8.0f, 10.0f),
+                   m * Core::Vector3(-4.0f, 6.0f, 7.0f));
+}
+
+static void testCombination() {
+    Core::Matrix m;
+    m.scale(2.0f);
+    m.translateX(1.0f);
+    m.translateY(2.0f);
+    m.translateZ(3.0f);
+    m.translate(Core::Vector3(-4.0f, 2.0f, 3.0f));
+    m.scale(Core::Vector3(2.0f, 3.0f, 4.0f));
+    m.scale(0.5f);
+    compareVectors(Core::Vector3(-1.0f, 9.0f, 16.0f),
+                   m * Core::Vector3(1.0f, 1.0f, 1.0f));
+}
+
+static void testMatrixCombination() {
+    Core::Matrix a;
+    a.scale(2.0f);
+    a.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+
+    Core::Matrix b;
+    b.scale(3.0f);
+    b.translate(Core::Vector3(1.0f, 1.0f, 1.0f));
+
+    Core::Matrix c;
+    c.translate(Core::Vector3(-1.0f, -2.0f, -3.0f));
+    c *= b * a;
+
+    compareVectors(Core::Vector3(9.0f, 11.0f, 13.0f),
+                   c * Core::Vector3(1.0f, 1.0f, 1.0f));
+}
+
+static void testRotateX() {
+    Core::Matrix m;
+    m.rotateX(90);
+    compareVectors(Core::Vector3(1.0f, 0.0f, 0.0f),
+                   m * Core::Vector3(1.0f, 0.0f, 0.0f));
+    compareVectors(Core::Vector3(0.0f, 0.0f, 1.0f),
+                   m * Core::Vector3(0.0f, 1.0f, 0.0f));
+    compareVectors(Core::Vector3(0.0f, -1.0f, 0.0f),
+                   m * Core::Vector3(0.0f, 0.0f, 1.0f));
+}
+
+static void testRotateY() {
+    Core::Matrix m;
+    m.rotateY(90);
+    compareVectors(Core::Vector3(0.0f, 0.0f, -1.0f),
+                   m * Core::Vector3(1.0f, 0.0f, 0.0f));
+    compareVectors(Core::Vector3(0.0f, 1.0f, 0.0f),
+                   m * Core::Vector3(0.0f, 1.0f, 0.0f));
+    compareVectors(Core::Vector3(1.0f, 0.0f, 0.0f),
+                   m * Core::Vector3(0.0f, 0.0f, 1.0f));
+}
+
+static void testRotateZ() {
+    Core::Matrix m;
+    m.rotateZ(90);
+    compareVectors(Core::Vector3(0.0f, 1.0f, 0.0f),
+                   m * Core::Vector3(1.0f, 0.0f, 0.0f));
+    compareVectors(Core::Vector3(-1.0f, 0.0f, 0.0f),
+                   m * Core::Vector3(0.0f, 1.0f, 0.0f));
+    compareVectors(Core::Vector3(0.0f, 0.0f, 1.0f),
+                   m * Core::Vector3(0.0f, 0.0f, 1.0f));
+}
+
+static void testToString() {
+    Core::ArrayString<1024> s;
+    Core::Matrix m;
+    m.set(0, Core::Vector4(1.0f, 2.0f, 3.0f, 4.0f));
+    m.set(1, Core::Vector4(5.0f, 6.0f, 7.0f, 8.0f));
+    m.set(2, Core::Vector4(9.0f, 10.0f, 11.0f, 12.0f));
+    m.set(3, Core::Vector4(13.0f, 14.0f, 15.0f, 16.0f));
+    CORE_TEST_FALSE(s.append(m));
+    CORE_TEST_STRING(
+        "[[1.00, 2.00, 3.00, 4.00], [5.00, 6.00, 7.00, 8.00], "
+        "[9.00, 10.00, 11.00, 12.00], [13.00, 14.00, 15.00, 16.00]]",
+        s);
+}
+
+static void testQuaternionMatrix() {
+    Core::Quaternion q1(Core::Vector3(1.0f, 0.0f, 0.0f), 48.0f);
+    Core::Quaternion q2(Core::Vector3(0.0f, 1.0f, 0.0f), 52.0f);
+    Core::Quaternion q3(Core::Vector3(0.0f, 0.0f, 1.0f), 60.0f);
+
+    Core::Matrix m;
+    m.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+    m.rotate(q1).rotate(q2).rotate(q3);
+    m.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+
+    Core::Matrix check;
+    check.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+    check.rotateX(48.0f).rotateY(52.0f).rotateZ(60.0f);
+    check.translate(Core::Vector3(1.0f, 2.0f, 3.0f));
+
+    for(int i = 0; i < 16; i++) {
+        CORE_TEST_FLOAT(check.getValues()[i], m.getValues()[i], eps);
+    }
+}
+
+void Core::MatrixTests::test() {
+    testInit();
+    testScale();
+    testUniformScale();
+    testTranspose();
+    testTranslateX();
+    testTranslateY();
+    testTranslateZ();
+    testTranslate();
+    testCombination();
+    testMatrixCombination();
+    testRotateX();
+    testRotateY();
+    testRotateZ();
+    testToString();
+    testQuaternionMatrix();
+}

+ 8 - 0
tests/MatrixTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_MATRIX_TESTS_H
+#define CORE_MATRIX_TESTS_H
+
+namespace Core::MatrixTests {
+    void test();
+}
+
+#endif

+ 6 - 0
tests/UtilityTests.cpp

@@ -15,6 +15,12 @@ static void testPopCount() {
     CORE_TEST_EQUAL(32, Core::popCount(-1));
 }
 
+static void testIf() {
+    CORE_TEST_TRUE((Core::IsSame<Core::If<true, int, double>, int>));
+    CORE_TEST_TRUE((Core::IsSame<Core::If<false, int, double>, double>));
+}
+
 void Core::UtilityTests::test() {
     testPopCount();
+    testIf();
 }

+ 13 - 13
tests/VectorTests.cpp

@@ -19,22 +19,22 @@ static void testInitAndRead() {
     Core::Vector2 v2(1.0f, 2.0f);
     Core::Vector3 v3(3.0f, 4.0f, 5.0f);
     Core::Vector4 v4(6.0f, 7.0f, 8.0f, 9.0f);
-    // CORE_TEST_EQUAL(0.0f, v1[0]);
-    // CORE_TEST_EQUAL(0.0f, v1[0]);
-    // CORE_TEST_EQUAL(0.0f, v1[0]);
-    // CORE_TEST_EQUAL(1.0f, v2[0]);
-    // CORE_TEST_EQUAL(2.0f, v2[1]);
-    // CORE_TEST_EQUAL(3.0f, v3[0]);
-    // CORE_TEST_EQUAL(4.0f, v3[1]);
-    // CORE_TEST_EQUAL(5.0f, v3[2]);
-    // CORE_TEST_EQUAL(6.0f, v4[0]);
-    // CORE_TEST_EQUAL(7.0f, v4[1]);
-    // CORE_TEST_EQUAL(8.0f, v4[2]);
-    // CORE_TEST_EQUAL(9.0f, v4[3]);
+    CORE_TEST_FLOAT(0.0f, v1[0], 0.0f);
+    CORE_TEST_FLOAT(0.0f, v1[0], 0.0f);
+    CORE_TEST_FLOAT(0.0f, v1[0], 0.0f);
+    CORE_TEST_FLOAT(1.0f, v2[0], 0.0f);
+    CORE_TEST_FLOAT(2.0f, v2[1], 0.0f);
+    CORE_TEST_FLOAT(3.0f, v3[0], 0.0f);
+    CORE_TEST_FLOAT(4.0f, v3[1], 0.0f);
+    CORE_TEST_FLOAT(5.0f, v3[2], 0.0f);
+    CORE_TEST_FLOAT(6.0f, v4[0], 0.0f);
+    CORE_TEST_FLOAT(7.0f, v4[1], 0.0f);
+    CORE_TEST_FLOAT(8.0f, v4[2], 0.0f);
+    CORE_TEST_FLOAT(9.0f, v4[3], 0.0f);
 }
 
 static void testSetAngles() {
-    float root = sqrtf(2.0f) * 0.5f;
+    float root = Core::Math::squareRoot(2.0f) * 0.5f;
     compareVectors(Core::Vector3(1.0f, 0.0f, 0.0f),
                    Core::Vector3().setAngles(0.0f, 0.0f));
     compareVectors(Core::Vector3(root, 0.0f, -root),

+ 21 - 28
utils/ArrayString.h

@@ -1,12 +1,8 @@
 #ifndef CORE_STRING_H
 #define CORE_STRING_H
 
-#include <stdarg.h>
-#include <stdio.h>
-
 #include "math/Math.h"
 #include "utils/Check.h"
-#include "utils/Types.h"
 #include "utils/Utility.h"
 
 namespace Core {
@@ -118,57 +114,57 @@ namespace Core {
 
         // returns true on error
         check_return bool append(signed short s) {
-            return appendFormat("%hd", s);
+            return convertAppend(s);
         }
 
         // returns true on error
         check_return bool append(unsigned short s) {
-            return appendFormat("%hu", s);
+            return convertAppend(s);
         }
 
         // returns true on error
         check_return bool append(signed int i) {
-            return appendFormat("%d", i);
+            return convertAppend(i);
         }
 
         // returns true on error
         check_return bool append(unsigned int i) {
-            return appendFormat("%u", i);
+            return convertAppend(i);
         }
 
         // returns true on error
         check_return bool append(signed long l) {
-            return appendFormat("%ld", l);
+            return convertAppend(l);
         }
 
         // returns true on error
         check_return bool append(unsigned long l) {
-            return appendFormat("%lu", l);
+            return convertAppend(l);
         }
 
         // returns true on error
         check_return bool append(signed long long ll) {
-            return appendFormat("%lld", ll);
+            return convertAppend(ll);
         }
 
         // returns true on error
         check_return bool append(unsigned long long ll) {
-            return appendFormat("%llu", ll);
+            return convertAppend(ll);
         }
 
         // returns true on error
         check_return bool append(float f) {
-            return appendFormat("%.2f", static_cast<double>(f));
+            return convertAppend(static_cast<double>(f));
         }
 
         // returns true on error
         check_return bool append(double d) {
-            return appendFormat("%.2f", d);
+            return convertAppend(d);
         }
 
         // returns true on error
         check_return bool append(long double ld) {
-            return appendFormat("%.2Lf", ld);
+            return convertAppend(ld);
         }
 
         // returns true on error
@@ -219,7 +215,7 @@ namespace Core {
             for(int i = 0; i < length; i++) {
                 u32 c = data[i];
                 if(c < (1 << 7)) {
-                    if(putchar(static_cast<int>(c & 0x7F)) == EOF) {
+                    if(Core::putChar(static_cast<int>(c & 0x7F))) {
                         return true;
                     }
                 } else if(c < (1 << 11)) {
@@ -247,7 +243,7 @@ namespace Core {
 
         // returns true on error
         check_return bool printLine() const {
-            return print() || putchar('\n') == EOF;
+            return print() || Core::putChar('\n');
         }
 
         // returns true on error
@@ -263,8 +259,9 @@ namespace Core {
 
     private:
         static bool printChar(u32 u, u32 shift, u32 a, u32 o) {
-            return putchar(static_cast<int>(((u >> shift) & a) | o)) == EOF;
+            return Core::putChar(static_cast<int>(((u >> shift) & a) | o));
         }
+
         static u32 read(const char*& s) {
             if(*s == '\0') {
                 return 0;
@@ -345,18 +342,14 @@ namespace Core {
         }
 
         // returns true on error
-        check_format(2, 3) check_return
-            bool appendFormat(const char* format, ...) {
+        template<typename T>
+        check_return bool convertAppend(T t) {
             constexpr int BUFFER_SIZE = 64;
             char buffer[BUFFER_SIZE];
-
-            va_list args;
-            va_start(args, format);
-            int written = vsnprintf(buffer, BUFFER_SIZE, format, args);
-            va_end(args);
-
-            return written >= BUFFER_SIZE ||
-                   append(static_cast<const char*>(buffer));
+            if(Core::toString(t, buffer, BUFFER_SIZE)) {
+                return true;
+            }
+            return append(static_cast<const char*>(buffer));
         }
     };
 }

+ 3 - 3
utils/HashCode.cpp

@@ -1,13 +1,13 @@
 #include "utils/HashCode.h"
 
-#include <string.h>
+#include "utils/Utility.h"
 
 template<typename T>
 u32 hashNumber(T t) {
     constexpr u32 L = sizeof(T) / 4 + (sizeof(T) % 4 != 0);
     u32 parts[L];
-    memset(parts, 0, sizeof(parts));
-    memcpy(parts, &t, sizeof(T));
+    Core::memorySet(parts, 0, sizeof(parts));
+    Core::memoryCopy(parts, &t, sizeof(T));
     u32 hash = 0;
     for(u32 i = 0; i < L; i++) {
         hash ^= parts[i];

+ 1 - 1
utils/HashCode.h

@@ -1,7 +1,7 @@
 #ifndef CORE_HASH_CODE_H
 #define CORE_HASH_CODE_H
 
-#include "utils/Types.h"
+#include "utils/Utility.h"
 
 namespace Core {
     template<typename H>

+ 7 - 13
utils/Logger.h

@@ -15,20 +15,14 @@ namespace Core::Logger {
             return;
         }
         Core::ArrayString<2048> s;
-        if(s.append(format)) {
-            if(printf("\33[1;31m%s:%d contains an invalid format "
-                      "string\33[39;49m\n",
-                      file, line) <= 0) {
-                Core::exitWithHandler(1);
-            }
+        if(s.append(start) || s.append("#:# | ") || s.format(file, line)) {
+            CORE_EXIT(1);
+        } else if(s.append(format)) {
+            CORE_EXIT(1);
         } else if(s.format(Core::forward<Args>(args)...)) {
-            if(printf("\33[1;31m%s:%d formatting failed\33[39;49m\n", file,
-                      line) <= 0) {
-                Core::exitWithHandler(1);
-            }
-        } else if(printf("%s%s:%d | ", start, file, line) <= 0 || s.print() ||
-                  printf("\33[39;49m\n") <= 0) {
-            Core::exitWithHandler(1);
+            CORE_EXIT(1);
+        } else if(s.append("\33[39;49m\n") || s.print()) {
+            CORE_EXIT(1);
         }
     }
 }

+ 0 - 15
utils/Types.h

@@ -1,15 +0,0 @@
-#ifndef CORE_TYPES_H
-#define CORE_TYPES_H
-
-#include <inttypes.h>
-
-typedef int64_t i64;
-typedef int32_t i32;
-typedef int16_t i16;
-typedef int8_t i8;
-typedef uint64_t u64;
-typedef uint32_t u32;
-typedef uint16_t u16;
-typedef uint8_t u8;
-
-#endif

+ 45 - 1
utils/Utility.cpp

@@ -1,5 +1,9 @@
 #include "utils/Utility.h"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 static Core::ExitHandler exitHandler = nullptr;
 static void* exitData = nullptr;
 
@@ -39,7 +43,8 @@ void* operator new(size_t bytes, void* p) noexcept {
     return p;
 }*/
 
-void Core::exitWithHandler(int value) {
+void Core::exitWithHandler(const char* file, int line, int value) {
+    printf("\33[1;31mExit from %s:%d\33[39;49m\n", file, line);
     if(exitHandler != nullptr) {
         exitHandler(value, exitData);
     }
@@ -49,4 +54,43 @@ void Core::exitWithHandler(int value) {
 void Core::setExitHandler(ExitHandler eh, void* data) {
     exitHandler = eh;
     exitData = data;
+}
+
+#define CORE_TO_STRING(type, cast, format)                                     \
+    check_return bool Core::toString(type t, char* buffer, int size) {         \
+        if(size < 0) {                                                         \
+            return true;                                                       \
+        }                                                                      \
+        return snprintf(buffer, static_cast<unsigned int>(size), format,       \
+                        static_cast<cast>(t)) >= size;                         \
+    }
+
+CORE_TO_STRING(signed short, signed short, "%hd")
+CORE_TO_STRING(unsigned short, unsigned short, "%hu")
+CORE_TO_STRING(signed int, signed int, "%d")
+CORE_TO_STRING(unsigned int, unsigned int, "%u")
+CORE_TO_STRING(signed long, signed long, "%ld")
+CORE_TO_STRING(unsigned long, unsigned long, "%lu")
+CORE_TO_STRING(signed long long, signed long long, "%lld")
+CORE_TO_STRING(unsigned long long, unsigned long long, "%llu")
+CORE_TO_STRING(float, double, "%.2f")
+CORE_TO_STRING(double, double, "%.2lf")
+CORE_TO_STRING(long double, long double, "%.2Lf")
+
+bool Core::putChar(int c) {
+    return putchar(c) == EOF;
+}
+
+void Core::memorySet(void* p, int c, int n) {
+    if(n <= 0) {
+        return;
+    }
+    memset(p, c, static_cast<unsigned int>(n));
+}
+
+void Core::memoryCopy(void* dest, const void* src, int n) {
+    if(n < 0) {
+        return;
+    }
+    memcpy(dest, src, static_cast<unsigned int>(n));
 }

+ 90 - 6
utils/Utility.h

@@ -1,7 +1,7 @@
 #ifndef CORE_UTILITY_H
 #define CORE_UTILITY_H
 
-#include <stdlib.h>
+#include "utils/Check.h"
 
 #define CORE_SIZE(t) static_cast<int>(sizeof(t))
 
@@ -9,17 +9,17 @@ namespace Core {
     namespace Internal {
         template<typename T>
         struct BaseRemoveReference {
-            typedef T t;
+            using Type = T;
         };
 
         template<typename T>
         struct BaseRemoveReference<T&> {
-            typedef T t;
+            using Type = T;
         };
 
         template<typename T>
         struct BaseRemoveReference<T&&> {
-            typedef T t;
+            using Type = T;
         };
 
         template<typename A, typename B>
@@ -31,14 +31,46 @@ namespace Core {
         struct BaseIsSame<T, T> {
             static constexpr bool value = true;
         };
+
+        template<bool C, typename A, typename B>
+        struct BaseIf {
+            using Type = A;
+        };
+
+        template<typename A, typename B>
+        struct BaseIf<false, A, B> {
+            using Type = B;
+        };
     }
 
     template<typename T>
-    using RemoveReference = Internal::BaseRemoveReference<T>::t;
+    using RemoveReference = Internal::BaseRemoveReference<T>::Type;
 
     template<typename T, typename U>
     constexpr bool IsSame = Internal::BaseIsSame<T, U>::value;
 
+    template<bool C, typename A, typename B>
+    using If = Internal::BaseIf<C, A, B>::Type;
+
+    namespace Internal {
+        template<int N, typename T1, typename T2, typename T3, typename T4,
+                 typename T5>
+        using SelectType =
+            If<sizeof(T1) == N, T1,
+               If<sizeof(T2) == N, T2,
+                  If<sizeof(T3) == N, T3, If<sizeof(T4) == N, T4, T5>>>>;
+
+        template<int N>
+        using SelectSigned =
+            SelectType<N, signed char, signed short, signed int, signed long,
+                       signed long long>;
+
+        template<int N>
+        using SelectUnsigned =
+            SelectType<N, unsigned char, unsigned short, unsigned int,
+                       unsigned long, unsigned long long>;
+    }
+
     template<typename T>
     constexpr RemoveReference<T>&& move(T&& t) {
         return static_cast<RemoveReference<T>&&>(t);
@@ -74,10 +106,62 @@ namespace Core {
 
     using ExitHandler = void (*)(int, void*);
 
-    void exitWithHandler(int value);
+    void exitWithHandler(const char* file, int line, int value);
     void setExitHandler(ExitHandler eh, void*);
+
+#define CORE_EXIT(exitValue)                                                   \
+    Core::exitWithHandler(__FILE__, __LINE__, exitValue)
+
+    // returns true on error
+    check_return bool toString(signed short s, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(unsigned short s, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(signed int i, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(unsigned int i, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(signed long l, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(unsigned long l, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(signed long long ll, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(unsigned long long ll, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(float f, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(double d, char* buffer, int size);
+    // returns true on error
+    check_return bool toString(long double ld, char* buffer, int size);
+
+    // returns true on error
+    check_return bool putChar(int c);
+
+    void memorySet(void* p, int c, int n);
+    void memoryCopy(void* dest, const void* src, int n);
 }
 
+using i64 = Core::Internal::SelectSigned<8>;
+using i32 = Core::Internal::SelectSigned<4>;
+using i16 = Core::Internal::SelectSigned<2>;
+using i8 = Core::Internal::SelectSigned<1>;
+using u64 = Core::Internal::SelectUnsigned<8>;
+using u32 = Core::Internal::SelectUnsigned<4>;
+using u16 = Core::Internal::SelectUnsigned<2>;
+using u8 = Core::Internal::SelectUnsigned<1>;
+
+static_assert(sizeof(i64) == 8, "invalid size");
+static_assert(sizeof(i32) == 4, "invalid size");
+static_assert(sizeof(i16) == 2, "invalid size");
+static_assert(sizeof(i8) == 1, "invalid size");
+static_assert(sizeof(u64) == 8, "invalid size");
+static_assert(sizeof(u32) == 4, "invalid size");
+static_assert(sizeof(u16) == 2, "invalid size");
+static_assert(sizeof(u8) == 1, "invalid size");
+
+using size_t = u64;
+
 void* operator new(size_t bytes) noexcept;
 void* operator new[](size_t bytes) noexcept;
 void operator delete(void* p) noexcept;