/*----------------------------------------------------------------*
 *
 * File : design.c
 * Author : NTM
 * Created : 06/07/06
 *
 *
 * 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>
#include <stdio.h>
#include <math.h> // log

#include "types.h" /* MOT */
#include "config.h" /* USING_STD, USING_WASP */
#include "pools.h" /* pool, getpoolvalue */
#include "fonc.h" /* firstDivisor */
#include "STD.h" /* compression, poolSizeSTD, calculateQ, calculateK */

#include "design.h"



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

/////////////// DECLARATIONS ///////////////

/*!
  \brief Return the average pool size.
  For STD we have a good calculation, this should be customized for
  different designs.
*/
static int poolSize(int n, int nbPools) ;


//////////////// BODIES ///////////////////

/*!
  \brief Return the average pool size.
  For STD we have a good calculation, this should be customized for
  different designs.
*/
static int poolSize(int n, int nbPools)
{
  /* NOTE: no custumization for USING_WASP, because this is only
     used for ipoolSimulations and ipoolValidation, where STD pools
     can be used. USING_WASP is only important for ipoolDecoding. */
#ifdef USING_STD
  return(poolSizeSTD(n,nbPools)) ;

#else
  /* TODO. In the meantime, just return poolSizeSTD, this
     will be sort of OK for some designs... really, FIXME! */
  return(poolSizeSTD(n,nbPools)) ;

#endif /* USING_STD */
}


/************************************************************************
 ******************* EXPORTED FUNCTIONS *********************************
 ************************************************************************/


/*!
  \brief return the max number of pools that can contain 
  any 2 variables. 
*/
int maxInterBetweenVars(MOT* tabpool, int n, int nbPools)
{
#ifdef USING_STD
  /* for STD: this is compression(q,n) */
  static int nbP0 = 0 ;
  static int n0 = 0 ;
  static int gamma = 0 ;

  if ((nbPools != nbP0) || (n != n0))
    {
      nbP0 = nbPools ;
      n0 = n ;
      int q = calculateQ(nbPools) ;

      /* silence compiler warnings */
      tabpool = NULL ;
  
      gamma = compression(q,n) ;
    }
  /* else correct value is already cached */
  return(gamma) ;

#else 
#ifdef USING_WASP
  /* specific for WASP pools (Worm project): as for STD, this is 
     compression(q,n), except that calculateQ won't work on 
     all batches. In fact, q is always 13 in the WASP project.
     Still test n and nbPools as a safeguard. */
  if (((nbPools==169) && ((n==1014) || (n==507) ||  // WASP6
			  (n==338) || (n==169))) || // regular WASP2 or WAMP
      ((n==338) && ((nbPools==263) || (nbPools==188) || (nbPools==244)))) // intermediate WASP2
    return(compression(13,n)) ;
  else
    {
      /* silence compiler warnings */
      tabpool = NULL ;

      fprintf(stderr, 
	      "USING_WASP is defined, but testing n (%d) and nbPools (%d) fails in maxInterBetweenVars.\n",
	      n, nbPools) ;
      exit(1) ;
    }
  
#else
  /* fall back to EXTREMELY expensive calculation. If you use
	 a different design with easily calculable intersection
	 size, add it here! */ 
  static int maxInter = 0 ;
  static MOT* tabpool0 = NULL ;

  if (tabpool != tabpool0)
    {
      tabpool0 = tabpool ;
      maxInter = 0 ;
      /* calculate correct value */
      int v1 ;
      for (v1=0; v1<n; v1++)
	{
	  int v2 ;
	  for (v2 = v1+1; v2<n; v2++)
	    {
	      int interSize = 0 ;
	      int absPoolNum ;
	      for (absPoolNum=0; absPoolNum < nbPools; absPoolNum++)
		{
		  MOT* currentPool = pool(tabpool,absPoolNum,n) ;
		  if ( (getpoolvalue(currentPool, v1) == 11) &&
		       (getpoolvalue(currentPool, v2) == 11) )
		    /* currentPool contains both v1 and v2 */
		    interSize++ ;
		}
	      if (interSize > maxInter)
		maxInter = interSize ;
	    }
	}
    }

  /* otherwise tabpool is identical so value cannot have changed,
     use cached value from static */

  return (maxInter) ;

#endif /* USING_WASP */
#endif /* USING_STD */
}


/*!
  \brief return the max number of pools that can contain
  any single variable.
*/
int maxPoolsPerVar(MOT* tabpool, int n, int nbPools)
{
#ifdef USING_STD
  /* for STD: k */
  static int nbP0 = 0 ;
  static int k = 0 ;
  
  if (nbPools != nbP0)
    {
      nbP0 = nbPools ;
      k = calculateK(nbPools) ;
      
      /* silence compiler warnings */
      tabpool = NULL ;
      n = 0 ;
    }
  /* else return cached value */
  return (k) ;

#else
#ifdef USING_WASP
  /* specific for WASP pools (Worm project): all batches have
     13x redundancy, but calculateK won't work for some batches
     (because calculateQ won't work). Still test n and nbPools
     as a safeguard. */
  if (((nbPools==169) && ((n==1014) || (n==507) ||  // WASP6
			  (n==338) || (n==169))) || // regular WASP2 or WAMP
      ((n==338) && ((nbPools==263) || (nbPools==188) || (nbPools==244)))) // intermediate WASP2
    return(13) ;
  else
    {
      /* silence compiler warnings */
      tabpool = NULL ;

      fprintf(stderr, 
	      "USING_WASP is defined, but testing n (%d) and nbPools (%d) fails in maxPoolsPerVar.\n",
	      n, nbPools) ;
      exit(1) ;
    }

#else
  /* fall back to expensive calculation. If you use
     a different design with easily calculable return value, 
     add it here. */ 
  static int maxPools = 0 ;
  static MOT* tabpool0 = NULL ;

  if (tabpool != tabpool0)
    {
      tabpool0 = tabpool ;
      maxPools = 0 ;
      /* calculate correct value */
      int v1 ;
      for (v1=0; v1<n; v1++)
	{
	  int nbP = 0 ;
	  int absPoolNum ;
	  for (absPoolNum=0; absPoolNum < nbPools; absPoolNum++)
	    {
	      MOT* currentPool = pool(tabpool,absPoolNum,n) ;
	      if (getpoolvalue(currentPool, v1) == 11)
		nbP++ ;
	    }
	  if (nbP > maxPools)
	    maxPools = nbP ;
	}
    }

  /* otherwise tabpool is identical so value cannot have changed,
     use cached value from static */

  return (maxPools) ;

#endif /* USING_WASP */
#endif /* USING_STD */
}


/*!
  Return the expected number of negative pools.
  This can be a rough estimate.
  The maths are similar to optimalq:
  P[pool containing F fishes is negative] = (1-fracPosVars)**F
  where F is the average pool size (n/q for STD), and fracPosVars = nbPosVars/n.
  Therefore the expected number of neg pools is (roughly):
  nbPools * (1 - nbPosVars/n)**(poolSize).
*/
int expectedNegPools(int n, int nbPools, int nbPosVars)
{
  double fracposvars = (double) nbPosVars / n ; // since nbPosVars is cast to double, n is autocast
  int avPoolSize = poolSize(n,nbPools) ;
  double numNegPools = nbPools * pow(1 - fracposvars, avPoolSize) ;
  return ((int)(numNegPools + 0.5)) ;
  // add 0.5 and cast to int returns the nearest int
}


