#include "solver_intern.h"
#include <pygsl/general_helpers.h>
#include <pygsl/block_helpers.h>
#include <pygsl/error_helpers.h>
#include <pygsl/string_helpers.h>
#if 0
#include <pygsl/function_helpers.h>
#endif
#include <setjmp.h>
#include <gsl/gsl_math.h>
#include <pygsl/error_helpers.h>
#include <strings.h>
#include "solver_doc.ic"


PyObject * module = NULL;
const char *filename = __FILE__;


static int
PyGSL_solver_set_called(PyGSL_solver *self)
{
     FUNC_MESS_BEGIN();
     if(self->set_called == 1)
	  return GSL_SUCCESS;
     DEBUG_MESS(2, "self->set_called was %d", self->set_called);
     pygsl_error("The set() method must be called before using the other methods!",
	       filename, __LINE__, GSL_EINVAL);
     FUNC_MESS_END();
     return GSL_EINVAL;
}

static PyObject* 
PyGSL_solver_restart(PyGSL_solver *self, PyObject *args) 
{
     FUNC_MESS_BEGIN();
     if (PyGSL_SOLVER_SET_CALLED(self) != GSL_SUCCESS)
	  return NULL;

     if(self->mstatic->cmethods.restart == NULL)
	  PyGSL_ERROR_NULL("Can not restart a solver of this type!", GSL_ESANITY);
     self->mstatic->cmethods.restart(self->solver);
     Py_INCREF(Py_None);
     FUNC_MESS_END();
     return Py_None;
}

static PyObject* 
PyGSL_solver_name(PyGSL_solver *self, PyObject *args) 
{
     PyObject * tmp;
     const char * ctmp;
     FUNC_MESS_BEGIN();
     if(self->mstatic->cmethods.name == NULL)
	  PyGSL_ERROR_NULL("Can not restart a solver of this type!", GSL_ESANITY);
     ctmp = self->mstatic->cmethods.name(self->solver);
     tmp =  PyGSL_string_from_string(ctmp);
     FUNC_MESS_END();
     return tmp;
}

static PyObject* 
PyGSL_solver_iterate(PyGSL_solver *self, PyObject *args) 
{
     int tmp;
     FUNC_MESS_BEGIN();
     if (PyGSL_SOLVER_SET_CALLED(self) != GSL_SUCCESS)
	  return NULL;

     if(self->mstatic->cmethods.iterate == NULL)
	  PyGSL_ERROR_NULL("Can not restart a solver of this type!", GSL_ESANITY);

     assert(self->mstatic->cmethods.iterate);
     assert(self->solver);
     tmp = (self->mstatic->cmethods.iterate(self->solver));
     if(PyGSL_ERROR_FLAG(tmp) != GSL_SUCCESS)
	  return NULL;

     return PyLong_FromLong((long) tmp);
}


static void
PyGSL_solver_dealloc(PyGSL_solver * self)
{
     /* struct pygsl_array_cache * cache_ptr; 
     int i, count;
     PyObject * ob;
     PyArrayObject *tmp;
     */
     FUNC_MESS_BEGIN();
     assert(self);
     assert(self->mstatic);

     if(self->mstatic->cmethods.free == NULL){
	  DEBUG_MESS(3, "Could not free solver @ %p. No free method specified!", self->solver);
     }else{
	  DEBUG_MESS(3, "Freeing a solver of type %s", self->mstatic->type_name);
	  if(self->solver != NULL){
	       self->mstatic->cmethods.free(self->solver);
	       self->solver = NULL;
	  }
     }

     Py_XDECREF(self->args);     
     self->args = NULL;
     if(self->c_sys){
	  DEBUG_MESS(3, "Freeing c_sys @ %p", self->c_sys);
	  free(self->c_sys);
	  self->c_sys = NULL;
     }

     /*
      * remove the cached arrays
      */
     if(self->cache == NULL){
	  DEBUG_MESS(2, "No cache was used cache = %p", self->cache);
     }else{
#if 0
	  cache_ptr = self->cache;
	  for(i = 0; i< PyGSL_SOLVER_N_ARRAYS; ++i){
	       tmp =  cache_ptr[i].ref;
	       ob = (PyObject *) tmp;
	       if(ob == NULL){
		    break;
	       }
	       count =  ob->ob_refcnt;
	       if(count == 0){
		    DEBUG_MESS(3, "object[%d] @ %p has a zero reference count!"
			       " array should be deposed already and ptr set to zero!", i, ob);
	       }else if(count == 1){
		    /* no one referencing the array; good */		    
		    DEBUG_MESS(3, "Dereferencing  object[%d] @ %p", i, ob);
		    Py_DECREF(ob);
		    cache_ptr[i].ref = NULL;
	       }else if(count < 0){
		    DEBUG_MESS(2, "I found an array (object) at %p which had "
			       "a count [%d] smaller than zero", ob, count);
		    
	       }else{
		    /* count > 1 */
		    fprintf(stderr, "In %s at %d array %d had %d refcouts.\n"
			    "This means you reference an array which was\n"
			    "passed to you while evaluating your callback.\n" 
			    "This produces a memory leak!\n", 
			    __FILE__, __LINE__, i, count);		    
	       
	       }
	  } /* handeled cached objects */
	  fprintf(stderr, "Freed %d cached objects\n", i);
	  /* free the cache */
	  free(self->cache);
	  self->cache = NULL;
#endif 

     } /* freeing cached objects */
     PyObject_Del(self);
     self = NULL;
     FUNC_MESS_END();

}

