/**
 * @file GMM.h
 * @brief Gaussian Mixture Model based on
  article{Calinon07SMC,
  title="On Learning, Representing and Generalizing a Task in a Humanoid
  Robot",
  author="S. Calinon and F. Guenter and A. Billard",
  journal="IEEE Transactions on Systems, Man and Cybernetics, Part B.
  Special issue on robot learning by observation, demonstration and
  imitation",
  year="2007",
  volume="37",
  number="2",
  pages="286--298"
}
 * @author Björn Fröhlich, Alexander Freytag
 * @date 05/14/2009

 */
#ifndef GMMINCLUDE
#define GMMINCLUDE

#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"

#include "vislearning/cbaselib/MultiDataset.h"
#include "vislearning/cbaselib/LocalizationResult.h"
#include "vislearning/cbaselib/CachedExample.h"
#include "vislearning/cbaselib/Example.h"

#include "vislearning/math/cluster/ClusterAlgorithm.h"
#include "core/vector/VVector.h"

namespace OBJREC {

class GMM : public ClusterAlgorithm
{
  protected:

    //! number of gaussians
    int i_numOfGaussians;

    //! dimension of each feature
    int dim;

    //! mean vectors
    NICE::VVector mu;

    //! sparse sigma vectors
    NICE::VVector sparse_sigma;
    
    //! the weight for each gaussian
    std::vector<double> priors;    

    //! sparse inverse sigma vectors (if usediag)
    NICE::VVector sparse_inv_sigma;

    //! save det_sigma for fast computing
    std::vector<double> log_det_sigma;



    //! parameters for other GM to compare
    NICE::VVector mu2;
    NICE::VVector sparse_sigma2;
    std::vector<double> priors2;
    bool b_compareTo2ndGMM;

    //! maximum number of iterations for EM
    int maxiter;

    //! how many features to use, use 0 for alle input features
    int featsperclass;

    //! for faster computing cdimval = dim*2*PI
    double cdimval;

    //! parameter for the map estimation
    double tau;

    //! whether to use pyramid initialisation or not
    bool pyramid;
    
  public:
    
    ///////////////////// ///////////////////// /////////////////////
    //                   CONSTRUCTORS / DESTRUCTORS
    ///////////////////// ///////////////////// /////////////////////      
    /**
      * simplest constructor
     */
    GMM();

    /**
     * simple constructor
     * @param _numOfGaussians
     */
    GMM ( int _numOfGaussians );

    /**
     * standard constructor
     * @param conf a Configfile
     * @param _numOfGaussians number of gaussian
     */
    GMM ( const NICE::Config *conf, int _numOfGaussians );
    
    /**
     * @brief recommended constructor
     * @author Alexander Freytag
     * @date 14-02-2014 ( dd-mm-yyyy )
     * @param _conf a Configfile
     * @param _confSection tag specifying the part in the config file
     */
    GMM ( const NICE::Config * _conf, const std::string & _confSection = "GMM" );    

    /**
     * standard destructor
     */
    ~GMM();
    
    /** 
     * @brief Jobs previously performed in the config-version of the constructor, read settings etc.
     * @author Alexander Freytag
     * @date 13-02-2014 ( dd-mm-yyyy )
     */    
    void initFromConfig ( const NICE::Config * _conf, const std::string & _confSection = "GMM");         
        
    ///////////////////// ///////////////////// /////////////////////
    //                      CLUSTERING STUFF
    ///////////////////// ///////////////////// //////////////////        

    /**
     * computes the mixture
     * @param examples the input features
     */
    void computeMixture ( Examples examples );

    /**
     * computes the mixture
     * @param DataSet the input features
     */
    void computeMixture ( const NICE::VVector &DataSet );

    /**
     * returns the probabilities for each gaussian in a sparse vector
     * @param vin input vector
     * @param probs BoV output vector
     */
    void getProbs ( const NICE::Vector &vin, NICE::SparseVector &probs );

    /**
     * returns the probabilities for each gaussian
     * @param vin input vector
     * @param probs BoV output vector
     */
    void getProbs ( const NICE::Vector &vin, NICE::Vector &probs );

