/*
  HandmadeMath.h v2.0.0

  This is a single header file with a bunch of useful types and functions for
  games and graphics. Consider it a lightweight alternative to GLM that works
  both C and C++.

  =============================================================================

  CONFIG

  By default, all angles in Handmade Math are specified in radians. However, it
  can be configured to use degrees or turns instead. Use one of the following
  defines to specify the default unit for angles:

    #define HANDMADE_MATH_USE_RADIANS
    #define HANDMADE_MATH_USE_DEGREES
    #define HANDMADE_MATH_USE_TURNS

  Regardless of the default angle, you can use the following functions to
  specify an angle in a particular unit:

    AngleRad(radians)
    AngleDeg(degrees)
    AngleTurn(turns)

  -----------------------------------------------------------------------------

  Handmade Math ships with SSE (SIMD) implementations of several common
  operations. To disable the use of SSE intrinsics, you must
  define HANDMADE_MATH_NO_SSE before including this file:

    #define HANDMADE_MATH_NO_SSE
    #include "HandmadeMath.h"

  -----------------------------------------------------------------------------

  To use Handmade Math without the C runtime library, you must provide your own
  implementations of basic math functions. Otherwise, HandmadeMath.h will use
  the runtime library implementation of these functions.

  Define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS and provide your own
  implementations of SINF, COSF, TANF, ACOSF, and SQRTF
  before including HandmadeMath.h, like so:

    #define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS
    #define SINF MySinF
    #define COSF MyCosF
    #define TANF MyTanF
    #define ACOSF MyACosF
    #define SQRTF MySqrtF
    #include "HandmadeMath.h"
  
  By default, it is assumed that your math functions take radians. To use
  different units, you must define ANGLE_USER_TO_INTERNAL and
  ANGLE_INTERNAL_TO_USER. For example, if you want to use degrees in your
  code but your math functions use turns:

    #define ANGLE_USER_TO_INTERNAL(a) ((a)*DegToTurn)
    #define ANGLE_INTERNAL_TO_USER(a) ((a)*TurnToDeg)

  =============================================================================
  
  LICENSE

  This software is in the public domain. Where that dedication is not
  recognized, you are granted a perpetual, irrevocable license to copy,
  distribute, and modify this file as you see fit.

  =============================================================================

  CREDITS

  Originally written by Zakary Strange.

  Functionality:
   Zakary Strange (strangezak@protonmail.com && @strangezak)
   Matt Mascarenhas (@miblo_)
   Aleph
   FieryDrake (@fierydrake)
   Gingerbill (@TheGingerBill)
   Ben Visness (@bvisness)
   Trinton Bullard (@Peliex_Dev)
   @AntonDan
   Logan Forman (@dev_dwarf)

  Fixes:
   Jeroen van Rijn (@J_vanRijn)
   Kiljacken (@Kiljacken)
   Insofaras (@insofaras)
   Daniel Gibson (@DanielGibson)
*/

#ifndef HANDMADE_MATH_H
#define HANDMADE_MATH_H

// Dummy macros for when test framework is not present.
#ifndef COVERAGE
# define COVERAGE(a, b)
#endif

#ifndef ASSERT_COVERED
# define ASSERT_COVERED(a)
#endif

/* let's figure out if SSE is really available (unless disabled anyway)
   (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support)
   => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */
#ifndef HANDMADE_MATH_NO_SSE
# ifdef _MSC_VER /* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */
#  if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 )
#   define HANDMADE_MATH__USE_SSE 1
#  endif
# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */
#  ifdef __SSE__ /* they #define __SSE__ if it's supported */
#   define HANDMADE_MATH__USE_SSE 1
#  endif /*  __SSE__ */
# endif /* not _MSC_VER */
#endif /* #ifndef HANDMADE_MATH_NO_SSE */

#if (!defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
# define HANDMADE_MATH__USE_C11_GENERICS 1
#endif

#ifdef HANDMADE_MATH__USE_SSE
# include <xmmintrin.h>
#endif

#ifdef _MSC_VER
#pragma warning(disable:4201)
#endif

#if defined(__GNUC__) || defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
# if (defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) || defined(__clang__)
#  pragma GCC diagnostic ignored "-Wmissing-braces"
# endif
# ifdef __clang__
#  pragma GCC diagnostic ignored "-Wgnu-anonymous-struct"
#  pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# endif
#endif

#if defined(__GNUC__) || defined(__clang__)
# define DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define DEPRECATED(msg)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#if !defined(HANDMADE_MATH_USE_DEGREES) \
    && !defined(HANDMADE_MATH_USE_TURNS) \
    && !defined(HANDMADE_MATH_USE_RADIANS)
# define HANDMADE_MATH_USE_RADIANS
#endif
    
#define PI 3.14159265358979323846
#define PI32 3.14159265359f
#define DEG180 180.0
#define DEG18032 180.0f
#define TURNHALF 0.5
#define TURNHALF32 0.5f
#define RadToDeg ((float)(DEG180/PI))
#define RadToTurn ((float)(TURNHALF/PI))
#define DegToRad ((float)(PI/DEG180))
#define DegToTurn ((float)(TURNHALF/DEG180))
#define TurnToRad ((float)(PI/TURNHALF))
#define TurnToDeg ((float)(DEG180/TURNHALF))

#if defined(HANDMADE_MATH_USE_RADIANS)
# define AngleRad(a) (a)
# define AngleDeg(a) ((a)*DegToRad)
# define AngleTurn(a) ((a)*TurnToRad)
#elif defined(HANDMADE_MATH_USE_DEGREES)
# define AngleRad(a) ((a)*RadToDeg)
# define AngleDeg(a) (a)
# define AngleTurn(a) ((a)*TurnToDeg)
#elif defined(HANDMADE_MATH_USE_TURNS)
# define AngleRad(a) ((a)*RadToTurn)
# define AngleDeg(a) ((a)*DegToTurn)
# define AngleTurn(a) (a)
#endif

#if !defined(HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS)
# include <math.h>
# define SINF sinf
# define COSF cosf
# define TANF tanf
# define SQRTF sqrtf
# define ACOSF acosf
#endif

#if !defined(ANGLE_USER_TO_INTERNAL)
# define ANGLE_USER_TO_INTERNAL(a) (ToRad(a))
#endif

#if !defined(ANGLE_INTERNAL_TO_USER)
# if defined(HANDMADE_MATH_USE_RADIANS)
#  define ANGLE_INTERNAL_TO_USER(a) (a) 
# elif defined(HANDMADE_MATH_USE_DEGREES)
#  define ANGLE_INTERNAL_TO_USER(a) ((a)*RadToDeg)
# elif defined(HANDMADE_MATH_USE_TURNS)
#  define ANGLE_INTERNAL_TO_USER(a) ((a)*RadToTurn)
# endif
#endif

#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define ABS(a) ((a) > 0 ? (a) : -(a))
#define MOD(a, m) (((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m)))
#define SQUARE(x) ((x) * (x))

typedef union Vec2
{
    struct
    {
        float X, Y;
    };

    // fuck off
    struct
    {
         float x, y;
    };

    struct
    {
        float U, V;
    };

    struct
    {
        float Left, Right;
    };

    struct
    {
        float Width, Height;
    };

    float Elements[2];

#ifdef __cplusplus
    inline float &operator[](const int &Index)
    {
        return Elements[Index];
    }
#endif
} Vec2;

typedef union Vec3
{
    struct
    {
        float X, Y, Z;
    };

    struct
    {
         float x, y, z;
    };

    struct
    {
        float U, V, W;
    };

    struct
    {
        float R, G, B;
    };

    struct
    {
        Vec2 XY;
        float _Ignored0;
    };

    struct
    {
        float _Ignored1;
        Vec2 YZ;
    };

    struct
    {
        Vec2 UV;
        float _Ignored2;
    };

    struct
    {
        float _Ignored3;
        Vec2 VW;
    };

    float Elements[3];

#ifdef __cplusplus
    inline float &operator[](const int &Index)
    {
        return Elements[Index];
    }
#endif
} Vec3;

typedef union Vec4
{
    struct
    {
        union
        {
            Vec3 XYZ;
            struct
            {
                float X, Y, Z;
            };
        };

        float W;
    };
    struct
    {
        union
        {
            Vec3 xyz;
            struct
            {
                float x, y, z;
            };
        };

        float w;
    };
    struct
    {
        union
        {
            Vec3 RGB;
            struct
            {
                float R, G, B;
            };
        };

        float A;
    };
    struct
    {
        union
        {
            Vec3 rgb;
            struct
            {
                float r, g, b;
            };
        };

        float a;
    };

    struct
    {
        Vec2 XY;
        float _Ignored0;
        float _Ignored1;
    };

    struct
    {
        float _Ignored2;
        Vec2 YZ;
        float _Ignored3;
    };

    struct
    {
        float _Ignored4;
        float _Ignored5;
        Vec2 ZW;
    };

    float Elements[4];

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSE;
#endif

#ifdef __cplusplus
    inline float &operator[](const int &Index)
    {
        return Elements[Index];
    }
#endif
} Vec4;

typedef union Mat2
{
    float Elements[2][2];
    Vec2 Columns[2];

#ifdef __cplusplus
    inline Vec2 &operator[](const int &Index)
    {
        return Columns[Index];
    }
#endif
} Mat2;
    
typedef union Mat3
{
    float Elements[3][3];
    Vec3 Columns[3];

#ifdef __cplusplus
    inline Vec3 &operator[](const int &Index)
    {
        return Columns[Index];
    }
#endif
} Mat3;

typedef union Mat4
{
    float Elements[4][4];
    Vec4 Columns[4];

#ifdef __cplusplus
    inline Vec4 &operator[](const int &Index)
    {
        return Columns[Index];
    }
#endif
} Mat4;

typedef union Quat
{
	struct
	{
		union
		{
			Vec3 XYZ;
			struct
			{
				float X, Y, Z;
			};
		};

		float W;
	};
	struct
	{
		union
		{
			Vec3 xyz;
			struct
			{
				float x, y, z;
			};
		};

		float w;
	};

	float Elements[4];

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSE;
#endif
} Quat;

typedef signed int Bool;

/*
 * Angle unit conversion functions
 */
static inline float ToRad(float Angle)
{
#if defined(HANDMADE_MATH_USE_RADIANS)
    float Result = Angle;
#elif defined(HANDMADE_MATH_USE_DEGREES) 
    float Result = Angle * DegToRad;
#elif defined(HANDMADE_MATH_USE_TURNS)
    float Result = Angle * TurnToRad;
#endif
    
    return Result;
}

static inline float ToDeg(float Angle)
{
#if defined(HANDMADE_MATH_USE_RADIANS)
    float Result = Angle * RadToDeg;
#elif defined(HANDMADE_MATH_USE_DEGREES) 
    float Result = Angle;
#elif defined(HANDMADE_MATH_USE_TURNS)
    float Result = Angle * TurnToDeg;
#endif
    
    return Result;
}

static inline float ToTurn(float Angle)
{
#if defined(HANDMADE_MATH_USE_RADIANS)
    float Result = Angle * RadToTurn;
#elif defined(HANDMADE_MATH_USE_DEGREES) 
    float Result = Angle * DegToTurn;
#elif defined(HANDMADE_MATH_USE_TURNS)
    float Result = Angle;
#endif
    
    return Result;
}

/*
 * Floating-point math functions
 */

COVERAGE(SinF, 1)
static inline float SinF(float Angle)
{
    ASSERT_COVERED(SinF);
    return SINF(ANGLE_USER_TO_INTERNAL(Angle));
}

COVERAGE(CosF, 1)
static inline float CosF(float Angle)
{
    ASSERT_COVERED(CosF);
    return COSF(ANGLE_USER_TO_INTERNAL(Angle));
}

COVERAGE(TanF, 1)
static inline float TanF(float Angle)
{
    ASSERT_COVERED(TanF);
    return TANF(ANGLE_USER_TO_INTERNAL(Angle));
}

COVERAGE(ACosF, 1)
static inline float ACosF(float Arg)
{
    ASSERT_COVERED(ACosF);
    return ANGLE_INTERNAL_TO_USER(ACOSF(Arg));
}

COVERAGE(SqrtF, 1)
static inline float SqrtF(float Float)
{
    ASSERT_COVERED(SqrtF);

    float Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 In = _mm_set_ss(Float);
    __m128 Out = _mm_sqrt_ss(In);
    Result = _mm_cvtss_f32(Out);
#else
    Result = SQRTF(Float);
#endif

    return Result;
}