static PyObject *
PyGSL_solver_type(PyGSL_solver * self, PyObject *unused)
{
     assert(self->mstatic->type_name);
     return PyGSL_string_from_string(self->mstatic->type_name);
}

static PyMethodDef solver_methods[] = {
     {"name",    (PyCFunction) PyGSL_solver_name,    METH_NOARGS, NULL},
     {"restart", (PyCFunction) PyGSL_solver_restart, METH_NOARGS, NULL},
     {"iterate", (PyCFunction) PyGSL_solver_iterate, METH_NOARGS, NULL},
     {"type",    (PyCFunction) PyGSL_solver_type,    METH_NOARGS, NULL},
     {NULL, NULL, 0, NULL}
};

#ifndef PyGSL_PY3K
static PyObject *
PyGSL_solver_getattr(PyGSL_solver * self, char * name)
{
     PyObject *tmp = NULL;
     FUNC_MESS_BEGIN();
     if(self->mstatic->pymethods)
       tmp = Py_FindMethod(self->mstatic->pymethods, (PyObject *) self, name);
     if(tmp == NULL){
	  PyErr_Clear();
	  tmp = Py_FindMethod(solver_methods, (PyObject *) self, name);
     }
     FUNC_MESS_END();
     return tmp;
}
#endif /* PyGSL_PY3K */

#ifdef PyGSL_PY3K
static PyTypeObject PyGSL_solver_pytype = {
	PyObject_HEAD_INIT(NULL)
	"PyGSL_solver",                    /* tp_name */
	sizeof(PyGSL_solver),              /* tp_basicsize */
	0,                                          /* tp_itemsize */
	(destructor) PyGSL_solver_dealloc, /* tp_dealloc */
	0,                       /* tp_print */
	0,                       /* tp_getattr */
	0,                       /* tp_setattr */
	0,                       /* tp_reserved */
	0,                       /* tp_repr */
	0,                       /* tp_as_number */
	0,                       /* tp_as_sequence */
	0,                       /* tp_as_mapping */
	0,                       /* tp_hash */
	0,                       /* tp_call */
	0,                       /* tp_str */
	0,                       /* tp_getattro */
	0,                       /* tp_setattro */
	0,                       /* tp_as_buffer */
	Py_TPFLAGS_DEFAULT,      /* tp_flags */
	(char *)PyGSL_solver_type_doc, /* tp_doc */
	0,                       /* tp_traverse */
	0,                       /* tp_clear */
	0,                       /* tp_richcompare */
	0,                       /* tp_weaklistoffset */
	0,                       /* tp_iter */
	0,                       /* tp_iternext */
	solver_methods,          /* tp_methods */
	0,                       /* tp_members */
	0,                       /* tp_getset */
	0,                       /* tp_base */
	0,                       /* tp_dict */
	0,                       /* tp_descr_get */
	0,                       /* tp_descr_set */
	0,                       /* tp_dictoffset */
	0,                       /* tp_init */
	0,                       /* tp_alloc */
	0,                       /* tp_new */
};
#else /* PyGSL_PY3K */
static PyTypeObject PyGSL_solver_pytype = {
  PyObject_HEAD_INIT(NULL)	 /* fix up the type slot in initcrng */
  0,				 /* ob_size */
  "PyGSL_solver",		 /* tp_name */
  sizeof(PyGSL_solver),	 /* tp_basicsize */
  0,				 /* tp_itemsize */

  /* standard methods */
  (destructor)  PyGSL_solver_dealloc, /* tp_dealloc  ref-count==0  */
  (printfunc)   0,		 /* tp_print    "print x"     */
  (getattrfunc) PyGSL_solver_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 */
  (char *) PyGSL_solver_type_doc		/* tp_doc */
};
#endif /* PyGSL_PY3K */

