/*
 *  mbConv.cpp
 *  fp
 *
 *  Created by Robert Delaney on 4/24/09.
 *  Copyright 2009 __Bob Delaney's Science Software__. All rights reserved.
 *
 */

#include "fp.h"

using namespace std;


// DecStrtoHexStr converts unsigned zString in place to a hex string
static bool DecStrtoHexStr(char* decStr)
{
	INT32				iChar, jByte, k, length, numBytes;
	unsigned char		ch1, ch2, ten=10;
	short				carry, temp, hundred=100;
	char 				*hlookup = "0123456789ABCDEF", ch;
	
	length = strlen(decStr);
	
	if(length==0)
		return false;
	
	if(length==1)
	{
		if(decStr[0]>='0' && decStr[0]<='9')
			return true;
		else
			return false;
	}
	
	// collapse the string into numerical bytes, each byte formed from two
	// adjacent chars, starting on the right
	// the final half of the string is then the number in base 100
	iChar = jByte = length-1;
	
	while(iChar>=0)
	{
		if(iChar>0)
		{
			ch1 = decStr[iChar-1];
			ch2 = decStr[iChar];
			if(!(ch1>='0' && ch1<='9'))
				return false;
			if(!(ch2>='0' && ch2<='9'))
				return false;
			decStr[jByte--] = ten*(ch1-'0') + (ch2-'0');
		}
		else
		{
			ch2 = decStr[iChar];
			if(!(ch2>='0' && ch2<='9'))
				return false;
			decStr[jByte--] = (ch2-'0');
		}
		iChar-=2;
	}
	
	// coming out jByte+1 indexes the leading numerical byte, the last is at length-1
	
	iChar = 0; // indexes where low order hex digit will go; they will be in reverse order
	jByte++;   // now indexes leading numerical byte
	numBytes = length - jByte;
	
	while(numBytes>0)
	{
		temp = decStr[jByte];
		for(k=jByte;k<length;k++)
		{
			// get low-order 4 bits of temp
			carry = (temp & 0xF);
			if(k==length-1)
			{
				decStr[k] = (temp>>4); // new byte value after dividing by sixteen
				decStr[iChar] = hlookup[carry]; // must be after previous line
				iChar++;
			}
			else
			{
				decStr[k] = (temp>>4); // new byte value after dividing by sixteen
				temp = hundred*carry + decStr[k+1];
			}
		}
		if(decStr[jByte]==0 || iChar==length)
		{
			jByte++;
			numBytes--;
		}
	}
	
	decStr[iChar] = 0;
	
	// reverse the chars
	length = strlen(decStr);
	for(k=0;k<length/2;k++)
	{
		ch = decStr[k];
		decStr[k] = decStr[length-1-k];
		decStr[length-1-k] = ch;
	}
	
	return true;
	
}/* DecStrtoHexStr */

