
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                         Long number - addition                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "..\include.h"

///////////////////////////////////////////////////////////////////////////////
// add 2 bignums together, store result here (numbers may overlap)

void bignum::Add(const bignum& num1, const bignum& num2)
{
	// special cases
	if (num1.Equ0()) { this->Copy(num2); return; }
	if (num2.Equ0()) { this->Copy(num1); return; }
	if (num1.EquM1()) { this->Dec(num2); return; }
	if (num2.EquM1()) { this->Dec(num1); return; }
	if (num1.Equ1()) { this->Inc(num2); return; }
	if (num2.Equ1()) { this->Inc(num1); return; }

	// prepare length with 2 numbers (dec1, dec2, dec, len1, len2, len)
	BIGPREP2();

	// copy start of decimal part
	if (dec1 > dec)
	{
		m_DecNum = dec1;
		this->CopyStr(this->Base(-dec1), num1.Base(-dec1), dec1 - dec);
	}
	else if (dec2 > dec)
	{
		m_DecNum = dec2;
		this->CopyStr(this->Base(-dec2), num2.Base(-dec2), dec2 - dec);
	}

	// operation on common data
	const buint* s1 = num1.Base(-dec);
	const buint* s2 = num2.Base(-dec);
	buint* d = this->Base(-dec);
	bint i = dec + len;
	buint cy = this->AddStr(0, d, s1, s2, i);
	s1 += i;
	s2 += i;
	d += i;

	// operation on larger part of one number
	if (len1 > len)
	{
		// dst = num1 + (k=0 or -1) + cy
		m_IntNum = len1;
		i = len1 - len;
		switch ((int)cy + (int)num2.High())
		{
		case 1: // cy=1, k=0
			cy = this->IncStr(d, s1, i);
			break;

		case -1: // cy=0, k=-1
			cy = this->DecStr(d, s1, i);
			break;

		default: // 0: cy=1, k=-1 / cy=0, k=0
			this->CopyStr(d, s1, i);
		}
		d += i;
	}
	else if (len2 > len)
	{
		// dst = (k=0 or -1) + num2 + cy
		m_IntNum = len2;
		i = len2 - len;
		switch ((bint)cy + (bint)num1.High())
		{
		case 1: // cy=1, k=0
			cy = this->IncStr(d, s2, i);
			break;

		case -1: // cy=0, k=-1
			cy = this->DecStr(d, s2, i);
			break;

		default:
			this->CopyStr(d, s2, i);
		}
		d += i;
	}

	// carry
	len = m_IntNum;
	buint k1 = (len < num1.m_IntNum) ? *num1.Base(len) : num1.High();
	buint k2 = (len < num2.m_IntNum) ? *num2.Base(len) : num2.High();
	buint r = k1 + k2 + cy;
	if (len < m_IntMax)
	{
		m_IntNum = len + 1;
		d[0] = r;
	}

	// sign
	m_IsNeg = (bint)r < 0;
	
	// data reduction
	this->Reduce();
}

///////////////////////////////////////////////////////////////////////////////
// add bignum and signed number, store result here (numbers may overlap)

void bignum::Add(const bignum& num1, bint num2)
{
	if (num2 < 0)
		this->SubU(num1, -num2);
	else
		this->AddU(num1, num2);
}

///////////////////////////////////////////////////////////////////////////////
// add bignum and unsigned number, store result here (numbers may overlap)

