/*----------------------------------------------------------------*
 *
 * File : doSimulations.c
 * Author : NTM
 * Created : 29/06/04
 *
 *
 * Copyright (C) Nicolas Thierry-Mieg, 2006.
 *
 *
 * This file is part of InterPool, written by 
 * Nicolas Thierry-Mieg (CNRS, France) Nicolas.Thierry-Mieg@imag.fr
 *
 * InterPool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * InterPool is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with InterPool; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *-----------------------------------------------------------------*/

#include <stdlib.h> /* free, exit, atoi */
#include <stdio.h> /* printf and friends */
#include <string.h> /* strcpy, strlen */
#include <limits.h> /* CHAR_BIT */

#include <sys/stat.h> /* for mkdir */
#include <sys/types.h> /* also for mkdir */
#include <errno.h> /* for mkdir return status */

#include "types.h" /* MOT, ARCH */
#include "config.h" /* OUTDIR */
#include "fonc.h" /* firstDivisor */
#include "design.h" /* expectedNegPools */
#include "jobs.h" /* JobIdentSim datatype definition, RANDGENLENGTH */
#include "myrand.h" /* chooseSeed, RANDGENNAME */
#include "simulation.h" /* simulation */

/*!
  \file doSimulations.c
  \brief This file defines the main function to perform simulations.
*/

/* UPDATE 01/04/05: ipoolSimulations now takes FRACTIONS for the numbers
   of false negs and false pos's (instead of absolute numbers: these are
   now calculated).

   UPDATE 25/06/04: I am removing avposvars/varposvars and replacing them
   with nbPosVars. There was no sense in using varposvars != 0, it is better
   to simply perform more simms using different values for nbPosVars.

   NOTE 11/03/04, UPDATED 29/04/04, UPDATED 17/07/06: 
   In the whole code, n is the number of variables.
   I don't have q and k any more, except in STD.c.
   Up to 17/07/06, we had: and k is the number of layers.
   This is a switch from the old code (prior to april 2004).
   It is done in accordance with my STD paper, and the switch
   is happening to homogenize with the Balding et al 1996 review and
   other papers.
*/



/************************************************************************
 ****************************** MAIN ************************************
 ************************************************************************/

