#pragma once

#include <cassert>


namespace lm
{


template<typename T>
class tuple3
{
public:
	enum { NUM_ELEMENTS = 3 };


public:
	T x , y , z;


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


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


	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 tuple3<T>& src );
	void set( const T* _v );
	void set( const T& _x, const T& _y, const T& _z );
	tuple3<T>& operator=( const tuple3<T>& t );

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

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


typedef tuple3<double>        tuple3d;
typedef tuple3<float>         tuple3f;
typedef tuple3<int>           tuple3i;
typedef tuple3<unsigned int>  tuple3ui;
typedef tuple3<long>          tuple3l;
typedef tuple3<unsigned long> tuple3ul;


// tuple implements


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


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


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


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


}
