//////////////////////////////////////////////////////////////////////
//
//	Crytek Common Source code
//	
//	File:Cry_Math.h
//	Description: Common math class
//
//	History:
//	-Feb 27,2003: Created by Ivo Herzeg
//
//////////////////////////////////////////////////////////////////////

#ifndef CRYMATH_H
#define CRYMATH_H

#if _MSC_VER > 1000
# pragma once
#endif

//========================================================================================

#include <math.h>
#include <assert.h>
#include "platform.h"

extern int inc_mod3[], dec_mod3[];
#ifdef PHYSICS_EXPORTS
#define incm3(i) inc_mod3[i]
#define decm3(i) dec_mod3[i]
#else
inline int incm3(int i) { return i+1 & (i-2)>>31; }
inline int decm3(int i) { return i-1 + ((i-1)>>31&3); }
#endif

///////////////////////////////////////////////////////////////////////////////
// Forward declarations                                                      //
///////////////////////////////////////////////////////////////////////////////
template <class F> struct Vec3_tpl;
template <class F> struct Ang3_tpl;
template <class F> struct AngleAxis_tpl;
template <class F> struct Quaternion_tpl;

template <class F> struct Matrix33diag_tpl;
template <class F,int SI,int SJ> struct Matrix33_tpl;
template <class F> struct Matrix34_tpl;
template <class F,int SI,int SJ> struct Matrix44_tpl;

#ifdef WIN64
// there's no sinf/cosf in Win64 debug
inline float cry_sinf(float x) {return (float)sin((double)x);}
inline float cry_cosf(float x) {return (float)cos((double)x);}
// the portability function
inline float cry_fmod(float x, float y) {return (float)fmod((double)x,(double)y);}
inline float cry_ceilf(float x) {return (float)ceil((double)x);}
inline float cry_asinf(float x) {return (float)asin((double)x);}
inline float cry_acosf(float x) {return (float)acos((double)x);}
inline float cry_atanf(float x) {return (float)atan((double)x);}
inline float cry_atan2f(float x, float y) {return (float)atan2((double)x,(double)y);}
inline float cry_sqrtf(float x) {return (float)sqrt((double)x);}
inline double cry_sinh(double z) {return (exp (z) - exp (-z)) * 0.5;}
inline double cry_cosh(double z) {return (exp (z) + exp (-z)) * 0.5;}
inline float cry_tanhf(float x)
{
	double expz = exp(double(x)), exp_z = exp(-double(x));
	return (expz-exp_z)/(expz+exp_z);
}
inline float cry_fabsf(float x) {return (float)fabs((double)x);}
inline float cry_expf(float x) {return (float)exp((double)x);}
inline float cry_logf(float x) {return (float)log((double)x);}
inline float cry_floorf(float x) {return (float)floor((double)x);}
inline float cry_tanf(float x) {return (float)tan((double)x);}
inline float cry_powf(float x, float y) {return (float) pow((double)x,(double)y);}
#else
inline float cry_sinf(float x) {return sinf(x);}
inline float cry_cosf(float x) {return cosf(x);}
inline float cry_fmod(float x, float y) {return (float)fmodf(x,y);}
inline float cry_ceilf(float x) {return ceilf(x);}
inline float cry_asinf(float x) {return asinf(x);}
inline float cry_acosf(float x) {return acosf(x);}
inline float cry_atanf(float x) {return atanf(x);}
inline float cry_atan2f(float x, float y) {return atan2f(x,y);}
inline float cry_sqrtf(float x) {return sqrtf(x);}
inline float cry_tanhf(float x) {return tanhf(x);}
inline float cry_fabsf(float x) {return fabsf(x);}
inline float cry_expf(float x) {return expf(x);}
inline float cry_logf(float x) {return logf(x);}
inline float cry_floorf(float x) {return floorf(x);}
inline float cry_tanf(float x) {return tanf(x);}
inline float cry_powf(float x, float y) {return powf(x,y);}
#endif



///////////////////////////////////////////////////////////////////////////////
// Typedef                                                                   //
///////////////////////////////////////////////////////////////////////////////
typedef double real;
typedef int index;

