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


#ifndef CWANNIER_ECACHE_H
#define CWANNIER_ECACHE_H

#include <stdlib.h>
#include <stdbool.h>
#include <gsl/gsl_vector.h>
#include "input.h"

typedef struct {
    int na;
    int nb;
    int nc;
    int num_bands;
    int G_order[3];
    int G_neg[3];
    InputFn Efn;
    bool use_cache;
    gsl_vector **energies;
} EnergyCache;

#include "submesh.h"

EnergyCache* init_EnergyCache(int na, int nb, int nc, int num_bands, int G_order[3], int G_neg[3], InputFn Efn, bool use_cache);

void free_EnergyCache(EnergyCache *Ecache);

void energy_from_cache(EnergyCache *Ecache, int i, int j, int k, gsl_vector *energies);

void verify_sort(gsl_vector *energies);

#endif // CWANNIER_ECACHE_H


/* trav.c
 *
 * Copyright (C) 2018 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_bst.h>
#include <gsl/gsl_errno.h>

int
gsl_bst_trav_init(gsl_bst_trav * trav, const gsl_bst_workspace * w)
{
  int status = (w->type->trav_init)((void *) &trav->trav_data, (const void *) &w->table);
  trav->type = w->type;
  return status;
}

/*
gsl_bst_trav_first()
  Initialize traverser to least-valued item in tree and return
a pointer to it. Return NULL if tree has no nodes.
*/

void *
gsl_bst_trav_first(gsl_bst_trav * trav, const gsl_bst_workspace * w)
{
  trav->type = w->type;
  return (w->type->trav_first)((void *) &trav->trav_data, (const void *) &w->table);
}

/*
gsl_bst_trav_last()
  Initializes |trav| for |tree| and selects and returns a pointer
to its greatest-valued item. Returns NULL if |tree| contains no nodes.
*/

void *
gsl_bst_trav_last (gsl_bst_trav * trav, const gsl_bst_workspace * w)
{
  trav->type = w->type;
  return (w->type->trav_last)((void * ) &trav->trav_data, (const void *) &w->table);
}

/*
gsl_bst_trav_find()
  Searches for |item| in tree. If found, initializes |trav| to
the item found and returns the item as well.  If there is no matching
item, initializes |trav| to the null item and returns |NULL|.
*/

void *
gsl_bst_trav_find (const void * item, gsl_bst_trav * trav, const gsl_bst_workspace * w)
{
  trav->type = w->type;
  return (w->type->trav_find)(item, (void * ) &trav->trav_data, (const void *) &w->table);
}

/*
gsl_bst_trav_insert()
  Attempts to insert |item| into tree.  If |item| is inserted
successfully, it is returned and |trav| is initialized to its location.
If a duplicate is found, it is returned and |trav| is initialized to
its location.  No replacement of the item occurs.
If a memory allocation failure occurs, |NULL| is returned and |trav|
is initialized to the null item.
*/

void *
gsl_bst_trav_insert (void * item, gsl_bst_trav * trav, gsl_bst_workspace * w)
{
  trav->type = w->type;
  return (w->type->trav_insert)(item, (void * ) &trav->trav_data, (void *) &w->table);
}

/*
gsl_bst_trav_copy()
  Copy traverser 'src' into 'dest'
*/

void *
gsl_bst_trav_copy(gsl_bst_trav * dest, const gsl_bst_trav * src)
{
  dest->type = src->type;
  return (src->type->trav_copy)((void * ) &dest->trav_data, (const void *) &src->trav_data);
}

/*
gsl_bst_trav_next()
  Update traverser to point to next sequential node, and
return a pointer to its data
*/

void *
gsl_bst_trav_next(gsl_bst_trav * trav)
{
  return (trav->type->trav_next)((void *) &trav->trav_data);
}

/*
gsl_bst_trav_prev()
  Update traverser to point to previous sequential node, and
return a pointer to its data
*/

void *
gsl_bst_trav_prev(gsl_bst_trav * trav)
{
  return (trav->type->trav_prev)((void *) &trav->trav_data);
}

/*
gsl_bst_trav_cur()
  Return a pointer to data of current traverser node
*/

void *
gsl_bst_trav_cur(const gsl_bst_trav * trav)
{
  return (trav->type->trav_cur)((const void *) &trav->trav_data);
}

/*
gsl_bst_trav_replace()
  Replace current item in trav with new_item and returns the item
replaced. The new item must not change the ordering of the tree.
*/

void *
gsl_bst_trav_replace (gsl_bst_trav * trav, void * new_item)
{
  return (trav->type->trav_replace)((void * ) &trav->trav_data, new_item);
}


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

#ifndef _HEBench_ClearText_EltMult_H_7e5fa8c2415240ea93eff148ed73539b
#define _HEBench_ClearText_EltMult_H_7e5fa8c2415240ea93eff148ed73539b

#include <gsl/gsl>

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

#include "clear_benchmark.h"

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

