//========================================================================================================
// E library
//
// - Matrix functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Aug-12 01:35:41
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//========================================================================================================
#ifndef _EMatrix_h
#define _EMatrix_h

#include <math.h>

#ifndef _ETypes_h
#include <ETypes.h>
#endif


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


//========================================================
// Transform [X, Y] with a Matrix's left 2x2 part
//========================================================
#define EMatrix_Transform2x2_s(pMatrix,  pInOut) do\
{\
	const typeof(pInOut.X)  lX = pInOut.X, lY = pInOut.Y;\
	pInOut.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10];\
	pInOut.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11];\
} while(0)

//========================================================
// Transform [X, Y] with a Matrix's left 2x2 part
//========================================================
#define EMatrix_Transform2x2_p(pMatrix,  pInOut) do\
{\
	const typeof(pInOut->X)  lX = pInOut->X, lY = pInOut->Y;\
	pInOut->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10];\
	pInOut->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11];\
} while(0)

//========================================================
// Transform [X, Y] with a Matrix's left 2x2 part
//========================================================
#define EMatrix_Transform2x2_ss(pMatrix,  pIn_X, pIn_Y,  pOut_X, pOut_Y) do\
{\
	const typeof(pIn_X)  lX = pIn_X, lY = pIn_Y;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11];\
} while(0)


//========================================================
// Transform [X, Y] with a Matrix's left 2x4 part
//========================================================
#define EMatrix_Transform2x4_s(pMatrix,  pIO_X, pIO_Y) do\
{\
	const typeof(pIO_X)  pX = pIO_X, pY = pIO_Y;\
	pIO_X = pX * (pMatrix)[M00] + pY * (pMatrix)[M10] + (pMatrix)[M30];\
	pIO_Y = pX * (pMatrix)[M01] + pY * (pMatrix)[M11] + (pMatrix)[M31];\
} while(0)



#define EMatrix_Transform3x3_s(pMatrix,  pIO_X, pIO_Y, pIO_Z) do\
{\
	const typeof(pIO_X)  lX = pIO_X, lY = pIO_Y, lZ = pIO_Z;\
	pIO_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pIO_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pIO_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
} while(0)

#define EMatrix_Transform3x3_a(pMatrix,  pIO) do\
{\
	const typeof(pIO[0])  lX = pIO[0], lY = pIO[1], lZ = pIO[2];\
	pIO[0] = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pIO[1] = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pIO[2] = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
} while(0)

#define EMatrix_Transform3x3_v(pMatrix,  pInOut) do\
{\
	const typeof(pInOut.X)  lX = pInOut.X, lY = pInOut.Y, lZ = pInOut.Z;\
	pInOut.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pInOut.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pInOut.Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pInOut);\
} while(0)

#define EMatrix_Transform3x3_p(pMatrix,  pInOut) do\
{\
	const typeof(pInOut->X)  lX = pInOut->X, lY = pInOut->Y, lZ = pInOut->Z;\
	pInOut->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pInOut->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pInOut->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWp(pInOut);\
} while(0)

#define EMatrix_Transform3x3_sv(pMatrix,  pX, pY, pZ,  pOut) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
	pOut.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pOut.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pOut.Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_ss(pMatrix,  pX, pY, pZ,  pOut_X, pOut_Y, pOut_Z) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pOut_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
} while(0)

#define EMatrix_Transform3x3_sp(pMatrix,  pX, pY, pZ,  pOut) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
	pOut->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pOut->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pOut->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWp(pOut);\
} while(0)

#define EMatrix_Transform3x3_vs(pMatrix,  pIn,  pOut_X, pOut_Y, pOut_Z) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	pOut_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
} while(0)