///////////////////////////////////////////////////////////////////////////////
// Definitions                                                               //
///////////////////////////////////////////////////////////////////////////////
const float gf_PI       =  3.14159265358979323846264338327950288419716939937510f; // pi
const float gf_PI_MUL_2 =  3.14159265358979323846264338327950288419716939937510f*2; // 2*pi
const float gf_PI_DIV_2 =  3.14159265358979323846264338327950288419716939937510f*0.5f; // pi/2
const float gf_DEGTORAD =  0.01745329251994329547f; // Degrees to Radians
const float gf_RADTODEG = 57.29577951308232286465f; // Radians to Degrees

// the check for compatibility with Max SDK: Max gfx.h header defines its own pi
#if !defined(_GFX_H_)
const real pi			= (real)3.1415926535897932384626433832795;
#endif
const real sqrt2	= (real)1.4142135623730950488016887242097;
const real sqrt3	= (real)1.7320508075688772935274463415059;

#define DEG2RAD( a ) ( (a) * (gf_PI/180.0f) )
#define RAD2DEG( a ) ( (a) * (180.0f/gf_PI) )
#define	VEC_EPSILON	0.01f


#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
#define MIN(a,b)    (((a) < (b)) ? (a) : (b))

//-----------------------------------------------------------------------

inline double cos_tpl(double op) { return cos(op); }
inline float  cos_tpl(float op) { return cry_cosf(op); }
inline double sin_tpl(double op) { return sin(op); }
inline float  sin_tpl(float op) { return cry_sinf(op); }
inline double acos_tpl(double op) { return acos(op); }
inline float  acos_tpl(float op) { return cry_acosf(op); }
inline double asin_tpl(double op) { return asin(op); }
inline float  asin_tpl(float op) { return cry_asinf(op); }
inline double atan_tpl(double op) { return atan(op); }
inline float  atan_tpl(float op) { return cry_atanf(op); }
inline double atan2_tpl(double op1,double op2) { return atan2(op1,op2); }
inline float  atan2_tpl(float op1,float op2) { return cry_atan2f(op1,op2); }
inline double exp_tpl(double op) { return exp(op); }
inline float  exp_tpl(float op) { return cry_expf(op); }
inline double log_tpl(double op) { return log(op); }
inline float  log_tpl(float op) { return cry_logf(op); }
inline double sqrt_tpl(double op) { return sqrt(op); }
inline float  sqrt_tpl(float op) { return cry_sqrtf(op); }
inline double fabs_tpl(double op) { return fabs(op); }
inline float  fabs_tpl(float op) { return cry_fabsf(op); }
inline int    fabs_tpl(int op) { int mask=op>>31; return op+mask^mask; }
inline int    floor_tpl(int op) {return op;}
inline float  floor_tpl(float op) {return cry_floorf(op);}
inline double floor_tpl(double op) {return floor(op);}
inline int    ceil_tpl(int op) {return op;}
inline float  ceil_tpl(float op) {return cry_ceilf(op);}
inline double ceil_tpl(double op) {return ceil(op);}
inline float  tan_tpl(float op) {return cry_tanf(op);}
inline double tan_tpl(double op) {return tan(op);}



template <class T> T clamp_tpl( T X, T Min, T Max )
{
	return X<Min ? Min : X<Max ? X : Max;
}

template<class F> inline F square(F fOp) { return(fOp*fOp); }

//this can easily be confused with square-root?
template<class F> inline F sqr(const F &op) { return op*op; }

template<class F> inline F cube(const F &op) { return op*op*op; }
template<class F> inline F sqr_signed(const F &op) { return op*fabs_tpl(op); }

#define ILINE __forceinline

#define cx csx[0] 
#define sx csx[1]
#define cy csy[0] 
#define sy csy[1]
#define cz csz[0] 
#define sz csz[1]

#include "Cry_Vector2.h"
#include "Cry_Vector3.h"
#include "Cry_Matrix.h"
#include "Cry_Quat.h"

#undef cx 
#undef sx
#undef cy 
#undef sy
#undef cz 
#undef sz



#if (defined(WIN32) || defined (_XBOX))
#include "Cry_XOptimise.h"
#endif

//#include "Cry_Intersection.h"


