/*----------------------------------------------------------------*
 *
 * File : pools.c
 * Author : NTM
 * Created : 30/09/03
 *
 *
 * 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 <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h> /* isdigit */
#include <limits.h> /* CHAR_BIT */

#include "masks.h" /* MASKS */
#include "types.h" /* MOT, ARCH */

#include "pools.h"


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

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

//given a pool and a variable, returns a pointer to the MOT of that pool where that
//variable's value is stored
static MOT* motpourvar(MOT* pool, int var);

// readDesign: fill tabpool with the pools stored in designFile.
static void readDesign(char* designFile, MOT* tabpool, int n, int nbPools) ;


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

///////////////////////////////////////////////////////////////
// given a pool (ie, MOT*) and a variable, returns a pointer to 
// the MOT of that pool where that variable's value is stored
static MOT* motpourvar(MOT* pool, int var)
{
  int mot=var/(sizeof(MOT)*4); // 2 bits used for each variable => 4 vars per byte
  return (pool + mot) ;
}


///////////////////////////////////////////////////////////////
// readDesign: fill tabpool with the pools stored in designFile.
// tabpool must be large enough to store nbPools pools with a
// total of n variables.
// format is: one line per pool, each line is a list of variables
// (ie, ints in {0,...,(n-1)}) separated by ':'
// the lines can end with a final ':', but this is optional.
static void readDesign(char* designFile, MOT* tabpool, int n,int nbPools)
{
  FILE* file = fopen(designFile, "r") ;
  if (file==NULL)
    {
      fprintf(stderr, "in readDesign, cannot open file %s\n", designFile) ;
      exit(1) ;
    }

  {
    int absPoolNum=0 ;
    MOT* currentpool = pool(tabpool, absPoolNum, n) ;
    int currentchar ;
    int var = -1 ;
    /* noMore: true if we cannot have any more pools (for dealing with last line) */
    int noMore = 0 ; 

    while((currentchar = getc(file)) != EOF)
      {
	if (noMore)
	  {
	    fprintf(stderr, "in readDesign: nbPools is %d but designFile has more lines than that\n",
		    nbPools) ;
	    exit(1) ;
	  }
	if (isdigit(currentchar))
	  {
	    if (var == -1)
	      var = (int)(currentchar - '0') ;
	    else
	      var = var*10 + (int)(currentchar - '0') ;
	  }
	
	else if (currentchar == ':')
	  {
	    /* separator: a var has been read, check it and update tabpool */
	    if (var == -1)
	      {
		fprintf(stderr, 
			"in readDesign: pool %d starts with a separator or has 2 consecutive separators?\n",
			absPoolNum) ;
		exit(1) ;
	      }
	    else if (var >= n)
	      {
		fprintf(stderr, "in readDesign: pool %d has variable %d >= n (%d)\n",
			absPoolNum, var, n) ;
		exit(1) ;
	      }
	    else
	      {
		setpoolvalue(currentpool,var,11);
		var = -1 ;
	      }
	  }
	
	else if (currentchar == '\n')
	  {
	    /* newline: current pool is done, move to the next */
	    if (var >= 0)
	      {
		/* pool didn't end with separator, process last variable */
		if (var >= n)
		  {
		    fprintf(stderr, "in readDesign: pool %d ends with variable %d >= n (%d)\n",
			    absPoolNum, var, n) ;
		    exit(1) ;
		  }
		else
		  {
		    setpoolvalue(currentpool,var,11);
		    var = -1 ;
		  }
	      }
	    /* in any case, move to next pool */
	    absPoolNum++ ;
	    if (absPoolNum == nbPools)
	      /* this is legal but it must be the final line of designFile */
	      noMore = 1 ;

	    currentpool = pool(tabpool, absPoolNum, n) ;
	  }
	
	else
	  {
	    // wrong format for input file, should not happen!
	    fprintf(stderr,
		    "in readDesign: wrong format for pool %d (offending character:%c)\n", 
		    absPoolNum, currentchar);
	    fprintf(stderr,
		    "your file must be a UNIX text file (end lines with LF, not CRLF or other)\n") ;
	    exit(1);
	  }
      }
    
    /* sanity check */
    if (!noMore)
      {
	fprintf(stderr, 
		"in readDesign, file %s has %d instead of nbPools (==%d) lines, illegal\n",
		designFile, absPoolNum, nbPools) ;
	if (absPoolNum == nbPools-1) 
	  fprintf(stderr, "perhaps the designFile is just missing a final newline?\n") ;
	exit(1) ;
      }

    fclose(file) ;
  }
}

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

