#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

//
// optional switches
//
#define GADGET           2
#define BYTESWAP         1
#define NPARTDENS        0
#define WRITEASCII       0

int ReadInt       (FILE *fptr,int *n,int swap);
int ReadFloat     (FILE *fptr,float *n, int swap);
int ReadDouble    (FILE *fptr,double *n,int swap);
int ReadLong      (FILE *fptr,long *n,int swap);
int ReadUInt      (FILE *fptr,unsigned int *n,int swap);
int ReadLongLong  (FILE *fptr,long long *n,int swap);
int get_density   (int NGRID);

#define GADGET_SKIP    ReadUInt(fd,&blklen,SWAPBYTES);
#define IDX(ix,iy,iz)  ((iz)*NGRID*NGRID + (iy)*NGRID + (ix))
#define pow2(x)        ((x)*(x))
#define pow3(x)        ((x)*(x)*(x))

#define TRUE    1
#define FALSE   0
#define NFILL   (256- 6*4- 6*8- 2*8- 2*4- 6*4- 2*4 - 4*8)

struct io_header_1
{
   int      npart[6];
   double   mass[6];
   double   time;
   double   redshift;
   int      flag_sfr;
   int      flag_feedback;
   int      npartTotal[6];
   int      flag_cooling;
   int      num_files;
   double   BoxSize;
   double   Omega0;
   double   OmegaLambda;
   double   HubbleParam; 
   char     fill[NFILL];  /* fills to 256 Bytes */
} header1;


unsigned int blklen;
int          NumPart, Ngas;
double       TotMass;

struct particle_data 
{
   float  Pos[3];
   float  Mass;  
   float  Rho;
   int    Id;
} *P;


int load_snapshot   (char *fname, int files);
int write_ascii     (char *file_name);
int write_binary    (char *file_name);
int allocate_memory (void);
int assign          (float *dens, int NGRID);
int interp          (float *dens, int NGRID);
int normalise       (float *dens, int NGRID);

//-----------------------------------------
//  calculate density at particle position
//-----------------------------------------
int main(int argc, char **argv)
{
   
   char indata[2048], outdata[2048];
   int  files, NGRID, ilen, i, j;
   
   if(argc!=3)
     {
      printf("Usage: gadgetPdens GADGET_file NGRID\n");
      exit(0);
     }
   
   printf("======================================================================\n");
   printf("  Read GADGET binary and calculate density at each particle position\n");
   printf("======================================================================\n");

   NGRID = (int) atoi(argv[2]);
   ilen = strlen(argv[1]);
   for(i=ilen-1; i>=0; i--)
      /* chop filename at last '/' */
      if(argv[1][i] == '/') break;
   for(j=i+1; j<ilen; j++)
      indata[j-(i+1)] = argv[1][j];
   indata[j-(i+1)] = '\0';
   sprintf(outdata,"Pdens-%d_%s",NGRID,indata);
   
   /* number of files per snapshot (works only for 1 file currently) */
   files=1;
   
   // Reads snapshot (already converts (x, v, m) to (Mpc/h, km/sec, Msun/h)
   load_snapshot(indata, files);
   
   // Calculate density at particle positions
   get_density(NGRID);
   
   // Writes output
#if (WRITEASCII==1)
   write_ascii(outdata);
#else
   write_binary(outdata);
#endif
   
   return 0;
   
}


//-------------------------------
//  get_density
//-------------------------------
int get_density(int NGRID)
{
   float *dens;
      
   // the grid that stores the density
   dens = (float *) calloc(NGRID*NGRID*NGRID, sizeof(float*));

   
   // assign particles to grid
   assign(dens, NGRID);
   
   // obtain density in terms of mean density
   normalise(dens, NGRID);
   
   // interpolate density back to particle positions
   interp(dens, NGRID);

   fprintf(stderr,"\n");
   
   free(dens);
}

//-------------------------------
//  normalise
//-------------------------------
int normalise(float *dens, int NGRID)
{
   double rho_mean;
   int    ix, iy, iz;
   
#if (NPARTDENS==1)
   rho_mean = (double)NumPart/(double)(NGRID*NGRID*NGRID);
#else
   rho_mean =         TotMass/(double)(NGRID*NGRID*NGRID);
#endif
   for(ix=0; ix<NGRID; ix++)
      for(iy=0; iy<NGRID; iy++)
         for(iz=0; iz<NGRID; iz++)
            dens[IDX(ix,iy,iz)] /= rho_mean;
}


