/*==================================================================================================
 *  MergerTree:   Merger Tree for BDM/AHF output
 *
 *
 *  input:    - how often to perform
 *            - 2x _particles files
 *
 *  output:   - 1x _mtree file
 *
 *
 * it is checked what halos in file2 make up the halos in file1, i.e.
 *
 *   file1   file2
 *
 *    0        0
 *    0       17
 *    0       31    -> halo #0 in file1 shares particles with halos #0,17,31 in file2
 *    1        2
 *    1       12
 *    1        4    -> halo #1 in file1 shares particles with halos #2,12,4  in file2
 *       etc.
 *
 *==================================================================================================*/

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

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

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

#define MINCOMMON     10
#define MHOSToverMSAT 10

#undef  PARTICLES_INFO
#define MTREE_SELF
#define CLUES

/*-------------------------------------------------------------------------------------
 *                                  THE STRUCTURES
 *-------------------------------------------------------------------------------------*/
typedef struct HALOS *HALOptr;
typedef struct HALOS
  {
    long   npart;
    long  *Pid;
  }HALOS;

typedef struct PARTS *PARTptr;
typedef struct PARTS
  {
    long  nhalos;
    long *Hid;
  }PARTS;

typedef struct MTREE *MTREEptr;
typedef struct MTREE
  {
    long unsigned id[2];
    long unsigned npart[2];
    long unsigned common;
  } MTREE;

/*-------------------------------------------------------------------------------------
 *                                 GLOBAL VARIABLES
 *-------------------------------------------------------------------------------------*/

HALOptr halos[2];
PARTptr parts[2];
long    nHalos[2];
long    PidMax=-1;
long    PidMin=1234567890;

MTREE *mtree;                // a memory copy of the actual *_mtree file
long unsigned  nlines;       // number of lines in *_mtree file
long unsigned  nstree;       // for how many halos to write *_stree file
long          *ihost;        // assigns one host to each halo

/*-------------------------------------------------------------------------------------
 *                                 ALL THOSE FUNCTIONS
 *-------------------------------------------------------------------------------------*/
int  create_mtree           (char HaloFile0[MAXSTRING], char HaloFile1[MAXSTRING], char OutFile[MAXSTRING]);
int  create_stree           (char OutFile[MAXSTRING]);
int  read_particles         (char filename[MAXSTRING], int isimu);
int  particle_halo_mapping  (int  isimu);
int  cross_correlation      (char OutFile[MAXSTRING]);
int  assign_progenitor      (char OutFile[MAXSTRING]);
int  assign_progenitors     (char OutFile[MAXSTRING]);
void read_mtree             (char *infile);
void assign_host            ();
void write_stree            (char *outfile);

/*==================================================================================================
 * main:
 *
 *       simply a wrapper for successive calls to create_mtree()
 *
 *==================================================================================================*/
