#ifndef PRIMAL_DUAL_LOOPLESS_H
#define PRIMAL_DUAL_LOOPLESS_H

#include "problem_data.h"

#include <string>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <stdio.h>      /* printf */
#include <time.h>
#include <fstream>
#include <algorithm>
#include <iomanip>
#include <ctime>
#include <sstream>
#include <omp.h>

//This class implements the loopless variance reduced type methods with arbitrary sampling

/*
The optimization problem to solve is:

\sum_{i=1}^n \lambda_i\phi_i(A_i^{\top} w)+ g(w)
Assumption 1: For each i, \phi_i is 1-smooth
By default, lambda_i=1/n for all i.
*
* The dual problem is
*         g*(-sum_{i=1}^n \lambda_i \alpha_i A_i)+\sum_{i=1}^n \lambda_i \phi*_i(\alpha_i)
*/

template<typename L, typename D>
class Primal_Dual_LOOPLESS: public problem_data<L, D>
{

private:

  // involved variables

  std::vector<D> proba_vector;

  std::vector<D>  tilde_proba_vector;

  std::vector<D> group_C;

  std::vector<D> index_group_C;

  std::vector<D> maxp_group_C;

  std::vector<D> isolated_I;

  std::vector<D> sump_group_C;
  vector<D> theta_S;   //theta_S in the paper

  std::vector<D> Li;

  std::vector<D> gk;     // the vector g^k in the paper

  // auxiliary variables
  std::vector<D> deltagk;
  std::vector<D> index_to_do_prox;
  std::vector<D> whether_or_not_to_do_prox;

  L nb_of_iters_per_loop;

  D primal_value;

  D dual_value;

  D epsilon;

  L max_nb_loops;

  D max_p;

  D maxv;

  D lambda_1;
  D lambda_2;

  D running_time;

  L nb_loops;

  L print_every_N;

  D delta;

  vector<L> batch_i;
  vector<L> my_batch;
  vector<D> batch_deltaalphai;

  L batch_size;

  L nb_groups;    //number of groups in group sampling

protected:

  std::vector<D> primal_x;     // the x variable

  string uniform;

  D Lf;

  D L2;

  D L1;

  D sumLi;

  D p; // the probability of changing w to x as in the paper
  L tau;  //number of threads on each node/computer

  D scaler;

  std::vector<D> dual_alpha;  //  dual_alpha[i]=\phi_i'(A_i't)

  std::vector<D> lambda_f;    // lambda_f[i]=lambda_i
  std::vector<D> baralpha;   // baralpha=sum_{i=1}^{nsamples} lambda_f[i]* dual_alpha[i]*A_i

  L current_nb_iters;

  L nb_iters;

  std::vector<D> last_update_i;

  ofstream samp;

public:

  gsl_rng * rng;
  virtual inline D gradient_of_phi_i(D, L){return D(NULL);}
  virtual inline D gradient_of_gstar_j(D, L){return D(NULL);}
  virtual inline D value_of_phi_i(D, L) {return D(NULL);}
  virtual inline D value_of_g_j(D, L){return D(NULL);}
  virtual inline D value_of_phistar_i(D,L) {return D(NULL);}
  virtual inline D value_of_gstar(vector<D> &){return D(NULL);}
  virtual inline D value_of_gstar_minus(vector<D> &, D){return D(NULL);}
  virtual inline D feasible_dual(vector<D> &){return D(NULL);}
  virtual inline D compute_delta_alpha(D,D,L){return D(NULL);}
  virtual inline void set_auxiliary_v(){}
  virtual inline D get_lambda1(){return D(NULL);}
  virtual inline D get_lambda2(){return D(NULL);}
  virtual inline D compute_current_xj_value(D , L, L , L){return D(NULL);}
  virtual inline D compute_current_xj_value_without_update(D , L, L , L){return D(NULL);}
  virtual inline void set_stepsize(){}
  virtual inline void update_x(D,L){}
  virtual inline void update_baralpha(){}

  Primal_Dual_LOOPLESS()
  : problem_data<L,D>()
  {

  }

  Primal_Dual_LOOPLESS(const char* matrix_file, const char* vector_file)
  : problem_data<L,D>(matrix_file, vector_file)
  {

  }

  Primal_Dual_LOOPLESS(const char* matrix_file)
  : problem_data<L,D>(matrix_file)
  {

  }

  void set_rng()
  {
    gsl_rng_env_setup();
    const gsl_rng_type * T;
    T = gsl_rng_default;
    rng = gsl_rng_alloc(T);
    gsl_rng_set(rng,time(NULL));
    //gsl_rng_set(rng, 27432042);

  }

  void set_print_every_N(L i){print_every_N=i;}

  void set_L1(L p){
    if(p==0){ //batch sampling mode  (stochastic process in the paper)
      L1=(1-1./tau)*Lf+L2;
    }else{ //group sampling mode
      L1=Lf+L2;
    }
    //  alpha_l=1/(4.*L1+2*L2);
  }

  void set_Li_Lf()
  {
    sumLi=0;
    maxv=0;
    D minv=std::numeric_limits<double>::max();
    Li.resize(this->nsamples,0);
    for(L i=0;i<this->nsamples;i++)
    {
      D vi=0;
      for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
      {
        vi+=lambda_f[i]*this->nsamples*this->A[k]*this->A[k];
      }
      Li[i]=vi;
      if(maxv<vi) maxv=vi;
      if(minv>vi) minv=vi;
    }
    for(L i=0;i<this->nsamples;i++){
      if(Li[i]<maxv*1e-15)
      {
        for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
        { this->A[k]=0;
          L j=this->row_idx[k];
          for (L kj = this->ptr_t[j]; kj < this->ptr_t[j + 1];	kj++)
          {
            L i2=this->col_idx[kj];
            if(i2==i) this->A_t[kj]=0;
          }
        }
        Li[i]=maxv*1e-15;
      }
      sumLi+=Li[i];
    }
    Lf=compute_lambda_max(10);
    cout<<"  max of v: "<<maxv<<"  min of v: "<<minv<<" sumof Li="<<sumLi<<" Lf= "<<Lf<<endl;
  }

  //compute the maximal eigenvalue of sum_{i=1}^m \lambda_f[i]*A_i*A_i' by power iteration
  D compute_lambda_max(L K){

    std::vector<D> bk(this->nfeatures);
    for (L j=0;j<this->nfeatures;j++)
    {
      bk[j]=1;
    }
    std::vector<D> yk(this->nsamples);
    D normk;
    D tmp;
    for(L kk=0;kk<K;kk++){
      for (L i=0;i<this->nsamples;i++){
        tmp=0;
        for (L k = this->ptr[i]; k < this->ptr[i + 1];	k++)
        {
          L j=this->row_idx[k];
          tmp+=this->A[k]*bk[j];
        }
        yk[i]=tmp;
      }
      normk=0;
      for (L j=0;j<this->nfeatures;j++){
        bk[j]=0;
        for (L k = this->ptr_t[j]; k < this->ptr_t[j + 1];	k++)
        {
          L i=this->col_idx[k];
          bk[j]+=this->A_t[k]*yk[i]*lambda_f[i];
        }
        normk+=bk[j]*bk[j];
      }
      normk=sqrt(normk);
      for (L j=0;j<this->nfeatures;j++)
      {bk[j]=bk[j]/normk; }
    }
    cout<<endl;
    D res=0;
    normk=0;
    for (L i=0;i<this->nsamples;i++){
      tmp=0;
      for (L k = this->ptr[i]; k < this->ptr[i + 1];	k++)
      {
        L j=this->row_idx[k];
        tmp+=this->A[k]*bk[j];
      }
      yk[i]=tmp;
      normk+=yk[i]*yk[i];
    }
    std::vector<D> bk2(this->nfeatures);
    for (L j=0;j<this->nfeatures;j++){
      bk2[j]=0;
      for (L k = this->ptr_t[j]; k < this->ptr_t[j + 1];	k++)
      {
        L i=this->col_idx[k];
        bk2[j]+=this->A_t[k]*yk[i]*lambda_f[i];
      }
    }
    for (L j=0;j<this->nfeatures;j++)
    res+=bk2[j]*bk[j];
    return res;
  }

  void set_optimal_probability()
  {
    tilde_proba_vector.clear();
    tilde_proba_vector.resize(this->nsamples,0);
    proba_vector.clear();
    proba_vector.resize(this->nsamples,0);
    D sum=0;
    for(L i=0; i<this->nsamples;i++)
    {
      tilde_proba_vector[i]=Li[i];
      sum+=Li[i];
    }
    max_p=0;
    for(L i=0; i<this->nsamples;i++)
    {
      tilde_proba_vector[i]=tilde_proba_vector[i]/sum;
      proba_vector[i]=1-pow(1-tilde_proba_vector[i],tau);
      if(max_p<tilde_proba_vector[i])
      {
        max_p=tilde_proba_vector[i];
      }
    }
    cout<<"sum="<<sum<<"; proba"<<tilde_proba_vector[0]<<endl;
  }

  void set_group_sampling_probability(){
    proba_vector.clear();
    proba_vector.resize(this->nsamples,0);
    std::vector<D> q(this->nsamples);
    D sumq=0;
    cout<<"start setting group sampling probablity"<<endl;
    for(L i=0; i<this->nsamples;i++)
    {
      q[i]=Li[i];
      sumq+=q[i];
    }
    D maxq=0;
    L nb=0;
    D tmp=0;
    for(L i=0; i<this->nsamples;i++)
    {
      q[i]=q[i]/sumq*tau;
      if(q[i]>maxq) maxq=q[i];
      if(q[i]>1) {
        nb++; //count the number of elements larger than 1
        tmp+=q[i]-1;
      }
    }
    cout<<"maxq="<<maxq<<endl;
    if(maxq<=1){
      for(L i=0; i<this->nsamples;i++)
      {
        proba_vector[i]=q[i];
      }
    }else{
      for(L i=0; i<this->nsamples;i++)
      {
        if(q[i]>1) q[i]=1;
        else {
          D deltaq=min(1-q[i],tmp);
          tmp=tmp-deltaq;
          q[i]+=deltaq;
        }
        proba_vector[i]=q[i];
      }
    }
  }

