/*----------------------------------------------------------------*
 *
 * File : doValidation.c
 * Author : NTM
 * Created : 15/09/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 "validation.h" /* validation */


/*!
  \file doValidation.c
  \brief This file contains the main for launching validations.
*/

#undef DEBUG 
/* #define DEBUG to produce additional output */


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


/************************************************************************
 ******************* LOCAL FUNCTIONS ************************************
 ************************************************************************/

/********************** DECLARATIONS ************************************/



/************************ BODIES ***************************************/




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

/*! 
  \brief main function for launching validations. This means we perform
  simms using 2 different decoding methods, and check that the results
  are identical.

  Usage is:
  - ipoolValidation n nbPools designFile nbPosVars falsePosFrac falseNegFrac nsim method1 method2
  - 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;
  - methodX: choose 2 different solvexp methods to use. Current valid values are:
    - 1 (NoisyNaive)
    - 2 (NoisyClosureSim with method=1)
    - 3 (NoisyClosureSim with method=2)
    - 4 (noisyClosureReal with method=3)
    - 5 (noisyClosureReal with method=4)
*/
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 != 10)
    {
      fprintf(stderr, "wrong number of args.\n") ;
      fprintf(stderr, "USAGE: ipoolValidation n nbPools designFile nbPosVars falsePosFrac falseNegFrac nsim method1 method2\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") ;
      fprintf(stderr, "methodX: choose 2 different solvexp methods to use. Current valid values are:\n") ;
      fprintf(stderr, "1 (Naive),\n2 (ClosureSim, Rec algo), 3 (ClosureSim, improved RecSubstracted algo),\n") ;
      fprintf(stderr, "4 (ClosureReal, Rec algo), 5 (ClosureReal, improved RecSubstracted algo).\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 doValidation.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]) ;
  int method1 = atoi(argv[8]) ;
  int method2 = atoi(argv[9]) ;


  /* 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 ( (method1 < 1) || (method1 > 5) || (method2 < 1) || (method2 > 5) || (method1==method2) )
    {
      fprintf(stderr, "in ipoolValidation: methods should be valid and different\n") ;
      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 ipoolValidation: 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.methods_%d_%d",
			    OUTDIR,baseName,nbPosVars,falsePos,falseNeg,simmsPerBatch,seed,method1,method2);
    // make sure file name was not too long
    if ((numchars < 0) || (numchars >= OUTFILELENGTH))
      {
	fprintf(stderr, "in main, 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 */
    validation(&thisJob, method1, method2) ;

  }
  return 0;
}

