/*----------------------------------------------------------------*
 *
 * File : closure.c
 * Author : NTM
 * Created : 17/11/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 <stdio.h> // for printing
#include <stdlib.h> // malloc, exit, qsort, ...
#include <string.h> /* memcpy */

#include "types.h" /* MOT */
#include "signa.h" /* signature, copysig, getsigvalue, setsigvalue, SIG_*, setOfSigs */
#include "pools.h" /* pool, getpoolvalue */
#include "poolInVector.h" /* nbMotsPerPoolsVec, getPoolInVector */

#include "closure.h"


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

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

/*!
  \brief Return a copy of myClosure. Content is copied into fresh memory.
*/
static closure* copyClosure(closure* myClosureP, int nbPools) ;


/*!
  \brief Add myClosure to the set of closures by copy, but don't bother
  checking for redundant entries: this must be done afterwards if required.
*/
static void addToSetOfClosures(closure* myClosureP, setOfClosures* mySetP, int nbPools) ;


/*!
  \brief Turn myClosures into an empty set (and free all useless memory)
*/
static void emptySetOfClosures(setOfClosures* myClosuresP) ;


/*!
  \brief Given one closure and the enriched sig, build the corresponding
  interpretation (ie, only SIG_POS and SIG_NEG values).
*/
static signature* closureToSig(closure* myClosureP, signature* enrichedSig) ;


/*
  Compare 2 closures (for use by qsort). 
  This is for removing duplicate closures: the sort order
  is not important, but return value 0 must mean that
  all content is identical.
  HUGE CAVEAT: this function uses compareVectors, which needs to know
  nbMots. However, since this is for qsort we cannot accept nbMots as
  a parameter! Therefore before using this function, BE SURE TO WRITE:
  (void) compareVectors(NULL, NULL, nbMots) ;
  where nbMots == nbMotsPerPoolsVec(nbPools).
  This way, compareVectors will save the nbMots value (in a static) and be
  able to use it when called by compareClosuresFull.
  Failure to do so will result in garbage and errors!
*/
static int compareClosures(const void* closure1PP, const void* closure2PP) ;


/*
  Compare 2 blocks representing negPools, or other xxxPools 
  (of nbMots each), and return 0 if content is identical, 
  and 1 or -1 otherwise. 
  We don't care what the order is, because this function is 
  used exclusively to remove duplicate closures... Therefore 
  this must simply define an order relation: we just compare 
  each pair of corresponding MOTs, and return the comparison 
  result as soon as a difference is found.
  NOTE: this is for qsort via compareClosures, we have to use
  a lousy trick: calling compareVectors(NULL, ?, nbMots) must 
  simply save nbMots in a static, and subsequent calls to 
  compareVectors(vec1, vec2, 0) will use the saved value.
  Typically, we should have nbMots = nbMotsPerPoolsVec(nbPools).
*/
static int compareVectors(MOT* vector1, MOT* vector2, int nbMots) ;



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

/*!
  \brief Return a copy of myClosure. Content is copied into fresh memory.
*/
static closure* copyClosure(closure* myClosureP, int nbPools)
{
  /* allocate all memory */
  closure* copiedClosureP = (closure*)malloc(sizeof(closure)) ;
  if (copiedClosureP==NULL)
    {
      fprintf(stderr, "In copyClosure, no more memory for copiedClosureP\n") ;
      exit(1) ;
    }

  int bytesInPools = nbMotsPerPoolsVec(nbPools) * sizeof(MOT) ;

  copiedClosureP->negPools = (MOT*)malloc(bytesInPools) ;
  if (copiedClosureP->negPools == NULL)
    {
      fprintf(stderr, "In copyClosure, no more memory for negPools\n") ;
      exit(1) ;
    }

  copiedClosureP->posPools = (MOT*)malloc(bytesInPools) ;
  if (copiedClosureP->posPools == NULL)
    {
      fprintf(stderr, "In copyClosure, no more memory for posPools\n") ;
      exit(1) ;
    }

  /* now copy all content */
  memcpy(copiedClosureP->negPools, myClosureP->negPools, bytesInPools) ;
  memcpy(copiedClosureP->posPools, myClosureP->posPools, bytesInPools) ;
  copiedClosureP->negPoolsCost = myClosureP->negPoolsCost ;
  copiedClosureP->score = myClosureP->score ;

  return (copiedClosureP) ;
}


