/*	Copyright  (c)	Günter Woigk 2010 - 2020
					mailto:kio@little-bat.de

	This file is free software.

	Permission to use, copy, modify, distribute, and sell this software
	and its documentation for any purpose is hereby granted without fee,
	provided that the above copyright notice appears in all copies and
	that both that copyright notice, this permission notice and the
	following disclaimer appear in supporting documentation.

	THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY,
	NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
	A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE COPYRIGHT HOLDER
	BE LIABLE FOR ANY DAMAGES ARISING FROM THE USE OF THIS SOFTWARE,
	TO THE EXTENT PERMITTED BY APPLICABLE LAW.
*/

//#define LOGLEVEL 1
#include "kio/kio.h"
#include "Type.h"
#include <limits>
#include <math.h>
#include <cfloat>
#include "utilities.h"
#include "globals.h"
#include "backends/TargetDescr.h"


ON_INIT([]{
	// if one of these tests fail, then maybe this system uses non-IEEE floats.
	// then we need to investigate what effects this has on what we can compile.
	static_assert(std::numeric_limits<float>().is_iec559); // actually tests for has_inf, has_nan and has_denorm !?!
	static_assert(FLT_MANT_DIG==24);
	static_assert(FLT_MIN_EXP==-125);
	static_assert(FLT_MAX_EXP==128);

	static_assert(std::numeric_limits<double>().is_iec559); // actually tests for has_inf, has_nan and has_denorm !?!
	static_assert(DBL_MANT_DIG==53);
	static_assert(DBL_MIN_EXP==-1021);
	static_assert(DBL_MAX_EXP==1024);

	static_assert(std::numeric_limits<long double>().is_iec559); // actually tests for has_inf, has_nan and has_denorm !?!
	static_assert(LDBL_MANT_DIG==64);
	static_assert(LDBL_MIN_EXP==-16381);
	static_assert(LDBL_MAX_EXP==16384);

	xlogline("maxf = %.20Lg", float128(std::numeric_limits<float>().max())); // 3.4028234663852885981e+38
	xlogline("minf = %.20Lg", float128(std::numeric_limits<float>().min())); // 1.175494350822287508e-38

	xlogline("maxd = %.20Lg", float128(std::numeric_limits<double>().max())); // 1.7976931348623157081e+308
	xlogline("mind = %.20Lg", float128(std::numeric_limits<double>().min())); // 2.2250738585072013831e-308

	xlogline("maxl = %.20Lg", std::numeric_limits<long double>().max()); // 1.189731495357231765e+4932
	xlogline("minl = %.20Lg", std::numeric_limits<long double>().min()); // 3.3621031431120935063e-4932

	static_assert(std::numeric_limits<float32>().max() == +3.402823466e+38f);

	// double:
	assert(!isinf(ldexp (0x0ffffffffu,1024-32)));
	assert( isinf(ldexp (0x100000000u,1024-32)));
	assert( ldexp(0x1fffffffffffffu,1024-53) == std::numeric_limits<double>().max());
	assert( ldexp(0x1fffffffffffffu,1024-53) == 1.79769313486231571e+308);

	// long double:
	assert(!isinfl(ldexpl(0xffffffffffffffffu,16384-64)));
	assert( isinfl(ldexpl(0x8000000000000000u,16384-63)));
	assert( ldexpl(0xffffffffffffffffu,16384-64) == std::numeric_limits<long double>().max());
	assert( ldexpl(0xffffffffffffffffu,16384-64) == 1.1897314953572317650e+4932L);

	xlogline("fmaxl = %.20Lg", std::numeric_limits<long double>().max()); // 1.189731495357231765e+4932
	xlogline("fminl = %.20Lg", std::numeric_limits<long double>().min()); // 3.3621031431120935063e-4932

	assert( isnormal(ldexpl(0x8000000000000000u, -16381-64)));
	assert( isnormal(ldexpl(0xffffffffffffffffu, -16381-64)));

	assert(!isnormal(ldexpl(0x8000000000000000u, -16381-65)));
	assert(!isnormal(ldexpl(0xfffffffffffffffeu, -16381-65)));	// <-- ok
  //assert(!isnormal(ldexpl(0xffffffffffffffffu, -16381-65)));	// <-- fails. rounding?
});