COVERAGE(InvSqrtF, 1)
static inline float InvSqrtF(float Float)
{
    ASSERT_COVERED(InvSqrtF);

    float Result;

    Result = 1.0f/SqrtF(Float);

    return Result;
}


/*
 * Utility functions
 */

COVERAGE(Lerp, 1)
static inline float Lerp(float A, float Time, float B)
{
    ASSERT_COVERED(Lerp);
    return (1.0f - Time) * A + Time * B;
}

COVERAGE(Clamp, 1)
static inline float Clamp(float Min, float Value, float Max)
{
    ASSERT_COVERED(Clamp);

    float Result = Value;

    if (Result < Min)
    {
        Result = Min;
    }

    if (Result > Max)
    {
        Result = Max;
    }

    return Result;
}


/*
 * Vector initialization
 */

COVERAGE(V2, 1)
static inline Vec2 V2(float X, float Y)
{
    ASSERT_COVERED(V2);

    Vec2 Result;
    Result.X = X;
    Result.Y = Y;

    return Result;
}

COVERAGE(V3, 1)
static inline Vec3 V3(float X, float Y, float Z)
{
    ASSERT_COVERED(V3);

    Vec3 Result;
    Result.X = X;
    Result.Y = Y;
    Result.Z = Z;

    return Result;
}

COVERAGE(V4, 1)
static inline Vec4 V4(float X, float Y, float Z, float W)
{
    ASSERT_COVERED(V4);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_setr_ps(X, Y, Z, W);
#else
    Result.X = X;
    Result.Y = Y;
    Result.Z = Z;
    Result.W = W;
#endif

    return Result;
}

COVERAGE(V4V, 1)
static inline Vec4 V4V(Vec3 Vector, float W)
{
    ASSERT_COVERED(V4V);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W);
#else
    Result.XYZ = Vector;
    Result.W = W;
#endif

    return Result;
}


/*
 * Binary vector operations
 */

COVERAGE(AddV2, 1)
static inline Vec2 AddV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(AddV2);

    Vec2 Result;
    Result.X = Left.X + Right.X;
    Result.Y = Left.Y + Right.Y;

    return Result;
}

COVERAGE(AddV3, 1)
static inline Vec3 AddV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(AddV3);

    Vec3 Result;
    Result.X = Left.X + Right.X;
    Result.Y = Left.Y + Right.Y;
    Result.Z = Left.Z + Right.Z;

    return Result;
}

COVERAGE(AddV4, 1)
static inline Vec4 AddV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(AddV4);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_add_ps(Left.SSE, Right.SSE);
#else
    Result.X = Left.X + Right.X;
    Result.Y = Left.Y + Right.Y;
    Result.Z = Left.Z + Right.Z;
    Result.W = Left.W + Right.W;
#endif

    return Result;
}

COVERAGE(SubV2, 1)
static inline Vec2 SubV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(SubV2);

    Vec2 Result;
    Result.X = Left.X - Right.X;
    Result.Y = Left.Y - Right.Y;

    return Result;
}

COVERAGE(SubV3, 1)
static inline Vec3 SubV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(SubV3);

    Vec3 Result;
    Result.X = Left.X - Right.X;
    Result.Y = Left.Y - Right.Y;
    Result.Z = Left.Z - Right.Z;

    return Result;
}

COVERAGE(SubV4, 1)
static inline Vec4 SubV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(SubV4);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE);
#else
    Result.X = Left.X - Right.X;
    Result.Y = Left.Y - Right.Y;
    Result.Z = Left.Z - Right.Z;
    Result.W = Left.W - Right.W;
#endif

    return Result;
}

COVERAGE(MulV2, 1)
static inline Vec2 MulV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(MulV2);

    Vec2 Result;
    Result.X = Left.X * Right.X;
    Result.Y = Left.Y * Right.Y;

    return Result;
}

COVERAGE(MulV2F, 1)
static inline Vec2 MulV2F(Vec2 Left, float Right)
{
    ASSERT_COVERED(MulV2F);

    Vec2 Result;
    Result.X = Left.X * Right;
    Result.Y = Left.Y * Right;

    return Result;
}

COVERAGE(MulV3, 1)
static inline Vec3 MulV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(MulV3);

    Vec3 Result;
    Result.X = Left.X * Right.X;
    Result.Y = Left.Y * Right.Y;
    Result.Z = Left.Z * Right.Z;

    return Result;
}

COVERAGE(MulV3F, 1)
static inline Vec3 MulV3F(Vec3 Left, float Right)
{
    ASSERT_COVERED(MulV3F);

    Vec3 Result;
    Result.X = Left.X * Right;
    Result.Y = Left.Y * Right;
    Result.Z = Left.Z * Right;

    return Result;
}

COVERAGE(MulV4, 1)
static inline Vec4 MulV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(MulV4);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_mul_ps(Left.SSE, Right.SSE);
#else
    Result.X = Left.X * Right.X;
    Result.Y = Left.Y * Right.Y;
    Result.Z = Left.Z * Right.Z;
    Result.W = Left.W * Right.W;
#endif

    return Result;
}

COVERAGE(MulV4F, 1)
static inline Vec4 MulV4F(Vec4 Left, float Right)
{
    ASSERT_COVERED(MulV4F);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 Scalar = _mm_set1_ps(Right);
    Result.SSE = _mm_mul_ps(Left.SSE, Scalar);
#else
    Result.X = Left.X * Right;
    Result.Y = Left.Y * Right;
    Result.Z = Left.Z * Right;
    Result.W = Left.W * Right;
#endif

    return Result;
}

COVERAGE(DivV2, 1)
static inline Vec2 DivV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(DivV2);

    Vec2 Result;
    Result.X = Left.X / Right.X;
    Result.Y = Left.Y / Right.Y;

    return Result;
}

COVERAGE(DivV2F, 1)
static inline Vec2 DivV2F(Vec2 Left, float Right)
{
    ASSERT_COVERED(DivV2F);

    Vec2 Result;
    Result.X = Left.X / Right;
    Result.Y = Left.Y / Right;

    return Result;
}

COVERAGE(DivV3, 1)
static inline Vec3 DivV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(DivV3);

    Vec3 Result;
    Result.X = Left.X / Right.X;
    Result.Y = Left.Y / Right.Y;
    Result.Z = Left.Z / Right.Z;

    return Result;
}

COVERAGE(DivV3F, 1)
static inline Vec3 DivV3F(Vec3 Left, float Right)
{
    ASSERT_COVERED(DivV3F);

    Vec3 Result;
    Result.X = Left.X / Right;
    Result.Y = Left.Y / Right;
    Result.Z = Left.Z / Right;

    return Result;
}

COVERAGE(DivV4, 1)
static inline Vec4 DivV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(DivV4);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_div_ps(Left.SSE, Right.SSE);
#else
    Result.X = Left.X / Right.X;
    Result.Y = Left.Y / Right.Y;
    Result.Z = Left.Z / Right.Z;
    Result.W = Left.W / Right.W;
#endif

    return Result;
}

COVERAGE(DivV4F, 1)
static inline Vec4 DivV4F(Vec4 Left, float Right)
{
    ASSERT_COVERED(DivV4F);

    Vec4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 Scalar = _mm_set1_ps(Right);
    Result.SSE = _mm_div_ps(Left.SSE, Scalar);
#else
    Result.X = Left.X / Right;
    Result.Y = Left.Y / Right;
    Result.Z = Left.Z / Right;
    Result.W = Left.W / Right;
#endif

    return Result;
}

COVERAGE(EqV2, 1)
static inline Bool EqV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(EqV2);
    return Left.X == Right.X && Left.Y == Right.Y;
}

COVERAGE(EqV3, 1)
static inline Bool EqV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(EqV3);
    return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z;
}

COVERAGE(EqV4, 1)
static inline Bool EqV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(EqV4);
    return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W;
}

COVERAGE(DotV2, 1)
static inline float DotV2(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(DotV2);
    return (Left.X * Right.X) + (Left.Y * Right.Y);
}

COVERAGE(DotV3, 1)
static inline float DotV3(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(DotV3);
    return (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z);
}

COVERAGE(DotV4, 1)
static inline float DotV4(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(DotV4);

    float Result;

    // NOTE(zak): IN the future if we wanna check what version SSE is support
    // we can use _mm_dp_ps (4.3) but for now we will use the old way.
    // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3
#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE);
    __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1));
    SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
    SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3));
    SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
    _mm_store_ss(&Result, SSEResultOne);
#else
    Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W));
#endif

    return Result;
}

COVERAGE(Cross, 1)
static inline Vec3 Cross(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(Cross);

    Vec3 Result;
    Result.X = (Left.Y * Right.Z) - (Left.Z * Right.Y);
    Result.Y = (Left.Z * Right.X) - (Left.X * Right.Z);
    Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X);

    return Result;
}


/*
 * Unary vector operations
 */

COVERAGE(LenSqrV2, 1)
static inline float LenSqrV2(Vec2 A)
{
    ASSERT_COVERED(LenSqrV2);
    return DotV2(A, A);
}

COVERAGE(LenSqrV3, 1)
static inline float LenSqrV3(Vec3 A)
{
    ASSERT_COVERED(LenSqrV3);
    return DotV3(A, A);
}

COVERAGE(LenSqrV4, 1)
static inline float LenSqrV4(Vec4 A)
{
    ASSERT_COVERED(LenSqrV4);
    return DotV4(A, A);
}

COVERAGE(LenV2, 1)
static inline float LenV2(Vec2 A)
{
    ASSERT_COVERED(LenV2);
    return SqrtF(LenSqrV2(A));
}

COVERAGE(LenV3, 1)
static inline float LenV3(Vec3 A)
{
    ASSERT_COVERED(LenV3);
    return SqrtF(LenSqrV3(A));
}

COVERAGE(LenV4, 1)
static inline float LenV4(Vec4 A)
{
    ASSERT_COVERED(LenV4);
    return SqrtF(LenSqrV4(A));
}

COVERAGE(NormV2, 1)
static inline Vec2 NormV2(Vec2 A)
{
    ASSERT_COVERED(NormV2);
    return MulV2F(A, InvSqrtF(DotV2(A, A)));
}

COVERAGE(NormV3, 1)
static inline Vec3 NormV3(Vec3 A)
{
    ASSERT_COVERED(NormV3);
    return MulV3F(A, InvSqrtF(DotV3(A, A)));
}

COVERAGE(NormV4, 1)
static inline Vec4 NormV4(Vec4 A)
{
    ASSERT_COVERED(NormV4);
    return MulV4F(A, InvSqrtF(DotV4(A, A)));
}

/*
 * Utility vector functions
 */

COVERAGE(LerpV2, 1)
static inline Vec2 LerpV2(Vec2 A, float Time, Vec2 B) 
{
    ASSERT_COVERED(LerpV2);
    return AddV2(MulV2F(A, 1.0f - Time), MulV2F(B, Time));
}

COVERAGE(LerpV3, 1)
static inline Vec3 LerpV3(Vec3 A, float Time, Vec3 B) 
{
    ASSERT_COVERED(LerpV3);
    return AddV3(MulV3F(A, 1.0f - Time), MulV3F(B, Time));
}

COVERAGE(LerpV4, 1)
static inline Vec4 LerpV4(Vec4 A, float Time, Vec4 B) 
{
    ASSERT_COVERED(LerpV4);
    return AddV4(MulV4F(A, 1.0f - Time), MulV4F(B, Time));
}

/*
 * SSE stuff
 */

COVERAGE(LinearCombineV4M4, 1)
static inline Vec4 LinearCombineV4M4(Vec4 Left, Mat4 Right)
{
    ASSERT_COVERED(LinearCombineV4M4);

    Vec4 Result;
#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x00), Right.Columns[0].SSE);
    Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x55), Right.Columns[1].SSE));
    Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE));
    Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE));
#else
    Result.X = Left.Elements[0] * Right.Columns[0].X;
    Result.Y = Left.Elements[0] * Right.Columns[0].Y;
    Result.Z = Left.Elements[0] * Right.Columns[0].Z;
    Result.W = Left.Elements[0] * Right.Columns[0].W;

    Result.X += Left.Elements[1] * Right.Columns[1].X;
    Result.Y += Left.Elements[1] * Right.Columns[1].Y;
    Result.Z += Left.Elements[1] * Right.Columns[1].Z;
    Result.W += Left.Elements[1] * Right.Columns[1].W;

    Result.X += Left.Elements[2] * Right.Columns[2].X;
    Result.Y += Left.Elements[2] * Right.Columns[2].Y;
    Result.Z += Left.Elements[2] * Right.Columns[2].Z;
    Result.W += Left.Elements[2] * Right.Columns[2].W;

    Result.X += Left.Elements[3] * Right.Columns[3].X;
    Result.Y += Left.Elements[3] * Right.Columns[3].Y;
    Result.Z += Left.Elements[3] * Right.Columns[3].Z;
    Result.W += Left.Elements[3] * Right.Columns[3].W;
