/* Odeint solver */
#include <pygsl/utils.h>
#include <pygsl/block_helpers.h>
#include <pygsl/error_helpers.h>
#include <Python.h>
#include <gsl/gsl_odeiv.h>
#include <gsl/gsl_errno.h>

char odeiv_module_doc[] = "XXX odeiv module doc missing!\n";

static char this_file[] = __FILE__;
static PyObject *module = NULL; /* set by initodeiv */

#if 0
static void			/* generic instance destruction */
generic_dealloc (PyObject *self)
{
  DEBUG_MESS(1, " *** generic_dealloc %p\n", (void *) self);
  PyMem_Free(self);
}
#endif

typedef struct {
     PyObject_HEAD
     gsl_odeiv_step * step;
     gsl_odeiv_system system;
     PyObject *py_func;
     PyObject *py_jac;
     PyObject *arguments;
     jmp_buf buffer;
}
PyGSL_odeiv_step;

typedef struct {
     PyObject_HEAD
     PyGSL_odeiv_step * step;
     gsl_odeiv_control * control;
} PyGSL_odeiv_control;

typedef struct {
     PyObject_HEAD
     PyGSL_odeiv_step * step;
     PyGSL_odeiv_control * control;
     gsl_odeiv_evolve * evolve;
} PyGSL_odeiv_evolve;

/*---------------------------------------------------------------------------
 * Declaration of the various Methods
 *---------------------------------------------------------------------------*/
/*
 * stepper
 */
static int
PyGSL_odeiv_func(double t, const double y[], double f[], void *params);
static int
PyGSL_odeiv_jac(double t, const double y[], double *dfdy, double dfdt[],
		void *params);
static PyObject *
PyGSL_odeiv_step_apply(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args);
static PyObject *
PyGSL_odeiv_step_order(PyGSL_odeiv_step *self, PyObject *args);
static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self);

/*
 * control
 */
static PyObject *
PyGSL_odeiv_control_hadjust(PyGSL_odeiv_control *self, PyObject *args);
static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args);
static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self);

/*
 * evolve
 */
static PyObject *
PyGSL_odeiv_evolve_apply(PyGSL_odeiv_evolve *self, PyObject *args);
static PyObject *
PyGSL_odeiv_evolve_apply_array(PyGSL_odeiv_evolve *self, PyObject *args);
static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args);
static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self);
/*---------------------------------------------------------------------------*/

static char  PyGSL_odeiv_step_doc[] = "XXX Documentation missing\n";
static char  PyGSL_odeiv_control_doc[] = "XXX Documentation missing\n";
static char  PyGSL_odeiv_evolve_doc[] = "XXX Documentation missing\n";

static char odeiv_step_apply_doc[] =  "XXX Documentation missing\n";
static char odeiv_step_name_doc[] =  "XXX Documentation missing\n";
static char odeiv_step_order_doc[] =  "XXX Documentation missing\n";
static char odeiv_step_reset_doc[] =  "XXX Documentation missing\n";

static char odeiv_control_name_doc[] =  "XXX Documentation missing\n";
static char odeiv_control_hadjust_doc[] =  "XXX Documentation missing\n";

static char odeiv_evolve_apply_doc[] =  "XXX Documentation missing\n";
static char odeiv_evolve_apply_array_doc[] =  "XXX Documentation missing\n";
static char odeiv_evolve_reset_doc[] =  "XXX Documentation missing\n";

static char * odeiv_step_init_err_msg = "odeiv_step.__init__";

static struct PyMethodDef PyGSL_odeiv_step_methods[] = {
     {"apply", (PyCFunction) PyGSL_odeiv_step_apply, METH_VARARGS, odeiv_step_apply_doc},
     {"reset", (PyCFunction) PyGSL_odeiv_step_reset, METH_VARARGS, odeiv_step_reset_doc},
     {"name",  (PyCFunction) PyGSL_odeiv_step_name,  METH_VARARGS, odeiv_step_name_doc},
     {"order", (PyCFunction) PyGSL_odeiv_step_order, METH_VARARGS, odeiv_step_order_doc},
     {NULL, NULL}
};
static struct PyMethodDef PyGSL_odeiv_control_methods[] = {
     {"hadjust", (PyCFunction) PyGSL_odeiv_control_hadjust, METH_VARARGS, odeiv_control_hadjust_doc},
     {"name",  (PyCFunction) PyGSL_odeiv_control_name,  METH_VARARGS, odeiv_control_name_doc},
     {NULL, NULL}
};

static struct PyMethodDef PyGSL_odeiv_evolve_methods[] = {
     {"apply", (PyCFunction) PyGSL_odeiv_evolve_apply, METH_VARARGS, odeiv_evolve_apply_doc},
     {"reset", (PyCFunction) PyGSL_odeiv_evolve_reset, METH_VARARGS, odeiv_evolve_reset_doc},
     {"apply_array", (PyCFunction) PyGSL_odeiv_evolve_apply_array, METH_VARARGS, odeiv_evolve_apply_array_doc},
     {NULL, NULL}
};

#define PyGSL_ODEIV_GENERIC_ALL                                                           \
static PyObject *									  \
PyGSL_ODEIV_GENERIC_GETATTR(PyGSL_ODEIV_GENERIC *self, char *name)		          \
{											  \
     PyObject *tmp = NULL;								  \
											  \
     FUNC_MESS_BEGIN();									  \
     tmp = Py_FindMethod(PyGSL_ODEIV_GENERIC_METHODS, (PyObject *) self, name);	          \
     if(NULL == tmp){	  								  \
	  PyGSL_add_traceback(module, __FILE__, "odeiv.__attr__", __LINE__ - 1);	  \
	  return NULL;									  \
     }											  \
     FUNC_MESS_END();									  \
     return tmp;                                                                          \
}                                                                                \
static PyTypeObject PyGSL_ODEIV_GENERIC_PYTYPE = {					  \
  PyObject_HEAD_INIT(NULL)	/* fix up the type slot in initcrng */			  \
  0,				/* ob_size */						  \
  PyGSL_ODEIV_GENERIC_NAME,			/* tp_name */			          \
  sizeof(PyGSL_ODEIV_GENERIC),  /* tp_basicsize */					  \
  0,				/* tp_itemsize */					  \
											  \
  /* standard methods */								  \
  (destructor)  PyGSL_ODEIV_GENERIC_DELETE,       /* tp_dealloc  ref-count==0  */	  \
  (printfunc)   0,		                      /* tp_print    "print x"     */	  \
  (getattrfunc) PyGSL_ODEIV_GENERIC_GETATTR,       /* tp_getattr  "x.attr"      */	  \
  (setattrfunc) 0,		   /* tp_setattr  "x.attr=v"    */			  \
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */			  \
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */			  \
											  \
  /* type categories */									  \
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/		  \
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/		  \
  0,				/* tp_as_mapping  [key], len, ...*/			  \
											  \
  /* more methods */									  \
  (hashfunc)     0,		/* tp_hash    "dict[x]" */				  \
  (ternaryfunc)  0,             /* tp_call    "x()"     */				  \
  (reprfunc)     0,             /* tp_str     "str(x)"  */				  \
  (getattrofunc) 0,		/* tp_getattro */					  \
  (setattrofunc) 0,		/* tp_setattro */					  \
  0,				/* tp_as_buffer */					  \
  0L,				/* tp_flags */						  \
  PyGSL_ODEIV_GENERIC_DOC       /* doc */                                                 \
};

#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_step
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_step"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_step_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_step_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_step_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_step_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_step_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_control
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_control"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_control_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_control_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_control_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_control_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_control_free
PyGSL_ODEIV_GENERIC_ALL
/**/;
#undef PyGSL_ODEIV_GENERIC
#undef PyGSL_ODEIV_GENERIC_NAME
#undef PyGSL_ODEIV_GENERIC_PYTYPE
#undef PyGSL_ODEIV_GENERIC_DOC
#undef PyGSL_ODEIV_GENERIC_GETATTR
#undef PyGSL_ODEIV_GENERIC_METHODS
#undef PyGSL_ODEIV_GENERIC_DELETE
#define PyGSL_ODEIV_GENERIC            PyGSL_odeiv_evolve
#define PyGSL_ODEIV_GENERIC_NAME      "PyGSL_odeiv_evolve"
#define PyGSL_ODEIV_GENERIC_PYTYPE     PyGSL_odeiv_evolve_pytype
#define PyGSL_ODEIV_GENERIC_DOC        PyGSL_odeiv_evolve_doc
#define PyGSL_ODEIV_GENERIC_GETATTR    PyGSL_odeiv_evolve_getattr
#define PyGSL_ODEIV_GENERIC_METHODS    PyGSL_odeiv_evolve_methods
#define PyGSL_ODEIV_GENERIC_DELETE     PyGSL_odeiv_evolve_free
PyGSL_ODEIV_GENERIC_ALL
/**/;

#define PyGSL_ODEIV_STEP_Check(v)    ((v)->ob_type == &PyGSL_odeiv_step_pytype)
#define PyGSL_ODEIV_CONTROL_Check(v) ((v)->ob_type == &PyGSL_odeiv_control_pytype)
#define PyGSL_ODEIV_EVOLVE_Check(v)  ((v)->ob_type == &PyGSL_odeiv_evolve_pytype)

/*---------------------------------------------------------------------------
  Wrapper functions to push call the approbriate Python Objects
  ---------------------------------------------------------------------------*/
static int
PyGSL_odeiv_func(double t, const double y[], double f[], void *params)
{
    int dimension, flag = GSL_FAILURE;

    PyObject *arglist = NULL, *result = NULL;
    PyArrayObject *yo = NULL;
    PyGSL_odeiv_step * step;
    gsl_vector_view yv, fv;
    PyGSL_error_info  info;

    FUNC_MESS_BEGIN();

    step = (PyGSL_odeiv_step *) params;
    if(!PyGSL_ODEIV_STEP_Check(step)){
	  PyGSL_add_traceback(module, this_file, __FUNCTION__,
			      __LINE__ - 2);
	  gsl_error("Param not a step type!",
		    this_file, __LINE__ -2, GSL_EFAULT);
	  goto fail;
    }
    dimension =  step->system.dimension;

    /* Do I need to copy the array ??? */
    yv = gsl_vector_view_array((double *) y, dimension);
    yo = PyGSL_copy_gslvector_to_pyarray(&yv.vector);
    if (yo == NULL) goto fail;

    FUNC_MESS("\t\tBuild args");
    arglist = Py_BuildValue("(dOO)", t, yo, step->arguments);
    FUNC_MESS("\t\tEnd Build args");

    info.callback = step->py_func;
    info.message  = "odeiv_func";
    result  = PyEval_CallObject(step->py_func, arglist);

    if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 1, &info)) != GSL_SUCCESS){
	  goto fail;
     }
    info.argnum = 1;
    fv = gsl_vector_view_array(f, dimension);
    if((flag = PyGSL_copy_pyarray_to_gslvector(&fv.vector, result, dimension,
					       &info)) != GSL_SUCCESS){
	  goto fail;
     }

    Py_DECREF(arglist);    arglist = NULL;
    Py_DECREF(yo);         yo = NULL;
    Py_DECREF(result);     result = NULL;
    FUNC_MESS_END();
    return GSL_SUCCESS;

 fail:
    FUNC_MESS("    IN Fail BEGIN");
    Py_XDECREF(yo);
    Py_XDECREF(result);
    Py_XDECREF(arglist);
    FUNC_MESS("    IN Fail END");
    assert(flag != GSL_SUCCESS);
    longjmp(step->buffer, flag);
    return flag;
}

