/*  pack								*/
/* & pjt								*/
/* : low-level-i/o							*/
/* +									*/
/*									*/
/*		This converts data between disk and internal		*/
/*		format. Disk format is IEEE reals and 16 or 32		*/
/*		bit integers (most significant byte first).		*/
/*									*/
/*		This assumes that these are the local machine format	*/
/*		(float == IEEE real, int == 32 bit integer,		*/
/*		short int == 16 bit integer).				*/
/*									*/
/*		packx_c, unpackx_c, pack32_c and unpack32_c are		*/
/*		implemented as macros (calling bcopy) in the		*/
/*		system dependent include file.				*/
/*--									*/
/*									*/
/*  History:								*/
/*    rjs  Dark-ages Original version.					*/
/*    bs   ?????89   Improved efficiency using "register" declarations.	*/
/*    rjs   1nov89   Incoporated Brian's changes.			*/
/*    mjs  28feb91   Merge Sun and Cray versions.			*/
/*    mjs  18mar91   Added convex definition.                           */
/*    mjs  19feb93   Added mips definition.                             */
/*    pjt  25jan95   linux kludge to include packALPHA.c                */
/*    pjt  14jun01   packALPHA.c now included in this source code       */
/*                   and using the standard WORDS_BIGENDIAN macro       */
/*    pjt  21jun02   MIR4 prototyping                                   */
/************************************************************************/

#include "sysdep.h"
#include "miriad.h"

#if defined(WORDS_BIGENDIAN)

static int words_bigendian = 1; /* never used actually, but handy symbol to find via nm(1) */

void	pack16_c(register int *from,char *to,int n)
{
	register short int	*tto;
	register int		i;

	tto = (short int *)to;
	for (i=0; i < n; i++)	*tto++ = *from++;
}
void	unpack16_c(char *from,register int *to,int n)
{
	register short int	*ffrom;
	register int		i;

	ffrom = (short int *)from;
	for (i=0; i < n; i++)	*to++ = *ffrom++;
}

void	pack64_c(register int *from,char *to,int n)
{
	register short int	*tto;
	register int		i;

	tto = (short int *)to;
	for (i=0; i < n; i++)	*tto++ = *from++;
}
void	unpack64_c(char *from,register int *to,int n)
{
	register short int	*ffrom;
	register int		i;

	ffrom = (short int *)from;
	for (i=0; i < n; i++)	*to++ = *ffrom++;
}

#endif