PyGSL_solver* 
_PyGSL_solver_init(const struct _SolverStatic *mstatic) 
{
     PyGSL_solver *solver_o=NULL;
     int line = -1;
     int i;

     FUNC_MESS_BEGIN();     
     if(mstatic->n_cbs > PyGSL_SOLVER_NCBS_MAX){
	  line = __LINE__ - 1;
	  pygsl_error("More callbacks requested than possible!", __FILE__,
		    line, GSL_ESANITY);
	  goto fail;
     }
     solver_o =  (PyGSL_solver *) PyObject_NEW(PyGSL_solver, &PyGSL_solver_pytype);
     if(solver_o == NULL){
	  line = __LINE__ -1;
	  goto fail;
     }
     
     solver_o->args = NULL;     
     solver_o->cache = NULL;
     solver_o->mstatic = NULL;
     solver_o->mstatic = mstatic;
     solver_o->solver = NULL;
     solver_o->c_sys = NULL;
     solver_o->set_called = 0;
     solver_o->isset = 0;

     for(i = 0; i < PyGSL_SOLVER_NCBS_MAX; ++i){
	  solver_o->cbs[i] = NULL;
     }

     for(i = 0; i < PyGSL_SOLVER_PB_ND_MAX; ++i){
	  solver_o->problem_dimensions[i] = -1;
     }

     FUNC_MESS_END();
     return solver_o;
 fail:
     FUNC_MESS("Fail");
     PyGSL_add_traceback(module, __FILE__, __FUNCTION__, line);
     return NULL;
}


static PyObject *
PyGSL_solver_dn_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc, int nd)
{
     PyGSL_solver *solver_o=NULL;
     int n1=1, n2=1;
     int line = -1;
     int flag=0;

     FUNC_MESS_BEGIN();
     assert(alloc);
     solver_o =  _PyGSL_solver_init(alloc->mstatic);
     if(solver_o == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     switch(nd){
     case 0: flag = 1; break;
     case 1: flag = PyArg_ParseTuple(args,"i", &n1); break;
     case 2: flag = PyArg_ParseTuple(args,"ii", &n1, &n2); break;
     case 3:
	  /* odeiv */
	  flag = 1; break;
     default:
	  line = __LINE__;
	  pygsl_error("Only 1 or two for number of problem_dimensions implemented!",
		    __FILE__, line, GSL_ESANITY);
	  goto fail;
     }
     if (0==flag){
	  /* Successful parsing of the arguments ?*/
	  line = __LINE__ - 1;
	  goto fail;
     }
     if (n1<=0) {
	  PyErr_SetString(PyExc_RuntimeError, "dimension 1 must be >0");
	  line = __LINE__ - 2;
	  goto fail;
     }
     if (n2<=0) {
	  PyErr_SetString(PyExc_RuntimeError, "dimension 2 must be >0");
	  line = __LINE__ - 2;
	  goto fail;
     }
     {
	  void *tmp = alloc->alloc;
	  switch(nd){
	  case 0:
	       solver_o->solver =  ((void_a_t)(tmp))(alloc->type);
	       break;
	  case 1: 
	       solver_o->solver =  (void *) ((void_an_t) tmp)(alloc->type, n1); 
	       break;
	  case 2: 
	       DEBUG_MESS(3, "Allocating solver with N=%d, p=%d", n1, n2);
	       solver_o->solver =  (void *) ((void_anp_t) tmp)(alloc->type, n1, n2); 
	       break;
	  case 3:
	       /* odeiv handles that itself */
	       break;
	  default:
	       pygsl_error("Only 0,1 or 2 for number of problem_dimensions implemented!",
			 __FILE__, __LINE__, GSL_ESANITY);
	       goto fail;
	  }
     }

     switch(nd){
     case 1:
     case 2:
	  if(solver_o->solver == NULL){
	       line = __LINE__ - 1;
	       goto fail;
	  }
	  break;	  
     default:
	  ;
     }
     switch(nd){
     case 1:
	  solver_o->problem_dimensions[0] = n1;
	  break;
     case 2:
	  solver_o->problem_dimensions[0] = n2;
	  solver_o->problem_dimensions[1] = n1;
	  break;
     default:
	  ;
     }

     solver_o->cache = (struct pygsl_array_cache *) calloc(PyGSL_SOLVER_N_ARRAYS, sizeof(struct pygsl_array_cache));
     if(solver_o->cache == NULL){
	  PyErr_NoMemory();
	  line = __LINE__ - 1;
	  goto fail;
     }

     FUNC_MESS_END();
     return (PyObject *) solver_o;
 fail:
     FUNC_MESS("Fail");
     DEBUG_MESS(3, "line was %d", line);
     PyGSL_add_traceback(module, __FILE__, __FUNCTION__, line);
     Py_XDECREF(solver_o);
     return NULL;
}

#if 0
static PyObject *
_PyGSL_solver_np_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc)
{
    FUNC_MESS_BEGIN();     
    return PyGSL_solver_dn_init(self, args, alloc, 2);
    FUNC_MESS_END();
}

