// file: $isip/class/algo/FourierTransform/FourierTransform.h
// version: $Id: FourierTransform.h 8311 2002-07-08 20:41:34Z parihar $
//

// make sure definitions are only made once
//
#ifndef ISIP_FOURIER_TRANSFORM
#define ISIP_FOURIER_TRANSFORM

// isip include files
//
#ifndef ISIP_ALGORITHM_BASE
#include <AlgorithmBase.h>
#endif

#ifndef ISIP_VECTORFLOAT
#include <VectorFloat.h>
#endif

#ifndef ISIP_LONG
#include <Long.h>
#endif

#ifndef ISIP_MEMORY_MANAGER
#include <MemoryManager.h>
#endif

// FourierTransform: a class to compute a fourier transform 
//
class FourierTransform : public AlgorithmBase {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------

  // define algorithm choices
  //
  enum ALGORITHM { DFT = 0, FFT, DCT, DEF_ALGORITHM = FFT };

  // define implementation choices
  //
  enum IMPLEMENTATION { CONVENTIONAL = 0, TRIGONOMETRIC,
			SPLIT_RADIX, RADIX_2, RADIX_4, FAST_HARTLEY, QF, DITF,
			TYPE_I, TYPE_II, TYPE_III, TYPE_IV,
			DEF_IMPLEMENTATION = SPLIT_RADIX};

  // define algorithm choices
  //
  enum DIRECTION { FORWARD = 0, INVERSE, DEF_DIRECTION = FORWARD };

  // define algorithm output mode
  //
  enum OTYPE { FULL = 0, SYMMETRIC, DEF_OTYPE = FULL };

  // define algorithm resolution
  //
  enum RESOLUTION { AUTO = 0, FIXED, DEF_RESOLUTION = AUTO };

  // define normalization choices
  //
  enum NORM { NONE = 0, UNIT_ENERGY, DEF_NORM = NONE };
  
  // define static NameMap objects
  //
  static const NameMap ALGO_MAP;
  static const NameMap IMPL_MAP;
  static const NameMap DIRE_MAP;
  static const NameMap OUTM_MAP;
  static const NameMap RESO_MAP;  
  static const NameMap NORM_MAP;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------

  static const String DEF_PARAM;
  static const String PARAM_ALGORITHM;
  static const String PARAM_IMPLEMENTATION;
  static const String PARAM_DIRECTION;
  static const String PARAM_OTYPE;
  static const String PARAM_RESOLUTION;
  static const String PARAM_INPUT_LEN;
  static const String PARAM_OUTPUT_LEN;
  static const String PARAM_ORDER;
  static const String PARAM_NORMALIZATION;

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------  
  
  // define the default fast fourier transform
  //
  static const IMPLEMENTATION DEF_FFT = SPLIT_RADIX;
  
  // define the default order of the fourier transform and i/o length
  //
  static const long DEF_LENGTH = -1;
  static const long DEF_ORDER = 1024;

  // define default argument(s)
  //
  static const AlgorithmData::COEF_TYPE DEF_COEF_TYPE = AlgorithmData::SIGNAL;

  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 70800;
  static const long ERR_INIT = 70801;
  static const long ERR_OUTLEN = 70802;
  static const long ERR_OUTTYP = 70803;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  // parameters related to the algorithm specification
  //
  ALGORITHM algorithm_d;		// algorithm type
  IMPLEMENTATION implementation_d;      // implementation type
  DIRECTION direction_d;                // direction type
  OTYPE otype_d;                        // FFT and DFT output type
  RESOLUTION resolution_d;              // resolution type
  NORM normalization_d;                 // normalization name
  Long ilen_d;                          // user specified input length
  Long olen_d;                          // user specified output length
  Long order_d;                         // FFT transform order

  // parameters related to fourier transform computation
  //
  VectorFloat wd_real_d;
  VectorFloat wd_imag_d;
  VectorFloat tempd_d;

  // parameters storing implementation type and transform order used last time
  //
  IMPLEMENTATION prev_implementation_d;
  Long prev_order_d;
  