//-------------------------------
//  assign
//-------------------------------
int assign(float *dens, int NGRID)
{
   int           ix, iy, iz, ixp1, iyp1, izp1, ixm1, iym1, izm1;
   int           ipart;
   float         rrx, rry, rrz, www;
   float         hx, hy, hz;
   float         hx0, hy0, hz0, hxp1, hyp1, hzp1, hxm1, hym1, hzm1;

#if (NPARTDENS==1)
   fprintf(stderr,"  o assigning particles to grid (number density)...");
#else
   fprintf(stderr,"  o assigning particles to grid (mass density)...");
#endif
   
   for(ix=0; ix<NGRID; ix++)
      for(iy=0; iy<NGRID; iy++)
         for(iz=0; iz<NGRID; iz++)
            dens[IDX(ix,iy,iz)]=0.0;

   for(ipart=0; ipart<NumPart; ipart++)
     {
      /* coordinates in grid units */
      rrx = P[ipart].Pos[0] * (float)(NGRID);
      rry = P[ipart].Pos[1] * (float)(NGRID);
      rrz = P[ipart].Pos[2] * (float)(NGRID);
      
      /* particle weight */
#if (NPARTDENS==1)
      www = 1.0;
#else
      www = P[ipart].Mass;
#endif
      
      /* index of nearest grid point */
      ix  = (int)(rrx+0.5);
      iy  = (int)(rry+0.5);
      iz  = (int)(rrz+0.5);
      
      /* distance to nearest grid point */
      hx  = rrx - (float)ix;
      hy  = rry - (float)iy;
      hz  = rrz - (float)iz;
      
      /* keep track of peridoc boundaries */
      ix=(int)fmod(ix,NGRID);
      iy=(int)fmod(iy,NGRID);
      iz=(int)fmod(iz,NGRID);
      
      /* calculate TSC weights */
      hx0=0.75 - hx*hx;
      hxp1=0.5* pow2(0.5 + hx);
      hxm1=0.5* pow2(0.5 - hx);
      hy0=0.75 - hy*hy;
      hyp1=0.5* pow2(0.5 + hy);
      hym1=0.5* pow2(0.5 - hy);
      hz0= 0.75 - hz*hz;
      hzp1=0.5* pow2(0.5 + hz);
      hzm1=0.5* pow2(0.5 - hz);
      
      /* keep track of peridoc boundaries */
      ixp1=(int)fmod(ix+1,NGRID);
      iyp1=(int)fmod(iy+1,NGRID);
      izp1=(int)fmod(iz+1,NGRID);
      ixm1=(int)fmod(ix-1+NGRID,NGRID);
      iym1=(int)fmod(iy-1+NGRID,NGRID);
      izm1=(int)fmod(iz-1+NGRID,NGRID);
      
      /* assign particle according to weights to 27 neighboring nodes */
      dens[IDX(ixm1,iym1,izm1)] += hxm1*hym1 *hzm1 * www;
      dens[IDX(ix,  iym1,izm1)] += hx0 *hym1 *hzm1 * www;
      dens[IDX(ixp1,iym1,izm1)] += hxp1*hym1 *hzm1 * www;
      dens[IDX(ixm1,  iy,izm1)] += hxm1*hy0  *hzm1 * www;
      dens[IDX(  ix,  iy,izm1)] += hx0 *hy0  *hzm1 * www;
      dens[IDX(ixp1,  iy,izm1)] += hxp1*hy0  *hzm1 * www;
      dens[IDX(ixm1,iyp1,izm1)] += hxm1*hyp1 *hzm1 * www;
      dens[IDX(  ix,iyp1,izm1)] += hx0 *hyp1 *hzm1 * www;
      dens[IDX(ixp1,iyp1,izm1)] += hxp1*hyp1 *hzm1 * www;
      dens[IDX(ixm1,iym1,  iz)] += hxm1*hym1 *hz0 * www;
      dens[IDX(  ix,iym1,  iz)] += hx0 *hym1 *hz0 * www;
      dens[IDX(ixp1,iym1,  iz)] += hxp1*hym1 *hz0 * www;
      dens[IDX(ixm1,  iy,  iz)] += hxm1*hy0  *hz0 * www;
      dens[IDX(  ix,  iy,  iz)] += hx0 *hy0  *hz0 * www;
      dens[IDX(ixp1,  iy,  iz)] += hxp1*hy0  *hz0 * www;
      dens[IDX(ixm1,iyp1,  iz)] += hxm1*hyp1 *hz0 * www;
      dens[IDX(  ix,iyp1,  iz)] += hx0 *hyp1 *hz0 * www;
      dens[IDX(ixp1,iyp1,  iz)] += hxp1*hyp1 *hz0 * www;
      dens[IDX(ixm1,iym1,izp1)] += hxm1*hym1 *hzp1 * www;
      dens[IDX(  ix,iym1,izp1)] += hx0 *hym1 *hzp1 * www;
      dens[IDX(ixp1,iym1,izp1)] += hxp1*hym1 *hzp1 * www;
      dens[IDX(ixm1,  iy,izp1)] += hxm1*hy0  *hzp1 * www;
      dens[IDX(  ix,  iy,izp1)] += hx0 *hy0  *hzp1 * www;
      dens[IDX(ixp1,  iy,izp1)] += hxp1*hy0  *hzp1 * www;
      dens[IDX(ixm1,iyp1,izp1)] += hxm1*hyp1 *hzp1 * www;
      dens[IDX(  ix,iyp1,izp1)] += hx0 *hyp1 *hzp1 * www;
      dens[IDX(ixp1,iyp1,izp1)] += hxp1*hyp1 *hzp1 * www;
     }
   
   fprintf(stderr,"done\n");
}