public:
    EltMult_Benchmark(hebench::cpp::BaseEngine &engine,
                      const hebench::APIBridge::BenchmarkDescriptor &bench_desc,
                      const hebench::APIBridge::WorkloadParams &bench_params);
    ~EltMult_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 eltMult(gsl::span<T> &result,
                        const gsl::span<const T> &V0, const gsl::span<const T> &V1,
                        std::size_t element_count);
};

#include "inl/bench_eltmult.inl"

#endif // defined _HEBench_ClearText_EltMult_H_7e5fa8c2415240ea93eff148ed73539b


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


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

int
main (void)
{
  int i;
  gsl_vector * v = gsl_vector_alloc (100);

  for (i = 0; i < 100; i++)
    {
      gsl_vector_set (v, i, 1.23 + i);
    }

  {
     FILE * f = fopen ("test.dat", "w");
     gsl_vector_fprintf (f, v, "%.5g");
     fclose (f);
  }

  gsl_vector_free (v);
  return 0;
}


/*
 * Copyright (c) 2016 Kengo Tateishi (@tkengo)
 * Licensed under MIT license.
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * MxRuby is really inspired by numpy.
 *
 * This file is implementation of extended array functions for ruby. The MX class is defined on ruby
 * interpreter by compiling it.
 */
#include <ruby.h>
/* #include <cblas.h> */
#include "mx.h"

/*
 * MX class object in C. This is initialized in Init_mxruby function.
 */
VALUE rb_cMx;
VALUE rb_eDataTypeError;

MX *mxx_initialize(VALUE shape, DTYPE dtype)
{
    MX *mx = MX_ALLOC(MX);
    mxx_setup(mx, dtype);
    mxx_initialize_shape(mx, shape);

    if (mx->size > 0) {
        mx->elptr = MX_ALLOC_N(char, DTYPE_SIZES[mx->dtype] * mx->size);
    }

    return mx;
}

void mxx_setup(MX *mx, DTYPE dtype)
{
    mx->dim = 0;
    mx->size = 0;
    mx->dtype = dtype;
    mx->shape = NULL;
    mx->elptr = NULL;
}

void mxx_initialize_shape(MX *mx, VALUE shape)
{
    char shape_is_array = TYPE(shape) == T_ARRAY;

    mx->size = 1;
    mx->dim = shape_is_array ? RARRAY_LEN(shape) : 1;
    mx->shape = MX_ALLOC_N(size_t, mx->dim);

    for (int i = 0; i < mx->dim; i++) {
        mx->shape[i] = FIX2INT(shape_is_array ? RARRAY_AREF(shape, i) : shape);
        mx->size *= mx->shape[i];
    }
}

void mxx_free(MX *mx)
{
    if (mx->shape != NULL) {
        MX_FREE(mx->shape);
    }
    if (mx->elptr != NULL) {
        MX_FREE(mx->elptr);
    }

    MX_FREE(mx);
}

static VALUE mx_alloc(VALUE klass)
{
    MX *mx = MX_ALLOC(MX);
    mxx_setup(mx, DTYPE_FLOAT64);

    return Data_Wrap_Struct(klass, 0, mxx_free, mx);
}

static VALUE mx_initialize(VALUE self, VALUE shape, VALUE initial_value, VALUE opt)
{
    MX *mx;
    Data_Get_Struct(self, MX, mx);

    if (!NIL_P(opt)) {
        VALUE rb_dtype = rb_hash_aref(opt, ID2SYM(rb_intern("dtype")));
        if (!NIL_P(rb_dtype)) {
            mx->dtype = mxx_dtype_from_symbol(rb_dtype);
        }
    }

    mxx_initialize_shape(mx, shape);

    if (mx->size > 0) {
        mx->elptr = MX_ALLOC_N(char, DTYPE_SIZES[mx->dtype] * mx->size);

        if (TYPE(initial_value) == T_ARRAY) {
            for (int i = 0; i < mx->size; i++) {
                VALUE v = RARRAY_AREF(initial_value, i);
                mxx_rb_to_c(mx->elptr + i * DTYPE_SIZES[mx->dtype], mx->dtype, v);
            }
        } else if (!NIL_P(initial_value)) {
            for (int i = 0; i < mx->size; i++) {
                mxx_rb_to_c(mx->elptr + i * DTYPE_SIZES[mx->dtype], mx->dtype, initial_value);
            }
        }
    }

    return self;
}

static VALUE mx_shape(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    VALUE *shape = MX_ALLOC_N(VALUE, mx->dim);
    for (int i = 0; i < mx->dim; i++) {
        shape[i] = INT2FIX(mx->shape[i]);
    }

    return rb_ary_new4(mx->dim, shape);
}

static VALUE mx_dim(VALUE self)
{
    return INT2FIX(MX_DATA_DIM(self));
}

static VALUE mx_size(VALUE self)
{
    return INT2FIX(MX_DATA_SIZE(self));
}

static VALUE mx_dtype(VALUE self)
{
    return ID2SYM(rb_intern(DTYPE_NAMES[MX_DATA_PTR(self)->dtype]));
}

