//Vector.h

#define _USE_MATH_DEFINES
#include <math.h>

#pragma once

inline float sqr(float x)
{
	return x*x;
}

inline float randf(float a, float b)
{
	if(b > a)
		return a + (b-a)*rand()/(float)RAND_MAX;
	else
		return (a + b) / 2;
}

///
/// 2-dimensional vector
///
class CVector2 
{
public:
	float X, Y;
	///
	/// Constructs CVector2 initializing X, Y by x, y
	///
	CVector2(float x, float y)
	{
		X = x;
		Y = y;
	}
	///
	/// Constructs CVector2 initializing X, Y by default
	///
	CVector2()
	{
		X = 0;
		Y = 0;
	}

	///
	/// Vector addition
	///
	__forceinline CVector2 operator + (CVector2 &V)
	{
		return CVector2(V.X + X, V.Y + Y);
	}

	///
	/// Vector subtraction
	///
	__forceinline CVector2 operator - (CVector2 &V)
	{
		return CVector2(V.X - X, V.Y - Y);
	}
	///
	/// Get vector which is simmetrical to this by point (0, 0)
	///
	__forceinline CVector2 operator - ()
	{
		return CVector2(-X, -Y);
	}
	///
	/// Divide vector on a floating number
	///
	__forceinline CVector2 operator / (float k)
	{
		return CVector2(X / k, Y / k);
	}
	__forceinline float operator ~()
	///
	/// Get vector length
	///
	{
		return sqrt(X*X + Y*Y);
	}
	__forceinline CVector2 operator !()
	///
	/// Normalize vector
	///
	{
		float d = sqrt(X*X + Y*Y);
		return CVector2(X/d, Y/d);
	}
};

///
/// Multiply vector on a number
///
__forceinline CVector2 operator * (CVector2 &V, float L)
{
	return CVector2(V.X * L, V.Y * L);
}

///
/// Multiply number on a vector
///
__forceinline CVector2 operator * (float L, CVector2 &V)
{
	return CVector2(V.X * L, V.Y * L);
}

///
/// Scalar Multiply of two vectors
///
__forceinline CVector2 operator * (CVector2 &V1, CVector2 &V2)
{
	return CVector2(V1.X*V2.X, V1.Y*V2.Y);
}

///
/// 3-dimensional vector
///
class CVector3;

///
/// Multiply number on a vector
///
__forceinline CVector3 operator * (float L, CVector3 &V);
///
/// Multiply vector on a number
///
__forceinline CVector3 operator * (CVector3 &V, float L);

///
/// Get cos of angle between vectors
///
__forceinline float cos(CVector3 &V1, CVector3 &V2);


///
/// 3-dimensional vector
///
class CVector3
{
public:
	float X, Y, Z;
	///
	/// Constructs CVector3 initializing X, Y, Z by x, y, z
	///
	CVector3(float x, float y, float z)
	{
		X = x;
		Y = y;
		Z = z;
	}
	///
	/// Constructs CVector3 initializing X, Y, Z as 2-dimensional vector by x, y
	///
	CVector3(float x, float y)
	{
		X = x;
		Y = y;
		Z = 0;
	}
	///
	/// Constructs CVector3 initializing X, Y, Z as in vector V
	///
	CVector3(CVector3 &V)
	{
		X = V.X;
		Y = V.Y;
		Z = V.Z;
	}
	///
	/// Constructs CVector3 initializing X, Y, Z as in 2-dimensional vector V
	///
	CVector3(CVector2 &V)
	{
		X = V.X;
		Y = V.Y;
		Z = 0;
	}
	///
	/// Constructs CVector3 initializing X, Y, Z by default
	///
	CVector3()
	{
		X = 0;
		Y = 0;
		Z = 0;
	}

	bool operator == (CVector3 &V)
	{
		return (X == V.X) && (Y == V.Y) && (Z == V.Z);
	}

	bool operator != (CVector3 &V)
	{
		return (X != V.X) || (Y != V.Y) || (Z != V.Z);
	}

	///
	/// Vector addition
	///
	__forceinline CVector3 operator + (CVector3 &V)
	{
		return CVector3(V.X + X, V.Y + Y, V.Z + Z);
	}

	///
	/// Add vector to this
	///
	__forceinline void operator += (CVector3 &V)
	{
		X += V.X;
		Y += V.Y;
		Z += V.Z;
	}

	///
	/// Vector subtraction
	///
	__forceinline CVector3 operator - (CVector3 &V)
	{
		return CVector3(X - V.X, Y - V.Y, Z - V.Z);
	}

	///
	/// Subtract vector from this
	///
	__forceinline void operator -= (CVector3 &V)
	{
		X -= V.X;
		Y -= V.Y;
		Z -= V.Z;
	}

	///
	/// Scale vector by floating number
	///
	__forceinline void operator *= (float k)
	{
		X *= k;
		Y *= k;
		Z *= k;
	}

	///
	/// Get vector which is simmetrical to this by point (0, 0, 0)
	///
	__forceinline CVector3 operator - ()
	{
		return CVector3(-X, -Y, -Z);
	}

