/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*                  This file is part of the program                         */
/*          GCG --- Generic Column Generation                                */
/*                  a Dantzig-Wolfe decomposition based extension            */
/*                  of the branch-cut-and-price framework                    */
/*         SCIP --- Solving Constraint Integer Programs                      */
/*                                                                           */
/* Copyright (C) 2010-2019 Operations Research, RWTH Aachen University       */
/*                         Zuse Institute Berlin (ZIB)                       */
/*                                                                           */
/* This program is 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 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 Lesser General Public License for more details.                       */
/*                                                                           */
/* You should have received a copy of the GNU Lesser General Public License  */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**@file   graph_gcg.h
 * @brief  Implementation of the graph which supports both node and edge weights.
 * @author Igor Pesic
 */

/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/

#ifndef GCG_GRAPH_GCG_H_
#define GCG_GRAPH_GCG_H_

#include <map>
#include "bridge.h"

#ifdef WITH_GSL
   #include <gsl/gsl_spmatrix.h>
   #include <gsl/gsl_spblas.h>
#endif

namespace gcg {

class EdgeGCG
{
public:
    int src, dest;
    double weight;
    EdgeGCG() {}
    EdgeGCG(int s, int d, double w): src(s), dest(d), weight(w) {}
} ;

class GraphGCG: public gcg::Bridge
{
private:
   bool undirected;
   bool locked;           // true if we are not allowed to change the graph anymore
   bool initialized;      // true if at least 1 node
   std::vector<int> nodes;
#ifdef WITH_GSL
   gsl_spmatrix* adj_matrix_sparse;
   gsl_spmatrix* working_adj_matrix;      // this one is used ONLY during MCL algorithm!
#else
   std::vector<std::vector<double>> adj_matrix;      /** For undirected graphs, this matrix is symmetrical */
#endif
   std::vector<EdgeGCG*> edges;

public:

   GraphGCG();
   GraphGCG(int _n_nodes, bool _undirected);

   virtual ~GraphGCG();
   virtual SCIP_RETCODE addNNodes(int _n_nodes);
   virtual SCIP_RETCODE addNNodes(int _n_nodes, std::vector<int> weights);
   virtual int getNNodes();
   virtual int getNEdges();

#ifdef WITH_GSL
   virtual gsl_spmatrix* getAdjMatrix();
   virtual void expand(int factor);
   virtual void inflate(double factor);
   virtual void colL1Norm();
   virtual void prune();
   virtual bool stopMCL(int iter);
   virtual std::vector<int> getClustersMCL();
   virtual void initMCL();
   virtual void clearMCL();
#else
   virtual std::vector<std::vector<double>> getAdjMatrix();
#endif
   virtual SCIP_RETCODE getEdges(std::vector<void*>& edges);
   virtual SCIP_Bool isEdge(int node_i, int node_j);
   virtual int getNNeighbors(int node);
   virtual std::vector<int> getNeighbors(int node);
   virtual std::vector<std::pair<int, double> > getNeighborWeights(int node);
   virtual SCIP_RETCODE addNode(int node, int weight);
   virtual SCIP_RETCODE addNode();                    /** Sets node weight to 0 and the ID to the next available. */
   virtual SCIP_RETCODE deleteNode(int node);
   virtual SCIP_RETCODE addEdge(int i, int j);        /** Sets edge weight to 1. */
   virtual SCIP_RETCODE addEdge(int node_i, int node_j, double weight);
   virtual SCIP_RETCODE setEdge(int node_i, int node_j, double weight);
   virtual SCIP_RETCODE deleteEdge(int node_i, int node_j);
   virtual int graphGetWeights(int node);
   virtual double getEdgeWeight(int node_i, int node_j);

   virtual int edgeComp(const EdgeGCG* a, const EdgeGCG* b);

   virtual SCIP_RETCODE flush();       // lock the graph and compresses the adj matrix if we use GSL
   virtual SCIP_RETCODE normalize();
   virtual double getEdgeWeightPercentile(double q);
};

} /* namespace gcg */
#endif /* GCG_GRAPH_GCG_H_ */


/*
* -----------------------------------------------------------------
*  Thermochemistry Library --- thrm_lib.c
*  Version: 1.6180
*  Date: Mar 15, 2010
* -----------------------------------------------------------------
*  Programmer: Americo Barbosa da Cunha Junior
*              americo.cunhajr@gmail.com
* -----------------------------------------------------------------
*  Copyright (c) 2010 by Americo Barbosa da Cunha Junior
*
*  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.
*
*  A copy of the GNU General Public License is available in
*  LICENSE.txt or http://www.gnu.org/licenses/.
* -----------------------------------------------------------------
*  This is the implementation file of a library
*  with thermochemistry routines.
* -----------------------------------------------------------------
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>

#include "../include/cklib.h"
#include "../include/ckthrm.h"
#include "../include/thrm_lib.h"

/*
*------------------------------------------------------------
*   thrm_title
*
*   This function prints the program title on the screen
*
*   last update: Oct 20, 2009
* -----------------------------------------------------------------
*/