#ifdef DEBUG
static_assert(FLT_RADIX==2,"");
static_assert(FLT_MANT_DIG==24,"");
static_assert(DBL_MANT_DIG==53,"");
static_assert(LDBL_MANT_DIG==64,"");

ON_INIT([]
{
	xlogline("float32:  mant=%u, exp=[%i..%i]", FLT_MANT_DIG, FLT_MIN_EXP, FLT_MAX_EXP);  // 24, -125 .. +128
	xlogline("float64:  mant=%u, exp=[%i..%i]", DBL_MANT_DIG, DBL_MIN_EXP, DBL_MAX_EXP);  // 53, -1021 .. +1024
	xlogline("float128: mant=%u, exp=[%i..%i]", LDBL_MANT_DIG,LDBL_MIN_EXP,LDBL_MAX_EXP); // 64, -16381 .. +16384

	//float f;
	//f = ldexpf(0.5f,FLT_MIN_EXP);    logline("0.5f << -125 = %g", double(f)); //assert( f != 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-2);  logline("0.5f << -127 = %g", double(f)); //assert( f == 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-23); logline("0.5f >> (125+23) = %g", double(f)); //assert( f == 0.0f);
	//f = ldexpf(0.5f,FLT_MIN_EXP-24); logline("0.5f >> (125+24) = %g", double(f)); //assert( f == 0.0f);

	pp::Type* t32 = new pp::Float32Type();
	pp::Type* t64 = new pp::Float64Type();
	pp::Type* t80 = new pp::Float128Type();

	t32->isNaN(nanl(""));
	t64->isNaN(0.0l/0.0l);
	t80->isNaN(sqrtl(-1.0l));

	assert(t64->isInfinity(+1e+350l));
	assert(t64->isInfinity(-1e+350l));
	assert(t64->isRoundToZero(+1e-350l));
	assert(t64->isRoundToZero(-1e-350l));
	assert(t64->isDenormalized(ldexpl(0.55l,DBL_MIN_EXP-10)));
	assert(t64->numLostBits(ldexpl(0.55l,DBL_MIN_EXP-10))==10);

	assert(t32->isInfinity(ldexpl(0.50000l,FLT_MAX_EXP+1)));
	assert(t32->isInfinity(ldexpl(0.99999l,FLT_MAX_EXP+1)));
	assert(t64->isInfinity(ldexpl(0.50000l,DBL_MAX_EXP+1)));
	assert(t64->isInfinity(ldexpl(0.99999l,DBL_MAX_EXP+1)));
	assert(t80->isInfinity(ldexpl(0.50001l,LDBL_MAX_EXP+1)));
	assert(t80->isInfinity(ldexpl(0.99999l,LDBL_MAX_EXP+1)));

	assert(!t32->isInfinity(ldexpl(0.50000l,FLT_MAX_EXP)));
	assert(!t32->isInfinity(ldexpl(0.99999l,FLT_MAX_EXP)));
	assert(!t64->isInfinity(ldexpl(0.50000l,DBL_MAX_EXP)));
	assert(!t64->isInfinity(ldexpl(0.99999l,DBL_MAX_EXP)));
	assert(!t80->isInfinity(ldexpl(0.50000l,LDBL_MAX_EXP)));
	assert(!t80->isInfinity(ldexpl(0.99999l,LDBL_MAX_EXP)));

	assert(t32->isRoundToZero(ldexpl(0.5l,FLT_MIN_EXP-FLT_MANT_DIG)));
	assert(t64->isRoundToZero(ldexpl(0.5l,DBL_MIN_EXP-DBL_MANT_DIG)));
	//assert(t80->isRoundToZero(ldexpl(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG)));	if round to 0 then it already is 0
	assert(ldexpl(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG) == 0.0l);
	assert(!t32->isRoundToZero(ldexpl(0.5l,FLT_MIN_EXP-FLT_MANT_DIG+1)));
	assert(!t64->isRoundToZero(ldexpl(0.5l,DBL_MIN_EXP-DBL_MANT_DIG+1)));
	assert(!t80->isRoundToZero(ldexpl(0.5l,LDBL_MIN_EXP-LDBL_MANT_DIG+1)));

	long double l;
	l = ldexpl(0.5l,DBL_MIN_EXP-DBL_MANT_DIG+1); xxlogline("0.5f >> (1021+53-1) = %g", double(l));
		assert(t64->numLostBits(l) == DBL_MANT_DIG-1);
		assert(t64->isDenormalized(l));
		assert(!t64->isRoundToZero(l));

	l = ldexpl(0.5l,DBL_MIN_EXP-DBL_MANT_DIG);   xxlogline("0.5f >> (1021+53)   = %g", double(l));
		assert(t64->numLostBits(l) == DBL_MANT_DIG);
		assert(t64->isDenormalized(l));
		assert(t64->isRoundToZero(l));

	l = ldexpl(0.5l, LDBL_MIN_EXP - LDBL_MANT_DIG + 1);
		assert(t80->numLostBits(l) == LDBL_MANT_DIG - 1);
		assert(t80->isDenormalized(l));
	l = ldexpl(0.5l, LDBL_MIN_EXP - 1);
		assert(t80->numLostBits(l) == 1);
		assert(t80->isDenormalized(l));
	l = ldexpl(0.9l, LDBL_MIN_EXP - 1);
		assert(t80->numLostBits(l) == 1);
		assert(t80->isDenormalized(l));
});
#endif