static int
PyGSL_odeiv_jac(double t, const double y[], double *dfdy, double dfdt[],
		void *params)
{
    int dimension, flag = GSL_FAILURE;
    PyGSL_odeiv_step *step = NULL;
    PyGSL_error_info  info;

    PyObject *arglist = NULL, *result = NULL, *tmp=NULL;
    PyArrayObject *yo = NULL;

    gsl_vector_view yv, dfdtv;
    gsl_matrix_view dfdyv;

    FUNC_MESS_BEGIN();

    step = (PyGSL_odeiv_step *) params;
    if(!PyGSL_ODEIV_STEP_Check(step)){
	  PyGSL_add_traceback(module, this_file, __FUNCTION__,
			      __LINE__ - 2);
	  gsl_error("Param not a step type!",
		    this_file, __LINE__ -2, GSL_EFAULT);
	  goto fail;
    }
    dimension = step->system.dimension;

    yv = gsl_vector_view_array((double *) y, dimension);
    yo = PyGSL_copy_gslvector_to_pyarray(&yv.vector);
    if (yo == NULL) goto fail;

    arglist = Py_BuildValue("(dOO)", t, yo, step->arguments);

    assert(step->py_jac);
    result  = PyEval_CallObject(step->py_jac, arglist);

    info.callback = step->py_jac;
    info.message  = "odeiv_jac";
    if((flag = PyGSL_CHECK_PYTHON_RETURN(result, 2, &info)) != GSL_SUCCESS){
	  goto fail;
     }

    info.argnum = 1;
    tmp = PyTuple_GET_ITEM(result, 0);
    dfdyv = gsl_matrix_view_array((double *) dfdy, dimension, dimension);
    if((flag = PyGSL_copy_pyarray_to_gslmatrix(&dfdyv.matrix, tmp, dimension, dimension, &info)) != GSL_SUCCESS){
	  goto fail;
    }

    info.argnum = 2;
    tmp = PyTuple_GET_ITEM(result, 1);
    dfdtv = gsl_vector_view_array((double *) dfdt, dimension);
    if((flag = PyGSL_copy_pyarray_to_gslvector(&dfdtv.vector, tmp, dimension, &info)) != GSL_SUCCESS){
	  goto fail;
    }

    Py_DECREF(arglist);    arglist = NULL;
    Py_DECREF(result);     result = NULL;
    Py_DECREF(yo);         yo = NULL;
    FUNC_MESS_END();
    return GSL_SUCCESS;

 fail:
    FUNC_MESS("IN Fail");
    assert(flag != GSL_SUCCESS);
    longjmp(step->buffer, flag);
    return flag;
}

/* Wrappers for the evaluation of the system */
static PyObject *
PyGSL_odeiv_step_apply(PyGSL_odeiv_step *self, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *y0_o = NULL, *dydt_in_o = NULL;
    PyArrayObject *volatile y0 = NULL, * volatile yerr = NULL,
	 *volatile dydt_in = NULL, *volatile dydt_out = NULL,
	 *volatile yout = NULL;

    double t=0, h=0, *volatile dydt_in_d;
    int dimension, r, flag;

    FUNC_MESS_BEGIN();
    assert(PyGSL_ODEIV_STEP_Check(self));
    if(! PyArg_ParseTuple(args, "ddOOO", &t, &h, &y0_o,  &dydt_in_o)){
      return NULL;
    }

    dimension = self->system.dimension;
    y0 = PyGSL_PyArray_PREPARE_gsl_vector_view(y0_o, PyArray_DOUBLE, 1, dimension, 1, NULL);
    if(y0 == NULL) goto fail;

    if (Py_None == dydt_in_o){
	 dydt_in_d = NULL;
    }else{
	 dydt_in = PyGSL_PyArray_PREPARE_gsl_vector_view(dydt_in_o, PyArray_DOUBLE, 1, dimension, 2, NULL);
	 if(dydt_in == NULL) goto fail;
	 dydt_in_d = (double *) dydt_in->data;
    }

    dydt_out = (PyArrayObject *)  PyArray_FromDims(1, &dimension, PyArray_DOUBLE);
    if (dydt_out == NULL) goto fail;

    yerr = (PyArrayObject *) PyArray_FromDims(1, &dimension, PyArray_DOUBLE);
    if(yerr == NULL) goto fail;

    yout = (PyArrayObject *) PyArray_CopyFromObject((PyObject * ) y0, PyArray_DOUBLE, 1, 1);
    if(yout == NULL) goto fail;

    if((flag=setjmp(self->buffer)) == 0){
	  FUNC_MESS("\t\t Setting Jmp Buffer");
    } else {
	  FUNC_MESS("\t\t Returning from Jmp Buffer");
	  goto fail;
    }

    r = gsl_odeiv_step_apply(self->step, t, h,
			     (double *) yout->data,
			     (double *) yerr->data,
			     dydt_in_d,
			     (double *) dydt_out->data,
			     &(self->system));
    if (GSL_SUCCESS != r){
	PyErr_SetString(PyExc_TypeError, "Error While evaluating gsl_odeiv");
      goto fail;
    }

    FUNC_MESS("    Returnlist create ");
    assert(yout != NULL);
    assert(yerr != NULL);
    assert(dydt_out != NULL);

    result = Py_BuildValue("(OOO)", yout, yerr, dydt_out);

    FUNC_MESS("    Memory free ");
    /* Deleting the arrays */
    Py_DECREF(y0);           y0 = NULL;
    Py_DECREF(yout);         yout = NULL;
    Py_DECREF(yerr);         yerr = NULL;
    Py_DECREF(dydt_out);     dydt_out = NULL;
    /* This array does not need to exist ... */
    Py_XDECREF(dydt_in);	 dydt_in=NULL;

    FUNC_MESS_END();
    return result;

    fail:
    FUNC_MESS("IN Fail");
    Py_XDECREF(y0);
    Py_XDECREF(yout);
    Py_XDECREF(yerr);
    Py_XDECREF(dydt_in);
    Py_XDECREF(dydt_out);
    FUNC_MESS("IN Fail End");
    return NULL;
}

static void
PyGSL_odeiv_step_free(PyGSL_odeiv_step * self)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     Py_DECREF(self->py_func);
     Py_XDECREF(self->py_jac);
     Py_DECREF(self->arguments);
     gsl_odeiv_step_free(self->step);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_step_reset(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     gsl_odeiv_step_reset(self->step);
     Py_INCREF(Py_None);
     return Py_None;
}

static PyObject *
PyGSL_odeiv_step_name(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyString_FromString(gsl_odeiv_step_name(self->step));
}

static PyObject *
PyGSL_odeiv_step_order(PyGSL_odeiv_step *self, PyObject *args)
{
     assert(PyGSL_ODEIV_STEP_Check(self));
     return PyInt_FromLong((long) gsl_odeiv_step_order(self->step));
}

/* --------------------------------------------------------------------------- */
/* control_hadjust needs a few arrays */
/*
extern int
gsl_odeiv_control_hadjust (gsl_odeiv_control * c, gsl_odeiv_step * s,
			   const double y0[],  const double yerr[],
			   const double dydt[], double * h);
*/
static PyObject *
PyGSL_odeiv_control_hadjust(PyGSL_odeiv_control *self, PyObject *args)
{

  PyObject *result = NULL;
  PyObject *y0_o = NULL, *yerr_o = NULL, *dydt_o = NULL;
  PyArrayObject *y0 = NULL, *yerr = NULL, *dydt = NULL;
  double h = 0;
  int r = 0;

  size_t dimension = 0;

  FUNC_MESS_BEGIN();
  assert(PyGSL_ODEIV_CONTROL_Check(self));
  if(!PyArg_ParseTuple(args, "OOOd",  &y0_o, &yerr_o, &dydt_o, &h)){
    return NULL;
  }

  dimension = self->step->system.dimension;

  y0 = PyGSL_PyArray_PREPARE_gsl_vector_view(y0_o, PyArray_DOUBLE, 1, dimension,  1, NULL);
  if(y0 == NULL)   goto fail;
  yerr = PyGSL_PyArray_PREPARE_gsl_vector_view(yerr_o, PyArray_DOUBLE, 1, dimension, 2, NULL);
  if(yerr == NULL) goto fail;
  dydt = PyGSL_PyArray_PREPARE_gsl_vector_view(yerr_o, PyArray_DOUBLE, 1, dimension, 3, NULL);
  if(dydt == NULL) goto fail;

  FUNC_MESS("      Array Pointers End");

  r = gsl_odeiv_control_hadjust(self->control, self->step->step,
				(double *) y0->data,
				(double *) yerr->data,
				(double *) dydt->data, &h);

  FUNC_MESS("      Function End");
  Py_DECREF(y0);       y0 = NULL;
  Py_DECREF(yerr);     yerr = NULL;
  Py_DECREF(dydt);     dydt = NULL;

  result = Py_BuildValue("di",h,r);
  FUNC_MESS_END();
  return result;

 fail:
  FUNC_MESS("IN Fail");
  Py_XDECREF(y0);
  Py_XDECREF(yerr);
  Py_XDECREF(dydt);
  FUNC_MESS("IN Fail END");
  return NULL;
}

static void
PyGSL_odeiv_control_free(PyGSL_odeiv_control * self)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     Py_DECREF(self->step);
     //gsl_odeiv_control_free(self->control);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_control_name(PyGSL_odeiv_control *self, PyObject *args)
{
     assert(PyGSL_ODEIV_CONTROL_Check(self));
     return PyString_FromString(gsl_odeiv_control_name(self->control));
}

static void
PyGSL_odeiv_evolve_free(PyGSL_odeiv_evolve * self)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     Py_DECREF(self->step);
     Py_DECREF(self->control);
     gsl_odeiv_evolve_free(self->evolve);
     PyMem_Free(self);
}