int main()
{
  int    i, nFiles, isimu, ihalo, ipart;
  char **HaloFile;
  char **OutFile, streeFile[MAXSTRING];
  
  /*==================================================================*
   *                          USER INTERFACE                          *
   *==================================================================*/
  printf("=======================================================================\n");
  printf("  construct a cross-correlation between consecutive *_particles files\n");
  printf("=======================================================================\n");
  printf("\nPlease give number of particles files (default=2):      ");
  scanf("%d", &nFiles);
  printf("%d\n",nFiles);
  
  /* allocate memory for nFiles filenames, each of size MAXSTRING */
  for(i=0; i<nFiles; i++)
    {
      HaloFile = (char **) calloc(nFiles, sizeof(char *));
      OutFile  = (char **) calloc(nFiles, sizeof(char *));
    }
  for(i=0; i<nFiles; i++)
    {
      HaloFile[i] = (char *) calloc(MAXSTRING, sizeof(char));
      OutFile[i]  = (char *) calloc(MAXSTRING, sizeof(char));
    }
  
  /* read input filenames from stdin */
  for(i=0; i<nFiles; i++)
    {
      printf("Please give name of %5d. *_particles file:            ",i+1);
      scanf("%s", HaloFile[i]);
      printf("%s\n",HaloFile[i]);
    }
  
  /* read output filenames from stdin */
#ifdef MTREE_SELF
  for(i=0; i<nFiles; i++)
#else
  for(i=0; i<nFiles-1; i++)
#endif
    {
      printf("Please give name of %5d. output file:                 ",i+1);
      scanf("%s", OutFile[i]);
      printf("%s\n\n",OutFile[i]);
    }
  
  
  /*======================================================================*
   *  CREATE CROSS-CORRELATION BETWEEN TWO CONSECUTIVE *_PARTICLES FILES  *
   *======================================================================*/
#ifdef MTREE_SELF
  for(i=0; i<nFiles; i++)
#else
  for(i=0; i<nFiles-1; i++)
#endif
     {
      /* mtree[] is filled by assign_progenitor()! */
      mtree = (MTREEptr) calloc(1, sizeof(MTREE));
      
#ifdef MTREE_SELF
      create_mtree(HaloFile[i],HaloFile[i],OutFile[i]);
      create_stree(OutFile[i]);
#else
      create_mtree(HaloFile[i],HaloFile[i+1],OutFile[i]);

      /* generate substructure tree? */
      if(strcmp(HaloFile[i],HaloFile[i+1]) == 0)
        create_stree(OutFile[i]);
#endif
      
      
      for(isimu=0; isimu<2; isimu++)
        {
          for(ihalo=0; ihalo<nHalos[isimu]; ihalo++)
              if(halos[isimu][ihalo].Pid != NULL) free(halos[isimu][ihalo].Pid);
          for(ipart=0; ipart<PidMax; ipart++)
              if(parts[isimu][ipart].Hid != NULL) free(parts[isimu][ipart].Hid);
          free(halos[isimu]);
          free(parts[isimu]);
        }
      free(mtree);
    }
  
  printf("STOP\n");
  return(1);
}


/*==================================================================================================
 * create_mtree:
 *
 *  generates the cross-correlation between two halo analysis
 *
 *  the correlation is done based upon the individual particle contents of halos
 *  and hence only requires the *_particles files...
 *
 *==================================================================================================*/
int create_mtree(char HaloFile0[MAXSTRING], char HaloFile1[MAXSTRING], char OutFile[MAXSTRING])
{
  long ihalo, jhalo;
  
  /* read in the two *_particle files to be correlated */
#ifdef CLUES
  read_particlesCLUES(HaloFile0, 0);
  read_particlesCLUES(HaloFile1, 1);
#else
  read_particles(HaloFile0, 0);
  read_particles(HaloFile1, 1);
#endif
  
  particle_halo_mapping(0);
  particle_halo_mapping(1);
  
  cross_correlation(OutFile);
  
  assign_progenitor(OutFile);
  //assign_progenitors(OutFile); //dumps information about progenitors (coded for Cesar!)
  
  return(1);
}

/*==================================================================================================
 * read_particles:
 *
 *  simply read a *_particles file and initialize
 *
 *      nHalos = number of halos found in file
 *      Pid    = id's of all those particles
 *
 *==================================================================================================*/
