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

#pragma once
#ifndef __HEXADECIMAL_FLOATING_POINT_CONVERSION_INCLUDED
#define __HEXADECIMAL_FLOATING_POINT_CONVERSION_INCLUDED

/**
 *	@file low/HexFloat.h
 *	@author -tHE SWINe-
 *	@date 2014
 *	@brief hexadecimal floating point number conversions
 *
 *	@note Note that in MSVC 6.0, /Op must be specified (enable float consistency) in release.
 *
 *	@todo This is probably slightly buggy, as the routines correctly do not work with denormals.
 *		CFloatUtils::n_Get_Mantissa() is now fixed.
 *
 */

#include "../Integer.h"
#include <math.h> // abs(), exp()
#include <stdlib.h> // abs(), rand()
#include <stdio.h> // sprintf(), fprintf()
#ifdef __HEXFLOAT_UNIT_TESTS
#include <string.h> // strcmp()
#endif // __HEXFLOAT_UNIT_TESTS

/**
 *	@def PRIxfloat
 *	@brief printf string for printing hexadecimal floats (use with PRIxfloatparams)
 *	@note A similar functionality is achieved by using "%a" on compilers that support it.
 */
#define PRIxfloat "%s0x1.%0*xp%c%x"

/**
 *	@def PRIxdouble
 *
 *	@brief printf string for printing hexadecimal doubles (use with PRIxdoubleparams)
 *
 *	Use:
 *	@code
 *	printf("%g = " PRIxdouble "\n", .0, PRIxdoubleparams(.0));
 *	printf("%g = " PRIxdouble "\n", 1.0, PRIxdoubleparams(1.0));
 *	printf("%g = " PRIxdouble "\n", 2.0, PRIxdoubleparams(2.0));
 *	printf("%g = " PRIxdouble "\n", 3.14159265358979, PRIxdoubleparams(3.14159265358979));
 *	@endcode
 *
 *	@note This relies on "%llx" for printing a 64-bit hexadecimal literal, which may
 *		be compiler-specific or unsupported on certain platforms.
 *	@note A similar functionality is achieved by using "%a" on compilers that support it.
 */
#define PRIxdouble "%s0x1.%0*" _PRIx64 "p%c%x"

/**
 *	@def PRIxfloatparams
 *	@brief printf arguments for printing hexadecimal floats (to be used with PRIxfloats)
 *	@param f is single-precision floating point number to be printed
 */
#define PRIxfloatparams(f) ((CFloatUtils<float>::n_Get_SignBit(f))? "-" : ""), \
	((!CFloatUtils<float>::n_Get_Fraction(f))? 1 : (CFloatCommon::n_HighestBit_Set( \
	CFloatCommon::n_SkipTrailingZeroes_Mod4(CFloatUtils<float>::n_Get_Mantissa(f) << \
	CFloatRepresentationTraits<float>::n_fraction_print_offset)) + 3) / 4 - 1), \
	CFloatCommon::n_SkipTrailingZeroes_Mod4(CFloatUtils<float>::n_Get_Fraction(f) << \
	CFloatRepresentationTraits<float>::n_fraction_print_offset), \
	((CFloatUtils<float>::n_Get_Exponent(f) < 0)? '-' : '+'), \
	abs(CFloatUtils<float>::n_Get_Exponent(f))

/**
 *	@def PRIxdoubleparams
 *	@brief printf arguments for printing hexadecimal doubles (to be used with PRIxdouble)
 *	@param f is double-precision floating point number to be printed
 */
#define PRIxdoubleparams(f) ((CFloatUtils<double>::n_Get_SignBit(f))? "-" : ""), \
	((!CFloatUtils<double>::n_Get_Fraction(f))? 1 : (CFloatCommon::n_HighestBit_Set( \
	CFloatCommon::n_SkipTrailingZeroes_Mod4(CFloatUtils<double>::n_Get_Mantissa(f) << \
	CFloatRepresentationTraits<double>::n_fraction_print_offset)) + 3) / 4 - 1), \
	CFloatCommon::n_SkipTrailingZeroes_Mod4(CFloatUtils<double>::n_Get_Fraction(f) << \
	CFloatRepresentationTraits<double>::n_fraction_print_offset), \
	((CFloatUtils<double>::n_Get_Exponent(f) < 0)? '-' : '+'), \
	abs(CFloatUtils<double>::n_Get_Exponent(f))