static PyObject *
PyGSL_odeiv_evolve_apply(PyGSL_odeiv_evolve *self, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *y0_o = NULL;
    PyArrayObject *volatile y0 = NULL, *volatile yout = NULL;

    double t=0, h=0, t1 = 0, flag;

    int dimension, r;

    assert(PyGSL_ODEIV_EVOLVE_Check(self));
    FUNC_MESS_BEGIN();

    if(! PyArg_ParseTuple(args, "dddO",
			  &t, &t1, &h, &y0_o)){
      return NULL;
    }

    dimension = self->step->system.dimension;

    y0 = PyGSL_PyArray_PREPARE_gsl_vector_view(y0_o, PyArray_DOUBLE, 1, dimension, 1, NULL);
    if(y0 == NULL) goto fail;

    yout = (PyArrayObject *)  PyArray_CopyFromObject((PyObject * ) y0, PyArray_DOUBLE, 1, 1);
    if(yout == NULL) goto fail;

    if((flag=setjmp(self->step->buffer)) == 0){
	  FUNC_MESS("\t\t Setting Jmp Buffer");
     } else {
	  FUNC_MESS("\t\t Returning from Jmp Buffer");
	  goto fail;
     }

    r = gsl_odeiv_evolve_apply(self->evolve,
			       self->control->control,
			       self->step->step,
			       &(self->step->system), &t, t1, &h,
			       (double * )yout->data);

    if (GSL_SUCCESS != r){
	 goto fail;
    }

    assert(yout != NULL);

    result = Py_BuildValue("(ddO)", t, h, yout);

    /* Deleting the arrays */
    /* Do I need to do that??? Not transfered Py_DECREF(yout);     yout = NULL; */
    Py_DECREF(y0);       y0=NULL;
    FUNC_MESS_END();
    return result;

 fail:
    FUNC_MESS("IN Fail");
    PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__);
    Py_XDECREF(y0);
    Py_XDECREF(yout);
    FUNC_MESS("IN Fail End");
    return NULL;
}

static PyObject *
PyGSL_odeiv_evolve_apply_array(PyGSL_odeiv_evolve *self, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *y0_o = NULL;
    PyObject *t_o = NULL;
    PyArrayObject *volatile y0 = NULL, *volatile yout = NULL, *volatile ta = NULL;

    double t=0, h=0, t1 = 0, flag;
    double *y0_data = NULL, *yout_data = NULL;
    int dimension, r, dims[2];
    int nlen=-1, i, j;

    assert(PyGSL_ODEIV_EVOLVE_Check(self));
    FUNC_MESS_BEGIN();

    if(! PyArg_ParseTuple(args, "OdO", &t_o, &h, &y0_o)){
      return NULL;
    }

    dimension = self->step->system.dimension;

    ta = PyGSL_PyArray_PREPARE_gsl_vector_view(t_o, PyArray_DOUBLE, 1, -1, 1, NULL);
    if(ta == NULL) goto fail;
    nlen = ta->dimensions[0];

    y0 = PyGSL_PyArray_PREPARE_gsl_vector_view(y0_o, PyArray_DOUBLE, 1, dimension, 1, NULL);
    if(y0 == NULL) goto fail;

    dims[0] = nlen;
    dims[1] = dimension;
    yout = (PyArrayObject *)  PyArray_FromDims(2, dims, PyArray_DOUBLE);
    if(yout == NULL) goto fail;

    if((flag=setjmp(self->step->buffer)) == 0){
	  FUNC_MESS("\t\t Setting Jmp Buffer");
     } else {
	  FUNC_MESS("\t\t Returning from Jmp Buffer");
	  goto fail;
     }

    DEBUG_MESS(5, "\t\t Yout Layout %p, shape=[%d,%d], strides=[%d,%d]",
	       yout->data, yout->dimensions[0], yout->dimensions[1], yout->strides[0], yout->strides[1]);
    r = GSL_CONTINUE;

    yout_data = (double *)(yout->data);
    /* Copy the start vector */
    for(j=0; j<dimension; j++){
	 yout_data[j] = *((double *)(y0->data + y0->strides[0] * j));
    }
    for(i=1; i<nlen; i++){
	 y0_data   = (double *)(yout->data + yout->strides[0]*(i-1));
	 yout_data = (double *)(yout->data + yout->strides[0]*(i)  );
	 /* Copy the start vector */
	 for(j=0; j<dimension; j++){
	      yout_data[j] = y0_data[j];
	 }
	 t =  *((double *) (ta->data + ta->strides[0]*(i-1)) );
	 t1 = *((double *) (ta->data + ta->strides[0]*(i)  ) );

	 while(t<t1){
	      r = gsl_odeiv_evolve_apply(self->evolve,
					 self->control->control,
					 self->step->step,
					 &(self->step->system), &t, t1, &h,
					 yout_data);
	      /* All GSL Errors are > 0. */
	      if (r != GSL_SUCCESS){
		   goto fail;
	      }
	 }
	 assert(r == GSL_SUCCESS);
    }

    result = (PyObject *) yout;

    /* Deleting the arrays */
    Py_DECREF(y0); y0=NULL;
    Py_DECREF(ta);
    FUNC_MESS_END();
    return result;

 fail:
    FUNC_MESS("IN Fail");
    PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__);
    Py_XDECREF(ta);  ta = NULL;
    Py_XDECREF(y0);  y0=NULL;
    Py_XDECREF(yout);
    FUNC_MESS("IN Fail End");
    return NULL;
}

static PyObject *
PyGSL_odeiv_evolve_reset(PyGSL_odeiv_evolve *self, PyObject *args)
{
     assert(PyGSL_ODEIV_EVOLVE_Check(self));
     gsl_odeiv_evolve_reset(self->evolve);
     Py_INCREF(Py_None);
     return Py_None;
}

static PyObject *
PyGSL_odeiv_step_init(PyObject *self, PyObject *args, PyObject *kwdict, const gsl_odeiv_step_type * odeiv_type)
{

     PyObject *func=NULL, *jac=NULL, *o_params=NULL;
     PyGSL_odeiv_step *odeiv_step = NULL;

     static char * kwlist[] = {"dimension", "func", "jac", "args", NULL};
     int dim, has_jacobian = 0;

     FUNC_MESS_BEGIN();
     assert(args);
     if (0 == PyArg_ParseTupleAndKeywords(args, kwdict, "iOOO:odeiv_step.__init__", kwlist,
					  &dim, &func, &jac, &o_params)){
	  PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__ - 2);
	  return NULL;
     }
     if (dim <= 0){
	  PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__ - 1);
	  gsl_error("The dimension of the problem must be at least 1",
		    this_file, __LINE__ -2, GSL_EDOM);
	  return NULL;
     }
     if(!PyCallable_Check(func)){
	  PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__ - 1);
	  gsl_error("The function object is not callable!",
		    this_file, __LINE__ -2, GSL_EBADFUNC);
	  goto fail;
     }

     if(jac == Py_None){
	  if(odeiv_type == gsl_odeiv_step_bsimp){
	       PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__ - 1);
	       gsl_error("The bsimp method needs a jacobian! You supplied None.",
			 this_file, __LINE__ -2, GSL_EBADFUNC);
	       goto fail;
	  }
     }else{
	  if(!PyCallable_Check(jac)){
	       PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg, __LINE__ - 1);
	       gsl_error("The jacobian object must be None or callable!",
			 this_file, __LINE__ -2, GSL_EBADFUNC);
	       goto fail;
	  }
	  has_jacobian = 1;

     }

     odeiv_step =  (PyGSL_odeiv_step *) PyObject_NEW(PyGSL_odeiv_step, &PyGSL_odeiv_step_pytype);
     if(odeiv_step == NULL){
	  PyErr_NoMemory();
	  goto fail;
     }
     odeiv_step->step=NULL;
     odeiv_step->py_func=NULL;
     odeiv_step->py_jac=NULL;
     odeiv_step->arguments=NULL;

     odeiv_step->system.dimension = dim;
     if(has_jacobian)
	  odeiv_step->system.jacobian = PyGSL_odeiv_jac;
     else
	  odeiv_step->system.jacobian = NULL;

     odeiv_step->system.function = PyGSL_odeiv_func;
     odeiv_step->system.params = (void *) odeiv_step;

     odeiv_step->step =  gsl_odeiv_step_alloc(odeiv_type, dim);
     if(odeiv_step->step == NULL){
	  Py_DECREF(odeiv_step);
	  PyErr_NoMemory();
	  return NULL;
     }

     odeiv_step->py_func=func;
     if(has_jacobian)
	  odeiv_step->py_jac=jac;

     odeiv_step->arguments = o_params;
     Py_INCREF(odeiv_step->py_func);
     Py_INCREF(odeiv_step->arguments);

     Py_XINCREF(odeiv_step->py_jac);

     FUNC_MESS_END();
     return (PyObject *) odeiv_step;
 fail:
     return NULL;
}
#define ADD_ODESTEPPER(mytype)                                                      \
static PyObject *                                                                   \
PyGSL_odeiv_step_init_ ## mytype (PyObject * self, PyObject * args, PyObject *kwdic)\
{                                                                                \
     return PyGSL_odeiv_step_init(self, args, kwdic, gsl_odeiv_step_ ## mytype);    \
}
ADD_ODESTEPPER(rk2)
ADD_ODESTEPPER(rk4)
ADD_ODESTEPPER(rkf45)
ADD_ODESTEPPER(rkck)
ADD_ODESTEPPER(rk8pd)
ADD_ODESTEPPER(rk2imp)
ADD_ODESTEPPER(rk4imp)
ADD_ODESTEPPER(bsimp)
ADD_ODESTEPPER(gear1)
ADD_ODESTEPPER(gear2)

static PyObject *
PyGSL_odeiv_control_init(PyObject *self, PyObject *args, void * type)
{
     int nargs = -1, tmp=0;
     double eps_abs, eps_rel, a_y, a_dydt;
     PyGSL_odeiv_step *step=NULL;
     PyGSL_odeiv_control *a_con=NULL;

     gsl_odeiv_control *(*evaluator_5)(double , double , double , double ) = NULL;
     gsl_odeiv_control *(*evaluator_3)(double , double ) = NULL;

     FUNC_MESS_BEGIN();
     /* The arguments depend on the type of control */
     if(type == (void *) gsl_odeiv_control_standard_new){
	  /* step, eps_abs, eps_rel, a_y, a_dydt */
	  nargs = 5;
     }else if(type == (void *) gsl_odeiv_control_y_new ||
	      type == (void *) gsl_odeiv_control_yp_new){
	  nargs = 3;
     }else{
	  PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg,
			      __LINE__ - 2);
	  gsl_error("Unknown control type",
		    this_file, __LINE__ -2, GSL_EFAULT);
	  goto fail;
     }
     assert(nargs > -1);

     switch(nargs){
     case 5:
	  tmp == PyArg_ParseTuple(args, "O!dddd:odeiv_control.__init__",
				  &PyGSL_odeiv_step_pytype, &step, &eps_abs, &eps_rel, &a_y, &a_dydt);
	  break;
     case 3:
	  tmp == PyArg_ParseTuple(args, "O!dd:odeiv_control.__init__",
				  &PyGSL_odeiv_step_pytype, &step, &eps_abs, &eps_rel);
	  break;
     default:
	  fprintf(stderr, "nargs = %d\n", nargs);
	  gsl_error("Unknown number of arguments",
		    this_file, __LINE__ -2, GSL_EFAULT);
	  goto fail; break;
     }

     if(!step){
	  PyErr_SetString(PyExc_TypeError, "The first argument must be a step object!");
	  goto fail;
     }

     if(tmp){
	  PyGSL_add_traceback(module, this_file, odeiv_step_init_err_msg,
			      __LINE__ - 2);
	  return NULL;
     }

     a_con =  PyObject_NEW(PyGSL_odeiv_control, &PyGSL_odeiv_control_pytype);
     if (NULL == a_con){
	  PyErr_NoMemory();
	  goto fail;
     }
     a_con->control=NULL;
     switch(nargs){
     case 5:
	  evaluator_5 = type;
	  a_con->control = evaluator_5(eps_abs, eps_rel, a_y, a_dydt);
	  break;
     case 3:
	  evaluator_3 = type;
	  a_con->control = evaluator_3(eps_abs, eps_rel);
	  break;
     default:
	  goto fail;
     }
     if (NULL == a_con->control){
	  PyErr_NoMemory();
	  goto fail;
     }
     assert(step);
     a_con->step =  step;
     Py_INCREF(step);
     FUNC_MESS_END();
     return (PyObject *) a_con;

 fail:
     FUNC_MESS("FAIL");
     Py_XDECREF(a_con);
     return NULL;

}