//-------------------------------
//  interp
//-------------------------------
int interp(float *dens, int NGRID)
{
   int           ix, iy, iz, ixp1, iyp1, izp1, ixm1, iym1, izm1;
   long unsigned ipart;
   float         rrx, rry, rrz;
   float         hx, hy, hz;
   float         hx0, hy0, hz0, hxp1, hyp1, hzp1, hxm1, hym1, hzm1;
   float         ac;

   fprintf(stderr,"  o interpolating density to particle positions...");
   
   for(ipart=0; ipart<NumPart; ipart++)
     {
      /* coordinates in grid units */
      rrx = P[ipart].Pos[0] * (float)(NGRID);
      rry = P[ipart].Pos[1] * (float)(NGRID);
      rrz = P[ipart].Pos[2] * (float)(NGRID);
      
      /* index of nearest grid point */
      ix  = (int)(rrx+0.5);
      iy  = (int)(rry+0.5);
      iz  = (int)(rrz+0.5);
      
      /* distance to nearest grid point */
      hx  = rrx - (float)ix;
      hy  = rry - (float)iy;
      hz  = rrz - (float)iz;
      
      /* keep track of peridoc boundaries */
      ix=(int)fmod(ix,NGRID);
      iy=(int)fmod(iy,NGRID);
      iz=(int)fmod(iz,NGRID);
      
      /* calculate TSC weights */
      hx0=0.75 - hx*hx;
      hxp1=0.5* pow2(0.5 + hx);
      hxm1=0.5* pow2(0.5 - hx);
      hy0=0.75 - hy*hy;
      hyp1=0.5* pow2(0.5 + hy);
      hym1=0.5* pow2(0.5 - hy);
      hz0= 0.75 - hz*hz;
      hzp1=0.5* pow2(0.5 + hz);
      hzm1=0.5* pow2(0.5 - hz);
      
      /* keep track of peridoc boundaries */
      ixp1=(int)fmod(ix+1,NGRID);
      iyp1=(int)fmod(iy+1,NGRID);
      izp1=(int)fmod(iz+1,NGRID);
      ixm1=(int)fmod(ix-1+NGRID,NGRID);
      iym1=(int)fmod(iy-1+NGRID,NGRID);
      izm1=(int)fmod(iz-1+NGRID,NGRID);
      
      
      ac =dens[IDX(ixm1,iym1,izm1)]* hxm1*hym1 *hzm1
         +dens[IDX(ix  ,iym1,izm1)]* hx0 *hym1 *hzm1
         +dens[IDX(ixp1,iym1,izm1)]* hxp1*hym1 *hzm1
         +dens[IDX(ixm1,iy  ,izm1)]* hxm1*hy0  *hzm1
         +dens[IDX(ix  ,iy  ,izm1)]* hx0 *hy0  *hzm1
         +dens[IDX(ixp1,iy  ,izm1)]* hxp1*hy0  *hzm1
         +dens[IDX(ixm1,iyp1,izm1)]* hxm1*hyp1 *hzm1
         +dens[IDX(ix  ,iyp1,izm1)]* hx0 *hyp1 *hzm1
         +dens[IDX(ixp1,iyp1,izm1)]* hxp1*hyp1 *hzm1
         +dens[IDX(ixm1,iym1,iz  )]* hxm1*hym1 *hz0
         +dens[IDX(ix  ,iym1,iz  )]* hx0 *hym1 *hz0
         +dens[IDX(ixp1,iym1,iz  )]* hxp1*hym1 *hz0
         +dens[IDX(ixm1,iy  ,iz  )]* hxm1*hy0  *hz0
         +dens[IDX(ix  ,iy  ,iz  )]* hx0 *hy0  *hz0;
      ac+=dens[IDX(ixp1,iy  ,iz  )]* hxp1*hy0  *hz0
         +dens[IDX(ixm1,iyp1,iz  )]* hxm1*hyp1 *hz0
         +dens[IDX(ix  ,iyp1,iz  )]* hx0 *hyp1 *hz0
         +dens[IDX(ixp1,iyp1,iz  )]* hxp1*hyp1 *hz0
         +dens[IDX(ixm1,iym1,izp1)]* hxm1*hym1 *hzp1
         +dens[IDX(ix  ,iym1,izp1)]* hx0 *hym1 *hzp1
         +dens[IDX(ixp1,iym1,izp1)]* hxp1*hym1 *hzp1
         +dens[IDX(ixm1,iy  ,izp1)]* hxm1*hy0  *hzp1
         +dens[IDX(ix  ,iy  ,izp1)]* hx0 *hy0  *hzp1
         +dens[IDX(ixp1,iy  ,izp1)]* hxp1*hy0  *hzp1
         +dens[IDX(ixm1,iyp1,izp1)]* hxm1*hyp1 *hzp1
         +dens[IDX(ix  ,iyp1,izp1)]* hx0 *hyp1 *hzp1
         +dens[IDX(ixp1,iyp1,izp1)]* hxp1*hyp1 *hzp1;
      
      P[ipart].Rho = ac;
     }
   
   fprintf(stderr,"done\n");
}


