/****************************************************************
 *								*
 * Copyright (c) 2001-2018 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gtm_string.h"

#include "arit.h"
#include "mvalconv.h"
#include "gtm_stdio.h"	/* this is here due to the need for an SNPRINTF,
			 * which is in turn due the kudge that is the current double2mval routine
			 */
/* we return strings for >18 digit 64-bit numbers, so pull in stringpool */
#include "stringpool.h"

GBLREF spdesc   stringpool;

LITREF int4 ten_pwr[];

#define BUFF_LEN	32 /* MAX of 16, 21 and 32 */

void i2smval(mval *v, uint4 i)
{
	char	*c;
	int	exp;
	int4	n;

	v->mvtype = MV_NM | MV_STR;
	v->m[1] = n = i;
	v->sgn = 0;
	c = v->str.addr;
	exp = 100000000;
	while (exp && !(n = i / exp))
		exp /= 10;
	if (!exp)
		*c++ = 0;
	else for (;;)
	{
		*c++ = n + '0';
		i -= n * exp;
		exp /= 10;
		if (!exp)
			break;
		n = i / exp;
	}
	if (v->m[1] < INT_HI)
	{
		v->mvtype |= MV_INT;
		v->m[1] = MV_BIAS * i;
	} else if (v->m[1] < MANT_HI)
	{
		v->m[1] *= ten_pwr[NUM_DEC_DG_1L - exp];
		v->m[0] = 0;
	} else
	{
		v->m[0] = (v->m[1] % 10) * MANT_LO;
		v->m[1] /= 10;
	}
	v->str.len = INTCAST(c - v->str.addr);
	v->e = v->str.len + MV_XBIAS;
	assert(v->m[1] < MANT_HI);
}

void xi2mval(mval *v, unsigned int i);

void i2usmval(mval *v, unsigned int i)
{
	v->mvtype = MV_NM;
	v->sgn = 0;

	xi2mval(v, i);
}

void i2mval(mval *v, int i)
{
	int4	n;

	v->mvtype = MV_NM;
	if (i < 0)
	{
		v->sgn = 1;
		n = -i;
	} else
	{
		n = i;
		v->sgn = 0;
	}

	xi2mval(v, n);
}

/* xi2mval does the bulk of the conversion for i2mval and i2usmval.
 * The primary routines set the sgn flag and pass the absolute value
 * to xi2mval. */

void xi2mval(mval *v, unsigned int i)
{
	int	exp;

	if (i < INT_HI)
	{
		v->mvtype |= MV_INT;
		v->m[1] = MV_BIAS * (v->sgn ? -(int)i : i);
	} else
	{
		if (i < MANT_HI)
		{
			for (exp = EXP_IDX_BIAL; i < MANT_LO; exp--)
				i *= 10;
			v->e = exp;
			v->m[0] = 0;
			v->m[1] = i;
		} else
		{
			v->m[0] = (i % 10) * MANT_LO;
			v->m[1] = i / 10;
			v->e = EXP_IDX_BIAL + 1;
		}
		assert(v->m[1] < MANT_HI);
		assert(v->m[1] >= MANT_LO);
	}
}

void xi82mval(mval *v, gtm_uint64_t i);

void ui82mval(mval *v, gtm_uint64_t i)
{
	v->mvtype = MV_NM;
	v->sgn = 0;

	xi82mval(v, i);
}

void i82mval(mval *v, gtm_int64_t i)
{
	gtm_uint64_t	absi;

	v->mvtype = MV_NM;
	if (i < 0)
	{
		v->sgn = 1;
		absi = -i;
	} else
	{
		v->sgn = 0;
		absi = i;
	}

	xi82mval(v, absi);
}

/* This function does the bulk of the conversion for i82mval and ui82mval. The primary routines set the sgn flag and pass the
 * absolute value to xi82mval(). In the case of a >18 digit number, xi82mval() examines the sgn flag to determine whether to
 * switch back to a signed value before string conversion.
 */