#define ADD_ODECONTROL(name)                                                  \
static PyObject *                                                             \
PyGSL_odeiv_control_init_ ## name (PyObject * self, PyObject * args)          \
{                                                                             \
     return PyGSL_odeiv_control_init(self, args, (void *) gsl_odeiv_control_ ## name);    \
}
ADD_ODECONTROL(standard_new)
ADD_ODECONTROL(y_new)
ADD_ODECONTROL(yp_new)

static PyObject *
PyGSL_odeiv_evolve_init(PyObject *self, PyObject *args)
{
     PyGSL_odeiv_step *step = NULL;
     PyGSL_odeiv_control *control = NULL;
     PyGSL_odeiv_evolve *a_ev = NULL;

     /* step, control */
     FUNC_MESS_BEGIN();
     if(0== PyArg_ParseTuple(args, "O!O!:odeiv_evolve.__init__",
			     &PyGSL_odeiv_step_pytype, &step,
			     &PyGSL_odeiv_control_pytype, &control)){
	  return NULL;
     }

     if(!step){
	  PyErr_SetString(PyExc_TypeError, "The first argument must be a step object!");
	  goto fail;
     }

     if(!control){
	  PyErr_SetString(PyExc_TypeError, "The second argument must be a control object!");
	  goto fail;
     }

     a_ev =  (PyGSL_odeiv_evolve *) PyObject_NEW(PyGSL_odeiv_evolve, &PyGSL_odeiv_evolve_pytype);
     if(NULL == a_ev){
	  PyErr_NoMemory();
	  return NULL;
     }

     a_ev->step = step;
     a_ev->control = control;

     a_ev->evolve = gsl_odeiv_evolve_alloc(step->system.dimension);
     if(NULL == a_ev){
	  PyErr_NoMemory();
	  goto fail;
     }
     Py_INCREF(step);
     Py_INCREF(control);
     FUNC_MESS_END();
     return (PyObject *) a_ev;
 fail:
     FUNC_MESS("FAIL");
     Py_DECREF(a_ev);
     return NULL;
}

static PyMethodDef PyGSL_odeiv_module_functions[] = {
     {"step_rk2",    PyGSL_odeiv_step_init_rk2,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4",    PyGSL_odeiv_step_init_rk4,    METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkf45",  PyGSL_odeiv_step_init_rkf45,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rkck",   PyGSL_odeiv_step_init_rkck,   METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk8pd",  PyGSL_odeiv_step_init_rk8pd,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk2imp", PyGSL_odeiv_step_init_rk2imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_rk4imp", PyGSL_odeiv_step_init_rk4imp, METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_bsimp",  PyGSL_odeiv_step_init_bsimp,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear1",  PyGSL_odeiv_step_init_gear1,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"step_gear2",  PyGSL_odeiv_step_init_gear2,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"control_standard_new", PyGSL_odeiv_control_init_standard_new, METH_VARARGS, NULL},
     {"control_y_new",        PyGSL_odeiv_control_init_y_new,        METH_VARARGS, NULL},
     {"control_yp_new",       PyGSL_odeiv_control_init_yp_new,       METH_VARARGS, NULL},
     {"evolve", PyGSL_odeiv_evolve_init, METH_VARARGS, NULL},
     {NULL, NULL, 0}        /* Sentinel */
};

void
initodeiv(void)
{
     PyObject *m=NULL, *item=NULL, *dict=NULL;

     FUNC_MESS_BEGIN();
     fprintf(stderr, "Compiled at %s %s\n", __DATE__, __TIME__);
     m = Py_InitModule("odeiv", PyGSL_odeiv_module_functions);
     assert(m);
     module = m;
     import_array();
     init_pygsl();

     PyGSL_odeiv_step_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_control_pytype.ob_type = &PyType_Type;
     PyGSL_odeiv_evolve_pytype.ob_type = &PyType_Type;

     dict = PyModule_GetDict(m);
     /* create_odeiv_step_types(dict); */
     if(!dict)
	  goto fail;

     if (!(item = PyString_FromString(odeiv_module_doc))){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not generate module doc string!");
	  goto fail;
     }
     if (PyDict_SetItemString(dict, "__doc__", item) != 0){
	  PyErr_SetString(PyExc_ImportError,
			  "I could not init doc string!");
	  goto fail;
     }

     FUNC_MESS_END();
     return;
 fail:
     FUNC_MESS("Fail");
     fprintf(stderr, "Import of module odeiv failed!\n");
}


/* permutation/gsl_permutation.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004, 2007 Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_PERMUTATION_H__
#define __GSL_PERMUTATION_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_permutation_struct
{
  size_t size;
  size_t *data;
};

typedef struct gsl_permutation_struct gsl_permutation;

gsl_permutation *gsl_permutation_alloc (const size_t n);
gsl_permutation *gsl_permutation_calloc (const size_t n);
void gsl_permutation_init (gsl_permutation * p);
void gsl_permutation_free (gsl_permutation * p);
int gsl_permutation_memcpy (gsl_permutation * dest, const gsl_permutation * src);

int gsl_permutation_fread (FILE * stream, gsl_permutation * p);
int gsl_permutation_fwrite (FILE * stream, const gsl_permutation * p);
int gsl_permutation_fscanf (FILE * stream, gsl_permutation * p);
int gsl_permutation_fprintf (FILE * stream, const gsl_permutation * p, const char *format);

size_t gsl_permutation_size (const gsl_permutation * p);
size_t * gsl_permutation_data (const gsl_permutation * p);

int gsl_permutation_swap (gsl_permutation * p, const size_t i, const size_t j);

int gsl_permutation_valid (const gsl_permutation * p);
void gsl_permutation_reverse (gsl_permutation * p);
int gsl_permutation_inverse (gsl_permutation * inv, const gsl_permutation * p);
int gsl_permutation_next (gsl_permutation * p);
int gsl_permutation_prev (gsl_permutation * p);
int gsl_permutation_mul (gsl_permutation * p, const gsl_permutation * pa, const gsl_permutation * pb);

int gsl_permutation_linear_to_canonical (gsl_permutation * q, const gsl_permutation * p);
int gsl_permutation_canonical_to_linear (gsl_permutation * p, const gsl_permutation * q);

size_t gsl_permutation_inversions (const gsl_permutation * p);
size_t gsl_permutation_linear_cycles (const gsl_permutation * p);
size_t gsl_permutation_canonical_cycles (const gsl_permutation * q);

INLINE_DECL size_t gsl_permutation_get (const gsl_permutation * p, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
size_t
gsl_permutation_get (const gsl_permutation * p, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= p->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return p->data[i];
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_PERMUTATION_H__ */


#pragma once

#include "exp_curve_fitter.h"
#include "optimizer_job.h"
#include "random_utils.h"
#include "schedule_params.h"
#include <gsl/gsl-lite.hpp>
#include <vector>

namespace angonoka::stun {
/**
    How many iterations without improvement before
    considering optimization complete.
*/
enum class MaxIdleIters : std::int_fast32_t;

/**
    Optimization algorithm based on stochastic tunneling.

    This is the primary facade for doing stochastic tunneling
    optimization.
*/
class Optimizer {
public:
    /**
        Constructor.

        @param params           Scheduling parameters
        @param batch_size       Number of iterations per update
        @param max_idle_iters   Stopping condition
    */
    Optimizer(
        const ScheduleParams& params,
        BatchSize batch_size,
        MaxIdleIters max_idle_iters);

    /**
        Run stochastic tunneling optimization batch.

        Does batch_size number of iterations and adjusts the estimated
        progress accordingly.
    */
    void update() noexcept;

    /**
        Checks if the stopping condition has been met.

        @return True when further improvements are unlikely
    */
    [[nodiscard]] bool has_converged() const noexcept;

    /**
        Estimated optimization progress from 0.0 to 1.0.

        @return Progress from 0.0 to 1.0
    */
    [[nodiscard]] float estimated_progress() const noexcept;

    /**
        The best schedule so far.

        @return A schedule.
    */
    [[nodiscard]] Schedule schedule() const;

    /**
        The best makespan so far.

        @return Makespan.
    */
    [[nodiscard]] float normalized_makespan() const;

    /**
        Reset the optimization to initial state.
    */
    void reset();

    /**
        Get the current ScheduleParams object.

        @return Schedule parameters.
    */
    [[nodiscard]] const ScheduleParams& params() const;

    /**
        Set the ScheduleParams object.

        @param params ScheduleParams object
    */
    void params(const ScheduleParams& params);

private:
    struct Impl;

    int16 batch_size;
    int16 max_idle_iters;
    int16 idle_iters{0};
    int16 epochs{0};
    float last_progress{0.F};
    float last_makespan{0.F};
    ExpCurveFitter exp_curve;

    /**
        An optimization job and a PRNG.

        @var random_utils   Random number generator utilities
        @var job            Optimization job
    */
    struct Job {
        /**
            Constructor.

            @param params       Scheduling parameters
            @param batch_size   Number of iterations per update
        */
        Job(const ScheduleParams& params, BatchSize batch_size);
        RandomUtils random_utils;
        OptimizerJob job;
    };
    std::vector<Job> jobs;
};
} // namespace angonoka::stun


/* permutation/gsl_permutation.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004, 2007 Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_PERMUTATION_H__
#define __GSL_PERMUTATION_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_permutation_struct
{
  size_t size;
  size_t *data;
};

typedef struct gsl_permutation_struct gsl_permutation;

GSL_FUN gsl_permutation *gsl_permutation_alloc (const size_t n);
GSL_FUN gsl_permutation *gsl_permutation_calloc (const size_t n);
GSL_FUN void gsl_permutation_init (gsl_permutation * p);
GSL_FUN void gsl_permutation_free (gsl_permutation * p);
GSL_FUN int gsl_permutation_memcpy (gsl_permutation * dest, const gsl_permutation * src);

GSL_FUN int gsl_permutation_fread (FILE * stream, gsl_permutation * p);
GSL_FUN int gsl_permutation_fwrite (FILE * stream, const gsl_permutation * p);
GSL_FUN int gsl_permutation_fscanf (FILE * stream, gsl_permutation * p);
GSL_FUN int gsl_permutation_fprintf (FILE * stream, const gsl_permutation * p, const char *format);

GSL_FUN size_t gsl_permutation_size (const gsl_permutation * p);
GSL_FUN size_t * gsl_permutation_data (const gsl_permutation * p);

GSL_FUN int gsl_permutation_swap (gsl_permutation * p, const size_t i, const size_t j);

GSL_FUN int gsl_permutation_valid (const gsl_permutation * p);
GSL_FUN void gsl_permutation_reverse (gsl_permutation * p);
GSL_FUN int gsl_permutation_inverse (gsl_permutation * inv, const gsl_permutation * p);
GSL_FUN int gsl_permutation_next (gsl_permutation * p);
GSL_FUN int gsl_permutation_prev (gsl_permutation * p);
GSL_FUN int gsl_permutation_mul (gsl_permutation * p, const gsl_permutation * pa, const gsl_permutation * pb);

GSL_FUN int gsl_permutation_linear_to_canonical (gsl_permutation * q, const gsl_permutation * p);
GSL_FUN int gsl_permutation_canonical_to_linear (gsl_permutation * p, const gsl_permutation * q);

GSL_FUN size_t gsl_permutation_inversions (const gsl_permutation * p);
GSL_FUN size_t gsl_permutation_linear_cycles (const gsl_permutation * p);
GSL_FUN size_t gsl_permutation_canonical_cycles (const gsl_permutation * q);

GSL_FUN INLINE_DECL size_t gsl_permutation_get (const gsl_permutation * p, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
size_t
gsl_permutation_get (const gsl_permutation * p, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= p->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return p->data[i];
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_PERMUTATION_H__ */


#ifndef petscutils_h
#define petscutils_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>
#include <petscfe.h>

CeedMemType MemTypeP2C(PetscMemType mtype);
PetscErrorCode ProjectToUnitSphere(DM dm);
PetscErrorCode Kershaw(DM dm_orig, PetscScalar eps);
typedef PetscErrorCode (*BCFunction)(PetscInt dim, PetscReal time,
                                     const PetscReal x[],
                                     PetscInt num_comp_u, PetscScalar *u, void *ctx);
PetscErrorCode SetupDMByDegree(DM dm, PetscInt degree, PetscInt num_comp_u,
                               PetscInt topo_dim,
                               bool enforce_bc,  BCFunction bc_func);
PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt P,
    CeedInt topo_dim, CeedInt height, DMLabel domain_label, CeedInt value,
    CeedElemRestriction *elem_restr);

