#ifndef __VECTOR_AMV
	#define __VECTOR_AMV

#include <limits>
#include "exception.h"
#include "allocator.h"
#include "iterator.h"
#include "algorithm.h"
#include "memoryAMV.h"

namespace amv	{

template <class type, class A=allocator<type> >
class vector {
public:
	//Tipos:
	typedef type * iterator;
	typedef const type * const_iterator;
	typedef A allocator_type;
	typedef A::size_type size_type;
	typedef A::difference_type difference_type;
	typedef A::pointer pointer;
	typedef A::const_pointer const_pointer;
	typedef A::reference reference;
	typedef A::const_reference const_reference;
	typedef A::value_type value_type;
	//typedef reverse_iterator<iterator,const_iterator,type> reverse_iterator;
	//typedef const_reverse_iterator<const_iterator,type> const_reverse_iterator;
	typedef reverse_iterator_pointer<iterator,type> reverse_iterator;
	typedef reverse_iterator_pointer<const_iterator, const type> const_reverse_iterator;


	//Iteradores:
	iterator begin();
	const_iterator begin() const;
	iterator end();
	const_iterator end() const;
	reverse_iterator rbegin();
	const_reverse_iterator rbegin() const;
	reverse_iterator rend();
	const_reverse_iterator rend() const;

	//Acceso a los elementos:
	reference operator[](size_type subscript);
	const_reference operator[](size_type subscript) const;
	reference at(size_type subscript);
	const_reference at(size_type subscript) const;
	reference front();
	const_reference front() const;
	reference back();
	const_reference back() const;

	//Constructores y otros:
	explicit vector(const A &sourceAllocator=A());
	explicit vector(size_type sourceSize, const type &sourceValue=type(), const A &sourceAllocator=A());
	//el compilador no entiende el tercer parámetro
	template <class In>
	vector(In itB, In itE/*, const A &sourceAllocator=A()*/):
	alloc(/*sourceAllocator*/), m_size(itE-itB), ptr(alloc.allocate(m_size))
		{
		uninitialized_copy(itB,itE,ptr,type());
		}
	vector(const vector<type,A> &v);
	~vector();
	vector<type,A> &operator=(const vector<type,A> &op2);
	template <class In>
	void assign(In first, In last)
		{
		clear();
		m_size=last-first;//distance
		ptr=alloc.allocate(m_size);
		uninitialized_copy(first,last,ptr,type());
		}

	template <class Size, class type2>
	void assign(Size n, const type2 &val=type2())
		{
		clear();
		m_size=n;
		ptr=alloc.allocate(m_size);
		type *pb=ptr, *pe=ptr+m_size;
		while(pb!=pe)
			{
			alloc.construct(pb,val);
			++pb;
			}
		}

//	void resizeI(size_type newSize, const type &pad=type());

	//Operaciones de pila y cola:
//	void push_front(const type &newObject);
//	void pop_front();
	void push_back(const type &newObject);
	void pop_back();

	//Operaciones de lista
	iterator insert(iterator pos, const type &val=type());
	void insert(iterator pos, size_type n, const type &val);
	template <class In>
	void insert(iterator pos, In first, In last)
		{
		size_type newSize=m_size+(last-first);
		type *pTemp=alloc.allocate(newSize), *pTemp1;
		pTemp1=uninitialized_copy(begin(),pos,pTemp,type());
		pTemp1=uninitialized_copy(first,last,pTemp1,type());
		uninitialized_copy(pos,end(),pTemp1,type());
		clear();
		m_size=newSize;
		ptr=pTemp;
		}
	iterator erase(iterator pos);
	iterator erase(iterator first, iterator last);
	void clear();

	//Tamaño y capacidad:
	size_type size() const;
	bool empty() const;
	size_type max_size() const;
	void resize(size_type newSize, const type &pad=type());
	size_type capacity() const;
	void reserve(size_type n);
	//operator bool() const;

