Repository: HandmadeMath/HandmadeMath (c)

Issue:
Remove the implementation section, shove it all in the header
I know we've gone back and forth on this before (#57) but I don't think we need to have a separate implementation section in Handmade Math.

Most of the function implementations are inline, since that gives the compiler the most context for optimizations (and math needs to be optimized!). The ones that are _not_ inline were made that way because we were worried about redundant definitions between translation units, but with link-time optimization, compilers are mostly able to prevent that from happening.

So basically we are forcing the user to set up the `HANDMADE_MATH_IMPLEMENTATION` part for about three functions, and for little to no benefit.

I propose that we make everything in Handmade Math inline for 2.0, and rip out `HANDMADE_MATH_IMPLEMENTATION` completely.


Discussion:
I’m fully onboard for this. LETS DO IT 👍

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,8 +1,6 @@
 # Understanding the structure of Handmade Math
 
-Most of the functions in Handmade Math are very short, and are the kind of functions you want to have inlined. Because of this, most functions in Handmade Math are defined with `HINLINE`, which is defined as `static inline`.
-
-The exceptions are functions like `HMM_Rotate`, which are long enough that it doesn't make sense to inline them. These functions are defined with an `HEXTERN` prototype, and implemented in the `#ifdef HANDMADE_MATH_IMPLEMENTATION` block.
+Most of the functions in Handmade Math are very short, and all are the kind of functions you want to be easily inlined for performance. Because of this, all functions in Handmade Math are defined with `HMM_INLINE`, which is defined as `static inline`.
 
 # Quick style guide
 
@@ -14,7 +12,7 @@ The exceptions are functions like `HMM_Rotate`, which are long enough that it do
   0.5f;
   1.0f;
   3.14159f;
-  
+
   // Bad
   1.f
   .0f
diff --git a/HandmadeMath.h b/HandmadeMath.h
--- a/HandmadeMath.h
+++ b/HandmadeMath.h
@@ -8,28 +8,12 @@
 
   =============================================================================
 
-  You MUST
-
-     #define HANDMADE_MATH_IMPLEMENTATION
-
-  in EXACTLY one C or C++ file that includes this header, BEFORE the
-  include, like this:
-
-     #define HANDMADE_MATH_IMPLEMENTATION
-     #include "HandmadeMath.h"
-
-  All other files should just #include "HandmadeMath.h" without the #define.
-
-  =============================================================================
-
   To disable SSE intrinsics, you MUST
 
   #define HANDMADE_MATH_NO_SSE
 
-  in EXACTLY one C or C++ file that includes this header, BEFORE the
-  include, like this:
+  BEFORE the include, like this:
 
-     #define HANDMADE_MATH_IMPLEMENTATION
      #define HANDMADE_MATH_NO_SSE
      #include "HandmadeMath.h"
 
@@ -48,8 +32,7 @@
      #define HMM_ATAN2F MYATan2F
 
   Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F,
-  ExpF, and LogF in EXACTLY one C or C++ file that includes this header,
-  BEFORE the include, like this:
+  ExpF, and LogF BEFORE the include, like this:
 
      #define HMM_SINF MySinF
      #define HMM_COSF MyCosF
@@ -60,7 +43,6 @@
      #define HMM_ACOSF MyACosF
      #define HMM_ATANF MyATanF
      #define HMM_ATAN2F MyATan2F
-     #define HANDMADE_MATH_IMPLEMENTATION
      #include "HandmadeMath.h"
 
   If you do not define all of these, HandmadeMath.h will use the
@@ -76,14 +58,15 @@
 
   CREDITS
 
-  Written by Zakary Strange (zak@strangedev.net && @strangezak)
+  Written by:
+   Zakary Strange (zakarystrange@gmail.com && @strangezak)
+   Ben Visness (ben@bvisness.me && @its_bvisness)
 
   Functionality:
    Matt Mascarenhas (@miblo_)
    Aleph
    FieryDrake (@fierydrake)
    Gingerbill (@TheGingerBill)
-   Ben Visness (@bvisness)
    Trinton Bullard (@Peliex_Dev)
    @AntonDan
 
@@ -150,7 +133,6 @@ extern "C"
 #endif
 
 #define HMM_INLINE static inline