inline int sgnnz(double x) { 
	union { float f; int i; } u; 
	u.f=(float)x; return ((u.i>>31)<<1)+1;
}

inline int sgnnz(float x) { 
	union { float f; int i; } u; 
	u.f=x; return ((u.i>>31)<<1)+1;
}
inline int sgnnz(int x) {
	return ((x>>31)<<1)+1;
}

inline int sgn(double x) { 
	union { float f; int i; } u; 
	u.f=(float)x; return (u.i>>31)+((u.i-1)>>31)+1;
}

inline int sgn(float x) { 
	union { float f; int i; } u; 
	u.f=x; return (u.i>>31)+((u.i-1)>>31)+1;
}
inline int sgn(int x) {
	return (x>>31)+((x-1)>>31)+1;
}

inline int isneg(double x) { 
	union { float f; unsigned int i; } u; 
	u.f=(float)x; return (int)(u.i>>31);
}

inline int isneg(float x) { 
	union { float f; unsigned int i; } u; 
	u.f=x; return (int)(u.i>>31);
}

inline int isneg(int x) {
	return (int)((unsigned int)x>>31);
}

inline int isnonneg(double x) { 
	union { float f; unsigned int i; } u; 
	u.f=(float)x; return (int)(u.i>>31^1);
}

inline int isnonneg(float x) { 
	union { float f; unsigned int i; } u; 
	u.f=x; return (int)(u.i>>31^1);
}
inline int isnonneg(int x) {
	return (int)((unsigned int)x>>31^1);
}

inline int iszero(double x) { 
	union { float f; int i; } u; 
	u.f=(float)x; 
	u.i&=0x7FFFFFFF; 
	return -((u.i>>31)^(u.i-1)>>31);
}

inline int iszero(float x) { 
	union { float f; int i; } u; 
	u.f=x; u.i&=0x7FFFFFFF; return -(u.i>>31^(u.i-1)>>31);
}

inline int iszero(int x) {
	return -(x>>31^(x-1)>>31);
}

#ifdef WIN64
// AMD64 port: TODO: optimize
inline __int64 iszero(__int64 x) {
	return -(x>>63^(x-1)>>63);
}
#endif

template<class F> int inrange(F x, F end1, F end2) {
	return isneg(fabs_tpl(end1+end2-x*(F)2) - fabs_tpl(end1-end2));
}

template<class F> F cond_select(int bFirst, F op1,F op2) {
	F arg[2] = { op1,op2 };
	return arg[bFirst^1];
}

template<class F> int idxmax3(F *pdata) {
	int imax = isneg(pdata[0]-pdata[1]);
	imax |= isneg(pdata[imax]-pdata[2])<<1;
	return imax & (2|(imax>>1^1));
}

template<class F> int idxmin3(F *pdata) {
	int imin = isneg(pdata[1]-pdata[0]);
	imin |= isneg(pdata[2]-pdata[imin])<<1;
	return imin & (2|(imin>>1^1));
}






inline int getexp(float x) { return (int)(*(unsigned int*)&x>>23&0x0FF)-127; }
inline int getexp(double x) { return (int)(*((unsigned int*)&x+1)>>20&0x7FF)-1023; }

inline float &setexp(float &x,int iexp) { (*(unsigned int*)&x &= ~(0x0FF<<23)) |= (iexp+127)<<23; return x; }
inline double &setexp(double &x,int iexp) { (*((unsigned int*)&x+1) &= ~(0x7FF<<20)) |= (iexp+1023)<<20; return x; }





