// file: $isip/class/algo/Statistics/stat_05.cc
// version: $Id: stat_05.cc 9085 2003-04-10 22:30:01Z parihar $
//

// isip include files
//
#include "Statistics.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 the appropriate computation methods
//
boolean Statistics::apply(Vector<AlgorithmData>& output_a,
			  const Vector< CircularBuffer<AlgorithmData> >&
			  input_a) {

  // set the length to the number of channels input
  //
  long len = input_a.length();
  output_a.setLength(len);

  // accumulate the frame index
  //
  frame_index_d++;
  
  // if this is the first frame for ACCUMULATE mode, initialize the
  // accumulation variables
  //
  if (frame_index_d == 0 && cmode_d == ACCUMULATE) {
    setAccumulateVar(len, input_a(0)(0).getVectorFloat().length());
  }

  // accumulate a result status -- if compute method returns false
  // this apply method should return false
  //  
  boolean res = true;

  // 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::makeVectorFloat to force the output vector
      // for this channel to be a VectorFloat, 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(input_a(c)(0).getCoefType());    
  }

  // 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) output data
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
//  return: a boolean value indicating status
//
// this method returns the required statistical parameter for the
// input data
//
boolean Statistics::compute(VectorFloat& output_a,
			    const VectorFloat& input_a,
			    AlgorithmData::COEF_TYPE input_coef_type_a,
			    long index_a) {

  if (cmode_d == FRAME_INTERNAL) 
    computeFrameInt(output_a, input_a, input_coef_type_a, index_a);
  
  else if (cmode_d == ACCUMULATE && dmode_d == FRAME_BASED) 
    computeFrameAccumulate(output_a, input_a, input_coef_type_a, index_a);
  
  else if (cmode_d == ACCUMULATE && dmode_d == SAMPLE_BASED) 
    computeSampleAccumulate(output_a, input_a, input_coef_type_a, index_a);

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

  // exit gracefully
  //  
  return true;
}


