// VLNumber.cpp: implementation of the VLNumber class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Calculator.h"
#include "VLNumber.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define MAXLEN	(100)

VLNumber::VLNumber()
{
	_s = new char[MAXLEN];
	init();
}

VLNumber::VLNumber(CString &x)
{
	_s = new char[MAXLEN];
	init();
	*this = x;
}

VLNumber::VLNumber(VLNumber &x)
{
	_s = new char[MAXLEN];
	_sign = x._sign;
	for(int i = 0; i < MAXLEN; i++) {
		_s[i] = x._s[i];
	}
}

VLNumber::~VLNumber()
{
	delete _s; _s = NULL;
}

void VLNumber::init()
{
	_sign = 0;
	for(int i = 0; i < MAXLEN; i++) {
		_s[i] = 0;
	}
}

void VLNumber::asNinesComplement()
{
	for(int i = 0; i < MAXLEN; i++) {
		_s[i] = 9 - _s[i];
	}
}

void VLNumber::asTensComplement()
{
	asNinesComplement();
	// plus 1
	int carry = 1;
	for(int i = MAXLEN-1; i >= 0; i--) {
		_s[i] = _s[i] + carry;
		if (_s[i] < 10) break;
		_s[i] = _s[i] - 10;
	}
}

char VLNumber::getDigitAt(int i)
{
	return _s[i];
}

void VLNumber::multiply10(BOOL &overflow)
{
	// do as shift left
	if (_s[0] != 0) {
		overflow = TRUE;
		return;
	}
	for (int i = 1; i < MAXLEN; i++)
		_s[i-1] = _s[i];
	_s[MAXLEN-1] = 0;
}

void VLNumber::divide10()
{
	// do as shift right
	for (int i = MAXLEN-2; i >= 0; i--)
		_s[i+1] = _s[i];
	_s[0] = 0;
}

void VLNumber::operator=(VLNumber &x)
{
	init();

	_sign = x._sign;
	for(int i = 0; i < MAXLEN; i++) {
		_s[i] = x._s[i];
	}
}

void VLNumber::operator=(CString &s)
{
	init();

	int i,j;
	i = MAXLEN-1;
	j = 1;

	int length = s.GetLength();
	if (s.GetAt(0) == '-') {
		_sign = 1;
	}

	while(j <= length-_sign) {
		_s[i] = s.GetAt(length-j) - '0';
		i--;
		j++;
	}
	if (_sign == 1) {
		asTensComplement();
	}
}

void VLNumber::toString(CString &target)
{
	int i;
	target = "";
	if (_sign == 1) {
		target = "-";
		asTensComplement();
	}
	for(i = 0; i < MAXLEN; i++) {
		if (_s[i] > 0) break;
	}
	if (i == MAXLEN) {
		target = "0";
		return;
	}
	while(i < MAXLEN) {
		char c = _s[i] + '0';
		target = target + c;
		i++;
	}
}

int VLNumber::compareTo(VLNumber &x)
{
	char signDifference;

	signDifference = _sign - x._sign;
	if (signDifference > 0) {
		return -1;
	} else if (signDifference < 0) {
		return 1;
	}

	for(int i = 0; i < MAXLEN; i++) {
		char c = _s[i] - x._s[i];
		if (c != 0) return c;
	}
	return 0;
}

VLNumber& VLNumber::minus(VLNumber x,BOOL &overflow)
{
	static VLNumber result;

	if (x._sign == 0) {
		x._sign = 1;
	} else { // y._sign == 1
		x._sign = 0;
	}
	x.asTensComplement();
/* a way to print intermediate result for debugging
#ifdef _DEBUG
	TRACE1("%d",x._sign);
	for(int index = 0; index < MAXLEN; index++) {
		TRACE1("%d",x._s[index]);
	}
	TRACE0("\n");
#endif
*/
	result = this->plus(x,overflow);
	return result;
}

VLNumber& VLNumber::plus(VLNumber x,BOOL &overflow)
{
	static VLNumber result;

	result.init();

	int carry = 0;

	for(int i = MAXLEN-1; i >= 0; i--) {
		result._s[i] = _s[i] + x._s[i] + carry;
		if (result._s[i] >= 10) {
			result._s[i] = result._s[i] - 10;
			carry = 1;
		} else {
			carry = 0;
		}
	}
	int c;
	if (carry == 1) {
		c = carry + _sign + x._sign;
		if (c == 1) {
			overflow = TRUE;
		} else if (c == 2) {
			result._sign = 0;
		} else { // c == 3
			result._sign = 1;
		}
	} else { // carry == 0
		c = _sign + x._sign;
		if (c == 0) {
			result._sign = 0;
		} else if (c == 1) {
			result._sign = 1;
		} else { // c == 2
			overflow = TRUE;
		}
	}
	return result;
}

VLNumber& VLNumber::multiply(VLNumber multiplier,BOOL &overflow)
{
	static VLNumber result;
	VLNumber multiplicand(*this);

	result.init();
	char resultSign = _sign ^ multiplier._sign;

	if (multiplicand._sign == 1) {
		multiplicand._sign = 0;
		multiplicand.asTensComplement();
	}
	if (multiplier._sign == 1) {
		multiplier._sign = 0;
		multiplier.asTensComplement();
	}
	VLNumber zeroNumber;
	while(multiplier.compareTo(zeroNumber) > 0) {
		char n = multiplier.getDigitAt(MAXLEN-1);
		while (n > 0) {
			result = result.plus(multiplicand,overflow);
			if (overflow) return result;
			n--;
		}
		multiplier.divide10();
		multiplicand.multiply10(overflow);
		if (overflow) return result;
	}
	result._sign = resultSign;
	if (result._sign == 1) {
		result.asTensComplement();
	}
	return result;
}

VLNumber& VLNumber::divide(VLNumber divider,BOOL &overflow)
{
	static VLNumber result;
	VLNumber dividend(*this);

	result.init();
	char resultSign = _sign ^ divider._sign;

	if (dividend._sign == 1) {
		dividend._sign = 0;
		dividend.asTensComplement();
	}
	if (divider._sign == 1) {
		divider._sign = 0;
		divider.asTensComplement();
	}

	CString one("1");
	VLNumber minusCountUnit = one;
	VLNumber maxValueToSubtract(divider);
	// find maxValueToSubtract
	while(dividend.compareTo(maxValueToSubtract) >= 0) {
		minusCountUnit.multiply10(overflow);
		maxValueToSubtract.multiply10(overflow);
	}
	minusCountUnit.divide10();
	maxValueToSubtract.divide10();

#ifdef _DEBUG
	TRACE1("%d",minusCountUnit._sign);
	for(int index = 0; index < MAXLEN; index++) {
		TRACE1("%d",minusCountUnit._s[index]);
	}
	TRACE0("\n");
#endif

	VLNumber zeroNumber;
	while(minusCountUnit.compareTo(zeroNumber) > 0) {
		while(dividend.compareTo(maxValueToSubtract) >= 0) {
			dividend = dividend.minus(maxValueToSubtract,overflow);
			result = result.plus(minusCountUnit,overflow);
		}
		minusCountUnit.divide10();
		maxValueToSubtract.divide10();
	}

	result._sign = resultSign;
	if (result._sign == 1) {
		result.asTensComplement();
	}
	return result;
}