#ifndef WORDS_BIGENDIAN 
#ifndef unicos
static int words_littleendian = 1; /* never used actually, but handy symbol to find via nm(1) */
/************************************************************************/
/*									*/
/*  The pack routines -- these convert between the host format and	*/
/*  the disk format. Disk format is IEEE 32 and 64 bit reals, and 2's	*/
/*  complement integers. Byte order is the FITS byte order (most	*/
/*  significant bytes first).						*/
/*									*/
/*  This version is for a machine which uses IEEE internally, but which	*/
/*  uses least significant bytes first (little endian), e.g. PCs and	*/
/*  Alphas.								*/
/*									*/
/*  History:								*/
/*    rjs  21nov94 Original version.					*/
/************************************************************************/
void pack16_c(int *in,char *out,int n)
/*
  Pack an integer array into 16 bit integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)in;
  for(i=0; i < n; i++){
    *out++ = *(s+1);
    *out++ = *s;
    s += sizeof(int);
  }
}
/************************************************************************/
void unpack16_c(char *in,int *out,int n)
/*
  Unpack an array of 16 bit integers into integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)out;
  for(i=0; i < n; i++){
    *s++ = *(in+1);
    *s++ = *in;
    if(0x80 & *in){
      *s++ = 0xFF;
      *s++ = 0xFF;
    } else {
      *s++ = 0;
      *s++ = 0;
    }
    in += 2;
  }
}
/************************************************************************/
void pack32_c(int *in,char *out,int n)
/*
  Pack an array of integers into 32 bit integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)in;
  for(i = 0; i < n; i++){
    *out++ = *(s+3);
    *out++ = *(s+2);
    *out++ = *(s+1);
    *out++ = *s;
    s += 4;
  }
}
/************************************************************************/
void unpack32_c(char *in,int *out,int n)
/*
  Unpack an array of 32 bit integers into integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)out;
  for(i = 0; i < n; i++){
    *s++ = *(in+3);
    *s++ = *(in+2);
    *s++ = *(in+1);
    *s++ = *in;
    in += 4;
  }
}
/************************************************************************/
void pack64_c(int8 *in,char *out,int n)
/*
  Pack an integer array into 64 bit integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)in;
  for(i=0; i < n; i++){
    *out++ = *(s+7);
    *out++ = *(s+6);
    *out++ = *(s+5);
    *out++ = *(s+4);
    *out++ = *(s+3);
    *out++ = *(s+2);
    *out++ = *(s+1);
    *out++ = *s;
    s += 8;
  }
}
/************************************************************************/
void unpack64_c(char *in,int8 *out,int n)
/*
  Unpack an array of 64 bit integers into integers.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)out;
  for(i=0; i < n; i++){
    *s++ = *(in+7);
    *s++ = *(in+6);
    *s++ = *(in+5);
    *s++ = *(in+4);
    *s++ = *(in+3);
    *s++ = *(in+2);
    *s++ = *(in+1);
    *s++ = *in;
    in += 8;
  }
}
/************************************************************************/
void packr_c(float *in,char *out,int n)
/*
  Pack an array of reals into IEEE reals -- just do byte reversal.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)in;
  for(i = 0; i < n; i++){
    *out++ = *(s+3);
    *out++ = *(s+2);
    *out++ = *(s+1);
    *out++ = *s;
    s += 4;
  }
}
/************************************************************************/
void unpackr_c(char *in,float *out,int n)
/*
  Unpack an array of IEEE reals into reals -- just do byte reversal.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)out;
  for(i = 0; i < n; i++){
    *s++ = *(in+3);
    *s++ = *(in+2);
    *s++ = *(in+1);
    *s++ = *in;
    in += 4;
  }
}
/************************************************************************/
void packd_c(double *in,char *out,int n)
/*
  Pack an array of doubles -- this involves simply performing byte
  reversal.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)in;
  for(i = 0; i < n; i++){
    *out++ = *(s+7);
    *out++ = *(s+6);
    *out++ = *(s+5);
    *out++ = *(s+4);
    *out++ = *(s+3);
    *out++ = *(s+2);
    *out++ = *(s+1);
    *out++ = *s;
    s += 8;
  }
}
/************************************************************************/
void unpackd_c(char *in,double *out,int n)
/*
  Unpack an array of doubles -- this involves simply performing byte
  reversal.
------------------------------------------------------------------------*/
{
  int i;
  char *s;

  s = (char *)out;
  for(i = 0; i < n; i++){
    *s++ = *(in+7);
    *s++ = *(in+6);
    *s++ = *(in+5);
    *s++ = *(in+4);
    *s++ = *(in+3);
    *s++ = *(in+2);
    *s++ = *(in+1);
    *s++ = *in;
    in += 8;
  }
}
#endif
#endif


#if defined(unicos)
static int words_unicos = 1;
#define TWO15  0x8000
#define TWO16  0x10000
#define TWO31  0x80000000
#define TWO32  0x100000000
#define HILONG 0xFFFFFFFF00000000
#define LOLONG 0x00000000FFFFFFFF
#define WORD0  0x000000000000FFFF
#define WORD1  0x00000000FFFF0000
#define WORD2  0x0000FFFF00000000
#define WORD3  0xFFFF000000000000

/* Masks for IEEE floating format (both hi and lo types). */

