/** 
* @file PDFMultinomial.cpp
* @brief Normal Distribution / Gaussian PDF
* @author Erik Rodner
* @date 01/29/2008

*/
#include <iostream>
#include <assert.h>

#include "vislearning/math/pdf/PDFMultinomial.h"
#include "vislearning/math/pdf/gslRandomNumberGenerator.h"

using namespace OBJREC;

using namespace std;
// refactor-nice.pl: check this substitution
// old: using namespace ice;
using namespace NICE;



PDFMultinomial::PDFMultinomial( int dimension ) : theta(dimension)
{
    assert ( dimension > 0 );
    N = 1;
    for ( int i = 0 ; i < dimension ; i++ )
	theta[i] = 1.0 / dimension;
}

// refactor-nice.pl: check this substitution
// old: PDFMultinomial::PDFMultinomial ( const Vector & _theta, int _N ) 
PDFMultinomial::PDFMultinomial ( const NICE::Vector & _theta, int _N ) 
    : theta(_theta), N(_N)
{
}

PDFMultinomial::~PDFMultinomial()
{
}

// refactor-nice.pl: check this substitution
// old: double PDFMultinomial::getNLogDensity ( const Vector & x ) const
double PDFMultinomial::getNLogDensity ( const NICE::Vector & x ) const
{
#ifdef NICE_USELIB_GSL
    assert ( x.size() == theta.size() );
    unsigned int *ix = new unsigned int[x.size()];
    double r = gsl_ran_multinomial_lnpdf ( theta.size(), theta.getDataPointerConst(), ix );
    delete [] ix;
    return r;
#else
    #warning "PDFMultinomial::getNLogDensity: this function needs the GNU Scientific Library"
    fprintf (stderr, "PDFMultinomial::getNLogDensity: this function needs the GNU Scientific Library\n");
    exit(-1);
    return 0.0;
#endif

}

// refactor-nice.pl: check this substitution
// old: double PDFMultinomial::getProb ( const Vector & x ) const
double PDFMultinomial::getProb ( const NICE::Vector & x ) const
{
#ifdef NICE_USELIB_GSL
    assert ( x.size() == theta.size() );
    unsigned int *ix = new unsigned int[theta.size()];
    double r = gsl_ran_multinomial_pdf ( theta.size(), theta.getDataPointerConst(), ix );
    delete [] ix;
    return r;
#else
    #warning "PDFMultinomial::getProb: this function needs the GNU Scientific Library"
    fprintf (stderr, "PDFMultinomial::getProb: this function needs the GNU Scientific Library\n");
    exit(-1);
    return 0.0;
#endif

}

int PDFMultinomial::getDimension () const
{
    return theta.size();
}

int PDFMultinomial::sample () const
{
#ifdef NICE_USELIB_GSL
    if ( theta.size() == 1 )
	return 0;

    initGSLRandom();
    double r = gsl_rng_uniform(randomGSL);
    NICE::Vector cumDist ( theta.size() );
    cumDist[0] = theta[0];
    for ( int i = 1 ; i < theta.size() ; i++ )
    {
		if ( r < cumDist[i-1] ) return i-1;
		cumDist[i] += cumDist[i-1] + theta[i];
    }
    return theta.size()-1;
#else
    #warning "PDFMultinomial::sample: this function needs the GNU Scientific Library"
    fprintf (stderr, "PDFMultinomial::sample: this function needs the GNU Scientific Library\n");
    exit(-1);
    return 0;
#endif

}

void PDFMultinomial::sample ( VVector & samples, int count ) const
{
#ifdef NICE_USELIB_GSL
    initGSLRandom();

    unsigned int *ix = new unsigned int[theta.size()];
    for ( int i = 0 ; i < count ; i++ )
    {
		NICE::Vector x ( theta.size() );
		gsl_ran_multinomial (randomGSL, theta.size(), N, theta.getDataPointerConst(), ix );

		for ( int j = 0 ; j < theta.size() ; j++ )
			x[j] = ix[j];

		samples.push_back ( x );
    }
    delete [] ix;
#else
    #warning "PDFMultinomial::sample: this function needs the GNU Scientific Library"
    fprintf (stderr, "PDFMultinomial::sample: this function needs the GNU Scientific Library\n");
    exit(-1);
#endif
}

double PDFMultinomial::getDiscreteProbability ( int index ) const
{
    assert ( (index >= 0) && (index < (int)theta.size()) );
    return theta[index];
}