int read_particles(char filename[MAXSTRING], int isimu)
{
  FILE *fpin;
  char  line[MAXSTRING];
  long  nPartInHalo, nPartInUse, ipart, jpart, ihalo, Pid, Ptype, numGoodHalos;
  
  fprintf(stderr,"o reading file %s ...",filename);
  
  fpin = fopen(filename,"r");
  if(fpin == NULL)
    {
      fprintf(stderr,"could not open file %s\nexiting!\n",filename);
      exit(0);
    }
  
  /* reset all variables */
  nHalos[isimu] = 0;
  ihalo         = -1;
  halos[isimu]   = NULL;
  
#ifdef PARTICLES_INFO
  /* if AHF was compiled with this flag the first line is "numGoodHaloes" */
  fgets(line,MAXSTRING,fpin);
#endif
  
  while( fgets(line,MAXSTRING,fpin) != NULL)
    {
      /* how many particles are in this halo? */
      sscanf(line,"%ld",&nPartInHalo);
      
      /* found yet another halo */
      ihalo++;
      nHalos[isimu] += 1;
      halos[isimu]    = (HALOptr) realloc(halos[isimu], nHalos[isimu]*sizeof(HALOS));
      
      /* store npart and allocate Pid[] accordingly */
      halos[isimu][ihalo].Pid   = (long *) calloc(nPartInHalo, sizeof(long));
      
      /* read all their id's */
      nPartInUse = 0;
      for(ipart=0; ipart<nPartInHalo; ipart++)
        {
          fgets(line,MAXSTRING,fpin);
          sscanf(line,"%ld %ld",&Pid,&Ptype);
          
          // restrict analysis to gas and stars only (1==stars, 2==gas, 3==DM, 4==gas for Cesar's ID files...)
          //if(Ptype == 1 || Ptype == 2 || Ptype == 4)
          //if(Ptype == 3)
            {
              halos[isimu][ihalo].Pid        = (long *) realloc(halos[isimu][ihalo].Pid, (nPartInUse+1)*sizeof(long));

              halos[isimu][ihalo].Pid[nPartInUse] = Pid;
          
              if(abs(halos[isimu][ihalo].Pid[nPartInUse]) > PidMax) PidMax = abs(halos[isimu][ihalo].Pid[nPartInUse]);
              if(abs(halos[isimu][ihalo].Pid[nPartInUse]) < PidMin) PidMin = abs(halos[isimu][ihalo].Pid[nPartInUse]);
              
              nPartInUse++;
            }
        }
      
      /* store number of particles in halo */
      halos[isimu][ihalo].npart = nPartInUse;

      
      if(nPartInUse == 0)
        {
          fprintf(stderr,"  => halo %ld of simu %ld contains %ld particles, yet you restricted it to %ld particles only\n",
                  ihalo,isimu,nPartInHalo,nPartInUse);
          exit(0);
        }
    }
  
  fclose(fpin);
  
  fprintf(stderr," done (ID range = %ld -> %ld)\n",PidMin,PidMax);
  
  return(1);
}

/*==================================================================================================
 * read_particlesCLUES:
 *
 *  simply read a *_particles file and initialize
 *
 *      nHalos = number of halos found in file
 *      Pid    = id's of all those particles
 *
 *==================================================================================================*/
int read_particlesCLUES(char filename[MAXSTRING], int isimu)
{
  FILE *fpin;
  char  line[MAXSTRING];
  long  nPartInUse, ipart, jpart, ihalo, Ptype;
  long  nPartInHalo;
  int   Pid;
  int   bytesint, bytesfloat;
  long  totNhalos, totNpart;
  int   ncols, icol;
  char  ccol;
  
  fprintf(stderr,"o reading file %s ...",filename);
  
  fpin = fopen(filename,"rb");
  if(fpin == NULL)
   {
    fprintf(stderr,"could not open file %s\nexiting!\n",filename);
    exit(0);
   }
  
  /* reset all variables */
  nHalos[isimu] = 0;
  ihalo         = -1;
  halos[isimu]   = NULL;
  
  fread(&bytesint,     sizeof(int),   1, fpin);
  fread(&bytesfloat,   sizeof(int),   1, fpin);
  fread(&totNhalos,    sizeof(long),  1, fpin);
  fread(&totNpart,     sizeof(long),  1, fpin);
  fread(&ncols,        sizeof(int),   1, fpin);
  for(icol=0; icol<ncols; icol++)
    fread(&ccol,       sizeof(char),   1, fpin);
    
  
  while(!feof(fpin))
   {
    fread(&nPartInHalo, sizeof(long), 1, fpin);
    
    /* found yet another halo */
    ihalo++;
    nHalos[isimu] += 1;
    halos[isimu]    = (HALOptr) realloc(halos[isimu], nHalos[isimu]*sizeof(HALOS));
    
    /* store npart and allocate Pid[] accordingly */
    halos[isimu][ihalo].Pid   = (long *) calloc(nPartInHalo, sizeof(long));
    
    /* read all their id's */
    nPartInUse = 0;
    for(ipart=0; ipart<nPartInHalo; ipart++)
     {
      fread(&Pid,   sizeof(int), 1, fpin);
      
      // restrict analysis to gas and stars only (1==stars, 2==gas, 3==DM, 4==gas for Cesar's ID files...)
      //if(Ptype == 1 || Ptype == 2 || Ptype == 4)
      //if(Ptype == 3)
      {
       halos[isimu][ihalo].Pid        = (long *) realloc(halos[isimu][ihalo].Pid, (nPartInUse+1)*sizeof(long));
       
       halos[isimu][ihalo].Pid[nPartInUse] = (long) Pid;
       
       if(abs(halos[isimu][ihalo].Pid[nPartInUse]) > PidMax) PidMax = abs(halos[isimu][ihalo].Pid[nPartInUse]);
       if(abs(halos[isimu][ihalo].Pid[nPartInUse]) < PidMin) PidMin = abs(halos[isimu][ihalo].Pid[nPartInUse]);
       
       nPartInUse++;
      }
     }
   
    /* store number of particles in halo */
    halos[isimu][ihalo].npart = nPartInUse;
    
    
    if(nPartInUse == 0)
     {
      fprintf(stderr,"  => halo %ld of simu %ld contains %ld particles, yet you restricted it to %ld particles only\n",
              ihalo,isimu,nPartInHalo,nPartInUse);
      exit(0);
     }
   }
  
  fclose(fpin);
  
  fprintf(stderr," done (ID range = %ld -> %ld)\n",PidMin,PidMax);
  
  return(1);
}

