/** 
* @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