static PyObject *
PyGSL_solver_n_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc)
{
     FUNC_MESS_BEGIN();
     return PyGSL_solver_dn_init(self, args, alloc, 1);
     FUNC_MESS_END();
}

static PyObject *
_PyGSL_solver_1_init(PyObject *self, PyObject *args, const solver_alloc_struct * alloc)
{
     FUNC_MESS_BEGIN();
     return PyGSL_solver_dn_init(self, args, alloc, 0);
     FUNC_MESS_END();
}

#endif
static double
PyGSL_gsl_function(double x, void * params)
{
     PyGSL_solver *s;
     double result = GSL_NAN;
     int flag = GSL_EFAILED;

     FUNC_MESS_BEGIN();
     assert(params);
     assert(PyGSL_solver_check((PyObject *) params));
     s = (PyGSL_solver *) params;

     flag = PyGSL_function_wrap_helper(x, &result, NULL, s->cbs[0], s->args, __FUNCTION__);
     if(flag == GSL_SUCCESS){
	  FUNC_MESS_END();
	  return result;
     }
     FUNC_MESS("Fail");
     if(s->isset)
	  longjmp(s->buffer, flag);
     DEBUG_MESS(2, "Found an error of %d but could not jump!", flag);
     return GSL_NAN;
}

static double
PyGSL_gsl_function_df(double x, void * params)
{
     PyGSL_solver *s;
     double result = GSL_NAN;
     int flag = GSL_EFAILED;

     FUNC_MESS_BEGIN();
     assert(params);
     assert(PyGSL_solver_check((PyObject *) params));
     s = (PyGSL_solver *) params;

     flag = PyGSL_function_wrap_helper(x, &result, NULL, s->cbs[1], s->args, __FUNCTION__);
     if(flag == GSL_SUCCESS){
	  FUNC_MESS_END();
	  return result;
     }
     FUNC_MESS("Fail");
     if(s->isset)
	  longjmp(s->buffer, flag);
     DEBUG_MESS(2, "Found an error of %d but could not jump!", flag);
     return GSL_NAN;
}

static void
PyGSL_gsl_function_fdf(double x, void * params,  double *f, double *df)
{
     PyGSL_solver *s;
     int flag = GSL_EFAILED;

     FUNC_MESS_BEGIN();
     assert(params);
     assert(PyGSL_solver_check((PyObject *) params));
     s = (PyGSL_solver *) params;

     assert(s->cbs[2]);
     assert(PyCallable_Check(s->cbs[2]));
     flag = PyGSL_function_wrap_helper(x, f, df, s->cbs[2], s->args, __FUNCTION__);
     if(flag == GSL_SUCCESS){
	  FUNC_MESS_END();
	  return;
     }
     FUNC_MESS("Fail");
     if(s->isset)
	  longjmp(s->buffer, flag);
     DEBUG_MESS(2, "Found an error of %d but could not jump!", flag);
     *f = GSL_NAN;
     *df = GSL_NAN;
}