void xi82mval(mval *v, gtm_uint64_t i)
{
	int	exp;
	uint4	low;
	uint4	high;
	char    buf[BUFF_LEN];	/* [possible] sign, [up to] 19L/20UL digits, and terminator. */
	int	len;

	if (i < INT_HI)
	{
		v->mvtype |= MV_INT;
		v->m[1] = MV_BIAS * (v->sgn ? -(int4)i : (uint4)i);
	} else
	{
		if (i < MANT_HI)
		{
			low = 0;
			high = i;
			exp = EXP_IDX_BIAL;
			while (high < MANT_LO)
			{
				high *= 10;
				exp--;
			}
			v->e = exp;
			v->m[0] = low;
			v->m[1] = high;
		} else if (i < (gtm_uint64_t)MANT_HI * MANT_HI)
		{
			low = i % MANT_HI;
			high = i / MANT_HI;
			exp = EXP_IDX_BIAL + 9;
			while (high < MANT_LO)
			{
				high = (high * 10) + (low / MANT_LO);
				low = (low % MANT_LO) * 10;
				exp--;
			}
			v->e = exp;
			v->m[0] = low;
			v->m[1] = high;
		} else
		{	/* The value won't fit in 18 digits, so return a string. */
			if (v->sgn)
				len = SNPRINTF(buf, BUFF_LEN, "%lld", -(gtm_int64_t)i);
			else
				len = SNPRINTF(buf, BUFF_LEN, "%llu", i);
			assert(18 < len);
			ENSURE_STP_FREE_SPACE(len);
			memcpy(stringpool.free, buf, len);
			v->mvtype = MV_STR;
			v->str.len = len;
			v->str.addr = (char *)stringpool.free;
			stringpool.free += len;
		}
		assert((v->mvtype != MV_NM) || (v->m[1] < MANT_HI));
		assert((v->mvtype != MV_NM) || (v->m[1] >= MANT_LO));
	}
}

double mval2double(mval *v)
{
	double	x, y;
	int	exp;

	MV_FORCE_NUM(v);
	x = v->m[1];
	if (v->mvtype & MV_INT)
		x /= MV_BIAS;
	else
	{
		exp = v->e;
		y = v->m[0];
		y = y / MANT_HI;
		while (exp > EXP_IDX_BIAL)
		{
			x *= MANT_HI;
			y *= MANT_HI;
			exp -= 9;
		}
		while (exp < MV_XBIAS)
		{
			x /= MANT_HI;
			y /= MANT_HI;
			exp += 9;
		}
		x /= ten_pwr[EXP_IDX_BIAL - exp];
		y /= ten_pwr[EXP_IDX_BIAL - exp];
		x += y;
		x = (v->sgn ? -x : x);
	}
	return x;
}

void float2mval(mval *dst, float src)
{
        char    buf[BUFF_LEN];	/* The maximum-length value in the 'F' representation would look like -0.123457 (that is,
				 * sign[1] + zero[1] + dot[1] + digits[6] = 9). Note that for values below -1 the number would
				 * be shorter, since the integer part would be counted against the precision capacity. However,
				 * a longer representation is possible in the 'E' format: -1.23456E+12 (that is, sign[1] +
				 * digit[1] + dot[1] + digits[5] + E[1] + sign[1] + exp[2] = 12). Although we only need to
				 * additionally worry about one termination character, we will be safe and allocate 16 bytes
				 * instead of 13. */

	SNPRINTF(buf, BUFF_LEN, "%.6G", src);
	dst->mvtype = MV_STR;
	dst->str.len = STRLEN(buf);
	dst->str.addr = buf;
	s2n(dst);
	dst->mvtype &= ~MV_STR;
	return;
}

void double2mval(mval *dst, double src)
{
        char    buf[BUFF_LEN];		/* The maximum-length value in the 'F' representation would look like -0.123456789012345
					 * (that is, sign[1] + zero[1] + dot[1] + digits[15] = 18). Note that for values below -1
					 * the number would be shorter, since the integer part would be counted against the
					 * precision capacity. However, a longer representation is possible in the 'E' format:
                                         * -1.234567890123456E+123 (that is, sign[1] + digit[1] + dot[1] + digits[14] + E[1] +
					 * sign[1] + exp[3] = 23). Although we only need to additionally worry about one termination
                                         * character, we will be safe and allocate 32 bytes instead of 24. */

	SNPRINTF(buf, BUFF_LEN, "%.15G", src);
	dst->mvtype = MV_STR;
	dst->str.len = STRLEN(buf);
	dst->str.addr = buf;
	s2n(dst);
	dst->mvtype &= ~MV_STR;
	return;
}

/* Converts an mval into a 32-bit signed integer, or MAXPOSINT4 on overflow. */
int4 mval2i(mval *v)
{
	int4	i;
	double	j;
	int	exp;

	MV_FORCE_NUM(v);
	if (v->mvtype & MV_INT)
		i = v->m[1] / MV_BIAS;
	else
	{
		exp = v->e;
		if (exp > EXP_IDX_BIAL)
		{
			j = mval2double(v);
			i = (MAXPOSINT4 >= j) ? (int4)j : MAXPOSINT4;
		} else if (exp < MV_XBIAS)
			i = 0;
		else
			i = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
	}
	return i;
}

