/* ode-initval2/driver.c
 *
 * Copyright (C) 2009, 2010 Tuomo Keskitalo
 *
 * 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.
 */

/* Driver routine for odeiv2. This is a wrapper for low level GSL
   functions that allows a simple interface to step, control and
   evolve layers.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_odeiv2.h>
#include <gsl/gsl_machine.h>

static gsl_odeiv2_driver *
driver_alloc (const gsl_odeiv2_system * sys, const double hstart,
              const gsl_odeiv2_step_type * T)
{
  /* Allocates and initializes an ODE driver system. Step and evolve
     objects are allocated here, but control object is allocated in
     another function.
   */

  gsl_odeiv2_driver *state =
    (gsl_odeiv2_driver *) malloc (sizeof (gsl_odeiv2_driver));

  if (state == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate space for driver state",
                      GSL_ENOMEM);
    }

  if (sys == NULL)
    {
      GSL_ERROR_NULL ("gsl_odeiv2_system must be defined", GSL_EINVAL);
    }

  {
    const size_t dim = sys->dimension;

    if (dim == 0)
      {
        GSL_ERROR_NULL
          ("gsl_odeiv2_system dimension must be a positive integer",
           GSL_EINVAL);
      }

    state->sys = sys;

    state->s = gsl_odeiv2_step_alloc (T, dim);

    if (state->s == NULL)
      {
        free (state);
        GSL_ERROR_NULL ("failed to allocate step object", GSL_ENOMEM);
      }

    state->e = gsl_odeiv2_evolve_alloc (dim);
  }

  if (state->e == NULL)
    {
      gsl_odeiv2_step_free (state->s);
      free (state);
      GSL_ERROR_NULL ("failed to allocate evolve object", GSL_ENOMEM);
    }

  if (hstart > 0.0 || hstart < 0.0)
    {
      state->h = hstart;
    }
  else
    {
      GSL_ERROR_NULL ("invalid hstart", GSL_EINVAL);
    }

  state->h = hstart;
  state->hmin = 0.0;
  state->hmax = GSL_DBL_MAX;
  state->nmax = 0;
  state->n = 0;
  state->c = NULL;

  return state;
}

int
gsl_odeiv2_driver_set_hmin (gsl_odeiv2_driver * d, const double hmin)
{
  /* Sets minimum allowed step size fabs(hmin) for driver. It is
     required that hmin <= fabs(h) <= hmax. */

  if ((fabs (hmin) > fabs (d->h)) || (fabs (hmin) > d->hmax))
    {
      GSL_ERROR_NULL ("hmin <= fabs(h) <= hmax required", GSL_EINVAL);
    }

  d->hmin = fabs (hmin);

  return GSL_SUCCESS;
}

int
gsl_odeiv2_driver_set_hmax (gsl_odeiv2_driver * d, const double hmax)
{
  /* Sets maximum allowed step size fabs(hmax) for driver. It is
     required that hmin <= fabs(h) <= hmax. */

  if ((fabs (hmax) < fabs (d->h)) || (fabs (hmax) < d->hmin))
    {
      GSL_ERROR_NULL ("hmin <= fabs(h) <= hmax required", GSL_EINVAL);
    }

  if (hmax > 0.0 || hmax < 0.0)
    {
      d->hmax = fabs (hmax);
    }
  else
    {
      GSL_ERROR_NULL ("invalid hmax", GSL_EINVAL);
    }

  return GSL_SUCCESS;
}

int
gsl_odeiv2_driver_set_nmax (gsl_odeiv2_driver * d,
                            const unsigned long int nmax)
{
  /* Sets maximum number of allowed steps (nmax) for driver */

  d->nmax = nmax;

  return GSL_SUCCESS;
}

gsl_odeiv2_driver *
gsl_odeiv2_driver_alloc_y_new (const gsl_odeiv2_system * sys,
                               const gsl_odeiv2_step_type * T,
                               const double hstart,
                               const double epsabs, const double epsrel)
{
  /* Initializes an ODE driver system with control object of type y_new. */

  gsl_odeiv2_driver *state = driver_alloc (sys, hstart, T);

  if (state == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate driver object", GSL_ENOMEM);
    }

  if (epsabs >= 0.0 && epsrel >= 0.0)
    {
      state->c = gsl_odeiv2_control_y_new (epsabs, epsrel);

      if (state->c == NULL)
        {
          gsl_odeiv2_driver_free (state);
          GSL_ERROR_NULL ("failed to allocate control object", GSL_ENOMEM);
        }
    }
  else
    {
      gsl_odeiv2_driver_free (state);
      GSL_ERROR_NULL ("epsabs and epsrel must be positive", GSL_EINVAL);
    }

  /* Distribute pointer to driver object */

  gsl_odeiv2_step_set_driver (state->s, state);
  gsl_odeiv2_evolve_set_driver (state->e, state);
  gsl_odeiv2_control_set_driver (state->c, state);

  return state;
}

gsl_odeiv2_driver *
gsl_odeiv2_driver_alloc_yp_new (const gsl_odeiv2_system * sys,
                                const gsl_odeiv2_step_type * T,
                                const double hstart,
                                const double epsabs, const double epsrel)
{
  /* Initializes an ODE driver system with control object of type yp_new. */

  gsl_odeiv2_driver *state = driver_alloc (sys, hstart, T);

  if (state == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate driver object", GSL_ENOMEM);
    }

  if (epsabs >= 0.0 && epsrel >= 0.0)
    {
      state->c = gsl_odeiv2_control_yp_new (epsabs, epsrel);

      if (state->c == NULL)
        {
          gsl_odeiv2_driver_free (state);
          GSL_ERROR_NULL ("failed to allocate control object", GSL_ENOMEM);
        }
    }
  else
    {
      gsl_odeiv2_driver_free (state);
      GSL_ERROR_NULL ("epsabs and epsrel must be positive", GSL_EINVAL);
    }

  /* Distribute pointer to driver object */

  gsl_odeiv2_step_set_driver (state->s, state);
  gsl_odeiv2_evolve_set_driver (state->e, state);
  gsl_odeiv2_control_set_driver (state->c, state);

  return state;
}

gsl_odeiv2_driver *
gsl_odeiv2_driver_alloc_standard_new (const gsl_odeiv2_system * sys,
                                      const gsl_odeiv2_step_type * T,
                                      const double hstart,
                                      const double epsabs,
                                      const double epsrel, const double a_y,
                                      const double a_dydt)
{
  /* Initializes an ODE driver system with control object of type
     standard_new.
   */

  gsl_odeiv2_driver *state = driver_alloc (sys, hstart, T);

  if (state == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate driver object", GSL_ENOMEM);
    }

  if (epsabs >= 0.0 && epsrel >= 0.0)
    {
      state->c =
        gsl_odeiv2_control_standard_new (epsabs, epsrel, a_y, a_dydt);

      if (state->c == NULL)
        {
          gsl_odeiv2_driver_free (state);
          GSL_ERROR_NULL ("failed to allocate control object", GSL_ENOMEM);
        }
    }
  else
    {
      gsl_odeiv2_driver_free (state);
      GSL_ERROR_NULL ("epsabs and epsrel must be positive", GSL_EINVAL);
    }

  /* Distribute pointer to driver object */

  gsl_odeiv2_step_set_driver (state->s, state);
  gsl_odeiv2_evolve_set_driver (state->e, state);
  gsl_odeiv2_control_set_driver (state->c, state);

  return state;
}

gsl_odeiv2_driver *
gsl_odeiv2_driver_alloc_scaled_new (const gsl_odeiv2_system * sys,
                                    const gsl_odeiv2_step_type * T,
                                    const double hstart,
                                    const double epsabs, const double epsrel,
                                    const double a_y, const double a_dydt,
                                    const double scale_abs[])
{
  /* Initializes an ODE driver system with control object of type
     scaled_new.
   */

  gsl_odeiv2_driver *state = driver_alloc (sys, hstart, T);

  if (state == NULL)
    {
      GSL_ERROR_NULL ("failed to allocate driver object", GSL_ENOMEM);
    }

  if (epsabs >= 0.0 && epsrel >= 0.0)
    {
      state->c = gsl_odeiv2_control_scaled_new (epsabs, epsrel, a_y, a_dydt,
                                                scale_abs, sys->dimension);

      if (state->c == NULL)
        {
          gsl_odeiv2_driver_free (state);
          GSL_ERROR_NULL ("failed to allocate control object", GSL_ENOMEM);
        }
    }
  else
    {
      gsl_odeiv2_driver_free (state);
      GSL_ERROR_NULL ("epsabs and epsrel must be positive", GSL_EINVAL);
    }

  /* Distribute pointer to driver object */

  gsl_odeiv2_step_set_driver (state->s, state);
  gsl_odeiv2_evolve_set_driver (state->e, state);
  gsl_odeiv2_control_set_driver (state->c, state);

  return state;
}

int
gsl_odeiv2_driver_apply (gsl_odeiv2_driver * d, double *t,
                         const double t1, double y[])
{
  /* Main driver function that evolves the system from t to t1. In
     beginning vector y contains the values of dependent variables at
     t. This function returns values at t=t1 in y. In case of
     unrecoverable error, y and t contains the values after the last
     successful step.
   */

  int sign = 0;
  d->n = 0;

  /* Determine integration direction sign */

  if (d->h > 0.0)
    {
      sign = 1;
    }
  else
    {
      sign = -1;
    }

  /* Check that t, t1 and step direction are sensible */

  if (sign * (t1 - *t) < 0.0)
    {
      GSL_ERROR_NULL
        ("integration limits and/or step direction not consistent",
         GSL_EINVAL);
    }

  /* Evolution loop */

  while (sign * (t1 - *t) > 0.0)
    {
      int s = gsl_odeiv2_evolve_apply (d->e, d->c, d->s, d->sys,
                                       t, t1, &(d->h), y);

      if (s != GSL_SUCCESS)
        {
          return s;
        }

      /* Check for maximum allowed steps */

      if ((d->nmax > 0) && (d->n > d->nmax))
        {
          return GSL_EMAXITER;
        }

      /* Set step size if maximum size is exceeded */

      if (fabs (d->h) > d->hmax)
        {
          d->h = sign * d->hmax;
        }

      /* Check for too small step size */

      if (fabs (d->h) < d->hmin)
        {
          return GSL_ENOPROG;
        }

      d->n++;
    }

  return GSL_SUCCESS;
}

int
gsl_odeiv2_driver_apply_fixed_step (gsl_odeiv2_driver * d, double *t,
                                    const double h, const unsigned long int n,
                                    double y[])
{
  /* Alternative driver function that evolves the system from t using
   * n steps of size h. In the beginning vector y contains the values
   * of dependent variables at t. This function returns values at t =
   * t + n * h in y. In case of an unrecoverable error, y and t
   * contains the values after the last successful step.
   */

  unsigned long int i;
  d->n = 0;

  /* Evolution loop */

  for (i = 0; i < n; i++)
    {
      int s = gsl_odeiv2_evolve_apply_fixed_step (d->e, d->c, d->s, d->sys,
                                                  t, h, y);

      if (s != GSL_SUCCESS)
        {
          return s;
        }

      d->n++;
    }

  return GSL_SUCCESS;
}

int
gsl_odeiv2_driver_reset (gsl_odeiv2_driver * d)
{
  /* Reset the driver. Resets evolve and step objects. */

  {
    int s = gsl_odeiv2_evolve_reset (d->e);

    if (s != GSL_SUCCESS)
      {
        return s;
      }
  }

  {
    int s = gsl_odeiv2_step_reset (d->s);

    if (s != GSL_SUCCESS)
      {
        return s;
      }
  }

  return GSL_SUCCESS;
}

int
gsl_odeiv2_driver_reset_hstart (gsl_odeiv2_driver * d, const double hstart)
{
  /* Resets current driver and sets initial step size to hstart */

  gsl_odeiv2_driver_reset (d);

  if ((d->hmin > fabs (hstart)) || (fabs (hstart) > d->hmax))
    {
      GSL_ERROR_NULL ("hmin <= fabs(h) <= hmax required", GSL_EINVAL);
    }

  if (hstart > 0.0 || hstart < 0.0)
    {
      d->h = hstart;
    }
  else
    {
      GSL_ERROR_NULL ("invalid hstart", GSL_EINVAL);
    }

  return GSL_SUCCESS;
}

