/* linalg/test_ldlt.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 <gsl/gsl_test.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_rng.h>

static double
test_ldlt_norm1(const gsl_matrix * m)
{
  const size_t N = m->size2;
  double value = 0.0;
  size_t j;

  for (j = 0; j < N; ++j)
    {
      gsl_vector_const_view v = gsl_matrix_const_column(m, j);
      double sum = gsl_blas_dasum(&v.vector);
      value = GSL_MAX(value, sum);
    }

  return value;
}

static int
test_ldlt_decomp_eps(const gsl_matrix * m, const double expected_rcond,
                     const double eps, const char * desc)
{
  int s = 0;
  size_t i, j, N = m->size2;

  gsl_matrix * V  = gsl_matrix_alloc(N, N);
  gsl_matrix * A  = gsl_matrix_alloc(N, N);
  gsl_matrix * L  = gsl_matrix_calloc(N, N);
  gsl_matrix * LT = gsl_matrix_calloc(N, N);
  gsl_vector_view d;

  gsl_matrix_memcpy(V, m);

  s += gsl_linalg_ldlt_decomp(V);

  /* compute L and LT */
  gsl_matrix_tricpy(CblasLower, CblasUnit, L, V);
  d = gsl_matrix_diagonal(L);
  gsl_vector_set_all(&d.vector, 1.0);
  gsl_matrix_transpose_tricpy(CblasLower, CblasNonUnit, LT, L);

  /* compute L <- L D */
  d = gsl_matrix_diagonal(V);
  for (i = 0; i < N; ++i)
    {
      gsl_vector_view c = gsl_matrix_column(L, i);
      double di = gsl_vector_get(&d.vector, i);
      gsl_vector_scale(&c.vector, di);
    }

  /* compute A = L D LT */
  gsl_blas_dgemm (CblasNoTrans, CblasNoTrans, 1.0, L, LT, 0.0, A);

  for (i = 0; i < N; i++)
    {
      for (j = 0; j < N; j++)
        {
          double Aij = gsl_matrix_get(A, i, j);
          double mij = gsl_matrix_get(m, i, j);

          gsl_test_rel(Aij, mij, eps,
                       "%s: (%3lu,%3lu)[%lu,%lu]: %22.18g   %22.18g\n",
                       desc, N, N, i, j, Aij, mij);
        }
    }

  if (expected_rcond > 0)
    {
      gsl_vector *work = gsl_vector_alloc(3 * N);
      double rcond;

      gsl_linalg_ldlt_rcond(V, &rcond, work);

      gsl_test_rel(rcond, expected_rcond, 1.0e-6,
                   "%s rcond: (%3lu,%3lu): %22.18g   %22.18g\n",
                   desc, N, N, rcond, expected_rcond);

      gsl_vector_free(work);
    }

  gsl_matrix_free(V);
  gsl_matrix_free(A);
  gsl_matrix_free(L);
  gsl_matrix_free(LT);

  return s;
}

static int
test_ldlt_decomp(gsl_rng * r)
{
  int s = 0;
  const size_t N_max = 50;
  size_t N;

  for (N = 1; N <= N_max; ++N)
    {
      gsl_matrix * m = gsl_matrix_alloc(N, N);

      create_posdef_matrix(m, r);

      test_ldlt_decomp_eps(m, -1.0, 1.0e2 * N * GSL_DBL_EPSILON, "ldlt_decomp random");

      if (N <= 12)
        {
          double expected_rcond = -1.0;

          if (hilb_rcond[N - 1] > 1.0e-12)
            expected_rcond = hilb_rcond[N - 1];

          create_hilbert_matrix2(m);
          test_ldlt_decomp_eps(m, expected_rcond, N * GSL_DBL_EPSILON, "ldlt_decomp hilbert");
        }

      gsl_matrix_free(m);
    }

  return s;
}

int
test_ldlt_solve_eps(const gsl_matrix * m, const gsl_vector * rhs,
                    const gsl_vector * sol, const double eps,
                    const char * desc)
{
  int s = 0;
  size_t i, N = m->size1;
  gsl_matrix * u  = gsl_matrix_alloc(N, N);
  gsl_vector * x = gsl_vector_calloc(N);

  gsl_matrix_memcpy(u, m);

  s += gsl_linalg_ldlt_decomp(u);
  s += gsl_linalg_ldlt_solve(u, rhs, x);

  for (i = 0; i < N; i++)
    {
      double xi = gsl_vector_get(x, i);
      double yi = gsl_vector_get(sol, i);

      gsl_test_rel(xi, yi, eps,
                   "%s: %3lu[%lu]: %22.18g   %22.18g\n",
                   desc, N, i, xi, yi);
    }

  gsl_vector_free(x);
  gsl_matrix_free(u);

  return s;
}