static PyObject* 
PyGSL_solver_set_f(PyGSL_solver *self, PyObject *pyargs, PyObject *kw, 
		    void *fptr, int isfdf) 
{

     PyObject *f = NULL, *df = NULL, *fdf = NULL, *args = Py_None;
     int  flag=GSL_EFAILED;
     void *c_sys = NULL;
     gsl_function * f_sys = NULL;
     gsl_function_fdf * fdf_sys = NULL;
     double x0, lower=0, upper=0;

     static const char *f_kwlist[]   = {"f", "x0", "upper", "lower", "args", NULL};
     static const char *fdf_kwlist[] = {"f", "df", "fdf", "x0", "args", NULL};

     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     if (self->solver == NULL) {
	  pygsl_error("Got a NULL Pointer of min.f",  filename, __LINE__ - 3, GSL_EFAULT);
	  return NULL;
     }

     assert(pyargs);
     /* arguments PyFunction, Parameters, start Vector, step Vector */

     if(isfdf == 0){
	  if (0==PyArg_ParseTupleAndKeywords(pyargs,kw,"OdddO", (char **)f_kwlist, &f,
					     &x0,&lower,&upper,&args))
	       return NULL;
     }else{
	  if (0==PyArg_ParseTupleAndKeywords(pyargs,kw,"OOOdO", (char **)fdf_kwlist, &f,&df,&fdf,
					     &x0,&args))
	       return NULL;	  
     }

     if(!PyCallable_Check(f)){
	  pygsl_error("First argument must be callable",  filename, __LINE__ - 3, GSL_EBADFUNC);
	  return NULL;	  
     }
     if(isfdf == 1){
	  if(!PyCallable_Check(df)){
	       pygsl_error("Second argument must be callable",  filename, __LINE__ - 3, GSL_EBADFUNC);
	       return NULL;	  
	  }
	  if(!PyCallable_Check(fdf)){
	       pygsl_error("Third argument must be callable",  filename, __LINE__ - 3, GSL_EBADFUNC);
	       return NULL;	  
	  }
     }
     if (self->c_sys != NULL) {
	  /* free the previous function and args */
	  c_sys = self->c_sys;
     } else {
	  /* allocate function space */
	  if(isfdf == 0)
	       c_sys=calloc(1, sizeof(gsl_function));
	  else
	       c_sys=calloc(1, sizeof(gsl_function_fdf));
	  if (c_sys == NULL) {
	       pygsl_error("Could not allocate the object for the minimizer function", 
			 filename, __LINE__ - 3, GSL_ENOMEM);
	       goto fail;
	  }
     }
     DEBUG_MESS(3, "Everything allocated args = %p", args);
     /* add new function  and parameters */

     if(PyGSL_solver_func_set(self, args, f, df, fdf) != GSL_SUCCESS)
	  goto fail;
     
     /* initialize the function struct */
     if(isfdf == 0){
          f_sys = (gsl_function *) c_sys;
	  f_sys->function=PyGSL_gsl_function;
	  f_sys->params=(void*)self;
     }else{
	  fdf_sys = (gsl_function_fdf *) c_sys;
	  fdf_sys->f=PyGSL_gsl_function;
	  fdf_sys->df=PyGSL_gsl_function_df;
	  fdf_sys->fdf=PyGSL_gsl_function_fdf;
	  fdf_sys->params=(void*)self;	  
     }
     DEBUG_MESS(3, "Setting jmp buffer isset = % d", self->isset);
     if((flag = setjmp(self->buffer)) == 0){
	  self->isset = 1;
	  if(isfdf == 0){
	       DEBUG_MESS(3, "Calling f isfdf = %d", isfdf);
	       flag = ((set_m_ddd_t) fptr)(self->solver, f_sys, x0, lower, upper);
	  }else{
	       DEBUG_MESS(3, "Calling fdf isfdf = %d", isfdf);
	       flag = ((set_m_d_t) fptr)(self->solver, f_sys, x0);
	  }
	  if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS){
	       goto fail;
	  }
     } else {
	  goto fail;
     }
     DEBUG_MESS(4, "Set evaluated. flag = %d", flag);
     self->c_sys = c_sys;
     self->set_called = 1;

     self->isset = 0;
     Py_INCREF(Py_None);
     FUNC_MESS_END();
     return Py_None;

     
 fail:
     FUNC_MESS("Fail");
     PyGSL_ERROR_FLAG(flag);
     self->isset = 0;
     return NULL;
     
}

/*
 * Set the solver
 */
