// file: $isip/class/stats/MixtureModel/MixtureModel.h
// version: $Id: MixtureModel.h 9742 2004-08-20 20:43:15Z may $
//

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

// isip include files
//
#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_SINGLE_LINKED_LIST
#include <SingleLinkedList.h>
#endif

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

#ifndef ISIP_STATISTICAL_MODEL
#include <StatisticalModel.h>
#endif

// MixtureModel: a class to score test vectors according to a
// distribution which is a linear combination of other StatisticalModels:
// 
// L(x) = w1 * S1(x) + w2 * S2(x) + ... + wN * SN(x)
//
// 'L' is the output likelihood
// 'N' is the number of models
// 'w's are the weights
// 'S's are the models
//
class MixtureModel : 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_WEIGHTS;
  static const String PARAM_MODELS;

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

  // define the default value(s) of the class data
  //
  static const float MIN_SCORE = -1e100;

  // default arguments to methods
  //
  static const float DEF_NORM = 1.0;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  

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

  // list of StatisticalModels
  //
  SingleLinkedList<StatisticalModel> models_d;

  // vector of weights
  //
  VectorFloat weights_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 inherited from base class
  //
  boolean debug(const unichar* msg) const;
     
  // method: destructor
  //
  ~MixtureModel() {}

  // method: default constructor
  //
  MixtureModel(MODE mode = DEF_MODE) {
    mode_d = mode;
    is_valid_d = false;
  }

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

  // method: assign
  //
  boolean assign(const MixtureModel& arg);

  // method: operator=
  //
  MixtureModel& operator=(const MixtureModel& arg) {
    assign(arg);
    return *this;
  }

  // method: sofSize
  //
  long sofSize() const {
    return (weights_d.sofSize() + models_d.sofSize());
  }

  // method: sofAccumulatorSize
  //
  long sofAccumulatorSize() const {
    long size = 0;
    for (boolean more =
       const_cast<SingleLinkedList<StatisticalModel> &>(models_d).gotoFirst();
       more;
       more =
       const_cast<SingleLinkedList<StatisticalModel> &>(models_d).gotoNext()) {
      size += models_d.getCurr()->sofAccumulatorSize();
    }
    return size;    
  }

  // method: sofOccupanciesSize
  //
  long sofOccupanciesSize() const {
    long size = 0;
    for (boolean more =
       const_cast<SingleLinkedList<StatisticalModel> &>(models_d).gotoFirst();
       more;
       more =
       const_cast<SingleLinkedList<StatisticalModel> &>(models_d).gotoNext()) {
      size += models_d.getCurr()->sofOccupanciesSize();
    }
    return size;        
  }  
  
  // 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 MixtureModel& 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) {
    weights_d.clear(cmode);
    models_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: getModels
  //
  SingleLinkedList<StatisticalModel>& getModels() {
    return models_d;
  }
  
  // method: setModels
  //
  boolean setModels(SingleLinkedList<StatisticalModel>& new_models);
  
  // methods to manipulate the weights
  //
  VectorFloat& getDaWeights() {
    return weights_d;
  }
  boolean getWeights(VectorFloat& arg) const;
  boolean setWeights(const VectorFloat& arg);

  // normalization methods
  //
  boolean initializeWeights();  
  boolean isNormalized(float norm = DEF_NORM) const;
  boolean normalizeWeights(float norm = DEF_NORM);

  // methods to manipulate the models
  //
  boolean add(StatisticalModelBase& ptr);

  //---------------------------------------------------------------------------
  //
  // 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: getMean
  //
  boolean getMean(VectorFloat& mean);
  
  // method: getCovariance
  //
  boolean getCovariance(MatrixFloat& cov);

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

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  mixture reestimation methods
  //
  //---------------------------------------------------------------------------

  // method: getOccupancy
  //
  double getOccupancy() {
    Double occupancy = 0;
    for (boolean more = models_d.gotoFirst(); more;
	 more = models_d.gotoNext()) {
      occupancy += models_d.getCurr()->getOccupancy();
    }
    return occupancy;
  }

  // method: setOccupancy
  //
  boolean setOccupancy(double arg) {
    for (boolean more = models_d.gotoFirst(); more;
	 more = models_d.gotoNext()) {
      models_d.getCurr()->setOccupancy(arg);
    }
    return true;
  }  

  // method: getAccessCount
  //
  long getAccessCount() {
    long num_access = 0;
    for (boolean more = models_d.gotoFirst(); more;
	 more = models_d.gotoNext()) {
      num_access += models_d.getCurr()->getAccessCount();
    }
    return num_access;
  }

  // method: setAccessCount
  //
  boolean setAccessCount(long arg) {
    for (boolean more = models_d.gotoFirst(); more;
	 more = models_d.gotoNext()) {
      models_d.getCurr()->setAccessCount(arg);
    }
    return true;
  }    

  // method: resetAccumulators
  //
  boolean resetAccumulators() {
    for (boolean more = models_d.gotoFirst(); more;
	 more = models_d.gotoNext()) {
      models_d.getCurr()->resetAccumulators();
    }
    return true;
  }

  // method: initialize
  //
  boolean initialize(VectorFloat& param);

  // method: accumulate
  //
  boolean accumulate(VectorFloat& data);
  
  // method: accumulate
  //
  boolean accumulate(VectorDouble& param,
		     VectorFloat& data, boolean precomp);

  // method: update
  //  
  boolean update(VectorFloat& varfloor, long min_count);
    
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

};

// end of include file
// 
#endif