static int
test_ldlt_solve(gsl_rng * r)
{
  int s = 0;
  const size_t N_max = 50;
  size_t N;

  for (N = 1; N <= N_max; ++N)
    {
      gsl_matrix * m = gsl_matrix_alloc(N, N);
      gsl_vector * rhs = gsl_vector_alloc(N);
      gsl_vector * sol = gsl_vector_alloc(N);

      create_posdef_matrix(m, r);
      create_random_vector(sol, r);
      gsl_blas_dsymv(CblasLower, 1.0, m, sol, 0.0, rhs);

      test_ldlt_solve_eps(m, rhs, sol, 64.0 * N * GSL_DBL_EPSILON, "ldlt_solve random");

      if (N <= 3)
        {
          create_hilbert_matrix2(m);
          gsl_blas_dsymv(CblasLower, 1.0, m, sol, 0.0, rhs);
          test_ldlt_solve_eps(m, rhs, sol, 1024.0 * N * GSL_DBL_EPSILON, "ldlt_solve hilbert");
        }

      gsl_matrix_free(m);
      gsl_vector_free(rhs);
      gsl_vector_free(sol);
    }

  return s;
}

static int
test_ldlt_band_decomp_eps(const size_t p, const gsl_matrix * m, const double eps, const char * desc)
{
  int s = 0;
  size_t i, j, N = m->size2;
  double rcond, rcond_expected;

  gsl_matrix * V  = gsl_matrix_alloc(N, p + 1);
  gsl_matrix * A  = gsl_matrix_alloc(N, N);
  gsl_matrix * L  = gsl_matrix_calloc(N, N);
  gsl_matrix * LT = gsl_matrix_calloc(N, N);
  gsl_vector * D = gsl_vector_alloc(N);
  gsl_vector * work = gsl_vector_alloc(3 * N);

  /* convert m to packed banded format */
  symm2band_matrix(p, m, V);

  s += gsl_linalg_ldlt_band_decomp(V);

  /* compute L and LT */
  gsl_linalg_ldlt_band_unpack(V, L, D);
  gsl_matrix_transpose_tricpy(CblasLower, CblasNonUnit, LT, L);

  /* compute L <- L D */
  for (i = 0; i < N; ++i)
    {
      gsl_vector_view c = gsl_matrix_column(L, i);
      double di = gsl_vector_get(D, i);
      gsl_vector_scale(&c.vector, di);
    }

  /* compute A = L D LT */
  gsl_blas_dgemm (CblasNoTrans, CblasNoTrans, 1.0, L, LT, 0.0, A);

  for (i = 0; i < N; i++)
    {
      for (j = 0; j < N; j++)
        {
          double Aij = gsl_matrix_get(A, i, j);
          double mij = gsl_matrix_get(m, i, j);

          gsl_test_rel(Aij, mij, eps,
                       "%s: (p=%zu,N=%zu)[%lu,%lu]: %22.18g   %22.18g\n",
                       desc, p, N, i, j, Aij, mij);
        }
    }

  /* test 1-norm calculation */
  if (p > 0)
    {
      double norm1_expected = test_ldlt_norm1(m);
      double norm1 = gsl_matrix_get(V, N - 1, p);

      gsl_test_rel(norm1, norm1_expected, eps,
                   "%s: (p=%zu,N=%zu) 1-norm: %22.18g   %22.18g\n",
                   desc, p, N, norm1, norm1_expected);
    }

  /* test rcond */
  gsl_matrix_memcpy(A, m);
  s += gsl_linalg_ldlt_decomp(A);
  s += gsl_linalg_ldlt_rcond(A, &rcond_expected, work);
  s += gsl_linalg_ldlt_band_rcond(V, &rcond, work);
  gsl_test_rel(rcond, rcond_expected, eps,
               "%s: (p=%zu,N=%zu) rcond: %22.18g   %22.18g\n",
               desc, p, N, rcond, rcond_expected);

  gsl_matrix_free(V);
  gsl_matrix_free(A);
  gsl_matrix_free(L);
  gsl_matrix_free(LT);
  gsl_vector_free(D);
  gsl_vector_free(work);

  return s;
}

static int
test_ldlt_band_decomp(gsl_rng * r)
{
  int s = 0;
  const size_t N_max = 50;
  size_t N, p;

  for (N = 1; N <= N_max; ++N)
    {
      gsl_matrix * m = gsl_matrix_alloc(N, N);

      for (p = 0; p < GSL_MIN(N, 10); ++p)
        {
          create_posdef_band_matrix(p, m, r);
          s += test_ldlt_band_decomp_eps(p, m, 1.0e3 * N * GSL_DBL_EPSILON, "ldlt_band_decomp random");
        }

      gsl_matrix_free(m);
    }

  return s;
}