#define EMatrix_Transform3x3_vv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	(pOut).X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut).Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut).Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_vp(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	(pOut)->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut)->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut)->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_pv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
	(pOut).X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut).Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut).Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_pp(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
	(pOut)->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut)->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut)->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_as(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn[E3dX])  lX = pIn[E3dX], lY = pIn[E3dY], lZ = pIn[E3dZ];\
	(pOut).X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut).Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut).Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_sa(pMatrix,  pIn_X, pIn_Y, pIn_Z,  pOut) do\
{\
	const typeof(pIn_X)  lX = pIn_X, lY = pIn_Y, lZ = pIn_Z;\
	(pOut)[E3dX] = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut)[E3dY] = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut)[E3dZ] = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x3_aa(pMatrix,  pIn,  pOut) do\
{\
	const typeof((pIn)[E3dX])  lX = (pIn)[E3dX], lY = (pIn)[E3dY], lZ = (pIn)[E3dZ];\
	(pOut)[E3dX] = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20];\
	(pOut)[E3dY] = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21];\
	(pOut)[E3dZ] = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22];\
	_M_ClearWs(pOut);\
} while(0)



//========================================================
// Transform [X, Y, Z] with a Matrix's left 3x4 part
//========================================================
#define EMatrix_Transform3x4_s(pMatrix,  pIO_X, pIO_Y, pIO_Z) do\
{\
	const typeof(pIO_X)  lX = pIO_X, lY = pIO_Y, lZ = pIO_Z;\
	pIO_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pIO_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pIO_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)

#define EMatrix_Transform3x4_a(pMatrix,  pIO) do\
{\
	const typeof(pIO[0])  lX =(pIO)[0], lY = (pIO)[1], lZ = (pIO)[2];\
	(pIO)[0] = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	(pIO)[1] = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	(pIO)[2] = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)

#define EMatrix_Transform3x4_as(pMatrix,  pIn, pOut) do\
{\
	const typeof((pIn)[0])  lX =(pIn)[0], lY = (pIn)[1], lZ = (pIn)[2];\
	(pOut).X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	(pOut).Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	(pOut).Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)

#define EMatrix_Transform3x4_v(pMatrix,  pIO) do\
{\
	const typeof(pIO.X)  lX = pIO.X, lY = pIO.Y, lZ = pIO.Z;\
	pIO.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pIO.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pIO.Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWs(pIO);\
} while(0)

#define EMatrix_Transform3x4_p(pMatrix,  pIO) do\
{\
	const typeof(pIO->X)  lX = pIO->X, lY = pIO->Y, lZ = pIO->Z;\
	pIO->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pIO->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pIO->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWs(pIO);\
} while(0)

#define EMatrix_Transform3x4_ss(pMatrix,  pIn_X, pIn_Y, pIn_Z,  pOut_X, pOut_Y, pOut_Z) do\
{\
	const typeof(pIn_X)  lX = pIn_X, lY = pIn_Y, lZ = pIn_Z;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)

#define EMatrix_Transform3x4_vs(pMatrix,  pIn,  pOut_X, pOut_Y, pOut_Z) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)

#define EMatrix_Transform3x4_vv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	pOut.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut.Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x4_vp(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
	pOut->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWp(pOut);\
} while(0)

#define EMatrix_Transform3x4_pv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
	pOut.X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut.Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut.Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x4_pp(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
	pOut->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWp(pOut);\
} while(0)

#define EMatrix_Transform3x4_sp(pMatrix,  pX, pY, pZ,  pOut) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
	(pOut)->X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	(pOut)->Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	(pOut)->Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWp(pOut);\
} while(0)

#define EMatrix_Transform3x4_sv(pMatrix,  pX, pY, pZ,  pOut) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
	(pOut).X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	(pOut).Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	(pOut).Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
	_M_ClearWs(pOut);\
} while(0)

#define EMatrix_Transform3x4_ps(pMatrix,  pIn,  pOut_X, pOut_Y, pOut_Z) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
	pOut_X = lX * (pMatrix)[M00] + lY * (pMatrix)[M10] + lZ * (pMatrix)[M20] + (pMatrix)[M30];\
	pOut_Y = lX * (pMatrix)[M01] + lY * (pMatrix)[M11] + lZ * (pMatrix)[M21] + (pMatrix)[M31];\
	pOut_Z = lX * (pMatrix)[M02] + lY * (pMatrix)[M12] + lZ * (pMatrix)[M22] + (pMatrix)[M32];\
} while(0)



//========================================================================
// Transform a homogenous 3D position [X, Y, Z, 1.0] with a 4x4 Matrix	
//========================================================================
#define EMatrix_Transform4x4_hv(pMatrix,  pInOut) do\
{\
	const typeof((pInOut).X)  lX = (pInOut).X, lY = (pInOut).Y, lZ = (pInOut).Z;\
\
	(pInOut).X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30],\
	(pInOut).Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31],\
	(pInOut).Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32],\
	(pInOut).W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)