/*!
  Add myClosure to the set of closures by copy, but don't bother
  checking for redundant entries: this must be done afterwards if required.
  Note: if mySet is empty (ie nbOfClosures==0 and allClosuresP==NULL), this
  function works as expected and mySet becomes a set of one closure.
*/
static void addToSetOfClosures(closure* myClosureP, setOfClosures* mySetP, int nbPools)
{
  mySetP->nbOfClosures++ ;
  /* realloc to store one extra closure pointer */
  mySetP->allClosuresP = (closure**)realloc(mySetP->allClosuresP, 
					       mySetP->nbOfClosures * sizeof(closure*)) ;
  if (mySetP->allClosuresP == NULL)
    {
      fprintf(stderr, "in addToSetOfClosures, no more memory for allClosuresP\n") ;
      exit(1) ;
    }

  /* copy myClosure into fresh memory */
  closure* copiedClosureP = copyClosure(myClosureP, nbPools) ;
  /* finally make the last pointer of allClosuresP point on this new closure */
  mySetP->allClosuresP[mySetP->nbOfClosures - 1] = copiedClosureP ;
}


/*!
  Turn myClosures into an empty set:
  Free all closures in the set, and the mem area for the table
  of closure pointers allClosuresP, and set to 0,NULL
*/
static void emptySetOfClosures(setOfClosures* myClosuresP)
{  
  int i ;
  for (i=0; i < myClosuresP->nbOfClosures; i++)
    freeClosure(myClosuresP->allClosuresP[i]) ;

  if (myClosuresP->nbOfClosures > 0)
    free(myClosuresP->allClosuresP) ;

  myClosuresP->nbOfClosures = 0 ;
  myClosuresP->allClosuresP = NULL ;
}


/*!
  Given one closure and the enriched sig, build the corresponding
  interpretation (ie, only SIG_POS and SIG_NEG values):
  - any pool present in myClosureP->negPools (should be conflicting
  neg or faint in enrichedSig) becomes POS;
  - any pool present in myClosureP->posPools (should be conflicting 
  pos or weak in enrichedSig) also becomes POS;
  - any remaining conflicting pool becomes NEG;
  - all other pools are non-conflicting: they are cleaned up to 
  their canonical interpretation value (ie weak becomes POS and
  faint becomes NEG).

  NOTE: we don't bother checking that pools present in 
  myClosureP were actually marked conflicting in enrichedSig.
*/
static signature* closureToSig(closure* myClosureP, signature* enrichedSig)
{
  signature* goodSig = copysig(enrichedSig) ;

  int nbPools = enrichedSig->nbPools ;
  MOT* negPools = myClosureP->negPools ;
  MOT* posPools = myClosureP->posPools ;

  /* change all pools from myClosureP to POS,
     all other conflicting pools to NEG,
     and all non-conflicting pools to their canonical interpretation. */
  int absPoolNum ;
  for(absPoolNum=0; absPoolNum<nbPools; absPoolNum++)
    {
      if (getPoolInVector(negPools, absPoolNum) == 1)
	setsigvalue(goodSig, absPoolNum, SIG_POS) ;
      else if (getPoolInVector(posPools, absPoolNum) == 1)
	setsigvalue(goodSig, absPoolNum, SIG_POS) ;
      else
	{
	  int sigvalue = getsigvalue(goodSig, absPoolNum) ;
	  if ( (sigvalue == SIG_POSCONF) || (sigvalue == SIG_WEAKCONF) ||
	       (sigvalue == SIG_FAINTCONF) || (sigvalue == SIG_NEGCONF) )
	    // pool was conflicting, change to NEG
	    setsigvalue(goodSig, absPoolNum, SIG_NEG) ;
	  else if (sigvalue == SIG_WEAK)
	    setsigvalue(goodSig, absPoolNum, SIG_POS) ;
	  else if (sigvalue == SIG_FAINT)
	    setsigvalue(goodSig, absPoolNum, SIG_NEG) ;
	  // else sigvalue was POS or NEG, do nothing
	}
    }
  
  return(goodSig) ;
}


