/* 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


/* spcompress.c
 *
 * Copyright (C) 2012-2014, 2016 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_errno.h>
#include <gsl/gsl_spmatrix.h>

/*
gsl_spmatrix_ccs()
  Create a sparse matrix in compressed column format

Inputs: T - sparse matrix in triplet format

Return: pointer to new matrix (should be freed when finished with it)
*/

gsl_spmatrix *
gsl_spmatrix_ccs(const gsl_spmatrix *T)
{
  if (!GSL_SPMATRIX_ISTRIPLET(T))
    {
      GSL_ERROR_NULL("matrix must be in triplet format", GSL_EINVAL);
    }
  else
    {
      const size_t *Tj; /* column indices of triplet matrix */
      size_t *Cp;       /* column pointers of compressed column matrix */
      size_t *w;        /* copy of column pointers */
      gsl_spmatrix *m;
      size_t n;

      m = gsl_spmatrix_alloc_nzmax(T->size1, T->size2, T->nz,
                                   GSL_SPMATRIX_CCS);
      if (!m)
        return NULL;

      Tj = T->p;
      Cp = m->p;

      /* initialize column pointers to 0 */
      for (n = 0; n < m->size2 + 1; ++n)
        Cp[n] = 0;

      /*
       * compute the number of elements in each column:
       * Cp[j] = # non-zero elements in column j
       */
      for (n = 0; n < T->nz; ++n)
        Cp[Tj[n]]++;

      /* compute column pointers: p[j] = p[j-1] + nnz[j-1] */
      gsl_spmatrix_cumsum(m->size2, Cp);

      /* make a copy of the column pointers */
      w = (size_t *) m->work;
      for (n = 0; n < m->size2; ++n)
        w[n] = Cp[n];

      /* transfer data from triplet format to CCS */
      for (n = 0; n < T->nz; ++n)
        {
          size_t k = w[Tj[n]]++;
          m->i[k] = T->i[n];
          m->data[k] = T->data[n];
        }

      m->nz = T->nz;

      return m;
    }
}

gsl_spmatrix *
gsl_spmatrix_compcol(const gsl_spmatrix *T)
{
  return gsl_spmatrix_ccs(T);
}

/*
gsl_spmatrix_crs()
  Create a sparse matrix in compressed row format

Inputs: T - sparse matrix in triplet format

Return: pointer to new matrix (should be freed when finished with it)
*/

gsl_spmatrix *
gsl_spmatrix_crs(const gsl_spmatrix *T)
{
  if (!GSL_SPMATRIX_ISTRIPLET(T))
    {
      GSL_ERROR_NULL("matrix must be in triplet format", GSL_EINVAL);
    }
  else
    {
      const size_t *Ti; /* row indices of triplet matrix */
      size_t *Cp;       /* row pointers of compressed row matrix */
      size_t *w;        /* copy of column pointers */
      gsl_spmatrix *m;
      size_t n;

      m = gsl_spmatrix_alloc_nzmax(T->size1, T->size2, T->nz,
                                   GSL_SPMATRIX_CRS);
      if (!m)
        return NULL;

      Ti = T->i;
      Cp = m->p;

      /* initialize row pointers to 0 */
      for (n = 0; n < m->size1 + 1; ++n)
        Cp[n] = 0;

      /*
       * compute the number of elements in each row:
       * Cp[i] = # non-zero elements in row i
       */
      for (n = 0; n < T->nz; ++n)
        Cp[Ti[n]]++;

      /* compute row pointers: p[i] = p[i-1] + nnz[i-1] */
      gsl_spmatrix_cumsum(m->size1, Cp);

      /* make a copy of the row pointers */
      w = (size_t *) m->work;
      for (n = 0; n < m->size1; ++n)
        w[n] = Cp[n];

      /* transfer data from triplet format to CRS */
      for (n = 0; n < T->nz; ++n)
        {
          size_t k = w[Ti[n]]++;
          m->i[k] = T->p[n];
          m->data[k] = T->data[n];
        }

      m->nz = T->nz;

      return m;
    }
}

/*
gsl_spmatrix_cumsum()

Compute the cumulative sum:

p[j] = Sum_{k=0...j-1} c[k]

0 <= j < n + 1

Alternatively,
p[0] = 0
p[j] = p[j - 1] + c[j - 1]

Inputs: n - length of input array
        c - (input/output) array of size n + 1
            on input, contains the n values c[k]
            on output, contains the n + 1 values p[j]

Return: success or error
*/

void
gsl_spmatrix_cumsum(const size_t n, size_t *c)
{
  size_t sum = 0;
  size_t k;

  for (k = 0; k < n; ++k)
    {
      size_t ck = c[k];
      c[k] = sum;
      sum += ck;
    }

  c[n] = sum;
} /* gsl_spmatrix_cumsum() */


/*
  10 Sept 08: Process_Error_Code is now static and thus private to
              this library.
*/

#include <cblas.h>
#include "lbl.h"
#include "ma57.h"

