/*
								+--------------------------------+
								|                                |
								|  ***  Hexadecimal floats  ***  |
								|                                |
								|  Copyright © -tHE SWINe- 2014  |
								|                                |
								|          HexFloat.cpp          |
								|                                |
								+--------------------------------+
*/

/**
 *	@file low/HexFloat.cpp
 *	@author -tHE SWINe-
 *	@date 2014
 *	@brief hexadecimal floating point number conversions
 */

#include "HexFloat.h"

/*
 *								=== CFloatCommon ===
 */

/**
 *	@brief joins two 32-bit numbers into a 64-bit one
 *
 *	@param[in] hi is the most significant 32 bits
 *	@param[in] lo is the least significant 32 bits
 *
 *	@return Returns <tt>hi << 32 | lo</tt>.
 */
#define n_Mask64_HiLo(hi,lo) ((((uint64_t)(hi)) << 32) | (lo))

CFloatCommon::TParseInfo CFloatCommon::t_ParseXLiteral(const char *p_s_literal)
{
	TParseInfo t_info = {0};
	t_info.b_parsed = false; // may be superifical
	// parse info

	_ASSERTE(p_s_literal);
	if(!p_s_literal)
		return t_info;

	bool b_negative = (*p_s_literal == '-');
	if(*p_s_literal == '+' || *p_s_literal == '-')
		++ p_s_literal;
	// skip sign

	if(*p_s_literal != '0' || p_s_literal[1] != 'x')
		return t_info;
	p_s_literal += 2;
	// skip "0x"

	uint64_t n_mantissa = 0;
	int n_mantissa_dp_pos = 0; // number of digits after the decimal point
	bool b_had_mantissa_dp = false;
	while(*p_s_literal && *p_s_literal != 'p') {
		char c = *p_s_literal;
		++ p_s_literal;
		// eat a character

		int n_digit;
		if(c >= '0' && c <= '9')
			n_digit = c - '0';
		else if(c >= 'a' && c <= 'f')
			n_digit = c - 'a' + 10;
		else if(c >= 'A' && c <= 'F')
			n_digit = c - 'A' + 10;
		else if(c == '.') {
			if(b_had_mantissa_dp)
				return t_info; // can't have two decimal points
			b_had_mantissa_dp = true;
			continue;
		} else
			return t_info;
		// decode it

		if(n_mantissa >> 60)
			return t_info;
		// overflow in mantissa

		n_mantissa <<= 4;
		n_mantissa |= n_digit;
		// shift digits

		if(b_had_mantissa_dp)
			++ n_mantissa_dp_pos;
		// count decimal places
	}
	// parse mantissa

	if(!n_mantissa)
		return t_info;
	// mantissa can't be zero

	int n_exp_sign = 1;
	int n_exponent = 0;
	if(*p_s_literal == 'p') {
		++ p_s_literal;
		// skip 'p'

		if(*p_s_literal == '-')
			n_exp_sign = -1;
		if(*p_s_literal == '+' || *p_s_literal == '-')
			++ p_s_literal;
		// parse and skip sign

		while(*p_s_literal) {
			char c = *p_s_literal;
			++ p_s_literal;
			// eat a character

			int n_digit;
			if(c >= '0' && c <= '9')
				n_digit = c - '0';
			else if(c >= 'a' && c <= 'f')
				n_digit = c - 'a' + 10;
			else if(c >= 'A' && c <= 'F')
				n_digit = c - 'A' + 10;
			else
				return t_info;
			// decode it

			if(n_exponent >> 28)
				return t_info;
			// overflow in exponent

			n_exponent <<= 4;
			n_exponent |= n_digit;
			// shift digits
		}
		n_exponent *= n_exp_sign;
	}
	// parse exponent

	if(*p_s_literal) {
		//return t_info;
		// ignore any trailing characters
	}
	// handle unparsed trailing characters

	t_info.b_parsed = true;
	t_info.b_sign_bit = b_negative;
	t_info.n_exponent = n_exponent;
	t_info.n_mantissa = n_mantissa;
	t_info.n_mantissa_dp_position = n_mantissa_dp_pos * 4; // nibble hex digits
	// write the outputs

	return t_info;
}