void
gsl_odeiv2_driver_free (gsl_odeiv2_driver * state)
{
  if (state->c != NULL)
    {
      gsl_odeiv2_control_free (state->c);
    }

  gsl_odeiv2_evolve_free (state->e);
  gsl_odeiv2_step_free (state->s);
  free (state);
}


#include <pygsl/solver.h>
#include <gsl/gsl_roots.h>

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

static const char root_f_type_name[] = "F-RootSolver";
static const char root_fdf_type_name[] = "FdF-RootSolver";
static const char root_f_root_doc[] = "Get the value of F";
static const char root_set_f_doc[] = "";
static const char root_set_fdf_doc[] = "";
static const char root_fdf_root_doc[] = "";
static const char root_x_lower_doc  [] = "Get the lower bound of x";
static const char root_x_upper_doc  [] = "Get the upper bound of x";

static PyObject*
PyGSL_root_f_root(PyGSL_solver *self, PyObject *args)
{
     return PyGSL_solver_ret_double(self, args, (double_m_t) gsl_root_fsolver_root);
}

static PyObject*
PyGSL_root_fdf_root(PyGSL_solver *self, PyObject *args)
{
     return PyGSL_solver_ret_double(self, args, (double_m_t) gsl_root_fdfsolver_root);
}

static PyObject*
PyGSL_root_x_lower(PyGSL_solver *self, PyObject *args)
{
     return PyGSL_solver_ret_double(self, args, (double_m_t) gsl_root_fsolver_x_lower);
}

static PyObject*
PyGSL_root_x_upper(PyGSL_solver *self, PyObject *args)
{
     return PyGSL_solver_ret_double(self, args, (double_m_t) gsl_root_fsolver_x_upper);
}

static PyObject*
PyGSL_root_solver_test_interval(PyGSL_solver * self, PyObject *args)
{
     double epsabs, epsrel;
     gsl_root_fsolver *s = (gsl_root_fsolver *) self->solver;
     if(!PyArg_ParseTuple(args, "dd", &epsabs, &epsrel))
	  return NULL;
     return PyInt_FromLong(gsl_root_test_interval(s->x_lower, s->x_upper, epsabs, epsrel));
}

static PyObject*
PyGSL_root_set_f(PyGSL_solver *self, PyObject *args, PyObject *kw)
{
     return PyGSL_solver_set_f(self, args, kw, (void *)gsl_root_fsolver_set, 0);
}

static PyObject*
PyGSL_root_set_fdf(PyGSL_solver *self, PyObject *args, PyObject *kw)
{
     return PyGSL_solver_set_f(self, args, kw,  (void *)gsl_root_fdfsolver_set, 1);
}

static PyMethodDef PyGSL_root_fmethods[] = {
     {"root",      (PyCFunction)PyGSL_root_f_root,     METH_NOARGS, (char *)root_f_root_doc},
     {"x_lower",   (PyCFunction)PyGSL_root_x_lower,    METH_NOARGS, (char *)root_x_lower_doc},
     {"x_upper",   (PyCFunction)PyGSL_root_x_upper,    METH_NOARGS, (char *)root_x_upper_doc},
     {"set",       (PyCFunction)PyGSL_root_set_f,      METH_VARARGS|METH_KEYWORDS, (char *)root_set_f_doc},
     {"test_interval",(PyCFunction)PyGSL_root_solver_test_interval, METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}           /* sentinel */
};
static PyMethodDef PyGSL_root_fdfmethods[] = {
     {"root",      (PyCFunction)PyGSL_root_fdf_root,     METH_NOARGS, (char *)root_fdf_root_doc},
     {"set",       (PyCFunction)PyGSL_root_set_fdf,      METH_VARARGS|METH_KEYWORDS, (char *)root_set_fdf_doc},
     {NULL, NULL, 0, NULL}           /* sentinel */
};

const struct _SolverStatic
root_solver_f = {{(void_m_t) gsl_root_fsolver_free,
		  /* gsl_multimin_fminimizer_restart */  (void_m_t) NULL,
		  (name_m_t) gsl_root_fsolver_name,
		  (int_m_t) gsl_root_fsolver_iterate},
		 1, PyGSL_root_fmethods, root_f_type_name},
root_solver_fdf = {{(void_m_t) gsl_root_fdfsolver_free,
		    /* gsl_multimin_fminimizer_restart */  (void_m_t) NULL,
		    (name_m_t) gsl_root_fdfsolver_name,
		     (int_m_t) gsl_root_fdfsolver_iterate},
		   3, PyGSL_root_fdfmethods, root_fdf_type_name};

static PyObject*
PyGSL_root_f_init(PyObject *self, PyObject *args,
		 const gsl_root_fsolver_type * type)
{

     PyObject *tmp=NULL;
     solver_alloc_struct s = {type, (void_an_t) gsl_root_fsolver_alloc,
			      &root_solver_f};
     FUNC_MESS_BEGIN();
     tmp = PyGSL_solver_dn_init(self, args, &s, 0);
     FUNC_MESS_END();
     return tmp;
}

static PyObject*
PyGSL_root_fdf_init(PyObject *self, PyObject *args,
		 const gsl_root_fdfsolver_type * type)
{

     PyObject *tmp=NULL;
     solver_alloc_struct s = {type, (void_an_t) gsl_root_fdfsolver_alloc,
			      &root_solver_fdf};
     FUNC_MESS_BEGIN();
     tmp = PyGSL_solver_dn_init(self, args, &s, 0);
     FUNC_MESS_END();
     return tmp;
}