  void set_L2(L p_mod, L u){
    if(p_mod==0){ //batch sampling mode
      D tmp=0;
      D st;
      for(L i=0;i<this->nsamples;i++){
        st=Li[i]/tilde_proba_vector[i];
        if (st>tmp) tmp=st;
      }
      L2=tmp/this->nsamples/tau;
      cout<<"L2="<<L2<<"; "<<tilde_proba_vector[0]<<endl;
    }
    else{ //group sampling mode
      D tmp=0;
      D st;
      for(L i=0;i<this->nsamples;i++){
        st=Li[i]/proba_vector[i];
        if(isolated_I[i]==1) st=st-Li[i];
        if(st>tmp) tmp=st;
      }
      L2=tmp/this->nsamples;
    }
    cout<<"set_L2="<<L2<<endl;
  }

  void set_uniform_probability(L p_mod)
  {
    if(p_mod==0) {
      //batch sampling (stochastic process as defined in the paper)
      proba_vector.clear();
      tilde_proba_vector.clear();
      tilde_proba_vector.resize(this->nsamples,1./this->nsamples);
      proba_vector.resize(this->nsamples,1-pow(1-1.0/this->nsamples,tau));
      max_p=1.0/this->nsamples;
      cout<<"uniform="<<tilde_proba_vector[0]<<endl;
    }
    else{ //group sampling
      D pi=(tau*this->c+0.0)/this->nsamples;
      proba_vector.clear();
      proba_vector.resize(this->nsamples,pi);
    }
  }

  void sort_p(){
    vector<pair<D,L> >a;
    for (L i = 0 ;i < this->nsamples ; i++) {
      a.push_back(make_pair(proba_vector[i],i)); // k = value, i = original index
    }
    sort(a.begin(),a.end());
    group_C.clear();
    group_C.resize(this->nsamples);
    isolated_I.clear();
    isolated_I.resize(this->nsamples,0);
    D tmp=0;
    D maxpi=0;
    D previous_i=0;
    index_group_C.clear();
    index_group_C.push_back(0);
    maxp_group_C.clear();
    sump_group_C.clear();

    for (L i = 0 ;i < this->nsamples ; i++){
      group_C[i]=a[this->nsamples-1-i].second;
      D pi=a[this->nsamples-1-i].first;
      if(tmp+pi<=1){
        if(pi>maxpi) maxpi=pi;
        tmp+=pi;
      }
      else{
        index_group_C.push_back(i);
        maxp_group_C.push_back(maxpi);
        sump_group_C.push_back(tmp);
        tmp=pi;
        maxpi=pi;
        if(i-previous_i==1) isolated_I[group_C[previous_i]]=1;
        previous_i=i;
      }
    }
    index_group_C.push_back(this->nsamples);
    maxp_group_C.push_back(maxpi);
    sump_group_C.push_back(tmp);
    nb_groups=sump_group_C.size();
    if(previous_i==this->nsamples-1) isolated_I[group_C[previous_i]]=1;
    cout<<"size of group="<<nb_groups<<endl;
  }

  inline L sampling(L n)
  {
    L i=(floor)(gsl_rng_uniform(rng)*n);
    D y=gsl_rng_uniform(rng);
    while(y*max_p>tilde_proba_vector[i])
    {
      i=(floor)(gsl_rng_uniform(rng)*n);
      y=gsl_rng_uniform(rng);
    }
    return i;
  }

  void set_initial_dual(){
    for(L i=0;i<this->nsamples;i++)
    {
      D res=0;
      for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
      {
        L j=this->row_idx[k];
        res+=this->A[k]*primal_x[j];
      }
      dual_alpha[i]=gradient_of_phi_i(res,i);
    }
  }

  void update_primal()
  {
    L nb_indices=0;
    //#pragma omp parallel for
    for(L i_t=0;i_t<batch_size;i_t++)
    {
      L i=batch_i[i_t];
      D deltaalphai=batch_deltaalphai[i_t];
      for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
      {
        L j=this->row_idx[k];
        if (whether_or_not_to_do_prox[j]==0) {
          //#pragma omp critical
          {
          whether_or_not_to_do_prox[j]=1;
          index_to_do_prox[nb_indices]=j;
          nb_indices++;
          deltagk[j]=0;
          }
        }
        deltagk[j]+=lambda_f[i]*deltaalphai*this->A[k]*theta_S[i];
      }
    }
    #pragma omp parallel for
    for(L i_d=0;i_d<nb_indices;i_d++){
      L j=index_to_do_prox[i_d];
      D xj=compute_current_xj_value(baralpha[j]+deltagk[j], j,last_update_i[j],current_nb_iters);
      //update_x(xj,j);
      last_update_i[j]=current_nb_iters;
      whether_or_not_to_do_prox[j]=0;
      index_to_do_prox[i_d]=-1;
    }

  }

  void update_baralpha(L  i, D deltaalphai)
  {
    for (L k = this->ptr[i]; k < this->ptr[i + 1];	k++)
    {
      L j=this->row_idx[k];
      baralpha[j]+=lambda_f[i]*deltaalphai*this->A[k];
    }
  }

  D compute_AiTxk(L i) //This function compute A_i^\top x^k
  {
    D res=0;
    D xj;
    for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
    {
      L j=this->row_idx[k];
      if(current_nb_iters!=last_update_i[j])
      {
        xj=compute_current_xj_value(baralpha[j], j,last_update_i[j],current_nb_iters);
        //update_x(xj,j);
      }
      res+=this->A[k]*primal_x[j];
      last_update_i[j]=current_nb_iters;
    }
    return res;
  }

  D compute_AiTxk_without_update(L i) //This function compute A_i^\top x^k  without updating x^k
  {
    D res=0;
    D xj;
    //#pragma omp parallel for reduction(+:res)
    for (L k = this->ptr[i]; k < this->ptr[i + 1];k++)
    {
      L j=this->row_idx[k];
      if(current_nb_iters!=last_update_i[j])
      {
        xj=compute_current_xj_value_without_update(baralpha[j], j,last_update_i[j],current_nb_iters);
      }
      else {
        xj=primal_x[j];
      }
      res+=this->A[k]*xj;
    }
    return res;
  }

  void compute_dual_value()
  {
    D res=0;
    //D alphaAx= 0;
    std::vector<D> alpha_tmp(this->nsamples,0);
    std::vector<D> baralpha_tmp(this->nfeatures,0);
    //#pragma omp parallel for schedule(dynamic)
    for(L i=0;i<this->nsamples;i++)
    {
      D aitx=compute_AiTxk_without_update(i);
      alpha_tmp[i]=gradient_of_phi_i(aitx,i);
      //alphaAx+= lambda_f[i]*alpha_tmp[i]*aitx;
      for (L k = this->ptr[i]; k < this->ptr[i + 1];	k++)
      {
        L j=this->row_idx[k];
        baralpha_tmp[j]+=lambda_f[i]*alpha_tmp[i]*this->A[k];
      }
    }

    D scal=feasible_dual(baralpha_tmp);
    //#pragma omp parallel for reduction(-:res)
    for(L i=0;i<this->nsamples;i++)
    {
      res-=value_of_phistar_i(scal*alpha_tmp[i],i)*lambda_f[i];
    }
    res-=value_of_gstar_minus(baralpha_tmp,scal); // should have used value_of_gstar(-baralpha)
    dual_value=res;
  }

  void compute_primal_value()
  {
    D res=0;
    //#pragma omp parallel for reduction(+:res)
    for(L i=0;i<this->nsamples;i++)
    {
      D aitx=compute_AiTxk_without_update(i);
      res+=lambda_f[i]*value_of_phi_i(aitx,i);
    }

    D res2=0;
    //#pragma omp parallel for reduction(+:res2)
    for(L j=0; j<this->nfeatures; j++)
    {
      D xj=compute_current_xj_value_without_update(baralpha[j], j,last_update_i[j],current_nb_iters);
      D gj=value_of_g_j(xj,j);
      res2+=gj;
    }
    primal_value= res+res2;
  }

  void compute_initial_dual_value()
  {

    D res=0;
    D scal=feasible_dual(baralpha);
    #pragma omp parallel for reduction(-:res)
    for(L i=0;i<this->nsamples;i++)
    {
      res-=value_of_phistar_i(scal*dual_alpha[i],i)*lambda_f[i];
    }
    //cout<< "d_1(x)= "<< res<< " d_2(x)= "<< value_of_gstar_minus(baralpha,scal)<< endl;
    res-=value_of_gstar_minus(baralpha,scal);  // should have used value_of_gstar(-baralpha)
    dual_value=res;
  }

  void compute_initial_primal_value()
  {
    D res=0;
    #pragma omp parallel for reduction(+:res)
    for(L j=0; j<this->nfeatures; j++)
    {
      res+=value_of_g_j(primal_x[j],j);
    }
    D res2=0;
    #pragma omp parallel for reduction(+:res2)
    for(L i=0;i<this->nsamples;i++)
    {
      D aitx=0;
      for (L k = this->ptr[i]; k < this->ptr[i + 1];	k++)
      {
        L j=this->row_idx[k];
        aitx+=this->A[k]*primal_x[j];
      }
      res2+=lambda_f[i]*value_of_phi_i(aitx,i);
    }
    //cout<< "p_1(x)= "<< res<< " p_2(x)= "<< res2<< endl;
    primal_value= res+res2;
  }

  void set_p(D scal_p){

    if(scal_p==4.6){
      p=sqrt(this->mu/L1*tau/this->nsamples);
    }
    else if (scal_p==5.6){
      p=this->mu/L1;
    }
    else{
      p=scal_p*tau/(0.0+this->nsamples);
    }
    cout<<"changing probablity: "<<p<<endl;
  }

