// file: $isip/class/algo/FourierTransform/ft_05.cc
// version: $Id: ft_05.cc 8226 2002-06-25 01:42:42Z picone $
//

// isip include files
//
#include "FourierTransform.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 FourierTransform::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;
  
  // loop over the channels and call the compute method
  //
  for (long c = 0; c < len; c++) {

    // branch on input data type
    //
    if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_FLOAT) {

      if (algorithm_d == DCT) {
	// 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);
      }
      else {
	// call AlgorithmData::makeComplexVectorFloat to force the
	// output vector for this channel to be a VectorComplexFloat.
	// 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).makeVectorComplexFloat(),
		       input_a(c)(0).getVectorFloat(),
		       input_a(c)(0).getCoefType(), c);
      }
    }

    else {

      // call AlgorithmData::makeComplexVectorFloat to force the output vector
      // for this channel to be a VectorComplexFloat.
      // call AlgorithmData::getComplexVectorFloat on the input for this
      // channel to check that the input is already a VectorFloat and return
      // that vector.
      //
      res &= compute(output_a(c).makeVectorComplexFloat(),
		     input_a(c)(0).getVectorComplexFloat(),
		     input_a(c)(0).getCoefType(), c);
    }

    // set the coefficient type for the output
    //
    if (direction_d == FORWARD) {
      output_a(c).setCoefType(AlgorithmData::SPECTRUM);
    }
    else {
      output_a(c).setCoefType(AlgorithmData::SIGNAL);
    }
  }
  
  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: compute
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method selects the appropriate computational method to compute
// fourier transform
//
boolean FourierTransform::compute(VectorComplexFloat& output_a,
				  const VectorFloat& input_a,
				  AlgorithmData::COEF_TYPE input_coef_type_a,
				  long chan_a) {

  // declare local variable
  //
  boolean status = false;
  long M;
  
  // Direction: FORWARD
  //
  if (direction_d == FORWARD) {

    // call the corresponding compute method
    //
    status = computeForward(output_a, input_a);
  }

  // Direction: INVERSE
  //
  else if (direction_d == INVERSE) {

    // call the corresponding compute method
    //
    status = computeInverse(output_a, input_a);
  }
  
  // error: unknown algorithm
  //
  else {
    status = Error::handle(name(), L"compute", ERR_UNKALG,
			   __FILE__, __LINE__);
  }

  // only output required number points
  //
  if (otype_d == FULL) {
    M = (olen_d == DEF_LENGTH) ? (long)order_d : (long)olen_d;
  }
  else {

    // under SYMMETRIC output mode, user specified output length can't
    // be greater than half of order length
    //
    if (olen_d > ((long)order_d / 2)) {
      return Error::handle(name(), L"compute", ERR_OUTLEN,
			   __FILE__, __LINE__);
    }

    //  here, order+1 is to garantee DFT has correct output length no
    //  matter it has odd order or not
    //
    M = (olen_d == DEF_LENGTH) ?
      (long)(((long)order_d + 1) / 2) : (long)olen_d;
  }
  output_a.setLength(M, true);  

  // normalization the output vector if necessary
  //
  if ((normalization_d == UNIT_ENERGY) && (M > 0)) {
    output_a.div(output_a.length());
  }

  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }

  // exit gracefully
  //
  return status;
}

