// file: $isip_ifc/class/algo/CorrelationEntropy/correntp_05.cc
// version: $Id: correntp_05.cc
//

// isip include files
//
#include "CorrelationEntropy.h"

// method: apply
//
// arguments:
//  Vector<AlgorithmData>& output: (output) output data
//  const Vector< CircularBuffer<AlgorithmData> >& input: (input) input data
//
// return: a boolean value indicating status
//
// this method calls computation method with appropriate input and
// output data types
//
boolean CorrelationEntropy::apply(Vector<AlgorithmData>& output_a,
			  const Vector< CircularBuffer<AlgorithmData> >&
			  input_a) {

  // determine the number of input channels and force the output to be
  // that number
  //
  long len = input_a.length();
  output_a.setLength(len);
  boolean res = true;

  frame_index_d++;
  
  // start the debugging output
  //
  displayStart(this);

  // loop over the channels and call the compute method
  //
  for (long c = 0; c < len; c++) {

    // display the channel number
    //
    displayChannel(c);

    // call AlgorithmData::makeMatrixFloat to force the output vector for
    // this channel to be a MatrixFloat, call AlgorithmData::getVectorFloat
    // on the input for this channel to check that the input is
    // already a VectorFloat and return that vector.
    //    
    res &= compute(output_a(c).makeVectorFloat(),
		   input_a(c)(0).getVectorFloat(),
		   input_a(c)(0).getCoefType(), c);

    // set the coefficient type for the output
    //    
    output_a(c).setCoefType(AlgorithmData::CORRELATION_ENTROPY);
  }

  // finish the debugging output
  //
  displayFinish(this);

  // exit gracefully
  //
  return res;
}

// method: compute
//
// arguments:
//  VectorFloat& output: (output) Correlation Entropy
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel number
//
// return: a boolean value indicating status
//
// this method computes the correlation entropy from signal
//
boolean CorrelationEntropy::compute(VectorFloat& output_a,
			    const VectorFloat& input_a,
			    AlgorithmData::COEF_TYPE input_coef_type_a,
			    long index_a) {
  
  // declare local variables
  //
  boolean status = false;

  // branch on algorithm
  //  Algorithm: NORMAL
  //
  if (algorithm_d == NORMAL) {
    if (input_coef_type_a == AlgorithmData::SIGNAL) {
      if (implementation_d == CORRELATION_INTEGRAL) {
	status = computeCorrelationEntropy(output_a, input_a);
      }
    }
    else {
      return Error::handle(name(), L"compute", ERR_UNCTYP, __FILE__, __LINE__);
    }
  }
  else {
    return Error::handle(name(), L"compute", ERR_UNKALG, __FILE__, __LINE__);
  }
  
  // possibly display the data
  //
  display(output_a, input_a, name());
  
  // exit gracefully
  //
  return status;
}

// method: computeCorrelationEntropy
// 
// arguments:
//  VectorFloat& output: (output) correlation entropy 
//  const VectorFloat& input: (input) time series
//
//  return: a boolean value indicating status
//
// this method computes the CorrelationEntropy from an input time series
//
boolean CorrelationEntropy::computeCorrelationEntropy(VectorFloat&
						      output_a,
						      const VectorFloat&
						      input_a) {

  // Checking for potential problems before beginning
  //
  if (epsilon_d <= (float) 0) {
    return Error::handle(name(), L"computeCorrelationEntropy", ERR_EPS,
			 __FILE__, __LINE__);
  }

  if (embed_dim_max_d < embed_dim_min_d) {
    return Error::handle(name(), L"computeCorrelationEntropy", ERR_DIM,
			 __FILE__, __LINE__);
  } 

  // Since we are only interested in getting entropy at a single value
  // of epsilon, epsilon_max is only a dummy variable to pass to the
  // CorrelationIntegral object
  //
  float epsilon_max = epsilon_d + (float)1.0;
  
  // Declaring a block to compute the Correlation Integral
  CorrelationIntegral corr_int;
  
  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int.set(CorrelationIntegral::NORMAL,
	       CorrelationIntegral::REGULAR,
	       CorrelationIntegral::LOGSCALE,
	       CorrelationIntegral::LINEAR_EPSILON_SPACING);

  // Setting the corr_int object with an epsilon_resolution of one, since
  // the value of the integral at only one value of epsilon is required
  //
  corr_int.setEpsilon(epsilon_d, epsilon_max, (long)1);
  corr_int.setTheilerCorrection(theiler_correction_d);

  output_a.setLength(embed_dim_max_d - embed_dim_min_d + 1);

  // Declaring a block to compute the Reconstructed Phase Space
  Rps rps_block;

  MatrixFloat rps_x1, rps_x2;
  VectorFloat temp1, temp2;

  for (long m = embed_dim_min_d ; m <= embed_dim_max_d ; m++) {
        
    // Reconstruct the attractor based on the embedding type specified
    //
    if (embedding_d == TIME_DELAY) {

      // Reconstructing the system attractor with an embedding dimension of m
      //
      rps_block.setSampleFrequency(sample_freq_d);    
      rps_block.set(Rps::TIME_DELAY_EMBEDDING,
		    Rps::EMBEDDING, m, m, delay_d);
      rps_block.compute(rps_x1, input_a);

      // Reconstructing the system attractor with an embedding dimension of m+1
      //    
      rps_block.set(Rps::TIME_DELAY_EMBEDDING,
		    Rps::EMBEDDING, m + 1, m + 1, delay_d);     
      rps_block.compute(rps_x2, input_a);
    }
    else if (embedding_d == SVD) {
      
      // Reconstructing the system attractor with an embedding dimension of m
      //
      rps_block.setSampleFrequency(sample_freq_d);    
      rps_block.set(Rps::SVD_EMBEDDING,
		    Rps::EMBEDDING, m, svd_window_size_d, delay_d);
      rps_block.compute(rps_x1, input_a);
      
      // Reconstructing the system attractor with an embedding dimension of m+1
      //    
      rps_block.set(Rps::SVD_EMBEDDING,
		    Rps::EMBEDDING, m + 1, svd_window_size_d, delay_d);     
      rps_block.compute(rps_x2, input_a);
    }
    
    // Getting rid of the last column of the RPS matrix (this column contains
    // boolean flags to indicate last row)
    //
    long rps_columns1 =  rps_x1.getNumColumns();
    long rps_rows1 =  rps_x1.getNumRows();
    rps_x1.setDimensions(rps_rows1, rps_columns1 - 1, true);
    
    // Getting rid of the last column of the RPS matrix (this column contains
    // boolean flags to indicate last row)
    //
    long rps_columns2 =  rps_x2.getNumColumns();
    long rps_rows2 =  rps_x2.getNumRows();
    rps_x2.setDimensions(rps_rows2, rps_columns2 - 1, true);
    
    // CorrelationIntegral of the two RPS matrices
    // Setting the coefficient type to GENERIC so that CorrelationIntegral does
    // not remove the last (continuity flag) column of the RPS matrix
    //
    corr_int.compute(temp1, rps_x1, AlgorithmData::GENERIC);
    corr_int.compute(temp2, rps_x2, AlgorithmData::GENERIC);

    // Computing the second order Kolmogorov entropy using:
    //                                 1         C(epsilon,m)
    // K(epsilon, d)  =    lim        ---  log  -------------
    //                  epsilon ->0   tau       C(epsilon,m+1)
    //
    output_a(m - embed_dim_min_d) = (temp1(0) - temp2(0)) / delay_d;
  }
   
  // exit gracefully
  //
  return true;
}

