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

#include "../src/param.h"
#include "../src/tdef.h"
#include "../src/common.c"

#include "../src/libutility/utility.h"

/*#define DEFCOLOR*/	/* define your own colors for stereo2 */
/*#define STEREO2*/		/* write stereo2 file */

/* io structure containing information about simulation details */
info_io       io;
info_global   global;
param_simu    simu;

void VelEllipsoid(FILE *fpout, long mhalo, long npart, float *vx, float *vy, float *vz,
                  double Sv_tensor[3][3], double *Sv_xx, double *Sv_yy, double *Sv_zz);
void read_simu  (char *filename);
void free_nquad (nqptr cur_nquad);
void free_cquad (cqptr cur_cquad);
void free_pquad(pqptr cur_pquad);

/*=========================================================================
* definitions for read_mlapm()
*=========================================================================*/
#define MLAPM_HEADERSTRING  100      /* this value must agree with the value in param.h */
#define MLAPM_FILLHEADER (508-MLAPM_HEADERSTRING-4-5*8-4-6*8-20*8)
struct mlapm_header
{
   char          header[MLAPM_HEADERSTRING];
   long          no_part;
   double        boxsize;
   double        omega0;
   double        lambda0;
   double        a_initial;
   double        a_current;
   int           no_timestep;
   double        K_initial;
   double        U_initial;
   double        K_current;
   double        U_current;
   double        Eintegral;
   double        Econst;
   double        numerics[20];
   char          dummy[MLAPM_FILLHEADER];
} mlapm_header;
/*=========================================================================
* definitions for read_gadget()
*=========================================================================*/
#define SIZEOFGADGETHEADER 256    /* Size of GADGET header in bytes */
struct io_gadget_header
{
   int      np[6];
   double   massarr[6];
   double   expansion;
   double   redshift;
   int      flagsfr;
   int      flagfeedback;
   int      nall[6];
   int      flagcooling;
   int      NumFiles;
   double   BoxSize;
   double   Omega0;
   double   OmegaLambda;
   double   HubbleParam;
   char     unused[SIZEOFGADGETHEADER- 6*4- 6*8- 2*8- 2*4- 6*4- 2*4 - 4*8];
} gadget.header;  

struct particle_data 
{
   float     Pos[3];       /* particle position at its current time */  
   float     Vel[3];       /* particle velocity at its current time */  
#ifdef LGADGET
   long long ID;           /* unique particle identifier */  
#else
   int       ID;
#endif
   float     Mass;
} *P_gadget;

/*=========================================================================
* definitions for read_ART()
*=========================================================================*/
/* for binary fortran files one needs to skip one float before actually reading */
float dustbin;
#define ART_SKIP(fp) fread(&dustbin, sizeof(dustbin), 1, fp)

struct CONTROL
{
   char    header_string[45];
   float   aexpn;
   float   aexp0;
   float   amplt;
   float   astep;
   int     istep;
   float   partw;
   float   tintg;
   float   ekin;
   float   ekin1;
   float   ekin2;
   float   au0;
   float   aeu0;
   int     nrowc;
   int     ngridc;
   int     nspecies;
   int     nseed;
   float   Om0;
   float   Oml0;
   float   hubble;
   float   wp5;
   float   Ocurv;
   float   extras[100];
} ART_header;