class unused_marker {
public:
	unused_marker() {}
	unused_marker& operator,(float &x) { *(int*)&x = 0xFFBFFFFF; return *this; }
	unused_marker& operator,(double &x) { *((int*)&x+1) = 0xFFF7FFFF; return *this; }
	unused_marker& operator,(int &x) { x=1<<31; return *this; }
	unused_marker& operator,(unsigned int &x) { x=1u<<31; return *this; }
	template<class ref> unused_marker& operator,(ref *&x) { x=(ref*)-1; return *this; }
	template<class F> unused_marker& operator,(Vec3_tpl<F> &x) { return *this,x.x; }
	template<class F> unused_marker& operator,(Quaternion_tpl<F> &x) { return *this,x.w; }
};
inline bool is_unused(const float &x) { return (*(int*)&x & 0xFFA00000) == 0xFFA00000; }
inline bool is_unused(int x) { return x==1<<31; }
inline bool is_unused(unsigned int x) { return x==1u<<31; }
template<class ref> bool is_unused(ref *x) { return x==(ref*)-1; }
template<class F> bool is_unused(const Vec3_tpl<F> &x) { return is_unused(x.x); }
template<class F> bool is_unused(const Quaternion_tpl<F> &x) { return is_unused(x.w); }
inline bool is_unused(const double &x) { return (*((int*)&x+1) & 0xFFF40000) == 0xFFF40000; }
#define MARK_UNUSED unused_marker(),


template<class dtype> class strided_pointer {
public:
	strided_pointer() { data=0; iStride=sizeof(dtype); }
	strided_pointer(dtype *pdata,int stride=sizeof(dtype)) { data=pdata; iStride=stride; }
	strided_pointer(const strided_pointer &src) { data=src.data; iStride=src.iStride; }
	template<class dtype1> strided_pointer(const strided_pointer<dtype1> &src) { data=src.data; iStride=src.iStride; }

	strided_pointer& operator=(dtype *pdata) { data=pdata; return *this; }
	strided_pointer& operator=(const strided_pointer<dtype> &src) { data=src.data; iStride=src.iStride; return *this; }
	template<class dtype1> strided_pointer& operator=(const strided_pointer<dtype1> &src) { data=src.data; iStride=src.iStride; return *this; }

	dtype& operator[](int idx) { return *(dtype*)((char*)data+idx*iStride); }
	const dtype& operator[](int idx) const { return *(const dtype*)((const char*)data+idx*iStride); }
	strided_pointer<dtype> operator+(int idx) const { return strided_pointer<dtype>((dtype*)((char*)data+idx*iStride),iStride); }
	strided_pointer<dtype> operator-(int idx) const { return strided_pointer<dtype>((dtype*)((char*)data-idx*iStride),iStride); }
	dtype& operator*() const { return *data; }
	operator void*() const { return (void*)data; }

	dtype *data;
	int iStride;
};

template<class F> F _condmax(F, int masknot) {
	return 1<<(sizeof(index)*8-2) & ~masknot;
}
inline float _condmax(float, int masknot) {
	return 1E30f*(masknot+1);
}

template<class F> int unite_lists(F *pSrc0,INT_PTR nSrc0, F *pSrc1,INT_PTR nSrc1, F *pDst,int szdst)	//AMD Port
{
	int i0,i1,n;
	INT_PTR inrange0=-nSrc0>>((sizeof(size_t)*8)-1),inrange1=-nSrc1>>((sizeof(size_t)*8)-1);			//AMD Port
	F a0,a1,ares,dummy=0;
	INT_PTR pDummy( (INT_PTR) &dummy );
	pSrc0 = (F*)((INT_PTR)pSrc0&inrange0 | pDummy&~inrange0); // make pSrc point to valid data even if nSrc is zero	//AMD Port
	pSrc1 = (F*)((INT_PTR)pSrc1&inrange1 | pDummy&~inrange1);									//AMD Port
	for(n=i0=i1=0; (inrange0 | inrange1) & n-szdst>>31; inrange0=(i0+=isneg(a0-ares-1))-nSrc0>>31, inrange1=(i1+=isneg(a1-ares-1))-nSrc1>>31) 
	{
		a0 = pSrc0[i0&inrange0] + _condmax(pSrc0[0],inrange0); //(1<<(sizeof(index)*8-2)&~inrange0);
		a1 = pSrc1[i1&inrange1] + _condmax(pSrc1[0],inrange1); //(1<<(sizeof(index)*8-2)&~inrange1);
		pDst[n++] = ares = min(a0,a1);
	}																				
	return n;
}

template<class F> int intersect_lists(F *pSrc0,int nSrc0, F *pSrc1,int nSrc1, F *pDst)
{
	int i0,i1,n; F ares;
	for(i0=i1=n=0; isneg(i0-nSrc0) & isneg(i1-nSrc1); i0+=isneg(pSrc0[i0]-ares-1),i1+=isneg(pSrc1[i1]-ares-1)) {
		pDst[n] = ares = min(pSrc0[i0],pSrc1[i1]); n += iszero(pSrc0[i0]-pSrc1[i1]);
	}
	return n;
}





