// file: normalize_features.cc
//
// this program does side-based cepstrum mean subtraction (cms) to
// the input mfcc files which were generated without mean subtraction,
//
//  synopsis: normalize_features.exe -input input.list -output output.list
//   -option: -input_mode binary
//            -output_mode binary
//            -num_coeffs 39
//            -num_subtract 12
//            -num_div 39

// system include files
//
#include <string.h>

// isip include files
//
#include "normalize_features.h"
#include "normalize_features_constants.h"

// main program
//
int main(int_4 argc, char_1** argv) {
  
  // default input and output mode: binary
  //
  int_4 input_mode = NF_DEF_IN_MODE;          // input mode
  int_4 output_mode = NF_DEF_OUT_MODE;        // output mode

  // default number of coefficients
  //
  int_4 num_coeffs = NF_DEF_NUM_COEFFS;
  int_4 num_subtract = NF_DEF_NUM_SUB;
  int_4 num_div = NF_DEF_NUM_DIV;
  
  // allocate memory for the input/output file name
  //
  char_1* input_file = new char_1[ISIP_MAX_FNAME_SIZE];
  char_1* output_file = new char_1[ISIP_MAX_FNAME_SIZE];

  // get the command line parameters
  //
  if (get_parameter_cc(argc, argv, input_file, output_file,
		       num_coeffs, num_subtract, num_div,
		       input_mode, output_mode) == ISIP_FALSE) {
    
    // output error message and exit
    //
    fprintf(stderr, "%s : Error reading command line parameters\n",
	    argv[0]);

    exit(ISIP_PROTO_ERROR);
  }

  // check for disambiguities in input parameters
  //
  if (num_coeffs <= 0) {
    printf("Total number of coefficients should be greater than zero\n");
    exit(ISIP_PROTO_ERROR);    
  }
  if (num_subtract > num_coeffs) {
    printf("Error: Number of coefficients to be subtracted cannot be greater than the total number of coefficients in the feature vector\n");
    exit(ISIP_PROTO_ERROR);
  }

  if (num_div > num_coeffs) {
    printf("Error: Number of coefficients to be divided cannot be greater than the total number of coefficients in the feature vector\n");
    exit(ISIP_PROTO_ERROR);
  }  

  
  // allocate memory for input and output vectors
  //
  float_8 mean_vector[num_coeffs];
  float_8 sum_of_squares_vector[num_coeffs];
  float_8 square_of_sum_vector[num_coeffs];
  float_8 variance_vector[num_coeffs];
  float_8 std_dev_vector[num_coeffs];    
  float_8* output_vector = (float_8*)NULL;
  int_4 count = 0;
  int_4 prev_count = 0;

  for (int_4 j = 0; j < num_coeffs; j++) {
    mean_vector[j] = 0;
    sum_of_squares_vector[j] = 0;
    square_of_sum_vector[j] = 0;
    variance_vector[j] = 0;
    std_dev_vector[j] = 0;
  }
  
  // open the files containing a list of file names
  //
  FILE* fin = fopen((char*)input_file, "r");
  if (fin == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open input file %s\n", input_file);
    exit(ISIP_PROTO_ERROR);
  }
  
  FILE* fout = fopen((char*)output_file, "r");
  if (fout == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open output file %s\n", output_file);
    exit(ISIP_PROTO_ERROR);
  }

  // count how many files we are going to process
  //
  int_4 num_files = 0;
  while (fgets((char*)input_file, ISIP_MAX_STRING_LENGTH, fin) !=
	 (char*)NULL) {

    if (fgets((char*)output_file, ISIP_MAX_STRING_LENGTH, fout)
	== (char*)NULL) {
      fprintf(stdout, "Error: mismatch in the number of input output files\n");
      exit(ISIP_PROTO_ERROR);
    }

    num_files++;
  }
  
  rewind(fin);
  rewind(fout);
  
  // declare an array to store the number of feature vectors in each file
  //
  int_4 vector_counts[num_files];
  int_4 file_ind = 0;

  // loop through the input file list and accumulate the mean and
  // sum of squares
  //
  while (fgets((char*)input_file, ISIP_MAX_STRING_LENGTH, fin) !=
	 (char*)NULL) {
    
    // chop the end of the line
    //
    int len = strlen((char*)input_file);
    if (input_file[len - 1] == '\n') {
      input_file[len - 1] = '\0';
    }
    
    // accumulate the statistics
    //
    accumulate_stats_cc(mean_vector, sum_of_squares_vector, count, num_coeffs,
		       input_file, input_mode);

    // remember how many feature vectors in this file
    //
    vector_counts[file_ind] = count - prev_count;
    prev_count = count;
    file_ind++;
    
  } // end of while loop 


  // calculate the statistics: square of the sum, variance, mean and 
  // standard deviation
  //
  for (int_4 j = 0; j < num_coeffs; j++) {
    square_of_sum_vector[j] = (mean_vector[j] *
			       mean_vector[j]) / (float_8)count;
    variance_vector[j] = (sum_of_squares_vector[j] -
			  square_of_sum_vector[j]) / (float_8)(count - 1);

    // check if the variance is less than zero
    //
    if (variance_vector[j] < 0.0) {
      printf(" Error: Variance is less than zero\n");
      exit(ISIP_PROTO_ERROR);
    }

    // find the standard deviation
    //
    else {
      std_dev_vector[j] = sqrt(variance_vector[j]);
    }

    // calculate the mean
    //
    mean_vector[j] /= (float_8)count;
  }
  
  // zero off the mean elements greater than num_subtract
  // this is done so that even on subtracting the whole mean vector
  // result is not affected
  //
  for (int_4 j = num_subtract; j < num_coeffs; j++) {
    mean_vector[j] = 0.0;
  }
  
  // make the elements of std deviation vector equal to one
  // if the element index is greater than the number of elements
  // to be divided
  //
  for (int_4 j = num_div; j < num_coeffs; j++) {
    std_dev_vector[j] = 1.0;
  }

  // reset counts and loop the file list again and subtract the mean
  // and divide the features by the standard deviation
  // 
  rewind(fin);  
  file_ind = 0;
  
  // loop through the file list
  //
  while (fgets((char*)input_file, ISIP_MAX_STRING_LENGTH, fin) !=
	 (char*)NULL) {
    
    // get the output file lists
    //
    fgets((char*)output_file, ISIP_MAX_STRING_LENGTH, fout);
    
    output_vector = new float_8[vector_counts[file_ind] * num_coeffs];
    
    // chop the end of the line
    //
    int len = strlen((char*)input_file);
    if (input_file[len - 1] == '\n') {
      input_file[len - 1] = '\0';
    }

    len = strlen((char*)output_file);
    if (output_file[len - 1] == '\n') {
      output_file[len - 1] = '\0';
    }

    // subtract mean and divide by the standard deviation from the features
    // in this input file, and output as a big vector
    //
    normalize_features_cc(output_vector, mean_vector, std_dev_vector,
			  num_coeffs, input_file, vector_counts[file_ind],
			  input_mode);

    // write the new features into file
    //
    write_cc(output_vector, vector_counts[file_ind], num_coeffs,
	     output_file, output_mode);

    // increase the file index
    //
    file_ind++;

    // free memory
    //
    delete [] output_vector;
    
  } // end of while loop 
      
  // clean up
  //
  fclose(fin);
  fclose(fout);	
  delete [] input_file;
  delete [] output_file;
   
  // return without error
  //
  return ISIP_NO_ERROR;
}