/*=========================================================================================*/
/*=========================================================================================*/
/*=========================================================================================*/
int main()
{
   int           BUFSIZE;
   float         fw[7];
   double        dw[7];
   double        dx, dy, dz;
   float        *xpart, *ypart, *zpart, *vxpart, *vypart, *vzpart;
   double       *dist2, dist;
   long unsigned no_part;
   partptr       cur_part;
   
   long         *id, nid;
   int          px, py, pz;
   
   long          ipart, *ihalo, nhalo, jhalo, j, mhalo, ifirst, ilast;
   double        x_fac, v_fac, m_fac;
   char          infile[MAXSTRING],outprefix[MAXSTRING],outfile[MAXSTRING],idfile[MAXSTRING],dummyline[MAXSTRING],PLfile[MAXSTRING];
   FILE          *fpin, *fpout, *fpid, *fpPL;
   int           single_file;
   double        r,g,b;
   int           ISEED=-123456;


   double        Sv_tensor[3][3], Sv_xx, Sv_yy, Sv_zz;
   
   
   printf("====================================================================\n");
   printf("  extract halo particles from AMIGA binary using AHF/BDM_particles\n");
   printf("               and calculate the velocity ellipsoid\n");
   printf("====================================================================\n");
   
   printf("Please give name of input file:                ");
   scanf("%s",infile);
   printf("%s\n",infile);
   printf("Please give name of AHF/BDM_particles:         ");
   scanf("%s",idfile);
   printf("%s\n",idfile);
   printf("Please give number of halos to extract:        ");
   scanf("%ld",&nhalo);
   printf("%ld\n",nhalo);
   
   /* a negative nhalo means that we want to extract all consecutive halos from halo #i -> halo #j */
   if(nhalo < 0)
     {
      printf("Please give id of first halo to extract:       ", j);
      scanf("%ld",&ifirst);
      printf("%ld\n",ifirst);
      printf("Please give id of last  halo to extract:       ", j);
      scanf("%ld",&ilast);
      printf("%ld\n",ilast);
      nhalo = ilast-ifirst+1;
      ihalo = (long *) calloc(nhalo, sizeof(long));
      for(j=0; j<nhalo; j++)
         ihalo[j] = ifirst+j;
     }
   /* otherwise specify the halo id's manually */
   else
     {
      ihalo = (long *) calloc(nhalo, sizeof(long));
      for(j=0; j<nhalo; j++)
        {
         printf("Please give id of %5ld.halo to extract:       ", j);
         scanf("%ld",&ihalo[j]);
         printf("%ld\n",ihalo[j]);
        }
     }
   printf("Please give name for output file:              ");
   scanf("%s",outprefix);
   printf("%s\n\n",outprefix);
   
#ifdef DEFCOLOR
   printf("RGB color (0.0-1.0):");
   scanf("%lf %lf %lf",&r, &g, &b);
   printf("%g %g %g\n\n",r,g,b);   
#endif   
   /*-------------------------------------------------------
    * read simulation raw data
    *-------------------------------------------------------*/
   read_simu(infile);
   
   /*-------------------------------------------------------
    * conversion factors
    *-------------------------------------------------------*/      
  m_fac  = io.header.pmass;
  x_fac  = io.header.boxsize;
  v_fac  = io.header.boxsize * H0 / io.header.a_current;
#ifdef XDOT
  v_fac  = io.header.boxsize * H0 / pow2(io.header.a_current);
#endif

     sprintf(outfile,"%s",outprefix);
     fprintf(stderr,"\n o writing all particles to %s\n",outfile);
     if((fpout=fopen(outfile,"w"))==NULL)
       {
        printf("I cannot open %s\n", outfile);
        exit(1);
       }

  /*----------------------------------------------------------------------
   * loop over all halos to extract
   *----------------------------------------------------------------------*/
  fprintf(stderr,"\n o extracting %5ld halos:\n",nhalo);
  for(mhalo=0; mhalo<nhalo; mhalo++)
    {
     /*----------------------------------------------------------------------
     * read in particles id's for halo "ihalo" from AHF/BDM_particles file
     *----------------------------------------------------------------------*/
     fprintf(stderr,"   o reading particle id's of halo #%5ld ... ",ihalo[mhalo]);
     
     if((fpid=fopen(idfile,"rb"))==NULL)
       {
        printf("I cannot open %s\n", idfile);
        exit(1);
       }
     
     jhalo = -1;
     id    = NULL;
     
     while( jhalo < ihalo[mhalo] )
       {
        /* how many id's in current halo */
        fscanf(fpid,"%ld",&nid);
        
        //fprintf(stderr,"(%ld particles) ",nid);

        /* allocate array to hold particle id's */
        id = (long *) realloc(id, nid*sizeof(long));
        
        for(j=0; j<nid; j++)
          {
           fscanf(fpid,"%ld",&id[j]);
           //fprintf(stderr,"%ld ",id[j]);
          }
        
        /* next halo */
        jhalo++;
       }
     fprintf(stderr,"done\n");
     fclose(fpid);
     
     /* assign a color to the particles in this particular halo */
#ifndef DEFCOLOR
     r = (double) ran3(&ISEED);
     g = (double) ran3(&ISEED);
     b = (double) ran3(&ISEED);
#endif    


     /* copy particles over to our temporary arrays */
     xpart  = (float *) calloc(nid, sizeof(float));
     ypart  = (float *) calloc(nid, sizeof(float));
     zpart  = (float *) calloc(nid, sizeof(float));
     vxpart = (float *) calloc(nid, sizeof(float));
     vypart = (float *) calloc(nid, sizeof(float));
     vzpart = (float *) calloc(nid, sizeof(float));

     for(ipart=0; ipart<nid; ipart++)
       {
        /* current particle coordinates */
        cur_part = global.fst_part + id[ipart];
        xpart[ipart]    = cur_part->pos[X]*x_fac;
        ypart[ipart]    = cur_part->pos[Y]*x_fac;
        zpart[ipart]    = cur_part->pos[Z]*x_fac;
	
        vxpart[ipart]   = cur_part->mom[X]*v_fac;
        vypart[ipart]   = cur_part->mom[Y]*v_fac;
        vzpart[ipart]   = cur_part->mom[Z]*v_fac;
       }
     
     /* pass halo particles to routine eventually calculating the velocity ellipsoid */
     VelEllipsoid(fpout, ihalo[mhalo], nid, vxpart, vypart, vzpart, Sv_tensor, &Sv_xx, &Sv_yy, &Sv_zz);


     /* dump velocity ellipsoid to file */
/*      fprintf(fpout,"%ld ",nid,
	     Sv_xx,Sv_tensor[0][0],Sv_tensor[0][1],Sv_tensor[0][2],
	     Sv_yy,Sv_tensor[1][0],Sv_tensor[1][1],Sv_tensor[1][2],
	     Sv_zz,Sv_tensor[2][0],Sv_tensor[2][1],Sv_tensor[2][2]);  */


     /* don't forget to free allocated memory! */
     free(xpart);
     free(ypart);
     free(zpart);
     free(vxpart);
     free(vypart);
     free(vzpart);

    } /* mhalo */

  fclose(fpout);
  fprintf(stderr,"done\n");
	
	
  free(global.fst_part); 
}
/*==============================================================================
* read AMIGA binary
*==============================================================================*/
void read_amiga(char *filename)
{
   partptr       cur_part;
   long unsigned ipart;
   int           BUFSIZE;
   float         fw[7];
   double        dw[7];
   int           j;
   FILE         *fpin;
   int           SWAPBYTES;
   
   fprintf(stderr,"\n o starting to read AMIGA binary %s ...",filename);
   
   /* open file */
   if((fpin=fopen(filename,"r"))==NULL)
     {
      printf("I cannot open %s\n", filename);
      exit(1);
     }
   
   /* read AMIGA header */
   read_amiga_header(fpin, &io, &SWAPBYTES);
   

#ifdef VERBOSE2
  /* print some information */
  printf("some information about input file:\n");
  printf("==================================\n");
  printf("%s\n\n",io.header.header);
  printf("multi-mass       = %d\n",io.header.multi_mass);
  printf("double precision = %d\n",io.header.double_precision);
  printf("no_part          = %ld\n",io.header.no_part);
  printf("no_vpart         = %g\n",io.header.no_vpart);
  printf("boxsize          = %g\n",io.header.boxsize);
  printf("Omega0           = %g\n",io.header.omega0);
  printf("Lambda0          = %g\n",io.header.lambda0);
  printf("z_initial        = %g (a=%lf)\n",
         1.0/io.header.a_initial-1.0,io.header.a_initial);
  printf("z_current        = %g (a=%lf)\n",
         1.0/io.header.a_current-1.0,io.header.a_current);
  printf("no_timestep      = %d\n",io.header.no_timestep);
#endif   


  if(io.header.multi_mass == 1)
     BUFSIZE = 7;
  else
     BUFSIZE = 6;

  /* read in coordinates */
  global.fst_part = c_part(io.header.no_part);
  cur_part        = global.fst_part;

  for(ipart=0;ipart<io.header.no_part;ipart++)
    {
     fw[6] = 1.0;
     
     if(io.header.double_precision == 1)
       {		  
        for(j=0; j<BUFSIZE; j++)
          {
           ReadDouble(fpin,&(dw[j]),SWAPBYTES);
           fw[j] = (float) dw[j];
          }
        
       }
     else /* double precision */
       {
        for(j=0; j<BUFSIZE; j++)
          {
           ReadFloat(fpin,&(fw[j]),SWAPBYTES);
          }
       }
     
     /* store particle information */
     cur_part->pos[X] = fw[0];
     cur_part->pos[Y] = fw[2];
     cur_part->pos[Z] = fw[4];
     
     cur_part->mom[X] = fw[1];
     cur_part->mom[Y] = fw[3];
     cur_part->mom[Z] = fw[5];

#ifdef MULTIMASS
     cur_part->weight = fw[6];
#endif     
     
     /* next particle */
     cur_part++;
    }

  /* close file again */
  fclose(fpin);


   /* store things in AMIGA common blocks */
   global.a     = io.header.a_current;
   global.z     = (double)1.0/io.header.a_current - (double)1.0;

   fprintf(stderr,"done\n");
}

/*==============================================================================
* read data from MLAPM initial conditions file
*==============================================================================*/
#ifdef MLAPM
void read_mlapm(char *filename)
{
   FILE          *icfile;
   partptr        cur_part;
   int            j, ino_part;
   long unsigned  ipart, ndummy;
   flouble        pos, mom, fweight, fweight_old;
   long           no_species;
   int            i;
   double         dummy;
   int            SWAPBYTES;
   
#ifdef BYTESWAP
   SWAPBYTES = TRUE;
#else
   SWAPBYTES = FALSE;
#endif
   
#ifdef VERBOSE
#ifdef BYTESWAP
   fprintf(stderr," reading BYTESWAPed MLAPM data %s\n",filename);
#else
   fprintf(stderr," reading MLAPM data %s\n",filename);
#endif
#endif /* VERBOSE */
   
   /* open file */
   if((icfile=fopen(filename,"r"))==NULL)
     {
      printf("I cannot open %s\n", filename);
      exit(1);
     }
   
#ifdef BYTESWAP
   /* read in IO header */
   ReadChars(icfile,mlapm_header.header,MLAPM_HEADERSTRING);
#ifdef LONG4
   ReadInt(icfile,&ino_part,SWAPBYTES);
   mlapm_header.no_part = (long unsigned) ino_part;
#else
   ReadLong(icfile,&(mlapm_header.no_part),SWAPBYTES);	
#endif
   ReadDouble(icfile,&(mlapm_header.boxsize),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.omega0),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.lambda0),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.a_initial),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.a_current),SWAPBYTES);
   ReadInt(icfile,&(mlapm_header.no_timestep),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.K_initial),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.U_initial),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.K_current),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.U_current),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.Eintegral),SWAPBYTES);
   ReadDouble(icfile,&(mlapm_header.Econst),SWAPBYTES);
   for (i=0;i<20;i++)
      ReadDouble(icfile,&(mlapm_header.numerics[i]),SWAPBYTES);