// this modifies hexStr
static char* HexStrtoDecStr(char* hexStr)
{
	INT32		iChar, jByte, k, length, numBytes;
	short		carry, temp, temp1, ten=10, sixteen=16;
	char*		decStr;
	char 		*dlookup = "0123456789", ch;
	
	length = strlen(hexStr);
	
	if(length==0)
	{
		decStr = (char*)malloc(sizeof(char));
		if(!decStr)
			exit(1);
		decStr[0] = 0;
		return decStr;
	}
		
	// since F->15 the worst case requires 2 dec digits per hexit
	
	decStr = (char*)malloc((2*length+10)*sizeof(char)); // don't free in this method!
	if(!decStr)
		exit(1);
	
	// we don't collapse the string, but we do have to get the numerical bytes for base 16
	for(k=0;k<length;k++)
	{
		ch = hexStr[k];
		if(!(ch>='0' && ch<='9') && !((ch>='A' && ch<='F') || (ch>='a' && ch<='f')))
		{
			decStr[0] = 0;
			return decStr;
		}
		
		if(ch>='0' && ch<='9')
			hexStr[k] = (ch - '0');
		
		if(ch>='A' && ch<='F')
			hexStr[k] = ten + (ch - 'A');
		
		if(ch>='a' && ch<='f')
			hexStr[k] = ten + (ch - 'a');
	}
	
	numBytes = length;
	iChar = 0;
	jByte = 0;
	
	while(numBytes>0)
	{
		temp = hexStr[jByte];
		for(k=jByte;k<length;k++)
		{
			// divide temp by 10 and get carry (0 to 9)
			temp1 = temp/ten;
			carry = temp - ten*temp1;
			hexStr[k] = temp1;  // new byte value after dividing by ten
			if(k==length-1)
			{
				decStr[iChar] = dlookup[carry];
				iChar++;
			}
			else
			{
				temp = sixteen*carry + hexStr[k+1];
			}
		}
		if(hexStr[jByte]==0)
		{
			jByte++;
			numBytes--;
		}
	}
	
	decStr[iChar] = 0;
	
	// reverse the chars
	length = strlen(decStr);
	for(k=0;k<length/2;k++)
	{
		ch = decStr[k];
		decStr[k] = decStr[length-1-k];
		decStr[length-1-k] = ch;
	}
	
	return decStr;
	
}/* HexStrtoDecStr */