/**
 *	@brief information about floating point representation
 *	@tparam CType is floating-point data type
 */
template <class CType>
class CFloatRepresentationTraits;

/**
 *	@brief class with implementation of utility function shared
 *		between different specializations of CFloatUtils
 */
class CFloatCommon {
public:
	/**
	 *	@brief names of FPU rounding modes
	 */
	enum {
		fmode_Unknown = -1, /**< unknown deterministic mode */
		fmode_Nearest_TiesToEven, /**< round to nearest, ties to even */
		fmode_Nearest_TiesAway, /**< round to nearest, ties away from zero */
		fmode_Nearest_TiesUnknown, /**< round to nearest, ties handled with unknown method */
		fmode_Nearest_TiesStochastic, /**< round to nearest, ties handled stochastically or alternately */
		fmode_TowardsPosInf, /**< round towards positive infinity */
		fmode_TowardsNegInf, /**< round towards negative infinity */
		fmode_TowardsZero, /**< round towards zero */
		fmode_Stochastic, /**< stochastic rounding */

		fmode_MaxModes /**< @brief number of FPU modes */
		// this must be the last item
	};

	/**
	 *	@brief hexadecimal literal parsing info
	 */
	struct TParseInfo {
		bool b_parsed; /**< @brief parsing success flag (if not set, the rest of the structure is not valid) */
		bool b_sign_bit; /**< @brief sign bit flag */
		int n_exponent; /**< @brief signed, unbiased value of exponent */
		uint64_t n_mantissa; /**< @brief value of mantissa, including the implied one */
		int n_mantissa_dp_position; /**< @brief number of mantissa bits after the decimal point */
	};

public:
	/**
	 *	@brief parses hexadecimal literal
	 *	@param[in] p_s_literal is null-terminated string, containing floating point literal
	 *		in the <tt>[+-]0x1.[fraction]p[+-][exponent]</tt> format
	 *	@return Returns structure, containing the parsed values.
	 */
	static TParseInfo t_ParseXLiteral(const char *p_s_literal);

	/**
	 *	@brief calculates position of the highest bit set
	 *	@param[in] n_mantissa is input number
	 *	@return Returns the position of the highest bit set, counted from LSB.
	 */
	static int n_HighestBit_Set(uint64_t n_mantissa);

	/**
	 *	@brief skips all trailing zeroes in a 32-bit number
	 *	@param[in] n is input number
	 *	@return Returns value of the input shifted right by the least amount, so that
	 *		the least significat bit is set (or returns zero if the input is zero).
	 */
	static uint32_t n_SkipTrailingZeroes(uint32_t n);

	/**
	 *	@brief skips all trailing zeroes in a 64-bit number
	 *	@param[in] n is input number
	 *	@return Returns value of the input shifted right by the least amount, so that
	 *		the least significat bit is set (or returns zero if the input is zero).
	 */
	static uint64_t n_SkipTrailingZeroes(uint64_t n);

	/**
	 *	@brief skips whole nibbles of trailing zeroes in a 32-bit number
	 *	@param[in] n is input number
	 *	@return Returns value of the input shifted right by the least amount, so that
	 *		the least significat bit is set (or returns zero if the input is zero).
	 */
	static uint32_t n_SkipTrailingZeroes_Mod4(uint32_t n);

	/**
	 *	@brief skips whole nibbles of trailing zeroes in a 64-bit number
	 *	@param[in] n is input number
	 *	@return Returns value of the input shifted right by the least amount, so that
	 *		the least significat bit is set (or returns zero if the input is zero).
	 */
	static uint64_t n_SkipTrailingZeroes_Mod4(uint64_t n);

	/**
	 *	@brief gets printable FPU rounding mode name
	 *	@param[in] n_mode is FPU rounding mode, one of fmode_*
	 *	@return Returns null-terminated string with printable name
	 *		of the specified rounding mode (may contain spaces and colons).
	 */
	static const char *p_s_FPU_ModeName(int n_mode);
};