/* Converts an mval into a 32-bit unsigned integer, or MAXUINT4 on overflow. */
uint4 mval2ui(mval *v)
{
	uint4	i;
	double	j;
	int	exp;

	MV_FORCE_NUM(v);
	if (v->mvtype & MV_INT)
		i = v->m[1] / MV_BIAS;
	else
	{
		exp = v->e;
		if (exp > EXP_IDX_BIAL)
		{
			j = mval2double(v);
			i = (MAXUINT4 >= j) ? (uint4)j : MAXUINT4;
		} else if (exp < MV_XBIAS)
			i = 0;
		else
			i = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
	}
	return i;
}


/* Converts an mval into a 64-bit unsigned integer. */
gtm_uint64_t mval2ui8(mval *v)
{
	return (gtm_uint64_t)mval2i8(v);
}

gtm_int64_t mval2i8(mval *v)
{
	gtm_int64_t	x, y;
	int		exp;

	MV_FORCE_NUM(v);
	if (v->mvtype & MV_INT)
		x = v->m[1] / MV_BIAS;
	else
	{
		exp = v->e;
		if (exp > EXP_IDX_BIAL)
		{	/* Case where to get the actual value we need to multiply by power of exponent. */
			x = v->m[1];
			y = v->m[0];
			if (y > 0)
			{	/* Both m[0] and m[1] are used, so multiply in parallel, but first ensure that the m[1] part has
				 * a decimal exponent of MANT_HI order.
				 */
				x *= MANT_HI;
				while (exp > EXP_IDX_BIAL + 18)
				{	/* Keep multiplying by 10^9, but keep a precision "buffer" of 18 to prevent further
					 * divisions, as we might otherwise compromise the available precision of mval.
					 */
					x *= MANT_HI;
					y *= MANT_HI;
					exp -= 9;
				}
				if (exp >= EXP_IDX_BIAL + 9)
				{	/* Multiply by the remaining power of the exponent. */
					x *= ten_pwr[exp - EXP_IDX_BIAL - 9];
					y *= ten_pwr[exp - EXP_IDX_BIAL - 9];
				} else
				{	/* Case where exponent indicates a total power of less than 10^9, which, given that both
					 * m[0] and m[1] are used and that x has already been multiplied by 10^9, requires a
					 * division to make the sum of m[0] and m[1] represent the right number.
					 */
					x /= ten_pwr[EXP_IDX_BIAL + 9 - exp];
					y /= ten_pwr[EXP_IDX_BIAL + 9 - exp];
				}
			} else
			{	/* Since m[0] is not used, just multiply x by the excess power of the exponent. */
				while (exp > EXP_IDX_BIAL + 9)
				{
					x *= MANT_HI;
					exp -= 9;
				}
				x *= ten_pwr[exp - EXP_IDX_BIAL];
			}

			x = (v->sgn ? -x - y : x + y);
		} else if (exp < MV_XBIAS)
			x = 0;
		else
			x = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
	}
	return x;
}

/* isint == v can be represented as a 9 digit (or less) integer (positive or negative).
 * If return value is TRUE, then "*intval" contains the integer value stored in "v".
 * Note: "*intval" could have been updated even if return value is FALSE.
 */
boolean_t isint(mval *v, int4 *intval)
{
	int			exp, m1, mvtype, divisor, m1_div;
	DEBUG_ONLY(boolean_t	is_canonical;)

	mvtype = v->mvtype;
	/* Note that input mval might have "MV_NM" bit set even though it is not a numeric (i.e. a string).
	 * This is possible in case the input mval is a constant literal string. In this case, since these
	 * might reside in read-only sections of the executable and the MV_FORCE_* macros might not be able
	 * to touch them, we define the numeric portions of the mval to be 0 and set the MV_NM bit as well.
	 * But in addition, the MV_NUM_APPROX bit will be set to indicate this is an approximation. So if we
	 * see the MV_NM bit set, we should also check the MV_NUM_APPROX bit is unset before we go ahead
	 * and check the numeric part of this mval for whether it is an integer.
	 */
	DEBUG_ONLY(is_canonical = MV_IS_CANONICAL(v));
	assert(!is_canonical || (MVTYPE_IS_NUMERIC(mvtype) && !MVTYPE_IS_NUM_APPROX(mvtype)));
	if (!MVTYPE_IS_NUMERIC(mvtype) || MVTYPE_IS_NUM_APPROX(mvtype))
		return FALSE;
	assert(v->m[1] < MANT_HI);
	if (mvtype & MV_INT)
	{
		divisor = MV_BIAS;
		m1 = v->m[1];
	} else
	{
		exp = v->e;
		if ((MV_XBIAS >= exp) || (EXP_IDX_BIAL < exp) || (0 != v->m[0]))
			return FALSE;
		divisor = ten_pwr[EXP_IDX_BIAL - exp];
		if (v->sgn)
			m1 = -v->m[1];
		else
			m1 = v->m[1];
	}
	m1_div = (m1 / divisor);
	assert(NULL != intval);
	*intval = m1_div;
	return ((m1_div * divisor) == m1);
}
