/*
*************************************************************************************************

任意型可変サイズ３次元行列。

基本的には std::vector を拡張しただけ。

operator()( x , y , z ) で３次元ベクターを簡単に利用できる。

*************************************************************************************************
*/


#pragma once

#include <vector>

#include <cassert>


namespace lm
{

//! 3D元版vectorのようなクラス
template<typename T>
class cubic_matrix
{
public:
	cubic_matrix(void);
	cubic_matrix(size_t num_x, size_t num_y, size_t num_z);
	cubic_matrix(size_t num_x, size_t num_y, size_t num_z, const T& initial_value);
	cubic_matrix(const cubic_matrix& m);

	T* v();
	const T* v() const;

	T& at(size_t idx_v);
	const T& at(size_t idx_v) const;
	T& operator[](size_t idx_v);
	const T& operator[](size_t idx_v) const;
	T& operator()(size_t idx_v);
	const T& operator()(size_t idx_v) const;

	T& at(size_t idx_x, size_t idx_y, size_t idx_z);
	const T& at(size_t idx_x, size_t idx_y, size_t idx_z) const;
	T& operator()(size_t idx_x, size_t idx_y, size_t idx_z);
	const T& operator()(size_t idx_x, size_t idx_y, size_t idx_z) const;

	void resize(size_t num_x, size_t num_y, size_t num_z);
	void resize(size_t num_x, size_t num_y, size_t num_z, const T& initial_value);

	void clear(void);

	size_t size(void) const;
	size_t size_x(void) const;
	size_t size_y(void) const;
	size_t size_z(void) const;

	bool empty(void) const;

	cubic_matrix& operator=(const cubic_matrix& m);

	bool operator==(const cubic_matrix& m);
	bool operator!=(const cubic_matrix& m);

	typedef typename std::vector<T>::iterator               iterator;
	typedef typename std::vector<T>::const_iterator         const_iterator;
	typedef typename std::vector<T>::reverse_iterator       reverse_iterator;
	typedef typename std::vector<T>::const_reverse_iterator const_reverse_iterator;
	iterator               begin  (void);
	iterator               end    (void);
	reverse_iterator       rbegin (void);
	reverse_iterator       rend   (void);
	const_iterator         begin  (void) const;
	const_iterator         end    (void) const;
	const_reverse_iterator rbegin (void) const;
	const_reverse_iterator rend   (void) const;