    /**
     * returns the fisher score for the gmm
     * @param vin input vector
     * @param probs Fisher score output vector
     */
    void getFisher ( const NICE::Vector &vin, NICE::SparseVector &probs );

    /**
     *   init the GaussianMixture by selecting randomized mean vectors and using the coovariance of all features
     * @param DataSet input Matrix
     */
    void initEM ( const NICE::VVector &DataSet );

    /**
     *   alternative for initEM: init the GaussianMixture with a K-Means clustering
     * @param DataSet input Matrix
     */
    void initEMkMeans ( const NICE::VVector &DataSet );

    /**
     * performs Expectation Maximization on the Dataset,  in order to obtain a nState GMM Dataset is a Matrix(no_classes,nDimensions)
     * @param DataSet input Matrix
     * @param gaussians number gaussians to use
     * @return number of iterations
     */
    int doEM ( const NICE::VVector &DataSet, int nbgaussians );

    /**
     * Compute log probabilty of vector v for the given state. *
     * @param Vin
     * @param state
     * @return
       */
    double logpdfState ( const NICE::Vector &Vin, int state );

    /**
     * determine the best mixture for the input feature
     * @param v input feature
     * @param bprob probability of the best mixture
     * @return numer of the best mixture
     */
    int getBestClass ( const NICE::Vector &v, double *bprob = NULL );

    /**
     * Cluster a given Set of features and return the labels for each feature
     * @param features input features
     * @param prototypes mean of the best gaussian
     * @param weights weight of the best gaussian
     * @param assignment number of the best gaussian
     */
    void cluster ( const NICE::VVector & features, NICE::VVector & prototypes, std::vector<double> & weights, std::vector<int> & assignment );

    /**
     * save GMM data
     * @param filename filename
     * @NOTE deprecated, use methods inherited from persistent instead!
     */
    void saveData ( const std::string filename );

    /**
    * load GMM data
    * @param filename filename
    * @return true if everything works fine
    * @NOTE deprecated, use methods inherited from persistent instead!
    */
    bool loadData ( const std::string filename );

    /**
     * return the parameter of the current mixture model
     * @param mu
     * @param sSigma
     * @param p
     */
    void getParams ( NICE::VVector &mean, NICE::VVector &sSigma, std::vector<double> &p ) const;

    /**
     * Set the parameters of an other mixture for comparing with this one
     * @param mean mean vectors
     * @param sSigma diagonal covariance Matrixs
     * @param p weights
     */
    void setGMMtoCompareWith ( NICE::VVector mean, NICE::VVector sSigma, std::vector<double> p );

    /**
     * probability product kernel
     * @param sigma1
     * @param sigma2
     * @param mu1
     * @param mu2
     * @param p
     * @return
     */
    double kPPK ( NICE::Vector sigma1, NICE::Vector sigma2, NICE::Vector mu1, NICE::Vector mu2, double p ) const;

    /**
     * starts a comparison between this Mixture and another one set bei "setGMMtoCompareWith"
     */
    double compareTo2ndGMM()  const;

    /**
     * whether to compare or not
     * @param _compareTo2ndGMM
     */
    void setCompareTo2ndGMM ( const bool & _compareTo2ndGMM = true );

    int getNumberOfGaussians() const; 
    
    ///////////////////// INTERFACE PERSISTENT /////////////////////
    // interface specific methods for store and restore
    ///////////////////// INTERFACE PERSISTENT /////////////////////      
    
    /**
    * @brief Load object from external file (stream)
    * @author Alexander Freytag
    * @date 13-02-2014 ( dd-mm-yyyy )
    */
    void restore ( std::istream & is, int format = 0 );

    /**
    * @brief Save object to external file (stream)
    * @author Alexander Freytag
    * @date 13-02-2014 ( dd-mm-yyyy )
    */
    void store ( std::ostream & os, int format = 0 ) const;

    /**
    * @brief Clear object
    * @author Alexander Freytag
    * @date 13-02-2014 ( dd-mm-yyyy )
    */
    void clear ();        
};


} // namespace

#endif