#define AROOT_F(name)                                                  \
static PyObject* PyGSL_root_init_ ## name (PyObject *self, PyObject *args)\
{                                                                             \
     PyObject *tmp = NULL;                                                    \
     FUNC_MESS_BEGIN();                                                       \
     tmp = PyGSL_root_f_init(self, args,  gsl_root_fsolver_ ## name); \
     if (tmp == NULL){                                                        \
	  PyGSL_add_traceback(module, __FILE__, __FUNCTION__, __LINE__); \
     }                                                                        \
     FUNC_MESS_END();                                                         \
     return tmp;                                                              \
}
#define AROOT_FDF(name)                                                  \
static PyObject* PyGSL_root_init_ ## name (PyObject *self, PyObject *args)\
{                                                                             \
     PyObject *tmp = NULL;                                                    \
     FUNC_MESS_BEGIN();                                                       \
     tmp = PyGSL_root_fdf_init(self, args,  gsl_root_fdfsolver_ ## name); \
     if (tmp == NULL){                                                        \
	  PyGSL_add_traceback(module, __FILE__, __FUNCTION__, __LINE__); \
     }                                                                        \
     FUNC_MESS_END();                                                         \
     return tmp;                                                              \
}
AROOT_F(bisection)
AROOT_F(falsepos)
AROOT_F(brent)
AROOT_FDF(newton)
AROOT_FDF(secant)
AROOT_FDF(steffenson)

static PyObject*
PyGSL_root_test_delta(PyObject * self, PyObject *args)
{
     double x_lower, x_upper, epsabs, epsrel;
     if(!PyArg_ParseTuple(args, "dddd", &x_lower, &x_upper, &epsabs, &epsrel))
	  return NULL;
     return PyInt_FromLong(gsl_root_test_delta(x_lower, x_upper, epsabs, epsrel));
}

static PyObject*
PyGSL_root_test_interval(PyObject * self, PyObject *args)
{
     double x_lower, x_upper, epsabs, epsrel;
     if(!PyArg_ParseTuple(args, "dddd", &x_lower, &x_upper, &epsabs, &epsrel))
	  return NULL;
     return PyInt_FromLong(gsl_root_test_interval(x_lower, x_upper, epsabs, epsrel));
}

static const char PyGSL_roots_module_doc [] = "XXX Missing ";
static PyMethodDef mMethods[] = {
     /* solver */
     {"bisection",  PyGSL_root_init_bisection, METH_NOARGS, NULL},
     {"falsepos",  PyGSL_root_init_falsepos, METH_NOARGS, NULL},
     {"brent",  PyGSL_root_init_brent, METH_NOARGS, NULL},
     {"newton",  PyGSL_root_init_newton, METH_NOARGS, NULL},
     {"secant",  PyGSL_root_init_secant, METH_NOARGS, NULL},
     {"steffenson",  PyGSL_root_init_steffenson, METH_NOARGS, NULL},
     /* functions */
     {"test_delta",  PyGSL_root_test_delta, METH_VARARGS, NULL},
     {"test_interval",  PyGSL_root_test_interval, METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}
};

void
initroots(void)
{
     PyObject* m, *dict, *item;
     FUNC_MESS_BEGIN();

     m=Py_InitModule("roots", mMethods);
     module = m;
     assert(m);
     dict = PyModule_GetDict(m);
     if(!dict)
	  goto fail;

     init_pygsl()
     import_pygsl_solver();
     assert(PyGSL_API);

     if (!(item = PyString_FromString((char*)PyGSL_roots_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();

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


/*
 * File:   rng.h
 * Author: fedro
 *
 * Created on November 3, 2014, 5:33 PM
 */

#ifndef RNG_H
#define	RNG_H

#include <gsl/gsl_rng.h>

class RNG
{
public:
    RNG(const gsl_rng_type* rng_type, const int seed=0)
    {
        rng_ = gsl_rng_alloc(rng_type);
        gsl_rng_set(rng_, seed);
    }
    RNG(const int seed=0)
    {
        rng_ = gsl_rng_alloc(gsl_rng_default);
        gsl_rng_set(rng_, seed);
    }
    virtual ~RNG()
    {
        gsl_rng_free(rng_);
    }
    gsl_rng* get() { return rng_; }
private:
    gsl_rng* rng_;
};

#endif	/* RNG_H */


/***************************************************************************/
/*                                                                         */
/* LU_decomp.h - LU Decomposition class for mruby                          */
/* Copyright (C) 2015 Paolo Bosetti                                        */
/* paolo[dot]bosetti[at]unitn.it                                           */
/* Department of Industrial Engineering, University of Trento              */
/*                                                                         */
/* This library is free software.  You can redistribute it and/or          */
/* modify it under the terms of the GNU GENERAL PUBLIC LICENSE 2.0.        */
/*                                                                         */
/* This library 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           */
/* Artistic License 2.0 for more details.                                  */
/*                                                                         */
/* See the file LICENSE                                                    */
/*                                                                         */
/***************************************************************************/

#ifndef LU_DECOMP_H
#define LU_DECOMP_H

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <time.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_permutation.h>

#include "mruby.h"
#include "mruby/variable.h"
#include "mruby/string.h"
#include "mruby/data.h"
#include "mruby/class.h"
#include "mruby/value.h"
#include "mruby/array.h"
#include "mruby/numeric.h"
#include "mruby/compile.h"

#define E_LU_DECOMP_ERROR (mrb_class_get(mrb, "LUDecompError"))

/***********************************************\
 LU Decomposition
\***********************************************/

typedef struct {
  gsl_matrix *mat;
  gsl_permutation *p;
  int sgn;
  size_t size;
} lu_decomp_data_s;

// Garbage collector handler, for play_data struct
// if play_data contains other dynamic data, free it too!
// Check it with GC.start
void lu_decomp_destructor(mrb_state *mrb, void *p_);

// Utility function for getting the struct out of the wrapping IV @data
void mrb_lu_decomp_get_data(mrb_state *mrb, mrb_value self, lu_decomp_data_s **data);

void mrb_gsl_lu_decomp_init(mrb_state *mrb);

#endif // LU_DECOMP_H


/* Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 *
 * Copyright 2021 INRIA.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

#ifndef SiconosBlas_H
#define SiconosBlas_H
#include "SiconosConfig.h"

#if defined(__cplusplus)
extern "C"
{
#endif

// tells include-what-you-use to keep this file
// and not to suggest cblas.h or alike.
// IWYU pragma: begin_exports
#if defined(HAS_MKL_CBLAS)
#include <mkl_cblas.h>
#elif defined(HAS_MATLAB_BLAS)
#include <blas.h>
#define cblas_daxpy daxpy
#define cblas_dcopy dcopy
#define cblas_ddot ddot
#define cblas_dgemm dgemm
#define cblas_dgemv dgemv
#define cblas_dnrm2 dnrm2
#define cblas_dscal dscal
#else
#include <cblas.h>
#endif
// IWYU pragma: end_exports

#ifdef __cplusplus
}
#undef restrict
#define restrict __restrict
#endif

static inline double* NMD_row_rmajor(double* restrict mat, unsigned ncols, unsigned rindx)
{
  return &mat[rindx*ncols];
}

static inline void NMD_copycol_rmajor(int nrows, double* col, double* restrict mat, int ncols, unsigned cindx)
{
  cblas_dcopy(nrows, col, 1, &mat[cindx], ncols);
}

static inline void NMD_dense_gemv(int nrows, int ncols, double alpha, double* restrict mat, double* restrict y, double beta, double* restrict x)
{
  cblas_dgemv(CblasColMajor, CblasTrans, ncols, nrows, alpha, mat, ncols, y, 1, beta, x, 1);
}

#endif // SiconosBlas_H


#ifndef PVIZUTIL_H
#define PVIZUTIL_H

#include <math.h>
#include <string>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

#include <H5Cpp.h>

using namespace std;
using namespace H5;

class PvizUtil
{
public:
	static void split(const int n, const int nsplit, int* counts, int* offsets)
	{
		if (nsplit == 0)
			throw "Cannot split by zero";

		// i = 0
		offsets[0] = 0;
		counts[0] = ceil(n/(double)nsplit);

		// i > 0
		for (int i = 1; i < nsplit; i++)
		{
			offsets[i] = offsets[i-1] + counts[i-1];
			counts[i] = ceil((n - offsets[i])/(double)(nsplit - i));
		}
	};

	static bool H5CheckDataset(const std::string &filename,
							   const std::string &datasetname)
	{
		bool rtn = true;
		try {
			H5::H5File file(filename, H5F_ACC_RDONLY );
			H5::DataSet dataset = file.openDataSet(datasetname);
		}
		catch (H5::FileIException e)
		{
			rtn = false;
		}

		return rtn;
	};

	static void H5GetDatasetDim(const std::string &filename,
								const std::string &datasetname,
								const int &rank,
								hsize_t *dim)
	{
		H5::H5File file( filename, H5F_ACC_RDONLY );
		H5::DataSet dataset = file.openDataSet(datasetname);

		/*
		 * Get dataspace of the dataset.
		 */
		H5::DataSpace dataspace = dataset.getSpace();

		/*
		 * Get the number of dimensions in the dataspace.
		 */
		if (rank != dataspace.getSimpleExtentNdims())
		{
			throw std::invalid_argument("Rank mismatch!!");
		}

		dataspace.getSimpleExtentDims(dim, NULL);
	};

	static void H5ReadByOffset(const std::string &filename,
							   const std::string &datasetname,
							   const H5::DataType &mem_type,
							   const size_t &rowOffset,
							   const size_t &size,
							   void *m)
	{
		H5::H5File file(filename, H5F_ACC_RDONLY);
		H5::DataSet dataset = file.openDataSet(datasetname);
		H5::DataSpace dataspace = dataset.getSpace();

		checkDataset(dataset, mem_type, rowOffset, size, m);

		hsize_t      offset[1];   // hyperslab offset in the file
		hsize_t      count[1];    // size of the hyperslab in the file
		offset[0] = rowOffset;
		count[0]  = size;

		dataspace.selectHyperslab(H5S_SELECT_SET, count, offset);

		H5::DataSpace memspace(1, count);

		dataset.read(m, mem_type, memspace, dataspace);
	};

	static void H5ReadByRow(const std::string &filename,
							const std::string &datasetname,
							const H5::DataType &mem_type,
							const size_t &rowOffset,
							const size_t &size1,
							const size_t &size2,
							void *m)
	{
		H5::H5File file(filename, H5F_ACC_RDONLY);
		H5::DataSet dataset = file.openDataSet(datasetname);
		H5::DataSpace dataspace = dataset.getSpace();

		checkDataset(dataset, mem_type, rowOffset, size1, size2, m);

		hsize_t      offset[2];   // hyperslab offset in the file
		hsize_t      count[2];    // size of the hyperslab in the file
		offset[0] = rowOffset;
		offset[1] = 0;
		count[0]  = size1;
		count[1]  = size2;

		dataspace.selectHyperslab(H5S_SELECT_SET, count, offset);

		H5::DataSpace memspace(2, count);

		dataset.read(m, mem_type, memspace, dataspace);
	};

	static void H5WriteByRow(const std::string &filename,
							 const std::string &datasetname,
							 const H5::DataType &mem_type,
							 const size_t &rowOffset,
							 const size_t &size1,
							 const size_t &size2,
							 const gsl_matrix *m)
	{
		H5::H5File file(filename, H5F_ACC_RDWR);
		H5::DataSet dataset = file.openDataSet(datasetname);
		H5::DataSpace dataspace = dataset.getSpace();

		checkDataset(dataset, mem_type, rowOffset, size1, size2, m);

		hsize_t      offset[2];   // hyperslab offset in the file
		hsize_t      count[2];    // size of the hyperslab in the file
		offset[0] = rowOffset;
		offset[1] = 0;
		count[0]  = size1;
		count[1]  = size2;

		dataspace.selectHyperslab(H5S_SELECT_SET, count, offset);

		H5::DataSpace memspace(2, count);

		dataset.write(m, mem_type, memspace, dataspace);
	};

protected:
	static void checkDataset(const H5::DataSet& dataset,
							 const H5::DataType &mem_type,
							 const size_t rowOffset,
							 const size_t &size1,
							 const size_t &size2,
							 const void *m)
	{
		// Check datatype
		/*
		H5T_class_t class_t = dataset.getTypeClass();

		if ((class_t != H5T_FLOAT) && (class_t != H5T_INTEGER))
		{
			throw std::runtime_error("Type mismatch!!");
		}
		*/

		H5::DataType dtype = dataset.getDataType();
		if (!(dtype == mem_type))
		{
			throw std::runtime_error("Data type mismatch!!");
		}

		H5::DataSpace dataspace = dataset.getSpace();

		// Check rank
		if (dataspace.getSimpleExtentNdims() != 2)
		{
			throw std::runtime_error("Rank mismatch!!");
		}

		// Check dimension
		hsize_t dims_out[2];
		dataspace.getSimpleExtentDims(dims_out, NULL);
		if (dims_out[0] < size1)
		{
			throw std::runtime_error("Dimension mismatch!!");
		}

		if (dims_out[1] != size2)
		{
			throw std::runtime_error("Dimension mismatch!!");
		}

		// Check parameter
		if (dims_out[0] <= rowOffset)
		{
			throw std::runtime_error("Offset is out of range!!");
		}

		if (dims_out[0] < rowOffset + size1)
		{
			throw std::runtime_error("Count is out of range!!");
		}
	};

	static void checkDataset(const H5::DataSet& dataset,
							 const H5::DataType &mem_type,
							 const size_t rowOffset,
							 const size_t &size,
							 const void *m)
	{
		// Check datatype
		/*
		 H5T_class_t class_t = dataset.getTypeClass();

		 if ((class_t != H5T_FLOAT) && (class_t != H5T_INTEGER))
		 {
		 throw std::runtime_error("Type mismatch!!");
		 }
		 */

		H5::DataType dtype = dataset.getDataType();
		if (!(dtype == mem_type))
		{
			throw std::runtime_error("Data type mismatch!!");
		}

		H5::DataSpace dataspace = dataset.getSpace();

		// Check rank
		if (dataspace.getSimpleExtentNdims() != 1)
		{
			throw std::runtime_error("Rank mismatch!!");
		}

		// Check dimension
		hsize_t dims_out[1];
		dataspace.getSimpleExtentDims(dims_out, NULL);
		if (dims_out[0] < size)
		{
			throw std::runtime_error("Dimension mismatch!!");
		}

		// Check parameter
		if (dims_out[0] <= rowOffset)
		{
			throw std::runtime_error("Offset is out of range!!");
		}

		if (dims_out[0] < rowOffset + size)
		{
			throw std::runtime_error("Count is out of range!!");
		}
	};
};

#endif //PVIZUTIL_H


//  This random generator is a C++ wrapper for the GNU Scientific Library
//  Copyright (C) 2001 Torbjorn Vik

//  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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#ifndef __min_fminimizer_h
#define __min_fminimizer_h

#include <gsl/gsl_errno.h>
#include <gsl/gsl_min.h>

namespace gsl{

//! Derive this class provide a user defined function for minimisation
struct min_f
{
	//! This operator must be overridden
	virtual double operator()(const double& x)=0;

	//! This is the function gsl calls to optimize f
	static double f(double x, void *p)
	{
		return (*(min_f *)p)(x);
	}
};

//! Class for minimizing one dimensional functions.
/*!
  Usage:
       - Create with optional minimize type
	   - Set with function object and inital bounds
	   - Loop the  iterate function until convergence or maxIterations (extra facility)

	   - Recover minimum and bounds
 */
class min_fminimizer
{
 public:
	//! choose between gsl_min_fminimizer_goldensection and gsl_min_fminimizer_brent
	min_fminimizer(const gsl_min_fminimizer_type* type=gsl_min_fminimizer_brent) : s(NULL), maxIterations(100), isSet(false)
	{
		s=gsl_min_fminimizer_alloc(type);
		nIterations=0;
		if (!s)
		{
			//error
			//cout << "ERROR Couldn't allocate memory for minimizer" << endl;
			//throw ?
			exit(-1);
		}
	}
	~min_fminimizer(){if (s) gsl_min_fminimizer_free(s);}
	//! returns GSL_FAILURE if the interval does not contain a minimum
	int set(min_f& function, double minimum, double x_lower, double x_upper)
	{
		isSet=false;
		f.function = &function.f;
		f.params = &function;
		int status=	gsl_min_fminimizer_set(s, &f, minimum, x_lower, x_upper);
		if (!status)
		{
			isSet=true;
			nIterations=0;
		}
		return status;
	}
	int set_with_values(min_f& function,
						double minimum, double f_minimum,
						double x_lower,double f_lower,
						double x_upper, double f_upper)
	{
		isSet=false;
		f.function = &function.f;
		f.params = &function;
		int status=	gsl_min_fminimizer_set_with_values(s, &f, minimum, f_minimum, x_lower, f_lower, x_upper, f_upper);
		if (!status)
		{
			isSet=true;
			nIterations=0;
		}
		return status;
	}
	int iterate()
	{
		assert_set();
		int status=gsl_min_fminimizer_iterate(s);
		nIterations++;
		if (status==GSL_FAILURE)
			isConverged=true;
		return status;
	}
	double minimum(){assert_set();return gsl_min_fminimizer_minimum(s);}
	double x_upper(){assert_set();return gsl_min_fminimizer_x_upper(s);}
	double x_lower(){assert_set();return gsl_min_fminimizer_x_lower(s);}
	void SetMaxIterations(int n){maxIterations=n;}
	int GetNIterations(){return nIterations;}
	bool is_converged(){if (nIterations>=maxIterations) return true; if (isConverged) return true; return false;}
	//string name() const;

 private:
	void assert_set(){if (!isSet)exit(-1);} // Old problem of error handling: TODO

	bool isSet;
	bool isConverged;
	int nIterations;
	int maxIterations;
	gsl_min_fminimizer* s;
	gsl_function f;
};
};	 // namespace gsl

#endif //__min_fminimizer_h


#ifndef TESTING_COMMON
#define TESTING_COMMON

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

// header ------------------------------------------
#include "testing_setting.h"
#undef _IEEE_

#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <cstdio>
#include <cstdlib>
#include <cmath>

#include <string.h>
#include <stdint.h>
#include <float.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <sys/time.h>
#include <unistd.h>
#include <getopt.h>
#include <ctype.h>
#include <sys/utsname.h>

#if defined (MKL)
//#include <mkl.h>
#include <mkl_cblas.h>
#include <mkl_trans.h>
//#include <mkl_spblas.h> // conflict with Bebop SMC
#else
#include <cblas.h>
#endif

#if defined (ARM)
#define __float128 long double
#endif

#if defined (CUBLAS)
#include <cublas_v2.h>
#elif defined (CUOZBLAS)
#include <cuozblas.h>
#elif defined (OZBLAS)
#include <ozblas.h>
#endif

#if defined (CUDA)
#include <cuda.h>
#include <cuda_runtime_api.h>
#include <cublas_v2.h>
#include <cusparse_v2.h>
#endif

#if defined (CSRMV) || defined (CG)
extern "C" {
#include <bebop/smc/sparse_matrix.h>
#include <bebop/smc/sparse_matrix_ops.h>
#include <bebop/smc/csr_matrix.h>
}
#endif

#if !defined (MPLAPACK)
#define mpreal double
#endif

// ===========================================================
// common for FP_TYPE
#if defined (PREC_Q_D) || defined (PREC_Q_S)
#define PREC_Q
#elif defined (PREC_D_D) || defined (PREC_D_S)
#define PREC_D
#elif defined (PREC_S_S) || defined (PREC_S_D)
#define PREC_S
#elif defined (PREC_32F_PDT_32F)
#define PREC_S
#elif defined (PREC_32F_TC_32F)
#define PREC_S
#elif defined (PREC_32F_TC_32TF)
#define PREC_S
#elif defined (PREC_16F_TC_32F)
#define PREC_H
#elif defined (PREC_16F_PDT_32F)
#define PREC_H
#elif defined (PREC_16F_TC_16F)
#define PREC_H
#elif defined (PREC_16F_PDT_16F)
#define PREC_H
#elif defined (PREC_64F_PDT_64F)
#define PREC_D
#elif defined (PREC_64F_TC_64F)
#define PREC_D
#endif
// ===========================================================

#if defined (MPLAPACK)
#if defined (PREC_Q)
#define MPFR_WANT_FLOAT128
#define _Float128 __float128 // this is needed if MPFR is >= 4.1.0
#endif
#include <mplapack/mpblas_mpfr.h>
#include <mpfr.h> // this is after above
#endif
#undef _Float128

// +++++++++++++++++++++
// FP_TYPE == binary128
// +++++++++++++++++++++
#if defined (PREC_Q)
#define FP_TYPE	__float128
#if defined (ARM)
#define FABS fabs
#define FP_MAX LDBL_MAX
#define FP_MIN LDBL_MIN
#define F_PI 3.1415926535897932384626433832795029L;
#else
#include <quadmath.h>
#define FABS fabsq
#define FP_MAX FLT128_MAX
#define FP_MIN FLT128_MIN
#define F_PI M_PIq
#endif
#if defined (MPLAPACK)
#include <mplapack/mpblas__Float128.h>
#endif

// +++++++++++++++++++++
// FP_TYPE == double-double
// +++++++++++++++++++++
#elif defined (PREC_DD)
#define FP_TYPE dd_real
#define FABS fabs
#if defined (MPLAPACK)
#include <qd/dd_real.h>
#include <mplapack/mpblas_dd.h>
#endif
#define FP_MAX DBL_MAX
#define FP_MIN DBL_MIN
#define F_PI dd_real::_pi

// +++++++++++++++++++++
// FP_TYPE == binary64
// +++++++++++++++++++++
#elif defined (PREC_D)
#define FP_TYPE	double
#define FABS fabs
#define FP_MAX DBL_MAX
#define FP_MIN DBL_MIN
#define F_PI M_PI

// +++++++++++++++++++++
// FP_TYPE == binary32
// +++++++++++++++++++++
#elif defined (PREC_S)
#define FP_TYPE	float
#define FABS fabs
#define FP_MAX FLT_MAX
#define FP_MIN FLT_MIN
#define F_PI M_PI

// +++++++++++++++++++++
// FP_TYPE == binary16
// +++++++++++++++++++++
#elif defined (PREC_H)
#include "half-2.1.0/half.hpp"
using half_float::half;
#define FP_TYPE	half
#define FABS fabs
#define FP_MAX 65504.
#define FP_MIN (2.e-14)-1
#define F_PI M_PI

#endif

// ===========================================================
// reference BLAS
#if defined (MPLAPACK)
#define refRdot												Rdot
#define refRgemv(tran,m,n,al,A,lda,X,ix,bt,Y,iy)			Rgemv(&tran,m,n,al,A,lda,X,ix,bt,Y,iy)
#define refRgemm(tranA,tranB,m,n,k,al,A,lda,B,ldb,bt,C,ldc) Rgemm(&tranA,&tranB,m,n,k,al,A,lda,B,ldb,bt,C,ldc)
#else
#define refRdot	cblas_ddot
#define refRgemv(tran,m,n,al,A,lda,X,ix,bt,Y,iy)			cblas_dgemv(CblasColMajor,ToCblasOp(tran),m,n,al,A,lda,X,ix,bt,Y,iy)
#define refRgemm(tranA,tranB,m,n,k,al,A,lda,B,ldb,bt,C,ldc) cblas_dgemm(CblasColMajor,ToCblasOp(tranA),ToCblasOp(tranB),m,n,k,al,A,lda,B,ldb,bt,C,ldc)
#endif

// ===========================================================
// argment transformation
#if defined (OZBLAS)
#define trgRdot(ha,n,x,ix,y,iy)								rdot(&ha,n,x,ix,y,iy)
#define trgRnrm2(ha,n,x,ix)									rnrm2(&ha,n,x,ix)
#define trgRaxpy(ha,n,a,x,ix,y,iy)							raxpy(&ha,n,a,x,ix,y,iy)
#define	trgRgemv(ha,ta,m,n,al,a,la,x,ix,bt,y,iy)			rgemv(&ha,ta,m,n,al,a,la,x,ix,bt,y,iy)
#define trgRgemm(ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)	rgemm(&ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)
#define	trgRcsrmv(ha,ta,m,n,nnz,al,da,a,ci,rp,x,bt,y)		rcsrmv(&ha,ta,m,n,nnz,al,da,a,ci,rp,x,bt,y)
#define	trgRcg(ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)			rcg(&ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)
#elif defined (CUOZBLAS)
#define trgRdot(ha,n,x,ix,y,iy,r)							rdot(&ha,n,x,ix,y,iy,r)
#define trgRnrm2(ha,n,x,ix,r)								rnrm2(&ha,n,x,ix,r)
#define trgRaxpy(ha,n,a,x,ix,y,iy)							raxpy(&ha,n,a,x,ix,y,iy)
#define	trgRgemv(ha,ta,m,n,al,a,la,x,ix,bt,y,iy)			rgemv(&ha,ta,m,n,al,a,la,x,ix,bt,y,iy)
#define trgRgemm(ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)	rgemm(&ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)
#define	trgRcsrmv(ha,ta,m,n,nnz,al,da,a,ci,rp,x,bt,y)		rcsrmv(&ha,ta,m,n,nnz,al,da,a,ci,rp,x,bt,y)
#define	trgRcg(ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)			rcg(&ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)
#elif defined (CUBLAS) && defined (GEMMEX)
#define trgRgemm(ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)	cublasGemmEx(ha,ToCublasOp(ta),ToCublasOp(tb),m,n,k,&al,A,DATA_TYPE_A,lda,B,DATA_TYPE_B,ldb,&bt,C,DATA_TYPE_C,ldc,COMP_TYPE,ALGO_TYPE)
#elif defined (CUBLAS)
#define trgRdot(ha,n,x,ix,y,iy,r)							rdot(ha,n,x,ix,y,iy,r)
#define trgRnrm2(ha,n,x,ix)									rnrm2(ha,n,x,ix)
#define trgRaxpy(ha,n,a,x,ix,y,iy)							raxpy(ha,n,a,x,ix,y,iy)
#define	trgRgemv(ha,ta,m,n,al,a,la,x,ix,bt,y,iy)			rgemv(ha,ToCublasOp(ta),m,n,&al,a,la,x,ix,&bt,y,iy)
#define trgRgemm(ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)	rgemm(ha,ToCublasOp(ta),ToCublasOp(tb),m,n,k,&al,A,lda,B,ldb,&bt,C,ldc)
#define	trgRcsrmv(ha,ta,m,n,nnz,al,da,a,ci,rp,x,bt,y)		rcsrmv(ha,ToCusparseOp(ta),m,n,nnz,al,da,a,ci,rp,x,bt,y)
//#define	trgRcg(ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)			rcg(&ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)
#elif defined (MPBLAS)
#define trgRdot(ha,n,x,ix,y,iy)								rdot(n,x,ix,y,iy)
#define trgRnrm2(ha,n,x,ix)									rnrm2(n,x,ix)
#define trgRaxpy(ha,n,a,x,ix,y,iy)							raxpy(ha,n,a,x,ix,y,iy)
#define	trgRgemv(ha,ta,m,n,al,a,la,x,ix,bt,y,iy)			rgemv(&ta,m,n,al,a,la,x,ix,bt,y,iy)
#define trgRgemm(ha,ta,tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)	rgemm(&ta,&tb,m,n,k,al,A,lda,B,ldb,bt,C,ldc)
//#define	trgRcg(ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)			rcg(&ha,ta,n,nnz,da,a,ci,rp,b,x,itr,tol)
#endif

// ===========================================================
#if defined (CUOZBLAS)
#define ozblasHandle_t		cuozblasHandle_t
#define ozblasCreate 		cuozblasCreate
#define ozblasDestroy		cuozblasDestroy
#endif

// ================================================
#if defined (PREC_Q_D)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<__float128,double>
#define rdot			cuozblasRdot<__float128,double>
#define rnrm2			cuozblasRnrm2<__float128,double>
#define raxpy			cuozblasRaxpy<__float128>
#define rgemv			cuozblasRgemv<__float128,double>
#define rgemm			cuozblasRgemm<__float128,double>
#define rcsrmv			cuozblasRcsrmv<__float128,double>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<__float128,double>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<__float128,double>
#define rdot			ozblasRdot<__float128,double>
#define rnrm2			ozblasRnrm2<__float128,double>
#define rgemv			ozblasRgemv<__float128,double>
#define rgemm			ozblasRgemm<__float128,double>
#define rcsrmv			ozblasRcsrmv<__float128,double>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<__float128,double>
#elif defined (MPBLAS)
#define	rdot			Rdot
#define	rgemv			Rgemv
#define	rgemm			Rgemm
#endif

// ================================================
#elif defined (PREC_Q_S)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<__float128,float>
#define rdot			cuozblasRdot<__float128,float>
#define rnrm2			cuozblasRnrm2<__float128,float>
#define raxpy			cuozblasRaxpy<__float128>
#define rgemv			cuozblasRgemv<__float128,float>
#define rgemm			cuozblasRgemm<__float128,float>
#define rcsrmv			cuozblasRcsrmv<__float128,float>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<__float128,float>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<__float128,float>
#define rdot			ozblasRdot<__float128,float>
#define rnrm2			ozblasRnrm2<__float128,float>
#define rgemv			ozblasRgemv<__float128,float>
#define rgemm			ozblasRgemm<__float128,float>
#define rcsrmv			ozblasRcsrmv<__float128,float>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<__float128,float>
#endif

// ================================================
#elif defined (PREC_D_S)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<double,float>
#define rdot			cuozblasRdot<double,float>
#define rnrm2			cuozblasRnrm2<double,float>
#define raxpy			cuozblasRaxpy<double>
#define rgemv			cuozblasRgemv<double,float>
#define rgemm			cuozblasRgemm<double,float>
#define rcsrmv			cuozblasRcsrmv<double,float>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<double,float>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<double,float>
#define rdot			ozblasRdot<double,float>
#define rnrm2			ozblasRnrm2<double,float>
#define rgemv			ozblasRgemv<double,float>
#define rgemm			ozblasRgemm<double,float>
#define rcsrmv			ozblasRcsrmv<double,float>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<double,float>
#endif

// ================================================
#elif defined (PREC_D_D)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<double,double>
#define rdot			cuozblasRdot<double,double>
#define rnrm2			cuozblasRnrm2<double,double>
#define raxpy			cuozblasRaxpy<double>
#define rgemv			cuozblasRgemv<double,double>
//#define rgemm			cuozblasDgemm
#define rgemm			cuozblasRgemm<double,double>
#define rcsrmv			cuozblasRcsrmv<double,double>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<double,double>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<double,double>
#define rdot			ozblasRdot<double,double>
#define rnrm2			ozblasRnrm2<double,double>
#define rgemv			ozblasRgemv<double,double>
//#define rgemv			ozblasDgemv
#define rgemm			ozblasRgemm<double,double>
#define rcsrmv			ozblasRcsrmv<double,double>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<double,double>
#elif defined (CUBLAS)
//#define rcg			cublasDcg
#define rdot			cublasDdot
#define rnrm2			cublasDnrm2
#define rgemv			cublasDgemv
#define rgemm			cublasDgemm
//#define rcsrmv		cublasDcsrmv
#endif

// ================================================
#elif defined (PREC_S_S)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<float,float>
#define rdot			cuozblasRdot<float,float>
#define rnrm2			cuozblasRnrm2<float,float>
#define raxpy			cuozblasRaxpy<float>
#define rgemv			cuozblasRgemv<float,float>
#define rgemm			cuozblasRgemm<float,float>
#define rcsrmv			cuozblasRcsrmv<float,float>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<float,float>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<float,float>
#define rdot			ozblasRdot<float,float>
#define rnrm2			ozblasRnrm2<float,float>
#define rgemv			ozblasRgemv<float,float>
#define rgemm			ozblasRgemm<float,float>
#define rcsrmv			ozblasRcsrmv<float,float>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<float,float>
#elif defined (CUBLAS)
//#define rcg			cublasScg
#define rdot			cublasSdot
#define rnrm2			cublasSnrm2
#define rgemv			cublasSgemv
#define rgemm			cublasSgemm
//#define rcsrmv		cublasScsrmv
#endif

// ================================================
#elif defined (PREC_S_D)
#if defined (CUOZBLAS)
#define rcg				cuozblasRcg<float,double>
#define rdot			cuozblasRdot<float,double>
#define rnrm2			cuozblasRnrm2<float,double>
#define raxpy			cuozblasRaxpy<float>
#define rgemv			cuozblasRgemv<float,double>
#define rgemm			cuozblasRgemm<float,double>
#define rcsrmv			cuozblasRcsrmv<float,double>
#define rcsrmvSplitA	cuozblasRcsrmvSplitA<float,double>
#elif defined (OZBLAS)
#define rcg				ozblasRcg<float,double>
#define rdot			ozblasRdot<float,double>
#define rnrm2			ozblasRnrm2<float,double>
#define rgemv			ozblasRgemv<float,double>
#define rgemm			ozblasRgemm<float,double>
#define rcsrmv			ozblasRcsrmv<float,double>
#define rcsrmvSplitA	ozblasRcsrmvSplitA<float,double>
#endif

// ================================================
#elif defined (PREC_DD)
#if defined (MPBLAS)
#define	rdot			Rdot
#define	rgemv			Rgemv
#define	rgemm			Rgemm
#endif

#endif

// ================================================
// GemmEx
#if defined (CUBLAS) && defined (GEMMEX)
#define rgemm			cublasGemmEx
// ----------------------------
#if defined (PREC_32F_PDT_32F)
#define DATA_TYPE_A CUDA_R_32F
#define DATA_TYPE_B CUDA_R_32F
#define DATA_TYPE_C CUDA_R_32F
#define COMP_TYPE CUBLAS_COMPUTE_32F_PEDANTIC
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_32F_TC_32F)
#define DATA_TYPE_A CUDA_R_32F
#define DATA_TYPE_B CUDA_R_32F
#define DATA_TYPE_C CUDA_R_32F
#define COMP_TYPE CUBLAS_COMPUTE_32F
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_32F_TC_32TF)
#define DATA_TYPE_A CUDA_R_32F
#define DATA_TYPE_B CUDA_R_32F
#define DATA_TYPE_C CUDA_R_32F
#define COMP_TYPE CUBLAS_COMPUTE_32F_FAST_TF32
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_16F_TC_32F)
#define DATA_TYPE_A CUDA_R_16F
#define DATA_TYPE_B CUDA_R_16F
#define DATA_TYPE_C CUDA_R_16F
#define COMP_TYPE CUBLAS_COMPUTE_32F
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_16F_PDT_32F)
#define DATA_TYPE_A CUDA_R_16F
#define DATA_TYPE_B CUDA_R_16F
#define DATA_TYPE_C CUDA_R_16F
#define COMP_TYPE CUBLAS_COMPUTE_32F_PEDANTIC
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_16F_TC_16F)
#define DATA_TYPE_A CUDA_R_16F
#define DATA_TYPE_B CUDA_R_16F
#define DATA_TYPE_C CUDA_R_16F
#define COMP_TYPE CUBLAS_COMPUTE_16F
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_16F_PDT_16F)
#define DATA_TYPE_A CUDA_R_16F
#define DATA_TYPE_B CUDA_R_16F
#define DATA_TYPE_C CUDA_R_16F
#define COMP_TYPE CUBLAS_COMPUTE_16F_PEDANTIC
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_64F_PDT_64F)
#define DATA_TYPE_A CUDA_R_64F
#define DATA_TYPE_B CUDA_R_64F
#define DATA_TYPE_C CUDA_R_64F
#define COMP_TYPE CUBLAS_COMPUTE_64F_PEDANTIC
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#elif defined (PREC_64F_TC_64F)
#define DATA_TYPE_A CUDA_R_64F
#define DATA_TYPE_B CUDA_R_64F
#define DATA_TYPE_C CUDA_R_64F
#define COMP_TYPE CUBLAS_COMPUTE_64F
#define ALGO_TYPE CUBLAS_GEMM_DEFAULT
// ----------------------------
#endif
#endif