#endif

    return Result;
}

/*
 * 2x2 Matrices
 */

COVERAGE(M2, 1)
static inline Mat2 M2(void)
{
    ASSERT_COVERED(M2);
    Mat2 Result = {0};
    return Result;
}

COVERAGE(M2D, 1)
static inline Mat2 M2D(float Diagonal)
{
    ASSERT_COVERED(M2D);
    
    Mat2 Result = {0};
    Result.Elements[0][0] = Diagonal;
    Result.Elements[1][1] = Diagonal;

    return Result;
}

COVERAGE(TransposeM2, 1)
static inline Mat2 TransposeM2(Mat2 Matrix)
{
    ASSERT_COVERED(TransposeM2);
    
    Mat2 Result = Matrix;

    Result.Elements[0][1] = Matrix.Elements[1][0];
    Result.Elements[1][0] = Matrix.Elements[0][1];
    
    return Result;
}

COVERAGE(AddM2, 1)
static inline Mat2 AddM2(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(AddM2);
    
    Mat2 Result;

    Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
    Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
   
    return Result;    
}

COVERAGE(SubM2, 1)
static inline Mat2 SubM2(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(SubM2);
    
    Mat2 Result;

    Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
    Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
    
    return Result;
}

COVERAGE(MulM2V2, 1)
static inline Vec2 MulM2V2(Mat2 Matrix, Vec2 Vector)
{
    ASSERT_COVERED(MulM2V2);
    
    Vec2 Result;

    Result.X = Vector.Elements[0] * Matrix.Columns[0].X;
    Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y;

    Result.X += Vector.Elements[1] * Matrix.Columns[1].X;
    Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y;

    return Result;    
}

COVERAGE(MulM2, 1)
static inline Mat2 MulM2(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(MulM2);
    
    Mat2 Result;
    Result.Columns[0] = MulM2V2(Left, Right.Columns[0]);
    Result.Columns[1] = MulM2V2(Left, Right.Columns[1]);

    return Result;    
}

COVERAGE(MulM2F, 1)
static inline Mat2 MulM2F(Mat2 Matrix, float Scalar)
{
    ASSERT_COVERED(MulM2F);
    
    Mat2 Result;

    Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
    
    return Result;
}

COVERAGE(DivM2F, 1)
static inline Mat2 DivM2F(Mat2 Matrix, float Scalar)
{
    ASSERT_COVERED(DivM2F);
    
    Mat2 Result;

    Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;

    return Result;
}

COVERAGE(DeterminantM2, 1)
static inline float DeterminantM2(Mat2 Matrix) 
{
    ASSERT_COVERED(DeterminantM2);
    return Matrix.Elements[0][0]*Matrix.Elements[1][1] - Matrix.Elements[0][1]*Matrix.Elements[1][0];
}


COVERAGE(InvGeneralM2, 1)
static inline Mat2 InvGeneralM2(Mat2 Matrix) 
{
    ASSERT_COVERED(InvGeneralM2);

    Mat2 Result;
    float InvDeterminant = 1.0f / DeterminantM2(Matrix);
    Result.Elements[0][0] = InvDeterminant * +Matrix.Elements[1][1];
    Result.Elements[1][1] = InvDeterminant * +Matrix.Elements[0][0];
    Result.Elements[0][1] = InvDeterminant * -Matrix.Elements[0][1];
    Result.Elements[1][0] = InvDeterminant * -Matrix.Elements[1][0];

    return Result;
}

/*
 * 3x3 Matrices
 */

COVERAGE(M3, 1)
static inline Mat3 M3(void)
{
    ASSERT_COVERED(M3);
    Mat3 Result = {0};
    return Result;
}

COVERAGE(M3D, 1)
static inline Mat3 M3D(float Diagonal)
{
    ASSERT_COVERED(M3D);
    
    Mat3 Result = {0};
    Result.Elements[0][0] = Diagonal;
    Result.Elements[1][1] = Diagonal;
    Result.Elements[2][2] = Diagonal;

    return Result;
}

COVERAGE(TransposeM3, 1)
static inline Mat3 TransposeM3(Mat3 Matrix)
{
    ASSERT_COVERED(TransposeM3);

    Mat3 Result = Matrix;

    Result.Elements[0][1] = Matrix.Elements[1][0];
    Result.Elements[0][2] = Matrix.Elements[2][0];
    Result.Elements[1][0] = Matrix.Elements[0][1];
    Result.Elements[1][2] = Matrix.Elements[2][1];
    Result.Elements[2][1] = Matrix.Elements[1][2];
    Result.Elements[2][0] = Matrix.Elements[0][2];
    
    return Result;
}

COVERAGE(AddM3, 1)
static inline Mat3 AddM3(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(AddM3);
    
    Mat3 Result;
    
    Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
    Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2];
    Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
    Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2];
    Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0];
    Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1];
    Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2];

    return Result;    
}

COVERAGE(SubM3, 1)
static inline Mat3 SubM3(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(SubM3);

    Mat3 Result;

    Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
    Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2];
    Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
    Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2];
    Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0];
    Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1];
    Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2];

    return Result;
}

COVERAGE(MulM3V3, 1)
static inline Vec3 MulM3V3(Mat3 Matrix, Vec3 Vector)
{
    ASSERT_COVERED(MulM3V3);
    
    Vec3 Result;

    Result.X = Vector.Elements[0] * Matrix.Columns[0].X;
    Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y;
    Result.Z = Vector.Elements[0] * Matrix.Columns[0].Z;

    Result.X += Vector.Elements[1] * Matrix.Columns[1].X;
    Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y;
    Result.Z += Vector.Elements[1] * Matrix.Columns[1].Z;

    Result.X += Vector.Elements[2] * Matrix.Columns[2].X;
    Result.Y += Vector.Elements[2] * Matrix.Columns[2].Y;
    Result.Z += Vector.Elements[2] * Matrix.Columns[2].Z;
    
    return Result;    
}

COVERAGE(MulM3, 1)
static inline Mat3 MulM3(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(MulM3);

    Mat3 Result;
    Result.Columns[0] = MulM3V3(Left, Right.Columns[0]);
    Result.Columns[1] = MulM3V3(Left, Right.Columns[1]);
    Result.Columns[2] = MulM3V3(Left, Right.Columns[2]);

    return Result;    
}

COVERAGE(MulM3F, 1)
static inline Mat3 MulM3F(Mat3 Matrix, float Scalar)
{
    ASSERT_COVERED(MulM3F);

    Mat3 Result;

    Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
    Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
    Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar;
    Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar;
    Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar;
    Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar;

    return Result;            
}

COVERAGE(DivM3, 1)
static inline Mat3 DivM3F(Mat3 Matrix, float Scalar)
{
    ASSERT_COVERED(DivM3);

    Mat3 Result;
    
    Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
    Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;
    Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar;
    Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar;
    Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar;
    Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar;

    return Result;                    
}

COVERAGE(DeterminantM3, 1)
static inline float DeterminantM3(Mat3 Matrix) 
{
    ASSERT_COVERED(DeterminantM3);

    Mat3 cross_result;
    cross_result.Columns[0] = Cross(Matrix.Columns[1], Matrix.Columns[2]);
    cross_result.Columns[1] = Cross(Matrix.Columns[2], Matrix.Columns[0]);
    cross_result.Columns[2] = Cross(Matrix.Columns[0], Matrix.Columns[1]);

    return DotV3(cross_result.Columns[2], Matrix.Columns[2]);
}

COVERAGE(InvGeneralM3, 1)
static inline Mat3 InvGeneralM3(Mat3 Matrix) 
{
    ASSERT_COVERED(InvGeneralM3);

    Mat3 cross_result;
    cross_result.Columns[0] = Cross(Matrix.Columns[1], Matrix.Columns[2]);
    cross_result.Columns[1] = Cross(Matrix.Columns[2], Matrix.Columns[0]);
    cross_result.Columns[2] = Cross(Matrix.Columns[0], Matrix.Columns[1]);

    float InvDeterminant = 1.0f / DotV3(cross_result.Columns[2], Matrix.Columns[2]);

    Mat3 Result;
    Result.Columns[0] = MulV3F(cross_result.Columns[0], InvDeterminant);
    Result.Columns[1] = MulV3F(cross_result.Columns[1], InvDeterminant);
    Result.Columns[2] = MulV3F(cross_result.Columns[2], InvDeterminant);

    return TransposeM3(Result);
}

/*
 * 4x4 Matrices
 */

COVERAGE(M4, 1)
static inline Mat4 M4(void)
{
    ASSERT_COVERED(M4);
    Mat4 Result = {0};
    return Result;
}

COVERAGE(M4D, 1)
static inline Mat4 M4D(float Diagonal)
{
    ASSERT_COVERED(M4D);

    Mat4 Result = {0};
    Result.Elements[0][0] = Diagonal;
    Result.Elements[1][1] = Diagonal;
    Result.Elements[2][2] = Diagonal;
    Result.Elements[3][3] = Diagonal;

    return Result;
}

COVERAGE(TransposeM4, 1)
static inline Mat4 TransposeM4(Mat4 Matrix)
{
    ASSERT_COVERED(TransposeM4);

    Mat4 Result = Matrix;
#ifdef HANDMADE_MATH__USE_SSE
    _MM_TRANSPOSE4_PS(Result.Columns[0].SSE, Result.Columns[1].SSE, Result.Columns[2].SSE, Result.Columns[3].SSE);
#else
    Result.Elements[0][1] = Matrix.Elements[1][0];
    Result.Elements[0][2] = Matrix.Elements[2][0];
    Result.Elements[0][3] = Matrix.Elements[3][0];
    Result.Elements[1][0] = Matrix.Elements[0][1];
    Result.Elements[1][2] = Matrix.Elements[2][1];
    Result.Elements[1][3] = Matrix.Elements[3][1];
    Result.Elements[2][1] = Matrix.Elements[1][2];
    Result.Elements[2][0] = Matrix.Elements[0][2];
    Result.Elements[2][3] = Matrix.Elements[3][2];
    Result.Elements[3][1] = Matrix.Elements[1][3];
    Result.Elements[3][2] = Matrix.Elements[2][3];
    Result.Elements[3][0] = Matrix.Elements[0][3];
#endif

    return Result;
}

COVERAGE(AddM4, 1)
static inline Mat4 AddM4(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(AddM4);

    Mat4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.Columns[0].SSE = _mm_add_ps(Left.Columns[0].SSE, Right.Columns[0].SSE);
    Result.Columns[1].SSE = _mm_add_ps(Left.Columns[1].SSE, Right.Columns[1].SSE);
    Result.Columns[2].SSE = _mm_add_ps(Left.Columns[2].SSE, Right.Columns[2].SSE);
    Result.Columns[3].SSE = _mm_add_ps(Left.Columns[3].SSE, Right.Columns[3].SSE);
#else
    Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1];
    Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2];
    Result.Elements[0][3] = Left.Elements[0][3] + Right.Elements[0][3];
    Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1];
    Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2];
    Result.Elements[1][3] = Left.Elements[1][3] + Right.Elements[1][3];
    Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0];
    Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1];
    Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2];
    Result.Elements[2][3] = Left.Elements[2][3] + Right.Elements[2][3];
    Result.Elements[3][0] = Left.Elements[3][0] + Right.Elements[3][0];
    Result.Elements[3][1] = Left.Elements[3][1] + Right.Elements[3][1];
    Result.Elements[3][2] = Left.Elements[3][2] + Right.Elements[3][2];
    Result.Elements[3][3] = Left.Elements[3][3] + Right.Elements[3][3];
#endif

    return Result;
}

COVERAGE(SubM4, 1)
static inline Mat4 SubM4(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(SubM4);

    Mat4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.Columns[0].SSE = _mm_sub_ps(Left.Columns[0].SSE, Right.Columns[0].SSE);
    Result.Columns[1].SSE = _mm_sub_ps(Left.Columns[1].SSE, Right.Columns[1].SSE);
    Result.Columns[2].SSE = _mm_sub_ps(Left.Columns[2].SSE, Right.Columns[2].SSE);
    Result.Columns[3].SSE = _mm_sub_ps(Left.Columns[3].SSE, Right.Columns[3].SSE);
#else
    Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0];
    Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1];
    Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2];
    Result.Elements[0][3] = Left.Elements[0][3] - Right.Elements[0][3];
    Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0];
    Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1];
    Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2];
    Result.Elements[1][3] = Left.Elements[1][3] - Right.Elements[1][3];
    Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0];
    Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1];
    Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2];
    Result.Elements[2][3] = Left.Elements[2][3] - Right.Elements[2][3];
    Result.Elements[3][0] = Left.Elements[3][0] - Right.Elements[3][0];
    Result.Elements[3][1] = Left.Elements[3][1] - Right.Elements[3][1];
    Result.Elements[3][2] = Left.Elements[3][2] - Right.Elements[3][2];
    Result.Elements[3][3] = Left.Elements[3][3] - Right.Elements[3][3];