  void initialize(vector<D> & x0, vector<D> & w0, vector<D> & L_phi, D val_mu, D val_epsilon, L max_nb, L nb_tau, L nb_c, L u, L p_mod, D scal_p)
  {
    cout<<"start initializing"<<" u="<<u<<endl;
    this->distributed_set_up(nb_c);
    if(nb_tau>this->noverc) perror("tau should be less than n over c");
    tau=nb_tau;
    nb_of_iters_per_loop=floor(this->nsamples/(this->c*(tau+0.0)));
    batch_i.clear();
    batch_deltaalphai.clear();
    batch_i.resize(this->nsamples,0);
    batch_deltaalphai.resize(this->nsamples,0);

    deltagk.clear();
    deltagk.resize(this->nfeatures,0);
    index_to_do_prox.clear();
    index_to_do_prox.resize(this->nfeatures,-1);
    whether_or_not_to_do_prox.clear();
    whether_or_not_to_do_prox.resize(this->nfeatures,0);
    gk.clear();
    gk.resize(this->nfeatures,0);

    /**setup parameters**/
    epsilon=val_epsilon;
    max_nb_loops=max_nb;
    this->mu=val_mu;
    cout<<"mu="<<this->mu<<endl;
    lambda_f.clear();
    lambda_f.resize(this->nsamples,0); //L_phi is the Lipschitz constant of the function \phi_i.
    for(int i=0;i< this->nsamples; i++){
      lambda_f[i]= L_phi[i]/this->nsamples;
    }

    set_rng();
    set_auxiliary_v();

    lambda_1=get_lambda1();
    lambda_2=get_lambda2();

    set_Li_Lf();

    running_time=0;
    /**setup probability**/
    if(p_mod==0)   // batch sampling (stochastic process as defined in the paper)
    {
      if(u==0)   // uniform sampling
      {
        set_uniform_probability(p_mod);
        this->uniform="uniform";
      }
      else{
        set_optimal_probability();
        this->uniform="nonuniform";
      }
    }
    else{  //group sampling
      if(u==0){
        set_uniform_probability(p_mod);
        this->uniform="uniform";
      }else{
        this->set_group_sampling_probability();
        this->uniform="nonuniform";
      }
      sort_p();
      cout<<"sort p"<<endl;
    }

    set_L2(p_mod,u);
    scaler=scal_p;

    set_L1(p_mod);
    cout<<"set L1="<<L1<<endl;
    set_p(scal_p);
    set_thetaS(p_mod);
    cout<<"setthetaS"<<endl;

    primal_x=w0;
    dual_alpha=x0;
    set_stepsize();
    cout<<"set stepsize"<<endl;

    set_initial_dual();

    baralpha.clear();
    baralpha.resize(this->nfeatures,0);
    last_update_i.clear();
    last_update_i.resize(this->nfeatures,0);
    for(L i=0;i<this->nsamples;i++)
    {
      update_baralpha(i, dual_alpha[i]);
    }

    current_nb_iters=0;
    D start_time_initial=std::clock();
    compute_initial_primal_value();
    compute_initial_dual_value();
    D end_time_initial=std::clock();
    cout<<"computation of initial primal dual value time="<<(end_time_initial-start_time_initial) / (double) CLOCKS_PER_SEC<<endl;

    cout<<"primal value="<<primal_value<<endl;
    cout<<"dual_value="<<dual_value<<endl;

    cout<<"Initialization is finished!"<<endl;
  }

  void result_record()
  {
    cout<<primal_value<<endl;
    cout<<dual_value<<endl;
    ofstream result;
    result.open("results/x0.dat");
    for (L j=0;j<this->nfeatures;j++){
      result <<setprecision(20)<< primal_x[j] << " ";
      //cout<< primal_x[j] << " ";
    }
    result << endl;
    ofstream resulta;
    resulta.open("results/alpha0.dat");
    for (L i=0;i<this->nsamples;i++)
    resulta << setprecision(20)<<dual_alpha[i] << " ";
    resulta << endl;
    result.close();
  }

  void read_w0(){
    ifstream recordw0("results/x0.dat");
    for(L j=0;j<this->nfeatures;j++)
    {
      recordw0>>primal_x[j];
    }
    recordw0.close();

    ifstream recordalpha0("results/alpha0.dat");
    for(L i=0;i<this->nsamples;i++)
    {
      recordalpha0>>dual_alpha[i];
    }
    recordalpha0.close();
  }

  void compute_and_record_result()
  {
    if(nb_loops%print_every_N==0){
      D start = std::clock();
      compute_primal_value();
      D endt=std::clock();
      cout<<"computation of primal value time="<<(endt-start) / (double) CLOCKS_PER_SEC<<endl;
      compute_dual_value();
      delta=primal_value-dual_value;
      cout<<setprecision(16)<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<";;;  "<<running_time<<"; primal dual gap="<<delta<<" primal value="<<primal_value<<"; dual value="<<dual_value<<endl;
      samp<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<" "<<delta<<" "<<running_time<<" "<<primal_value<<endl;
    }

  }

  void batch_sampling()
  {
    batch_size=0;
    L i;
    for(L it_t=0;it_t<tau;it_t++)
    {
      i=sampling(this->nsamples);
      batch_i[batch_size]=i;
      batch_size++;
    }
  }

  void set_thetaS(L p_mod){
    theta_S.clear();
    theta_S.resize(this->nsamples,0);
    if(p_mod==0){
      for(L i=0;i<this->nsamples;i++)
      theta_S[i]=1.0/(tau*tilde_proba_vector[i]);
    }
    else{
      for(L i=0;i<this->nsamples;i++)
      theta_S[i]=1.0/proba_vector[i];
    }
  }

  void group_sampling(){
    batch_size=0;
    std::vector<L> sampled_groups;
    for(L i=0;i<nb_groups;i++){
      D y=gsl_rng_uniform(rng);
      if(y<sump_group_C[i])
      {
        sampled_groups.push_back(i);
        batch_size++;
      }
    }
    for(L t=0;t<batch_size;t++){
      L group_i=sampled_groups[t];
      L s1=index_group_C[group_i];
      L s2=index_group_C[group_i+1];
      L i=s1+(floor)(gsl_rng_uniform(rng)*(s2-s1));
      D y=gsl_rng_uniform(rng);
      D maxpi=maxp_group_C[group_i];
      while(y*maxpi>proba_vector[group_C[i]])
      {
        i=s1+(floor)(gsl_rng_uniform(rng)*(s2-s1));
        y=gsl_rng_uniform(rng);
      }
      batch_i[t]=group_C[i];
    }
  }

  void loopless(vector<D> & x0, vector<D> & w0, string filename, vector<D> & L_phi, D val_mu, D val_epsilon, L max_nb, L nb_tau, L nb_c, L u, L p_mod, D scal_p)
  {
    initialize(x0,  w0,  L_phi, val_mu, val_epsilon,  max_nb,  nb_tau, nb_c,  u, p_mod, scal_p);
    string sampname="results/L_"+filename+uniform;
    if(p_mod==0) sampname=sampname+"_batch";
    else sampname=sampname+"_group";
    string scal_str;
    stringstream  scal_convert;
    scal_convert<<scal_p;
    scal_str=scal_convert.str();
    sampname+=scal_str;
    cout<<"running Loopless"<<" ; "<<sampname<<endl;
    samp.open(sampname.c_str());

    delta=primal_value-dual_value;
    nb_loops=0;
    nb_iters=0;

    cout<<setprecision(9)<<"initial: "<<" delta: "<<delta<<" primal: "<<primal_value<<"; dual: "<<dual_value<<"epsilon="<<epsilon<<endl;
    samp<<((0.0+nb_iters)*this->c*tau/(this->nsamples))<<" "<<delta<<running_time<<" "<<primal_value<<endl;
    srand48(27432042);
    //srand(time(NULL));
    printf("There are %d threads\n",omp_get_num_threads());

    D start;
    while(delta>=epsilon && nb_loops<max_nb_loops)
    {
      compute_and_record_result();

      if(delta<epsilon) break;
      start = std::clock();

      nb_loops++;

      //cout<<"before the loop time elapsed="<<duration<<endl;
      start = std::clock();

      //cout<<"nb_of_iters_per_loop="<<nb_of_iters_per_loop<<endl;
      for(L it=0;it<nb_of_iters_per_loop;it++)
      {
        if(p_mod==0)
        batch_sampling();
        else
        group_sampling();

        //start = std::clock();
        start=omp_get_wtime();
        #pragma omp parallel
        {
          #pragma omp for
          for(L i_t=0;i_t<batch_size;i_t++)
          {
          L i=batch_i[i_t];
          D aitg=compute_AiTxk(i);
          D deltaalphai=(gradient_of_phi_i(aitg,i)-dual_alpha[i]);
          batch_deltaalphai[i_t]=deltaalphai;
         }
      }
        //cout<<omp_get_wtime()-start<<endl;
        current_nb_iters++;
        update_primal();
        update_baralpha();
        nb_iters++;
        D end_time = omp_get_wtime();
        //running_time+= ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
        running_time+=end_time-start;

      }

      //cout<<"after the loop time elapsed="<<duration<<endl;
    }
    if(delta>=epsilon) {compute_primal_value();
      compute_dual_value();
      delta=primal_value-dual_value;
      cout<<setprecision(9)<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<";  "<<running_time<<"; primal dual gap="<<delta<<" primal value="<<primal_value<<"; dual value="<<dual_value<<endl;
      samp<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<" "<<delta<<" "<<running_time<<" "<<primal_value<<endl;
    }
    cout<<setprecision(9)<<"nb_loops: "<<nb_loops<<"nb_iters"<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<";  delta: "<<delta<<" primal: "<<primal_value<<"; dual: "<<dual_value<<endl;
    samp.close();
  }

  void loopless2(vector<D> & x0, vector<D> & w0, string filename, vector<D> & L_phi, D val_mu, D val_epsilon, L max_nb, L nb_tau, L nb_c, L u, L p_mod, D scal_p)
  {
    initialize(x0,  w0,  L_phi, val_mu, val_epsilon,  max_nb,  nb_tau, nb_c,  u, p_mod, scal_p);
    string sampname="results/L_"+filename+uniform;
    if(p_mod==0) sampname=sampname+"_batch";
    else sampname=sampname+"_group";
    string scal_str;
    stringstream  scal_convert;
    scal_convert<<scal_p;
    scal_str=scal_convert.str();
    sampname+=scal_str;
    cout<<"running Loopless"<<" ; "<<sampname<<endl;
    samp.open(sampname.c_str());

    delta=primal_value-dual_value;
    nb_loops=0;
    nb_iters=0;

    cout<<setprecision(9)<<"initial: "<<" delta: "<<delta<<" primal: "<<primal_value<<"; dual: "<<dual_value<<endl;
    samp<<((0.0+nb_iters)*this->c*tau/(this->nsamples))<<" "<<delta<<running_time<<" "<<primal_value<<endl;
    //srand48(27432042);
    srand(time(NULL));

    D start;
    while(nb_loops<max_nb_loops)
    {
      compute_and_record_result();

      start = std::clock();

      nb_loops++;

      //cout<<"before the loop time elapsed="<<duration<<endl;
      start = std::clock();
      //cout<<"nb_of_iters_per_loop="<<nb_of_iters_per_loop<<endl;
      for(L it=0;it<nb_of_iters_per_loop;it++)
      {
        if(p_mod==0)
        batch_sampling();
        else
        group_sampling();

        start = std::clock();
        for(L i_t=0;i_t<batch_size;i_t++)
        {
          L i=batch_i[i_t];
          D aitg=compute_AiTxk(i);
          D deltaalphai=(gradient_of_phi_i(aitg,i)-dual_alpha[i]);
          batch_deltaalphai[i_t]=deltaalphai;
        }
        current_nb_iters++;
        update_primal();
        update_baralpha();
        nb_iters++;
        running_time+= ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
      }
      //cout<<"after the loop time elapsed="<<duration<<endl;
    }
    cout<<setprecision(9)<<"nb_loops: "<<floor(((0.0+nb_iters)*this->c*tau/(this->nsamples)))<<";  delta: "<<delta<<" primal: "<<primal_value<<"; dual: "<<dual_value<<endl;
    samp.close();
  }

};