#define IEEE_HISIGN     0x8000000000000000
#define IEEE_HIEXPO     0x7F80000000000000
#define IEEE_HIMANT     0x007FFFFF00000000
#define IEEE_LOSIGN     0x0000000080000000
#define IEEE_LOEXPO     0x000000007F800000
#define IEEE_LOMANT     0x00000000007FFFFF
#define IEEE_DMANT      0x000FFFFFFFFFFFF0
#define IEEE_DEXPO      0x7FF0000000000000

/* Masks for Cray floating format. */

#define CRAY_MANT       0x0000FFFFFF000000      /* Including unhidden bit. */
#define CRAY_MANT1      0x00007FFFFF000000      /* No unhidden bit. */
#define CRAY_DMANT      0x0000FFFFFFFFFFFF
#define CRAY_DMANT1     0x00007FFFFFFFFFFF
#define CRAY_EXPO       0x7FFF000000000000
#define SIGN            0x8000000000000000

/* Mask of a pointer to char giving the character offset in a Cray word. */

#define CHAR_OFFSET 0xE000000000000000

/************************************************************************/
void pack16_c(int *in,char *out,int n)
/*
  Pack an integer array into 16 bit integers for unicos
------------------------------------------------------------------------*/
{
  int temp,offset,*outd,in1,in2,in3,in4,i;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)out;
  offset = ( temp & CHAR_OFFSET ) >> 62;        /* Get offset of first word. */
  outd = (int *)(temp & ~CHAR_OFFSET);  /* Get address of words. */

/* Handle the first few which are not aligned on a Cray word. */

  switch(offset){
    case 1: *outd = (*outd & ~WORD2) | ((*in++ << 32) & WORD2);
            if(--n == 0)break;
    case 2: *outd = (*outd & ~WORD1) | ((*in++ << 16) & WORD1);
            if(--n == 0)break;
    case 3: *outd = (*outd & ~WORD0) | ((*in++    ) & WORD0);
            outd++;
  }

/* Handle the ones which are aligned on a Cray word. */

  for(i=0; i < n-3; i=i+4){
    in1 = *in++ << 48;
    in2 = *in++ << 32;
    in3 = *in++ << 16;
    in4 = *in++;
    *outd++ = (in1 & WORD3) | (in2 & WORD2) | (in3 & WORD1) | (in4 & WORD0);
  }
  n -= i;

/* Handle the last ones which are not aligned on a Cray word. */

  if(n-- > 0){
    *outd = (*outd & ~WORD3) | ((*in++ << 48) & WORD3);
    if(n-- > 0){
      *outd = (*outd & ~WORD2) | ((*in++ << 32) & WORD2);
      if(n-- > 0){
        *outd = (*outd & ~WORD1) | ((*in++ << 16) & WORD1);
      }
    }
  }
}
/************************************************************************/
void unpack16_c(char *in,int *out,int n)
/*
  Unpack an array of 16 bit integers into integers for unicos
------------------------------------------------------------------------*/
{
  int temp,offset,*ind,i;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)in;
  offset = ( temp & CHAR_OFFSET ) >> 62;        /* Get offset of first word. */
  ind = (int *)(temp & ~CHAR_OFFSET);   /* Get address of words. */

/* Handle the first few which are not word aligned. */

  switch(offset){
    case 1:  temp = (*ind >> 32) & WORD0;
             *out++ = (temp < TWO15 ? temp : temp - TWO16);
             if(--n == 0) break;
    case 2:  temp = (*ind >> 16) & WORD0;
             *out++ = (temp < TWO15 ? temp : temp - TWO16);
             if(--n == 0) break;
    case 3:  temp = (*ind++    ) & WORD0;
             *out++ = (temp < TWO15 ? temp : temp - TWO16);
             if(--n == 0) break;
  }

/* Handle those that are Cray-word-aligned. */

  for(i=0; i < n-3; i=i+4){
    temp = (*ind >> 48) & WORD0;
    *out++ = (temp < TWO15 ? temp : temp - TWO16);
    temp = (*ind >> 32) & WORD0;
    *out++ = (temp < TWO15 ? temp : temp - TWO16);
    temp = (*ind >> 16) & WORD0;
    *out++ = (temp < TWO15 ? temp : temp - TWO16);
    temp = (*ind++    ) & WORD0;
    *out++ = (temp < TWO15 ? temp : temp - TWO16);
  }
  n -= i;