//-------------------------------
//  write_ascii
//-------------------------------
int write_ascii(char *file_name)
{
   int    i;
   FILE  *fd;
   double RhoMin=1E40, RhoMax=-1E40;
   
   printf("o writing %s\n", file_name);
   if(!(fd=fopen(file_name,"w")))
     {
      printf("can't open file `%s`\n",file_name);
      exit(0);
     }
   
   /* write number of comment lines */
   fprintf(fd,"%d\n",13);
   
   fprintf(fd, "# npart         = %d  %d  %d  %d  %d  %d\n", header1.npart[0], header1.npart[1], header1.npart[2], header1.npart[3], header1.npart[4], header1.npart[5]);
   fprintf(fd, "# mass          = %lf  %lf  %lf  %lf  %lf  %lf\n", header1.mass[0], header1.mass[1], header1.mass[2], header1.mass[3], header1.mass[4],header1. mass[5]);
   fprintf(fd, "# time          = %lf\n", header1.time);
   fprintf(fd, "# redshift      = %lf\n", header1.redshift);
   fprintf(fd, "# fralg_str     = %d\n", header1.flag_sfr);
   fprintf(fd, "# flag_feedback = %d\n", header1.flag_feedback);
   fprintf(fd, "# npartTotal    = %d  %d  %d  %d  %d  %d\n", header1.npartTotal[0], header1.npartTotal[1], header1.npartTotal[2], header1.npartTotal[3], header1.npartTotal[4], header1.npartTotal[5]);
   fprintf(fd, "# flag_cooling  = %d\n", header1.flag_cooling);
   fprintf(fd, "# num_files     = %d\n", header1.num_files);
   fprintf(fd, "# BoxSize       = %lf\n", header1.BoxSize);
   fprintf(fd, "# Omega0        = %lf\n", header1.Omega0);
   fprintf(fd, "# OmegaLambda   = %lf\n", header1.OmegaLambda);
   fprintf(fd, "# HubbleParam   = %lf\n", header1.HubbleParam);
   
   /* write DM particle number to file */
   fprintf(fd,"%d\n",NumPart);
   
   for(i=1; i<=NumPart; i++)
     {
      fprintf(fd, "%f  %f  %f  %f  %d\n", P[i].Pos[0], P[i].Pos[1], P[i].Pos[2], P[i].Rho, P[i].Id);
      if(P[i].Rho > RhoMax) RhoMax = P[i].Rho;
      if(P[i].Rho < RhoMin) RhoMin = P[i].Rho;
     }
   
   fclose(fd);
   
   fprintf(stderr,"done (RhoMin=%g RhoMax=%g)\n\n",RhoMin,RhoMax);
   
   return 0;
   
}

