/* -*- C -*- */
#define PY_SSIZE_T_CLEAN
#include <pygsl/numpy_api_version.h>
/* numpy needs to be imported directly for NPY_UNIT */
#include <numpy/arrayobject.h>

static PyObject*
PyGSL_rng_multinomial_hand(PyGSL_rng *rng, PyObject *args)
{
     PyObject *po;
     PyArrayObject *array_p = NULL, *array_n = NULL;

     int line = __LINE__;
     unsigned int N;
     long dim=1;
     PyGSL_array_index_t k = 0, i = 0, dims[2] = {1,1};

     FUNC_MESS_BEGIN();
     assert(args);

     if(0 == PyArg_ParseTuple(args, "kO|l", &N, &po, &dim)){
	  line = __LINE__ -1;
	  goto fail;
     }

     /* Todo: should be an at least 1d */
     array_p = PyGSL_vector_check(po, -1, PyGSL_DARRAY_INPUT(1), NULL, NULL);
     if(array_p == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     k = PyArray_DIM(array_p, 0);
     dims[0] = dim;
     dims[1] = k;
     array_n = PyGSL_New_Array(2, dims, NPY_UINT);
     if(array_n == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     DEBUG_MESS(4, "Built Matrix object @ %p with refcount %ld dims = [%ld, %ld], dim= %ld, k = %ld",
		(void *) array_n, PyGSL_PY_ARRAY_GET_REFCNT(array_n),
		(long) PyArray_DIM(array_n, 0), (long) PyArray_DIM(array_n, 1), dim, (long) k);

     double *p = PyArray_DATA(array_p);
     for(i = 0; i<PyArray_DIM(array_n, 0); ++i){
	 unsigned int *n = (unsigned int *) PyArray_GETPTR1(array_n, i);
	 gsl_ran_multinomial(rng->rng, k, N, p, n);
     }

     DEBUG_MESS(4, "Dereferencing p @ %p", (void *) array_p);
     Py_XDECREF(array_p);
     FUNC_MESS_END();
     return (PyObject *) array_n;

 fail:
     FUNC_MESS("FAIL");
     PyGSL_add_traceback(NULL, __FILE__, __FUNCTION__, line);
     Py_XDECREF(array_p);
     Py_XDECREF(array_n);
     return NULL;
}


static PyObject*
PyGSL_rng_multinomial_pdf_support(PyObject *self, PyObject *args,
				  double (*evaluator)(size_t K, const double p[], const unsigned int n[])
    )
{
    PyObject *po, *no;
    PyArrayObject *array_p = NULL, *array_n = NULL, *pdfa = NULL;
    PyGSL_array_index_t k = 0, n_sample = 0, i;
    double *phi, *pdf;
    int line = __LINE__;

    FUNC_MESS_BEGIN();
    if(0 == PyArg_ParseTuple(args, "OO", &po, &no)){
	  line = __LINE__ -1;
	  goto fail;
     }

     array_p = PyGSL_vector_check(po, -1, PyGSL_DARRAY_CINPUT(1), NULL, NULL);
     if(array_p == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     k = PyArray_DIM(array_p, 0);

     DEBUG_MESS(4, "array_p dims %d, [%ld] k %ld", PyArray_NDIM(array_p),
		(long) PyArray_DIM(array_p, 0), (long) k);

     array_n = PyGSL_matrix_check(no, -1, k,
				  PyGSL_BUILD_ARRAY_INFO(PyGSL_CONTIGUOUS|PyGSL_INPUT_ARRAY, NPY_UINT, sizeof(uint), 2),
				  NULL, NULL, NULL);
     if(array_n == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     n_sample = PyArray_DIM(array_n, 0);
     DEBUG_MESS(2, "array_n [%ld, %ld] n_sample %ld",
		(long )PyArray_DIM(array_n, 0), (long) PyArray_DIM(array_n, 1), n_sample);
     pdfa = PyGSL_New_Array(1, &n_sample, NPY_DOUBLE);
     if(pdfa == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }

     DEBUG_MESS(4, "before evaluation: phi[%ld] @ %p pdf[%ld] of %d dims @ %p",
		(long) PyArray_DIM(array_p, 0), (void *) array_p,
		(long) PyArray_DIM(pdfa, 0), PyArray_NDIM(pdfa), (void *) pdfa);

     phi = (double *) PyArray_DATA(array_p);
     pdf = (double *) PyArray_DATA(pdfa);


     for(i = 0; i < n_sample; ++i){
	 unsigned int *n = (unsigned int *) PyArray_GETPTR1(array_n, i);
	 size_t K = n_sample;
	 pdf[i] = evaluator(K, phi, n);
     }

     Py_DECREF(array_n);
     Py_DECREF(array_p);
     FUNC_MESS_END();
     return (PyObject *) pdfa;

 fail:
    FUNC_MESS_FAILED();
    DEBUG_MESS(2, "Failed at line %d", line);
    PyGSL_add_traceback(NULL, __FILE__, __FUNCTION__, line);
    Py_XDECREF(array_n);
    Py_XDECREF(array_p);
    Py_XDECREF(pdfa);
    return NULL;
}

static PyObject*
PyGSL_rng_multinomial_pdf(PyObject *self, PyObject *args){
    PyObject *r = NULL;
    FUNC_MESS_BEGIN();
    r =  PyGSL_rng_multinomial_pdf_support(self, args, gsl_ran_multinomial_pdf);
    FUNC_MESS_END();
    return r;
}

static PyObject*
PyGSL_rng_multinomial_lnpdf(PyObject *self, PyObject *args){
    PyObject *r = NULL;
    FUNC_MESS_BEGIN();
    r =  PyGSL_rng_multinomial_pdf_support(self, args, gsl_ran_multinomial_lnpdf);
    FUNC_MESS_END();
    return r;
}