-#define HMM_EXTERN extern
 
 #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \
     !defined(HMM_SQRTF) || !defined(HMM_EXPF) || !defined(HMM_LOGF) || \
@@ -529,7 +511,29 @@ HMM_INLINE float HMM_RSquareRootF(float Float)
     return(Result);
 }
 
-HMM_EXTERN float HMM_Power(float Base, int Exponent);
+COVERAGE(HMM_Power, 2)
+HMM_INLINE float HMM_Power(float Base, int Exponent)
+{
+    ASSERT_COVERED(HMM_Power);
+
+    float Result = 1.0f;
+    float Mul = Exponent < 0 ? 1.f / Base : Base;
+    int X = Exponent < 0 ? -Exponent : Exponent;
+    while (X)
+    {
+        if (X & 1)
+        {
+            ASSERT_COVERED(HMM_Power);
+
+            Result *= Mul;
+        }
+
+        Mul *= Mul;
+        X >>= 1;
+    }
+
+    return (Result);
+}
 
 COVERAGE(HMM_PowerF, 1)
 HMM_INLINE float HMM_PowerF(float Base, float Exponent)
@@ -1293,28 +1297,40 @@ HMM_INLINE hmm_mat4 HMM_Mat4d(float Diagonal)
     return (Result);
 }
 
-#ifdef HANDMADE_MATH__USE_SSE
 COVERAGE(HMM_Transpose, 1)
 HMM_INLINE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix)
 {
     ASSERT_COVERED(HMM_Transpose);
 
+#ifdef HANDMADE_MATH__USE_SSE
     hmm_mat4 Result = Matrix;
 
     _MM_TRANSPOSE4_PS(Result.Columns[0], Result.Columns[1], Result.Columns[2], Result.Columns[3]);
 
     return (Result);
-}
 #else
-HMM_EXTERN hmm_mat4 HMM_Transpose(hmm_mat4 Matrix);
+    hmm_mat4 Result;
+
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
+        }
+    }
+
+    return (Result);
 #endif
+}
 
-#ifdef HANDMADE_MATH__USE_SSE
 COVERAGE(HMM_AddMat4, 1)
 HMM_INLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right)
 {
     ASSERT_COVERED(HMM_AddMat4);
 
+#ifdef HANDMADE_MATH__USE_SSE
     hmm_mat4 Result;
 
     Result.Columns[0] = _mm_add_ps(Left.Columns[0], Right.Columns[0]);
@@ -1323,17 +1339,29 @@ HMM_INLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right)
     Result.Columns[3] = _mm_add_ps(Left.Columns[3], Right.Columns[3]);
 
     return (Result);
-}
 #else
-HMM_EXTERN hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right);
+    hmm_mat4 Result;
+
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
+        }
+    }
+
+    return (Result);
 #endif
+}
 
-#ifdef HANDMADE_MATH__USE_SSE
 COVERAGE(HMM_SubtractMat4, 1)
 HMM_INLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right)
 {
     ASSERT_COVERED(HMM_SubtractMat4);
 
+#ifdef HANDMADE_MATH__USE_SSE
     hmm_mat4 Result;
 
     Result.Columns[0] = _mm_sub_ps(Left.Columns[0], Right.Columns[0]);
@@ -1342,19 +1370,63 @@ HMM_INLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right)
     Result.Columns[3] = _mm_sub_ps(Left.Columns[3], Right.Columns[3]);
 
     return (Result);
-}
 #else
-HMM_EXTERN hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right);
+    hmm_mat4 Result;
+
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
+        }
+    }
+
+    return (Result);
 #endif
+}
+
+COVERAGE(HMM_MultiplyMat4, 1)
+HMM_INLINE hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right)
+{
+    ASSERT_COVERED(HMM_MultiplyMat4);
 
-HMM_EXTERN hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right);
+    hmm_mat4 Result;
 
 #ifdef HANDMADE_MATH__USE_SSE
