// file: $isip/class/stat/GaussianModel/GaussianModel.h
// version: $Id: GaussianModel.h 8972 2003-01-11 15:51:19Z jelinek $
//

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

// isip include files
//
#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

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

#ifndef ISIP_VECTOR_DOUBLE
#include <VectorDouble.h>
#endif

#ifndef ISIP_MATRIX_FLOAT
#include <MatrixFloat.h>
#endif

#ifndef ISIP_MATRIX_DOUBLE
#include <MatrixDouble.h>
#endif

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

#ifndef ISIP_STATISTICAL_MODEL_BASE
#include <StatisticalModelBase.h>
#endif

// GaussianModel: a class to score test vectors according to a
// multi-dimensional Gaussian distribution:
//
//             1 
//  -------------------------  * exp (-1/2 * [(x-u)' * inverse(Cov) * (x-u)])
//   sqrt( (2*pi)^N * |Cov| )
//
//  'N' is the dimension of the probability space
//  'x' is the input vector
//  'u' is the mean of the distribution
//  'Cov' is the covariance of the distribution
//
class GaussianModel : public StatisticalModelBase {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  static const String DEF_PARAM;
  static const String PARAM_MEAN;
  static const String PARAM_COVARIANCE;
  static const String PARAM_MEAN_ACCUM;    
  static const String PARAM_COVARIANCE_ACCUM;
  static const String PARAM_OCCUPANCY_ACCUM;
  static const String PARAM_ACCESS_ACCUM;  

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------  

  // define the default value(s) of the class data
  //
  static const float DEF_SCALE_FACTOR = 0.0;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  

  static const long ERR = 60100;
    
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // accumulator used in training
  //
  Double occ_accum_d;
  Long access_accum_d;  
  VectorDouble mean_accum_d;  
  MatrixDouble covar_accum_d;
  
  // mean vector and covariance matrix
  //
  VectorFloat mean_d;
  MatrixFloat covariance_d;
  MatrixFloat orig_covar_d;  
  
  // scale factor
  //
  double scale_d;
  
  // static deviation vector to store (input_observation - mean_d)
  // when in pre-compute mode
  //
  static VectorFloat deviation_d;

  // 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);

  // debug methods
  //  setDebug is inherited from base class
  //
  boolean debug(const unichar* msg) const;
  
  // method: destructor
  //
  ~GaussianModel() {}

  // method: default constructor
  //
  GaussianModel(MODE mode = DEF_MODE) {
    mode_d = mode;
    occ_accum_d = 0.0;
    access_accum_d = 0;    
    mean_accum_d.assign(0.0);
    covar_accum_d.assign(0.0);
    scale_d = DEF_SCALE_FACTOR;
    is_valid_d = false;
  }

  // method: copy constructor
  //
  GaussianModel(const GaussianModel& arg) {
    assign(arg);
  }

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

  // method: operator=
  //
  GaussianModel& operator=(const GaussianModel& arg) {
    assign(arg);
    return *this;
  }
  
  // method: sofSize
  //
  long sofSize() const {
    return (mean_d.sofSize() + covariance_d.sofSize());
  }

  // method: sofAccumulatorSize
  //
  long sofAccumulatorSize() const {
    return (occ_accum_d.sofSize() + access_accum_d.sofSize() + mean_accum_d.sofSize() + covar_accum_d.sofSize());
  }

  // method: sofOccupanciesSize
  //
  long sofOccupanciesSize() const {
    return occ_accum_d.sofSize();
  }  
  
  // other i/o methods
  //
  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;

  boolean readAccumulator(Sof& sof, long tag,
		    const String& name = CLASS_NAME);  
  boolean writeAccumulator(Sof& sof, long tag,
		     const String& name = CLASS_NAME) const;
  
  boolean readAccumulatorData(Sof& sof, const String& pname = DEF_PARAM,
			long size = SofParser::FULL_OBJECT,
			boolean param = true,
			boolean nested = false);  
  boolean writeAccumulatorData(Sof& sof,
			 const String& pname = DEF_PARAM) const;

  boolean readOccupancies(Sof& sof, long tag,
			  const String& name = CLASS_NAME);  
  boolean writeOccupancies(Sof& sof, long tag,
		     const String& name = CLASS_NAME) const;
  
  boolean readOccupanciesData(Sof& sof, const String& pname = DEF_PARAM,
			      long size = SofParser::FULL_OBJECT,
			      boolean param = true,
			      boolean nested = false);  
  boolean writeOccupanciesData(Sof& sof,
			 const String& pname = DEF_PARAM) const;
  
  // equality methods
  //
  boolean eq(const GaussianModel& arg) const;

  // method: operator new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

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

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

