/* ieeeflt64 - converts 64-bit IEEE floating-point format to or from Unix format
 *
 * Note:  This is the Unix version
 *
 *		Version 1, D. Gudehus, June 1, 2005
 *
 * There are two subroutines in this module:  ieee2unix64 and unix2ieee64 for 
 * double precision. They are capable of converting a 64-bit ANSI/IEEE-754
 * floating-point number or numbers to standard Unix 64-bit floating format,
 * and vice versa.  This module was based on ieeeflt.c, the single-precision
 * version.
 *
 * The calling sequence for a C program is: 
 * Method 1
 * #define MAX 1000
 * void ieee2unix64 (int ieee64[], int *nreals, int *bswap, int *wswap);
 * main() {
 *   int i;
 *   union {
 *     long int data_i[2];
 *     double data_f;
 *   } ieee64[MAX];
 *   int nreals, bswap, wswap;
 *
 *   ieee2unix64 (data_i,&nreals,&bswap,&wswap)
 * }
 *   !  Returned floating point data is in ieee64[i].data_f
 * Method 2
 * void ieee_unix64(unsigned long int *, int *, int *, int *);
 * main() {
 *   int nreals, bswap, wswap;
 * 
 *   ieee2unix64 ((double *)buf,&nreals,&bswap,&wswap);
 *   
 *
 * The calling sequence for a FORTRAN program is:
 *	PARAMETER MAX_NCOL=4096 
 *	INTEGER DATA_I(MAX_NCOL*2)
 *	INTEGER NREALS,BSWAP,WSWAP
 *	REAL*8 DATA_R(MAX_NCOL)
 * The floating point values should be equivalenced to the integer values 
 * in the calling routine with a statement such as:
 *	EQUIVALENCE (DATA_I(1),DATA_R(1))
 *
 *	CALL IEEE2UNIX64(DATA_I,NREALS,BSWAP,WSWAP)
 *
 * and the calling sequence for unix2ieee64 is identical.  The conversion is
 * done "in place".  If the original data is read as bytes, then the bytes
 * should be equivalenced to the integer array before calling the routines.
 *
 *
 * This module includes optional byte/word swapping.  An IEEE word looks like:
 *
 *    S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
 *    0 1        11 12                                                63
 *
 * where S = the sign bit, E = the exponent bits, and F = the fraction bits.
 * If 0<E<2047 then V=(-1)**S * 2 ** (E-1023) * (1.F) where "1.F" is intended 
 * to represent the binary number created by prefixing F with an implicit
 * leading 1 and a binary point.
 *
 *    0 00000000000 0000000000000000000000000000000000000000000000000000 = 0
 *    1 00000000000 0000000000000000000000000000000000000000000000000000 = -0
 *
 *    0 11111111 00000000000000000000000 = Infinity
 *    1 11111111 00000000000000000000000 = -Infinity
 *
 * If E=2047 and F is nonzero, then the value is NaN (not a number)
 *
 * Values read in, e.g. from a National Instruments GPIB bus will in
 * general need to be word-swapped.  However, data read from a byte stream,
 * e.g. data read in image mode across a TCP/IP network FTP link, will not
 * have to be word swapped but will need byte-swapped.  Same for tape data.
 *
 *----------------------------------------------------------------------
 */

union fstruc64 {
  unsigned long int longword[2];
  short int words[4];			/* for swapping */
  char bytes[8];			/* "   "        */
} ;

void ieee2unix64_ (buf, nreals, bswap, wswap)
  unsigned long int *buf;
  int *nreals, *bswap, *wswap;	/* pointers so Fortran calling possible */

{
  float temp;
  int i;
  short int itemp;		/* for swapping words */
  long int ltemp;		/* for swapping words */
  char ctemp;			/* for swapping bytes */
  union fstruc64 ieee64;

  if (*nreals < 1) 
    return;
  for (i=0; i<(*nreals)*2; i=i+2) {
    ieee64.longword[0] = buf[i];	/* Uses int (flt can cause fault) */
    ieee64.longword[1] = buf[i+1];
    if (*bswap == 1) {
      ctemp = ieee64.bytes[0];		/* byte swap (8 bytes) */
      ieee64.bytes[0] = ieee64.bytes[1];
      ieee64.bytes[1] = ctemp;
      ctemp = ieee64.bytes[2];
      ieee64.bytes[2] = ieee64.bytes[3];
      ieee64.bytes[3] = ctemp;
      ctemp = ieee64.bytes[4];
      ieee64.bytes[4] = ieee64.bytes[5];
      ieee64.bytes[5] = ctemp;
      ctemp = ieee64.bytes[6];
      ieee64.bytes[6] = ieee64.bytes[7];
      ieee64.bytes[7] = ctemp;
    }
    if (*wswap == 1) {
      itemp = ieee64.words[0];		/* word swap */
      ieee64.words[0] = ieee64.words[1];
      ieee64.words[1] = itemp;
      itemp = ieee64.words[2];
      ieee64.words[2] = ieee64.words[3];
      ieee64.words[3] = itemp;
      ltemp = ieee64.longword[0];
      ieee64.longword[0]=ieee64.longword[1];
      ieee64.longword[1]=ltemp;
    }

    buf[i] = ieee64.longword[0];	/* puts Unix value back */
    buf[i+1] = ieee64.longword[1];
  }
}

void unix2ieee64_(buf, nreals, wswap, bswap)
  unsigned long int *buf;
  int *nreals, *wswap, *bswap; /* pointers so Fortran calling possible */
{
  float temp;
  int	i;
  short int itemp;		/* for swapping words */
  long int ltemp;               /* for swapping words */
  char ctemp;			/* for swapping bytes */
  union fstruc64 unixf64;

  if (*nreals < 1) 
    return;
  for (i=0; i<(*nreals)*2; i=i+2) {
    unixf64.longword[0] = buf[i];
    unixf64.longword[1] = buf[i+1];
    if (*bswap == 1) {		/* byte swap needed */
      ctemp = unixf64.bytes[0];
      unixf64.bytes[0] = unixf64.bytes[1];
      unixf64.bytes[1] = ctemp;
      ctemp = unixf64.bytes[2];
      unixf64.bytes[2] = unixf64.bytes[3];
      unixf64.bytes[3] = ctemp;
      ctemp = unixf64.bytes[4];
      unixf64.bytes[4] = unixf64.bytes[5];
      unixf64.bytes[5] = ctemp;
      ctemp = unixf64.bytes[6];
      unixf64.bytes[6] = unixf64.bytes[7];
      unixf64.bytes[7] = ctemp;
    }
    if (*wswap == 1) {		/* word swap needed */
      itemp = unixf64.words[0];
      unixf64.words[0] = unixf64.words[1];
      unixf64.words[1] = itemp;
      itemp = unixf64.words[2];
      unixf64.words[2] = unixf64.words[3];
      unixf64.words[3] = itemp;
      ltemp = unixf64.longword[0];
      unixf64.longword[0]=unixf64.longword[1];
      unixf64.longword[1] = ltemp; 
    }
    buf[i] = unixf64.longword[0];	/* puts IEEE value back */
    buf[i+1] = unixf64.longword[1];
  }
}