#endif
 
    return Result;
}

COVERAGE(MulM4, 1)
static inline Mat4 MulM4(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(MulM4);

    Mat4 Result;
    Result.Columns[0] = LinearCombineV4M4(Right.Columns[0], Left);
    Result.Columns[1] = LinearCombineV4M4(Right.Columns[1], Left);
    Result.Columns[2] = LinearCombineV4M4(Right.Columns[2], Left);
    Result.Columns[3] = LinearCombineV4M4(Right.Columns[3], Left);

    return Result;
}

COVERAGE(MulM4F, 1)
static inline Mat4 MulM4F(Mat4 Matrix, float Scalar)
{
    ASSERT_COVERED(MulM4F);

    Mat4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSEScalar = _mm_set1_ps(Scalar);
    Result.Columns[0].SSE = _mm_mul_ps(Matrix.Columns[0].SSE, SSEScalar);
    Result.Columns[1].SSE = _mm_mul_ps(Matrix.Columns[1].SSE, SSEScalar);
    Result.Columns[2].SSE = _mm_mul_ps(Matrix.Columns[2].SSE, SSEScalar);
    Result.Columns[3].SSE = _mm_mul_ps(Matrix.Columns[3].SSE, SSEScalar);
#else
    Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar;
    Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar;
    Result.Elements[0][3] = Matrix.Elements[0][3] * Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar;
    Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar;
    Result.Elements[1][3] = Matrix.Elements[1][3] * Scalar;
    Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar;
    Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar;
    Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar;
    Result.Elements[2][3] = Matrix.Elements[2][3] * Scalar;
    Result.Elements[3][0] = Matrix.Elements[3][0] * Scalar;
    Result.Elements[3][1] = Matrix.Elements[3][1] * Scalar;
    Result.Elements[3][2] = Matrix.Elements[3][2] * Scalar;
    Result.Elements[3][3] = Matrix.Elements[3][3] * Scalar;
#endif

    return Result;
}

COVERAGE(MulM4V4, 1)
static inline Vec4 MulM4V4(Mat4 Matrix, Vec4 Vector)
{
    ASSERT_COVERED(MulM4V4);
    return LinearCombineV4M4(Vector, Matrix);
}

COVERAGE(DivM4F, 1)
static inline Mat4 DivM4F(Mat4 Matrix, float Scalar)
{
    ASSERT_COVERED(DivM4F);

    Mat4 Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSEScalar = _mm_set1_ps(Scalar);
    Result.Columns[0].SSE = _mm_div_ps(Matrix.Columns[0].SSE, SSEScalar);
    Result.Columns[1].SSE = _mm_div_ps(Matrix.Columns[1].SSE, SSEScalar);
    Result.Columns[2].SSE = _mm_div_ps(Matrix.Columns[2].SSE, SSEScalar);
    Result.Columns[3].SSE = _mm_div_ps(Matrix.Columns[3].SSE, SSEScalar);
#else
    Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar;
    Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar;
    Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar;
    Result.Elements[0][3] = Matrix.Elements[0][3] / Scalar;
    Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar;
    Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar;
    Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar;
    Result.Elements[1][3] = Matrix.Elements[1][3] / Scalar;
    Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar;
    Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar;
    Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar;
    Result.Elements[2][3] = Matrix.Elements[2][3] / Scalar;
    Result.Elements[3][0] = Matrix.Elements[3][0] / Scalar;
    Result.Elements[3][1] = Matrix.Elements[3][1] / Scalar;
    Result.Elements[3][2] = Matrix.Elements[3][2] / Scalar;
    Result.Elements[3][3] = Matrix.Elements[3][3] / Scalar;
#endif

    return Result;
}

COVERAGE(DeterminantM4, 1)
static inline float DeterminantM4(Mat4 Matrix) 
{
    ASSERT_COVERED(DeterminantM4);

    Vec3 C01 = Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ);
    Vec3 C23 = Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ);
    Vec3 B10 = SubV3(MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W));
    Vec3 B32 = SubV3(MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W));
    
    return DotV3(C01, B32) + DotV3(C23, B10);
}

COVERAGE(InvGeneralM4, 1)
static inline Mat4 InvGeneralM4(Mat4 Matrix) 
{
    ASSERT_COVERED(InvGeneralM4);

    Vec3 C01 = Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ);
    Vec3 C23 = Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ);
    Vec3 B10 = SubV3(MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W));
    Vec3 B32 = SubV3(MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W));
    
    float InvDeterminant = 1.0f / (DotV3(C01, B32) + DotV3(C23, B10));
    C01 = MulV3F(C01, InvDeterminant);
    C23 = MulV3F(C23, InvDeterminant);
    B10 = MulV3F(B10, InvDeterminant);
    B32 = MulV3F(B32, InvDeterminant);

    Mat4 Result;
    Result.Columns[0] = V4V(AddV3(Cross(Matrix.Columns[1].XYZ, B32), MulV3F(C23, Matrix.Columns[1].W)), -DotV3(Matrix.Columns[1].XYZ, C23));
    Result.Columns[1] = V4V(SubV3(Cross(B32, Matrix.Columns[0].XYZ), MulV3F(C23, Matrix.Columns[0].W)), +DotV3(Matrix.Columns[0].XYZ, C23));
    Result.Columns[2] = V4V(AddV3(Cross(Matrix.Columns[3].XYZ, B10), MulV3F(C01, Matrix.Columns[3].W)), -DotV3(Matrix.Columns[3].XYZ, C01));
    Result.Columns[3] = V4V(SubV3(Cross(B10, Matrix.Columns[2].XYZ), MulV3F(C01, Matrix.Columns[2].W)), +DotV3(Matrix.Columns[2].XYZ, C01));
        
    return TransposeM4(Result);
}

/*
 * Common graphics transformations
 */

COVERAGE(Orthographic_RH_NO, 1)
static inline Mat4 Orthographic_RH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
    ASSERT_COVERED(Orthographic_RH_NO);

    Mat4 Result = {0};

    Result.Elements[0][0] = 2.0f / (Right - Left);
    Result.Elements[1][1] = 2.0f / (Top - Bottom);
    Result.Elements[3][3] = 1.0f;

    Result.Elements[3][0] = (Left + Right) / (Left - Right);
    Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);

    Result.Elements[2][2] = 2.0f / (Near - Far);
    Result.Elements[3][2] = (Far + Near) / (Near - Far);

    return Result;
}

COVERAGE(Orthographic_RH_ZO, 1)
static inline Mat4 Orthographic_RH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
    ASSERT_COVERED(Orthographic_RH_ZO);

    Mat4 Result = {0};

    Result.Elements[0][0] = 2.0f / (Right - Left);
    Result.Elements[1][1] = 2.0f / (Top - Bottom);
    Result.Elements[3][3] = 1.0f;

    Result.Elements[3][0] = (Left + Right) / (Left - Right);
    Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top);

    Result.Elements[2][2] = 1.0f / (Near - Far);
    Result.Elements[3][2] = (Near) / (Near - Far);

    return Result;
}

COVERAGE(Orthographic_LH_NO, 1)
static inline Mat4 Orthographic_LH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
    ASSERT_COVERED(Orthographic_LH_NO);

    Mat4 Result = Orthographic_RH_NO(Left, Right, Bottom, Top, Near, Far);
    Result.Elements[2][2] = -Result.Elements[2][2];
    
    return Result;
}

COVERAGE(Orthographic_LH_ZO, 1)
static inline Mat4 Orthographic_LH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far)
{
    ASSERT_COVERED(Orthographic_LH_ZO);

    Mat4 Result = Orthographic_RH_ZO(Left, Right, Bottom, Top, Near, Far);
    Result.Elements[2][2] = -Result.Elements[2][2];
    
    return Result;
}

COVERAGE(InvOrthographic, 1) 
static inline Mat4 InvOrthographic(Mat4 OrthoMatrix)
{
    ASSERT_COVERED(InvOrthographic);

    Mat4 Result = {0};
    Result.Elements[0][0] = 1.0f / OrthoMatrix.Elements[0][0];
    Result.Elements[1][1] = 1.0f / OrthoMatrix.Elements[1][1];
    Result.Elements[2][2] = 1.0f / OrthoMatrix.Elements[2][2];
    Result.Elements[3][3] = 1.0f;
    
    Result.Elements[3][0] = -OrthoMatrix.Elements[3][0] * Result.Elements[0][0];
    Result.Elements[3][1] = -OrthoMatrix.Elements[3][1] * Result.Elements[1][1];
    Result.Elements[3][2] = -OrthoMatrix.Elements[3][2] * Result.Elements[2][2];

    return Result;
}

COVERAGE(Perspective_RH_NO, 1)
static inline Mat4 Perspective_RH_NO(float FOV, float AspectRatio, float Near, float Far)
{
    ASSERT_COVERED(Perspective_RH_NO);

    Mat4 Result = {0};

    // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml

    float Cotangent = 1.0f / TanF(FOV / 2.0f);
    Result.Elements[0][0] = Cotangent / AspectRatio;
    Result.Elements[1][1] = Cotangent;
    Result.Elements[2][3] = -1.0f;

    Result.Elements[2][2] = (Near + Far) / (Near - Far);
    Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
    
    return Result;
}

COVERAGE(Perspective_RH_ZO, 1)
static inline Mat4 Perspective_RH_ZO(float FOV, float AspectRatio, float Near, float Far)
{
    ASSERT_COVERED(Perspective_RH_ZO);

    Mat4 Result = {0};

    // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml

    float Cotangent = 1.0f / TanF(FOV / 2.0f);
    Result.Elements[0][0] = Cotangent / AspectRatio;
    Result.Elements[1][1] = Cotangent;
    Result.Elements[2][3] = -1.0f;

    Result.Elements[2][2] = (Far) / (Near - Far);
    Result.Elements[3][2] = (Near * Far) / (Near - Far);

    return Result;
}

COVERAGE(Perspective_LH_NO, 1)
static inline Mat4 Perspective_LH_NO(float FOV, float AspectRatio, float Near, float Far)
{ 
    ASSERT_COVERED(Perspective_LH_NO);

    Mat4 Result = Perspective_RH_NO(FOV, AspectRatio, Near, Far);
    Result.Elements[2][2] = -Result.Elements[2][2];
    Result.Elements[2][3] = -Result.Elements[2][3];
    
    return Result;
}

COVERAGE(Perspective_LH_ZO, 1)
static inline Mat4 Perspective_LH_ZO(float FOV, float AspectRatio, float Near, float Far)
{ 
    ASSERT_COVERED(Perspective_LH_ZO);

    Mat4 Result = Perspective_RH_ZO(FOV, AspectRatio, Near, Far);
    Result.Elements[2][2] = -Result.Elements[2][2];
    Result.Elements[2][3] = -Result.Elements[2][3];
    
    return Result;
}

COVERAGE(InvPerspective, 1)
static inline Mat4 InvPerspective(Mat4 PerspectiveMatrix) 
{
    ASSERT_COVERED(InvPerspective);

    Mat4 Result = {0};
    Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0];
    Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1];
    Result.Elements[2][2] = 0.0f;

    Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2];
    Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * Result.Elements[2][3];
    Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3];

    return Result;
}

COVERAGE(Translate, 1)
static inline Mat4 Translate(Vec3 Translation)
{
    ASSERT_COVERED(Translate);

    Mat4 Result = M4D(1.0f);
    Result.Elements[3][0] = Translation.X;
    Result.Elements[3][1] = Translation.Y;
    Result.Elements[3][2] = Translation.Z;

    return Result;
}

COVERAGE(InvTranslate, 1)
static inline Mat4 InvTranslate(Mat4 TranslationMatrix)
{
    ASSERT_COVERED(InvTranslate);

    Mat4 Result = TranslationMatrix;
    Result.Elements[3][0] = -Result.Elements[3][0];
    Result.Elements[3][1] = -Result.Elements[3][1];
    Result.Elements[3][2] = -Result.Elements[3][2];

    return Result;
}