  // parameters related to the Decimation In Time Frequency (DITF)
  // transformation
  //
  VectorFloat ditf_trans_factor_indices_d;
  VectorFloat ditf_indices_d;

  // parameters related to the Quick Fourier (QF) transformation
  //
  VectorFloat qf_comp_real_coeff_d;
  VectorFloat qf_comp_imag_coeff_d;
  VectorFloat qf_comp_temp_d;

  // workspace related pointers for Quick Fourier (QF) transformation
  //
  VectorFloat qf_ws_d;  // workspace
  long qf_sc_d;	        // secant table addr.	
  long qf_nc_d;	        // current QFT length	
  long qf_ic_d;	        // current secant inc.	
  long qf_mc_d;	        // current output pruning
  long qf_nn_d;	        // pruned QFT input length
  long qf_mm_d;	        // pruned QFT output length
  long qf_ii_d;	        // current QFT recursion level
  long qf_input_d;         // dct/dst calling arguments
  long qf_output_d;        // dct/dst calling arguments
  
  // static memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:
    
  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }

  // other static methods
  //   
  static boolean diagnose(Integral::DEBUG debug_level);
  
  // method: setDebug
  //  these methods are inherited from the AlgorithmBase class

  // other debug methods
  //
  boolean debug(const unichar* msg) const;

  // method: destructor
  //
  ~FourierTransform() {}

  // method: default constructor
  //
  FourierTransform(ALGORITHM algorithm = DEF_ALGORITHM);
  
  // method: copy constructor
  //
  FourierTransform(const FourierTransform& arg) {
    assign(arg);
  } 

  // assign methods
  //
  boolean assign(const FourierTransform& arg);

  // method: operator=
  //
  FourierTransform& operator= (const FourierTransform& arg) {
    assign(arg);
    return *this;
  }
  
  // i/o methods
  //
  long sofSize() const;
  
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME);
  
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME) const;
  
  boolean readData(Sof& sof, const String& pname = DEF_PARAM,
		   long size = SofParser::FULL_OBJECT,
		   boolean param = true,
		   boolean nested = false);
  
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) const;

  // equality methods
  //
  boolean eq(const FourierTransform& arg) const;
  
  // method: new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

  // method: new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }
  
  // method: delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }

  // method: delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }

  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }

  // other memory management methods
  //
  boolean clear(Integral::CMODE ctype = Integral::DEF_CMODE);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods
  //  set methods
  //
  //---------------------------------------------------------------------------

  // method: setAlgorithm
  //
  boolean setAlgorithm(const ALGORITHM algorithm) {
    if (algorithm_d != algorithm) {
      is_valid_d = false;
    }
    algorithm_d = algorithm;
    return true;  
  }

  // method: setImplementation
  //
  boolean setImplementation(const IMPLEMENTATION implementation) {
    implementation_d = implementation;
    return true;  
  }

  // method: setDirection
  //
  boolean setDirection(const DIRECTION direction) {
    direction_d = direction;
    return true;
  }
  
  // method: setOutputType
  //
  boolean setOutputType(const OTYPE otype) {
    otype_d = otype;
    return true;
  }

  // method: setResolution
  //
  boolean setResolution(const RESOLUTION arg) {
    resolution_d = arg;
    return true;  
  }

  // method: setInputLength
  //
  boolean setInputLength(const long ilen = DEF_LENGTH) {
    ilen_d = ilen;
    return true;
  }

  // method: setOutputLength
  //
  boolean setOutputLength(long olen = DEF_LENGTH) {
    olen_d = olen;
    return true;
  }
  
  // method: setOrder
  //
  boolean setOrder(long order) {
    order_d = order;
    resolution_d = FIXED;
    is_valid_d = false;
    return true;  
  }

  // method: setNormalization
  //
  boolean setNormalization(NORM normalization) {
    normalization_d = normalization;
    return true;  
  }

  // method: set
  //
  boolean set(ALGORITHM algorithm = DFT,
	      IMPLEMENTATION implementation = CONVENTIONAL,
	      DIRECTION direction = FORWARD,
	      OTYPE otype = FULL,
	      RESOLUTION resolution = AUTO,
	      long order = DEF_ORDER,
	      NORM normalization = DEF_NORM) {
    setAlgorithm(algorithm);
    setImplementation(implementation);
    setDirection(direction);
    setOutputType(otype);
    setResolution(resolution);
    if (resolution == FIXED) setOrder(order);
    setNormalization(normalization);
    return true;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods
  //  get methods
  //
  //---------------------------------------------------------------------------
  
  // method: getAlgorithm
  //
  ALGORITHM getAlgorithm() const {
    return algorithm_d;
  }

  // method: getImplementation
  //
  IMPLEMENTATION getImplementation() const {
    return implementation_d;
  }

  // method: getDirection
  //
  DIRECTION getDirection() const {
    return direction_d;  
  }

  // method: getOutputType
  //
  OTYPE getOutputType() const {
    return otype_d;  
  }

  // method: getResolution
  //
  RESOLUTION getResolution() const {
    return resolution_d;  
  }
    
  // method: getInputLength
  //
  long getInputLength() const {
    return ilen_d;
  }

  // method: getOutputLength
  //
  long getOutputLength() const {
    return olen_d;
  }
  
  // method: getOrder
  //
  long getOrder() const {
    return order_d;
  }

  // method: getNormalization
  //
  NORM getNormalization() const {
    return normalization_d;
  }

  // method: get
  //
  boolean get(ALGORITHM& algorithm, IMPLEMENTATION& implementation,
	      DIRECTION& direction, OTYPE& otype,
	      RESOLUTION& resolution, long& order, NORM& normalization) const {
    algorithm = algorithm_d;
    implementation = implementation_d;
    direction = direction_d;
    resolution = resolution_d;
    order = order_d;
    normalization = normalization_d;
    return true;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  computational methods
  //
  //  the first two methods use a conservative approach for computing the
  //  fourier transform. if the chosen fourier transform algorithm supports
  //  computation on a vector the length of the input vector then that
  //  algorithm is used, otherwise a discrete fourier transform is used.
  //
  //  the second set of methods are more aggressive in following the user's
  //  desires. if the input vector length is less than the specified
  //  transform order, the input vector is zero-padded up to the length of
  //  the specified "required_order". if the input vector is greater than the
  //  specified order, then only the elements up to the specified
  //  required order are used (i.e. the input is truncated).
  //
  //  note that these methods do not alter the input vector. i.e. the user is
  //  assured that the input vector after the call to compute() will be no
  //  different than the input vector they passed in.
  //
  //---------------------------------------------------------------------------

  // single-channel compute methods
  //
  boolean compute(VectorComplexFloat& output, const VectorFloat& input,
		  AlgorithmData::COEF_TYPE input_coef_type = DEF_COEF_TYPE,
		  long chan = DEF_CHANNEL_INDEX);

  boolean compute(VectorFloat& output, const VectorComplexFloat& input,
		  AlgorithmData::COEF_TYPE input_coef_type =
		  AlgorithmData::SPECTRUM, long chan = DEF_CHANNEL_INDEX);

  boolean compute(VectorComplexFloat& output, const VectorComplexFloat& input,
		  AlgorithmData::COEF_TYPE input_coef_type = DEF_COEF_TYPE,
		  long chan = DEF_CHANNEL_INDEX);

  boolean compute(VectorFloat& output, const VectorFloat& input,
		  AlgorithmData::COEF_TYPE input_coef_type = DEF_COEF_TYPE,
		  long chan = DEF_CHANNEL_INDEX);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  public methods required by the AlgorithmBase interface contract
  //
  //---------------------------------------------------------------------------

  // assign method
  //
  boolean assign(const AlgorithmBase& arg);
  
  // equality method
  //
  boolean eq(const AlgorithmBase& arg) const;

  // method: className
  //
  const String& className() const {
    return CLASS_NAME;
  }

  // apply method
  //
  boolean apply(Vector<AlgorithmData>& output,
		const Vector< CircularBuffer<AlgorithmData> >& input);
  
  // method to set the parser
  //
  boolean setParser(SofParser* parser);

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

  // common i/o methods
  //
  boolean readDataCommon(Sof& sof, const String& pname,
			 long size, boolean param, boolean nested);
  boolean writeDataCommon(Sof& sof, const String& pname) const;

  // miscellaneous math methods
  //
  static boolean isPower(long& exponent, long base, long value);

  // algorithm checking methods:
  //  check the order of the algorithm and assign a default algorithm
  //  if the chosen algorithm does not support the provided order
  //
  boolean validateImplementation();
  
  // algorithm-specific computation methods: FORWARD
  //
  boolean computeForward(VectorComplexFloat& output,
			 const VectorFloat& input);
  
  boolean computeForward(VectorComplexFloat& output,
			 const VectorComplexFloat& input);

  boolean computeForward(VectorFloat& output,
			 const VectorFloat& input);
  
  // algorithm-specific computation methods: INVERSE
  //
  boolean computeInverse(VectorComplexFloat& output,
			 const VectorComplexFloat& input);
  
  boolean computeInverse(VectorFloat& output,
			 const VectorComplexFloat& input);

  boolean computeInverse(VectorFloat& output,
			 const VectorFloat& input);

  boolean computeInverse(VectorComplexFloat& output,
			 const VectorFloat& input);
  
  // algorithm-specific methods: DFT
  //  the init methods are called to set up the lookup tables for each
  //  algorithm as needed. the real and complex computation methods
  //  generate fourier transform coefficients for real and complex input
  //  respectively.
  //
  boolean dfInit(long order);
  boolean dfReal(VectorFloat& output, const VectorFloat& input);
  boolean dfComplex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: TRIGONOMETRIC
  //
  boolean triInit(long order);
  boolean triReal(VectorFloat& output, const VectorFloat& input);
  boolean triComplex(VectorFloat& output, const VectorFloat& input);
  
  // algorithm-specific methods: SPLIT_RADIX
  //
  boolean srInit(long order);
  boolean srReal(VectorFloat& output, const VectorFloat& input);
  boolean srComplex(VectorFloat& output, const VectorFloat& input);
  
  // algorithm-specific methods: RAD_2
  //
  boolean rad2Init(long order);
  boolean rad2Real(VectorFloat& output, const VectorFloat& input);
  boolean rad2Complex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: RAD_4
  //
  boolean rad4Init(long order);
  boolean rad4Real(VectorFloat& output, const VectorFloat& input);
  boolean rad4Complex(VectorFloat& output, const VectorFloat& input);
  
  // algorithm-specific methods: FAST_HARTLEY
  //
  boolean fhInit(long order);
  boolean fhReal(VectorFloat& output, const VectorFloat& input,
		 boolean isReal = true);
  boolean fhComplex(VectorFloat& output, const VectorFloat& input);
    
  // algorithm-specific methods: QFT
  //
  boolean qfInit(long order);  
  boolean qfReal(VectorFloat& output, const VectorFloat& input);
  boolean qfComplex(VectorFloat& output, const VectorFloat& input);
    
  // algorithm-specific methods: DITF
  //
  boolean ditfInit(long order);
  boolean ditfReal(VectorFloat& output, const VectorFloat& input);
  boolean ditfComplex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: DCT TYPE_I
  //
  boolean dct1Init(long order);
  boolean dct1Real(VectorFloat& output, const VectorFloat& input);
  boolean dct1Complex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: DCT TYPE_II
  //
  boolean dct2Init(long order);
  boolean dct2Real(VectorFloat& output, const VectorFloat& input);
  boolean dct2Complex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: DCT TYPE_III
  //
  boolean dct3Init(long order);
  boolean dct3Real(VectorFloat& output, const VectorFloat& input);
  boolean dct3Complex(VectorFloat& output, const VectorFloat& input);

  // algorithm-specific methods: DCT TYPE_IV
  //
  boolean dct4Init(long order);
  boolean dct4Real(VectorFloat& output, const VectorFloat& input);
  boolean dct4Complex(VectorFloat& output, const VectorFloat& input);
  
  // miscellaneous DCT/DST methods
  //
  boolean dct_real_cc(VectorFloat& output, const VectorFloat& input);
  boolean dst_real_cc(VectorFloat& output, const VectorFloat& input);
};

// end of include file
// 
#endif
