// square().

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

// Specification.
#include "cln/lfloat.h"


// Implementation.

#include "cl_LF.h"
#include "cl_LF_impl.h"
#include "cl_DS.h"
#include "cl_F.h"

namespace cln {

const cl_LF square (const cl_LF& x)
{
// Methode: wie operator*(x,x).
      var uintC len = TheLfloat(x)->len;
      var uintL uexp = TheLfloat(x)->expo;
      if (uexp==0) // x=0.0 -> Ergebnis 0.0
        { return x; }
      // Exponenten addieren:
      // (uexp-LF_exp_mid) + (uexp-LF_exp_mid) = (2*uexp-LF_exp_mid)-LF_exp_mid
      if ((sintL)uexp >= 0)
        // kein Carry
        { uexp = 2*uexp;
          if (uexp < LF_exp_mid+LF_exp_low)
            { if (underflow_allowed())
                { cl_error_floating_point_underflow(); }
                else
                { return encode_LF0(len); } // Ergebnis 0.0
        }   }
        else
        // Carry
        { uexp = 2*uexp;
          if (uexp > (uintL)(LF_exp_mid+LF_exp_high+1)) { cl_error_floating_point_overflow(); }
        }
      uexp = uexp - LF_exp_mid;
      // Nun ist LF_exp_low <= uexp <= LF_exp_high+1.
      // neues Long-Float allozieren:
      var Lfloat y = allocate_lfloat(len,uexp,0);
      // Produkt bilden:
      var const uintD* x_LSDptr = arrayLSDptr(TheLfloat(x)->data,len);
      var uintD* MSDptr;
      var uintD* LSDptr;
      CL_ALLOCA_STACK;
      num_stack_alloc(2*len,MSDptr=,LSDptr=);
      cl_UDS_mul_square(x_LSDptr,len,LSDptr);
      {var uintD* midptr = MSDptr mspop len; // Pointer in die Mitte der 2*len Digits
       if ((sintD)mspref(MSDptr,0) >= 0) // führendes Bit abtesten
         { // erste n+1 Digits um 1 Bit nach links schieben:
           shift1left_loop_lsp(midptr mspop 1,len+1);
           // Exponenten decrementieren:
           if ((TheLfloat(y)->expo)-- == LF_exp_low-1)
             { if (underflow_allowed())
                 { cl_error_floating_point_underflow(); }
                 else
                 { return encode_LF0(len); } // Ergebnis 0.0
             }
         }
       // erste Hälfte des Mantissenprodukts übertragen:
       {var uintD* y_mantMSDptr = arrayMSDptr(TheLfloat(y)->data,len);
        var uintD* y_mantLSDptr = copy_loop_msp(MSDptr,y_mantMSDptr,len);
        // Runden:
        if ( ((sintD)mspref(midptr,0) >= 0) // nächstes Bit =0 -> abrunden
             || ( ((mspref(midptr,0) & ((uintD)bit(intDsize-1)-1)) ==0) // Bit =1, weitere Bits >0 -> aufrunden
                  && !test_loop_msp(midptr mspop 1,len-1)
                  // round-to-even
                  && ((lspref(midptr,0) & bit(0)) ==0)
           )    )
          // abrunden
          {}
          else
          // aufrunden
          { if ( inc_loop_lsp(y_mantLSDptr,len) )
              { // Übertrag durchs Aufrunden (kann nur auftreten,
                // wenn vorhin um 1 Bit nach links geschoben wurde)
                mspref(y_mantMSDptr,0) = bit(intDsize-1); // Mantisse := 10...0
                (TheLfloat(y)->expo)++; // Exponent wieder zurück-erhöhen
          }   }
        // LF_exp_low <= exp <= LF_exp_high sicherstellen:
        if (TheLfloat(y)->expo == LF_exp_high+1) { cl_error_floating_point_overflow(); }
      }}
      return y;
}
// Bit complexity (N = length(x)): O(M(N)).

}  // namespace cln