// z must already be inited
// mbConv converts an unsigned integer C string to an mb;
// Basically we are converting to base 2^32. We do this by first converting to hex, then convert the hex string to blocks in mb
bool mbConvFromStr(mb& z, const char* inString)
{
	INT32		length, numBlocks, lowNumHexits;
	char		hexBlock[9], *decString, ch;
	INT32		i, j, decStart, zeroCount, posDecPoint;
	bool		isNegative, isNumber;
	fp			zfp;
    
    if(z.nn && z.b)
        free(z.b);
	
	z.n = z.nn = 0;
	z.b = 0;
	
	length = strlen(inString);
	
	if(!length || (length==1 && inString[0]=='0'))
		return true;
	
	// is there 'e' or 'E' in inString?
	for(i=0;i<length;i++)
	{
		if(inString[i]=='e' || inString[i]=='E')
		{
			if(!equate(zfp, inString))
			   return false;
			equate(z, zfp);
			return true;
		}
	}
	
	decString = (char*)malloc((length+1)*sizeof(char));
	if(!decString)
		exit(1);
    
	// copy to decString, but remove all spaces
	j = 0;
	for(i=0;i<length;i++)
	{
		ch = inString[i];
		if(!(ch==' '))
			decString[j++] = ch;
	}
	decString[j] = 0;
	length = strlen(decString);
	
	//strcpy(decString, inString);
	
	// if there is a decimal point, remove it and all after
	posDecPoint = -1;
	for(i=0;i<length;i++)
	{
		if(decString[i]=='.')
		{
			posDecPoint = i;
			break;
		}
	}
	
	if(!(posDecPoint==-1))
	{
		// remove decimal point and all after
		decString[posDecPoint] = 0;  // posDecPoint is now the string length
		length = posDecPoint;
		// do we have at least one number? (could have had -.345 say)
		isNumber = false;
		for(i=0;i<length;i++)
		{
			if(decString[i]>='0' && decString[i]<='9')
			{
				isNumber = true;
				break;
			}
		}
		if(!isNumber)
		{
			z.n = 0;
			free(decString);
			return true;
		}
	}
	
	// find if negative
	isNegative = false;
	// skip leading spaces
	i = 0;
	ch = decString[i++];
	while(ch==' ')
		ch = decString[i++];
	// now ch is true first character, and i points to next character
	if(!(ch=='+' || ch=='-' || (ch>='0' && ch<='9')))
	{
		free(decString);
		return false;
	}

	if(ch=='-')
	{
		isNegative = true;
		// skip spaces after minus sign
		while(decString[i]==' ' && i<length)
			i++;
	}
	else
	if(ch=='+')
	{
		// skip spaces after plus sign
			
		while(decString[i]==' ' && i<length)
			i++;
	}
	else
		i--;
	
	decStart = i;
	
	// ignore spaces at end
	while(decString[length-1]==' ' && length>0)
		length--;
		
	if(!length)
	{
		free(decString);
		return false;
	}
	
	// shift up by decStart
	if(decStart)
	{
		length-=decStart;
		for(i=0;i<length;i++)
			decString[i] = decString[i+decStart];
		decString[length] = 0;
	}
	
	// now check that rest of characters are decimal
	i = 0;
	ch = decString[i];
	while(i<length)
	{
		if(!(ch>='0' && ch<='9'))
		{
			free(decString);
			return false;
		}
		ch = decString[i++];
	}
	
	// get rid of leading zeroes but keep last zero
	zeroCount = 0;
	ch = decString[zeroCount];
	while(ch=='0')
		ch = decString[++zeroCount];
		
	// zeroCount now points to first non-zero so if zeroCount is not zero we shift up by zeroCount
	if(zeroCount)
	{
		length-=zeroCount;
		for(i=0;i<length;i++)
			decString[i] = decString[i+zeroCount];
		decString[length] = 0;
	}
	
	if(!length)
	{
		// we had all zeroes so 
		free(decString);
		z.n = z.nn = 0;
		z.b = 0;
		return true;
	}
	
	if(strlen(decString)==1 && decString[0]=='0')
	{
		z.n = z.nn = 0;
		z.b = 0;
		free(decString);
		return true;
	}
	
	
	if(!DecStrtoHexStr(decString))
	{
		free(decString);
		return false;
	}
	
	length = strlen(decString);  // gives number of hex characters
	
	// calculate the number of blocks needed, each block can hold 32 bits or 8 hexits
	numBlocks = length/8;
	lowNumHexits = length-8*numBlocks;
	
	if(lowNumHexits>0)
		numBlocks++;
	
	//z.n = z.nn = numBlocks;
	//z.b = (UINT32*)malloc(numBlocks*sizeof(UINT32));
	z.n = numBlocks;
	update(z);
	
	// fill blocks from high order to low order
	// if lowNumHexits>0 fill high order special
	j = 0; // index to hex zString
	
	if(lowNumHexits>0)
	{
		for(i=0;i<lowNumHexits;i++)
			hexBlock[i] = decString[j++];
			
		hexBlock[lowNumHexits] = 0;
		
		// convert hexBlock to UINT32
		if(sscanf(hexBlock,"%X", &z.b[numBlocks-1])==0)
		{
			free(decString);
			if(z.nn && z.b)
				free(z.b);
			z.n = z.nn = 0;
			z.b = 0;
			return false;
		}
			
		numBlocks--;
	}
	
	hexBlock[8] = 0; // always
	
	while(numBlocks>0)
	{
		for(i=0;i<8;i++)
			hexBlock[i] = decString[j++];
			
		// convert hexBlock to UINT32
		
		if(sscanf(hexBlock,"%X", &z.b[numBlocks-1])==0)
		{
			free(decString);
			if(z.nn && z.b)
				free(z.b);
			z.n = z.nn = 0;
			z.b = 0;
			return false;
		}
		
		numBlocks--;
	}
	
	if(isNegative && !(abs(z.n)==1 && z.b[0]==0))
		z.n = -z.n;
	
	free(decString);
	return true;
	
}/* mbConvFromStr */


// This should only be used on a local static variable in a function, never on a returned mb&
// z = x ; assume z inited, worry about z being x
void equateWithCare(mb& z, const mb& x)
{
	INT32	i;
	
	if(z.n==x.n && z.nn==x.nn && z.b==x.b)
		return;
	
	z.n = x.n;
	updateWithCare(z);
	
	for(i=0;i<abs(x.n);i++)
		z.b[i] = x.b[i];
	
}/* equateWithCare */