#ifdef LONG4
   ReadChars(icfile,mlapm_header.dummy,MLAPM_FILLHEADER+4);
#else
   ReadChars(icfile,mlapm_header.dummy,MLAPM_FILLHEADER);
#endif
   
#else  /* BYTESWAP */
   if(fread(&(mlapm_header), sizeof(mlapm_header), 1, icfile) != 1)
     {
      fprintf(io.logfile,"\n\ninput: could not read MLAPM IO header\n");
      fflush(io.logfile);
      fclose(io.logfile);
      exit(1);
     }
#endif /* BYTESWAP */
   
#ifdef VERBOSE
   /* dump MLAPM header (*before* reading particles) */
   fprintf(stderr,"%s\n",mlapm_header.header);
   fprintf(stderr,"mlapm_header.no_part               = %ld\n",mlapm_header.no_part);
   fprintf(stderr,"mlapm_header.no_timestep           = %d\n",mlapm_header.no_timestep);
   fprintf(stderr,"mlapm_header.boxsize               = %g\n",mlapm_header.boxsize);
   fprintf(stderr,"mlapm_header.omega0                = %g\n",mlapm_header.omega0);
   fprintf(stderr,"mlapm_header.lambda0               = %g\n",mlapm_header.lambda0);   
   fprintf(stderr,"mlapm_header.pmass                 = %g\n",mlapm_header.numerics[9]);
   fprintf(stderr,"mlapm_header.cur_reflevel          = %g\n",mlapm_header.numerics[7]);
   fprintf(stderr,"mlapm_header.cur_frcres            = %g\n",mlapm_header.numerics[8]);
   fprintf(stderr,"mlapm_header.a_initial             = %g\n",mlapm_header.a_initial);
   fprintf(stderr,"mlapm_header.a_current             = %g\n",mlapm_header.a_current);
   fprintf(stderr,"mlapm_header.K_initial             = %g\n",mlapm_header.K_initial);
   fprintf(stderr,"mlapm_header.K_current             = %g\n",mlapm_header.K_current);
   fprintf(stderr,"mlapm_header.U_initial             = %g\n",mlapm_header.U_initial);
   fprintf(stderr,"mlapm_header.U_current             = %g\n",mlapm_header.U_current);
   fprintf(stderr,"mlapm_header.Eintegral             = %g\n",mlapm_header.Eintegral);
   fprintf(stderr,"mlapm_header.Econst                = %g\n",mlapm_header.Econst);
   fprintf(stderr,"=======================================\n");
   fprintf(stderr,"                 done\n");
   fprintf(stderr,"=======================================\n");
#endif /* VERBOSE */
   
   /*transfer everything from MLAPM header to AMIGA header */
   strcpy(io.header.header,mlapm_header.header);
   io.header.no_part     = mlapm_header.no_part;
   io.header.boxsize     = mlapm_header.boxsize;
   io.header.omega0      = mlapm_header.omega0;
   io.header.lambda0     = mlapm_header.lambda0;
   io.header.a_initial   = mlapm_header.a_initial;
   io.header.a_current   = mlapm_header.a_current;
   io.header.no_timestep = mlapm_header.no_timestep;
   io.header.K_initial   = mlapm_header.K_initial;
   io.header.K_current   = mlapm_header.K_current;
   io.header.U_initial   = mlapm_header.U_initial;
   io.header.U_current   = mlapm_header.U_current;
   io.header.Eintegral   = mlapm_header.Eintegral;
   io.header.Econst      = mlapm_header.Econst;
   io.header.cur_reflevel= mlapm_header.numerics[7];
   io.header.cur_frcres  = mlapm_header.numerics[8];
   io.header.pmass       = mlapm_header.numerics[9];
   
   /* store things */
   simu.no_part       = io.header.no_part;
   simu.boxsize       = io.header.boxsize;
   simu.omega0        = io.header.omega0;
   simu.lambda0       = io.header.lambda0;
   simu.a_initial     = io.header.a_initial;
   simu.z_initial     = (double)1.0/simu.a_initial - (double)1.0;
   simu.t_initial     = calc_t(simu.a_initial);
   /* simu.pmass is calculated below... */
   global.a           = io.header.a_current;
   global.z           = (double)1.0/io.header.a_current - (double)1.0;

#ifdef MULTIMASS
   simu.no_vpart   = (double)0.0;
   simu.max_weight = (double)0.0;
   no_species      = 0;
   fweight         = 0.0;
   fweight_old     = 0.0;
#endif
   
   global.fst_part = c_part(simu.no_part);
   cur_part        = global.fst_part;
   for(ipart = 0; ipart < simu.no_part; ipart++)
     {
      for(j = X; j <= Z; j++)
        {
#ifdef DOUBLE
         ReadDouble(icfile,&pos,SWAPBYTES);
         ReadDouble(icfile,&mom,SWAPBYTES);
#else
         ReadFloat(icfile,&pos,SWAPBYTES);
         ReadFloat(icfile,&mom,SWAPBYTES);
#endif
         cur_part->pos[j] = fmod((double)pos + 1.0, 1.0);
         cur_part->mom[j] = mom;
         
        }
      
#ifdef MULTIMASS
#ifdef DOUBLE
      ReadDouble(icfile,&fweight,SWAPBYTES);
#else
      ReadFloat(icfile,&fweight,SWAPBYTES);
#endif
      
      /* keep track of number of species (assumes "mass ordered" particles!) */
      if(fabs(fweight-fweight_old) > ZERO && fweight > ZERO)
        {
         no_species += 1;
         fweight_old = fweight;
         
         if(fweight > simu.max_weight) 
            simu.max_weight = fweight;
        }
      
      cur_part->weight  = fweight; 
      simu.no_vpart    += (double)cur_part->weight;
#endif
      cur_part++;
     }
   
#ifdef MULTIMASS
   simu.no_species = no_species;
#else
   simu.no_species = 1;
   simu.no_vpart   = simu.no_part;
#endif
#ifdef MULTIMASS
   simu.pmass     = simu.omega0 * rhoc0 * pow3(simu.boxsize)/simu.no_vpart;   
#else /* MULTIMASS */
   simu.pmass     = simu.omega0 * rhoc0 * pow3(simu.boxsize)/simu.no_part;
#endif /* MULTIMASS */
   
   /* store pmass in AMIGA header */
   io.header.pmass = simu.pmass;
} 
#endif /* MLAPM */