//-------------------------------
//  write_binary
//-------------------------------
int write_binary(char *file_name)
{
   int    i;
   FILE  *fd;
   double RhoMin=1E40, RhoMax=-1E40;
   
   printf("o writing %s\n", file_name);
   if(!(fd=fopen(file_name,"wb")))
     {
      printf("can't open file `%s`\n",file_name);
      exit(0);
     }
   
   /* write DM particle number to file */
   fwrite(&NumPart, sizeof(int), 1, fd);
   
   for(i=1; i<=NumPart; i++)
     {
      fwrite(&(P[i]), sizeof(struct particle_data), 1, fd);
      if(P[i].Rho > RhoMax) RhoMax = P[i].Rho;
      if(P[i].Rho < RhoMin) RhoMin = P[i].Rho;
     }
   
   fclose(fd);
   
   fprintf(stderr,"done (RhoMin=%g RhoMax=%g)\n\n",RhoMin,RhoMax);

   return 0;
   
}

/* this routine loads particle data from Gadget's default
* binary file format. (A snapshot may be distributed
                       * into multiple files.
                       */
int load_snapshot(char *fname, int files)
{
   FILE  *fd;
   char   buf[200], DATA[512];
   int    i,j,k,ntot_withmasses;
   int    t,n,off,pc,pc_new,pc_sph;
   int    ic,c,SWAPBYTES;
   float  Vel[3];
   float  x_min=1E40, x_max=-1E40, y_min=1E40, y_max=-1E40, z_min=1E40, z_max=-1E40;
   
   for(i=0, pc=1; i<files; i++, pc=pc_new)
     {
      if(files>1)
         sprintf(buf,"%s.%d",fname,i);
      else
         sprintf(buf,"%s",fname);
      
      if(!(fd=fopen(buf,"r")))
        {
         printf("can't open file `%s`\n",buf);
         exit(0);
        }
      
#if (BYTESWAP==1)
      SWAPBYTES = TRUE;
      printf("o reading BYTESWAPed `%s' ...\n",buf); fflush(stdout);
#else
      SWAPBYTES = FALSE;
      printf("o reading `%s' ...\n",buf); fflush(stdout);
#endif
      
      /*================= read in GADGET IO header =================*/
#if (GADGET==2)
      GADGET_SKIP;
      fread(DATA,sizeof(char),4,fd);
      DATA[4] = '\0';
      fprintf(stderr,"\n o reading %s\n",DATA);
      GADGET_SKIP;
      GADGET_SKIP;
#endif
      
      GADGET_SKIP;
      ReadInt(fd, &(header1.npart[0]), SWAPBYTES);
      ReadInt(fd, &(header1.npart[1]), SWAPBYTES);
      ReadInt(fd, &(header1.npart[2]), SWAPBYTES);
      ReadInt(fd, &(header1.npart[3]), SWAPBYTES);
      ReadInt(fd, &(header1.npart[4]), SWAPBYTES);
      ReadInt(fd, &(header1.npart[5]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[0]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[1]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[2]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[3]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[4]), SWAPBYTES);
      ReadDouble(fd, &(header1.mass[5]), SWAPBYTES);
      ReadDouble(fd, &(header1.time), SWAPBYTES);
      ReadDouble(fd, &(header1.redshift), SWAPBYTES);
      ReadInt(fd, &(header1.flag_sfr), SWAPBYTES);
      ReadInt(fd, &(header1.flag_feedback), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[0]), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[1]), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[2]), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[3]), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[4]), SWAPBYTES);
      ReadInt(fd, &(header1.npartTotal[5]), SWAPBYTES);
      ReadInt(fd, &(header1.flag_cooling), SWAPBYTES);
      ReadInt(fd, &(header1.num_files), SWAPBYTES);
      ReadDouble(fd, &(header1.BoxSize), SWAPBYTES);
      ReadDouble(fd, &(header1.Omega0), SWAPBYTES);
      ReadDouble(fd, &(header1.OmegaLambda), SWAPBYTES);
      ReadDouble(fd, &(header1.HubbleParam), SWAPBYTES);
      for(ic=0; ic<NFILL; ic++)
        {
         c = fgetc(fd);
         if (c == EOF)
            return(FALSE);
         header1.fill[ic]   = c;
         header1.fill[ic+1] = '\0';
        }
      GADGET_SKIP;
      
      printf("   Header:\n");
      printf("   npart         = %d  %d  %d  %d  %d  %d\n", header1.npart[0], header1.npart[1], header1.npart[2], header1.npart[3], header1.npart[4], header1.npart[5]);
      printf("   mass          = %lf  %lf  %lf  %lf  %lf  %lf\n", header1.mass[0], header1.mass[1], header1.mass[2], header1.mass[3], header1.mass[4],header1. mass[5]);
      printf("   time          = %lf\n", header1.time);
      printf("   redshift      = %lf\n", header1.redshift);
      printf("   fralg_str     = %d\n", header1.flag_sfr);
      printf("   flag_feedback = %d\n", header1.flag_feedback);
      printf("   npartTotal    = %d  %d  %d  %d  %d  %d\n", header1.npartTotal[0], header1.npartTotal[1], header1.npartTotal[2], header1.npartTotal[3], header1.npartTotal[4], header1.npartTotal[5]);
      printf("   flag_cooling  = %d\n", header1.flag_cooling);
      printf("   num_files     = %d\n", header1.num_files);
      printf("   BoxSize       = %lf\n", header1.BoxSize);
      printf("   Omega0        = %lf\n", header1.Omega0);
      printf("   OmegaLambda   = %lf\n", header1.OmegaLambda);
      printf("   HubbleParam   = %lf\n\n", header1.HubbleParam);
      
      if(files==1)
        {
         for(k=0, NumPart=0, TotMass=0.0, ntot_withmasses=0; k<5; k++)
            NumPart+= header1.npart[k];
         Ngas= header1.npart[0];
        }
      else
        {
         for(k=0, NumPart=0, ntot_withmasses=0; k<5; k++)
            NumPart+= header1.npartTotal[k];
         Ngas= header1.npartTotal[0];
        }
      
      for(k=0, ntot_withmasses=0; k<5; k++)
        {
         if(header1.mass[k]==0)
            ntot_withmasses+= header1.npart[k];
        }
      
      if(i==0)
         allocate_memory();
      
      
      /*================= read in GADGET particles =================*/