COVERAGE(Rotate_RH, 1)
static inline Mat4 Rotate_RH(float Angle, Vec3 Axis)
{
    ASSERT_COVERED(Rotate_RH);

    Mat4 Result = M4D(1.0f);

    Axis = NormV3(Axis);

    float SinTheta = SinF(Angle);
    float CosTheta = CosF(Angle);
    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(Rotate_LH, 1)
static inline Mat4 Rotate_LH(float Angle, Vec3 Axis)
{
    ASSERT_COVERED(Rotate_LH);
    /* NOTE(lcf): Matrix will be inverse/transpose of RH. */
    return Rotate_RH(-Angle, Axis);
}

COVERAGE(InvRotate, 1)
static inline Mat4 InvRotate(Mat4 RotationMatrix)
{
    ASSERT_COVERED(InvRotate);
    return TransposeM4(RotationMatrix);
}

COVERAGE(Scale, 1)
static inline Mat4 Scale(Vec3 Scale)
{
    ASSERT_COVERED(Scale);

    Mat4 Result = M4D(1.0f);
    Result.Elements[0][0] = Scale.X;
    Result.Elements[1][1] = Scale.Y;
    Result.Elements[2][2] = Scale.Z;

    return Result;
}

COVERAGE(InvScale, 1)
static inline Mat4 InvScale(Mat4 ScaleMatrix) 
{
    ASSERT_COVERED(InvScale);

    Mat4 Result = ScaleMatrix;
    Result.Elements[0][0] = 1.0f / Result.Elements[0][0];
    Result.Elements[1][1] = 1.0f / Result.Elements[1][1];
    Result.Elements[2][2] = 1.0f / Result.Elements[2][2];

    return Result;
}

static inline Mat4 _LookAt(Vec3 F,  Vec3 S, Vec3 U,  Vec3 Eye)
{
    Mat4 Result;

    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] = -DotV3(S, Eye);
    Result.Elements[3][1] = -DotV3(U, Eye);
    Result.Elements[3][2] = DotV3(F, Eye);
    Result.Elements[3][3] = 1.0f;

    return Result;
}

COVERAGE(LookAt_RH, 1)
static inline Mat4 LookAt_RH(Vec3 Eye, Vec3 Center, Vec3 Up)
{
    ASSERT_COVERED(LookAt_RH);

    Vec3 F = NormV3(SubV3(Center, Eye));
    Vec3 S = NormV3(Cross(F, Up));
    Vec3 U = Cross(S, F);

    return _LookAt(F, S, U, Eye);
}

COVERAGE(LookAt_LH, 1)
static inline Mat4 LookAt_LH(Vec3 Eye, Vec3 Center, Vec3 Up)
{
    ASSERT_COVERED(LookAt_LH);

    Vec3 F = NormV3(SubV3(Eye, Center));
    Vec3 S = NormV3(Cross(F, Up));
    Vec3 U = Cross(S, F);

    return _LookAt(F, S, U, Eye);
}

COVERAGE(InvLookAt, 1)
static inline Mat4 InvLookAt(Mat4 Matrix)
{
    ASSERT_COVERED(InvLookAt);
    Mat4 Result;

    Mat3 Rotation = {0};
    Rotation.Columns[0] = Matrix.Columns[0].XYZ;
    Rotation.Columns[1] = Matrix.Columns[1].XYZ;
    Rotation.Columns[2] = Matrix.Columns[2].XYZ;
    Rotation = TransposeM3(Rotation);

    Result.Columns[0] = V4V(Rotation.Columns[0], 0.0f);
    Result.Columns[1] = V4V(Rotation.Columns[1], 0.0f);
    Result.Columns[2] = V4V(Rotation.Columns[2], 0.0f);
    Result.Columns[3] = MulV4F(Matrix.Columns[3], -1.0f);
    Result.Elements[3][0] = -1.0f * Matrix.Elements[3][0] /
        (Rotation.Elements[0][0] + Rotation.Elements[0][1] + Rotation.Elements[0][2]);
    Result.Elements[3][1] = -1.0f * Matrix.Elements[3][1] /
        (Rotation.Elements[1][0] + Rotation.Elements[1][1] + Rotation.Elements[1][2]);
    Result.Elements[3][2] = -1.0f * Matrix.Elements[3][2] /
        (Rotation.Elements[2][0] + Rotation.Elements[2][1] + Rotation.Elements[2][2]);
    Result.Elements[3][3] = 1.0f;

    return Result;
}

/*
 * Quaternion operations
 */

COVERAGE(Q, 1)
static inline Quat Make_Q(float X, float Y, float Z, float W)
{
    ASSERT_COVERED(Q);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_setr_ps(X, Y, Z, W);
#else
    Result.X = X;
    Result.Y = Y;
    Result.Z = Z;
    Result.W = W;
#endif

    return Result;
}

COVERAGE(QV4, 1)
static inline Quat QV4(Vec4 Vector)
{
    ASSERT_COVERED(QV4);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = Vector.SSE;
#else
    Result.X = Vector.X;
    Result.Y = Vector.Y;
    Result.Z = Vector.Z;
    Result.W = Vector.W;
#endif

    return Result;
}

COVERAGE(AddQ, 1)
static inline Quat AddQ(Quat Left, Quat Right)
{
    ASSERT_COVERED(AddQ);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_add_ps(Left.SSE, Right.SSE);
#else

    Result.X = Left.X + Right.X;
    Result.Y = Left.Y + Right.Y;
    Result.Z = Left.Z + Right.Z;
    Result.W = Left.W + Right.W;
#endif

    return Result;
}

COVERAGE(SubQ, 1)
static inline Quat SubQ(Quat Left, Quat Right)
{
    ASSERT_COVERED(SubQ);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE);
#else
    Result.X = Left.X - Right.X;
    Result.Y = Left.Y - Right.Y;
    Result.Z = Left.Z - Right.Z;
    Result.W = Left.W - Right.W;
#endif

    return Result;
}

COVERAGE(MulQ, 1)
static inline Quat MulQ(Quat Left, Quat Right)
{
    ASSERT_COVERED(MulQ);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.f, -0.f, 0.f, -0.f));
    __m128 SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(0, 1, 2, 3));
    __m128 SSEResultThree = _mm_mul_ps(SSEResultTwo, SSEResultOne);

    SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(1, 1, 1, 1)) , _mm_setr_ps(0.f, 0.f, -0.f, -0.f));
    SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(1, 0, 3, 2));
    SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));

    SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.f, 0.f, 0.f, -0.f));
    SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(2, 3, 0, 1));
    SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));

    SSEResultOne = _mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(3, 3, 3, 3));
    SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(3, 2, 1, 0));
    Result.SSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne));
#else
    Result.X =  Right.Elements[3] * +Left.Elements[0];
    Result.Y =  Right.Elements[2] * -Left.Elements[0];
    Result.Z =  Right.Elements[1] * +Left.Elements[0];
    Result.W =  Right.Elements[0] * -Left.Elements[0];

    Result.X += Right.Elements[2] * +Left.Elements[1];
    Result.Y += Right.Elements[3] * +Left.Elements[1];
    Result.Z += Right.Elements[0] * -Left.Elements[1];
    Result.W += Right.Elements[1] * -Left.Elements[1];
    
    Result.X += Right.Elements[1] * -Left.Elements[2];
    Result.Y += Right.Elements[0] * +Left.Elements[2];
    Result.Z += Right.Elements[3] * +Left.Elements[2];
    Result.W += Right.Elements[2] * -Left.Elements[2];

    Result.X += Right.Elements[0] * +Left.Elements[3];
    Result.Y += Right.Elements[1] * +Left.Elements[3];
    Result.Z += Right.Elements[2] * +Left.Elements[3];
    Result.W += Right.Elements[3] * +Left.Elements[3];
#endif

    return Result;
}

COVERAGE(MulQF, 1)
static inline Quat MulQF(Quat Left, float Multiplicative)
{
    ASSERT_COVERED(MulQF);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 Scalar = _mm_set1_ps(Multiplicative);
    Result.SSE = _mm_mul_ps(Left.SSE, Scalar);
#else
    Result.X = Left.X * Multiplicative;
    Result.Y = Left.Y * Multiplicative;
    Result.Z = Left.Z * Multiplicative;
    Result.W = Left.W * Multiplicative;
#endif

    return Result;
}

COVERAGE(DivQF, 1)
static inline Quat DivQF(Quat Left, float Divnd)
{
    ASSERT_COVERED(DivQF);

    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 Scalar = _mm_set1_ps(Divnd);
    Result.SSE = _mm_div_ps(Left.SSE, Scalar);
#else
    Result.X = Left.X / Divnd;
    Result.Y = Left.Y / Divnd;
    Result.Z = Left.Z / Divnd;
    Result.W = Left.W / Divnd;
#endif

    return Result;
}

COVERAGE(DotQ, 1)
static inline float DotQ(Quat Left, Quat Right)
{
    ASSERT_COVERED(DotQ);

    float Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE);
    __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1));
    SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
    SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3));
    SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo);
    _mm_store_ss(&Result, SSEResultOne);
#else
    Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W));
#endif

    return Result;
}

COVERAGE(InvQ, 1)
static inline Quat InvQ(Quat Left)
{
    ASSERT_COVERED(InvQ);
    
    Quat Result;
    Result.X = -Left.X;
    Result.Y = -Left.Y;
    Result.Z = -Left.Z;
    Result.W = Left.W;

    return DivQF(Result, (DotQ(Left, Left)));
}

COVERAGE(NormQ, 1)
static inline Quat NormQ(Quat q)
{
    ASSERT_COVERED(NormQ);

    /* NOTE(lcf): Take advantage of SSE implementation in NormV4 */
    Vec4 v = {q.X, q.Y, q.Z, q.W};
    v = NormV4(v);

    Quat Result = {v.X, v.Y, v.Z, v.W};

    return Result;
}

static inline Quat _MixQ(Quat Left, float MixLeft, Quat Right, float MixRight) {
    Quat Result;

#ifdef HANDMADE_MATH__USE_SSE
    __m128 ScalarLeft = _mm_set1_ps(MixLeft);
    __m128 ScalarRight = _mm_set1_ps(MixRight);
    __m128 SSEResultOne = _mm_mul_ps(Left.SSE, ScalarLeft);
    __m128 SSEResultTwo = _mm_mul_ps(Right.SSE, ScalarRight);
    Result.SSE = _mm_add_ps(SSEResultOne, SSEResultTwo);
#else
    Result.X = Left.X*MixLeft + Right.X*MixRight;
    Result.Y = Left.Y*MixLeft + Right.Y*MixRight;
    Result.Z = Left.Z*MixLeft + Right.Z*MixRight;
    Result.W = Left.W*MixLeft + Right.W*MixRight;
#endif

    return Result;
}

COVERAGE(NLerp, 1)
static inline Quat NLerp(Quat Left, float Time, Quat Right)
{
    ASSERT_COVERED(NLerp);

    Quat Result = _MixQ(Left, 1.0f-Time, Right, Time);
    Result = NormQ(Result);

    return Result;
}

COVERAGE(SLerp, 1)
static inline Quat SLerp(Quat Left, float Time, Quat Right)
{
    ASSERT_COVERED(SLerp);

    Quat Result;

    float Cos_Theta = DotQ(Left, Right);

    if (Cos_Theta < 0.0f) { /* NOTE(lcf): Take shortest path on Hyper-sphere */
        Cos_Theta = -Cos_Theta;
        Right = Make_Q(-Right.X, -Right.Y, -Right.Z, -Right.W);
    }
    
    /* NOTE(lcf): Use Normalized Linear interpolation when vectors are roughly not L.I. */
    if (Cos_Theta > 0.9995f) {
        Result = NLerp(Left, Time, Right);
    } else {
        float Angle = ACosF(Cos_Theta);
        float MixLeft = SinF((1.0f - Time) * Angle);
        float MixRight = SinF(Time * Angle);

        Result = _MixQ(Left, MixLeft, Right, MixRight);
        Result = NormQ(Result);
    }
    
    return Result;
}