#endif /* MIN_SMOOTH_CONVEX_H */


/*
 # Copyright (C) 2009-2013 Thierry Sousbie
 # University of Tokyo / CNRS, 2009
 #
 # This file is part of porject DisPerSE
 #
 #  Author          : Thierry Sousbie
 #  Contact         : tsousbie@gmail.com
 #
 #  Licenses        : This file is 'dual-licensed', you have to choose one
 #                    of the two licenses below to apply.
 #
 #                    CeCILL-C
 #                    The CeCILL-C license is close to the GNU LGPL.
 #                    ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
 #
 #                or  CeCILL v2.0
 #                    The CeCILL license is compatible with the GNU GPL.
 #                    ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
 #
 #  This software is governed either by the CeCILL or the CeCILL-C license
 #  under French law and abiding by the rules of distribution of free software.
 #  You can  use, modify and or redistribute the software under the terms of
 #  the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
 #  at the following URL : "http://www.cecill.info".
 #
 #  As a counterpart to the access to the source code and  rights to copy,
 #  modify and redistribute granted by the license, users are provided only
 #  with a limited warranty  and the software's author,  the holder of the
 #  economic rights,  and the successive licensors  have only  limited
 #  liability.
 #
 #  In this respect, the user's attention is drawn to the risks associated
 #  with loading,  using,  modifying and/or developing or reproducing the
 #  software by the user in light of its specific status of free software,
 #  that may mean  that it is complicated to manipulate,  and  that  also
 #  therefore means  that it is reserved for developers  and  experienced
 #  professionals having in-depth computer knowledge. Users are therefore
 #  encouraged to load and test the software's suitability as regards their
 #  requirements in conditions enabling the security of their systems and/or
 #  data to be ensured and,  more generally, to use and operate it in the
 #  same conditions as regards security.
 #
 #  The fact that you are presently reading this means that you have had
 #  knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "dtfi.h"

#ifdef HAVE_GSL
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

// mu is the average value
unsigned int randomPoisson(double mu)
{
  static int first_call = 1;
  static const gsl_rng_type *T;
  static gsl_rng * r;

  if (first_call) {
    first_call=0;
    gsl_rng_env_setup();
    T = gsl_rng_default;
    r = gsl_rng_alloc(T);
  }

  return gsl_ran_poisson (r, mu);
}

void randomSimplexCoords(double **coord, int ndims, double *result, double *P)
{
  static int first_call = 1;
  static const gsl_rng_type *T;
  static gsl_rng * r;
  int i,j,k;
  double u;
  //double P[50];

  if (first_call) {
    first_call=0;
    gsl_rng_env_setup();
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);
  }

  result[0]=gsl_rng_uniform (r);

  if (ndims==3)
    {
      u=gsl_rng_uniform (r);
      if (u<result[0])
	{
	  result[1]=result[0];
	  result[0]=u;
	}
      else result[1]=u;

      u=gsl_rng_uniform (r);
      if (u<result[0])
	{
	  result[2]=result[1];
	  result[1]=result[0];
	  result[0]=u;
	}
      else if (u<result[1])
	{
	  result[2]=result[1];
	  result[1]=u;
	}
      else result[2]=u;

    }
  else
    {
      for (i=1;i<ndims;i++)
	{
	  u = gsl_rng_uniform (r);

	  for (j=0;j<i;j++)
	    if (u<result[j])
	      {
		result[i]=result[j];
		result[j]=u;
		break;
	      }

	  if (j==i) result[i]=u;
	  else {
	    j++;
	    for (;j<i;j++)
	      for (k=j+1;k<=i;k++)
		{
		  if (result[j]>result[k])
		    {
		      double tmp = result[k];
		      result[k]=result[j];
		      result[j]=tmp;
		    }
		}
	  }
	}
    }

  P[0]=result[0];
  for (i=1;i<ndims;i++) P[i]= result[i]-result[i-1];
  P[ndims]=1.-result[ndims-1];

  for (i=0;i<ndims;i++) result[i]=P[0]*coord[0][i];

  for (i=1;i<ndims+1;i++)
    for (j=0;j<ndims;j++)
      result[j]+=P[i]*coord[i][j];

}

void genDTFIInSimplex(double **coord, double *Proba, int ngen, int ndims, double **result_p)
{

  if (*result_p==NULL) {
    *result_p=(double*)malloc(sizeof(double)*ngen*ndims);
  }

  double *result = *result_p;
  double P[ndims];
  int i,n;
  double keepProba;
  double pmax_inv=0;

  static int first_call = 1;
  static const gsl_rng_type *T;
  static gsl_rng * r;

  //if (ngen>100) printf("negen = %d\n",ngen);

  if (first_call) {
    first_call=0;
    gsl_rng_env_setup();
    T = gsl_rng_default;
    r = gsl_rng_alloc (T);
  }

  if (Proba!=NULL) {
    pmax_inv=0;
    for (i=0;i<ndims+1;i++)
      if (Proba[i]>pmax_inv) pmax_inv=Proba[i];
    pmax_inv=(double)1./pmax_inv;
  }

  for (n=0;n<ngen;)
    {
      randomSimplexCoords(coord,ndims,&(result[n*ndims]),P);
      if (Proba!=NULL) {
	keepProba=Proba[0]*pmax_inv*P[0];
	for (i=1;i<ndims+1;i++) keepProba+=Proba[i]*pmax_inv*P[i];

	if (gsl_rng_uniform (r)<=keepProba) n++;
      } else n++;
    }

}

#else

unsigned int randomPoisson(double mu)
{
  fprintf(stderr,"ERROR: Function 'randomPoisson' needs GSL.\n");
  fprintf(stderr,"Please, recompile with GSL.\n");

  return -1;
}

void randomSimplexCoords(double **coord, int ndims, double *result, double *P)
{
  fprintf(stderr,"ERROR: Function 'randomSimplexCoords' needs GSL.\n");
  fprintf(stderr,"Please, recompile with GSL.\n");

  return -1;
}

void genDTFIInSimplex(double **coord, double *Proba, int ngen, int ndims, double **result_p)
{
  fprintf(stderr,"ERROR: Function 'genDTFIInSimplex' needs GSL.\n");
  fprintf(stderr,"Please, recompile with GSL.\n");
}

#endif


//cmc_stateSpace.c --
// this program creates and outputs a coalescent markov chain's complete state space
// given a particular model

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include "AFS.h"

int n1, n2;

void getParameters(int argc,const char **argv);
void usage();

int main(int argc, const char * argv[]){
	int i;
	afsObject *A;
	afsStateSpace *stateSpace;

	getParameters(argc,argv);

	stateSpace = afsStateSpaceNew();

	A = afsObjectNew(n1,n2);
 	afsObjectInit(A,n1,n2);
//
// 	printf("%s\n",A->matString );
// //	exit(1);
	stateSpace->states[0] = A;
	stateSpace->nstates=1;
//	stateSpace->tmpStorage[A->nalleles][n1][0] = A;
//	stateSpace->nstateArray[A->nalleles][n1] =1;

	g_hash_table_insert(stateSpace->stateHash, A->matString, "TRUE");

	preorderEvolveState(stateSpace, A, n1, n2);

	//qsort(stateSpace->states,stateSpace->nstates, sizeof(stateSpace->states[0]), afsObjectQsortCompare);

	printf("n1: %d n2: %d nstates: %d\n",n1,n2,stateSpace->nstates);
	for(i=0;i<stateSpace->nstates;i++){
		afsObjectPrint(stateSpace->states[i]);
		printf("-----------\n");

	}

	afsStateSpaceFree(stateSpace);
	return(0);
}

void getParameters(int argc,const char **argv){
//	int args;
//	int i;

	if( argc < 2){
		usage();
	}
	n1 = atoi(argv[1]);
	n2 = atoi(argv[2]);
}

void usage(){
	fprintf(stderr,"usage: cmc_stateSpace n1 n2\n");
//	fprintf(stderr,"parameters: \n");
	exit(1);
}


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

  Library   : Image Registration Toolkit (IRTK)
  Module    : $Id$
  Copyright : Imperial College, Department of Computing
              Visual Information Processing (VIP), 2008 onwards
  Date      : $Date$
  Version   : $Revision$
  Changes   : $Author$

Copyright (c) 1999-2014 and onwards, Imperial College London
All rights reserved.
See LICENSE for details

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

#ifndef _IRTKVECTOR_H

#define _IRTKVECTOR_H

#ifdef USE_VXL
// VXL header file was loaded in irtkMatrix.h
#include <vnl/vnl_diag_matrix.h>
#else
#include <gsl/gsl_vector.h>
#endif

#include <iostream>
#include <cmath>

#include <irtkObject.h>

/**

  Vector class.

*/

class irtkVector : public irtkObject
{

protected:

  /// Number of rows
  int _rows;

  /// Data
  double *_vector;

public:

  /// Default constructor
  irtkVector();

  /// Constructor for given row dimensions
  irtkVector(int);

  /// Copy constructor
  irtkVector(const irtkVector &);

  /// Destructor
  ~irtkVector();

  /// Intialize matrix with number of rows
  void Initialize(int);

  //
  // Vector access functions
  //

  /// Returns number of rows
  int Rows() const;

  /// Puts vector value
  void   Put(int, double);

  /// Gets vector value
  double Get(int) const;

  //
  // Operators for vector access
  //

  /// Puts vector value
  double &operator()(int);

  /// Gets vector value
  double  operator()(int) const;

  //
  // Vector operators for doubles
  //

  /// Subtraction of a double
  irtkVector& operator-=(double);

  /// Addition of a double
  irtkVector& operator+=(double);

  /// Multiplication with a double
  irtkVector& operator*=(double);

  /// Division by a double
  irtkVector& operator/=(double);

  /// Return result of subtraction of a double
  irtkVector  operator- (double);

  /// Return result of addition of a double
  irtkVector  operator+ (double);

  /// Return result of multiplication with a double
  irtkVector  operator* (double);

  /// Return result of division by a double
  irtkVector  operator/ (double);

  //
  // Vector operators for vectors
  //