#if (GADGET==2)
      GADGET_SKIP;
      fread(DATA,sizeof(char),4,fd);
      DATA[4] = '\0';
      fprintf(stderr,"\n o reading %s\n",DATA);
      GADGET_SKIP;
      GADGET_SKIP;
#endif
      GADGET_SKIP;
      for(k=0,pc_new=pc;k<6;k++)
        {
         for(n=0;n<header1.npart[k];n++)
           {
            ReadFloat(fd, &(P[pc_new].Pos[0]), SWAPBYTES);
            ReadFloat(fd, &(P[pc_new].Pos[1]), SWAPBYTES);
            ReadFloat(fd, &(P[pc_new].Pos[2]), SWAPBYTES);
            
            /* convert to [0,1] */
            P[pc_new].Pos[0] /= header1.BoxSize;
            P[pc_new].Pos[1] /= header1.BoxSize;
            P[pc_new].Pos[2] /= header1.BoxSize;
            
            if(P[pc_new].Pos[0] > x_max) x_max=P[pc_new].Pos[0];
            if(P[pc_new].Pos[1] > y_max) y_max=P[pc_new].Pos[1];
            if(P[pc_new].Pos[2] > z_max) z_max=P[pc_new].Pos[2];
            if(P[pc_new].Pos[0] < x_min) x_min=P[pc_new].Pos[0];
            if(P[pc_new].Pos[1] < y_min) y_min=P[pc_new].Pos[1];
            if(P[pc_new].Pos[2] < z_min) z_min=P[pc_new].Pos[2];
            
            
            if(n==0)
               printf("   P[%d].Pos = %f  %f  %f\n", pc_new, P[pc_new].Pos[0], P[pc_new].Pos[1], P[pc_new].Pos[2]);
            pc_new++;
           }
        }
      GADGET_SKIP;
      printf("   ranges:\n");
      printf("   x = %g  %g\n",x_min,x_max);
      printf("   y = %g  %g\n",y_min,y_max);
      printf("   z = %g  %g\n",z_min,z_max);
      
      /*================= read in GADGET velocities =================*/
#if (GADGET==2)
      GADGET_SKIP;
      fread(DATA,sizeof(char),4,fd);
      DATA[4] = '\0';
      fprintf(stderr,"\n o reading %s\n",DATA);
      GADGET_SKIP;
      GADGET_SKIP;