#endif


/* monte/gsl_monte_miser.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Michael Booth
 * Copyright (C) 2009 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.
 */

/* Author: MJB */

#ifndef __GSL_MONTE_MISER_H__
#define __GSL_MONTE_MISER_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 <gsl/gsl_rng.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_plain.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

typedef struct {
  size_t min_calls;
  size_t min_calls_per_bisection;
  double dither;
  double estimate_frac;
  double alpha;
  size_t dim;
  int estimate_style;
  int depth;
  int verbose;
  double * x;
  double * xmid;
  double * sigma_l;
  double * sigma_r;
  double * fmax_l;
  double * fmax_r;
  double * fmin_l;
  double * fmin_r;
  double * fsum_l;
  double * fsum_r;
  double * fsum2_l;
  double * fsum2_r;
  size_t * hits_l;
  size_t * hits_r;
} gsl_monte_miser_state;

GSL_FUN int gsl_monte_miser_integrate(gsl_monte_function * f,
                              const double xl[], const double xh[],
                              size_t dim, size_t calls,
                              gsl_rng *r,
                              gsl_monte_miser_state* state,
                              double *result, double *abserr);

GSL_FUN gsl_monte_miser_state* gsl_monte_miser_alloc(size_t dim);

GSL_FUN int gsl_monte_miser_init(gsl_monte_miser_state* state);