int
test_ldlt_band_solve_eps(const size_t p, const gsl_matrix * m, const gsl_vector * rhs,
                         const gsl_vector * sol, const double eps, const char * desc)
{
  int s = 0;
  size_t i, N = m->size1;
  gsl_matrix * u  = gsl_matrix_alloc(N, p + 1);
  gsl_vector * x = gsl_vector_alloc(N);

  /* convert m to packed banded format */
  symm2band_matrix(p, m, u);

  s += gsl_linalg_ldlt_band_decomp(u);
  s += gsl_linalg_ldlt_band_solve(u, rhs, x);

  for (i = 0; i < N; i++)
    {
      double xi = gsl_vector_get(x, i);
      double yi = gsl_vector_get(sol, i);

      gsl_test_rel(xi, yi, eps,
                   "%s: p=%zu N=%zu [%lu]: %22.18g   %22.18g\n",
                   desc, p, N, i, xi, yi);
    }

  gsl_vector_free(x);
  gsl_matrix_free(u);

  return s;
}

static int
test_ldlt_band_solve(gsl_rng * r)
{
  int s = 0;
  const size_t N_max = 50;
  size_t N, p;

  for (N = 1; N <= N_max; ++N)
    {
      gsl_matrix * m = gsl_matrix_alloc(N, N);
      gsl_vector * rhs = gsl_vector_alloc(N);
      gsl_vector * sol = gsl_vector_alloc(N);

      for (p = 0; p < GSL_MIN(N, 10); ++p)
        {
          create_posdef_band_matrix(p, m, r);
          create_random_vector(sol, r);
          gsl_blas_dsymv(CblasLower, 1.0, m, sol, 0.0, rhs);

          test_ldlt_band_solve_eps(p, m, rhs, sol, 64.0 * N * GSL_DBL_EPSILON, "ldlt_band_solve random");
        }

      gsl_matrix_free(m);
      gsl_vector_free(rhs);
      gsl_vector_free(sol);
    }

  return s;
}


