// cl_SF internals

#ifndef _CL_SF_H
#define _CL_SF_H

#include "cln/number.h"
#include "cl_F.h"

namespace cln {

// The immediate word contains:
//   |..|.......|..........................|....|
//  sign exponent             mantissa      tag

  #define SF_value_shift 7	// could also be = cl_value_shift
  #define SF_exp_len    8	// number of bits in the exponent
  #define SF_mant_len  16	// number of bits in the mantissa
				// (excluding the hidden bit)
  #define SF_mant_hiddenbit 1	// yes, we have a hidden bit representation
				// (this is hardwired in some of the code)
  #define SF_exp_low   1			// minimum exponent
  #define SF_exp_mid   bit(SF_exp_len-1)	// exponent bias
  #define SF_exp_high  (bit(SF_exp_len)-1)	// maximum exponent
  #define SF_exp_shift  (SF_mant_len+SF_mant_shift) // lowest exponent bit
  #define SF_mant_shift  SF_value_shift		    // lowest mantissa bit
  #define SF_sign_shift  (cl_pointer_size - 1)

// Builds a float from the immediate word.
inline cl_SF::cl_SF (struct cl_sfloat * null, cl_uint w)
	: cl_F ((cl_private_thing) w) { unused null; }
inline const cl_SF cl_SF_from_word (cl_uint word)
{
	return cl_SF((struct cl_sfloat *) 0, word);
}

// Builds a float word from sign (0 or -1), exponent and mantissa.
inline cl_uint make_SF_word (cl_sint sign, unsigned int exp, cl_uint mant)
{
	return (sign & ((cl_uint)1 << SF_sign_shift))
	       | (exp << SF_exp_shift)
	       #if SF_mant_hiddenbit
	       | ((mant & (bit(SF_mant_len)-1)) << SF_mant_shift)
	       #else
	       | (mant << SF_mant_shift)
	       #endif
	       | (cl_SF_tag << cl_tag_shift);
}

// Builds a float from sign (0 or -1), exponent and mantissa.
inline const cl_SF make_SF (cl_sint sign, unsigned int exp, cl_uint mant)
{
	return cl_SF_from_word(make_SF_word(sign,exp,mant));
}

// Short Float 0.0
  #define SF_0  make_SF(0,0,0)
// Short Float 1.0
  #define SF_1  make_SF(0,SF_exp_mid+1,bit(SF_mant_len))
// Short Float -1.0
  #define SF_minus1  make_SF(-1,SF_exp_mid+1,bit(SF_mant_len))


// Entpacken eines Short-Float:
// SF_decode(obj, zero_statement, sign=,exp=,mant=);
// zerlegt ein Short-Float obj.
// Ist obj=0.0, wird zero_statement ausgeführt.
// Sonst: cl_signean sign = Vorzeichen (0 = +, -1 = -),
//        sintL exp = Exponent (vorzeichenbehaftet),
//        uintL mant = Mantisse (>= 2^SF_mant_len, < 2^(SF_mant_len+1))
inline uintL SF_uexp (const cl_SF& x)
{
	return (x.word >> SF_exp_shift) & (bit(SF_exp_len)-1);
}
inline cl_signean SF_sign (const cl_SF& x)
{
	return ((cl_sint)x.word << (cl_pointer_size-1 - SF_sign_shift)) >> (cl_pointer_size-1);
}
inline uintL SF_mant (const cl_SF& x)
{
	return
	       #if SF_mant_hiddenbit
	       bit(SF_mant_len) |
	       #endif
	       ((uintL)(x.word >> SF_mant_shift) & (bit(SF_mant_len)-1));
}
#define SF_decode(_x, zero_statement, sign_zuweisung,exp_zuweisung,mant_zuweisung)  \
  { var uintL uexp = SF_uexp(_x);					\
    if (uexp==0)							\
      { zero_statement } /* e=0 -> Zahl 0.0 */				\
      else								\
      { exp_zuweisung (sintL)(uexp - SF_exp_mid);	/* Exponent */	\
        unused (sign_zuweisung SF_sign(_x));		/* Vorzeichen */\
        mant_zuweisung SF_mant(_x);			/* Mantisse */  \
  }   }

// Einpacken eines Short-Float:
// encode_SF(sign,exp,mant)
// liefert ein Short-Float.
// > cl_signean sign: Vorzeichen, 0 für +, -1 für negativ.
// > sintL exp: Exponent
// > uintL mant: Mantisse, sollte >= 2^SF_mant_len und < 2^(SF_mant_len+1) sein.
// < object ergebnis: ein Short-Float
// Der Exponent wird auf Überlauf/Unterlauf getestet.
inline const cl_SF encode_SF (cl_signean sign, sintL exp, uintL mant)
{
	if (exp < (sintL)(SF_exp_low-SF_exp_mid))
	  { if (underflow_allowed())
	      { cl_error_floating_point_underflow(); }
	      else
	      { return SF_0; }
	  }
	else
	if (exp > (sintL)(SF_exp_high-SF_exp_mid))
	  { cl_error_floating_point_overflow(); }
	else
	return make_SF(sign, exp+SF_exp_mid, mant);
}


// Liefert zu einem Short-Float x : (futruncate x), ein SF.
// x wird von der 0 weg zur nächsten ganzen Zahl gerundet.
extern const cl_SF futruncate (const cl_SF& x);

// SF_to_I(x) wandelt ein Short-Float x, das eine ganze Zahl darstellt,
// in ein Integer um.
extern const cl_I cl_SF_to_I (const cl_SF& x);

// cl_I_to_SF(x) wandelt ein Integer x in ein Short-Float um und rundet dabei.
extern const cl_SF cl_I_to_SF (const cl_I& x);

// cl_RA_to_SF(x) wandelt eine rationale Zahl x in ein Short-Float um
// und rundet dabei.
extern const cl_SF cl_RA_to_SF (const cl_RA& x);

}  // namespace cln

#endif /* _CL_SF_H */