#if defined(PHYSICS_EXPORTS) || (defined(WIN64) && defined(CRYENTITYDLL_EXPORTS))
#ifdef max
 #undef max
#endif
#ifdef min
 #undef min
#endif
inline double min(double op1,double op2) { return (op1+op2-fabs(op1-op2))*0.5; }
inline double max(double op1,double op2) { return (op1+op2+fabs(op1-op2))*0.5; }
inline float max(float op1,float op2) { return (op1+op2+fabsf(op1-op2))*0.5f; }
inline float min(float op1,float op2) { return (op1+op2-fabsf(op1-op2))*0.5f; }
inline int max(int op1,int op2) { return op1 - (op1-op2 & (op1-op2)>>31); }
inline int min(int op1,int op2) { return op2 + (op1-op2 & (op1-op2)>>31); }
inline double minmax(double op1,double op2,int bMax) { return (op1+op2+fabs(op1-op2)*(bMax*2-1))*0.5; } 
inline float minmax(float op1,float op2,int bMax) { return (op1+op2+fabsf(op1-op2)*(bMax*2-1))*0.5f; } 
inline int minmax(int op1,int op2,int bMax) {	return (op1&-bMax | op2&~-bMax) + ((op1-op2 & (op1-op2)>>31)^-bMax)+bMax; }
#endif

template<class F> inline F max_safe(F op1,F op2) { return op1>op2 ? op1:op2; }//int mask=isneg(op2-op1); return op1*mask+op2*(mask^1); }
template<class F> inline F min_safe(F op1,F op2) { return op1<op2 ? op1:op2; }//{ int mask=isneg(op1-op2); return op1*mask+op2*(mask^1); }


#ifndef PHYSICS_EXPORTS
#define VALIDATOR_LOG(pLog,str)
#define VALIDATORS_START
#define VALIDATOR(member)
#define VALIDATOR_NORM(member)
#define VALIDATOR_NORM_MSG(member,msg,member1)
#define VALIDATOR_RANGE(member,minval,maxval)
#define VALIDATOR_RANGE2(member,minval,maxval)
#define VALIDATORS_END
#endif


typedef struct VALUE16 {
	union {
		struct { unsigned char a,b; } c;
		unsigned short ab;
	};
} VALUE16;

inline unsigned short SWAP16(unsigned short l) {
	VALUE16 l16;
	unsigned char a,b;
	l16.ab=l;
 	a=l16.c.a;  b=l16.c.b;
 	l16.c.a=b; 	l16.c.b=a;
	return l16.ab;
}

//--------------------------------------------

typedef struct VALUE32 {
	union {
		struct { unsigned char a,b,c,d; } c;
		float FLOAT;
		unsigned long abcd;
		const void* ptr;
	};
} VALUE32;

inline unsigned long SWAP32(unsigned long l) {
	VALUE32 l32;
	unsigned char a,b,c,d;
	l32.abcd=l;
 	a=l32.c.a;  b=l32.c.b;  c=l32.c.c;  d=l32.c.d;
 	l32.c.a=d;	l32.c.b=c; 	l32.c.c=b; 	l32.c.d=a;
	return l32.abcd;
}

inline const void* SWAP32(const void* l) {
	VALUE32 l32;
	unsigned char a,b,c,d;
	l32.ptr=l;
 	a=l32.c.a;  b=l32.c.b;  c=l32.c.c;  d=l32.c.d;
 	l32.c.a=d;	l32.c.b=c; 	l32.c.c=b; 	l32.c.d=a;
	return l32.ptr;
}


inline float FSWAP32(float f) {
	VALUE32 l32;
	unsigned char a,b,c,d;
	l32.FLOAT=f;
	a=l32.c.a;  b=l32.c.b;  c=l32.c.c;  d=l32.c.d;
	l32.c.a=d;	l32.c.b=c; 	l32.c.c=b; 	l32.c.d=a;
	return l32.FLOAT;
}


#endif //math