void bignum::AddU(const bignum& num1, buint num2)
{
	// special cases
	if (num1.Equ0()) { this->SetU(num2); return; }
	if (num2 == 0) { this->Copy(num1); return; }
	if (num2 == 1) { this->Inc(num1); return; }

	// length of decimal part
	bint i = num1.m_DecNum;
	if (i > m_DecMax) i = m_DecMax;
	m_DecNum = i;

	// copy decimal part
	const buint* s = num1.Base(-i);
	buint* d = this->Base(-i);
	this->CopyStr(d, s, i);
	s += i;
	d += i;

	// length of integer part
	i = num1.m_IntNum;
	if (i > m_IntMax) i = m_IntMax;
	m_IntNum = i;

	// add number
	buint r;
	buint cy = 0;
	if (i > 0)
	{
		r = *s++ + num2;
		*d++ = r;
		cy = (r < num2);
		i--;
	}

	// if source has no integer part
	else
	{
		r = num1.High();
		r += num2;
		cy = (r < num2);
		if (m_IntMax > 0)
		{
			m_IntNum = 1;
			*d++ = r;
		}
	}

	// add rest of integer part
	if (cy == 0)
		this->CopyStr(d, s, i);
	else
		cy = this->IncStr(d, s, i);
	d += i;

	// carry
	i = m_IntNum;
	buint k = (i < num1.m_IntNum) ? *num1.Base(i) : num1.High();
	r = k + cy;
	if (i < m_IntMax)
	{
		m_IntNum = i + 1;
		d[0] = r;
	}

	// sign
	m_IsNeg = (bint)r < 0;

	// data reduction
	this->Reduce();
}

///////////////////////////////////////////////////////////////////////////////
// add bignum to this number (numbers may overlap)

void bignum::Add(const bignum& num)
{
	// special cases
	if (this->Equ0()) { this->Copy(num); return; }
	if (num.Equ0()) { return; }
	if (this->Equ1()) { this->Inc(num); return; }
	if (num.Equ1()) { this->Inc(); return; }
	if (this->EquM1()) { this->Dec(num); return; }
	if (num.EquM1()) { this->Dec(); return; }

	// prepare length with 1 number (dec1, dec2, dec, len1, len2, len)
	BIGPREP1();

	// copy start of decimal part
	if (dec1 > dec)
	{
		m_DecNum = dec1;
	}
	else if (dec2 > dec)
	{
		m_DecNum = dec2;
		this->CopyStr(this->Base(-dec2), num.Base(-dec2), dec2 - dec);
	}

	// operation on common data
	const buint* s = num.Base(-dec);
	buint* d = this->Base(-dec);
	bint i = dec + len;
	buint r;
	buint cy = this->AddStr(0, d, s, i);
	s += i;
	d += i;

	// operation on larger part of one number
	if (len1 > len)
	{
		m_IntNum = len1;
		i = len1 - len;
		switch ((bint)cy + (bint)num.High())
		{
		case 1: // cy=1, k=0
			cy = this->IncStr(d, i);
			break;

		case -1: // cy=0, k=-1
			cy = this->DecStr(d, i);
			break;
		}
		d += i;
	}
	else if (len2 > len)
	{
		m_IntNum = len2;
		i = len2 - len;
		switch ((bint)cy + (bint)num.High())
		{
		case 1: // cy=1, k=0
			cy = this->IncStr(d, s, i);
			break;

		case -1: // cy=0, k=-1
			cy = this->DecStr(d, s, i);
			break;

		default:
			this->CopyStr(d, s, i);
		}
		d += i;
	}

	// carry
	len = m_IntNum;
	buint k1 = this->High();
	buint k2 = (len < num.m_IntNum) ? *num.Base(len) : num.High();
	r = k1 + k2 + cy;
	if (len < m_IntMax)
	{
		m_IntNum = len + 1;
		d[0] = r;
	}

	// sign
	m_IsNeg = (bint)r < 0;
	
	// data reduction
	this->Reduce();
}

///////////////////////////////////////////////////////////////////////////////
// add signed number to this number

void bignum::Add(bint num)
{
	if (num < 0)
		this->SubU(-num);
	else
		this->AddU(num);
}

///////////////////////////////////////////////////////////////////////////////
// add unsigned number to this number

void bignum::AddU(buint num)
{
	// special cases
	if (this->Equ0()) { this->SetU(num); return; }
	if (num == 0) { return; }
	if (num == 1) { this->Inc(); return; }

	// expand to overflow
	this->ExpInt(m_IntNum + 1);

	// add number
	buint* d = this->Base();
	buint r;
	buint cy = 0;
	bint i = m_IntNum;
	if (i > 0)
	{
		r = d[0] + num;
		*d++ = r;
		cy = (r < num);
		i--;
	}

	// carry
	if (cy != 0) cy = this->IncStr(d, i);

	// overflow
	r = this->High() + cy;

	// sign
	m_IsNeg = ((bint)r < 0);

	// data reduction
	this->Reduce();
}
