int main(int argc, char *argv[]){
   double  redshift;
   char    filename_in[MAX_FILENAME_LENGTH];
   char    filename_out[MAX_FILENAME_LENGTH];
   char    filename_cosmology[MAX_FILENAME_LENGTH];
   double  box_size;
   double  lM_min,dlM;
   char   *line=NULL;
   size_t  line_length=0;
   int     M_column;
   int     n_bins;
   int     flag_log;
 
   // Initialization -- MPI etc.
   SID_init(&argc,&argv,NULL,NULL);
   if(argc!=11)
     SID_trap_error("Incorrect syntax.",ERROR_SYNTAX);

   // Parse arguments
   strcpy(filename_in, argv[1]);
   strcpy(filename_out,argv[2]);
   redshift=(double)atof(argv[3]);
   strcpy(filename_cosmology,argv[4]);
   box_size=(double)atof(argv[5]);
   M_column=(int)   atoi(argv[6]);
   flag_log=(int)   atoi(argv[7]);
   lM_min  =(double)atof(argv[8]);
   dlM     =(double)atof(argv[9]);
   n_bins  =(int)   atoi(argv[10]);

   SID_log("Producing a mass function for ascii file {%s}...",SID_LOG_OPEN|SID_LOG_TIMER,filename_in);
 
   // Initialize cosmology
   cosmo_info *cosmo;
   read_gbpCosmo_file(&cosmo,filename_cosmology);
   double h_Hubble=((double *)ADaPS_fetch(cosmo,"h_Hubble"))[0];

   // Open file
   FILE *fp_in;
   if((fp_in=fopen(filename_in,"r"))==NULL)
      SID_trap_error("Could not open {%s} for reading.",ERROR_IO_OPEN,filename_in);

   // Allocate memory for the data. Read it and sort it in ascending order 
   SID_log("Reading data...",SID_LOG_OPEN|SID_LOG_TIMER);
   int     n_data_in=count_lines_data(fp_in);
   SID_log("(%d items)...",SID_LOG_CONTINUE,n_data_in);
   double *data  =(double *)malloc(sizeof(double)*n_data_in);
   int n_data=0;
   for(int i=0;i<n_data_in;i++){
     double data_in;
     grab_next_line_data(fp_in,&line,&line_length);
     grab_double(line,M_column,&data_in);
     if(!flag_log)
        data_in=take_log10(data_in);
     if(data_in>=lM_min)
        data[n_data++]=data_in;
   }
   SID_log("(%d will be used)...",SID_LOG_CONTINUE,n_data);
   fclose(fp_in);
   SID_free(SID_FARG line);
   SID_log("Done.",SID_LOG_CLOSE);

   // Perform sort
   SID_log("Sorting data...",SID_LOG_OPEN|SID_LOG_TIMER);
   merge_sort(data,n_data,NULL,SID_DOUBLE,SORT_INPLACE_ONLY,SORT_COMPUTE_INPLACE);
   SID_log("Done.",SID_LOG_CLOSE);

   // Compile histogram
   SID_log("Computing mass function...",SID_LOG_OPEN|SID_LOG_TIMER);
   double *bin       =(double *)SID_malloc(sizeof(double)*(n_bins+1));
   double *bin_median=(double *)SID_malloc(sizeof(double)*n_bins);
   int    *hist      =(int    *)SID_calloc(sizeof(int)   *n_bins);
   double  lM_bin_min=lM_min;
   double  lM_bin_max=lM_min;
   int     i_data_lo=-1;
   int     i_data_hi=-1;
   int     i_bin =0;
   int     i_data=0;
   for(i_bin=0;i_bin<n_bins;i_bin++){
     lM_bin_min=lM_bin_max;
     lM_bin_max=lM_min+((double)(i_bin+1))*dlM;
     bin[i_bin]=lM_bin_min;
     i_data_lo=i_data;
     i_data_hi=i_data;
     while(data[i_data]<lM_bin_max && i_data<n_data){
        hist[i_bin]++;
        i_data_hi=i_data;
        i_data++;
        if(i_data>=n_data) break;
     }
     int i_data_mid=(i_data_lo+i_data_hi)/2;
     if(hist[i_bin]>0){
       if(hist[i_bin]%2)
         bin_median[i_bin]=data[i_data_mid];
       else
         bin_median[i_bin]=0.5*(data[i_data_mid]+data[i_data_mid+1]);
     }
     else
        bin_median[i_bin]=0.5*(lM_bin_max+lM_bin_min);
   }
   bin[i_bin]=lM_bin_max;
   SID_log("Done.",SID_LOG_CLOSE);

   // Write mass function
   FILE *fp_out;
   if((fp_out=fopen(filename_out,"w"))==NULL){
     fprintf(stderr,"Error opening output file {%s}.\n",filename_out);
     SID_free(SID_FARG data);
     return(1);
   }
   SID_log("Writing results to {%s}...",SID_LOG_OPEN|SID_LOG_TIMER,filename_out);
   double box_volume=box_size*box_size*box_size;
   fprintf(fp_out,"# Mass function for column %d in {%s}\n",M_column,filename_in);
   fprintf(fp_out,"# Column (01): M_lo     [source units]\n");
   fprintf(fp_out,"#        (02): M_median [source units]\n");
   fprintf(fp_out,"#        (03): M_hi     [source units]\n");
   fprintf(fp_out,"#        (04): No. in bin\n");
   fprintf(fp_out,"#        (05): MFn (per unit volume, per dlogM)\n");
   fprintf(fp_out,"#        (06): +/- MFn\n");
   fprintf(fp_out,"#        (07): Sheth & Tormen MFn\n");
   fprintf(fp_out,"#        (08): Watson MFn\n");
   fprintf(fp_out,"#        (09): No. w/ M>M_lo\n");
   fprintf(fp_out,"#        (10): Cumulative MFn (per unit volume)\n");
   fprintf(fp_out,"#        (11): +/- Cumulative MFn\n");
   fprintf(fp_out,"#        (12): Sheth & Tormen Cumulative MFn\n");
   fprintf(fp_out,"#        (13): Watson Cumulative MFn\n");
   double M_sol_inv_h=M_SOL/h_Hubble;
   double Mpc_inv_h  =M_PER_MPC/h_Hubble;
   for(int i=0;i<n_bins;i++){
     double dn_dlogM_theory_1=mass_function(take_alog10(bin_median[i])*M_sol_inv_h,
                                            redshift,
                                            &cosmo,
                                            MF_ST)*pow(Mpc_inv_h,3.0);
     double n_theory_1=mass_function_cumulative(take_alog10(bin[i])*M_sol_inv_h,
                                                redshift,
                                                &cosmo,
                                                MF_ST)*pow(Mpc_inv_h,3.0);
     double dn_dlogM_theory_2=mass_function(take_alog10(bin_median[i])*M_sol_inv_h,
                                            redshift,
                                            &cosmo,
                                            MF_WATSON)*pow(Mpc_inv_h,3.0);
     double n_theory_2=mass_function_cumulative(take_alog10(bin[i])*M_sol_inv_h,
                                                redshift,
                                                &cosmo,
                                                MF_WATSON)*pow(Mpc_inv_h,3.0);
     // Compute cumulative histogram
     int cumulative_hist=0;
     for(int j_bin=i;j_bin<n_bins;j_bin++)
        cumulative_hist+=hist[j_bin];
     fprintf(fp_out,"%11.4le %11.4le %11.4le %6d %11.4le %11.4le %10.4le %10.4le %6d %10.4le %10.4le %10.4le %10.4le\n",
             bin[i],
             bin_median[i],
             bin[i+1],
             hist[i],
             (double)(hist[i])/(box_volume*dlM),
             sqrt((double)(hist[i]))/(box_volume*dlM),
             dn_dlogM_theory_1,dn_dlogM_theory_2,
             cumulative_hist,
             (double)(cumulative_hist)/box_volume,
             sqrt((double)(cumulative_hist))/box_volume,
             n_theory_1,n_theory_2);
   }
   fclose(fp_out);
   SID_log("Done.",SID_LOG_CLOSE);

   // Free allocated memory
   SID_free(SID_FARG data);
   SID_free(SID_FARG bin);
   SID_free(SID_FARG bin_median);
   SID_free(SID_FARG hist);
  
   SID_log("Done.",SID_LOG_CLOSE);
   SID_exit(ERROR_NONE);
}
int main(int argc, char *argv[]){
  char    filename_properties[256];
  char    filename_profiles[256];
  char    filename_out_root[256];
  char    filename_out[256];
  char    filename_SSimPL[MAX_FILENAME_LENGTH];
  char    filename_halo_type[MAX_FILENAME_LENGTH];
  int     snap_number;
  int     snap_number_start;
  int     snap_number_stop;
  int     snap_number_step;

  SID_init(&argc,&argv,NULL,NULL);

  strcpy(filename_SSimPL,   argv[1]);
  strcpy(filename_halo_type,argv[2]);
  snap_number_start   =atoi(argv[3]);
  snap_number_stop    =atoi(argv[4]);
  snap_number_step    =atoi(argv[5]);
  strcpy(filename_out_root, argv[6]);

  int flag_use_profiles=FALSE;

  if(SID.I_am_Master){
    SID_log("Processing catalogs for snaps %d->%d...",SID_LOG_OPEN|SID_LOG_TIMER,snap_number_start,snap_number_stop);
    for(snap_number=snap_number_start;snap_number<=snap_number_stop;snap_number++){

         // Open halos
         char filename_halos[256];
         sprintf(filename_halos,"%s/halos/%s_%03d.catalog_groups",filename_SSimPL,filename_halo_type,snap_number);
         FILE *fp_halos=NULL;
         if((fp_halos=fopen(filename_halos,"r"))==NULL)
            SID_trap_error("Could not open halo file {%s} for reading.",ERROR_IO_OPEN,filename_halos);
         int n_groups_halos,group_offset_byte_size;
         fread_verify(&n_groups_halos,        sizeof(int),1,fp_halos);
         fread_verify(&group_offset_byte_size,sizeof(int),1,fp_halos);

         // Skip group sizes and offsets
         fseeko(fp_halos,(off_t)(n_groups_halos*(sizeof(int)+group_offset_byte_size)),SEEK_CUR);

         // Open catalogs
         char filename_cat_root[256];
         sprintf(filename_cat_root,"%s/catalogs/%s",filename_SSimPL,filename_halo_type);
         fp_catalog_info fp_catalog_groups;
         fp_catalog_info fp_catalog_subgroups;
         fopen_catalog(filename_cat_root,
                       snap_number,
                       READ_CATALOG_GROUPS|READ_CATALOG_PROPERTIES|READ_CATALOG_PROPERTIES,
                       &fp_catalog_groups);
         fopen_catalog(filename_cat_root,
                       snap_number,
                       READ_CATALOG_SUBGROUPS|READ_CATALOG_PROPERTIES|READ_CATALOG_PROPERTIES,
                       &fp_catalog_subgroups);

         // Open SO files if they're available
         fp_multifile_info fp_SO;
         int flag_use_SO=fopen_multifile("%s/catalogs/%s_%03d.catalog_groups_SO",sizeof(float),&fp_SO,filename_SSimPL,filename_halo_type,snap_number);
         if(flag_use_SO)
            SID_log("SO files present.",SID_LOG_COMMENT);

         // Sanity check
         if(n_groups_halos!=fp_catalog_groups.n_halos_total)
            SID_trap_error("Group counts in halo and catalog files don't match (ie. %d!=%d).",ERROR_LOGIC,n_groups_halos,fp_catalog_groups.n_halos_total);

         // Process halos
         SID_log("Processing snapshot #%03d...",SID_LOG_OPEN,snap_number);
         SID_log("(%d groups, %d subgroups)...",SID_LOG_CONTINUE,fp_catalog_groups.n_halos_total,fp_catalog_subgroups.n_halos_total);

         // Initialzie halo trend data structure
         halo_trend_info  halo_trend_data;
         char             filename_run[MAX_FILENAME_LENGTH];
         sprintf(filename_run,"%s/run/run.txt",filename_SSimPL);
         parameter_list_info *parameter_list=NULL;
         init_parameter_list(&parameter_list);
         add_parameter_to_list(parameter_list,"box_size",SID_DOUBLE,   PARAMETER_MODE_DEFAULT);
         add_parameter_to_list(parameter_list,"N_dark",  SID_SIZE_T,   PARAMETER_MODE_DEFAULT);
         add_parameter_to_list(parameter_list,"m_dark",  SID_DOUBLE,   PARAMETER_MODE_DEFAULT);
         read_gbpParam_file(filename_run,parameter_list);
         fetch_parameter_data(parameter_list,"box_size",&(halo_trend_data.box_size)); 
         fetch_parameter_data(parameter_list,"m_dark",  &(halo_trend_data.m_p)); 
         free_parameter_list(&parameter_list);
         char             filename_snaps[MAX_FILENAME_LENGTH];
         sprintf(filename_snaps,"%s/run/a_list.txt",filename_SSimPL);
         FILE *fp_snaps=fopen(filename_snaps,"r");
         size_t line_length=0;
         char  *line=NULL;
         halo_trend_data.n_snaps=count_lines_data(fp_snaps);
         halo_trend_data.z_list =(double *)SID_malloc(sizeof(double)*halo_trend_data.n_snaps);
         for (int i_snap=0;i_snap<halo_trend_data.n_snaps;i_snap++){
            double a_i;
            grab_next_line_data(fp_snaps,&line,&line_length);
            grab_double(line,1,&a_i);
            halo_trend_data.z_list[i_snap]=z_of_a(a_i);
         }
         SID_free(SID_FARG line);
         fclose(fp_snaps);

         // Initialize halo data structure
         halo_info        halo_data;
         halo_data.flag_use_profiles  = flag_use_profiles;
         halo_data.flag_use_SO        = flag_use_SO;
         halo_data.snapshot           = snap_number;
         halo_data.properties_group   =(halo_properties_info *)SID_malloc(sizeof(halo_properties_info));
         halo_data.properties_subgroup=(halo_properties_info *)SID_malloc(sizeof(halo_properties_info));
         halo_data.profiles_group     =(halo_profile_info    *)SID_malloc(sizeof(halo_profile_info));
         halo_data.profiles_subgroup  =(halo_profile_info    *)SID_malloc(sizeof(halo_profile_info));

         // Initialize trends
         trend_info *trend_M_FoF=NULL;
         init_trend(&trend_M_FoF,"SSFctn",&halo_trend_data,init_halo_trend_property_logM_FoF,free_halo_trend_property_logM_FoF,calc_halo_trend_property_index_logM_FoF);
         init_halo_trend_coordinate(&halo_trend_data,trend_M_FoF,"SSFctn");

         // Read halos and construct histograms
         for(int i_group=0,i_subgroup=0;i_group<fp_catalog_groups.n_halos_total;i_group++){
            int n_subgroups_group;
            // Read group catalog
            fread_catalog_file(&fp_catalog_groups,NULL,NULL,halo_data.properties_group,halo_data.profiles_group,i_group);
            // Read number of subgroups
            fread_verify(&n_subgroups_group,sizeof(int),1,fp_halos);
            // Read SO masses (if available)
            if(flag_use_SO)
               fread_multifile(&fp_SO,halo_data.SO_data_group,i_group);
            // Loop over subgroups
            halo_data.n_sub         =n_subgroups_group;
            halo_data.np_sub        =0;
            halo_data.np_sub_largest=0;
            for(int j_subgroup=0;j_subgroup<n_subgroups_group;i_subgroup++,j_subgroup++){
               // Read subgroup properties
               fread_catalog_file(&fp_catalog_subgroups,NULL,NULL,halo_data.properties_subgroup,halo_data.profiles_subgroup,i_subgroup);
               int np_i=halo_data.properties_subgroup->n_particles;
               halo_data.np_sub+=np_i;
               if(np_i>halo_data.np_sub_largest)
                  halo_data.np_sub_largest=np_i;
               // Add halo to subgroup trends
            }
            // Add halo to group trends
            add_item_to_trend(trend_M_FoF,GBP_ADD_ITEM_TO_TREND_DEFAULT,&halo_data);
         }

         // Write results
         char filename_out[MAX_FILENAME_LENGTH];
         sprintf(filename_out,"%s_%03d",filename_out_root,snap_number);
         write_trend_ascii(trend_M_FoF,filename_out);
         free_trend(&trend_M_FoF);

         // Clean-up
         SID_free(SID_FARG halo_trend_data.z_list);
         SID_free(SID_FARG halo_data.properties_group);
         SID_free(SID_FARG halo_data.properties_subgroup);
         SID_free(SID_FARG halo_data.profiles_group);
         SID_free(SID_FARG halo_data.profiles_subgroup);
         fclose(fp_halos);
         fclose_catalog(&fp_catalog_groups);
         fclose_catalog(&fp_catalog_subgroups);
         fclose_multifile(&fp_SO);
         SID_log("Done.",SID_LOG_CLOSE);
     }
     SID_log("Done.",SID_LOG_CLOSE);
  }  

  SID_exit(ERROR_NONE);
}
int main(int argc, char *argv[]) {
    SID_Init(&argc, &argv, NULL);

    // Parse arguments
    char filename_cosmo[SID_MAX_FILENAME_LENGTH];
    char filename_list_in[SID_MAX_FILENAME_LENGTH];
    strcpy(filename_cosmo, argv[1]);
    strcpy(filename_list_in, argv[2]);

    // Read input list
    char *  line        = NULL;
    size_t  line_length = 0;
    FILE *  fp_in       = fopen(filename_list_in, "r");
    int     n_list      = count_lines_data(fp_in);
    double *a_list      = (double *)SID_malloc(sizeof(double) * n_list);
    for(int i_list = 0; i_list < n_list; i_list++) {
        grab_next_line_data(fp_in, &line, &line_length);
        grab_double(line, 1, &(a_list[i_list]));
    }
    SID_free(SID_FARG line);

    double a_lo_table = a_list[0];
    double a_hi_table = a_list[n_list - 1];
    for(int i_list = 0; i_list < n_list; i_list++) {
        a_lo_table = GBP_MIN(a_lo_table, a_list[i_list]);
        a_hi_table = GBP_MAX(a_hi_table, a_list[i_list]);
    }
    a_lo_table = GBP_MIN(a_lo_table, 1e-4);

    SID_log("Creating extended snapshot list...", SID_LOG_OPEN);

    // Initialize cosmology
    cosmo_info *cosmo = NULL;
    read_gbpCosmo_file(&cosmo, filename_cosmo);

    // Creeate interpolation tables
    int     n_table     = 500;
    double *a_table     = (double *)SID_malloc(n_table * sizeof(double));
    double *t_table     = (double *)SID_malloc(n_table * sizeof(double));
    double *n_dyn_table = (double *)SID_malloc(n_table * sizeof(double));
    double  da_table    = (a_hi_table - a_lo_table) / (double)(n_table - 1);
    for(int i_table = 0; i_table < n_table; i_table++) {
        // Expansion factor
        if(i_table == 0)
            a_table[i_table] = a_lo_table;
        else if(i_table == (n_table - 1))
            a_table[i_table] = a_hi_table;
        else
            a_table[i_table] = a_table[0] + da_table * (double)i_table;
        // Cosmic time
        t_table[i_table] = t_age_a(a_table[i_table], &cosmo);
        // Number of dynamical times
        if(i_table == 0)
            n_dyn_table[i_table] = 0.;
        else
            n_dyn_table[i_table] = delta_n_dyn(a_table[0], a_table[i_table], &cosmo);
    }
    interp_info *n_of_a_interp = NULL;
    init_interpolate(a_table, n_dyn_table, n_table, gsl_interp_akima, &n_of_a_interp);

    // Generate desired table
    double a_last   = 0.;
    double z_last   = 0.;
    double t_last   = 0.;
    double n_last   = 0.;
    double a_i      = 0.;
    double z_i      = 1.e22;
    double t_i      = 0.;
    double n_i      = 0.;
    double da       = 0.;
    double dz       = 0.;
    double dt       = 0.;
    double dn       = 0.;
    int    i_column = 1;
    printf("# Table of times, etc. generated from expansion factors in {%s}\n", filename_list_in);
    printf("# Column (%02d): snapshot number\n", i_column++);
    printf("#        (%02d): a:=expansion factor\n", i_column++);
    printf("#        (%02d): da\n", i_column++);
    printf("#        (%02d): z:=redshift\n", i_column++);
    printf("#        (%02d): dz\n", i_column++);
    printf("#        (%02d): t:=cosmic time  [years]\n", i_column++);
    printf("#        (%02d): dt\n", i_column++);
    printf("#        (%02d): n_dyn:=No. of dynamical times\n", i_column++);
    printf("#        (%02d): dn_dyn\n", i_column++);
    for(int i_list = 0; i_list < n_list; i_list++) {
        a_last = a_i;
        z_last = z_i;
        t_last = t_i;
        n_last = n_i;
        a_i    = a_list[i_list];
        z_i    = z_of_a(a_i);
        t_i    = t_age_a(a_i, &cosmo) / S_PER_YEAR;
        n_i    = interpolate(n_of_a_interp, a_i);
        da     = a_i - a_last;
        dz     = fabs(z_i - z_last);
        dt     = t_i - t_last;
        dn     = n_i - n_last;
        printf("%03d %le %le %le %le %le %le %le %le\n", i_list, a_i, da, z_i, dz, t_i, dt, n_i, dn);
    }

    // Clean-up
    SID_free(SID_FARG a_list);
    SID_free(SID_FARG a_table);
    SID_free(SID_FARG t_table);
    SID_free(SID_FARG n_dyn_table);
    free_interpolate(SID_FARG n_of_a_interp, NULL);
    free_cosmo(&cosmo);

    SID_log("Done.", SID_LOG_CLOSE);
    SID_Finalize();
}