COVERAGE(QToM4, 1)
static inline Mat4 QToM4(Quat Left)
{
    ASSERT_COVERED(QToM4);

    Mat4 Result;

    Quat NormalizedQ = NormQ(Left);

    float XX, YY, ZZ,
          XY, XZ, YZ,
          WX, WY, WZ;

    XX = NormalizedQ.X * NormalizedQ.X;
    YY = NormalizedQ.Y * NormalizedQ.Y;
    ZZ = NormalizedQ.Z * NormalizedQ.Z;
    XY = NormalizedQ.X * NormalizedQ.Y;
    XZ = NormalizedQ.X * NormalizedQ.Z;
    YZ = NormalizedQ.Y * NormalizedQ.Z;
    WX = NormalizedQ.W * NormalizedQ.X;
    WY = NormalizedQ.W * NormalizedQ.Y;
    WZ = NormalizedQ.W * NormalizedQ.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(M4ToQ_RH, 4)
static inline Quat M4ToQ_RH(Mat4 M)
{
    float T;
    Quat Q;

    if (M.Elements[2][2] < 0.0f) {
        if (M.Elements[0][0] > M.Elements[1][1]) {
            ASSERT_COVERED(M4ToQ_RH);

            T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2];
            Q = Make_Q(
                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(M4ToQ_RH);

            T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2];
            Q = Make_Q(
                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(M4ToQ_RH);

            T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2];
            Q = Make_Q(
                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(M4ToQ_RH);

            T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2];
            Q = Make_Q(
                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 = MulQF(Q, 0.5f / SqrtF(T));

    return Q;
}

COVERAGE(M4ToQ_LH, 4)
static inline Quat M4ToQ_LH(Mat4 M)
{
    float T;
    Quat Q;

    if (M.Elements[2][2] < 0.0f) {
        if (M.Elements[0][0] > M.Elements[1][1]) {
            ASSERT_COVERED(M4ToQ_LH);

            T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2];
            Q = Make_Q(
                T,
                M.Elements[0][1] + M.Elements[1][0],
                M.Elements[2][0] + M.Elements[0][2],
                M.Elements[2][1] - M.Elements[1][2]
            );
        } else {
            ASSERT_COVERED(M4ToQ_LH);

            T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2];
            Q = Make_Q(
                M.Elements[0][1] + M.Elements[1][0],
                T,
                M.Elements[1][2] + M.Elements[2][1],
                M.Elements[0][2] - M.Elements[2][0]
            );
        }
    } else {
        if (M.Elements[0][0] < -M.Elements[1][1]) {
            ASSERT_COVERED(M4ToQ_LH);

            T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2];
            Q = Make_Q(
                M.Elements[2][0] + M.Elements[0][2],
                M.Elements[1][2] + M.Elements[2][1],
                T,
                M.Elements[1][0] - M.Elements[0][1]
            );
        } else {
            ASSERT_COVERED(M4ToQ_LH);

            T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2];
            Q = Make_Q(
                M.Elements[2][1] - M.Elements[1][2],
                M.Elements[0][2] - M.Elements[2][0],
                M.Elements[1][0] - M.Elements[0][2],
                T
            );
        }
    }

    Q = MulQF(Q, 0.5f / SqrtF(T));

    return Q;
}


COVERAGE(QFromAxisAngle_RH, 1)
static inline Quat QFromAxisAngle_RH(Vec3 Axis, float AngleOfRotation)
{
    ASSERT_COVERED(QFromAxisAngle_RH);

    Quat Result;

    Vec3 AxisNormalized = NormV3(Axis);
    float SineOfRotation = SinF(AngleOfRotation / 2.0f);

    Result.XYZ = MulV3F(AxisNormalized, SineOfRotation);
    Result.W = CosF(AngleOfRotation / 2.0f);

    return Result;
}

COVERAGE(QFromAxisAngle_LH, 1)
static inline Quat QFromAxisAngle_LH(Vec3 Axis, float AngleOfRotation)
{
    ASSERT_COVERED(QFromAxisAngle_LH);

    return QFromAxisAngle_RH(Axis, -AngleOfRotation);
}


#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

COVERAGE(LenV2CPP, 1)
static inline float Len(Vec2 A)
{
    ASSERT_COVERED(LenV2CPP);
    return LenV2(A);
}

COVERAGE(LenV3CPP, 1)
static inline float Len(Vec3 A)
{
    ASSERT_COVERED(LenV3CPP);
    return LenV3(A);
}

COVERAGE(LenV4CPP, 1)
static inline float Len(Vec4 A)
{
    ASSERT_COVERED(LenV4CPP);
    return LenV4(A);
}

COVERAGE(LenSqrV2CPP, 1)
static inline float LenSqr(Vec2 A)
{
    ASSERT_COVERED(LenSqrV2CPP);
    return LenSqrV2(A);
}

COVERAGE(LenSqrV3CPP, 1)
static inline float LenSqr(Vec3 A)
{
    ASSERT_COVERED(LenSqrV3CPP);
    return LenSqrV3(A);
}

COVERAGE(LenSqrV4CPP, 1)
static inline float LenSqr(Vec4 A)
{
    ASSERT_COVERED(LenSqrV4CPP);
    return LenSqrV4(A);
}

COVERAGE(NormV2CPP, 1)
static inline Vec2 Norm(Vec2 A)
{
    ASSERT_COVERED(NormV2CPP);
    return NormV2(A);
}

COVERAGE(NormV3CPP, 1)
static inline Vec3 Norm(Vec3 A)
{
    ASSERT_COVERED(NormV3CPP);
    return NormV3(A);
}

COVERAGE(NormV4CPP, 1)
static inline Vec4 Norm(Vec4 A)
{
    ASSERT_COVERED(NormV4CPP);
    return NormV4(A);
}

COVERAGE(NormQCPP, 1)
static inline Quat Norm(Quat A)
{
    ASSERT_COVERED(NormQCPP);
    return NormQ(A);
}

COVERAGE(DotV2CPP, 1)
static inline float Dot(Vec2 Left, Vec2 VecTwo)
{
    ASSERT_COVERED(DotV2CPP);
    return DotV2(Left, VecTwo);
}

COVERAGE(DotV3CPP, 1)
static inline float Dot(Vec3 Left, Vec3 VecTwo)
{
    ASSERT_COVERED(DotV3CPP);
    return DotV3(Left, VecTwo);
}

COVERAGE(DotV4CPP, 1)
static inline float Dot(Vec4 Left, Vec4 VecTwo)
{
    ASSERT_COVERED(DotV4CPP);
    return DotV4(Left, VecTwo);
}
 
COVERAGE(LerpV2CPP, 1)
static inline Vec2 Lerp(Vec2 Left, float Time, Vec2 Right) 
{
    ASSERT_COVERED(LerpV2CPP);
    return LerpV2(Left, Time, Right);
}

COVERAGE(LerpV3CPP, 1)
static inline Vec3 Lerp(Vec3 Left, float Time, Vec3 Right) 
{
    ASSERT_COVERED(LerpV3CPP);
    return LerpV3(Left, Time, Right);
}

COVERAGE(LerpV4CPP, 1)
static inline Vec4 Lerp(Vec4 Left, float Time, Vec4 Right) 
{
    ASSERT_COVERED(LerpV4CPP);
    return LerpV4(Left, Time, Right);
}

COVERAGE(TransposeM2CPP, 1)
static inline Mat2 Transpose(Mat2 Matrix)
{
    ASSERT_COVERED(TransposeM2CPP);
    return TransposeM2(Matrix);
}

COVERAGE(TransposeM3CPP, 1)
static inline Mat3 Transpose(Mat3 Matrix)
{
    ASSERT_COVERED(TransposeM3CPP);
    return TransposeM3(Matrix);
}

COVERAGE(TransposeM4CPP, 1)
static inline Mat4 Transpose(Mat4 Matrix)
{
    ASSERT_COVERED(TransposeM4CPP);
    return TransposeM4(Matrix);
}

COVERAGE(DeterminantM2CPP, 1)
static inline float Determinant(Mat2 Matrix)
{
    ASSERT_COVERED(DeterminantM2CPP);
    return DeterminantM2(Matrix);
}

COVERAGE(DeterminantM3CPP, 1)
static inline float Determinant(Mat3 Matrix)
{
    ASSERT_COVERED(DeterminantM3CPP);
    return DeterminantM3(Matrix);
}

COVERAGE(DeterminantM4CPP, 1)
static inline float Determinant(Mat4 Matrix)
{
    ASSERT_COVERED(DeterminantM4CPP);
    return DeterminantM4(Matrix);
}

COVERAGE(InvGeneralM2CPP, 1)
static inline Mat2 InvGeneral(Mat2 Matrix)
{
    ASSERT_COVERED(InvGeneralM2CPP);
    return InvGeneralM2(Matrix);
}

COVERAGE(InvGeneralM3CPP, 1)
static inline Mat3 InvGeneral(Mat3 Matrix)
{
    ASSERT_COVERED(InvGeneralM3CPP);
    return InvGeneralM3(Matrix);
}

COVERAGE(InvGeneralM4CPP, 1)
static inline Mat4 InvGeneral(Mat4 Matrix)
{
    ASSERT_COVERED(InvGeneralM4CPP);
    return InvGeneralM4(Matrix);
}

COVERAGE(DotQCPP, 1)
static inline float Dot(Quat QuatOne, Quat QuatTwo)
{
    ASSERT_COVERED(DotQCPP);
    return DotQ(QuatOne, QuatTwo);
}

COVERAGE(AddV2CPP, 1)
static inline Vec2 Add(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(AddV2CPP);
    return AddV2(Left, Right);
}

COVERAGE(AddV3CPP, 1)
static inline Vec3 Add(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(AddV3CPP);
    return AddV3(Left, Right);
}

COVERAGE(AddV4CPP, 1)
static inline Vec4 Add(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(AddV4CPP);
    return AddV4(Left, Right);
}

COVERAGE(AddM2CPP, 1)
static inline Mat2 Add(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(AddM2CPP);
    return AddM2(Left, Right);
}

COVERAGE(AddM3CPP, 1)
static inline Mat3 Add(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(AddM3CPP);
    return AddM3(Left, Right);
}

COVERAGE(AddM4CPP, 1)
static inline Mat4 Add(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(AddM4CPP);
    return AddM4(Left, Right);
}

COVERAGE(AddQCPP, 1)
static inline Quat Add(Quat Left, Quat Right)
{
    ASSERT_COVERED(AddQCPP);
    return AddQ(Left, Right);
}

COVERAGE(SubV2CPP, 1)
static inline Vec2 Sub(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(SubV2CPP);
    return SubV2(Left, Right);
}

COVERAGE(SubV3CPP, 1)
static inline Vec3 Sub(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(SubV3CPP);
    return SubV3(Left, Right);
}

COVERAGE(SubV4CPP, 1)
static inline Vec4 Sub(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(SubV4CPP);
    return SubV4(Left, Right);
}

COVERAGE(SubM2CPP, 1)
static inline Mat2 Sub(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(SubM2CPP);
    return SubM2(Left, Right);
}

COVERAGE(SubM3CPP, 1)
static inline Mat3 Sub(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(SubM3CPP);
    return SubM3(Left, Right);
}

COVERAGE(SubM4CPP, 1)
static inline Mat4 Sub(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(SubM4CPP);
    return SubM4(Left, Right);
}

COVERAGE(SubQCPP, 1)
static inline Quat Sub(Quat Left, Quat Right)
{
    ASSERT_COVERED(SubQCPP);
    return SubQ(Left, Right);
}

COVERAGE(MulV2CPP, 1)
static inline Vec2 Mul(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(MulV2CPP);
    return MulV2(Left, Right);
}

COVERAGE(MulV2FCPP, 1)
static inline Vec2 Mul(Vec2 Left, float Right)
{
    ASSERT_COVERED(MulV2FCPP);
    return MulV2F(Left, Right);
}

COVERAGE(MulV3CPP, 1)
static inline Vec3 Mul(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(MulV3CPP);
    return MulV3(Left, Right);
}

COVERAGE(MulV3FCPP, 1)
static inline Vec3 Mul(Vec3 Left, float Right)
{
    ASSERT_COVERED(MulV3FCPP);
    return MulV3F(Left, Right);
}

COVERAGE(MulV4CPP, 1)
static inline Vec4 Mul(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(MulV4CPP);
    return MulV4(Left, Right);
}

COVERAGE(MulV4FCPP, 1)
static inline Vec4 Mul(Vec4 Left, float Right)
{
    ASSERT_COVERED(MulV4FCPP);
    return MulV4F(Left, Right);
}

COVERAGE(MulM2CPP, 1)
static inline Mat2 Mul(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(MulM2CPP);
    return MulM2(Left, Right);
}

COVERAGE(MulM3CPP, 1)
static inline Mat3 Mul(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(MulM3CPP);
    return MulM3(Left, Right);
}

COVERAGE(MulM4CPP, 1)
static inline Mat4 Mul(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(MulM4CPP);
    return MulM4(Left, Right);
}

COVERAGE(MulM2FCPP, 1)
static inline Mat2 Mul(Mat2 Left, float Right)
{
    ASSERT_COVERED(MulM2FCPP);
    return MulM2F(Left, Right);
}