/*==================================================================================================
 * particle_halo_mapping:
 *
 *  for each particle remember to which halo(s) it belongs
 *
 *==================================================================================================*/
int particle_halo_mapping(int isimu)
{
  long ihalo, ipart, jpart;
  
  fprintf(stderr,"o creating particle<->halo mapping for file %d ...",isimu);
  
  parts[isimu] = (PARTptr) calloc(PidMax+1, sizeof(PARTS));
  
  /* recording every halo it belongs to */
  for(ihalo=0; ihalo<nHalos[isimu]; ihalo++)
    {
      for(jpart=0; jpart<halos[isimu][ihalo].npart; jpart++)
        {
          ipart = abs(halos[isimu][ihalo].Pid[jpart]);
          
          parts[isimu][ipart].nhalos++;
          parts[isimu][ipart].Hid = (long *) realloc(parts[isimu][ipart].Hid,
                                                    parts[isimu][ipart].nhalos*sizeof(long));
          
          parts[isimu][ipart].Hid[parts[isimu][ipart].nhalos-1] = ihalo;
        }
    }
  
  fprintf(stderr," done\n");
  return(1);
}

/*==================================================================================================
 * cross_correlation:
 *
 *  for each halo at isimu=0 figure out how many particles are in common with khalo at isimu=1
 *
 *==================================================================================================*/
int cross_correlation(char OutFile[MAXSTRING])
{
  long  ihalo, jhalo, khalo, ipart, jpart;
  long *common;
  FILE *fpout;
  
  fprintf(stderr,"o generating cross-correlation ...");
  
  fpout = fopen(OutFile,"w");
  if(fpout == NULL)
    {
      fprintf(stderr,"could not open file %s\nexiting\n",OutFile);
      exit(0);
    }
  
  for(ihalo=0; ihalo<nHalos[0]; ihalo++)
    {
      /* common records how many particles ihalo(isimu=0) has in common with khalo(isimu=1) */
      common = (long *) calloc(nHalos[1], sizeof(long));
      
      for(jpart=0; jpart<halos[0][ihalo].npart; jpart++)
        {
          ipart = halos[0][ihalo].Pid[jpart];
          
          /* ipart belongs to nhalos halos in isimu=1 */
          for(jhalo=0; jhalo<parts[1][ipart].nhalos; jhalo++)
            {            
              khalo          = parts[1][ipart].Hid[jhalo];
              common[khalo] += 1;
              
#ifdef DEBUG
              if(common[khalo] > halos[1][khalo].npart)
                {
                  long kpart;
                  int  ifound=0;
                  for(kpart=0; kpart<halos[1][khalo].npart; kpart++)
                    if(halos[1][khalo].Pid[kpart] == ipart)
                      {
                        ifound = 1;
                        fprintf(stderr,"found: ihalo=%ld khalo=%ld: %ld %ld\n",ihalo,khalo,ipart,halos[1][khalo].Pid[kpart]);
                      }
                }
#endif
            }
        }
      
      /* write info to file */
      for(khalo=0; khalo<nHalos[1]; khalo++)
        {
          if(common[khalo] > MINCOMMON)
            fprintf(fpout,"%12ld  %12ld  %12ld  %12ld  %12ld\n",
                    ihalo,
                    halos[0][ihalo].npart,
                    common[khalo],
                    khalo,
                    halos[1][khalo].npart);
        }
      
      free(common);
      common = NULL;
    }
  
  fclose(fpout);
  
  fprintf(stderr," done\n");
  return(1);
}