#endif
      GADGET_SKIP;
      for(k=0,pc_new=pc;k<6;k++)
        {
         for(n=0;n<header1.npart[k];n++)
           {
            ReadFloat(fd, &(Vel[0]), SWAPBYTES);
            ReadFloat(fd, &(Vel[1]), SWAPBYTES);
            ReadFloat(fd, &(Vel[2]), SWAPBYTES);
            
            /* convert to peculiar velocities in km/sec */
            Vel[0] *= sqrt(header1.time);
            Vel[1] *= sqrt(header1.time);
            Vel[2] *= sqrt(header1.time);
            
            if(n==0)
               printf("   P[%d].Vel = %f  %f  %f\n", pc_new, Vel[0], Vel[1], Vel[2]);
            pc_new++;
           }
        }
      GADGET_SKIP;
      
      /*================= read in GADGET id's =================*/   
#if (GADGET==2)
      GADGET_SKIP;
      fread(DATA,sizeof(char),4,fd);
      DATA[4] = '\0';
      fprintf(stderr,"\n o reading %s\n",DATA);
      GADGET_SKIP;
      GADGET_SKIP;
#endif
      GADGET_SKIP;
      for(k=0,pc_new=pc;k<6;k++)
        {
         for(n=0;n<header1.npart[k];n++)
           {
            ReadInt(fd, &(P[pc_new].Id), SWAPBYTES);
            if(n==0)
               printf("   P[%d].Id = %d\n", pc_new, P[pc_new].Id);
            pc_new++;
           }
        }
      GADGET_SKIP;
      
      /*================= possibly read in GADGET masses =================*/   
      if(ntot_withmasses>0)
        {
#if (GADGET==2)
         GADGET_SKIP;
         fread(DATA,sizeof(char),4,fd);
         DATA[4] = '\0';
         fprintf(stderr,"\n o reading %s\n",DATA);
         GADGET_SKIP;
         GADGET_SKIP;
#endif
         GADGET_SKIP;
        }
      for(k=0, pc_new=pc; k<6; k++)
        {
         for(n=0;n<header1.npart[k];n++)
           {
            if(header1.mass[k]==0)
              {
               ReadFloat(fd, &(P[pc_new].Mass), SWAPBYTES);
               if(n==0)
                  printf("   P[%d].Mass = %f", pc_new, P[pc_new].Mass);
              }
            else
              {
               P[pc_new].Mass= header1.mass[k];
              }
            
            TotMass += P[pc_new].Mass;
            
            pc_new++;
           }
        }
     }
   
   fclose(fd);
   fprintf(stderr,"\n");
}




/* this routine allocates the memory for the 
* particle data.
*/
int allocate_memory(void)
{
   printf(" o allocating memory for %d particles...",NumPart);
   
   if(!(P=malloc(NumPart*sizeof(struct particle_data))))
     {
      fprintf(stderr,"failed to allocate memory.\n");
      exit(0);
     }
   
   P--;   /* start with offset 1 */
   
   printf("done\n");
}




/****************************************************************************/
/************    UTILITY ROUTINES TO READ BYTESWAPPED DATA    ***************/
/****************************************************************************/


/*
 Read a string of n characters
 */
int ReadString(FILE *fptr,char *s,int n)
{
   int i,c;
   
   if(sizeof(char) != 1)
     {
      fprintf(stderr,"ReadString: sizeof(char)=%ld and not 1\n",sizeof(char));
      exit(0);
     }
   
   s[0] = '\0';
   for (i=0;i<n;i++) {
      c = fgetc(fptr);
      if (c == EOF)
         return(FALSE);
      s[i] = c;
      s[i+1] = '\0';
   }
   return(TRUE);
}

/*
 Read a possibly byte swapped integer
 */
int ReadInt(FILE *fptr,int *n,int swap)
{
   unsigned char *cptr,tmp;
   
   if(sizeof(int) != 4)
     {
      fprintf(stderr,"ReadInt: sizeof(int)=%ld and not 4\n",sizeof(int));
      exit(0);
     }
   
   if (fread(n,4,1,fptr) != 1)
      return(FALSE);
   if (swap) {
      cptr = (unsigned char *)n;
      tmp     = cptr[0];
      cptr[0] = cptr[3];
      cptr[3] = tmp;
      tmp     = cptr[1];
      cptr[1] = cptr[2];
      cptr[2] = tmp;
   }
   return(TRUE);
}

/*
 Read a possibly byte swapped unsigned integer
 */
