//========================================================================================================
// E library
//
// - Basic math and vector functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Oct-11 09:24:47
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//========================================================================================================
#ifndef _EMath_h
#define _EMath_h

#include <math.h>

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


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


static inline EIndex E_ClampMin_Index(const EIndex PX,  const EIndex PMin)
{
	return(PX < PMin ? PMin : PX);
}

static inline EIndex E_ClampMax_Index(const EIndex PX,  const EIndex PMax)
{
	return(PX > PMax ? PMax : PX);
}

static inline EIndex E_ClampMinMax_Index(const EIndex PX,  const EIndex PMin, const EIndex PMax)
{
	return(PX < PMin ? PMin : (PX > PMax ? PMax : PX));
}

#define E_MaxOfN(pArray, pCount, pResult) do\
{\
	pResult = pArray[0];int  lI = 1;for(;lI < pCount;lI++) if(pResult < pArray[lI]) pResult = pArray[lI];\
} while(0)

#define E_Min3(p00, p01, p02,  p10, p11, p12,  pOut0, pOut1, pOut2) do\
{\
	pOut0 = E_Min(p00, p10);\
	pOut1 = E_Min(p01, p11);\
	pOut2 = E_Min(p02, p12);\
} while(0)

#define E_Max3(p00, p01, p02,  p10, p11, p12,  pOut0, pOut1, pOut2) do\
{\
	pOut0 = E_Max(p00, p10);\
	pOut1 = E_Max(p01, p11);\
	pOut2 = E_Max(p02, p12);\
} while(0)

#define E_MinMax(pA, pB,  pMin, pMax) do\
{\
 if(pA < pB) { pMin = pA;pMax = pB; }\
 else { pMin = pB;pMax = pA; }\
} while(0)

#define E_MinOf3(a,b,c) ( ((a) > (b)) ? ( ((b) > (c)) ? (c):(b)) : ( ((a) > (c)) ? (c) : (a)) )
#define E_MaxOf3(a,b,c) ( ((a) < (b)) ? ( ((b) > (c)) ? (b):(c)) : ( ((a) > (c)) ? (a) : (c)) )

#define E_DotRGB_vs(p, p10, p11, p12)  (p).R * (p10) + (p).G * (p11) + (p).B * (p12)


static inline float E_Lerp_f(const float P0, const float P1, const float PT)
{
	const float  LTInv = 1.0 - PT;
	return(P0 * LTInv + P1 * PT);
}

static inline float E_Smoothstep_f(const float PEdge0, const float PEdge1, const float PX)
{
	if(PX < PEdge0) return(0.0);
	if(PX >= PEdge1) return(1.0);

// Scale/bias into [0..1] range
//
	const float  LX = (PX - PEdge0) / (PEdge1 - PEdge0);

	return(LX * LX * (3.0 - 2.0 * LX));
}

static inline float E_InverseSmoothstep_f(const float PX)
{
  return(0.5 - sin(asin(1.0 - 2.0 * PX) / 3.0));
}

static inline float E_Smootherstep_f(const float PEdge0, const float PEdge1, const float PX)
{
// Scale, and clamp x to 0..1 range
	const float  LV = (PX - PEdge0) / (PEdge1 - PEdge0);
	const float  LX = E_Clamp(LV, 0.0, 1.0);

// Evaluate polynomial
  return(LX * LX * LX * (LX * (LX * 6.0 - 15.0) + 10.0));
}


//================================================
// Normalize a 3D vector
//================================================
static inline EFloat32_3 EFloat32_3_Normalize(const EFloat32_3 PIn)
{
	const float  LInX = PIn.X, LInY = PIn.Y, LInZ = PIn.Z;
	const float  LL = sqrtf(LInX * LInX + LInY * LInY + LInZ * LInZ);if(LL > 0.0)
	{
		return(EFloat32_3s(LInX / LL, LInY / LL, LInZ  / LL));
	}
	else return(EFloat32_3s(0.0, 0.0, 0.0));
}


#define E_BBoxCheckCoord_vv(pBBox, pV, pCoord) do\
{\
	if(pBBox.Min.pCoord > pV.pCoord) pBBox.Min.pCoord = pV.pCoord;\
	else if(pBBox.Max.pCoord < pV.pCoord) pBBox.Max.pCoord = pV.pCoord;\
} while(0)

#define E_BBoxCheckCoord_vp(pBBox, pV, pCoord) do\
{\
	if(pBBox.Min.pCoord > pV->pCoord) pBBox.Min.pCoord = pV->pCoord;\
	else if(pBBox.Max.pCoord < pV->pCoord) pBBox.Max.pCoord = pV->pCoord;\
} while(0)