/*==================================================================================================
 * assign_progenitor:
 *
 *  assign a unique progenitor to each halo based upon common particles
 *
 *  update 10/10/2007:
 *       each subhalo shares the most particles with its host :-(
 *       -> tried to fix this issue..
 *
 *==================================================================================================*/
int assign_progenitor(char OutFile[MAXSTRING])
{
  FILE   *fpin, *fpout;
  char    OutFile_idx[MAXSTRING], line[MAXSTRING];
  long unsigned   id1, npart1, common, id2, npart2, iline;
  double  xcommon, xnpart1, xnpart2, ratio, ratio_max;
  long    prev_id1, iprog;
  
  fprintf(stderr,"o assigning progenitors ...");
  
  strcpy(OutFile_idx, OutFile);
  strcat(OutFile_idx, "_idx");
  
  
  fpin  = fopen(OutFile,    "r");
  
  /* open output file */
  fpout = fopen(OutFile_idx,"w");
  if(fpout == NULL)
    {
      fprintf(stderr,"could not open file %s\nexiting\n",OutFile_idx);
      exit(0);
    }
  
  /* read *_mtree file */
  read_mtree(OutFile);
  
  prev_id1  = mtree[0].id[0];  
  iprog     = 0;
  ratio_max = -1000.;
  
  for(iline=0; iline<nlines; iline++)
    {
      /* copy information from just read *_mtree file */
      id1    = mtree[iline].id[0];
      id2    = mtree[iline].id[1];
      npart1 = mtree[iline].npart[0];
      npart2 = mtree[iline].npart[1];
      common = mtree[iline].common;
      
      /* calculate progenitor criterion */
      /* on some machines (incl. my old PowerMac running Tiger) a long is only 4 bytes :-( */
      xcommon   = (double)common;
      xnpart1   = (double)npart1;
      xnpart2   = (double)npart2;
      ratio     = pow2(xcommon)/(xnpart1*xnpart2);
      
      /* is current line still for the same id1 halo? */
      if(id1 == prev_id1)
        {
          /*yes: is current id2 a more appropriate progenitor? */
          if(ratio > ratio_max)
            {
              ratio_max = ratio;
              iprog     = id2;
            }
        }
      else
        {
          /*no: dump the most appropriate progenitor to file */
          fprintf(fpout,"%ld %ld\n",prev_id1,iprog);
          
          /* we already read in the first progenitor for the next id1 halo... */
          prev_id1  = id1;
          iprog     = id2;
          
          /* calculate progenitor criterion */
          xcommon   = (double)common;
          xnpart1   = (double)npart1;
          xnpart2   = (double)npart2;
          ratio     = pow2(xcommon)/(xnpart1*xnpart2);
          ratio_max = ratio;
          
          //         fprintf(stderr,"%ld %ld %ld %ld %ld    %g\n",id1,npart1,common,id2,npart2,ratio);
          
        }      
    }
  
  fclose(fpin);
  fclose(fpout);
  
  fprintf(stderr," done\n");
  return(1);
}   

/*==================================================================================================
 * assign_progenitors:
 *
 *  get statistics for multiple progenitors (e.g. mass ratios, etc.)
 *
 *  update 10/10/2007:
 *       each subhalo shares the most particles with its host :-(
 *       -> tried to fix this issue..
 *
 *==================================================================================================*/