#ifdef __cplusplus
extern "C" {   /* To prevent C++ compilers from mangling symbols */
#endif

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Process_Error_Code"
static int Process_Error_Code( Ma57_Data *ma57, int nerror ) {

    LOGMSG( " [%3d]", nerror );
    /* Take appropriate action according to error code */
    /* Inflation factors are from the MA57 spec sheet  */
    switch( nerror ) {

    case 0:
      break;

      /* Warnings */
    case 1:
      LOGMSG( " Found and ignored %d indices out of range.\n", ma57->info[2] );
      break;

    case 2:
      LOGMSG( " Found and summed %d duplicate entries.\n", ma57->info[3] );
      break;

    case 3:
      LOGMSG( " Found duplicate and out-of-range indices.\n" );
      break;

    case 4:
      LOGMSG( " Matrix has rank %d, deficient.\n", ma57->info[24] );
      break;

    case 5:
      LOGMSG( " Found %d pivot sign changes in definite matrix.\n",
              ma57->info[25] );
      break;

    case 8:
      LOGMSG( " Infinity norm of solution found to be zero.\n" );
      break;

    case 10:
      LOGMSG( " Insufficient real space. Increased LFACT to %d.\n",
              ma57->lfact );
      break;

    case 11:
      LOGMSG( " Insufficient integer space. Increased LIFACT to %d.\n",
              ma57->lifact );
      break;

      /* Errors */
    case -1:
      LOGMSG( " Value of n is out of range: %d.\n", ma57->info[1] );
      break;

    case -2:
      LOGMSG( " Value of nz is out of range: %d.\n", ma57->info[1] );
      break;

    case -3:
      LOGMSG( " Adjusted size of array FACT to %d.\n", ma57->lfact );
      break;

    case -4:
      LOGMSG( " Adjusting size of array IFACT to %d.\n", ma57->lifact );
      break;

    case -5:
      LOGMSG( " Small pivot encountered at pivot step %d. Threshold = %lf.\n",
              ma57->info[1], ma57->cntl[1] );

    case -6:
      LOGMSG( " Change in pivot sign detected at pivot step %d.\n",
              ma57->info[1] );
      break;

    case -7:
      LOGMSG( " Erroneous sizing of array FACT or IFACT.\n" );
      break;

    case -8:
      LOGMSG( " Iterative refinement failed to converge.\n" );
      break;

    case -9:
      LOGMSG( " Error in user-supplied permutation array in component %d.\n",
              ma57->info[1] );
      break;

    case -10:
      LOGMSG( " Unknown pivoting strategy: %d\n.", ma57->info[1] );
      break;

    case -11:
      LOGMSG( " Size of RHS must be %d. Received %d.\n",
              ma57->n, ma57->info[1] );
      break;

    case -12:
      LOGMSG( " Invalid value of JOB (%d).\n", ma57->info[1] );
      break;

    case -13:
      LOGMSG( " Invalid number of iterative refinement steps (%d).\n",
              ma57->info[1] );
      break;

    case -14:
      LOGMSG( " Failed to estimate condition number.\n" );
      break;

    case -15:
      LOGMSG( " LKEEP has value %d, less than minimum allowed.\n",
              ma57->info[1] );
      break;

    case -16:
      LOGMSG( " Invalid number of RHS (%d).\n", ma57->info[1] );
      break;

    case -17:
      LOGMSG( " Increasing size of LWORK to %d.\n", ma57->lwork );
      break;

    case -18:
      LOGMSG( " MeTiS library not available or not found.\n" );
      break;

    default:
      LOGMSG( " Unrecognized flag from Factorize()." );
      nerror = -30;
    }
    return nerror;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "MA57_Initialize"
  Ma57_Data *Ma57_Initialize( int nz, int n, FILE *logfile ) {

    /* Call initialize subroutine MA57ID and set defaults */
    Ma57_Data *ma57 = (Ma57_Data *)LBL_Calloc( 1, sizeof(Ma57_Data) );
    ma57->logfile   = logfile;

    LOGMSG( " MA57 :: Initializing..." );

    ma57->n         = n;
    ma57->nz        = nz;
    ma57->fetched   = 0;
    ma57->irn       = (int *)LBL_Calloc( nz, sizeof(int) );
    ma57->jcn       = (int *)LBL_Calloc( nz, sizeof(int) );
    ma57->lkeep     = 5*n + nz + imax(n,nz) + 42 + n; // Add n to suggested val.
    ma57->keep      = (int *)LBL_Calloc( ma57->lkeep, sizeof(int) );
    ma57->iwork     = (int *)LBL_Calloc( 5*n, sizeof(int) );
    ma57->work      = NULL; // Will be initialized in Ma57_Solve()

    LOGMSG( " Calling ma57id..." );
    MA57ID( ma57->cntl, ma57->icntl ); // Initialize all parameters

    // Ensure some default parameters are appropriate
    ma57->icntl[0] = -1;  // Stream for error messages.
    ma57->icntl[1] = -1;  // Stream for warning messages.
    ma57->icntl[2] = -1;  // Stream for monitoring printing.
    ma57->icntl[3] = -1;  // Stream for printing of statistics.
    ma57->icntl[4] =  0;  // Verbosity: 0=none, 1=errors, 2=1+warnings,
                          //            3=2+monitor, 4=3+input,output
    ma57->icntl[5] =  5;  // Pivot selection strategy:
                          //  0: AMD using MC47
                          //  1: User-supplied pivot sequence
                          //  2: AMD with dense row strategy
                          //  3: MD as in MA27
                          //  4: MeTiS
                          //  5: Automatic (4 with fallback on 2)
    ma57->icntl[6] =  1;  // Numerical pivoting strategy
                          //  1: Do pivoting using value in cntl(1)
                          //  2: No pivoting; exit on sign change or zero
                          //  3: No pivoting; exit if pivot modulus < cntl(2)
                          //  4: No pivoting; alter pivots to have same sign
    ma57->icntl[7] =  1;  // Memory will be re-allocated if necessary
    ma57->icntl[15] = 0;  // No need to scale system before factorizing
    ma57->fetched = 0;

    LOGMSG( " done\n");
    return ma57;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_Analyze"
  int Ma57_Analyze( Ma57_Data *ma57 ) {

    int finished = 0, error;
    LOGMSG( " MA57 :: Analyzing..." );

    /* Unpack data structure and call MA57AD */
    while( !finished ) {
      LOGMSG( "\n         Calling ma57ad... " );

      MA57AD( &(ma57->n), &(ma57->nz), ma57->irn, ma57->jcn,
              &(ma57->lkeep), ma57->keep, ma57->iwork, ma57->icntl,
              ma57->info, ma57->rinfo );

      error = ma57->info[0];

      if( !error )
        finished = 1;
      else {
        error = Process_Error_Code( ma57, error );
        if( error != -3 && error != -4 && error != -8 && error != -14 )
          return error;
      }
    }

    // Allocate data for Factorize()
    ma57->lfact = ceil( LFACT_GROW * ma57->info[8] );
    ma57->fact = (double *)LBL_Calloc( ma57->lfact, sizeof(double) );
    ma57->lifact = ceil( LIFACT_GROW * ma57->info[9] );
    ma57->ifact = (int *)LBL_Calloc( ma57->lifact, sizeof(int) );
    LBL_Free( ma57->iwork );
    ma57->iwork = (int *)LBL_Calloc( ma57->n, sizeof(int) );
    ma57->work = NULL;

    LOGMSG( " done\n");
    return 0;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_Factorize"
  int Ma57_Factorize( Ma57_Data *ma57, double A[] ) {

    // To ensure consistency with the MA27 interface, the user must pass
    // the array A, *including* its zero-th element. Internally, A+1 is
    // passed to MA57.

    double *newFact;
    int    *newIfact;
    int     newSize, one = 1, zero = 0;
    int     finished = 0, error;
    LOGMSG( " MA57 :: Factorizing...\n" );

    /* Unpack data structure and call MA57BD */
    while( !finished ) {
      LOGMSG( "         Calling ma57bd... " );

      MA57BD( &(ma57->n), &(ma57->nz), A, ma57->fact, &(ma57->lfact),
              ma57->ifact, &(ma57->lifact), &(ma57->lkeep), ma57->keep,
              ma57->iwork, ma57->icntl, ma57->cntl, ma57->info, ma57->rinfo );

      error = ma57->info[0];

      if( !error ) {

        finished = 1;

      } else {

        error = Process_Error_Code( ma57, error );

        if( error == -3 || error == 10 ) {

          /* Resize real workspace */
          newSize = (error == 10) ? ma57->lfact : ma57->info[16];
          newSize = ceil( LFACT_GROW * newSize );
          LOGMSG("         Resizing real workspace for factors to %d", newSize);
          newFact = (double *)LBL_Calloc( newSize, sizeof(double) );
          MA57ED( &(ma57->n), &zero, ma57->keep, ma57->fact, &(ma57->lfact),
                  newFact, &newSize, ma57->ifact, &(ma57->lifact), NULL,
                  &zero, ma57->info );
          LBL_Free( ma57->fact );
          ma57->fact = newFact; newFact = NULL;
          ma57->lfact = newSize;

        } else if( error == -4 || error == 11 ) {

          /* Resize integer workspace */
          newSize = (error == 11) ? ma57->lifact : ma57->info[17];
          newSize = ceil( LIFACT_GROW * newSize );
          LOGMSG("         Resizing int workspace for factors to %d", newSize);
          newIfact = (int *)LBL_Calloc( newSize, sizeof(int) );
          MA57ED( &(ma57->n), &one, ma57->keep, ma57->fact, &(ma57->lfact),
                  NULL, &zero, ma57->ifact, &(ma57->lifact), newIfact,
                  &newSize, ma57->info );
          LBL_Free( ma57->ifact );
          ma57->ifact = newIfact; newIfact = NULL;
          ma57->lifact = newSize;

        } else {
          finished = 1;
          if( error < 0 ) return error;
        }
      }
    }

    LOGMSG("\n");
    LOGMSG("         %-23s: %6i   %-28s: %6i\n",
           "No. of 2x2 pivots"            , ma57->info[21],
           "Pivot step for modification"  , ma57->info[26]);
    LOGMSG("         %-28s: %6i   %-28s: %6i\n",
           "No. of negative e-vals"       , ma57->info[23],
           "No. of entries in factor"     , ma57->info[13]);
    LOGMSG("         %-28s: %6i   %-28s: %6i\n",
           "Rank of factorization"        , ma57->info[24],
           "No. of pivot sign changes"    , ma57->info[25]);
    LOGMSG("         done\n");
    return 0;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_Solve"
  int Ma57_Solve( Ma57_Data *ma57, double x[] ) {

    int one = 1, finished = 0, error;

    LOGMSG( " MA57 :: Solving..." );

    ma57->job = 1;
    ma57->lrhs = ma57->n;
    ma57->nrhs = 1;
    ma57->lwork = ma57->n * ma57->nrhs;
    if( !ma57->work ) {
      LOGMSG("\n         Sizing work array to size %d ", ma57->lwork);
      ma57->work = (double *)LBL_Calloc( ma57->lwork, sizeof(double) );
    }

    while( !finished ) {
      LOGMSG( "\n         Calling ma57cd... " );

      /* Unpack data structure and call MA57CD */
      MA57CD( &(ma57->job), &(ma57->n), ma57->fact, &(ma57->lfact),
              ma57->ifact, &(ma57->lifact), &ma57->nrhs, x, &(ma57->lrhs),
              ma57->work, &(ma57->lwork), ma57->iwork, ma57->icntl,
              ma57->info );

      error = ma57->info[0];
      if( error == -17 ) {
        LBL_Free( ma57->work );
        ma57->lwork = ceil( 1.2 * ma57->lwork );
        LOGMSG("\n         Resizing work array to size %d ", ma57->lwork);
        ma57->work = (double *)LBL_Calloc( ma57->lwork, sizeof(double) );
      } else
        finished = 1;
      if( error ) error = Process_Error_Code( ma57, error );
    }

    LOGMSG( " done\n" );
    return error;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_Refine"
  int Ma57_Refine( Ma57_Data *ma57, double x[], double rhs[],
                   double A[], int maxitref, int job ) {

    int error;

    LOGMSG( " MA57 :: Performing iterative refinement..." );

    ma57->job = job;
    /* For values of 'job' that demand the presence of the residual,
     * it should be placed by the user in ma57->residual prior to calling
     * this function.
     */

    /* Allocate work space */
    ma57->icntl[8] = imax( 1, maxitref );  // Number of refinement iterations
    ma57->icntl[9] = 1; // Return estimates of condition number
    LBL_Free( ma57->iwork );
    ma57->iwork = (int *)LBL_Calloc( ma57->n, sizeof(int) );

    LBL_Free( ma57->work );
    ma57->lwork = ma57->n;
    if( ma57->icntl[8] > 1 ) {
      ma57->lwork += 2 * ma57->n;
      if( ma57->icntl[9] > 0 )
        ma57->lwork += 2 * ma57->n;
    }
    ma57->work = (double *)LBL_Calloc( ma57->lwork, sizeof(double) );
    ma57->residual = (double *)LBL_Calloc( ma57->n, sizeof(double) );

    /* Perform iterative refinement */
    MA57DD( &(ma57->job), &(ma57->n), &(ma57->nz), A, ma57->irn,
            ma57->jcn, ma57->fact, &(ma57->lfact), ma57->ifact,
            &(ma57->lifact), rhs, x, ma57->residual, ma57->work,
            ma57->iwork, ma57->icntl, ma57->cntl, ma57->info,
            ma57->rinfo );

    error = ma57->info[0];
    if( error ) error = Process_Error_Code(ma57, ma57->info[0]);
    LOGMSG( " done\n" );
    return error;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_Finalize"
  void Ma57_Finalize( Ma57_Data *ma57 ) {

    /* Free allocated memory */
    LOGMSG( " MA57 :: Deallocating data arrays..." );
    LBL_Free( ma57->irn );
    LBL_Free( ma57->jcn );
    LBL_Free( ma57->keep );
    LBL_Free( ma57->iwork );
    LBL_Free( ma57->fact );
    LBL_Free( ma57->ifact );
    LBL_Free( ma57->work );
    if( ma57->residual ) LBL_Free(ma57->residual);
    LOGMSG( " done.\n" );
    free(ma57);
    return;
  }

  /* ================================================================= */
    /* Currently, this routine doesn't do error checking on the
       allowable parameter values. */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_set_int_parm"
  void Ma57_set_int_parm( Ma57_Data *ma57, int parm, int val ) {

      switch (parm) {
      case MA57_I_PIV_SELECTION:
          ma57->icntl[MA57_I_PIV_SELECTION] = val;
          LOGMSG(" Set %-20s = %10d\n","I_PIV_SELECTION",val);
          break;
      case MA57_I_PIV_NUMERICAL:
          ma57->icntl[MA57_I_PIV_NUMERICAL] = val;
          LOGMSG(" Set %-20s = %10d\n","I_PIV_NUMERICAL",val);
          break;
      case MA57_I_SCALING:
          ma57->icntl[MA57_I_SCALING] = val;
          LOGMSG(" Set %-20s = %10d\n","I_SCALING",val);
          break;
      }
      return;
  }

  /* ================================================================= */
    /* Currently, this routine doesn't do error checking on the
       allowable parameter values. */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_set_real_parm"
  void Ma57_set_real_parm( Ma57_Data *ma57, int parm, double val ) {

      switch (parm) {
      case MA57_D_PIV_THRESH:
          ma57->cntl[MA57_D_PIV_THRESH] = val;
          LOGMSG(" Set %-20s = %10.2e\n","D_PIV_THRESH",val);
          break;
      case MA57_D_PIV_NONZERO:
          ma57->cntl[MA57_D_PIV_NONZERO] = val;
          LOGMSG(" Set %-20s = %10.2e\n","D_PIV_NONZERO",val);
          break;
      }
      return;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_get_int_parm"
  int Ma57_get_int_parm( Ma57_Data *ma57, int parm ) {

      switch (parm) {
      case MA57_I_PIV_SELECTION:
          return ma57->icntl[MA57_I_PIV_SELECTION];
      case MA57_I_PIV_NUMERICAL:
          return ma57->icntl[MA57_I_PIV_NUMERICAL];
      case MA57_I_SCALING:
          return ma57->icntl[MA57_I_SCALING];
      }
      return 0;
  }

  /* ================================================================= */

#ifdef  __FUNCT__
#undef  __FUNCT__
#endif
#define __FUNCT__ "Ma57_get_real_parm"
  double Ma57_get_real_parm( Ma57_Data *ma57, int parm ) {

      switch (parm) {
      case MA57_D_PIV_THRESH:
          return ma57->cntl[MA57_D_PIV_THRESH];
      case MA57_D_PIV_NONZERO:
          return ma57->cntl[MA57_D_PIV_NONZERO];
      }
      return 0.0;
  }

  /* ================================================================= */

#ifdef __cplusplus
}              /* Closing brace for  extern "C"  block */
#endif


/* multimin/fdfminimizer.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Fabrice Rossi
 *
 * 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 <gsl/gsl_errno.h>
#include <gsl/gsl_multimin.h>

gsl_multimin_fdfminimizer *
gsl_multimin_fdfminimizer_alloc (const gsl_multimin_fdfminimizer_type * T,
				 size_t n)
{
  int status;

  gsl_multimin_fdfminimizer *s =
    (gsl_multimin_fdfminimizer *) malloc (sizeof (gsl_multimin_fdfminimizer));

  if (s == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for minimizer struct",
		     GSL_ENOMEM, 0);
    }

  s->type = T;

  s->x = gsl_vector_calloc (n);

  if (s->x == 0)
    {
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for x", GSL_ENOMEM, 0);
    }

  s->gradient = gsl_vector_calloc (n);

  if (s->gradient == 0)
    {
      gsl_vector_free (s->x);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for gradient", GSL_ENOMEM, 0);
    }

  s->dx = gsl_vector_calloc (n);

  if (s->dx == 0)
    {
      gsl_vector_free (s->x);
      gsl_vector_free (s->gradient);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for dx", GSL_ENOMEM, 0);
    }

  s->state = malloc (T->size);

  if (s->state == 0)
    {
      gsl_vector_free (s->x);
      gsl_vector_free (s->gradient);
      gsl_vector_free (s->dx);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for minimizer state",
		     GSL_ENOMEM, 0);
    }

  status = (T->alloc) (s->state, n);

  if (status != GSL_SUCCESS)
    {
      free (s->state);
      gsl_vector_free (s->x);
      gsl_vector_free (s->gradient);
      gsl_vector_free (s->dx);
      free (s);

      GSL_ERROR_VAL ("failed to initialize minimizer state", GSL_ENOMEM, 0);
    }

  return s;
}

int
gsl_multimin_fdfminimizer_set (gsl_multimin_fdfminimizer * s,
                               gsl_multimin_function_fdf * fdf,
                               const gsl_vector * x,
                               double step_size, double tol)
{
  if (s->x->size != fdf->n)
    {
      GSL_ERROR ("function incompatible with solver size", GSL_EBADLEN);
    }

  if (x->size != fdf->n)
    {
      GSL_ERROR ("vector length not compatible with function", GSL_EBADLEN);
    }

  s->fdf = fdf;

  gsl_vector_memcpy (s->x,x);
  gsl_vector_set_zero (s->dx);

  return (s->type->set) (s->state, s->fdf, s->x, &(s->f), s->gradient, step_size, tol);
}

void
gsl_multimin_fdfminimizer_free (gsl_multimin_fdfminimizer * s)
{
  (s->type->free) (s->state);
  free (s->state);
  gsl_vector_free (s->dx);
  gsl_vector_free (s->gradient);
  gsl_vector_free (s->x);
  free (s);
}

int
gsl_multimin_fdfminimizer_iterate (gsl_multimin_fdfminimizer * s)
{
  return (s->type->iterate) (s->state, s->fdf, s->x, &(s->f), s->gradient, s->dx);
}

int
gsl_multimin_fdfminimizer_restart (gsl_multimin_fdfminimizer * s)
{
  return (s->type->restart) (s->state);
}

const char *
gsl_multimin_fdfminimizer_name (const gsl_multimin_fdfminimizer * s)
{
  return s->type->name;
}

gsl_vector *
gsl_multimin_fdfminimizer_x (gsl_multimin_fdfminimizer * s)
{
  return s->x;
}

gsl_vector *
gsl_multimin_fdfminimizer_dx (gsl_multimin_fdfminimizer * s)
{
  return s->dx;
}

gsl_vector *
gsl_multimin_fdfminimizer_gradient (gsl_multimin_fdfminimizer * s)
{
  return s->gradient;
}

double
gsl_multimin_fdfminimizer_minimum (gsl_multimin_fdfminimizer * s)
{
  return s->f;
}


/** @file */
#ifndef __CCL_CORE_H_INCLUDED__
#define __CCL_CORE_H_INCLUDED__

#include <stdbool.h>
#include <stdio.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>
#include <gsl/gsl_const_mksa.h>

#include "ccl_utils.h"
#include "ccl_f1d.h"
#include "ccl_f2d.h"

CCL_BEGIN_DECLS

/**
 * Struct to hold physical constants.
 */
typedef struct ccl_physical_constants {
  /**
   * Lightspeed / H0 in units of Mpc/h (from CODATA 2014)
   */
  double CLIGHT_HMPC;

  /**
   * Newton's gravitational constant in units of m^3/Kg/s^2
   */
  double GNEWT;

  /**
   * Solar mass in units of kg (from GSL)
   */
  double SOLAR_MASS;

  /**
   * Mpc to meters (from PDG 2013)
   */
  double MPC_TO_METER;

  /**
   * pc to meters (from PDG 2013)
   */
  double PC_TO_METER;

  /**
   * Rho critical in units of M_sun/h / (Mpc/h)^3
   */
  double RHO_CRITICAL;

  /**
   * Boltzmann constant in units of J/K
  */
  double KBOLTZ;

  /**
   * Stefan-Boltzmann constant in units of kg/s^3 / K^4
   */
  double STBOLTZ;

  /**
   * Planck's constant in units kg m^2 / s
   */
  double HPLANCK;

  /**
   * The speed of light in m/s
   */
  double CLIGHT;

  /**
   * Electron volt to Joules convestion
   */
  double EV_IN_J;

  /**
   * Temperature of the CMB in K
   */
  double T_CMB;

  /**
   * T_ncdm, as taken from CLASS, explanatory.ini
   */
  double TNCDM;

  /**
   * neutrino mass splitting differences
   * See Lesgourgues and Pastor, 2012 for these values.
   * Adv. High Energy Phys. 2012 (2012) 608515,
   * arXiv:1212.6154, page 13
  */
  double DELTAM12_sq;
  double DELTAM13_sq_pos;
  double DELTAM13_sq_neg;
} ccl_physical_constants;

extern ccl_physical_constants ccl_constants;

/**
 * Struct that contains all the parameters needed to create certain splines.
 * This includes splines for the scale factor, masses, and power spectra.
 */
typedef struct ccl_spline_params {
  // scale factor splines
  int A_SPLINE_NA;
  double A_SPLINE_MIN;
  double A_SPLINE_MINLOG_PK;
  double A_SPLINE_MIN_PK;
  double A_SPLINE_MINLOG_SM;
  double A_SPLINE_MIN_SM;
  double A_SPLINE_MAX;
  double A_SPLINE_MINLOG;
  int A_SPLINE_NLOG;

  //Mass splines
  double LOGM_SPLINE_DELTA;
  int LOGM_SPLINE_NM;
  double LOGM_SPLINE_MIN;
  double LOGM_SPLINE_MAX;

  //PS a and k spline
  int A_SPLINE_NA_SM;
  int A_SPLINE_NLOG_SM;
  int A_SPLINE_NA_PK;
  int A_SPLINE_NLOG_PK;

  //k-splines and integrals
  double K_MAX_SPLINE;
  double K_MAX;
  double K_MIN;
  double DLOGK_INTEGRATION;
  int N_K;
  int N_K_3DCOR;

  //Correlation function parameters
  double ELL_MIN_CORR;
  double ELL_MAX_CORR;
  int N_ELL_CORR;

  // interpolation types
  const gsl_interp_type* A_SPLINE_TYPE;
  const gsl_interp_type* K_SPLINE_TYPE;
  const gsl_interp_type* M_SPLINE_TYPE;
  const gsl_interp_type* D_SPLINE_TYPE;
  const gsl_interp2d_type* PNL_SPLINE_TYPE;
  const gsl_interp2d_type* PLIN_SPLINE_TYPE;
  const gsl_interp_type* CORR_SPLINE_TYPE;
} ccl_spline_params;

extern const ccl_spline_params default_spline_params;

/**
 * Struct that contains parameters that control the accuracy of various GSL
 * routines.
 */
typedef struct ccl_gsl_params {
  // General parameters
  size_t N_ITERATION;

  // Integration
  int INTEGRATION_GAUSS_KRONROD_POINTS;
  double INTEGRATION_EPSREL;
  // Limber integration
  int INTEGRATION_LIMBER_GAUSS_KRONROD_POINTS;
  double INTEGRATION_LIMBER_EPSREL;
  // Distance integrals
  double INTEGRATION_DISTANCE_EPSREL;
  // sigma_R integral
  double INTEGRATION_SIGMAR_EPSREL;
  // k_NL integral
  double INTEGRATION_KNL_EPSREL;

  // Root finding
  double ROOT_EPSREL;
  int ROOT_N_ITERATION;

  // ODE
  double ODE_GROWTH_EPSREL;

  // growth
  double EPS_SCALEFAC_GROWTH;

  // halo model
  double HM_MMIN;
  double HM_MMAX;
  double HM_EPSABS;
  double HM_EPSREL;
  size_t HM_LIMIT;
  int HM_INT_METHOD;

} ccl_gsl_params;

extern const ccl_gsl_params default_gsl_params;

/**
 * Struct containing the parameters defining a cosmology
 */
typedef struct ccl_parameters {

  // Densities: CDM, baryons, total matter, neutrinos, curvature
  double Omega_c; /**< Density of CDM relative to the critical density*/
  double Omega_b; /**< Density of baryons relative to the critical density*/
  double Omega_m; /**< Density of all matter relative to the critical density*/
  double Omega_k; /**< Density of curvature relative to the critical density*/
  double sqrtk; /**< Square root of the magnitude of curvature, k */ //TODO check
  int k_sign; /**<Sign of the curvature k */

  // Dark Energy
  double w0;
  double wa;

  // Hubble parameters
  double H0;
  double h;

  // Neutrino properties

  double Neff; // Effective number of relativistic neutrino species in the early universe.
  int N_nu_mass; // Number of species of neutrinos which are nonrelativistic today
  double N_nu_rel;  // Number of species of neutrinos which are relativistic  today
  double *m_nu;  // total mass of massive neutrinos (This is a pointer so that it can hold multiple masses.)
  double sum_nu_masses; // sum of the neutrino masses.
  double Omega_nu_mass; // Omega_nu for MASSIVE neutrinos
  double Omega_nu_rel; // Omega_nu for MASSLESS neutrinos

  // Primordial power spectra
  double A_s;
  double n_s;

  // Radiation parameters
  double Omega_g;
  double T_CMB;

  // BCM baryonic model parameters
  double bcm_log10Mc;
  double bcm_etab;
  double bcm_ks;

  // mu / Sigma quasistatica parameterisation of modified gravity params
  double mu_0;
  double sigma_0;

  // Derived parameters
  double sigma8;
  double Omega_l;
  double z_star;

  //Modified growth rate
  bool has_mgrowth;
  int nz_mgrowth;
  double *z_mgrowth;
  double *df_mgrowth;
} ccl_parameters;

/**
 * Struct containing references to gsl splines for distance and acceleration calculations
 */
typedef struct ccl_data {
  // These are all functions of the scale factor a.

  // Distances are defined in Mpc
  double growth0;
  gsl_spline * chi;
  gsl_spline * growth;
  gsl_spline * fgrowth;
  gsl_spline * E;
  gsl_spline * achi;

  // Function of Halo mass M
  gsl_spline2d * logsigma;

  // real-space splines for RSD
  ccl_f1d_t* rsd_splines[3];
  double rsd_splines_scalefactor;
} ccl_data;

/**
 * Sturct containing references to instances of the above structs, and boolean flags of precomputed values.
 */
typedef struct ccl_cosmology {
  ccl_parameters    params;
  ccl_configuration config;
  ccl_data          data;
  ccl_spline_params spline_params;
  ccl_gsl_params    gsl_params;

  bool computed_distances;
  bool computed_growth;
  bool computed_sigma;

  int status;
  //this is optional - less tedious than tracking all numerical values for status in error handler function
  char status_message[500];

  // other flags?
} ccl_cosmology;

// Initialization and life cycle of objects
ccl_cosmology * ccl_cosmology_create(ccl_parameters params, ccl_configuration config);

/* Internal function to set the status message safely. */
void ccl_cosmology_set_status_message(ccl_cosmology * cosmo, const char * status_message, ...);

// User-facing creation routines
/**
 * Create a cosmology
 * @param Omega_c Omega_c
 * @param Omega_b Omega_b
 * @param Omega_k Omega_k
 * @param Neff Number of relativistic neutrino species in the early universe
 * @param mnu neutrino mass, either sum or list of length 3
 * @param mnu_type determines neutrino mass convention (ccl_mnu_list, ccl_mnu_sum, ccl_mnu_sum_inverted, ccl_mnu_sum_equal)
 * @param w0 Dark energy EoS parameter
 * @param wa Dark energy EoS parameter
 * @param h Hubble constant in units of 100 km/s/Mpc
 * @param norm_pk the normalization of the power spectrum, either A_s or sigma8
 * @param n_s the power-law index of the power spectrum
 * @param bcm_log10Mc log10 cluster mass, one of the parameters of the BCM model
 * @param bcm_etab ejection radius parameter, one of the parameters of the BCM model
 * @param bcm_ks wavenumber for the stellar profile, one of the parameters of the BCM model
 * @param nz_mgrowth the number of redshifts where the modified growth is provided
 * @param zarr_mgrowth the array of redshifts where the modified growth is provided
 * @param dfarr_mgrowth the modified growth function vector provided
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * For specific cases see documentation for ccl_error.c
 * @return void
 */
ccl_parameters ccl_parameters_create(double Omega_c, double Omega_b, double Omega_k,
				     double Neff, double* mnu, int n_mnu,
				     double w0, double wa, double h, double norm_pk,
				     double n_s, double bcm_log10Mc, double bcm_etab, double bcm_ks,
				     double mu_0, double sigma_0, int nz_mgrowth, double *zarr_mgrowth,
				     double *dfarr_mgrowth, int *status);

/**
 * Free a parameters struct
 * @param params ccl_parameters struct
 * @return void
 */
void ccl_parameters_free(ccl_parameters * params);

/**
 * Write a cosmology parameters object to a file in yaml format, .
 * @param params Cosmological parameters
 * @param filename Name of file to create and write
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * @return void
 */
void ccl_parameters_write_yaml(ccl_parameters * params, const char * filename, int * status);

/**
 * Read a cosmology parameters object from a file in yaml format, .
 * @param filename Name of existing file to read from
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * @return cosmo Cosmological parameters
 */
ccl_parameters ccl_parameters_read_yaml(const char * filename, int *status);

/**
 * Free a cosmology struct
 * @param cosmo Cosmological parameters
 * @return void
 */
void ccl_cosmology_free(ccl_cosmology * cosmo);

int ccl_get_pk_spline_na(ccl_cosmology *cosmo);
int ccl_get_pk_spline_nk(ccl_cosmology *cosmo);
void ccl_get_pk_spline_a_array(ccl_cosmology *cosmo,int ndout,double* doutput,int *status);
void ccl_get_pk_spline_lk_array(ccl_cosmology *cosmo,int ndout,double* doutput,int *status);

CCL_END_DECLS

#endif


/*
    Copyright (C) 2008 Wei Dong <wdong@princeton.edu>. All Rights Reserved.

    This file is part of LSHKIT.

    LSHKIT 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.

    LSHKIT 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 LSHKIT.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 *  \file fitdata.cpp
 *  \brief Gather statistics from dataset for MPLSH tuning.
 *
 *  This program gahters statistical data from a small sample dataset
 *  for automatic MPLSH parameter tuning.  It carries out the following
 *  steps:
 *  -# Sample N points from the dataset. Only those N points will be used for future computation.
 *  -# Sample P pairs of points from the sample, calculate the distance for each pair.
 *  -# Sample Q points from the sample as queries points.
 *  -# Divide the sample into F folds.
 *  -# For i = 1 to F, take i folds and run K-NN search, so the query points
 *     will be searched against sample datasets of N/F, 2N/F, ..., N/F points.
 *
 *  The statistical data is printed to standard output after the progress display.
 *
\verbatim
Allowed options:
  -h [ --help ]          produce help message.
  -N [ -- ] arg (=0)     number of points to use
  -P [ -- ] arg (=50000) number of pairs to sample
  -Q [ -- ] arg (=1000)  number of queries to sample
  -K [ -- ] arg (=100)   search for K nearest neighbors
  -F [ -- ] arg (=10)    divide the sample to F folds
  -D [ --data ] arg      data file
\endverbatim
 */

#ifndef _MPLSH_FITDATA_H_
#define _MPLSH_FITDATA_H_

#include "logging.h"

#include <cstdlib>
#include <sstream>
#include <gsl/gsl_multifit.h>

namespace lshkit {

inline
bool is_good_value (double v) {
    return ((v > -std::numeric_limits<double>::max()) &&
            (v < std::numeric_limits<double>::max()));
}

inline
std::string FitData(const FloatMatrix& data,
                    unsigned N,            // number of points to use
                    unsigned P,            // number of pairs to sample
                    unsigned Q,            // number of queries to sample
                    unsigned K,            // search for K neighbors neighbors
                    unsigned F             // divide the sample to F folds
                   )
{
    LOG(LIB_INFO) << "started running FitData" << std::endl;

    std::vector<unsigned> idx(data.getSize());
    for (size_t i = 0; i < idx.size(); ++i) idx[i] = i;
    std::random_shuffle(idx.begin(), idx.end());

    if (N > 0 && N < static_cast<unsigned>(data.getSize())) idx.resize(N);

    metric::l2sqr<float> l2sqr(data.getDim());

    DefaultRng rng;
    boost::variate_generator<DefaultRng &, UniformUnsigned> gen(rng,
             UniformUnsigned(0, idx.size()-1));

    double gM = 0.0;
    double gG = 0.0;
    {
        // sample P pairs of points
        for (unsigned k = 0; k < P; ++k)
        {
            double dist, logdist;
            for (;;)
            {
                unsigned i = gen();
                unsigned j = gen();
                if (i == j) continue;
                dist = l2sqr(data[idx[i]], data[idx[j]]);
                logdist = log(dist);
                if (is_good_value(logdist)) break;
            }
            gM += dist;
            gG += logdist;
        }
        gM /= P;
        gG /= P;
        gG = exp(gG);
    }

    if (Q > idx.size()) Q = idx.size();
    if (K > idx.size() - Q) K = idx.size() - Q;
    /* sample query */
    std::vector<unsigned> qry(Q);

    SampleQueries(&qry, idx.size(), rng);

    /* do the queries */
    std::vector<Topk<unsigned> > topks(Q);
    for (unsigned i = 0; i < Q; ++i) topks[i].reset(K);

    /* ... */
    gsl_matrix *X = gsl_matrix_alloc(F * K, 3);
    gsl_vector *yM = gsl_vector_alloc(F * K);
    gsl_vector *yG = gsl_vector_alloc(F * K);
    gsl_vector *pM = gsl_vector_alloc(3);
    gsl_vector *pG = gsl_vector_alloc(3);
    gsl_matrix *cov = gsl_matrix_alloc(3,3);

    std::vector<double> M(K);
    std::vector<double> G(K);

    unsigned m = 0;
    for (unsigned l = 0; l < F; l++)
    {
        // Scan
        for (unsigned i = l; i< idx.size(); i += F)
        {
            for (unsigned j = 0; j < Q; j++)
            {
                unsigned id = qry[j];
                if (i != id)
                {
                    float d = l2sqr(data[idx[id]], data[idx[i]]);
                    if (is_good_value(log(double(d))))
                      topks[j] << Topk<unsigned>::Element(i, d);
                }
            }
        }

        fill(M.begin(), M.end(), 0.0);
        fill(G.begin(), G.end(), 0.0);

        for (unsigned i = 0; i < Q; i++)
        {
            for (unsigned k = 0; k < K; k++)
            {
                M[k] += topks[i][k].dist;
                G[k] += log(topks[i][k].dist);
            }
        }

        for (unsigned k = 0; k < K; k++)
        {
            M[k] = log(M[k]/Q);
            G[k] /= Q;
            gsl_matrix_set(X, m, 0, 1.0);
            gsl_matrix_set(X, m, 1, log(double(data.getSize() * (l + 1)) / double(F)));
            gsl_matrix_set(X, m, 2, log(double(k + 1)));
            gsl_vector_set(yM, m, M[k]);
            gsl_vector_set(yG, m, G[k]);
            ++m;
        }

        //++progress;
    }

    gsl_multifit_linear_workspace *work = gsl_multifit_linear_alloc(F * K, 3);

    double chisq;

    gsl_multifit_linear(X, yM, pM, cov, &chisq, work);
    gsl_multifit_linear(X, yG, pG, cov, &chisq, work);

    std::stringstream ss;
    ss << gM << " " << gG << std::endl;
    ss << gsl_vector_get(pM, 0) << " "
       << gsl_vector_get(pM, 1) << " "
       << gsl_vector_get(pM, 2) << std::endl;
    ss << gsl_vector_get(pG, 0) << " "
       << gsl_vector_get(pG, 1) << " "
       << gsl_vector_get(pG, 2) << std::endl;

    gsl_matrix_free(X);
    gsl_matrix_free(cov);
    gsl_vector_free(pM);
    gsl_vector_free(pG);
    gsl_vector_free(yM);
    gsl_vector_free(yG);

    LOG(LIB_INFO) << ss.str();
    LOG(LIB_INFO) << "finished FitData";

    return ss.str();
}

}     // namespace lshkit

#endif


#ifndef SOURCE_UTILS_H_
#define SOURCE_UTILS_H_

// *** source/utils.h ***
// Author: Kevin Wolz, date: 08/2018
//
// Various utilities used in the code.

#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <string>
#include <unistd.h>
#include <algorithm>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_sort_vector.h>
#include <gsl/gsl_permutation.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_cdf.h>

std::string Timestamp();

double get_signum(double x);

void PrintGslVector(gsl_vector* vector);

void PrintGslVector(gsl_vector* vector, std::string label);

void PrintGslMatrix(gsl_matrix* matrix);

void PrintGslMatrix(gsl_matrix* matrix, std::string label);

bool file_exists(const std::string& filename);

void AppendTwoGslVectors(gsl_vector* vector1, gsl_vector* vector2,
                         gsl_vector* new_vector);

void ConcatenateTwoGslMatrices(gsl_matrix* mmatrix1, gsl_matrix* matrix2,
                               gsl_matrix* new_matrix, bool vertical_axis);

void BlockDiagFromTwoGslMatrices(gsl_matrix* matrix1, gsl_matrix* matrix2,
                                 gsl_matrix* new_matrix, unsigned int dimnew1,
                                 unsigned int dimnew2);

void SubtractTwoDoubleVectors(const std::vector<double> &vector1,
                              const std::vector<double> &vector2,
                              std::vector<double> &difference);

double GslVec1MatVec2(gsl_vector* vector1, gsl_matrix* matrix,
                      gsl_vector* vector2, int dim);

void InvertGslMatrix(gsl_matrix* tobeinverted, unsigned int dim,
                     gsl_matrix* inverted);

double DeterminantGslMatrix(gsl_matrix* square_matrix, unsigned int dim);

double Vec1MatVec2(std::vector<double> vector1, gsl_matrix* matrix,
                   std::vector<double> vector2, int dim);

double TraceGslMatrix(gsl_matrix* square_matrix, int dim);

void MultiplyFourGslMatrices(gsl_matrix* matrix1, gsl_matrix* matrix2,
                             gsl_matrix* matrix3, gsl_matrix* matrix4,
                             int dim, gsl_matrix* resulting_matrix);

void MultiplyThreeGslMatrices(gsl_matrix* matrix1, gsl_matrix* matrix2,
                              gsl_matrix* matrix3, int dim,
                              gsl_matrix* resulting_matrix);

#endif // SOURCE_UTILS_H_


/* ode-initval/gsl_odeiv.h
 *
 * 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
 */
#ifndef __GSL_ODEIV_H__
#define __GSL_ODEIV_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 <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_types.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

/* Description of a system of ODEs.
 *
 * y' = f(t,y) = dydt(t, y)
 *
 * The system is specified by giving the right-hand-side
 * of the equation and possibly a jacobian function.
 *
 * Some methods require the jacobian function, which calculates
 * the matrix dfdy and the vector dfdt. The matrix dfdy conforms
 * to the GSL standard, being a continuous range of floating point
 * values, in row-order.
 *
 * As with GSL function objects, user-supplied parameter
 * data is also present.
 */

typedef struct
{
  int (* function) (double t, const double y[], double dydt[], void * params);
  int (* jacobian) (double t, const double y[], double * dfdy, double dfdt[], void * params);
  size_t dimension;
  void * params;
}
gsl_odeiv_system;

#define GSL_ODEIV_FN_EVAL(S,t,y,f)  (*((S)->function))(t,y,f,(S)->params)
#define GSL_ODEIV_JA_EVAL(S,t,y,dfdy,dfdt)  (*((S)->jacobian))(t,y,dfdy,dfdt,(S)->params)

/* General stepper object.
 *
 * Opaque object for stepping an ODE system from t to t+h.
 * In general the object has some state which facilitates
 * iterating the stepping operation.
 */

typedef struct
{
  const char * name;
  int can_use_dydt_in;
  int gives_exact_dydt_out;
  void * (*alloc) (size_t dim);
  int  (*apply)  (void * state, size_t dim, double t, double h, double y[], double yerr[], const double dydt_in[], double dydt_out[], const gsl_odeiv_system * dydt);
  int  (*reset) (void * state, size_t dim);
  unsigned int  (*order) (void * state);
  void (*free)  (void * state);
}
gsl_odeiv_step_type;

typedef struct {
  const gsl_odeiv_step_type * type;
  size_t dimension;
  void * state;
}
gsl_odeiv_step;

/* Available stepper types.
 *
 * rk2    : embedded 2nd(3rd) Runge-Kutta
 * rk4    : 4th order (classical) Runge-Kutta
 * rkck   : embedded 4th(5th) Runge-Kutta, Cash-Karp
 * rk8pd  : embedded 8th(9th) Runge-Kutta, Prince-Dormand
 * rk2imp : implicit 2nd order Runge-Kutta at Gaussian points
 * rk4imp : implicit 4th order Runge-Kutta at Gaussian points
 * gear1  : M=1 implicit Gear method
 * gear2  : M=2 implicit Gear method
 */

GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk2;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk4;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rkf45;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rkck;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk8pd;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk2imp;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk2simp;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_rk4imp;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_bsimp;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_gear1;
GSL_VAR const gsl_odeiv_step_type *gsl_odeiv_step_gear2;

/* Constructor for specialized stepper objects.
 */
GSL_FUN gsl_odeiv_step * gsl_odeiv_step_alloc(const gsl_odeiv_step_type * T, size_t dim);
GSL_FUN int  gsl_odeiv_step_reset(gsl_odeiv_step * s);
GSL_FUN void gsl_odeiv_step_free(gsl_odeiv_step * s);

/* General stepper object methods.
 */
GSL_FUN const char * gsl_odeiv_step_name(const gsl_odeiv_step * s);
GSL_FUN unsigned int gsl_odeiv_step_order(const gsl_odeiv_step * s);

GSL_FUN int  gsl_odeiv_step_apply(gsl_odeiv_step * s, double t, double h, double y[], double yerr[], const double dydt_in[], double dydt_out[], const gsl_odeiv_system * dydt);

/* General step size control object.
 *
 * The hadjust() method controls the adjustment of
 * step size given the result of a step and the error.
 * Valid hadjust() methods must return one of the codes below.
 *
 * The general data can be used by specializations
 * to store state and control their heuristics.
 */

typedef struct
{
  const char * name;
  void * (*alloc) (void);
  int  (*init) (void * state, double eps_abs, double eps_rel, double a_y, double a_dydt);
  int  (*hadjust) (void * state, size_t dim, unsigned int ord, const double y[], const double yerr[], const double yp[], double * h);
  void (*free) (void * state);
}
gsl_odeiv_control_type;

typedef struct
{
  const gsl_odeiv_control_type * type;
  void * state;
}
gsl_odeiv_control;

/* Possible return values for an hadjust() evolution method.
 */
#define GSL_ODEIV_HADJ_INC   1  /* step was increased */
#define GSL_ODEIV_HADJ_NIL   0  /* step unchanged     */
#define GSL_ODEIV_HADJ_DEC (-1) /* step decreased     */

GSL_FUN gsl_odeiv_control * gsl_odeiv_control_alloc(const gsl_odeiv_control_type * T);
GSL_FUN int gsl_odeiv_control_init(gsl_odeiv_control * c, double eps_abs, double eps_rel, double a_y, double a_dydt);
GSL_FUN void gsl_odeiv_control_free(gsl_odeiv_control * c);
GSL_FUN int gsl_odeiv_control_hadjust (gsl_odeiv_control * c, gsl_odeiv_step * s, const double y[], const double yerr[], const double dydt[], double * h);
GSL_FUN const char * gsl_odeiv_control_name(const gsl_odeiv_control * c);

/* Available control object constructors.
 *
 * The standard control object is a four parameter heuristic
 * defined as follows:
 *    D0 = eps_abs + eps_rel * (a_y |y| + a_dydt h |y'|)
 *    D1 = |yerr|
 *    q  = consistency order of method (q=4 for 4(5) embedded RK)
 *    S  = safety factor (0.9 say)
 *
 *                      /  (D0/D1)^(1/(q+1))  D0 >= D1
 *    h_NEW = S h_OLD * |
 *                      \  (D0/D1)^(1/q)      D0 < D1
 *
 * This encompasses all the standard error scaling methods.
 *
 * The y method is the standard method with a_y=1, a_dydt=0.
 * The yp method is the standard method with a_y=0, a_dydt=1.
 */

GSL_FUN gsl_odeiv_control * gsl_odeiv_control_standard_new(double eps_abs, double eps_rel, double a_y, double a_dydt);
GSL_FUN gsl_odeiv_control * gsl_odeiv_control_y_new(double eps_abs, double eps_rel);
GSL_FUN gsl_odeiv_control * gsl_odeiv_control_yp_new(double eps_abs, double eps_rel);

/* This controller computes errors using different absolute errors for
 * each component
 *
 *    D0 = eps_abs * scale_abs[i] + eps_rel * (a_y |y| + a_dydt h |y'|)
 */
GSL_FUN gsl_odeiv_control * gsl_odeiv_control_scaled_new(double eps_abs, double eps_rel, double a_y, double a_dydt, const double scale_abs[], size_t dim);

/* General evolution object.
 */
typedef struct {
  size_t dimension;
  double * y0;
  double * yerr;
  double * dydt_in;
  double * dydt_out;
  double last_step;
  unsigned long int count;
  unsigned long int failed_steps;
}
gsl_odeiv_evolve;

/* Evolution object methods.
 */
GSL_FUN gsl_odeiv_evolve * gsl_odeiv_evolve_alloc(size_t dim);
GSL_FUN 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[]);
GSL_FUN int gsl_odeiv_evolve_reset(gsl_odeiv_evolve * e);
GSL_FUN void gsl_odeiv_evolve_free(gsl_odeiv_evolve * e);

__END_DECLS

#endif /* __GSL_ODEIV_H__ */


/*
 * author: Achim Gaedke
 * created: January 2002
 * file: pygsl/src/multiminmodule.c
 * $Id: multiminmodule.c,v 1.5 2004/03/08 08:42:16 schnizer Exp $
 */
#include <setjmp.h>
#include <pygsl/block_helpers.h>
#include <pygsl/error_helpers.h>
#include <pygsl/function_helpers.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_multimin.h>
#include "multiminmodule_doc.h"
/*
 * I have two different types to minimize and I am too lazy to implement the same
 * twice. Therefore I use unions and add a flag to the minimizer for the type
 */

union pygsl_multimin_minimizer_type{
     const gsl_multimin_fminimizer_type   *f;
     const gsl_multimin_fdfminimizer_type *fdf;
};

union pygsl_multimin_minimizer{
     gsl_multimin_fminimizer   *f;
     gsl_multimin_fdfminimizer *fdf;
};

union pygsl_multimin_func{
     gsl_multimin_function     *f;
     gsl_multimin_function_fdf *fdf;
};

struct pygsl_solver_types{
     const char * f_minimizer;
     const char * fdf_minimizer;
};
static struct pygsl_solver_types my_solvers = {"F-Minimizer", "Fdf-Minimizer"};

/*
 *  type:
 *	 == 0 -> f
 *	 != 0 -> fdf
 */
typedef struct {
     PyObject_HEAD
     PyObject* py_f;
     PyObject* py_df;
     PyObject* py_fdf;
     PyObject* trailing_params;
     union pygsl_multimin_func      func;
     union pygsl_multimin_minimizer min;
     size_t n;
     const char *mytype;
     int isset; /* Used as a flag if the jmp_buf is set */
     jmp_buf buffer;
} PyGSL_multimin;

#define PyGSL_multimin_check(op)  ((op)->ob_type == &PyGSL_multimin_pytype)
#define PyGSL_multimin_isf(op)    ((op)->mytype  == my_solvers.f_minimizer)

static void
PyGSL_multimin_dealloc(PyGSL_multimin* self);
static PyObject*
PyGSL_multimin_getattr(PyGSL_multimin * obj, char *name);

PyTypeObject PyGSL_multimin_pytype = {
  PyObject_HEAD_INIT(NULL)	 /* fix up the type slot in initcrng */
  0,				 /* ob_size */
  "PyGSL_multimin",		 /* tp_name */
  sizeof(PyGSL_multimin),	 /* tp_basicsize */
  0,				 /* tp_itemsize */

  /* standard methods */
  (destructor)  PyGSL_multimin_dealloc, /* tp_dealloc  ref-count==0  */
  (printfunc)   0,		 /* tp_print    "print x"     */
  (getattrfunc) PyGSL_multimin_getattr,/* tp_getattr  "x.attr"      */
  (setattrfunc) 0,		 /* tp_setattr  "x.attr=v"    */
  (cmpfunc)     0,		   /* tp_compare  "x > y"       */
  (reprfunc)    0,                 /* tp_repr     `x`, print x  */

  /* type categories */
  0,				/* tp_as_number   +,-,*,/,%,&,>>,pow...*/
  0,				/* tp_as_sequence +,[i],[i:j],len, ...*/
  0,				/* tp_as_mapping  [key], len, ...*/

  /* more methods */
  (hashfunc)     0,		/* tp_hash    "dict[x]" */
  (ternaryfunc)  0,      /* tp_call    "x()"     */
  (reprfunc)     0,             /* tp_str     "str(x)"  */
  (getattrofunc) 0,		/* tp_getattro */
  (setattrofunc) 0,		/* tp_setattro */
  0,				/* tp_as_buffer */
  0L,				/* tp_flags */
  (char *) PyGSL_multimin_type_doc		/* tp_doc */
};

/* Reference to this module */
PyObject *module = NULL;
static const char filename[] = __FILE__;

/* The Callbacks */
double
PyGSL_multimin_function_f(const gsl_vector* x, void* params)
{
     double result;
     int flag;
     int i;

     FUNC_MESS_BEGIN();
     PyGSL_multimin *min_o;
     min_o = (PyGSL_multimin *) params;
     assert(PyGSL_multimin_check(min_o));
     for(i = 0; i<x->size; i++){
	  DEBUG_MESS(2, "Got a x[%d] of %f", i, gsl_vector_get(x, i));
     }
     flag = PyGSL_function_wrap_On_O(x, min_o->py_f, min_o->trailing_params, &result,
				     NULL, x->size, __FUNCTION__);
     if (flag!= GSL_SUCCESS){
	  result = gsl_nan();
	  if(min_o->isset == 1) longjmp(min_o->buffer,flag);
     }
     DEBUG_MESS(2, "Got a result of %f", result);
     FUNC_MESS_END();
     return result;
}

void
PyGSL_multimin_function_df(const gsl_vector* x, void* params, gsl_vector *df)
{
     int flag, i;
     PyGSL_multimin *min_o;
     min_o = (PyGSL_multimin *) params;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(min_o));
     for(i = 0; i<x->size; i++){
	  DEBUG_MESS(2, "Got a x[%d] of %f", i, gsl_vector_get(x, i));
     }
     flag = PyGSL_function_wrap_Op_On(x, df, min_o->py_df, min_o->trailing_params,
				      x->size, x->size, __FUNCTION__);
     for(i = 0; i<df->size; i++){
	  DEBUG_MESS(2, "Got df x[%d] of %f", i, gsl_vector_get(df, i));
     }
     if(flag!=GSL_SUCCESS){
	  if(min_o->isset == 1) longjmp(min_o->buffer,flag);
     }
     FUNC_MESS_END();
}
void
PyGSL_multimin_function_fdf(const gsl_vector* x, void* params, double *f, gsl_vector *df)
{
     int flag, i;
     PyGSL_multimin *min_o;

     FUNC_MESS_BEGIN();
     min_o = (PyGSL_multimin *) params;
     assert(PyGSL_multimin_check(min_o));
     for(i = 0; i<x->size; i++){
	  DEBUG_MESS(2, "Got a x[%d] of %f", i, gsl_vector_get(x, i));
     }
     flag = PyGSL_function_wrap_On_O(x, min_o->py_fdf, min_o->trailing_params, f,
				     df, x->size, __FUNCTION__);
     DEBUG_MESS(2, "Got a result of %f", *f);
     for(i = 0; i<df->size; i++){
	  DEBUG_MESS(2, "Got df x[%d] of %f", i, gsl_vector_get(df, i));
     }
     if (flag!= GSL_SUCCESS){
	  *f = gsl_nan();
	  if(min_o->isset == 1) longjmp(min_o->buffer,flag);
     }
     FUNC_MESS_END();
     return;
}

static PyObject*
PyGSL_multimin_set_f(PyGSL_multimin *self, PyObject *args, PyObject *kw)
{

     PyObject* func = NULL, * params = NULL, * x = NULL, * steps = NULL;
     PyArrayObject * xa = NULL, * stepsa = NULL;
     int n=0, flag=GSL_EFAILED;
     int stride_recalc;
     gsl_vector_view gsl_x;
     gsl_vector_view gsl_steps;
     static const char *kwlist[] = {"f", "x0", "args", "steps", NULL};

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

     assert(args);
     /* arguments PyFunction, Parameters, start Vector, step Vector */
     if (0==PyArg_ParseTupleAndKeywords(args,kw,"OOOO", (char **) kwlist, &func,&x,&params,&steps))
	  return NULL;

     if(!PyCallable_Check(func)){
	  gsl_error("First argument must be callable",  filename, __LINE__ - 3, GSL_EBADFUNC);
	  return NULL;
     }
     n=self->n;
     xa  = PyGSL_PyArray_PREPARE_gsl_vector_view(x, PyArray_DOUBLE, 0, n, 4, NULL);
     if (xa == NULL){
	  PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__ - 1);
	  goto fail;
     }
     if(PyGSL_STRIDE_RECALC(xa->strides[0],sizeof(double), &stride_recalc) != GSL_SUCCESS)
	  goto fail;

     gsl_x = gsl_vector_view_array_with_stride((double *)(xa->data), stride_recalc, xa->dimensions[0]);
     stepsa =  PyGSL_PyArray_PREPARE_gsl_vector_view(steps, PyArray_DOUBLE, 0, n, 5, NULL);
     if (stepsa == NULL){
	  PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__ - 1);
	  goto fail;
     }
     if(PyGSL_STRIDE_RECALC(stepsa->strides[0],sizeof(double), &stride_recalc) != GSL_SUCCESS)
	  goto fail;

     gsl_steps = gsl_vector_view_array_with_stride((double *)(stepsa->data), stride_recalc, stepsa->dimensions[0]);

     if (self->func.f != NULL) {
	  /* free the previous function and params */
	  Py_XDECREF(self->trailing_params);
	  Py_XDECREF(self->py_f);
     } else {
	  /* allocate function space */
	  self->func.f=calloc(1, sizeof(gsl_multimin_function));
	  if (self->func.f==NULL) {
	       gsl_error("Could not allocate the object for the minimizer function",
			 filename, __LINE__ - 3, GSL_ENOMEM);
	       goto fail;
	  }
     }
     /* add new function  and parameters */
     self->trailing_params=params;
     Py_INCREF(params);
     self->py_f=func;
     Py_INCREF(func);

     /* initialize the function struct */
     self->func.f->n=n;
     self->func.f->f=PyGSL_multimin_function_f;
     self->func.f->params=(void*)self;

     if((flag = setjmp(self->buffer)) == 0){
	  self->isset = 1;
	  flag = gsl_multimin_fminimizer_set(self->min.f,self->func.f, &gsl_x.vector, &gsl_steps.vector);
	  if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS){
	       goto fail;
	  }
     } else {
	  goto fail;
     }
     Py_DECREF(xa);
     Py_DECREF(stepsa);

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

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

}
static PyObject*
PyGSL_multimin_set_fdf(PyGSL_multimin *self, PyObject *args, PyObject *kw)
{

     PyObject * f = NULL, * df = NULL, * fdf = NULL, * params = NULL,
	      * x = NULL;
     PyArrayObject * xa = NULL;
     int n=0, flag=GSL_EFAILED;
     int stride_recalc=-1;
     double step=0.01, tol=1e-4;
     gsl_vector_view gsl_x;
     static const char *kwlist[] = {"f", "df", "fdf",  "x0", "args", "step", "tol", NULL};

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if (self->min.fdf == NULL) {
	  gsl_error("Got a NULL Pointer of min.fdf", filename, __LINE__ - 3, GSL_EFAULT);
	       return NULL;
	  }

     /* arguments PyFunction, Parameters, start Vector, step Vector */
     if (0==PyArg_ParseTupleAndKeywords(args, kw, "OOOO|Odd", (char **)kwlist,
					&f, &df, &fdf, &x, &params, &step, &tol))
	  return NULL;

     n=self->n;
     xa  = PyGSL_PyArray_PREPARE_gsl_vector_view(x, PyArray_DOUBLE, 0, n, 4, NULL);
     if (xa == NULL){
	  PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__ - 1);
	  goto fail;
     }
     if(params == NULL){
          /* Reference counter increased later, when the parameters are set */
	  params = Py_None;
     }
     if(PyGSL_STRIDE_RECALC(xa->strides[0],sizeof(double), &stride_recalc) != GSL_SUCCESS)
	  goto fail;
     gsl_x = gsl_vector_view_array_with_stride((double *)(xa->data), stride_recalc, xa->dimensions[0]);

     if (self->func.fdf != NULL) {
	  /* free the previous function and params */
	  Py_XDECREF(self->trailing_params);
	  Py_XDECREF(self->py_f);
	  Py_XDECREF(self->py_df);
	  Py_XDECREF(self->py_fdf);
     } else {
	  /* allocate function space */
	  self->func.fdf=malloc(sizeof(gsl_multimin_function_fdf));
	  if (self->func.fdf==NULL) {
	       gsl_error("Could not allocate the object for the minimizer function",
			 filename, __LINE__ - 3, GSL_ENOMEM);
	       goto fail;
	  }
     }
     /* add new function  and parameters */
     self->trailing_params=params;     Py_INCREF(params);
     self->py_f=f;                     Py_INCREF(f);
     self->py_df=df;                   Py_INCREF(df);
     self->py_fdf=fdf;                 Py_INCREF(fdf);

     /* initialize the function struct */
     self->func.fdf->n=n;
     self->func.fdf->f  =PyGSL_multimin_function_f;
     self->func.fdf->df =PyGSL_multimin_function_df;
     self->func.fdf->fdf=PyGSL_multimin_function_fdf;
     self->func.fdf->params=(void*)self;

     if((flag = setjmp(self->buffer)) == 0){
	  self->isset = 1;
	  flag = gsl_multimin_fdfminimizer_set(self->min.fdf,self->func.fdf, &gsl_x.vector, step, tol);
	  if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS){
	       goto fail;
	  }
     }else{
	  goto fail;
     }
     self->isset = 0;
     Py_DECREF(xa);

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

 fail:
     PyGSL_ERROR_FLAG(flag);
     self->isset = 0;
     Py_XDECREF(xa);
     return NULL;

}
/*
static PyObject*
PyGSL_multimin_set(PyGSL_multimin *self, PyObject *args)
{

     if(PyGSL_multimin_isf(self)){
	  return PyGSL_multimin_set_f(self, args);
     }else {
	  return PyGSL_multimin_set_fdf(self, args);
     }
}
*/
static PyObject*
PyGSL_multimin_iterate(PyGSL_multimin *self, PyObject *args)
{
     int result, flag;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if ( self->min.f ==NULL || self->func.f ==NULL) {
	  gsl_error("Got a NULL Pointer of min.f", filename, __LINE__ - 3, GSL_EFAULT);
	  return NULL;
     }

     if((flag = setjmp(self->buffer)) == 0){
	  self->isset = 1;
	  if(PyGSL_multimin_isf(self)){
	       result = gsl_multimin_fminimizer_iterate(self->min.f);
	  } else {
	       result = gsl_multimin_fdfminimizer_iterate(self->min.fdf);
	  }
     } else {
	  PyGSL_ERROR_FLAG(flag);
	  self->isset = 0;
	  return NULL;
     }
     self->isset = 0;
     FUNC_MESS_END();
     return PyGSL_error_flag_to_pyint(result);
}