PyGSL_API_EXTERN PyObject *
PyGSL_solver_n_set(PyGSL_solver *self, PyObject *pyargs, PyObject *kw, 
		    const struct pygsl_solver_n_set * info)
{
     int n, flag=GSL_EFAILED;
     PyGSL_array_index_t stride;

     PyObject *args=Py_None, *f=NULL, *df=NULL, *fdf=NULL, *x;
     PyArrayObject * xa = NULL;
     gsl_vector_view gsl_x;
     void *c_sys;
     int line = -1;
     static const char *f_kwlist[]   = {"f", "x0", "args", NULL};
     static const char *fdf_kwlist[] = {"f", "df", "fdf", "x0", "args", NULL};

     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     if (self->solver == NULL) {
	  pygsl_error("solver ==  NULL at solver_n_set", filename, __LINE__ - 3, GSL_EFAULT);
	  return NULL;
     }	  

     /* arguments PyFunction, Parameters, start Vector, step Vector */
     if(info->is_fdf == 0){
	  if (0==PyArg_ParseTupleAndKeywords(pyargs, kw, "OO|O", (char **)f_kwlist, 
					     &f, &x, &args))
	       return NULL;
     } else {
	  if (0==PyArg_ParseTupleAndKeywords(pyargs, kw, "OOOO|O", (char **)fdf_kwlist, 
					     &f, &df, &fdf, &x, &args))
	       return NULL;
     }

     n=self->problem_dimensions[0];
     DEBUG_MESS(3, "len(x) should be %d", n);
     xa  = PyGSL_vector_check(x, n, PyGSL_DARRAY_INPUT(2), &stride, NULL);
     if (xa == NULL){
	  line = __LINE__ - 2;
	  goto fail;
     }
     gsl_x = gsl_vector_view_array_with_stride((double *)PyArray_DATA(xa), stride,
					       PyArray_DIM(xa, 0));


     if (self->c_sys != NULL) {
	  c_sys = self->c_sys;
     } else {
	  c_sys=info->c_sys;
     }

     if(PyGSL_solver_func_set(self, args, f, df, fdf) != GSL_SUCCESS){
	  line = __LINE__ - 1;
	  goto fail;
     }
     if((flag = setjmp(self->buffer)) == 0){
	  self->isset = 1;	  
	  flag = info->set(self->solver, c_sys, &gsl_x.vector);
	  if((PyGSL_ERROR_FLAG(flag)) != GSL_SUCCESS){
	  line = __LINE__ - 2;
	       goto fail;
	  }
     }else{
	  line = __LINE__ - 9;
	  goto fail;
     }
     self->c_sys = c_sys;
     self->isset = 0;
     Py_DECREF(xa);
     self->set_called = 1;

     Py_INCREF(Py_None);
     FUNC_MESS_END();
     return Py_None;

 fail:
     PyGSL_add_traceback(module, __FILE__, __FUNCTION__, line);
     self->isset = 0;
     self->set_called = 0;
     Py_XDECREF(xa);
     return NULL;
     
}

static PyObject* 
PyGSL_solver_ret_size_t(PyGSL_solver *self, PyObject *args, 
			size_t_m_t func)
{
     size_t result;
     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     result=func(self->solver);
     FUNC_MESS_END();
     return (PyObject *) PyLong_FromLong((long)result);
} 

static PyObject* 
PyGSL_solver_ret_int(PyGSL_solver *self, PyObject *args, 
			int_m_t func)
{
     int result;
     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     result=func(self->solver);
     FUNC_MESS_END();
     return (PyObject *) PyLong_FromLong((long)result);
} 

static PyObject* 
PyGSL_solver_ret_double(PyGSL_solver *self, PyObject *args, 
			double_m_t func)
{
     double result;
     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     result=func(self->solver);
     FUNC_MESS_END();
     return (PyObject *) PyFloat_FromDouble(result);
} 

static PyObject* 
PyGSL_solver_ret_vec(PyGSL_solver *self, PyObject *args, 
		     ret_vec func)
{
     gsl_vector *result;
     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));     
     result=func(self->solver);
     if(result == NULL)
	  PyGSL_ERROR_NULL("Could not retrive vector ...", GSL_ESANITY);
     FUNC_MESS_END();
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(result);
} 

/*
 * evaluates a C function taking an vector and a double as input and returning a status.
 */
static PyObject*
PyGSL_solver_vd_i(PyObject * self, PyObject *args, int_f_vd_t func)
{
     PyObject *g=NULL;
     PyArrayObject *ga=NULL;
     gsl_vector_view gradient;

     double epsabs;
     int flag = GSL_EFAILED;
     PyGSL_array_index_t stride_recalc=-1;

     FUNC_MESS_BEGIN();
     if (0==PyArg_ParseTuple(args,"Od", &g, &epsabs))
	  return NULL;     

     ga  = PyGSL_vector_check(g, -1, PyGSL_DARRAY_INPUT(1), &stride_recalc, NULL);
     if (ga == NULL){
	  PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__ - 1);
	  return NULL;
     }
     gradient = gsl_vector_view_array_with_stride((double *)(PyArray_DATA(ga)), stride_recalc,
						  PyArray_DIM(ga, 0));
     flag = func(&gradient.vector, epsabs);
     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);
     
}