namespace pp
{

static cType ti8  (NameID::tINT8,   TypeID::INT8,    8,  minint8, maxint8);
static cType tu8  (NameID::tUINT8,  TypeID::UINT8,   8,        0, maxuint8);
static cType ti16 (NameID::tINT16,  TypeID::INT16,  16, minint16, maxint16);
static cType tu16 (NameID::tUINT16, TypeID::UINT16, 16,        0, maxuint16);
static cType ti32 (NameID::tINT32,  TypeID::INT32,  32, minint32, maxint32);
static cType tu32 (NameID::tUINT32, TypeID::UINT32, 32,        0, maxuint32);
static cType ti64 (NameID::tINT64,  TypeID::INT64,  64, minint64, maxint64);
static cType tu64 (NameID::tUINT64, TypeID::UINT64, 64,        0, maxuint64);
static cType tvoi (NameID::tVOID,   TypeID::VOID,    0,        0, 0);

cType *const tint8   = &ti8;
cType *const tuint8  = &tu8;
cType *const tint16  = &ti16;
cType *const tuint16 = &tu16;
cType *const tint32  = &ti32;
cType *const tuint32 = &tu32;
cType *const tint64  = &ti64;
cType *const tuint64 = &tu64;
cType *const tvoid   = &tvoi;

static cType f32ie {
	NameID::tSFLOAT, TypeID::FLOAT32, 32,
	float128(+3.402823466e+38f),	// +float128(std::numeric_limits<float32>().max()),
	float128(-3.402823466e+38f),	// -float128(std::numeric_limits<float32>().max()),
	24, 			// FLT_MANT_DIG, // 24	  num digits for mantissa
	-125,			// FLT_MIN_EXP,	 // -125  min. exponent for mantissa in range 0.5 ≤ mant < 1.0
	+128,			// FLT_MAX_EXP,	 // +128  max. exponent for mantissa in range 0.5 ≤ mant < 1.0
	yes };			// denormalizes before tiny values are rounded to zero

static cType f64ie {
	NameID::tFLOAT, TypeID::FLOAT64, 64,
	float128(+1.79769313486231571e+308), // +ldexpl( 0x1fffffffffffffu,1024-53), // +float128(std::numeric_limits<float64>().max()),
	float128(-1.79769313486231571e+308), // -ldexpl( 0x1fffffffffffffu,1024-53), // -float128(std::numeric_limits<float64>().max()),
	53,				// DBL_MANT_DIG, // 53
	-1021,			// DBL_MIN_EXP,	 // -1021
	+1024,			// DBL_MAX_EXP,	 // +1024
	yes };

static cType f128ie {
	NameID::tLFLOAT, TypeID::FLOAT128, 128,
	+1.189731495357231765e+4932l, // +ldexpl( 0xffffffffffffffffu,16384-64), // +std::numeric_limits<float128>().max(),
	-1.189731495357231765e+4932l, // -ldexpl( 0xffffffffffffffffu,16384-64), // -std::numeric_limits<float128>().max(),
	64,				// LDBL_MANT_DIG,	// 64
	-16381,			// LDBL_MIN_EXP,	// -16381
	+16384,			// LDBL_MAX_EXP,	// +16384
	yes };

cType *const tf32ieee  = &f32ie;
cType *const tf64ieee  = &f64ie;
cType *const tf128ieee = &f128ie;

static cType f32 {
	NameID::tSFLOAT, TypeID::FLOAT32, 32,
	float128(+std::numeric_limits<float32>().max()),
	float128(-std::numeric_limits<float32>().max()),
	FLT_MANT_DIG,	// 24	 num digits for mantissa
	FLT_MIN_EXP,	// -125  min. exponent for mantissa in range 0.5 ≤ mant < 1.0
	FLT_MAX_EXP,	// +128  max. exponent for mantissa in range 0.5 ≤ mant < 1.0
	std::numeric_limits<float32>().has_denorm };

static cType f64 {
	NameID::tFLOAT, TypeID::FLOAT64, 64,
	float128(+std::numeric_limits<float64>().max()), // +1.79769313486231571e+308l, // +ldexpl( 0x1fffffffffffffu,1024-53),
	float128(-std::numeric_limits<float64>().max()), // -1.79769313486231571e+308l, // -ldexpl( 0x1fffffffffffffu,1024-53),
	DBL_MANT_DIG,	// 53
	DBL_MIN_EXP,	// -1021
	DBL_MAX_EXP,	// +1024
	std::numeric_limits<float64>().has_denorm };

static cType f128 {
	NameID::tLFLOAT,TypeID::FLOAT128, 128,
	+std::numeric_limits<float128>().max(), // +1.189731495357231765e+4932l, // +ldexpl( 0xffffffffffffffffu,16384-64),
	-std::numeric_limits<float128>().max(), // -1.189731495357231765e+4932l, // -ldexpl( 0xffffffffffffffffu,16384-64),
	LDBL_MANT_DIG,	// 64
	LDBL_MIN_EXP,	// -16381
	LDBL_MAX_EXP,	// +16384
	std::numeric_limits<float128>().has_denorm };

cType *const tf32  = &f32;	// floats for this system
cType *const tf64  = &f64;
cType *const tf128 = &f128;

cType* tfloat;		// -> bpsf	NULL if if target supports no floating point numbers
cType* tsfloat;		// -> bpf	""
cType* tlfloat;		// -> bplf	""
cType* tbyte;
cType* tubyte;		// -> bpb
cType* tshort;
cType* tushort;		// -> bps
cType* tint;
cType* tuint;		// -> bpi
cType* tlong;
cType* tulong;		// -> bpl
cType* tbool;		// -> bpb
cType* tchar;		// -> bpc
cType* tcstr;





// ---------------------------------------------------------------
//			class Type
// ---------------------------------------------------------------

Type::Type (cType* basetype, TypeID type, cstr name, uint bits)
:
	basetype(basetype),
	name(newcopy(name)),
	type(type),
	bits(bits),
	subtypes(),
	ref_type(nullptr)
{
	if (basetype) basetype->subtypes.append(this);
}

Type::~Type ()
{
	for (uint i=0; i<subtypes.count(); i++) { delete subtypes[i]; }
}

void Type::print (FD& fd, cstr indent) const
{
	fd.write_fmt("%s%s\n", indent, name);
}




// ---------------------------------------------------------------
//			class BasicType
// ---------------------------------------------------------------

BasicType::BasicType (TypeID tid, cstr name, uint bits, float128 min, float128 max)
:
	Type(nullptr,tid,name,bits),
	precision(0),min_exp(0),max_exp(0),denormalizing(no),
	max(max),min(min)
{
	assert(isBasicType());
	assert(!isFloat());
}

BasicType::BasicType (TypeID tid, cstr name, uint bits, float128 min, float128 max, uint16 precision, int16 min_exp, int16 max_exp, bool denorm)
:
	Type(nullptr,tid,name,bits),
	precision(precision),
	min_exp(min_exp),
	max_exp(max_exp),
	denormalizing(denorm),
	max(max),min(min)
{
	assert(isBasicType());
}

bool BasicType::canRepresent(float128 value) const noexcept
{
	assert(isNumeric());

	if (isInteger()) { return value >= min && value <= max; }

	int exp;
	(void) frexpl(value, &exp);
	return exp >= min_exp && exp <= max_exp;
}

bool BasicType::isNaN (float128 value) const noexcept
{
	return isnanl(value);
}

bool BasicType::isInfinity (float128 value) const noexcept
{
	assert(isFloat());
	if (isinfl(value)) return yes;

	int exp;
	(void) frexpl(value, &exp);
	return exp > max_exp;
}

bool BasicType::isRoundToZero (float128 value) const noexcept
{
	assert(isFloat());

	if (value == 0.0l) return no;

	int exp;
	(void) frexpl(value, &exp);
	return denormalizing ? exp <= min_exp-precision : exp < min_exp;
}

bool BasicType::isDenormalized (float128 value) const noexcept
{
	assert(isFloat());

	if (value == 0.0l) return no;

	int exp;
	(void) frexpl(value, &exp);
	return exp < min_exp;
}

uint BasicType::numLostBits (float128 value) const noexcept	// due to denormalization / rounding to zero
{
	assert(isFloat());

	if (value == 0.0l) return 0;

	int exp;
	float128 mant = frexpl(value, &exp);
	(void) mant;

	if (exp >= min_exp) return 0;
	// exp < min_exp
	if (!denormalizing) return precision;				// round to zero => all lost
	if (exp <= min_exp - precision) return precision;	// even denormaization can help
	// precision > min_exp - exp
	return uint(min_exp - exp);
}

PtrType::PtrType (cType* basetype, TypeID type) :
	Type(basetype, type, catstr(basetype->name,(type==PTR?"*":"&")), target.bpp)
{
	assert(type==PTR || type==REF);
}

AllocatedType::AllocatedType (cType* basetype, TypeID type, cstr name) :
	Type(basetype, type, name, target.bpp)
{}

ArrayType::ArrayType (cType* itemtype, uint initialcount) :
	AllocatedType(itemtype, ARRAY, catstr(itemtype->name,"[]")),
	initialcount(initialcount)
{}

EnumType::EnumType (cBasicType* basetype, cstr name) :
	Type(basetype, TypeID(ENUM|basetype->type), name, basetype->bits)
{
	#if XSAFE
	for (uint i = 0; i < subtypes.count(); i++)
	{
		cType* t = subtypes[i];
		assert(!t->isEnum() || ne(t->name,name) || t == this);
	}
	#endif
}


void PtrType::print (FD& , cstr ) const		{TODO();}
void PtrType::serialize(FD&, void*) const			{TODO();}
void PtrType::deserialize(FD&, void*)				{TODO();}

void ArrayType::print (FD& , cstr ) const	{TODO();}
void ArrayType::serialize(FD&, void*) const		{TODO();}
void ArrayType::deserialize(FD&, void*)			{TODO();}

void BasicType::print (FD& , cstr ) const	{TODO();}
void BasicType::serialize(FD&, void*) const	{TODO();}
void BasicType::deserialize(FD&, void*)		{TODO();}

void EnumType::print (FD& , cstr ) const	{TODO();}
void EnumType::serialize(FD&, void*) const	{TODO();}
void EnumType::deserialize(FD&, void*)		{TODO();}


} // namespace