/*==============================================================================
* read data from initial conditions file provided in GADGET format
* kindly provided by Chris Power, August 2004
* adapted for multiple GADGET files and stars/gas by A. Knebe, August 2005
*==============================================================================*/
#ifdef GADGET
int blklen;
#define GADGET_SKIP fread(&blklen,sizeof(int),1,icfile);
/*==========================================
* read_gadget():   actual reading routine
*==========================================*/
void read_gadget(char *filename)
{
   FILE          *icfile;
   partptr        cur_part;
   long unsigned  ipart;
   long           ID, IDmin, IDmax;
#ifdef LIGHTCONE
   bckptr         backup_part;
#endif /* LIGHTCONE */
#ifdef MULTIMASS
   float          fweight, fweight_old;
   int            no_species;
#endif
   double         tot_mass[6];
   double         mmin;               /* Minimum mass particle... */
   
   int i,j,k;            /* Dummy variables */
   char DATA[MAXSTRING];
   
   int    no_part, ppart;    /* Number of particles in simulation */
   float  dummy[3];   /* Dummy float variable, for reading (x,y,z,vx,vy,vz,m) */
   
   double x_fac, v_fac, m_fac;
   
   int    massflag;           /* Are the masses contained in the header? */
   int    SWAPBYTES;
   
   /* so far works only for one single GADGET file... */
   gadget.no_gadget_files  = 1;
   gadget.i_gadget_file    = 0;
   gadget.np[0]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   gadget.np[1]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   gadget.np[2]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   gadget.np[3]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   gadget.np[4]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   gadget.np[5]     = (int *) calloc(gadget.no_gadget_files, sizeof(int *));
   
   
#ifdef BYTESWAP
   SWAPBYTES = TRUE;
   fprintf(stderr,"   start reading BYTESWAPed GADGET data\n");
#else
   SWAPBYTES = FALSE;
   fprintf(stderr,"   start reading GADGET data\n");
#endif
   fprintf(stderr,"==========================================\n");
   
   /* open file */
   if((icfile=fopen(filename,"r"))==NULL)
     {
      printf("I cannot open %s\n", filename);
      exit(1);
     }
   
   /*================= read in GADGET IO header =================*/
   GADGET_SKIP;
#ifdef GADGET2
   fread(DATA,sizeof(char),4,icfile);
   DATA[4] = '\0';
   GADGET_SKIP;
   GADGET_SKIP;
   GADGET_SKIP;
   fprintf(stderr,"reading... %s\n",DATA);
#endif
   
#ifdef BYTESWAP
   ReadInt(icfile,&(gadget.header.np[0]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.np[1]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.np[2]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.np[3]),SWAPBYTES);    /* number of particles in current file */
   ReadInt(icfile,&(gadget.header.np[4]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.np[5]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[0]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[1]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[2]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[3]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[4]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.massarr[5]),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.expansion),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.redshift),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.flagsfr),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.flagfeedback),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.nall[0]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.nall[1]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.nall[2]),SWAPBYTES);  /* total number of particles in simulation */
   ReadInt(icfile,&(gadget.header.nall[3]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.nall[4]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.nall[5]),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.flagcooling),SWAPBYTES);
   ReadInt(icfile,&(gadget.header.NumFiles),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.BoxSize),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.Omega0),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.OmegaLambda),SWAPBYTES);
   ReadDouble(icfile,&(gadget.header.HubbleParam),SWAPBYTES);
   ReadChars(icfile,&(gadget.header.unused[0]),SIZEOFGADGETHEADER- 6*4- 6*8- 2*8- 2*4- 6*4- 2*4 - 4*8);
#else
   fread(&gadget.header,sizeof(gadget.header),1,icfile);
#endif
   GADGET_SKIP;
   fprintf(stderr,"\nFinished reading GADGET header\n\n");
   /*================= read in GADGET IO header =================*/
   
   fprintf(stderr,"expansion factor: %lf\n",gadget.header.expansion);
   fprintf(stderr,"redshift:         %lf\n",gadget.header.redshift);
   fprintf(stderr,"boxsize:          %lf (%lf Mpc/h)\n",gadget.header.BoxSize,gadget.header.BoxSize*GADGET_LUNIT);
   fprintf(stderr,"omega0:           %lf\n",gadget.header.Omega0);
   fprintf(stderr,"lambda0:          %lf\n",gadget.header.OmegaLambda);
   fprintf(stderr,"HubbleParam:      %lf\n\n",gadget.header.HubbleParam);
   
   /* do we need to read a mass array? (also count no_part in current file...) */
   massflag   = 0;
   no_part    = 0;
   for(i=0;i<6;i++) 
     {
      no_part += gadget.header.np[i];
      if(gadget.header.massarr[i] < ZERO && gadget.header.np[i] > 0)
         massflag=1;  
     }
   
   /* allocate particle array */
   if(!(P_gadget=malloc(no_part*sizeof(struct particle_data))))
     {
      fprintf(io.logfile,"\nfailed to allocate memory for GADGET data\n");
      exit(1);
     }
   
   fprintf(stderr,"gas:    np[0]=%9d\t nall[0]=%9d\t massarr[0]=%g\n",gadget.header.np[0],gadget.header.nall[0],gadget.header.massarr[0]); 
   fprintf(stderr,"halo:   np[1]=%9d\t nall[1]=%9d\t massarr[1]=%g\n",gadget.header.np[1],gadget.header.nall[1],gadget.header.massarr[1]); 
   fprintf(stderr,"disk:   np[2]=%9d\t nall[2]=%9d\t massarr[2]=%g\n",gadget.header.np[2],gadget.header.nall[2],gadget.header.massarr[2]); 
   fprintf(stderr,"bulge:  np[3]=%9d\t nall[3]=%9d\t massarr[3]=%g\n",gadget.header.np[3],gadget.header.nall[3],gadget.header.massarr[3]); 
   fprintf(stderr,"stars:  np[4]=%9d\t nall[4]=%9d\t massarr[4]=%g\n",gadget.header.np[4],gadget.header.nall[4],gadget.header.massarr[4]); 
   fprintf(stderr,"bndry:  np[5]=%9d\t nall[5]=%9d\t massarr[5]=%g\n",gadget.header.np[5],gadget.header.nall[5],gadget.header.massarr[5]); 
   
   gadget.np[0][gadget.i_gadget_file] = gadget.header.np[0];
   gadget.np[1][gadget.i_gadget_file] = gadget.header.np[1];
   gadget.np[2][gadget.i_gadget_file] = gadget.header.np[2];
   gadget.np[3][gadget.i_gadget_file] = gadget.header.np[3];
   gadget.np[4][gadget.i_gadget_file] = gadget.header.np[4];
   gadget.np[5][gadget.i_gadget_file] = gadget.header.np[5];
   
   fprintf(stderr,"\nreading %d particles from GADGET file #%d...\n\n",no_part,gadget.i_gadget_file);
   
   /*================= read in GADGET particles =================*/
   GADGET_SKIP;
#ifdef GADGET2
   fread(DATA,sizeof(char),4,icfile);
   DATA[4] = '\0';
   GADGET_SKIP;
   GADGET_SKIP;
   GADGET_SKIP;
   fprintf(stderr,"reading... %s (%g MB) ",DATA,blklen/1024./1024.);
#endif
   
   for(i=0;i<no_part;i++)
     {
#ifdef BYTESWAP
      ReadFloat(icfile,&(dummy[0]),SWAPBYTES);
      ReadFloat(icfile,&(dummy[1]),SWAPBYTES);
      ReadFloat(icfile,&(dummy[2]),SWAPBYTES);
#else
      fread(&dummy[0],sizeof(float),3,icfile);
#endif      
      P_gadget[i].Pos[0]=dummy[0]*GADGET_LUNIT;
      P_gadget[i].Pos[1]=dummy[1]*GADGET_LUNIT;
      P_gadget[i].Pos[2]=dummy[2]*GADGET_LUNIT;
     }
#ifdef GADGET2
   fprintf(stderr,"done\n");
#endif
   GADGET_SKIP;
   /*================= read in GADGET particles =================*/
   
   
   
   /*================= read in GADGET velocities =================*/
   GADGET_SKIP;   
#ifdef GADGET2
   fread(DATA,sizeof(char),4,icfile);
   DATA[4] = '\0';
   GADGET_SKIP;
   GADGET_SKIP;
   GADGET_SKIP;
   fprintf(stderr,"reading... %s (%g MB) ",DATA,blklen/1024./1024.);