GSL_FUN void gsl_monte_miser_free(gsl_monte_miser_state* state);

typedef struct {
  double estimate_frac;
  size_t min_calls;
  size_t min_calls_per_bisection;
  double alpha;
  double dither;
} gsl_monte_miser_params;

GSL_FUN void gsl_monte_miser_params_get (const gsl_monte_miser_state * state,
				 gsl_monte_miser_params * params);

GSL_FUN void gsl_monte_miser_params_set (gsl_monte_miser_state * state,
				 const gsl_monte_miser_params * params);

__END_DECLS

#endif /* __GSL_MONTE_MISER_H__ */


/* sort/test.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Thomas Walter, 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_heapsort.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_sort_vector.h>
#include <gsl/gsl_ieee_utils.h>

size_t urand (size_t);

#include "test_heapsort.c"

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "test_source.c"
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

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

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

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

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

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

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

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

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

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

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

int
main (void)
{
  size_t i, s;

  gsl_ieee_env_setup ();

  /* Test for lengths of 1 ... 31, then 32, 64, 128, 256, ... */

  for (i = 1; i < 1024; i = (i < 32) ? i + 1 : 2 * i)
    test_heapsort (i);

  for (i = 1; i < 1024; i = (i < 32) ? i + 1 : 2 * i)
    {
      for (s = 1; s < 4; s++)
	{
	  test_sort_vector (i, s);
	  test_sort_vector_float (i, s);
	  test_sort_vector_long_double (i, s);
	  test_sort_vector_ulong (i, s);
	  test_sort_vector_long (i, s);
	  test_sort_vector_uint (i, s);
	  test_sort_vector_int (i, s);
	  test_sort_vector_ushort (i, s);
	  test_sort_vector_short (i, s);
	  test_sort_vector_uchar (i, s);
	  test_sort_vector_char (i, s);
	}
    }

  exit (gsl_test_summary ());
}

size_t
urand (size_t N)
{
  static unsigned long int x = 1;
  x = (1103515245 * x + 12345) & 0x7fffffffUL;
  return (size_t) ((x / 2147483648.0) * N);
}


/* ode-initval/evolve.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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.
 */

/* Author:  G. Jungman
 */
#include <config.h>
#include <string.h>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_odeiv.h>

#include "odeiv_util.h"

gsl_odeiv_evolve *
gsl_odeiv_evolve_alloc (size_t dim)
{
  gsl_odeiv_evolve *e =
    (gsl_odeiv_evolve *) malloc (sizeof (gsl_odeiv_evolve));

  if (e == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for evolve struct",
                      GSL_ENOMEM);
    }

  e->y0 = (double *) malloc (dim * sizeof (double));

  if (e->y0 == 0)
    {
      free (e);
      GSL_ERROR_NULL ("failed to allocate space for y0", GSL_ENOMEM);
    }

  e->yerr = (double *) malloc (dim * sizeof (double));

  if (e->yerr == 0)
    {
      free (e->y0);
      free (e);
      GSL_ERROR_NULL ("failed to allocate space for yerr", GSL_ENOMEM);
    }

  e->dydt_in = (double *) malloc (dim * sizeof (double));

  if (e->dydt_in == 0)
    {
      free (e->yerr);
      free (e->y0);
      free (e);
      GSL_ERROR_NULL ("failed to allocate space for dydt_in", GSL_ENOMEM);
    }

  e->dydt_out = (double *) malloc (dim * sizeof (double));

  if (e->dydt_out == 0)
    {
      free (e->dydt_in);
      free (e->yerr);
      free (e->y0);
      free (e);
      GSL_ERROR_NULL ("failed to allocate space for dydt_out", GSL_ENOMEM);
    }

  e->dimension = dim;
  e->count = 0;
  e->failed_steps = 0;
  e->last_step = 0.0;

  return e;
}

int
gsl_odeiv_evolve_reset (gsl_odeiv_evolve * e)
{
  e->count = 0;
  e->failed_steps = 0;
  e->last_step = 0.0;
  return GSL_SUCCESS;
}

void
gsl_odeiv_evolve_free (gsl_odeiv_evolve * e)
{
  RETURN_IF_NULL (e);
  free (e->dydt_out);
  free (e->dydt_in);
  free (e->yerr);
  free (e->y0);
  free (e);
}

/* Evolution framework method.
 *
 * Uses an adaptive step control object
 */
int
gsl_odeiv_evolve_apply (gsl_odeiv_evolve * e,
                        gsl_odeiv_control * con,
                        gsl_odeiv_step * step,
                        const gsl_odeiv_system * dydt,
                        double *t, double t1, double *h, double y[])
{
  const double t0 = *t;
  double h0 = *h;
  int step_status;
  int final_step = 0;
  double dt = t1 - t0;  /* remaining time, possibly less than h */

  if (e->dimension != step->dimension)
    {
      GSL_ERROR ("step dimension must match evolution size", GSL_EINVAL);
    }

  if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0))
    {
      GSL_ERROR ("step direction must match interval direction", GSL_EINVAL);
    }

  /* No need to copy if we cannot control the step size. */

  if (con != NULL)
    {
      DBL_MEMCPY (e->y0, y, e->dimension);
    }

  /* Calculate initial dydt once if the method can benefit. */

  if (step->type->can_use_dydt_in)
    {
      int status = GSL_ODEIV_FN_EVAL (dydt, t0, y, e->dydt_in);

      if (status)
        {
          return status;
        }
    }

try_step:

  if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt))
    {
      h0 = dt;
      final_step = 1;
    }
  else
    {
      final_step = 0;
    }

  if (step->type->can_use_dydt_in)
    {
      step_status =
        gsl_odeiv_step_apply (step, t0, h0, y, e->yerr, e->dydt_in,
                              e->dydt_out, dydt);
    }
  else
    {
      step_status =
        gsl_odeiv_step_apply (step, t0, h0, y, e->yerr, NULL, e->dydt_out,
                              dydt);
    }

  /* Check for stepper internal failure */

  if (step_status != GSL_SUCCESS)
    {
      *h = h0;  /* notify user of step-size which caused the failure */
      *t = t0;  /* restore original t value */
      return step_status;
    }

  e->count++;
  e->last_step = h0;

  if (final_step)
    {
      *t = t1;
    }
  else
    {
      *t = t0 + h0;
    }

  if (con != NULL)
    {
      /* Check error and attempt to adjust the step. */

      double h_old = h0;

      const int hadjust_status
        = gsl_odeiv_control_hadjust (con, step, y, e->yerr, e->dydt_out, &h0);

      if (hadjust_status == GSL_ODEIV_HADJ_DEC)
        {
          /* Check that the reported status is correct (i.e. an actual
             decrease in h0 occured) and the suggested h0 will change
             the time by at least 1 ulp */

          double t_curr = GSL_COERCE_DBL(*t);
          double t_next = GSL_COERCE_DBL((*t) + h0);

          if (fabs(h0) < fabs(h_old) && t_next != t_curr)
            {
              /* Step was decreased. Undo step, and try again with new h0. */
              DBL_MEMCPY (y, e->y0, dydt->dimension);
              e->failed_steps++;
              goto try_step;
            }
          else
            {
              h0 = h_old; /* keep current step size */
            }
        }
    }

  *h = h0;  /* suggest step size for next time-step */

  return step_status;
}


