#pragma once

#include <cassert>


namespace lm
{

template<typename T>
class tuple4
{
public:
	enum { NUM_ELEMENTS = 4 };


public:
	T x , y , z , w ;


public:
	tuple4(void)                                               : x(     ), y(     ), z(     ), w(     ) {}
	tuple4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x   ), y(_y   ), z(_z   ), w(_w   ) {}
	tuple4(const tuple4<T>& t)                                 : x(t.x  ), y(t.y  ), z(t.z  ), w(t.w  ) {}
	tuple4(const T* _v)                                        : x(_v[0]), y(_v[1]), z(_v[2]), w(_v[3]) {}


	// 要素型が異なるtupleへのキャスト
	template<typename U>
	operator tuple4<U>(void) const
	{
		return tuple4<U>( static_cast<U>(x) , static_cast<U>(y) , static_cast<U>(z) , static_cast<U>(w) );
	}


	T*       v(void)       { return &x; }
	const T* v(void) const { return &x; }

	T&       operator[]( size_t idx )       { return v()[ idx ]; }
	const T& operator[]( size_t idx ) const { return v()[ idx ]; }

	T&       at( size_t idx );
	const T& at( size_t idx ) const;

	void set( const tuple4<T>& src );
	void set( const T* _v );
	void set( const T& _x, const T& _y, const T& _z, const T& _w );
	tuple4<T>& operator=( const tuple4<T>& t );

	void get( tuple3<T>& dst ) const;
	void get( T* _v ) const;
	void get( T& _x, T& _y, T& _z, T& _w ) const;

	bool equals( const tuple4<T>& t ) const;
	bool equals( const T* v ) const;
	bool equals( const T& _x, const T& _y, const T& _z, const T& _w ) const;
	bool operator==( const tuple4<T>& t ) const;
	bool operator!=( const tuple4<T>& t ) const;
};


typedef tuple4<double>        tuple4d;
typedef tuple4<float>         tuple4f;
typedef tuple4<int>           tuple4i;
typedef tuple4<unsigned int>  tuple4ui;
typedef tuple4<long>          tuple4l;
typedef tuple4<unsigned long> tuple4ul;


// tuple implements

template<typename T> inline
T& tuple4<T>::at( size_t idx )
{
	assert( idx < NUM_ELEMENTS );
	return v()[ idx ];
}
template<typename T> inline
const T& tuple4<T>::at( size_t idx ) const
{
	assert( idx < NUM_ELEMENTS );
	return v()[ idx ];
}


template<typename T> inline
void tuple4<T>::set( const tuple4<T>& src )
{
	set( src.x , src.y , src.z , src.w );
}
template<typename T> inline
void tuple4<T>::set( const T* _v )
{
	set( _v[0] , _v[1] , _v[2] , _v[3] );
}
template<typename T> inline
void tuple4<T>::set( const T& _x, const T& _y, const T& _z, const T& _w )
{
	x = _x ;  y = _y ;  z = _z ;  w = _w ;
}
template<typename T> inline
tuple4<T>& tuple4<T>::operator=( const tuple4<T>& t )
{
	set( t );
	return (*this);
}


template<typename T> inline
void tuple4<T>::get( tuple3<T>& dst ) const
{
	dst = *this;
}
template<typename T> inline
void tuple4<T>::get( T* _v ) const
{
	get( _v[0] , _v[1] , _v[2] , _v[3] );
}
template<typename T> inline
void tuple4<T>::get( T& _x, T& _y, T& _z, T& _w ) const
{
	_x = this->x ;  _y = this->y ;  _z = this->z ;  _w = this->w ;
}


template<typename T> inline
bool tuple4<T>::equals( const tuple4<T>& t ) const
{
	return equals( t.x , t.y , t.z , t.w );
}
template<typename T> inline
bool tuple4<T>::equals( const T* v ) const
{
	return equals( v[0] , v[1] , v[2] , v[3] );
}
template<typename T> inline
bool tuple4<T>::equals( const T& _x, const T& _y, const T& _z, const T& _w ) const
{
	return ( x == _x && y == _y && z == _z && w == _w );
}
template<typename T> inline
bool tuple4<T>::operator==( const tuple4<T>& t ) const
{
	return equals( t );
}
template<typename T> inline
bool tuple4<T>::operator!=( const tuple4<T>& t ) const
{
	return !equals( t );
}


}