#define E3d_BBoxCheckCoord_pp(pBBox, pV, pCoord) do\
{\
	if(pBBox->Min.pCoord > pV->pCoord) pBBox->Min.pCoord = pV->pCoord;\
	else if(pBBox->Max.pCoord < pV->pCoord) pBBox->Max.pCoord = pV->pCoord;\
} while(0)


#define E3d_BBoxCheckXYZ_vv(pBBox, pV) do\
{\
	E_BBoxCheckCoord_vv(pBBox, pV, X);\
	E_BBoxCheckCoord_vv(pBBox, pV, Y);\
	E_BBoxCheckCoord_vv(pBBox, pV, Z);\
} while(0)

#define E3d_BBoxCheckXYZ_vp(pBBox, pV) do\
{\
	E_BBoxCheckCoord_vp(pBBox, pV, X);\
	E_BBoxCheckCoord_vp(pBBox, pV, Y);\
	E_BBoxCheckCoord_vp(pBBox, pV, Z);\
} while(0)

#define E_BBoxCheckXYZ_pp(pBBox, pV) do\
{\
	E3d_BBoxCheckCoord_pp(pBBox, pV, X);\
	E3d_BBoxCheckCoord_pp(pBBox, pV, Y);\
	E3d_BBoxCheckCoord_pp(pBBox, pV, Z);\
} while(0)


#define E_Set3_v(pV,  pX, pY, pZ) do\
{\
	(pV).X = pX;\
	(pV).Y = pY;\
	(pV).Z = pZ;\
} while(0)


#define E_Add3_vv(pA, pDst)        do { pDst.X  += pA.X, pDst.Y += pA.Y, pDst.Z += pA.Z; } while(0)
#define E_Add3_pvv(pA, pB, pDst)   do { pDst.X  = pA->X  + pB.X, pDst.Y  = pA->Y  + pB.Y, pDst.Z  = pA->Z  + pB.Z; } while(0)
#define E_Add3_vvv(pA, pB, pDst)   do { pDst.X  = pA.X  + pB.X, pDst.Y  = pA.Y  + pB.Y, pDst.Z  = pA.Z  + pB.Z; } while(0)
#define E_Add3_vvp(pA, pB, pDst)   do { pDst->X = pA.X  + pB.X, pDst->Y = pA.Y  + pB.Y, pDst->Z = pA.Z  + pB.Z; } while(0)
#define E_Add3_pvp(pA, pB, pDst)   do { pDst->X = pA->X + pB.X, pDst->Y = pA->Y + pB.Y, pDst->Z = pA->Z + pB.Z; } while(0)

#define E_Sub3_vvv(pA, pB, pDst)   do { pDst.X  = pA.X  - pB.X, pDst.Y  = pA.Y  - pB.Y, pDst.Z  = pA.Z  - pB.Z; } while(0)
#define E_Sub3_vvp(pA, pB, pDst)   do { pDst->X = pA.X  - pB.X, pDst->Y = pA.Y  - pB.Y, pDst->Z = pA.Z  - pB.Z; } while(0)
#define E_Sub3_pvp(pA, pB, pDst)   do { pDst->X = pA->X - pB.X, pDst->Y = pA->Y - pB.Y, pDst->Z = pA->Z - pB.Z; } while(0)


#define E_Sub2i_vv(pResult,  pA, pB)                  pResult = { { (pA).X - (pB).X },      { (pA).Y - (pB).Y } }
#define E_AddScaled2i_vv(pResult,  pA, pB,  pS)       pResult = { { (pA).X + (pB).X * pS }, { (pA).Y + (pB).Y * pS } }

#define E_Scale3i_s(pV, pS)                           { .X= (pV).X * (pS), .Y= (pV).Y * (pS), .Z= (pV).Z * (pS) };

#define E_AddScaled3_vvv(pA, pB, pC,  pResult)        do { const E3dCoordinate  lc = pC;pResult.X  = (pA).X + (pB).X * lc, pResult.Y  = (pA).Y + (pB).Y * lc, pResult.Z  = (pA).Z + (pB).Z * lc; } while(0)
#define E_AddScaled3_vvp(pA, pB, pC,  pResult)        do { const E3dCoordinate  lc = pC;pResult->X = (pA).X + (pB).X * lc, pResult->Y = (pA).Y + (pB).Y * lc, pResult->Z = (pA).Z + (pB).Z * lc; } while(0)
#define E_AddScaled3_vvs(pA, pB,  pC)                 { .X= (pA).X + (pB).X * (pC), .Y= (pA).Y + (pB).Y * (pC), .Z= (pA).Z + (pB).Z * (pC) }