static VALUE mx_dot(VALUE self, VALUE target)
{
    int N = 2;
    MX *mx = MX_DATA_PTR(self);
    void *c = MX_ALLOC_N(char, mx->shape[0] * mx->shape[1] * DTYPE_SIZES[mx->dtype]);

    /* cblas_sgemm( */
    /*         CblasRowMajor, CblasNoTrans, CblasNoTrans, */
    /*         N, N, N, */
    /*         1, mx->elptr, N, */
    /*         MX_DATA_ELPTR(target), N, */
    /*         0, c, N */
    /*         ); */

    MX_FREE(mx->elptr);
    mx->elptr = c;
    return self;
}

static VALUE mx_astype(int argc, VALUE *argv, VALUE self)
{
    VALUE rb_new_dtype, rb_opt;
    rb_scan_args(argc, argv, "1:", &rb_new_dtype, &rb_opt);

    DTYPE new_dtype = mxx_dtype_from_symbol(rb_new_dtype);
    MX *dest = MX_ALLOC(MX);
    mxx_copy_cast(MX_DATA_PTR(self), dest, new_dtype);

    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, dest);
}

static void mxs_to_a(VALUE *ary, size_t shape_index, size_t array_index, MX *mx)
{
    if (shape_index == mx->dim - 1) {
        for (int i = 0; i < mx->shape[shape_index]; i++) {
            void *p = (char *)mx->elptr + (i + array_index) * DTYPE_SIZES[mx->dtype];
            ary[i] = mxx_c_to_rb(p, mx->dtype);
        }
    } else {
        size_t skip = 1;
        for (int i = shape_index + 1; i < mx->dim; i++) {
            skip *= mx->shape[i];
        }

        for (int i = 0; i < mx->shape[shape_index]; i++) {
            VALUE *v = MX_ALLOC_N(VALUE, mx->shape[shape_index + 1]);
            mxs_to_a(v, shape_index + 1, skip * i + array_index, mx);
            ary[i] = rb_ary_new4(mx->shape[shape_index + 1], v);
        }
    }
}

static VALUE mx_to_a(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    VALUE *ary = MX_ALLOC_N(VALUE, mx->shape[0]);
    mxs_to_a(ary, 0, 0, mx);
    return rb_ary_new4(mx->shape[0], ary);
}

static VALUE mx_flatten(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    VALUE *el = MX_ALLOC_N(VALUE, mx->size);
    size_t dsize = DTYPE_SIZES[mx->dtype];
    for (int i = 0; i < mx->size; i++) {
        el[i] = mxx_c_to_rb((char *)mx->elptr + i * dsize, mx->dtype);
    }
    return rb_ary_new4(mx->size, el);
}

static VALUE mx_first(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    if (mx->size > 0) {
        return mxx_c_to_rb(mx->elptr, mx->dtype);
    }
    return Qnil;
}

static VALUE mx_second(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    if (mx->size > 1) {
        return mxx_c_to_rb(mx->elptr + DTYPE_SIZES[mx->dtype], mx->dtype);
    }
    return Qnil;
}

static VALUE mx_last(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    if (mx->size > 0) {
        return mxx_c_to_rb(mx->elptr + (mx->size - 1) * DTYPE_SIZES[mx->dtype], mx->dtype);
    }
    return Qnil;
}

static VALUE mx_reshape(VALUE self, VALUE shape)
{
    MX *mx = MX_DATA_PTR(self);

    MX *reshaped_mx = MX_ALLOC(MX);
    mxx_initialize_shape(reshaped_mx, shape);

    if (mx->size != reshaped_mx->size) {
        rb_raise(rb_eArgError, "Cannot do reshape operation between shape size of [ %ld ] and [ %ld ]. Total size of new array must be unchanged.", mx->size, reshaped_mx->size);
    }

    reshaped_mx->dtype = mx->dtype;
    reshaped_mx->elptr = MX_ALLOC_N(char, reshaped_mx->size * DTYPE_SIZES[reshaped_mx->dtype]);

    mxx_copy_elptr(mx, reshaped_mx);
    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, reshaped_mx);
}

static VALUE mx_sum(int argc, VALUE *argv, VALUE self)
{
    MX *mx = MX_DATA_PTR(self);

    if (DTYPE_IS_INT(mx->dtype)) {
        long sum = (long)mxx_sum(mx);
        return INT2FIX(sum);
    } else {
        return rb_float_new(mxx_sum(mx));
    }
}

static VALUE mx_is_empty(VALUE self)
{
    MX *mx = MX_DATA_PTR(self);
    return mx->size == 0 ? Qtrue : Qfalse;
}

static VALUE mx_set(int argc, VALUE *argv, VALUE self)
{
    return self;
}

/**
 * Slice the `src` matrix, then the sliced matrix will be stored into the dest matrix.
 * @params src   Source matrix to be sliced
 * @params dest  Destination matrix
 * @params src_pos   Copy start position in the source matrix
 * @params count Copy element count
 * @params depth ?
 * @params idx
 * @params idx_len
 */