	//Otras
	void swap(vector<type,A> &v)
		{
		swap(ptr,v.ptr);
		swap(m_size,v.m_size);
		}
	allocator_type get_allocator() const { return alloc; }
	bool operator==(const vector<type,A> &op2) const;
	bool operator!=(const vector<type,A> &op2) const;

protected:
	A alloc;
	size_type m_size;
	type *ptr;

};

//Iteradores:

template <class type, class A>
inline vector<type,A>::iterator vector<type,A>::begin()
{
return ptr;
}

template <class type, class A>
inline vector<type,A>::const_iterator vector<type,A>::begin() const
{
return static_cast<const_iterator>(ptr);
}

template <class type, class A>
inline vector<type,A>::iterator vector<type,A>::end()
{
return ptr+m_size;
}

template <class type, class A>
inline vector<type,A>::const_iterator vector<type,A>::end() const
{
return static_cast<const_iterator>(ptr+m_size);
}

template <class type, class A>
inline vector<type,A>::reverse_iterator vector<type,A>::rbegin()
{
return ptr+m_size;
}

template <class type, class A>
inline vector<type,A>::const_reverse_iterator vector<type,A>::rbegin() const
{
return static_cast<const_iterator>(ptr+m_size);
}

template <class type, class A>
inline vector<type,A>::reverse_iterator vector<type,A>::rend()
{
return ptr;
}

template <class type, class A>
inline vector<type,A>::const_reverse_iterator vector<type,A>::rend() const
{
return static_cast<const_iterator>(ptr);
}

//Acceso a los elementos:

template <class type, class A>
inline vector<type,A>::reference vector<type,A>::operator[](size_type subscript) 
{
return ptr[subscript];
}

template <class type, class A>
inline vector<type,A>::const_reference vector<type,A>::operator[](size_type subscript) const 
{
return ptr[subscript];
}

template <class type, class A>
inline vector<type,A>::reference vector<type,A>::at(size_type subscript) 
{
if(subscript<m_size)
	return ptr[subscript];
else
	throw out_of_range();
}

template <class type, class A>
inline vector<type,A>::const_reference vector<type,A>::at(size_type subscript) const 
{
if(subscript<m_size)
	return ptr[subscript];
else
	throw out_of_range();
}

template <class type, class A>
inline vector<type,A>::reference vector<type,A>::front()
{
return *ptr;
}

template <class type, class A>
inline vector<type,A>::const_reference vector<type,A>::front() const
{
return *ptr;
}

template <class type, class A>
inline vector<type,A>::reference vector<type,A>::back()
{
return (*(ptr+m_size-1));
}

template <class type, class A>
inline vector<type,A>::const_reference vector<type,A>::back() const
{
return (*(ptr+m_size-1));
}

//Constructores y otros:

template <class type, class A>
inline vector<type,A>::vector(const A &sourceAllocator):
alloc(sourceAllocator), m_size(0), ptr(0)
{ 

}

template <class type, class A>
inline vector<type,A>::vector(size_type sourceSize, const type &sourceValue, const A &sourceAllocator):
alloc(sourceAllocator), m_size(sourceSize), ptr(alloc.allocate(sourceSize)) 
{
type *pTemp=ptr, *pEnd=ptr+m_size;
while(pTemp!=pEnd)
	{
	alloc.construct(pTemp++,sourceValue);
	}

}

template <class type, class A>
inline vector<type,A>::vector(const vector<type,A> &v):
m_size(v.m_size), ptr(m_size? alloc.allocate(m_size): 0)
{
uninitialized_copy(v.ptr,v.ptr+v.m_size,ptr,type());
//copy(v.ptr,v.ptr+v.m_size,ptr);
}

template <class type, class A>
inline vector<type,A>::~vector()
{
type *pTemp=ptr, *pEnd=ptr+m_size;
while(pTemp!=pEnd)
	{
	alloc.destroy(pTemp++);
	}

alloc.deallocate(ptr);
}

template <class type, class A>
inline vector<type,A> &vector<type,A>::operator=(const vector<type,A> &op2)
{
if(this==&op2)
	return *this;

this->~vector<type,A>();
m_size=op2.m_size;
if(!m_size)
	{
	ptr=0;
	return *this;
	}
ptr=alloc.allocate(m_size);
uninitialized_copy(op2.ptr,op2.ptr+op2.m_size,ptr,type());

return *this;
}

/*
template <class type, class A>
inline void vector<type,A>::resizeI(size_type newSize, const type &pad)
{
if(!newSize)
	{
	alloc.deallocate(ptr);
	ptr=0;
	m_size=0;
	return;
	}
if(newSize==m_size)
	return;

size_type i;
if(!ptr)
	{
	*this=vector<type,A>(newSize,pad);
	return;
	}

type *pNewPtr=alloc.allocate(newSize), *pTemp=pNewPtr, *pTemp1=ptr;

pTemp+=newSize-1;
pTemp1+=m_size-1;

if(newSize>m_size)
	{
	for(i=0; i<m_size; ++i)
		{
		alloc.construct(pTemp,*pTemp1);
		--pTemp;
		--pTemp1;
		}
	for(; i<newSize; ++i)
		{
		alloc.construct(pTemp,pad);
		--pTemp;
		}
	}
else
	{
	for(i=0; i<newSize; ++i)
		{
		alloc.construct(pTemp,*pTemp1);
		--pTemp;
		--pTemp1;
		}
	}		

alloc.deallocate(ptr);
ptr=pNewPtr;
m_size=newSize;
}
*/

//Operaciones de pila y cola:
/*
template <class type, class A>
inline void vector<type,A>::push_front(const type &newObject)
{
type *newPtr=alloc.allocate(m_size+1);
type *pTemp1=newPtr+1, *pTemp2=ptr, *pEnd=ptr+m_size;
*newPtr=newObject;
while(pTemp2!=pEnd)
	*pTemp1++=*pTemp2++;
alloc.deallocate(ptr);
ptr=newPtr;
++m_size;
}

template <class type, class A>
inline void vector<type,A>::pop_front()
{
type *newPtr=alloc.allocate(--m_size);
type *pTemp1=newPtr, *pTemp2=ptr+1, *pEnd=newPtr+m_size;
while(pTemp1!=pEnd)
	*pTemp1++=*pTemp2++;
alloc.deallocate(ptr);
ptr=newPtr;
}
*/

template <class type, class A>
inline void vector<type,A>::push_back(const type &newObject)
{
type *newPtr=alloc.allocate(m_size+1);
type *pTemp1=newPtr, *pTemp2=ptr, *pEnd=ptr+m_size;
while(pTemp2!=pEnd)
	*pTemp1++=*pTemp2++;
*pTemp1=newObject;
alloc.deallocate(ptr);
ptr=newPtr;
++m_size;
}

template <class type, class A>
inline void vector<type,A>::pop_back()
{
type *newPtr=alloc.allocate(--m_size);
type *pTemp1=newPtr, *pTemp2=ptr, *pEnd=newPtr+m_size;
while(pTemp1!=pEnd)
	*pTemp1++=*pTemp2++;
alloc.deallocate(ptr);
ptr=newPtr;
}

//Operaciones de lista
template <class type, class A>
inline vector<type,A>::iterator insert(vector<type,A>::iterator pos, const type &val)
{
size_type newSize=m_size+1;
type *pTemp=alloc.allocate(newSize), *pTemp1;
pTemp1=uninitialized_copy(begin(),pos,pTemp,type());
alloc.construct(pTemp1,val);
++pTemp1;
uninitialized_copy(pos,end(),pTemp1,type());
clear();
m_size=newSize;
ptr=pTemp;
}

template <class type, class A>
inline void vector<type,A>::insert(vector<type,A>::iterator pos, size_type n, const type &val)
{
size_type newSize=m_size+n;
type *pTemp=alloc.allocate(newSize), *pTemp1;
pTemp1=uninitialized_copy(begin(),pos,pTemp,type());
while(n)
	{
	alloc.construct(pTemp1,val);
	++pTemp1;
	--n;
	}
uninitialized_copy(pos,end(),pTemp1,type());
clear();
m_size=newSize;
ptr=pTemp;
}

template <class type, class A>
inline vector<type,A>::iterator vector<type,A>::erase(vector<type,A>::iterator pos)
{
size_type newSize=m_size-1;
type *pTemp=alloc.allocate(newSize), *pTemp1;
pTemp1=uninitialized_copy(begin(),pos,pTemp,type());
uninitialized_copy(pos+1,end(),pTemp1,type());
clear();
m_size=newSize;
ptr=pTemp;
}

template <class type, class A>
inline vector<type,A>::iterator vector<type,A>::erase(vector<type,A>::iterator first, vector<type,A>::iterator last)
{
size_type newSize=m_size-(last-first);
type *pTemp=alloc.allocate(newSize), *pTemp1;
pTemp1=uninitialized_copy(begin(),first,pTemp,type());
uninitialized_copy(last,end(),pTemp1,type());
clear();
m_size=newSize;
ptr=pTemp;
}

template <class type, class A>
inline void vector<type,A>::clear()
{
this->~vector<type,A>();
m_size=0;
ptr=0;
}

//Tamaño:
template <class type, class A>
inline vector<type,A>::size_type vector<type,A>::size() const
{
return m_size;
}

template <class type, class A>
inline bool vector<type,A>::empty() const
{
return m_size==0;
}

template <class type, class A>
inline vector<type,A>::size_type vector<type,A>::max_size() const
{
return (std::numeric_limits<size_type>::max());
}

template <class type, class A>
inline void vector<type,A>::resize(size_type newSize, const type &pad)
{
if(!newSize)
	{
	alloc.deallocate(ptr);//y destruir?, tal vez clear()
	ptr=0;
	m_size=0;
	return;
	}
if(newSize==m_size)
	return;

size_type i;
if(!ptr)
	{
	*this=vector<type,A>(newSize,pad);
	return;
	}

type *pNewPtr=alloc.allocate(newSize), *pTemp=pNewPtr, *pTemp1=ptr;

if(newSize>m_size)
	{
	for(i=0; i<m_size; ++i)
		{
		alloc.construct(pTemp,*pTemp1);
		++pTemp;
		++pTemp1;
		}
	for(;i<newSize; ++i)
		{
		alloc.construct(pTemp,pad);
		++pTemp;
		}
	}
else
	{
	for(i=0; i<newSize; ++i)
		{
		alloc.construct(pTemp,*pTemp1);
		++pTemp;
		++pTemp1;
		}
	}		

alloc.deallocate(ptr);
ptr=pNewPtr;
m_size=newSize;
}

template <class type, class A>
inline vector<type,A>::size_type vector<type,A>::capacity() const
{
return m_size;
}

template <class type, class A>
inline void vector<type,A>::reserve(size_type n)
{

}

/*
template <class type, class A>
inline vector<type,A>::operator bool() const
{
return m_size;
}
*/

template <class type, class A>
inline bool vector<type,A>::operator==(const vector<type,A> &op2) const
{
if(m_size!=op2.m_size)
	return false;
for(size_type i=0; i<m_size; ++i)
	{
	if(ptr[i]!=op2.ptr[i])
		return false;
	}

return true;
}

template <class type, class A>
inline bool vector<type,A>::operator!=(const vector<type,A> &op2) const
{
return !(*this==op2);
}

}

#endif