int CFloatCommon::n_HighestBit_Set(uint64_t n_mantissa)
{
	uint64_t n_mantissa_fill = n_mantissa;
	n_mantissa_fill |= n_mantissa_fill >> 32;
	n_mantissa_fill |= n_mantissa_fill >> 16;
	n_mantissa_fill |= n_mantissa_fill >> 8;
	n_mantissa_fill |= n_mantissa_fill >> 4;
	n_mantissa_fill |= n_mantissa_fill >> 2;
	n_mantissa_fill |= n_mantissa_fill >> 1;
	// make mantissa into a string of ones, starting at the first set bit

	const uint64_t B[] = {
		n_Mask64_HiLo(0x55555555U, 0x55555555U), n_Mask64_HiLo(0x33333333U, 0x33333333U),
		n_Mask64_HiLo(0x0f0f0f0fU, 0x0f0f0f0fU)
#ifdef _DEBUG
		, n_Mask64_HiLo(0x00ff00ffU, 0x00ff00ffU), n_Mask64_HiLo(0x0000ffffU, 0x0000ffffU),
		n_Mask64_HiLo(0x00000000U, 0xffffffffU) // only needed for doublechecking
#endif // _DEBUG
	};
	uint64_t n_mantissa_bits = n_mantissa_fill - ((n_mantissa_fill >> 1) & B[0]);
	n_mantissa_bits = ((n_mantissa_bits >> 2) & B[1]) + (n_mantissa_bits & B[1]);
	int n_highest_mantissa_bit = (int)((((n_mantissa_bits +
		(n_mantissa_bits >> 4)) & B[2]) * n_Mask64_HiLo(0x01010101U, 0x01010101U)) >> 56);
	_ASSERTE(((!n_mantissa && n_highest_mantissa_bit == 0) || n_highest_mantissa_bit > 0) && n_highest_mantissa_bit <= 64);
	_ASSERTE(!n_highest_mantissa_bit || (((uint64_t)1 << (n_highest_mantissa_bit - 1)) |
		(((uint64_t)1 << (n_highest_mantissa_bit - 1)) - 1)) == n_mantissa_fill); // mask equals filled mantissa
	_ASSERTE((n_mantissa_fill & n_mantissa) == n_mantissa); // mantissa and mask equals mantissa
	_ASSERTE((~n_mantissa_fill & n_mantissa) == 0); // mantissa and inverse mask equals zero
	// get highest bit

#ifdef _DEBUG
	n_mantissa_bits = ((n_mantissa_bits >> 4) + n_mantissa_bits) & B[2];
	n_mantissa_bits = ((n_mantissa_bits >> 8) + n_mantissa_bits) & B[3];
	n_mantissa_bits = ((n_mantissa_bits >> 16) + n_mantissa_bits) & B[4];
	n_mantissa_bits = ((n_mantissa_bits >> 32) + n_mantissa_bits) & B[5];
	int n_highest_mantissa_bit_ref = (int)n_mantissa_bits;
	_ASSERTE(n_highest_mantissa_bit == n_highest_mantissa_bit_ref); // should be the same, but slower
	// count number of set bits in mantissa
#endif // _DEBUG

	return n_highest_mantissa_bit;
}

uint32_t CFloatCommon::n_SkipTrailingZeroes(uint32_t n)
{
	if(!n)
		return n;
	if(!(n & 0xffff))
		n >>= 16;
	if(!(n & 0xff))
		n >>= 8;
	if(!(n & 0xf))
		n >>= 4;
	if(!(n & 0x3))
		n >>= 2;
	if(!(n & 0x1))
		n >>= 1;
	_ASSERTE(n & 1); // make sure there is no trailing zero
	return n;
}

uint64_t CFloatCommon::n_SkipTrailingZeroes(uint64_t n)
{
	if(!n)
		return n;
	if(!(n & 0xffffffffU))
		n >>= 32;
	if(!(n & 0xffff))
		n >>= 16;
	if(!(n & 0xff))
		n >>= 8;
	if(!(n & 0xf))
		n >>= 4;
	if(!(n & 0x3))
		n >>= 2;
	if(!(n & 0x1))
		n >>= 1;
	_ASSERTE(n & 1); // make sure there is no trailing zero
	return n;
}

uint32_t CFloatCommon::n_SkipTrailingZeroes_Mod4(uint32_t n)
{
	if(!n)
		return n;
	if(!(n & 0xffff))
		n >>= 16;
	if(!(n & 0xff))
		n >>= 8;
	if(!(n & 0xf))
		n >>= 4;
	/*if(!(n & 0x3)) // only to whole hex digits
		n >>= 2;
	if(!(n & 0x1))
		n >>= 1;*/
	return n;
}

uint64_t CFloatCommon::n_SkipTrailingZeroes_Mod4(uint64_t n)
{
	if(!n)
		return n;
	if(!(n & 0xffffffffU))
		n >>= 32;
	if(!(n & 0xffff))
		n >>= 16;
	if(!(n & 0xff))
		n >>= 8;
	if(!(n & 0xf))
		n >>= 4;
	/*if(!(n & 0x3)) // only to whole hex digits
		n >>= 2;
	if(!(n & 0x1))
		n >>= 1;*/
	return n;
}

const char *CFloatCommon::p_s_FPU_ModeName(int n_mode)
{
	_ASSERTE(n_mode >= fmode_Unknown && n_mode < fmode_MaxModes);
	static const char *p_mode_table[] = {
		"unknown",
		"to nearest, ties to even",
		"to nearest, ties away from zero",
		"to nearest, unknown ties handling",
		"to nearest, stochastic ties handling",
		"towards positive infinity",
		"towards negative infinity", "towards zero", "stochastic"
	};
	_ASSERTE(sizeof(p_mode_table) / sizeof(p_mode_table[0]) == fmode_MaxModes - fmode_Unknown);
	return p_mode_table[n_mode - fmode_Unknown];
}

/*
 *								=== ~CFloatCommon ===
 */

/*
 *								=== CFloatRepresentationTraits<float> ===
 */

const char *CFloatRepresentationTraits<float>::p_s_XString(float f)
{
	static char p_s_string[256];
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	sprintf_s(p_s_string, sizeof(p_s_string), PRIxfloat, PRIxfloatparams(f));
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	sprintf(p_s_string, PRIxfloat, PRIxfloatparams(f));
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	return p_s_string;
}

/*
 *								=== ~CFloatRepresentationTraits<float> ===
 */

/*
 *								=== CFloatRepresentationTraits<double> ===
 */

const char *CFloatRepresentationTraits<double>::p_s_XString(double f)
{
	static char p_s_string[256];
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	sprintf_s(p_s_string, sizeof(p_s_string), PRIxdouble, PRIxdoubleparams(f));
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	sprintf(p_s_string, PRIxdouble, PRIxdoubleparams(f));
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	return p_s_string;
}

/*
 *								=== ~CFloatRepresentationTraits<double> ===
 */
