#ifndef __DEQUE_AMV
	#define __DEQUE_AMV

#include "exception.h"
#include "allocator.h"
#include "iterator.h"
#include "algorithm.h"
#include "memoryAMV.h"

namespace amv	{

//*********************** Arreglo Flexible *********************//
/*
Este arreglo está optimizado para el redimensionamiento, pues no se
pierde tanto tiempo en copia de elementos como con un arreglo típico.
En realidad funciona como una cola de doble extremo.
*/

#define PAGE_SIZE 4096
#define OBJECTS_FOR_BLOCK (PAGE_SIZE<sizeof(type)? 1: PAGE_SIZE/sizeof(type))

template <class type, class A=allocator<type> >
class deque {
public:
	//Tipos:
	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;

	class const_iterator;
	class iterator:	public
	RanIt<value_type,difference_type,pointer,reference>	{
		friend class deque<type,A>;
		friend class const_iterator;
		type *pObject, *blockBegin, *blockEnd;
		type **pBlocks;

		iterator(type **newPBlocks):
		pObject(*newPBlocks),
		blockBegin(pObject),
		blockEnd(blockBegin+OBJECTS_FOR_BLOCK),
		pBlocks(newPBlocks)
			{
			}
		iterator(type *pSourceObject, type **newPBlocks):
		pObject(pSourceObject),
		blockBegin(*newPBlocks),
		blockEnd(blockBegin+OBJECTS_FOR_BLOCK),
		pBlocks(newPBlocks)
			{
			}
	public:/*
		typedef random_access_iterator_tag iterator_category;
		typedef value_type value_type;
		typedef difference_type difference_type;
		typedef pointer pointer;
		typedef reference reference;*/

		iterator(): pObject(0),
		blockBegin(0), blockEnd(0), pBlocks(0)
			{
			}
		type &operator *() const
			{
			return *pObject;
			}
		type *operator->() const
			{ 
			return pObject;
			}
		iterator &operator ++()
			{
			if(++pObject==blockEnd)
				{
				pObject=blockBegin=*++pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				}
			return *this;
			}
		iterator operator ++(int i)
			{
			iterator temp(*this);
			++(*this);
			return temp;
			}
		iterator &operator --()
			{
			if(pObject==blockBegin)
				{
				blockBegin=*--pBlocks;
				pObject=blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				}
			--pObject;
			return *this;
			}
		iterator operator --(int i)
			{
			iterator temp(*this);
			--(*this);
			return temp;
			}
		iterator &operator+=(difference_type n)
			{//se supone n>=0
			type *p=pObject+n;
			if(p>=blockEnd)
				{
				difference_type blocksDifference=((p-blockBegin)/OBJECTS_FOR_BLOCK);
				difference_type offset=(p-blockBegin)%OBJECTS_FOR_BLOCK;

				pBlocks+=blocksDifference;
				blockBegin=*pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				pObject=blockBegin+offset;
				}
			else
				pObject=p;

			return *this;
			}
		iterator &operator-=(difference_type n)
			{//se supone n>=0
			type *p=pObject-n;
			if(p<blockBegin)
				{
				difference_type m=(blockBegin-p)%OBJECTS_FOR_BLOCK;
				difference_type blocksDifference=((blockBegin-p)/OBJECTS_FOR_BLOCK)+(m? 1: 0);
				difference_type offset=m? OBJECTS_FOR_BLOCK-m: 0;

				pBlocks-=blocksDifference;
				blockBegin=*pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				pObject=blockBegin+offset;
				}
			else
				pObject=p;

			return *this;
			}
		iterator operator+(difference_type n) const
			{
			iterator it(*this);
			return (it+=n);
			}
		iterator operator-(difference_type n) const
			{
			iterator it(*this);
			return (it-=n);
			}
		difference_type operator-(const iterator &it) const
			{
			return (pBlocks==it.pBlocks? pObject-it.pObject:
					OBJECTS_FOR_BLOCK*(pBlocks-it.pBlocks-1)+
					(pObject-blockBegin)+(it.blockEnd-it.pObject));
			}
		reference operator[](difference_type n) const
			{
			return (*(*this+n));
			}
		operator bool() const
			{
			return pObject;
			}
		bool operator==(const iterator & Iterator) const
			{
			return (pObject==Iterator.pObject);
			}
		bool operator!=(const iterator & Iterator) const
			{
			return !(*this==Iterator);
			}
		bool operator<(const iterator &Iterator) const
			{
			return (pBlocks<Iterator.pBlocks ||
					pBlocks==Iterator.pBlocks && pObject<Iterator.pObject);
			}
		bool operator<=(const iterator &Iterator) const
			{
			return !(Iterator<*this);
			}
		bool operator>(const iterator &Iterator) const
			{
			return (Iterator<*this);
			}
		bool operator>=(const iterator &Iterator) const
			{
			return !(*this<Iterator);
			}
	};