// method: computeFrameInt
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
//  return: a boolean value indicating status
//
// this method returns the required statistical parameter for the
// input data
//
boolean Statistics::computeFrameInt(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: MINIMUM
  //
  if (algorithm_d == MINIMUM) {
    status = computeMin(output_a, input_a);
  }
  
  // Algorithm: MINIMUM_MAG
  //    
  else if (algorithm_d == MINIMUM_MAG) {
    status = computeMinMag(output_a, input_a);
  }
  
  // Algorithm: MAXIMUM
  //
  else if (algorithm_d == MAXIMUM) {
    status = computeMax(output_a, input_a);
  }
  
  // Algorithm: MAXIMUM_MAG
  //
  else if (algorithm_d == MAXIMUM_MAG) {
    status = computeMax(output_a, input_a);
  }    
  
  // Algorithm: MEAN
  //
  else if (algorithm_d == MEAN) {
    status = computeMean(output_a, input_a);
  }
  
  // Algorithm: MEDIAN
  //
  else if (algorithm_d == MEDIAN) {
    status = computeMedian(output_a, input_a);
  }
  
  // Algorithm: VARIANCE
  //
  else if (algorithm_d == VARIANCE) {
    status = computeVar(output_a, input_a);
  }
  
  // Algorithm: STDEV
  //  
  else if (algorithm_d == STDEV) {
    status = computeStdDev(output_a, input_a);
  }
  
  // Algorithm: SKEW    
  //  
  else if (algorithm_d == SKEW) {
    return Error::handle(name(), L"computeFrameInt",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }
  
  // Algorithm: KURTOSIS
  //  
  else if (algorithm_d == KURTOSIS) {
    return Error::handle(name(), L"computeFrameInt",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }  
  
  // an unknown input type was specified
  //
  else {
    return Error::handle(name(), L"computeFrameInt", ERR_UNKALG,
			 __FILE__, __LINE__);
  }
  
  // set the calculated flag
  //
  is_calculated_d = true;
  
  // exit gracefully
  //
  return status;
}

// method: computeFrameAccumulate
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
//  return: a boolean value indicating status
//
// this method returns the required statistical parameter for the
// input data
//
boolean Statistics::computeFrameAccumulate(VectorFloat& output_a,
					   const VectorFloat& input_a,
					   AlgorithmData::COEF_TYPE
					   input_coef_type_a,
					   long index_a) {

  // declare local variables
  //
  boolean status = false;
  
  long len = input_a.length();

  // Algorithm: MEAN
  //  
  if (algorithm_d == MEAN) {
    for (long i = 0; i < len; i++)  {
      accum_sum_d(index_a).getVectorFloat()(i) += input_a(i);
    }
    
    output_a.setLength(len);
    
    for (long i = 0; i < len; i++)  {
      output_a(i) =
	accum_sum_d(index_a).getVectorFloat()(i) / (float)(frame_index_d + 1);
    }
    status = true;
  }
    
  // Algorithm: MINIMUM, MINIMUM_MAG, MAXIMUM, MAXIMUM_MAG
  //
  else if (algorithm_d == MINIMUM ||
	   algorithm_d == MINIMUM_MAG ||
	   algorithm_d == MAXIMUM ||
	   algorithm_d == MAXIMUM_MAG) {

    output_a.setLength(len);
     
    // First frame and the rest of frame is different
    //
    for (long i = 0; i < len; i++)  {
      if (frame_index_d == 0) {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	computeFrameInt(tempout, tempin);
	output_a(i).assign(tempout(0));
	accum_result_d(index_a).getVectorFloat()(i) = output_a(i);
      }      
      else {
	VectorFloat temp(2);
	temp(0) = accum_result_d(index_a).getVectorFloat()(i);
	temp(1).assign(input_a(i));
	VectorFloat tempout(1);
	computeFrameInt(tempout, temp);
	output_a(i).assign(tempout(0));
	accum_result_d(index_a).getVectorFloat()(i) = output_a(i);
      } //end of else frame_index_d
    }
    
    status = true;    
  }  //end of else if algorithm_d

  // Algorithm: MEDIAN
  //
  else if (algorithm_d == MEDIAN) {

    output_a.setLength(len);
    for (long i = 0; i < len; i++)  {
      if (frame_index_d == 0) {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat()(0) = input_a(i);
	computeMedian(tempout, tempin);      
	output_a(i).assign(tempout(0));
      }
      else {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat().concat(tempin);
	computeMedian(tempout,
		      accum_frame_buff_d(index_a)(i).getVectorFloat());      
	output_a(i).assign(tempout(0));
	
      }
    }    
  }
  
  // Algorithm: VARIANCE
  //
  else if (algorithm_d == VARIANCE) {

    output_a.setLength(len);
    for (long i = 0; i < len; i++)  {
      if (frame_index_d == 0) {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat()(0) = input_a(i);
	computeVar(tempout, tempin);      
	output_a(i).assign(tempout(0));
      }
      else {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat().concat(tempin);
	computeVar(tempout,
		      accum_frame_buff_d(index_a)(i).getVectorFloat());      
	output_a(i).assign(tempout(0));	
      }
    }
  }

  // Algorithm: STDEV
  //
  else if (algorithm_d == STDEV) {

    output_a.setLength(len);
    for (long i = 0; i < len; i++)  {
      if (frame_index_d == 0) {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat()(0) = input_a(i);
	computeStdDev(tempout, tempin);      
	output_a(i).assign(tempout(0));
      }
      else {
	VectorFloat tempout(1);
	VectorFloat tempin(1);
	tempin.assign(input_a(i));
	accum_frame_buff_d(index_a)(i).getVectorFloat().concat(tempin);
	computeStdDev(tempout,
		      accum_frame_buff_d(index_a)(i).getVectorFloat());      
	output_a(i).assign(tempout(0));	
      }
    }
  }
  
  // Algorithm: SKEW    
  //  
  else if (algorithm_d == SKEW) {
    return Error::handle(name(), L"computeFrameAccumulate",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }
  
  // Algorithm: KURTOSIS
  //  
  else if (algorithm_d == KURTOSIS) {
    return Error::handle(name(), L"computeFrameAccumulate",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }

  else {
    // unsupported input
    //
    return Error::handle(name(), L"computeFrameAccumulate",
			 ERR_UNKIMP, __FILE__, __LINE__);    
  }
  
  // set the calculated flag
  //
  is_calculated_d = true;
  
  // exit gracefully
  //
  return status;
}

// method: computeSampleAccumulate
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
//  return: a boolean value indicating status
//
// this method returns the required statistical parameter for the
// input data
//
boolean Statistics::computeSampleAccumulate(VectorFloat& output_a,
					    const VectorFloat& input_a,
					    AlgorithmData::COEF_TYPE
					    input_coef_type_a,
					    long index_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the one value
  //
  output_a.setLength(1);

  accum_samples_d += input_a.length();
  
  accum_sum_d(index_a).getVectorFloat()(0) += input_a.sum();
      
  accum_sumsqr_d(index_a).getVectorFloat()(0) += input_a.sumSquare();

  // Algorithm: MEAN
  //  
  if (algorithm_d == MEAN) {
    output_a.setLength(1);
    output_a(0) =
      accum_sum_d(index_a).getVectorFloat()(0) / (float)accum_samples_d;
  }

  // Algorithm: MINIMUM, MINIMUM_MAG, MAXIMUM, MAXIMUM_MAG
  //
  else if (algorithm_d == MINIMUM ||
	   algorithm_d == MINIMUM_MAG ||
	   algorithm_d == MAXIMUM ||
	   algorithm_d == MAXIMUM_MAG) {
    // First frame and the rest of frame is different
    //
    if (frame_index_d == 0) {
      computeFrameInt(output_a, input_a);
      accum_result_d(index_a).getVectorFloat()(0) = output_a(0);
    }
    else {
      VectorFloat temp(1);
      temp(0) = accum_result_d(index_a).getVectorFloat()(0);
      temp.concat(input_a);
      computeFrameInt(output_a, temp);

    } //end of else frame_index_d
    
    accum_result_d(index_a).getVectorFloat()(0) = output_a(0);
    
  }  //end of else if algorithm_d
    
  // Algorithm: MEDIAN
  //
  else if (algorithm_d == MEDIAN) {
    if (frame_index_d == 0) {
      accum_frame_data_d(index_a).getVectorFloat().assign(input_a);
    }
    else {
      accum_frame_data_d(index_a).getVectorFloat().concat(input_a);
      
    } //end of else frame_index_d

    computeMedian(output_a,
			  accum_frame_data_d(index_a).getVectorFloat());
  }
  
  // Algorithm: VARIANCE
  //
  else if (algorithm_d == VARIANCE) {
    output_a(0) = accum_sumsqr_d(index_a).getVectorFloat()(0) -
      accum_sum_d(index_a).getVectorFloat()(0) * accum_sum_d(index_a).getVectorFloat()(0) / (float)accum_samples_d;
    output_a(0) /= (float)accum_samples_d;
  }
  
  // Algorithm: STDEV
  //  
  else if (algorithm_d == STDEV) {
    output_a(0) = accum_sumsqr_d(index_a).getVectorFloat()(0) -
      accum_sum_d(index_a).getVectorFloat()(0) * accum_sum_d(index_a).getVectorFloat()(0) / (float)accum_samples_d;
    output_a(0) /= (float)accum_samples_d;
    output_a(0) = Integral::sqrt(output_a(0));
  }
  
  // Algorithm: SKEW    
  //  
  else if (algorithm_d == SKEW) {
    return Error::handle(name(), L"computeSampleAccumulate",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }
  
  // Algorithm: KURTOSIS
  //  
  else if (algorithm_d == KURTOSIS) {
    return Error::handle(name(), L"computeSampleAccumulate",
			 ERR_UNKIMP, __FILE__, __LINE__);
  }  
  
  // an unknown input type was specified
  //
  else {
    return Error::handle(name(), L"computeSampleAccumulate", ERR_UNKALG,
			 __FILE__, __LINE__);
  }
  
  // set the calculated flag
  //
  is_calculated_d = true;
  
  // exit gracefully
  //
  return true;
}

// method: computeMin
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the minimum of the input data
//
boolean Statistics::computeMin(VectorFloat& output_a,
			       const VectorFloat& input_a) {

  // set the length of the output vector: should be 1 as it only
  // contains the minimum value
  //
  output_a.setLength(1);
    
  // assign the value of minimum to the output
  //
  output_a(0) = input_a.min();

  // exit gracefully
  //  
  return true;
}

// method: computeMax
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method return the maximum of the input data 
//
boolean Statistics::computeMax(VectorFloat& output_a,
			       const VectorFloat& input_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the maximum value
  //
  output_a.setLength(1);
  
  // assign the value of maximum to the output
  //
  output_a(0) = input_a.max();
  
  // exit gracefully
  //  
  return true;
}

// method: computeMinMag
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method return the minimum magnitude of the input data
//
boolean Statistics::computeMinMag(VectorFloat& output_a,
				  const VectorFloat& input_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the minimum magnitude value
  //
  output_a.setLength(1);
  
  // assign the value of minimum magnitude to the output
  //
  output_a(0) = input_a.minMag();
  
  // exit gracefully
  //  
  return true;
}

// method: computeMaxMag
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the maximum magnitude of the input data
//
boolean Statistics::computeMaxMag(VectorFloat& output_a,
				  const VectorFloat& input_a) {

  // set the length of the output vector: should be 1 as it only
  // contains the maximum magnitude value
  //
  output_a.setLength(1);
    
  // assign the value of maximum magnitude to the output
  //
  output_a(0) = input_a.maxMag();

  // exit gracefully
  //  
  return true;
}

// method: computeVar
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the variance of the input data
//
boolean Statistics::computeVar(VectorFloat& output_a,
			       const VectorFloat& input_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the variance
  //
  output_a.setLength(1);
    
  // assign the value of variance
  //
  output_a(0) = input_a.var();

  // exit gracefully
  //  
  return true;
}

// method: computeStdDev
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the standard deviation of the input data
//
boolean Statistics::computeStdDev(VectorFloat& output_a,
				  const VectorFloat& input_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the standard deviation
  //
  output_a.setLength(1);
  
  // assign the value of standard deviation
  //
  output_a(0) = input_a.stdev();

  // exit gracefully
  //  
  return true;
}

// method: computeMedian
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the median of the input data
//
boolean Statistics::computeMedian(VectorFloat& output_a,
				  const VectorFloat& input_a) {

  // set the length of the output vector: should be 1 as it only
  // contains the median
  //
  output_a.setLength(1);
    
  // assign the value of the median
  //
  output_a(0) = input_a.median();

  // exit gracefully
  //  
  return true;
}

// method: computeMean
// 
// arguments:
//  VectorFloat& output: (output) output data
//  const VectorFloat& input: (input) input data
//  
//  return: a boolean value indicating status
//
// this method returns the mean of the input data 
//
boolean Statistics::computeMean(VectorFloat& output_a,
				const VectorFloat& input_a) {
  
  // set the length of the output vector: should be 1 as it only
  // contains the mean
  //
  output_a.setLength(1);
    
  // assign the value of the mean
  //
  output_a(0) = input_a.mean();

  // exit gracefully
  //  
  return true;
}