static PyObject*
PyGSL_multimin_x(PyGSL_multimin *self, PyObject *args)
{
     gsl_vector* result;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if (self->min.f==NULL || self->func.f==NULL) {
	  gsl_error("Got a NULL Pointer for min.f", filename, __LINE__ - 3, GSL_EFAULT);
	  return NULL;
     }
     if(PyGSL_multimin_isf(self)){
	  result=gsl_multimin_fminimizer_x(self->min.f);
     } else {
	  result=gsl_multimin_fdfminimizer_x(self->min.fdf);
     }
     if (result==NULL) {
	  gsl_error("How could that happen?",  filename, __LINE__ - 3, GSL_ESANITY);
	  return NULL;
     }
     FUNC_MESS_END();
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(result);
}

static PyObject*
PyGSL_multimin_minimum(PyGSL_multimin *self, PyObject *args)
{
     double min;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if (self->min.f == NULL || self->func.f ==NULL) {
	  gsl_error("Got a NULL Pointer of min.f", filename, __LINE__ - 3, GSL_EFAULT);
	  return NULL;
     }
     if(PyGSL_multimin_isf(self)){
	  min = gsl_multimin_fminimizer_minimum(self->min.f);
     } else {
	  min = gsl_multimin_fdfminimizer_minimum(self->min.fdf);
     }
     FUNC_MESS_END();
     return PyFloat_FromDouble(min);
}