#endif // petscutils_h


#pragma once
#include <gsl/multi_span>
#include <droidCrypto/Defines.h>
#include <droidCrypto/MatrixView.h>
#include <cstring>

namespace droidCrypto
{
    enum class AllocType
    {
        Uninitialized,
        Zeroed
    };

    template<typename T>
    class Matrix : public MatrixView<T>
    {
        uint64_t mCapacity = 0;
    public:
        Matrix()
        {}

        Matrix(uint64_t rows, uint64_t columns, AllocType t = AllocType::Zeroed)
        {
            resize(rows, columns, t);
        }

        Matrix(const MatrixView<T>& copy)
            : MatrixView<T>(new T[copy.size()], copy.bounds()[0], copy.stride())
            , mCapacity(copy.size())
        {
            memcpy(MatrixView<T>::mView.data(), copy.data(), copy.mView.size_bytes());
        }

        Matrix(Matrix<T>&& copy)
            : MatrixView<T>(copy.data(), copy.bounds()[0], copy.stride())
            , mCapacity(copy.mCapacity)
        {
            copy.mView = span<T>();
            copy.mStride = 0;
            copy.mCapacity = 0;
        }

        ~Matrix()
        {
            delete[] MatrixView<T>::mView.data();
        }

        const Matrix<T>& operator=(const Matrix<T>& copy)
        {
            resize(copy.rows(), copy.stride());
            memcpy(MatrixView<T>::mView.data(), copy.data(), copy.mView.size_bytes());
            return copy;
        }

        void resize(uint64_t rows, uint64_t columns, AllocType type = AllocType::Zeroed)
        {
            if (rows * columns > mCapacity)
            {
                mCapacity = rows * columns;
                auto old = MatrixView<T>::mView;

                if (type == AllocType::Zeroed)
                    MatrixView<T>::mView = span<T>(new T[mCapacity](), mCapacity);
                else
                    MatrixView<T>::mView = span<T>(new T[mCapacity], mCapacity);

                auto min = std::min<uint64_t>(old.size(), mCapacity) * sizeof(T);
                memcpy(MatrixView<T>::mView.data(), old.data(), min);

                delete[] old.data();

            }
            else
            {
                auto newSize = rows * columns;
                if (newSize > MatrixView<T>::size() && type == AllocType::Zeroed)
                {
                    memset(MatrixView<T>::data() + MatrixView<T>::size(), 0, newSize - MatrixView<T>::size());
                }

                MatrixView<T>::mView = span<T>(MatrixView<T>::data(), newSize);
            }

            MatrixView<T>::mStride = columns;
        }

        // return the internal memory, stop managing its lifetime, and set the current container to null.
        T* release()
        {
            auto ret = MatrixView<T>::mView.data();
            MatrixView<T>::mView = {};
            mCapacity = 0;
            return ret;
        }
    };

}


/* spswap.c
 *
 * Copyright (C) 2014 Patrick Alken
 * Copyright (C) 2016 Alexis Tantet
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spmatrix.h>

#include "avl.c"

/*
gsl_spmatrix_transpose()
  Replace the sparse matrix src by its transpose,
keeping the matrix in the same storage format

Inputs: A - (input/output) sparse matrix to transpose.
*/

int
gsl_spmatrix_transpose(gsl_spmatrix * m)
{
  if (GSL_SPMATRIX_ISTRIPLET(m))
    {
      size_t n;

      /* swap row/column indices */
      for (n = 0; n < m->nz; ++n)
        {
          size_t tmp = m->p[n];
          m->p[n] = m->i[n];
          m->i[n] = tmp;
        }

      /* need to rebuild AVL tree, or element searches won't
       * work correctly with transposed indices */
      gsl_spmatrix_tree_rebuild(m);
    }
  else
    {
      GSL_ERROR("unknown sparse matrix type", GSL_EINVAL);
    }

  /* swap dimensions */
  if (m->size1 != m->size2)
    {
      size_t tmp = m->size1;
      m->size1 = m->size2;
      m->size2 = tmp;
    }

  return GSL_SUCCESS;
}

/*
gsl_spmatrix_transpose2()
  Replace the sparse matrix src by its transpose either by
  swapping its row and column indices if it is in triplet storage,
  or by switching its major if it is in compressed storage.

Inputs: A - (input/output) sparse matrix to transpose.
*/

int
gsl_spmatrix_transpose2(gsl_spmatrix * m)
{
  if (GSL_SPMATRIX_ISTRIPLET(m))
    {
      return gsl_spmatrix_transpose(m);
    }
  else if (GSL_SPMATRIX_ISCCS(m))
    {
      m->sptype = GSL_SPMATRIX_CRS;
    }
  else if (GSL_SPMATRIX_ISCRS(m))
    {
      m->sptype = GSL_SPMATRIX_CCS;
    }
  else
    {
      GSL_ERROR("unknown sparse matrix type", GSL_EINVAL);
    }

  /* swap dimensions */
  if (m->size1 != m->size2)
    {
      size_t tmp = m->size1;
      m->size1 = m->size2;
      m->size2 = tmp;
    }

  return GSL_SUCCESS;
}

int
gsl_spmatrix_transpose_memcpy(gsl_spmatrix *dest, const gsl_spmatrix *src)
{
  const size_t M = src->size1;
  const size_t N = src->size2;

  if (M != dest->size2 || N != dest->size1)
    {
      GSL_ERROR("dimensions of dest must be transpose of src matrix",
                GSL_EBADLEN);
    }
  else if (dest->sptype != src->sptype)
    {
      GSL_ERROR("cannot copy matrices of different storage formats",
                GSL_EINVAL);
    }
  else
    {
      int s = GSL_SUCCESS;
      const size_t nz = src->nz;

      if (dest->nzmax < src->nz)
        {
          s = gsl_spmatrix_realloc(src->nz, dest);
          if (s)
            return s;
        }

      if (GSL_SPMATRIX_ISTRIPLET(src))
        {
          size_t n;
          void *ptr;

          for (n = 0; n < nz; ++n)
            {
              dest->i[n] = src->p[n];
              dest->p[n] = src->i[n];
              dest->data[n] = src->data[n];

              /* copy binary tree data */
              ptr = avl_insert(dest->tree_data->tree, &dest->data[n]);
              if (ptr != NULL)
                {
                  GSL_ERROR("detected duplicate entry", GSL_EINVAL);
                }
            }
        }
      else if (GSL_SPMATRIX_ISCCS(src))
        {
          size_t *Ai = src->i;
          size_t *Ap = src->p;
          double *Ad = src->data;
          size_t *ATi = dest->i;
          size_t *ATp = dest->p;
          double *ATd = dest->data;
          size_t *w = (size_t *) dest->work;
          size_t p, j;

          /* initialize to 0 */
          for (p = 0; p < M + 1; ++p)
            ATp[p] = 0;

          /* compute row counts of A (= column counts for A^T) */
          for (p = 0; p < nz; ++p)
            ATp[Ai[p]]++;

          /* compute row pointers for A (= column pointers for A^T) */
          gsl_spmatrix_cumsum(M, ATp);

          /* make copy of row pointers */
          for (j = 0; j < M; ++j)
            w[j] = ATp[j];

          for (j = 0; j < N; ++j)
            {
              for (p = Ap[j]; p < Ap[j + 1]; ++p)
                {
                  size_t k = w[Ai[p]]++;
                  ATi[k] = j;
                  ATd[k] = Ad[p];
                }
            }
        }
      else if (GSL_SPMATRIX_ISCRS(src))
        {
          size_t *Aj = src->i;
          size_t *Ap = src->p;
          double *Ad = src->data;
          size_t *ATj = dest->i;
          size_t *ATp = dest->p;
          double *ATd = dest->data;
          size_t *w = (size_t *) dest->work;
          size_t p, i;

          /* initialize to 0 */
          for (p = 0; p < N + 1; ++p)
            ATp[p] = 0;

          /* compute column counts of A (= row counts for A^T) */
          for (p = 0; p < nz; ++p)
            ATp[Aj[p]]++;

          /* compute column pointers for A (= row pointers for A^T) */
          gsl_spmatrix_cumsum(N, ATp);

          /* make copy of column pointers */
          for (i = 0; i < N; ++i)
            w[i] = ATp[i];

          for (i = 0; i < M; ++i)
            {
              for (p = Ap[i]; p < Ap[i + 1]; ++p)
                {
                  size_t k = w[Aj[p]]++;
                  ATj[k] = i;
                  ATd[k] = Ad[p];
                }
            }
        }
      else
        {
          GSL_ERROR("unknown sparse matrix type", GSL_EINVAL);
        }

      dest->nz = nz;

      return s;
    }
} /* gsl_spmatrix_transpose_memcpy() */


