/*---------------------------------------------------------------
	FILE: CVectorTemplate.h

   Part of the MRPT Library
   ISA - Universidad de Malaga - http://www.isa.uma.es

   Autor: Antonio J. Ortiz de Galisteo (AJOGD)
  ---------------------------------------------------------------*/
#ifndef CVectorTemplate_H
#define CVectorTemplate_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CMatrixTemplateNumeric.h>

namespace UTILS
{
	/**  This template class provides the basic functionality for a general 1D any-size, resizable container of numerical or non-numerical elements.
	* NOTES:
	*		- This class is not serializable since it is a template.
	*		- First element index is "0".
	*		- Please DO NOT use as template class type any other class. It can be safely used the following types:
	*			- Elemental types (int,char,float,doble,...)
	*			- Data struct (Not classes!)
	*			- Any kind of pointers (user is responsible for allocating and freeing the memory addressed by pointers).
	*
	*/

	template <class T>
	class CVectorTemplate:public std::vector<T>
	{
	public:

#if !(defined(_MSC_VER) && (_MSC_VER==1200))
	/** Returns the size of the vector
	  */
    size_t size() const
    {
        return std::vector<T>::size();
    }
#endif

	/** This function extract a part of a vector. It's necessary initialize the output vector to desired size.
	*
	*/
		void  extract_vector(const size_t &index, std::vector<T> &out)
		{
			size_t	i;

			if (index + out.size() > size())
				THROW_EXCEPTION("extract_vector: Index out of bounds");

			for (i=index;i<index+out.size();i++)
                out[i-index] = (*this)[i];
		}
/*****************************************************AJOGD*********************************************************/
		/** This function extract a part of a vector. It's necessary initialize the output vector to desired size.
	*
	*/
		CVectorTemplate<T>  extract_vector(const size_t index, const unsigned int length)
		{
			if (index + length > size())
				THROW_EXCEPTION("extract_vector: Index out of bounds");

			size_t	i;
			CVectorTemplate<T> out;
			out.resize(length);

			for (i=index;i<index+length;i++)
                out[i-index] = (*this)[i];

			return out;
		}
/*******************************************************************************************************************/
	/** This function insert the vector "in" into in the vector from an index.
	*
	*/
		void  insert_vector(const size_t &index, const std::vector<T> &in)
		{
			size_t	i;

			if (index + in.size()>size())
				THROW_EXCEPTION("insert_vector: Index out of bounds");

			for (i=index;i<index+in.size();i++)
                (*this)[i] = in[i-index];
		}

	/** This function join two vectors in a new vector.
	*
	*/
		void  concatenate(const CVectorTemplate &first, const CVectorTemplate &second)
		{
			size_t i;
			(*this).resize(first.size()+second.size());
			for (i=0;i<first.size();i++)
                (*this)[i] = first[i];
			for (i=0;i<second.size();i++)
				(*this)[i+first.size()] = second[i];
		}


	/** This function split a vectors in a two new vector.
	*
	*/
		void  deconcatenate(CVectorTemplate &first, CVectorTemplate &second, const size_t &index)
		{
			if (index>size())
				THROW_EXCEPTION("Error in CVectorTemplate::deconcatenate. Index out of bounds");

			size_t i;

			first.resize(index);
			second.resize((*this).size()-index);
			for (i=0;i<index;i++)
                first[i] = (*this)[i];
			for (i=0;i<second.size();i++)
				second[i] = (*this)[i+index];
		}

/*************************************************AJOGD****************************************************************/
	/** This function find the maximun value of a vector and return this value and his index
	*
	*/
		void  find_max(size_t &index, T &val)
		{
			if ((*this).size()<1)
				THROW_EXCEPTION("vector without dimensions in CVectorTemplate::find_max");

			val=(*this)[0];
			index=0;
			for (size_t i=1; i<(*this).size();i++)
			{
				if (val<(*this)[i])
				{
					val=(*this)[i];
					index = i;
				}
			}
		}
/*************************************************AJOGD****************************************************************/
	/** This function find the minimun value of a vector and return this value and his index
	*
	*/
		void  find_min(size_t &index, T &val)
		{
			if ((*this).size()<1)
				THROW_EXCEPTION("vector without dimensions in CVectorTemplate::find_max");

			val=(*this)[0];
			index=0;
			for (size_t i=1; i<(*this).size();i++)
			{
				if (val>(*this)[i])
				{
					val=(*this)[i];
					index = i;
				}
			}
		}

/*************************************************AJOGD****************************************************************/
	/** This function find the maximun and minimun value of a vector and return this value and his index
	*
	*/
		void  find_min_max(size_t &index_min, size_t &index_max, T &min, T &max)
		{
			if ((*this).size()<1)
				THROW_EXCEPTION("vector without dimensions in CVectorTemplate::find_max");

			min=(*this)[0];
			max=(*this)[0];
			index_min=0;
			index_max=0;
			for (size_t i=1; i<(*this).size();i++)
			{
				if (min>(*this)[i])
				{
					min=(*this)[i];
					index_min = i;
				}
				else if (max<(*this)[i])
				{
					max=(*this)[i];
					index_max = i;
				}
			}
		}
/*************************************************AJOGD****************************************************************/
	/** This function make the vector absolute value for each element
	*
	*/
		void  abs()
		{
			for (size_t i=0; i<(*this).size();i++)
				if ((*this)[i] < 0)
					(*this)[i] *= -1;

		}
/*************************************************AJOGD****************************************************************/
	/** This function is a fast solution to read a vector from text, but isn't optimum
	*
	*/
		void  loadFromTextFile(const std::string &file)
		{
			CMatrixTemplateNumeric<T> aux;
			aux.loadFromTextFile(file);
			unsigned int row = aux.getRowCount();
			unsigned int col = aux.getColCount();
			if ((row!=1)&&(col!=1))
				THROW_EXCEPTION("Error loading vector from text file, isn't a vector");

			if (row==1)	//row vector
			{
				(*this).resize(col);
				for (size_t i=0;i<col;i++)
					(*this)[i] = aux(0,i);
			}
			else	//col vector
			{
				(*this).resize(row);
				for (size_t j=0;j<row;j++)
					(*this)[j] = aux(j,0);
			}
		}

/*************************************************AJOGD****************************************************************/
	/** This function is a fast solution to write a vector into a text file, but isn't optimum
	*
	*/
		void  saveToTextFile(const std::string &file)
		{
			CMatrixTemplateNumeric<T> aux(1,(*this).size());
			for (size_t i=0;i<(*this).size();i++)
				aux(0,i) = (*this)[i];
			aux.saveToTextFile(file);
		}