/*
  Compare 2 closures (for use by qsort). 
  This is for removing duplicate closures: the sort order
  is not important, but return value 0 must mean that
  all content is identical.
  HUGE CAVEAT: this function uses compareVectors, which needs to know
  nbMots. However, since this is for qsort we cannot accept nbMots as
  a parameter! Therefore before using this function, BE SURE TO WRITE:
  (void) compareVectors(NULL, NULL, nbMots) ;
  where nbMots == nbMotsPerPoolsVec(nbPools).
  This way, compareVectors will save the nbMots value (in a static) and be
  able to use it when called by compareClosuresFull.
  Failure to do so will result in garbage and errors!
*/
static int compareClosures(const void* closure1PP, const void* closure2PP)
{
  closure* closure1P = (*(closure**)closure1PP) ;
  closure* closure2P = (*(closure**)closure2PP) ;
  
  int retval ; /* will be returned */

  /* we could start be comparing scores, but this function 
     should usually be called on pairs of highest-scoring
     closures, hence scores will be identical...
     So, we don't start with scores. And when we're
     done examining the negPools and posPools, if
     we have no difference the scores must also be equal.
     Therefore in fact we never examine the scores. */

  /* first compare negPoolsCost */
  if (closure1P->negPoolsCost < closure2P->negPoolsCost)
    retval = -1 ;
  else if (closure1P->negPoolsCost > closure2P->negPoolsCost)
    retval = 1 ;
  else
    {
      /* get negPools vectors for each arg */
      MOT* negPools1 = closure1P->negPools ;
      MOT* negPools2 = closure2P->negPools ;

      /* then just use compareVectors, with nbMots==0 : this tells 
	 compareVectors to actually compare the vectors... 
	 ugly, I know... */
      retval = compareVectors(negPools1, negPools2, 0) ;
      if (retval == 0)
	{
	  /* still identical! compare posPools */
	  MOT* posPools1 = closure1P->posPools ;
	  MOT* posPools2 = closure2P->posPools ;
	  retval = compareVectors(posPools1, posPools2, 0) ;
	}
    }

  return(retval) ;
}


/*
  Compare 2 blocks representing negPools, or other xxxPools 
  (of nbMots each), and return 0 if content is identical, 
  and 1 or -1 otherwise. 
  We don't care what the order is, because this function is 
  used exclusively to remove duplicate closures... Therefore 
  this must simply define an order relation: we just compare 
  each pair of corresponding MOTs, and return the comparison 
  result as soon as a difference is found.
  NOTE: this is for qsort via compareClosures, we have to use
  a lousy trick: calling compareVectors(NULL, ?, nbMots) must 
  simply save nbMots in a static, and subsequent calls to 
  compareVectors(vec1, vec2, 0) will use the saved value.
  Typically, we should have nbMots = nbMotsPerPoolsVec(nbPools).
*/
static int compareVectors(MOT* vector1, MOT* vector2, int nbMots)
{
  static int nbMotsSaved ;

  /* check params */
  if (vector1==NULL)
    {
      /* save nbMots value and return */
      nbMotsSaved = nbMots ;
      return(0) ; /* return value will be discarded anyways */
    }

  else if (nbMots!=0)
    {
      fprintf(stderr, "compareVectors called with vector1 non-NULL and nbMots!=0: DON'T DO THIS!\n") ;
      exit(1) ;
    }

  else
    {
      /* recall saved values, then compare contents */
      nbMots = nbMotsSaved ;

      int retval = 0 ; /* return value, default to 0 in case no 
			  differences are found */

      int i ;
      for (i=nbMots; i>0; i--)
	{
	  if (*vector1 < *vector2)
	    {
	      retval = -1 ;
	      break ;
	    }
	  else if (*vector1 > *vector2)
	    {
	      retval = 1 ;
	      break ;
	    }
	  /* else current MOTs are equal, try the next ones */
	  else
	    {
	      vector1++ ;
	      vector2++ ;
	    }
	}
      return(retval) ;
    }
}


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

