// file: $isip_ifc/class/algo/CorrelationDimension/cdim_05.cc
// version: $Id: cdim_05.cc 10567 2006-04-25 04:14:45Z pannuri $
//


// isip include files
//
#include "CorrelationDimension.h"
#include <Calculus.h>
#include <CorrelationIntegral.h>
#include <Rps.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 CorrelationDimension::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.
    //
    if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_FLOAT) {
      
      res &= compute(output_a(c).makeVectorFloat(),
		     input_a(c)(0).getMatrixFloat(),
		     input_a(c)(0).getCoefType(), c);
    }

    // error unknown coefficient type
    //
    else {
      return Error::handle(name(), L"apply", ERR_UNCTYP,
			   __FILE__, __LINE__);
    }
    
    // set the coefficient type for the output
    //    
    output_a(c).setCoefType(AlgorithmData::CORRELATION_DIMENSION);
  }

  // possibly display the data
  //
  display(output_a, input_a, name());
    
  // finish the debugging output
  //
  displayFinish(this);

  // exit gracefully
  //
  return res;
}

// method: compute
//
// arguments:
//  VectorFloat& output: (output) CorrelationDimension matrix
//  const MatrixFloat& input: (input) input RPS matrix
//  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 Integral from a signal
//
boolean CorrelationDimension::compute(VectorFloat& output_a,
				      const MatrixFloat& input_a,
				      AlgorithmData::COEF_TYPE input_coef_type_a,
				      long index_a) {
 
  // declare local variable
  //
  boolean status = false;

  // if the algorithm is normal
  //
  if (algorithm_d == NORMAL) {
    
    if (implementation_d == BACKWARD) {
      status = computeCorrDimBackward(output_a, input_a);
    }
    if (implementation_d == CENTRAL) {
      status = computeCorrDimCentral(output_a, input_a);
    }
    if (implementation_d == SMOOTH) {
      status = computeCorrDimSmooth(output_a, input_a);
    }
  }    

  // error: unknown algorithm
  //
  else {
    status = Error::handle(name(), L"compute",
			   ERR_UNKALG, __FILE__, __LINE__);
  }
  
  // possibly display the data
  //
  display(output_a, input_a, name());
  
  // exit gracefully
  //
  return status;
}
    
// method: computeCorrDimBackward
// arguments:
//  VectorFloat& correlation_dimension: (output) correlation integral matrix
//  const MatrixFloat& rps: (input) RPS matrix
//
//  return: a boolean value indicating status
//
// this method computes the CorrelationDimension from an input RPS matrix
//
boolean CorrelationDimension::computeCorrDimBackward(VectorFloat&
						     correlation_dimension_a,
						     const MatrixFloat& rps_a) {

  // Checking for potential problems before beginning
  //
  if (epsilon_min_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_EPSMIN, __FILE__, __LINE__);
  }

  if (epsilon_max_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_EPSMAX, __FILE__, __LINE__);
  }
  
  if (epsilon_res_d == (long) 0) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_EPSRES, __FILE__, __LINE__);
  }

  if (epsilon_max_d < epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_EPS, __FILE__, __LINE__);
  }
  if (delta_d > epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_DELTA_EPSMIN, __FILE__, __LINE__);
  }
  
  if (delta_d > epsilon_max_d) {
    return Error::handle(name(), L"computeCorrDimBackward", ERR_DELTA_EPSMAX, __FILE__, __LINE__);
  }
  
  // get the values of the epsilon min, epsilon max and the res to compute the
  // slopes
  //
  // get the range of epsilons from the values set
  //
  CorrelationIntegral corr_int_block;
  VectorFloat corr_int;
  VectorFloat epsilon;
  
  // if the scaling set is linear
  //
  if (scaling_d == LINSCALE) {
    epsilon.setLength(epsilon_res_d);
    float normalize = (float) epsilon_res_d - (float) 1;
    float add = (epsilon_max_d - epsilon_min_d);
    add /= normalize;
    epsilon(0) = epsilon_min_d;
    for (long i = 1; i < epsilon_res_d; i++) {
      epsilon(i) = epsilon_min_d;
      epsilon(i) += i * add;
    }
  }
  if (scaling_d == LOGSCALE) {
    float temp;
    float num_epsilon;
    num_epsilon = epsilon_max_d / epsilon_min_d;
    num_epsilon = Integral::log(num_epsilon);
    num_epsilon /= delta_d;
    epsilon.setLength((long)num_epsilon + 1);
    
    // finding the vector of epsilons so that the denominator is the same
    //
    for (long i = 0; i < (long)num_epsilon + 1; i++)
      {
	temp = (float)i;
	temp *= delta_d;
	epsilon(i) = epsilon_min_d;
	epsilon(i) *= Integral::exp(temp);
	temp = (float) 0;
      }
  }
  long len_eps = epsilon.length();
  VectorFloat epsilon_modified, output;
  epsilon_modified.setLength(2);
  CorrelationIntegral corr_int_modified;
  VectorFloat corr_modified(2);
  corr_int_modified.setSpacingMode(CorrelationIntegral::USER_SPECIFIED_EPSILON_SPACING);
  correlation_dimension_a.setLength(len_eps);
  float den;
  for (long i = 0; i < len_eps; i++) {
    epsilon_modified(0).assign(epsilon(i) - delta_d);
    epsilon_modified(1).assign(epsilon(i));
    corr_int_modified.setEpsilon(epsilon_modified);
    corr_int_modified.compute(corr_modified, rps_a);
    correlation_dimension_a(i) = corr_modified(1) - corr_modified(0);
    den = Integral::log(epsilon_modified(1)) - Integral::log(epsilon_modified(0));
    correlation_dimension_a(i) /= den;
  }
    
  // exit gracefully
  //
  return true;
}

