// cl_RA_to_LF().

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

// Specification.
#include "cl_LF.h"


// Implementation.

#include "cl_LF_impl.h"
#include "cl_RA.h"
#include "cl_integer.h"
#include "cl_I.h"
#include "cl_F.h"

cl_LF cl_RA_to_LF (const cl_RA& x, uintC len)
{
// Methode:
// x ganz -> klar.
// x = +/- a/b mit Integers a,b>0:
//   Sei k,m so gewählt, daß
//     2^(k-1) <= a < 2^k, 2^(m-1) <= b < 2^m.
//   Dann ist 2^(k-m-1) < a/b < 2^(k-m+1).
//   Ergebnis-Vorzeichen := Vorzeichen von x.
//   Berechne k=(integer-length a) und m=(integer-length b).
//   Ergebnis-Exponent := k-m.
//   Ergebnis-Mantisse:
//     Berechne floor(2^(-k+m+16n+1)*a/b) :
//       Bei k-m>=16n+1 dividiere a durch (ash b (k-m-16n-1)),
//       bei k-m<16n+1 dividiere (ash a (-k+m+16n+1)) durch b.
//     Der erste Wert ist >=2^16n, <2^(16n+2).
//     Falls er >=2^(16n+1) ist, erhöhe Exponent um 1,
//       runde 2 Bits weg und schiebe dabei um 2 Bits nach rechts;
//     falls er <2^(16n+1) ist,
//       runde 1 Bit weg und schiebe dabei um 1 Bit nach rechts.
      if (integerp(x)) {
        DeclareType(cl_I,x);
        return cl_I_to_LF(x,len);
      }
 {    // x Ratio
      DeclareType(cl_RT,x);
      var cl_I a = numerator(x); // +/- a
      var const cl_I& b = denominator(x); // b
      var cl_signean sign = -(cl_signean)minusp(a); // Vorzeichen
      if (!(sign==0)) { a = -a; } // Betrag nehmen, liefert a
      var sintL lendiff = (sintL)integer_length(a) // (integer-length a)
                          - (sintL)integer_length(b); // (integer-length b)
      // |lendiff| < intDsize*2^intCsize. Da für LF-Exponenten ein sintL zur
      // Verfügung steht, braucht man keinen Test auf Overflow oder Underflow.
      var uintL difflimit = intDsize*(uintL)len + 1; // 16n+1
      var cl_I zaehler;
      var cl_I nenner;
      if (lendiff > (sintL)difflimit)
        // 0 <= k-m-16n-1 < k < intDsize*2^intCsize
        { nenner = ash(b,(uintL)(lendiff - difflimit));
          zaehler = a;
        }
        else
        // 0 < -k+m+16n+1 <= m+1 + 16n < intDsize*2^intCsize + intDsize*2^intCsize
        { zaehler = ash(a,(uintL)(difflimit - lendiff)); // (ash a -k+m+16n+1)
          nenner = b; // b
        }
      // Division zaehler/nenner durchführen:
      var cl_I_div_t q_r = cl_divide(zaehler,nenner);
      var cl_I& q = q_r.quotient;
      var cl_I& r = q_r.remainder;
      // 2^16n <= q < 2^(16n+2), also ist q Bignum mit n+1 Digits.
      var Lfloat y = allocate_lfloat(len,lendiff+LF_exp_mid,sign); // neues Long-Float
      var uintD* y_mantMSDptr = arrayMSDptr(TheLfloat(y)->data,len);
      {var uintD* q_MSDptr = arrayMSDptr(TheBignum(q)->data,len+1);
       if (mspref(q_MSDptr,0) == 1) // erstes Digit =1 oder =2,3 ?
         // 2^16n <= q < 2^(16n+1), also 2^(k-m-1) < a/b < 2^(k-m).
         { // Mantisse mit einer Schiebeschleife um 1 Bit nach rechts füllen:
           var uintD rounding_bit =
             shiftrightcopy_loop_msp(q_MSDptr mspop 1,y_mantMSDptr,len,1,1);
           if ( (rounding_bit == 0) // herausgeschobenes Bit =0 -> abrunden
                || ( eq(r,0) // =1 und Rest r > 0 -> aufrunden
                     // round-to-even
                     && ((mspref(y_mantMSDptr,len-1) & bit(0)) ==0)
              )    )
             goto ab; // abrunden
             else
             goto auf; // aufrunden
         }
         else
         // 2^(16n+1) <= q < 2^(16n+2), also 2^(k-m) < a/b < 2^(k-m+1).
         { // Mantisse mit einer Schiebeschleife um 2 Bit nach rechts füllen:
           var uintD rounding_bits =
             shiftrightcopy_loop_msp(q_MSDptr mspop 1,y_mantMSDptr,len,2,mspref(q_MSDptr,0));
           (TheLfloat(y)->expo)++; // Exponenten incrementieren auf k-m+1
           if ( ((sintD)rounding_bits >= 0) // herausgeschobenes Bit =0 -> abrunden
                || ( ((rounding_bits & bit(intDsize-2)) ==0) // =1 und nächstes Bit =1 oder Rest r > 0 -> aufrunden
                     && eq(r,0)
                     // round-to-even
                     && ((mspref(y_mantMSDptr,len-1) & bit(0)) ==0)
              )    )
             goto ab; // abrunden
             else
             goto auf; // aufrunden
         }
      }
      auf: // aufrunden
        { if ( inc_loop_lsp(y_mantMSDptr mspop len,len) )
            // Übertrag durchs Aufrunden
            { mspref(y_mantMSDptr,0) = bit(intDsize-1); // Mantisse := 10...0
              (TheLfloat(y)->expo)++; // Exponenten incrementieren
        }   }
      ab: // abrunden
      return y;
}}