static PyObject*
PyGSL_multimin_name(PyGSL_multimin *self, PyObject *args)
{
     const char * name;
     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  name = gsl_multimin_fminimizer_name(self->min.f);
     } else {
	  name = gsl_multimin_fdfminimizer_name(self->min.fdf);
     }
     FUNC_MESS_END();
     return PyString_FromString(name);
}

static PyObject*
PyGSL_multimin_size(PyGSL_multimin *self, PyObject *args)
{
     double size;
     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if ( self->min.f ==NULL || self->func.f ==NULL) {
	  PyErr_SetString(PyExc_RuntimeError,"no function specified!");
	  return NULL;
     }
     if(PyGSL_multimin_isf(self)){
	  size = gsl_multimin_fminimizer_size(self->min.f);
     } else {
	  gsl_error("Can not calculate size for a FDF", filename, __LINE__, GSL_ESANITY);
	  return NULL;
     }
     FUNC_MESS_END();
     return PyFloat_FromDouble(size);
}

static PyObject*
PyGSL_multimin_test_size_method(PyGSL_multimin *self, PyObject *args)
{
     int flag=GSL_EFAILED;
     double epsabs;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if ( self->min.f ==NULL || self->func.f ==NULL) {
	  PyErr_SetString(PyExc_RuntimeError,"no function specified!");
	  return NULL;
     }

     if(PyGSL_multimin_isf(self)){
	  if (0==PyArg_ParseTuple(args,"d", &epsabs))
	       return NULL;
	  flag = gsl_multimin_test_size(gsl_multimin_fminimizer_size(self->min.f), epsabs);
     } else {
	  gsl_error("Can not calculate size for a FDF", filename, __LINE__, GSL_ESANITY);
	  return NULL;
     }

     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);
}