// method: computeCorrDimCentral
// arguments:
//  VectorFloat& correlation_dimension: (output) correlation integral matrix
//  const MatrixFloat& rps: (input) RPS matrix
//
//  return: a boolean value indicating status
//
// this method computes the CorrelationDimension from an input RPS matrix
//
boolean CorrelationDimension::computeCorrDimCentral(VectorFloat&
						    correlation_dimension_a,
						    const MatrixFloat& rps_a) {

  // Checking for potential problems before beginning
  //
  if (epsilon_min_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_EPSMIN, __FILE__, __LINE__);
  }

  if (epsilon_max_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_EPSMAX, __FILE__, __LINE__);
  }
  
  if (epsilon_res_d == (long) 0) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_EPSRES, __FILE__, __LINE__);
  }

  if (epsilon_max_d < epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_EPS, __FILE__, __LINE__);
  }

  if (delta_d > epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_DELTA_EPSMIN, __FILE__, __LINE__);
  }
  
  if (delta_d > epsilon_max_d) {
    return Error::handle(name(), L"computeCorrDimCentral", ERR_DELTA_EPSMAX, __FILE__, __LINE__);
  }
  
  // get the values of the epsilon min, epsilon max and the res to compute the
  // slopes
  //
  // get the range of epsilons from the values set
  //
  CorrelationIntegral corr_int_block;
  VectorFloat corr_int;
  VectorFloat epsilon;

  // if the scaling is linear
  //
  if (scaling_d == LINSCALE) {
    epsilon.setLength(epsilon_res_d);
    float normalize = (float) epsilon_res_d - (float) 1;
    float add = (epsilon_max_d - epsilon_min_d);
    add /= normalize;
    epsilon(0) = epsilon_min_d;
    for (long i = 1; i < epsilon_res_d; i++) {
      epsilon(i) = epsilon_min_d;
      epsilon(i) += i * add;
    }
  }
  
  // if the scaling is logarithmic
  //
  if (scaling_d == LOGSCALE) {
    float temp;
    float num_epsilon;
    num_epsilon = epsilon_max_d / epsilon_min_d;
    num_epsilon = Integral::log(num_epsilon);
    num_epsilon /= delta_d;
    epsilon.setLength((long)num_epsilon + 1);
    
    // finding the vector of epsilons so that the denominator is the same
    //
    for (long i = 0; i < (long)num_epsilon + 1; i++)
      {
	temp = (float)i;
	temp *= delta_d;
	epsilon(i) = epsilon_min_d;
	epsilon(i) *= Integral::exp(temp);
	temp = (float) 0;
      }
  }
  long len_eps = epsilon.length();
  VectorFloat epsilon_modified, output;
  epsilon_modified.setLength(2);
  CorrelationIntegral corr_int_modified;
  VectorFloat corr_modified(2);
  corr_int_modified.setSpacingMode(CorrelationIntegral::USER_SPECIFIED_EPSILON_SPACING);
  correlation_dimension_a.setLength(len_eps);
  float den;
  for (long i = 0; i < len_eps; i++) {
    epsilon_modified(0).assign(epsilon(i) - delta_d);
    epsilon_modified(1).assign(epsilon(i) + delta_d);
    corr_int_modified.setEpsilon(epsilon_modified);
    corr_int_modified.compute(corr_modified, rps_a);
    correlation_dimension_a(i) = corr_modified(1) - corr_modified(0);
    den = Integral::log(epsilon_modified(1)) - Integral::log(epsilon_modified(0));
    correlation_dimension_a(i) /= den;
  }
  return true;
}