#define E_SubFromScaled3_vvv(pA, pB, pC,  pResult)    do { const E3dCoordinate  lb = pB;pResult.X  = (pA).X * lb - (pC).X, pResult.Y  = (pA).Y * lb - pC.Y, pResult.Z  = (pA).Z * lb - (pC).Z; } while(0)

#define E_Sub2i_pp(pResult,  pA, pB)                  pResult = { (pA)->X - (pB)->X,  (pA)->Y - (pB)->Y }

#define E_Neg3_v(pA)                                  { .X= -(pA).X, .Y= -(pA).Y,.Z= -(pA).Z }
#define E_Sub3_vv(pA, pB)                             { .X= (pA).X - (pB).X, .Y= (pA).Y - (pB).Y,.Z= (pA).Z - (pB).Z }
#define E_Add3i_vv(pResult,  pA, pB)                  pResult = { .X= (pA).X + (pB).X, .Y= (pA).Y + (pB).Y, .Z= (pA).Z + (pB).Z }
#define E_Sub3i_vv(pResult,  pA, pB)                  pResult = { .X= (pA).X - (pB).X,    .Y= (pA).Y - (pB).Y,    .Z= (pA).Z - (pB).Z }
#define E_Sub3i_pv(pResult,  pA, pB)                  pResult = { .X= (pA)->X - (pB).X,   .Y= (pA)->Y - (pB).Y,   .Z= (pA)->Z - (pB).Z }
#define E_Sub3i_vp(pResult,  pA, pB)                  pResult = { .X= (pA).X - (pB)->X,   .Y= (pA).Y - (pB)->Y,   .Z= (pA).Z - (pB)->Z }
#define E_Sub3i_pp(pResult,  pA, pB)                  pResult = { .X= (pA)->X - (pB)->X,  .Y= (pA)->Y - (pB)->Y,  .Z= (pA)->Z - (pB)->Z }
#define E_Sub3i_aa(pResult,  pA, pB)                  pResult = { .X= (pA)[E3dX] - (pB)[E3dX],  .Y= (pA)[E3dY] - (pB)[E3dY],  .Z= (pA)[E3dZ] - (pB)[E3dZ] }

#define E_Mul3_vvv(pA, pB, pDst)                      do { pDst.X = pA.X * pB.X, pDst.Y = pA.Y * pB.Y, pDst.Z = pA.Z * pB.Z; } while(0)
#define E_Mul3_vsv(pIn, pScaler, pOut) do\
{\
	const typeof(pScaler)  lScaler = pScaler;\
	pOut.X = pIn.X * lScaler;\
	pOut.Y = pIn.Y * lScaler;\
	pOut.Z = pIn.Z * lScaler;\
} while(0)
#define E_Mul3_vs(pInOut, pScaler) do\
{\
	const typeof(pScaler)  lScaler = pScaler;\
	pInOut.X *= lScaler;\
	pInOut.Y *= lScaler;\
	pInOut.Z *= lScaler;\
} while(0)
#define E_Mul3i_vs(pResult,  pA, pB)                  pResult = { .X= (pA).X * (pB), .Y= (pA).Y * (pB), .Z= (pA).Z * (pB) }
#define E_Div3i_vs(pResult,  pA, pB)                  pResult = { .X= (pA).X / (pB), .Y= (pA).Y / (pB), .Z= (pA).Z / (pB) }
#define E_Div3_vsv(pIn, pScaler,  pOut) do\
{\
	const typeof(pScaler)  lScaler = pScaler;\
	pOut.X = (pIn).X / lScaler;\
	pOut.Y = (pIn).Y / lScaler;\
	pOut.Z = (pIn).Z / lScaler;\
} while(0)

#define E_AddScaled3i_vv(pResult,  pA, pB,  pS)        pResult = { .X= (pA).X + (pB).X * (pS), .Y= (pA).Y + (pB).Y * (pS), .Z= (pA).Z + (pB).Z * (pS) }
#define E_AddScaledToScaled3i_vv(pResult,  pA, pAMult,  pB, pBMult)  pResult = { .X= (pA).X * (pAMult) + (pB).X * (pBMult), .Y= (pA).Y * (pAMult) + (pB).Y * (pBMult), .Z= (pA).Z * (pAMult) + (pB).Z * (pBMult) }

#define E_MidPoint3_vvv(pPoint0, pPoint1,  pResult) do\
{\
	(pResult).X = (pPoint1).X + ((pPoint0).X - (pPoint1).X) / 2.0;\
	(pResult).Y = (pPoint1).Y + ((pPoint0).Y - (pPoint1).Y) / 2.0;\
	(pResult).Z = (pPoint1).Z + ((pPoint0).Z - (pPoint1).Z) / 2.0;\
} while(0)

