
/******************************************************************

 This software package delivers a scoring tool, called lf_evaluator,
 for Logic Form Identification (LFI).

 Usage: lf_evaluator gold_standard_file.lf evaluated_file.lf
 where: - gold_standard_file.lf is a file containing the correct logic forms
        - evaluated_file.lf is a file that is to be evaluated
 
 Output: lf_evaluator reports four measures:
             - predicate level precision
             - predicate level recall
             - argument level precision
             - argument level recall

 ******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_INPUT_LINE 4096
#define MAX_ARGUMENTS   128
#define MAX_PREDICATES  128

typedef struct predicate{
  char wordPredicate[MAX_INPUT_LINE];
  char pos[MAX_INPUT_LINE];
  char  arguments [MAX_ARGUMENTS][6];
  int nrOfArguments;
  int valid;
} Predicate;

typedef struct predicates{
  Predicate *predicates[MAX_PREDICATES];
  int nrOfPredicates;
} Predicates;

typedef struct mapping{
  char argFirst[6];
  char argSecond[6];
} Mapping;

typedef struct results{
  int overallMatchingAtPosition;
  int overallGoldArguments;
  int overallEvaluatedArguments;
  int overallGoldPredicates;
  int overallEvaluatedPredicates;
  int overallMatchingPredicates;
} Results;

/**********************************************************
 * TITLE: initPredicate
 *
 * DESCRIPTION:  initializes a structure of type Predicate
 * PARAMETERS:   predicate - pointer to the structure to
 *                           be initialized
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void initPredicate(Predicate *predicate){

  int i;

  strcpy(predicate -> wordPredicate, "");
  strcpy(predicate -> pos, "");

  for (i = 0; i < MAX_ARGUMENTS; i++){
    strcpy (predicate -> arguments [i], "");
  }

  predicate -> nrOfArguments = 0;
  predicate -> valid = 1; // at first, every predicate is considered valid

  return;

}

/**********************************************************
 * TITLE: displayPredicate
 *
 * DESCRIPTION:  displays a structure of type Predicate
 * PARAMETERS:   predicate - pointer to the structure
 *                           to be displayed
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void displayPredicate(Predicate *predicate){

  int i;

  printf("%s:%s_", predicate -> wordPredicate, predicate -> pos);
  printf("(");
  for (i = 0; i < predicate -> nrOfArguments; i++){
    if ( 0 == i)
      printf("%s", predicate -> arguments [i]);
    else
      printf(", %s", predicate -> arguments [i]);
  }
  printf(")");

  return;

}

/**********************************************************
 * TITLE: initPredicates
 *
 * DESCRIPTION:  initializes a structure of type Predicates
 * PARAMETERS:   predicates - pointer to the structure
 *                           to be initialized
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void initPredicates(Predicates *predicates){

  int i;

  for(i=0; i<MAX_PREDICATES; i++){
    predicates -> predicates [i] = NULL;
  }
  predicates -> nrOfPredicates = 0;

  return;

}

/**********************************************************
 * TITLE: freePredicates
 *
 * DESCRIPTION:  frees a structure of type Predicates
 * PARAMETERS:   predicates - pointer to the structure
 *                           to be freed
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void freePredicates(Predicates *predicates){

  int i;

  for(i=0; i<predicates -> nrOfPredicates; i++){
    if ( NULL != predicates -> predicates [i] )
      free(predicates -> predicates [i]);
  }
  predicates -> nrOfPredicates = 0;

  return;

}

/**********************************************************
 * TITLE: displayPredicates
 *
 * DESCRIPTION:  displays a structure of type Predicates
 * PARAMETERS:   predicates - pointer to the structure
 *                           to be displayed
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void displayPredicates(Predicates *predicates){

  int i;

  printf("The predicates are: ");

  for(i=0; i<predicates -> nrOfPredicates; i++){
    if ( NULL != predicates -> predicates [i] ){
      displayPredicate(predicates -> predicates [i]);
      printf(" ");
    }
  }

  printf("\n");

  return;

}

/**********************************************************
 * TITLE: getPredicates
 *
 * DESCRIPTION:  extracts from input parameter line 
 *               predicates and places them in a structure
 *               of type Predicates
 * PARAMETERS:   predicates - pointer to the structure
 *                          to be filled (output parameter)
 *               line - a line containing a sentence in LF
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void getPredicates(Predicates *predicates, char *line){

  char *startPredicate, *endPredicate, *currPtr, *pc;
  char predicate [MAX_INPUT_LINE];
  char wordPredicate[MAX_INPUT_LINE];
  char pos[MAX_INPUT_LINE];
  int firstTime;

  currPtr = line;
  startPredicate = currPtr;
  endPredicate = currPtr;

  // extract predicate
  firstTime = 1;
  while (*currPtr != '\0'){

    // skip white spaces
    while ( (*currPtr != '\0') && (isspace(*currPtr)) ) currPtr++ ;

    if ( (*currPtr == '\0') && (firstTime) ){
      printf("There is a sudden termination of line %s. Exit.", line);
      printf("Warning: No empty lines are allowed in input files\n.");
      exit(0);
    }

    startPredicate = currPtr;

    // locate end of predicate
    while ( (*currPtr != '\0') && (*currPtr != ')') ) currPtr ++;
    if ( (*currPtr == '\0') && (firstTime) ){
      printf("There is a ) missing in line %s. Exit", line);
      exit(0);
    } else 

      // if end of input was reached 
      // then avoid processing any further predicates
      if ( *currPtr != '\0'){

	endPredicate = currPtr;

	// move after '('
	currPtr++;

	strcpy(predicate, "");
	strncpy(predicate, startPredicate, endPredicate - startPredicate + 1);
	predicate [endPredicate - startPredicate + 1] = '\0';
	
	// printf ("Processing predicate %s (%d)", predicate, predicates -> nrOfPredicates + 1);
	// printf ("\n");

	predicates -> nrOfPredicates = predicates -> nrOfPredicates + 1;
	if ( NULL == (predicates -> predicates [predicates -> nrOfPredicates - 1] = (Predicate *) malloc (sizeof(Predicate)))){
	  printf("Out of Memory. Exit.\n");
	  exit(0);
	};
	initPredicate(predicates -> predicates [predicates -> nrOfPredicates - 1]);
	
	strcpy(wordPredicate, "");
	strcpy(pos, "");
	
	// extract word predicate
	pc = startPredicate;
	while ( (*pc != '(') &&
		(*pc != ')') &&
		(*pc != ':') && 
		(*pc != ' ') ) pc ++;
	endPredicate = pc;
	strncpy(wordPredicate, startPredicate, endPredicate - startPredicate);
	wordPredicate [endPredicate - startPredicate] = '\0';
	strcpy(predicates -> predicates [predicates -> nrOfPredicates - 1] -> wordPredicate, wordPredicate);
	
	//printf ("Found word predicate %s", wordPredicate);
	//printf ("\n");
	
	// locate pos if any
	pc = startPredicate;
	while ( (*pc != '(') &&
		(*pc != ')') &&
		(*pc != ':') ) pc ++;
	if ( *pc == ':'){
	  strncpy(pos, pc + 1, 1);
	  pos [1] = '\0';
	}
	
	strcpy(predicates -> predicates [predicates -> nrOfPredicates - 1] -> pos, pos);
	//printf ("Found pos %s", pos);
	//printf ("\n");
	
	// extract arguments starting from where the pos was found
	while ( (*pc != '\0') && (*pc != ')') ){
	  
	  while ( (*pc != '\0') && (*pc != 'x') && (*pc != 'e') ) pc ++;
	  startPredicate = pc;
	  while ( (*pc != '\0') && (*pc != ')')
		  && (*pc != ',') ) pc ++;
	  endPredicate = pc;
	  
	  strncpy(pos, startPredicate, endPredicate - startPredicate);
	  pos [endPredicate - startPredicate] = '\0';
	  predicates -> predicates [predicates -> nrOfPredicates - 1] -> nrOfArguments = predicates -> predicates [predicates -> nrOfPredicates - 1] -> nrOfArguments + 1;
	  strcpy(predicates -> predicates [predicates -> nrOfPredicates - 1] -> arguments [predicates -> predicates [predicates -> nrOfPredicates - 1] -> nrOfArguments - 1] , pos);
	  
	  //printf ("Found argument %s", pos);
	  //printf ("\n");
	  
	}
      }

    firstTime = 0;

  }

  return;

}

/**********************************************************
 * TITLE: getScore
 *
 * DESCRIPTION:  retrieves necessary data from a Predicates
 *               structure to compute the scores
 * PARAMETERS:   goldPredicates - pointer to the structure
 *                           containg the gold/perfect 
 *                           predicates
 *               evaluatedPredicates - pointer to the 
 *                           structure containing the 
 *                           predicates to be scored
 *               overallResults - pointer to a Results
 *                           structure that will record the
 *                           scoring data
 * RETURN VALUE: none
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

void getScore(Predicates *goldPredicates, Predicates *evaluatedPredicates, Results *overallResults){

  int i, j, k;
  Mapping mappings[MAX_ARGUMENTS];
  int nrOfMappings;
  int nrOfEvaluatedArguments = 0, nrOfNotGoldedEvaluatedArguments = 0;
  int nrOfMatchingAtPositionArguments = 0;

  // initialize mappings
  for (i=0; i<MAX_ARGUMENTS; i++){
    strcpy(mappings[i].argFirst, "");
    strcpy(mappings[i].argSecond, "");
  }
  nrOfMappings = 0;
  
  // validate predicates and do mapping based on
  // first argument of each predicate
  for (i = 0; i<evaluatedPredicates -> nrOfPredicates; i++){
    	overallResults -> overallEvaluatedPredicates = overallResults -> overallEvaluatedPredicates + 1;
    for(j = 0; j<goldPredicates -> nrOfPredicates; j++){

      if ( (strcmp(evaluatedPredicates -> predicates [i] -> wordPredicate, goldPredicates -> predicates [j] -> wordPredicate) == 0) 
	   && (strcmp(evaluatedPredicates -> predicates [i] -> pos, goldPredicates -> predicates [j] -> pos) == 0 ) 
	   // not already matched
	   && (1 == evaluatedPredicates -> predicates [i] -> valid)
	   && (1 == goldPredicates -> predicates [j] -> valid) ) {
	evaluatedPredicates -> predicates [i] -> valid ++ ;
	goldPredicates -> predicates [j] -> valid ++ ;
	overallResults -> overallMatchingPredicates = overallResults -> overallMatchingPredicates + 1;
	// check arguments only for validated predicates in evaluatedPredicates
	for(k=0; k<nrOfMappings; k++){
	  if (strcmp(mappings[k].argFirst, evaluatedPredicates -> predicates [i] -> arguments [0]) == 0)
	    break;
	}
	if (k == nrOfMappings){
	  // add new mapping
	  // NOTE: if multiple predicates with the same name are in 
	  // gold predicates, they are matched in order: first with the first
	  // second with the second, and so on.
	  nrOfMappings = nrOfMappings + 1;
	  strcpy(mappings[nrOfMappings - 1].argFirst, evaluatedPredicates -> predicates [i] -> arguments [0]);
	  strcpy(mappings[nrOfMappings - 1].argSecond, goldPredicates -> predicates [j] -> arguments [0]);
	}
      }
    }
  }
  
  // displayMapping
  // printf("******* MAPPING ********\n");
  // for (i = 0; i < nrOfMappings; i++){
  // printf("%s -> %s\n", mappings [i] . argFirst, mappings [i] . argSecond);
  // }
  // printf("******* END OF MAPPING ********\n");
  
  // do the mapping
  for (i = 0; i<evaluatedPredicates -> nrOfPredicates; i++){

    if ( 2 == evaluatedPredicates -> predicates [i] -> valid)
      for(j = 0; j<evaluatedPredicates -> predicates[i] -> nrOfArguments; j++){
	// find the mapping of current argument
	for(k=0; k<nrOfMappings; k++){
	  if (strcmp(mappings[k].argFirst, evaluatedPredicates -> predicates [i] -> arguments [j]) == 0){
	    strcpy(evaluatedPredicates -> predicates [i] -> arguments [j], "g");
	    strcat(evaluatedPredicates -> predicates [i] -> arguments [j], mappings[k].argSecond);
	  }
	}
      }
  }

  // displayPredicates(evaluatedPredicates);

  // count how many positions are not golded
  nrOfNotGoldedEvaluatedArguments = 0;
  nrOfEvaluatedArguments = 0;
  for (i = 0; i<evaluatedPredicates -> nrOfPredicates; i++){
    for(j = 0; j<evaluatedPredicates -> predicates[i] -> nrOfArguments; j++){
      if (evaluatedPredicates -> predicates [i] -> arguments [j][0] != 'g')
	nrOfNotGoldedEvaluatedArguments = nrOfNotGoldedEvaluatedArguments + 1;
      nrOfEvaluatedArguments = nrOfEvaluatedArguments + 1;
    }
  }

  // count arguments and positions
  // validate predicates and do mapping based on
  // first argument of each predicate
  nrOfMatchingAtPositionArguments = 0;
  for (i = 0; i<evaluatedPredicates -> nrOfPredicates; i++){
    for(j = 0; j<goldPredicates -> nrOfPredicates; j++){
      if ( (strcmp(evaluatedPredicates -> predicates [i] -> wordPredicate, goldPredicates -> predicates [j] -> wordPredicate) == 0) 
	   && (strcmp(evaluatedPredicates -> predicates [i] -> pos, goldPredicates -> predicates [j] -> pos) == 0 ) ) {

	// check if arguments and positions match, one by one
	for (k = 0; k < evaluatedPredicates -> predicates [i] -> nrOfArguments; k++){
	  if (k < goldPredicates -> predicates [j] -> nrOfArguments)
	    if ( evaluatedPredicates -> predicates [i] -> arguments [k][0]  == 'g' )
	      if ( 0 == strcmp(evaluatedPredicates -> predicates [i] -> arguments[k] + 1, goldPredicates -> predicates [j] -> arguments [k]) ) {
		// this is a match
		nrOfMatchingAtPositionArguments = nrOfMatchingAtPositionArguments + 1;
	      }
	}
      }
    }
  }

  // printf("Results:\n--------\n");
  // printf("%d(~g) %d(evAll) %d(@)\n", nrOfNotGoldedEvaluatedArguments, nrOfEvaluatedArguments, nrOfMatchingAtPositionArguments);
  
  overallResults -> overallEvaluatedArguments = overallResults -> overallEvaluatedArguments + nrOfEvaluatedArguments;
  overallResults -> overallMatchingAtPosition = overallResults -> overallMatchingAtPosition + nrOfMatchingAtPositionArguments;

  for(i = 0; i<goldPredicates -> nrOfPredicates; i++){
    overallResults -> overallGoldPredicates = overallResults -> overallGoldPredicates + 1;
    for (k = 0; k < goldPredicates -> predicates [i] -> nrOfArguments; k++){
      overallResults -> overallGoldArguments = overallResults -> overallGoldArguments + 1;
    }
  }

  return;

}

/**********************************************************
 * TITLE: main
 *
 * DESCRIPTION:  main entry of the lf_evaluator tool.
 *               It displays the scores.
 * PARAMETERS:   arg 1 - gold standard file
 *               arg 2 - evaluated file
 * RETURN VALUE: 0 - when successfully finished.
 *
 *   DATE          VERSION     AUTHOR
 *  ======================================
 * | 02/07/2001  | 0.1       | Vasile Rus |
 *  ======================================
 *
 **********************************************************/