void thrm_title()
{
    time_t curtime = time (NULL);
    struct tm *loctime = localtime (&curtime);

    printf("\n");
    printf("=============================================");
    printf("\n Themochemistry Direct Simulation\n");
    printf("\n Author:");
    printf("\n Americo Barbosa da Cunha Junior --- PUC-Rio");
    printf("\n americo.cunhajr@gmail.com\n");
    printf("\n Date:");
    printf("\n %s", asctime(loctime) );
    printf("=============================================");
    printf("\n");

    return;
}

/*
*------------------------------------------------------------
*   thrm_mech
*
*   This function prints on the screen informations about the
*   chemical kinetics mechanism
*
*   Input:
*   n_e  - # of elements
*   n_s  - # of species
*   n_r  - # of reactions
*   mech - mechanism name
*
*   last update: Jul 22, 2009
*------------------------------------------------------------
*/

void thrm_mech(int n_e,int n_s,int n_r,char *mech)
{
    printf("\n");
    printf("\n Thermochemistry Mechanism: %s", mech);
    printf("\n # of elements  = %d", n_e);
    printf("\n # of species   = %d", n_s);
    printf("\n # of reactions = %d", n_r);
    printf("\n");
    printf("\n");

    return;
}
/*------------------------------------------------------------*/

/*
* -----------------------------------------------------------------
*   thrm_input
*
*   This function receives the pmsr parameters
*   and returns GSL_SUCCESS if there is no error.
*
*   Input:
*   Np       - # of particles
*   k        - # of discrete time steps
*   delta_t  - time step
*   tau_res  - residence time
*   tau_mix  - mixture time
*   tau_pair - pairwize time
*
*   Output:
*   success or error
*
*   last update: Feb 5, 2009
* -----------------------------------------------------------------
*/

int thrm_input(int *steps,double *t0,double *delta_t,
                            double *Tmax,double *Tmin,double *tol)
{
    printf("\n Input simulation parameters:\n");

    printf("\n # of integration steps:");
    scanf("%d", steps);
    printf("\n %d\n", *steps);
    if( *steps <= 0 )
	return GSL_EINVAL;

    printf("\n initial time:");
    scanf("%lf", t0);
    printf("\n %+.1e\n", *t0);
    if( *t0 < 0.0 )
	return GSL_EINVAL;

    printf("\n time step:");
    scanf("%lf", delta_t);
    printf("\n %+.1e\n", *delta_t);
    if( *delta_t <= 0.0 )
	return GSL_EINVAL;

    printf("\n temperature upper bound:");
    scanf("%lf", Tmax);
    printf("\n %+.1e\n", *Tmax);
    if( *Tmax < 0.0 )
	return GSL_EINVAL;

    printf("\n temperature lower bound:");
    scanf("%lf", Tmin);
    printf("\n %+.1e\n", *Tmin);
    if( *Tmin < 0.0 || *Tmin > *Tmax )
	return GSL_EINVAL;

    printf("\n tolerance:");
    scanf("%lf", tol);
    printf("\n %+.1e\n", *tol);
    if( *tol <= 0.0 )
	return GSL_EINVAL;

    return GSL_SUCCESS;
}
/*----------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_alloc
*
*   This function allocates a struct thermochemistry.
*
*   Output:
*   thrm - pointer to a struct thermochemistry
*
*   last update: May 10, 2009
*------------------------------------------------------------
*/