/* Handle the last few which are not Cray-word-aligned. */

  if(n-- > 0){
    temp = (*ind >> 48) & WORD0;
    *out++ = (temp < TWO15 ? temp : temp - TWO16);
    if(n-- > 0){
      temp = (*ind >> 32) & WORD0;
      *out++ = (temp < TWO15 ? temp : temp - TWO16);
      if(n-- > 0){
        temp = (*ind >> 16) & WORD0;
        *out++ = (temp < TWO15 ? temp : temp - TWO16);
      }
    }
  }
}
/************************************************************************/
void pack32_c(int *in,char *out,int n)
/*
  Pack an array of integers into 32 bit integers for unicos
------------------------------------------------------------------------*/
{
  int temp,offset,*outd,i,in1,in2;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)out;
  offset = ( temp & CHAR_OFFSET ) >> 63;        /* Get offset of first long. */
  outd = (int *)(temp & ~CHAR_OFFSET);  /* Get address of words. */

/* Do the first one, if it is not aligned on a Cray word. */

  if(offset==1){
    *outd = (*outd & ~LOLONG) | (*in++ & LOLONG);
    outd++;
  }
  n -= offset;

/* Do those which are Cray word aligned. */

  for(i=0; i < n-1; i=i+2){
    in1 = *in++ << 32;
    in2 = *in++;
    *outd++ = (in1 & HILONG) | (in2 & LOLONG);
  }
  n -= i;

/* Handle the last one, if there is one. */

  if(n==1)*outd =  (*outd & ~HILONG) | ((*in++ << 32) & HILONG);
}
/************************************************************************/
void unpack32_c(char *in,int *out,int n)
/*
  Unpack an array of 32 bit integers into integers for unicos
------------------------------------------------------------------------*/
{
  int temp,offset,*ind,i;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)in;
  offset = ( temp & CHAR_OFFSET ) >> 63;        /* Get offset of first word. */
  ind = (int *)(temp & ~CHAR_OFFSET);   /* Get address of words. */

/* Handle one which is not Cray word aligned. */

  if(offset==1){
    temp = (*ind++ & LOLONG);
    *out++ = (temp < TWO31 ? temp : temp - TWO32);
  }
  n -= offset;

/* Handle those which are Cray word aligned. */

  for(i=0; i < n-1; i=i+2){
    temp = (*ind >> 32) & LOLONG;
    *out++ = (temp < TWO31 ? temp : temp - TWO32);
    temp = (*ind++    ) & LOLONG;
    *out++ = (temp < TWO31 ? temp : temp - TWO32);
  }
  n -= i;

/* Possibly handle a last one which is not Cray word aligned. */

  if(n==1){
    temp = (*ind >> 32) & LOLONG;
    *out++ = (temp < TWO31 ? temp : temp - TWO32);
  }
}
/************************************************************************/
void packr_c(float *in,char *out,int n)
/*
  Pack an array of Cray reals into IEEE reals.
------------------------------------------------------------------------*/
{
  int temp,offset,*outd,bias,*ind,tin,tout,i;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)out;
  offset = ( temp & CHAR_OFFSET ) >> 63;        /* Get offset of first long. */
  outd = (int *)(temp & ~CHAR_OFFSET);  /* Get address of words. */
  bias = (16384 - 126) << 48;
  ind = (int *)in;

/* Do the first one, if it is not aligned on a Cray word. */

  if(offset==1){
    tin     = *ind++;
    *outd    =   (*outd & ~LOLONG) |
                 (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) >> 25) |
                ((tin & CRAY_MANT1) >> 24) | ((tin & SIGN) >> 32) : 0);
    outd++;
  }
  n -= offset;