COVERAGE(MulM3FCPP, 1)
static inline Mat3 Mul(Mat3 Left, float Right)
{
    ASSERT_COVERED(MulM3FCPP);
    return MulM3F(Left, Right);
}

COVERAGE(MulM4FCPP, 1)
static inline Mat4 Mul(Mat4 Left, float Right)
{
    ASSERT_COVERED(MulM4FCPP);
    return MulM4F(Left, Right);
}

COVERAGE(MulM2V2CPP, 1)
static inline Vec2 Mul(Mat2 Matrix, Vec2 Vector)
{
    ASSERT_COVERED(MulM2V2CPP);
    return MulM2V2(Matrix, Vector);
}

COVERAGE(MulM3V3CPP, 1)
static inline Vec3 Mul(Mat3 Matrix, Vec3 Vector)
{
    ASSERT_COVERED(MulM3V3CPP);
    return MulM3V3(Matrix, Vector);
}

COVERAGE(MulM4V4CPP, 1)
static inline Vec4 Mul(Mat4 Matrix, Vec4 Vector)
{
    ASSERT_COVERED(MulM4V4CPP);
    return MulM4V4(Matrix, Vector);
}

COVERAGE(MulQCPP, 1)
static inline Quat Mul(Quat Left, Quat Right)
{
    ASSERT_COVERED(MulQCPP);
    return MulQ(Left, Right);
}

COVERAGE(MulQFCPP, 1)
static inline Quat Mul(Quat Left, float Right)
{
    ASSERT_COVERED(MulQFCPP);
    return MulQF(Left, Right);
}

COVERAGE(DivV2CPP, 1)
static inline Vec2 Div(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(DivV2CPP);
    return DivV2(Left, Right);
}

COVERAGE(DivV2FCPP, 1)
static inline Vec2 Div(Vec2 Left, float Right)
{
    ASSERT_COVERED(DivV2FCPP);
    return DivV2F(Left, Right);
}

COVERAGE(DivV3CPP, 1)
static inline Vec3 Div(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(DivV3CPP);
    return DivV3(Left, Right);
}

COVERAGE(DivV3FCPP, 1)
static inline Vec3 Div(Vec3 Left, float Right)
{
    ASSERT_COVERED(DivV3FCPP);
    return DivV3F(Left, Right);
}

COVERAGE(DivV4CPP, 1)
static inline Vec4 Div(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(DivV4CPP);
    return DivV4(Left, Right);
}

COVERAGE(DivV4FCPP, 1)
static inline Vec4 Div(Vec4 Left, float Right)
{
    ASSERT_COVERED(DivV4FCPP);
    return DivV4F(Left, Right);
}

COVERAGE(DivM2FCPP, 1)
static inline Mat2 Div(Mat2 Left, float Right)
{
    ASSERT_COVERED(DivM2FCPP);
    return DivM2F(Left, Right);
}

COVERAGE(DivM3FCPP, 1)
static inline Mat3 Div(Mat3 Left, float Right)
{
    ASSERT_COVERED(DivM3FCPP);
    return DivM3F(Left, Right);
}

COVERAGE(DivM4FCPP, 1)
static inline Mat4 Div(Mat4 Left, float Right)
{
    ASSERT_COVERED(DivM4FCPP);
    return DivM4F(Left, Right);
}

COVERAGE(DivQFCPP, 1)
static inline Quat Div(Quat Left, float Right)
{
    ASSERT_COVERED(DivQFCPP);
    return DivQF(Left, Right);
}

COVERAGE(EqV2CPP, 1)
static inline Bool Eq(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(EqV2CPP);
    return EqV2(Left, Right);
}

COVERAGE(EqV3CPP, 1)
static inline Bool Eq(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(EqV3CPP);
    return EqV3(Left, Right);
}

COVERAGE(EqV4CPP, 1)
static inline Bool Eq(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(EqV4CPP);
    return EqV4(Left, Right);
}

COVERAGE(AddV2Op, 1)
static inline Vec2 operator+(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(AddV2Op);
    return AddV2(Left, Right);
}

COVERAGE(AddV3Op, 1)
static inline Vec3 operator+(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(AddV3Op);
    return AddV3(Left, Right);
}

COVERAGE(AddV4Op, 1)
static inline Vec4 operator+(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(AddV4Op);
    return AddV4(Left, Right);
}

COVERAGE(AddM2Op, 1)
static inline Mat2 operator+(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(AddM2Op);
    return AddM2(Left, Right);
}

COVERAGE(AddM3Op, 1)
static inline Mat3 operator+(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(AddM3Op);
    return AddM3(Left, Right);
}

COVERAGE(AddM4Op, 1)
static inline Mat4 operator+(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(AddM4Op);
    return AddM4(Left, Right);
}

COVERAGE(AddQOp, 1)
static inline Quat operator+(Quat Left, Quat Right)
{
    ASSERT_COVERED(AddQOp);
    return AddQ(Left, Right);
}

COVERAGE(SubV2Op, 1)
static inline Vec2 operator-(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(SubV2Op);
    return SubV2(Left, Right);
}

COVERAGE(SubV3Op, 1)
static inline Vec3 operator-(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(SubV3Op);
    return SubV3(Left, Right);
}

COVERAGE(SubV4Op, 1)
static inline Vec4 operator-(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(SubV4Op);
    return SubV4(Left, Right);
}

COVERAGE(SubM2Op, 1)
static inline Mat2 operator-(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(SubM2Op);
    return SubM2(Left, Right);
}

COVERAGE(SubM3Op, 1)
static inline Mat3 operator-(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(SubM3Op);
    return SubM3(Left, Right);
}

COVERAGE(SubM4Op, 1)
static inline Mat4 operator-(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(SubM4Op);
    return SubM4(Left, Right);
}

COVERAGE(SubQOp, 1)
static inline Quat operator-(Quat Left, Quat Right)
{
    ASSERT_COVERED(SubQOp);
    return SubQ(Left, Right);
}

COVERAGE(MulV2Op, 1)
static inline Vec2 operator*(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(MulV2Op);
    return MulV2(Left, Right);
}

COVERAGE(MulV3Op, 1)
static inline Vec3 operator*(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(MulV3Op);
    return MulV3(Left, Right);
}

COVERAGE(MulV4Op, 1)
static inline Vec4 operator*(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(MulV4Op);
    return MulV4(Left, Right);
}

COVERAGE(MulM2Op, 1)
static inline Mat2 operator*(Mat2 Left, Mat2 Right)
{
    ASSERT_COVERED(MulM2Op);
    return MulM2(Left, Right);
}

COVERAGE(MulM3Op, 1)
static inline Mat3 operator*(Mat3 Left, Mat3 Right)
{
    ASSERT_COVERED(MulM3Op);
    return MulM3(Left, Right);
}

COVERAGE(MulM4Op, 1)
static inline Mat4 operator*(Mat4 Left, Mat4 Right)
{
    ASSERT_COVERED(MulM4Op);
    return MulM4(Left, Right);
}

COVERAGE(MulQOp, 1)
static inline Quat operator*(Quat Left, Quat Right)
{
    ASSERT_COVERED(MulQOp);
    return MulQ(Left, Right);
}

COVERAGE(MulV2FOp, 1)
static inline Vec2 operator*(Vec2 Left, float Right)
{
    ASSERT_COVERED(MulV2FOp);
    return MulV2F(Left, Right);
}

COVERAGE(MulV3FOp, 1)
static inline Vec3 operator*(Vec3 Left, float Right)
{
    ASSERT_COVERED(MulV3FOp);
    return MulV3F(Left, Right);
}

COVERAGE(MulV4FOp, 1)
static inline Vec4 operator*(Vec4 Left, float Right)
{
    ASSERT_COVERED(MulV4FOp);
    return MulV4F(Left, Right);
}

COVERAGE(MulM2FOp, 1)
static inline Mat2 operator*(Mat2 Left, float Right)
{
    ASSERT_COVERED(MulM2FOp);
    return MulM2F(Left, Right);
}

COVERAGE(MulM3FOp, 1)
static inline Mat3 operator*(Mat3 Left, float Right)
{
    ASSERT_COVERED(MulM3FOp);
    return MulM3F(Left, Right);
}

COVERAGE(MulM4FOp, 1)
static inline Mat4 operator*(Mat4 Left, float Right)
{
    ASSERT_COVERED(MulM4FOp);
    return MulM4F(Left, Right);
}

COVERAGE(MulQFOp, 1)
static inline Quat operator*(Quat Left, float Right)
{
    ASSERT_COVERED(MulQFOp);
    return MulQF(Left, Right);
}

COVERAGE(MulV2FOpLeft, 1)
static inline Vec2 operator*(float Left, Vec2 Right)
{
    ASSERT_COVERED(MulV2FOpLeft);
    return MulV2F(Right, Left);
}

COVERAGE(MulV3FOpLeft, 1)
static inline Vec3 operator*(float Left, Vec3 Right)
{
    ASSERT_COVERED(MulV3FOpLeft);
    return MulV3F(Right, Left);
}

COVERAGE(MulV4FOpLeft, 1)
static inline Vec4 operator*(float Left, Vec4 Right)
{
    ASSERT_COVERED(MulV4FOpLeft);
    return MulV4F(Right, Left);
}

COVERAGE(MulM2FOpLeft, 1)
static inline Mat2 operator*(float Left, Mat2 Right)
{
    ASSERT_COVERED(MulM2FOpLeft);
    return MulM2F(Right, Left);
}

COVERAGE(MulM3FOpLeft, 1)
static inline Mat3 operator*(float Left, Mat3 Right)
{
    ASSERT_COVERED(MulM3FOpLeft);
    return MulM3F(Right, Left);
}

COVERAGE(MulM4FOpLeft, 1)
static inline Mat4 operator*(float Left, Mat4 Right)
{
    ASSERT_COVERED(MulM4FOpLeft);
    return MulM4F(Right, Left);
}

COVERAGE(MulQFOpLeft, 1)
static inline Quat operator*(float Left, Quat Right)
{
    ASSERT_COVERED(MulQFOpLeft);
    return MulQF(Right, Left);
}

COVERAGE(MulM2V2Op, 1)
static inline Vec2 operator*(Mat2 Matrix, Vec2 Vector)
{
    ASSERT_COVERED(MulM2V2Op);
    return MulM2V2(Matrix, Vector);
}

COVERAGE(MulM3V3Op, 1)
static inline Vec3 operator*(Mat3 Matrix, Vec3 Vector)
{
    ASSERT_COVERED(MulM3V3Op);
    return MulM3V3(Matrix, Vector);
}

COVERAGE(MulM4V4Op, 1)
static inline Vec4 operator*(Mat4 Matrix, Vec4 Vector)
{
    ASSERT_COVERED(MulM4V4Op);
    return MulM4V4(Matrix, Vector);
}

COVERAGE(DivV2Op, 1)
static inline Vec2 operator/(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(DivV2Op);
    return DivV2(Left, Right);
}

COVERAGE(DivV3Op, 1)
static inline Vec3 operator/(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(DivV3Op);
    return DivV3(Left, Right);
}

COVERAGE(DivV4Op, 1)
static inline Vec4 operator/(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(DivV4Op);
    return DivV4(Left, Right);
}

COVERAGE(DivV2FOp, 1)
static inline Vec2 operator/(Vec2 Left, float Right)
{
    ASSERT_COVERED(DivV2FOp);
    return DivV2F(Left, Right);
}

COVERAGE(DivV3FOp, 1)
static inline Vec3 operator/(Vec3 Left, float Right)
{
    ASSERT_COVERED(DivV3FOp);
    return DivV3F(Left, Right);
}

COVERAGE(DivV4FOp, 1)
static inline Vec4 operator/(Vec4 Left, float Right)
{
    ASSERT_COVERED(DivV4FOp);
    return DivV4F(Left, Right);
}

COVERAGE(DivM4FOp, 1)
static inline Mat4 operator/(Mat4 Left, float Right)
{
    ASSERT_COVERED(DivM4FOp);
    return DivM4F(Left, Right);
}

COVERAGE(DivM3FOp, 1)
static inline Mat3 operator/(Mat3 Left, float Right)
{
    ASSERT_COVERED(DivM3FOp);
    return DivM3F(Left, Right);
}

COVERAGE(DivM2FOp, 1)
static inline Mat2 operator/(Mat2 Left, float Right)
{
    ASSERT_COVERED(DivM2FOp);
    return DivM2F(Left, Right);
}

COVERAGE(DivQFOp, 1)
static inline Quat operator/(Quat Left, float Right)
{
    ASSERT_COVERED(DivQFOp);
    return DivQF(Left, Right);
}

