/* *********************************************************************
 * This Original Work is copyright of 51 Degrees Mobile Experts Limited.
 * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
 * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
 *
 * This Original Work is licensed under the European Union Public Licence
 * (EUPL) v.1.2 and is subject to its terms as set out below.
 *
 * If a copy of the EUPL was not distributed with this file, You can obtain
 * one at https://opensource.org/licenses/EUPL-1.2.
 *
 * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
 * amended by the European Commission) shall be deemed incompatible for
 * the purposes of the Work and the provisions of the compatibility
 * clause in Article 5 of the EUPL shall not apply.
 *
 * If using the Work as, or as part of, a network application, by
 * including the attribution notice(s) required under Article 5 of the EUPL
 * in the end user terms of the application under an appropriate heading,
 * such notice(s) shall fulfill the requirements of that article.
 * ********************************************************************* */

#ifndef FIFTYONE_DEGREES_FLOAT_H_INCLUDED
#define FIFTYONE_DEGREES_FLOAT_H_INCLUDED

/**
 * @ingroup FiftyOneDegreesCommon
 * @defgroup fiftyoneDegreesFloat Float
 *
 * IEEE Single Precision Floating Point standard implementation 
 * and methods to convert to native float type.
 *
 * ## Introduction
 * IEEE Single Precision Floating Point standard is supported in
 * majority of modern computers. However, it is not always guaranteed,
 * so a indepdent implementation is required so that on the machines where
 * this standard is not supported, the float type from the data file can
 * still be read correctly.
 *
 * ## Endianness
 * Endianess difference between machines will not be handled at the
 * moment, until it is supported by 51Degrees data file.
 *
 * ## Limitations
 * Positive sign will always be defaulted to during a conversion from native
 * float type when input float is NaN (Not a Number) or Inf (Infinity).
 * 
 * When converting from 51Degrees implementation to native float type, if it results
 * in a NaN or Inf, the value from compiler macros that represent
 * these number will be returned. i.e. NAN and INFINITY
 *
 * @{
 */

#include <stdint.h>
#include "data.h"
#include "common.h"

/**
 * IEEE single precision floating point bias value
 */
#define FIFTYONE_DEGREES_FLOAT_BIAS 127
/**
 * IEEE single precision floating point number of bytes
 */
#define FIFTYONE_DEGREES_FLOAT_SIZE 4
/**
 * IEEE single precision floating point base value
 */
#define FIFTYONE_DEGREES_FLOAT_RADIX 2
/**
 * IEEE single precision floating point number of bits for sgn
 */
#define FIFTYONE_DEGREES_FLOAT_SIGN_SIZE 1
/**
 * IEEE single precision floating point number of bits for exponent
 */
#define FIFTYONE_DEGREES_FLOAT_EXP_SIZE 8
/**
 * IEEE single precision floating point number of bits for mantissa
 */
#define FIFTYONE_DEGREES_FLOAT_MANT_SIZE 23
/**
 * IEEE single precision floating point max positive value
 */
#define FIFTYONE_DEGREES_FLOAT_MAX 3.402823466E38f
/**
 * IEEE single precision floating point min positive value
 */
#define FIFTYONE_DEGREES_FLOAT_MIN 1.175494351E-38f
/**
 * IEEE single precision floating point min negative value
 */
#define FIFTYONE_DEGREES_FLOAT_MIN_NEG -3.402823466E38f
/**
 * IEEE single precision floating point max exponent value where all bits
 * are 1. This can only happen in NaN (Not a Number) and Inf (Infinity) cases.
 */
#define FIFTYONE_DEGREES_FLOAT_EXP_MAX 255
/**
 * IEEE single precision floating point max mantissa value where all bits
 * are 1.
 */
#define FIFTYONE_DEGREES_FLOAT_MANT_MAX 8388607

/**
 * Struture that represents 51Degrees implementation, which encapsulate
 * an array of 4 bytes. This array will be formated accordingly to
 * the IEEE standard
 */