#ifndef __STATS_H__
#define __STATS_H__

#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>

#include <gsl/gsl_rstat.h>

typedef uint64_t word;

#define TIMER_STACK 8
enum Timer{MutTimer, Gen0Timer, Gen1Timer, MiscTimer, MaxTimer};

typedef struct {
  uint64_t allocated;
  uint64_t nursery_n_collections;
  uint64_t nursery_time_max;
  uint64_t nursery_copied;

  uint64_t gen1_collections;
  uint64_t gen1_copied;

  uint64_t max_heap;
  uint64_t max_residency;

  uint64_t start_time;
  enum Timer active_timer[TIMER_STACK];
  uint64_t n_timers;
  uint64_t timers[MaxTimer];

  gsl_rstat_quantile_workspace *latency_50;
  gsl_rstat_quantile_workspace *latency_90;
  gsl_rstat_quantile_workspace *latency_99;
} Stats;

void stats_init(Stats *s);
void stats_pprint(Stats *s);

static uint64_t clock_gettime_nsec(clockid_t clk_id) {
  struct timespec t;
  clock_gettime(clk_id,&t);
  return (uint64_t)t.tv_sec*1e9 + t.tv_nsec;
}

static char *pp_time(uint64_t time) {
  static int i=0;
  static char buffer[10][128];
  i = (i+1) % 10;
  if(time < 1000000) {
    snprintf(buffer[i], 128, "%4.0f us", ((double)time)/1000);
  } else if(time < 10000000) {
    snprintf(buffer[i], 128, "%4.2f ms", ((double)time)/1000000);
  } else if(time < 100000000) {
    snprintf(buffer[i], 128, "%4.1f ms", ((double)time)/1000000);
  } else if(time < 1000000000) {
    snprintf(buffer[i], 128, "%4.0f ms", ((double)time)/1000000);
  } else {
    snprintf(buffer[i], 128, "%4.2f s", ((double)time)/1000000000);
  }
  return buffer[i];
}

static char *pp_speed(uint64_t words, uint64_t time) {
  static int i=0;
  static char buffer[10][128];
  uint64_t rate = words*8/1024 / (time/1e9);
  i = (i+1) % 10;
  if(rate < 1024) {
    snprintf(buffer[i], 128, "%4" PRIu64 " KB/s", rate);
  } if(rate < 1024*1024*10) { // Less than 10000 MB/s
    snprintf(buffer[i], 128, "%4" PRIu64 " MB/s", rate/1024);
  } else {
    snprintf(buffer[i], 128, "%4.1f GB/s", (double)rate/1024/1024);
  }
  return buffer[i];
}

static char *pp_bytes(uint64_t words) {
  static int i=0;
  static char buffer[10][128];
  uint64_t bytes = words * 8;
  i = (i+1) % 10;
  if(bytes < 1024) {
    snprintf(buffer[i], 128, "%4" PRIu64 " B", bytes);
  } else if(bytes < 1024*1024) {
    snprintf(buffer[i], 128, "%4" PRIu64 " KB", bytes/1024);
  } else if(bytes < (uint64_t)1024*1024*1024*10) {
    snprintf(buffer[i], 128, "%4" PRIu64 " MB", bytes/1024/1024);
  } else {
    snprintf(buffer[i], 128, "%4.1f GB", ((double)bytes)/1024/1024/1024);
  }
  return buffer[i];
}

#define CLOCK_ID CLOCK_PROCESS_CPUTIME_ID

static void stats_timer_begin(Stats *s, enum Timer t) {
  uint64_t now = clock_gettime_nsec(CLOCK_ID);
  assert(s->n_timers < TIMER_STACK);
  s->active_timer[s->n_timers] = t;
  if(s->n_timers > 0) {
    s->timers[s->active_timer[s->n_timers-1]] += now - s->start_time;
  }

  s->n_timers++;
  s->start_time = now;
}
static void stats_timer_end(Stats *s) {
  uint64_t now = clock_gettime_nsec(CLOCK_ID);
  uint64_t t = now - s->start_time;
  assert(s->n_timers > 0);
  if(s->active_timer[s->n_timers-1] == Gen0Timer) {
    gsl_rstat_quantile_add((double)t, s->latency_50);
    gsl_rstat_quantile_add((double)t, s->latency_90);
    gsl_rstat_quantile_add((double)t, s->latency_99);
    if(t > s->nursery_time_max) {
      s->nursery_time_max = t;
    }
  }
  s->timers[s->active_timer[s->n_timers-1]] += t;
  s->start_time = now;
  s->n_timers--;
}

#endif


/* block/test.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_block.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_test.h>

int status = 0;

#ifndef DESC
#define DESC ""
#endif

#define N 1027

#define BASE_GSL_COMPLEX_LONG
#include "templates_on.h"
#include "test_complex_source.c"
#if HAVE_PRINTF_LONGDOUBLE
#include "test_complex_io.c"
#endif
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_LONG

#define BASE_GSL_COMPLEX
#include "templates_on.h"
#include "test_complex_source.c"
#include "test_complex_io.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX

#define BASE_GSL_COMPLEX_FLOAT
#include "templates_on.h"
#include "test_complex_source.c"
#include "test_complex_io.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_FLOAT

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "test_source.c"
#if HAVE_PRINTF_LONGDOUBLE
#include "test_io.c"
#endif
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

#define BASE_DOUBLE
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#define BASE_ULONG
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_ULONG

#define BASE_LONG
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_LONG

#define BASE_UINT
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_UINT

#define BASE_INT
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_INT

#define BASE_USHORT
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_USHORT

#define BASE_SHORT
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_SHORT

#define BASE_UCHAR
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_UCHAR

#define BASE_CHAR
#include "templates_on.h"
#include "test_source.c"
#include "test_io.c"
#include "templates_off.h"
#undef  BASE_CHAR

void my_error_handler (const char *reason, const char *file,
                       int line, int err);

int
main (void)
{
  gsl_ieee_env_setup ();

  test_func ();
  test_float_func ();
  test_long_double_func ();
  test_ulong_func ();
  test_long_func ();
  test_uint_func ();
  test_int_func ();
  test_ushort_func ();
  test_short_func ();
  test_uchar_func ();
  test_char_func ();
  test_complex_func ();
  test_complex_float_func ();
  test_complex_long_double_func ();

  test_text ();
  test_float_text ();
#if HAVE_PRINTF_LONGDOUBLE
  test_long_double_text ();
#endif
  test_ulong_text ();
  test_long_text ();
  test_uint_text ();
  test_int_text ();
  test_ushort_text ();
  test_short_text ();
  test_uchar_text ();
  test_char_text ();
  test_complex_text ();
  test_complex_float_text ();
#if HAVE_PRINTF_LONGDOUBLE
  test_complex_long_double_text ();
#endif

  test_binary ();
  test_float_binary ();
  test_long_double_binary ();
  test_ulong_binary ();
  test_long_binary ();
  test_uint_binary ();
  test_int_binary ();
  test_ushort_binary ();
  test_short_binary ();
  test_uchar_binary ();
  test_char_binary ();
  test_complex_binary ();
  test_complex_float_binary ();
  test_complex_long_double_binary ();

  gsl_set_error_handler (&my_error_handler);

  test_alloc_zero_length ();
  test_float_alloc_zero_length ();
  test_long_double_alloc_zero_length ();
  test_ulong_alloc_zero_length ();
  test_long_alloc_zero_length ();
  test_uint_alloc_zero_length ();
  test_int_alloc_zero_length ();
  test_ushort_alloc_zero_length ();
  test_short_alloc_zero_length ();
  test_uchar_alloc_zero_length ();
  test_char_alloc_zero_length ();
  test_complex_alloc_zero_length ();
  test_complex_float_alloc_zero_length ();
  test_complex_long_double_alloc_zero_length ();

  test_calloc_zero_length ();
  test_float_calloc_zero_length ();
  test_long_double_calloc_zero_length ();
  test_ulong_calloc_zero_length ();
  test_long_calloc_zero_length ();
  test_uint_calloc_zero_length ();
  test_int_calloc_zero_length ();
  test_ushort_calloc_zero_length ();
  test_short_calloc_zero_length ();
  test_uchar_calloc_zero_length ();
  test_char_calloc_zero_length ();
  test_complex_calloc_zero_length ();
  test_complex_float_calloc_zero_length ();
  test_complex_long_double_calloc_zero_length ();

  exit (gsl_test_summary ());
}

void
my_error_handler (const char *reason, const char *file, int line, int err)
{
  if (0)
    printf ("(caught [%s:%d: %s (%d)])\n", file, line, reason, err);
  status = 1;
}


/*
GENETIC - A simple genetic algorithm.

Copyright 2014, Javier Burguete Tolosa.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
	list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Javier Burguete Tolosa ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL Javier Burguete Tolosa OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * \file genetic.c
 * \brief Source file to define genetic algorithm main function.
 * \author Javier Burguete Tolosa.
 * \copyright Copyright 2014 Javier Burguete Tolosa. All rights reserved.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <glib.h>
#if HAVE_MPI
#include <mpi.h>
#endif
#include "bits.h"
#include "entity.h"
#include "population.h"
#include "reproduction.h"
#include "selection.h"
#include "evolution.h"
#include "genetic.h"

#define DEBUG_GENETIC 0         ///< Macro to debug the genetic functions.

static Population genetic_population[1];
///< Population of the genetic algorithm.
static double (*genetic_simulation) (Entity *);
///< Pointer to the function to perform a simulation.

/**
 * Function to get a variable encoded in the genome of an entity.
 *
 * \return Variable value.
 */