  /// Vector copy operator
  irtkVector& operator =(const irtkVector&);

  /// Vector subtraction operator
  irtkVector& operator-=(const irtkVector&);

  /// Vector addition operator
  irtkVector& operator+=(const irtkVector&);

  /// Vector componentwise multiplication operator (no scalar nor cross product)
  irtkVector& operator*=(const irtkVector&);

  /// Vector componentwise division operator
  irtkVector& operator/=(const irtkVector&);

  /// Return result for vector subtraction
  irtkVector  operator- (const irtkVector&);

  /// Return result for vector addition
  irtkVector  operator+ (const irtkVector&);

  /// Return result for componentwise vector multiplication (no scalar nor cross product)
  irtkVector  operator* (const irtkVector&);

  /// Return result for componentwise vector division
  irtkVector  operator/ (const irtkVector&);

  /// Comparison operator ==
  bool operator==(const irtkVector &);

#ifndef USE_STL
  /// Comparison operator != (if USE_STL is defined, negate == operator)
  bool operator!=(const irtkVector &);
#endif

  /// Comparison operator <
  bool operator<(const irtkVector &);

  /// Scalar/dot product
  double ScalarProduct(const irtkVector&);

  /// Vector/cross product
  irtkVector CrossProduct(const irtkVector&);

  /// Returns norm of a vector
  double Norm(void) const;

  /// Vector normalization
  void   Normalize(void);

  //
  // Vector in- and output
  //

  /// Interface to output stream
  friend std::ostream& operator<< (std::ostream&, const irtkVector&);

  /// Interface to input stream
  friend std::istream& operator>> (std::istream&, irtkVector&);

  /// Print vector
  void Print();

  /// Read vector from file
  void Read (char *);

  /// Write vector to file
  void Write(char *);

#ifdef USE_VXL
  template <class T> void Vector2Vnl(vnl_diag_matrix<T>*) const;

  template <class T> void Vnl2Vector(vnl_diag_matrix<T>*);

#else
  /// Conversion to GSL vector (memory must be allocated)
  void Vector2GSL(gsl_vector *) const;

  /// Conversion from GSL vector
  void GSL2Vector(gsl_vector *);
#endif

};

//
// Access operators
//

inline int irtkVector::Rows() const
{
  return _rows;
}

inline void irtkVector::Put(int rows, double vector)
{
  _vector[rows] = vector;
}

inline double irtkVector::Get(int rows) const
{
  return _vector[rows];
}

inline double& irtkVector::operator()(int rows)
{
  return _vector[rows];
}

inline double irtkVector::operator()(int rows) const
{
  return _vector[rows];
}

//
// Vector operators for doubles
//

inline irtkVector& irtkVector::operator-=(double x)
{
  int i;

  for (i = 0; i < _rows; i++) {
    _vector[i] -= x;
  }

  return *this;
}

inline irtkVector& irtkVector::operator+=(double x)
{
  int i;

  for (i = 0; i < _rows; i++) {
    _vector[i] += x;
  }

  return *this;
}

inline irtkVector& irtkVector::operator*=(double x)
{
  int i;

  for (i = 0; i < _rows; i++) {
    _vector[i] *= x;
  }

  return *this;
}

inline irtkVector& irtkVector::operator/=(double x)
{
  int i;

  for (i = 0; i < _rows; i++) {
    _vector[i] /= x;
  }

  return *this;
}

inline irtkVector irtkVector::operator- (double x)
{
  int i;

  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] - x;
  }
  return m;
}

inline irtkVector irtkVector::operator+ (double x)
{
  int i;

  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] + x;
  }
  return m;
}

inline irtkVector irtkVector::operator* (double x)
{
  int i;

  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] * x;
  }
  return m;
}

inline irtkVector irtkVector::operator/ (double x)
{
  int i;

  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] / x;
  }
  return m;
}

//
// Vector operators for vectors
//

inline irtkVector& irtkVector::operator =(const irtkVector& v)
{
  int i;

  // Copy size
  this->Initialize(v._rows);

  // Copy vector
  for (i = 0; i < _rows; i++) {
    _vector[i] = v._vector[i];
  }
  return *this;
}

inline irtkVector& irtkVector::operator-=(const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator-=: Size mismatch" << std::endl;
    exit(1);
  }
  for (i = 0; i < _rows; i++) {
    _vector[i] -= v._vector[i];
  }

  return *this;
}

inline irtkVector& irtkVector::operator+=(const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator+=: Size mismatch" << std::endl;
    exit(1);
  }
  for (i = 0; i < _rows; i++) {
    _vector[i] += v._vector[i];
  }

  return *this;
}

inline irtkVector& irtkVector::operator*=(const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator*=: Size mismatch" << std::endl;
    exit(1);
  }
  for (i = 0; i < _rows; i++) {
    _vector[i] *= v._vector[i];
  }

  return *this;
}

inline irtkVector& irtkVector::operator/=(const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator/=: Size mismatch" << std::endl;
    exit(1);
  }
  for (i = 0; i < _rows; i++) {
    _vector[i] /= v._vector[i];
  }

  return *this;
}

inline irtkVector irtkVector::operator- (const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator-: Size mismatch" << std::endl;
    exit(1);
  }
  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] - v._vector[i];
  }
  return m;
}

inline irtkVector irtkVector::operator+ (const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator+: Size mismatch" << std::endl;
    exit(1);
  }
  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] + v._vector[i];
  }
  return m;
}

inline irtkVector irtkVector::operator* (const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator*: Size mismatch" << std::endl;
    exit(1);
  }
  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] * v._vector[i];
  }
  return m;
}

inline irtkVector irtkVector::operator/ (const irtkVector& v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::operator/: Size mismatch" << std::endl;
    exit(1);
  }
  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = _vector[i] / v._vector[i];
  }
  return m;
}

//
// Comparison
//

inline bool  irtkVector::operator==(const irtkVector &v)
{
  if (_rows != v._rows) {
    return false;
  }
  for (int i = 0; i < _rows; i++) {
    if (_vector[i] != v._vector[i]) return false;
  }
  return true;

}

#ifndef USE_STL
inline bool  irtkVector::operator!=(const irtkVector &v)
{
  if (_rows != v._rows) {
    return true;
  }
  for (int i = 0; i < _rows; i++) {
    if (_vector[i] != v._vector[i]) return true;
  }
  return false;
}
#endif

inline bool  irtkVector::operator<(const irtkVector &v)
{
  if (_rows > v._rows) {
    return false;
  }
  for (int i = 0; i < _rows; i++) {
    if (_vector[i] >= v._vector[i]) return false;
  }
  return true;

}

//
// Vector products
//

inline double irtkVector::ScalarProduct(const irtkVector &v)
{
  int i;
  double scalar_product=0;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::ScalarProduct: Size mismatch" << std::endl;
    exit(1);
  }

  for (i = 0; i < _rows; i++) {
    scalar_product += _vector[i]*v._vector[i];
  }
  return scalar_product;
}

inline irtkVector irtkVector::CrossProduct(const irtkVector &v)
{
  int i;

  if (_rows != v._rows) {
    std::cerr << "irtkVector::CrossProduct: Size mismatch" << std::endl;
    exit(1);
  }

  irtkVector m(_rows);
  for (i = 0; i < _rows; i++) {
    m._vector[i] = (_vector[(i+1)%_rows]*v._vector[(i+2)%_rows]-
                    _vector[(i+2)%_rows]*v._vector[(i+1)%_rows]);
  }
  return m;
}

//
// Functions
//

inline double irtkVector::Norm(void) const
{
  double norm = 0;

  for (int i = 0; i < _rows; i++) {
    norm += _vector[i]*_vector[i];
  }
  return std::sqrt(norm);
}

inline void irtkVector::Normalize(void)
{
  double norm = Norm();

  if (norm != 0) {
    *this /= norm;
  }
}

#ifdef USE_VXL

template <class T>
inline void irtkVector::Vector2Vnl(vnl_diag_matrix<T>* m) const
{
  unsigned i;

  for (i = 0; i < (unsigned) _rows; i++) {
    (*m)(i) = (T) _vector[i];
  }
}

template <class T>
inline void irtkVector::Vnl2Vector(vnl_diag_matrix<T>* m)
{
  unsigned i;

  for (i = 0; i < (unsigned) _rows; i++) {
    _vector[i] = (float) (*m)(i);
  }
}

#endif

#endif


////////////////////////////////////////////////////////////
//
// Copyright (c) 2018 Sir Ementaler
//
// 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 PICTOLEV_TILES_H
#define PICTOLEV_TILES_H

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <vector>
#include <gsl/gsl_assert>
#include <gsl/gsl_util>
#include <gsl/span>
#include "grid_size.h"

template<class T>
using image_fragment = std::vector<gsl::span<T>>;

template<class T>
auto buffer_to_image(gsl::span<T> buffer, grid_size size) {
	Expects(gsl::narrow_cast<std::size_t>(buffer.size()) == grid_area(size));
	image_fragment<T> image(size.height);
	std::generate(image.begin(), image.end(), [buffer, width = size.width]() mutable {
		gsl::span<T> row = buffer.subspan(0, width);
		buffer = buffer.subspan(width);
		return row;
	});
	return image;
}

template<class T>
using tile_vector = std::vector<image_fragment<T>>;

template<class ForwardIt>
auto image_to_tile_list(ForwardIt first, ForwardIt last, std::size_t tile_size) {
	Expects(std::distance(first, last) % tile_size == 0);
	Expects(first == last || first->size() % tile_size == 0);
	using container_type = std::remove_reference_t<typename ForwardIt::reference>;
	using value_type = std::remove_reference_t<typename container_type::reference>;
	const grid_size size {
		first != last ? first->size() / tile_size : 0,
		std::distance(first, last) / tile_size,
	};
	tile_vector<value_type> tiles(grid_area(size), image_fragment<value_type>(tile_size));
	auto out = tiles.begin();
	while (first != last) {
		ForwardIt second = first;
		std::advance(second, tile_size);
		for (gsl::index x = 0; x != first->size(); x += tile_size) {
			std::transform(first, second, out->begin(), [=](const auto& row) {
				return gsl::span<value_type>(row).subspan(x, tile_size);
			});
			++out;
		}
		first = second;
	}
	return tiles;
}

#endif


#include <cblas.h>

#include "tasks.h"