	void swap(cubic_matrix<T>& m);

protected:
	size_t m_size_x;
	size_t m_size_y;
	size_t m_size_z;
	std::vector<T> buf;
};


// implement

template<typename T> inline
cubic_matrix<T>::cubic_matrix(void)
	: m_size_x( 0 )
	, m_size_y( 0 )
	, m_size_z( 0 ) 
{}

template<typename T> inline
cubic_matrix<T>::cubic_matrix(size_t num_x, size_t num_y, size_t num_z)
	: m_size_x( num_x )
	, m_size_y( num_y )
	, m_size_z( num_z ) 
{
	buf.resize( m_size_x * m_size_y * m_size_z );
}

template<typename T> inline
cubic_matrix<T>::cubic_matrix(size_t num_x, size_t num_y, size_t num_z, const T& initial_value)
	: m_size_x( num_x )
	, m_size_y( num_y )
	, m_size_z( num_z ) 
{
	buf.resize( m_size_x * m_size_y * m_size_z );
	std::fill( buf.begin() , buf.end() , initial_value );
}


template<typename T> inline
T* cubic_matrix<T>::v()
{
	if( buf.empty() ) return NULL    ;
	else              return &buf[0] ;
}
template<typename T> inline
const T* cubic_matrix<T>::v() const
{
	if( buf.empty() ) return NULL    ;
	else              return &buf[0] ;
}


template<typename T> inline
T& cubic_matrix<T>::at(size_t idx_v)
{
	return buf[idx_v];
}
template<typename T> inline
const T& cubic_matrix<T>::at(size_t idx_v) const
{
	return buf[idx_v];
}
template<typename T> inline
T& cubic_matrix<T>::operator[](size_t idx_v)
{
	return at(idx_v);
}
template<typename T> inline
const T& cubic_matrix<T>::operator[](size_t idx_v) const
{
	return at(idx_v);
}
template<typename T> inline
T& cubic_matrix<T>::operator()(size_t idx_v)
{
	return at(idx_v);
}

template<typename T> inline
const T& cubic_matrix<T>::operator()(size_t idx_v) const
{
	return at(idx_v);
}


template<typename T> inline
T& cubic_matrix<T>::at(size_t idx_x, size_t idx_y, size_t idx_z)
{
#ifdef _DEBUG
	// Matrix subscript out of range
	assert( idx_x < m_size_x && idx_y < m_size_y && idx_z < m_size_z );
#endif
	return buf[ idx_x + m_size_x * ( idx_y + m_size_y * idx_z ) ];
}
template<typename T> inline
const T& cubic_matrix<T>::at(size_t idx_x, size_t idx_y, size_t idx_z) const
{
#ifdef _DEBUG
	// Matrix subscript out of range
	assert( idx_x < m_size_x && idx_y < m_size_y && idx_z < m_size_z );
#endif
	return buf[ idx_x + m_size_x * ( idx_y + m_size_y * idx_z ) ];
}
template<typename T> inline
T& cubic_matrix<T>::operator()(size_t idx_x, size_t idx_y, size_t idx_z)
{
	return at( idx_x , idx_y , idx_z );
}
template<typename T> inline
const T& cubic_matrix<T>::operator()(size_t idx_x, size_t idx_y, size_t idx_z) const
{
	return at( idx_x , idx_y , idx_z );
}
template<typename T> inline
cubic_matrix<T>::cubic_matrix(const cubic_matrix& m)
{
	buf = m.buf;
	m_size_x = m.size_x();
	m_size_y = m.size_y();
	m_size_z = m.size_z();
}


template<typename T> inline
void cubic_matrix<T>::resize(size_t num_x, size_t num_y, size_t num_z)
{
	m_size_x = num_x ;
	m_size_y = num_y ;
	m_size_z = num_z ;
	buf.resize( m_size_x * m_size_y * m_size_z );
}
template<typename T> inline
void cubic_matrix<T>::resize(size_t num_x, size_t num_y, size_t num_z, const T& initial_value)
{
	m_size_x = num_x ;
	m_size_y = num_y ;
	m_size_z = num_z ;
	buf.resize( m_size_x * m_size_y * m_size_z );
	std::fill( buf.begin() , buf.end() , initial_value );
}


template<typename T> inline
void cubic_matrix<T>::clear(void)
{
	m_size_x = 0;
	m_size_y = 0;
	m_size_z = 0;
	buf.clear();
}


template<typename T> inline
size_t cubic_matrix<T>::size(void) const
{
	return buf.size();
}
template<typename T> inline
size_t cubic_matrix<T>::size_x(void) const
{
	return m_size_x;
}
template<typename T> inline
size_t cubic_matrix<T>::size_y(void) const
{
	return m_size_y;
}
template<typename T> inline
size_t cubic_matrix<T>::size_z(void) const
{
	return m_size_z;
}

template<typename T> inline
bool cubic_matrix<T>::empty(void) const
{
	return buf.empty();
}


template<typename T> inline
cubic_matrix<T>& cubic_matrix<T>::operator=(const cubic_matrix& m)
{
	buf = m.buf;
	m_size_x = m.size_x();
	m_size_y = m.size_y();
	m_size_z = m.size_z();
	return (*this);
}


template<typename T> inline
bool cubic_matrix<T>::operator==(const cubic_matrix& m)
{
	if( m_size_x != m.m_size_x || m_size_y != m.m_size_y || m_size_z != m.m_size_z ) return false;
	for(size_t i=0;i<m.size();i++) if( (*this)(i) != m(i) ) return false;
	return true;
}
template<typename T> inline
bool cubic_matrix<T>::operator!=(const cubic_matrix& m)
{
	return !( (*this) == m );
}


template<typename T> inline
typename cubic_matrix<T>::iterator cubic_matrix<T>::begin(void)
{
	return buf.begin();
}
template<typename T> inline
typename cubic_matrix<T>::iterator cubic_matrix<T>::end(void)
{
	return buf.end();
}
template<typename T> inline
typename cubic_matrix<T>::reverse_iterator cubic_matrix<T>::rbegin(void)
{
	return buf.rbegin();
}
template<typename T> inline
typename cubic_matrix<T>::reverse_iterator cubic_matrix<T>::rend(void)
{
	return buf.rend();
}
template<typename T> inline
typename cubic_matrix<T>::const_iterator cubic_matrix<T>::begin(void) const
{
	return buf.begin();
}
template<typename T> inline
typename cubic_matrix<T>::const_iterator cubic_matrix<T>::end(void) const
{
	return buf.end();
}
template<typename T> inline
typename cubic_matrix<T>::const_reverse_iterator cubic_matrix<T>::rbegin(void) const
{
	return buf.rbegin();
}
template<typename T> inline
typename cubic_matrix<T>::const_reverse_iterator cubic_matrix<T>::rend(void) const
{
	return buf.rend();
}

template<typename T> inline
void cubic_matrix<T>::swap(cubic_matrix<T>& m)
{
	std::swap(m_size_x, m.m_size_x);
	std::swap(m_size_y, m.m_size_y);
	std::swap(m_size_z, m.m_size_z);
	buf.swap(m.buf);
}


}