int assign_progenitors(char OutFile[MAXSTRING])
{
  FILE   *fpin, *fpout;
  char    OutFile_idx[MAXSTRING], line[MAXSTRING];
  long unsigned   *id1, *npart1, *common, *id2, *npart2, *idx, iline;
  double  xcommon, xnpart1, xnpart2, *ratio;
  long    prev_id1, nprog, iprog;
  
  fprintf(stderr,"o assigning progenitors ...");
  
  strcpy(OutFile_idx, OutFile);
  strcat(OutFile_idx, "_idx");
  
  
  fpin  = fopen(OutFile,    "r");
  
  /* open output file */
  fpout = fopen(OutFile_idx,"w");
  if(fpout == NULL)
    {
      fprintf(stderr,"could not open file %s\nexiting\n",OutFile_idx);
      exit(0);
    }
  
  /* read *_mtree file */
  read_mtree(OutFile);
  
  prev_id1  = mtree[0].id[0];  
  nprog     = 0;
  id1    = (long *) calloc(1, sizeof(long));
  id2    = (long *) calloc(1, sizeof(long));
  npart1 = (long *) calloc(1, sizeof(long));
  npart2 = (long *) calloc(1, sizeof(long));
  common = (long *) calloc(1, sizeof(long));
  
  for(iline=0; iline<nlines; iline++)
    {
      if(mtree[iline].id[0] == prev_id1)
        {
          /* make room for one more progenitor */
          nprog++;
          id1    = (long *) realloc(id1,    (nprog)*sizeof(long));
          id2    = (long *) realloc(id2,    (nprog)*sizeof(long));
          npart1 = (long *) realloc(npart1, (nprog)*sizeof(long));
          npart2 = (long *) realloc(npart2, (nprog)*sizeof(long));
          common = (long *) realloc(common, (nprog)*sizeof(long));
          
          /* copy information from mtree[] */
          id1[nprog-1]    = mtree[iline].id[0];
          id2[nprog-1]    = mtree[iline].id[1];
          npart1[nprog-1] = mtree[iline].npart[0];
          npart2[nprog-1] = mtree[iline].npart[1];
          common[nprog-1] = mtree[iline].common;
        }
      else
        {
          ratio = (double *)        calloc(nprog+1, sizeof(double));
          idx   = (long unsigned *) calloc(nprog+1, sizeof(long unsigned));
          
          for(iprog=0; iprog<nprog; iprog++)
            {
              /* calculate progenitor criterion */
              xcommon          = (double)common[iprog];
              xnpart1          = (double)npart1[iprog];
              xnpart2          = (double)npart2[iprog];
              ratio[iprog]     = pow2(xcommon)/(xnpart1*xnpart2);
            }
          indexx(nprog, ratio-1, idx-1);
          
          /* dump the most appropriate progenitors to file */
          if(nprog > 2)
            fprintf(fpout,"%ld %ld %ld %ld  %lf\n",prev_id1,id2[idx[nprog-1]-1],id2[idx[nprog-2]-1],id2[idx[nprog-3]-1],(double)common[idx[nprog-3]-1]/(double)common[idx[nprog-2]-1]);
          else if (nprog > 1)
            fprintf(fpout,"%ld %ld %ld  -1  0.0\n",prev_id1,id2[idx[nprog-1]-1],id2[idx[nprog-2]-1]);
          else
            fprintf(fpout,"%ld %ld -1   -1  0.0\n",prev_id1,id2[idx[nprog-1]-1]);
  
          free(ratio);
          free(idx);
          
          /* start a new progenitor list */
          free(id1);
          free(id2);
          free(npart1);
          free(npart2);
          free(common);
          
          nprog  = 1;
          id1    = (long *) calloc(nprog, sizeof(long));
          id2    = (long *) calloc(nprog, sizeof(long));
          npart1 = (long *) calloc(nprog, sizeof(long));
          npart2 = (long *) calloc(nprog, sizeof(long));
          common = (long *) calloc(nprog, sizeof(long));
          
          id1[nprog-1]    = mtree[iline].id[0];
          id2[nprog-1]    = mtree[iline].id[1];
          npart1[nprog-1] = mtree[iline].npart[0];
          npart2[nprog-1] = mtree[iline].npart[1];
          common[nprog-1] = mtree[iline].common;
          
          prev_id1        = id1[nprog-1];
          
        }
    }
  
  if(id1 != NULL) free(id1);
  if(id2 != NULL) free(id2);
  if(npart1 != NULL) free(npart1);
  if(npart2 != NULL) free(npart2);
  if(common != NULL) free(common);
  
  fclose(fpin);
  fclose(fpout);
  
  fprintf(stderr," done\n");
  return(1);
}