/*! 
  \brief main function to perform simulations.

  Usage is:
  ipoolSimulations n nbPools designFile nbPosVars falsePosFrac falseNegFrac nsim
  - n: number of variables;
  - nbPools: number of pools;
  - designFile: pooling design; 
  - nbPosVars: number of positive variables;
  - falsePosFrac: fraction of neg pools that are erroneously observed as pos/weak;
  - falseNegFrac: fraction of pos pools that are erroneously observed as neg/faint;
  - nsim: number of simms to perform for each set of q,k values;
*/
int main(int argc, char* argv[])
{
  /* test if ARCH corresponds to the MOT size */
  if (ARCH != sizeof(MOT)*CHAR_BIT)
    {
      fprintf(stderr, 
	      "ARCH is not in accord with MOT (both defined in types.h)!\n") ;
      fprintf(stderr, "You must change one or the other and recompile.\n") ;
      fprintf(stderr, "ARCH should correspond to your architecture for optimal performance.\n") ;
      exit(1) ;
    }
  
  if (argc != 8)
    {
      fprintf(stderr, "wrong number of args.\n") ;
      fprintf(stderr, "USAGE: ipoolSimulations n nbPools designFile nbPosVars falsePosFrac falseNegFrac nsim\n") ;
      fprintf(stderr, "n: number of variables;\nnbPools: number of pools;\n") ;
      fprintf(stderr, "designFile: a design file in interPool format;\n") ;
      fprintf(stderr, "nbPosVars: number of positive variables;\n") ;
      fprintf(stderr, "falsePosFrac: fraction of neg pools that are erroneously observed as pos or weak;\n") ;
      fprintf(stderr, "falseNegFrac: fraction of pos pools that are erroneously observed as neg or faint;\n") ;
      fprintf(stderr, "nsim: number of simms to perform.\n") ;
      exit(1) ;
    }


  /* set values for the parameters to simulation: build a JobIdentSim */
  int n = atoi(argv[1]) ;
  int nbPools = atoi(argv[2]) ;
  char designFile[OUTFILELENGTH] ;
  (void)strncpy(designFile, argv[3], OUTFILELENGTH) ;
  // make sure file name was not too long
  if (designFile[OUTFILELENGTH-1] != '\0')
    {
      fprintf(stderr, "in doSimulations.c, error building designFile: too long?\n");
      exit(1);
    }
  int nbPosVars = atoi(argv[4]) ;
  double falsePosFrac = strtod(argv[5], NULL) ;
  double falseNegFrac = strtod(argv[6], NULL) ;
  int nsim = atoi(argv[7]) ;

  /* for distribution: always use mode 3, ie RecSubstractedSimm */
  int mode = 3 ;


  /* check some parameters */
  if (n <= 0)
    {
      fprintf(stderr, "in main: n must be positive!\n") ;
      exit(1) ;
    }
  if ( (falsePosFrac < 0) || (falsePosFrac > 1) || 
       (falseNegFrac < 0) || (falseNegFrac > 1) )
    {
      fprintf(stderr, "in main: falsePosFrac and falseNegFrac must be in [0,1]\n") ;
      exit(1) ;
    }
  if ((mode < 1) || (mode > 5))
    {
      fprintf(stderr, "in ipoolSimulation: mode %d is invalid\n", mode) ;
      exit(1) ;
    }

  /* make the OUTDIR directory if it doesn't exist */
  {
    /* create output directory with perms 755 if it doesn't exist */
    extern int errno ;
    int status = mkdir(OUTDIR, 511) ;
    if (status !=0)
      { /* if dir just already existed, continue; else die */
	if (errno != EEXIST)
	  {
	    fprintf(stderr, "Error creating directory OUTDIR\n") ;
	    exit(1) ;
	  }
      }
  }


  ///////////////////////////////////////
  // perform all the simulations
  ///////////////////////////////////////
  {
    /* for JLR: perhaps this is where parallelization should happen? */

    /* build the JobIdentSim for the current job */
    JobIdentSim thisJob ;

    /* calculate falsePos and falseNeg from the Fractions */
    int expNegPools = expectedNegPools(n,nbPools,nbPosVars) ;
    int falsePos = (int)(falsePosFrac * expNegPools) ;
    int falseNeg = (int)(falseNegFrac * (nbPools - expNegPools)) ;


    /* how many simms should be performed in each batch? */
    int simmsPerBatch = nsim ; /* for sequential program: do all simms in one batch */


    char randomGenMethod[RANDGENLENGTH] ;
    /* choose random generator and seed */

    if (strlen(RANDGENNAME) >= RANDGENLENGTH)
      {
	fprintf(stderr, "in main, the random generator name RANDGENNAME is too long\n") ;
	exit(1) ;
      }
    (void)strcpy(randomGenMethod, RANDGENNAME) ;
    
    /* choose seed */
    int seed = chooseSeed() ;
    
    /* build output filename */
    char outFileName[OUTFILELENGTH] ;

    /* get rid of the path in designFile: */
    /* initially, point baseName to the last char of designFile */
    char* baseName = designFile + strlen(designFile) ;
    while((baseName != designFile) && (*(baseName-1) != '/'))
      baseName-- ;

    /* check that we found something */
    if (baseName == designFile + strlen(designFile))
      {
	fprintf(stderr, "in ipoolSimulation: designFile can begin with a path, but must have a file name.\n") ;
	exit(1) ;
      }

    int numchars = snprintf(outFileName,OUTFILELENGTH,"%s/%s.pos%d.fp%d.fn%d.nsim%d.seed%d",
			    OUTDIR,baseName,nbPosVars,falsePos,falseNeg,simmsPerBatch,seed);
    // make sure file name was not too long
    if ((numchars < 0) || (numchars >= OUTFILELENGTH))
      {
	fprintf(stderr, "in ipoolSimulations, error building outFileName: too long?\n");
	exit(1);
      }

    thisJob.n = n ;
    thisJob.nbPools = nbPools ;
    thisJob.designFileName = designFile ;
    thisJob.nbPosVars = nbPosVars ;
    thisJob.falsePos = falsePos ;
    thisJob.falseNeg = falseNeg ;
    thisJob.nsim = simmsPerBatch ;
    thisJob.randomGenMethod = randomGenMethod ;
    thisJob.seed = seed ;
    thisJob.outFileName = outFileName ;
    /* launch the job */
    simulation(&thisJob, mode) ;
  }

  return 0;
}