#endif
   
   for(i=0;i<no_part;i++)
     {
#ifdef BYTESWAP
      ReadFloat(icfile,&(dummy[0]),SWAPBYTES);
      ReadFloat(icfile,&(dummy[1]),SWAPBYTES);
      ReadFloat(icfile,&(dummy[2]),SWAPBYTES);
#else
      fread(&dummy[0],sizeof(float),3,icfile);
#endif 
      P_gadget[i].Vel[0]=dummy[0];
      P_gadget[i].Vel[1]=dummy[1];
      P_gadget[i].Vel[2]=dummy[2]; 
     }
#ifdef GADGET2
   fprintf(stderr,"done\n");
#endif
   GADGET_SKIP;
   /*================= read in GADGET velocities =================*/
   
   
   /*================= read in GADGET id's =================*/
   GADGET_SKIP;
   
#ifdef GADGET2
   fread(DATA,sizeof(char),4,icfile);
   DATA[4] = '\0';
   GADGET_SKIP;
   GADGET_SKIP;
   GADGET_SKIP;
   fprintf(stderr,"reading... %s (%g MB) ",DATA,blklen/1024./1024.);
#endif
   
#ifdef LGADGET      
   for(i=0;i<no_part;i++)
     {
#ifdef BYTESWAP
      ReadLong(icfile,&(P_gadget[i].ID),SWAPBYTES);
#else
      fread(&P_gadget[i].ID,sizeof(long),1,icfile);
#endif
     }
#else /* LGADGET */
   for(i=0;i<no_part;i++)
     {
#ifdef BYTESWAP
      ReadInt(icfile,&(P_gadget[i].ID),SWAPBYTES);
#else
      fread(&P_gadget[i].ID,sizeof(int),1,icfile);
#endif
     }
#endif
#ifdef GADGET2
   fprintf(stderr,"done\n");
#endif
   GADGET_SKIP;
   /*================= read in GADGET id's =================*/
   
   
   k = 0;
   
   /* massflag == 1 indicates that massarr[i] = 0 and hence need to read in particle masses */
   if(massflag==1) 
     {
      /*================= read in GADGET individual particle masses =================*/
      GADGET_SKIP; 
#ifdef GADGET2
      fread(DATA,sizeof(char),4,icfile);
      DATA[4] = '\0';
      GADGET_SKIP;
      GADGET_SKIP;
      GADGET_SKIP;
      fprintf(stderr,"reading... %s (%g MB): ",DATA,blklen/1024./1024.);
#endif
      for(i=0;i<6;i++)
        {
         tot_mass[i] = 0.;
         if (gadget.header.np[i] > 0 && gadget.header.massarr[i] < ZERO  ) 
           {
            
            fprintf(stderr," %d, ",i);
            
            for(j=0; j<gadget.header.np[i]; j++)
              {
#ifdef BYTESWAP
               ReadFloat(icfile,&(dummy[0]),SWAPBYTES);
#else
               fread(&dummy[0],sizeof(float),1,icfile);
#endif
               P_gadget[k].Mass  = dummy[0];
               tot_mass[i]      += dummy[0];
               
               k++;
              }
           }
         else
           {
            /* simply copy appropriate massarr[i] to particles */
            for(j=0; j<gadget.header.np[i]; j++) 
              {
               P_gadget[k].Mass = gadget.header.massarr[i];
               k++;
              }
            tot_mass[i] = gadget.header.np[i]*gadget.header.massarr[i];
           }
        }
      
#ifdef GADGET2
      fprintf(stderr," done\n");
#endif
      GADGET_SKIP;
      /*================= read in GADGET individual particle masses =================*/
     } 
   
   /* simply copy appropriate massarr[i] to particles */
   else 
     {
      k=0;
      for(i=0;i<6;i++)
        {
         for(j=0;j<gadget.header.np[i];j++) 
           {
            P_gadget[k].Mass = gadget.header.massarr[i];
            k++;
           }
         tot_mass[i] = gadget.header.np[i]*gadget.header.massarr[i];
        }
     }
   
   if(gadget.header.np[0] > 0) fprintf(stderr,"    gas:    tot_mass[0]=%g (%g)\n",tot_mass[0],tot_mass[0]/(double)gadget.header.np[0]);
   if(gadget.header.np[1] > 0) fprintf(stderr,"    halo:   tot_mass[1]=%g (%g)\n",tot_mass[1],tot_mass[1]/(double)gadget.header.np[1]);
   if(gadget.header.np[2] > 0) fprintf(stderr,"    disk:   tot_mass[2]=%g (%g)\n",tot_mass[2],tot_mass[2]/(double)gadget.header.np[2]);
   if(gadget.header.np[3] > 0) fprintf(stderr,"    bulge:  tot_mass[3]=%g (%g)\n",tot_mass[3],tot_mass[3]/(double)gadget.header.np[3]);
   if(gadget.header.np[4] > 0) fprintf(stderr,"    stars:  tot_mass[4]=%g (%g)\n",tot_mass[4],tot_mass[4]/(double)gadget.header.np[4]);
   if(gadget.header.np[5] > 0) fprintf(stderr,"    bndry:  tot_mass[5]=%g (%g)\n",tot_mass[5],tot_mass[5]/(double)gadget.header.np[5]);
   
   
   /* single GADGET file => determine mmin */
   if(gadget.no_gadget_files == 1)
     {
      if(gadget.header.massarr[1] < ZERO)
         mmin = gadget.header.massarr[1];
      else
        {
         mmin = 1e30;
         for(k=gadget.header.np[0]; k<gadget.header.np[0]+gadget.header.np[1]; k++)
           {
            if(P_gadget[k].Mass < mmin)
               mmin = P_gadget[k].Mass;
           }
        }
      gadget.mmin = mmin;
     }
   
   fprintf(stderr,"\nFinished assigning GADGET masses (mmin=%g)\n",gadget.mmin);   
   
   
   /*====================================== AMIGA header ======================================*/
   /* count *all* particles in GADGET run (no_part = # of part's in current file...) */
   io.header.no_part = 0;
   for(i=0;i<6;i++)
      io.header.no_part+=gadget.header.nall[i];
   
   io.header.pmass     = gadget.mmin    * GADGET_MUNIT;
   io.header.boxsize   = gadget.header.BoxSize * GADGET_LUNIT;
   io.header.omega0    = gadget.header.Omega0;
   io.header.lambda0   = gadget.header.OmegaLambda;
   io.header.a_current = 1.0/(1.0+gadget.header.redshift);  
   io.header.a_initial = 1.0/(1.0+gadget.header.redshift);  /* only correct for IC data */
   
   simu.no_part          = io.header.no_part;
   simu.boxsize          = gadget.header.BoxSize*GADGET_LUNIT;
   simu.omega0           = gadget.header.Omega0;
   simu.lambda0          = gadget.header.OmegaLambda;
   simu.a_initial        = 1.0/(1.0+gadget.header.redshift);
   simu.z_initial        = gadget.header.redshift;
   simu.t_initial        = calc_t(simu.a_initial);
   energy.K_initial      = 0.0;
   energy.U_initial      = 0.0;
   energy.K_current      = 0.0;
   energy.U_current      = 0.0;
   energy.integral       = 0.0;
   energy.econst         = 0.0;
   energy.aold           = 1.0/(1.0+gadget.header.redshift);
#ifdef ENERGY
   energy.echeck      = 0.0;
#endif
   
#ifdef MULTIMASS
   if(gadget.i_gadget_file == 0)
      simu.no_vpart   = (double)0.0;
   simu.max_weight = (double)0.0;
   no_species      = 0;
   fweight         = 0.0;
   fweight_old     = 0.0;