thrm_wrk* thrm_alloc()
{
    thrm_wrk *thrm = NULL;

    /* memory allocation for ck_wrk struct */
    thrm = (thrm_wrk *) malloc(sizeof(thrm_wrk));
    if ( thrm == NULL )
        return NULL;

    /* setting ck_wrk elements equal NULL and 0.0 */
    thrm->iwk    = NULL;
    thrm->rwk    = NULL;
    thrm->cwk    = NULL;
    thrm->leniwk = 0;
    thrm->lenrwk = 0;
    thrm->lencwk = 0;
    thrm->n_e    = 0;
    thrm->n_s    = 0;
    thrm->n_r    = 0;

    return thrm;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_free
*
*   This function frees the memory used by CK work vectors
*
*   Input:
*   thrm - pointer to a struct chemkin
*
*   Output:
*   void
*
*   last update: Jul 21, 2009
*------------------------------------------------------------
*/

void thrm_free(void **thrm_bl)
{
    thrm_wrk* thrm = NULL;

    /* checking if thrm_bl is NULL */
    if ( *thrm_bl == NULL )
        return;

    thrm = (thrm_wrk *) (*thrm_bl);

    /* releasing ck work vectors */
    if( thrm->iwk != NULL )
    {
        free(thrm->iwk);
        thrm->iwk = NULL;
    }

    if( thrm->rwk != NULL )
    {
        free(thrm->rwk);
        thrm->rwk = NULL;
    }

    if( thrm->cwk != NULL )
    {
        free(thrm->cwk);
        thrm->cwk = NULL;
    }

    /* releasing thrm struct */
    free(*thrm_bl);
    *thrm_bl = NULL;

    return;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_init
*
*   This function initiates chemkin work vectors and the
*   mechanism parameters.
*
*   Input:
*   thrm - pointer to a struct thermochemistry
*
*   Output:
*   success or error
*
*   last update: Feb 19, 2010
*------------------------------------------------------------
*/

int thrm_init(thrm_wrk *thrm)
{
    if ( thrm == NULL )
        return GSL_EINVAL;

    /* calling ck_wrk_len subroutine */
    ck_wrk_len(&(thrm->leniwk),&(thrm->lenrwk),&(thrm->lencwk));

    /* memory allocation for ck work vectors */
    thrm->iwk =    (int *) malloc(   (thrm->leniwk)*sizeof(int));
    thrm->rwk = (double *) malloc(   (thrm->lenrwk)*sizeof(double));
    thrm->cwk =   (char *) malloc(16*(thrm->lencwk)*sizeof(char));
    if ( thrm->iwk == NULL || thrm->rwk == NULL || thrm->cwk == NULL )
    {
        thrm_free((void **)&thrm);
        return GSL_ENOMEM;
    }

    /* calling ck_wrk_init subroutine */
    ck_wrk_init(&(thrm->leniwk),&(thrm->lenrwk),&(thrm->lencwk),
        thrm->iwk,thrm->rwk,thrm->cwk);

    /* calling ck_wrk_indx subroutine */
    ck_wrk_indx(thrm->iwk,thrm->rwk,&(thrm->n_e),&(thrm->n_s),&(thrm->n_r));

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_composition
*
*   This function sets composition vector with values.
*
*   Input:
*   thrm - pointer to a struct thermochemistry
*
*   Output:
*   phi - composition vector
*   success or error
*
*   last update: Mar 2, 2010
*------------------------------------------------------------
*/

void thrm_composition(thrm_wrk *thrm,gsl_vector *phi)
{
    /* calling ck_wrk_comp subroutine */
    ck_composition(&(thrm->n_s),thrm->iwk,thrm->rwk,thrm->cwk,phi->data);

    return;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_page_heading0
*
*   This function prints the properties names on the screen.
*
*   Input:
*   thrm - pointer to a struct thermochemistry
*
*   last update: Nov 6, 2009
*------------------------------------------------------------
*/

void thrm_page_heading0(FILE *file)
{
    fprintf(file," t(s)      ");
    fprintf(file," T(K)      ");
    fprintf(file," h         ");
    fprintf(file," p         ");
    fprintf(file," CO2       ");
    fprintf(file," O2        ");
    fprintf(file," CO        ");
    fprintf(file," O         ");

    return;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_h2T
*
*   This function computes the mixture temperature for a
*   given enthalpy, pressure and species mass fraction.
*
*   Input:
*   ck  - pointer to a struct chemkin
*   phi - composition vector
*
*   Output:
*   T   - mixture temperature
*
*   last update: Mar 2, 2010
*------------------------------------------------------------
*/

double thrm_h2T(thrm_wrk *thrm,double *phi,double Tmax,double Tmin,double tol)
{
    double T = 0.0;

    ck_h2t_bisec(&(thrm->n_s),phi,&Tmax,&Tmin,&tol,thrm->iwk,thrm->rwk,&T);
    /*ck_h2t_newt(&(thrm->n_s),phi,&tol,thrm->iwk,thrm->rwk,&T,&T);*/

    return T;
}
/*------------------------------------------------------------*/

/*
* -----------------------------------------------------------------
*   thrm_temp_meanvar
*
*   This function computes the temperature mean and variance
*   using the algorithm of Knuth/Welford.
*
*   Ref.:
*   D. E. Knuth (1998)
*   The Art of Computer Programming,
*   volume 2: Seminumerical Algorithms
*   3rd ed., Addison-Wesley.
*
*   B. P. Welford (1962).
*   "Note on a method for calculating corrected sums
*   of squares and products". Technometrics 4(3):419–420
*
*   Input:
*   Np   - # of particles
*   ps   - system of particle
*   thrm - pointer to a struct thermochemistry
*   Tmax - temperature upper bound
*   Tmin - temperature lower bound
*   tol  - tolerance
*   mean - temperature mean
*   var  - temperature variance
*
*   Output:
*   success or error
*
*   last update: Mar 4, 2010
* -----------------------------------------------------------------
*/

void thrm_temp_meanvar(int Np,gsl_vector **ps,thrm_wrk *thrm,
        double Tmax,double Tmin,double tol,double *mean,double *var)
{
    int i;
    double T     = 0.0;
    double S     = 0.0;
    double M     = 0.0;
    double delta = 0.0;

    for ( i = 0; i < Np; i++ )
    {
        T     = thrm_h2T(thrm,ps[i]->data,Tmax,Tmin,tol);
	delta = T - M;
	M    += delta/(double) (i+1);
        S    += delta*(T - M);
    }

    /* mean */
    *mean  = M;

    /* variance */
    *var   = S/(double) (i-1);

    return;
}
/*----------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_rrsum
*
*   This function computes the reaction rates sum given the
*   reaction rate vector.
*
*   Input:
*   phi - composition vector
*
*   Output:
*   sum - reaction rates sum
*
*   last update: Sep 28, 2009
*------------------------------------------------------------
*/

double thrm_rrsum(gsl_vector *phi)
{
    unsigned int i;
    double sum = 0.0;

    for (i = 2; i < phi->size; i++)
        sum += phi->data[i];

    return sum;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_mfsum
*
*   This function computes the mass fraction sum given a
*   composition vector.
*
*   Input:
*   phi - composition vector
*
*   Output:
*   sum - mass fraction sum
*
*   last update: Sep 24, 2009
*------------------------------------------------------------
*/

double thrm_mfsum(gsl_vector *phi)
{
    unsigned int i;
    double sum = 0.0;

    for (i = 2; i < phi->size; i++)
        sum += phi->data[i];

    return sum;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_mfsign
*
*   This function checks if the mass fraction
*   components are all positive in a composition vector.
*
*   Input:
*   phi  - composition vector
*
*   Output:
*   flag - success or fail
*
*   last update: Sep 28, 2009
*------------------------------------------------------------
*/

int thrm_mfsign(gsl_vector *phi)
{
    unsigned int i;
    int flag = GSL_SUCCESS;

    for (i = 2; i < phi->size; i++)
        if ( phi->data[i] < 0.0 )
            flag = GSL_EFAILED;

    return flag;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_eqs
*
*   This function defines the set of governing diferential
*   equations, one for enthalpy, one for pressure and ns
*   for species mass fractions.
*
*   Input:
*   t         - instant time
*   phi       - composition vector
*   Sphi      - reaction rate vector
*   thrm_data - pointer to external data
*
*   Output:
*   success or error
*
*   last update: Feb 19, 2010
*------------------------------------------------------------
*/

int thrm_eqs(double t,N_Vector phi,N_Vector Sphi,void *thrm_data)
{
    int i, Neq;
    double tol = 1.0e-4;
    double Tmax = 5000.0, Tmin = 100.0;
    double p, T, rho;
    double *phi_data  = NULL;
    double *Sphi_data = NULL;
    double *wdot      = NULL;
    double *wt        = NULL;
    thrm_wrk *thrm    = NULL;

    /* initiating ck workspace */
    thrm = (thrm_wrk *)thrm_data;
    if ( thrm == NULL )
        return GSL_EINVAL;

    /* obtaining phi and Rphi length and components */
    Neq       = NV_LENGTH_S(phi);
    phi_data  = NV_DATA_S(phi);
    Sphi_data = NV_DATA_S(Sphi);

    /* memory allocation for species molar mass and reaction rate vectors */
    wt   = (double *) malloc((Neq-2)*sizeof(double));
    wdot = (double *) malloc((Neq-2)*sizeof(double));
    if( wt == NULL || wdot == NULL )
    {
        free(wt);
        free(wdot);
        return GSL_ENOMEM;
    }

    /* pressure (dynes/cm^2) */
    p = phi_data[1];

    /* mixture temperature (K) */
    T = thrm_h2T(thrm,phi_data,Tmax,Tmin,tol);
    if( T < tol )
        return GSL_EFAILED;

    /* mixture density (g/cm^3)*/
    ckrhoy(&p,&T,&(phi_data[2]),thrm->iwk,thrm->rwk,&rho);

    /* species molar weight (g/mol) */
    ckwt(thrm->iwk,thrm->rwk,wt);

    /* species molar production rates (mol/(cm^3.s)) */
    ckwyp(&p,&T,&(phi_data[2]),thrm->iwk,thrm->rwk,wdot);

    /* dh/dt = 0 (isoenthalpic) */
    Sphi_data[0] = 0.0;

    /* dp/dt = 0 (isobaric) */
    Sphi_data[1] = 0.0;

    /* dY[i]/dt = wdot[i-2].wt[i-2]/rho */
    for ( i = 2; i < Neq; i++ )
        Sphi_data[i] = wdot[i-2]*wt[i-2]/rho;

    /* releasing allocated memory */
    free(wdot);
    free(wt);
    phi_data  = NULL;
    Sphi_data = NULL;
    wdot      = NULL;
    wt        = NULL;
    thrm      = NULL;

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/

/*
*------------------------------------------------------------
*   thrm_eqs2
*
*   This function defines the set of governing diferential
*   equations, one for temperature and ns
*   for species mass fractions.
*
*   Input:
*   t         - instant time
*   phi       - state vector
*   Sphi      - reaction rate vector
*   thrm_data - pointer to external data
*
*   Output:
*   success or error
*
*   last update: Dec 28, 2010
*------------------------------------------------------------
*/

int thrm_eqs2(double t,N_Vector phi,N_Vector Sphi,void *thrm_data)
{
    int i, Neq;
    double T, p, rho, cpb, sum = 0.0;
    double *phi_data  = NULL;
    double *Sphi_data = NULL;
    double *wdot      = NULL;
    double *wt        = NULL;
    double *h         = NULL;
    thrm_wrk *thrm    = NULL;

    /* initiating ck workspace */
    thrm = (thrm_wrk *)thrm_data;
    if ( thrm == NULL )
        return GSL_EINVAL;

    /* obtaining phi and Rphi length and components */
    Neq       = NV_LENGTH_S(phi);
    phi_data  = NV_DATA_S(phi);
    Sphi_data = NV_DATA_S(Sphi);

    /* memory allocation for species molar mass and reaction rate vectors */
    wt   = (double *) malloc((Neq-2)*sizeof(double));
    wdot = (double *) malloc((Neq-2)*sizeof(double));
    h    = (double *) malloc((Neq-2)*sizeof(double));
    if( wt == NULL || wdot == NULL || h == NULL)
    {
        free(wt);
        free(wdot);
	free(h);
        return GSL_ENOMEM;
    }

    /* mixture temperature (K) */
    T = phi_data[0];

    /* pressure (dynes/cm^2) */
    p = phi_data[1];

    /* mixture density (g/cm^3)*/
    ckrhoy(&p,&T,&(phi_data[2]),thrm->iwk,thrm->rwk,&rho);

    /* mean specific heat at constant pressure (ergs/g.K) */
    ckcpbs(&T,&(phi_data[2]),thrm->iwk,thrm->rwk,&cpb);

    /* species molar weight (g/mol) */
    ckwt(thrm->iwk,thrm->rwk,wt);

    /* species molar production rates (mol/(cm^3.s)) */
    ckwyp(&p,&T,&(phi_data[2]),thrm->iwk,thrm->rwk,wdot);

    /* species specific enthalpy (ergs/g) */
    ckhms(&T,thrm->iwk,thrm->rwk,h);

    /* dY[i+2]/dt = wdot[i].wt[i]/rho */
    for ( i = 0; i < thrm->n_s; i++ )
    {
        Sphi_data[i+2] = wdot[i]*wt[i]/rho;
	sum += h[i]*wdot[i]*wt[i];
    }

    /* dT/dt =  -1/(rho.cpb) . sum_i=1^ns h[i].wdot[i].wt[i] */
    Sphi_data[0] = -sum/(rho*cpb);

    /* dp/dt = 0 (isobaric) */
    Sphi_data[1] = 0.0;

    /* releasing allocated memory */
    free(wdot);
    free(wt);
    free(h);
    phi_data  = NULL;
    Sphi_data = NULL;
    wdot      = NULL;
    wt        = NULL;
    h         = NULL;
    thrm      = NULL;

    return GSL_SUCCESS;
}
/*------------------------------------------------------------*/


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*!
 *  \file dnest_line2d.c
 *  \brief run dnest for 2d line analysis.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_interp.h>
#include <mpi.h>

#include "brains.h"

DNestFptrSet *fptrset_line2d;

/*!
 * this function does dnest sampling.
 */
int dnest_line2d(int argc, char **argv)
{
  int i;

  set_blr_model2d();

  num_params_blr = num_params_blr_model + num_params_nlr
                 + num_params_res + num_params_linecenter + 1; /* include line sys err */
  num_params_blr_tot = num_params_blr;
  num_params = parset.n_con_recon + num_params_blr_tot + num_params_var;
  idx_resp = num_params_blr_tot + num_params_drw + num_params_trend;
  idx_difftrend = idx_resp + num_params_resp;
  idx_linecenter = num_params_blr_model + num_params_nlr + num_params_res;

  par_fix = (int *) malloc(num_params * sizeof(int));
  par_fix_val = (double *) malloc(num_params * sizeof(double));
  par_range_model = malloc( num_params * sizeof(double *));
  par_prior_gaussian = malloc( num_params * sizeof(double *));
  for(i=0; i<num_params; i++)
  {
    par_range_model[i] = malloc(2*sizeof(double));
    par_prior_gaussian[i] = malloc(2*sizeof(double));
  }
  par_prior_model = malloc( num_params * sizeof(int));

  fptrset_line2d = dnest_malloc_fptrset();
  /* setup functions used for dnest*/
  fptrset_line2d->from_prior = from_prior_line2d;
  fptrset_line2d->print_particle = print_particle_line2d;
  fptrset_line2d->restart_action = restart_action_2d;
  fptrset_line2d->accept_action = accept_action_2d;
  fptrset_line2d->kill_action = kill_action_2d;
  fptrset_line2d->perturb = perturb_line2d;
  fptrset_line2d->read_particle = read_particle_line2d;

  if(parset.flag_exam_prior != 1)
  {
    fptrset_line2d->log_likelihoods_cal_initial = log_likelihoods_cal_initial_line2d;
    fptrset_line2d->log_likelihoods_cal_restart = log_likelihoods_cal_restart_line2d;
    fptrset_line2d->log_likelihoods_cal = log_likelihoods_cal_line2d;
  }
  else
  {
    fptrset_line2d->log_likelihoods_cal_initial = log_likelihoods_cal_line2d_exam;
    fptrset_line2d->log_likelihoods_cal_restart = log_likelihoods_cal_line2d_exam;
    fptrset_line2d->log_likelihoods_cal = log_likelihoods_cal_line2d_exam;
  }

  set_par_range_model2d();
  /* setup the fixed parameters */
  set_par_fix_blrmodel();

  /* setup the remaining paramters */
  for(i=num_params_blr_model; i<num_params; i++)
  {
    par_fix[i] = 0;
    par_fix_val[i] = -DBL_MAX;
  }

  /* cope with narrow line */
  if(parset.flag_narrowline == 2)
  {
    /* flux */
    if(parset.flux_narrowline_err == 0.0)
    {
      par_fix[num_params_blr_model] = 1.0;
      par_fix_val[num_params_blr_model] = 0.0;
    }
  }
  if(parset.flag_narrowline >= 2)
  {
    /* width */
    if(parset.width_narrowline_err == 0.0)
    {
      par_fix[num_params_blr_model+1] = 1.0;
      par_fix_val[num_params_blr_model+1] = 0.0;
    }
    /* shift */
    if(parset.shift_narrowline_err == 0.0)
    {
      par_fix[num_params_blr_model+2] = 1.0;
      par_fix_val[num_params_blr_model+2] = 0.0;
    }
  }

  /* if flag_fixvar is true, fix continuum variation parameter sigma and tau */
  if(parset.flag_fixvar == 1)
  {
    par_fix[num_params_blr + 1] = 1;
    par_fix_val[num_params_blr + 1] = var_param[1];
    par_fix[num_params_blr + 2] = 1;
    par_fix_val[num_params_blr + 2] = var_param[2];
  }

  /* fix systematic error of line */
  if(parset.flag_line_sys_err != 1)
  {
    par_fix[num_params_blr-1] = 1;
    par_fix_val[num_params_blr-1] = log(1.0);
  }

  /* fix systematic error of continuum */
  if(parset.flag_con_sys_err != 1)
  {
    par_fix[num_params_blr] = 1;
    par_fix_val[num_params_blr] = log(1.0);
  }

  /* fix non-linear response */
  if(parset.flag_nonlinear !=1)
  {
    par_fix[idx_resp + 1] = 1;
    par_fix_val[idx_resp + 1] = 0.0;
  }

  print_par_names_model2d();

  force_update = parset.flag_force_update;
  if(parset.flag_para_name != 1)
    logz_line2d = dnest(argc, argv, fptrset_line2d, num_params, dnest_options_file);

  dnest_free_fptrset(fptrset_line2d);
  return 0;
}

/*!
 * this function setups parameter ranges.
 *
 * The order of parameters is:                              \n
 *   I.   blr model.............()                          \n
 *   II.  narrow line...........(if flag_narrowline is true)\n
 *   III. spectral broadening...()                          \n
 *   IV.  line center...........(if flag_linecenter is true)\n
 *   V.   systematic error......()                          \n
 *   VI.  variability...........()                          \n
 *   VII. long-term trend.......()                          \n
 *   VIII.response A and Ag.....()                          \n
 *   IX.  different trend.......(if flag_difftend is ture)  \n
 *   X.   continuum light curve.()                          \n
 */
void set_par_range_model2d()
{
  int i, j, i1, i2;

  /* setup parameter range, BLR parameters first */
  for(i=0; i<num_params_blr_model; i++)
  {
    par_range_model[i][0] = blr_range_model[i][0];
    par_range_model[i][1] = blr_range_model[i][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;
  }

  /* cope with narrow line */
  for(i=num_params_blr_model; i<num_params_blr_model + num_params_nlr; i++)
  {
    par_range_model[i][0] = nlr_range_model[i - num_params_blr_model][0];
    par_range_model[i][1] = nlr_range_model[i - num_params_blr_model][1];

    par_prior_model[i] = nlr_prior_model[i - num_params_blr_model];
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0; /* note that for logarithm prior of flux, this value is not used, so does not matter */
  }
  /* cope with spectral broadening */
  for(i=num_params_blr_model + num_params_nlr; i<num_params_blr_model + num_params_nlr + num_params_res; i++)
  {
    par_range_model[i][0] = -10.0;
    par_range_model[i][1] =  10.0;

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }
  /* cope with line center */
  for(i=num_params_blr-num_params_linecenter-1; i< num_params_blr-1; i++)
  {
    par_range_model[i][0] = -10.0;
    par_range_model[i][1] =  10.0;

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }

  /* the last is systematic error */
  i = num_params_blr-1;
  par_range_model[i][0] = sys_err_line_range[0];
  par_range_model[i][1] = sys_err_line_range[1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  /* variability parameters */
   /* first systematic error */
  i = num_params_blr;
  par_range_model[i][0] = var_range_model[0][0];
  par_range_model[i][1] = var_range_model[0][1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  for(i=num_params_blr+1; i<num_params_drw + num_params_blr; i++)
  {
    if(var_param_std[i-num_params_blr] > 0.0)
    {
      par_range_model[i][0] = var_param[i-num_params_blr] - 5.0 * var_param_std[i-num_params_blr];
      par_range_model[i][1] = var_param[i-num_params_blr] + 5.0 * var_param_std[i-num_params_blr];

      /* make sure that the range lies within the initial range */
      par_range_model[i][0] = fmax(par_range_model[i][0], var_range_model[i-num_params_blr][0]);
      par_range_model[i][1] = fmin(par_range_model[i][1], var_range_model[i-num_params_blr][1]);

      par_prior_model[i] = GAUSSIAN;
      par_prior_gaussian[i][0] = var_param[i-num_params_blr];
      par_prior_gaussian[i][1] = var_param_std[i-num_params_blr];
    }
    else
    {
      par_range_model[i][0] = var_range_model[i-num_params_blr][0];
      par_range_model[i][1] = var_range_model[i-num_params_blr][1];

      par_prior_model[i] = UNIFORM;
      par_prior_gaussian[i][0] = 0.0;
      par_prior_gaussian[i][1] = 0.0;
    }
  }
  /* long-term trend of continuum */
  for(i=num_params_drw + num_params_blr; i< num_params_drw + num_params_trend + num_params_blr; i++)
  {
    par_range_model[i][0] = var_range_model[3][0];
    par_range_model[i][1] = var_range_model[3][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }

  /* response A and Ag */
  j = 0;
  i1 = idx_resp;
  i2 = idx_resp + num_params_resp;
  for(i=i1; i<i2; i++)
  {
    par_range_model[i][0] = resp_range[j][0];
    par_range_model[i][1] = resp_range[j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }

  /* different trend in continuum and line */
  j = 0;
  i1 = idx_difftrend;
  i2 = idx_difftrend + num_params_difftrend;
  for(i=i1; i< i2; i++)
  {
    par_range_model[i][0] = var_range_model[4 + j][0];
    par_range_model[i][1] = var_range_model[4 + j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }

  /* continuum ligth curve values */
  for(i=num_params_blr+num_params_var; i<num_params; i++)
  {
    par_range_model[i][0] = var_range_model[4+num_params_difftrend][0];
    par_range_model[i][1] = var_range_model[4+num_params_difftrend][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }

  return;
}

/*!
 *  print names and prior ranges for parameters
 *
 */
void print_par_names_model2d()
{
  if(thistask != roottask)
    return;

  int i, j;
  FILE *fp;
  char fname[BRAINS_MAX_STR_LENGTH], str_fmt[BRAINS_MAX_STR_LENGTH];

  sprintf(fname, "%s/%s", parset.file_dir, "data/para_names_model2d.txt");
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
    exit(0);
  }

  strcpy(str_fmt, "%4d %-15s %10.6f %10.6f %4d %4d %15.6e\n");

  printf("# Print parameter name in %s\n", fname);

  fprintf(fp, "#*************************************************\n");
  fprint_version(fp);
  fprintf(fp, "#*************************************************\n");

  fprintf(fp, "%4s %-15s %10s %10s %4s %4s %15s\n", "#", "Par", "Min", "Max", "Prior", "Fix", "Val");

  i=-1;
  for(j=0; j<num_params_blr_model; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "BLR model", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  for(j=0; j<num_params_nlr; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "narrow line", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }
  for(j=0; j<num_params_res; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "line broaden", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }
  for(j=0; j<num_params_linecenter; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "line center", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "sys_err_line", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  i++;
  fprintf(fp, str_fmt, i, "sys_err_con", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "sigmad", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "taud", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_trend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "A", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  i++;
  fprintf(fp, str_fmt, i, "Ag", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_difftrend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "diff trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  for(j=0; j<parset.n_con_recon; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "time series", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  fclose(fp);
}

/*!
 * this function generates a sample from prior.
 */
void from_prior_line2d(void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    if(par_prior_model[i] == GAUSSIAN)
    {
      pm[i] = dnest_randn()*par_prior_gaussian[i][1] + par_prior_gaussian[i][0];
      dnest_wrap(&pm[i], par_range_model[i][0], par_range_model[i][1]);
    }
    else
    {
      pm[i] = par_range_model[i][0] + dnest_rand() * (par_range_model[i][1] - par_range_model[i][0]);
    }
  }

  /* cope with fixed parameters */
  for(i=0; i<num_params_blr + num_params_var; i++)
  {
    if(par_fix[i] == 1)
      pm[i] = par_fix_val[i];
  }

  which_parameter_update = -1;

  return;
}

/*!
 * this function calculates likelihood at initial step.
 */
double log_likelihoods_cal_initial_line2d(const void *model)
{
  double logL;
  logL = prob_initial_line2d(model);
  return logL;
}

/*!
 * this function calculates likelihood at initial step.
 */
double log_likelihoods_cal_restart_line2d(const void *model)
{
  double logL;
  logL = prob_restart_line2d(model);
  return logL;
}

/*!
 * this function calculates likelihood.
 */
double log_likelihoods_cal_line2d(const void *model)
{
  double logL;
  logL = prob_line2d(model);
  return logL;
}

/*!
 * this function prints out parameters.
 */
void print_particle_line2d(FILE *fp, const void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    fprintf(fp, "%e ", pm[i] );
  }
  fprintf(fp, "\n");
  return;
}

/*!
 * This function read the particle from the file.
 */
void read_particle_line2d(FILE *fp, void *model)
{
  int j;
  double *psample = (double *)model;

  for(j=0; j < dnest_num_params; j++)
  {
    if(fscanf(fp, "%lf", psample+j) < 1)
    {
      printf("%f\n", *psample);
      fprintf(stderr, "#Error: Cannot read file %s.\n", options.sample_file);
      exit(0);
    }
  }
  return;
}

/*!
 * this function perturbs parameters.
 */
double perturb_line2d(void *model)
{
  double *pm = (double *)model;
  double logH = 0.0, limit1, limit2, width, rnd;
  int which, which_level;

  /*
   * fixed parameters need not to update
   * perturb important parameters more frequently
   */
  do
  {
    rnd = dnest_rand();
    if(rnd < fmax(0.2, 1.0*(num_params_blr+num_params_var)/num_params))
      which = dnest_rand_int(num_params_blr + num_params_var);
    else
      which = dnest_rand_int(parset.n_con_recon) + num_params_blr + num_params_var;
  }while(par_fix[which] == 1);

  which_parameter_update = which;

  /* level-dependent width */
  which_level_update = dnest_get_which_level_update();
  which_level = which_level_update > (size_levels-10)?(size_levels-10):which_level_update;

  if( which_level > 0)
  {
    limit1 = limits[(which_level-1) * num_params *2 + which *2];
    limit2 = limits[(which_level-1) * num_params *2 + which *2 + 1];
    width = limit2 - limit1;
  }
  else
  {
    width = ( par_range_model[which][1] - par_range_model[which][0] );
  }

  width /= (2.35);

  if(par_prior_model[which] == GAUSSIAN)
  {
    logH -= (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
    pm[which] += dnest_randh() * width;
    dnest_wrap(&pm[which], par_range_model[which][0], par_range_model[which][1]);
    logH += (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
  }
  else
  {
    pm[which] += dnest_randh() * width;
    dnest_wrap(&(pm[which]), par_range_model[which][0], par_range_model[which][1]);
  }

  return logH;
}

/*!
 * this function calculates likelihood.
 */
double log_likelihoods_cal_line2d_exam(const void *model)
{
  return 0.0;
}

void accept_action_2d()
{
  int param;
  double *ptemp;

  // the parameter previously updated
  param = which_parameter_update;
  // continuum parameter is updated
  if(param >= num_params_blr)
  {
    /*
     *note that (response) Fline is also changed as long as Fcon is changed.
     *num_params_blr-the parameter is the systematic error of continuum.
     *the change of this parameter also changes continuum reconstruction.
     */

    ptemp = Fcon_rm_particles[which_particle_update];
    Fcon_rm_particles[which_particle_update] = Fcon_rm_particles_perturb[which_particle_update];
    Fcon_rm_particles_perturb[which_particle_update] = ptemp;

    if(force_update != 1)
    {
      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;
    }
  }
  else if( param < num_params_blr -1 && force_update != 1)
  {
    /* BLR parameter is updated
     * Note a) that the (num_par_blr-1)-th parameter is systematic error of line.
     * when this parameter is updated, Trans2D and Fline are unchanged.
     *      b) Fline is always changed, except for param = num_params_blr-1.
     */

    {
      ptemp = TransTau_particles[which_particle_update];
      TransTau_particles[which_particle_update] = TransTau_particles_perturb[which_particle_update];
      TransTau_particles_perturb[which_particle_update] = ptemp;

      ptemp = Trans2D_at_veldata_particles[which_particle_update];
      Trans2D_at_veldata_particles[which_particle_update] = Trans2D_at_veldata_particles_perturb[which_particle_update];
      Trans2D_at_veldata_particles_perturb[which_particle_update] = ptemp;

      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;

    }
  }

  return;
}

/*
 * action when particle i is killed in cdnest sampling.
 * particle i_copy's properties is copyed to particle i.
 */
void kill_action_2d(int i, int i_copy)
{
  memcpy(Fcon_rm_particles[i], Fcon_rm_particles[i_copy], parset.n_con_recon * sizeof(double));
  memcpy(Fline_at_data_particles[i], Fline_at_data_particles[i_copy], n_line_data * n_vel_data_ext  * sizeof(double));
  memcpy(TransTau_particles[i], TransTau_particles[i_copy], parset.n_tau*sizeof(double));
  memcpy(Trans2D_at_veldata_particles[i], Trans2D_at_veldata_particles[i_copy], parset.n_tau * n_vel_data_ext * sizeof(double));
  return;
}