  // method: operator 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);
  }

  // method: clear
  //
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE) {
    mean_d.clear(cmode);
    covariance_d.clear(cmode);
    orig_covar_d.clear(cmode);    
    is_valid_d = false;
    return true;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  additional methods unique to this class needed to support the
  //  interface contract
  //
  //--------------------------------------------------------------------------

  // method: constructor
  //
  GaussianModel(VectorFloat mean, MatrixFloat cov, MODE mode = DEF_MODE) {
    mean_d.assign(mean);
    covariance_d.assign(cov);
    orig_covar_d.assign(cov);        
    scale_d = 0;
    mode_d = mode;
    is_valid_d = false;
  }

  // method: constructor
  //
  GaussianModel(float mean, float variance, MODE mode = DEF_MODE) {
    VectorFloat vmean(1, mean);
    mean_d.assign(vmean);
    covariance_d.setDimensions(1, 1, false, Integral::SYMMETRIC);
    covariance_d.assign(variance);
    orig_covar_d.setDimensions(1, 1, false, Integral::SYMMETRIC);
    orig_covar_d.assign(variance);    
    scale_d = 0;
    mode_d = mode;
    is_valid_d = false;
  }

  // method: setMean
  //
  boolean setMean(const VectorFloat& mean) {
    mean_d.assign(mean);
    return true;
  }

  // method: setMean
  //
  boolean setMean(float mean) {
    VectorFloat vmean(1, mean);
    mean_d.assign(vmean);
    return true;
  }

  // method: setCovariance
  //
  boolean setCovariance(const MatrixFloat& cov) {
    covariance_d.assign(cov);
    orig_covar_d.assign(cov);
    is_valid_d = false;
    return true;
  }

  // method: setCovariance
  //
  boolean setCovariance(float cov) {
    covariance_d.setDimensions(1, 1, false, Integral::SYMMETRIC);
    covariance_d.assign(cov);
    orig_covar_d.setDimensions(1, 1, false, Integral::SYMMETRIC);
    orig_covar_d.assign(cov);    
    is_valid_d = false;
    return true;
  }

  // method: getMean
  //
  boolean getMean(VectorFloat& mean) {
    return mean.assign(mean_d);
  }

  // method: getMeanAccumulator
  //
  boolean getMeanAccumulator(VectorDouble& mean_accum) {
    mean_accum.assign(mean_accum_d);
    return true;
  }
  
  // method: getCovariance
  //
  boolean getCovariance(MatrixFloat& cov) {
    
    // if we are not in the precompute mode, return the covariance
    // matrix
    //
    if (mode_d != PRECOMPUTE) {
      return cov.assign(covariance_d);
    }
    
    // else return the original covariance matrix
    //
    else
      return cov.assign(orig_covar_d);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  required for the base class interface contract
  //
  //--------------------------------------------------------------------------

  // StatisticalModelBase required methods
  //
  boolean assign(const StatisticalModelBase& arg);
  boolean eq(const StatisticalModelBase& arg) const;

  // set methods
  //
  boolean setMode(MODE arg);

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

  // initialization methods
  //
  boolean init();

  // method: getLikelihood
  //
  float getLikelihood(const VectorFloat& input) {
    return Integral::exp(getLogLikelihood(input));
  }

  // computational methods
  //
  float getLogLikelihood(const VectorFloat& input);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  accumulate and update methods needed for training models
  //
  //---------------------------------------------------------------------------

  // method: resetAccumulators
  //
  boolean resetAccumulators() {
    access_accum_d = 0;
    occ_accum_d = 0.0;
    mean_accum_d.assign(0.0);
    covar_accum_d.assign(0.0);
    return true;
  }

  // method: getOccupancy
  //
  double getOccupancy() {
    return occ_accum_d;
  }

  // method: setOccupancy
  //
  boolean setOccupancy(double arg) {
    occ_accum_d = arg;
    return true;
  }  

  // method: getAccessCount
  //
  long getAccessCount() {
    return access_accum_d;
  }

  // method: setAccessCount
  //
  boolean setAccessCount(long arg) {
    access_accum_d = arg;
    return true;
  }  
  
  // method that accumulates the statistics for the model which are
  // needed to update the model parameters during training
  //
  boolean accumulate(VectorDouble& param,
		     VectorFloat& data, boolean precomp);

  // method that updates the statistical model parameters using the
  // accumulated statistics during training
  //  
  boolean update(VectorFloat& varfloor, long min_count);

  // methods that initializes the statistical model parameters using
  // accumulated feature vectors
  //
  boolean accumulate(VectorFloat& data);    
  boolean initialize(VectorFloat& param);
  
  // method that adapts mean parameters given the transformation matrix
  //
  boolean adapt(const Vector<VectorFloat>& transform);

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
};

// end of include file
// 
#endif