/*
 * TableFunction.h
 *
 * Author:
 *       Oleg Kalashev
 *
 * Copyright (c) 2020 Institute for Nuclear Research, RAS
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef TABLEFUNCTION_H_
#define TABLEFUNCTION_H_

#include <iostream>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>

#include "MathUtils.h"
#include "TableReader.h"

namespace Utils {

template<typename X = double >
class IScale
{
public:
	virtual X ToScale(X aX) = 0;
	virtual X FromScale(X aXprime) = 0;
	virtual IScale<X>* Clone() const = 0;
	virtual ~IScale(){}
};

template<typename X = double >
class LogScaleX : public IScale<X>
{
public:
	X ToScale(X aX);
	X FromScale(X aXprime);
	IScale<X>* Clone() const;
};

typedef LogScaleX<double> LogScale;

template<typename X = double >
class TableFunctionX : public SmartReferencedObj, virtual public FunctionX<X>{
public:
	static int FindLeftX(const std::vector<X>&, X xValue);
	TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, X _leftVal=0., X _rightVal=0.);

	//takes ownership of aXscale and aYscale
	TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, IScale<X>* aXscale, IScale<X>* aYscale, X _leftVal=0., X _rightVal=0.);

	//stores smart ref to aReader
	TableFunctionX(TableReaderX<X>* aReader, X _leftVal=0., X _rightVal=0.);

	bool InTableRange(X aArg) const;

	virtual X f(X _x) const;
	virtual X f_scaled(X _x) const = 0;
	virtual X Xmin() const;
	virtual X Xmax() const;

	void SetAutoLimits();
	void Print(std::ostream& aOut, X aXcoef=1., X aYcoef=1.);
private:
	//common part of constructors
	void Init();
protected:
	int FindLeftX(X xValue) const;
	//used for cloning
	TableFunctionX(const TableFunctionX<X>& aTableFunction);

	std::vector<X>			fCloneX;
	std::vector<X>			fCloneY;
	const std::vector<X>&	m_x;
	const std::vector<X>&	m_y;
	X						m_leftVal;
	X						m_rightVal;
	SafePtr<IScale<X> >		fXscale;
	SafePtr<IScale<X> >		fYscale;
	SmartPtr<TableReaderX<X> >	fTable;
	X						fXmin;
	X						fXmax;
};

typedef TableFunctionX<double> TableFunction;

template<typename X = double >
class LinearFuncX  : public TableFunctionX<X>
{
public:
	LinearFuncX(const std::vector<X>& _x,const  std::vector<X>& _y, X _leftVal=0., X _rightVal=0.):
	  TableFunctionX<X>(_x, _y, _leftVal, _rightVal)  {};

	//stores smart ref to aReader
	LinearFuncX(TableReaderX<X>* aReader, X _leftVal=0., X _rightVal=0.):
		TableFunctionX<X>(aReader,_leftVal,_rightVal) {};

	//takes ownership of aXscale and aYscale
	LinearFuncX(const std::vector<X>& _x,const std::vector<X>& _y, IScale<X>* aXscale, IScale<X>* aYscale, X _leftVal=0., X _rightVal=0.):
		TableFunctionX<X>(_x, _y, aXscale, aYscale, _leftVal, _rightVal) {}

	X f_scaled(X _x) const;
	FunctionX<X>* Clone() const { return new LinearFuncX<X>(*this); }
protected:
	LinearFuncX(const TableFunctionX<X>& aTableFunction):
		TableFunctionX<X>(aTableFunction) {};
};

typedef LinearFuncX<double> LinearFunc;

class GSLTableFunc  : public TableFunction
{
public:
	GSLTableFunc(const Vector& _x, const Vector& _y, const gsl_interp_type * aInterpType=gsl_interp_linear,
			double _leftVal=0., double _rightVal=0.):
	  TableFunction(_x, _y, _leftVal, _rightVal),fAcc(0),fSpline(0) {Init(aInterpType);}

	//stores smart ref to aReader
	GSLTableFunc(TableReader *aReader, const gsl_interp_type * aInterpType=gsl_interp_linear, double _leftVal=0., double _rightVal=0.):
		TableFunction(aReader,_leftVal,_rightVal),fAcc(0),fSpline(0) {Init(aInterpType);}

	//takes ownership of aXscale and aYscale
	//_x and _y are values are modified with scale functions given by aXscale and aYscale parameters
	GSLTableFunc(const Vector& _x, const Vector& _y, IScale<double>* aXscale, IScale<double>* aYscale, const gsl_interp_type * aInterpType=gsl_interp_linear, double _leftVal=0., double _rightVal=0.):
		TableFunction(_x, _y, aXscale, aYscale, _leftVal, _rightVal) {Init(aInterpType);}

	virtual ~GSLTableFunc();

	double f_scaled(double _x) const;
	Function* Clone() const { return new GSLTableFunc(*this); }
protected:
	GSLTableFunc(const GSLTableFunc& aTableFunction):
		TableFunction(aTableFunction) {Init(aTableFunction.fSpline->interp->type);};
private:
	void Init(const gsl_interp_type * aInterpType);
    gsl_interp_accel *fAcc;
    gsl_spline *fSpline;
    const gsl_interp_type *fInterpType;
};

template<typename X = double >
class MatrixFunctionX : public Function2X<X>
{
public:
	MatrixFunctionX(const std::string& aFile)
	{
		std::ifstream dataFile(aFile.c_str());
		if(!dataFile)
			Exception::Throw("Failed to open " + aFile);
		std::string header;
		std::getline(dataFile, header);
		int logscX,logscY;
		const char* headerFormat = "# minX=%lg stepX=%lg logscaleX=%d minY=%lg stepY=%lg logscaleY=%d";
		if(sscanf(header.c_str(),headerFormat,
				  &xMin,&xStep,&logscX,&yMin,&yStep,&logscY)!=6)
			Exception::Throw("Invalid header format in " + aFile + "\nExpected format: " + headerFormat);
		logScaleX = (bool)logscX;
		logScaleY = (bool)logscY;
		if((logScaleX && (xMin<=0 || xStep<=1.))||xStep<=0.)
			Exception::Throw("Invalid X scale in " + aFile);
		if((logScaleY && (yMin<=0 || yStep<=1.))||yStep<=0.)
			Exception::Throw("Invalid Y scale in " + aFile);
		fData = new TableReaderX<X>(dataFile, TableReader::Auto);
		nCols = fData->numberOfColumns();
		if(logScaleX) {
			xMax = xMin*pow(xStep,nCols-1);
			xStep = log(xStep);//convert multiplier to log step
		}
		else{
			xMax = xMin + xStep*(nCols-1);
		}
		nRows = fData->getColumn(0).size();
		if(logScaleY){
			yMax = yMin*pow(yStep,nRows-1);
			yStep = log(yStep);//convert multiplier to log step
		}
		else{
			yMax = yMin + yStep*(nRows-1);
		}
	}

	X f(double x, double y) const
	{
		double binX, binY;

		if(logScaleX)
		{//log scale
			binX = log(x/xMin)/xStep;
		}
		else
		{//linear scale
			binX = (x-xMin)/xStep;
		}
		if(binX<0. || binX>nCols-1)
			return 0.;
		if(logScaleY)
		{//log scale
			binY = log(y/yMin)/yStep;
		}
		else
		{//linear scale
			binY = (y-yMin)/yStep;
		}
		if(binY<0. || binY>nRows-1)
			return 0.;
		int iX = floor(binX);
		int iY = floor(binY);
		double weightX = 1.-binX+iX;
		double weightY = 1.-binY+iY;

		//linear f interpolation //TODO implement logscale f interpolation
		double f_11 = fData->getColumn(iX)[iY]*weightX*weightY;
		double f_21 = (weightX<1.)? (fData->getColumn(iX+1)[iY]*(1.-weightX)*weightY) : 0.;
		double f_12 = (weightY<1.)? (fData->getColumn(iX)[iY+1]*weightX*(1.-weightY)) : 0.;
		double f_22 = (weightX<1.&&weightY<1.)?(fData->getColumn(iX+1)[binY+1]*(1.-weightX)*(1.-weightY)) : 0.;
		double result = f_11 + f_21 + f_12 + f_22;
		return result;
	}

	virtual X MinArg(int aArgNo) const {return aArgNo==1?xMin:yMin;};
	virtual X MaxArg(int aArgNo) const {return aArgNo==1?xMax:yMax;}

private:
	SafePtr<TableReaderX<X> >  fData;
	X xMin;
	X yMin;
	X xMax;
	X yMax;
	X xStep;
	X yStep;
	int nCols;
	int nRows;
	bool logScaleX;
	bool logScaleY;
};

typedef MatrixFunctionX<double> MatrixFunction;

} /* namespace Utils */