/*
 *
 * A cross-platform way of providing access to gsl_ieee_env_setup
 * from Fortran on any system MITgcm runs on currently.
 */

#ifdef USE_GSL_IEEE
#include <gsl/gsl_math.h>
#include <gsl/gsl_ieee_utils.h>

void fgsl_ieee_env_setup ()
{
  gsl_ieee_env_setup ();
}

void fgsl_ieee_env_setup_ ()
{
  gsl_ieee_env_setup ();
}

void fgsl_ieee_env_setup__ ()
{
  gsl_ieee_env_setup ();
}

void FGSL_IEEE_ENV_SETUP ()
{
  gsl_ieee_env_setup ();
}
#endif


/**
 * Copyright (c) 2019-2020 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Imported from:
 *
 * @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.8.0
 * @author Jakub Kurzak
 * @author Hatem Ltaief
 * @date 2010-11-15
 *
 **/
#ifndef _PLASMA_CORE_BLAS_H_
#define _PLASMA_CORE_BLAS_H_

#include "cores/dplasma_plasmatypes.h"
#include "cores/descriptor.h"
#include <cblas.h>
#include <lapacke.h>

#include "cores/core_zblas.h"
#include "cores/core_dblas.h"
#include "cores/core_cblas.h"
#include "cores/core_sblas.h"
#include "cores/core_zcblas.h"
#include "cores/core_dsblas.h"

#ifdef __cplusplus
extern "C" {
#endif

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

/* CBLAS requires for scalar arguments to be passed by address rather than by value */
#ifndef CBLAS_SADDR
#define CBLAS_SADDR( _val_ ) &(_val_)
#endif

 /** ****************************************************************************
  *  External interface of the GKK algorithm for InPlace Layout Translation
  **/
int  GKK_minloc(int n, int *T);
void GKK_BalanceLoad(int thrdnbr, int *Tp, int *leaders, int nleaders, int L);
int  GKK_getLeaderNbr(int me, int ne, int *nleaders, int **leaders);

void CORE_pivot_update(int m, int n, int *ipiv, int *indices,
                       int offset, int init);

#ifdef __cplusplus
}
#endif

#endif /* _PLASMA_CORE_BLAS_H_ */


#include <pygsl/solver.h>
#include <gsl/gsl_multifit_nlin.h>

const char  * filename = __FILE__;
PyObject *module = NULL;
static const char multifit_f_type_name[] = "F-MultiFitSolver";
static const char multifit_fdf_type_name[] = "FdF-MultiFitSolver";

static int
PyGSL_multifit_function_wrap(const gsl_vector *x, void *params, gsl_vector *f)
{
     PyGSL_solver *self = (PyGSL_solver *) params;
     return PyGSL_function_wrap_Op_On(x, f, self->cbs[0], self->args, x->size, f->size,
				      __FUNCTION__);
}

static int
PyGSL_multifit_function_wrap_df(const gsl_vector *x, void *params, gsl_matrix *df)
{
     PyGSL_solver *self = (PyGSL_solver *) params;
     /* size 1 or size 2 from matrix ? */
     return PyGSL_function_wrap_Op_Opn(x, df, self->cbs[1], self->args, df->size1, x->size,
				       __FUNCTION__);
}

static int
PyGSL_multifit_function_wrap_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *df)
{
     PyGSL_solver *self = (PyGSL_solver *) params;
     /* size 1 or size 2 from matrix ? */
     return PyGSL_function_wrap_Op_On_Opn(x, f, df, self->cbs[2], self->args, f->size, x->size,
					  __FUNCTION__);
}

static PyObject *
PyGSL_multifit_fdfsolver_set(PyGSL_solver *self, PyObject *pyargs, PyObject *kw)
{

     gsl_multifit_function_fdf * c_sys;
     struct pygsl_solver_n_set info = {1, NULL, (set_m_t)gsl_multifit_fdfsolver_set};
     PyObject * tmp;

     FUNC_MESS_BEGIN();
     if(self->c_sys == NULL){
	  if((c_sys = calloc(1, sizeof(gsl_multifit_function_fdf))) == NULL){
	       PyGSL_ERROR_NULL("Could not allocate the memory for the c_sys", GSL_ENOMEM);
	  }
	  c_sys->n=self->problem_dimensions[1];
	  c_sys->p=self->problem_dimensions[0];
	  c_sys->f  = PyGSL_multifit_function_wrap;
	  c_sys->df = PyGSL_multifit_function_wrap_df;
	  c_sys->fdf = PyGSL_multifit_function_wrap_fdf;
	  c_sys->params=(void*)self;
	  info.c_sys = c_sys;
     }else{
	  info.c_sys = self->c_sys;
     }
     tmp =  PyGSL_solver_n_set(self, pyargs, kw, &info);
     if(tmp == NULL){
	  PyGSL_add_traceback(module, __FILE__, __FUNCTION__, __LINE__ - 2);
     }
     FUNC_MESS_END();
     return tmp;
}

static PyObject*
PyGSL_multifit_fdfsolver_position(PyGSL_solver *self, PyObject *args)
{
     return PyGSL_solver_ret_vec(self, args, (ret_vec)gsl_multifit_fdfsolver_position);
}
static gsl_multifit_fdfsolver *
PyGSL_get_multifit_solver(PyGSL_solver *self)
{
     FUNC_MESS_BEGIN();
     assert(PyGSL_solver_check(self));
     FUNC_MESS_END();
     return (gsl_multifit_fdfsolver *) (self->solver);
}

static PyObject*
PyGSL_multifit_fdfsolver_x(PyGSL_solver *self, PyObject *args)
{
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(PyGSL_get_multifit_solver(self)->x);
}

static PyObject*
PyGSL_multifit_fdfsolver_dx(PyGSL_solver *self, PyObject *args)
{
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(PyGSL_get_multifit_solver(self)->dx);
}

static PyObject*
PyGSL_multifit_fdfsolver_f(PyGSL_solver *self, PyObject *args)
{
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(PyGSL_get_multifit_solver(self)->f);
}

static PyObject*
PyGSL_multifit_fdfsolver_J(PyGSL_solver *self, PyObject *args)
{
     return (PyObject *) PyGSL_copy_gslmatrix_to_pyarray(PyGSL_get_multifit_solver(self)->J);
}

static PyObject*
PyGSL_multifit_fdfsolver_test_delta(PyGSL_solver *self, PyObject *args)
{
     int flag;
     double epsabs, epsrel;
     gsl_multifit_fdfsolver *s = self->solver;
     if(!PyArg_ParseTuple(args, "dd", &epsabs, &epsrel))
	  return NULL;
     flag = gsl_multifit_test_delta(s->dx, s->x, epsabs, epsrel);
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);
}

static PyObject*
PyGSL_multifit_fdfsolver_test_gradient(PyGSL_solver *self, PyObject *args)
{
     int flag;
     double epsabs;
     gsl_vector *g = NULL;
     gsl_multifit_fdfsolver *s = self->solver;

     if(!PyArg_ParseTuple(args, "d", &epsabs))
	  return NULL;
     flag = gsl_multifit_gradient(s->J, s->f, g);
     if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS)
	  return NULL;
     flag = gsl_multifit_test_gradient(g, epsabs);
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);
}

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

static PyMethodDef PyGSL_multifit_fdfmethods[] = {
     {"J",    (PyCFunction)PyGSL_multifit_fdfsolver_J,   METH_NOARGS, NULL},
     {"dx",   (PyCFunction)PyGSL_multifit_fdfsolver_dx,   METH_NOARGS, NULL},
     {"x",    (PyCFunction)PyGSL_multifit_fdfsolver_x,   METH_NOARGS, NULL},
     {"position",    (PyCFunction)PyGSL_multifit_fdfsolver_position,   METH_NOARGS, NULL},
     {"f",    (PyCFunction)PyGSL_multifit_fdfsolver_f,    METH_NOARGS, NULL},
     {"set",  (PyCFunction)PyGSL_multifit_fdfsolver_set,  METH_VARARGS|METH_KEYWORDS, NULL},
     {"test_delta",  (PyCFunction)PyGSL_multifit_fdfsolver_test_delta,  METH_VARARGS, NULL},
     {"test_gradient",  (PyCFunction)PyGSL_multifit_fdfsolver_test_gradient,  METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}
};

const struct _SolverStatic
multifit_solver_f   = {{ (void_m_t) gsl_multifit_fsolver_free,
			 /* gsl_multifit_fsolver_restart */  (void_m_t) NULL,
			 (name_m_t) gsl_multifit_fsolver_name,
			 (int_m_t) gsl_multifit_fsolver_iterate},
		       1, PyGSL_multifit_fmethods, multifit_f_type_name},
multifit_solver_fdf = {{(void_m_t) gsl_multifit_fdfsolver_free,
		     /* gsl_multifit_fdfsolver_restart (void_m_t) */ NULL,
		     (name_m_t) gsl_multifit_fdfsolver_name,
		     (int_m_t)  gsl_multifit_fdfsolver_iterate},
		       3, PyGSL_multifit_fdfmethods, multifit_fdf_type_name};

static PyObject*
PyGSL_multifit_f_init(PyObject *self, PyObject *args,
		      const gsl_multifit_fsolver_type * type)
{

     PyObject *tmp=NULL;
     solver_alloc_struct s = {type, (void_an_t) gsl_multifit_fsolver_alloc,
			      &multifit_solver_f};
     FUNC_MESS_BEGIN();
     tmp = PyGSL_solver_dn_init(self, args, &s, 2);
     FUNC_MESS_END();
     return tmp;
}

static PyObject*
PyGSL_multifit_fdf_init(PyObject *self, PyObject *args,
		      const gsl_multifit_fdfsolver_type * type)
{

     PyObject *tmp=NULL;
     solver_alloc_struct s = {type, (void_an_t) gsl_multifit_fdfsolver_alloc,
			      &multifit_solver_fdf};
     FUNC_MESS_BEGIN();
     tmp = PyGSL_solver_dn_init(self, args, &s, 2);
     FUNC_MESS_END();
     return tmp;
}

