// cl_double_approx().

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

// Specification.
#include "cl_rational.h"


// Implementation.

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

double cl_double_approx (const cl_RA& x)
{
// Method: same as cl_RA_to_DF().
      if (integerp(x)) {
        DeclareType(cl_I,x);
        return cl_double_approx(x);
      }
 {    // x Ratio
      DeclareType(cl_RT,x);
      union { dfloat eksplicit; double machine_double; } u;
      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)
      if (lendiff > DF_exp_high-DF_exp_mid) // Exponent >= n-m > Obergrenze ?
        {
          #if (cl_word_size==64)
          u.eksplicit =
              ((sint64)sign & bit(63))
            | ((uint64)(bit(DF_exp_len)-1) << DF_mant_len); // Infinity
          #else
          u.eksplicit.semhi =
              ((sint32)sign & bit(31))
            | ((uint32)(bit(DF_exp_len)-1) << (DF_mant_len-32)); // Infinity
          u.eksplicit.mlo = 0;
          #endif
          return u.machine_double;
        }
      if (lendiff < DF_exp_low-DF_exp_mid-2) // Exponent <= n-m+2 < Untergrenze ?
        {
          #if (cl_word_size==64)
          u.eksplicit = ((sint64)sign & bit(63)); // 0.0
          #else
          u.eksplicit.semhi = ((sint32)sign & bit(31)); // 0.0
          u.eksplicit.mlo = 0;
          #endif
          return u.machine_double;
        }
      var cl_I zaehler;
      var cl_I nenner;
      if (lendiff >= DF_mant_len+2)
        // n-m-54>=0
        { nenner = ash(b,lendiff - (DF_mant_len+2)); // (ash b n-m-54)
          zaehler = a; // a
        }
        else
        { zaehler = ash(a,(DF_mant_len+2) - lendiff); // (ash a -n+m+54)
          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^53 <= q < 2^55, also ist q Bignum mit ceiling(55/intDsize) Digits.
      var const uintD* ptr = BN_MSDptr(q);
      #if (cl_word_size==64)
      var uint64 mant = get_max64_Dptr(55,ptr);
      if (mant >= bit(DF_mant_len+2))
        // 2^54 <= q < 2^55, schiebe um 2 Bits nach rechts
        { var uint64 rounding_bits = mant & (bit(2)-1);
          lendiff = lendiff+1; // Exponent := n-m+1
          mant = mant >> 2;
          if ( (rounding_bits < bit(1)) // 00,01 werden abgerundet
               || ( (rounding_bits == bit(1)) // 10
                    && (eq(r,0)) // und genau halbzahlig (r=0)
                    && ((mant & bit(0)) ==0) // -> round-to-even
             )    )
            // abrunden
            goto ab;
            else
            // aufrunden
            goto auf;
        }
        else
        { var uint64 rounding_bit = mant & bit(0);
          mant = mant >> 1;
          if ( (rounding_bit == 0) // 0 wird abgerundet
               || ( (eq(r,0)) // genau halbzahlig (r=0)
                    && ((mant & bit(0)) ==0) // -> round-to-even
             )    )
            // abrunden
            goto ab;
            else
            // aufrunden
            goto auf;
        }
      auf:
      mant += 1;
      if (mant >= bit(DF_mant_len+1)) // rounding overflow?
        { mant = mant>>1; lendiff = lendiff+1; }
      ab:
      // Fertig.
      if (lendiff < (sintL)(DF_exp_low-DF_exp_mid))
        { u.eksplicit = ((sint64)sign & bit(63)); } // 0.0
      else if (lendiff > (sintL)(DF_exp_high-DF_exp_mid))
        { u.eksplicit =
              ((sint64)sign & bit(63))
            | ((uint64)(bit(DF_exp_len)-1) << DF_mant_len); // Infinity
        }
      else
        { u.eksplicit =
              ((sint64)sign & bit(63))                      /* Vorzeichen */
            | ((uint64)(lendiff+DF_exp_mid) << DF_mant_len) /* Exponent   */
            | ((uint64)mant & (bit(DF_mant_len)-1));        /* Mantisse   */
        }
      return u.machine_double;
      #else
      var uint32 manthi = get_max32_Dptr(23,ptr);
      var uint32 mantlo = get_32_Dptr(ptr mspop ceiling(23,intDsize));
      if (manthi >= bit(DF_mant_len-32+2))
        // 2^54 <= q < 2^55, schiebe um 2 Bits nach rechts
        { var uintL rounding_bits = mantlo & (bit(2)-1);
          lendiff = lendiff+1; // Exponent := n-m+1
          mantlo = (mantlo >> 2) | (manthi << 30); manthi = manthi >> 2;
          if ( (rounding_bits < bit(1)) // 00,01 werden abgerundet
               || ( (rounding_bits == bit(1)) // 10
                    && (eq(r,0)) // und genau halbzahlig (r=0)
                    && ((mantlo & bit(0)) ==0) // -> round-to-even
             )    )
            // abrunden
            goto ab;
            else
            // aufrunden
            goto auf;
        }
        else
        { var uintL rounding_bit = mantlo & bit(0);
          mantlo = (mantlo >> 1) | (manthi << 31); manthi = manthi >> 1;
          if ( (rounding_bit == 0) // 0 wird abgerundet
               || ( (eq(r,0)) // genau halbzahlig (r=0)
                    && ((mantlo & bit(0)) ==0) // -> round-to-even
             )    )
            // abrunden
            goto ab;
            else
            // aufrunden
            goto auf;
        }
      auf:
      mantlo += 1;
      if (mantlo==0)
        { manthi += 1;
          if (manthi >= bit(DF_mant_len-32+1)) // rounding overflow?
            { manthi = manthi>>1; lendiff = lendiff+1; }
        }
      ab:
      // Fertig.
      if (lendiff < (sintL)(DF_exp_low-DF_exp_mid))
        { u.eksplicit.semhi = ((sint32)sign & bit(31)); // 0.0
          u.eksplicit.mlo = 0;
        }
      else if (lendiff > (sintL)(DF_exp_high-DF_exp_mid))
        { u.eksplicit.semhi =
              ((sint32)sign & bit(31))
            | ((uint32)(bit(DF_exp_len)-1) << (DF_mant_len-32)); // Infinity
          u.eksplicit.mlo = 0;
        }
      else
        { u.eksplicit.semhi =
              ((sint32)sign & bit(31))                           /* Vorzeichen */
            | ((uint32)(lendiff+DF_exp_mid) << (DF_mant_len-32)) /* Exponent   */
            | ((uint32)manthi & (bit(DF_mant_len-32)-1));        /* Mantisse   */
          u.eksplicit.mlo = mantlo;
        }
      return u.machine_double;
      #endif
}}