/*==================================================================================================
 * read_mtree:
 *
 *       simply reads in the *_mtree file and 
 *       puts it into the array of structures mtree[iline].XYZ
 *
 * Note: at this stage we just treat these entries as "lines" -> no connection to halos yet!
 *
 *==================================================================================================*/
void read_mtree(char *infile)
{
  long unsigned iline;
  char          line[MAXSTRING];
  FILE         *fpin;
  
  if((fpin = fopen(infile,"r")) == NULL)
    {
      fprintf(stderr,"cannot open  %s\nEXIT\n",infile);
      exit(0);
    }
  
  // count number of lines
  nlines = 0;
  while(!feof(fpin))
    {
      nlines++;
      fgets(line,MAXSTRING,fpin);
    }
  
  //fprintf(stderr,"o found %ld lines in:  %s\n",nlines,infile);
  
  // allocate memory
  mtree = (MTREEptr) realloc((MTREEptr)mtree, nlines*sizeof(MTREE));
  
  // actually read the file
  rewind(fpin);
  for(iline=0; iline<nlines; iline++)
    {
      // read next line from file
      fgets(line,MAXSTRING,fpin);
      sscanf(line,"%ld %ld %ld %ld %ld",
             &(mtree[iline].id[0]), 
             &(mtree[iline].npart[0]), 
             &(mtree[iline].common), 
             &(mtree[iline].id[1]), 
             &(mtree[iline].npart[1]));
      //fprintf(stderr,"iline=%ld id[0]=%ld\n",iline,mtree[iline].id[0]);
    }
  
  fclose(fpin);
}

/*==================================================================================================
 * assign_host:
 *
 *       loop through *_mtree file (held in memory in array mtree[].XYZ) and
 *       assign one host to each halo in there; host halo "-1" means isolated halo
 *
 *==================================================================================================*/
void assign_host()
{
  long unsigned iline, ihalo;
  long unsigned npart0, npart1, id0, id1, common;
  
  // the largest halo id
  nstree = (mtree[nlines-1].id[0])+1;
  
  // allocate (and initialize) array to hold host id's
  ihost = (long *) realloc((long *)ihost, nstree*sizeof(long));
  for(ihalo=0; ihalo<nstree; ihalo++) ihost[ihalo] = -1;
  
  // fill ihost array for every possible halo
  for(iline=0; iline<nlines; iline++)
    {
      // for simplicity copy *_mtree values over to local variables
      npart0 = mtree[iline].npart[0];
      npart1 = mtree[iline].npart[1];
      id0    = mtree[iline].id[0];
      id1    = mtree[iline].id[1];
      common = mtree[iline].common;
      
      // the current halo for which we want to set the host
      ihalo  = id1;
      
      if(  id0 != id1         &&        // halos cannot be their own hosts
         ihost[ihalo] < 0   &&        // did we already assign a host
         npart1 == common   &&        // >>all<< particles should be >>inside<< host
         npart1 < npart0              // the host has to be more massive
         )
        {
          ihost[ihalo] = id0;
        }
    }
}


/*==================================================================================================
 * write_stree:
 *
 *       simply dump ihost[] array to file *_stree
 *
 *==================================================================================================*/
void write_stree(char *outfile)
{
  long unsigned ihalo;
  FILE         *fpout;
  
  fprintf(stderr,"o writing %s\n",outfile);
  
  if((fpout = fopen(outfile,"w")) == NULL)
    {
      fprintf(stderr,"cannot open  $s\nEXIT\n",outfile);
      exit(0);
    }
  
  for(ihalo=0; ihalo<nstree; ihalo++)
    {
      fprintf(fpout,"%16ld   %16ld\n", ihalo, ihost[ihalo]);
    }
  
  fclose(fpout);
}

/*==================================================================================================
 * create_stree:
 *
 *  assign a host to each halo in the catalogue, -1 means isolated halo
 *
 *==================================================================================================*/
int create_stree(char OutFile[MAXSTRING])
{
  char streeFile[MAXSTRING];
  
  strcpy(streeFile, OutFile);
  strcat(streeFile, "_stree");
  
  ihost = (long *) calloc(1, sizeof(long));
  assign_host();
  
  write_stree(streeFile);
  free(ihost);
  
  return(1);
}