// z = x ; assume z inited, worry about z being x
void equate(mb& z, const mb& x)
{
	INT32	i;
	
	if(z.n==x.n && z.nn==x.nn && z.b==x.b)
		return;
	
	z.n = x.n;
    z.nn = x.nn;
	update(z);
	
	for(i=0;i<abs(x.n);i++)
		z.b[i] = x.b[i];
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, double x)
{
	static fp		xt;
	static bool		initGood=false;
	
	if(!initGood)
	{
		init(xt);
		initGood = true;
	}
	
	if(x==0)
	{
		z.n = z.nn = 0;
		z.b = 0;
		return;
	}
	
	equate(xt, x);
	
	equate(z, xt);
	
}/* equate */


void equate(double& z, const mb& x)
{
	static fp		xt;
	static bool		initGood=false;
	
	if(!initGood)
	{
		init(xt);
		initGood = true;
	}
	
	equate(xt, x);
	equate(z, xt);
	
}/* equate */


bool equate(UINT8& z, const mb& x)
{
	UINT32		temp;
	
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>1 || x.n<0)
	{
		z = 0;
		return false;
	}
	
	temp = 1;
	temp = (temp<<8); // 0x100
	if(x.b[0]>=temp)
	{
		z = 0;
		return false;
	}
	
	z = x.b[0];
	
	return true;
	
}/* equate */


bool equate(INT8& z, const mb& x)
{
	UINT32		temp;
	
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>1)
	{
		z = 0;
		return false;
	}
	
	temp = 1;
	temp = (temp<<7); //0x80
	if(x.b[0]>temp)
	{
		z = 0;
		return false;
	}
	
	z = x.b[0];
	
	if(x.n<0)
		z = -z;
	
	return true;
	
}/* equate */


bool equate(UINT16& z, const mb& x)
{
	UINT32		temp;
	
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>1 || x.n<0)
	{
		z = 0;
		return false;
	}
	
	temp = 1;
	temp = (temp<<16); // 0x10000
	if(x.b[0]>=temp)
	{
		z = 0;
		return false;
	}
	
	z = x.b[0];
	
	return true;
	
}/* equate */


bool equate(INT16& z, const mb& x)
{
	UINT32		temp;
	
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>1)
	{
		z = 0;
		return false;
	}
	
	temp = 1;
	temp = (temp<<15); //0x8000
	if(x.b[0]>temp)
	{
		z = 0;
		return false;
	}
	
	z = x.b[0];
	
	if(x.n<0)
		z = -z;
	
	return true;
	
}/* equate */


bool equate(UINT32& z, const mb& x)
{
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>1 || x.n<0)
	{
		z = 0;
		return false;
	}
	
	z = x.b[0];
		
	return true;
	
}/* equate */


bool equate(INT32& z, const mb& x)
{
	z = 0;
	
	if(x.n==0)
		return true;
	
	if(abs(x.n)>1)
		return false;
	
	z = x.b[0];
	if(z<0)
		return false;
		
	if(x.n<0)
		z = -z;
	
	return true;
	
}/* equate */


bool equate(UINT64& z, const mb& x)
{
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>2 || x.n<0)
	{
		z = 0;
		return false;
	}
	
	if(x.n==1)
	{
		z = x.b[0];
	}
	else
	{
		z = x.b[1];
		z = (z<<32) + x.b[0];
	}
	
	return true;
	
}/* equate */


bool equate(INT64& z, const mb& x)
{
	if(!x.n)
	{
		z = 0;
		return true;
	}
	
	if(abs(x.n)>2)
	{
		z = 0;
		return false;
	}
	
	if(abs(x.n)==1)
	{
		z = x.b[0];
	}
	else
	{
		z = x.b[1];
		z = (z<<32) + x.b[0];
	}
	
	if(x.n<0)
		z = -z;
	
	return true;
	
}/* equate */