+    Result.Columns[0] = HMM_LinearCombineSSE(Right.Columns[0], Left);
+    Result.Columns[1] = HMM_LinearCombineSSE(Right.Columns[1], Left);
+    Result.Columns[2] = HMM_LinearCombineSSE(Right.Columns[2], Left);
+    Result.Columns[3] = HMM_LinearCombineSSE(Right.Columns[3], Left);
+#else
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            float Sum = 0;
+            int CurrentMatrice;
+            for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice)
+            {
+                Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice];
+            }
+
+            Result.Elements[Columns][Rows] = Sum;
+        }
+    }
+#endif
+
+    return (Result);
+}
+
 COVERAGE(HMM_MultiplyMat4f, 1)
 HMM_INLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar)
 {
     ASSERT_COVERED(HMM_MultiplyMat4f);
 
+#ifdef HANDMADE_MATH__USE_SSE
     hmm_mat4 Result;
 
     __m128 SSEScalar = _mm_set1_ps(Scalar);
@@ -1364,19 +1436,55 @@ HMM_INLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar)
     Result.Columns[3] = _mm_mul_ps(Matrix.Columns[3], SSEScalar);
 
     return (Result);
-}
 #else
-HMM_EXTERN hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar);
+    hmm_mat4 Result;
+
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
+        }
+    }
+
+    return (Result);
 #endif
+}
+
+COVERAGE(HMM_MultiplyMat4ByVec4, 1)
+HMM_INLINE hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector)
+{
+    ASSERT_COVERED(HMM_MultiplyMat4ByVec4);
 
-HMM_EXTERN hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector);
+    hmm_vec4 Result;
 
 #ifdef HANDMADE_MATH__USE_SSE
+    Result.InternalElementsSSE = HMM_LinearCombineSSE(Vector.InternalElementsSSE, Matrix);
+#else
+    int Columns, Rows;
+    for(Rows = 0; Rows < 4; ++Rows)
+    {
+        float Sum = 0;
+        for(Columns = 0; Columns < 4; ++Columns)
+        {
+            Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
+        }
+
+        Result.Elements[Rows] = Sum;
+    }
+#endif
+
+    return (Result);
+}
+
 COVERAGE(HMM_DivideMat4f, 1)
 HMM_INLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar)
 {
     ASSERT_COVERED(HMM_DivideMat4f);
 
+#ifdef HANDMADE_MATH__USE_SSE
     hmm_mat4 Result;
 
     __m128 SSEScalar = _mm_set1_ps(Scalar);
@@ -1386,10 +1494,22 @@ HMM_INLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar)
     Result.Columns[3] = _mm_div_ps(Matrix.Columns[3], SSEScalar);
 
     return (Result);
-}
 #else
-HMM_EXTERN hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar);
+    hmm_mat4 Result;
+
+    int Columns;
+    for(Columns = 0; Columns < 4; ++Columns)
+    {
+        int Rows;
+        for(Rows = 0; Rows < 4; ++Rows)
+        {
+            Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
+        }
+    }
+
+    return (Result);
 #endif