static PyObject*
PyGSL_multimin_restart(PyGSL_multimin *self, PyObject *args)
{
     int flag;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  gsl_error("Can not restart for a F type solver", filename, __LINE__, GSL_ESANITY);
	  return NULL;
     }
     flag = gsl_multimin_fdfminimizer_restart(self->min.fdf);

     if(PyGSL_ERROR_FLAG(flag) != GSL_SUCCESS){
	  return NULL;
     }
     FUNC_MESS_END();
     Py_INCREF(Py_None);
     return Py_None;
}
static PyObject*
PyGSL_multimin_vec_fdf(PyGSL_multimin *self, PyObject *args,
		       gsl_vector *(*func)(gsl_multimin_fdfminimizer * s))
{
     gsl_vector *result;
     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  gsl_error("Can not retrieve this information for a F type solver!", filename, __LINE__, GSL_ESANITY);
	  return NULL;
     } else {
	  result=func(self->min.fdf);
     }
     FUNC_MESS_END();
     return (PyObject *) PyGSL_copy_gslvector_to_pyarray(result);
}

static PyObject*
PyGSL_multimin_istype(PyGSL_multimin *self, PyObject *args)
{
     static const char *f = "F-Minimizer",  *fdf = "Fdf-Minimizer";
     const char *p;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  p = f;
     } else {
	  p = fdf;
     }
     FUNC_MESS_END();
     return PyString_FromString(p);
}