// z = converted xString; z must be inited
bool equate(mb& z, char *xString)
{
	if(z.nn && z.b)
	{
		free(z.b);
		z.n = z.nn = 0;
		z.b = 0;
	}
	
	return init(z, xString);
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, UINT8 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	equate(z, (UINT32)x);
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, INT8 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	equate(z, (INT32)x);
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, UINT16 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	equate(z, (UINT32)x);
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, INT16 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	equate(z, (INT32)x);
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, UINT32 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	z.n = 1;
	update(z);
	
	z.b[0] = x;
	
}/* equate */


// z = x; z must be inited
void equate(mb& z, INT32 x)
{
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	z.n = 1;
	update(z);
	
	z.b[0] = abs(x);
		
	if(x<0)
		z.n = -z.n;
	
}/* equate */


void equate(mb& z, UINT64 x)
{
	UINT64		temp;
	UINT32		x0, x1;
	
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	temp = 1;
	temp = (temp<<32); // 2^32
	
	// x = x0 + x1*2^32  where x0 and x1 are UINT32
	x1 = x/temp;
	x0 = x - temp*x1;
	
	z.n = 2;
	update(z);
	
	z.b[0] = x0;
	z.b[1] = x1;
	
	mbNormalize(z); // x1 might be zero
	
}/* equate */


void equate(mb& z, INT64 x)
{
	UINT64		xt;
	
	// worry about x = 0x8000000000000000 for which x = -x; works OK!
	
	if(!x)
	{
		z.n = 0;
		return;
	}
	
	if(x<0)
		xt = -x;
	else
		xt = x;
	
	equate(z, xt);
	if(x<0)
		z.n = -z.n;
	
}/* equate */


void equate(char*& xString, const mb& x)
{
	xString = mb2Str(x);
}


bool init(mb& x)
{
	x.n = x.nn = 0;
	x.b = 0;
	return true;
	
}/* init as zero */


bool init(mb& x, char *inString)
{
	x.n = x.nn = 0;
	x.b = 0;
	
	if(!strlen(inString) || (strlen(inString)==1 && inString[0]=='0'))
	   return true;
	   
	return(mbConvFromStr(x, inString));
	
}/* init from string */


bool init(mb& x, INT32 y)
{
	UINT32		ys;
	
	x.n = x.nn = 0;
	x.b = 0;
	
	if(!y)
		return true;
	
	ys = abs(y);
	
	x.n = x.nn = 1;
	x.b = (UINT32*)malloc(x.nn*sizeof(UINT32));
	if(!x.b)
		exit(1);
	x.b[0] = ys;
	
	if(y<0)
		x.n = -x.n;
	
	return true;
	
}/* init from INT32 */

// also do a convert from INT64
bool init(mb& x, INT64 y)
{
    equate(x, y);
    
    return true;
    
}/* init from INT64 */

void long32tomb(mb& z, long x)
{
    UINT32		xs;
    
    z.n = z.nn = 0;
    z.b = 0;
    
    if(!x)
        return;
    
    xs = abs(x);
    
    z.n = z.nn = 1;
    z.b = (UINT32*)malloc(z.nn*sizeof(UINT32));
    if(!z.b)
        exit(1);
    z.b[0] = xs;
    
    if(x<0)
        z.n = -z.n;
    
}/* long32tomb */

void long64tomb(mb& z, long x)
{
    UINT64		xt;
    UINT64		temp;
    UINT32		x0, x1;
    
    // worry about x = 0x8000000000000000 for which x = -x; works OK!
    
    if(!x)
    {
        z.n = 0;
        return;
    }
    
    if(x<0)
        xt = -x;
    else
        xt = x;
    
    temp = 1;
    temp = (temp<<32); // 2^32
    
    // xt = x0 + x1*2^32  where x0 and x1 are UINT32
    x1 = xt/temp;
    x0 = xt - temp*x1;
    
    z.n = 2;
    update(z);
    
    z.b[0] = x0;
    z.b[1] = x1;
    
    mbNormalize(z); // x1 might be zero
    if(x<0)
        z.n = -z.n;
    
}/* long64tomb */