+}
 
 
 /*
@@ -1450,7 +1570,33 @@ HMM_INLINE hmm_mat4 HMM_Translate(hmm_vec3 Translation)
     return (Result);
 }
 
-HMM_EXTERN hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis);
+COVERAGE(HMM_Rotate, 1)
+HMM_INLINE hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis)
+{
+    ASSERT_COVERED(HMM_Rotate);
+
+    hmm_mat4 Result = HMM_Mat4d(1.0f);
+
+    Axis = HMM_NormalizeVec3(Axis);
+
+    float SinTheta = HMM_SinF(AngleRadians);
+    float CosTheta = HMM_CosF(AngleRadians);
+    float CosValue = 1.0f - CosTheta;
+
+    Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta;
+    Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta);
+    Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta);
+
+    Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta);
+    Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta;
+    Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta);
+
+    Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta);
+    Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta);
+    Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta;
+
+    return (Result);
+}
 
 COVERAGE(HMM_Scale, 1)
 HMM_INLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale)
@@ -1466,7 +1612,39 @@ HMM_INLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale)
     return (Result);
 }
 
-HMM_EXTERN hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up);
+COVERAGE(HMM_LookAt, 1)
+HMM_INLINE hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up)
+{
+    ASSERT_COVERED(HMM_LookAt);
+
+    hmm_mat4 Result;
+
+    hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye));
+    hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up));
+    hmm_vec3 U = HMM_Cross(S, F);
+
+    Result.Elements[0][0] = S.X;
+    Result.Elements[0][1] = U.X;
+    Result.Elements[0][2] = -F.X;
+    Result.Elements[0][3] = 0.0f;
+
+    Result.Elements[1][0] = S.Y;
+    Result.Elements[1][1] = U.Y;
+    Result.Elements[1][2] = -F.Y;
+    Result.Elements[1][3] = 0.0f;
+
+    Result.Elements[2][0] = S.Z;
+    Result.Elements[2][1] = U.Z;
+    Result.Elements[2][2] = -F.Z;
+    Result.Elements[2][3] = 0.0f;
+
+    Result.Elements[3][0] = -HMM_DotVec3(S, Eye);
+    Result.Elements[3][1] = -HMM_DotVec3(U, Eye);
+    Result.Elements[3][2] = HMM_DotVec3(F, Eye);
+    Result.Elements[3][3] = 1.0f;
+
+    return (Result);
+}
 
 
 /*
@@ -1624,8 +1802,6 @@ HMM_INLINE hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Divid
     return (Result);
 }
 
-HMM_EXTERN hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left);
-
 COVERAGE(HMM_DotQuaternion, 1)
 HMM_INLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right)
 {
@@ -1647,6 +1823,29 @@ HMM_INLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right)
     return (Result);
 }
 
+COVERAGE(HMM_InverseQuaternion, 1)
+HMM_INLINE hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left)
+{
+    ASSERT_COVERED(HMM_InverseQuaternion);
+
+    hmm_quaternion Conjugate;
+    hmm_quaternion Result;
+    float Norm = 0;
+    float NormSquared = 0;
+
+    Conjugate.X = -Left.X;
+    Conjugate.Y = -Left.Y;
+    Conjugate.Z = -Left.Z;
+    Conjugate.W = Left.W;
+
+    Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left));
+    NormSquared = Norm * Norm;
+
+    Result = HMM_DivideQuaternionF(Conjugate, NormSquared);
+
+    return (Result);
+}
+
 COVERAGE(HMM_NormalizeQuaternion, 1)
 HMM_INLINE hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left)
 {
@@ -1684,14 +1883,167 @@ HMM_INLINE hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quatern
     return (Result);
 }
 
-HMM_EXTERN hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right);
-HMM_EXTERN hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left);
-HMM_EXTERN hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 Left);
-HMM_EXTERN hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians);
-
-#ifdef __cplusplus
-}
-#endif
+COVERAGE(HMM_Slerp, 1)
+HMM_INLINE hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right)
+{
+    ASSERT_COVERED(HMM_Slerp);
+
+    hmm_quaternion Result;
+    hmm_quaternion QuaternionLeft;
+    hmm_quaternion QuaternionRight;
+
+    float Cos_Theta = HMM_DotQuaternion(Left, Right);
+    float Angle = HMM_ACosF(Cos_Theta);
+
+    float S1 = HMM_SinF((1.0f - Time) * Angle);
+    float S2 = HMM_SinF(Time * Angle);
+    float Is = 1.0f / HMM_SinF(Angle);
+
+    QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1);
+    QuaternionRight = HMM_MultiplyQuaternionF(Right, S2);
+
+    Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight);
+    Result = HMM_MultiplyQuaternionF(Result, Is);
+
+    return (Result);
+}
+
+COVERAGE(HMM_QuaternionToMat4, 1)
+HMM_INLINE hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left)
+{
+    ASSERT_COVERED(HMM_QuaternionToMat4);
+
+    hmm_mat4 Result;
+
+    hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left);
+
+    float XX, YY, ZZ,
+          XY, XZ, YZ,
+          WX, WY, WZ;
+
+    XX = NormalizedQuaternion.X * NormalizedQuaternion.X;
+    YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y;
+    ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z;
+    XY = NormalizedQuaternion.X * NormalizedQuaternion.Y;
+    XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z;
+    YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z;
+    WX = NormalizedQuaternion.W * NormalizedQuaternion.X;
+    WY = NormalizedQuaternion.W * NormalizedQuaternion.Y;
+    WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z;
+
+    Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ);
+    Result.Elements[0][1] = 2.0f * (XY + WZ);
+    Result.Elements[0][2] = 2.0f * (XZ - WY);
+    Result.Elements[0][3] = 0.0f;
+
+    Result.Elements[1][0] = 2.0f * (XY - WZ);
+    Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ);
+    Result.Elements[1][2] = 2.0f * (YZ + WX);
+    Result.Elements[1][3] = 0.0f;
+
+    Result.Elements[2][0] = 2.0f * (XZ + WY);
+    Result.Elements[2][1] = 2.0f * (YZ - WX);
+    Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY);
+    Result.Elements[2][3] = 0.0f;
+
+    Result.Elements[3][0] = 0.0f;
+    Result.Elements[3][1] = 0.0f;
+    Result.Elements[3][2] = 0.0f;
+    Result.Elements[3][3] = 1.0f;
+
+    return (Result);
+}
+
+// This method taken from Mike Day at Insomniac Games.
+// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
+//
+// Note that as mentioned at the top of the paper, the paper assumes the matrix
+// would be *post*-multiplied to a vector to rotate it, meaning the matrix is
+// the transpose of what we're dealing with. But, because our matrices are
+// stored in column-major order, the indices *appear* to match the paper.
+//
+// For example, m12 in the paper is row 1, column 2. We need to transpose it to
+// row 2, column 1. But, because the column comes first when referencing
+// elements, it looks like M.Elements[1][2].
+//
+// Don't be confused! Or if you must be confused, at least trust this
+// comment. :)
+COVERAGE(HMM_Mat4ToQuaternion, 4)
+HMM_INLINE hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 M)
+{
+    float T;
+    hmm_quaternion Q;
+
+    if (M.Elements[2][2] < 0.0f) {
+        if (M.Elements[0][0] > M.Elements[1][1]) {
+            ASSERT_COVERED(HMM_Mat4ToQuaternion);
+
+            T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2];
+            Q = HMM_Quaternion(
+                T,
+                M.Elements[0][1] + M.Elements[1][0],
+                M.Elements[2][0] + M.Elements[0][2],
+                M.Elements[1][2] - M.Elements[2][1]
+            );
+        } else {
+            ASSERT_COVERED(HMM_Mat4ToQuaternion);
+
+            T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2];
+            Q = HMM_Quaternion(
+                M.Elements[0][1] + M.Elements[1][0],
+                T,
+                M.Elements[1][2] + M.Elements[2][1],
+                M.Elements[2][0] - M.Elements[0][2]
+            );
+        }
+    } else {
+        if (M.Elements[0][0] < -M.Elements[1][1]) {
+            ASSERT_COVERED(HMM_Mat4ToQuaternion);
+
+            T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2];
+            Q = HMM_Quaternion(
+                M.Elements[2][0] + M.Elements[0][2],
+                M.Elements[1][2] + M.Elements[2][1],
+                T,
+                M.Elements[0][1] - M.Elements[1][0]
+            );
+        } else {
+            ASSERT_COVERED(HMM_Mat4ToQuaternion);
+
+            T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2];
+            Q = HMM_Quaternion(
+                M.Elements[1][2] - M.Elements[2][1],
+                M.Elements[2][0] - M.Elements[0][2],
+                M.Elements[0][1] - M.Elements[1][0],
+                T
+            );
+        }
+    }
+
+    Q = HMM_MultiplyQuaternionF(Q, 0.5f / HMM_SquareRootF(T));
+
+    return Q;
+}
+
+COVERAGE(HMM_QuaternionFromAxisAngle, 1)
+HMM_INLINE hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians)
+{
+    ASSERT_COVERED(HMM_QuaternionFromAxisAngle);
+
+    hmm_quaternion Result;
+
+    hmm_vec3 AxisNormalized = HMM_NormalizeVec3(Axis);
+    float SineOfRotation = HMM_SinF(AngleOfRotationRadians / 2.0f);
+
+    Result.XYZ = HMM_MultiplyVec3f(AxisNormalized, SineOfRotation);
+    Result.W = HMM_CosF(AngleOfRotationRadians / 2.0f);
+
+    return (Result);
+}
+
+#ifdef __cplusplus
+}
+#endif
 
 #ifdef __cplusplus
 
@@ -2792,444 +3144,3 @@ HMM_INLINE hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right)
 #endif
 
 #endif /* HANDMADE_MATH_H */