// method: compute
//
// arguments:
//  VectorFloat& output: (output) output data vector
//  const VectorComplexFloat& input: (input) input data vector
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method selects the appropriate computational method to compute
// fourier transform
//
boolean FourierTransform::compute(VectorFloat& output_a,
				  const VectorComplexFloat& input_a,
				  AlgorithmData::COEF_TYPE input_coef_type_a,
				  long chan_a) {


  // declare local variable
  //
  boolean status = false;
  
  // Direction: INVERSE
  //
  if (direction_d == INVERSE) {

    // call the corresponding compute method
    //
    status = computeInverse(output_a, input_a);
  }
  
  // error: unknown algorithm
  //
  else {
    status = Error::handle(name(), L"compute", ERR_UNKALG,
			   __FILE__, __LINE__);
  }

  // only output required number points
  //  Note: complex input can't be under SYMMETRIC output mode
  //
  if (otype_d == SYMMETRIC) {
    return Error::handle(name(), L"compute", ERR_OUTTYP,
			 __FILE__, __LINE__);
  }
  long M = (olen_d == DEF_LENGTH) ? (long)order_d : (long)olen_d;
  output_a.setLength(M, true);  

  // normalization the output vector if necessary
  //
  if ((normalization_d == UNIT_ENERGY) && (output_a.length() > 0)) {
    output_a.div(output_a.length());
  }

  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }

  // exit gracefully
  //
  return status;
}

// method: compute
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorComplexFloat& input: (input) input data vector
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method selects the appropriate computational method to compute
// fourier transform
//
boolean FourierTransform::compute(VectorComplexFloat& output_a,
				  const VectorComplexFloat& input_a,
				  AlgorithmData::COEF_TYPE input_coef_type_a,
				  long chan_a) {
 
  // declare local variable
  //
  boolean status = false;
  
  // Direction: FORWARD
  //
  if (direction_d == FORWARD) {

    // call the corresponding compute method
    //
    status = computeForward(output_a, input_a);
  }

  // algorithm: INVERSE
  //
  else if (direction_d == INVERSE) {
    
    // call the corresponding compute method
    //
    status = computeInverse(output_a, input_a);
  }
  
  // error: unknown algorithm
  //
  else {
    status = Error::handle(name(), L"compute", ERR_UNKALG,
			   __FILE__, __LINE__);
  }

  // only output required number points
  //  Note: complex input can't be under SYMMETRIC output mode
  //
  if (otype_d == SYMMETRIC) {
    return Error::handle(name(), L"compute", ERR_OUTTYP,
			 __FILE__, __LINE__);
  }
  long M = (olen_d == DEF_LENGTH) ? (long)order_d : (long)olen_d;
  output_a.setLength(M, true);

  // normalization the output vector if necessary
  //
  if ((normalization_d == UNIT_ENERGY) && (output_a.length() > 0)) {
    output_a.div(output_a.length());
  }

  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }

  // exit gracefully
  //
  return status;
}

// method: compute
//
// arguments:
//  VectorFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long chan: (input) channel number
//
// return: a boolean value indicating status
//
// this method selects the appropriate computational method to compute
// fourier transform
//
boolean FourierTransform::compute(VectorFloat& output_a,
				  const VectorFloat& input_a,
				  AlgorithmData::COEF_TYPE input_coef_type_a,
				  long chan_a) {
  // declare local variable
  //
  boolean status = false;
  
  // Direction: FORWARD
  //
  if (direction_d == FORWARD) {

    // call the corresponding compute method
    //
    status = computeForward(output_a, input_a);
  }
  
  // Direction: INVERSE
  //
  else if (direction_d == INVERSE) {
    
    // call the corresponding compute method
    //
    status = computeInverse(output_a, input_a);
  }

  // error: unknown algorithm
  //
  else {
    status = Error::handle(name(), L"compute", ERR_UNKALG,
			   __FILE__, __LINE__);
  }

  // only output required number points
  //  Note: DCT transform ignores output mode parameter
  //
  long M = (olen_d == DEF_LENGTH) ? (long)order_d : (long)olen_d;
  output_a.setLength(M, true);  

  // normalization the output vector if necessary
  //
  if ((normalization_d == UNIT_ENERGY) && (output_a.length() > 0)) {
    output_a.div(output_a.length());
  }

  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }

  // exit gracefully
  //
  return status;
}