int main(int argc,
	  char *argv[]){

  FILE *fp_goldStandardFile, *fp_evaluatedFile;
  char goldStandardLine[MAX_INPUT_LINE], evaluatedLine[MAX_INPUT_LINE];
  Predicates goldPredicates, evaluatedPredicates;

  Results overallResults;

  if (argc != 3){
    printf("Wrong number of arguments.\n");
    printf("Usage: lf_evaluator gold_standard_file.lf evaluated_file.lf\n");
    exit(0);
  }

  if ( NULL == (fp_goldStandardFile = fopen(argv[1], "r")) ){
    printf("Could open file %s\n. Exit.\n", argv[1]);
    exit(0);
  }

  if ( NULL == (fp_evaluatedFile = fopen(argv[2], "r")) ){
    printf("Could open file %s\n. Exit.\n", argv[2]);
    exit(0);
  }

  overallResults . overallEvaluatedArguments = 0;
  overallResults . overallMatchingAtPosition = 0;
  overallResults . overallGoldArguments = 0;
  overallResults . overallEvaluatedPredicates = 0;
  overallResults . overallGoldPredicates = 0;
  overallResults . overallMatchingPredicates = 0;

  while( (NULL != fgets(goldStandardLine, MAX_INPUT_LINE, fp_goldStandardFile))
	 && (NULL != fgets(evaluatedLine, MAX_INPUT_LINE, fp_evaluatedFile))
	 ){

    goldStandardLine [strlen(goldStandardLine)] = '\0';
    printf("From %s: %s", argv[1], goldStandardLine);
    initPredicates(&goldPredicates);
    getPredicates( &goldPredicates, goldStandardLine);
    // displayPredicates( &goldPredicates);

    printf("From %s: %s", argv[2], evaluatedLine);
    evaluatedLine [strlen(evaluatedLine)] = '\0';
    initPredicates(&evaluatedPredicates);
    getPredicates( &evaluatedPredicates, evaluatedLine);
    // displayPredicates ( &evaluatedPredicates);

    // score
    getScore( &goldPredicates, &evaluatedPredicates, &overallResults);

    freePredicates(&goldPredicates);
    freePredicates(&evaluatedPredicates);
    printf("\n");

    strcpy(goldStandardLine, "");
    strcpy(evaluatedLine, "");

  }

  printf("\n==============================\n");

  printf("Argument Precision = %f\n", ((float) overallResults.overallMatchingAtPosition)/overallResults.overallEvaluatedArguments);
  printf("Argument Recall = %f\n", ((float) overallResults.overallMatchingAtPosition)/overallResults.overallGoldArguments);
  printf("Predicate Precision = %f\n", ((float) overallResults.overallMatchingPredicates)/overallResults.overallEvaluatedPredicates);
  printf("Predicate Recall = %f\n", ((float) overallResults.overallMatchingPredicates)/overallResults.overallGoldPredicates);

  printf("==============================\n");

  fclose(fp_goldStandardFile);
  fclose(fp_evaluatedFile);

  return 0;

}