/* Do those which are Cray word aligned. */

  for(i=0; i < n-1; i=i+2){
    tin = *ind++;
    tout =      (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) << 7) |
                ((tin & CRAY_MANT1) << 8) |   (tin & SIGN)        : 0);
    tin = *ind++;
    *outd++ =   tout |
                (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) >> 25) |
                ((tin & CRAY_MANT1) >> 24) | ((tin & SIGN) >> 32) : 0);
  }
  n -= i;

/* Handle the last one, if there is one. */

  if(n==1){
    tin = *ind;
    *outd = (*outd & ~HILONG) |
            (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) << 7) |
            ((tin & CRAY_MANT1) << 8) |   (tin & SIGN)        : 0);
  }
}
/************************************************************************/
void unpackr_c(char *in,float *out,int n)
/*
  Unpack an array of IEEE reals into Cray reals.
------------------------------------------------------------------------*/
{
  int temp,tin,*ind,*outd,offset,i,bias;

  if(n <= 0)return;                             /* Return if nothing to do. */
  temp = (int)in;
  offset = ( temp & CHAR_OFFSET ) >> 63;        /* Get offset of first word. */
  ind = (int *)(temp & ~CHAR_OFFSET);   /* Get address of words. */
  outd = (int *)out;
  bias = ((16384-126) <<48) + (1 << 47);

/* Handle the first one if it is not aligned on a Cray word. */

  if(offset==1){
    tin = *ind++;
    *outd++ = (tin & IEEE_LOEXPO ? (((tin & IEEE_LOEXPO) << 25)+bias) |
        ((tin & IEEE_LOMANT) << 24) | ((tin & IEEE_LOSIGN) << 32) : 0);
  }
  n -= offset;

/* Handle the bulk of them that are aligned on Cray words. */

  for(i=0; i < n-1; i=i+2){
    tin = *ind++;
    *outd++ = (tin & IEEE_HIEXPO ? (((tin & IEEE_HIEXPO) >> 7)+bias) |
        ((tin & IEEE_HIMANT) >> 8 ) |  (tin & IEEE_HISIGN)        : 0);
    *outd++ = (tin & IEEE_LOEXPO ? (((tin & IEEE_LOEXPO) << 25)+bias) |
        ((tin & IEEE_LOMANT) << 24) | ((tin & IEEE_LOSIGN) << 32) : 0);
  }
  n -= i;

/* Handle the last one, if needed, which is not aligned on a Cray word. */

  if(n==1){
    tin = *ind;
    *outd++ = (tin & IEEE_HIEXPO ? (((tin & IEEE_HIEXPO) >> 7)+bias) |
        ((tin & IEEE_HIMANT) >> 8 ) |  (tin & IEEE_HISIGN)        : 0);
  }
}
/************************************************************************/
void packd_c(double *in,char *out,int n)
/*
  Pack an array of Cray reals into IEEE double precision. This assumes
  that a "double" and a "float" are identical.
------------------------------------------------------------------------*/
{
  int *ind,*outd,bias,i,tin;

  ind = (int *)in;
  outd = (int *)out;
  bias = (16384 - 1022) << 48;

  for(i=0; i < n; i++){
    tin = *ind++;
    *outd++ = (tin & CRAY_DMANT ? (tin & SIGN) |
      (((tin & CRAY_EXPO)-bias) << 4) | ((tin & CRAY_DMANT1) << 5) : 0 );
  }
}
/************************************************************************/
void unpackd_c(char *in,double *out,int n)
/*
  Unpack an array of IEEE double precision numbers into Cray reals. This
  assumes that a "double" and a "float" are identical.
------------------------------------------------------------------------*/
{
  int *ind,*outd,bias,i,tin;

  ind = (int *)in;
  outd = (int *)out;
  bias = ((16384 - 1022) << 48) | (1 << 47);

  for(i=0; i < n; i++){
    tin = *ind++;
    *outd++ = (tin & IEEE_DEXPO ? (tin & SIGN) |
      (((tin & IEEE_DEXPO) >> 4) + bias) | ((tin & IEEE_DMANT) >> 5) : 0 );
  }
}

#endif