#endif
   
   /*====================================== AMIGA particles ======================================*/
   /* transfer to AMIGA particle arrays */
   x_fac  = 1.0/io.header.boxsize;
   v_fac  = sqrt(io.header.a_current) * io.header.a_current / (io.header.boxsize * H0);
   m_fac  = 1.0/gadget.mmin;
   
#ifndef GADGET_IGNORE_IDS
   /* single GADGET file => determine minimum/maximum halo particle ID */
   if(gadget.no_gadget_files == 1)
     {
      IDmin = 1000000000;
      IDmax = 0;
      for(i=gadget.header.np[0]; i<gadget.header.np[0]+gadget.header.np[1]; i++)
        {
         if(P_gadget[i].ID < IDmin)
            IDmin = P_gadget[i].ID;
         
         if(P_gadget[i].ID > IDmax)
            IDmax = P_gadget[i].ID;
        }         
      
      fprintf(stderr,"\nUsing GAGDET's halo particle id's from %d to %d\n",IDmin,IDmax);
      
      if(IDmax-IDmin+1 != gadget.header.nall[1])
        {
         fprintf(stderr,"\nPROBLEM:  halo partice ID's are not consecutive from IDmin (%d) to IDmax (%d) [%d]\n\n",IDmin,IDmax,gadget.header.nall[1]);
         fprintf(stderr,"                     please re-compile with -DGADGET_IGNORE_IDS\n\n");
         exit(0);
        }
      
      gadget.IDmin = IDmin;
      gadget.IDmax = IDmax;
     }
#endif
   
   /* allocate memory for all GADGET particles across all files */
   if(gadget.i_gadget_file == 0)
     {
      global.fst_part = c_part(simu.no_part);
#ifdef GADGET_IGNORE_IDS
      cur_part = global.fst_part;
#endif
     }
#ifdef GADGET_IGNORE_IDS
   else
     {
      cur_part = gadget.lst_part+1;
     }
#endif
   
   /*--------------------------
      * loop over all particles
      *--------------------------*/
   ppart = 0;
   for(i=0; i<6; i++)
     {
      for(j=0; j<gadget.header.np[i]; j++)
        {
#ifndef GADGET_IGNORE_IDS
         /* always use same halo particle ID's to allow for identification across multiple output files */
         if(i == 1)
           {
            ID       = (long) P_gadget[ppart].ID - gadget.IDmin;
            cur_part = global.fst_part + ID;
           }
         
         /* manually assign ID's for gas, stars, etc. (not consistent across different snapshots!) */
         else
           {
            /* jump past all halo particle ID's */
            ipart = gadget.IDmax-gadget.IDmin+1;
            
            /* jump past all other species ID's */
            for(k=0; k<i; k++)
               /* do not count nall[1] twice! */
               if(k != 1)
                  ipart += gadget.header.nall[k];
            
            /* take care of particles across P-GADGET output files */
            for(k=0; k<gadget.i_gadget_file; k++)
               ipart += k*gadget.np[i][k];
            
            /* ...and finally assign an ID to the current particle */
            ipart += j;
            
            cur_part = global.fst_part + ipart;
           }
#endif         
         /* position and velocity */
         for(k = X; k <= Z; k++)
           {
            cur_part->pos[k] = (float)fmod(P_gadget[ppart].Pos[k] * x_fac + 1.0, 1.0);
            cur_part->mom[k] = (float)    (P_gadget[ppart].Vel[k] * v_fac);
           }
         
         /* mass */
#ifdef MULTIMASS
         if(P_gadget[ppart].Mass < ZERO)
           {
            fprintf(stderr,"zero mass GADGET particle (%ld): %g (setting to min. particle mass %g)\n",ppart,P_gadget[ppart].Mass,gadget.mmin);
            P_gadget[ppart].Mass = gadget.mmin;
           }
         cur_part->weight = P_gadget[ppart].Mass * m_fac;
         simu.no_vpart   += (double)cur_part->weight;
         
         /* keep track of number of species (assumes "mass ordered" particles!) */
         fweight = cur_part->weight;
         if(fabs(fweight-fweight_old) > ZERO && fweight > ZERO)
           {
            no_species += 1;
            fweight_old = fweight;
            
            if(fweight > simu.max_weight) 
               simu.max_weight = fweight;
           }
#endif /* MULTIMASS */
         
         ppart++;
         
#ifdef GADGET_IGNORE_IDS
         cur_part++;
#endif
        } /* j<np[i] */

     } /* i=0,5 */
   
#ifdef MULTIMASS
   simu.no_species = no_species;
#else
   simu.no_species = 1;
   simu.no_vpart   = simu.no_part;
#endif
   
   fprintf(stderr,"\npreliminary values:\n",simu.no_species);
   fprintf(stderr,"no_part (%2d)    = %ld (=? %d)\n",gadget.i_gadget_file,no_part,ppart);
   fprintf(stderr,"simu.no_species = %d\n",simu.no_species);
   fprintf(stderr,"simu.no_part    = %ld\n",simu.no_part);
   fprintf(stderr,"simu.no_vpart   = %g\n",simu.no_vpart);
   fprintf(stderr,"================== FINISHED READING GADGET DATA ==================\n");

#ifdef GADGET_IGNORE_IDS
   gadget.lst_part = cur_part-1;
#endif

   /* now it's time to free GADGET's P array */
   free(P_gadget);
}
#endif /* GADGET */

/*==============================================================================
* read ART binary data
*==============================================================================*/
#ifdef ART
void read_ARTbinaries(char *PMcrd, char *PMcrs);

read_art(char *PMcrs)
{
   char   PMcrd[MAXSTRING];
   int    ilen, i, j;
   
#ifdef VERBOSE
#ifdef BYTESWAP
   fprintf(stderr," reading BYTESWAPed ART data:\n");
#else
   fprintf(stderr," reading ART data:\n");
#endif
#endif /* VERBOSE */
   
   
   /* prepare ART filename PMcrd.DAT filename */
   ilen = strlen(PMcrs);
   for(i=ilen-1; i>=0; i--)
      if(PMcrs[i] == 's')
         break;
   for(j=0; j<i; j++)
      PMcrd[j] = PMcrs[j];
   PMcrd[i] = 'd';
   for(j=i+2; j<ilen; j++)
      PMcrd[j-1] = PMcrs[j];
   PMcrd[j-1] = '\0';
   
   
   fprintf(stderr,"   %s\n",PMcrd);
   fprintf(stderr,"   %s\n",PMcrs);
   
   /* finally read binaries */
   read_ARTbinaries(PMcrd, PMcrs);
}