#endif /* TABLEFUNCTION_H_ */


/* Copyright (c) 2014, Giuseppe Argentieri <giuseppe.argentieri@ts.infn.it>

 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
/*
 *
 *
 *       Filename:  distance.h
 *
 *    Description:  Distance in trace norm among the stationary and thermal
 *    			states.
 *
 *        Version:  1.0
 *        Created:  22/06/2014 22:21:50
 *       Revision:  none
 *        License:  BSD
 *
 *         Author:  Giuseppe Argentieri (ga), giuseppe.argentieri@ts.infn.it
 *   Organization:  Università degli Studi di Trieste
 *
 *
 */

#include <gsl/gsl_vector.h>
#include "funcs.h"

/*
 *      FUNCTION
 *         Name:  dist
 *  Description:  distance between two states in Bloch vector form. It does not
 *  			check the positivity of the states
 *
 */
double dist ( const gsl_vector* rho1, const gsl_vector* rho2 );
gsl_vector therm_state ( const double beta, const gsl_matrix* H );


/***************************************************************************
    File                 : Fit.h
    Project              : QtiPlot
    --------------------------------------------------------------------
    Copyright            : (C) 2006 by Ion Vasilief
    Email (use @ for *)  : ion_vasilief*yahoo.fr
    Description          : Fit base class

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#ifndef FIT_H
#define FIT_H

#include <QObject>

#include "../ApplicationWindow.h"
#include "Filter.h"

#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_multimin.h>

class Table;
class Matrix;

//! Fit base class
class Fit : public Filter
{
	Q_OBJECT

	public:

		typedef double (*fit_function_simplex)(const gsl_vector *, void *);
		typedef int (*fit_function)(const gsl_vector *, void *, gsl_vector *);
		typedef int (*fit_function_df)(const gsl_vector *, void *, gsl_matrix *);
		typedef int (*fit_function_fdf)(const gsl_vector *, void *, gsl_vector *, gsl_matrix *);

		enum Algorithm{ScaledLevenbergMarquardt, UnscaledLevenbergMarquardt, NelderMeadSimplex};
		enum WeightingMethod{NoWeighting, Instrumental, Statistical, Dataset};
        enum FitType{BuiltIn = 0, Plugin = 1, User = 2};

		Fit(ApplicationWindow *parent, Graph *g = 0, const QString& name = QString());
		Fit(ApplicationWindow *parent, Table *t, const QString& name = QString());
		~Fit();

		//! Actually does the fit. Should be reimplemented in derived classes.
		virtual void fit();
        virtual bool run(){fit(); return true;};

		//! Sets the data set to be used for weighting
		bool setWeightingData(WeightingMethod w, const QString& colName = QString::null);

		void setDataCurve(int curve, double start, double end);
		bool setDataFromTable(Table *t, const QString& xColName, const QString& yColName, int from = 1, int to = -1);

		QString resultFormula(){return d_result_formula;};
		QString formula(){return d_formula;};
		virtual void setFormula(const QString&){};

		int numParameters(){return d_p;};
		QStringList parameterNames(){return d_param_names;};
		virtual void setParametersList(const QStringList&){};
        void setParameterExplanations(const QStringList& lst){d_param_explain = lst;};

        double initialGuess(int parIndex){return gsl_vector_get(d_param_init, parIndex);};
		void setInitialGuess(int parIndex, double val){gsl_vector_set(d_param_init, parIndex, val);};
		void setInitialGuesses(double *x_init);

		virtual void guessInitialValues(){};

		void setParameterRange(int parIndex, double left, double right);
		void setAlgorithm(Algorithm s){d_solver = s;};

		//! Specifies weather the result of the fit is a function curve
		void generateFunction(bool yes, int points = 100);

		//! Output string added to the plot as a new legend
		virtual QString legendInfo();

		//! Returns a vector with the fit results
		double* results(){return d_results;};

		//! Returns a vector with the fit residuals
		double* residuals();

		//! Plot residuals and display data values in a column
		QwtPlotCurve* showResiduals();

		void showPredictionLimits(double confidenceLevel);
		void showConfidenceLimits(double confidenceLevel);
		//! Lower Confidence Limit
		double lcl(int parIndex, double confidenceLevel);
		//! Upper Confidence Limit
		double ucl(int parIndex, double confidenceLevel);

		//! Returns a vector with the standard deviations of the results
		double* errors();

		//! Returns the sum of squares of the residuals from the best-fit line
		double chiSquare() {return chi_2;};

		//! Returns R^2
		double rSquare();

		//! Returns adjusted R^2
		double adjustedRSquare(){return d_adjusted_r_square;};

		//! Returns the Residual Sum of Squares
		double rss(){return d_rss;};

		//! Returns the Root Mean Squared Error
		double rmse(){return sqrt(d_rss/(d_n - d_p));};

		//! Specifies wheather the errors must be scaled with sqrt(chi_2/dof)
		void scaleErrors(bool yes = true){d_scale_errors = yes;};

		Table* parametersTable(const QString& tableName);
		void writeParametersToTable(Table *t, bool append = false);

		Matrix* covarianceMatrix(const QString& matrixName);

        bool save(const QString& fileName);
        bool load(const QString& fileName);

        FitType type(){return d_fit_type;};
        void setType(FitType t){d_fit_type = t;};

        QString fileName(){return d_file_name;};
		void setFileName(const QString& fn){d_file_name = fn;};

        //! Frees the memory allocated for the X and Y data sets
        void freeMemory();

        //! Calculates the data for the output fit curve
        virtual double eval(double *, double){return 0.0;};

	private:
		void init();

		//! Pointer to the GSL multifit minimizer (for simplex algorithm)
		gsl_multimin_fminimizer * fitSimplex(gsl_multimin_function f, int &iterations, int &status);

		//! Pointer to the GSL multifit solver
		gsl_multifit_fdfsolver * fitGSL(gsl_multifit_function_fdf f, int &iterations, int &status);

		//! Customs and stores the fit results according to the derived class specifications. Used by exponential fits.
		virtual void customizeFitResults(){};

	protected:
		//! Allocates the memory for the fit workspace
		void initWorkspace(int par);
		//! Frees the memory allocated for the fit workspace
		void freeWorkspace();
		//! Adds the result curve as a FunctionCurve to the plot, if d_gen_function = true
		virtual FunctionCurve * insertFitFunctionCurve(const QString& name, double *x, double *y, int penWidth = 1);

		//! Adds the result curve to the plot
		virtual void generateFitCurve();

        //! Calculates the data for the output fit curve and store itin the X an Y vectors
		virtual void calculateFitCurveData(double *X, double *Y) {Q_UNUSED(X) Q_UNUSED(Y)};

		//! Output string added to the result log
		virtual QString logFitInfo(int iterations, int status);

		fit_function d_f;
		fit_function_df d_df;
		fit_function_fdf d_fdf;
		fit_function_simplex d_fsimplex;

		//! Number of fit parameters
		int d_p;

		//! Initial guesses for the fit parameters
		gsl_vector *d_param_init;

		/*! \brief Tells whether the fitter uses non-linear/simplex fitting
		 * with an initial parameters set, that must be freed in the destructor.
		 */
		bool is_non_linear;

		//! weighting data set used for the fit
		double *d_w;

		//! Names of the fit parameters
		QStringList d_param_names;

		//! Stores a list of short explanations for the significance of the fit parameters
		QStringList d_param_explain;

		//! Specifies weather the result curve is a FunctionCurve or a normal curve with the same x values as the fit data
		bool d_gen_function;

		//! Algorithm type
		Algorithm d_solver;

		//! The fit formula given on input
		QString d_formula;

		//! The result fit formula, where the fit parameters are replaced with the calculated values.
		QString d_result_formula;

		//! Covariance matrix
		gsl_matrix *covar;

		//! The kind of weighting to be performed on the data
		WeightingMethod d_weighting;

		//! The name of the weighting dataset
		QString weighting_dataset;

		//! Stores the result parameters
		double *d_results;

		//! Stores standard deviations of the result parameters
		double *d_errors;

		//! Stores fit residuals
		double *d_residuals;

		//! The sum of squares of the residuals from the best-fit line
		double chi_2;

		//! Residual sum of squares
		double d_rss;

		//! Adjusted R^2
		double d_adjusted_r_square;

		//! Specifies wheather the errors must be scaled with sqrt(chi_2/dof)
		bool d_scale_errors;

		//! Table window used for the output of fit parameters
		Table *d_param_table;

		//! Matrix window used for the output of covariance matrix
		Matrix *d_cov_matrix;

		FitType d_fit_type;

		//! Path of the XML file where the user stores the fit model
        QString d_file_name;

		//! Stores the left limits of the research interval for the result parameters
		double *d_param_range_left;

		//! Stores the right limits of the research interval for the result parameters
		double *d_param_range_right;
};