/*!
  \brief designFile must be readable and contain a set of pools
  in the correct format.
  format is: one line per pool, each line is a list of variables
  (ie, ints in {0,...,(n-1)}) separated by ':'
*/
MOT* buildPools(char* designFile, int n, int nbPools)
{
  /* allocate the necessary memory for tabpool */
  int tabpoolsize = nbmotparpool(n)*nbPools ; // number of MOTs in tabpool
  /* tabpool: the pointer that will be returned */
  MOT* tabpool=(MOT *)malloc(tabpoolsize * sizeof(MOT));
  if(tabpool==NULL)
    {
      fprintf(stderr,"in buildPools, not enough memory for tabpool\n");
      exit(1);
    }
  // initialize tabpool with 0's (we will later only change the required values to 11)
  memset(tabpool, 0, tabpoolsize * sizeof(MOT));

  readDesign(designFile, tabpool, n, nbPools) ;

  return(tabpool) ;
}



///////////////////////////////////////////////////////////////
// nbmotparpool: returns the number of MOTs used to store one pool
// (since this function is called many times, use tricks)
int nbmotparpool(int n)
{
  /* initial formulation is: 1 + (n-1)/(sizeof(MOT)*CHAR_BIT/2)
     (we use 2 bits per variable).
     To speed things up:
     1. replace sizeof(MOT)*CHAR_BIT/2 by sizeof(MOT)*CHAR_BIT >> 1
     2. store the result in a static */

  static int nvalue = 0 ;
  static int nbmotpool = 0 ;
  
  if (n != nvalue)
    {
      /* n is not the last value used: recompute nbmotpool.
	 otherwise nbmotpool already holds the correct value, just return it */
      nvalue = n ;
      nbmotpool = 1 + (--n) / ((sizeof(MOT) * CHAR_BIT) >> 1) ;
    }
  return nbmotpool ;
}


///////////////////////////////////////////////////////////////
// pool: return the pool (ie, pointer to the first MOT where it is stored)
// in the global table tabpool, for pool of absolute number absPoolNum.
// Reminder: with STD, the absolute pool number is absPoolNum = q*layer + poolnumber.
// n is the total number of variables and q is the STD parameter (number 
// of pools per layer)
MOT* pool(MOT* tabpool, int absPoolNum, int n)
{
  //nbmotpool==number of MOTs in a pool
  int nbmotpool=nbmotparpool(n);

  return (tabpool + (absPoolNum*nbmotpool)) ;
}