void read_ARTbinaries(char *PMcrd, char *PMcrs)
{
   FILE *fPMcrd, *fPMcrs;
   
   int   i;
   char  c;
   
   float *recdat;
   float xpar, ypar, zpar;
   float vxpar,vypar,vzpar;
   float *wspecies;
   int   *lspecies;
   int   nspecies;
   long   npart;
   double nvpart;
   float partw, boxsize;
   
   int   NROW, NGRID, NPAGE, NRECL;
   
   double x_fac, v_fac;
   
   int   Npages, N_in_last, N_particles, IROW, In_page, IN, Icount, Ipart, iL;
   float w;
   
   partptr cur_part;
   
   /*==========================
      * opening both input files
      *==========================*/
   if((fPMcrd = fopen(PMcrd,"rb")) == NULL)
     {
      fprintf(stderr,"read_ART: could not open %s\n",PMcrd);
      exit(0);
     }
   if((fPMcrs = fopen(PMcrs,"rb")) == NULL)
     {
      fprintf(stderr,"read_ART: could not open %s\n",PMcrs);
      exit(0);
     }
   
   
   /*=============================
      * read ART header information
      *=============================*/
   ART_SKIP(fPMcrd);
   for(i=0; i<45; i++)
     {
      fread(&c, sizeof(char), 1, fPMcrd);
      strcpy(&(ART_header.header_string[i]), &c);
     }
   fread(&ART_header.aexpn,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.aexp0,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.amplt,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.astep,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.istep,    sizeof(int),   1, fPMcrd);
   fread(&ART_header.partw,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.tintg,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.ekin,     sizeof(float), 1, fPMcrd);
   fread(&ART_header.ekin1,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.ekin2,    sizeof(float), 1, fPMcrd);
   fread(&ART_header.au0,      sizeof(float), 1, fPMcrd);
   fread(&ART_header.aeu0,     sizeof(float), 1, fPMcrd);
   fread(&ART_header.nrowc,    sizeof(int),   1, fPMcrd);
   fread(&ART_header.ngridc,   sizeof(int),   1, fPMcrd);
   fread(&ART_header.nspecies, sizeof(int),   1, fPMcrd);
   fread(&ART_header.nseed,    sizeof(int),   1, fPMcrd);
   fread(&ART_header.Om0,      sizeof(float), 1, fPMcrd);
   fread(&ART_header.Oml0,     sizeof(float), 1, fPMcrd);
   fread(&ART_header.hubble,   sizeof(float), 1, fPMcrd);
   fread(&ART_header.wp5,      sizeof(float), 1, fPMcrd);
   fread(&ART_header.Ocurv,    sizeof(float), 1, fPMcrd);
   for(i=0; i<100; i++)
      fread(&(ART_header.extras[i]), sizeof(float), 1, fPMcrd);
   
#ifdef BYTESWAP
   sexchange(&ART_header.aexpn,    sizeof(float));
   sexchange(&ART_header.aexp0,    sizeof(float));
   sexchange(&ART_header.amplt,    sizeof(float));
   sexchange(&ART_header.astep,    sizeof(float));
   sexchange(&ART_header.istep,    sizeof(int));
   sexchange(&ART_header.partw,    sizeof(float));
   sexchange(&ART_header.tintg,    sizeof(float));
   sexchange(&ART_header.ekin,     sizeof(float));
   sexchange(&ART_header.ekin1,    sizeof(float));
   sexchange(&ART_header.ekin2,    sizeof(float));
   sexchange(&ART_header.au0,      sizeof(float));
   sexchange(&ART_header.aeu0,     sizeof(float));
   sexchange(&ART_header.nrowc,    sizeof(int));  
   sexchange(&ART_header.ngridc,   sizeof(int));  
   sexchange(&ART_header.nspecies, sizeof(int));  
   sexchange(&ART_header.nseed,    sizeof(int));  
   sexchange(&ART_header.Om0,      sizeof(float));
   sexchange(&ART_header.Oml0,     sizeof(float));
   sexchange(&ART_header.hubble,   sizeof(float));
   sexchange(&ART_header.wp5,      sizeof(float));
   sexchange(&ART_header.Ocurv,    sizeof(float));
   for(i=0; i<100; i++)
      sexchange(&ART_header.extras[i], sizeof(float));
#endif
   
   /*==========================
      * dump some info to screen
      *==========================*/
   fprintf(stderr,"%s\n",ART_header.header_string);
   fprintf(stderr,"aexpn    =   %g\n",ART_header.aexpn);
   fprintf(stderr,"aexp0    =   %g\n",ART_header.aexp0);
   fprintf(stderr,"astep    =   %g\n",ART_header.astep);
   fprintf(stderr,"istep    =   %d\n",ART_header.istep);
   fprintf(stderr,"Om0      =   %g\n",ART_header.Om0);
   fprintf(stderr,"Oml0     =   %g\n",ART_header.Oml0);
   fprintf(stderr,"hubble   =   %g\n",ART_header.hubble);
   fprintf(stderr,"nspecies =   %d\n",ART_header.nspecies);
   fprintf(stderr,"boxsize  =   %g\n",ART_header.extras[99]);
   fprintf(stderr,"NROW     =   %d\n",ART_header.nrowc);
   fprintf(stderr,"NGRID    =   %d\n",ART_header.ngridc);
   
   fclose(fPMcrd);
   
#ifndef MULTIMASS
   if(nspecies > 1)
     {
      fprintf(stderr,"Please compile read_art with -DMULTIMASS\n");
      exit(0);
     }
#endif
   
   /*=====================
      * declare some things
      *=====================*/
   NGRID    = ART_header.ngridc;
   NROW     = ART_header.nrowc;
   NPAGE    = pow2(NROW);
   NRECL    = 6*NPAGE;
   nspecies = ART_header.nspecies;
   partw    = ART_header.partw;
   boxsize  = ART_header.extras[100-1];
   
   x_fac    = 1.0/(double)NGRID;
   v_fac    = 1.0/(double)NGRID;
   
   wspecies =       &(ART_header.extras[0]);
   lspecies = (int*)&(ART_header.extras[10]);
   
   if(nspecies == 0)
     {
      N_particles = pow3(NROW);
      Npages      = NROW;
      N_in_last   = NPAGE;
     }
   else
     {
      N_particles =  lspecies[nspecies-1];
      Npages      = (N_particles -1)/NPAGE +1;
      N_in_last   =  N_particles -NPAGE*(Npages-1);
     }
   
   fprintf(stderr,"no_part  =   %d\n\n", N_particles);
   
   /*==================
      * read actual data
      *==================*/
   global.fst_part = c_part(N_particles);
   cur_part        = global.fst_part;
   recdat          = (float *) malloc(NRECL*sizeof(float));
   
   for(IROW=0; IROW<Npages; IROW++)
     {
      In_page = NPAGE;
      
      if(IROW == Npages-1)
         In_page = N_in_last;
      
      fprintf(stderr," o reading page %10d:  %10d   %10d\n",IROW,In_page,Npages);
      
      iL = NPAGE*IROW;
      fread(&recdat[0], sizeof(float), NRECL, fPMcrs);
      
#ifdef BYTESWAP
      for(i=0; i<NRECL; i++)
         sexchange(&recdat[i], sizeof(float));
#endif
      
      for(IN=0; IN<In_page; IN++)
        {
         xpar  = recdat[        IN];
         ypar  = recdat[  NPAGE+IN];
         zpar  = recdat[2*NPAGE+IN];
         vxpar = recdat[3*NPAGE+IN];
         vypar = recdat[4*NPAGE+IN];
         vzpar = recdat[5*NPAGE+IN];
         
#ifdef DEBUG_ART
         fprintf(stderr,"%d  %12.8g %12.8g %12.8g  %12.8g %12.8g %12.8g (%12.8g)\n",
                 IN,xpar,ypar,zpar,vxpar,vypar,vzpar,recdat[IN]);
#endif
         
         cur_part->pos[X] = x_fac * (xpar-1.);
         cur_part->pos[Y] = x_fac * (ypar-1.);
         cur_part->pos[Z] = x_fac * (zpar-1.);
         cur_part->mom[X] = v_fac * (vxpar);
         cur_part->mom[Y] = v_fac * (vypar);
         cur_part->mom[Z] = v_fac * (vzpar);
         
         if(nspecies == 0)
            w = partw;
         else
           {
            Ipart = IN+iL;
            for(i=0; i<nspecies; i++)
              {
               if(Ipart < lspecies[i])
                 {
                  w = wspecies[i];
                  break;
                 }
              }
           }
         
#ifdef MULTIMASS
         cur_part->weight = w;
#endif
         
         Icount++;
         cur_part++;
        }
     }
   
   fclose(fPMcrs);
   
   fprintf(stderr,"finished reading ART data\n");
   free(recdat);
   
   
   /*===================
      * fill AMIGA header
      *===================*/
   strcpy(io.header.header,ART_header.header_string);
   io.header.no_part   = N_particles;
   io.header.no_species= nspecies;
   io.header.pmass     = ART_header.extras[59-1];
   io.header.boxsize   = ART_header.extras[100-1];
   io.header.omega0    = ART_header.Om0;
   io.header.lambda0   = ART_header.Oml0;
   io.header.a_current = ART_header.aexpn;  
   io.header.a_initial = ART_header.aexp0;
   
   global.a           = io.header.a_current;
   global.z           = (double)1.0/io.header.a_current - (double)1.0;
   
   simu.no_part          = N_particles;
   simu.pmass            = io.header.pmass;
   simu.boxsize          = io.header.boxsize;
   simu.omega0           = io.header.omega0;
   simu.lambda0          = io.header.lambda0;
   simu.a_initial        = io.header.a_initial;
   simu.z_initial        = 1./io.header.a_initial - 1.;
   simu.t_initial        = calc_t(simu.a_initial);
   
#ifdef MULTIMASS
   npart  = 0;
   nvpart = 0.;
   fprintf(stderr,"\nmass species in ART file:\n");
   for(i=0; i<10; i++)
     {
      nvpart += (double)(lspecies[i]-npart)*wspecies[i];
      if(wspecies[i] > 0)
         fprintf(stderr,"%12d %12.8g %16.4g\n",lspecies[i]-npart,wspecies[i],nvpart);
      npart   = lspecies[i];
     }
   
   io.header.no_vpart   = (double)nvpart;
   simu.no_vpart        = (double)nvpart;
   simu.max_weight      = (double)wspecies[0];
   simu.no_species      = nspecies;
   simu.pmass           = simu.omega0 * rhoc0 * pow3(simu.boxsize)/simu.no_vpart;
   io.header.multi_mass = 1;
#else
   io.header.no_vpart   = (double)N_particles;
   simu.no_vpart        = (double)N_particles;
   simu.max_weight      = (double)wspecies[0];
   simu.no_species      = 1;
   simu.pmass           = simu.omega0 * rhoc0 * pow3(simu.boxsize)/simu.no_part;
   io.header.multi_mass = 0;
#endif
}
#endif