	class const_iterator: public
	RanIt<value_type,difference_type,const_pointer,const_reference>	{
		friend class deque<type,A>;
		friend class iterator;
		const type *pObject, *blockBegin, *blockEnd;
		const type **pBlocks;

		const_iterator(const type **newPBlocks):
		pObject(*newPBlocks),
		blockBegin(pObject),
		blockEnd(blockBegin+OBJECTS_FOR_BLOCK),
		pBlocks(newPBlocks)
			{
			}
	public:/*
		typedef random_access_iterator_tag iterator_category;
		typedef value_type value_type;
		typedef difference_type difference_type;
		typedef const_pointer pointer;
		typedef const_reference reference;*/

		const_iterator(): pObject(0),
		blockBegin(0), blockEnd(0), pBlocks(0)
			{
			}
		const_iterator(const iterator &Iterator):
		pObject(Iterator.pObject),
		blockBegin(Iterator.blockBegin),
		blockEnd(Iterator.blockEnd),
		pBlocks(const_cast<const type **>(Iterator.pBlocks))
			{
			}
		const_iterator &operator=(const iterator &Iterator)
			{
			pObject=Iterator.pObject;
			blockBegin=Iterator.blockBegin;
			blockEnd=Iterator.blockEnd;
			pBlocks=const_cast<const type **>(Iterator.pBlocks);
			
			return *this;
			}

		const type &operator *() const
			{
			return *pObject;
			}
		const type *operator->() const
			{
			return pObject;
			}
		const_iterator &operator ++()
			{
			if(++pObject==blockEnd)
				{
				pObject=blockBegin=*++pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				}
			return *this;
			}
		const_iterator operator ++(int i)
			{
			const_iterator temp(*this);
			++(*this);
			return temp;
			}
		const_iterator &operator --()
			{
			if(pObject==blockBegin)
				{
				blockBegin=*--pBlocks;
				pObject=blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				}
			--pObject;
			return *this;
			}
		const_iterator operator --(int i)
			{
			const_iterator temp(*this);
			--(*this);
			return temp;
			}
		const_iterator &operator+=(difference_type n)
			{//se supone n>=0
			const type *p=pObject+n;
			if(p>=blockEnd)
				{
				difference_type blocksDifference=((p-blockBegin)/OBJECTS_FOR_BLOCK);
				difference_type offset=(p-blockBegin)%OBJECTS_FOR_BLOCK;

				pBlocks+=blocksDifference;
				blockBegin=*pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				pObject=blockBegin+offset;
				}
			else
				pObject=p;

			return *this;
			}
		const_iterator &operator-=(difference_type n)
			{//se supone n>=0
			const type *p=pObject-n;
			if(p<blockBegin)
				{
				difference_type m=(blockBegin-p)%OBJECTS_FOR_BLOCK;
				difference_type blocksDifference=((blockBegin-p)/OBJECTS_FOR_BLOCK)+(m? 1: 0);
				difference_type offset=m? OBJECTS_FOR_BLOCK-m: 0;

				pBlocks-=blocksDifference;
				blockBegin=*pBlocks;
				blockEnd=blockBegin+OBJECTS_FOR_BLOCK;
				pObject=blockBegin+offset;
				}
			else
				pObject=p;

			return *this;
			}
		const_iterator operator+(difference_type n) const
			{
			const_iterator it(*this);
			return (it+=n);
			}
		const_iterator operator-(difference_type n) const
			{
			const_iterator it(*this);
			return (it-=n);
			}
		difference_type operator-(const const_iterator &it) const
			{
			return (pBlocks==it.pBlocks? pObject-it.pObject:
					OBJECTS_FOR_BLOCK*(pBlocks-it.pBlocks-1)+
					(pObject-blockBegin)+(it.blockEnd-it.pObject));
			}
		const_reference operator[](difference_type n) const
			{
			return (*(*this+n));
			}
		operator bool() const
			{
			return pObject;
			}
		bool operator==(const const_iterator & Iterator) const
			{
			return (pObject==Iterator.pObject);
			}
		bool operator!=(const const_iterator & Iterator) const
			{
			return !(*this==Iterator);
			}
		bool operator<(const const_iterator &Iterator) const
			{
			return (pBlocks<Iterator.pBlocks ||
					pBlocks==Iterator.pBlocks && pObject<Iterator.pObject);
			}
		bool operator<=(const const_iterator &Iterator) const
			{
			return !(Iterator<*this);
			}
		bool operator>(const const_iterator &Iterator) const
			{
			return (Iterator<*this);
			}
		bool operator>=(const const_iterator &Iterator) const
			{
			return !(*this<Iterator);
			}
	};

//	typedef reverse_iterator<iterator,const_iterator,type> reverse_iterator;
//	typedef const_reverse_iterator<const_iterator,type> const_reverse_iterator;
	typedef reverse_iterator<const_iterator> const_reverse_iterator;
	typedef reverse_iterator<iterator> 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:
	deque(const A &sourceAllocator=A());
	deque(size_type sourceSize, const type &sourceValue=type(), const A &sourceAllocator=A());
	deque(const deque<type,A> &d);
	~deque();
	deque<type,A> &operator=(const deque<type,A> &op2);
	void clear();

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