#define AMFIT_FDF(name)                                                  \
static PyObject* PyGSL_multifit_init_ ## name (PyObject *self, PyObject *args)\
{                                                                             \
     PyObject *tmp = NULL;                                                    \
     FUNC_MESS_BEGIN();                                                       \
     tmp = PyGSL_multifit_fdf_init(self, args,  gsl_multifit_fdfsolver_ ## name); \
     if (tmp == NULL){                                                        \
	  PyGSL_add_traceback(module, __FILE__, __FUNCTION__, __LINE__); \
     }                                                                        \
     FUNC_MESS_END();                                                         \
     return tmp;                                                              \
}

AMFIT_FDF(lmsder)
AMFIT_FDF(lmder)

PyObject *
PyGSL_multifit_gradient(PyObject *self, PyObject *args)
{
  PyArrayObject *J_a = NULL, *f_a = NULL, *g_a = NULL;
  PyObject *J_o = NULL, *f_o = NULL;
  gsl_vector_view f;
  gsl_vector_view g;
  gsl_matrix_view J;

  PyGSL_array_index_t stride_recalc, dimension;
  int flag;

  if(!PyArg_ParseTuple(args, "OO:gsl_multifit_gradient", &J_o, &f_o)){
       return NULL;
  }

  J_a = PyGSL_matrix_check(J_o, -1, -1, PyGSL_DARRAY_CINPUT(1), NULL, NULL, NULL);
  if(J_a == NULL) goto fail;

  dimension = J_a->dimensions[0];
  /* Numpy calculates strides in bytes, gsl in basis type */
  f_a = PyGSL_vector_check(f_o, dimension, PyGSL_DARRAY_INPUT(2), &stride_recalc, NULL);
  if(f_a == NULL) goto fail;

  dimension = J_a->dimensions[1];
  g_a = (PyArrayObject *) PyGSL_New_Array(1, &dimension, PyArray_DOUBLE);
  if(g_a == NULL) goto fail;

  J = gsl_matrix_view_array((double *) J_a->data, J_a->dimensions[0], J_a->dimensions[1]);
  f = gsl_vector_view_array_with_stride((double *) f_a->data, stride_recalc, f_a->dimensions[0]);
  g = gsl_vector_view_array((double *) g_a->data, dimension);
  flag = gsl_multifit_gradient(&J.matrix, &f.vector, &g.vector);

  Py_DECREF(J_a);
  Py_DECREF(f_a);

  if((PyGSL_ERROR_FLAG(flag)) != GSL_SUCCESS)
       goto fail;

  return (PyObject * )g_a;

  fail :
    Py_XDECREF(J_a);
    Py_XDECREF(f_a);
    Py_XDECREF(g_a);
    return NULL;
}

PyObject *
PyGSL_multifit_covar(PyObject *self, PyObject *args)
{
  PyArrayObject *J_a = NULL, *C_a = NULL;
  PyObject *J_o = NULL;
  gsl_matrix_view J, C;
  PyGSL_array_index_t dimensions[2];
  int flag;
  double epsrel;

  if(!PyArg_ParseTuple(args, "Od:gsl_multifit_covar", &J_o, &epsrel)){
    return NULL;
  }

  J_a = PyGSL_matrix_check(J_o, -1, -1, PyGSL_DARRAY_CINPUT(1), NULL, NULL, NULL);
  if(J_a == NULL) goto fail;

  dimensions[0] = J_a->dimensions[1];
  dimensions[1] = J_a->dimensions[1];
  C_a = (PyArrayObject *) PyGSL_New_Array(2, dimensions, PyArray_DOUBLE);
  if(C_a == NULL) goto fail;

  J = gsl_matrix_view_array((double *) J_a->data, J_a->dimensions[0], J_a->dimensions[1]);
  C = gsl_matrix_view_array((double *) C_a->data, C_a->dimensions[0], C_a->dimensions[1]);

  flag = gsl_multifit_covar(&J.matrix, epsrel, &C.matrix);

  Py_DECREF(J_a);
  if((PyGSL_ERROR_FLAG(flag)) != GSL_SUCCESS)
       goto fail;
  return (PyObject * )C_a;

  fail :
    Py_XDECREF(J_a);
    Py_XDECREF(C_a);
    return NULL;
}

static PyObject *
PyGSL_multifit_test_delta(PyObject * self, PyObject * args)
{
     return PyGSL_solver_vvdd_i(self, args, gsl_multifit_test_delta);
}

static PyObject *
PyGSL_multifit_test_gradient(PyObject * self, PyObject * args)
{
     return PyGSL_solver_vd_i(self, args, gsl_multifit_test_gradient);
}

static PyMethodDef mMethods[] = {
     /* multifit solvers */
     {"lmder",          PyGSL_multifit_init_lmder,  METH_VARARGS, NULL},
     {"lmsder",          PyGSL_multifit_init_lmsder,  METH_VARARGS, NULL},
     /* multifit funcs */
     {"fit_test_delta",    PyGSL_multifit_test_delta,     METH_VARARGS, NULL},
     {"fit_test_gradient", PyGSL_multifit_test_gradient,  METH_VARARGS, NULL},
     {"gradient",          PyGSL_multifit_gradient,  METH_VARARGS, NULL},
     {"covar",             PyGSL_multifit_covar,  METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}
};

static const char PyGSL_multifit_nlin_module_doc[] = "XXX Missing \n";
void
initmultifit_nlin(void)
{
     PyObject* m, *dict, *item;
     FUNC_MESS_BEGIN();

     m=Py_InitModule("multifit_nlin", mMethods);
     module = m;
     assert(m);
     dict = PyModule_GetDict(m);
     if(!dict)
	  goto fail;

     init_pygsl()
     import_pygsl_solver();
     assert(PyGSL_API);

     if (!(item = PyString_FromString((char*)PyGSL_multifit_nlin_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");
     return;
}


// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#ifndef _HEBench_ClearText_EltAdd_H_7e5fa8c2415240ea93eff148ed73539b
#define _HEBench_ClearText_EltAdd_H_7e5fa8c2415240ea93eff148ed73539b

#include <gsl/gsl>

#include "hebench/api_bridge/cpp/hebench.hpp"

#include "clear_benchmark.h"

template <class T>
class EltAdd_Benchmark : public ClearTextBenchmark
{
private:
    HEBERROR_DECLARE_CLASS_NAME(EltAdd_Benchmark)

public:
    EltAdd_Benchmark(hebench::cpp::BaseEngine &engine,
                     const hebench::APIBridge::BenchmarkDescriptor &bench_desc,
                     const hebench::APIBridge::WorkloadParams &bench_params);
    ~EltAdd_Benchmark() override;

    hebench::APIBridge::Handle encode(const hebench::APIBridge::DataPackCollection *p_parameters) override;
    void decode(hebench::APIBridge::Handle encoded_data, hebench::APIBridge::DataPackCollection *p_native) override;

    hebench::APIBridge::Handle load(const hebench::APIBridge::Handle *p_local_data, std::uint64_t count) override;
    void store(hebench::APIBridge::Handle remote_data,
               hebench::APIBridge::Handle *p_local_data, std::uint64_t count) override;

    hebench::APIBridge::Handle operate(hebench::APIBridge::Handle h_remote_packed,
                                       const hebench::APIBridge::ParameterIndexer *p_param_indexers) override;

protected:
    std::uint64_t m_vector_size;

private:
    static void eltAdd(gsl::span<T> &result,
                       const gsl::span<const T> &M0, const gsl::span<const T> &M1,
                       std::size_t element_count);
};

#include "inl/bench_eltadd.inl"

#endif // defined _HEBench_ClearText_EltAdd_H_7e5fa8c2415240ea93eff148ed73539b


/* spoper.c
 *
 * Copyright (C) 2012 Patrick Alken
 *
 * 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 <gsl/gsl_spblas.h>

int
gsl_spmatrix_scale(gsl_spmatrix *m, const double x)
{
  size_t i;

  for (i = 0; i < m->nz; ++i)
    m->data[i] *= x;

  return GSL_SUCCESS;
} /* gsl_spmatrix_scale() */

int
gsl_spmatrix_minmax(const gsl_spmatrix *m, double *min_out, double *max_out)
{
  double min, max;
  size_t n;

  if (m->nz == 0)
    {
      GSL_ERROR("matrix is empty", GSL_EINVAL);
    }

  min = m->data[0];
  max = m->data[0];

  for (n = 1; n < m->nz; ++n)
    {
      double x = m->data[n];

      if (x < min)
        min = x;

      if (x > max)
        max = x;
    }

  *min_out = min;
  *max_out = max;

  return GSL_SUCCESS;
} /* gsl_spmatrix_minmax() */

/*
gsl_spmatrix_add()
  Add two sparse matrices

Inputs: c - (output) a + b
        a - (input) sparse matrix
        b - (input) sparse matrix

Return: success or error
*/

int
gsl_spmatrix_add(gsl_spmatrix *c, const gsl_spmatrix *a,
                 const gsl_spmatrix *b)
{
  const size_t M = a->size1;
  const size_t N = a->size2;

  if (b->size1 != M || b->size2 != N || c->size1 != M || c->size2 != N)
    {
      GSL_ERROR("matrices must have same dimensions", GSL_EBADLEN);
    }
  else if (a->sptype != b->sptype || a->sptype != c->sptype)
    {
      GSL_ERROR("matrices must have same sparse storage format",
                GSL_EINVAL);
    }
  else if (GSL_SPMATRIX_ISTRIPLET(a))
    {
      GSL_ERROR("triplet format not yet supported", GSL_EINVAL);
    }
  else
    {
      int status = GSL_SUCCESS;
      size_t *w = (size_t *) a->work;
      double *x = (double *) b->work;
      size_t *Cp, *Ci;
      double *Cd;
      size_t j, p;
      size_t nz = 0; /* number of non-zeros in c */
      size_t inner_size, outer_size;

      if (GSL_SPMATRIX_ISCCS(a))
        {
          inner_size = M;
          outer_size = N;
        }
      else if (GSL_SPMATRIX_ISCRS(a))
        {
          inner_size = N;
          outer_size = M;
        }
      else
        {
          GSL_ERROR("unknown sparse matrix type", GSL_EINVAL);
        }

      if (c->nzmax < a->nz + b->nz)
        {
          status = gsl_spmatrix_realloc(a->nz + b->nz, c);
          if (status)
            return status;
        }

      /* initialize w = 0 */
      for (j = 0; j < inner_size; ++j)
        w[j] = 0;

      Ci = c->i;
      Cp = c->p;
      Cd = c->data;

      for (j = 0; j < outer_size; ++j)
        {
          Cp[j] = nz;

          /* CCS: x += A(:,j); CRS: x += A(j,:) */
          nz = gsl_spblas_scatter(a, j, 1.0, w, x, j + 1, c, nz);

          /* CCS: x += B(:,j); CRS: x += B(j,:) */
          nz = gsl_spblas_scatter(b, j, 1.0, w, x, j + 1, c, nz);

          for (p = Cp[j]; p < nz; ++p)
            Cd[p] = x[Ci[p]];
        }

      /* finalize last column of c */
      Cp[j] = nz;
      c->nz = nz;

      return status;
    }
} /* gsl_spmatrix_add() */

/*
gsl_spmatrix_d2sp()
  Convert a dense gsl_matrix to sparse (triplet) format

Inputs: S - (output) sparse matrix in triplet format
        A - (input) dense matrix to convert
*/

int
gsl_spmatrix_d2sp(gsl_spmatrix *S, const gsl_matrix *A)
{
  int s = GSL_SUCCESS;
  size_t i, j;

  gsl_spmatrix_set_zero(S);
  S->size1 = A->size1;
  S->size2 = A->size2;

  for (i = 0; i < A->size1; ++i)
    {
      for (j = 0; j < A->size2; ++j)
        {
          double x = gsl_matrix_get(A, i, j);

          if (x != 0.0)
            gsl_spmatrix_set(S, i, j, x);
        }
    }

  return s;
} /* gsl_spmatrix_d2sp() */

/*
gsl_spmatrix_sp2d()
  Convert a sparse matrix to dense format
*/

int
gsl_spmatrix_sp2d(gsl_matrix *A, const gsl_spmatrix *S)
{
  if (A->size1 != S->size1 || A->size2 != S->size2)
    {
      GSL_ERROR("matrix sizes do not match", GSL_EBADLEN);
    }
  else
    {
      gsl_matrix_set_zero(A);

      if (GSL_SPMATRIX_ISTRIPLET(S))
        {
          size_t n;

          for (n = 0; n < S->nz; ++n)
            {
              size_t i = S->i[n];
              size_t j = S->p[n];
              double x = S->data[n];

              gsl_matrix_set(A, i, j, x);
            }
        }
      else
        {
          GSL_ERROR("non-triplet formats not yet supported", GSL_EINVAL);
        }

      return GSL_SUCCESS;
    }
} /* gsl_spmatrix_sp2d() */


/* ieee-utils/env.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_errno.h>

void
gsl_ieee_env_setup (void)
{
  const char * p = getenv("GSL_IEEE_MODE") ;

  int precision = 0, rounding = 0, exception_mask = 0 ;

  int comma = 0 ;

  if (p == 0)  /* GSL_IEEE_MODE environment variable is not set */
    return ;

  if (*p == '\0') /* GSL_IEEE_MODE environment variable is empty */
    return ;

  gsl_ieee_read_mode_string (p, &precision, &rounding, &exception_mask) ;

  gsl_ieee_set_mode (precision, rounding, exception_mask) ;

  fprintf(stderr, "GSL_IEEE_MODE=\"") ;

  /* Print string with a preceeding comma if the list has already begun */

#define PRINTC(x) do {if(comma) fprintf(stderr,","); fprintf(stderr,x); comma++ ;} while(0)

  switch (precision)
    {
    case GSL_IEEE_SINGLE_PRECISION:
      PRINTC("single-precision") ;
      break ;
    case GSL_IEEE_DOUBLE_PRECISION:
      PRINTC("double-precision") ;
      break ;
    case GSL_IEEE_EXTENDED_PRECISION:
      PRINTC("extended-precision") ;
      break ;
    }

  switch (rounding)
    {
    case GSL_IEEE_ROUND_TO_NEAREST:
      PRINTC("round-to-nearest") ;
      break ;
    case GSL_IEEE_ROUND_DOWN:
      PRINTC("round-down") ;
      break ;
    case GSL_IEEE_ROUND_UP:
      PRINTC("round-up") ;
      break ;
    case GSL_IEEE_ROUND_TO_ZERO:
      PRINTC("round-to-zero") ;
      break ;
    }

  if ((exception_mask & GSL_IEEE_MASK_ALL) == GSL_IEEE_MASK_ALL)
    {
      PRINTC("mask-all") ;
    }
  else if ((exception_mask & GSL_IEEE_MASK_ALL) == 0)
    {
      PRINTC("trap-common") ;
    }
  else
    {
      if (exception_mask & GSL_IEEE_MASK_INVALID)
	PRINTC("mask-invalid") ;

      if (exception_mask & GSL_IEEE_MASK_DENORMALIZED)
	PRINTC("mask-denormalized") ;

      if (exception_mask & GSL_IEEE_MASK_DIVISION_BY_ZERO)
	PRINTC("mask-division-by-zero") ;

      if (exception_mask & GSL_IEEE_MASK_OVERFLOW)
	PRINTC("mask-overflow") ;

      if (exception_mask & GSL_IEEE_MASK_UNDERFLOW)
	PRINTC("mask-underflow") ;
    }

  if (exception_mask & GSL_IEEE_TRAP_INEXACT)
    PRINTC("trap-inexact") ;

  fprintf(stderr,"\"\n") ;
}


/* This library is used by compare_hashes_* tests.
 */

#ifndef COMPARISON_STATS_H
#define COMPARISON_STATS_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <gsl/gsl_sf_log.h>
#include <gsl/gsl_math.h>

struct comparison_stats {
	uint64_t *ones_count;
	uint64_t tot_count;
	uint hash_width;
	uint64_t (*calc_hash)(void *hash_info, unsigned char *str, unsigned length);
	void *hash_info;
};

typedef enum {
	report_type_short,
	report_type_long } report_type;

void basic_init_comparison_stats(struct comparison_stats *cs, uint width);
void process_comparison_stats(struct comparison_stats *cs, unsigned char *str, unsigned sz);
void report_comparison_stats(struct comparison_stats *cs, const char *name, report_type rtyp);
void free_comparison_stats(struct comparison_stats *cs);

#endif


/* ******************************************************************************
 *
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License, Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0.
 *
 *  See the NOTICE file distributed with this work for additional
 *  information regarding copyright ownership.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 ******************************************************************************/

//
// Created by agibsonccc on 2/6/16.
//

#ifndef NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H
#define NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H
#include <cblas.h>

/*
enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102 };
enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113,
    AtlasConj=114};
enum CBLAS_UPLO  {CblasUpper=121, CblasLower=122};
enum CBLAS_DIAG  {CblasNonUnit=131, CblasUnit=132};
enum CBLAS_SIDE  {CblasLeft=141, CblasRight=142};
*/

#ifdef __cplusplus
extern "C" {
#endif
/**
 * Converts a character
 * to its proper enum
 * for row (c) or column (f) ordering
 * default is row major
 */
CBLAS_ORDER convertOrder(int from);
/**
 * Converts a character to its proper enum
 * t -> transpose
 * n -> no transpose
 * c -> conj
 */
CBLAS_TRANSPOSE convertTranspose(int from);
/**
 * Upper or lower
 * U/u -> upper
 * L/l -> lower
 *
 * Default is upper
 */
CBLAS_UPLO convertUplo(int from);

/**
 * For diagonals:
 * u/U -> unit
 * n/N -> non unit
 *
 * Default: unit
 */
CBLAS_DIAG convertDiag(int from);
/**
 * Side of a matrix, left or right
 * l /L -> left
 * r/R -> right
 * default: left
 */
CBLAS_SIDE convertSide(int from);

#ifdef __cplusplus
}
#endif

#endif  // NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H


/* Copyright (c) 2011-2012, Jérémy Fix. All rights reserved. */

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

/* * Redistributions of source code must retain the above copyright notice, */
/* this list of conditions and the following disclaimer. */
/* * 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. */
/* * None of the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. */

/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. */

#ifndef UKF_SAMPLES_H
#define UKF_SAMPLES_H

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sort_vector_double.h>
#include <gsl/gsl_permute_vector.h>
#include <gsl/gsl_randist.h>

#include <time.h>

#include <iostream>

namespace ukf
{
    namespace samples
    {
        /**
          * @short Generate 1D samples according to a uniform distribution :
          * @brief In fact, gsl_ran_choose would do the job!!
          */
        class RandomSample_1D
        {
        public:
            static void generateSample(gsl_vector * samples, unsigned int nb_samples, double min, double max, double delta)
            {
                if(samples->size != nb_samples)
                {
                    std::cerr << "[ERROR] RandomSample_1D : samples should be allocated to the size of the requested number of samples" << std::endl;
                    return;
                }

                for(unsigned int i = 0 ; i < nb_samples ; i++)
                    gsl_vector_set(samples, i, min + floor((max - min)*rand()/double(RAND_MAX)/delta)    * delta  );
            }
        };

        /**
          * @short Generate 2D samples according to a uniform distribution
          * and put them alternativaly at the odd/even positions
          */
        class RandomSample_2D
        {
        public:
            static void generateSample(gsl_vector * samples, unsigned int nb_samples, double minx, double maxx, double deltax, double miny, double maxy, double deltay)
            {
                if(samples->size != 2*nb_samples)
                {
                    std::cerr << "[ERROR] RandomSample_2D : samples should be allocated to the size of the 2 times the requested number of samples" << std::endl;
                    return;
                }
		gsl_vector_view vec_view = gsl_vector_subvector_with_stride (samples, 0, 2, nb_samples);
                RandomSample_1D::generateSample(&vec_view.vector, nb_samples, minx, maxx, deltax);
		vec_view = gsl_vector_subvector_with_stride (samples, 1, 2, nb_samples);
                RandomSample_1D::generateSample(&vec_view.vector, nb_samples, miny, maxy, deltay);
            }
        };

        /**
          * @short Generate 3D samples according to a uniform distribution
          * and put them alternativaly at the odd/even positions
          */
        class RandomSample_3D
        {
        public:
            static void generateSample(gsl_vector * samples, unsigned int nb_samples, double minx, double maxx, double deltax, double miny, double maxy, double deltay, double minz, double maxz, double deltaz)
            {
                if(samples->size != 3*nb_samples)
                {
                    std::cerr << "[ERROR] RandomSample_2D : samples should be allocated to the size of the 2 times the requested number of samples" << std::endl;
                    return;
                }
		gsl_vector_view vec_view = gsl_vector_subvector_with_stride (samples, 0, 3, nb_samples);
                RandomSample_1D::generateSample(&vec_view.vector, nb_samples, minx, maxx, deltax);
		vec_view = gsl_vector_subvector_with_stride (samples, 1, 3, nb_samples);
                RandomSample_1D::generateSample(&vec_view.vector, nb_samples, miny, maxy, deltay);
		vec_view = gsl_vector_subvector_with_stride (samples, 2, 3, nb_samples);
                RandomSample_1D::generateSample(&vec_view.vector, nb_samples, minz, maxz, deltaz);
            }
        };

        /**
          * @short Extract the indexes of the nb_samples highest values of a vector
          * Be carefull, this function is extracting the indexes as, the way it is used, it doesn't not to which sample a vector index corresponds
          */
        class MaximumIndexes_1D
        {
        public:
            static void generateSample(gsl_vector * v, gsl_vector * indexes_samples, unsigned int nb_samples)
            {
                if(indexes_samples->size != nb_samples)
                {
                    std::cerr << "[ERROR] MaximumIndexes_1D : samples should be allocated to the size of the requested number of samples" << std::endl;
                    return;
                }
                if(nb_samples > v->size)
                {
                    std::cerr << "[ERROR] MaximumIndexes_1D : the vector v must hold at least nb_samples elements" << std::endl;
                    return;
                }

                gsl_permutation * p = gsl_permutation_alloc(v->size);
                gsl_sort_vector_index(p,v);
                for(unsigned int i = 0 ; i < nb_samples ; i++)
                    gsl_vector_set(indexes_samples, i, gsl_permutation_get(p, p->size - i - 1));
                gsl_permutation_free(p);
            }
        };

        /**
          * @short Extract the indexes of the nb_samples highest values of a matrix
          * Be carefull, this function is extracting the indexes as, the way it is used, it doesn't not to which sample a vector index corresponds
          */
        class MaximumIndexes_2D
        {
        public:
            static void generateSample(gsl_matrix * m, gsl_vector * indexes_samples, unsigned int nb_samples)
            {

                if(indexes_samples->size != 2*nb_samples)
                {
                    std::cerr << "[ERROR] MaximumIndexes_2D : samples should be allocated to the size of 2 times the requested number of samples" << std::endl;
                    return;
                }
                if(nb_samples > m->size1 * m->size2 )
                {
                    std::cerr << "[ERROR] MaximumIndexes_2D : the vector v must hold at least nb_samples elements" << std::endl;
                    return;
                }

                gsl_permutation * p = gsl_permutation_alloc(m->size1 * m->size2);
                // We cast the matrix in a vector ordered in row-major and get the permutation to order the data by increasing value
		gsl_vector_view vec_view = gsl_vector_view_array(m->data,m->size1 * m->size2);
                gsl_sort_vector_index(p,&vec_view.vector);

                // The indexes stored in p are in row-major order
                // we then convert the nb_samples first indexes into matrix indexes and copy them in indexes_samples
                int k,l;
                for(unsigned int i = 0 ; i < nb_samples ; i++)
                {
                    // Line
                    k = gsl_permutation_get(p,p->size - i - 1) / m->size2;
                    // Column
                    l = gsl_permutation_get(p,p->size - i - 1) % m->size2;
                    gsl_vector_set(indexes_samples, 2 * i, k);
                    gsl_vector_set(indexes_samples, 2 * i + 1, l);
                }

                gsl_permutation_free(p);
            }
        };

        /**
          * @short Generate 1D samples according to a discrete vectorial distribution
          */
        class DistributionSample_1D
        {
        public:
            static void generateSample(gsl_vector * v, gsl_vector * indexes_samples, unsigned int nb_samples)
            {
                if(indexes_samples->size != nb_samples)
                {
                    std::cerr << "[ERROR] RandomSample_1D : samples should be allocated to the size of the requested number of samples" << std::endl;
                    return;
                }

                // Generating nb_samples samples from an array of weights for each index is easily done in the gsl with gsl_ran_discrete_* methods
                // the weights v do not need to add up to one
                gsl_rng * r = gsl_rng_alloc(gsl_rng_default);
                // Init the random seed
                gsl_rng_set(r, time(NULL));
                gsl_ran_discrete_t * ran_pre = gsl_ran_discrete_preproc(v->size, v->data);

                for(unsigned int i = 0 ; i < nb_samples ; i++)
                    gsl_vector_set(indexes_samples, i , gsl_ran_discrete(r, ran_pre));

                gsl_ran_discrete_free(ran_pre);
                gsl_rng_free(r);
            }
        };

        /**
          * @short Generate 2D samples according to a discrete matricial distribution
          */
        class DistributionSample_2D
        {
        public:
            static void generateSample(gsl_matrix * m, gsl_vector * indexes_samples, unsigned int nb_samples)
            {
                if(indexes_samples->size != 2*nb_samples)
                {
                    std::cerr << "[ERROR] RandomSample_1D : samples should be allocated to the size of the requested number of samples" << std::endl;
                    return;
                }

                // To generate 2D samples according to a matrix of weights
                // we simply cast the matrix into a vector, ask DistributionSample_1D to do the job
                // and then convert the linear row-major indexes back into 2D indexes

                gsl_vector * samples_tmp = gsl_vector_alloc(nb_samples);
		gsl_vector_view vec_view = gsl_vector_view_array(m->data,m->size1 * m->size2);
                DistributionSample_1D::generateSample(&vec_view.vector, samples_tmp, nb_samples);

                int k,l;
                for(unsigned int i = 0 ; i < nb_samples ; i++)
                {
                    k = int(gsl_vector_get(samples_tmp,i)) / m->size2;
                    l = int(gsl_vector_get(samples_tmp,i)) % m->size2;
                    gsl_vector_set(indexes_samples, 2*i, k);
                    gsl_vector_set(indexes_samples, 2*i+1, l);
                }

                gsl_vector_free(samples_tmp);
            }
        };
    }
}

#endif // UKF_SAMPLES_H