double
genetic_get_variable (Entity * entity,  ///< Entity struct.
                      GeneticVariable * variable)       ///< Variable data.
{
  double x;
#if DEBUG_GENETIC
  fprintf (stderr, "get variable: start\n");
#endif
  x = variable->minimum
    + bit_get_value (entity->genome, variable->location, variable->nbits)
    * (variable->maximum - variable->minimum)
    / ((unsigned long long int) 1L << variable->nbits);
#if DEBUG_GENETIC
  fprintf (stderr, "get variable: value=%lg\n", x);
  fprintf (stderr, "get variable: end\n");
#endif
  return x;
}

/**
 * Funtion to apply the evolution of a population.
 */
static inline void
genetic_evolution (Population * population,     ///< Population
                   gsl_rng * rng)       ///< GSL random numbers generator.
{
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_evolution: start\n");
#endif
  evolution_mutation (population, rng);
  evolution_reproduction (population, rng);
  evolution_adaptation (population, rng);
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_evolution: end\n");
#endif
}

/**
 * Funtion to perform the simulations on a thread.
 */
static void
genetic_simulation_thread (GeneticThreadData * data)    ///< Thread data.
{
  unsigned int i;
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_thread: start\n");
  fprintf (stderr, "genetic_simulation_thread: nmin=%u nmax=%u\n",
           data->nmin, data->nmax);
#endif
  for (i = data->nmin; i < data->nmax && !genetic_population->stop; ++i)
    {
      genetic_population->objective[i]
        = genetic_simulation (genetic_population->entity + i);
      if (genetic_population->objective[i] < genetic_population->threshold)
        {
          g_mutex_lock (mutex);
          genetic_population->stop = 1;
          g_mutex_unlock (mutex);
          break;
        }
    }
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_thread: end\n");
#endif
}

/**
 * Function to perform the simulations on a task.
 */
static void
genetic_simulation_master (unsigned int nsurvival)
                           ///< Number of survival entities already simulated.
{
  unsigned int j, nsimulate, nmin, nmax;
  GThread *thread[nthreads];
  GeneticThreadData thread_data[nthreads];
  Population *population;
#if HAVE_MPI
  unsigned int i;
  unsigned int n[ntasks + 1];
  unsigned int stop[ntasks];
  char *genome_array;
  MPI_Status mpi_status;
#endif

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_master: start\n");
#endif

  population = genetic_population;
  nmax = population->nentities;
  nsimulate = nmax - nsurvival;
  nmin = nsurvival;
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_master: nmax=%u nmin=%u nsimulate=%u\n",
           nmax, nmin, nsimulate);
#endif

#if HAVE_MPI
  nmax = nmin + nsimulate / ntasks;

  // Send genome information to the slaves
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_master: nmax=%u nmin=%u nsimulate=%u\n",
           nmax, nmin, nsimulate);
#endif

  genome_array = (char *) g_malloc (nsimulate * population->genome_nbytes);
  for (j = 0; j < nsimulate; ++j)
    memcpy (genome_array + j * population->genome_nbytes,
            population->entity[nmin + j].genome, population->genome_nbytes);
  n[0] = 0;
  for (i = 0; (int) ++i < ntasks;)
    {
      n[i] = i * nsimulate / ntasks;
      j = (i + 1) * nsimulate / ntasks - n[i];
#if DEBUG_GENETIC
      fprintf (stderr,
               "genetic_simulation_master: send %u bytes on %u to task %u\n",
               j * population->genome_nbytes, nsurvival + n[i], i);
#endif
      MPI_Send (genome_array + n[i] * population->genome_nbytes,
                j * population->genome_nbytes, MPI_CHAR, i, 1, MPI_COMM_WORLD);
    }
  n[i] = nsimulate;

#if DEBUG_GENETIC
  for (j = 0; j <= ntasks; ++j)
    fprintf (stderr, "genetic_simulation_master: n[%u]=%u\n", j, n[j]);
#endif
  g_free (genome_array);

  nsimulate = nmax - nmin;

#endif

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_master: nsimulate=%u\n", nsimulate);
  fprintf (stderr, "genetic_simulation_master: performing simulations\n");
#endif

  thread_data[0].nmin = nmin;
  for (j = 0; ++j < nthreads;)
    thread_data[j - 1].nmax = thread_data[j].nmin
      = nmin + j * nsimulate / nthreads;
  thread_data[j - 1].nmax = nmax;
  for (j = 0; j < nthreads; ++j)
    thread[j] = g_thread_new
      (NULL, (GThreadFunc) (void (*)(void)) genetic_simulation_thread,
       thread_data + j);
  for (j = 0; j < nthreads; ++j)
    g_thread_join (thread[j]);

#if HAVE_MPI
  // Receive objective function resuts from the slaves
  for (j = 0; (int) ++j < ntasks;)
    {
#if DEBUG_GENETIC
      fprintf (stderr,
               "genetic_simulation_master: "
               "receive %u reals from task %u on %u\n",
               n[j + 1] - n[j], j, nsurvival + n[j]);
#endif
      MPI_Recv (population->objective + nsurvival + n[j], n[j + 1] - n[j],
                MPI_DOUBLE, j, 1, MPI_COMM_WORLD, &mpi_status);
#if DEBUG_GENETIC
      fprintf (stderr,
               "genetic_simulation_master: receive one integer from task %u\n",
               j);
#endif
      MPI_Recv (stop + j, j, MPI_UNSIGNED, j, 1, MPI_COMM_WORLD, &mpi_status);
      if (stop[j])
        genetic_population->stop = 1;
    }
  // Sending stop instruction to the slaves
  for (j = 0; (int) ++j < ntasks;)
    {
#if DEBUG_GENETIC
      fprintf (stderr,
               "genetic_simulation_master: sending one integer to task %u\n",
               j);
#endif
      MPI_Send (&genetic_population->stop, 1, MPI_UNSIGNED, j, 1,
                MPI_COMM_WORLD);
    }
#endif

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_master: end\n");
#endif
}

#if HAVE_MPI

/**
 * Function to perform the simulations on a task.
 */
static void
genetic_simulation_slave (unsigned int nsurvival,
                          ///< Number of survival entities already simulated.
                          int rank)     ///< Number of task.
{
  unsigned int j, nsimulate, nmin, nmax, stop;
  GThread *thread[nthreads];
  GeneticThreadData thread_data[nthreads];
  Population *population;
  char *genome_array;
  MPI_Status mpi_status;

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_slave: rank=%d start\n", rank);
#endif

  population = genetic_population;
  nmax = population->nentities;
  nsimulate = nmax - nsurvival;
  nmin = nsurvival;
  nmax = nmin + (rank + 1) * nsimulate / ntasks;
  nmin += rank * nsimulate / ntasks;

  // Receive genome information from the master
  nsimulate = nmax - nmin;
  genome_array = (char *) g_malloc (nsimulate * population->genome_nbytes);
#if DEBUG_GENETIC
  fprintf (stderr,
           "genetic_simulation_slave: rank=%d receive %u bytes from master\n",
           rank, nsimulate * population->genome_nbytes);
#endif
  MPI_Recv (genome_array, nsimulate * population->genome_nbytes, MPI_CHAR, 0,
            1, MPI_COMM_WORLD, &mpi_status);
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_slave: rank=%d nmin=%u nsimulate=%u\n",
           rank, nmin, nsimulate);
#endif
  for (j = 0; j < nsimulate; ++j)
    memcpy (population->entity[nmin + j].genome,
            genome_array + j * population->genome_nbytes,
            population->genome_nbytes);
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_slave: rank=%d freeing\n", rank);
#endif
  g_free (genome_array);

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_slave: rank=%d performing simulations\n",
           rank);
#endif
  thread_data[0].nmin = nmin;
  for (j = 0; ++j < nthreads;)
    thread_data[j - 1].nmax = thread_data[j].nmin
      = nmin + j * nsimulate / nthreads;
  thread_data[j - 1].nmax = nmax;
  for (j = 0; j < nthreads; ++j)
    thread[j] = g_thread_new
      (NULL, (GThreadFunc) (void (*)(void)) genetic_simulation_thread,
       thread_data + j);
  for (j = 0; j < nthreads; ++j)
    g_thread_join (thread[j]);

#if DEBUG_GENETIC
  fprintf (stderr,
           "genetic_simulation_slave: rank=%d send %u reals on %u to master\n",
           rank, nsimulate, nmin);
#endif
  // Send the objective function resuts to the master
  MPI_Send (population->objective + nmin, nsimulate, MPI_DOUBLE, 0, 1,
            MPI_COMM_WORLD);
#if DEBUG_GENETIC
  fprintf (stderr,
           "genetic_simulation_slave: rank=%d send one integer to master\n",
           rank);
#endif
  // Send the stop variable from the master
  MPI_Send (&genetic_population->stop, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD);
#if DEBUG_GENETIC
  fprintf (stderr,
           "genetic_simulation_slave: "
           "rank=%d receive one integer from master\n", rank);
#endif
  // Receive the stop variable from the master
  MPI_Recv (&stop, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &mpi_status);
  if (stop)
    genetic_population->stop = 1;

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_simulation_slave: rank=%d end\n", rank);
#endif
}

#endif

/**
 * Function to create the data of the genetic algorithm.
 *
 * \return 1 on succes, 0 on error.
 */
int
genetic_new (unsigned int nvariables,   ///< Number of variables.
             GeneticVariable * variable,        ///< Array of variables data.
             unsigned int nentities,
             ///< Number of entities in each generation.
             double mutation_ratio,     ///< Mutation ratio.
             double reproduction_ratio, ///< Reproduction ratio.
             double adaptation_ratio,   ///< Adaptation ratio.
             double threshold)  ///< Threshold to finish the simulations.
{
  unsigned int i, genome_nbits, nprocesses;

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_new: start\n");
#endif

  // Checking variables number
  if (!nvariables)
    {
      fprintf (stderr, "ERROR: no variables\n");
      return 0;
    }

  // Checking variable bits number
  for (i = genome_nbits = 0; i < nvariables; ++i)
    {
      if (!variable[i].nbits || variable[i].nbits > 32)
        {
          fprintf (stderr, "ERROR: bad bits number in variable %u\n", i + 1);
          return 0;
        }
      variable[i].location = genome_nbits;
      genome_nbits += variable[i].nbits;
    }

  // Checking processes number
  nprocesses = ntasks * nthreads;
  if (!nprocesses)
    {
      fprintf (stderr, "ERROR: no processes\n");
      return 0;
    }

  // Init the population
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_new: init the population\n");
#endif
  if (!population_new (genetic_population, variable, nvariables, genome_nbits,
                       nentities, mutation_ratio, reproduction_ratio,
                       adaptation_ratio, threshold))
    return 0;

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_new: end\n");
#endif
  return 1;
}

/**
 * Function to perform the genetic algorithm.
 *
 * \return 1 on succes, 0 on error.
 */
