// cl_I_to_SF().

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

// Specification.
#include "cl_SF.h"


// Implementation.

#include "cln/integer.h"
#include "cl_I.h"
#include "cl_DS.h"

namespace cln {

const cl_SF cl_I_to_SF (const cl_I& x)
{
// Methode:
// x=0 -> Ergebnis 0.0
// Merke Vorzeichen von x.
// x:=(abs x)
// Exponent:=(integer-length x)
//   Greife die 18 höchstwertigen Bits heraus (angeführt von einer 1).
//   Runde das letzte Bit weg:
//     Bit 0 = 0 -> abrunden,
//     Bit 0 = 1 und Rest =0 -> round-to-even,
//     Bit 0 = 1 und Rest >0 -> aufrunden.
//   Dabei um ein Bit nach rechts schieben.
//   Bei Aufrundung auf 2^17 (rounding overflow) Mantisse um 1 Bit nach rechts
//     schieben und Exponent incrementieren.
      if (eq(x,0)) { return SF_0; }
      var cl_signean sign = -(cl_signean)minusp(x); // Vorzeichen
      var cl_I abs_x = (sign==0 ? x : -x);
      var uintL exp = integer_length(abs_x); // (integer-length x)
      // NDS zu |x|>0 bilden:
      var const uintD* MSDptr;
      var uintC len;
      I_to_NDS_nocopy(abs_x, MSDptr=,len=,,cl_false,);
      // MSDptr/len/LSDptr ist die NDS zu x, len>0.
      // Führende Digits holen: Brauche SF_mant_len+1 Bits, dazu intDsize
      // Bits (die NDS kann mit bis zu intDsize Nullbits anfangen).
      // Dann werden diese Bits um (exp mod intDsize) nach rechts geschoben.
      var uintD msd = msprefnext(MSDptr); // erstes Digit
      #if (intDsize==64)
      var uintD msdd = 0; // weiteres Digit
      if (--len == 0) goto ok;
      msdd = msprefnext(MSDptr);
      #else // (intDsize<=32)
      var uint32 msdd = 0; // weitere min(len-1,32/intDsize) Digits
      #define NEXT_DIGIT(i)  \
        { if (--len == 0) goto ok;                                   \
          msdd |= (uint32)msprefnext(MSDptr) << (32-(i+1)*intDsize); \
        }
      DOCONSTTIMES(32/intDsize,NEXT_DIGIT);
      #undef NEXT_DIGIT
      #endif
      --len; ok:
      #if (intDsize==64)
      // Die NDS besteht aus msd, msdd, und len weiteren Digits.
      // Das höchste in 2^intDsize*msd+msdd gesetzte Bit ist Bit Nummer
      // intDsize-1 + (exp mod intDsize).
      var uintL shiftcount = exp % intDsize;
      var uint64 mant = // führende 64 Bits
        (shiftcount==0
         ? msdd
         : ((msd << (64-shiftcount)) | (msdd >> shiftcount))
        );
      // Das höchste in mant gesetzte Bit ist Bit Nummer 63.
      if ( ((mant & bit(62-SF_mant_len)) ==0) // Bit 46 =0 -> abrunden
           || ( ((mant & (bit(62-SF_mant_len)-1)) ==0) // Bit 46 =1 und Bits 45..0 =0
                && ((msdd & (bit(shiftcount)-1)) ==0) // und weitere Bits aus msdd =0
                && (!test_loop_msp(MSDptr,len)) // und alle weiteren Digits =0
                // round-to-even, je nach Bit 47 :
                && ((mant & bit(63-SF_mant_len)) ==0)
         )    )
        // abrunden
        { mant = mant >> (63-SF_mant_len); }
        else
        // aufrunden
        { mant = mant >> (63-SF_mant_len);
          mant += 1;
          if (mant >= bit(SF_mant_len+1)) // rounding overflow?
            { mant = mant>>1; exp = exp+1; }
        }
      #else
      // Die NDS besteht aus msd, msdd, und len weiteren Digits.
      // Das höchste in 2^32*msd+msdd gesetzte Bit ist Bit Nummer
      // 31 + (exp mod intDsize).
      var uintL shiftcount = exp % intDsize;
      var uint32 mant = // führende 32 Bits
        (shiftcount==0
         ? msdd
         : (((uint32)msd << (32-shiftcount)) | (msdd >> shiftcount))
        );
      // Das höchste in mant gesetzte Bit ist Bit Nummer 31.
      if ( ((mant & bit(30-SF_mant_len)) ==0) // Bit 14 =0 -> abrunden
           || ( ((mant & (bit(30-SF_mant_len)-1)) ==0) // Bit 14 =1 und Bits 13..0 =0
                && ((msdd & (bit(shiftcount)-1)) ==0) // und weitere Bits aus msdd =0
                && (!test_loop_msp(MSDptr,len)) // und alle weiteren Digits =0
                // round-to-even, je nach Bit 15 :
                && ((mant & bit(31-SF_mant_len)) ==0)
         )    )
        // abrunden
        { mant = mant >> (31-SF_mant_len); }
        else
        // aufrunden
        { mant = mant >> (31-SF_mant_len);
          mant += 1;
          if (mant >= bit(SF_mant_len+1)) // rounding overflow?
            { mant = mant>>1; exp = exp+1; }
        }
      #endif
      return encode_SF(sign,(sintL)exp,mant);
}

}  // namespace cln