#define E_Dot2_vv(pA, pB)   (pA.X * pB.X  +  pA.Y * pB.Y)
#define E_Dot2_av(pA, pB)   (pA[0] * pB.X + pA[1] * pB.Y)
#define E_Dot3_vv(pA, pB)   (pA.X * pB.X  +  pA.Y * pB.Y  +  pA.Z * pB.Z)
#define E_Dot3_cc(pA, pB)   (pA##X * pB##X  +  pA##Y * pB##Y  +  pA##Z * pB##Z)
#define E_Dot3_av(pA, pB)   (pA[0] * pB.X  +  pA[1] * pB.Y  +  pA[2] * pB.Z)
#define E_Dot3_vp(pA, pB)   (pA.X * pB->X  +  pA.Y * pB->Y  +  pA.Z * pB->Z)
#define E_Dot3_pp(pA, pB)   (pA->X * pB->X  +  pA->Y * pB->Y  +  pA->Z * pB->Z)
#define E_Dot3_pv(pA, pB)   (pA->X * pB.X  +  pA->Y * pB.Y  +  pA->Z * pB.Z)
#define E_Dot4_vv(pA, pB)   (pA.X * pB.X  +  pA.Y * pB.Y  +  pA.Z * pB.Z  +  pA.W * pB.W)
#define E_Dot4H_vv(pA, pB)  (pA.X * pB.X  +  pA.Y * pB.Y  +  pA.Z * pB.Z  +  pA.W)


//================================================
// 3D cross product of two 2D vectors
//================================================
#define E_Cross2_vva(pResult,  pA, pB) do\
{\
	pResult.X = - pB[1], pResult.Y = pB[0], pResult.Z = pA.X * pB[1] - pA.Y * pB[0];\
} while(0)

//================================================
// 3D cross product of two vectors
//================================================
#define E_Cross3i_vvv(pResult,  pA, pB)  pResult = { .X= (pA).Y * (pB).Z - (pA).Z * (pB).Y, .Y= (pA).Z * (pB).X - (pA).X * (pB).Z, .Z= (pA).X * (pB).Y - (pA).Y * (pB).X };\

#define E_Cross3_vvv(pA, pB, pC) do\
{\
	(pC).X = (pA).Y * (pB).Z - (pA).Z * (pB).Y, (pC).Y = (pA).Z * (pB).X - (pA).X * (pB).Z, (pC).Z = (pA).X * (pB).Y - (pA).Y * (pB).X;\
} while(0)

static inline EFloat64_3 E_Cross3_f64(const EFloat64_3 PA, const EFloat64_3 PB)
{
	const EFloat64_3  LResult = EFloat64_3s(PA.Y * PB.Z - PA.Z * PB.Y, PA.Z * PB.X - PA.X * PB.Z, PA.X * PB.Y - PA.Y * PB.X);
	return(LResult);
}

#define E_Cross3_sss(pAX, pAY, pAZ,  pBX, pBY, pBZ,  pCX, pCY, pCZ) do\
{\
	pCX = pAY * pBZ - pAZ * pBY, pCY = pAZ * pBX - pAX * pBZ, pCZ = pAX * pBY - pAY * pBX;\
} while(0)

#define E_Cross3_ssp(pAX, pAY, pAZ,  pBX, pBY, pBZ,  pC) do\
{\
	(pC)->X = pAY * pBZ - pAZ * pBY, (pC)->Y = pAZ * pBX - pAX * pBZ, (pC)->Z = pAX * pBY - pAY * pBX;\
} while(0)

#define E_Cross3_ssv(pAX, pAY, pAZ,  pBX, pBY, pBZ,  pC) do\
{\
	(pC).X = pAY * pBZ - pAZ * pBY, (pC).Y = pAZ * pBX - pAX * pBZ, (pC).Z = pAX * pBY - pAY * pBX;\
} while(0)

#define E_Cross3_svv(pAX, pAY, pAZ,  pB,  pC) do\
{\
	(pC).X = pAY * pB.Z - pAZ * pB.Y, (pC).Y = pAZ * pB.X - pAX * pB.Z, (pC).Z = pAX * pB.Y - pAY * pB.X;\
} while(0)

#define E_Cross3_svp(pA,  pBX, pBY, pBZ,  pC) do\
{\
	(pC)->X = pA.Y * pBZ - pA.Z * pBY, (pC)->Y = pA.Z * pBX - pA.X * pBZ, (pC)->Z = pA.X * pBY - pA.Y * pBX;\
} while(0)