	//Tamaño:
	size_type size() const;
	bool empty() const;
	operator bool() const;

	//Comparaciones:
	bool operator==(const deque<type,A> &d) const;
	
protected:
	A alloc;
	iterator itBegin;
	iterator itEnd;
	size_type m_size;
	size_type initialPos;
	size_type nBlocks;
	type **pBlocks;
	size_type realSize;

	//Funciones para agregar y quitar bloques:
	void addBlockBack();
	void addBlockFront();
	void removeBlockBack();
	void removeBlockFront();

};

//Iteradores:

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

template <class type, class A>
inline deque<type,A>::const_iterator deque<type,A>::begin() const
{
return itBegin;
}

template <class type, class A>
inline deque<type,A>::iterator deque<type,A>::end()
{
return itEnd;
}

template <class type, class A>
inline deque<type,A>::const_iterator deque<type,A>::end() const
{
return itEnd;
}

template <class type, class A>
inline deque<type,A>::reverse_iterator deque<type,A>::rbegin()
{
return itEnd;
}

template <class type, class A>
inline deque<type,A>::const_reverse_iterator deque<type,A>::rbegin() const
{
return itEnd;
}

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

template <class type, class A>
inline deque<type,A>::const_reverse_iterator deque<type,A>::rend() const
{
return itBegin;
}

//Acceso a los elementos

template <class type, class A>
inline deque<type,A>::reference deque<type,A>::operator[](size_type subscript) 
{
subscript+=initialPos;
return pBlocks[subscript/OBJECTS_FOR_BLOCK][subscript%OBJECTS_FOR_BLOCK];
}

template <class type, class A>
inline deque<type,A>::const_reference deque<type,A>::operator[](size_type subscript) const 
{
subscript+=initialPos;
return pBlocks[subscript/OBJECTS_FOR_BLOCK][subscript%OBJECTS_FOR_BLOCK];
}

template <class type, class A>
inline deque<type,A>::reference deque<type,A>::at(size_type subscript) 
{
if(subscript>=m_size)
	throw out_of_range();
return (*this)[subscript];
}

template <class type, class A>
inline deque<type,A>::const_reference deque<type,A>::at(size_type subscript) const 
{
if(subscript>=m_size)
	throw out_of_range();
return (*this)[subscript];
}

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

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

template <class type, class A>
inline deque<type,A>::reference deque<type,A>::back()
{
iterator temp(itEnd);
return *(--temp);
}

template <class type, class A>
inline deque<type,A>::const_reference deque<type,A>::back() const
{
const_iterator temp(itEnd);
return *(--temp);
}

//Constructores y otros:

template <class type, class A>
inline deque<type,A>::deque(const A &sourceAllocator):
alloc(sourceAllocator), m_size(0), initialPos(0), nBlocks(0), pBlocks(0),
realSize(0), itBegin(), itEnd()
{ 

}

template <class type, class A>
inline deque<type,A>::deque(size_type sourceSize, const type &sourceValue, const A &sourceAllocator):
alloc(sourceAllocator), m_size(sourceSize), initialPos(0), nBlocks(0), pBlocks(0), realSize(0),
itBegin(), itEnd()
{
int module;
if(module=(m_size%OBJECTS_FOR_BLOCK))
	nBlocks=(m_size/OBJECTS_FOR_BLOCK)+1;
else
	nBlocks=m_size/OBJECTS_FOR_BLOCK;
realSize=nBlocks*OBJECTS_FOR_BLOCK;

pBlocks=static_cast<type **>(alloc.allocateBytes((nBlocks+1)*sizeof(type *)));

type *pB, *pE;
size_type nFullBlocks=nBlocks-1,i;
for(i=0; i<nFullBlocks; ++i)
	{
	pB=pBlocks[i]=alloc.allocate(OBJECTS_FOR_BLOCK);
	pE=pB+OBJECTS_FOR_BLOCK;
	while(pB!=pE)
		{
		alloc.construct(pB,sourceValue);
		++pB;
		}
	}
pB=pBlocks[i]=alloc.allocate(OBJECTS_FOR_BLOCK);
pE=pB+(module? module: OBJECTS_FOR_BLOCK);
while(pB!=pE)
	{
	alloc.construct(pB,sourceValue);
	++pB;
	}
pBlocks[nBlocks]=0;

itBegin=iterator(pBlocks);
if(module)
	itEnd=iterator(pE,pBlocks+nBlocks-1);
else
	itEnd=iterator(pBlocks+nBlocks);

}

template <class type, class A>
inline deque<type,A>::deque(const deque<type,A> &d):
alloc(d.alloc), itBegin(), itEnd(),
m_size(d.m_size), initialPos(d.initialPos),
nBlocks(d.nBlocks), pBlocks(0), realSize(d.realSize)
{
if(m_size)
	{
	type **pTemp, **pEnd;

	pTemp=pBlocks=static_cast<type **>(alloc.allocateBytes((nBlocks+1)*sizeof(type *)));
	pEnd=pTemp+nBlocks;
	while(pTemp!=pEnd)
		{
		*pTemp=alloc.allocate(OBJECTS_FOR_BLOCK);
		++pTemp;
		}
	pBlocks[nBlocks]=0;
	itBegin=iterator((*pBlocks)+initialPos,pBlocks);
	if(d.itEnd)
		{
		difference_type offset=d.itEnd.pObject-d.itEnd.blockBegin;
		itEnd=iterator(pBlocks+nBlocks-1);
		itEnd.pObject+=offset;
		}
	else
		itEnd=iterator(pBlocks+nBlocks);
	//copy(d.itBegin,d.itEnd,itBegin);
	uninitialized_copy(d.itBegin,d.itEnd,itBegin,type());
	}

}

template <class type, class A>
inline deque<type,A>::~deque()
{
size_type i;
for(i=0; i<m_size; ++i)
	alloc.destroy(&((*this)[i]));
for(i=0; i<nBlocks; ++i)
	alloc.deallocate(pBlocks[i]);
alloc.deallocate(pBlocks);
}

template <class type, class A>
inline deque<type,A> &deque<type,A>::operator=(const deque<type,A> &op2)
{
this->~deque<type,A>();
m_size=op2.m_size;
initialPos=op2.initialPos;
nBlocks=op2.nBlocks;
realSize=op2.realSize;

if(m_size)
	{
	type **pTemp, **pEnd;

	pTemp=pBlocks=static_cast<type **>(alloc.allocateBytes((nBlocks+1)*sizeof(type *)));
	pEnd=pTemp+nBlocks;
	while(pTemp!=pEnd)
		{
		*pTemp=alloc.allocate(OBJECTS_FOR_BLOCK);
		++pTemp;
		}
	pBlocks[nBlocks]=0;
	itBegin=iterator((*pBlocks)+initialPos,pBlocks);
	if(op2.itEnd)
		{
		difference_type offset=op2.itEnd.pObject-op2.itEnd.blockBegin;
		itEnd=iterator(pBlocks+nBlocks-1);
		itEnd.pObject+=offset;
		}
	else
		itEnd=iterator(pBlocks+nBlocks);
	copy(op2.itBegin,op2.itEnd,itBegin);
	}
else
	{
	pBlocks=0;
	itBegin=iterator();
	itEnd=iterator();
	}

return *this;
}

template <class type, class A>
inline void deque<type,A>::clear()
{
this->~deque<type,A>();
m_size=0;
initialPos=0;
nBlocks=0;
pBlocks=0;
realSize=0;
itBegin=iterator();
itEnd=iterator();
}

//Operaciones de pila y cola:

template <class type, class A>
inline void deque<type,A>::push_front(const type &newObject)
{
if(initialPos>=1)
	{
	--initialPos;
	++m_size;
	pBlocks[0][initialPos]=newObject;
	--itBegin;
	}
else
	{
	addBlockFront();
	initialPos=OBJECTS_FOR_BLOCK-1;
	++m_size;
	pBlocks[0][initialPos]=newObject;
	}
}

template <class type, class A>
inline void deque<type,A>::pop_front()
{
if(--m_size)
	{
	if(initialPos<(OBJECTS_FOR_BLOCK-1))
		{
		++initialPos;
		++itBegin;
		}
	else
		{
		removeBlockFront();
		initialPos=0;
		}
	}
else
	{
	alloc.destroy(itBegin.pObject);
	clear();
	}

}

template <class type, class A>
inline void deque<type,A>::push_back(const type &newObject)
{
size_type realSubscript=initialPos+m_size;

if((realSubscript)<realSize)
	{
	alloc.construct(itEnd.pObject,newObject);
	++itEnd;
	++m_size;
	}
else
	{
	addBlockBack();
	alloc.construct(itEnd.blockBegin,newObject);
	++m_size;
	}
}

template <class type, class A>
inline void deque<type,A>::pop_back()
{
if(--m_size)
	{
	if(!((initialPos+m_size)%OBJECTS_FOR_BLOCK))
		{
		alloc.destroy(itEnd.blockBegin);
		removeBlockBack();
		}
	else
		{
		--itEnd;
		alloc.destroy(itEnd.pObject);
		}
	}
else
	{
	alloc.destroy(itBegin.pObject);
	clear();
	}
}

//Tamaño:

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

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

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

//Comparaciones:

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

return true;
}