#endif


#if !defined(PETIGAFTN_H)
#define PETIGAFTN_H

#include <petsc.h>

EXTERN_C_BEGIN
extern void IGA_Quadrature_1D(PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_Quadrature_2D(PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_Quadrature_3D(PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscInt,const PetscReal[],const PetscReal[],const PetscReal*,
                              PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

EXTERN_C_BEGIN
extern void IGA_BasisFuns_1D(PetscInt,
                             PetscInt,PetscInt,const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_BasisFuns_2D(PetscInt,
                             PetscInt,PetscInt,const PetscReal[],
                             PetscInt,PetscInt,const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_BasisFuns_3D(PetscInt,
                             PetscInt,PetscInt,const PetscReal[],
                             PetscInt,PetscInt,const PetscReal[],
                             PetscInt,PetscInt,const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

EXTERN_C_BEGIN
extern void IGA_Rationalize_1D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_Rationalize_2D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_Rationalize_3D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

EXTERN_C_BEGIN
extern void IGA_GeometryMap_1D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_GeometryMap_2D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_GeometryMap_3D(PetscInt,PetscInt,PetscInt,const PetscReal[],
                               const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_GeometryMap   (PetscInt,PetscInt,PetscInt,PetscInt,PetscInt,const PetscReal[],
                               const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                               PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

EXTERN_C_BEGIN
extern void IGA_InverseMap_1D(PetscInt,PetscInt,
                              const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                              PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_InverseMap_2D(PetscInt,PetscInt,
                              const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                              PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_InverseMap_3D(PetscInt,PetscInt,
                              const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                              PetscReal[],PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

EXTERN_C_BEGIN
extern void IGA_ShapeFuns_1D(PetscInt,PetscInt,PetscInt,
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_ShapeFuns_2D(PetscInt,PetscInt,PetscInt,
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
extern void IGA_ShapeFuns_3D(PetscInt,PetscInt,PetscInt,
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             const PetscReal[],const PetscReal[],const PetscReal[],const PetscReal[],
                             PetscReal[],PetscReal[],PetscReal[],PetscReal[]);
EXTERN_C_END

#endif/*PETIGAFTN_H*/


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_qrng.h>
#include <gsl/gsl_histogram2d.h>
#include <gsl/gsl_sf_laguerre.h>

#include <courier.h>
#include "data.h"

static const char* localhost = "127.0.0.1";
extern void splineval_init(int* nx, double *x, double *fcn_1d);
extern void spline_dump(int* nx);
extern void throw_darts(const int* ndarts, const int *nx, double *x, double *y, int* hits);
extern void classify(const int *nx, double *x, double *y, int* hits);

void report(size_t ndarts, double* x, double* y, int* hits, Point* rowvec) {
    int i;
}
void explore(const int ndarts, size_t nreps, size_t nx, size_t ny,
        double xmin, double xmax, double ymin, double ymax) {
    double u, v;
    int i, j;
    const char* deps[] = {"Point"};
    const gsl_rng_type * T = gsl_rng_default;
    gsl_rng * r = gsl_rng_alloc (T);
    gsl_histogram2d *h = gsl_histogram2d_alloc(nx,ny);
    gsl_histogram2d_pdf *p = gsl_histogram2d_pdf_alloc(nx,ny);

    gsl_histogram2d_set_ranges_uniform(h, xmin, xmax, ymin, ymax);
    gsl_histogram2d_shift(h, 100.0);

    double *x = malloc(sizeof(double)*ndarts),
           *y = malloc(sizeof(double)*ndarts);
    int *c = malloc(sizeof(int)*ndarts);
    Point *rowvec = malloc(sizeof(Point)*ndarts);
    int breakout = 0;
    for (j = 0; j < nreps; j++) {
        gsl_histogram2d_pdf_init(p,h);

        for (i = 0; i < ndarts; i++) {
            u = gsl_rng_uniform(r);
            v = gsl_rng_uniform(r);
            gsl_histogram2d_pdf_sample (p, u, v, &x[i], &y[i]);
        }
        classify(&ndarts,x,y,c);
        for (i = 0; i < ndarts; i++) {
            rowvec[i].x = x[i];
            rowvec[i].y = y[i];
            rowvec[i].category = c[i];
        }
        courier_write_table("Point", rowvec, sizeof(Point), ndarts, 0);
        char* res = courier_request_analysis(localhost, "Update", deps, 1);
        {
            char* token = strtok(res, " ");
            while (token != NULL)
            {
                size_t idx = strtod(token, NULL) - 1;
                token = strtok(NULL, " ");
                double w = strtod(token, NULL);
                h->bin[idx] = 1;//fmax(0,10 / w);
                token = strtok(NULL, " ");
            }
            if (gsl_histogram2d_sum(h) <= nx*ny) {
                printf("Stopping due to exhausted histogram\n");
                breakout = 1;
            }
        }
        courier_free_data(res);
        if (breakout) {
            printf("Iteration %d\n",j);
            break;
        }
    }
    FILE *hf = fopen("hist.tab", "w");
    gsl_histogram2d_fprintf(hf,h,"%g","%g");
    fclose(hf);

    free(rowvec);
    free(c);
    free(x);
    free(y);
    gsl_rng_free (r);
    gsl_histogram2d_pdf_free(p);
    gsl_histogram2d_free(h);
}
int main(int argc, char** argv) {
    int nx = 100;
    int i, j;
    double x[nx], fcn_1d[nx];
    const gsl_rng_type * T;
    gsl_rng * r;

    gsl_rng_env_setup();

    T = gsl_rng_default;
    r = gsl_rng_alloc (T);
    fcn_1d[0] = 0;
    x[0] = 0;
    for (i = 1; i < nx; i++)
    {
        x[i] = i + 1.0;
        double h = gsl_ran_levy_skew(r, 1, 1.0, 1.0);
        if (h < 1 && h > -1) { h = -5; }
        fcn_1d[i] = fmax(25,fmin(75,fcn_1d[i-1]+h));
    }
    splineval_init(&nx,x,fcn_1d);
    explore(1000, 1000, 64, 64, 0, 100, 0, 100);
    spline_dump(&nx);
    gsl_rng_free (r);
    return 0;
}


/*
  NAME:
      read_data
  PURPOSE:
      read the data from the data input file
  CALLING SEQUENCE:
      read_data(inputfilename)
  INPUT:
      inputfilename - name of the data file
  OUPUT:
      reads everything into the right structures
  REVISION HISTORY:
     2008-09-21 - Written Bovy
*/
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <proj_gauss_main.h>
#include <proj_gauss_mixtures.h>

bool read_data(char inputfilename[]){

  FILE *inputfile;
  if ( (inputfile= fopen(inputfilename,"r")) == NULL){
    printf ("Opening the data file failed...\n");
    return false;
  }

  //First count the number of datapoints
  N=0;
  while (feof(inputfile) ==0) if (getc(inputfile) == '\n') N++;

  printf("%i datapoints found in ",N);
  printf("%s", inputfilename);
  printf("\n");

  //Then read the actual data
  fseek(inputfile,0,SEEK_SET);

  //allocate data
  data = (struct datapoint *) malloc(N*sizeof (struct datapoint));
  if (data == NULL){
    printf("Allocation of arrays failed, not enough free memory?\n");
    exit(-1);
  }
  startdata = data;

  //current line holds a maximum of d+dV+d*d elements
  //First read the whole line
  double *curr_line = (double *) calloc(d+dV+d*d, sizeof (double) );
  bool end_line;
  int ii=0,dd=0,di;//di is the dimension of the individual datapoints
  while (feof(inputfile) == 0){
    char curr_value[20]="";
    end_line = read_till_sep(curr_value,inputfile,'|');
    *(curr_line++) = atof(curr_value);
    ++dd;
    if (end_line){
      ++ii;
      curr_line -= dd;
      //Determine the dimension of the datapoint, di, from the equation di+di*(di+1)/2+d*di=dd, or,
      di = (int) ((-(3 + 2 * d) + sqrt((3 + 2 * d)*(3 + 2 * d)+8 * dd))/2);
      //then write data values to memory
      //first allocate the space for the data
      data->ww = gsl_vector_alloc(di);
      data->SS = gsl_matrix_alloc(di,di);
      data->RR = gsl_matrix_alloc(di,d);
      int dd1,dd2;
      for (dd1=0; dd1 != di; ++dd1)
	gsl_vector_set(data->ww,dd1,*(curr_line++));
      for (dd1=0; dd1 != di; ++dd1)
	gsl_matrix_set(data->SS,dd1,dd1,*(curr_line++));
      for (dd1=0; dd1 != di-1; ++dd1)
	for (dd2=dd1+1; dd2 != di; ++dd2){
	  gsl_matrix_set(data->SS,dd1,dd2,*curr_line);
	  gsl_matrix_set(data->SS,dd2,dd1,*curr_line);
	  curr_line++;
	}
      for (dd1=0; dd1 != di; ++dd1)
	for (dd2=0; dd2 != d; ++dd2)
	  gsl_matrix_set(data->RR,dd1,dd2,*(curr_line++));
      curr_line -= dd;
      dd = 0;
      data++;
      if (ii == N) break;
    }
  }

  data = startdata;

  fclose(inputfile);
  free(curr_line);

  return true;
}


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

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_chebyshev.h>

int gsl_cheb_calc_integ(gsl_cheb_series * integ, const gsl_cheb_series * f)
{
  const size_t n = f->order + 1;
  const double con = 0.25 * (f->b - f->a);

  if(integ->order != f->order)
    {
      GSL_ERROR ("order of chebyshev series must be equal", GSL_ENOMEM);
    }

  /* set the other parameters in the chebyshev struct */

  integ->a = f->a;
  integ->b = f->b;

  /* FIXME:  should probably set integ->f[] as well */

  if(n == 1) {
    integ->c[0] = 0.;
  }
  else if(n == 2) {
    integ->c[1] = con * f->c[0];
    integ->c[0] = 2.0 * integ->c[1];
  }
  else {
    double sum = 0.0;
    double fac = 1.0;
    size_t i;
    for(i=1; i<=n-2; i++) {
      integ->c[i] = con * (f->c[i-1] - f->c[i+1])/((double)i);
      sum += fac * integ->c[i];
      fac = -fac;
    }
    integ->c[n-1] = con * f->c[n-2]/(n-1.0);
    sum += fac * integ->c[n-1];
    integ->c[0] = 2.0 * sum;
  }

  return GSL_SUCCESS;
}