	/** Calculate the mean value of a vector
	*
	*/
		T  mean()
		{
			T sum=0.0;
			for (size_t i=0;i<(*this).size();i++)
				sum += (*this)[i];
			return sum/(*this).size();
		}

	/** Return the vector like a matrix with dimension [1 , vector-size]
	*
	*/
		CMatrixTemplateNumeric<T> likeMatrix()
		{
			CMatrixTemplateNumeric<T>	out;
			out.setSize(1,(*this).size());
			for( size_t i = 0; i < (*this).size() ; i++)
				out(0,i) = (*this)[i];
			return out;
		}
/********************************************OPERATORS*****************************************************************/
/** A template function for adding std::vector's having the same number of elements.
*/
		CVectorTemplate<T>  operator + (CVectorTemplate &b)
		{
			ASSERT_(this->size()==b.size());

			CVectorTemplate<T>	res;	res.assign(this->size(),0);
			typename std::vector<T>::iterator	it_a,it_b,it_res;

			for (it_a=this->begin(), it_b=b.begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_b++, it_res++)
				*it_res = *it_a + *it_b;
			return res;
		}


/** A template function for substracting std::vector's having the same number of elements.
*/
		CVectorTemplate<T>  operator - (CVectorTemplate &b)
		{
		ASSERT_(this->size()==b.size());

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_b,it_res;

		for (it_a=this->begin(), it_b=b.begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_b++, it_res++)
			*it_res = *it_a - *it_b;
		return res;
		}


/** A template function for scalar product (element by element, like .* in MATLAB) of std::vector's having the same number of elements.
*/
		CVectorTemplate<T>  operator * (CVectorTemplate &b)
		{
		ASSERT_(this->size()==b.size());

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_b,it_res;

		for (it_a=this->begin(), it_b=b.begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_b++, it_res++)
			*it_res = (*it_a) * (*it_b);
		return res;
		}


/** A template function for scalar division (element by element, like ./ in MATLAB) of std::vector's having the same number of elements.
*/
		CVectorTemplate<T>  operator / (CVectorTemplate &b)
		{
		ASSERT_(this->size()==b.size());

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_b,it_res;

		for (it_a=this->begin(), it_b=b.begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_b++, it_res++)
			*it_res = (*it_a) / (*it_b);
		return res;
		}


/** A template function for adding a scalar to a std::vector.
*/
		CVectorTemplate<T>  operator + (T b)
		{

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_res;

		for (it_a=this->begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_res++)
			*it_res = (*it_a) + b;
		return res;
		}

/** A template function for subtracting a scalar to a std::vector.
*/
		CVectorTemplate<T>  operator - (T b)
		{

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_res;

		for (it_a=this->begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_res++)
			*it_res = (*it_a) - b;
		return res;
		}

/** A template function for multiplying a scalar to a std::vector.
*/
		CVectorTemplate<T>  operator * (T b)
		{

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_res;

		for (it_a=this->begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_res++)
			*it_res = (*it_a) * b;
		return res;
		}

/** A template function for adding a scalar to a std::vector.
*/
		CVectorTemplate<T>  operator / (T b)
		{

		CVectorTemplate<T>	res;	res.assign(this->size(),0);
		typename std::vector<T>::iterator	it_a,it_res;

		for (it_a=this->begin(), it_res=res.begin(); it_a!=this->end(); it_a++, it_res++)
			*it_res = (*it_a) / b;
		return res;
		}

/** unary transpose operator
 */
        CMatrixTemplateNumeric<T>  operator ~ ()
		{
            CMatrixTemplateNumeric<T>	temp((*this).size(),1);
            for (size_t i=0; i < (*this).size(); i++)
                temp(i,0) = (*this)[i];
            return temp;
		}

	}; // end class



} // end namespace

#endif //__STD_Vector_H
