#ifndef TINY_VECDTOR_H
#define TINY_VECDTOR_H

#include <sstream> 
#include <istream>
#include <ostream>
#include <string>
#include <cmath>
#include <cassert>
#include <cfloat>
#include <cstring>

template <typename T, int N>
class TinyVector
{
public:

	// construction
	TinyVector()
	{
		reset_zero();
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(const TinyVector<T, N> &TV)
	{
		copy(TV.data_);
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(const T *V)
	{
		copy(V);
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(char *s)
	{
		std::istringstream ins(s);
		for (int i = 0; i < N; i++)
		{ ins >> data_[i]; }
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(const T x, const T y)
	{
		data_[0] = x;
		data_[1] = y;
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(const T x, const T y, const T z)
	{
		data_[0] = x;
		data_[1] = y;
		data_[2] = z;
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector(const T x, const T y, const T z, const T w)
	{
		data_[0] = x;
		data_[1] = y;
		data_[2] = z;
		data_[3] = w;
	}
	//////////////////////////////////////////////////////////////////////////
	~TinyVector()
	{
	}
	//////////////////////////////////////////////////////////////////////////
	void reset_zero()
	{
		memset(data_, 0, sizeof(T)*N);
	}
	//////////////////////////////////////////////////////////////////////////
	//access
	inline operator const T *() const
	{
		return data_;
	}
	//////////////////////////////////////////////////////////////////////////
	inline operator T *()
	{
		return data_;
	}
	//////////////////////////////////////////////////////////////////////////
	inline T &operator()(int i)
	{
		return data_[i - 1];
	}
	//////////////////////////////////////////////////////////////////////////
	inline const T &operator() (int i) const
	{
		return data_[i - 1];
	}
	//////////////////////////////////////////////////////////////////////////
	inline T &operator[](int i)
	{
		return data_[i];
	}
	//////////////////////////////////////////////////////////////////////////
	inline const T &operator[](int i) const
	{
		return data_[i];
	}
	//////////////////////////////////////////////////////////////////////////
	//assignment
	inline TinyVector<T, N> &operator=(const TinyVector<T, N> &A)
	{
		if (data_ == A.data_)
		{ return *this; }

		copy(A.data_);
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> &operator=(const T *A)
	{
		if (data_ == A)
		{ return *this; }

		copy(A);
		return *this;
	}
	////////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> &operator=(const T &scalar)
	{
		set(scalar);
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	// comparison
	bool operator== (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) == 0;
	}
	//////////////////////////////////////////////////////////////////////////
	bool operator!= (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) != 0;
	}
	//////////////////////////////////////////////////////////////////////////
	bool operator< (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) < 0;
	}
	//////////////////////////////////////////////////////////////////////////
	bool operator<= (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) <= 0;
	}
	//////////////////////////////////////////////////////////////////////////
	bool operator> (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) > 0;
	}
	//////////////////////////////////////////////////////////////////////////
	bool operator>= (const TinyVector<T, N> &rkV) const
	{
		return CompareArrays(rkV) >= 0;
	}
	//////////////////////////////////////////////////////////////////////////

	// arithmetic operations
	inline TinyVector<T, N> operator+ (const TinyVector<T, N> &rkV) const
	{
		TinyVector<T, N> tmp;
		for (int i = 0; i < N; i++)
		{
			tmp.data_[i] = data_[i] + rkV.data_[i];
		}
		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> operator- (const TinyVector<T, N> &rkV) const
	{
		TinyVector<T, N> tmp;
		for (int i = 0; i < N; i++)
		{
			tmp.data_[i] = data_[i] - rkV.data_[i];
		}
		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> operator* (T fScalar) const
	{
		TinyVector<T, N> tmp;
		for (int i = 0; i < N; i++)
		{
			tmp.data_[i] = fScalar * data_[i];
		}
		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> operator/ (T fScalar) const
	{
		TinyVector<T, N> tmp;

		if (fScalar != (T)0.0)
		{
			T fInvScalar = ((T)1.0) / fScalar;
			for (int i = 0; i < N; i++)
			{
				tmp.data_[i] = fInvScalar * data_[i];
			}
		}
		else
		{
			for (int i = 0; i < N; i++)
			{
				tmp.data_[i] = FLT_MAX;
			}
		}

		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> operator- () const
	{
		TinyVector<T, N> tmp;
		for (int i = 0; i < N; i++)
		{
			tmp.data_[i] = -data_[i];
		}
		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	// arithmetic updates
	inline TinyVector<T, N> &operator+= (const TinyVector<T, N> &rkV)
	{
		for (int i = 0; i < N; i++)
		{
			data_[i] += rkV.data_[i];
		}
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> &operator-= (const TinyVector<T, N> &rkV)
	{
		for (int i = 0; i < N; i++)
		{
			data_[i] -= rkV.data_[i];
		}
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> &operator*= (T fScalar)
	{
		for (int i = 0; i < N; i++)
		{
			data_[i] *= fScalar;
		}
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> &operator/= (T fScalar)
	{
		if (fScalar != (T)0.0)
		{
			T fInvScalar = ((T)1.0) / fScalar;
			for (int i = 0; i < N; i++)
			{
				data_[i] *= fInvScalar;
			}
		}
		else
		{
			for (int i = 0; i < N; i++)
			{
				data_[i] = FLT_MAX;
			}
		}
		return *this;
	}
	//////////////////////////////////////////////////////////////////////////
	// vector operations
	inline T Length () const
	{
		return sqrt(SquaredLength());
	}
	//////////////////////////////////////////////////////////////////////////
	inline T SquaredLength () const
	{
		T sum = (T)0.0;
		for (int i = 0; i < N; i++)
		{
			sum += data_[i] * data_[i];
		}
		return sum;
	}
	//////////////////////////////////////////////////////////////////////////
	inline T Dot (const TinyVector<T, N> &TV) const
	{
		T sum = (T)0.0;
		for (int i = 0; i < N; i++)
		{
			sum += data_[i] * TV.data_[i];
		}
		return sum;
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector<T, N> Cross(const TinyVector<T, N> &TV) const
	{
		assert(N > 1);

		TinyVector<T, N> tmp;

		if ( N == 2)
		{
			tmp[0] = data_[0] * TV.data_[1] - data_[1] * TV.data_[0];
			tmp[1] = 0;
		}
		else if ( N == 3)
		{
			tmp[0] = data_[1] * TV.data_[2] - data_[2] * TV.data_[1];
			tmp[1] = data_[2] * TV.data_[0] - data_[0] * TV.data_[2];
			tmp[2] = data_[0] * TV.data_[1] - data_[1] * TV.data_[0];
		}
		else if (N > 3)
		{
			for (int i = 0; i < N; i++)
			{
				int id1 = (i + 1) % N;
				int id2 = (i + 2) % N;
				tmp[i] = data_[id1] * TV.data_[id2] - data_[id2] * TV.data_[id1];
			}
		}

		return tmp;
	}
	//////////////////////////////////////////////////////////////////////////
	TinyVector<T, N> UnitCross(const TinyVector<T, N> &TV) const
	{
		return Cross(TV).GetNormalized();
	}
	//////////////////////////////////////////////////////////////////////////
	inline T Normalize ()
	{
		T fLength = Length();

		if (fLength != (T)0.0)
		{
			T fInvLength = ((T)1.0) / fLength;
			for (int i = 0; i < N; i++)
			{
				data_[i] *= fInvLength;
			}
		}
		else
		{
			fLength = (T)0.0;
			set((T)0.0);
		}

		return fLength;
	}
	//////////////////////////////////////////////////////////////////////////
	inline TinyVector<T, N> GetNormalized ()
	{
		T fLength = Length();

		TinyVector<T, N> tmp;

		if (fLength != 0)
		{
			T fInvLength = ((T)1.0) / fLength;
			for (int i = 0; i < N; i++)
			{
				tmp.data_[i] = fInvLength * data_[i];
			}
		}
		else
		{
			for (int i = 0; i < N; i++)
			{
				tmp.data_[i] = (T)0.0;
			}
		}

		return tmp;
	}

	//////////////////////////////////////////////////////////////////////////
	static void ComputeExtremes (int iVQuantity, const TinyVector<T, N> *akPoint,
		TinyVector<T, N> &rkMin, TinyVector<T, N> &rkMax)
	{
		assert(iVQuantity > 0 && akPoint);

		rkMin = akPoint[0];
		rkMax = rkMin;
		for (int i = 1; i < iVQuantity; i++)
		{
			const TinyVector<T, N> &rkPoint = akPoint[i];
			for (int j = 0; j < N; j++)
			{
				if (rkPoint[j] < rkMin[j])
				{
					rkMin[j] = rkPoint[j];
				}
				else if (rkPoint[j] > rkMax[j])
				{
					rkMax[j] = rkPoint[j];
				}
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////

private:
	//////////////////////////////////////////////////////////////////////////
	void copy(const T *V)
	{
		memcpy(data_, V, sizeof(T)*N);
	}
	//////////////////////////////////////////////////////////////////////////
	void set(const T &scalar)
	{
		for (int i = 0; i < N; i++)
		{
			data_[i] = scalar;
		}
	}
	//////////////////////////////////////////////////////////////////////////
	int CompareArrays (const TinyVector<T, N> &rkV) const
	{
		return memcmp(data_, rkV.data_, N * sizeof(T));
	}
	//////////////////////////////////////////////////////////////////////////
private:
	T data_[N];
};

//typedef TinyVector<double, 3> Vector3d;
typedef TinyVector<double, 2> Vector2d;
typedef TinyVector<float, 3> Vector3f;
typedef TinyVector<float, 2> Vector2f;
//////////////////////////////////////////////////////////////////////////
// arithmetic operations
template <typename T, int N>
TinyVector<T, N> operator* (T fScalar, const TinyVector<T, N> &rkV)
{
	TinyVector<T, N> tmp;
	for (int i = 0; i < N; i++)
	{
		tmp[i] = fScalar * rkV[i];
	}
	return tmp;
}
//////////////////////////////////////////////////////////////////////////
template <typename T, int N>
T operator* (const TinyVector<T, N> &rkU, const TinyVector<T, N> &rkV)
{
	return rkU.Dot(rkV);
}
//////////////////////////////////////////////////////////////////////////

template <typename T, int N>
std::ostream &operator<<(std::ostream &s, const TinyVector<T, N> &A)
{
	for (int i = 0; i < N - 1; i++)
	{ s  << A[i] << " "; }
	s << A[N - 1]; // <<"\n";
	return s;
}
//////////////////////////////////////////////////////////////////////////
template <typename T, int N>
std::istream &operator>>(std::istream &s, TinyVector<T, N> &A)
{
	for (int i = 0; i < N; i++)
	{ s >> A[i]; }
	return s;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
T dot(int N, const T *vec_x, const T *vec_y)
{
	T sum = 0;
	for (int i = 0; i < N; i++)
	{
		sum += vec_x[i] * vec_y[i];
	}
	return sum;
}
//////////////////////////////////////////////////////////////////////////
#endif