// format_scale_exponent().

// General includes.
#include "cl_sysdep.h"

CL_PROVIDE(cl_fmt_scaleexp)

// Specification.
#include "cl_format.h"


// Implementation.

#include "cln/real.h"
#include "cln/integer.h"
#include "cln/float.h"
#include "cl_F.h"
#include "cl_SF.h"
#include "cl_FF.h"
#include "cl_DF.h"
#include "cl_LF.h"

namespace cln {

// NOTE: This may introduce roundoff-errors, through the use of *, /, expt.
// But this doesn't matter since format_float_to_string() works with
// exact integers, starting with integer_decode_float().

// For a floating point format f, five characteristic numbers:
struct float_format_params {
	cl_F zero;	// cl_float(0,f)
	cl_F one;	// cl_float(1,f)
	cl_F ten;	// cl_float(10,f)
	cl_F tenth;	// cl_float(1/10,f)
	cl_F lg2;	// log(10,2), as needed (max. 32 bits)
// Constructor:
	float_format_params (cl_F a, cl_F b, cl_F c, cl_F d, cl_F e)
		: zero(a), one(b), ten(c), tenth(d), lg2(e) {}
};

static const cl_RA tenth = (cl_RA)"1/10";
static const cl_SF SF_zero = cl_RA_to_SF(0);
static const cl_SF SF_one = cl_RA_to_SF(1);
static const cl_SF SF_ten = cl_RA_to_SF(10);
static const cl_SF SF_tenth = cl_RA_to_SF(tenth);
static const cl_FF FF_zero = cl_RA_to_FF(0);
static const cl_FF FF_one = cl_RA_to_FF(1);
static const cl_FF FF_ten = cl_RA_to_FF(10);
static const cl_FF FF_tenth = cl_RA_to_FF(tenth);
static const cl_DF DF_zero = cl_RA_to_DF(0);
static const cl_DF DF_one = cl_RA_to_DF(1);
static const cl_DF DF_ten = cl_RA_to_DF(10);
static const cl_DF DF_tenth = cl_RA_to_DF(tenth);
static const cl_SF SF_lg2 = (cl_SF)"0.30103";
static const cl_DF DF_lg2 = (cl_DF)"0.30102999566";

static const float_format_params get_float_params (const cl_F& arg)
{
	floattypecase(arg
	,	return float_format_params(SF_zero,SF_one,SF_ten,SF_tenth,SF_lg2);
	,	return float_format_params(FF_zero,FF_one,FF_ten,FF_tenth,SF_lg2);
	,	return float_format_params(DF_zero,DF_one,DF_ten,DF_tenth,SF_lg2);
	,	var uintC len = TheLfloat(arg)->len;
		return float_format_params(
			cl_I_to_LF(0,len),
			cl_I_to_LF(1,len),
			cl_I_to_LF(10,len),
			cl_RA_to_LF(tenth,len),
			DF_lg2 // lg2 wird mit 32 Bit Genauigkeit gebraucht
		       );
	);
}

const decoded_float format_scale_exponent (const cl_F& arg)
{
	// Get float format parameters.
	var const float_format_params params = get_float_params(arg);
	var const cl_F& zero = params.zero;
	var const cl_F& one = params.one;
	var const cl_F& ten = params.ten;
	var const cl_F& tenth = params.tenth;
	var const cl_F& lg2 = params.lg2;
	// Decode arg.
	if (zerop(arg))
		return decoded_float(zero,0,one);
	var cl_F abs_arg = abs(arg);
	var decoded_float decoded = decode_float(abs_arg);
	var cl_I& expon = decoded.exponent;
	var cl_I expon10a = truncate1(expon*lg2); // nicht round, um Überlauf zu vermeiden
	var cl_F signif10a = abs_arg / expt(ten,expon10a);
	// Maybe need to increment expon10.
	var cl_I expon10b = expon10a;
	var cl_F signif10b = signif10a;
	{
		var cl_F tenpow = ten;
		until (signif10b < one) {
			expon10b = expon10b + 1;
			signif10b = signif10a / tenpow;
			tenpow = tenpow * ten;
		}
	}
	// Maybe need to decrement expon10.
	var cl_I expon10c = expon10b;
	var cl_F signif10c = signif10b;
	{
		var cl_F tenpow = ten;
		until (signif10c >= tenth) {
			expon10c = expon10c - 1;
			signif10c = signif10b * tenpow;
			tenpow = tenpow * ten;
		}
	}
	return decoded_float(signif10c,expon10c,float_sign(arg));
}

}  // namespace cln

CL_PROVIDE_END(cl_fmt_scaleexp)