/**
 *	@brief floating point utilities
 *	@tparam CType is floating-point type (float or double)
 */
template <class CType>
class CFloatUtils {
public:
	typedef CType TType; /**< @brief floating-point type */
	typedef CFloatRepresentationTraits<TType> TTraits; /**< @brief floating-point type traits */
	typedef typename TTraits::TIntType TIntType; /**< @brief matching unsigned integer type */

	/**
	 *	@brief floating-point type parameters, stored as enum
	 */
	enum {
		n_sign_bit_shift = sizeof(TType) * 8 - 1, /**< bit position of the sign bit */
		n_exponent_bit_num = TTraits::n_exponent_bit_num, /**< number of exponent bits */
		n_exponent_bias = TTraits::n_exponent_bias, /**< exponent bias */
		n_exponent_special_low = TTraits::n_exponent_special_low, /**< special exponent low value (used in IEEE-754 to represent zero) */
		n_exponent_special_high = TTraits::n_exponent_special_high, /**< special exponent high value (used in IEEE-754 to represent infinity or NaN) */
		n_fraction_bit_num = TTraits::n_fraction_bit_num, /**< number of fraction bits */
		n_mantissa_bit_num = n_fraction_bit_num + 1 /**< number of mantissa bits */
	};

	/**
	 *	@brief a helper union for safe type puning
	 */
	union UFloatInt {
		TType f; /**< @brief floating-point representation */
		TIntType n; /**< @brief integer representation */
	};

public:
	/**
	 *	@brief gets mask of exponent bits
	 *	@return Returns mask of exponent bits.
	 */
	static inline TIntType n_Exponent_Mask();

	/**
	 *	@brief gets mask of fraction bits
	 *	@return Returns mask of fraction bits.
	 */
	static inline TIntType n_Fraction_Mask();

	/**
	 *	@brief gets mask of the (implied) leading mantissa one bit
	 *	@return Returns the (implied) leading mantissa one bit.
	 */
	static inline TIntType n_Mantissa_One();

	/**
	 *	@brief gets sign bit from a float
	 *	@param[in] f is floating point input (assumed IEEE-754)
	 *	@return Returns the value of the sign bit (1 if negative, 0 if positive).
	 */
	static inline int n_Get_SignBit(TType f);

	/**
	 *	@brief gets exponent part from a float
	 *	@param[in] f is floating point input (assumed IEEE-754)
	 *	@return Returns the value of the exponent.
	 */
	static inline int n_Get_Exponent(TType f);

	/**
	 *	@brief gets fraction from a float
	 *	@param[in] f is floating point input (assumed IEEE-754)
	 *	@return Returns the value of the fraction part of the mantissa.
	 *	@note The value of the fraction is aligned to the right (has some leading zeros).
	 */
	static inline TIntType n_Get_Fraction(TType f);

	/**
	 *	@brief gets mantissa from a float
	 *	@param[in] f is single precision floating point input (assumed IEEE-754)
	 *	@return Returns the value of the mantissa (including the leading 1 bit).
	 *	@note The value of the mantissa is aligned to the right (has some leading zeros).
	 */
	static inline TIntType n_Get_Mantissa(TType f);

	/**
	 *	@brief returns (signed) size of the unit at the last place
	 *	@param[in] f is input value (mantissa is not significant)
	 *	@return Returns a signed size of the unit at the last place of the specified value.
	 *	@note ULP of very small numbers may be not representable, and zero is returned in such case.
	 */
	static inline TType f_ULP(TType f);

	/**
	 *	@brief makes a floating point number from its components
	 *
	 *	@param[in] b_negative_sign is sign flag (if nonzero, the output is negative)
	 *	@param[in] n_mantissa is mantissa value (including the leading number)
	 *	@param[in] n_exponent is exponent value (signed, unbiased)
	 *
	 *	@return Returns the specified floating point number.
	 */
	static inline TType f_MakeFloat(bool b_negative_sign, TIntType n_mantissa, int n_exponent);

	/**
	 *	@brief gets value of infinity
	 *	@return Returns value of infinity.
	 */
	static inline TType f_Infinity();