static PyObject*
PyGSL_multimin_dx(PyGSL_multimin *self, PyObject *args)
{
     return PyGSL_multimin_vec_fdf(self, args, gsl_multimin_fdfminimizer_dx);
}

static PyObject*
PyGSL_multimin_gradient(PyGSL_multimin *self, PyObject *args)
{
     return PyGSL_multimin_vec_fdf(self, args, gsl_multimin_fdfminimizer_gradient);
}

static PyObject*
PyGSL_multimin_test_gradient_method(PyGSL_multimin * self, PyObject *args)
{

     double epsabs;
     int flag;
     FUNC_MESS_BEGIN();

     assert(PyGSL_multimin_check(self));
     if (0==PyArg_ParseTuple(args,"d", &epsabs))
	  return NULL;

     if(PyGSL_multimin_isf(self)){
	  gsl_error("Can not retrieve this information for a F type solver!", filename, __LINE__, GSL_ESANITY);
	  return NULL;
     }
     flag = gsl_multimin_test_gradient(gsl_multimin_fdfminimizer_gradient(self->min.fdf), epsabs);
     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);

}

#define PyGSL_MULTIMIN_COMMON_METHODS  \
  {"iterate",     (PyCFunction)PyGSL_multimin_iterate,METH_NOARGS,(char *)multimin_iterate_doc,}, \
  {"x",           (PyCFunction)PyGSL_multimin_x,      METH_NOARGS,(char *)multimin_x_doc,      }, \
  {"minimum",     (PyCFunction)PyGSL_multimin_minimum,METH_NOARGS,(char *)multimin_minimum_doc,}, \
  {"name",        (PyCFunction)PyGSL_multimin_name,   METH_NOARGS,(char *)multimin_name_doc,   }, \
  {"type",        (PyCFunction)PyGSL_multimin_istype, METH_NOARGS,(char *)multimin_istype_doc, },