int ReadUInt(FILE *fptr,unsigned int *n,int swap)
{
   unsigned char *cptr,tmp;
   
   if(sizeof(int) != 4)
     {
      fprintf(stderr,"ReadInt: sizeof(int)=%ld and not 4\n",sizeof(int));
      exit(0);
     }
   
   if (fread(n,4,1,fptr) != 1)
      return(FALSE);
   if (swap) {
      cptr = (unsigned char *)n;
      tmp     = cptr[0];
      cptr[0] = cptr[3];
      cptr[3] = tmp;
      tmp     = cptr[1];
      cptr[1] = cptr[2];
      cptr[2] = tmp;
   }
   return(TRUE);
}

/*
 Read a possibly byte swapped long integer
 */
int ReadLong(FILE *fptr,long *n,int swap)
{
   unsigned char *cptr,tmp;
   
   if(sizeof(long) == 4)
     {
      if (fread(n,4,1,fptr) != 1)
         return(FALSE);
      if (swap) {
         cptr = (unsigned char *)n;
         tmp     = cptr[0];
         cptr[0] = cptr[3];
         cptr[3] = tmp;
         tmp     = cptr[1];
         cptr[1] = cptr[2];
         cptr[2] = tmp;
      }
     }
   else if(sizeof(long) == 8)
     {
      if (fread(n,8,1,fptr) != 1)
         return(FALSE);
      if (swap) {
         cptr = (unsigned char *)n;
         tmp     = cptr[0];
         cptr[0] = cptr[7];
         cptr[7] = tmp;
         tmp     = cptr[1];
         cptr[1] = cptr[6];
         cptr[6] = tmp;
         tmp     = cptr[2];
         cptr[2] = cptr[5];
         cptr[5] = tmp;
         tmp     = cptr[3];
         cptr[3] = cptr[4];
         cptr[4] = tmp;
      }
     }
   else
     {
      fprintf(stderr,"ReadLong: something wrong...cannot read long\n");
      exit(0);
     }
   
   
   
   return(TRUE);
}

/*
 Read a possibly byte swapped long long integer
 */
int ReadLongLong(FILE *fptr,long long *n,int swap)
{
   unsigned char *cptr,tmp;
   
   if (fread(n,8,1,fptr) != 1)
      return(FALSE);
   if (swap) {
      cptr = (unsigned char *)n;
      tmp     = cptr[0];
      cptr[0] = cptr[7];
      cptr[7] = tmp;
      tmp     = cptr[1];
      cptr[1] = cptr[6];
      cptr[6] = tmp;
      tmp     = cptr[2];
      cptr[2] = cptr[5];
      cptr[5] = tmp;
      tmp     = cptr[3];
      cptr[3] = cptr[4];
      cptr[4] = tmp;
   }
   return(TRUE);
}

/*
 Read a possibly byte swapped double precision number
 Assume IEEE
 */
int ReadDouble(FILE *fptr,double *n,int swap)
{
   unsigned char *cptr,tmp;
   
   if(sizeof(double) != 8)
     {
      fprintf(stderr,"ReadDouble: sizeof(double)=%ld and not 8\n",sizeof(double));
      exit(0);
     }
   
   if (fread(n,8,1,fptr) != 1)
      return(FALSE);
   if (swap) {
      cptr = (unsigned char *)n;
      tmp     = cptr[0];
      cptr[0] = cptr[7];
      cptr[7] = tmp;
      tmp     = cptr[1];
      cptr[1] = cptr[6];
      cptr[6] = tmp;
      tmp     = cptr[2];
      cptr[2] = cptr[5];
      cptr[5] = tmp;
      tmp     = cptr[3];
      cptr[3] = cptr[4];
      cptr[4] = tmp;
   }
   
   return(TRUE);
}

/*
 Read a possibly byte swapped floating point number
 Assume IEEE format
 */
int ReadFloat(FILE *fptr,float *n, int swap)
{
   unsigned char *cptr,tmp;
   
   if(sizeof(float) != 4)
     {
      fprintf(stderr,"ReadFloat: sizeof(float)=%ld and not 4\n",sizeof(float));
      exit(0);
     }
   
   if (fread(n,4,1,fptr) != 1)
      return(FALSE);
   if (swap) 
     {
      cptr = (unsigned char *)n;
      tmp     = cptr[0];
      cptr[0] = cptr[3];
      cptr[3] = tmp;
      tmp     = cptr[1];
      cptr[1] = cptr[2];
      cptr[2] = tmp;
     }
   return(TRUE);
}
