	/**
	 *	@brief gets value of not-a-number
	 *	@return Returns value of not-a-number.
	 */
	static inline TType f_Not_a_Number();

	/**
	 *	@brief gets parse error constant
	 *	@return Returns value of the parse error constant.
	 *	@note This is returned by f_ParseXFloat() upon error. It is a denormalized
	 *		number, and as such should not occur from common arithmetic manipulation.
	 */
	static inline TType f_ParseXError();

	/**
	 *	@brief checks if an argument is finite
	 *	@param[in] f is number to check
	 *	@return Returns false if the argument is either not a number
	 *		or positive or negative infinity, otherwise returns true.
	 */
	static inline bool b_Is_Finite(TType f);

	/**
	 *	@brief checks if an argument is not a number
	 *	@param[in] f is number to check
	 *	@return Returns 1 if the argument is not a number, otherwise returns 0.
	 */
	static inline bool b_Is_NaN(TType f);

	/**
	 *	@brief checks if an argument is positive infinity
	 *	@param[in] f is number to check
	 *	@return Returns 1 if the argument is positive infinity, otherwise returns 0.
	 */
	static inline bool b_Is_Infinity(TType f);

	/**
	 *	@brief checks if an argument is positive infinity
	 *	@param[in] f is number to check
	 *	@return Returns 1 if the argument is positive infinity, otherwise returns 0.
	 */
	static inline bool b_Is_NegativeInfinity(TType f);

	/**
	 *	@brief parses hexadecimal floating-point string
	 *	@param[in] p_s_float is null-terminated string, containing floating point
	 *		in the <tt>[+-]0x1.[fraction]p[+-][exponent]</tt> format
	 *	@return Returns parsed value on success, or f_ParseXError() on failure.
	 */
	static inline TType f_ParseXFloat(const char *p_s_float);

	/**
	 *	@brief parses hexadecimal floating-point string
	 *
	 *	@param[out] r_f_result is filled with the parsed value upon successful return
	 *	@param[in] p_s_float is null-terminated string, containing floating point
	 *		in the <tt>[+-]0x1.[fraction]p[+-][exponent]</tt> format
	 *
	 *	@return Returns true on success, false on failure.
	 */
	static bool ParseXFloat(TType &r_f_result, const char *p_s_float);

	/**
	 *	@brief tests if the current FPU mode applies tie-breaking to nearest even number
	 *	@return Returns 1 if the current FPU mode applies tie-breaking to nearest even,
	 *		otherwise returns 0.
	 *	@note This only tests once, in case stochastic rounding is in place, the result
	 *		may not be reliable.
	 */
	static bool b_TieBreak_ToEven();

	/**
	 *	@brief tests if the current FPU mode applies tie-breaking away from zero
	 *	@return Returns 1 if the current FPU mode applies tie-breaking away from zero,
	 *		otherwise returns 0.
	 *	@note This only tests once, in case stochastic rounding is in place, the result
	 *		may not be reliable.
	 */
	static bool b_TieBreak_AwayFromZero();

	/**
	 *	@brief detects the current FPU rounding mode
	 *
	 *	@return Returns one of CFloatCommon::fmode_*.
	 *
	 *	@note This function performs the test several times in order to detect stochastic
	 *		or alternate rounding modes.
	 *	@note Use CFloatCommon::p_s_FPU_ModeName() to get printable representation of the name.
	 */
	static int n_FPU_RoundMode();

#ifdef __HEXFLOAT_UNIT_TESTS
	/**
	 *	@brief performs unit tests for the above floating-point functions
	 *	@return Returns true is unit tests passed, returns false if some of the tests failed.
	 */
	static bool UnitTests();
#endif // __HEXFLOAT_UNIT_TESTS

protected:
#ifdef __HEXFLOAT_UNIT_TESTS
	static void RT_AssertHandler(const char *p_s_expr, int n_line, const char *p_s_file);
#endif // __HEXFLOAT_UNIT_TESTS
};

#include "HexFloat.inl"

#endif // !__HEXADECIMAL_FLOATING_POINT_CONVERSION_INCLUDED