void solve_task_seq(void *ptr)
{
    struct solve_task_arg *arg = (struct solve_task_arg*) ptr;

    int n     = arg->n;
    int nrhs  = arg->nrhs;
    double *L = arg->L;
    int ldL   = arg->ldL;
    double *X = arg->X;
    int ldX   = arg->ldX;

    // Solve L * X = B, where B and X share the same memory.
    cblas_dtrsm(CblasColMajor, CblasLeft, CblasLower, CblasNoTrans, CblasNonUnit,
                n, nrhs,
                1.0, L, ldL,
                     X, ldX);
}


#include <gsl/gsl_spline.h>
#include "param.h"
#include "root_func.h"
#include "eos_pres.h"
#include "global.h"

double
root_func (double rho, void *params)
{
  struct param *root_func_params = (struct param *) params;
  double (*pres) (double, void *);
  pres = &eos_pres;
  return (tmp_pres - pres (rho, root_func_params));
}


/**
 * A metropolis hasting sampler for bremsstrahlung and pair production cross sections
 *
 * copyright  (C) 2007
 * the icecube collaboration
 *
 * $Id: I3MetropolisHastings.h 143040 2016-03-11 17:35:25Z nega $
 *
 * @version $Revision: 143040 $
 * @date $LastChangedDate: 2016-03-11 10:35:25 -0700 (Fri, 11 Mar 2016) $
 * @author Bernhard Voigt <bernhard.voigt@desy.de>   $LastChangedBy$
 */

#ifndef I3METROPOLISHASTINGS_h_
#define I3METROPOLISHASTINGS_h_

// icetray includes
#include "icetray/I3Logging.h"
#include "cmc/I3CascadeMCCommon.h"

// gsl includes
#include <gsl/gsl_rng.h>

// c++ includes
#include <string>

/**
 * @brief This class implements a Metropolis Hastings sampler
 *
 * It uses a pdf and a proposal distribution to efficiently
 * produce random samples from the pdf
 *
 * The proposal distribution used is a beta distribution, which
 * matches almost the shape of the bremsstrahlung and pair production
 * cross sections, using the right parameters. See I3CascadeSimulation
 * for the usage.
 *
 * For details on the algorithm, take a look at :
 * http://en.wikipedia.org/wiki/Metropolis-Hastings_algorithm
 */
class I3MetropolisHastings : public I3CascadeMCCommon::Sampler {

  // set logging name for icetray
  SET_LOGGER("I3MetropolisHastings");

 public:

  /**
   * Default Constructor
   */
  I3MetropolisHastings() {}

  /**
   * Constructor
   *
   * @param rng      a gsl_random instance
   * @param pdf      a function with two parameters, ie. the energy and
   *                 fractional energy of the cross section
   * @param proposal a gsl random instance sampling from the proposal
   *                 distribution which is a beta distribution
   * @param proposalDistribution a beta function in x
   * @param name     name of this instance for logging purposes
   *
   * The proposal gsl random instance as well as the proposalDistribution are implemented
   * in a struct beta_dist in the header file I3CascadeSimulation.h
   */
  I3MetropolisHastings(I3RandomServicePtr rng,
                       double (*pdf)(const double&, const double&),
                       double (*proposal)(const gsl_rng*),
                       double (*proposalDistribution)(const double&),
                       std::string name);

  /**
   * Get the total sample loop count
   */
  unsigned long GetCounter() {
    return counter_;
  }

  /**
   * Reset the total sample loop count
   */
  void ResetCounter() {
    counter_ = 0;
  }

  /**
   * Get the number of returned samples
   */
  unsigned long GetYield() {
    return yield_;
  }

  /**
   * Reset the number of returned samples
   */
  void ResetYield() {
    yield_ = 0;
  }

  /**
   * Samples n times starting at start position
   *
   * Used to forget the initial state
   *
   * @param energy parameter of underlying pdf
   * @param start starting position
   * @param n number of samples
   */
  void BurnIn(double energy, double start, unsigned int n=100);

  /**
   * Samples the underlying pdf in the given range
   *
   * @param energy parameter to the underlying pdf
   * @param lower edge of sample range
   * @param higher edge of sample range
   * @return random number
   */
  double Sample(double energy, double lower=0, double higher=1);

 private:

  /**
   * @brief pdf, ie a cross section with energy and fractional energy parameters
   */
  double (*pdf_)(const double&, const double&);

  /**
   * @brief a gls random instance drawing samples from the proposal distribution
   */
  double (*proposal_)(const gsl_rng*);

  /**
   * @brief a proposal distribution, in this case a beta distribution in x
   */
  double (*proposalDistribution_)(const double&);

  /**
   * @brief loop counter
   */
  unsigned long counter_;

  /**
   * @brief counts how many successful attemps have been made to draw a new random number
   */
  unsigned long yield_;

  /**
   * @brief name of this instance, for logging purpose
   */
  std::string name_;

  /**
   * @brief last drawn sample
   */
  double x_;

  /**
   * @brief last value of pdf(x)
   */
  double y_;

  /**
   * @brief last value of proposalDist(x)
   */
  double propDistY_;

};

#endif


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

/* Author:  G. Jungman */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sum.h>

int
gsl_sum_levin_utrunc_accel (const double *array,
                            const size_t array_size,
                            gsl_sum_levin_utrunc_workspace * w,
                            double *sum_accel, double *abserr_trunc)
{
  return gsl_sum_levin_utrunc_minmax (array, array_size,
                                      0, array_size - 1,
                                      w, sum_accel, abserr_trunc);
}

int
gsl_sum_levin_utrunc_minmax (const double *array,
                             const size_t array_size,
                             const size_t min_terms,
                             const size_t max_terms,
                             gsl_sum_levin_utrunc_workspace * w,
                             double *sum_accel, double *abserr_trunc)
{
  if (array_size == 0)
    {
      *sum_accel = 0.0;
      *abserr_trunc = 0.0;
      w->sum_plain = 0.0;
      w->terms_used = 0;
      return GSL_SUCCESS;
    }
  else if (array_size == 1)
    {
      *sum_accel = array[0];
      *abserr_trunc = GSL_POSINF;
      w->sum_plain = array[0];
      w->terms_used = 1;
      return GSL_SUCCESS;
    }
  else
    {
      const double SMALL = 0.01;
      const size_t nmax = GSL_MAX (max_terms, array_size) - 1;
      double trunc_n = 0.0, trunc_nm1 = 0.0;
      double actual_trunc_n = 0.0, actual_trunc_nm1 = 0.0;
      double result_n = 0.0, result_nm1 = 0.0;
      size_t n;
      int better = 0;
      int before = 0;
      int converging = 0;
      double least_trunc = GSL_DBL_MAX;
      double result_least_trunc;

      /* Calculate specified minimum number of terms. No convergence
         tests are made, and no truncation information is stored. */

      for (n = 0; n < min_terms; n++)
        {
          const double t = array[n];

          result_nm1 = result_n;
          gsl_sum_levin_utrunc_step (t, n, w, &result_n);
        }

      /* Assume the result after the minimum calculation is the best. */

      result_least_trunc = result_n;

      /* Calculate up to maximum number of terms. Check truncation
         condition. */

      for (; n <= nmax; n++)
        {
          const double t = array[n];

          result_nm1 = result_n;
          gsl_sum_levin_utrunc_step (t, n, w, &result_n);

          /* Compute the truncation error directly */

          actual_trunc_nm1 = actual_trunc_n;
          actual_trunc_n = fabs (result_n - result_nm1);

          /* Average results to make a more reliable estimate of the
             real truncation error */

          trunc_nm1 = trunc_n;
          trunc_n = 0.5 * (actual_trunc_n + actual_trunc_nm1);

          /* Determine if we are in the convergence region. */

          better = (trunc_n < trunc_nm1 || trunc_n < SMALL * fabs (result_n));
          converging = converging || (better && before);
          before = better;

          if (converging)
            {
              if (trunc_n < least_trunc)
                {
                  /* Found a low truncation point in the convergence
                     region. Save it. */

                  least_trunc = trunc_n;
                  result_least_trunc = result_n;
                }

              if (fabs (trunc_n / result_n) < 10.0 * GSL_MACH_EPS)
                break;
            }
        }

      if (converging)
        {
          /* Stopped in the convergence region. Return result and
             error estimate. */

          *sum_accel = result_least_trunc;
          *abserr_trunc = least_trunc;
          w->terms_used = n;
          return GSL_SUCCESS;
        }
      else
        {
          /* Never reached the convergence region. Use the last
             calculated values. */

          *sum_accel = result_n;
          *abserr_trunc = trunc_n;
          w->terms_used = n;
          return GSL_SUCCESS;
        }
    }
}

int
gsl_sum_levin_utrunc_step (const double term,
                           const size_t n,
                           gsl_sum_levin_utrunc_workspace * w, double *sum_accel)
{
  if (term == 0.0)
    {
      /* This is actually harmless when treated in this way. A term
         which is exactly zero is simply ignored; the state is not
         changed. We return GSL_EZERODIV as an indicator that this
         occured. */

      return GSL_EZERODIV;
    }
  else if (n == 0)
    {
      *sum_accel = term;
      w->sum_plain = term;
      w->q_den[0] = 1.0 / term;
      w->q_num[0] = 1.0;
      return GSL_SUCCESS;
    }
  else
    {
      double factor = 1.0;
      double ratio = (double) n / (n + 1.0);
      int j;

      w->sum_plain += term;
      w->q_den[n] = 1.0 / (term * (n + 1.0) * (n + 1.0));
      w->q_num[n] = w->sum_plain * w->q_den[n];

      for (j = n - 1; j >= 0; j--)
        {
          double c = factor * (j + 1) / (n + 1);
          factor *= ratio;
          w->q_den[j] = w->q_den[j + 1] - c * w->q_den[j];
          w->q_num[j] = w->q_num[j + 1] - c * w->q_num[j];
        }

      *sum_accel = w->q_num[0] / w->q_den[0];
      return GSL_SUCCESS;
    }
}


#include <stdio.h>
#include <stdlib.h>
#include <gsl/gsl_statistics.h>
#include <assert.h>
#include <float.h>

extern "C" {
#include <quadmath.h>
}

#ifndef N
#define N 32
#endif

#define K N/2

#ifndef S
#define S K/2
#endif

#ifndef SEED
#define SEED 1
#endif

#ifndef FB
#define FB 64
#endif

#if(FB==64)
#define FT double
#elif(FB==80)
#define FT long double
#endif

