// file: $isip_ifc/class/algo/Rps/rps_05.cc
// version: $Id: rps_05.cc 10484 2006-03-14 23:50:33Z srinivas $
//

// isip include files
//
#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 Rps::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, get the data-type of the input
    // for this channel and call the appropriate compute method
    //
    if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_FLOAT) {
      res &= compute(output_a(c).makeMatrixFloat(),
		     input_a(c)(0).getVectorFloat(),
		     input_a(c)(0).getCoefType(),
		     c);
    }
    else if (input_a(c)(0).getDataType() == AlgorithmData::MATRIX_FLOAT){
      res &= compute(output_a(c).makeMatrixFloat(),
		     input_a(c)(0).getMatrixFloat(),
		     input_a(c)(0).getCoefType(),
		     c);
    }
    
    output_a(c).setCoefType(AlgorithmData::RPS);
  }

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

  // exit gracefully
  //
  return res;
}

// method: compute
//
// arguments:
//  MatrixFloat& output: (output) RPS matrix
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method computes the covariance coefficients from signal
//
boolean Rps::compute(MatrixFloat& output_a,
		     const VectorFloat& input_a,
		     AlgorithmData::COEF_TYPE input_coef_type_a,
		     long chan_a) {

  // If time delay is not specified, set it to 1 sample.
  //
  if (delay_d < (float)0) {
    delay_d = 1 / sample_freq_d;
  }

  if (cmode_d == FRAME_INTERNAL) {
    computeFrameInt(output_a, input_a);
  }
  
  else if (cmode_d == ACCUMULATE) {
    computeAccumulate(output_a, input_a, chan_a);
  }

  else {
    return Error::handle(name(), L"compute", ERR_UNCTYP, __FILE__, __LINE__);
  }

  // exit gracefully
  //  
  return true;
}

// method: compute
//
// arguments:
//  MatrixFloat& output: (output) RPS matrix
//  const MatrixFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method computes the covariance coefficients from signal
//
boolean Rps::compute(MatrixFloat& output_a,
		     const MatrixFloat& input_a,
		     AlgorithmData::COEF_TYPE input_coef_type_a,
		     long chan_a) {
  if (cmode_d == FRAME_INTERNAL) {
    computeFrameInt(output_a, input_a);
  }
  else {
    return Error::handle(name(), L"compute", ERR_UNCTYP, __FILE__, __LINE__);
  }

  // exit gracefully
  //  
  return true;
}

// method: computeFrameInt
// 
// arguments:
//  MatrixFloat& rps: (output) RPS matrix
//  const VectorFloat& input: (input) input data
//
//  return: a boolean value indicating status
//
// this method computes the RPS matrix for one frame of an input
// time-series
//
boolean Rps::computeFrameInt(MatrixFloat& rps_a,
			     const VectorFloat& input_a) {

  // declare local variables
  //
  boolean status = false;
  
  // branch on algorithm:
  //  Algorithm: TIME_DELAY_EMBEDDING
  //
  if (algorithm_d == TIME_DELAY_EMBEDDING) {
    status = computeTimeDelayEmbedding(rps_a, input_a);
  }

  // branch on algorithm:
  //  Algorithm: SVD_EMBEDDING
  //
  else if (algorithm_d == SVD_EMBEDDING) {
    if (svd_window_size_d > embed_dimension_d)
      status = computeSVDEmbedding(rps_a, input_a);
    else if (svd_window_size_d == embed_dimension_d) {
      status = computeTimeDelayEmbedding(rps_a, input_a);
    }
    else {
      return Error::handle(name(), L"computeFrameInt", ERR_SVDDIM,
			   __FILE__, __LINE__);
    }
  }

  // an unknown algorithm was specified
  //
  else {
    return Error::handle(name(), L"computeFrameInt", ERR_UNKALG,
			 __FILE__, __LINE__);
  }

  // exit gracefully
  //
  return status;
}

// method: computeFrameInt
// 
// arguments:
//  MatrixFloat& rps: (output) RPS matrix
//  const MatrixFloat& input: (input) input data
//
//  return: a boolean value indicating status
//
// this method computes the RPS matrix for one frame of an input
// time-series
//
boolean Rps::computeFrameInt(MatrixFloat& rps_a,
			     const MatrixFloat& input_a) {

  // declare local variables
  //
  boolean status = false;
  
  // branch on algorithm:
  //  Algorithm: TIME_DELAY_EMBEDDING
  //
  if (algorithm_d == TIME_DELAY_EMBEDDING) {
    status = computeTimeDelayEmbedding(rps_a, input_a);
  }

  // branch on algorithm:
  //  Algorithm: SVD_EMBEDDING
  //
  else if (algorithm_d == SVD_EMBEDDING) {
    if (svd_window_size_d > embed_dimension_d)
      status = computeSVDEmbedding(rps_a, input_a);
    else if (svd_window_size_d == embed_dimension_d) {
      status = computeTimeDelayEmbedding(rps_a, input_a);
    }
    else {
      return Error::handle(name(), L"computeFrameInt", ERR_SVDDIM,
			   __FILE__, __LINE__);
    }
  }

  // an unknown algorithm was specified
  //
  else {
    return Error::handle(name(), L"computeFrameInt", ERR_UNKALG,
			 __FILE__, __LINE__);
  }

  // exit gracefully
  //
  return status;
}

// method: computeAccumulate
//
// arguments:
//  MatrixFloat& rps: (output) covariance coefficients
//  const VectorFloat& input: (input) input data
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method computes the RPS matrix for the whole file
//
boolean Rps::computeAccumulate(MatrixFloat& rps_a,
				     const VectorFloat& input_a,
				     long chan_a) {

  boolean status = false;
  long  num_samples = (long)(signal_duration_d * sample_freq_d);

  //through the last frame keep accumulating signal
  //
  if (frame_index_d == 0) {
    accum_buf_d(chan_a).assign(input_a);
  }
  else {
    accum_buf_d(chan_a).concat(input_a);
  }
  
  // compute RPS only at the last frame
  //
  if (frame_index_d == num_samples - (long)1) {

    // branch on algorithm:
    //  Algorithm: TIME_DELAY_EMBEDDING
    //
    if (algorithm_d == TIME_DELAY_EMBEDDING) {
      status = computeTimeDelayEmbedding(rps_a, accum_buf_d(chan_a));
    }

    // branch on algorithm:
    //  Algorithm: SVD_EMBEDDING
    //
    else if (algorithm_d == SVD_EMBEDDING) {
      if (svd_window_size_d > embed_dimension_d)
	status = computeSVDEmbedding(rps_a, accum_buf_d(chan_a));
      else if (svd_window_size_d == embed_dimension_d) {
	status = computeTimeDelayEmbedding(rps_a, accum_buf_d(chan_a));
      }
      else {
	return Error::handle(name(), L"computeAccumulate", ERR_SVDDIM,
			     __FILE__, __LINE__);
      }
    }

    // an unknown algorithm was specified
    //
    else {
      return Error::handle(name(), L"computeAccumulate", ERR_UNKALG,
			  __FILE__, __LINE__);
    }
  }
  else if (frame_index_d > num_samples - 1) {
    return Error::handle(name(), L"computeAccumulate", ERR_UNCTYP,
			 __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
}