static void _mxs_slice(MX *src, MX *dest, VALUE idx, size_t idx_len, int depth, size_t size,
        size_t src_pos, size_t *dest_pos)
{
    if (depth == idx_len) {
        mxx_copy_by_size(
            MX_DATA_POS(src,  src_pos),
            MX_DATA_POS(dest, *dest_pos),
            size,
            dest->dtype
        );

        *dest_pos += size;
    } else {
        size_t skip = 1;
        for (int i = depth + 1; i < src->dim; i++) {
            skip *= src->shape[i];
        }

        long beg, len;
        VALUE v = RARRAY_AREF(idx, depth);
        if (FIXNUM_P(v)) {
            _mxs_slice(src, dest, idx, idx_len, depth + 1, skip, src_pos + FIX2INT(v) * skip, dest_pos);
        } else if (rb_range_beg_len(v, &beg, &len, src->shape[depth], 0) == Qtrue) {
            for (long i = 0; i < len; i++) {
                _mxs_slice(src, dest, idx, idx_len, depth + 1, skip, src_pos + (beg + i) * skip, dest_pos);
            }
        }
    }
}

static MX *mxs_slice(MX *src, VALUE idx, size_t idx_len)
{
    /**
     * mx = [ [ [1,1], [2,2], [3,3], [4,4] ],
     *        [ [0,0], [0,0], [0,0], [0,0] ],
     *        [ [5,5], [6,6], [7,7], [8,8] ] ], shape = [3, 4, 2]
     *
     * mx[0]    =   [ [1,1], [2,2], [3,3], [4,4] ],   shape = [4, 2]
     * mx[0..1] = [ [ [1,1], [2,2], [3,3], [4,4] ],
     *              [ [0,0], [0,0], [0,0], [0,0] ] ], shape = [2, 4, 2]
     *
     * mx[0, 0..1] = [ [1,1], [2,2] ],   shape = [2, 2]
     *
     * mx[0..1, 0]    = [ [1,1],
     *                    [0,0] ], shape = [2, 2]
     * mx[0..1, 0..1] = [ [ [1,1], [2,2] ],
     *                    [ [0,0], [0,0] ] ], shape = [2, 2, 2]
     *
     * mx[0..1, 0..1, 0] = [ [ 1, 2 ],
     *                       [ 0, 0 ] ], shape = [2, 2]
     *
     * mx = [ [1, 2, 3],
     *        [4, 5, 6],
     *        [7, 8, 9] ], shape = [3, 3]
     *
     * mx[0]         =   [1, 2, 3],   shape = [3]
     * mx[0..1]      = [ [1, 2, 3]
     *                   [4, 5, 6] ], shape = [2, 3]
     * mx[0..1, 0]   = [ 1
     *                   4 ],         shape = [2, 1]
     * mx[0..1, [0]] = [ [1]
       *                 [4] ],       shape = [2, 1]
     *
     * mx[0, 0]    =     1,  shape = nil
     * mx[0, 0..1] = [1, 2], shape = [2]
     *
     * mx[[0]]    = [ [1, 2, 3] ], shape = [1, 3]
     * mx[0..0]   = [ [1, 2, 3]
     *                [4, 5, 6] ], shape = [2, 3]
     * mx[[0, 1]] = [ [1, 2, 3]
     *                [4, 5, 6] ], shape = [2, 3]
     *
     * 引数がFIXNUMだったらdimが1個減る
     * 引数がrangeまたはarrayだったら次元は減らずにshapeがその長さになる
     */
    long beg, len;
    size_t new_dim = 0;
    size_t *tmp = MX_ALLOC_N(size_t, src->dim);
    for (int i = 0; i < src->dim; i++) {
        if (i < idx_len) {
            VALUE v = RARRAY_AREF(idx, i);
            if (rb_range_beg_len(v, &beg, &len, src->shape[i], 0) == Qtrue) {
                tmp[new_dim++] = len;
            }
        } else {
            tmp[new_dim++] = src->shape[i];
        }
    }

    VALUE *new_shape = MX_ALLOC_N(VALUE, new_dim);
    for (int i = 0; i < new_dim; i++) {
        new_shape[i] = INT2FIX(tmp[i]);
    }

    MX *new_mx = MX_ALLOC(MX);
    mxx_initialize_shape(new_mx, rb_ary_new4(new_dim, new_shape));
    new_mx->dtype = src->dtype;
    new_mx->elptr = MX_ALLOC_N(char, new_mx->size * DTYPE_SIZES[new_mx->dtype]);
    MX_FREE(tmp);

    size_t dest_pos = 0;
    _mxs_slice(src, new_mx, idx, idx_len, 0, 0, 0, &dest_pos);
}

static VALUE mx_get(int argc, VALUE *argv, VALUE self)
{
    VALUE idx;
    rb_scan_args(argc, argv, "0*", &idx);
    MX *mx = MX_DATA_PTR(self);

    size_t idx_len = RARRAY_LEN(idx);
    if (idx_len > mx->dim) {
        return Qnil;
    }

    MX *new_mx = mxs_slice(mx, idx, idx_len);
    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, new_mx);

    return self;
}

