/*******************************************************************************
+
+  LEDA 3.5
+
+  _floatingpoint.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
// This file contains some basic functions for hardware-specific
// floating point arithmetic with doubles

#include <LEDA/floatingpoint.h>

// Two switches are important here:
//  -- The switch WORD_LENGTH_64 indicates that the word length is 64 bit
//     instead of the expected standard length of 32
//  -- The switch LITTLE_ENDIAN_MACHINE indicates for a 32 bit machine that the high
//     part of a double is stored last in memory, {\em after} the low part;
//     the standard is that the high word comes first

// --------------------------------------------------------------------
// functions with common code for all switches
// --------------------------------------------------------------------

int float_sign(const double& x)
{
#ifndef WORD_LENGTH_64
  unsigned long* exp_ptr = (unsigned long*) &x;
#ifdef LITTLE_ENDIAN_MACHINE
  exp_ptr++;
#endif
  if (*exp_ptr & exponent_mask) // if is_normalized(x)
  {
     if (*exp_ptr & signbit_mask) // if sign_bit(x)
        return -1;
     else
        return 1;
  }
#endif
  if (x == 0)
     return 0;
  if (x > 0) return 1;
  return -1;
}

long get_biased_exponent(const double& x)
{
  long *p = (long*) &x;
  long high_word;
#ifndef LITTLE_ENDIAN_MACHINE
  high_word = *p; 
#else
  high_word = *(++p);
#endif
#ifndef WORD_LENGTH_64
  return (high_word & exponent_mask) >> 20;
#else
  return (high_word & exponent_mask) >> 52;
#endif
}


long get_unbiased_exponent(const double& x)
{
  long *p = (long*) &x;
  long high_32;
#ifndef LITTLE_ENDIAN_MACHINE
  high_32 = *p; 
#else
  high_32 = *(++p);
#endif
#ifndef WORD_LENGTH_64
  long biased_exponent =
     (high_32 & exponent_mask) >> 20;
#else
  long biased_exponent =
     (high_32 & exponent_mask) >> 52;
#endif
  return biased_exponent-1023;
}

// --------------------------------------------------------------------
// functions with common code for all BIG EDNIAN machines
// --------------------------------------------------------------------


#ifndef LITTLE_ENDIAN_MACHINE

double power_two(long l)
{
   double result = 0;
   unsigned long& exponent = *((unsigned long*) &result);
   exponent = l + 1023;
#ifndef WORD_LENGTH_64
   exponent <<= 20;
#else
   exponent <<= 52;
#endif
   exponent &= exponent_mask;
   return result;
}

#else

double power_two(long l)
{
   double result = 0;
   unsigned long* p = (unsigned long*) &result;
   unsigned long& exponent = *(++p);
   exponent = l + 1023;
   exponent <<= 20;
   exponent &= exponent_mask;
   return result;
}

#endif


// --------------------------------------------------------------------
// functions without common code for the different switches
// --------------------------------------------------------------------



#ifndef WORD_LENGTH_64

double compose_parts(int sign_1, unsigned long exp_11,
                     unsigned long most_sig_20, unsigned long least_sig_32)
{
  double a;

  unsigned long high_32=0;
  if (sign_1) 
    high_32 |= signbit_mask;
  high_32 |= (exp_11 << 20);
  high_32 |= most_sig_20;
  
  unsigned long *p;
  p=(unsigned long*)&a;
#ifndef LITTLE_ENDIAN_MACHINE
  *p =high_32; 
   p++; 
  *p =least_sig_32;
#else
  *p =least_sig_32; 
   p++; 
  *p =high_32;
#endif

  return a;
}


void read_parts(const double& a,
                int& sign_1, long& exp_11,
                unsigned long& most_sig_20, unsigned long& least_sig_32)
{
  unsigned long *p = (unsigned long*) &a;
  unsigned long high_32;
#ifndef LITTLE_ENDIAN_MACHINE
  high_32 = *p; 
  p++; 
  least_sig_32 = *p;
#else
  least_sig_32 = *p; 
  p++; 
  high_32 = *p;
#endif

  sign_1 = (int) (high_32 & signbit_mask) >> 31;
  exp_11 = (high_32 & exponent_mask) >> 20;
  most_sig_20 = (high_32 & 0x000fffff);
}

long binary_equal(const double& x, const double& y)
{
  unsigned long* x_ptr = (unsigned long*) &x;
  unsigned long* y_ptr = (unsigned long*) &y;
  if (*x_ptr != *y_ptr)
    return 0;
  x_ptr++; y_ptr++;
  if (*x_ptr != *y_ptr)
    return 0;
  return 1;
}

#else

double compose_parts(int sign_1, unsigned long exp_11,
                     unsigned long most_sig_20, unsigned long least_sig_32)
{
  double a;

  unsigned long word_64 = (exp_11 << 20) + most_sig_20;
  word_64 = (word_64 << 32) + least_sig_32;
  if (sign_1) 
    word_64 |= signbit_mask;
  
  unsigned long *p;
  p=(unsigned long*)&a;
  *p = word_64;

  return a;
}


void read_parts(const double& a,
                int& sign_1, long& exp_11,
                unsigned long& most_sig_20, unsigned long& least_sig_32)
{
  unsigned long *p = (unsigned long*) &a;
  unsigned long word_64 = *p;
  sign_1 = (int) (word_64 >> 63);
  exp_11 = (word_64 & exponent_mask) >> 52;
  most_sig_20  = (word_64 & 0x000fffff00000000) >> 32;
  least_sig_32 = word_64 & 0x00000000ffffffff;
}

long binary_equal(const double& x, const double& y)
{
  unsigned long* x_ptr = (unsigned long*) &x;
  unsigned long* y_ptr = (unsigned long*) &y;
  return (*x_ptr == *y_ptr);
}

#endif



const double double_min=compose_parts(0,1,0,0);
const double NaN_double=compose_parts(0,2047,0,1);
const double pInf_double=compose_parts(0,2047,0,0);
const double nInf_double=-pInf_double;
const double pZero_double=compose_parts(0,0,0,0);
const double nZero_double=compose_parts(1,0,0,0);




#include <iostream.h>
#include <stdlib.h>

enum hardware_type { B_ENDIAN=0, L_ENDIAN=1, LENGTH_64 = 2};

hardware_type check_status()
{
   int size = sizeof(double);

#ifndef WORD_LENGTH_64
   if (size == sizeof(long))
   {
      cout << 
        "error in compilation of floatingpoint.c: use flag -DWORD_LENGTH_64\n";
      exit(-1);
   }
   double x = 1;
   long *px = (long *) &x;
#ifndef LITTLE_ENDIAN_MACHINE
   if (*(++px))
   {
     cout <<   
       "error in compilation of floatingpoint.c: use flag -DLITTLE_ENDIAN_MACHINE\n";
     exit(-1);
   }
   return B_ENDIAN;
#else
   if (*px)
   {
     cout << 
        "error in compilation of floatingpoint.c: don't use flag -DLITTLE_ENDIAN_MACHINE\n";
     exit(-1);
   }
   return L_ENDIAN;
   #endif


#else
   if (sizeof(long) < size)
   {
      cout << 
        "error in compilation of floatingpoint.c: don't use flag -DWORD_LENGTH_64\n";
      exit(-1);
   }
   return LENGTH_64;
#endif

}

hardware_type type = check_status();