COVERAGE(AddV2Assign, 1)
static inline Vec2 &operator+=(Vec2 &Left, Vec2 Right)
{
    ASSERT_COVERED(AddV2Assign);
    return Left = Left + Right;
}

COVERAGE(AddV3Assign, 1)
static inline Vec3 &operator+=(Vec3 &Left, Vec3 Right)
{
    ASSERT_COVERED(AddV3Assign);
    return Left = Left + Right;
}

COVERAGE(AddV4Assign, 1)
static inline Vec4 &operator+=(Vec4 &Left, Vec4 Right)
{
    ASSERT_COVERED(AddV4Assign);
    return Left = Left + Right;
}

COVERAGE(AddM2Assign, 1)
static inline Mat2 &operator+=(Mat2 &Left, Mat2 Right)
{
    ASSERT_COVERED(AddM2Assign);
    return Left = Left + Right;
}

COVERAGE(AddM3Assign, 1)
static inline Mat3 &operator+=(Mat3 &Left, Mat3 Right)
{
    ASSERT_COVERED(AddM3Assign);
    return Left = Left + Right;
}

COVERAGE(AddM4Assign, 1)
static inline Mat4 &operator+=(Mat4 &Left, Mat4 Right)
{
    ASSERT_COVERED(AddM4Assign);
    return Left = Left + Right;
}

COVERAGE(AddQAssign, 1)
static inline Quat &operator+=(Quat &Left, Quat Right)
{
    ASSERT_COVERED(AddQAssign);
    return Left = Left + Right;
}

COVERAGE(SubV2Assign, 1)
static inline Vec2 &operator-=(Vec2 &Left, Vec2 Right)
{
    ASSERT_COVERED(SubV2Assign);
    return Left = Left - Right;
}

COVERAGE(SubV3Assign, 1)
static inline Vec3 &operator-=(Vec3 &Left, Vec3 Right)
{
    ASSERT_COVERED(SubV3Assign);
    return Left = Left - Right;
}

COVERAGE(SubV4Assign, 1)
static inline Vec4 &operator-=(Vec4 &Left, Vec4 Right)
{
    ASSERT_COVERED(SubV4Assign);
    return Left = Left - Right;
}

COVERAGE(SubM2Assign, 1)
static inline Mat2 &operator-=(Mat2 &Left, Mat2 Right)
{
    ASSERT_COVERED(SubM2Assign);
    return Left = Left - Right;
}

COVERAGE(SubM3Assign, 1)
static inline Mat3 &operator-=(Mat3 &Left, Mat3 Right)
{
    ASSERT_COVERED(SubM3Assign);
    return Left = Left - Right;
}

COVERAGE(SubM4Assign, 1)
static inline Mat4 &operator-=(Mat4 &Left, Mat4 Right)
{
    ASSERT_COVERED(SubM4Assign);
    return Left = Left - Right;
}

COVERAGE(SubQAssign, 1)
static inline Quat &operator-=(Quat &Left, Quat Right)
{
    ASSERT_COVERED(SubQAssign);
    return Left = Left - Right;
}

COVERAGE(MulV2Assign, 1)
static inline Vec2 &operator*=(Vec2 &Left, Vec2 Right)
{
    ASSERT_COVERED(MulV2Assign);
    return Left = Left * Right;
}

COVERAGE(MulV3Assign, 1)
static inline Vec3 &operator*=(Vec3 &Left, Vec3 Right)
{
    ASSERT_COVERED(MulV3Assign);
    return Left = Left * Right;
}

COVERAGE(MulV4Assign, 1)
static inline Vec4 &operator*=(Vec4 &Left, Vec4 Right)
{
    ASSERT_COVERED(MulV4Assign);
    return Left = Left * Right;
}

COVERAGE(MulV2FAssign, 1)
static inline Vec2 &operator*=(Vec2 &Left, float Right)
{
    ASSERT_COVERED(MulV2FAssign);
    return Left = Left * Right;
}

COVERAGE(MulV3FAssign, 1)
static inline Vec3 &operator*=(Vec3 &Left, float Right)
{
    ASSERT_COVERED(MulV3FAssign);
    return Left = Left * Right;
}

COVERAGE(MulV4FAssign, 1)
static inline Vec4 &operator*=(Vec4 &Left, float Right)
{
    ASSERT_COVERED(MulV4FAssign);
    return Left = Left * Right;
}

COVERAGE(MulM2FAssign, 1)
static inline Mat2 &operator*=(Mat2 &Left, float Right)
{
    ASSERT_COVERED(MulM2FAssign);
    return Left = Left * Right;
}

COVERAGE(MulM3FAssign, 1)
static inline Mat3 &operator*=(Mat3 &Left, float Right)
{
    ASSERT_COVERED(MulM3FAssign);
    return Left = Left * Right;
}

COVERAGE(MulM4FAssign, 1)
static inline Mat4 &operator*=(Mat4 &Left, float Right)
{
    ASSERT_COVERED(MulM4FAssign);
    return Left = Left * Right;
}

COVERAGE(MulQFAssign, 1)
static inline Quat &operator*=(Quat &Left, float Right)
{
    ASSERT_COVERED(MulQFAssign);
    return Left = Left * Right;
}

COVERAGE(DivV2Assign, 1)
static inline Vec2 &operator/=(Vec2 &Left, Vec2 Right)
{
    ASSERT_COVERED(DivV2Assign);
    return Left = Left / Right;
}

COVERAGE(DivV3Assign, 1)
static inline Vec3 &operator/=(Vec3 &Left, Vec3 Right)
{
    ASSERT_COVERED(DivV3Assign);
    return Left = Left / Right;
}

COVERAGE(DivV4Assign, 1)
static inline Vec4 &operator/=(Vec4 &Left, Vec4 Right)
{
    ASSERT_COVERED(DivV4Assign);
    return Left = Left / Right;
}

COVERAGE(DivV2FAssign, 1)
static inline Vec2 &operator/=(Vec2 &Left, float Right)
{
    ASSERT_COVERED(DivV2FAssign);
    return Left = Left / Right;
}

COVERAGE(DivV3FAssign, 1)
static inline Vec3 &operator/=(Vec3 &Left, float Right)
{
    ASSERT_COVERED(DivV3FAssign);
    return Left = Left / Right;
}

COVERAGE(DivV4FAssign, 1)
static inline Vec4 &operator/=(Vec4 &Left, float Right)
{
    ASSERT_COVERED(DivV4FAssign);
    return Left = Left / Right;
}

COVERAGE(DivM4FAssign, 1)
static inline Mat4 &operator/=(Mat4 &Left, float Right)
{
    ASSERT_COVERED(DivM4FAssign);
    return Left = Left / Right;
}

COVERAGE(DivQFAssign, 1)
static inline Quat &operator/=(Quat &Left, float Right)
{
    ASSERT_COVERED(DivQFAssign);
    return Left = Left / Right;
}

COVERAGE(EqV2Op, 1)
static inline Bool operator==(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(EqV2Op);
    return EqV2(Left, Right);
}

COVERAGE(EqV3Op, 1)
static inline Bool operator==(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(EqV3Op);
    return EqV3(Left, Right);
}

COVERAGE(EqV4Op, 1)
static inline Bool operator==(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(EqV4Op);
    return EqV4(Left, Right);
}

COVERAGE(EqV2OpNot, 1)
static inline Bool operator!=(Vec2 Left, Vec2 Right)
{
    ASSERT_COVERED(EqV2OpNot);
    return !EqV2(Left, Right);
}

COVERAGE(EqV3OpNot, 1)
static inline Bool operator!=(Vec3 Left, Vec3 Right)
{
    ASSERT_COVERED(EqV3OpNot);
    return !EqV3(Left, Right);
}

COVERAGE(EqV4OpNot, 1)
static inline Bool operator!=(Vec4 Left, Vec4 Right)
{
    ASSERT_COVERED(EqV4OpNot);
    return !EqV4(Left, Right);
}

COVERAGE(UnaryMinusV2, 1)
static inline Vec2 operator-(Vec2 In)
{
    ASSERT_COVERED(UnaryMinusV2);

    Vec2 Result;
    Result.X = -In.X;
    Result.Y = -In.Y;

    return Result;
}

COVERAGE(UnaryMinusV3, 1)
static inline Vec3 operator-(Vec3 In)
{
    ASSERT_COVERED(UnaryMinusV3);

    Vec3 Result;
    Result.X = -In.X;
    Result.Y = -In.Y;
    Result.Z = -In.Z;

    return Result;
}

COVERAGE(UnaryMinusV4, 1)
static inline Vec4 operator-(Vec4 In)
{
    ASSERT_COVERED(UnaryMinusV4);

    Vec4 Result;
#if HANDMADE_MATH__USE_SSE
    Result.SSE = _mm_xor_ps(In.SSE, _mm_set1_ps(-0.0f));
#else
    Result.X = -In.X;
    Result.Y = -In.Y;
    Result.Z = -In.Z;
    Result.W = -In.W;
#endif

    return Result;
}

#endif /* __cplusplus*/

#ifdef HANDMADE_MATH__USE_C11_GENERICS
#define Add(A, B) _Generic((A), \
        Vec2: AddV2, \
        Vec3: AddV3, \
        Vec4: AddV4, \
        Mat2: AddM2, \
        Mat3: AddM3, \
        Mat4: AddM4, \
        Quat: AddQ \
)(A, B)

#define Sub(A, B) _Generic((A), \
        Vec2: SubV2, \
        Vec3: SubV3, \
        Vec4: SubV4, \
        Mat2: SubM2, \
        Mat3: SubM3, \
        Mat4: SubM4, \
        Quat: SubQ \
)(A, B)

#define Mul(A, B) _Generic((B), \
     float: _Generic((A), \
        Vec2: MulV2F, \
        Vec3: MulV3F, \
        Vec4: MulV4F, \
        Mat2: MulM2F, \
        Mat3: MulM3F, \
        Mat4: MulM4F, \
        Quat: MulQF \
     ), \
     Mat2: MulM2, \
     Mat3: MulM3, \
     Mat4: MulM4, \
     Quat: MulQ, \
     default: _Generic((A), \
        Vec2: MulV2, \
        Vec3: MulV3, \
        Vec4: MulV4, \
        Mat2: MulM2V2, \
        Mat3: MulM3V3, \
        Mat4: MulM4V4 \
    ) \
)(A, B)

#define Div(A, B) _Generic((B), \
     float: _Generic((A), \
        Mat2: DivM2F, \
        Mat3: DivM3F, \
        Mat4: DivM4F, \
        Vec2: DivV2F, \
        Vec3: DivV3F, \
        Vec4: DivV4F, \
        Quat: DivQF  \
     ), \
     Mat2: DivM2, \
     Mat3: DivM3, \
     Mat4: DivM4, \
     Quat: DivQ, \
     default: _Generic((A), \
        Vec2: DivV2, \
        Vec3: DivV3, \
        Vec4: DivV4  \
    ) \
)(A, B)

#define Len(A) _Generic((A), \
        Vec2: LenV2, \
        Vec3: LenV3, \
        Vec4: LenV4  \
)(A)

#define LenSqr(A) _Generic((A), \
        Vec2: LenSqrV2, \
        Vec3: LenSqrV3, \
        Vec4: LenSqrV4  \
)(A)

#define Norm(A) _Generic((A), \
        Vec2: NormV2, \
        Vec3: NormV3, \
        Vec4: NormV4  \
)(A)

#define Dot(A, B) _Generic((A), \
        Vec2: DotV2, \
        Vec3: DotV3, \
        Vec4: DotV4  \
)(A, B)

#define Lerp(A, T, B) _Generic((A), \
        float: Lerp, \
        Vec2: LerpV2, \
        Vec3: LerpV3, \
        Vec4: LerpV4 \
)(A, T, B)

#define Eq(A, B) _Generic((A), \
        Vec2: EqV2, \
        Vec3: EqV3, \
        Vec4: EqV4  \
)(A, B)

#define Transpose(M) _Generic((M), \
        Mat2: TransposeM2, \
        Mat3: TransposeM3, \
        Mat4: TransposeM4  \
)(M)

#define Determinant(M) _Generic((M), \
        Mat2: DeterminantM2, \
        Mat3: DeterminantM3, \
        Mat4: DeterminantM4  \
)(M)

#define InvGeneral(M) _Generic((M), \
        Mat2: InvGeneralM2, \
        Mat3: InvGeneralM3, \
        Mat4: InvGeneralM4  \
)(M)

#endif

#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif

#endif /* HANDMADE_MATH_H */