static VALUE mx_ewadd(VALUE self, VALUE other)
{
    MX *mx = MX_DATA_PTR(self);
    MX *new_mx = MX_ALLOC(MX);

    if (IS_MX(other)) {
        MX *other_mx = MX_DATA_PTR(other);
        if (!mxx_is_same_shape(mx, other_mx)) {
            rb_raise(rb_eDataTypeError, "Cannot do addition operation between different shapes");
        }
        mxx_ewadd_array(mx, other_mx, new_mx);
    } else {
        if (TYPE(other) == T_FLOAT) {
            mxx_ewadd_dblscalar(mx, NUM2DBL(other), new_mx);
        } else {
            mxx_ewadd_intscalar(mx, FIX2INT(other), new_mx);
        }
    }

    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, new_mx);
}

static VALUE mx_ewsub(VALUE self, VALUE other)
{
    MX *mx = MX_DATA_PTR(self);
    MX *new_mx = MX_ALLOC(MX);

    if (IS_MX(other)) {
        MX *other_mx = MX_DATA_PTR(other);
        if (!mxx_is_same_shape(mx, other_mx)) {
            rb_raise(rb_eDataTypeError, "Cannot do subtraction operation between different shapes");
        }
        mxx_ewsub_array(mx, other_mx, new_mx);
    } else {
        if (TYPE(other) == T_FLOAT) {
            mxx_ewsub_dblscalar(mx, NUM2DBL(other), new_mx);
        } else {
            mxx_ewsub_intscalar(mx, FIX2INT(other), new_mx);
        }
    }

    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, new_mx);
}

static VALUE mx_ewmul(VALUE self, VALUE other)
{
    MX *mx = MX_DATA_PTR(self);
    MX *new_mx = MX_ALLOC(MX);

    if (IS_MX(other)) {
        MX *other_mx = MX_DATA_PTR(other);
        if (!mxx_is_same_shape(mx, other_mx)) {
            rb_raise(rb_eDataTypeError, "Cannot do multiplication operation between different shapes");
        }
        mxx_ewmul_array(mx, other_mx, new_mx);
    } else {
        if (TYPE(other) == T_FLOAT) {
            mxx_ewmul_dblscalar(mx, NUM2DBL(other), new_mx);
        } else {
            mxx_ewmul_intscalar(mx, FIX2INT(other), new_mx);
        }
    }

    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, new_mx);
}

static VALUE mx_ewpow(VALUE self, VALUE other)
{
    MX *mx = MX_DATA_PTR(self);
    MX *new_mx = MX_ALLOC(MX);

    if (IS_MX(other)) {
        MX *other_mx = MX_DATA_PTR(other);
        if (!mxx_is_same_shape(mx, other_mx)) {
            rb_raise(rb_eDataTypeError, "Cannot do power operation between different shapes");
        }

        if (DTYPE_IS_INT(other_mx->dtype)) {
            mxx_ewintpow_array(mx, other_mx, new_mx);
        } else {
            mxx_ewpow_array(mx, other_mx, new_mx);
        }
    } else {
        if (TYPE(other) == T_FIXNUM) {
            mxx_ewpow_intscalar(mx, FIX2INT(other), new_mx);
        } else {
            mxx_ewpow_dblscalar(mx, NUM2DBL(other), new_mx);
        }
    }

    return Data_Wrap_Struct(CLASS_OF(self), 0, mxx_free, new_mx);
}

static VALUE mx_sing_eye(int argc, VALUE *argv, VALUE klass)
{
    int row, col, dim = 2;
    VALUE rb_row, rb_col, rb_opt;
    rb_scan_args(argc, argv, "11:", &rb_row, &rb_col, &rb_opt);
    DTYPE dtype = mxx_dtype_from_opt(rb_opt, DTYPE_FLOAT64);

    VALUE *shape = MX_ALLOC_N(VALUE, dim);
    shape[0] = rb_row;
    if (NIL_P(rb_col)) {
        shape[1] = rb_row;
    } else {
        shape[1] = rb_col;
    }
    int count = MX_MIN(FIX2INT(shape[0]), FIX2INT(shape[1]));
    if (count < 0) {
        MX_FREE(shape);
        rb_raise(rb_eArgError, "Negative dimensions are not allowed");
    }

    MX *mx = mxx_initialize(rb_ary_new4(dim, shape), dtype);

    memset(mx->elptr, 0x00, mx->size * DTYPE_SIZES[mx->dtype]);
    mxx_eye(mx, count);
    MX_FREE(shape);
    return Data_Wrap_Struct(klass, 0, mxx_free, mx);;
}