/***********************************************************
 * for closure datatype
 ***********************************************************/

/*!
  \brief Build and return a pointer to an empty closure (ie negPools 
  and posPools are empty (NOT NULL!), and negPoolsCost==score==0).
*/
closure* buildEmptyClosure(int nbPools)
{
  /* allocate all memory */

  closure* newClosureP = (closure*)malloc(sizeof(closure)) ;
  if (newClosureP==NULL)
    {
      fprintf(stderr, "in buildEmptyClosure, no more memory for newClosureP\n") ;
      exit(1) ;
    }

  int bytesInPools = nbMotsPerPoolsVec(nbPools) * sizeof(MOT) ;

  newClosureP->negPools = (MOT*)malloc(bytesInPools) ;
  if (newClosureP->negPools == NULL)
    {
      fprintf(stderr, "In buildEmptyClosure, no more memory for negPools\n") ;
      exit(1) ;
    }
  newClosureP->posPools = (MOT*)malloc(bytesInPools) ;
  if (newClosureP->posPools == NULL)
    {
      fprintf(stderr, "In buildEmptyClosure, no more memory for posPools\n") ;
      exit(1) ;
    }

  /* initialize to 0 */
  memset(newClosureP->negPools, 0, bytesInPools) ;
  memset(newClosureP->posPools, 0, bytesInPools) ;
  newClosureP->negPoolsCost = 0 ;
  newClosureP->score = 0 ; 

  return(newClosureP) ;
}


/*!
  \brief Free all memory relevant to myClosureP. <br>
  We free the vectors, and also the closure area itself: don't try
  accessing or using the closure after this!
*/
void freeClosure(closure* myClosureP)
{
  /* free xxxPools */
  free(myClosureP->negPools) ;
  free(myClosureP->posPools) ;
  /* and free the closure itself */
  free(myClosureP) ;
}


/***********************************************************
 * for setOfClosures datatype
 ***********************************************************/

/*!
  Return an empty setOfClosures (mem allocated here).
  content is (O, NULL).
*/
setOfClosures* buildEmptySetOfClosures(void)
{
  setOfClosures* mySetP = (setOfClosures*)malloc(sizeof(setOfClosures)) ;
  if (mySetP==NULL)
    {
      fprintf(stderr, "in buildEmptySetOfClosures, no more memory\n") ;
      exit(1) ;
    }
  mySetP->nbOfClosures = 0 ;
  mySetP->allClosuresP = NULL ;
  return(mySetP) ;
}


/*
  free all memory in mySetP, including the setOfClosures itself.
*/
void freeSetOfClosures(setOfClosures* mySetP)
{
  int i ;
  for (i=0; i < mySetP->nbOfClosures; i++)
    freeClosure(mySetP->allClosuresP[i]) ;

  free(mySetP->allClosuresP) ;

  free(mySetP) ;
}


/*!
  Update *bestClosuresP by possibly emptying it 
  and copying currentClosureP into it, 
  if currentClosureP->score is a current best.<br>
  bestScore is the score of the bestClosuresP closures.<br>
  Returns the new best score (==bestScore if current was not 
  better than previous best).
*/
int updateBestSetOfClosures(closure* currentClosureP, setOfClosures* bestClosuresP,
			    int bestScore, int nbPools)
{
  /* compare score of currentClosure with bestScore, update if needed */
  if (currentClosureP->score >= bestScore)
    {
      /* if current is better, empty previous set before adding current to it */
      if (currentClosureP->score > bestScore)
	{
	  emptySetOfClosures(bestClosuresP) ;
	  bestScore = currentClosureP->score ;
	}

      addToSetOfClosures(currentClosureP, bestClosuresP, nbPools) ;
    }
  return(bestScore) ;
}