static PyMethodDef PyGSL_multimin_fmethods[] = {
     PyGSL_MULTIMIN_COMMON_METHODS
     {"set",      (PyCFunction)PyGSL_multimin_set_f,           METH_VARARGS|METH_KEYWORDS, (char *)multimin_set_f_doc    },
     {"size",     (PyCFunction)PyGSL_multimin_size,            METH_NOARGS,  (char *)multimin_size_doc     },
     {"test_size",(PyCFunction)PyGSL_multimin_test_size_method,METH_VARARGS, (char *)multimin_test_size_doc},
     {NULL, NULL, 0, NULL}           /* sentinel */
};

static PyMethodDef PyGSL_multimin_fdfmethods[] = {
     PyGSL_MULTIMIN_COMMON_METHODS
     {"set",      (PyCFunction)PyGSL_multimin_set_fdf,                 METH_VARARGS|METH_KEYWORDS, (char *)multimin_set_fdf_doc      },
     {"restart",  (PyCFunction)PyGSL_multimin_restart,                 METH_NOARGS,  (char *)multimin_restart_doc      },
     {"dx",       (PyCFunction)PyGSL_multimin_dx,                      METH_NOARGS,  (char *)multimin_dx_doc           },
     {"gradient", (PyCFunction)PyGSL_multimin_gradient,                METH_NOARGS,  (char *)multimin_gradient_doc     },
     {"test_gradient",(PyCFunction)PyGSL_multimin_test_gradient_method,METH_VARARGS, (char *)multimin_test_gradient_doc},
     {NULL, NULL, 0, NULL}           /* sentinel */
};