static VALUE mx_sing_arange(int argc, VALUE *argv, VALUE klass)
{
    double start, stop, step;
    int start_type, stop_type, step_type;
    VALUE rb_start, rb_stop, rb_step, rb_opt;
    DTYPE dtype;

    rb_scan_args(argc, argv, "12:", &rb_start, &rb_stop, &rb_step, &rb_opt);

    if (NIL_P(rb_stop)) {
        start      = 0;
        stop       = NUM2DBL(rb_start);
        start_type = T_FIXNUM;
        stop_type  = TYPE(rb_start);
    } else {
        start      = NUM2DBL(rb_start);
        stop       = NUM2DBL(rb_stop);
        start_type = TYPE(rb_start);
        stop_type  = TYPE(rb_stop);
    }

    step      = NIL_P(rb_step) ? 1 : NUM2DBL(rb_step);
    step_type = NIL_P(rb_step) ? T_FIXNUM : TYPE(rb_step);

    if (step == 0) {
        rb_raise(rb_eArgError, "Cannot specify 0 to the step argument");
    } else if (step > 0 && start > stop) {
        rb_raise(rb_eArgError, "Confuse arguments order. start > stop.");
    } else if (step < 0 && start < stop) {
        rb_raise(rb_eArgError, "Confuse arguments order. start < stop.");
    } else if (start == stop) {
        MX *mx = mxx_initialize(INT2FIX(0), DTYPE_INT64);
        return Data_Wrap_Struct(klass, 0, mxx_free, mx);
    }

    // T_FIXNUM => FIXNUM_P
    if (step_type == T_FIXNUM && start_type == T_FIXNUM && stop_type == T_FIXNUM) {
        dtype = DTYPE_INT64;
    } else {
        dtype = DTYPE_FLOAT64;
    }

    size_t shape = (size_t)fabs(ceil((stop - start) / step));
    MX *mx = mxx_initialize(INT2FIX(shape), mxx_dtype_from_opt(rb_opt, dtype));
    mxx_linspace(mx, start, step);

    return Data_Wrap_Struct(klass, 0, mxx_free, mx);
}

static VALUE mx_sing_linspace(int argc, VALUE *argv, VALUE klass)
{
    VALUE rb_start, rb_stop, rb_num, rb_opt;
    rb_scan_args(argc, argv, "21:", &rb_start, &rb_stop, &rb_num, &rb_opt);

    if (NIL_P(rb_num)) {
        rb_num = INT2FIX(100);
    }

    if (FIX2INT(rb_num) < 0) {
        rb_raise(rb_eArgError, "Cannot specify negative number to the shape argument");
    }

    DTYPE dtype = mxx_dtype_from_opt(rb_opt, DTYPE_FLOAT64);
    MX *mx = mxx_initialize(rb_num, dtype);
    double start = NUM2DBL(rb_start);
    double stop  = NUM2DBL(rb_stop);
    double step  = (stop - start) * (1.0 / (double)(mx->size - 1));
    mxx_linspace(mx, start, step);

    return Data_Wrap_Struct(klass, 0, mxx_free, mx);
}

void Init_mxruby()
{
    rb_cMx = rb_define_class("MX", rb_cObject);
    rb_eDataTypeError = rb_define_class("DataTypeError", rb_eStandardError);

    rb_define_alloc_func(rb_cMx, mx_alloc);

    rb_define_protected_method(rb_cMx, "__set__", mx_initialize, 3);

    rb_define_method(rb_cMx, "shape", mx_shape, 0);
    rb_define_method(rb_cMx, "dim", mx_dim, 0);
    rb_define_method(rb_cMx, "size", mx_size, 0);
    rb_define_method(rb_cMx, "dtype", mx_dtype, 0);
    rb_define_method(rb_cMx, "dot", mx_dot, 1);
    rb_define_method(rb_cMx, "astype", mx_astype, -1);
    rb_define_method(rb_cMx, "to_a", mx_to_a, 0);
    rb_define_method(rb_cMx, "flatten", mx_flatten, 0);
    rb_define_method(rb_cMx, "first", mx_first, 0);
    rb_define_method(rb_cMx, "second", mx_second, 0);
    rb_define_method(rb_cMx, "last", mx_last, 0);
    rb_define_method(rb_cMx, "reshape", mx_reshape, 1);
    rb_define_method(rb_cMx, "sum", mx_sum, -1);

    rb_define_method(rb_cMx, "empty?", mx_is_empty, 0);

    rb_define_method(rb_cMx, "[]=", mx_set, -1);
    rb_define_method(rb_cMx, "[]", mx_get, -1);
    rb_define_method(rb_cMx, "+", mx_ewadd, 1);
    rb_define_method(rb_cMx, "-", mx_ewsub, 1);
    rb_define_method(rb_cMx, "*", mx_ewmul, 1);
    rb_define_method(rb_cMx, "**", mx_ewpow, 1);

    rb_define_singleton_method(rb_cMx, "eye", mx_sing_eye, -1);
    rb_define_singleton_method(rb_cMx, "arange", mx_sing_arange, -1);
    rb_define_singleton_method(rb_cMx, "linspace", mx_sing_linspace, -1);

    Init_random();
}


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


#ifndef LIBTENSOR_CBLAS_H_H
#define LIBTENSOR_CBLAS_H_H

extern "C" {  // Fixes older cblas.h versions without extern "C"
#include <cblas.h>
}

#endif  // LIBTENSOR_CBLAS_H_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;
}


/* gsl_spmatrix.h
 *
 * Copyright (C) 2012-2014 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.
 */

#ifndef __GSL_SPMATRIX_H__
#define __GSL_SPMATRIX_H__

#include <stdlib.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.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