-
-#ifdef HANDMADE_MATH_IMPLEMENTATION
-
-COVERAGE(HMM_Power, 2)
-float HMM_Power(float Base, int Exponent)
-{
-    ASSERT_COVERED(HMM_Power);
-
-    float Result = 1.0f;
-    float Mul = Exponent < 0 ? 1.f / Base : Base;
-    int X = Exponent < 0 ? -Exponent : Exponent;
-    while (X)
-    {
-        if (X & 1)
-        {
-            ASSERT_COVERED(HMM_Power);
-
-            Result *= Mul;
-        }
-
-        Mul *= Mul;
-        X >>= 1;
-    }
-
-    return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_Transpose, 1)
-hmm_mat4 HMM_Transpose(hmm_mat4 Matrix)
-{
-    ASSERT_COVERED(HMM_Transpose);
-
-    hmm_mat4 Result;
-
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows];
-        }
-    }
-
-    return (Result);
-}
-#endif
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_AddMat4, 1)
-hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right)
-{
-    ASSERT_COVERED(HMM_AddMat4);
-
-    hmm_mat4 Result;
-
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows];
-        }
-    }
-
-    return (Result);
-}
-#endif
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_SubtractMat4, 1)
-hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right)
-{
-    ASSERT_COVERED(HMM_SubtractMat4);
-
-    hmm_mat4 Result;
-
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows];
-        }
-    }
-
-    return (Result);
-}
-#endif
-
-COVERAGE(HMM_MultiplyMat4, 1)
-hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right)
-{
-    ASSERT_COVERED(HMM_MultiplyMat4);
-
-    hmm_mat4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
-    Result.Columns[0] = HMM_LinearCombineSSE(Right.Columns[0], Left);
-    Result.Columns[1] = HMM_LinearCombineSSE(Right.Columns[1], Left);
-    Result.Columns[2] = HMM_LinearCombineSSE(Right.Columns[2], Left);
-    Result.Columns[3] = HMM_LinearCombineSSE(Right.Columns[3], Left);
-#else
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            float Sum = 0;
-            int CurrentMatrice;
-            for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice)
-            {
-                Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice];
-            }
-
-            Result.Elements[Columns][Rows] = Sum;
-        }
-    }
-#endif
-
-    return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_MultiplyMat4f, 1)
-hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar)
-{
-    ASSERT_COVERED(HMM_MultiplyMat4f);
-
-    hmm_mat4 Result;
-
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar;
-        }
-    }
-
-    return (Result);
-}
-#endif
-
-COVERAGE(HMM_MultiplyMat4ByVec4, 1)
-hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector)
-{
-    ASSERT_COVERED(HMM_MultiplyMat4ByVec4);
-
-    hmm_vec4 Result;
-
-#ifdef HANDMADE_MATH__USE_SSE
-	Result.InternalElementsSSE = HMM_LinearCombineSSE(Vector.InternalElementsSSE, Matrix);
-#else
-    int Columns, Rows;
-    for(Rows = 0; Rows < 4; ++Rows)
-    {
-        float Sum = 0;
-        for(Columns = 0; Columns < 4; ++Columns)
-        {
-            Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns];
-        }
-
-        Result.Elements[Rows] = Sum;
-    }
-#endif
-
-    return (Result);
-}
-
-#ifndef HANDMADE_MATH__USE_SSE
-COVERAGE(HMM_DivideMat4f, 1);
-hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar)
-{
-    ASSERT_COVERED(HMM_DivideMat4f);
-
-    hmm_mat4 Result;
-
-    int Columns;
-    for(Columns = 0; Columns < 4; ++Columns)
-    {
-        int Rows;
-        for(Rows = 0; Rows < 4; ++Rows)
-        {
-            Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar;
-        }
-    }
-
-    return (Result);
-}
-#endif
-
-COVERAGE(HMM_Rotate, 1)
-hmm_mat4 HMM_Rotate(float AngleRadians, hmm_vec3 Axis)
-{
-    ASSERT_COVERED(HMM_Rotate);
-
-    hmm_mat4 Result = HMM_Mat4d(1.0f);
-
-    Axis = HMM_NormalizeVec3(Axis);
-
-    float SinTheta = HMM_SinF(AngleRadians);
-    float CosTheta = HMM_CosF(AngleRadians);
-    float CosValue = 1.0f - CosTheta;
-
-    Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta;
-    Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta);
-    Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta);
-
-    Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta);
-    Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta;
-    Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta);
-
-    Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta);
-    Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta);
-    Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta;
-
-    return (Result);
-}
-
-COVERAGE(HMM_LookAt, 1)
-hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up)
-{
-    ASSERT_COVERED(HMM_LookAt);
-
-    hmm_mat4 Result;
-
-    hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye));
-    hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up));
-    hmm_vec3 U = HMM_Cross(S, F);
-
-    Result.Elements[0][0] = S.X;
-    Result.Elements[0][1] = U.X;
-    Result.Elements[0][2] = -F.X;
-    Result.Elements[0][3] = 0.0f;
-
-    Result.Elements[1][0] = S.Y;
-    Result.Elements[1][1] = U.Y;
-    Result.Elements[1][2] = -F.Y;
-    Result.Elements[1][3] = 0.0f;
-
-    Result.Elements[2][0] = S.Z;
-    Result.Elements[2][1] = U.Z;
-    Result.Elements[2][2] = -F.Z;
-    Result.Elements[2][3] = 0.0f;
-
-    Result.Elements[3][0] = -HMM_DotVec3(S, Eye);
-    Result.Elements[3][1] = -HMM_DotVec3(U, Eye);
-    Result.Elements[3][2] = HMM_DotVec3(F, Eye);
-    Result.Elements[3][3] = 1.0f;
-
-    return (Result);
-}
-
-COVERAGE(HMM_InverseQuaternion, 1)
-hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left)
-{
-    ASSERT_COVERED(HMM_InverseQuaternion);
-
-    hmm_quaternion Conjugate;
-    hmm_quaternion Result;
-    float Norm = 0;
-    float NormSquared = 0;
-
-    Conjugate.X = -Left.X;
-    Conjugate.Y = -Left.Y;
-    Conjugate.Z = -Left.Z;
-    Conjugate.W = Left.W;
-
-    Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left));
-    NormSquared = Norm * Norm;
-
-    Result = HMM_DivideQuaternionF(Conjugate, NormSquared);
-
-    return (Result);
-}
-
-COVERAGE(HMM_Slerp, 1)
-hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right)
-{
-    ASSERT_COVERED(HMM_Slerp);
-
-    hmm_quaternion Result;
-    hmm_quaternion QuaternionLeft;
-    hmm_quaternion QuaternionRight;
-
-    float Cos_Theta = HMM_DotQuaternion(Left, Right);
-    float Angle = HMM_ACosF(Cos_Theta);
-
-    float S1 = HMM_SinF((1.0f - Time) * Angle);
-    float S2 = HMM_SinF(Time * Angle);
-    float Is = 1.0f / HMM_SinF(Angle);
-
-    QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1);
-    QuaternionRight = HMM_MultiplyQuaternionF(Right, S2);
-
-    Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight);
-    Result = HMM_MultiplyQuaternionF(Result, Is);
-
-    return (Result);
-}
-
-COVERAGE(HMM_QuaternionToMat4, 1)
-hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left)
-{
-    ASSERT_COVERED(HMM_QuaternionToMat4);
-
-    hmm_mat4 Result;
-
-    hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left);
-
-    float XX, YY, ZZ,
-          XY, XZ, YZ,
-          WX, WY, WZ;
-
-    XX = NormalizedQuaternion.X * NormalizedQuaternion.X;
-    YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y;
-    ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z;
-    XY = NormalizedQuaternion.X * NormalizedQuaternion.Y;
-    XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z;
-    YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z;
-    WX = NormalizedQuaternion.W * NormalizedQuaternion.X;
-    WY = NormalizedQuaternion.W * NormalizedQuaternion.Y;
-    WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z;
-
-    Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ);
-    Result.Elements[0][1] = 2.0f * (XY + WZ);
-    Result.Elements[0][2] = 2.0f * (XZ - WY);
-    Result.Elements[0][3] = 0.0f;
-
-    Result.Elements[1][0] = 2.0f * (XY - WZ);
-    Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ);
-    Result.Elements[1][2] = 2.0f * (YZ + WX);
-    Result.Elements[1][3] = 0.0f;
-
-    Result.Elements[2][0] = 2.0f * (XZ + WY);
-    Result.Elements[2][1] = 2.0f * (YZ - WX);
-    Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY);
-    Result.Elements[2][3] = 0.0f;
-
-    Result.Elements[3][0] = 0.0f;
-    Result.Elements[3][1] = 0.0f;
-    Result.Elements[3][2] = 0.0f;
-    Result.Elements[3][3] = 1.0f;
-
-    return (Result);
-}
-
-// This method taken from Mike Day at Insomniac Games.
-// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
-//
-// Note that as mentioned at the top of the paper, the paper assumes the matrix
-// would be *post*-multiplied to a vector to rotate it, meaning the matrix is
-// the transpose of what we're dealing with. But, because our matrices are
-// stored in column-major order, the indices *appear* to match the paper.
-//
-// For example, m12 in the paper is row 1, column 2. We need to transpose it to
-// row 2, column 1. But, because the column comes first when referencing
-// elements, it looks like M.Elements[1][2].
-//
-// Don't be confused! Or if you must be confused, at least trust this
-// comment. :)
-COVERAGE(HMM_Mat4ToQuaternion, 4)
-hmm_quaternion HMM_Mat4ToQuaternion(hmm_mat4 M)
-{
-    float T;
-    hmm_quaternion Q;
-
-    if (M.Elements[2][2] < 0.0f) {
-        if (M.Elements[0][0] > M.Elements[1][1]) {
-            ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
-            T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2];
-            Q = HMM_Quaternion(
-                T,
-                M.Elements[0][1] + M.Elements[1][0],
-                M.Elements[2][0] + M.Elements[0][2],
-                M.Elements[1][2] - M.Elements[2][1]
-            );
-        } else {
-            ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
-            T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2];
-            Q = HMM_Quaternion(
-                M.Elements[0][1] + M.Elements[1][0],
-                T,
-                M.Elements[1][2] + M.Elements[2][1],
-                M.Elements[2][0] - M.Elements[0][2]
-            );
-        }
-    } else {
-        if (M.Elements[0][0] < -M.Elements[1][1]) {
-            ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
-            T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2];
-            Q = HMM_Quaternion(
-                M.Elements[2][0] + M.Elements[0][2],
-                M.Elements[1][2] + M.Elements[2][1],
-                T,
-                M.Elements[0][1] - M.Elements[1][0]
-            );
-        } else {
-            ASSERT_COVERED(HMM_Mat4ToQuaternion);
-
-            T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2];
-            Q = HMM_Quaternion(
-                M.Elements[1][2] - M.Elements[2][1],
-                M.Elements[2][0] - M.Elements[0][2],
-                M.Elements[0][1] - M.Elements[1][0],
-                T
-            );
-        }
-    }
-
-    Q = HMM_MultiplyQuaternionF(Q, 0.5f / HMM_SquareRootF(T));
-
-    return Q;
-}
-
-COVERAGE(HMM_QuaternionFromAxisAngle, 1)
-hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotationRadians)
-{
-    ASSERT_COVERED(HMM_QuaternionFromAxisAngle);
-
-    hmm_quaternion Result;
-
-    hmm_vec3 AxisNormalized = HMM_NormalizeVec3(Axis);
-    float SineOfRotation = HMM_SinF(AngleOfRotationRadians / 2.0f);
-
-    Result.XYZ = HMM_MultiplyVec3f(AxisNormalized, SineOfRotation);
-    Result.W = HMM_CosF(AngleOfRotationRadians / 2.0f);
-
-    return (Result);
-}
-
-#endif /* HANDMADE_MATH_IMPLEMENTATION */


Test patch:
diff --git a/test/HandmadeMath.c b/test/HandmadeMath.c
--- a/test/HandmadeMath.c
+++ b/test/HandmadeMath.c
@@ -2,6 +2,4 @@
 #include "HandmadeTest.h"
 #endif
 
-#define HANDMADE_MATH_IMPLEMENTATION
-#define HANDMADE_MATH_NO_INLINE
 #include "../HandmadeMath.h"
diff --git a/test/test.bat b/test/test.bat
--- a/test/test.bat
+++ b/test/test.bat
@@ -12,16 +12,16 @@ if "%1%"=="travis" (
 if not exist "build" mkdir build
 pushd build
 
-cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c
+cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B
 hmm_test_c
 
-cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c
+cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B
 hmm_test_c_no_sse
 
-cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp
+cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B
 hmm_test_cpp
 
-cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp
+cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B
 hmm_test_cpp_no_sse
 
 popd