#define E_Cross3_ppp(pA, pB, pC) do\
{\
	(pC)->X = (pA)->Y * (pB)->Z - (pA)->Z * (pB)->Y, (pC)->Y = (pA)->Z * (pB)->X - (pA)->X * (pB)->Z, (pC)->Z = (pA)->X * (pB)->Y - (pA)->Y * (pB)->X;\
} while(0)

#define E_Cross3_ppv(pA, pB, pC) do\
{\
	(pC).X = (pA)->Y * (pB)->Z - (pA)->Z * (pB)->Y, (pC).Y = (pA)->Z * (pB)->X - (pA)->X * (pB)->Z, (pC).Z = (pA)->X * (pB)->Y - (pA)->Y * (pB)->X;\
} while(0)

#define E_Cross3_vvv(pA, pB, pC) do\
{\
	(pC).X = (pA).Y * (pB).Z - (pA).Z * (pB).Y, (pC).Y = (pA).Z * (pB).X - (pA).X * (pB).Z, (pC).Z = (pA).X * (pB).Y - (pA).Y * (pB).X;\
} while(0)

#define E_Cross3_vpv(pA, pB, pC) do\
{\
	(pC).X = (pA).Y * (pB)->Z - (pA).Z * (pB)->Y, (pC).Y = (pA).Z * (pB)->X - (pA).X * (pB)->Z, (pC).Z = (pA).X * (pB)->Y - (pA).Y * (pB)->X;\
} while(0)

#define E_Cross3_pvv(pA, pB, pC) do\
{\
	(pC).X = (pA)->Y * (pB).Z - (pA)->Z * (pB).Y, (pC).Y = (pA)->Z * (pB).X - (pA)->X * (pB).Z, (pC).Z = (pA)->X * (pB).Y - (pA)->Y * (pB).X;\
} while(0)

#define E_Cross3i_vv(pC, pA, pB)\
	E3dCoord3  pC = E3dCoord3_s((pA).Y * (pB).Z - (pA).Z * (pB).Y, (pA).Z * (pB).X - (pA).X * (pB).Z, (pA).X * (pB).Y - (pA).Y * (pB).X)

#define E_Cross3_vvs(pA, pB,  pC_X, pC_Y, pC_Z) do\
{\
	pC_X = (pA).Y * (pB).Z - (pA).Z * (pB).Y, pC_Y = (pA).Z * (pB).X - (pA).X * (pB).Z, pC_Z = (pA).X * (pB).Y - (pA).Y * (pB).X;\
} while(0)

#define E_Cross3_vvp(pA, pB, pC) do\
{\
	(pC)->X = (pA).Y * (pB).Z - (pA).Z * (pB).Y, (pC)->Y = (pA).Z * (pB).X - (pA).X * (pB).Z, (pC)->Z = (pA).X * (pB).Y - (pA).Y * (pB).X;\
} while(0)

#define E_Cross3_pvp(pA, pB, pC) do\
{\
	(pC)->X = (pA)->Y * (pB).Z - (pA)->Z * (pB).Y, (pC)->Y = (pA)->Z * (pB).X - (pA)->X * (pB).Z, (pC)->Z = (pA)->X * (pB).Y - (pA)->Y * (pB).X;\
} while(0)


#define E_DistanceSq3_vv(pA, pB,  pDst) do\
{\
	const typeof(pDst)  lDX = (pA).X - (pB).X, lDY = (pA).Y - (pB).Y, lDZ = (pA).Z - (pB).Z;\
	pDst = lDX * lDX + lDY * lDY + lDZ * lDZ;\
} while(0)


//================================================================
// Calculate Euclidean distance between two RGB colors / points
//================================================================
static inline float E_DistanceSq3_f32(const EFloat32_3 P0, const EFloat32_3 P1)
{
	const float  LDX = P0.X - P1.X, LDY = P0.Y - P1.Y, LDZ = P0.Z - P1.Z;
	return((LDX * LDX + LDY * LDY + LDZ * LDZ));  // We can skip the sqrt and compare distance squares because we are only comparing for < or >
}


#define E_Normalize3_f32s(pX, pY, pZ) do\
{\
	const float  lL = sqrtf(pX * pX + pY * pY + pZ * pZ);if(lL > 0.0)\
	{\
		const float  lLInv = 1.0 / lL;\
		pX *= lLInv;pY *= lLInv;pZ *= lLInv;\
	}\
	else\
	{\
		pX = pY = pZ = 0.0;\
	}\
} while(0)



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


#endif // _EMath_h

