#ifndef __UINTBCD_AMV
	#define __UINTBCD_AMV

#include "vectorx.h"
#include "REEvaluator.h"
#include "algorithm.h"
#include "stringAMV.h"
#include "amvdefs.h"

namespace amv	{

//Convierte el parámetro que se supone está en formato ASCII a BCD
//el parámetro debe de estar en el intervalo ['0'-'9']
inline char AsciiBCD(const char ch)
{
return (ch&0x0F);
}

//Convierte el parámetro que se supone está en formato BCD a ASCII
//el parámetro debe de estar en el intervalo [0-9]
inline char BCDAscii(const char ch)
{
return (ch|0x30);
}

class uintBCD: public vectorx<char>	{
	static REEvaluator REEuintBCD;
public:
	static bool eval(const char *number);
	uintBCD();
	explicit uintBCD(size_type Size);
	uintBCD(const char *number);
	uintBCD(const string &s);
	void reduce();
	uintBCD &operator+=(const uintBCD &op2);
	uintBCD operator+(const uintBCD &op2) const;
	uintBCD &operator-=(const uintBCD &op2);
	uintBCD operator-(const uintBCD &op2) const;
	bool operator<(const uintBCD &op2) const;
	bool operator>=(const uintBCD &op2) const;
	uintBCD snippet(size_type subscript, size_type n) const;
	friend ostream &operator<<(ostream &os, const uintBCD &n);
	friend istream &operator>>(istream &is, uintBCD &n);

};

REEvaluator uintBCD::REEuintBCD("[0-9]+");

inline bool uintBCD::eval(const char *number)
{
return (REEuintBCD.eval(number));
}

inline uintBCD::uintBCD(): vectorx<char>()
{
}

inline uintBCD::uintBCD(size_type Size): vectorx<char>(Size)
{
fill(*this,0);
}

inline uintBCD::uintBCD(const char *number)
{
size_type Size=strlen(number);
resize(Size);
for(size_type i=0; i<Size; ++i)
	{
	ptr[i]=AsciiBCD(*number);
	++number;
	}
reduce();
}

inline uintBCD::uintBCD(const string &s)
{
resize(s.size());
string::const_iterator itSource(s.begin());
uintBCD::iterator itTarget(begin());

while(itSource!=s.end())
	{
	*itTarget=AsciiBCD(*itSource);
	++itTarget;
	++itSource;
	}

reduce();
}

inline void uintBCD::reduce()
{
if(!m_size)
	return;
size_type zeros=0;
char *pBegin=ptr, *pEnd=ptr+m_size;
while(pBegin!=pEnd)
	{
	if(*pBegin)
		break;
	else
		++zeros;
	++pBegin;
	}
if(zeros)
	resizeI(m_size-zeros,0);
}

inline uintBCD &uintBCD::operator+=(const uintBCD &op2)
{
if(!op2.m_size)
	return *this;
if(!m_size)
	{
	*this=op2;
	return *this;
	}

uintBCD result(m_size>op2.m_size? m_size+1: op2.m_size+1);
size_type i,j,k;
char carry=0;

if(m_size>op2.m_size)
	{
	for(i=result.m_size-1, j=m_size-1, k=op2.m_size-1; k>=0; --i, --j, --k)
		{
		result[i]=carry+ptr[j]+op2.ptr[k];
		if(result[i]>9)
			{
			result[i]%=10;
			carry=1;
			}
		else
			carry=0;
		}
	for(; j>=0; --i, --j)
		{
		result[i]=carry+ptr[j];
		if(result[i]>9)
			{
			result[i]%=10;
			carry=1;
			}
		else
			carry=0;
		}
	result[i]=carry;
	}
else
	{
	for(i=result.m_size-1, j=m_size-1, k=op2.m_size-1; j>=0; --i, --j, --k)
		{
		result[i]=carry+ptr[j]+op2.ptr[k];
		if(result[i]>9)
			{
			result[i]%=10;
			carry=1;
			}
		else
			carry=0;
		}
	for(; k>=0; --i, --k)
		{
		result[i]=carry+op2.ptr[k];
		if(result[i]>9)
			{
			result[i]%=10;
			carry=1;
			}
		else
			carry=0;
		}
	if(!i)
		result[i]=carry;
	}
result.reduce();
*this=result;

return *this;
}

inline uintBCD uintBCD::operator+(const uintBCD &op2) const
{
uintBCD temp(*this);
temp+=op2;

return temp;
}

//El minuendo debe ser menor al sustraendo, de otra manera, el resultado
//es impredecible
inline uintBCD &uintBCD::operator-=(const uintBCD &op2)
{
if(!op2.m_size)
	return *this;
	
uintBCD result(m_size);
size_type i,j,k,limit;
char digit1,digit2,carry=0;

for(i=m_size-1, j=op2.m_size-1, k=0; k<op2.m_size; --i, --j, ++k)
	{
	digit1=ptr[i];
	digit2=carry+op2.ptr[j];
	if(digit1<digit2)
		{
		result[i]=digit1+10-digit2;
		carry=1;
		}
	else
		{
		result[i]=digit1-digit2;
		carry=0;
		}
	}

limit=i+1;
for(k=0; k<limit; --i, ++k)
	{
	digit1=ptr[i];
	digit2=carry;
	if(digit1<digit2)
		{
		result[i]=digit1+10-digit2;
		carry=1;
		}
	else
		{
		result[i]=digit1-digit2;
		carry=0;
		}
	}
result.reduce();
*this=result;

return *this;
}

inline uintBCD uintBCD::operator-(const uintBCD &op2) const
{
uintBCD temp(*this);
temp-=op2;

return temp;
}

inline bool uintBCD::operator<(const uintBCD &op2) const
{
if(!m_size && !op2.m_size)
	return false;
if(!m_size)
	{
	if(op2.m_size==count(op2,0))
		return false;
	else
		return true;
	}
else if(!op2.m_size)
	return false;
else
	{
	bool op1Zero, op2Zero;
	op1Zero=m_size==count(*this,0);
	op2Zero=op2.m_size==count(op2,0);
	if(op1Zero && !op2Zero)
		return true;
	if(op2Zero)
		return false;

	size_type i,j;
	for(i=0; i<m_size; ++i)
		{
		if(ptr[i]!=0)
			break;
		}
	for(j=0; j<op2.m_size; ++j)
		{
		if(op2.ptr[j]!=0)
			break;
		}
	size_type digits1=m_size-i, digits2=op2.m_size-j;
	if(digits1<digits2)
		return true;
	else if(digits1>digits2)
		return false;

	for(;i<m_size; ++i,++j)
		{
		if(ptr[i]<op2.ptr[j])
			return true;
		else if(ptr[i]>op2.ptr[j])
			return false;
		}
	return false;
	}
}

inline bool uintBCD::operator>=(const uintBCD &op2) const
{
return !(*this<op2);
}

inline uintBCD uintBCD::snippet(size_type subscript, size_type n) const
{
uintBCD temp(n);
copy(ptr+subscript,ptr+subscript+n,temp.ptr);

return temp;
}

ostream &operator<<(ostream &os, const uintBCD &n)
{
if(n.size())
	{
	for(uintBCD::size_type i=0; i<n.m_size; ++i)
		os<<int(n.ptr[i]);
	}
else
	os<<'0';

return os;
}

istream &operator>>(istream &is, uintBCD &n)
{
string s;
if(uintBCD::REEuintBCD.getLexema(is,s))
	n=uintBCD(s);

return is;
}

}

#endif