// method: computeForward
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls the fft algorithm chosen by the user in the
// forward algorithm. if the input data length is not compatible with
// the chosen algorithm then the dft is used
//
boolean FourierTransform::computeForward(VectorComplexFloat& output_a,
					 const VectorFloat& input_a) {

  // variables abour length
  //  input data length: L
  //  user specified input length: N
  //  user specified output length: M
  //  FFT order: order
  //
  long L = input_a.length();
  long N = (ilen_d == DEF_LENGTH) ? L : (long)ilen_d;

  order_d = ((algorithm_d != FFT) || (resolution_d == AUTO)) ?
    (Long)N : order_d;

  // for other fast fourier transform implementation, check the order of the
  // implementation and assign a default implementation if necessary
  //
  if (!validateImplementation()) {
    
    // if the input order is not a valid value for the given implementation
    //
    return Error::handle(name(), L"computeForward", ERR_ORDER,
			 __FILE__, __LINE__);
  }

  // save the current implementation since the internal implementation
  // may change during this method
  //
  IMPLEMENTATION backup_impl = implementation_d;

  // create a vector for use when we have to truncate
  //
  VectorFloat tmp_input;

  // check the required order - when fixed order is not required, if
  // the required order is greater than the length of the input vector
  // then we need to zero-pad the input vector (with a trick).  if it
  // is less than the length of the input vector then we need to
  // truncate, else use the given input vector
  //
  const VectorFloat* algo_input = &input_a;

  // if N < L, truncate input data to N length
  //
  if (N < L) {
    tmp_input.setLength(N);
    tmp_input.move(input_a, N, 0, 0);

    // set the pointer
    //
    algo_input = &tmp_input;
  }

  // if order > L, pad zeros to input data to order length
  //  here is a trick, we set order_d for DFT and DCT to required
  //  input length N
  //
  if (order_d > L) {

    if (N >= L) {
      tmp_input.assign(input_a);
    }
    tmp_input.setLength(order_d, true);
    algo_input = &tmp_input;
  }

  // create a temporary output vector
  //
  VectorFloat tmp_output;

  // Algorithm: DFT
  //
  if (algorithm_d == DFT) {

    // Implementation: CONVENSIONAL
    //
    if (implementation_d == CONVENTIONAL) {
      if (!dfReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: TRIGONOMETRIC
    //
    else if (implementation_d == TRIGONOMETRIC) {
      if (!triReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // Algorithm: FFT
  //
  else if (algorithm_d == FFT) {
  
    // Implementation: SPLIT_RADIX
    //
    if (implementation_d == SPLIT_RADIX) {
      if (!srReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }
  
    // Implementation: RADIX_2
    //
    else if (implementation_d == RADIX_2) {
      if (!rad2Real(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }
    
    // Implementation: RADIX_4
    //
    else if (implementation_d == RADIX_4) {
    if (!rad4Real(tmp_output, *algo_input)) {
      return Error::handle(name(), L"computeForward", ERR_COMPF,
			   __FILE__, __LINE__, Error::WARNING);
    }
    }
  
    // Implementation: FAST_HARLEY
    //
    else if (implementation_d == FAST_HARTLEY) {
      if (!fhReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }
     
    // Implementation: QF
    //  for quick fourier transform implementation
    //
    else if (implementation_d == QF) {
      if (!qfReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DITF
    //  for decimation in time frequency implementation
    //
    else if (implementation_d == DITF) {
      if (!ditfReal(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }

    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // Algorithm: DCT
  //  for discrete cosine transform algorithm
  //
  else if (algorithm_d == DCT) {

    // Implementation: DCT_I
    //
    if (implementation_d == TYPE_I) {
      if (!dct1Real(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_II
    //
    else if (implementation_d == TYPE_II) {
      if (!dct3Real(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_III
    //
    else if (implementation_d == TYPE_III) {
      if (!dct3Real(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_IV
    //
    else if (implementation_d == TYPE_IV) {
      if (!dct4Real(tmp_output, *algo_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }
    
    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // else: not implemented yet
  //
  else {
    return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  if (algorithm_d == DCT) {

    // convert the temporary output vector to complex output vector
    //
    output_a.setLength(tmp_output.length());
    for (long i = 0; i < tmp_output.length(); i++) {
      output_a(i).assign(complexfloat(tmp_output(i), 0));
    }
  }

  else {

    // convert the temporary output vector to complex output vector
    //
    output_a.setLength(tmp_output.length() / 2);

    for (long i = 0, j = 0; i < output_a.length(); i++) {
      output_a(i).assign(complexfloat(tmp_output(j), tmp_output(j + 1)));
      j += 2;
    }
  }

  // restore the implementation
  //
  implementation_d = backup_impl;

  // exit gracefully
  //
  return true;
}

// method: computeForward
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorComplexFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls the fft algorithm chosen by the user in the
// forward algorithm. if the input data length is not compatible with
// the chosen algorithm then the dft is used
//
boolean FourierTransform::computeForward(VectorComplexFloat& output_a,
					 const VectorComplexFloat& input_a) {

  // variables abour length
  //  input data length: L
  //  user specified input length: N
  //  user specified output length: M
  //  FFT order: order
  //
  long L = input_a.length();
  long N = (ilen_d == DEF_LENGTH) ? L : (long)ilen_d;

  order_d = ((algorithm_d != FFT) || (resolution_d == AUTO)) ? (Long)N : order_d;

  // for other fast fourier transform implementation, check the order of the
  // implementation and assign a default implementation if necessary
  //
  if (!validateImplementation()) {
    
    // if the input order is not a valid value for the given implementation
    //
    return Error::handle(name(), L"computeForward",
			 ERR_ORDER, __FILE__, __LINE__);
  }

  // save the current implementation since the internal implementation
  // may change during this method
  //
  IMPLEMENTATION backup_impl = implementation_d;

  // create a vector for use when we have to truncate
  //
  VectorFloat tmp_input(L * 2);

  // expand complex vector to the temporay vectors
  //  
  for (long i = 0, j = 0; i < L; i++) {
    tmp_input(j++) = input_a(i).real();
    tmp_input(j++) = input_a(i).imag();
  }

  // if N < L, truncate input data to N length
  //
  if (N < L) {
    tmp_input.setLength(N * (long)2);
  }

  // if order > L, pad zeros to input data to order length
  //  here is a trick, we set order_d for DFT and DCT to required
  //  input length N
  //
  if (order_d > L) {
    tmp_input.setLength((long)order_d * 2, true);
  }

  // create a temporary output vector
  //
  VectorFloat tmp_output;

  // Algorithm: DFT
  //
  if (algorithm_d == DFT) {

    // Implementation: CONVENSIONAL
    //
    if (implementation_d == CONVENTIONAL) {
      if (!dfComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: TRIGONOMETRIC
    //
    else if (implementation_d == TRIGONOMETRIC) {
      if (!triComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // Algorithm: FFT
  //
  else if (algorithm_d == FFT) {
  
    // Implementation: SPLIT_RADIX
    //
    if (implementation_d == SPLIT_RADIX) {
      if (!srComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }
  
    // Implementation: RADIX_2
    //
    else if (implementation_d == RADIX_2) {
      if (!rad2Complex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }
    
    // Implementation: RADIX_4
    //
    else if (implementation_d == RADIX_4) {
    if (!rad4Complex(tmp_output, tmp_input)) {
      return Error::handle(name(), L"computeForward",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }
    }
  
    // Implementation: FAST_HARLEY
    //
    else if (implementation_d == FAST_HARTLEY) {
      if (!fhComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward", ERR_COMPF,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }
     
    // Implementation: QF
    //  for quick fourier transform implementation
    //
    else if (implementation_d == QF) {
      if (!qfComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DITF
    //  for decimation in time frequency implementation
    //
    else if (implementation_d == DITF) {
      if (!ditfComplex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // Algorithm: DCT
  //  for discrete cosine transform algorithm
  //
  else if (algorithm_d == DCT) {

    // Implementation: DCT_I
    //
    if (implementation_d == TYPE_I) {
      if (!dct1Complex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_II
    //
    else if (implementation_d == TYPE_II) {
      if (!dct2Complex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_III
    //
    else if (implementation_d == TYPE_III) {
      if (!dct3Complex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }

    // Implementation: DCT_IV
    //
    else if (implementation_d == TYPE_IV) {
      if (!dct4Complex(tmp_output, tmp_input)) {
	return Error::handle(name(), L"computeForward",
			     ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
      }
    }
    
    // else: not implemented yet
    //
    else {
      return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // else: not implemented yet
  //
  else {
    return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  if (algorithm_d == DCT) {

    // convert the temporary output vector to complex output vector
    //
    output_a.setLength(tmp_output.length());
    for (long i = 0; i < tmp_output.length(); i++) {
      output_a(i).assign(complexfloat(tmp_output(i), 0));
    }
  }

  else {

    // convert the temporary output vector to complex output vector
    //
    output_a.setLength(tmp_output.length() / 2);

    for (long i = 0, j = 0; i < output_a.length(); i++) {
      output_a(i).assign(complexfloat(tmp_output(j), tmp_output(j + 1)));
      j += 2;
    }
  }

  // provide some useful debugging information
  //
  if (debug_level_d > Integral::NONE) {
    if (debug_level_d >= Integral::ALL) {
      debug(PARAM_DBGL);
    }    
    if (debug_level_d >= Integral::DETAILED) {
      input_a.debug(CLASS_NAME);
    }
    if (debug_level_d >= Integral::BRIEF) {
      output_a.debug(CLASS_NAME);
    }
  }

  // restore the implementation
  //
  implementation_d = backup_impl;

  // exit gracefully
  //
  return true;
}

// method: computeForward
//
// arguments:
//  VectorFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls only DCT forward algorithm since only DCT has real
// signal input and real spectrum output.
//
boolean FourierTransform::computeForward(VectorFloat& output_a,
					 const VectorFloat& input_a) {

  // only applied to DCT algorithm
  //
  if (algorithm_d != DCT) {
    return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // variables abour length
  //  input data length: L
  //  user specified input length: N
  //  user specified output length: M
  //  FFT order: order
  //
  long L = input_a.length();
  long N = (ilen_d == DEF_LENGTH) ? L : (long)ilen_d;
  order_d = N;
  
  // create a vector for use when we have to truncate
  //
  VectorFloat tmp_input;

  // check the required order - when fixed order is not required, if
  // the required order is greater than the length of the input vector
  // then we need to zero-pad the input vector (with a trick).  if it
  // is less than the length of the input vector then we need to
  // truncate, else use the given input vector
  //
  const VectorFloat* algo_input = &input_a;

  // if N < L, truncate input data to N length
  //
  if (N < L) {
    tmp_input.setLength(N);
    tmp_input.move(input_a, N, 0, 0);

    // set the pointer
    //
    algo_input = &tmp_input;
  }

  // if N > L, pad zeros to input data to order length
  //
  if (N > L) {
    tmp_input.assign(input_a);
    tmp_input.setLength(N, true);
    algo_input = &tmp_input;
  }

  // Implementation: DCT_I
  //
  if (implementation_d == TYPE_I) {
    if (!dct1Real(output_a, *algo_input)) {
      return Error::handle(name(), L"computeForward",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  // Implementation: DCT_II
  //
  else if (implementation_d == TYPE_II) {
    if (!dct2Real(output_a, *algo_input)) {
      return Error::handle(name(), L"computeForward",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  // Implementation: DCT_III
  //
  else if (implementation_d == TYPE_III) {
    if (!dct3Real(output_a, *algo_input)) {
      return Error::handle(name(), L"computeForward",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  // Implementation: DCT_IV
  //
  else if (implementation_d == TYPE_IV) {
    if (!dct4Real(output_a, *algo_input)) {
      return Error::handle(name(), L"computeForward",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  // else: not implemented yet
  //
  else {
    return Error::handle(name(), L"computeForward", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // exit gracefully
  //
  return true;
}

// method: computeInverse
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorComplexFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls the fft algorithm chosen by the user in the
// inverse algorithm. if the input data length is not compatible with
// the chosen algorithm then the dft is used
//
boolean FourierTransform::computeInverse(VectorComplexFloat& output_a,
					 const VectorComplexFloat& input_a) {

  // implementation DCT can not support inverse algorithm
  //
  if (algorithm_d == DCT) {
    return Error::handle(name(), L"computeInverse", ERR_UNKALG, __FILE__, __LINE__);
  }

  VectorComplexFloat temp_output;
  
  // for inverse transform, it's equivalent to run forward transform on 
  // the circular shifted and scaled input vector
  //
  if (!computeForward(temp_output, input_a)) {
    return Error::handle(name(), L"computeInverse", Error::NOT_IMPLEM,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // initialize output vector
  //
  output_a.setLength(temp_output.length());
  output_a(0) = temp_output(0);
  
  // circularly shift the data
  //
  for (long i = 1; i < order_d; i++) {
    output_a(i) = temp_output(order_d - i);
  }

  // scale the output vector
  //
  output_a.mult((float)(1.0 / order_d));

  // exit gracefully
  //
  return true;
}

// method: computeInverse
//
// arguments:
//  VectorFloat& output: (output) output data vector
//  const VectorComplexFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls the fft algorithm chosen by the user in inverse
// algorithm. if the input data length is not compatible with the
// chosen algorithm then the dft is used
//
boolean FourierTransform::computeInverse(VectorFloat& output_a,
					 const VectorComplexFloat& input_a) {

  // declare local variables
  //
  VectorComplexFloat temp_output;

  // for inverse transform, it's equivalent to run forward transform on 
  // the circular shifted and scaled input vector
  //
  if (!computeForward(temp_output, input_a)) {
    return Error::handle(name(), L"computeInverse",
			 ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
  }
  
  // store the real part of temporary complex vector to the output real vector
  //
  temp_output.real(output_a);

  // exit gracefully
  //
  return true;
}

// method: computeInverse
//
// arguments:
//  VectorFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls only DCT inverse algorithm since only IDCT has real
// spectrum input and real signal output.
//
boolean FourierTransform::computeInverse(VectorFloat& output_a,
					 const VectorFloat& input_a) {

  // for inverse transform, it's equivalent to run forward transform
  //
  if (algorithm_d == DCT) {
    if (!computeForward(output_a, input_a)) {
      return Error::handle(name(), L"computeInverse",
			   ERR_COMPF, __FILE__, __LINE__, Error::WARNING);
    }      
  }
  
  // exit gracefully
  //
  return true;
}

// method: computeInverse
//
// arguments:
//  VectorComplexFloat& output: (output) output data vector
//  const VectorFloat& input: (input) input data vector
//
// return: a boolean value indicating status
//
// this method calls the fft algorithm chosen by the user in the
// inverse algorithm. if the input data length is not compatible with
// the chosen algorithm then the dft is used
//
boolean FourierTransform::computeInverse(VectorComplexFloat& output_a,
					 const VectorFloat& input_a) {

  // implementation DCT can not support inverse algorithm
  //
  if (algorithm_d == DCT) {
    return Error::handle(name(), L"computeInverse", ERR_UNKALG, __FILE__, __LINE__);
  }

  VectorComplexFloat temp_output;
  
  // for inverse transform, it's equivalent to run forward transform on 
  // the circular shifted and scaled input vector
  //
  if (!computeForward(temp_output, input_a)) {
    return Error::handle(name(), L"computeInverse", Error::NOT_IMPLEM,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // initialize output vector
  //
  output_a.setLength(temp_output.length());
  output_a(0) = temp_output(0);
  
  // circularly shift the data
  //
  for (long i = 1; i < order_d; i++) {
    output_a(i) = temp_output(order_d - i);
  }

  // scale the output vector
  //
  output_a.mult((float)(1.0 / order_d));

  // exit gracefully
  //
  return true;
}