///////////////////////////////////////////////////////////////
// returns the value (0,1,10 or 11 currently) of variable var in the pool.
int getpoolvalue(MOT* pool, int var)
{
  static MOT tabmask[ARCH+1] = MASKS ;
  
  static int nbvarparmot = (sizeof(MOT) * CHAR_BIT) >> 1 ;

  // fetch the MOT containing var
  MOT motvar = *motpourvar(pool,var);

  // in motvar, we must find the 2 bits coding for variable var
  // these are bit1 and bit2 (respectively low and high weight), such that:
  // bit1 = motvar & mask1 and bit2= motvar & mask2, where the masks are MOTS
  // containing a single 1, in positions bitpos and bitpos+1, such that:
  // let numvarinmot=var % nbvarparmot, which can be written as 
  // numvarinmot=var & (nbvarparmot-1);
  // bitpos = 2*((nbvarparmot-1)-numvarinmot) (since variable 0 is on the 2 bits of
  // most heavy weight, not bits number 0 and 1)
  // example: if MOT==char (size 8 bits), variables 0-3 are coded on a MOT as follows:
  // v0  v1  v2  v3    (assuming values are v0->00, v1->11, v2->01, v3->00)
  // 0 0 1 1 0 1 0 0
  // in this example, for v0 numvarinmot=0, therefore bitpos=6, which is correct:
  // the bits are in positions 6 (for bit1) and 7 (for bit2)
  //
  // In fact, bitpos can be calculated directly as follows.
  // This works because the "& (nbvarparmot-1)" part ensures we only
  // use the bits of ~var of lowest weight, upto the binary coding of nbvarparmot-1.
  // furthermore, using ~var means that we do the required inversion of order
  // (equivalent to (nbvarparmot-1)-numvarinmot).
  int bitpos = (~var) & (nbvarparmot-1) ;
  bitpos = bitpos << 1 ; // multiply by 2


  /* following test commented: testing that ARCH is set correctly
     should be done once and for all, in the main. */
  /*
  if (sizeof(MOT)*CHAR_BIT != ARCH)
    {
      fprintf(stderr, "in getpoolvalue, cannot use current tabmask because too many bits in MOT\n") ;
      exit(1) ;
    }
  */


  /* old code not using tabmask:
     MOT mask1;
     MOT mask2;
     //mask1=(int)(pow(2,bitpos));
     //mask2=(int)(pow(2,bitpos+1));
     // pour des puissances de 2, il vaut mieux faire des decalages:
     mask1 = ((MOT)1) << bitpos;
     mask2 = ((MOT)1) << (bitpos+1);
  */
  /* using tabmask we can do immediately:
     MOT bit1 = motvar & tabmask[bitpos] ;
     MOT bit2 = motvar & tabmask[bitpos+1] ;
     
    if(bit1==0)
      if (bit2==0)
	return 00;
      else
	return 10 ;
    else
      if (bit2==0)
	return 01;
      else
	return 11 ;
  */
  /* previous calculation of return value is not optimal: better way is:
     int bit1 = (motvar & mask1) ? 1 : 0 ;
     int bit2 = (motvar & mask2) ? 10 : 0 ;
     return (bit1+bit2) ;
  */
  /* or even more compactly: */
  return( ( (motvar & tabmask[bitpos]) ? 1 : 0 ) 
	  + ( (motvar & tabmask[bitpos+1]) ? 10 : 0 ) ) ;
}


///////////////////////////////////////////////////////////////
// set the value of variable var in pool to value.
// value should be 0, 1, 10 or 11 (stored on 2 bits in pool)
void setpoolvalue(MOT* pool, int var, int value)
{
  static int nbvarparmot = (sizeof(MOT) * CHAR_BIT) >> 1 ;

  /* attempt at optimization: store statically the masks */
  static MOT tabmask[ARCH+1] = MASKS ;
  
  /* get the MOT that holds var */
  MOT* poolmot=motpourvar(pool,var);
  
  // as in getpoolvalue, calculate the bitpos for var
  int bitpos = (~var) & (nbvarparmot-1) ;
  bitpos = bitpos << 1 ; // mult by 2
  
  MOT valLow = tabmask[bitpos] ; // low-weight bit for var
  MOT valHigh = tabmask[bitpos+1] ; // high-weight bit for var
  
  // now set the value of poolmot:
  // if value for a bit is 0, use & ~; if it is 1, use |
  if (value >= 10)
    {
      // activate high weight bit
      *poolmot = *poolmot | valHigh ;
      value -= 10 ; // substract 10, so value should now be 0 or 1
    }
  else
    // inactivate high weight bit
    *poolmot = *poolmot & (~valHigh) ;
  
  if (value==1)
    // activate low-weight
    *poolmot = *poolmot | valLow ;
  else if (value==0)
    // inactivate low-weight
    *poolmot = *poolmot & (~valLow);
  else
    {
      fprintf(stderr, "Error in setpoolvalue: value is %d, should be 0, 1, 10 or 11\n", value);
      exit (1);
    }
}