static PyObject*
PyGSL_multimin_init(PyObject *self, PyObject *args,
		    union pygsl_multimin_minimizer_type type, int t)
{

     PyGSL_multimin *min_o=NULL;
     size_t n;
     /* static const char functionname [] = __FUNCTION__; */

     FUNC_MESS_BEGIN();
     min_o =  (PyGSL_multimin *) PyObject_NEW(PyGSL_multimin, &PyGSL_multimin_pytype);
     if(min_o == NULL){
	  return NULL;
     }

     if (0==PyArg_ParseTuple(args,"l", &n))
	  return NULL;

     if (n<=0) {
	  PyErr_SetString(PyExc_RuntimeError, "dimension must be >0");
	  return NULL;
     }
     min_o->n=n;
     min_o->min.f=NULL;
     min_o->min.fdf=NULL;
     min_o->func.f=NULL;
     min_o->func.fdf=NULL;
     min_o->py_f=NULL;
     min_o->py_df=NULL;
     min_o->py_fdf=NULL;
     min_o->trailing_params=NULL;
     min_o->mytype=NULL;

     if(t == 0){
	  min_o->min.f = gsl_multimin_fminimizer_alloc(type.f,n);
	  if (min_o->min.f == NULL) {
		gsl_error("Could not allocate the object for the minimizer",
			  filename, __LINE__ - 3, GSL_ENOMEM);
	       goto fail;
	  }
	  min_o->mytype = my_solvers.f_minimizer;
	  assert(PyGSL_multimin_isf(min_o));
     } else {
	  min_o->min.fdf = gsl_multimin_fdfminimizer_alloc(type.fdf,n);
	  if (min_o->min.fdf == NULL) {
		gsl_error("Could not allocate the object for the fdfminimizer",
			  filename, __LINE__ - 3, GSL_ENOMEM);
	       goto fail;
	  }
	  min_o->mytype = my_solvers.fdf_minimizer;
	  assert(!PyGSL_multimin_isf(min_o));
     }
     FUNC_MESS_END();
     return (PyObject *) min_o;
 fail:
     Py_XDECREF(min_o);
     return NULL;
}

#define AMINIMIZER(name)                                                      \
static PyObject* PyGSL_multimin_init_ ## name (PyObject *self, PyObject *args)\
{                                                                             \
     PyObject *tmp = NULL;                                                    \
     union pygsl_multimin_minimizer_type type;                                \
     FUNC_MESS_BEGIN();                                                       \
     type.f  = gsl_multimin_fminimizer_ ## name;                              \
     tmp = PyGSL_multimin_init(self, args, type, 0);                          \
     if (tmp == NULL){                                                        \
	  PyGSL_add_traceback(module, (char *) filename, __FUNCTION__, __LINE__);      \
     }                                                                        \
     FUNC_MESS_END();                                                         \
     return tmp;                                                              \
}
#define AMINIMIZER_FDF(name)                                                  \
static PyObject* PyGSL_multimin_init_ ## name (PyObject *self, PyObject *args)\
{                                                                             \
     PyObject *tmp = NULL;                                                    \
     union pygsl_multimin_minimizer_type type;                                \
     FUNC_MESS_BEGIN();                                                       \
     type.fdf  = gsl_multimin_fdfminimizer_ ## name;                          \
     tmp = PyGSL_multimin_init(self, args,  type, 1);                         \
     if (tmp == NULL){                                                        \
	  PyGSL_add_traceback(module, (char *) filename, __FUNCTION__, __LINE__);      \
     }                                                                        \
     FUNC_MESS_END();                                                         \
     return tmp;                                                              \
}

AMINIMIZER(nmsimplex)
AMINIMIZER_FDF(steepest_descent)
AMINIMIZER_FDF(vector_bfgs)
AMINIMIZER_FDF(conjugate_pr)
AMINIMIZER_FDF(conjugate_fr)

static void
PyGSL_multimin_dealloc(PyGSL_multimin *self)
{
     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  if (self->min.f  != NULL)   gsl_multimin_fminimizer_free(self->min.f);
	  if (self->func.f != NULL)  free(self->func.f);
     }else{
	  if (self->min.fdf  != NULL)   gsl_multimin_fdfminimizer_free(self->min.fdf);
	  if (self->func.fdf != NULL)  free(self->func.fdf);
     }

     Py_XDECREF(self->trailing_params);
     Py_XDECREF(self->py_f);
     Py_XDECREF(self->py_df);
     Py_XDECREF(self->py_fdf);
     PyMem_Free(self);
     FUNC_MESS_END();
}

static PyObject*
PyGSL_multimin_getattr(PyGSL_multimin *self, char *name)
{

     PyObject *tmp = NULL;

     FUNC_MESS_BEGIN();
     assert(PyGSL_multimin_check(self));
     if(PyGSL_multimin_isf(self)){
	  tmp = Py_FindMethod(PyGSL_multimin_fmethods, (PyObject *) self, name);
     } else {
	  tmp = Py_FindMethod(PyGSL_multimin_fdfmethods, (PyObject *) self, name);
     }
     FUNC_MESS_END();
     return tmp;
}

static PyObject*
PyGSL_multimin_test_size(PyObject * self, PyObject *args)
{
     double size, epsabs;
     int flag = GSL_EFAILED;
     FUNC_MESS_BEGIN();
     if (0==PyArg_ParseTuple(args,"dd", &size, &epsabs))
	  return NULL;
     flag = gsl_multimin_test_size(size, epsabs);
     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);

}

static PyObject*
PyGSL_multimin_test_gradient(PyObject * self, PyObject *args)
{
     PyObject *g=NULL;
     PyArrayObject *ga=NULL;
     gsl_vector_view gradient;

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

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

     ga  = PyGSL_PyArray_PREPARE_gsl_vector_view(g, PyArray_DOUBLE, 0, -1, 1, NULL);
     if (ga == NULL){
	  PyGSL_add_traceback(module, filename, __FUNCTION__, __LINE__ - 1);
	  return NULL;
     }
     if((PyGSL_STRIDE_RECALC(ga->strides[0],sizeof(double), &stride_recalc)) != GSL_SUCCESS){
	  Py_XDECREF(ga);
	  return NULL;
     }
     gradient = gsl_vector_view_array_with_stride((double *)(ga->data), stride_recalc, ga->dimensions[0]);

     flag = gsl_multimin_test_gradient(&gradient.vector, epsabs);
     FUNC_MESS_END();
     return PyGSL_ERROR_FLAG_TO_PYINT(flag);

}

static PyMethodDef multiminMethods[] = {
     {"nmsimplex",        PyGSL_multimin_init_nmsimplex,        METH_VARARGS, (char *)nmsimplex_doc       },
     {"steepest_descent", PyGSL_multimin_init_steepest_descent, METH_VARARGS, (char *)steepest_descent_doc},
     {"vector_bfgs",      PyGSL_multimin_init_vector_bfgs,      METH_VARARGS, (char *)vector_bfgs_doc     },
     {"conjugate_pr",     PyGSL_multimin_init_conjugate_pr,     METH_VARARGS, (char *)conjugate_pr_doc    },
     {"conjugate_fr",     PyGSL_multimin_init_conjugate_fr,     METH_VARARGS, (char *)conjugate_fr_doc    },
     {"test_size",        PyGSL_multimin_test_size,             METH_VARARGS, (char *)test_size_doc       },
     {"test_gradient",    PyGSL_multimin_test_gradient,         METH_VARARGS, (char *)test_gradient_doc   },
     {NULL, NULL, 0, NULL}        /* Sentinel */
};

void
initmultimin(void)
{
  PyObject* m, *dict, *item;
  m=Py_InitModule("multimin", multiminMethods);
  import_array();
  init_pygsl();
  /* init multimin type */
  PyGSL_multimin_pytype.ob_type  = &PyType_Type;

  module = m;

  Py_INCREF((PyObject*)&PyGSL_multimin_pytype);

  dict = PyModule_GetDict(m);
  if(!dict)
       goto fail;

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

 fail:
  return;
}