int
genetic_algorithm (unsigned int nvariables,     ///< Number of variables.
                   GeneticVariable * variable,  ///< Array of variables data.
                   unsigned int nentities,
                   ///< Number of entities in each generation.
                   unsigned int ngenerations,   ///< Number of generations.
                   double mutation_ratio,       ///< Mutation ratio.
                   double reproduction_ratio,   ///< Reproduction ratio.
                   double adaptation_ratio,     ///< Adaptation ratio.
                   const gsl_rng_type * type_random,
                   ///< Type of GSL random numbers generator algorithm.
                   unsigned long random_seed,
                   ///< Seed of the GSL random numbers generator.
                   unsigned int type_reproduction,
                   ///< Type of reproduction algorithm.
                   unsigned int type_selection_mutation,
                   ///< Type of mutation selection algorithm.
                   unsigned int type_selection_reproduction,
                   ///< Type of reproduction selection algorithm.
                   unsigned int type_selection_adaptation,
                   ///< Type of adaptation selection algorithm.
                   double threshold,
                   ///< Threshold to finish the simulations.
                   double (*simulate_entity) (Entity *),
///< Pointer to the function to perform a simulation of an entity.
                   char **best_genome,  ///< Best genome.
                   double **best_variables,     ///< Best array of variables.
                   double *best_objective)
                   ///< Best objective function value.
{
  unsigned int i;
  double *bv;
  gsl_rng *rng;
  Entity *best_entity;
#if HAVE_MPI
  int rank;
#endif

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_algorithm: start\n");
#endif

  // Init the data
  if (!genetic_new (nvariables, variable, nentities,
                    mutation_ratio, reproduction_ratio, adaptation_ratio,
                    threshold))
    return 0;

  // Init the evaluation function
  genetic_simulation = simulate_entity;

  // Get the rank
#if HAVE_MPI
  MPI_Comm_rank (MPI_COMM_WORLD, &rank);
#endif

  // Variables to init only by master task
#if HAVE_MPI
  if (rank == 0)
    {
#endif
      // Init the GSL random numbers generator
      rng = gsl_rng_alloc (type_random);
      if (random_seed)
        gsl_rng_set (rng, random_seed);

      // Init genomes
      population_init_genomes (genetic_population, rng);

      // Init selection, mutation and reproduction algorithms
      reproduction_init (type_reproduction);
      selection_init (type_selection_mutation, type_selection_reproduction,
                      type_selection_adaptation);

#if HAVE_MPI
    }
  if (rank == 0)
    {
#endif

      // First simulation of all entities
#if DEBUG_GENETIC
      fprintf (stderr, "genetic_algorithm: first simulation of all entities\n");
#endif
      genetic_simulation_master (0);

      // Sorting by objective function results
#if DEBUG_GENETIC
      fprintf (stderr,
               "genetic_algorithm: sorting by objective function results\n");
#endif
      evolution_sort (genetic_population);

      // Population generations
#if DEBUG_GENETIC
      fprintf (stderr, "genetic_algorithm: ngenerations=%u\n", ngenerations);
#endif
      for (i = 1; i < ngenerations && !genetic_population->stop; ++i)
        {
          // Evolution
#if DEBUG_GENETIC
          fprintf (stderr, "genetic_algorithm: evolution of the population\n");
#endif
          genetic_evolution (genetic_population, rng);

          // Simulation of the new entities
#if DEBUG_GENETIC
          fprintf (stderr,
                   "genetic_algorithm: simulation of the new entities\n");
#endif
          genetic_simulation_master (genetic_population->nsurvival);

          // Sorting by objective function results
#if DEBUG_GENETIC
          fprintf (stderr,
                   "genetic_algorithm: sorting by objective function results\n");
#endif
          evolution_sort (genetic_population);
        }

      // Saving the best
#if DEBUG_GENETIC
      fprintf (stderr, "genetic_algorithm: saving the best\n");
#endif
      best_entity = genetic_population->entity;
      *best_objective = genetic_population->objective[0];
      *best_genome = (char *) g_malloc (genetic_population->genome_nbytes);
      memcpy (*best_genome, best_entity->genome,
              genetic_population->genome_nbytes);
      *best_variables = bv = (double *) g_malloc (nvariables * sizeof (double));
      for (i = 0; i < nvariables; ++i)
        bv[i] = genetic_get_variable (best_entity, variable + i);
      gsl_rng_free (rng);

#if HAVE_MPI

    }
  else
    {
      // First simulation of all entities
#if DEBUG_GENETIC
      fprintf (stderr, "genetic_algorithm: first simulation of all entities\n");
#endif
      genetic_simulation_slave (0, rank);

      // Population generations
      for (i = 1; i < ngenerations && !genetic_population->stop; ++i)
        {
          // Simulation of the new entities
#if DEBUG_GENETIC
          fprintf (stderr,
                   "genetic_algorithm: simulation of the new entities\n");
#endif
          genetic_simulation_slave (genetic_population->nsurvival, rank);
        }
    }

#endif

  // Freeing memory
#if DEBUG_GENETIC
  fprintf (stderr, "genetic_algorithm: freeing memory\n");
#endif
  population_free (genetic_population);

#if DEBUG_GENETIC
  fprintf (stderr, "genetic_algorithm: rank=%d\n", rank);
  fprintf (stderr, "genetic_algorithm: end\n");
#endif
  return 1;
}

/**
 * Function to perform the genetic algorithm with default random and evolution
 *   algorithms.
 *
 * \return 1 on succes, 0 on error.
 */
int
genetic_algorithm_default (unsigned int nvariables,
                           ///< Number of variables.
                           GeneticVariable * variable,
                           ///< Array of variables data.
                           unsigned int nentities,
                           ///< Number of entities in each generation.
                           unsigned int ngenerations,
                           ///< Number of generations.
                           double mutation_ratio,       ///< Mutation ratio.
                           double reproduction_ratio,   ///< Reproduction ratio.
                           double adaptation_ratio,     ///< Adaptation ratio.
                           unsigned long random_seed,
                           ///< Seed of the GSL random numbers generator.
                           double threshold,
                           ///< Threshold to finish the simulations.
                           double (*simulate_entity) (Entity *),
///< Pointer to the function to perform a simulation of an entity.
                           char **best_genome,  ///< Best genome.
                           double **best_variables,
                           ///< Best array of variables.
                           double *best_objective)
                           ///< Best objective function value.
{
  return genetic_algorithm (nvariables,
                            variable,
                            nentities,
                            ngenerations,
                            mutation_ratio,
                            reproduction_ratio,
                            adaptation_ratio,
                            gsl_rng_mt19937,
                            random_seed,
                            0,
                            0,
                            0,
                            0,
                            threshold,
                            simulate_entity,
                            best_genome, best_variables, best_objective);
}


#pragma once

#include <queue>
#include <vector>

#include <gsl/gsl>

#define EMPTY_NODE '\0'

class minqueue_node {
    public:
    minqueue_node(char value, unsigned int score);
    ~minqueue_node();

    unsigned int score;
    char value;

    minqueue_node* left;
    minqueue_node* right;
};

struct cmp_minqueue_nodes {
    bool operator()(
        const gsl::not_null<minqueue_node*> left, const gsl::not_null<minqueue_node*> right);
};

class minqueue {
    std::priority_queue<
        minqueue_node*, std::vector<minqueue_node*>, cmp_minqueue_nodes> nodes;

    public:
    minqueue();
    ~minqueue();

    void push(gsl::not_null<minqueue_node*> node);
    minqueue_node* pop();

    bool empty();
    int size();
};


#include <stdio.h>
#include <gsl/gsl_ieee_utils.h>

int main() {
    //gsl_ieee_printf_double
    double a = 1.1;
    while (a > 0){
        gsl_ieee_printf_double(&a);
        printf("\r\n");
        a /= 2.0;
    }

    float b = 1.1;
    while (b > 0){
        gsl_ieee_printf_float(&b);
        printf("\r\n");
        b /= 2.0;
    }

    return 0;
}


#ifndef libceed_solids_examples_setup_dm_h
#define libceed_solids_examples_setup_dm_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>
#include <petscfe.h>
#include "../include/structs.h"

// -----------------------------------------------------------------------------
// Setup DM
// -----------------------------------------------------------------------------
PetscErrorCode CreateBCLabel(DM dm, const char name[]);

// Read mesh and distribute DM in parallel
PetscErrorCode CreateDistributedDM(MPI_Comm comm, AppCtx app_ctx, DM *dm);

// Setup DM with FE space of appropriate degree
PetscErrorCode SetupDMByDegree(DM dm, AppCtx app_ctx, PetscInt order,
                               PetscBool boundary, PetscInt num_comp_u);

#endif // libceed_solids_examples_setup_dm_h


/**
 *
 * @file core_blas.h
 *
 *  PLASMA auxiliary routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Jakub Kurzak
 * @author Hatem Ltaief
 * @date 2010-11-15
 *
 **/
#ifndef _PLASMA_CORE_BLAS_H_
#define _PLASMA_CORE_BLAS_H_

#ifdef USE_MKL
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#include "plasmatypes.h"
#include "descriptor.h"

#include "core_dblas.h"

#ifdef __cplusplus
extern "C" {
#endif

  /*
   * Coreblas Error
   */
#define coreblas_error(k, str) fprintf(stderr, "%s: Parameter %d / %s\n", __func__, k, str);

 /** ****************************************************************************
  *  LAPACK Constants
  **/
extern char *plasma_lapack_constants[];
#define lapack_const(plasma_const) plasma_lapack_constants[plasma_const][0]

  /*
   * CBlas enum
   */
#define CBLAS_TRANSPOSE int
#define CBLAS_UPLO      int
#define CBLAS_DIAG      int
#define CBLAS_SIDE      int
//#define CBLAS_TRANSPOSE enum CBLAS_TRANSPOSE
//#define CBLAS_UPLO      enum CBLAS_UPLO
//#define CBLAS_DIAG      enum CBLAS_DIAG
//#define CBLAS_SIDE      enum CBLAS_SIDE

#ifdef __cplusplus
}
#endif

#endif /* _PLASMA_CORE_BLAS_H_ */


#include <gsl/gsl_interp.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char const *argv[]) {
  double x = 4096.0;
  int iterations = 1500;
  for(int i=0; i<iterations; i++){
    printf("%lf\n",x);
    gsl_ieee_printf_double(&x);
    printf("---------\n");
    x = x/2;
  }
  return 0;
}


/*
 * utils.h
 *
 *  Created on: Dec 14, 2016
 *      Author: hazem
 */
#include <cublas_v2.h>
#include <curand.h>
#include <stdlib.h>
#include <stdio.h>
#include "papi.h"
#include <cblas.h>
#include <pthread.h>
#include <sys/mman.h>
#ifndef UTILS_H_
#define UTILS_H_

float convert_to_sec(long long time_usec);
float calculate_average(float* array, int repeat);
void GPU_fill_rand(float *A, int nr_rows_A, int nr_cols_A);
void fill_2d_matrix(float* matrix, int n, int m, float value);
void print_matrix(const float *A, int nr_rows_A, int nr_cols_A);
#endif /* UTILS_H_ */