/*
 * Binary tree data structure for storing sparse matrix elements
 * in triplet format. This is used for efficiently detecting
 * duplicates and element retrieval via gsl_spmatrix_get
 */
typedef struct
{
  void *tree;       /* tree structure */
  void *node_array; /* preallocated array of tree nodes */
  size_t n;         /* number of tree nodes in use (<= nzmax) */
} gsl_spmatrix_tree;

/*
 * Triplet format:
 *
 * If data[n] = A_{ij}, then:
 *   i = A->i[n]
 *   j = A->p[n]
 *
 * Compressed column format (CCS):
 *
 * If data[n] = A_{ij}, then:
 *   i = A->i[n]
 *   A->p[j] <= n < A->p[j+1]
 * so that column j is stored in
 * [ data[p[j]], data[p[j] + 1], ..., data[p[j+1] - 1] ]
 *
 * Compressed row format (CRS):
 *
 * If data[n] = A_{ij}, then:
 *   j = A->i[n]
 *   A->p[i] <= n < A->p[i+1]
 * so that row i is stored in
 * [ data[p[i]], data[p[i] + 1], ..., data[p[i+1] - 1] ]
 */

typedef struct
{
  size_t size1;  /* number of rows */
  size_t size2;  /* number of columns */

  /* i (size nzmax) contains:
   *
   * Triplet/CCS: row indices
   * CRS: column indices
   */
  size_t *i;

  double *data;  /* matrix elements of size nzmax */

  /*
   * p contains the column indices (triplet) or column pointers (compcol)
   *
   * triplet: p[n] = column number of element data[n]
   * CCS:     p[j] = index in data of first non-zero element in column j
   * CRS:     p[i] = index in data of first non-zero element in row i
   */
  size_t *p;

  size_t nzmax;  /* maximum number of matrix elements */
  size_t nz;     /* number of non-zero values in matrix */

  gsl_spmatrix_tree *tree_data; /* binary tree for sorting triplet data */

  /*
   * workspace of size MAX(size1,size2)*MAX(sizeof(double),sizeof(size_t))
   * used in various routines
   */
  void *work;

  size_t sptype; /* sparse storage type */
} gsl_spmatrix;

#define GSL_SPMATRIX_TRIPLET      (0)
#define GSL_SPMATRIX_CCS          (1)
#define GSL_SPMATRIX_CRS          (2)

#define GSL_SPMATRIX_ISTRIPLET(m) ((m)->sptype == GSL_SPMATRIX_TRIPLET)
#define GSL_SPMATRIX_ISCCS(m)     ((m)->sptype == GSL_SPMATRIX_CCS)
#define GSL_SPMATRIX_ISCRS(m)     ((m)->sptype == GSL_SPMATRIX_CRS)

/*
 * Prototypes
 */

gsl_spmatrix *gsl_spmatrix_alloc(const size_t n1, const size_t n2);
gsl_spmatrix *gsl_spmatrix_alloc_nzmax(const size_t n1, const size_t n2,
                                       const size_t nzmax, const size_t flags);
void gsl_spmatrix_free(gsl_spmatrix *m);
int gsl_spmatrix_realloc(const size_t nzmax, gsl_spmatrix *m);
int gsl_spmatrix_set_zero(gsl_spmatrix *m);
size_t gsl_spmatrix_nnz(const gsl_spmatrix *m);
int gsl_spmatrix_compare_idx(const size_t ia, const size_t ja,
                             const size_t ib, const size_t jb);
int gsl_spmatrix_tree_rebuild(gsl_spmatrix * m);

/* spcopy.c */
int gsl_spmatrix_memcpy(gsl_spmatrix *dest, const gsl_spmatrix *src);

/* spgetset.c */
double gsl_spmatrix_get(const gsl_spmatrix *m, const size_t i,
                        const size_t j);
int gsl_spmatrix_set(gsl_spmatrix *m, const size_t i, const size_t j,
                     const double x);
double *gsl_spmatrix_ptr(gsl_spmatrix *m, const size_t i, const size_t j);

/* spcompress.c */
gsl_spmatrix *gsl_spmatrix_compcol(const gsl_spmatrix *T);
gsl_spmatrix *gsl_spmatrix_ccs(const gsl_spmatrix *T);
gsl_spmatrix *gsl_spmatrix_crs(const gsl_spmatrix *T);
void gsl_spmatrix_cumsum(const size_t n, size_t *c);

/* spio.c */
int gsl_spmatrix_fprintf(FILE *stream, const gsl_spmatrix *m,
                         const char *format);
gsl_spmatrix * gsl_spmatrix_fscanf(FILE *stream);
int gsl_spmatrix_fwrite(FILE *stream, const gsl_spmatrix *m);
int gsl_spmatrix_fread(FILE *stream, gsl_spmatrix *m);

/* spoper.c */
int gsl_spmatrix_scale(gsl_spmatrix *m, const double x);
int gsl_spmatrix_minmax(const gsl_spmatrix *m, double *min_out,
                        double *max_out);
int gsl_spmatrix_add(gsl_spmatrix *c, const gsl_spmatrix *a,
                     const gsl_spmatrix *b);