#define EMatrix_Transform4x4_hss(pMatrix,  pX, pY, pZ,  pOut_X, pOut_Y, pOut_Z, pOut_W) do\
{\
  const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
\
	pOut_X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30];\
	pOut_Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31];\
	pOut_Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32];\
	pOut_W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)

#define EMatrix_Transform4x4_hsv(pMatrix,  pX, pY, pZ,  pOut) do\
{\
	const typeof(pX)  lX = pX, lY = pY, lZ = pZ;\
\
	pOut.X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30];\
	pOut.Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31];\
	pOut.Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32];\
	pOut.W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)

#define EMatrix_Transform4x4_hvs(pMatrix,  pIn,  pOut_X, pOut_Y, pOut_Z, pOut_W) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
\
	pOut_X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30];\
	pOut_Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31];\
	pOut_Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32];\
	pOut_W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)

#define EMatrix_Transform4x4_hvv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn.X)  lX = pIn.X, lY = pIn.Y, lZ = pIn.Z;\
\
	pOut.X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30];\
	pOut.Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31];\
	pOut.Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32];\
	pOut.W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)

#define EMatrix_Transform4x4_hpv(pMatrix,  pIn,  pOut) do\
{\
	const typeof(pIn->X)  lX = pIn->X, lY = pIn->Y, lZ = pIn->Z;\
\
	pOut.X = lX * pMatrix[M00] + lY * pMatrix[M10] + lZ * pMatrix[M20] + pMatrix[M30];\
	pOut.Y = lX * pMatrix[M01] + lY * pMatrix[M11] + lZ * pMatrix[M21] + pMatrix[M31];\
	pOut.Z = lX * pMatrix[M02] + lY * pMatrix[M12] + lZ * pMatrix[M22] + pMatrix[M32];\
	pOut.W = lX * pMatrix[M03] + lY * pMatrix[M13] + lZ * pMatrix[M23] + pMatrix[M33];\
} while(0)


//================================================================================================================================================
// Invert a 3x3 matrix made of floats (3x in 3xf32 means that the matrix is stored as 3x3 (9) floats, so we use the M3x** addressing values)
//================================================================================================================================================
static inline void EMatrix_Invert3x3_f32(const float* PSrc, float* PDst)
{
	const float  LM00 = PSrc[M3x00], LM01 = PSrc[M3x01], LM02 = PSrc[M3x02],  // Use temporary storage, in case PSrc == PDst
	             LM10 = PSrc[M3x10], LM11 = PSrc[M3x11], LM12 = PSrc[M3x12],
	             LM20 = PSrc[M3x20], LM21 = PSrc[M3x21], LM22 = PSrc[M3x22];
	const float  LDeterminant = (LM22 * LM11 - LM21 * LM12) * LM00 - (LM22 * LM10 + LM20 * LM12) * LM01 + (LM21 * LM10 - LM20 * LM11) * LM02;

	const float  LInvDeterminant = 1.0 / LDeterminant;

	PDst[M3x00] =  (LM22 * LM11 - LM21 * LM12) * LInvDeterminant;
	PDst[M3x01] = (-LM22 * LM01 + LM21 * LM02) * LInvDeterminant;
	PDst[M3x02] =  (LM12 * LM01 - LM11 * LM02) * LInvDeterminant;
	PDst[M3x10] = (-LM22 * LM10 + LM20 * LM12) * LInvDeterminant;
	PDst[M3x11] =  (LM22 * LM00 - LM20 * LM02) * LInvDeterminant;
	PDst[M3x12] = (-LM12 * LM00 + LM10 * LM02) * LInvDeterminant;
	PDst[M3x20] =  (LM21 * LM10 - LM20 * LM11) * LInvDeterminant;
	PDst[M3x21] = (-LM21 * LM00 + LM20 * LM01) * LInvDeterminant;
	PDst[M3x22] =  (LM11 * LM00 - LM10 * LM01) * LInvDeterminant;
}