typedef struct fiftyone_degrees_float_type {
	byte value[FIFTYONE_DEGREES_FLOAT_SIZE];
} fiftyoneDegreesFloatInternal;

/**
 * Union that breaks down 51Degrees implementation to its components:
 * sign, exponent and mantissa.
 */
typedef union {
	fiftyoneDegreesFloatInternal fValue;
	struct {
		uint32_t mantissa : FIFTYONE_DEGREES_FLOAT_MANT_SIZE;
		uint32_t exponent : FIFTYONE_DEGREES_FLOAT_EXP_SIZE;
		uint32_t sign : FIFTYONE_DEGREES_FLOAT_SIGN_SIZE;
	} parts;
} fiftyoneDegreesFloatU;

/**
 * Function that converts from a 51Degrees float implementation to a
 * native float value.
 * @param f input 51Degrees float value
 * @return a native float value
 */
EXTERNAL float fiftyoneDegreesFloatToNative(fiftyoneDegreesFloatInternal f);
/**
 * Function that converts from a native float value to 51Degrees float
 * value.
 * @param f input native float value
 * @return a 51Degrees float value
 */
EXTERNAL fiftyoneDegreesFloatInternal fiftyoneDegreesNativeToFloat(float f);
/**
 * Function that compare if two 51Degrees float values are equal
 * @param f1 first input 51Degrees float value
 * @param f2 second input 51Degrees float value
 * @return 0 if the two are equal and 1 if they are not.
 */
EXTERNAL int fiftyoneDegreesFloatIsEqual(fiftyoneDegreesFloatInternal f1, fiftyoneDegreesFloatInternal f2);

/**
 * For some compilers such as clang and Microsoft C or computers where
 * the IEEE single precision standard is supported, default the type
 * to the native float type.
 */
#if _MSC_VER || (FLT_RADIX == 2 && FLT_MANT_DIG == 24 && FLT_MAX_EXP == 128 && FLT_MIN_EXP == -125)
/**
 * Define 51Degrees float implementation as native float.
 */
typedef float fiftyoneDegreesFloat;

/**
 * Convert from 51Degrees float to native float
 * @param f 51Degrees float
 * @return native float value. In this case, it is a straight map
 * to the input value itself.
 */
#define FIFTYONE_DEGREES_FLOAT_TO_NATIVE(f) f
/**
 * Convert from native float to 51Degrees float
 * @param f native float type
 * @return a 51Degrees float value. In this case, it is a straight
 * map to the input value itself.
 */
#define FIFTYONE_DEGREES_NATIVE_TO_FLOAT(f) f
/**
 * Compare two 51Degrees float input values.
 * @param f1 a 51Degrees float input value.
 * @param f2 a 51Degrees float input value.
 * @return 0 if the two values are the same and 1 otherwise.
 */
#define FIFTYONE_DEGREES_FLOAT_IS_EQUAL(f1, f2) (f1 == f2 ? 0 : 1)
#else
/**
 * Define 51Degrees float implementation as the internal type
 * IEEE standard is not supported in this case
 */
typedef fiftyoneDegreesFloatInternal fiftyoneDegreesFloat;
/**
 * Function that converts from a 51Degrees float implementation to a
 * native float value.
 * @param f input 51Degrees float value
 * @return a native float value
 */
#define FIFTYONE_DEGREES_FLOAT_TO_NATIVE(f) fiftyoneDegreesFloatToNative(f)
/**
 * Function that converts from a native float value to 51Degrees float
 * value.
 * @param f input native float value
 * @return a 51Degrees float value
 */
#define FIFTYONE_DEGREES_NATIVE_TO_FLOAT(f) fiftyoneDegreesNativeToFloat(f)
/**
 * Function that compare if two 51Degrees float values are equal
 * @param f1 first input 51Degrees float value
 * @param f2 second input 51Degrees float value
 * @return 0 if the two are equal and 1 if they are not.
 */
#define FIFTYONE_DEGREES_FLOAT_IS_EQUAL(f1, f2) fiftyoneDegreesFloatIsEqual(f1, f2)
#endif

/**
 * @}
 */

#endif