/*!
  \brief Remove any duplicates in *myClosuresP if there are any.

  We use compareClosures to sort the closures: therefore, any
  duplicates will be neighbours, and we can then eliminate them easily.
  NOTE: we use the ugly compareVectors nbMots caching thing (it was that 
  or rewrite a sort algorithm instead of qsort... see compareClosures).
*/
void removeDuplicateClosures(setOfClosures* myClosuresP, int nbPools)
{
  int initialNbOfC = myClosuresP->nbOfClosures ;
  if (initialNbOfC>0)
    {
      /* initialNbOfC==0 probably shouldn't happen, but just in case... */
      
      /* initialize nbMots in compareVectors */
      (void) compareVectors(NULL, NULL, nbMotsPerPoolsVec(nbPools)) ;
      
      /* sort the closures */
      qsort(myClosuresP->allClosuresP, initialNbOfC, 
	    sizeof(closure*), compareClosures) ;

      /* now duplicates are neighbours, simply compare each closure to 
	 its predecessor */
      closure* previousClosureP = myClosuresP->allClosuresP[0] ;
      
      /* identify and remove dupes */
      {
	int nextSlot = 1 ; /* next empty slot, where the next non-dupe should go */
	int i ;
	for (i = 1 ; i < initialNbOfC ; i++)
	  {
	    closure* currentClosureP = myClosuresP->allClosuresP[i] ;
	    if (compareClosures(&previousClosureP, &currentClosureP) != 0)
	      {
		/* closures are different: save current in nextSlot */
		myClosuresP->allClosuresP[nextSlot] = currentClosureP ;
		nextSlot++ ;
		previousClosureP = currentClosureP ;
	      }
	    else
	      {
		/* closures are identical: free current */
		freeClosure(currentClosureP) ;
		myClosuresP->nbOfClosures-- ;
	      }
	  }
	/* sanity check */
	if (nextSlot != myClosuresP->nbOfClosures)
	  {
	    fprintf(stderr, 
		    "in removeDuplicateClosures, sanity check fails: nextSlot=%d, nbOfClosures=%d. DEBUG ME!\n",
		    nextSlot, myClosuresP->nbOfClosures) ;
	    exit(1) ;
	  }
      }
    }
}


/*!
  \brief Given a (non-empty) set of best-scoring closures and 
  an enriched sig, convert the closures to a setOfSigs (which
  are interpretations: they only have SIG_POS or SIG_NEG).
  costOfConfPW is the total cost (ie distance) of all
  conflicting pos or weak pools in enrichedSig.
*/
setOfSigs allClosuresToSigs(setOfClosures* bestClosuresP, int costOfConfPW,
			    signature* enrichedSig)
{
  int nbOfClosures = bestClosuresP->nbOfClosures ;

  /* bestClosures should always hold at least one closure. die if it doesn't */
  if (nbOfClosures==0)
    {
      fprintf(stderr, "in closuresToSigs: nbOfClosures is 0, shouldn't happen!\n") ;
      exit(1) ;
    }

  setOfSigs bestSigs = buildEmptySetOfSigs() ;

  int closurenum ;
  for (closurenum=0; closurenum<nbOfClosures; closurenum++)
    {
      signature* thisSig = closureToSig(bestClosuresP->allClosuresP[closurenum], enrichedSig) ;
      addSigToSet(&bestSigs, thisSig) ;
      /* don't free thisSig, addSigToSet doesn't copy it */
    }
  
  /* distance of the sigs is costOfConfPW - score, where score is the score
     of any maximal-scoring closure. Since bestClosures is supposed
     to hold only maximal-scoring closures, use the first closure to
     calculate the distance. */
  bestSigs.distance = costOfConfPW - bestClosuresP->allClosuresP[0]->score ;

  return(bestSigs) ;
}

