/**
* @file KernelData.h
* @author Erik Rodner
* @date 01/19/2010

*/
#ifndef _NICE_OBJREC_KERNELDATAINCLUDE
#define _NICE_OBJREC_KERNELDATAINCLUDE

#include "core/basics/Config.h"
#include "core/algebra/CholeskyRobust.h"
#include "core/vector/MatrixT.h"

namespace OBJREC {

/** @class KernelData
 * caching some kernel data
 *
 * @author Erik Rodner
 */
class KernelData
{
public:
    enum {
        QUADRATIC_DISTANCES = 0
    };

protected:
    bool verbose;

    NICE::CholeskyRobust *cr;

protected:
    NICE::Matrix kernelMatrix;

    NICE::Matrix inverseKernelMatrix;

    NICE::Matrix choleskyMatrix;

    std::map<int, NICE::Matrix *> cachedMatrices;

    double logdet;

    void initFromConfig ( const NICE::Config *conf, const std::string & section );

    NICE::Matrix B;
    NICE::Matrix U;
    NICE::Matrix V;
    NICE::Matrix F;
    NICE::Matrix F_inv;

public:

    /** standard stuff */
    KernelData();

    /** copy constructor */
    KernelData( const KernelData & src );

    /** simple constructor using config settings for numerical details */
    KernelData( const NICE::Config *conf, const std::string & section = "Kernel" );

    /** the config contains information about numerical setting of the cholesky factorization etc. */
    KernelData( const NICE::Config *conf, const NICE::Matrix & kernelMatrix, const std::string & section = "Kernel", bool updateCholesky = true );

    /** simple destructor */
    virtual ~KernelData();

    /** update the cholesky factorization necessary to use computeInverseKernelMultiply */
    virtual void updateCholeskyFactorization ();

    /** in nearly all cases computeInverseKernelMultiply can be used instead */
    virtual void updateInverseKernelMatrix ();

    /** compute K^{-1} * x */
    virtual void computeInverseKernelMultiply ( const NICE::Vector & x, NICE::Vector & result ) const;

    /** standard const and non-const get functions */
    virtual const NICE::Matrix & getKernelMatrix() const;
    virtual NICE::Matrix & getKernelMatrix();
    virtual const NICE::Matrix & getInverseKernelMatrix() const;
    virtual NICE::Matrix & getInverseKernelMatrix();
    virtual const NICE::Matrix & getCholeskyMatrix() const;

    /** get the logdet of the current kernel matrix (cholesky factorization has to be computed in advance) */
    double getLogDetKernelMatrix () const {
        return logdet;
    };

    /** get the numbers of rows (and columns) of the kernel matrix */
    virtual uint getKernelMatrixSize () const;

    /** get a pre-cached matrix */
    const NICE::Matrix & getCachedMatrix (int i) const;
    /** set a pre-cached matrix */
    void setCachedMatrix (int i, NICE::Matrix *m);

    /** did we already start updateCholeskyFactorization() */
    bool hasCholeskyFactorization () const {
        return (choleskyMatrix.rows() == kernelMatrix.rows());
    };

    /** get efficient GP regression loo estimates and corresponding variances, which can be used
      * to perform model selection (cf. Rasmussen and Williams) */
    void getLooEstimates ( const NICE::Vector & y, NICE::Vector & muLoo, NICE::Vector & sigmaLoo ) const;

    /** clone this object */
    virtual KernelData *clone(void) const;

    void getGPLikelihoodWithOneNewRow( const NICE::Vector & y, const double & oldLogdetK, const int & rowIndex, const NICE::Vector & newRow, const NICE::Vector & oldAlpha, NICE::Vector & newAlpha, double & loglike);

    void getGPLikelihoodWithOneNewRow_FirstPart( const int & rowIndex, const NICE::Vector & newRow);

    void getGPLikelihoodWithOneNewRow_SecondPart( const NICE::Vector & y, const double & oldLogdetK, const NICE::Vector & oldAlpha, NICE::Vector & newAlpha, double & loglike);

    void perform_Rank_2_Update(const int & rowIndex, const NICE::Vector & newRow);
    void perform_Rank_2k_Update(const std::vector<int> & rowIndices, const std::vector<NICE::Vector> & newRows);
    void delete_one_row(const int & rowIndex);
    void delete_multiple_rows(std::vector<int> & indices);

    void setKernelMatrix(const NICE::Matrix & k_matrix);

    void increase_size_by_One();
    void increase_size_by_k(const uint & k);

    void set_verbose(const bool & _verbose) {
        verbose = _verbose;
    };
    bool get_verbose() {
        return verbose;
    };
};

}

#endif