	///
	/// Get vector length
	///
	float operator ~()
	{
		return sqrt(X*X + Y*Y + Z*Z);
	}

	float Length()
	{
		return sqrt(X*X + Y*Y + Z*Z);
	}

	float LengthSqr()
	{
		return X*X + Y*Y + Z*Z;
	}

	///
	/// Normalize vector
	///
	CVector3 operator !()
	{
		float d = sqrt(X*X + Y*Y + Z*Z);
		return CVector3(X/d, Y/d, Z/d);
	}

	__forceinline void operator |=(CVector3 &V)
	{
		X *= V.X;
		Y *= V.Y;
		Z *= V.Z;
	}

	///
	/// Scalar multiply two vectors
	///
	__forceinline float operator * (CVector3 &V)
	{
		return X*V.X + Y*V.Y + Z*V.Z;
	}

	///
	/// Cross product of two vectors
	///
	__forceinline CVector3 operator & (CVector3 &V)
	{
		return CVector3(Y*V.Z - Z*V.Y, Z*V.X - X*V.Z, X*V.Y - Y*V.X);
	}
	
	///
	/// Rotate vector around OX
	///
	void RotateX(float Ang)
	{
		float cAng = cos(Ang), sAng = sin(Ang);
		float Ytmp = Y*cAng + Z*sAng;
		Z = -Y*sAng + Z*cAng;
		Y = Ytmp;
	}
	///
	/// Rotate vector around OY
	///
	void RotateY(float Ang)
	{
		float cAng = cos(Ang), sAng = sin(Ang);
		float Xtmp = X*cAng + Z*sAng;
		Z = -X*sAng + Z*cAng;
		X = Xtmp;
	}
	///
	/// Rotate vector around OZ
	///
	void RotateZ(float Ang)
	{
		float cAng = cos(Ang), sAng = sin(Ang);
		float Xtmp = X*cAng + Y*sAng;
		Y = -X*sAng + Y*cAng;
		X = Xtmp;
	}
	///
	/// Rotate vector around vector V
	///
	void RotateAround(CVector3 &V, float Ang)
	{
		CVector3 Vn, VCos, VSin;

		Vn = (*this) * cos (V, *this);
		VCos = (*this) - Vn;
		VSin = V & VCos;

		*this = Vn + cos(Ang) * VCos + sin(-Ang) * VSin;
	}
};

__forceinline CVector3 operator | (CVector3 &V1, CVector3 &V2)
{
	return CVector3(V1.X*V2.X, V1.Y*V2.Y, V1.Z*V2.Z);
}

__forceinline CVector3 operator * (CVector3 &V, float L)
{
	return CVector3(V.X * L, V.Y * L, V.Z * L);
}

__forceinline CVector3 operator * (float L, CVector3 &V)
{
	return CVector3(V.X * L, V.Y * L, V.Z * L);
}

__forceinline CVector3 operator / (CVector3 &V, float L)
{
	return CVector3(V.X / L, V.Y / L, V.Z / L);
}

__forceinline float cos(CVector3 &V1, CVector3 &V2)
{
	return (V1*V2)/((~V1)*(~V2));
}

__forceinline CVector3 Mirror(CVector3 &V, CVector3 &N)
{
	return V - 2*N*(N*V);
}

///
/// 4-dimensional vector
///
class CVector4 : public CVector3
{
public:
	float W;
	///
	/// Constructs CVector4 initializing X, Y, Z, W by x, y, z, w
	///
	CVector4(float x, float y, float z, float w)
	{
		X = x;
		Y = y;
		Z = z;
		W = w;
	}
	///
	/// Constructs CVector4 initializing X, Y, Z, W as a 3-dimensional vector by x, y, z
	///
	CVector4(float x, float y, float z)
	{
		X = x;
		Y = y;
		Z = z;
		W = 0;
	}
	///
	/// Constructs CVector4 initializing X, Y, Z, W by default
	///
	CVector4()
	{
		X = 0;
		Y = 0;
		Z = 0;
		W = 0;
	}
	///
	/// Constructs CVector4 initializing X, Y, Z as in 3-dimensional vector V
	///
	CVector4(CVector3& V)
	{
		X = V.X;
		Y = V.Y;
		Z = V.Z;
		W = 0;
	}
};

#define _0 CVector3()
#define _1 CVector3(1.0f, 1.0f, 1.0f)
#define _L(a) CVector3((float)a, (float)a, (float)a)
#define _V(x, y, z) CVector3((float)x, (float)y, (float)z)
#define _X CVector3(1.0f, 0.0f, 0.0f)
#define _Y CVector3(0.0f, 1.0f, 0.0f)
#define _Z CVector3(0.0f, 0.0f, 1.0f)
#define _LX(a) CVector3((float)a, 0.0f, 0.0f)
#define _LY(a) CVector3(0.0f, (float)a, 0.0f)
#define _LZ(a) CVector3(0.0f, 0.0f, (float)a)