/*==============================================================================
* wrapper for call to appropriate binary reading routine
*==============================================================================*/
void read_simu(char *filename)
{   
#ifdef MLAPM
   read_mlapm(filename);
#else /* MLAPM not defined ... but maybe GADGET ?! */
#ifdef GADGET
   read_gadget(filename);
#else /* GADGET not defined ... but maybe ART ?! */
#ifdef ART
   read_art(filename);
#else
   read_amiga(filename);
#endif /* ART */
#endif /* GADGET */
#endif /* MLAPM */
}


/*==============================================================================
 * VelEllipsoid()
 *
 * input:
 *          fpout                           = file pointer to output file
 *          mhalo                           = id of halo currently working with
 *          npart                           = number of particles
 *          vx[npart], vy[npart], vz[npart] = velocities of those particles
 *
 * output:
 *          Sv_tensor[3][3]                 = velocity dispersion tensor
 *          Sv_xx                           = 1. eigenvalue of that tensor
 *          Sv_yy                           = 2. eigenvalue of that tensor
 *          Sv_zz                           = 3. eigenvalue of that tensor
 *
 *==============================================================================*/
void VelEllipsoid(FILE *fpout, long mhalo, long npart, float *vx, float *vy, float *vz,
                  double Sv_tensor[3][3], double *Sv_xx, double *Sv_yy, double *Sv_zz)
{
   long ipart, i, j;
   
   double  sumv1,  sumv2,  sumv3;
   double meanv1, meanv2, meanv3;
   
   
   /*calculate the mean value for each velocity component*/
   sumv1 = 0.0;
   sumv2 = 0.0;
   sumv3 = 0.0;
   for(ipart=0; ipart<npart; ipart++)
     {
      sumv1 += vx[ipart];
      sumv2 += vy[ipart];
      sumv3 += vz[ipart];
     }
   meanv1 = sumv1 / (double)npart;
   meanv2 = sumv2 / (double)npart;
   meanv3 = sumv3 / (double)npart;
   
   fprintf(stderr,"      velocity means               => <v1>=%7.3g\t<v2>=%7.3g\t<v3>=%7.3g\n",meanv1,meanv2,meanv3);

   /*calculate the velocity dispersion tensor*/
   for(i=0; i<3; i++)
      for(j=0; j<3; j++)
         Sv_tensor[i][j] = 0.0;

   for(ipart = 0; ipart < npart; ipart++)
	  {
		Sv_tensor[0][0] += (vx[ipart] - meanv1)*(vx[ipart] - meanv1);
		Sv_tensor[1][1] += (vy[ipart] - meanv2)*(vy[ipart] - meanv2);
		Sv_tensor[2][2] += (vz[ipart] - meanv3)*(vz[ipart] - meanv3);
      
		Sv_tensor[0][1] += (vx[ipart] - meanv1)*(vy[ipart] - meanv2);
		Sv_tensor[1][0] += (vx[ipart] - meanv1)*(vy[ipart] - meanv2);
      
		Sv_tensor[0][2] += (vx[ipart] - meanv1)*(vz[ipart] - meanv3);
		Sv_tensor[2][0] += (vx[ipart] - meanv1)*(vz[ipart] - meanv3);

		Sv_tensor[1][2] += (vy[ipart] - meanv2)*(vz[ipart] - meanv3);
		Sv_tensor[2][1] += (vy[ipart] - meanv2)*(vz[ipart] - meanv3);
	  }
   
   for(i=0; i<3; i++)
      for(j=0; j<3; j++)
         Sv_tensor[i][j] = (Sv_tensor[i][j]/(double)npart);

   fprintf(stderr,"      velocity dispersion tensor   =>  %8.2lf   %8.2lf   %8.2lf\n",
           Sv_tensor[0][0],Sv_tensor[0][1],Sv_tensor[0][2]);
   fprintf(stderr,"                                       %8.2lf   %8.2lf   %8.2lf\n",
           Sv_tensor[1][0],Sv_tensor[1][1],Sv_tensor[1][2]);
   fprintf(stderr,"                                       %8.2lf   %8.2lf   %8.2lf\n",
           Sv_tensor[2][0],Sv_tensor[2][1],Sv_tensor[2][2]);
   
   /* call utility.c routine to diagonlize tensor */
   get_axes(Sv_tensor, Sv_xx, Sv_yy, Sv_zz);
   
   fprintf(stderr,"       => eigenvalues of velocity dispersion tensor: Sv_xx=%8.2lf   Sv_yy=%8.2lf   Sv_zz=%8.2lf\n\n",
           *Sv_xx, *Sv_yy, *Sv_zz);
   
   
   /* eventually dump relevant information to output file */
   fprintf(fpout,"%10ld     %12.5lf %12.5lf %12.5lf %12.5lf      %12.5lf %12.5lf %12.5lf %12.5lf      %12.5lf %12.5lf %12.5lf %12.5lf\n",
           mhalo, 
           *Sv_xx, Sv_tensor[0][0], Sv_tensor[1][0], Sv_tensor[2][0],
           *Sv_yy, Sv_tensor[0][1], Sv_tensor[1][1], Sv_tensor[2][1], 
           *Sv_zz, Sv_tensor[0][2], Sv_tensor[1][2], Sv_tensor[2][2]);
}