int
main(int argc, char *argv[])
{
  assert(argc == 4);

  int i, j;
  char *cinname = argv[1];
  char *sinname = argv[2];
  char *outname = argv[3];
  FILE *cinfile = fopen(cinname, "r");
  FILE *infile = fopen(sinname, "r");
  FILE *outfile = fopen(outname, "w");

  srand(SEED);

  int index[K];
  for (i=0; i<K; i++)
    index[i]=i;
  for (i=0; i<K-1; i++){
    j = rand()%(K-1);
    int tmp = index[i];
    index[i] = index[j];
    index[j] = tmp;
  }

  FT data[K], w[K];
  FT wtss;

  // read concrete input file
  float cinput[N]; i=0;
  while(i<N && fscanf(cinfile, "%f", &cinput[i])>0){
      i++;
  }
  assert(i>=N);
  fclose(cinfile);

  for (i = 0; i < K-S; i++){
    w[index[i]]=cinput[index[i]];
  }

  for (i = 0 ; i <K ; i++){
    data[i] = cinput[i+K];
  }

  // read symbolic input data
  float sinput[S];
  for (i = 0 ; i < S ; i++) {
    __float128 in_data;
    fread(&in_data, sizeof(__float128), 1, infile);
    sinput[i] = (float) in_data;
  }
  fclose(infile);

  for (i = 0 ; i<S; i++){
    w[index[K-1-i]] = sinput[i] / FLT_MAX * 100;
  }

  // library call
  #if(FB==64)
    wtss     = gsl_stats_wtss(w, 1, data, 1, K);
  #elif(FB==80)
    wtss     = gsl_stats_long_double_wtss(w, 1, data, 1, K);
  #else
    exit(-1);
  #endif

  __float128 out_data;
  out_data = (__float128) wtss;
  fwrite(&out_data, sizeof(__float128), 1, outfile);
  fclose(outfile);

  return 0;
}


/*! /file global.h
 *  /brief Declarations of global variables and functions. */

#ifndef GLOBAL_H
#define GLOBAL_H

#ifdef COOLING_CPU
#include <gsl/gsl_spline.h>
#include <gsl/gsl_spline2d.h>
#endif

#if PRECISION==1
#ifndef TYPEDEF_DEFINED_REAL
typedef float Real;
#endif
#endif
#if PRECISION==2
#ifndef TYPEDEF_DEFINED_REAL
typedef double Real;
#endif
#endif

#define MAXLEN 140
#define TINY_NUMBER 1.0e-20
#define PI 3.141592653589793
#define MP 1.672622e-24 // mass of proton, grams
#define KB 1.380658e-16 // boltzmann constant, cgs
//#define GN 6.67259e-8 // gravitational constant, cgs
#define GN 4.49451e-18 // gravitational constant, kpc^3 / M_sun / kyr^2

#define MYR 31.536e12 //Myears in secs
#define KPC 3.086e16 // kpc in km
#define G_COSMO 4.300927161e-06; // gravitational constant, kpc km^2 s^-2 Msun^-1
#define MSUN_CGS 1.98847e33; //Msun in gr
#define KPC_CGS 3.086e21;  //kpc in cm
#define KM_CGS 1e5; //km in cm

#define TIME_UNIT 3.15569e10 // 1 kyr in s
#define LENGTH_UNIT 3.08567758e21 // 1 kpc in cm
#define MASS_UNIT 1.98855e33 // 1 solar mass in grams
#define DENSITY_UNIT (MASS_UNIT/(LENGTH_UNIT*LENGTH_UNIT*LENGTH_UNIT))
#define VELOCITY_UNIT (LENGTH_UNIT/TIME_UNIT)
#define ENERGY_UNIT (DENSITY_UNIT*VELOCITY_UNIT*VELOCITY_UNIT)
#define PRESSURE_UNIT (DENSITY_UNIT*VELOCITY_UNIT*VELOCITY_UNIT)
#define SP_ENERGY_UNIT (VELOCITY_UNIT*VELOCITY_UNIT)

#define LOG_FILE_NAME "run_output.log"

//Conserved Floor Values
#define TEMP_FLOOR 1e-3
#define DENS_FLOOR 1e-5

//Parameter for Enzo dual Energy Condition
#define DE_ETA_1 0.001 //Ratio of U to E for wich  Inetrnal Energy is used to compute the Pressure
#define DE_ETA_2 0.035 //Ratio of U to max(E_local) used to select wich Internal Energy is used for the update.

// Maximum time step for cosmological simulations
#define MAX_DELTA_A 0.001
#define MAX_EXPANSION_RATE 0.01  // Limit delta(a)/a

#ifdef COOLING_GRACKLE
  #ifdef GRACKLE_METALS
  #define NSCALARS 7
  #else
  #define NSCALARS 6
  #endif // GRACKLE_METALS
#else
#ifdef SCALAR
// Set Number of scalar fields when not using grackle
#define NSCALARS 1
#endif//SCALAR
#endif//COOLING_GRACKLE

// Inital Chemistry fractions
#define INITIAL_FRACTION_HI        0.75984603480
#define INITIAL_FRACTION_HII       1.53965115054e-4
#define INITIAL_FRACTION_HEI       0.23999999997
#define INITIAL_FRACTION_HEII      9.59999999903e-15
#define INITIAL_FRACTION_HEIII     9.59999999903e-18
#define INITIAL_FRACTION_ELECTRON  1.53965115054e-4
#define INITIAL_FRACTION_METAL     1.00000000000e-10

//Default Gravity Compiler Flags
#define GRAVITY_LONG_INTS
#define COUPLE_GRAVITATIONAL_WORK

//Default Particles Compiler Flags
#define PARTICLES_LONG_INTS
#define PARTICLES_KDK

#ifdef GRAVITY
#ifdef GRAVITY_5_POINTS_GRADIENT
#ifdef PARTICLES
#define N_GHOST_POTENTIAL 3 // 3 ghost cells are needed for 5 point gradient, ( one is for the CIC interpolation of the potential )
#else
#define N_GHOST_POTENTIAL 2 // 2 ghost cells are needed for 5 point gradient
#endif //PARTICLES

#else
#ifdef PARTICLES
#define N_GHOST_POTENTIAL 2 // 2 ghost cells are needed for 3 point gradient, ( one is for the CIC interpolation of the potential )
#else
#define N_GHOST_POTENTIAL 1 // 1 ghost cells are needed for 3 point gradient
#endif //PARTICLES
#endif //GRAVITY_5_POINTS_GRADIENT

#ifdef GRAVITY_LONG_INTS
typedef long int grav_int_t;
#else
typedef int grav_int_t;
#endif//GRAVITY_LONG_INTS
#endif

#ifdef PARTICLES
#ifdef PARTICLES_LONG_INTS
typedef long int part_int_t;
#else
typedef int part_int_t;
#endif//PARTICLES_LONG_INTS

#include <vector>
typedef std::vector<Real> real_vector_t;
typedef std::vector<part_int_t> int_vector_t;
#ifdef MPI_CHOLLA
// Constants for the inital size of the buffers for particles transfer
// and the number of data transfered for each particle
extern int N_PARTICLES_TRANSFER;
extern int N_DATA_PER_PARTICLE_TRANSFER;
#endif//MPI_CHOLLA

#ifdef AVERAGE_SLOW_CELLS
#define SLOW_FACTOR 10
#endif//AVERAGE_SLOW_CELLS

#endif//PARTICLES

#define SIGN(a) ( ((a) < 0.) ? -1. : 1. )

/* Global variables */
extern Real gama; // Ratio of specific heats
extern Real C_cfl; // CFL number (0 - 0.5)
extern Real t_comm;
extern Real t_other;

#ifdef COOLING_CPU
extern gsl_interp_accel *acc;
extern gsl_interp_accel *xacc;
extern gsl_interp_accel *yacc;
extern gsl_spline *highT_C_spline;
extern gsl_spline2d *lowT_C_spline;
extern gsl_spline2d *lowT_H_spline;
#endif
#ifdef COOLING_GPU
extern float *cooling_table;
extern float *heating_table;
#endif

/*! \fn void Set_Gammas(Real gamma_in)
 *  \brief Set gamma values for Riemann solver. */
extern void Set_Gammas(Real gamma_in);

/*! \fn double get_time(void)
 *  \brief Returns the current clock time. */
extern double get_time(void);

/*! \fn int sgn
 *  \brief Mathematical sign function. Returns sign of x. */
extern int sgn(Real x);

#ifndef CUDA
/*! \fn Real calc_eta(Real cW[], Real gamma)
 *  \brief Calculate the eta value for the H correction. */
extern Real calc_eta(Real cW[], Real gamma);
#endif

struct parameters
{
  int nx;
  int ny;
  int nz;
  double tout;
  double outstep;
  int n_steps_output;
  Real gamma;
  char init[MAXLEN];
  int nfile;
  int outstep_hydro;
  int outstep_particle;
  int outstep_projection;
  int outstep_rotated_projection;
  int outstep_slice;
  Real xmin;
  Real ymin;
  Real zmin;
  Real xlen;
  Real ylen;
  Real zlen;
  int xl_bcnd;
  int xu_bcnd;
  int yl_bcnd;
  int yu_bcnd;
  int zl_bcnd;
  int zu_bcnd;
#ifdef   MPI_CHOLLA
  int xlg_bcnd;
  int xug_bcnd;
  int ylg_bcnd;
  int yug_bcnd;
  int zlg_bcnd;
  int zug_bcnd;
#endif /*MPI_CHOLLA*/
  char custom_bcnd[MAXLEN];
  char outdir[MAXLEN];
  char indir[MAXLEN]; //Folder to load Initial conditions from
  Real rho;
  Real vx;
  Real vy;
  Real vz;
  Real P;
  Real A;
  Real rho_l;
  Real v_l;
  Real P_l;
  Real rho_r;
  Real v_r;
  Real P_r;
  Real diaph;
#ifdef ROTATED_PROJECTION
  int nxr;
  int nzr;
  Real delta;
  Real theta;
  Real phi;
  Real Lx;
  Real Lz;
  int n_delta;
  Real ddelta_dt;
  int flag_delta;
#endif /*ROTATED_PROJECTION*/
#ifdef COSMOLOGY
  Real H0;
  Real Omega_M;
  Real Omega_L;
  Real Omega_b;
  Real Init_redshift;
  Real End_redshift;
  char scale_outputs_file[MAXLEN]; //File for the scale_factor output values for cosmological simulations
#endif //COSMOLOGY
#ifdef TILED_INITIAL_CONDITIONS
  Real tile_length;
#endif //TILED_INITIAL_CONDITIONS

#ifdef SET_MPI_GRID
  // Set the MPI Processes grid [n_proc_x, n_proc_y, n_proc_z]
  int n_proc_x;
  int n_proc_y;
  int n_proc_z;
#endif
  int bc_potential_type;
#ifdef COOLING_GRACKLE
  char UVB_rates_file[MAXLEN];
#endif
#ifdef ANALYSIS
  char analysis_scale_outputs_file[MAXLEN]; //File for the scale_factor output values for cosmological simulations {{}}
  char analysisdir[MAXLEN];
  int lya_skewers_stride;
  Real lya_Pk_d_log_k;
  #ifdef OUTPUT_SKEWERS
  char skewersdir[MAXLEN];
  #endif
#endif
};