//Funciones para agregar y quitar bloques:

template <class type, class A>
inline void deque<type,A>::addBlockBack()
{
type **newPBlocks;
newPBlocks=static_cast<type **>(alloc.allocateBytes((nBlocks+2)*sizeof(type *)));
copy(pBlocks,pBlocks+nBlocks,newPBlocks);
newPBlocks[nBlocks++]=alloc.allocate(OBJECTS_FOR_BLOCK);
newPBlocks[nBlocks]=0;
alloc.deallocate(pBlocks);
pBlocks=newPBlocks;
realSize=nBlocks*OBJECTS_FOR_BLOCK;
if(itBegin.pObject==itBegin.blockBegin)
	itBegin=iterator(pBlocks);
else
	{
	difference_type offset=itBegin.pObject-itBegin.blockBegin;
	itBegin=iterator(pBlocks);
	itBegin.pObject+=offset;
	}
itEnd=iterator(pBlocks+nBlocks-1);
++itEnd;
}

template <class type, class A>
inline void deque<type,A>::addBlockFront()
{
type **newPBlocks;
newPBlocks=static_cast<type **>(alloc.allocateBytes((++nBlocks+1)*sizeof(type *)));
newPBlocks[0]=alloc.allocate(OBJECTS_FOR_BLOCK);
copy(pBlocks,pBlocks+nBlocks-1,newPBlocks+1);
newPBlocks[nBlocks]=0;
alloc.deallocate(pBlocks);
pBlocks=newPBlocks;
realSize=nBlocks*OBJECTS_FOR_BLOCK;
itBegin=iterator(pBlocks);
itBegin.pObject+=OBJECTS_FOR_BLOCK-1;
if(itEnd)
	{
	difference_type offset=itEnd.pObject-itEnd.blockBegin;
	itEnd=iterator(pBlocks+nBlocks-1);
	itEnd.pObject+=offset;
	}
else
	itEnd=iterator(pBlocks+nBlocks);

}