int gsl_spmatrix_d2sp(gsl_spmatrix *S, const gsl_matrix *A);
int gsl_spmatrix_sp2d(gsl_matrix *A, const gsl_spmatrix *S);

/* spprop.c */
int gsl_spmatrix_equal(const gsl_spmatrix *a, const gsl_spmatrix *b);

/* spswap.c */
int gsl_spmatrix_transpose(gsl_spmatrix * m);
int gsl_spmatrix_transpose2(gsl_spmatrix * m);
int gsl_spmatrix_transpose_memcpy(gsl_spmatrix *dest, const gsl_spmatrix *src);

__END_DECLS

#endif /* __GSL_SPMATRIX_H__ */


/* gsl_histogram_calloc_range.c
 * Copyright (C) 2000  Simone Piccardi
 *
 * This library 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 library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
/***************************************************************
 *
 * File gsl_histogram_calloc_range.c:
 * Routine to create a variable binning histogram providing
 * an input range vector. Need GSL library and header.
 * Do range check and allocate the histogram data.
 *
 * Author: S. Piccardi
 * Jan. 2000
 *
 ***************************************************************/
#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram.h>

gsl_histogram *
gsl_histogram_calloc_range (size_t n, double *range)
{
  size_t i;
  gsl_histogram *h;

  /* check arguments */

  if (n == 0)
    {
      GSL_ERROR_VAL ("histogram length n must be positive integer",
                        GSL_EDOM, 0);
    }

  /* check ranges */

  for (i = 0; i < n; i++)
    {
      if (range[i] >= range[i + 1])
        {
          GSL_ERROR_VAL ("histogram bin extremes  must be "
                            "in increasing order", GSL_EDOM, 0);
        }
    }

  /* Allocate histogram  */

  h = (gsl_histogram *) malloc (sizeof (gsl_histogram));

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

  h->range = (double *) malloc ((n + 1) * sizeof (double));

  if (h->range == 0)
    {
      /* exception in constructor, avoid memory leak */
      free (h);
      GSL_ERROR_VAL ("failed to allocate space for histogram ranges",
                        GSL_ENOMEM, 0);
    }

  h->bin = (double *) malloc (n * sizeof (double));

  if (h->bin == 0)
    {
      /* exception in constructor, avoid memory leak */
      free (h->range);
      free (h);
      GSL_ERROR_VAL ("failed to allocate space for histogram bins",
                        GSL_ENOMEM, 0);
    }

  /* initialize ranges */

  for (i = 0; i <= n; i++)
    {
      h->range[i] = range[i];
    }

  /* clear contents */

  for (i = 0; i < n; i++)
    {
      h->bin[i] = 0;
    }

  h->n = n;

  return h;
}


/* Siconos-Numerics, Copyright INRIA 2005-2011.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY, siconos-team@lists.gforge.inria.fr
 */

#ifndef SiconosBlas_H
#define SiconosBlas_H

#include "SiconosConfig.h"

#if defined(__cplusplus)
#include <algorithm>
using std::min;
using std::max;
#else
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a) >= (b) ? (a) : (b))
#endif

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

#if defined(HAS_MKL_CBLAS)
#include <mkl_cblas.h>
#elif defined(HAS_ATLAS_CBLAS)
#include <cblas.h>
#elif defined(HAS_ACCELERATE)
#include <Accelerate.h>
#else
#include <cblas.h>
#endif

#ifdef __cplusplus
}
#endif

#include "matrix_la.h"

#endif // SiconosBlas_H


/*
 * Partition.h
 * Provides class prototype that simplifies partition vectors.
 *
   Copyright 2017 Dariusz Kuchta

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

#pragma once

#include <vector>
#include <gsl.h>

namespace spectre::unsupervised
{
/// <summary>
/// Class storing simplified representation of partition vectors.
/// </summary>
class Partition
{
public:

    /// <summary>
    /// Default constructor. Deleted so no dataless instance can be created.
    /// </summary>
    Partition() = delete;

    /// <summary>
    /// Constructor taking partition data and computing its simplified version.
    /// </summary>
    /// <param name="partition">Input partition data.</param>
    explicit Partition(gsl::span<unsigned int> partition);

    /// <summary>
    /// Getter for simplified partition data.
    /// </summary>
    /// <returns>Stored simplified partition.</returns>
    const std::vector<unsigned int>& Get() const;

    /// <summary>
    /// Method for comparing specified partitions.
    /// </summary>
    /// <param name="lhs">The first partition.</param>
    /// <param name="rhs">The second partition.</param>
    /// <param name="tolerance">The tolerance rate of mismatch.</param>
    static bool Compare(const Partition &lhs, const Partition &rhs, double tolerance = 0);

    /// <summary>
    /// Equality operator. Compares with tolerance = 0.
    /// </summary>
    /// <param name="other">The second partition.</param>
    bool operator==(const Partition &other) const;

    /// <summary>
    /// Inequality operator. Compares with tolerance = 0.
    /// </summary>
    /// <param name="other">The second partition.</param>
    bool operator!=(const Partition &other) const;

private:
    std::vector<unsigned int> m_Partition;
};
}