// comupte correlation dimension smooth
//
// method: computeCorrDimSmooth
// arguments:
//  VectorFloat& correlation_dimension: (output) correlation integral matrix
//  const MatrixFloat& rps: (input) RPS matrix
//
//  return: a boolean value indicating status
//
// this method computes the CorrelationDimension from an input RPS matrix
//
boolean CorrelationDimension::computeCorrDimSmooth(VectorFloat&
						   correlation_dimension_a,
						   const MatrixFloat& rps_a) {

  // Checking for potential problems before beginning
  //
  if (epsilon_min_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_EPSMIN, __FILE__, __LINE__);
  }

  if (epsilon_max_d == (float) 0) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_EPSMAX, __FILE__, __LINE__);
  }
  
  if (epsilon_res_d == (long) 0) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_EPSRES, __FILE__, __LINE__);
  }
  
  if (epsilon_max_d < epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_EPS, __FILE__, __LINE__);
  }
  
  if (delta_d > epsilon_min_d) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_DELTA_EPSMIN, __FILE__, __LINE__);
  }
  
  if (delta_d > epsilon_max_d) {
    return Error::handle(name(), L"computeCorrDimSmooth", ERR_DELTA_EPSMAX, __FILE__, __LINE__);
  }
  
  // get the values of the epsilon min, epsilon max and the res to compute the
  // slopes
  //
  // get the range of epsilons from the values set
  //
  CorrelationIntegral corr_int_block;
  VectorFloat corr_int;
  VectorFloat epsilon;
  
  // for linear scaling
  //
  if (scaling_d == LINSCALE) {
    epsilon.setLength(epsilon_res_d);
    float normalize = (float) epsilon_res_d - (float) 1;
    float add = (epsilon_max_d - epsilon_min_d);
    add /= normalize;
    epsilon(0) = epsilon_min_d;
    for (long i = 1; i < epsilon_res_d; i++) {
      epsilon(i) = epsilon_min_d;
      epsilon(i) += i * add;
    }
  }
  if (scaling_d == LOGSCALE) {
    float temp;
    float num_epsilon;
    num_epsilon = epsilon_max_d / epsilon_min_d;
    num_epsilon = Integral::log(num_epsilon);
    num_epsilon /= delta_d;
    epsilon.setLength((long)num_epsilon + 1);
    
    // finding the vector of epsilons so that the denominator is the same
    //
    for (long i = 0; i < (long)num_epsilon + 1; i++)
      {
	temp = (float)i;
	temp *= delta_d;
	epsilon(i) = epsilon_min_d;
	epsilon(i) *= Integral::exp(temp);
	temp = (float) 0;
      }
  }
  long len_eps = epsilon.length();
  
  // get the vector of epsilons with half and double the value of epsilon
  //
  VectorFloat epsilon_2, epsilon_half;
  epsilon_2.setLength(len_eps);
  epsilon_half.setLength(len_eps);
  
  for (long i = 0, j = 0; j < len_eps; i++, j++) {
    epsilon_2(i).assign(epsilon(i) * (float)2);
    epsilon_half(i).assign(epsilon(i) / (float)2);
  }
  CorrelationIntegral corr_int_2, corr_int_half;
  VectorFloat corr_2, corr_half;
  corr_int_2.setSpacingMode(CorrelationIntegral::USER_SPECIFIED_EPSILON_SPACING);
  corr_int_2.setScaling(CorrelationIntegral::LINSCALE);
  corr_int_half.setSpacingMode(CorrelationIntegral::USER_SPECIFIED_EPSILON_SPACING);
  corr_int_half.setScaling(CorrelationIntegral::LINSCALE);
  corr_int_2.setEpsilon(epsilon_2);
  corr_int_half.setEpsilon(epsilon_half);
  corr_int_2.compute(corr_2, rps_a);
  corr_int_half.compute(corr_half, rps_a);
  
  // get the vector of slopes
  //
  correlation_dimension_a.setLength(len_eps);
  
  float result;
  for (long i = 0; i < len_eps; i++) {
    result = (float) corr_2(i) / (float) corr_half(i);
    result = Integral::log2(result);
    result *= 0.5;
    correlation_dimension_a(i).assign(result);     
  }    
  return true;
}