static inline void EMatrix_Mult3x3_f32(const float* PMatrix, const float PIn[3], float POut[3])
{
	const float  LIn0 = PIn[0], LIn1 = PIn[1], LIn2 = PIn[2];;
	POut[0] = PMatrix[M3x00] * LIn0 + PMatrix[M3x01] * LIn1 + PMatrix[M3x02] * LIn2;
	POut[1] = PMatrix[M3x10] * LIn0 + PMatrix[M3x11] * LIn1 + PMatrix[M3x12] * LIn2;
	POut[2] = PMatrix[M3x20] * LIn0 + PMatrix[M3x21] * LIn1 + PMatrix[M3x22] * LIn2;
}


static inline void EMatrix_LoadIdentity3x3_f32(float* PMatrix)
{
	int  LI = 0;
	PMatrix[LI++] = 1.0;PMatrix[LI++] = 0.0;PMatrix[LI++] = 0.0;
	PMatrix[LI++] = 0.0;PMatrix[LI++] = 1.0;PMatrix[LI++] = 0.0;
	PMatrix[LI++] = 0.0;PMatrix[LI++] = 0.0;PMatrix[LI++] = 1.0;
}


static inline void EMatrix_LoadDiagonal3x3_f32(float* PMatrix,  const float PV0, const float PV1, const float PV2)
{
	int  LI = 0;
	PMatrix[LI++] = PV0;PMatrix[LI++] = 0.0;PMatrix[LI++] = 0.0;
	PMatrix[LI++] = 0.0;PMatrix[LI++] = PV1;PMatrix[LI++] = 0.0;
	PMatrix[LI++] = 0.0;PMatrix[LI++] = 0.0;PMatrix[LI++] = PV2;
}


//========================================
// Multiply 2 3x3 Matrices
//========================================
static inline void EMatrix_Mult3x3_3_f32(const float* PMatrix1, const float* PMatrix2, float* PDstMatrix)
{
// Load source into constants. It's faster and it works, even if the source and destination are the same
//
	const float  LM1_00 = PMatrix1[M3x00], LM1_01 = PMatrix1[M3x01], LM1_02 = PMatrix1[M3x02];
	const float  LM1_10 = PMatrix1[M3x10], LM1_11 = PMatrix1[M3x11], LM1_12 = PMatrix1[M3x12];
	const float  LM1_20 = PMatrix1[M3x20], LM1_21 = PMatrix1[M3x21], LM1_22 = PMatrix1[M3x22];

	const float  LM2_00 = PMatrix2[M3x00], LM2_01 = PMatrix2[M3x01], LM2_02 = PMatrix2[M3x02];
	const float  LM2_10 = PMatrix2[M3x10], LM2_11 = PMatrix2[M3x11], LM2_12 = PMatrix2[M3x12];
	const float  LM2_20 = PMatrix2[M3x20], LM2_21 = PMatrix2[M3x21], LM2_22 = PMatrix2[M3x22];

	PDstMatrix[M3x00] = LM1_00 * LM2_00 + LM1_01 * LM2_10 + LM1_02 * LM2_20;
	PDstMatrix[M3x01] = LM1_00 * LM2_01 + LM1_01 * LM2_11 + LM1_02 * LM2_21;
	PDstMatrix[M3x02] = LM1_00 * LM2_02 + LM1_01 * LM2_12 + LM1_02 * LM2_22;

	PDstMatrix[M3x10] = LM1_10 * LM2_00 + LM1_11 * LM2_10 + LM1_12 * LM2_20;
	PDstMatrix[M3x11] = LM1_10 * LM2_01 + LM1_11 * LM2_11 + LM1_12 * LM2_21;
	PDstMatrix[M3x12] = LM1_10 * LM2_02 + LM1_11 * LM2_12 + LM1_12 * LM2_22;

	PDstMatrix[M3x20] = LM1_20 * LM2_00 + LM1_21 * LM2_10 + LM1_22 * LM2_20;
	PDstMatrix[M3x21] = LM1_20 * LM2_01 + LM1_21 * LM2_11 + LM1_22 * LM2_21;
	PDstMatrix[M3x22] = LM1_20 * LM2_02 + LM1_21 * LM2_12 + LM1_22 * LM2_22;
}

extern void  EMatrix_Print3x3_f32(const float* PSrc);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif


#endif // _EMatrix_h