template <class type, class A>
inline void deque<type,A>::removeBlockBack()
{
type **newPBlocks;
newPBlocks=static_cast<type **>(alloc.allocateBytes((--nBlocks+1)*sizeof(type *)));
copy(pBlocks,pBlocks+nBlocks,newPBlocks);
alloc.deallocate(pBlocks[nBlocks]);
newPBlocks[nBlocks]=0;
alloc.deallocate(pBlocks);
pBlocks=newPBlocks;
realSize=nBlocks*OBJECTS_FOR_BLOCK;
if(itBegin.pObject==itBegin.blockBegin)
	itBegin=iterator(pBlocks);
else
	{
	difference_type offset=itBegin.pObject-itBegin.blockBegin;
	itBegin=iterator(pBlocks);
	itBegin.pObject+=offset;
	}
itEnd=iterator(pBlocks+nBlocks);

}

template <class type, class A>
inline void deque<type,A>::removeBlockFront()
{
type **newPBlocks;
newPBlocks=static_cast<type **>(alloc.allocateBytes((--nBlocks+1)*sizeof(type *)));
copy(pBlocks+1,pBlocks+nBlocks+1,newPBlocks);
alloc.deallocate(pBlocks[0]);
newPBlocks[nBlocks]=0;
alloc.deallocate(pBlocks);
pBlocks=newPBlocks;
realSize=nBlocks*OBJECTS_FOR_BLOCK;
itBegin=iterator(pBlocks);
if(itEnd)
	{
	difference_type offset=itEnd.pObject-itEnd.blockBegin;
	itEnd=iterator(pBlocks+nBlocks-1);
	itEnd.pObject+=offset;
	}
else
	itEnd=iterator(pBlocks+nBlocks);

}

}

#endif