v2:

- Fixed whitespace
- Added implementation for a few more functions and added simple test cases for them
- Removed unused C++/CX interop functions

From 0afef3733674c7f7bdde444454813f9c5b39276d Mon Sep 17 00:00:00 2001
From: Alvin Wong <al...@alvinhc.com>
Date: Thu, 20 Oct 2022 23:15:47 +0800
Subject: [PATCH] headers: Add windowsnumerics.h, windowsnumerics.impl.h

These headers are only partially implemented. They include all the type
definitions and struct members according to the docs. All the functions
have been declared but not all of them are defined. Attempting to use
the undefined functions will likely produce a compiler warning followed
by a linker error.

Signed-off-by: Alvin Wong <al...@alvinhc.com>
---
 mingw-w64-headers/include/windowsnumerics.h   |   30 +
 .../include/windowsnumerics.impl.h            | 1064 +++++++++++++++++
 .../testcases/test_windowsnumerics.cpp        |  955 +++++++++++++++
 3 files changed, 2049 insertions(+)
 create mode 100644 mingw-w64-headers/include/windowsnumerics.h
 create mode 100644 mingw-w64-headers/include/windowsnumerics.impl.h
 create mode 100644 mingw-w64-headers/testcases/test_windowsnumerics.cpp

diff --git a/mingw-w64-headers/include/windowsnumerics.h 
b/mingw-w64-headers/include/windowsnumerics.h
new file mode 100644
index 000000000..e97086538
--- /dev/null
+++ b/mingw-w64-headers/include/windowsnumerics.h
@@ -0,0 +1,30 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+#ifndef _WINDOWSNUMERICS_
+#define _WINDOWSNUMERICS_
+
+#ifndef __cplusplus
+#error windowsnumerics.h requires C++
+#endif /* __cplusplus */
+
+#define _WINDOWS_NUMERICS_NAMESPACE_ Windows::Foundation::Numerics
+
+#define _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ \
+  namespace Windows { \
+    namespace Foundation { \
+      namespace Numerics
+
+#define _WINDOWS_NUMERICS_END_NAMESPACE_ \
+    } \
+  }
+
+#include "windowsnumerics.impl.h"
+
+#undef _WINDOWS_NUMERICS_NAMESPACE_
+#undef _WINDOWS_NUMERICS_BEGIN_NAMESPACE_
+#undef _WINDOWS_NUMERICS_END_NAMESPACE_
+
+#endif /* _WINDOWSNUMERICS_ */
diff --git a/mingw-w64-headers/include/windowsnumerics.impl.h 
b/mingw-w64-headers/include/windowsnumerics.impl.h
new file mode 100644
index 000000000..7785b1eba
--- /dev/null
+++ b/mingw-w64-headers/include/windowsnumerics.impl.h
@@ -0,0 +1,1064 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+/**
+ * Normal users should include `windowsnumerics.h` instead of this header.
+ * However, the cppwinrt headers set `_WINDOWS_NUMERICS_NAMESPACE_`,
+ * `_WINDOWS_NUMERICS_BEGIN_NAMESPACE_` and `_WINDOWS_NUMERICS_END_NAMESPACE_`
+ * to custom values and include `windowsnumerics.impl.h`. Therefore this shall
+ * be considered a public header, and these macros are public API.
+*/
+
+
+#ifdef min
+#  pragma push_macro("min")
+#  undef min
+#  define _WINDOWS_NUMERICS_IMPL_PUSHED_MIN_
+#endif
+
+#ifdef max
+#  pragma push_macro("max")
+#  undef max
+#  define _WINDOWS_NUMERICS_IMPL_PUSHED_MAX_
+#endif
+
+#include <algorithm>
+#include <cmath>
+
+#include "directxmath.h"
+
+
+// === Internal macros ===
+#define _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(_ty1, _op, _ty2) \
+  inline _ty1 &operator _op ## =(_ty1 &val1, _ty2 val2) { \
+    val1 = operator _op (val1, val2); \
+    return val1; \
+  }
+
+
+// === Internal functions ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+  namespace _impl {
+
+#if 0 && defined(__cpp_lib_clamp)
+    using std::clamp;
+#else
+    constexpr const float &clamp(const float &val, const float &min, const 
float &max) {
+      return val < min ? min : (val > max ? max : val);
+    }
+#endif
+
+#if 0 && defined(__cpp_lib_interpolate)
+    using std::lerp;
+#else
+    constexpr float lerp(float val1, float val2, float amount) {
+      // Don't do (val2 - val1) * amount + val1 as it has worse precision.
+      return val2 * amount + val1 * (1.0f - amount);
+    }
+#endif
+
+  }
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === Forward decls ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float2;
+  struct float3;
+  struct float4;
+  struct float3x2;
+  struct float4x4;
+  struct plane;
+  struct quaternion;
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === float2: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float2 {
+    float2() = default;
+    constexpr float2(float x, float y)
+      : x(x), y(y)
+    {}
+    constexpr explicit float2(float val)
+      : x(val), y(val)
+    {}
+
+    static constexpr float2 zero() {
+      return float2(0.0f);
+    }
+    static constexpr float2 one() {
+      return float2(1.0f);
+    }
+    static constexpr float2 unit_x() {
+      return { 1.0f, 0.0f };
+    }
+    static constexpr float2 unit_y() {
+      return { 0.0f, 1.0f };
+    }
+
+    float x;
+    float y;
+  };
+
+  // Forward decl functions
+  inline float length(const float2 &val);
+  inline float length_squared(const float2 &val);
+  inline float distance(const float2 &val1, const float2 &val2);
+  inline float distance_squared(const float2 &val1, const float2 &val2);
+  inline float dot(const float2 &val1, const float2 &val2);
+  inline float2 normalize(const float2 &val);
+  inline float2 reflect(const float2 &vec, const float2 &norm);
+  inline float2 min(const float2 &val1, const float2 &val2);
+  inline float2 max(const float2 &val1, const float2 &val2);
+  inline float2 clamp(const float2 &val, const float2 &min, const float2 &max);
+  inline float2 lerp(const float2 &val1, const float2 &val2, float amount);
+  inline float2 transform(const float2 &pos, const float3x2 &mat);
+  inline float2 transform(const float2 &pos, const float4x4 &mat);
+  inline float2 transform_normal(const float2 &norm, const float3x2 &mat);
+  inline float2 transform_normal(const float2 &norm, const float4x4 &mat);
+  inline float2 transform(const float2 &val, const quaternion &rot);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { val1.x _op val2.x, val1.y _op val2.y }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float2, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float2, -)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float2, *)
+  inline float2 operator*(const float2 &val1, float val2) {
+    return { val1.x * val2, val1.y * val2 };
+  }
+  inline float2 operator*(float val1, const float2 &val2) {
+    return operator*(val2, val1);
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float2, /)
+  inline float2 operator/(const float2 &val1, float val2) {
+    return operator*(val1, 1.0f / val2);
+  }
+  inline float2 operator-(const float2 &val) {
+    return { -val.x, -val.y };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, +, const float2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, -, const float2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, *, const float2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, *, float)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, /, const float2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float2, /, float)
+  inline bool operator==(const float2 &val1, const float2 &val2) {
+    return val1.x == val2.x && val1.y == val2.y;
+  }
+  inline bool operator!=(const float2 &val1, const float2 &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === float3: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float3 {
+    float3() = default;
+    constexpr float3(float x, float y, float z)
+      : x(x), y(y), z(z)
+    {}
+    constexpr float3(float2 val, float z)
+      : x(val.x), y(val.y), z(z)
+    {}
+    constexpr explicit float3(float val)
+      : x(val), y(val), z(val)
+    {}
+
+    static constexpr float3 zero() {
+      return float3(0.0);
+    }
+    static constexpr float3 one() {
+      return float3(1.0);
+    }
+    static constexpr float3 unit_x() {
+      return { 1.0f, 0.0f, 0.0f };
+    }
+    static constexpr float3 unit_y() {
+      return { 0.0f, 1.0f, 0.0f };
+    }
+    static constexpr float3 unit_z() {
+      return { 0.0f, 0.0f, 1.0f };
+    }
+
+    float x;
+    float y;
+    float z;
+  };
+
+  // Forward decl functions
+  inline float length(const float3 &val);
+  inline float length_squared(const float3 &val);
+  inline float distance(const float3 &val1, const float3 &val2);
+  inline float distance_squared(const float3 &val1, const float3 &val2);
+  inline float dot(const float3 &val1, const float3 &val2);
+  inline float3 normalize(const float3 &val);
+  inline float3 cross(const float3 &val1, const float3 &val2);
+  inline float3 reflect(const float3 &vec, const float3 &norm);
+  inline float3 min(const float3 &val1, const float3 &val2);
+  inline float3 max(const float3 &val1, const float3 &val2);
+  inline float3 clamp(const float3 &val, const float3 &min, const float3 &max);
+  inline float3 lerp(const float3 &val1, const float3 &val2, float amount);
+  inline float3 transform(const float3 &pos, const float4x4 &mat);
+  inline float3 transform_normal(const float3 &norm, const float4x4 &mat);
+  inline float3 transform(const float3 &val, const quaternion &rot);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { val1.x _op val2.x, val1.y _op val2.y, val1.z _op val2.z }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3, -)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3, *)
+  inline float3 operator*(const float3 &val1, float val2) {
+    return { val1.x * val2, val1.y * val2, val1.z * val2 };
+  }
+  inline float3 operator*(float val1, const float3 &val2) {
+    return operator*(val2, val1);
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3, /)
+  inline float3 operator/(const float3 &val1, float val2) {
+    return operator*(val1, 1.0f / val2);
+  }
+  inline float3 operator-(const float3 &val) {
+    return { -val.x, -val.y, -val.z };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, +, const float3 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, -, const float3 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, *, const float3 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, *, float)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, /, const float3 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3, /, float)
+  inline bool operator==(const float3 &val1, const float3 &val2) {
+    return val1.x == val2.x && val1.y == val2.y && val1.z == val2.z;
+  }
+  inline bool operator!=(const float3 &val1, const float3 &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === float4: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float4 {
+    float4() = default;
+    constexpr float4(float x, float y, float z, float w)
+      : x(x), y(y), z(z), w(w)
+    {}
+    constexpr float4(float2 val, float z, float w)
+      : x(val.x), y(val.y), z(z), w(w)
+    {}
+    constexpr float4(float3 val, float w)
+      : x(val.x), y(val.y), z(val.z), w(w)
+    {}
+    constexpr explicit float4(float val)
+      : x(val), y(val), z(val), w(val)
+    {}
+
+    static constexpr float4 zero() {
+      return float4(0.0);
+    }
+    static constexpr float4 one() {
+      return float4(1.0);
+    }
+    static constexpr float4 unit_x() {
+      return { 1.0f, 0.0f, 0.0f, 0.0f };
+    }
+    static constexpr float4 unit_y() {
+      return { 0.0f, 1.0f, 0.0f, 0.0f };
+    }
+    static constexpr float4 unit_z() {
+      return { 0.0f, 0.0f, 1.0f, 0.0f };
+    }
+    static constexpr float4 unit_w() {
+      return { 0.0f, 0.0f, 0.0f, 1.0f };
+    }
+
+    float x;
+    float y;
+    float z;
+    float w;
+  };
+
+  // Forward decl functions
+  inline float length(const float4 &val);
+  inline float length_squared(const float4 &val);
+  inline float distance(const float4 &val1, const float4 &val2);
+  inline float distance_squared(const float4 &val1, const float4 &val2);
+  inline float dot(const float4 &val1, const float4 &val2);
+  inline float4 normalize(const float4 &val);
+  inline float4 min(const float4 &val1, const float4 &val2);
+  inline float4 max(const float4 &val1, const float4 &val2);
+  inline float4 clamp(const float4 &val, const float4 &min, const float4 &max);
+  inline float4 lerp(const float4 &val1, const float4 &val2, float amount);
+  inline float4 transform(const float4 &pos, const float4x4 &mat);
+  inline float4 transform4(const float3 &pos, const float4x4 &mat);
+  inline float4 transform4(const float2 &pos, const float4x4 &mat);
+  inline float4 transform(const float4 &val, const quaternion &rot);
+  inline float4 transform4(const float3 &val, const quaternion &rot);
+  inline float4 transform4(const float2 &val, const quaternion &rot);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { val1.x _op val2.x, val1.y _op val2.y, val1.z _op val2.z, val1.w 
_op val2.w }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4, -)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4, *)
+  inline float4 operator*(const float4 &val1, float val2) {
+    return { val1.x * val2, val1.y * val2, val1.z * val2, val1.w * val2 };
+  }
+  inline float4 operator*(float val1, const float4 &val2) {
+    return operator*(val2, val1);
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4, /)
+  inline float4 operator/(const float4 &val1, float val2) {
+    return operator*(val1, 1.0f / val2);
+  }
+  inline float4 operator-(const float4 &val) {
+    return { -val.x, -val.y, -val.z, -val.w };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, +, const float4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, -, const float4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, *, const float4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, *, float)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, /, const float4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4, /, float)
+  inline bool operator==(const float4 &val1, const float4 &val2) {
+    return val1.x == val2.x && val1.y == val2.y && val1.z == val2.z && val2.w 
== val2.w;
+  }
+  inline bool operator!=(const float4 &val1, const float4 &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === float3x2: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float3x2 {
+    float3x2() = default;
+    constexpr float3x2(
+      float m11, float m12,
+      float m21, float m22,
+      float m31, float m32
+    )
+      : m11(m11), m12(m12)
+      , m21(m21), m22(m22)
+      , m31(m31), m32(m32)
+    {}
+
+    static constexpr float3x2 identity() {
+      return {
+        1.0f, 0.0f,
+        0.0f, 1.0f,
+        0.0f, 0.0f,
+      };
+    }
+
+    float m11; float m12;
+    float m21; float m22;
+    float m31; float m32;
+  };
+
+  // Forward decl functions
+  inline float3x2 make_float3x2_translation(const float2 &pos);
+  inline float3x2 make_float3x2_translation(float xpos, float ypos);
+  inline float3x2 make_float3x2_scale(float xscale, float yscale);
+  inline float3x2 make_float3x2_scale(float xscale, float yscale, const float2 
&center);
+  inline float3x2 make_float3x2_scale(const float2 &xyscale);
+  inline float3x2 make_float3x2_scale(const float2 &xyscale, const float2 
&center);
+  inline float3x2 make_float3x2_scale(float scale);
+  inline float3x2 make_float3x2_scale(float scale, const float2 &center);
+  inline float3x2 make_float3x2_skew(float xrad, float yrad);
+  inline float3x2 make_float3x2_skew(float xrad, float yrad, const float2 
&center);
+  inline float3x2 make_float3x2_rotation(float rad);
+  inline float3x2 make_float3x2_rotation(float rad, const float2 &center);
+  inline bool is_identity(const float3x2 &val);
+  inline float determinant(const float3x2 &val);
+  inline float2 translation(const float3x2 &val);
+  inline bool invert(const float3x2 &val, float3x2 *out);
+  inline float3x2 lerp(const float3x2 &mat1, const float3x2 &mat2, float 
amount);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { \
+      val1.m11 _op val2.m11, val1.m12 _op val2.m12, \
+      val1.m21 _op val2.m21, val1.m22 _op val2.m22, \
+      val1.m31 _op val2.m31, val1.m32 _op val2.m32, \
+    }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3x2, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float3x2, -)
+  inline float3x2 operator*(const float3x2 &val1, const float3x2 &val2) {
+    // 2D transformation matrix has an implied 3rd column with (0, 0, 1)
+    const float3 v1r1(val1.m11, val1.m12, 0.0f);
+    const float3 v1r2(val1.m21, val1.m22, 0.0f);
+    const float3 v1r3(val1.m31, val1.m32, 1.0f);
+    const float3 v2c1(val2.m11, val2.m21, val2.m31);
+    const float3 v2c2(val2.m12, val2.m22, val2.m32);
+    // const float3 v2c3(0.0f, 0.0f, 1.0f);
+    return {
+      dot(v1r1, v2c1), dot(v1r1, v2c2),
+      dot(v1r2, v2c1), dot(v1r2, v2c2),
+      dot(v1r3, v2c1), dot(v1r3, v2c2),
+    };
+  }
+  inline float3x2 operator*(const float3x2 &val1, float val2) {
+    return {
+      val1.m11 * val2, val1.m12 * val2,
+      val1.m21 * val2, val1.m22 * val2,
+      val1.m31 * val2, val1.m32 * val2,
+    };
+  }
+  inline float3x2 operator-(const float3x2 &val) {
+    return {
+      -val.m11, -val.m12,
+      -val.m21, -val.m22,
+      -val.m31, -val.m32,
+    };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3x2, +, const float3x2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3x2, -, const float3x2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3x2, *, const float3x2 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float3x2, *, float)
+  inline bool operator==(const float3x2 &val1, const float3x2 &val2) {
+    return
+      val1.m11 == val2.m11 && val1.m12 == val2.m12 &&
+      val1.m21 == val2.m21 && val1.m22 == val2.m22 &&
+      val1.m31 == val2.m31 && val1.m32 == val2.m32;
+  }
+  inline bool operator!=(const float3x2 &val1, const float3x2 &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === float4x4: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct float4x4 {
+    float4x4() = default;
+    constexpr float4x4(
+      float m11, float m12, float m13, float m14,
+      float m21, float m22, float m23, float m24,
+      float m31, float m32, float m33, float m34,
+      float m41, float m42, float m43, float m44
+    )
+      : m11(m11), m12(m12), m13(m13), m14(m14)
+      , m21(m21), m22(m22), m23(m23), m24(m24)
+      , m31(m31), m32(m32), m33(m33), m34(m34)
+      , m41(m41), m42(m42), m43(m43), m44(m44)
+    {}
+
+    static constexpr float4x4 identity() {
+      return {
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 1.0f, 0.0f,
+        0.0f, 0.0f, 0.0f, 1.0f,
+      };
+    }
+
+    float m11; float m12; float m13; float m14;
+    float m21; float m22; float m23; float m24;
+    float m31; float m32; float m33; float m34;
+    float m41; float m42; float m43; float m44;
+  };
+
+  // Forward decl functions
+  inline float4x4 make_float4x4_billboard(const float3 &objpos, const float3 
&camerapos, const float3 &cameraup, const float3 &camerafwd);
+  inline float4x4 make_float4x4_constrained_billboard(const float3 &objpos, 
const float3 &camerapos, const float3 &rotateaxis, const float3 &camerafwd, 
const float3 &objfwd);
+  inline float4x4 make_float4x4_translation(const float3 &pos);
+  inline float4x4 make_float4x4_translation(float xpos, float ypos, float 
zpos);
+  inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale);
+  inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale, const float3 &center);
+  inline float4x4 make_float4x4_scale(const float3 &xyzscale);
+  inline float4x4 make_float4x4_scale(const float3 &xyzscale, const float3 
&center);
+  inline float4x4 make_float4x4_scale(float scale);
+  inline float4x4 make_float4x4_scale(float scale, const float3 &center);
+  inline float4x4 make_float4x4_rotation_x(float rad);
+  inline float4x4 make_float4x4_rotation_x(float rad, const float3 &center);
+  inline float4x4 make_float4x4_rotation_y(float rad);
+  inline float4x4 make_float4x4_rotation_y(float rad, const float3 &center);
+  inline float4x4 make_float4x4_rotation_z(float rad);
+  inline float4x4 make_float4x4_rotation_z(float rad, const float3 &center);
+  inline float4x4 make_float4x4_from_axis_angle(const float3 &axis, float 
angle);
+  inline float4x4 make_float4x4_perspective_field_of_view(float fov, float 
aspect, float nearplane, float farplane);
+  inline float4x4 make_float4x4_perspective(float w, float h, float nearplane, 
float farplane);
+  inline float4x4 make_float4x4_perspective_off_center(float left, float 
right, float bottom, float top, float nearplane, float farplane);
+  inline float4x4 make_float4x4_orthographic(float w, float h, float 
znearplane, float zfarplane);
+  inline float4x4 make_float4x4_orthographic_off_center(float left, float 
right, float bottom, float top, float znearplane, float zfarplane);
+  inline float4x4 make_float4x4_look_at(const float3 &camerapos, const float3 
&target, const float3 &cameraup);
+  inline float4x4 make_float4x4_world(const float3 &pos, const float3 &fwd, 
const float3 &up);
+  inline float4x4 make_float4x4_from_quaternion(const quaternion &quat);
+  inline float4x4 make_float4x4_from_yaw_pitch_roll(float yaw, float pitch, 
float roll);
+  inline float4x4 make_float4x4_shadow(const float3 &lightdir, const plane 
&plane);
+  inline float4x4 make_float4x4_reflection(const plane &plane);
+  inline bool is_identity(const float4x4 &val);
+  inline float determinant(const float4x4 &val);
+  inline float3 translation(const float4x4 &val);
+  inline bool invert(const float4x4 &mat, float4x4 *out);
+  inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion 
*out_rot, float3 *out_translate);
+  inline float4x4 transform(const float4x4 &val, const quaternion &rot);
+  inline float4x4 transpose(const float4x4 &val);
+  inline float4x4 lerp(const float4x4 &val1, const float4x4 &val2, float 
amount);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { \
+      val1.m11 _op val2.m11, val1.m12 _op val2.m12, val1.m13 _op val2.m13, 
val1.m14 _op val2.m14, \
+      val1.m21 _op val2.m21, val1.m22 _op val2.m22, val1.m23 _op val2.m23, 
val1.m24 _op val2.m24, \
+      val1.m31 _op val2.m31, val1.m32 _op val2.m32, val1.m33 _op val2.m33, 
val1.m34 _op val2.m34, \
+      val1.m41 _op val2.m41, val1.m42 _op val2.m42, val1.m43 _op val2.m43, 
val1.m44 _op val2.m44, \
+    }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4x4, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(float4x4, -)
+  inline float4x4 operator*(const float4x4 &val1, const float4x4 &val2) {
+    const float4 v1r1(val1.m11, val1.m12, val1.m13, val1.m14);
+    const float4 v1r2(val1.m21, val1.m22, val1.m23, val1.m24);
+    const float4 v1r3(val1.m31, val1.m32, val1.m33, val1.m34);
+    const float4 v1r4(val1.m41, val1.m42, val1.m43, val1.m44);
+    const float4 v2c1(val2.m11, val2.m21, val2.m31, val2.m41);
+    const float4 v2c2(val2.m12, val2.m22, val2.m32, val2.m42);
+    const float4 v2c3(val2.m13, val2.m23, val2.m33, val2.m43);
+    const float4 v2c4(val2.m14, val2.m24, val2.m34, val2.m44);
+    return {
+      dot(v1r1, v2c1), dot(v1r1, v2c2), dot(v1r1, v2c3), dot(v1r1, v2c4),
+      dot(v1r2, v2c1), dot(v1r2, v2c2), dot(v1r2, v2c3), dot(v1r2, v2c4),
+      dot(v1r3, v2c1), dot(v1r3, v2c2), dot(v1r3, v2c3), dot(v1r3, v2c4),
+      dot(v1r4, v2c1), dot(v1r4, v2c2), dot(v1r4, v2c3), dot(v1r4, v2c4),
+    };
+  }
+  inline float4x4 operator*(const float4x4 &val1, float val2) {
+    return {
+      val1.m11 * val2, val1.m12 * val2, val1.m13 * val2, val1.m14 * val2,
+      val1.m21 * val2, val1.m22 * val2, val1.m23 * val2, val1.m24 * val2,
+      val1.m31 * val2, val1.m32 * val2, val1.m33 * val2, val1.m34 * val2,
+      val1.m41 * val2, val1.m42 * val2, val1.m43 * val2, val1.m44 * val2,
+    };
+  }
+  inline float4x4 operator-(const float4x4 &val) {
+    return {
+      -val.m11, -val.m12, -val.m13, -val.m14,
+      -val.m21, -val.m22, -val.m23, -val.m24,
+      -val.m31, -val.m32, -val.m33, -val.m34,
+      -val.m41, -val.m42, -val.m43, -val.m44,
+    };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4x4, +, const float4x4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4x4, -, const float4x4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4x4, *, const float4x4 &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(float4x4, *, float)
+  inline bool operator==(const float4x4 &val1, const float4x4 &val2) {
+    return
+      val1.m11 == val2.m11 && val1.m12 == val2.m12 && val1.m13 == val2.m13 && 
val1.m14 == val2.m14 &&
+      val1.m21 == val2.m21 && val1.m22 == val2.m22 && val1.m23 == val2.m23 && 
val1.m24 == val2.m24 &&
+      val1.m31 == val2.m31 && val1.m32 == val2.m32 && val1.m33 == val2.m33 && 
val1.m34 == val2.m34 &&
+      val1.m41 == val2.m41 && val1.m42 == val2.m42 && val1.m43 == val2.m43 && 
val1.m44 == val2.m44;
+  }
+  inline bool operator!=(const float4x4 &val1, const float4x4 &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === plane: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct plane {
+    plane() = default;
+    constexpr plane(float x, float y, float z, float d)
+      : normal(float3(x, y, z)), d(d)
+    {}
+    constexpr plane(float3 normal, float d)
+      : normal(normal), d(d)
+    {}
+    constexpr explicit plane(float4 val)
+      : normal(float3(val.x, val.y, val.z)), d(val.w)
+    {}
+
+    float3 normal;
+    float d;
+  };
+
+  // Forward decl functions
+  inline plane make_plane_from_vertices(const float3 &pt1, const float3 &pt2, 
const float3 &pt3);
+  inline plane normalize(const plane &val);
+  inline plane transform(const plane &plane, const float4x4 &mat);
+  inline plane transform(const plane &plane, const quaternion &rot);
+  inline float dot(const plane &plane, const float4 &val);
+  inline float dot_coordinate(const plane &plane, const float3 &val);
+  inline float dot_normal(const plane &plane, const float3 &val);
+
+  // Define operators
+  inline bool operator==(const plane &val1, const plane &val2) {
+    return val1.normal == val2.normal && val1.d == val2.d;
+  }
+  inline bool operator!=(const plane &val1, const plane &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === quaternion: Struct and function defs ===
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  struct quaternion {
+    quaternion() = default;
+    constexpr quaternion(float x, float y, float z, float w)
+      : x(x), y(y), z(z), w(w)
+    {}
+    constexpr quaternion(float3 vecPart, float scalarPart)
+      : x(vecPart.x), y(vecPart.y), z(vecPart.z), w(scalarPart)
+    {}
+
+    static constexpr quaternion identity() {
+      return { 0.0f, 0.0f, 0.0f, 1.0f };
+    }
+
+    float x;
+    float y;
+    float z;
+    float w;
+  };
+
+  // Forward decl functions
+  inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float 
angle);
+  inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float 
pitch, float roll);
+  inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat);
+  inline bool is_identity(const quaternion &val);
+  inline float length(const quaternion &val);
+  inline float length_squared(const quaternion &val);
+  inline float dot(const quaternion &val1, const quaternion &val2);
+  inline quaternion normalize(const quaternion &val);
+  inline quaternion conjugate(const quaternion &val);
+  inline quaternion inverse(const quaternion &val);
+  inline quaternion slerp(const quaternion &val1, const quaternion &val2, 
float amount);
+  inline quaternion lerp(const quaternion &val1, const quaternion &val2, float 
amount);
+  inline quaternion concatenate(const quaternion &val1, const quaternion 
&val2);
+
+  // Define operators
+#define _WINDOWS_NUMERICS_IMPL_BINARY_OP(_ty, _op) \
+  inline _ty operator _op(const _ty &val1, const _ty &val2) { \
+    return { val1.x _op val2.x, val1.y _op val2.y, val1.z _op val2.z, val1.w 
_op val2.w }; \
+  }
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(quaternion, +)
+  _WINDOWS_NUMERICS_IMPL_BINARY_OP(quaternion, -)
+  inline quaternion operator*(const quaternion &val1, const quaternion &val2) {
+    return {
+      val1.w * val2.x + val1.x * val2.w + val1.y * val2.z - val1.z * val2.y,
+      val1.w * val2.y - val1.x * val2.z + val1.y * val2.w + val1.z * val2.x,
+      val1.w * val2.z + val1.x * val2.y - val1.y * val2.x + val1.z * val2.w,
+      val1.w * val2.w - val1.x * val2.x - val1.y * val2.y - val1.z * val2.z,
+    }; }
+  inline quaternion operator*(const quaternion &val1, float val2) {
+    return { val1.x * val2, val1.y * val2, val1.z * val2, val1.w * val2 };
+  }
+  inline quaternion operator/(const quaternion &val1, const quaternion &val2) {
+    return operator*(val1, inverse(val2));
+  }
+  inline quaternion operator-(const quaternion &val) {
+    return { -val.x, -val.y, -val.z, -val.w };
+  }
+#undef _WINDOWS_NUMERICS_IMPL_BINARY_OP
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(quaternion, +, const quaternion &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(quaternion, -, const quaternion &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(quaternion, *, const quaternion &)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(quaternion, *, float)
+  _WINDOWS_NUMERICS_IMPL_ASSIGN_OP(quaternion, /, const quaternion &)
+  inline bool operator==(const quaternion &val1, const quaternion &val2) {
+    return val1.x == val2.x && val1.y == val2.y && val1.z == val2.z && val2.w 
== val2.w;
+  }
+  inline bool operator!=(const quaternion &val1, const quaternion &val2) {
+    return !operator==(val1, val2);
+  }
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// === Function definitions ===
+
+// Define float2 functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  inline float length(const float2 &val) {
+    return ::std::sqrt(length_squared(val));
+  }
+  inline float length_squared(const float2 &val) {
+    return val.x * val.x + val.y * val.y;
+  }
+  inline float distance(const float2 &val1, const float2 &val2) {
+    return length(val2 - val1);
+  }
+  inline float distance_squared(const float2 &val1, const float2 &val2) {
+    return length_squared(val2 - val1);
+  }
+  inline float dot(const float2 &val1, const float2 &val2) {
+    return val1.x * val2.x + val1.y * val2.y;
+  }
+  inline float2 normalize(const float2 &val) {
+    return val / length(val);
+  }
+  inline float2 reflect(const float2 &vec, const float2 &norm) {
+    // norm is assumed to be normalized.
+    return vec - 2.0f * dot(vec, norm) * norm;
+  }
+  inline float2 min(const float2 &val1, const float2 &val2) {
+    return { ::std::min(val1.x, val2.x), ::std::min(val1.y, val2.y) };
+  }
+  inline float2 max(const float2 &val1, const float2 &val2) {
+    return { ::std::max(val1.x, val2.x), ::std::max(val1.y, val2.y) };
+  }
+  inline float2 clamp(const float2 &val, const float2 &min, const float2 &max) 
{
+    return { _impl::clamp(val.x, min.x, max.x), _impl::clamp(val.y, min.y, 
max.y) };
+  }
+  inline float2 lerp(const float2 &val1, const float2 &val2, float amount) {
+    return { _impl::lerp(val1.x, val2.x, amount), _impl::lerp(val1.y, val2.y, 
amount) };
+  }
+  // TODO: impl
+  inline float2 transform(const float2 &pos, const float3x2 &mat);
+  inline float2 transform(const float2 &pos, const float4x4 &mat);
+  inline float2 transform_normal(const float2 &norm, const float3x2 &mat);
+  inline float2 transform_normal(const float2 &norm, const float4x4 &mat);
+  inline float2 transform(const float2 &val, const quaternion &rot);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define float3 functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  inline float length(const float3 &val) {
+    return ::std::sqrt(length_squared(val));
+  }
+  inline float length_squared(const float3 &val) {
+    return val.x * val.x + val.y * val.y + val.z * val.z;
+  }
+  inline float distance(const float3 &val1, const float3 &val2) {
+    return length(val2 - val1);
+  }
+  inline float distance_squared(const float3 &val1, const float3 &val2) {
+    return length_squared(val2 - val1);
+  }
+  inline float dot(const float3 &val1, const float3 &val2) {
+    return val1.x * val2.x + val1.y * val2.y + val1.z * val2.z;
+  }
+  inline float3 normalize(const float3 &val) {
+    return val / length(val);
+  }
+  inline float3 cross(const float3 &val1, const float3 &val2) {
+    return {
+      val1.y * val2.z - val2.y * val1.z,
+      val1.z * val2.x - val2.z * val1.x,
+      val1.x * val2.y - val2.x * val1.y,
+    };
+  }
+  inline float3 reflect(const float3 &vec, const float3 &norm) {
+    // norm is assumed to be normalized.
+    return vec - 2.0f * dot(vec, norm) * norm;
+  }
+  inline float3 min(const float3 &val1, const float3 &val2) {
+    return { ::std::min(val1.x, val2.x), ::std::min(val1.y, val2.y), 
::std::min(val1.z, val2.z) };
+  }
+  inline float3 max(const float3 &val1, const float3 &val2) {
+    return { ::std::max(val1.x, val2.x), ::std::max(val1.y, val2.y), 
::std::max(val1.z, val2.z) };
+  }
+  inline float3 clamp(const float3 &val, const float3 &min, const float3 &max) 
{
+    return { _impl::clamp(val.x, min.x, max.x), _impl::clamp(val.y, min.y, 
max.y), _impl::clamp(val.z, min.z, max.z) };
+  }
+  inline float3 lerp(const float3 &val1, const float3 &val2, float amount) {
+    return { _impl::lerp(val1.x, val2.x, amount), _impl::lerp(val1.y, val2.y, 
amount), _impl::lerp(val1.z, val2.z, amount) };
+  }
+  // TODO: impl
+  inline float3 transform(const float3 &pos, const float4x4 &mat);
+  inline float3 transform_normal(const float3 &norm, const float4x4 &mat);
+  inline float3 transform(const float3 &val, const quaternion &rot);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define float4 functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  inline float length(const float4 &val) {
+    return ::std::sqrt(length_squared(val));
+  }
+  inline float length_squared(const float4 &val) {
+    return val.x * val.x + val.y * val.y + val.z * val.z + val.w * val.w;
+  }
+  inline float distance(const float4 &val1, const float4 &val2) {
+    return length(val2 - val1);
+  }
+  inline float distance_squared(const float4 &val1, const float4 &val2) {
+    return length_squared(val2 - val1);
+  }
+  inline float dot(const float4 &val1, const float4 &val2) {
+    return val1.x * val2.x + val1.y * val2.y + val1.z * val2.z + val1.w * 
val2.w;
+  }
+  inline float4 normalize(const float4 &val) {
+    return val / length(val);
+  }
+  inline float4 min(const float4 &val1, const float4 &val2) {
+    return {
+      ::std::min(val1.x, val2.x),
+      ::std::min(val1.y, val2.y),
+      ::std::min(val1.z, val2.z),
+      ::std::min(val1.w, val2.w),
+    };
+  }
+  inline float4 max(const float4 &val1, const float4 &val2) {
+    return {
+      ::std::max(val1.x, val2.x),
+      ::std::max(val1.y, val2.y),
+      ::std::max(val1.z, val2.z),
+      ::std::max(val1.w, val2.w),
+    };
+  }
+  inline float4 clamp(const float4 &val, const float4 &min, const float4 &max) 
{
+    return {
+      _impl::clamp(val.x, min.x, max.x),
+      _impl::clamp(val.y, min.y, max.y),
+      _impl::clamp(val.z, min.z, max.z),
+      _impl::clamp(val.w, min.w, max.w),
+    };
+  }
+  inline float4 lerp(const float4 &val1, const float4 &val2, float amount) {
+    return {
+      _impl::lerp(val1.x, val2.x, amount),
+      _impl::lerp(val1.y, val2.y, amount),
+      _impl::lerp(val1.z, val2.z, amount),
+      _impl::lerp(val1.w, val2.w, amount),
+    };
+  }
+  // TODO: impl
+  inline float4 transform(const float4 &pos, const float4x4 &mat);
+  inline float4 transform4(const float3 &pos, const float4x4 &mat);
+  inline float4 transform4(const float2 &pos, const float4x4 &mat);
+  inline float4 transform(const float4 &val, const quaternion &rot);
+  inline float4 transform4(const float3 &val, const quaternion &rot);
+  inline float4 transform4(const float2 &val, const quaternion &rot);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define float3x2 functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  // TODO: impl
+  inline float3x2 make_float3x2_translation(const float2 &pos);
+  inline float3x2 make_float3x2_translation(float xpos, float ypos);
+  inline float3x2 make_float3x2_scale(float xscale, float yscale);
+  inline float3x2 make_float3x2_scale(float xscale, float yscale, const float2 
&center);
+  inline float3x2 make_float3x2_scale(const float2 &xyscale);
+  inline float3x2 make_float3x2_scale(const float2 &xyscale, const float2 
&center);
+  inline float3x2 make_float3x2_scale(float scale);
+  inline float3x2 make_float3x2_scale(float scale, const float2 &center);
+  inline float3x2 make_float3x2_skew(float xrad, float yrad);
+  inline float3x2 make_float3x2_skew(float xrad, float yrad, const float2 
&center);
+  inline float3x2 make_float3x2_rotation(float rad);
+  inline float3x2 make_float3x2_rotation(float rad, const float2 &center);
+  inline bool is_identity(const float3x2 &val) {
+    return val == float3x2::identity();
+  }
+  inline float determinant(const float3x2 &val) {
+    // 2D transformation matrix has an implied 3rd column with (0, 0, 1)
+    // det = m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32
+    //     - m31 * m22 * m13 - m21 * m12 * m33 - m11 * m32 * m23;
+    return val.m11 * val.m22 - val.m21 * val.m12;
+  }
+  inline float2 translation(const float3x2 &val) {
+    return { val.m31, val.m32 };
+  }
+  inline bool invert(const float3x2 &val, float3x2 *out);
+  inline float3x2 lerp(const float3x2 &mat1, const float3x2 &mat2, float 
amount);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define float4x4 functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  // TODO: impl
+  inline float4x4 make_float4x4_billboard(const float3 &objpos, const float3 
&camerapos, const float3 &cameraup, const float3 &camerafwd);
+  inline float4x4 make_float4x4_constrained_billboard(const float3 &objpos, 
const float3 &camerapos, const float3 &rotateaxis, const float3 &camerafwd, 
const float3 &objfwd);
+  inline float4x4 make_float4x4_translation(const float3 &pos);
+  inline float4x4 make_float4x4_translation(float xpos, float ypos, float 
zpos);
+  inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale);
+  inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale, const float3 &center);
+  inline float4x4 make_float4x4_scale(const float3 &xyzscale);
+  inline float4x4 make_float4x4_scale(const float3 &xyzscale, const float3 
&center);
+  inline float4x4 make_float4x4_scale(float scale);
+  inline float4x4 make_float4x4_scale(float scale, const float3 &center);
+  inline float4x4 make_float4x4_rotation_x(float rad);
+  inline float4x4 make_float4x4_rotation_x(float rad, const float3 &center);
+  inline float4x4 make_float4x4_rotation_y(float rad);
+  inline float4x4 make_float4x4_rotation_y(float rad, const float3 &center);
+  inline float4x4 make_float4x4_rotation_z(float rad);
+  inline float4x4 make_float4x4_rotation_z(float rad, const float3 &center);
+  inline float4x4 make_float4x4_from_axis_angle(const float3 &axis, float 
angle);
+  inline float4x4 make_float4x4_perspective_field_of_view(float fov, float 
aspect, float nearplane, float farplane);
+  inline float4x4 make_float4x4_perspective(float w, float h, float nearplane, 
float farplane);
+  inline float4x4 make_float4x4_perspective_off_center(float left, float 
right, float bottom, float top, float nearplane, float farplane);
+  inline float4x4 make_float4x4_orthographic(float w, float h, float 
znearplane, float zfarplane);
+  inline float4x4 make_float4x4_orthographic_off_center(float left, float 
right, float bottom, float top, float znearplane, float zfarplane);
+  inline float4x4 make_float4x4_look_at(const float3 &camerapos, const float3 
&target, const float3 &cameraup);
+  inline float4x4 make_float4x4_world(const float3 &pos, const float3 &fwd, 
const float3 &up);
+  inline float4x4 make_float4x4_from_quaternion(const quaternion &quat);
+  inline float4x4 make_float4x4_from_yaw_pitch_roll(float yaw, float pitch, 
float roll);
+  inline float4x4 make_float4x4_shadow(const float3 &lightdir, const plane 
&plane);
+  inline float4x4 make_float4x4_reflection(const plane &plane);
+  inline bool is_identity(const float4x4 &val) {
+    return val == float4x4::identity();
+  }
+  inline float determinant(const float4x4 &val) {
+    const float det_33_44 = (val.m33 * val.m44 - val.m34 * val.m43);
+    const float det_32_44 = (val.m32 * val.m44 - val.m34 * val.m42);
+    const float det_32_43 = (val.m32 * val.m43 - val.m33 * val.m42);
+    const float det_31_44 = (val.m31 * val.m44 - val.m34 * val.m41);
+    const float det_31_43 = (val.m31 * val.m43 - val.m33 * val.m41);
+    const float det_31_42 = (val.m31 * val.m42 - val.m32 * val.m41);
+    return val.m11 * (val.m22 * det_33_44 - val.m23 * det_32_44 + val.m24 * 
det_32_43)
+      - val.m12 * (val.m21 * det_33_44 - val.m23 * det_31_44 + val.m24 * 
det_31_43)
+      + val.m13 * (val.m21 * det_32_44 - val.m22 * det_31_44 + val.m24 * 
det_31_42)
+      - val.m14 * (val.m21 * det_32_43 - val.m22 * det_31_43 + val.m23 * 
det_31_42);
+  }
+  inline float3 translation(const float4x4 &val) {
+    return { val.m41, val.m42, val.m43 };
+  }
+  inline bool invert(const float4x4 &mat, float4x4 *out);
+  inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion 
*out_rot, float3 *out_translate);
+  inline float4x4 transform(const float4x4 &val, const quaternion &rot);
+  inline float4x4 transpose(const float4x4 &val) {
+    return  {
+      val.m11, val.m21, val.m31, val.m41,
+      val.m12, val.m22, val.m32, val.m42,
+      val.m13, val.m23, val.m33, val.m43,
+      val.m14, val.m24, val.m34, val.m44,
+    };
+  }
+  inline float4x4 lerp(const float4x4 &val1, const float4x4 &val2, float 
amount);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define plane functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  // TODO: impl
+  inline plane make_plane_from_vertices(const float3 &pt1, const float3 &pt2, 
const float3 &pt3);
+  inline plane normalize(const plane &val) {
+    const float invlen = 1.0f / length(val.normal);
+    return { val.normal * invlen, val.d * invlen };
+  }
+  inline plane transform(const plane &plane, const float4x4 &mat);
+  inline plane transform(const plane &plane, const quaternion &rot);
+  inline float dot(const plane &plane, const float4 &val);
+  inline float dot_coordinate(const plane &plane, const float3 &val);
+  inline float dot_normal(const plane &plane, const float3 &val);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+// Define quaternion functions
+_WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
+
+  // TODO: impl
+  inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float 
angle);
+  inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float 
pitch, float roll);
+  inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat);
+  inline bool is_identity(const quaternion &val) {
+    return val == quaternion::identity();
+  }
+  inline float length(const quaternion &val) {
+    return ::std::sqrt(length_squared(val));
+  }
+  inline float length_squared(const quaternion &val) {
+    return val.x * val.x + val.y * val.y + val.z * val.z + val.w * val.w;
+  }
+  inline float dot(const quaternion &val1, const quaternion &val2);
+  inline quaternion normalize(const quaternion &val) {
+    return operator*(val, 1.0f / length(val));
+  }
+  inline quaternion conjugate(const quaternion &val) {
+    return { -val.x, -val.y, -val.z, val.w};
+  }
+  inline quaternion inverse(const quaternion &val) {
+    return operator*(conjugate(val), 1.0f / length_squared(val));
+  }
+  inline quaternion slerp(const quaternion &val1, const quaternion &val2, 
float amount);
+  inline quaternion lerp(const quaternion &val1, const quaternion &val2, float 
amount);
+  inline quaternion concatenate(const quaternion &val1, const quaternion 
&val2);
+
+} _WINDOWS_NUMERICS_END_NAMESPACE_
+
+
+/**
+ * FIXME: Implement interop functions with DirectXMath.
+ * This is where we are supposed to define the functions to convert between
+ * Windows::Foundation::Numerics types and XMVECTOR / XMMATRIX. But our
+ * directxmath.h does not contain the definitions for these types...
+ */
+#if 0
+// === DirectXMath Interop ===
+namespace DirectX {
+
+  // TODO: impl
+  XMVECTOR XMLoadFloat2(const _WINDOWS_NUMERICS_NAMESPACE_::float2 *src);
+  XMVECTOR XMLoadFloat3(const _WINDOWS_NUMERICS_NAMESPACE_::float3 *src);
+  XMVECTOR XMLoadFloat4(const _WINDOWS_NUMERICS_NAMESPACE_::float4 *src);
+  XMMATRIX XMLoadFloat3x2(const _WINDOWS_NUMERICS_NAMESPACE_::float3x2 *src);
+  XMMATRIX XMLoadFloat4x4(const _WINDOWS_NUMERICS_NAMESPACE_::float4x4 *src);
+  XMVECTOR XMLoadPlane(const _WINDOWS_NUMERICS_NAMESPACE_::plane *src);
+  XMVECTOR XMLoadQuaternion(const _WINDOWS_NUMERICS_NAMESPACE_::quaternion 
*src);
+  void XMStoreFloat2(_WINDOWS_NUMERICS_NAMESPACE_::float2 *out, FXMVECTOR in);
+  void XMStoreFloat3(_WINDOWS_NUMERICS_NAMESPACE_::float3 *out, FXMVECTOR in);
+  void XMStoreFloat4(_WINDOWS_NUMERICS_NAMESPACE_::float4 *out, FXMVECTOR in);
+  void XMStoreFloat3x2(_WINDOWS_NUMERICS_NAMESPACE_::float3x2 *out, FXMMATRIX 
in);
+  void XMStoreFloat4x4(_WINDOWS_NUMERICS_NAMESPACE_::float4x4 *out, FXMMATRIX 
in);
+  void XMStorePlane(_WINDOWS_NUMERICS_NAMESPACE_::plane *out, FXMVECTOR in);
+  void XMStoreQuaternion(_WINDOWS_NUMERICS_NAMESPACE_::quaternion *out, 
FXMVECTOR in);
+
+} /* namespace DirectX */
+#endif
+
+
+#undef _WINDOWS_NUMERICS_IMPL_ASSIGN_OP
+
+#ifdef _WINDOWS_NUMERICS_IMPL_PUSHED_MIN_
+#  undef _WINDOWS_NUMERICS_IMPL_PUSHED_MIN_
+#  pragma pop_macro("min")
+#endif
+
+#ifdef _WINDOWS_NUMERICS_IMPL_PUSHED_MAX_
+#  undef _WINDOWS_NUMERICS_IMPL_PUSHED_MAX_
+#  pragma pop_macro("max")
+#endif
diff --git a/mingw-w64-headers/testcases/test_windowsnumerics.cpp 
b/mingw-w64-headers/testcases/test_windowsnumerics.cpp
new file mode 100644
index 000000000..e4c9ed5b0
--- /dev/null
+++ b/mingw-w64-headers/testcases/test_windowsnumerics.cpp
@@ -0,0 +1,955 @@
+/**
+ * Copyright (c) 2022 Alvin Wong
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <windowsnumerics.h>
+#include <sstream>
+
+using namespace Windows::Foundation::Numerics;
+
+std::ostream &operator<<(std::ostream &os, float2 const &v) {
+  os << "{" << v.x << ", " << v.y << "}";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, float3 const &v) {
+  os << "{" << v.x << ", " << v.y << ", " << v.z << "}";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, float4 const &v) {
+  os << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, float3x2 const &v) {
+  os << "{ {" << v.m11 << ", " << v.m12
+    << "}, {" << v.m21 << ", " << v.m22
+    << "}, {" << v.m31 << ", " << v.m32
+    << "} }";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, float4x4 const &v) {
+  os << "{ {" << v.m11 << ", " << v.m12 << ", " << v.m13 << ", " << v.m14
+    << "}, {" << v.m21 << ", " << v.m22 << ", " << v.m23 << ", " << v.m24
+    << "}, {" << v.m31 << ", " << v.m32 << ", " << v.m33 << ", " << v.m34
+    << "}, {" << v.m41 << ", " << v.m42 << ", " << v.m43 << ", " << v.m44
+    << "} }";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, plane const &v) {
+  os << "{ {" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z
+    << "}, " << v.d << "}";
+  return os;
+}
+
+std::ostream &operator<<(std::ostream &os, quaternion const &v) {
+  os << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}";
+  return os;
+}
+
+#define CATCH_CONFIG_RUNNER
+// https://github.com/catchorg/Catch2/releases/tag/v2.13.10
+#include "catch.hpp"
+
+class eq2
+  : public Catch::MatcherBase<float2>
+{
+  float m_x{NAN};
+  float m_y{NAN};
+public:
+  explicit eq2(float2 v)
+    : m_x(v.x), m_y(v.y)
+  {}
+  eq2(float x, float y)
+    : m_x(x), m_y(y)
+  {}
+  bool match(float2 const& v) const override {
+    return v.x == Approx(m_x) && v.y == Approx(m_y);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << float2(m_x, m_y);
+    return ss.str();
+  }
+};
+
+class eq3
+  : public Catch::MatcherBase<float3>
+{
+  float m_x{NAN};
+  float m_y{NAN};
+  float m_z{NAN};
+public:
+  explicit eq3(float3 v)
+    : m_x(v.x), m_y(v.y), m_z(v.z)
+  {}
+  eq3(float x, float y, float z)
+    : m_x(x), m_y(y), m_z(z)
+  {}
+  bool match(float3 const& v) const override {
+    return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << float3(m_x, m_y, m_z);
+    return ss.str();
+  }
+};
+
+class eq4
+  : public Catch::MatcherBase<float4>
+{
+  float m_x{NAN};
+  float m_y{NAN};
+  float m_z{NAN};
+  float m_w{NAN};
+public:
+  explicit eq4(float4 v)
+    : m_x(v.x), m_y(v.y), m_z(v.z), m_w(v.w)
+  {}
+  eq4(float x, float y, float z, float w)
+    : m_x(x), m_y(y), m_z(z), m_w(w)
+  {}
+  bool match(float4 const& v) const override {
+    return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z) && 
v.w == Approx(m_w);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << float4(m_x, m_y, m_z, m_w);
+    return ss.str();
+  }
+};
+
+class eq3x2
+  : public Catch::MatcherBase<float3x2>
+{
+  float m_11{NAN};
+  float m_12{NAN};
+  float m_21{NAN};
+  float m_22{NAN};
+  float m_31{NAN};
+  float m_32{NAN};
+public:
+  explicit eq3x2(float3x2 v)
+    : m_11(v.m11), m_12(v.m12)
+    , m_21(v.m21), m_22(v.m22)
+    , m_31(v.m31), m_32(v.m32)
+  {}
+  eq3x2(
+    float m11, float m12,
+    float m21, float m22,
+    float m31, float m32
+  )
+    : m_11(m11), m_12(m12)
+    , m_21(m21), m_22(m22)
+    , m_31(m31), m_32(m32)
+  {}
+  bool match(float3x2 const& v) const override {
+    return
+      v.m11 == Approx(m_11) && v.m12 == Approx(m_12) &&
+      v.m21 == Approx(m_21) && v.m22 == Approx(m_22) &&
+      v.m31 == Approx(m_31) && v.m32 == Approx(m_32);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << float3x2(
+      m_11, m_12,
+      m_21, m_22,
+      m_31, m_32
+    );
+    return ss.str();
+  }
+};
+
+class eq4x4
+  : public Catch::MatcherBase<float4x4>
+{
+  float m_11{NAN};
+  float m_12{NAN};
+  float m_13{NAN};
+  float m_14{NAN};
+  float m_21{NAN};
+  float m_22{NAN};
+  float m_23{NAN};
+  float m_24{NAN};
+  float m_31{NAN};
+  float m_32{NAN};
+  float m_33{NAN};
+  float m_34{NAN};
+  float m_41{NAN};
+  float m_42{NAN};
+  float m_43{NAN};
+  float m_44{NAN};
+public:
+  explicit eq4x4(float4x4 v)
+    : m_11(v.m11), m_12(v.m12), m_13(v.m13), m_14(v.m14)
+    , m_21(v.m21), m_22(v.m22), m_23(v.m23), m_24(v.m24)
+    , m_31(v.m31), m_32(v.m32), m_33(v.m33), m_34(v.m34)
+    , m_41(v.m41), m_42(v.m42), m_43(v.m43), m_44(v.m44)
+  {}
+  eq4x4(
+    float m11, float m12, float m13, float m14,
+    float m21, float m22, float m23, float m24,
+    float m31, float m32, float m33, float m34,
+    float m41, float m42, float m43, float m44
+  )
+    : m_11(m11), m_12(m12), m_13(m13), m_14(m14)
+    , m_21(m21), m_22(m22), m_23(m23), m_24(m24)
+    , m_31(m31), m_32(m32), m_33(m33), m_34(m34)
+    , m_41(m41), m_42(m42), m_43(m43), m_44(m44)
+  {}
+  bool match(float4x4 const& v) const override {
+    return
+      v.m11 == Approx(m_11) && v.m12 == Approx(m_12) && v.m13 == Approx(m_13) 
&& v.m14 == Approx(m_14) &&
+      v.m21 == Approx(m_21) && v.m22 == Approx(m_22) && v.m23 == Approx(m_23) 
&& v.m24 == Approx(m_24) &&
+      v.m31 == Approx(m_31) && v.m32 == Approx(m_32) && v.m33 == Approx(m_33) 
&& v.m34 == Approx(m_34) &&
+      v.m41 == Approx(m_41) && v.m42 == Approx(m_42) && v.m43 == Approx(m_43) 
&& v.m44 == Approx(m_44);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << float4x4(
+      m_11, m_12, m_13, m_14,
+      m_21, m_22, m_23, m_24,
+      m_31, m_32, m_33, m_34,
+      m_41, m_42, m_43, m_44
+    );
+    return ss.str();
+  }
+};
+
+class eqp
+  : public Catch::MatcherBase<plane>
+{
+  float m_x{NAN};
+  float m_y{NAN};
+  float m_z{NAN};
+  float m_d{NAN};
+public:
+  explicit eqp(plane v)
+    : m_x(v.normal.x), m_y(v.normal.y), m_z(v.normal.z), m_d(v.d)
+  {}
+  eqp(float x, float y, float z, float d)
+    : m_x(x), m_y(y), m_z(z), m_d(d)
+  {}
+  bool match(plane const& v) const override {
+    return v.normal.x == Approx(m_x) && v.normal.y == Approx(m_y) &&
+      v.normal.z == Approx(m_z) && v.d == Approx(m_d);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << plane(m_x, m_y, m_z, m_d);
+    return ss.str();
+  }
+};
+
+class eqq
+  : public Catch::MatcherBase<quaternion>
+{
+  float m_x{NAN};
+  float m_y{NAN};
+  float m_z{NAN};
+  float m_w{NAN};
+public:
+  explicit eqq(quaternion v)
+    : m_x(v.x), m_y(v.y), m_z(v.z), m_w(v.w)
+  {}
+  eqq(float x, float y, float z, float w)
+    : m_x(x), m_y(y), m_z(z), m_w(w)
+  {}
+  bool match(quaternion const& v) const override {
+    return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z) && 
v.w == Approx(m_w);
+  }
+  std::string describe() const override {
+    std::ostringstream ss;
+    ss << "is equal to ";
+    ss << quaternion(m_x, m_y, m_z, m_w);
+    return ss.str();
+  }
+};
+
+using namespace Catch::literals;
+
+// === float2 ===
+
+TEST_CASE("float2-ctors") {
+  CHECK_THAT(float2(2.0f, 3.0f), eq2(2.0f, 3.0f));
+  CHECK_THAT(float2(2.0f), eq2(2.0f, 2.0f));
+}
+
+TEST_CASE("float2-methods") {
+  CHECK_THAT(float2::zero(), eq2(0.0f, 0.0f));
+  CHECK(float2::zero().x == 0.0f);
+  CHECK(float2::zero().y == 0.0f);
+  CHECK_THAT(float2::one(), eq2(1.0f, 1.0f));
+  CHECK(float2::one().x == 1.0f);
+  CHECK(float2::one().y == 1.0f);
+  CHECK_THAT(float2::unit_x(), eq2(1.0f, 0.0f));
+  CHECK(float2::unit_x().x == 1.0f);
+  CHECK(float2::unit_x().y == 0.0f);
+  CHECK_THAT(float2::unit_y(), eq2(0.0f, 1.0f));
+  CHECK(float2::unit_y().x == 0.0f);
+  CHECK(float2::unit_y().y == 1.0f);
+}
+
+TEST_CASE("float2-operators") {
+  const float2 v1{ 3.0f, 4.0f };
+  const float2 v2{ 8.0f, 16.0f };
+  CHECK_THAT(v1 + v2, eq2(11.0f, 20.0f));
+  CHECK_THAT(v1 - v2, eq2(-5.0f, -12.0f));
+  CHECK_THAT(v1 * v2, eq2(24.0f, 64.0f));
+  CHECK_THAT(v1 * 2.0f, eq2(6.0f, 8.0f));
+  CHECK_THAT(3.0f * v1, eq2(9.0f, 12.0f));
+  CHECK_THAT(v1 / v2, eq2(0.375f, 0.25f));
+  CHECK_THAT(v1 / 10.0f, eq2(0.3f, 0.4f));
+  CHECK_THAT(-v1, eq2(-3.0f, -4.0f));
+  float2 v3;
+  v3 = {1.0f, 2.0f};
+  CHECK_THAT(v3 += v1, eq2(4.0f, 6.0f));
+  v3 = {1.0f, 2.0f};
+  CHECK_THAT(v3 -= v1, eq2(-2.0f, -2.0f));
+  v3 = {1.5f, 2.0f};
+  CHECK_THAT(v3 *= v1, eq2(4.5f, 8.0f));
+  v3 = {1.0f, 2.0f};
+  CHECK_THAT(v3 *= 3.0f, eq2(3.0f, 6.0f));
+  v3 = {1.0f, 2.0f};
+  CHECK_THAT(v3 /= v1, eq2(0.333333f, 0.5f));
+  v3 = {1.0f, 2.0f};
+  CHECK_THAT(v3 /= 5.0f, eq2(0.2f, 0.4f));
+  v3 = {3.0f, 4.0f};
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("float2-functions") {
+  const float2 v1{ 3.0f, 4.0f };
+  const float2 v2{ 8.0f, 16.0f };
+  const float2 v3{ 10.0f, 10.0f };
+
+  CHECK(length(v1) == 5.0_a);
+  CHECK(length(v2) == 17.88854_a);
+  CHECK(length_squared(v1) == 25.0_a);
+  CHECK(length_squared(v2) == 320.0_a);
+  CHECK(distance(v1, v2) == 13.0_a);
+  CHECK(distance_squared(v1, v2) == 169.0_a);
+  CHECK(dot(v1, v2) == 88.0_a);
+  CHECK_THAT(normalize(v1), eq2(0.6f, 0.8f));
+  CHECK_THAT(normalize(v2), eq2(0.4472136f, 0.8944272f));
+  CHECK_THAT(reflect(v1, float2::unit_x()), eq2(-3.0f, 4.0f));
+  CHECK_THAT(reflect(v1, float2::unit_y()), eq2(3.0f, -4.0f));
+  CHECK_THAT(reflect(v1, normalize(v2)), eq2(-1.4f, -4.8f));
+  CHECK_THAT(reflect(v1, v2), eq2(-1405, -2812)); // Appears to be garbage
+  CHECK_THAT(min(v2, v3), eq2(8.0f, 10.0f));
+  CHECK_THAT(max(v2, v3), eq2(10.0f, 16.0f));
+  CHECK_THAT(clamp(v1, v2, v3), eq2(8.0f, 16.0f));
+  CHECK_THAT(clamp(v2, v1, v3), eq2(8.0f, 10.0f));
+  CHECK_THAT(lerp(v1, v2, 0.0f), eq2(3.0f, 4.0f));
+  CHECK_THAT(lerp(v1, v2, 0.5f), eq2(5.5f, 10.0f));
+  CHECK_THAT(lerp(v1, v2, 1.0f), eq2(8.0f, 16.0f));
+  CHECK_THAT(lerp(v1, v2, -0.5f), eq2(0.5f, -2.0f));
+  CHECK_THAT(lerp(v1, v2, 1.5f), eq2(10.5f, 22.0f));
+  // TODO: transform
+}
+
+// === float3 ===
+
+TEST_CASE("float3-ctors") {
+  CHECK_THAT(float3(2.0f, 3.0f, 4.0f), eq3(2.0f, 3.0f, 4.0f));
+  CHECK_THAT(float3(float2(2.0f, 3.0f), 4.0f), eq3(2.0f, 3.0f, 4.0f));
+  CHECK_THAT(float3(2.0f), eq3(2.0f, 2.0f, 2.0f));
+}
+
+TEST_CASE("float3-methods") {
+  CHECK_THAT(float3::zero(), eq3(0.0f, 0.0f, 0.0f));
+  CHECK(float3::zero().x == 0.0f);
+  CHECK(float3::zero().y == 0.0f);
+  CHECK(float3::zero().z == 0.0f);
+  CHECK_THAT(float3::one(), eq3(1.0f, 1.0f, 1.0f));
+  CHECK(float3::one().x == 1.0f);
+  CHECK(float3::one().y == 1.0f);
+  CHECK(float3::one().z == 1.0f);
+  CHECK_THAT(float3::unit_x(), eq3(1.0f, 0.0f, 0.0f));
+  CHECK(float3::unit_x().x == 1.0f);
+  CHECK(float3::unit_x().y == 0.0f);
+  CHECK(float3::unit_x().z == 0.0f);
+  CHECK_THAT(float3::unit_y(), eq3(0.0f, 1.0f, 0.0f));
+  CHECK(float3::unit_y().x == 0.0f);
+  CHECK(float3::unit_y().y == 1.0f);
+  CHECK(float3::unit_y().z == 0.0f);
+  CHECK_THAT(float3::unit_z(), eq3(0.0f, 0.0f, 1.0f));
+  CHECK(float3::unit_z().x == 0.0f);
+  CHECK(float3::unit_z().y == 0.0f);
+  CHECK(float3::unit_z().z == 1.0f);
+}
+
+TEST_CASE("float3-operators") {
+  const float3 v1{ 3.0f, 4.0f, 5.0f };
+  const float3 v2{ 8.0f, 16.0f, 24.0f };
+  CHECK_THAT(v1 + v2, eq3(11.0f, 20.0f, 29.0f));
+  CHECK_THAT(v1 - v2, eq3(-5.0f, -12.0f, -19.0f));
+  CHECK_THAT(v1 * v2, eq3(24.0f, 64.0f, 120.0f));
+  CHECK_THAT(v1 * 2.0f, eq3(6.0f, 8.0f, 10.0f));
+  CHECK_THAT(3.0f * v1, eq3(9.0f, 12.0f, 15.0f));
+  CHECK_THAT(v1 / v2, eq3(0.375f, 0.25f, 0.208333f));
+  CHECK_THAT(v1 / 10.0f, eq3(0.3f, 0.4f, 0.5f));
+  CHECK_THAT(-v1, eq3(-3.0f, -4.0f, -5.0f));
+  float3 v3;
+  v3 = {1.0f, 2.0f, 3.0f};
+  CHECK_THAT(v3 += v1, eq3(4.0f, 6.0f, 8.0f));
+  v3 = {1.0f, 2.0f, 3.0f};
+  CHECK_THAT(v3 -= v1, eq3(-2.0f, -2.0f, -2.0f));
+  v3 = {1.5f, 2.0f, 2.5f};
+  CHECK_THAT(v3 *= v1, eq3(4.5f, 8.0f, 12.5f));
+  v3 = {1.0f, 2.0f, 3.0f};
+  CHECK_THAT(v3 *= 3.0f, eq3(3.0f, 6.0f, 9.0f));
+  v3 = {1.0f, 2.0f, 3.0f};
+  CHECK_THAT(v3 /= v1, eq3(0.333333f, 0.5f, 0.6f));
+  v3 = {1.0f, 2.0f, 3.0f};
+  CHECK_THAT(v3 /= 5.0f, eq3(0.2f, 0.4f, 0.6f));
+  v3 = {3.0f, 4.0f, 5.0f};
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("float3-functions") {
+  const float3 v1{ 3.0f, 4.0f, 5.0f };
+  const float3 v2{ 8.0f, 16.0f, 24.0f };
+  const float3 v3{ 10.0f, 10.0f, 10.0f };
+
+  CHECK(length(v1) == 7.07107_a);
+  CHECK(length(v2) == 29.93326_a);
+  CHECK(length_squared(v1) == 50.0_a);
+  CHECK(length_squared(v2) == 896.0_a);
+  CHECK(distance(v1, v2) == 23.02173_a);
+  CHECK(distance_squared(v1, v2) == 530.0_a);
+  CHECK(dot(v1, v2) == 208.0_a);
+  CHECK_THAT(normalize(v1), eq3(0.424264f, 0.565685f, 0.707107f));
+  CHECK_THAT(normalize(v2), eq3(0.267261f, 0.534522f, 0.801784f));
+  CHECK_THAT(cross(v1, v2), eq3(16.0f, -32.0f, 16.0f));
+  CHECK_THAT(cross(v2, v1), eq3(-16.0f, 32.0f, -16.0f));
+  CHECK_THAT(cross(v1, v3), eq3(-10.0f, 20.0f, -10.0f));
+  CHECK_THAT(cross(v2, v3), eq3(-80.0f, 160.0f, -80.0f));
+  CHECK_THAT(reflect(v1, float3::unit_x()), eq3(-3.0f, 4.0f, 5.0f));
+  CHECK_THAT(reflect(v1, float3::unit_y()), eq3(3.0f, -4.0f, 5.0f));
+  CHECK_THAT(reflect(v1, float3::unit_z()), eq3(3.0f, 4.0f, -5.0f));
+  CHECK_THAT(reflect(v1, normalize(v2)), eq3(-0.714285f, -3.42857f, 
-6.14286f));
+  CHECK_THAT(reflect(v1, v2), eq3(-3325, -6652, -9979)); // Appears to be 
garbage
+  CHECK_THAT(min(v2, v3), eq3(8.0f, 10.0f, 10.0f));
+  CHECK_THAT(max(v2, v3), eq3(10.0f, 16.0f, 24.0f));
+  CHECK_THAT(clamp(v1, v2, v3), eq3(8.0f, 16.0f, 24.0f));
+  CHECK_THAT(clamp(v2, v1, v3), eq3(8.0f, 10.0f, 10.0f));
+  CHECK_THAT(lerp(v1, v2, 0.0f), eq3(3.0f, 4.0f, 5.0f));
+  CHECK_THAT(lerp(v1, v2, 0.5f), eq3(5.5f, 10.0f, 14.5f));
+  CHECK_THAT(lerp(v1, v2, 1.0f), eq3(8.0f, 16.0f, 24.0f));
+  CHECK_THAT(lerp(v1, v2, -0.5f), eq3(0.5f, -2.0f, -4.5f));
+  CHECK_THAT(lerp(v1, v2, 1.5f), eq3(10.5f, 22.0f, 33.5f));
+  // TODO: transform
+}
+
+// === float4 ===
+
+TEST_CASE("float4-ctors") {
+  CHECK_THAT(float4(2.0f, 3.0f, 4.0f, 5.0f), eq4(2.0f, 3.0f, 4.0f, 5.0f));
+  CHECK_THAT(float4(float2(2.0f, 3.0f), 4.0f, 5.0f), eq4(2.0f, 3.0f, 4.0f, 
5.0f));
+  CHECK_THAT(float4(float3(2.0f, 3.0f, 4.0f), 5.0f), eq4(2.0f, 3.0f, 4.0f, 
5.0f));
+  CHECK_THAT(float4(2.0f), eq4(2.0f, 2.0f, 2.0f, 2.0f));
+}
+
+TEST_CASE("float4-methods") {
+  CHECK_THAT(float4::zero(), eq4(0.0f, 0.0f, 0.0f, 0.0f));
+  CHECK(float4::zero().x == 0.0f);
+  CHECK(float4::zero().y == 0.0f);
+  CHECK(float4::zero().z == 0.0f);
+  CHECK(float4::zero().w == 0.0f);
+  CHECK_THAT(float4::one(), eq4(1.0f, 1.0f, 1.0f, 1.0f));
+  CHECK(float4::one().x == 1.0f);
+  CHECK(float4::one().y == 1.0f);
+  CHECK(float4::one().z == 1.0f);
+  CHECK(float4::one().w == 1.0f);
+  CHECK_THAT(float4::unit_x(), eq4(1.0f, 0.0f, 0.0f, 0.0f));
+  CHECK(float4::unit_x().x == 1.0f);
+  CHECK(float4::unit_x().y == 0.0f);
+  CHECK(float4::unit_x().z == 0.0f);
+  CHECK(float4::unit_x().w == 0.0f);
+  CHECK_THAT(float4::unit_y(), eq4(0.0f, 1.0f, 0.0f, 0.0f));
+  CHECK(float4::unit_y().x == 0.0f);
+  CHECK(float4::unit_y().y == 1.0f);
+  CHECK(float4::unit_y().z == 0.0f);
+  CHECK(float4::unit_y().w == 0.0f);
+  CHECK_THAT(float4::unit_z(), eq4(0.0f, 0.0f, 1.0f, 0.0f));
+  CHECK(float4::unit_z().x == 0.0f);
+  CHECK(float4::unit_z().y == 0.0f);
+  CHECK(float4::unit_z().z == 1.0f);
+  CHECK(float4::unit_z().w == 0.0f);
+  CHECK_THAT(float4::unit_w(), eq4(0.0f, 0.0f, 0.0f, 1.0f));
+  CHECK(float4::unit_w().x == 0.0f);
+  CHECK(float4::unit_w().y == 0.0f);
+  CHECK(float4::unit_w().z == 0.0f);
+  CHECK(float4::unit_w().w == 1.0f);
+}
+
+TEST_CASE("float4-operators") {
+  const float4 v1{ 3.0f, 4.0f, 5.0f, 6.0f };
+  const float4 v2{ 8.0f, 16.0f, 24.0f, 13.0f };
+  CHECK_THAT(v1 + v2, eq4(11.0f, 20.0f, 29.0f, 19.0f));
+  CHECK_THAT(v1 - v2, eq4(-5.0f, -12.0f, -19.0f, -7.0f));
+  CHECK_THAT(v1 * v2, eq4(24.0f, 64.0f, 120.0f, 78.0f));
+  CHECK_THAT(v1 * 2.0f, eq4(6.0f, 8.0f, 10.0f, 12.0f));
+  CHECK_THAT(3.0f * v1, eq4(9.0f, 12.0f, 15.0f, 18.0f));
+  CHECK_THAT(v1 / v2, eq4(0.375f, 0.25f, 0.208333f, 0.461538f));
+  CHECK_THAT(v1 / 10.0f, eq4(0.3f, 0.4f, 0.5f, 0.6f));
+  CHECK_THAT(-v1, eq4(-3.0f, -4.0f, -5.0f, -6.0f));
+  float4 v3;
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK_THAT(v3 += v1, eq4(4.0f, 6.0f, 8.0f, 10.0f));
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK_THAT(v3 -= v1, eq4(-2.0f, -2.0f, -2.0f, -2.0f));
+  v3 = {1.5f, 2.0f, 2.5f, 3.0f};
+  CHECK_THAT(v3 *= v1, eq4(4.5f, 8.0f, 12.5f, 18.0f));
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK_THAT(v3 *= 3.0f, eq4(3.0f, 6.0f, 9.0f, 12.0f));
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK_THAT(v3 /= v1, eq4(0.333333f, 0.5f, 0.6f, 0.666667f));
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK_THAT(v3 /= 5.0f, eq4(0.2f, 0.4f, 0.6f, 0.8f));
+  v3 = {3.0f, 4.0f, 5.0f, 6.0f};
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("float4-functions") {
+  const float4 v1{ 3.0f, 4.0f, 5.0f, 6.0f };
+  const float4 v2{ 8.0f, 16.0f, 24.0f, 13.0f };
+  const float4 v3{ 10.0f, 10.0f, 10.0f, 10.0f };
+
+  CHECK(length(v1) == 9.27362_a);
+  CHECK(length(v2) == 32.63434_a);
+  CHECK(length_squared(v1) == 86.0_a);
+  CHECK(length_squared(v2) == 1065.0_a);
+  CHECK(distance(v1, v2) == 24.06242_a);
+  CHECK(distance_squared(v1, v2) == 579.0_a);
+  CHECK(dot(v1, v2) == 286.0_a);
+  CHECK_THAT(normalize(v1), eq4(0.323498f, 0.431331f, 0.539164f, 0.646997f));
+  CHECK_THAT(normalize(v2), eq4(0.245141f, 0.490281f, 0.735422f, 0.398353f));
+  CHECK_THAT(min(v2, v3), eq4(8.0f, 10.0f, 10.0f, 10.0f));
+  CHECK_THAT(max(v2, v3), eq4(10.0f, 16.0f, 24.0f, 13.0f));
+  CHECK_THAT(clamp(v1, v2, v3), eq4(8.0f, 16.0f, 24.0f, 13.0f));
+  CHECK_THAT(clamp(v2, v1, v3), eq4(8.0f, 10.0f, 10.0f, 10.0f));
+  CHECK_THAT(lerp(v1, v2, 0.0f), eq4(3.0f, 4.0f, 5.0f, 6.0f));
+  CHECK_THAT(lerp(v1, v2, 0.5f), eq4(5.5f, 10.0f, 14.5f, 9.5f));
+  CHECK_THAT(lerp(v1, v2, 1.0f), eq4(8.0f, 16.0f, 24.0f, 13.0f));
+  CHECK_THAT(lerp(v1, v2, -0.5f), eq4(0.5f, -2.0f, -4.5f, 2.5f));
+  CHECK_THAT(lerp(v1, v2, 1.5f), eq4(10.5f, 22.0f, 33.5f, 16.5f));
+  // TODO: transform
+}
+
+// === float3x2 ===
+
+TEST_CASE("float3x2-ctors") {
+  CHECK_THAT(float3x2(
+    1.0f, 2.0f,
+    3.0f, 4.0f,
+    5.0f, 6.0f
+  ), eq3x2(
+    1.0f, 2.0f,
+    3.0f, 4.0f,
+    5.0f, 6.0f
+  ));
+}
+
+TEST_CASE("float3x2-methods") {
+  CHECK_THAT(float3x2::identity(), eq3x2(
+    1.0f, 0.0f,
+    0.0f, 1.0f,
+    0.0f, 0.0f
+  ));
+}
+
+TEST_CASE("float3x2-operators") {
+  const float3x2 v1{
+    1.0f, 2.0f,
+    3.0f, 4.0f,
+    5.0f, 6.0f,
+  };
+  const float3x2 v2{
+    7.0f, 4.0f,
+    2.0f, 5.0f,
+    9.0f, 3.0f,
+  };
+  CHECK_THAT(v1 + v2, eq3x2(
+    8.0f, 6.0f,
+    5.0f, 9.0f,
+    14.0f, 9.0f
+  ));
+  CHECK_THAT(v1 - v2, eq3x2(
+    -6.0f, -2.0f,
+    1.0f, -1.0f,
+    -4.0f, 3.0f
+  ));
+  CHECK_THAT(v1 * v2, eq3x2(
+    // v1.m11 * v2.m11 + v1.m12 * v2.m21 + 0.0f * v2.m31, v1.m11 * v2.m12 + 
v1.m12 * v2.m22 + 0.0f * v2.m32,
+    // v1.m21 * v2.m11 + v1.m22 * v2.m21 + 0.0f * v2.m31, v1.m21 * v2.m12 + 
v1.m22 * v2.m22 + 0.0f * v2.m32,
+    // v1.m31 * v2.m11 + v1.m32 * v2.m21 + 1.0f * v2.m31, v1.m31 * v2.m12 + 
v1.m32 * v2.m22 + 1.0f * v2.m32
+    11.0f, 14.0f,
+    29.0f, 32.0f,
+    56.0f, 53.0f
+  ));
+  CHECK_THAT(v1 * 2.0f, eq3x2(
+    2.0f, 4.0f,
+    6.0f, 8.0f,
+    10.0f, 12.0f
+  ));
+  CHECK_THAT(-v1, eq3x2(
+    -1.0f, -2.0f,
+    -3.0f, -4.0f,
+    -5.0f, -6.0f
+  ));
+  float3x2 v3;
+  v3 = {
+    9.0f, 3.0f,
+    8.0f, 2.0f,
+    7.0f, 1.0f,
+  };
+  CHECK_THAT(v3 += v1, eq3x2(
+    10.0f, 5.0f,
+    11.0f, 6.0f,
+    12.0f, 7.0f
+  ));
+  v3 = {
+    9.0f, 3.0f,
+    8.0f, 2.0f,
+    7.0f, 1.0f,
+  };
+  CHECK_THAT(v3 -= v1, eq3x2(
+    8.0f, 1.0f,
+    5.0f, -2.0f,
+    2.0f, -5.0f
+  ));
+  v3 = {
+    9.0f, 3.0f,
+    8.0f, 2.0f,
+    7.0f, 1.0f,
+  };
+  CHECK_THAT(v3 *= v1, eq3x2(
+    18.0f, 30.0f,
+    14.0f, 24.0f,
+    15.0f, 24.0f
+  ));
+  v3 = {
+    9.0f, 3.0f,
+    8.0f, 2.0f,
+    7.0f, 1.0f,
+  };
+  CHECK_THAT(v3 *= 3.0f, eq3x2(
+    27.0f, 9.0f,
+    24.0f, 6.0f,
+    21.0f, 3.0f
+  ));
+  v3 = {
+    1.0f, 2.0f,
+    3.0f, 4.0f,
+    5.0f, 6.0f,
+  };
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("float3x2-functions") {
+  const float3x2 v1{
+    1.0f, 2.0f,
+    3.0f, 4.0f,
+    5.0f, 6.0f,
+  };
+  CHECK(is_identity(v1) == false);
+  CHECK(is_identity(float3x2::identity()) == true);
+  CHECK(is_identity(float3x2{
+    1.0f, 0.0f,
+    0.0f, 1.0f,
+    0.0f, 0.0f,
+  }) == true);
+  CHECK(determinant(v1) == -2.0_a);
+  CHECK_THAT(translation(v1), eq2(5.0f, 6.0f));
+  // TODO
+}
+
+// === float4x4 ===
+
+TEST_CASE("float4x4-ctors") {
+  CHECK_THAT(float4x4(
+    1.0f, 2.0f, 3.0f, 4.0f,
+    5.0f, 6.0f, 7.0f, 8.0f,
+    9.0f, 10.0f, 11.0f, 12.0f,
+    13.0f, 14.0f, 15.0f, 16.0f
+  ), eq4x4(
+    1.0f, 2.0f, 3.0f, 4.0f,
+    5.0f, 6.0f, 7.0f, 8.0f,
+    9.0f, 10.0f, 11.0f, 12.0f,
+    13.0f, 14.0f, 15.0f, 16.0f
+  ));
+}
+
+TEST_CASE("float4x4-methods") {
+  CHECK_THAT(float4x4::identity(), eq4x4(
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f
+  ));
+}
+
+TEST_CASE("float4x4-operators") {
+  const float4x4 v1{
+    1.0f, 2.0f, 3.0f, 4.0f,
+    5.0f, 6.0f, 7.0f, 8.0f,
+    9.0f, 10.0f, 11.0f, 12.0f,
+    13.0f, 14.0f, 15.0f, 16.0f,
+  };
+  const float4x4 v2{
+    3.0f, 7.0f, 4.0f, 13.0f,
+    5.0f, 2.0f, 9.0f, 21.0f,
+    17.0f, 14.0f, 28.0f, 11.0f,
+    8.0f, 1.0f, 18.0f, 15.0f,
+  };
+  CHECK_THAT(v1 + v2, eq4x4(
+    4.0f, 9.0f, 7.0f, 17.0f,
+    10.0f, 8.0f, 16.0f, 29.0f,
+    26.0f, 24.0f, 39.0f, 23.0f,
+    21.0f, 15.0f, 33.0f, 31.0f
+  ));
+  CHECK_THAT(v1 - v2, eq4x4(
+    -2.0f, -5.0f, -1.0f, -9.0f,
+    0.0f, 4.0f, -2.0f, -13.0f,
+    -8.0f, -4.0f, -17.0f, 1.0f,
+    5.0f, 13.0f, -3.0f, 1.0f
+  ));
+  CHECK_THAT(v1 * v2, eq4x4(
+    96.0f, 57.0f, 178.0f, 148.0f,
+    228.0f, 153.0f, 414.0f, 388.0f,
+    360.0f, 249.0f, 650.0f, 628.0f,
+    492.0f, 345.0f, 886.0f, 868.0f
+  ));
+  CHECK_THAT(v1 * 2.0f, eq4x4(
+    2.0f, 4.0f, 6.0f, 8.0f,
+    10.0f, 12.0f, 14.0f, 16.0f,
+    18.0f, 20.0f, 22.0f, 24.0f,
+    26.0f, 28.0f, 30.0f, 32.0f
+  ));
+  CHECK_THAT(-v1, eq4x4(
+    -1.0f, -2.0f, -3.0f, -4.0f,
+    -5.0f, -6.0f, -7.0f, -8.0f,
+    -9.0f, -10.0f, -11.0f, -12.0f,
+    -13.0f, -14.0f, -15.0f, -16.0f
+  ));
+  float4x4 v3;
+  v3 = {
+    5.0f, 2.0f, 9.0f, 21.0f,
+    3.0f, 7.0f, 4.0f, 13.0f,
+    8.0f, 1.0f, 18.0f, 15.0f,
+    17.0f, 14.0f, 28.0f, 11.0f,
+  };
+  CHECK_THAT(v3 += v1, eq4x4(
+    6.0f, 4.0f, 12.0f, 25.0f,
+    8.0f, 13.0f, 11.0f, 21.0f,
+    17.0f, 11.0f, 29.0f, 27.0f,
+    30.0f, 28.0f, 43.0f, 27.0f
+  ));
+  v3 = {
+    5.0f, 2.0f, 9.0f, 21.0f,
+    3.0f, 7.0f, 4.0f, 13.0f,
+    8.0f, 1.0f, 18.0f, 15.0f,
+    17.0f, 14.0f, 28.0f, 11.0f,
+  };
+  CHECK_THAT(v3 -= v1, eq4x4(
+    4.0f, 0.0f, 6.0f, 17.0f,
+    -2.0f, 1.0f, -3.0f, 5.0f,
+    -1.0f, -9.0f, 7.0f, 3.0f,
+    4.0f, 0.0f, 13.0f, -5.0f
+  ));
+  v3 = {
+    5.0f, 2.0f, 9.0f, 21.0f,
+    3.0f, 7.0f, 4.0f, 13.0f,
+    8.0f, 1.0f, 18.0f, 15.0f,
+    17.0f, 14.0f, 28.0f, 11.0f,
+  };
+  CHECK_THAT(v3 *= v1, eq4x4(
+    369.0f, 406.0f, 443.0f, 480.0f,
+    243.0f, 270.0f, 297.0f, 324.0f,
+    370.0f, 412.0f, 454.0f, 496.0f,
+    482.0f, 552.0f, 622.0f, 692.0f
+  ));
+  v3 = {
+    5.0f, 2.0f, 9.0f, 21.0f,
+    3.0f, 7.0f, 4.0f, 13.0f,
+    8.0f, 1.0f, 18.0f, 15.0f,
+    17.0f, 14.0f, 28.0f, 11.0f,
+  };
+  CHECK_THAT(v3 *= 3.0f, eq4x4(
+    15.0f, 6.0f, 27.0f, 63.0f,
+    9.0f, 21.0f, 12.0f, 39.0f,
+    24.0f, 3.0f, 54.0f, 45.0f,
+    51.0f, 42.0f, 84.0f, 33.0f
+  ));
+  v3 = {
+    1.0f, 2.0f, 3.0f, 4.0f,
+    5.0f, 6.0f, 7.0f, 8.0f,
+    9.0f, 10.0f, 11.0f, 12.0f,
+    13.0f, 14.0f, 15.0f, 16.0f,
+  };
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("float4x4-functions") {
+  const float4x4 v1{
+    1.0f, 2.0f, 3.0f, 4.0f,
+    5.0f, 6.0f, 7.0f, 8.0f,
+    9.0f, 10.0f, 11.0f, 12.0f,
+    13.0f, 14.0f, 15.0f, 16.0f,
+  };
+  const float4x4 v2{
+    7.0f, 5.0f, 2.4f, 0.0f,
+    4.0f, 1.3f, 5.9f, 0.0f,
+    8.2f, 3.4f, 1.0f, 0.0f,
+    4.7f, 5.1f, 2.2f, 1.0f,
+  };
+  CHECK(is_identity(v1) == false);
+  CHECK(is_identity(float4x4::identity()) == true);
+  CHECK(is_identity(float4x4{
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f,
+  }) == true);
+  CHECK(determinant(v1) == 0.0_a);
+  CHECK(determinant(v2) == 97.636_a);
+  CHECK_THAT(translation(v2), eq3(4.7f, 5.1f, 2.2f));
+  CHECK_THAT(transpose(v1), eq4x4(
+    1.0f, 5.0f, 9.0f, 13.0f,
+    2.0f, 6.0f, 10.0f, 14.0f,
+    3.0f, 7.0f, 11.0f, 15.0f,
+    4.0f, 8.0f, 12.0f, 16.0f
+  ));
+  CHECK_THAT(transpose(v2), eq4x4(
+    7.0f, 4.0f, 8.2f, 4.7f,
+    5.0f, 1.3f, 3.4f, 5.1f,
+    2.4f, 5.9f, 1.0f, 2.2f,
+    0.0f, 0.0f, 0.0f, 1.0f
+  ));
+  // TODO
+}
+
+// === plane ===
+
+TEST_CASE("plane-ctors") {
+  CHECK_THAT(plane(1.0f, 2.0f, 3.0f, 4.0f), eqp(1.0f, 2.0f, 3.0f, 4.0f));
+  CHECK_THAT(plane(float3{1.0f, 2.0f, 3.0f}, 4.0f), eqp(1.0f, 2.0f, 3.0f, 
4.0f));
+  CHECK_THAT(plane(float4{1.0f, 2.0f, 3.0f, 4.0f}), eqp(1.0f, 2.0f, 3.0f, 
4.0f));
+}
+
+TEST_CASE("plane-operators") {
+  const plane v1 { 1.0f, 2.0f, 3.0f, 4.0f };
+  const plane v2 { 3.0f, 7.0f, 19.0f, 23.0f };
+  const plane v3 { 1.0f, 2.0f, 3.0f, 4.0f };
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("plane-functions") {
+  const plane v1 { 1.0f, 2.0f, 3.0f, 4.0f };
+  CHECK_THAT(normalize(v1), eqp(0.267261f, 0.534522f, 0.801784f, 1.06904f));
+  // TODO
+}
+
+// === quaternion ===
+
+TEST_CASE("quaternion-ctors") {
+  CHECK_THAT(quaternion(1.0f, 2.0f, 3.0f, 4.0f), eqq(1.0f, 2.0f, 3.0f, 4.0f));
+  CHECK_THAT(quaternion(float3{1.0f, 2.0f, 3.0f}, 4.0f), eqq(1.0f, 2.0f, 3.0f, 
4.0f));
+}
+
+TEST_CASE("quaternion-methods") {
+  CHECK_THAT(quaternion::identity(), eqq(0.0, 0.0, 0.0, 1.0));
+}
+
+TEST_CASE("quaternion-operators") {
+  const quaternion v1 { 1.0f, 2.0f, 3.0f, 4.0f };
+  const quaternion v2 { 3.0f, 7.0f, 19.0f, 23.0f };
+  CHECK_THAT(v1 + v2, eqq(4.0f, 9.0f, 22.0f, 27.0f));
+  CHECK_THAT(v1 - v2, eqq(-2.0f, -5.0f, -16.0f, -19.0f));
+  CHECK_THAT(v1 * v2, eqq(52.0f, 64.0f, 146.0f, 18.0f));
+  CHECK_THAT(v1 * 2.0f, eqq(2.0f, 4.0f, 6.0f, 8.0f));
+  CHECK_THAT(v1 / v2, eqq(-0.00632911f, 0.0295359f, -0.00843882f, 0.175105f));
+  CHECK_THAT(-v1, eqq(-1.0f, -2.0f, -3.0f, -4.0f));
+  quaternion v3;
+  v3 = {3.0f, 4.0f, 5.0f, 6.0f};
+  CHECK_THAT(v3 += v1, eqq(4.0f, 6.0f, 8.0f, 10.0f));
+  v3 = {3.0f, 4.0f, 5.0f, 6.0f};
+  CHECK_THAT(v3 -= v1, eqq(2.0f, 2.0f, 2.0f, 2.0f));
+  v3 = {1.5f, 2.0f, 2.5f, 3.0f};
+  CHECK_THAT(v3 *= v1, eqq(10.0f, 12.0f, 20.0f, -1.0f));
+  v3 = {3.0f, 4.0f, 5.0f, 6.0f};
+  CHECK_THAT(v3 *= 3.0f, eqq(9.0f, 12.0f, 15.0f, 18.0f));
+  v3 = {3.0f, 4.0f, 5.0f, 6.0f};
+  CHECK_THAT(v3 /= v1, eqq(0.133333f, 0.266667f, -5.96046e-8f, 1.66667f));
+  v3 = {1.0f, 2.0f, 3.0f, 4.0f};
+  CHECK((v3 == v1) == true);
+  CHECK((v3 == v2) == false);
+  CHECK((v3 != v1) == false);
+  CHECK((v3 != v2) == true);
+}
+
+TEST_CASE("quaternion-functions") {
+  const quaternion v1 { 1.0f, 2.0f, 3.0f, 4.0f };
+  CHECK(is_identity(v1) == false);
+  CHECK(is_identity(quaternion::identity()) == true);
+  CHECK(is_identity(quaternion{0.0f, 0.0f, 0.0f, 1.0f}) == true);
+  CHECK(length(v1) == 5.47723_a);
+  CHECK(length_squared(v1) == 30.0_a);
+  CHECK_THAT(normalize(v1), eqq(0.182574f, 0.365148f, 0.547723f, 0.730297f));
+  CHECK_THAT(conjugate(v1), eqq(-1.0f, -2.0f, -3.0f, 4.0f));
+  CHECK_THAT(inverse(v1), eqq(-0.0333333f, -0.0666667f, -0.1f, 0.133333f));
+  // TODO
+}
+
+
+int main(int argc, char* argv[]) {
+  return Catch::Session().run(argc, argv);
+}
-- 
2.37.1

_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to