static PyObject *
PyGSL_solver_vvdd_i(PyObject * self, PyObject * args, int_f_vvdd_t func)
{
     int flag;
     int line = -1;
     double epsabs, epsrel;
     PyObject *dx_o, *x_o;
     PyArrayObject *dx_a = NULL, *x_a = NULL;
     gsl_vector_view x, dx;
     PyGSL_array_index_t dimension, stride;

     FUNC_MESS_BEGIN();
     if(!PyArg_ParseTuple(args, "OOdd", &dx_o, &x_o, &epsabs, &epsrel))
	  return NULL;

     dx_a = PyGSL_vector_check(dx_o, -1, PyGSL_DARRAY_INPUT(1), &stride, NULL);
     if(dx_a == NULL){
	  line = __LINE__ - 4;
	  goto fail;
     }
     
     dimension = PyArray_DIM(dx_a, 0);
     dx = gsl_vector_view_array_with_stride((double *)(PyArray_DATA(dx_a)), stride, PyArray_DIM(dx_a, 0));

     x_a = PyGSL_vector_check(x_o, dimension, PyGSL_DARRAY_CINPUT(2), &stride,  NULL);
     if(x_a == NULL){
	  line = __LINE__ - 4;
	  goto fail;
     }
     x = gsl_vector_view_array_with_stride((double *)(PyArray_DATA(x_a)), stride, PyArray_DIM(x_a, 0));
     flag = func(&(dx.vector), &(x.vector), epsabs, epsrel);
     Py_DECREF(x_a);
     Py_DECREF(dx_a);
     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);

 fail:
     PyGSL_add_traceback(module, __FILE__, __FUNCTION__, line);
     Py_XDECREF(dx_a);
     Py_XDECREF(x_a);
     return NULL;
}

PyGSL_API_EXTERN int
PyGSL_Callable_Check(PyObject *f, const char * myname)
{
     FUNC_MESS_BEGIN();
     if(!PyCallable_Check(f)){
	  char str[256];
	  snprintf(str, 254, "Callback named %s is not callable!", myname);
	  PyGSL_ERROR(str, GSL_EINVAL);
     }
     FUNC_MESS_END();
     return GSL_SUCCESS;
}

PyGSL_API_EXTERN int
PyGSL_solver_func_set(PyGSL_solver *self, PyObject *args, PyObject *f,
		       PyObject *df, PyObject *fdf)
{
     int flag = GSL_EFAILED;
     if(df){
	  if(!fdf)
	       PyGSL_ERROR("If df is given, fdf must be given as well!", GSL_ESANITY);
	  Py_XDECREF(self->cbs[1]);
	  Py_XDECREF(self->cbs[2]);
	  self->cbs[1] = NULL;	  
	  self->cbs[2] = NULL;
     }
     Py_XDECREF(self->args);
     Py_XDECREF(self->cbs[0]);
     self->args = NULL;     
     self->cbs[0] = NULL;

     
     DEBUG_MESS(3, "args = %p", (void *) args);
     self->args=args; Py_XINCREF(args);
     assert(f);
     if((flag = PyGSL_CALLABLE_CHECK(f, "f")) != GSL_SUCCESS)
	  return flag;
     self->cbs[0] = f; Py_INCREF(f);
     if(df){
	  assert(fdf);
	  if((flag = PyGSL_CALLABLE_CHECK(df, "df")) != GSL_SUCCESS)
	       return flag;
	  if((flag = PyGSL_CALLABLE_CHECK(fdf, "fdf")) != GSL_SUCCESS)
	       return flag;
	  self->cbs[1]=df;   Py_INCREF(df);
	  self->cbs[2]=fdf;  Py_INCREF(fdf);
     }
     return GSL_SUCCESS;     
}


static PyObject *
PyGSL_solver_GetSet(PyObject *self, PyObject *args, void * address, enum PyGSL_GETSET_typemode mode)
{
     PyObject *ret=NULL, *input=NULL;
     unsigned long ultmp;
     int flag;

     if (!PyArg_ParseTuple(args, "|O", &input))	  
	  return NULL;

     if(input){
	  switch(mode){
	  case PyGSL_MODE_DOUBLE:
	       flag = PyGSL_PYFLOAT_TO_DOUBLE(input, (double *) address, NULL);
	       break;
	  case PyGSL_MODE_INT:
	       flag = PyGSL_PYINT_TO_INT(input, (int *) address, NULL);
	       break;
	  case PyGSL_MODE_SIZE_T:
	       flag = PyGSL_PYLONG_TO_ULONG(input, &ultmp, NULL);
	       *((size_t *) address) = ultmp;
	       break;
	  default:
	       PyGSL_ERROR_NULL("Unknown mode",GSL_ESANITY);
	  }
	  if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS)
	       return NULL;

	  Py_INCREF(Py_None);
	  ret = Py_None;
	  return ret;     
     }

     switch(mode){
     case PyGSL_MODE_DOUBLE:
	  ret = PyFloat_FromDouble(*((double *) address));
	  break;
     case PyGSL_MODE_INT:
          ret = PyLong_FromLong(((long) *((int *) address)));
	  break;
     case PyGSL_MODE_SIZE_T:
	  ret = PyLong_FromUnsignedLong(((unsigned long) *((size_t *) address)));
	  break;
     default:
	  PyGSL_ERROR_NULL("Unknown mode",GSL_ESANITY);
     }
     return ret;
}