/*! \fn void parse_params(char *param_file, struct parameters * parms);
 *  \brief Reads the parameters in the given file into a structure. */
extern void parse_params (char *param_file, struct parameters * parms);

/*! \fn int is_param_valid(char *name);
 * \brief Verifies that a param is valid (even if not needed).  Avoids "warnings" in output. */
extern int is_param_valid(const char *name);

#endif //GLOBAL_H


/**
 * @file optimizers.h
 * @author Carl Boettiger <cboettig@gmail.com>
 * @date 22 April 2011
 *
 * @section LICENSE
 *
 * 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 <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_siman.h>

/* set up parameters for this simulated annealing run */
#define N_TRIES 200     	/* how many points do we try before stepping */
#define ITERS_FIXED_T 200	/* how many iterations for each T? */
#define STEP_SIZE .02 		/* max step size in random walk */
#define K 1.0				/* Boltzmann constant */
#define T_INITIAL 0.05		/* initial temperature */
#define MU_T 1.004		    /* damping factor for temperature */
#define T_MIN .008

/* setup parameters for multimin method*/
#define INIT_STEP .2
#define MAX_ITER 10000
#define ERR_TOL 1e-8
#define PRINT 1
#include <gsl/gsl_multimin.h>

double optim_func (const gsl_vector *v, void *params);
double multimin(gsl_vector *x, void * params);
double siman(gsl_vector * x, void * params, gsl_rng * rng);


#include <ceed.h>
#include <petsc.h>
#include "../problems/neo-hookean.h"

// Build libCEED context object
PetscErrorCode PhysicsContext_NH(MPI_Comm comm, Ceed ceed, Units *units,
                                 CeedQFunctionContext *ctx) {
  PetscErrorCode ierr;
  Physics_NH phys;

  PetscFunctionBegin;

  ierr = PetscMalloc1(1, units); CHKERRQ(ierr);
  ierr = PetscMalloc1(1, &phys); CHKERRQ(ierr);
  ierr = ProcessPhysics_NH(comm, phys, *units); CHKERRQ(ierr);
  CeedQFunctionContextCreate(ceed, ctx);
  CeedQFunctionContextSetData(*ctx, CEED_MEM_HOST, CEED_COPY_VALUES,
                              sizeof(*phys), phys);
  ierr = PetscFree(phys); CHKERRQ(ierr);

  PetscFunctionReturn(0);
}

// Build libCEED smoother context object
PetscErrorCode PhysicsSmootherContext_NH(MPI_Comm comm, Ceed ceed,
    CeedQFunctionContext ctx, CeedQFunctionContext *ctx_smoother) {
  PetscErrorCode ierr;
  PetscScalar nu_smoother = 0;
  PetscBool nu_flag = PETSC_FALSE;
  Physics_NH phys, phys_smoother;

  PetscFunctionBegin;

  ierr = PetscOptionsBegin(comm, NULL,
                           "Neo-Hookean physical parameters for smoother", NULL);
  CHKERRQ(ierr);

  ierr = PetscOptionsScalar("-nu_smoother", "Poisson's ratio for smoother",
                            NULL, nu_smoother, &nu_smoother, &nu_flag);
  CHKERRQ(ierr);

  ierr = PetscOptionsEnd(); CHKERRQ(ierr); // End of setting Physics

  if (nu_flag) {
    // Copy context
    CeedQFunctionContextGetData(ctx, CEED_MEM_HOST, &phys);
    ierr = PetscMalloc1(1, &phys_smoother); CHKERRQ(ierr);
    ierr = PetscMemcpy(phys_smoother, phys, sizeof(*phys)); CHKERRQ(ierr);
    CeedQFunctionContextRestoreData(ctx, &phys);
    // Create smoother context
    CeedQFunctionContextCreate(ceed, ctx_smoother);
    phys_smoother->nu = nu_smoother;
    CeedQFunctionContextSetData(*ctx_smoother, CEED_MEM_HOST, CEED_COPY_VALUES,
                                sizeof(*phys_smoother), phys_smoother);
    ierr = PetscFree(phys_smoother); CHKERRQ(ierr);
  } else {
    *ctx_smoother = NULL;
  }

  PetscFunctionReturn(0);
}

// Process physics options - Neo-Hookean
PetscErrorCode ProcessPhysics_NH(MPI_Comm comm, Physics_NH phys, Units units) {
  PetscErrorCode ierr;
  PetscBool nu_flag = PETSC_FALSE;
  PetscBool Young_flag = PETSC_FALSE;
  phys->nu = 0;
  phys->E = 0;
  units->meter     = 1;        // 1 meter in scaled length units
  units->second    = 1;        // 1 second in scaled time units
  units->kilogram  = 1;        // 1 kilogram in scaled mass units

  PetscFunctionBeginUser;

  ierr = PetscOptionsBegin(comm, NULL, "Neo-Hookean physical parameters", NULL);
  CHKERRQ(ierr);

  ierr = PetscOptionsScalar("-nu", "Poisson's ratio", NULL, phys->nu, &phys->nu,
                            &nu_flag); CHKERRQ(ierr);

  ierr = PetscOptionsScalar("-E", "Young's Modulus", NULL, phys->E, &phys->E,
                            &Young_flag); CHKERRQ(ierr);

  ierr = PetscOptionsScalar("-units_meter", "1 meter in scaled length units",
                            NULL, units->meter, &units->meter, NULL);
  CHKERRQ(ierr);
  units->meter = fabs(units->meter);

  ierr = PetscOptionsScalar("-units_second", "1 second in scaled time units",
                            NULL, units->second, &units->second, NULL);
  CHKERRQ(ierr);
  units->second = fabs(units->second);

  ierr = PetscOptionsScalar("-units_kilogram", "1 kilogram in scaled mass units",
                            NULL, units->kilogram, &units->kilogram, NULL);
  CHKERRQ(ierr);
  units->kilogram = fabs(units->kilogram);

  ierr = PetscOptionsEnd(); CHKERRQ(ierr); // End of setting Physics

  // Check for all required options to be set
  if (!nu_flag) {
    SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "-nu option needed");
  }
  if (!Young_flag) {
    SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "-E option needed");
  }

  // Define derived units
  units->Pascal = units->kilogram / (units->meter * PetscSqr(units->second));

  // Scale E to Pa
  phys->E *= units->Pascal;

  PetscFunctionReturn(0);
};


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once
#include "Data\Pose.h"
#include "Data\Data.h"

#include <gsl\gsl>

namespace mage
{
    /**
     * Converts the contents of a CVMat into the mage::Matrix data container by copying the values
     */
    Matrix ToMageMat(const cv::Matx34f& cvMat);
    Matrix ToMageMat(const cv::Matx44f& cvMat);

    Position ToMagePos(const cv::Point3f& cvPt);
    Direction ToMageDir(const cv::Point3f& cvDir);

    cv::Vec3f FromMageDir(const Direction& mageDir);

    Matrix ToMageMat(const std::array<float, 4 * 4>& f);

    Direction ToMageDir(const std::array<float, 3>& dir);

    mage::Pose MageMatrixToMagePose(const mage::Matrix& viewMatrix);

    void ConvertBGRToNV12(const cv::Mat& srcImg, gsl::span<uint8_t> destBuffer);

    bool IsIdentity(const Matrix& m);

    inline mage::Matrix CreateIdentityMageMatrix()
    {
        return{ 1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f };
    };

    // Interpolates values between min and max to the range defined from a to b,
    // [a:b] can be either an increasing or decreasing range, min is always mapped to a, and max to b.
    unsigned char LinearInterpolationToChar(float value, float min, float max, unsigned char a, unsigned char b);

    // Construct external normal (z positive) from the X,Y components of the internal normal (z negative).
    // Synthetic normals are facing the camera (external),
    // generated normals are not (internal), this function is used to make the conversion,
    // by changing the sign of the coordinates.
    cv::Vec3f ConstructExternalNormalFromXY(float normalX, float normalY);
}


/*
/
/ adkGSL.h
/
/ homebrewed addons to gsl */

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_sort_vector.h>
#include <math.h>
#include <assert.h>

int gsl_vector_includes(gsl_vector *aVec, double aValue);
double gsl_vector_sum(gsl_vector *aVec, int aVecSize);
double gsl_matrix_row_sum(gsl_matrix *mat, int row, int rowSize);
double log_sum(gsl_vector *vec);
double  *gsl_matrix_2_lapack(gsl_matrix *m);
double gsl_vector_dot_product(gsl_vector *vec1, gsl_vector *vec2);
void gsl_vector_outer_product(gsl_vector *vec1, gsl_vector *vec2, gsl_matrix *result);

gsl_matrix *gsl_matrix_power(gsl_matrix *A, int k);
gsl_matrix *gsl_matrix_power_logs(gsl_matrix *A, int k);
void gsl_matrix_lower_tri_copy(gsl_matrix *src, gsl_matrix *dest);
void gsl_matrix_upper_tri_copy(gsl_matrix *src, gsl_matrix *dest);
void gsl_matrix_lower_tri(gsl_matrix *x);
void gsl_matrix_upper_tri(gsl_matrix *x);
double gsl_matrix_trace(gsl_matrix *x);
void gsl_matrix_covariance(gsl_matrix *data, gsl_matrix *cov);

void fillMatrixFromArray(double *numbers, gsl_matrix *dest, int nrow, int ncol);
void fillMatrixFromVector(gsl_vector *numbers, gsl_matrix *dest, int nrow, int ncol);
void fillArrayFromMatrix(gsl_matrix *src, gsl_vector *dest);

void fillCholArrayFromMatrix(gsl_matrix *src, gsl_vector *dest);
void fillMatrixFromCholArray(double *numbers, gsl_matrix *dest, int nrow, int ncol);

//lapack wrappers
int dgeev(char jobvl, char jobvr, int n,  double *a, int lda, double *wr, double *wi, double *vl,
	  int ldvl,double *vr, int ldvr, double *work, int lwork);
int dhseqr(char job, char compz, int n, int ilo, int ihi,double *h, int ldh, double *wr, double *wi, double *z, int ldz, double *work, int lwork);