#ifdef ONEFILE

#include "chars.c"
#include "function_helpers2.c"
/* 

#include "function_helpers.c"
#include "integrate.c"
#include "odeiv.c"
#include "roots.c"
#include "minimize.c"
#include "multifit_nlin.c"
#include "multimin.c" 
#include "multiroot.c"
*/
#endif /* ONEFILE */

static PyMethodDef solverMethods[] = {
     {NULL, NULL, 0, NULL}
};

void
init_api(void)
{
     FUNC_MESS_BEGIN();    
     PyGSL_API[PyGSL_solver_type_NUM            ] = (void *) &PyGSL_solver_pytype     ;
     PyGSL_API[PyGSL_solver_ret_int_NUM         ] = (void *) &PyGSL_solver_ret_int    ;
     PyGSL_API[PyGSL_solver_ret_double_NUM      ] = (void *) &PyGSL_solver_ret_double ;
     PyGSL_API[PyGSL_solver_ret_size_t_NUM      ] = (void *) &PyGSL_solver_ret_size_t ;
     PyGSL_API[PyGSL_solver_ret_vec_NUM         ] = (void *) &PyGSL_solver_ret_vec    ;
     PyGSL_API[PyGSL_solver_dn_init_NUM         ] = (void *) &PyGSL_solver_dn_init    ;
     PyGSL_API[PyGSL_solver_vd_i_NUM            ] = (void *) &PyGSL_solver_vd_i       ;
     PyGSL_API[PyGSL_solver_vvdd_i_NUM          ] = (void *) &PyGSL_solver_vvdd_i     ;
     PyGSL_API[PyGSL_Callable_Check_NUM         ] = (void *) &PyGSL_Callable_Check    ;
     PyGSL_API[PyGSL_solver_func_set_NUM        ] = (void *) &PyGSL_solver_func_set   ;
     PyGSL_API[PyGSL_function_wrap_OnOn_On_NUM  ] = (void *) &PyGSL_function_wrap_OnOn_On;
     PyGSL_API[PyGSL_function_wrap_On_O_NUM     ] = (void *) &PyGSL_function_wrap_On_O;
     PyGSL_API[PyGSL_function_wrap_Op_On_NUM    ] = (void *) &PyGSL_function_wrap_Op_On;
     PyGSL_API[PyGSL_function_wrap_Op_Opn_NUM   ] = (void *) &PyGSL_function_wrap_Op_Opn;
     PyGSL_API[PyGSL_function_wrap_Op_On_Opn_NUM] = (void *) &PyGSL_function_wrap_Op_On_Opn;
     PyGSL_API[PyGSL_solver_n_set_NUM           ] = (void *) &PyGSL_solver_n_set      ;
     PyGSL_API[PyGSL_solver_set_f_NUM           ] = (void *) &PyGSL_solver_set_f      ;
     PyGSL_API[PyGSL_solver_getset_NUM          ] = (void *) &PyGSL_solver_GetSet     ;
     FUNC_MESS_END();
}



#ifdef PyGSL_PY3K
static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "pygsl.testing.solver",
        NULL,
        -1,
        solverMethods,
        NULL,
        NULL,
        NULL,
        NULL
};
#endif 

#ifdef __cplusplus
extern "C"
#endif


#ifdef PyGSL_PY3K
PyObject *PyInit_solver(void)
#define RETVAL m
#else /* PyGSL_PY3K */
DL_EXPORT(void) initsolver(void)
#define RETVAL
#endif /* PyGSL_PY3K */
{
  PyObject* m = NULL, *dict, *item;

  FUNC_MESS_BEGIN();

#ifdef PyGSL_PY3K
  m = PyModule_Create(&moduledef);
#else /* PyGSL_PY3K */    
  m=Py_InitModule("solver", solverMethods);
#endif /* PyGSL_PY3K */

  if(m == NULL)
    goto fail;

  init_pygsl();

  /* init multimin type */
#ifdef PyGSL_PY3K
  if (PyType_Ready(&PyGSL_solver_pytype) < 0)
		return NULL;
#else /* PyGSL_PY3K */    
  PyGSL_solver_pytype.ob_type  = &PyType_Type;
#endif /* PyGSL_PY3K */

  init_api();


  module = m;

  Py_INCREF((PyObject*)&PyGSL_solver_pytype);

  dict = PyModule_GetDict(m);
  if(!dict)
       goto fail;
  
  if (!(item = PyGSL_string_from_string((char*)PyGSL_solver_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 RETVAL;

 fail:
  FUNC_MESS("FAIL");
  return RETVAL;

}
