#ifndef INIT
#define INIT

#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
/* #include <gsl/gsl_linalg.h> */

#include "macros.h"
#include "algebra.h"
#include "util.h"

/**
 * Initialization of params and vectors.
 * These functions are not (necessarily) optimized for performance.
 */

void init_random(curandGenerator_t *gen) {
  static unsigned int seed = 1234; // TODO manage externally from this function
  seed++;
  // TODO do this on CPU to avoid data copying
  curandCreateGenerator(gen, CURAND_RNG_PSEUDO_XORWOW);
  /* curandCreateGenerator(gen, CURAND_RNG_PSEUDO_MT19937); */
  curandSetPseudoRandomGeneratorSeed(*gen, seed);
}

void randomize(float *x, float *d_x, size_t len, curandGenerator_t gen) {
  // TODO do this on cpu or at runtime (per batch)
  curandGenerateUniform(gen, d_x, len);
  cuR( curandGenerateUniform(gen, d_x, len * sizeof(float) / 32) );
  cudaMemcpy(x, d_x, len * sizeof(float), cudaMemcpyDeviceToHost);
  cudaDeviceSynchronize();
}

///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
namespace init {
///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

void derive_secondary_geometry(struct Geometry& p) {
  p.gridSize = {p.blockDim.x * p.gridDim.x,
                p.blockDim.y * p.gridDim.y,
                1};
  p.batch_size = {MIN(p.n.x, p.thread_size.x * p.gridSize.x),
                  MIN(p.n.y, p.thread_size.y * p.gridSize.y)};

#ifdef SQUARE_TARGET_BATCHES
  if (p.n.y > p.batch_size.y) {
    // round down to the nearest square
    const size_t sq =  sqrt(p.batch_size.y);
    p.batch_size.y = sq * sq;
  }
#endif

  p.n_batches = {CEIL(p.n.x, p.batch_size.x),
                 CEIL(p.n.y, p.batch_size.y)};
  check_hyper_params(p);
}

Geometry simple_geometry(const size_t x, const size_t y) {
  Geometry p;
  p.blockDim = {1,1,1};
  p.gridDim = {1,1,1};
  p.n = {x, y};
  p.thread_size = {1,1};
  p.n_streams = 1;
  derive_secondary_geometry(p);
  return p;
}

Geometry geometry(struct Params& params, const size_t x, const size_t y) {
  Geometry p;
  p.algorithm = params.algorithm;
  p.blockDim = params.blockDim;
  p.gridDim = params.gridDim;
  p.n = {x, y};
  p.thread_size = params.thread_size;
  p.n_streams = params.n_streams;

  derive_secondary_geometry(p);
  return p;
}

/**
 * Distribute sampling points over a 2D plane in 3D space.
 */
void plane(std::vector<SPACE> &v, const Plane p) {
  // TODO return ptr to device memory, copy pos data to CPU during batches
  const size_t n = v.size() / DIMS;
  printf("offset: %f\n", p.offset.z);
  for (unsigned int i = 0; i < n; ++i)
    v[i*DIMS + 2] = p.offset.z;
  /*
   * Assume HD dimensions, keep remaining pixels for kernel geometry compatibility
   * solve for x:  `n * (ratio) = x * y * (x/y) = x^2   ->   x = sqrt(n * ratio)`
   */
  auto
    x = (size_t) sqrt(n * p.aspect_ratio),
    y = n / x;
  if (p.aspect_ratio != 1.0) {
    printf("Screen dimensions: %i x %i\t", x, y);
    printf("Remaining points: %i/%i\n", n - x*y, n);
  }
  assert(x * y <= n);

  // distribute points over a lattice (each point in the center of a cell in a grid)
  const double
    height = p.width / p.aspect_ratio,
    dx = p.width / (double) x, // excl. boundary; don't divide by (x-1)
    dy = height / (double) y,
    x_half = (p.width - dx) / 2., // subtract dx/2 to position points in center
    y_half = (height - dy) / 2.;

  printf("init::plane, width: %e, n_x: %lu, dx: %e, x_half: %e\n", p.width, x, dx, x_half);
  // properties to generate random offsets
  const double
    rel_margin = p.randomize ? 0.05 : 0.0,
    x_margin = rel_margin * dx, // TODO min space between projector pixels
    y_margin = rel_margin * dy,
    x_random_range = dx - 0.5 * x_margin,
    y_random_range = dy - 0.5 * y_margin;

  const size_t n_random = x + y;
  curandGenerator_t generator;
  float *d_random, random[n_random];

  if (p.randomize) {
    init_random(&generator);
    cu( cudaMalloc( (void **) &d_random, n_random * sizeof(float) ) );
  }

  for (unsigned int i = 0; i < x; ++i) {
    if (p.randomize)
      randomize(random, d_random, n_random, generator);

    for (unsigned int j = 0; j < y; ++j) {
      v[Ix2D(i,j,0,y)] = i * dx - x_half + p.offset.x;
      v[Ix2D(i,j,1,y)] = j * dy - y_half + p.offset.y;
      v[Ix2D(i,j,2,y)] = p.offset.z;

      if (p.randomize) {
        v[Ix2D(i,j,0,y)] += x_random_range * (random[j*2] - 0.5);
        v[Ix2D(i,j,1,y)] += y_random_range * (random[j*2+1] - 0.5);
      }
    }
  }
  if (p.randomize) {
    curandDestroyGenerator(generator);
    cu( cudaFree( d_random ) );
  }

  // fill rest of array with semi-realistic values to minimize incompatibility issues
  for (unsigned int i = x * y; i < n; ++i)
    for (unsigned int j = 0; j < DIMS; ++j)
      v[Ix(i, j)] = v[Ix(0,j)];
}

std::vector<SPACE> sparse_plane(std::vector<SPACE> &u, Shape shape, double width,
                                const Cartesian<double> &offset, double modulate = 0.) {
  const size_t n = u.size() / DIMS;
  for (unsigned int i = 0; i < n; ++i)
    u[Ix(i, 2)] = offset.z;

  assert(n != 0);
  if (n == 1) {
    u[Ix(0,0)] = offset.x;
    u[Ix(0,1)] = offset.y;
    return u;
  }

  // set the x,y dimensions
  switch (shape) {
  case Shape::Line: {
    // Distribute datapoints over a line
    auto
      du = width / (double) (n-1), // TODO use SCALE?
      half_width = width / 2.0;

    for (unsigned int i = 0; i < n; ++i)
      u[Ix(i,0)] = i * du - half_width;

    break;
  }
  case Shape::LogLine: {
    const auto x = geomspace(n, 1, 1 + width);
    for (auto& i : range(n))
      u[Ix(i,0)] = x[i] - 1 + width / 2.;
    break;
  }
  case Shape::Circle: {
    // Distribute datapoints over a circle
    // (using polar coordinates, s.t. $phi_i \equiv i \Delta \phi$)
    auto
      radius = width / 2.0,
      /* circumference = TWO_PI * pow(radius, 2), */
      d_phase = TWO_PI / (double) n,
      arbitrary_offset = 0.1125 + modulate * TWO_PI;

    for (unsigned int i = 0; i < n; ++i) {
      u[Ix(i,0)] = sin(i * d_phase + arbitrary_offset) * radius;
      u[Ix(i,1)] = cos(i * d_phase + arbitrary_offset) * radius;
    }
    break;
  }
  case Shape::DottedCircle: {
    // (using polar coordinates)
    auto
      radius = width / 2.0,
      /* circumference = TWO_PI * pow(radius, 2), */
      /* don't include end; divide by n */
      d_phase = TWO_PI / (double) (n-1), // subtract center point
      arbitrary_offset = 0.1125 + modulate * TWO_PI;

    // TODO randomize slightly?
    for (unsigned int i = 1; i < n; ++i) {
      u[Ix(i,0)] = sin(i * d_phase + arbitrary_offset) * radius;
      u[Ix(i,1)] = cos(i * d_phase + arbitrary_offset) * radius;
    }

    break;
  }
  }

  if (offset.x != 0 || offset.y != 0) {
    for (unsigned int i = 0; i < n; ++i) {
      u[Ix(i,0)] += offset.x;
      u[Ix(i,1)] += offset.y;
    }
  }

  return u;
}

template<typename T>
std::vector<T*> malloc_vectors(T **d_ptr, size_t dim1, size_t dim2) {
  // TODO rename => malloc_arrays
  // Return a host vector of device pointers
  assert(dim1 >= 1); assert(dim2 >= 1);
  cu( cudaMalloc( d_ptr, dim1 * dim2 * sizeof(T) ) );
  auto vec = std::vector<T*>(dim1);
  for (size_t i = 0; i < dim1; ++i)
    vec[i] = *d_ptr + i * dim2;
  return vec;
 }

template<typename T>
std::vector<CUDAVector<T>> malloc_matrix(T **d_ptr, size_t dim1, size_t dim2) {
  // Return a host vector of CUDAVector elements
  assert(dim1 >= 1); assert(dim2 >= 1);
  cu( cudaMalloc( d_ptr, dim1 * dim2 * sizeof(T) ) );
  auto matrix = std::vector<CUDAVector<T>>(dim1);
  // std::vector<T>(*d_ptr + a, *d_ptr + b); has weird side effects
  // note that *ptr+i == &ptr[i], but that ptr[i] cannot be read by host if it's located on device
  for (size_t i = 0; i < dim1; ++i)
    matrix[i] = CUDAVector<T>{data: *d_ptr + i * dim2,
                              size: dim2};
  return matrix;
}

template<typename T>
std::vector<T*> pinned_malloc_vectors(T **d_ptr, size_t dim1, size_t dim2) {
  // Return a host vector of pointers
  assert(dim1 >= 1); assert(dim2 >= 1);
  // TODO consider cudaHostAllocMapped(..., cudaHostAllocMapped)
  cu( cudaMallocHost( d_ptr, dim1 * dim2 * sizeof(T) ) );
  auto vec = std::vector<T*>(dim1);
  for (size_t i = 0; i < dim1; ++i)
    vec[i] = *d_ptr + i * dim2;
  return vec;
}

template<typename T>
std::vector<CUDAVector<T>> pinned_malloc_matrix(T **d_ptr, size_t dim1, size_t dim2) {
  // Return a host vector of DeviceVector elements
  assert(dim1 >= 1); assert(dim2 >= 1);
  cu( cudaMallocHost( d_ptr, dim1 * dim2 * sizeof(T) ) );
  auto matrix = std::vector<CUDAVector<T>>(dim1);
  // std::vector<T>(*d_ptr + a, *d_ptr + b); has weird side effects
  // note that *ptr+i == &ptr[i], but that ptr[i] cannot be read by host if it's located on device
  for (size_t i = 0; i < dim1; ++i)
    matrix[i] = CUDAVector<T>{data: *d_ptr + i * dim2,
                              size: dim2};
  return matrix;
}

///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
} // end namespace
///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
#endif


#pragma once

#include "random_utils.h"
#include "schedule.h"
#include "schedule_params.h"
#include "stochastic_tunneling.h"
#include "temperature.h"
#include "utils.h"
#include <gsl/gsl-lite.hpp>

namespace angonoka::stun {
/**
    How many stochastic tunneling iterations to go through
    during each update.
*/
enum class BatchSize : std::int_fast32_t;

/**
    A single optimization job, meant to be launched in a thread pool.

    Optimizer starts many OptimizerJobs in parallel,
    pruning unseccessful jobs as needed.
*/
class OptimizerJob {
public:
    /**
        OptimizerJob options.

        @var params Schedule parameters
        @var random Random utils.
    */
    struct Options {
        gsl::not_null<const ScheduleParams*> params;
        gsl::not_null<RandomUtils*> random;
    };

    /**
        Constructor.

        @param params           Scheduling parameters
        @param random_utils     Random number generator utilities
        @param batch_size       Number of iterations per update
    */
    OptimizerJob(
        const ScheduleParams& params,
        RandomUtils& random_utils,
        BatchSize batch_size);
    OptimizerJob(const Options& options, BatchSize batch_size);

    /**
        Run stochastic tunneling optimization batch.

        Does batch_size number of iterations.
    */
    void update() noexcept;

    /**
        The best schedule so far.

        @return A schedule.
    */
    [[nodiscard]] Schedule schedule() const;

    /**
        The best makespan so far.

        @return Makespan.
    */
    [[nodiscard]] float normalized_makespan() const;

    /**
        Reset the optimization to initial state.
    */
    void reset();

    /**
        Get current options.

        @return Options.
    */
    [[nodiscard]] Options options() const;

    /**
        Set options.

        @param options Options.
    */
    void options(const Options& options);

    OptimizerJob(const OptimizerJob& other);
    OptimizerJob(OptimizerJob&& other) noexcept;
    OptimizerJob& operator=(const OptimizerJob& other);
    OptimizerJob& operator=(OptimizerJob&& other) noexcept;
    ~OptimizerJob() noexcept;

private:
    static constexpr auto beta_scale = 1e-4F;
    static constexpr auto stun_window = 10000;
    static constexpr auto gamma = .5F;
    static constexpr auto restart_period = 1 << 20;
    static constexpr auto initial_beta = 1.0F;

    int16 batch_size;
    Mutator mutator;
    Makespan makespan;
    Temperature temperature;
    StochasticTunneling stun;
};
} // namespace angonoka::stun


#include <math.h>
#include <stdlib.h>
#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <string.h>

#include <fftw3.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_erf.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_legendre.h>
#include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_sf_expint.h>
#include <gsl/gsl_deriv.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>

#include "../cosmolike_core/theory/basics.c"
#include "../cosmolike_core/theory/structs.c"
#include "../cosmolike_core/theory/parameters.c"
#include "../cosmolike_core/emu17/P_cb/emu.c"
#include "../cosmolike_core/theory/recompute.c"
#include "../cosmolike_core/theory/cosmo3D.c"
#include "../cosmolike_core/theory/redshift_spline.c"
#include "../cosmolike_core/theory/halo.c"
#include "../cosmolike_core/theory/HOD.c"
#include "../cosmolike_core/theory/pt.c"
#include "../cosmolike_core/theory/cosmo2D_fourier.c"
#include "../cosmolike_core/theory/IA.c"
#include "../cosmolike_core/theory/cluster.c"
#include "../cosmolike_core/theory/BAO.c"
#include "../cosmolike_core/theory/external_prior.c"
#include "../cosmolike_core/theory/init_baryon.c"
#include "init_DESxPlanck.c"

// Naming convention:
// g = galaxy positions ("g" as in "galaxy")
// k = kappa CMB ("k" as in "kappa")
// s = kappa from source galaxies ("s" as in "shear")
// And alphabetical order

typedef double (*C_tomo_pointer)(double l, int n1, int n2);
void twopoint_via_hankel(double **xi, double *logthetamin, double *logthetamax, C_tomo_pointer C_tomo, int ni, int nj, int N_Bessel);

#include "../cosmolike_core/theory/CMBxLSS_fourier.c"

typedef struct input_nuisance_params_des {
    double bias[10];
    // double bias2[10];
    double b_mag[10];
    double source_z_bias[10];
    double lens_z_bias[10];
    double shear_m[10];
    double A_ia;
    double eta_ia;
    // double bary[3];
} input_nuisance_params_des;

typedef struct input_cosmo_params_des {
    double omega_m;
    double sigma_8;
    double n_s;
    double w0;
    double wa;
    double omega_b;
    double h0;
    double MGSigma;
    double MGmu;
} input_cosmo_params_des;

double C_shear_tomo_sys(double ell,int z1,int z2);
double C_cgl_tomo_sys(double ell_Cluster,int zl,int nN, int zs);
double C_gl_tomo_sys(double ell,int zl,int zs);
double C_ks_sys(double ell, int zs);
void set_data_shear(int Ncl, double *ell, double *data, int start);
void set_data_ggl(int Ncl, double *ell, double *data, int start);
void set_data_clustering(int Ncl, double *ell, double *data, int start);
void set_data_gk(double *ell, double *data, int start);
void set_data_ks(double *ell, double *data, int start);
void set_data_kk(double *ell, double *data, int start);
void compute_data_vector(char *details, double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double BMAG1, double BMAG2, double BMAG3, double BMAG4,double BMAG5, double BMAG6, double BMAG7, double BMAG8, double BMAG9, double BMAG10, double SP1, double SP2, double SP3, double SP4, double SP5,double SP6, double SP7, double SP8, double SP9, double SP10, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double eta_ia);
double log_multi_like(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double BMAG1, double BMAG2, double BMAG3, double BMAG4,double BMAG5, double BMAG6, double BMAG7, double BMAG8, double BMAG9, double BMAG10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double eta_ia);
void write_datavector_wrapper(char *details, input_cosmo_params_des ic, input_nuisance_params_des in);
double log_like_wrapper(input_cosmo_params_des ic, input_nuisance_params_des in);
int get_N_tomo_shear(void);
int get_N_tomo_clustering(void);
int get_N_ggl(void);
int get_N_ell(void);

int get_N_tomo_shear(void){
  return tomo.shear_Nbin;
}
int get_N_tomo_clustering(void){
  return tomo.clustering_Nbin;
}
int get_N_ggl(void){
  return tomo.ggl_Npowerspectra;
}
int get_N_ell(void){
  return like.Ncl;
}

double C_shear_tomo_sys(double ell, int z1, int z2)
{
  double C;
  // C= C_shear_tomo_nointerp(ell,z1,z2);
  // if(like.IA==1) C+=C_II_nointerp(ell,z1,z2)+C_GI_nointerp(ell,z1,z2);

  // if(like.IA!=1) C= C_shear_tomo_nointerp(ell,z1,z2);
  // //if(like.IA==1) C= C_shear_shear_IA(ell,z1,z2);
  // if(like.IA==1) C = C_shear_tomo_nointerp(ell,z1,z2)+C_II_nointerp(ell,z1,z2)+C_GI_nointerp(ell,z1,z2);
  // if(like.IA==2) C += C_II_lin_nointerp(ell,z1,z2)+C_GI_lin_nointerp(ell,z1,z2);
  if(like.IA==4){C = C_shear_shear_IA_tab(ell,z1,z2);}
  else{printf("only support IA==4!\n");exit(1);}
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[z1])*(1.0+nuisance.shear_calibration_m[z2]);
  //printf("%le %d %d %le\n",ell,z1,z2,C_shear_tomo_nointerp(ell,z1,z2)+C_II_JB_nointerp(ell,z1,z2)+C_GI_JB_nointerp(ell,z1,z2));
return C;
}

double C_gl_tomo_sys(double ell,int zl,int zs)
{
  double C;
  // C=C_gl_tomo_nointerp(ell,zl,zs);
  // if(like.IA==1) C += C_gI_nointerp(ell,zl,zs);

  // if(like.IA!=1) C=C_gl_tomo_nointerp(ell,zl,zs);
  // if(like.IA==1) C = C_ggl_IA(ell,zl,zs);
  // if(like.IA==2) C += C_gI_lin_nointerp(ell,zl,zs);
  if(like.IA==4){C = C_ggl_IA_tab(ell,zl,zs);}
  else{printf("only support IA==4!\n");exit(1);}
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[zs]);
return C;
}

double C_cgl_tomo_sys(double ell_Cluster, int zl,int nN, int zs)
{
  double C;
  C=C_cgl_tomo_nointerp(ell_Cluster,zl,nN,zs);
  //if(like.IA!=0) C +=
  if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[zs]);
return C;
}

double C_ks_sys(double ell, int zs)
{
   double C;
   C = C_ks(ell,zs);
   if(like.shearcalib==1) C *=(1.0+nuisance.shear_calibration_m[zs]);
   return C;
}

void set_data_shear(int Ncl, double *ell, double *data, int start)
{
  int i,z1,z2,nz;
  double a;
  for (nz = 0; nz < tomo.shear_Npowerspectra; nz++){
    z1 = Z1(nz); z2 = Z2(nz);
    for (i = 0; i < Ncl; i++){
      // if (ell[i] < like.lmax_shear){ data[Ncl*nz+i] = C_shear_tomo_sys(ell[i],z1,z2);}
      if (mask(Ncl*nz+i)){ data[Ncl*nz+i] = C_shear_tomo_sys(ell[i],z1,z2);}
      else {data[Ncl*nz+i] = 0.;}
    }
  }
}

void set_data_ggl(int Ncl, double *ell, double *data, int start)
{
  int i, zl,zs,nz;
  for (nz = 0; nz < tomo.ggl_Npowerspectra; nz++){
    zl = ZL(nz); zs = ZS(nz);
    for (i = 0; i < Ncl; i++){
      // if (test_kmax(ell[i],zl)){
      if (mask(start+(Ncl*nz)+i)){
        data[start+(Ncl*nz)+i] = C_gl_tomo_sys(ell[i],zl,zs);
      }
      else{
        data[start+(Ncl*nz)+i] = 0.;
      }
    }
  }
}

void set_data_clustering(int Ncl, double *ell, double *data, int start){
  int i, nz;
  for (nz = 0; nz < tomo.clustering_Npowerspectra; nz++){
    //printf("%d %e %e\n",nz, gbias.b[nz][1],pf_photoz(gbias.b[nz][1],nz));
    for (i = 0; i < Ncl; i++){
      // if (test_kmax(ell[i],nz))
      if (mask(start+(Ncl*nz)+i)) {
        data[start+(Ncl*nz)+i] = C_cl_tomo_nointerp(ell[i],nz,nz);
      }
      else{data[start+(Ncl*nz)+i] = 0.;}
      //printf("%d %d %le %le\n",nz,nz,ell[i],data[Ncl*(tomo.shear_Npowerspectra+tomo.ggl_Npowerspectra + nz)+i]);
    }
  }
}

void set_data_gk(double *ell, double *data, int start)
{
   for (int nz=0; nz<tomo.clustering_Nbin; nz++){
      for (int i=0; i<like.Ncl; i++){
         // if (ell[i]<like.lmax_kappacmb && test_kmax(ell[i],nz)){
         if (mask(start+(like.Ncl*nz)+i)) {
            data[start+(like.Ncl*nz)+i] = C_gk(ell[i],nz);
         }
         else{
            data[start+(like.Ncl*nz)+i] = 0.;
         }
      }
   }
}

void set_data_ks(double *ell, double *data, int start)
{
   for (int nz=0; nz<tomo.shear_Nbin; nz++){
      for (int i=0; i<like.Ncl; i++){
         // if (ell[i]<like.lmax_kappacmb) {
         if (mask(start+(like.Ncl*nz)+i)) {
            data[start+(like.Ncl*nz)+i] = C_ks_sys(ell[i],nz);
         }
         else{
            data[start+(like.Ncl*nz)+i] = 0.;
         }
      }
   }
}

void set_data_kk(double *ell, double *data, int start)
{
   for (int i=0; i<like.Ncl; i++){
      // if (ell[i]<like.lmax_kappacmb){
      if (mask(start+i)) {
         data[start+i] = C_kk(ell[i]);
      }
      else{
         data[start+i] = 0.;
      }
   }
}

int set_cosmology_params(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu)
{
  cosmology.Omega_m=OMM;
  cosmology.Omega_v= 1.0-cosmology.Omega_m;
  cosmology.sigma_8=S8;
  cosmology.n_spec= NS;
  cosmology.w0=W0;
  cosmology.wa=WA;
  cosmology.omb=OMB;
  cosmology.h0=H0;
  cosmology.MGSigma=MGSigma;
  cosmology.MGmu=MGmu;

  if (cosmology.Omega_m < 0.05 || cosmology.Omega_m > 0.6) return 0;
  if (cosmology.omb < 0.04 || cosmology.omb > 0.055) return 0;
  if (cosmology.sigma_8 < 0.5 || cosmology.sigma_8 > 1.1) return 0;
  if (cosmology.n_spec < 0.84 || cosmology.n_spec > 1.06) return 0;
  if (cosmology.w0 < -2.1 || cosmology.w0 > -0.0) return 0;
  if (cosmology.wa < -2.6 || cosmology.wa > 2.6) return 0;
  if (cosmology.h0 < 0.4 || cosmology.h0 > 0.9) return 0;
  //CH BEGINS
  //CH: to use for running planck15_BA0_w0_wa prior alone)
  //printf("like_fourier.c from WFIRST_forecasts: cosmology bounds set for running with planck15_BA0_w0_wa prior\n");
  //if (cosmology.Omega_m < 0.05 || cosmology.Omega_m > 0.6) return 0;
  //if (cosmology.omb < 0.01 || cosmology.omb > 0.1) return 0;
  //if (cosmology.sigma_8 < 0.5 || cosmology.sigma_8 > 1.1) return 0;
  //if (cosmology.n_spec < 0.84 || cosmology.n_spec > 1.06) return 0;
  //if (cosmology.w0 < -2.1 || cosmology.w0 > 1.5) return 0;
  //if (cosmology.wa < -5.0 || cosmology.wa > 2.6) return 0;
  //if (cosmology.h0 < 0.3 || cosmology.h0 > 0.9) return 0;
  //CH ENDS
  return 1;
}

void set_nuisance_shear_calib(double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10)
{
  nuisance.shear_calibration_m[0] = M1;
  nuisance.shear_calibration_m[1] = M2;
  nuisance.shear_calibration_m[2] = M3;
  nuisance.shear_calibration_m[3] = M4;
  nuisance.shear_calibration_m[4] = M5;
  nuisance.shear_calibration_m[5] = M6;
  nuisance.shear_calibration_m[6] = M7;
  nuisance.shear_calibration_m[7] = M8;
  nuisance.shear_calibration_m[8] = M9;
  nuisance.shear_calibration_m[9] = M10;
}

int set_nuisance_shear_photoz(double SP1,double SP2,double SP3,double SP4,double SP5,double SP6,double SP7,double SP8,double SP9,double SP10)
{
  int i;
  nuisance.bias_zphot_shear[0]=SP1;
  nuisance.bias_zphot_shear[1]=SP2;
  nuisance.bias_zphot_shear[2]=SP3;
  nuisance.bias_zphot_shear[3]=SP4;
  nuisance.bias_zphot_shear[4]=SP5;
  nuisance.bias_zphot_shear[5]=SP6;
  nuisance.bias_zphot_shear[6]=SP7;
  nuisance.bias_zphot_shear[7]=SP8;
  nuisance.bias_zphot_shear[8]=SP9;
  nuisance.bias_zphot_shear[9]=SP10;

  // for (i=0;i<tomo.shear_Nbin; i++){
  //   nuisance.sigma_zphot_shear[i]=SPS1;
  //   if (nuisance.sigma_zphot_shear[i]<0.0001) return 0;
  // }
  return 1;
}

int set_nuisance_clustering_photoz(double CP1,double CP2,double CP3,double CP4,double CP5,double CP6,double CP7,double CP8,double CP9,double CP10)
{
  int i;
  nuisance.bias_zphot_clustering[0]=CP1;
  nuisance.bias_zphot_clustering[1]=CP2;
  nuisance.bias_zphot_clustering[2]=CP3;
  nuisance.bias_zphot_clustering[3]=CP4;
  nuisance.bias_zphot_clustering[4]=CP5;
  nuisance.bias_zphot_clustering[5]=CP6;
  nuisance.bias_zphot_clustering[6]=CP7;
  nuisance.bias_zphot_clustering[7]=CP8;
  nuisance.bias_zphot_clustering[8]=CP9;
  nuisance.bias_zphot_clustering[9]=CP10;

  // for (i=0;i<tomo.clustering_Nbin; i++){
  //   nuisance.sigma_zphot_clustering[i]=CPS1;
  //   if (nuisance.sigma_zphot_clustering[i]<0.0001) return 0;
  // }
  return 1;
}

int set_nuisance_ia(double A_ia, double eta_ia)
{
  nuisance.A_ia=A_ia;
  nuisance.eta_ia=eta_ia;
  nuisance.oneplusz0_ia = 1.62;
  if (nuisance.A_ia < 0.0 || nuisance.A_ia > 10.0) return 0;
  if (nuisance.eta_ia < -10.0 || nuisance.eta_ia> 10.0) return 0;
return 1;
}

// int set_nuisance_ia(double A_ia, double beta_ia, double eta_ia, double eta_ia_highz, double LF_alpha, double LF_P, double LF_Q, double LF_red_alpha, double LF_red_P, double LF_red_Q)
// {
//   nuisance.A_ia=A_ia;
//   nuisance.beta_ia=beta_ia;
//   nuisance.eta_ia=eta_ia;
//   nuisance.eta_ia_highz=eta_ia_highz;
//   nuisance.LF_alpha=LF_alpha;
//   nuisance.LF_P=LF_P;
//   nuisance.LF_Q=LF_Q;
//   nuisance.LF_red_alpha=LF_red_alpha;
//   nuisance.LF_red_P=LF_red_P;
//   nuisance.LF_red_Q=LF_red_Q;
//   if (nuisance.A_ia < 0.0 || nuisance.A_ia > 10.0) return 0;
//   if (nuisance.beta_ia < -4.0 || nuisance.beta_ia > 6.0) return 0;
//   if (nuisance.eta_ia < -10.0 || nuisance.eta_ia> 10.0) return 0;
//   if (nuisance.eta_ia_highz < -1.0 || nuisance.eta_ia_highz> 1.0) return 0;
//   // if(like.IA!=0){
//   //  if (check_LF()) return 0;
//   // }
// return 1;
// }

int set_nuisance_cluster_Mobs(double cluster_Mobs_lgN0,  double cluster_Mobs_alpha, double cluster_Mobs_beta, double cluster_Mobs_sigma0, double cluster_Mobs_sigma_qm, double cluster_Mobs_sigma_qz)
{
  //  nuisance.cluster_Mobs_lgM0 = mass_obs_norm;  //fiducial : 1.72+log(1.e+14*0.7); could use e.g. sigma = 0.2 Gaussian prior
  //  nuisance.cluster_Mobs_alpha = mass_obs_slope; //fiducial: 1.08; e.g. sigma = 0.1 Gaussian prior
  //  nuisance.cluster_Mobs_beta = mass_z_slope; //fiducial: 0.0; e.g. sigma = 0.1 Gaussian prior
  //  nuisance.cluster_Mobs_sigma = mass_obs_scatter; //fiducial 0.25; e.g. sigma = 0.05 Gaussian prior

  // fiducial values and priors from Murata et al. (2018) except for redshift-related parameters
  nuisance.cluster_Mobs_lgN0 = cluster_Mobs_lgN0; //fiducial: 3.207, flat prior [0.5, 5.0]
  nuisance.cluster_Mobs_alpha = cluster_Mobs_alpha; //fiducial: 0.993, flat prior [0.0, 2.0]
  nuisance.cluster_Mobs_beta = cluster_Mobs_beta; //fiducial: 0.0, flat prior [-1.5, 1.5]
  nuisance.cluster_Mobs_sigma0 = cluster_Mobs_sigma0; //fiducial: 0.456, flat prior [0.0, 1.5]
  nuisance.cluster_Mobs_sigma_qm = cluster_Mobs_sigma_qm; //fiducial: -0.169, flat prior [-1.5, 1.5]
  nuisance.cluster_Mobs_sigma_qz = cluster_Mobs_sigma_qz; //fiducial: 0.0, flat prior [-1.5, 1.5]

  if (nuisance.cluster_Mobs_lgN0 < 0.5 || nuisance.cluster_Mobs_lgN0 > 5.0) return 0;
  if (nuisance.cluster_Mobs_alpha < 0.0 || nuisance.cluster_Mobs_alpha > 2.0) return 0;
  if (nuisance.cluster_Mobs_beta < -1.5 || nuisance.cluster_Mobs_beta > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma0 < 0.0|| nuisance.cluster_Mobs_sigma0 > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma_qm < -1.5 && nuisance.cluster_Mobs_sigma_qm > 1.5) return 0;
  if (nuisance.cluster_Mobs_sigma_qz < -1.5 && nuisance.cluster_Mobs_sigma_qz > 1.5)return 0;

return 1;
}

int set_nuisance_gbias(double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8,double B9, double B10)
{

  int i;
  gbias.b[0] = B1;
  gbias.b[1] = B2;
  gbias.b[2] = B3;
  gbias.b[3] = B4;
  gbias.b[4] = B5;
  gbias.b[5] = B6;
  gbias.b[6] = B7;
  gbias.b[7] = B8;
  gbias.b[8] = B9;
  gbias.b[9] = B10;
  for (i = 0; i < tomo.clustering_Nbin; i++){
  //    printf("in set routine %d %le\n",i,gbias.b[i]);
    if (gbias.b[i] < 0.4 || gbias.b[i] > 3.0) return 0;
  }

  return 1;
}

int set_nuisance_bmag(double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8,double B9, double B10)
{

  int i;
  gbias.b_mag[0] = B1;
  gbias.b_mag[1] = B2;
  gbias.b_mag[2] = B3;
  gbias.b_mag[3] = B4;
  gbias.b_mag[4] = B5;
  gbias.b_mag[5] = B6;
  gbias.b_mag[6] = B7;
  gbias.b_mag[7] = B8;
  gbias.b_mag[8] = B9;
  gbias.b_mag[9] = B10;
  for (i = 0; i < tomo.clustering_Nbin; i++){
  //    printf("in set routine %d %le\n",i,gbias.b[i]);
    if (gbias.b_mag[i] < -5.0 || gbias.b_mag[i] > 5.0) return 0;
  }

  return 1;
}

double log_multi_like(double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double BMAG1, double BMAG2, double BMAG3, double BMAG4,double BMAG5, double BMAG6, double BMAG7, double BMAG8, double BMAG9, double BMAG10, double SP1, double SP2, double SP3, double SP4, double SP5, double SP6, double SP7, double SP8, double SP9, double SP10, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double eta_ia)
{
  int i,j,k,m=0,l;
  static double *pred;
  static double *ell;
  static double *ell_Cluster;
  static double darg;
  double chisqr,a,log_L_prior=0.0, log_L=0.0;;

  if(ell==0){
    pred= create_double_vector(0, like.Ndata-1);
    ell= create_double_vector(0, like.Ncl-1);
    darg=(log(like.lmax)-log(like.lmin))/like.Ncl;
    for (l=0;l<like.Ncl;l++){
      ell[l]=exp(log(like.lmin)+(l+0.5)*darg);
    }
    // ell_Cluster= create_double_vector(0, Cluster.lbin-1);
    // darg=(log(Cluster.l_max)-log(Cluster.l_min))/Cluster.lbin;
    // for (l=0;l<Cluster.lbin;l++){
    //   ell_Cluster[l]=exp(log(Cluster.l_min)+(l+0.5)*darg);
    // }
  }
  if (set_cosmology_params(OMM,S8,NS,W0,WA,OMB,H0,MGSigma,MGmu)==0){
    printf("Cosmology out of bounds\n");
    return -1.0e15;
  }
  set_nuisance_shear_calib(M1,M2,M3,M4,M5,M6,M7,M8,M9,M10);
  if (set_nuisance_shear_photoz(SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,SP9,SP10)==0){
    printf("Shear photo-z sigma too small\n");
    return -1.0e15;
  }
  if (set_nuisance_clustering_photoz(CP1,CP2,CP3,CP4,CP5,CP6,CP7,CP8,CP9,CP10)==0){
    printf("Clustering photo-z sigma too small\n");
    return -1.0e15;
  }
  if (set_nuisance_ia(A_ia,eta_ia)==0){
    printf("IA parameters out of bounds\n");
    return -1.0e15;
  }
  if (set_nuisance_gbias(B1,B2,B3,B4,B5,B6,B7,B8,B9,B10)==0){
    printf("Bias out of bounds\n");
    return -1.0e15;
  }
  if (set_nuisance_bmag(BMAG1,BMAG2,BMAG3,BMAG4,BMAG5,BMAG6,BMAG7,BMAG8,BMAG9,BMAG10)==0){
    printf("Bmag out of bounds\n");
    return -1.0e15;
  }
  // if (set_nuisance_cluster_Mobs(mass_obs_norm, mass_obs_slope, mass_z_slope, mass_obs_scatter_norm, mass_obs_scatter_mass_slope, mass_obs_scatter_z_slope)==0){
  //   printf("Mobs out of bounds\n");
  //   return -1.0e15;
  // }

  // for (i=0; i<10; i++){
  //   printf("nuisance %le %le %le\n",nuisance.shear_calibration_m[i],nuisance.bias_zphot_shear[i],nuisance.sigma_zphot_shear[i]);
  // }

  log_L_prior=0.0;
  // if(like.Aubourg_Planck_BAO_SN==1) log_L_prior+=log_L_Planck_BAO_SN();
  // if(like.SN==1) log_L_prior+=log_L_SN();
  //if(like.BAO==1) log_L_prior+=log_L_BAO();
  // if(like.Planck==1) log_L_prior+=log_L_Planck();
  // if(like.Planck15_BAO_w0wa==1) log_L_prior+=log_L_Planck15_BAO_w0wa();//CH
  //if(like.Planck15_BAO_H070p6_JLA_w0wa==1) log_L_prior+=log_L_Planck15_BAO_H070p6_JLA_w0wa();//CH
  // if(like.IA!=0) log_L_prior+=log_L_ia();
  // if(like.IA!=0) log_L_prior+=log_like_f_red();
  if(like.wlphotoz!=0) log_L_prior+=log_L_wlphotoz();
  if(like.clphotoz!=0) log_L_prior+=log_L_clphotoz();
  if(like.shearcalib==1) log_L_prior+=log_L_shear_calib();
  // if(like.IA!=0) {
  //   log_L = 0.0;
  //   log_L -= pow((nuisance.A_ia - prior.A_ia[0])/prior.A_ia[1],2.0);
  //   log_L -= pow((nuisance.beta_ia - prior.beta_ia[0])/prior.beta_ia[1],2.0);
  //   log_L -= pow((nuisance.eta_ia - prior.eta_ia[0])/prior.eta_ia[1],2.0);
  //   log_L -= pow((nuisance.eta_ia_highz - prior.eta_ia_highz[0])/prior.eta_ia_highz[1],2.0);
  //   log_L_prior+=0.5*log_L;
  // }
  // if(like.baryons==1){
  //   log_L = 0.0;
  //   log_L -= pow((Q1 - prior.bary_Q1[0])/prior.bary_Q1[1],2.0);
  //   log_L -= pow((Q2 - prior.bary_Q2[0])/prior.bary_Q2[1],2.0);
  //   log_L -= pow((Q3 - prior.bary_Q3[0])/prior.bary_Q3[1],2.0);
  //   log_L_prior+=0.5*log_L;
  // }

  // if(like.clusterMobs==1) log_L_prior+=log_L_clusterMobs();

  // printf("%d %d %d %d\n",like.BAO,like.wlphotoz,like.clphotoz,like.shearcalib);
  // printf("logl %le %le %le %le\n",log_L_shear_calib(),log_L_wlphotoz(),log_L_clphotoz(),log_L_clusterMobs());
  int start=0;

  if(like.shear_shear==1) {
    set_data_shear(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.shear_Npowerspectra;
  }
  if(like.shear_pos==1){
    set_data_ggl(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.ggl_Npowerspectra;
  }
  if(like.pos_pos==1){
    set_data_clustering(like.Ncl,ell,pred, start);
    start=start+like.Ncl*tomo.clustering_Npowerspectra;
  }
  if(like.gk==1) {
    set_data_gk(ell, pred, start);
    start += like.Ncl*tomo.clustering_Nbin;
  }
  if(like.ks==1) {
    set_data_ks(ell, pred, start);
    start += like.Ncl*tomo.shear_Nbin;
  }
  if(like.kk==1) {
    set_data_kk(ell, pred, start);
    start += like.Ncl;
  }

  chisqr=0.0;
  for (i=0; i<like.Ndata; i++){
    for (j=0; j<like.Ndata; j++){
      // a=(pred[i]-data_read(1,i)+Q1*bary_read(1,0,i)+Q2*bary_read(1,1,i)+Q3*bary_read(1,2,i))*invcov_read(1,i,j)*(pred[j]-data_read(1,j)+Q1*bary_read(1,0,j)+Q2*bary_read(1,1,j)+Q3*bary_read(1,2,j));
      a=(pred[i]-data_read(1,i))*invcov_mask(1,i,j)*(pred[j]-data_read(1,j));
      // if(a>10){printf("a,i,j: %le, %d, %d, %le, %le, %le\n",a,i,j,pred[i]-data_read(1,i),pred[j]-data_read(1,j), invcov_mask(1,i,j));}
      chisqr=chisqr+a;
    }
    // if (fabs(data_read(1,i)) < 1.e-25){
    //    printf("%d %le %le %le\n",i,data_read(1,i),pred[i],invcov_read(1,i,i));
    // }
  }
  if (chisqr<0.0){
    printf("error: chisqr = %le\n",chisqr);
    //exit(EXIT_FAILURE);
  }
  printf("%le\n",chisqr);
  return -0.5*chisqr+log_L_prior;
}

void compute_data_vector(char *details, double OMM, double S8, double NS, double W0,double WA, double OMB, double H0, double MGSigma, double MGmu, double B1, double B2, double B3, double B4,double B5, double B6, double B7, double B8, double B9, double B10, double BMAG1, double BMAG2, double BMAG3, double BMAG4,double BMAG5, double BMAG6, double BMAG7, double BMAG8, double BMAG9, double BMAG10, double SP1, double SP2, double SP3, double SP4, double SP5,double SP6, double SP7, double SP8, double SP9, double SP10, double CP1, double CP2, double CP3, double CP4, double CP5, double CP6, double CP7, double CP8, double CP9, double CP10, double M1, double M2, double M3, double M4, double M5, double M6, double M7, double M8, double M9, double M10, double A_ia, double eta_ia)
{

  int i,j,k,m=0,l;
  static double *pred;
  static double *ell;
  static double *ell_Cluster;
  static double darg;
  double chisqr,a,log_L_prior=0.0;

  if(ell==0){
    pred= create_double_vector(0, like.Ndata-1);
    ell= create_double_vector(0, like.Ncl-1);
    darg=(log(like.lmax)-log(like.lmin))/like.Ncl;
    for (l=0;l<like.Ncl;l++){
      ell[l]=exp(log(like.lmin)+(l+0.5)*darg);
    }
    // ell_Cluster= create_double_vector(0, Cluster.lbin-1);
    // darg=(log(Cluster.l_max)-log(Cluster.l_min))/Cluster.lbin;
    // for (l=0;l<Cluster.lbin;l++){
    //   ell_Cluster[l]=exp(log(Cluster.l_min)+(l+0.5)*darg);
    // }
  }
// for (l=0;l<like.Ncl;l++){
//   printf("%d %le\n",i,ell[l]);
// }

  set_cosmology_params(OMM,S8,NS,W0,WA,OMB,H0,MGSigma,MGmu);
  set_nuisance_shear_calib(M1,M2,M3,M4,M5,M6,M7,M8,M9,M10);
  set_nuisance_shear_photoz(SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,SP9,SP10);
  set_nuisance_clustering_photoz(CP1,CP2,CP3,CP4,CP5,CP6,CP7,CP8,CP9,CP10);
  set_nuisance_ia(A_ia,eta_ia);
  set_nuisance_gbias(B1,B2,B3,B4,B5,B6,B7,B8,B9,B10);
  set_nuisance_bmag(BMAG1,BMAG2,BMAG3,BMAG4,BMAG5,BMAG6,BMAG7,BMAG8,BMAG9,BMAG10);
  // set_nuisance_cluster_Mobs(mass_obs_norm,mass_obs_slope,mass_z_slope,mass_obs_scatter_norm,mass_obs_scatter_mass_slope,mass_obs_scatter_z_slope);

  int start=0;
  if(like.shear_shear==1) {
    set_data_shear(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.shear_Npowerspectra;
  }
  if(like.shear_pos==1){
    //printf("ggl\n");
    set_data_ggl(like.Ncl, ell, pred, start);
    start=start+like.Ncl*tomo.ggl_Npowerspectra;
  }
  if(like.pos_pos==1){
    //printf("clustering\n");
    set_data_clustering(like.Ncl,ell,pred, start);
    start=start+like.Ncl*tomo.clustering_Npowerspectra;
  }

  if(like.gk==1) {
    printf("Computing data vector: gk\n");
    set_data_gk(ell, pred, start);
    start += like.Ncl * tomo.clustering_Nbin;
  }
  if(like.ks==1) {
    printf("Computing data vector: ks\n");
    set_data_ks(ell, pred, start);
    start += like.Ncl * tomo.shear_Nbin;
  }
  if (like.kk) {
    printf("Computing data vector: kk\n");
    set_data_kk(ell, pred, start);
    start += like.Ncl;
  }

  FILE *F;
  char filename[300];
  if (strstr(details,"FM") != NULL){
    sprintf(filename,"%s",details);
  }
  else {sprintf(filename,"datav/%s_%s",like.probes,details);}
  F=fopen(filename,"w");
  for (i=0;i<like.Ndata; i++){
    fprintf(F,"%d %le\n",i,pred[i]);
    //printf("%d %le\n",i,pred[i]);
  }
  fclose(F);
  // printf("&gbias.b1_function %p\n",&gbias.b1_function);
  // printf("gbias.b1_function  %p\n",gbias.b1_function);
  // printf("bgal_z   %p\n",bgal_z);
  // printf("&bgal_z  %p\n",&bgal_z);
  // printf("b1_per_bin   %p\n",b1_per_bin);
  // printf("&b1_per_bin  %p\n",&b1_per_bin);

}

void write_datavector_wrapper(char *details, input_cosmo_params_des ic, input_nuisance_params_des in)
{
  compute_data_vector(details, ic.omega_m, ic.sigma_8, ic.n_s, ic.w0, ic.wa, ic.omega_b, ic.h0, ic.MGSigma, ic.MGmu,
    in.bias[0], in.bias[1], in.bias[2], in.bias[3],in.bias[4], in.bias[5], in.bias[6], in.bias[7],in.bias[8], in.bias[9],
    in.b_mag[0], in.b_mag[1], in.b_mag[2], in.b_mag[3],in.b_mag[4], in.b_mag[5], in.b_mag[6], in.b_mag[7],in.b_mag[8], in.b_mag[9],
    in.source_z_bias[0], in.source_z_bias[1], in.source_z_bias[2], in.source_z_bias[3], in.source_z_bias[4],
    in.source_z_bias[5], in.source_z_bias[6], in.source_z_bias[7], in.source_z_bias[8], in.source_z_bias[9],
    in.lens_z_bias[0], in.lens_z_bias[1], in.lens_z_bias[2], in.lens_z_bias[3], in.lens_z_bias[4],
    in.lens_z_bias[5], in.lens_z_bias[6], in.lens_z_bias[7], in.lens_z_bias[8], in.lens_z_bias[9],
    in.shear_m[0], in.shear_m[1], in.shear_m[2], in.shear_m[3], in.shear_m[4],
    in.shear_m[5], in.shear_m[6], in.shear_m[7], in.shear_m[8], in.shear_m[9],
    in.A_ia, in.eta_ia);
}

double log_like_wrapper(input_cosmo_params_des ic, input_nuisance_params_des in)
{
  double like = log_multi_like(ic.omega_m, ic.sigma_8, ic.n_s, ic.w0, ic.wa, ic.omega_b, ic.h0, ic.MGSigma, ic.MGmu,
    in.bias[0], in.bias[1], in.bias[2], in.bias[3],in.bias[4], in.bias[5], in.bias[6], in.bias[7],in.bias[8], in.bias[9],
    in.b_mag[0], in.b_mag[1], in.b_mag[2], in.b_mag[3],in.b_mag[4], in.b_mag[5], in.b_mag[6], in.b_mag[7],in.b_mag[8], in.b_mag[9],
    in.source_z_bias[0], in.source_z_bias[1], in.source_z_bias[2], in.source_z_bias[3], in.source_z_bias[4],
    in.source_z_bias[5], in.source_z_bias[6], in.source_z_bias[7], in.source_z_bias[8], in.source_z_bias[9],
    in.lens_z_bias[0], in.lens_z_bias[1], in.lens_z_bias[2], in.lens_z_bias[3], in.lens_z_bias[4],
    in.lens_z_bias[5], in.lens_z_bias[6], in.lens_z_bias[7], in.lens_z_bias[8], in.lens_z_bias[9],
    in.shear_m[0], in.shear_m[1], in.shear_m[2], in.shear_m[3], in.shear_m[4],
    in.shear_m[5], in.shear_m[6], in.shear_m[7], in.shear_m[8], in.shear_m[9],
    in.A_ia, in.eta_ia);
  return like;
}

void save_zdistr_sources(int zs){
  double z,dz =(redshift.shear_zdistrpar_zmax-redshift.shear_zdistrpar_zmin)/300.0;
  printf("Printing redshift distribution n(z) for source redshift bin %d\n",zs);

   FILE *F1;
   char filename[300];
   sprintf(filename,"zdistris/zdist_sources_bin%d.txt",zs);
   F1 = fopen(filename,"w");
   for (z =redshift.shear_zdistrpar_zmin; z< redshift.shear_zdistrpar_zmax; z+= dz){
      fprintf(F1,"%e %e\n", z, zdistr_photoz(z,zs));
   }
}

void save_zdistr_lenses(int zl){
   double z,dz =(redshift.clustering_zdistrpar_zmax-redshift.clustering_zdistrpar_zmin)/300.0;
  printf("Printing redshift distribution n(z) and bias b(z) for lens redshift bin %d\n",zl);

   FILE *F1;
   char filename[300];
   sprintf(filename,"zdistris/zdist_lenses_bin%d.txt", zl);
   F1 = fopen(filename,"w");
   for (z =redshift.clustering_zdistrpar_zmin; z< redshift.clustering_zdistrpar_zmax; z+= dz){
      fprintf(F1,"%e %e\n", z, pf_photoz(z,zl));
   }
}

 int main(int argc, char** argv)
{
  int i;
  // char arg1[400],arg2[400],arg3[400];
/* here, do your time-consuming job */
  // int sce=atoi(argv[1]);

  // int N_scenarios=2;

  // double sigma_zphot_shear[3]={0.05,0.05};
  // double sigma_zphot_clustering[3]={0.03,0.03};

  // double area_table[2]={12300.0,16500.0}; // Y1 corresponds to DESC SRD Y1, Y6 corresponds to assuming that we cover the full SO area=0.4*fsky and at a depth of 26.1 which is in a range of reasonable scenarios (see https://github.com/LSSTDESC/ObsStrat/tree/static/static )
  // double nsource_table[2]={11.0,23.0};
  // double nlens_table[2]={18.0,41.0};

  // char survey_designation[2][200]={"LSSTxSO_Y1","LSSTxSO_Y6"};
  // char tomo_binning_source[2][200]={"source_std","source_std"};
  // char tomo_binning_lens[2][200]={"LSST_gold","LSST_gold"};

  // char source_zfile[2][400]={"src_LSSTY1","src_LSSTY6"};
  // char lens_zfile[2][400]={"lens_LSSTY1", "lens_LSSTY6"};

  init_cosmo_runmode("halofit");
  // init_bary(argv[2]);
  init_binning_fourier(20,30.0,3000.0,3000.0,20.0);

  // init_priors(0.002,sigma_zphot_shear[sce],0.001,0.001,sigma_zphot_clustering[sce],0.001,0.001,3.0,1.2,3.8,2.0,16.0,5.0,0.8);
  // init_survey(survey_designation[sce],nsource_table[sce],nlens_table[sce],area_table[sce]);
  // sprintf(arg1,"zdistris/%s",source_zfile[sce]);
  // sprintf(arg2,"zdistris/%s",lens_zfile[sce]);
  // init_galaxies(arg1,arg2,"gaussian","gaussian",tomo_binning_source[sce],tomo_binning_lens[sce]);

  double b1[10]={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.}, b2[10] ={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.},b_mag[10] ={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
  b1[0] = 1.7;
  b1[1] = 1.7;
  b1[2] = 1.7;
  b1[3] = 2.0;
  b1[4] = 2.0;

  b_mag[0] = -0.19375;
  b_mag[1] = -0.6285407;
  b_mag[2] = -0.69319886;
  b_mag[3] = 1.17735723;
  b_mag[4] = 1.87509758;

  // init_source_sample_mpp("./zdistris/mcal_1101_source.nz",4);
  init_source_sample_mpp("./zdistris/source_DESY6_3bins.nz",3);
  init_lens_sample_mpp("./zdistris/mcal_1101_lens.nz",5);
  init_IA_mpp(4);
  init_probes("6x2pt");

  init_cmb("planck");
  compute_data_vector("desy6xplanck_6x2pt_3bins_2.txt",0.25,0.75,0.97,-1.,0.,0.048,0.69,0.,0.,\
    b1[0],b1[1],b1[2],b1[3],b1[4],\
    b1[5],b1[6],b1[7],b1[8],b1[9],\
    b_mag[0],b_mag[1],b_mag[2],b_mag[3],b_mag[4],\
    b_mag[5],b_mag[6],b_mag[7],b_mag[8],b_mag[9],\
    0.0,0.0,0.0,0.0,0.0,\
    0.0,0.0,0.0,0.0,0.0,\
    0.0,0.0,0.0,0.0,0.0,\
    0.0,0.0,0.0,0.0,0.0,\
    0.0,0.0,0.0,0.0,0.0,\
    0.0,0.0,0.0,0.0,0.0,\
    0.5,0.0);

  // init_data_cov_mask("cov/cov_desy3xplanck","datav/6x2pt_desy3xplanck_6x2pt.txt", "datav/xi_Y3_6x2pt.mask");
  // // init_data_cov_mask("cov/cov_desy6xplanck_3src","datav/6x2pt_desy6xplanck_6x2pt_3bins.txt", "datav/xi_Y6_6x2pt_3src.mask");

  // for(double omm=0.2;omm<0.5; omm+=0.01){
  // printf("%le, %le\n", omm, log_multi_like(omm,0.82355,0.97,-1.,0.,0.048,0.69,0.,0.,\
  //   b1[0],b1[1],b1[2],b1[3],b1[4],\
  //   b1[5],b1[6],b1[7],b1[8],b1[9],\
  //   b_mag[0],b_mag[1],b_mag[2],b_mag[3],b_mag[4],\
  //   b_mag[5],b_mag[6],b_mag[7],b_mag[8],b_mag[9],\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.0,0.0,0.0,0.0,0.0,\
  //   0.5,0.0));
  // exit(0);
  // }
  return 0;
}


/*****************************************************************************
*
* Rokko: Integrated Interface for libraries of eigenvalue decomposition
*
* Copyright (C) 2012-2015 Rokko Developers https://github.com/t-sakashita/rokko
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
*****************************************************************************/

#ifndef ROKKO_PBLAS_INTERFACE_H
#define ROKKO_PBLAS_INTERFACE_H

#include <lapacke.h>
#undef I
#include <rokko/mangling.h>

/* PvCOPY */

#define PBLAS_PCOPY_DECL(NAMES, NAMEL, TYPE) \
void ROKKO_GLOBAL(NAMES, NAMEL) (const int* N, const TYPE * X, const int* IX, const int* JX, const int* DESCX, const int* INCX, TYPE * Y, const int* IY, int* JY, const int* DESCY, const int* INCY);

#ifdef __cplusplus
extern "C" {
#endif

PBLAS_PCOPY_DECL(pscopy, PSCOPY, float);
PBLAS_PCOPY_DECL(pdcopy, PDCOPY, double);
PBLAS_PCOPY_DECL(pccopy, PCCOPY, lapack_complex_float);
PBLAS_PCOPY_DECL(pzcopy, PZCOPY, lapack_complex_double);

#ifdef __cplusplus
}
#endif

#undef PBLAS_PCOPY_DECL

/* PvDOT, PvDOTU, PvDOTC */

#define PBLAS_PDOT_DECL(NAMES, NAMEL, TYPE) \
void ROKKO_GLOBAL(NAMES, NAMEL) (const int* N, TYPE * DOT, const TYPE * X, const int* IX, const int* JX, const int* DESCX, const int* INCX, const TYPE * Y, const int* IY, const int* JY, const int* DESCY, const int* INCY);

#ifdef __cplusplus
extern "C" {
#endif

PBLAS_PDOT_DECL(psdot, PSDOT, float);
PBLAS_PDOT_DECL(pddot, PDDOT, double);
PBLAS_PDOT_DECL(pcdotu, PCDOTU, lapack_complex_float);
PBLAS_PDOT_DECL(pzdotu, PZDOTU, lapack_complex_double);
PBLAS_PDOT_DECL(pcdotc, PCDOTC, lapack_complex_float);
PBLAS_PDOT_DECL(pzdotc, PZDOTC, lapack_complex_double);

#ifdef __cplusplus
}
#endif

#undef PBLAS_PDOT_DECL

/* PvGEMV */

#define PBLAS_PGEMV_DECL(NAMES, NAMEL, TYPE) \
void ROKKO_GLOBAL(NAMES, NAMEL) (const char* TRANS, const int* M, const int* N, const TYPE * ALPHA, const TYPE * A, const int* IA, const int* JA, const int* DESCA, const TYPE * X, const int* IX, const int* JX, const int* DESCX, const int* INCX, const TYPE * BETA, TYPE * Y, const int* IY, const int* JY, const int* DESCY, const int* INCY);

#ifdef __cplusplus
extern "C" {
#endif

PBLAS_PGEMV_DECL(psgemv, PSGEMV, float);
PBLAS_PGEMV_DECL(pdgemv, PDGEMV, double);
PBLAS_PGEMV_DECL(pcgemv, PCGEMV, lapack_complex_float);
PBLAS_PGEMV_DECL(pzgemv, PZGEMV, lapack_complex_double);

#ifdef __cplusplus
}
#endif

#undef PBLAS_PGEMV_DECL

/* PvGEMM */

#define PBLAS_PGEMM_DECL(NAMES, NAMEL, TYPE) \
void ROKKO_GLOBAL(NAMES, NAMEL) (const char* TRANSA, const char* TRANSB, const int* M, const int* N, const int* K, const TYPE * ALPHA, const TYPE * A, const int* IA, const int* JA, const int* DESCA, const TYPE * B, const int* IB, const int* JB, const int* DESCB, const TYPE * BETA, TYPE * C, const int* IC, const int* JC, const int* DESCC);

#ifdef __cplusplus
extern "C" {
#endif

PBLAS_PGEMM_DECL(psgemm, PSGEMM, float);
PBLAS_PGEMM_DECL(pdgemm, PDGEMM, double);
PBLAS_PGEMM_DECL(pcgemm, PCGEMM, lapack_complex_float);
PBLAS_PGEMM_DECL(pzgemm, PZGEMM, lapack_complex_double);

#ifdef __cplusplus
}
#endif

#undef PBLAS_PGEMM_DECL

#endif // ROKKO_PBLAS_INTERFACE_H


/**
 * @file tracker.h
 * @brief Beamforming and speaker tracking with a spherical microphone array.
 * @author John McDonough
 */
#ifndef TRACKER_H
#define TRACKER_H

#include <stdio.h>
#include <assert.h>
#include <float.h>

#include <gsl/gsl_block.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_complex_math.h>

#include "common/refcount.h"
#include "common/jexception.h"

#include "stream/stream.h"
#include "beamformer/spectralinfoarray.h"
#include "modulated/modulated.h"
#include "aec/aec.h"

// ----- definition of class 'BaseDecomposition' -----
//
class BaseDecomposition : public Countable {
public:
  class SubbandEntry {
  public:
    SubbandEntry(unsigned subbandX, const gsl_complex& Bkl);

    unsigned subbandX() const { return subbandX_; }
    gsl_complex bkl()   const { return bkl_; }

    void* operator new(size_t sz) { return memoryManager().new_elem(); }
    void  operator delete(void* e) { memoryManager().delete_elem(e); }

    static MemoryManager<SubbandEntry>& memoryManager();

  private:
    const unsigned			subbandX_;
    const gsl_complex			bkl_;
  };

  class GreaterThan {
  public:
    bool operator()(SubbandEntry* sbX1, SubbandEntry* sbX2) {
      return (gsl_complex_abs(sbX1->bkl()) > gsl_complex_abs(sbX2->bkl()));
    }
  };

  class Iterator;

  class SubbandList : public Countable {
  public:
    SubbandList(const gsl_vector_complex* bkl, unsigned useSubbandsN = 0);
    ~SubbandList();

    unsigned useSubbandsN() const { return useSubbandsN_; }
    SubbandEntry** subbands() { return subbands_; }

    friend class Iterator;

  private:
    const unsigned			subbandsN_;
    const unsigned			useSubbandsN_;
    SubbandEntry**			subbands_;
  };

  typedef refcountable_ptr<SubbandList> SubbandListPtr;

  class Iterator {
  public:
    Iterator(const SubbandListPtr& subbandList)
      : subbandX_(0), useSubbandsN_(subbandList->useSubbandsN()), subbands_(subbandList->subbands()) { }
    void operator++(int) { subbandX_++; }
    bool more()          { return subbandX_ < useSubbandsN_; }
    const SubbandEntry& operator*() { return *(subbands_[subbandX_]); }

  private:
    unsigned				subbandX_;
    const unsigned			useSubbandsN_;
    SubbandEntry**			subbands_;
  };

 public:
  BaseDecomposition(unsigned orderN, unsigned subbandsN, double a, double sampleRate, unsigned useSubbandsN = 0, bool spatial = false);
  ~BaseDecomposition();

  unsigned orderN()         const { return orderN_;         }
  unsigned modesN()         const { return modesN_;         }
  unsigned subbandsN2()     const { return subbandsN2_;     }
  unsigned subbandsN()      const { return subbandsN_;      }
  unsigned useSubbandsN()   const { return useSubbandsN_;   }
  unsigned subbandLengthN() const { return subbandLengthN_; }

  SubbandListPtr subbandList()  { return subbandList_;    }

  virtual void reset();

  static gsl_complex harmonic(int order, int degree, double theta, double phi);
  gsl_complex harmonic(int order, int degree, unsigned channelX) const;
  static gsl_complex harmonic_deriv_polar_angle(int order, int degree, double theta, double phi);
  static gsl_complex harmonic_deriv_azimuth(int order, int degree, double theta, double phi);
  static gsl_complex modal_coefficient(unsigned order, double ka);
  gsl_complex modal_coefficient(unsigned order, unsigned subbandX) const;
  virtual void transform(const gsl_vector_complex* initial, gsl_vector_complex* transformed) { }
  virtual void estimate_Bkl(double theta, double phi, const gsl_vector_complex* snapshot, unsigned subbandX) = 0;
  virtual void calculate_gkl(double theta, double phi, unsigned subbandX) = 0;
  virtual const gsl_matrix_complex* linearize(gsl_vector* xk, int frame_no) = 0;
  virtual const gsl_vector_complex* predicted_observation(gsl_vector* xk, int frame_no) = 0;

#ifdef ENABLE_LEGACY_BTK_API
  static gsl_complex harmonicDerivPolarAngle(int order, int degree, double theta, double phi){
    return harmonic_deriv_polar_angle(order, degree, theta, phi);
  }
  static gsl_complex harmonicDerivAzimuth(int order, int degree, double theta, double phi){
    return harmonic_deriv_azimuth(order, degree, theta, phi);
  }
  static gsl_complex modalCoefficient(unsigned order, double ka){
    return modal_coefficient(order, ka);
  }
#endif

  static const unsigned				StateN_;
  static const unsigned				ChannelsN_;
  static const gsl_complex 			ComplexZero_;
  static const gsl_complex 			ComplexOne_;
  static const double				SpeedOfSound_;

 protected:
  static gsl_complex calc_in_(int n);
  void set_eigenmike_geometry_();
  gsl_complex calc_Gnm_(unsigned subbandX, int n, int m, double theta, double phi);
  gsl_complex calc_dGnm_dtheta_(unsigned subbandX, int n, int m, double theta, double phi);
  static double calc_Pnm_(int order, int degree, double theta);
  static double calc_dPnm_dtheta_(int n, int m, double theta);
  static double calc_normalization_(int order, int degree);

  const unsigned				orderN_;
  const unsigned				modesN_;
  const unsigned				subbandsN_;
  const unsigned				subbandsN2_;
  const unsigned				useSubbandsN_;
  const unsigned				subbandLengthN_;
  const double					samplerate_;
  const double					a_;
  gsl_vector_complex**				bn_;
  gsl_vector*					theta_s_;
  gsl_vector*					phi_s_;
  gsl_vector_complex**				spherical_component_;
  gsl_vector_complex*				bkl_;
  gsl_vector_complex*				dbkl_dtheta_;
  gsl_vector_complex*				dbkl_dphi_;
  gsl_vector_complex**				gkl_;
  gsl_vector_complex**				dgkl_dtheta_;
  gsl_vector_complex**				dgkl_dphi_;
  gsl_vector_complex*				vkl_;
  gsl_matrix_complex*				Hbar_k_;
  gsl_vector_complex*				yhat_k_;

  SubbandListPtr				subbandList_;
};

typedef refcountable_ptr<BaseDecomposition> BaseDecompositionPtr;

// ----- definition of class 'ModalDecomposition' -----
//
class ModalDecomposition : public BaseDecomposition {
public:
  ModalDecomposition(unsigned orderN, unsigned subbandsN, double a, double sampleRate, unsigned useSubbandsN = 0);
  ~ModalDecomposition() { }

  virtual void estimate_Bkl(double theta, double phi, const gsl_vector_complex* snapshot, unsigned subbandX);
  virtual void transform(const gsl_vector_complex* initial, gsl_vector_complex* transformed);
  virtual const gsl_matrix_complex* linearize(gsl_vector* xk, int frame_no);
  virtual const gsl_vector_complex* predicted_observation(gsl_vector* xk, int frame_no);
  virtual void calculate_gkl(double theta, double phi, unsigned subbandX);
};

typedef Inherit<ModalDecomposition, BaseDecompositionPtr>	ModalDecompositionPtr;

// ----- definition of class 'SpatialDecomposition' -----
//
class SpatialDecomposition : public BaseDecomposition {
public:
  SpatialDecomposition(unsigned orderN, unsigned subbandsN, double a, double sampleRate, unsigned useSubbandsN = 0);
  ~SpatialDecomposition() { }

  virtual void estimate_Bkl(double theta, double phi, const gsl_vector_complex* snapshot, unsigned subbandX);
  virtual const gsl_matrix_complex* linearize(gsl_vector* xk, int frame_no);
  virtual const gsl_vector_complex* predicted_observation(gsl_vector* xk, int frame_no);
  virtual void calculate_gkl(double theta, double phi, unsigned subbandX);
};

typedef Inherit<SpatialDecomposition, BaseDecompositionPtr>	SpatialDecompositionPtr;

// ----- definition of class 'BaseSphericalArrayTracker' -----
//
class BaseSphericalArrayTracker : public VectorFloatFeatureStream {
  typedef list<VectorComplexFeatureStreamPtr>	ChannelList_;
  typedef ChannelList_::iterator		ChannelIterator_;
  typedef BaseDecomposition::SubbandEntry	SubbandEntry;
  typedef BaseDecomposition::SubbandListPtr	SubbandListPtr;
  typedef BaseDecomposition::Iterator		Iterator;

public:
  BaseSphericalArrayTracker(BaseDecompositionPtr& baseDecomposition, double sigma2_u = 10.0, double sigma2_v = 10.0, double sigma2_init = 10.0,
			    unsigned maxLocalN = 1, const String& nm = "BaseSphericalArrayTracker");
  ~BaseSphericalArrayTracker();

  virtual const gsl_vector_float* next(int frame_no = -5) = 0;

  virtual void reset();

  unsigned chanN() const { return channelList_.size(); }
  void set_channel(VectorComplexFeatureStreamPtr& chan);
  void set_V(const gsl_matrix_complex* Vk, unsigned subbandX);
  void next_speaker();
  void set_initial_position(double theta, double phi);

#ifdef ENABLE_LEGACY_BTK_API
  void setChannel(VectorComplexFeatureStreamPtr& chan){ set_channel(chan); }
  void setV(const gsl_matrix_complex* Vk, unsigned subbandX){ set_V(Vk, subbandX); }
  void nextSpeaker(){ next_speaker(); }
  void setInitialPosition(double theta, double phi){ set_initial_position(theta, phi); }
#endif

protected:
  static void printMatrix_(const gsl_matrix_complex* mat);
  static void printMatrix_(const gsl_matrix* mat);
  static void printVector_(const gsl_vector_complex* vec);
  static void printVector_(const gsl_vector* vec);

  static double calc_givens_rotation_(double v1, double v2, double& c, double& s);
  static void apply_givens_rotation_(double v1, double v2, double c, double s, double& v1p, double& v2p);

  void alloc_image_();
  void update_(const gsl_matrix_complex* Hbar_k, const gsl_vector_complex* yhat_k, const SubbandListPtr& subbandList);
  void lower_triangularize_();
  void copy_position_();
  void check_physical_constraints_();
  double calc_residual_();
  void realify_(const gsl_matrix_complex* Hbar_k, const gsl_vector_complex* yhat_k);
  void realify_residual_();

  static const unsigned 			StateN_;
  static const gsl_complex 			ComplexZero_;
  static const gsl_complex 			ComplexOne_;
  static const double      			Epsilon_;
  static const double				Tolerance_;

  bool						firstFrame_;
  const unsigned				subbandsN_;
  const unsigned				subbandsN2_;
  const unsigned				useSubbandsN_;
  const unsigned 				modesN_;
  const unsigned 				subbandLengthN_;
  const unsigned 				observationN_;
  const unsigned 				maxLocalN_;
  bool						is_end_;
  const double 					sigma_init_;

  SnapShotArrayPtr				snapshot_array_;
  BaseDecompositionPtr				base_decomposition_;
  ChannelList_					channelList_;

  // these quantities are stored as Cholesky factors
  gsl_matrix*					U_;
  gsl_matrix*					V_;
  gsl_matrix*					K_k_k1_;

  // work space for state estimate update
  gsl_matrix*					prearray_;
  gsl_vector_complex*				vk_;
  gsl_matrix*					Hbar_k_;
  gsl_vector*					yhat_k_;
  gsl_vector*					correction_;
  gsl_vector*					position_;
  gsl_vector*					eta_i_;
  gsl_vector*					delta_;
  gsl_vector_complex*				residual_;
  gsl_vector*					residual_real_;
  gsl_vector*					scratch_;
};

typedef Inherit<BaseSphericalArrayTracker, VectorFloatFeatureStreamPtr> BaseSphericalArrayTrackerPtr;

// ----- definition of class 'ModalSphericalArrayTracker' -----
//
class ModalSphericalArrayTracker : public BaseSphericalArrayTracker {
  typedef list<VectorComplexFeatureStreamPtr>	ChannelList_;
  typedef ChannelList_::iterator		ChannelIterator_;

public:
  ModalSphericalArrayTracker(ModalDecompositionPtr& modalDecomposition, double sigma2_u = 10.0, double sigma2_v = 10.0, double sigma2_init = 10.0,
			     unsigned maxLocalN = 1, const String& nm = "ModalSphericalArrayTracker");
  ~ModalSphericalArrayTracker() { }

  const gsl_vector_float* next(int frame_no = -5);
};

typedef Inherit<ModalSphericalArrayTracker, VectorFloatFeatureStreamPtr> ModalSphericalArrayTrackerPtr;

// ----- definition of class 'SpatialSphericalArrayTracker' -----
//
class SpatialSphericalArrayTracker : public BaseSphericalArrayTracker {
  typedef list<VectorComplexFeatureStreamPtr>	ChannelList_;
  typedef ChannelList_::iterator		ChannelIterator_;

public:
 SpatialSphericalArrayTracker(SpatialDecompositionPtr& spatialDecomposition, double sigma2_u = 10.0, double sigma2_v = 10.0, double sigma2_init = 10.0,
			       unsigned maxLocalN = 1, const String& nm = "SpatialSphericalArrayTracker");
  ~SpatialSphericalArrayTracker() { }

  const gsl_vector_float* next(int frame_no= -5);
};

typedef Inherit<SpatialSphericalArrayTracker, VectorFloatFeatureStreamPtr> SpatialSphericalArrayTrackerPtr;

// ----- definition of class 'PlaneWaveSimulator' -----
//
class PlaneWaveSimulator : public VectorComplexFeatureStream {
public:
  PlaneWaveSimulator(const VectorComplexFeatureStreamPtr& source, ModalDecompositionPtr& modalDecomposition,
		     unsigned channelX, double theta, double phi, const String& nm = "Plane Wave Simulator");
  ~PlaneWaveSimulator();

  const gsl_vector_complex* next(int frame_no = -5);

  virtual void reset();

private:
  static const gsl_complex			ComplexZero_;

  const unsigned				subbandsN_;
  const unsigned				subbandsN2_;
  const unsigned				channelX_;
  const double					theta_;
  const double					phi_;

  VectorComplexFeatureStreamPtr			source_;
  ModalDecompositionPtr				modalDecomposition_;
  gsl_vector_complex*				subbandCoefficients_;
};

typedef Inherit<PlaneWaveSimulator, VectorComplexFeatureStreamPtr> PlaneWaveSimulatorPtr;

#ifdef HAVE_GSL_V11X

int gsl_matrix_complex_add( gsl_matrix_complex *am, gsl_matrix_complex *bm )
{
  if( am->size1 != bm->size1 ){
    fprintf(stderr,"Dimension error \n",am->size1, bm->size1 );
    return 0;
  }

  if( am->size2 != bm->size2 ){
    fprintf(stderr,"Dimension error \n",am->size2, bm->size2 );
    return 0;
  }

  for(size_t i=0;i<am->size1;i++){
    for(size_t j=0;j<am->size2;j++){
      gsl_complex val = gsl_complex_add( gsl_matrix_complex_get(am,i,j), gsl_matrix_complex_get(bm,i,j) );
      gsl_matrix_complex_set(am,i,j,val);
    }
  }

  return 1;
}

int gsl_vector_complex_add( gsl_vector_complex *av, gsl_vector_complex *bv )
{
  if( av->size != bv->size ){
    fprintf(stderr,"Dimension error \n",av->size, bv->size);
    return 0;
  }

  for(size_t i=0;i<av->size;i++){
    gsl_complex val = gsl_complex_add( gsl_vector_complex_get(av,i), gsl_vector_complex_get(bv,i) );
    gsl_vector_complex_set(av,i,val);
  }

  return 1;
}

int gsl_vector_complex_sub( const gsl_vector_complex *av, const gsl_vector_complex *bv )
{
  if( av->size != bv->size ){
    fprintf(stderr,"Dimension error \n",av->size, bv->size);
    return 0;
  }

  for(size_t i=0;i<av->size;i++){
    gsl_complex val = gsl_complex_sub( gsl_vector_complex_get(av,i), gsl_vector_complex_get(bv,i) );
    gsl_vector_complex_set((gsl_vector_complex*)av,i,val);
  }

  return 1;
}

#endif /* End of HAVE_GSL_V11X */

#endif


#ifndef CWANNIER_PARTIALDOS_VALUES_H
#define CWANNIER_PARTIALDOS_VALUES_H

#include <gsl/gsl_permutation.h>
#include <gsl/gsl_permute_vector.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_sort_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_eigen.h>
#include "HTightBinding.h"
#include "ctetra/partial.h"

double** PartialDosValues(HTightBinding *Hrs, gsl_matrix *R, int na, int nb, int nc, double sigma, double **Es, double num_dos);

void sort_evals_evecs(gsl_vector *energies, gsl_matrix_complex *evecs, int num_bands);

#endif // CWANNIER_PARTIALDOS_VALUES_H


/*
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

#include "spc_FITScards.h"
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spce_PET.h"
#include "inout_aper.h"
#include "trace_conf.h"
#include "aper_conf.h"
#include "disp_conf.h"
#include "drizzle_utils.h"
#include "crossdisp_utils.h"
#include "spc_cfg.h"
#include "inima_utils.h"
#include "spce_output.h"

#define AXE_IMAGE_PATH "AXE_IMAGE_PATH"
#define AXE_OUTPUT_PATH "AXE_OUTPUT_PATH"
#define AXE_CONFIG_PATH "AXE_CONFIG_PATH"

int
main (int argc, char *argv[])
{
  char *opt;
  char aper_file[MAXCHAR];
  char aper_file_path[MAXCHAR];

  char list_file[MAXCHAR];
  char list_file_path[MAXCHAR];

  char conf_file[MAXCHAR];
  char conf_file_path[MAXCHAR];

  char grism_file[MAXCHAR];
  char grism_file_path[MAXCHAR];

  char PET_file[MAXCHAR];
  char PET_file_path[MAXCHAR];

  char SEC_file[MAXCHAR];
  char SEC_file_path[MAXCHAR];

  char outputroot[MAXCHAR];
  char outputroot_path[MAXCHAR];

  char output_path[MAXCHAR];

  aperture_conf *conf;

  object **oblist;
  observation *obs;

  ap_pixel *pri_PET;
  ap_pixel *sec_PET;

  d_point     pixel;
  d_point     refwave_pos;
  d_point     outref;
  d_point     minxy_PET;
  px_point    pixmax;
  dispstruct  *disp, *outdisp;
  trace_func  *trace;
  gsl_matrix  *drizzcoeffs;
  //gsl_vector  *lambdaref = gsl_vector_alloc(2);

  FILE *in_file;

  objectobs  **allobjects;

  int nobjects, nobjects2;
  int boxwidth, boxheight,trlength;
  double relx, rely, objwidth, orient;
  //double min_px, min_py;
  double drizzle_width;
  double cdref, cdscale, cdcorr, cdmeanscale;
  double sprefreso, spreso, spmeanreso, spcorr;
  double sky_cps;
  double exptime;

  double *gaga;

  int index, i, for_grism, in_index;
  char label[MAXCHAR];

  fitsfile *PET_ptr, *SEC_ptr, *DPP_ptr;
  int f_status = 0;

  int aperID, beamID, objindex;
  FITScards *cards;

  drzstamp_dim dimension;
  drzprep *drzprep_stamps;

  axe_inputs *input_list;

  //int rectified = 0;
  //int drizzled  = 0;
  int bckmode   = 0;
  int backpet   = 1;
  //int usemode   = 0;
  int quant_cont= 0;
  int opt_extr  = 0;

  if ((argc < 3) || (opt = get_online_option ("help", argc, argv)))
    {
      fprintf (stdout,
	       "aXe_DRZPREP Version %s:\n"
	       "           aXe task to produce a set of Drizzle PrePare (DPP) files\n"
	       "           for a set of images given in an image list. A DPP-file is\n"
	       "           a multi extension fits file with an pixel stamp image and a\n"
	       "           contamination stamp image for each aperture in the\n"
	       "           grism image. Uses the PET file to derive the pixel/contamination\n"
	       "           values for the stamp images and the aperture files\n"
	       "           (AF's) to define a common geometry for the individual objects,\n"
	       "           which are usually located on several images/PET's. The task also\n"
	       "           derives and stores keywords such that aXe_DRIZZLE can create\n"
	       "           coadded images for all objects in the set of DPP-files.\n"
	       "\n"
	       "           The image list is looked for in the current directory.\n"
	       "           The images listed in the image list is looked for in\n"
	       "           $AXE_IMAGE_PATH. The OAF/BAF- and PET-files computed from\n"
	       "           those images is looked for in $AXE_OUTPUT_PATH\n"
	       "           The DPP's are written to $AXE_OUTPUT_PATH\n"
	       "\n"
	       "Usage:\n"
	       "      aXe_DRZPREP [image list filename] [aXe_config1,aXe_config2,..] [options]\n"
	       "\n"
	       "Options:\n"
	       "           -bck  - generating a background DPP from a background PET\n"
	       "\n",RELEASE);
      exit (1);
    }

  fprintf (stdout, "aXe_DRZPREP: Starting...\n\n");

  index = 0;
  strcpy (list_file, argv[++index]);
  strcpy (list_file_path, list_file);

  strcpy (conf_file, argv[++index]);

  // build the aXe inputs structure
  input_list = get_axe_inputs(list_file_path, conf_file);
  //print_axe_inputs(input_list);

  /* Determine if we are using the special bck mode */
  /* In this mode, file names are handled differently */
  if ((opt = get_online_option("bck", argc, argv)))
    bckmode = 1;
  if ((opt = get_online_option("backpet", argc, argv)))
    backpet = 1;
  if ((opt = get_online_option("opt_extr", argc, argv)))
    opt_extr = 1;

  // give feedback onto the screen:
  // report on input and output
  // and also on specific parameter
  fprintf (stdout, "aXe_DRZPREP: Input Image List:           %s\n",
	   list_file_path);
  fprintf (stdout, "aXe_DRZPREP: Configuration file names:   %s\n",
	   conf_file);
  if (opt_extr)
    fprintf (stdout, "aXe_DRZPREP: Storing optimal weighting extentions.\n");
  fprintf(stdout, "\n\n");

  nobjects = 0;
  nobjects2 = 0;

  allobjects = malloc_objectobs();

  fprintf (stdout, "aXe_DRZPREP: Checking input files ...");
  for (in_index=0; in_index < input_list->nitems; in_index++)
    {
      // copy the config file name and the grism file
      // name from the struct to loacal variables
      strcpy(conf_file, input_list->axe_items[in_index].config_file);
      strcpy(grism_file, input_list->axe_items[in_index].grism_file);

      fprintf(stdout, "(%i): %s, %s\n", in_index, conf_file, grism_file);

      // construct the full path to the current configuration fille
      build_path (AXE_CONFIG_PATH, conf_file, conf_file_path);
      for (i=0; i<= DRZMAX; i++)
	{
	  for_grism = check_for_grism (conf_file_path,i);
	  if (!for_grism)
	    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			 "aXe_DRZPREP: Beam %c in configuration file %s has no grism dispersion and can not be drizzled.\n",
			 BEAM(i), conf_file_path);
	}

      conf = get_aperture_descriptor (conf_file_path);

      // compose the full name to the grism image
      build_path (AXE_IMAGE_PATH, grism_file, grism_file_path);

      // check whether the grism file exists
      in_file = fopen(grism_file_path, "r");
      if (!in_file)
	aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		     "aXe_DRZPREP: File %s does not exist!\n", grism_file_path);
      fclose (in_file);

      get_extension_numbers(grism_file_path, conf,conf->optkey1,conf->optval1);

      // find the full name to the PET file
      if (bckmode)
	{
	  /* Build object PET file name */
	  replace_file_extension (grism_file, PET_file, ".fits",
				  ".BCK.PET.fits", conf->science_numext);
	      build_path (AXE_OUTPUT_PATH, PET_file, PET_file_path);
	}
      else
	{
	  /* Build object PET file name */
	  replace_file_extension (grism_file, PET_file, ".fits",
				  ".PET.fits", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, PET_file, PET_file_path);
	}

      //  Open the OPET file for reading
      fits_open_file (&PET_ptr, PET_file_path, READONLY, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_DRZPREP: Could not open file: %s\n",
		       PET_file_path);
	}

      if (!bckmode)
	// check whether there is quantitative contamination
	quant_cont = check_quantitative_contamination(PET_ptr);
      else
	quant_cont=1;

      // check whether optimal extraction can be done
      if (opt_extr && !quant_cont)
	aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		     "aXe_DRZPREP: PET file: %s was assembled without quantitative contamination.\n",
		     "Optimal extraction is not possible.", PET_file_path);

      // close the PET file
      fits_close_file (PET_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_DRZPREP: Could not" " close PET file: %s\n", PET_file_path);
	}

      if (bckmode && opt_extr)
	{
	  /* Build object PET file name */
	  replace_file_extension (grism_file, PET_file, ".fits",
				  ".PET.fits", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, PET_file, PET_file_path);

	  //  Open the OPET file for reading
	  fits_open_file (&PET_ptr, PET_file_path, READONLY, &f_status);
	  if (f_status)
	    {
	      ffrprt (stderr, f_status);
	      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			   "aXe_DRZPREP: Could not open file: %s\n",
			   PET_file_path);
	    }

	  // check whether there is quantitative contamination
	  quant_cont = check_quantitative_contamination(PET_ptr);

	  if (!quant_cont)
	    aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			 "aXe_DRZPREP: PET file: %s was assembled without quantitative contamination.\n",
			 "Optimal extraction is not possible.", PET_file_path);

	  // close the PET file
	  fits_close_file (PET_ptr, &f_status);
	  if (f_status)
	    {
	      ffrprt (stderr, f_status);
	      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			   "aXe_DRZPREP: Could not" " close PET file: %s\n", PET_file_path);
	    }
	}

    }
  fprintf (stdout, "Done.\n\n");

  for (i=0; i < input_list->nitems; i++)
    {
      // copy the config file name and the grism file
      // name from the struct to loacal variables
      strcpy(conf_file, input_list->axe_items[i].config_file);
      strcpy(grism_file, input_list->axe_items[i].grism_file);

      build_path (AXE_CONFIG_PATH, conf_file, conf_file_path);
      conf = get_aperture_descriptor (conf_file_path);

      build_path (AXE_IMAGE_PATH, grism_file, grism_file_path);
      get_extension_numbers(grism_file_path, conf,conf->optkey1,conf->optval1);

      if (bckmode){
	/* Build aperture file name */
	replace_file_extension (grism_file, aper_file, ".fits",
				".OAF", conf->science_numext);
	build_path (AXE_OUTPUT_PATH, aper_file, aper_file_path);
      }
      else{
	if (backpet)
	  replace_file_extension (grism_file, aper_file, ".fits",
				  ".OAF", conf->science_numext);
	else
	  replace_file_extension (grism_file, aper_file, ".fits",
				  ".OAF", conf->science_numext);
	build_path (AXE_OUTPUT_PATH, aper_file, aper_file_path);
      }

      fprintf(stdout,
	      "aXe_DRZPREP: Input image file list:    %s\n", list_file_path);
      fprintf(stdout,
	      "aXe_DRZPREP: Input grism image:        %s\n", grism_file_path);
      fprintf(stdout,
	      "aXe_DRZPREP: Input aperture file:      %s\n", aper_file_path);
      fprintf(stdout,
	      "aXe_DRZPREP: Input configuration file: %s\n", conf_file_path);

      obs = load_dummy_observation ();
      /* Loading the object list */
      fprintf (stdout, "aXe_DRZPREP: Loading object aperture list ... ");
      oblist = file_to_object_list_seq (aper_file_path, obs);

      if (oblist != NULL) {
	pixmax = get_npixels (grism_file_path, conf->science_numext);
	fprintf (stdout,"%d objects loaded.\n\n",object_list_size(oblist));
	nobjects2 = add_observation(grism_file_path, conf_file_path,
				    allobjects, nobjects2, oblist,
				    object_list_size(oblist), pixmax,
				    conf->science_numext);
	free_oblist(oblist);
      }
    }

  fprintf (stdout, "aXe_DRZPREP: %i objects in all observations.\n", nobjects2);
  fprintf (stdout, "aXe_DRZPREP: Starting to compute the mean values...");
  f_status =  make_refpoints(conf_file_path, grism_file_path,
			     pixmax, allobjects, nobjects2);
  fprintf (stdout, "Done.\n\n");

  for (in_index=0; in_index < input_list->nitems; in_index++)
    {
      // copy the config file name and the grism file
      // name from the struct to loacal variables
      strcpy(conf_file, input_list->axe_items[in_index].config_file);
      strcpy(grism_file, input_list->axe_items[in_index].grism_file);

      build_path (AXE_CONFIG_PATH, conf_file, conf_file_path);
      conf = get_aperture_descriptor (conf_file_path);

      build_path (AXE_IMAGE_PATH, grism_file, grism_file_path);

      //
      // Start of working on one image
      //
      //
      //

      /* Determine where the various extensions are in the FITS file */
      get_extension_numbers(grism_file_path, conf,conf->optkey1,conf->optval1);
      sky_cps = (double)get_float_from_keyword(grism_file_path, conf->science_numext, "SKY_CPS");
      if (isnan(sky_cps))
	sky_cps = 0.0;
      //
      // try to get the descriptor 'sky_cps' from the 'sci'-extension
      //
      exptime = (double)get_float_from_keyword(grism_file_path, conf->science_numext, conf->exptimekey);
      if (isnan(exptime))
	exptime = (double)get_float_from_keyword(grism_file_path, 1, conf->exptimekey);
      if (isnan(exptime))
	exptime = 1.0;

      if (bckmode)
	{
	  /* Build aperture file name */
	  //	  replace_file_extension (grism_file, aper_file, ".fits",
	  //				  ".BAF", conf->science_numext);
	  replace_file_extension (grism_file, aper_file, ".fits",
				  ".OAF", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, aper_file, aper_file_path);

	  /* Build object PET file name */
	  replace_file_extension (grism_file, PET_file, ".fits",
				  ".BCK.PET.fits", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, PET_file, PET_file_path);

	  if (opt_extr)
	    {
	      /* Build second PET file name */
	      replace_file_extension (grism_file, SEC_file, ".fits",
				      ".PET.fits", conf->science_numext);
	      build_path (AXE_OUTPUT_PATH, SEC_file, SEC_file_path);
	    }
	}
      else
	{
	  /* Build aperture file name */
	  //	  replace_file_extension (grism_file, aper_file, ".fits",
	  //				  ".BAF", conf->science_numext);
	  replace_file_extension (grism_file, aper_file, ".fits",
				  ".OAF", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, aper_file, aper_file_path);

	  /* Build object PET file name */
	  replace_file_extension (grism_file, PET_file, ".fits",
				  ".PET.fits", conf->science_numext);
	  build_path (AXE_OUTPUT_PATH, PET_file, PET_file_path);
	}

      if ( (opt = get_online_option ("outputroot", argc, argv)) ){
	strcpy (outputroot, opt);
      }
      else{
	replace_file_extension (PET_file, outputroot, ".PET.fits", "",-1);
      }

      build_path (AXE_OUTPUT_PATH, outputroot, outputroot_path);
      sprintf(output_path,"%s.DPP.fits",outputroot_path);

      fprintf (stdout, "aXe_DRZPREP: Input PET file name:       %s\n",
	       PET_file_path);
      fprintf (stdout, "aXe_DRZPREP: Input Aperture file name:  %s\n",
	       aper_file_path);
      fprintf (stdout, "aXe_DRZPREP: Name of DPP file :         %s\n",
	       output_path);

      obs = load_dummy_observation ();
      /* Loading the object list */
      fprintf (stdout, "aXe_DRZPREP: Loading object aperture list...");
      oblist = file_to_object_list_seq (aper_file_path, obs);
      fprintf (stdout,"%d objects loaded.\n",object_list_size(oblist));

      //  Open the OPET file for reading
      fits_open_file (&PET_ptr, PET_file_path, READONLY, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_DRZPREP: Could not open file: %s\n",
		       PET_file_path);
	}

      if (!bckmode)
	// check whether there is quantitative contamination
	quant_cont = check_quantitative_contamination(PET_ptr);
      else
	quant_cont=1;

      if (bckmode && opt_extr)
	{
	  //  Open the OPET file for reading
	  fits_open_file (&SEC_ptr, SEC_file_path, READONLY, &f_status);
	  if (f_status)
	    {
	      ffrprt (stderr, f_status);
	      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			   "aXe_DRZPREP: Could not open file: %s\n",
			   SEC_file_path);
	    }
	}

      DPP_ptr = create_FITSimage_opened (output_path, 1);
      /* Copy the header info from the grism image */
      {
	FITScards *cards;
	if (bckmode && opt_extr)
	  cards = get_FITS_cards_opened (SEC_ptr);
	else
	  cards = get_FITS_cards_opened (PET_ptr);
	put_FITS_cards_opened(DPP_ptr,cards);
	free_FITScards(cards);
      }

      i = 0;
      if (oblist!=NULL)
	{
	  while (1)
	    {

	      /* Get the PET for this object */
	      pri_PET = get_ALL_from_next_in_PET(PET_ptr, &aperID, &beamID);
	      if ((aperID==-1) && (beamID==-1)) break;

	      if (bckmode && opt_extr)
		sec_PET = get_ALL_from_next_in_PET(SEC_ptr, &aperID, &beamID);
	      else
		sec_PET = NULL;

	      if (beamID >= 0 && beamID <= DRZMAX){
		//	      if (beamID == 0){
		fprintf (stdout, "aXe_DRZPREP: BEAM_%d%c.", aperID, BEAM(beamID));
		objindex =  find_object_in_object_list(oblist,aperID);

		sprintf (label, "%s.%d%c.ps/CPS", outputroot_path,
			 oblist[objindex]->ID, BEAM (oblist[objindex]->beams[beamID].ID));

		pixel.x = oblist[objindex]->beams[beamID].refpoint.x - conf->refx;
		pixel.y = oblist[objindex]->beams[beamID].refpoint.y - conf->refy;
		disp = get_dispstruct_at_pos(conf_file_path, 1,
					     oblist[objindex]->beams[beamID].ID,pixel);
		trace = oblist[objindex]->beams[beamID].spec_trace;

		outref = get_mean_refpoint(allobjects, nobjects2, oblist[objindex]->ID,
					   &boxwidth, &boxheight, &relx, &rely, &objwidth,
					   &orient, &cdref, &cdscale, &cdmeanscale,
					   &sprefreso, &spreso, &spmeanreso, &trlength);

		minxy_PET = get_minxy_from_PET(pri_PET);
		relx = oblist[objindex]->beams[beamID].refpoint.x - minxy_PET.x;
		rely = oblist[objindex]->beams[beamID].refpoint.y - minxy_PET.y;

		//*************************************************
		// patch to correct the reference point in case
		// that the the trace descritpion
		// does have a non negligeable first order term!
		gaga = oblist[objindex]->beams[beamID].spec_trace->data;
		rely = rely + gaga[1];
		//**************************************************

		outdisp = get_dispstruct_at_pos(conf_file_path, 1,
						  oblist[objindex]->beams[beamID].ID,outref);

		cdcorr = cdscale / cdref;
		spcorr = spreso  / sprefreso;

		// determine the trace length
		// NOTE: putting in this line is wrong, but opens to make
		//       drizzle results as in aXe-1.7
		//trlength = get_beam_trace_length(oblist[objindex]->beams[beamID]);

		drizzcoeffs = get_drizzle_coeffs( disp, trace, boxwidth, boxheight,
						  trlength, relx, rely, conf, orient,
						  outdisp, cdcorr, sprefreso, spmeanreso);

		drizzle_width = cdcorr * get_drizzle_width(oblist[objindex],beamID,trace);
		objwidth      = cdmeanscale / cdref * objwidth;

		refwave_pos = get_refwave_position( disp,  trace, pixel, conf);
		refwave_pos.x = refwave_pos.x - minxy_PET.x;
		refwave_pos.y = refwave_pos.y - minxy_PET.y;

		trlength = gsl_matrix_get(drizzcoeffs, 0,10);
		dimension =  get_drzprep_dim(pri_PET, oblist[objindex]->beams[beamID].width,
					     boxwidth, boxheight);
		{
		  px_point tmp_in;
		  px_point tmp_out;

		  d_point new_pos;

		  tmp_in.x = dimension.xsize;
		  tmp_in.y = dimension.ysize;

		  tmp_out.x = (int)trlength;
		  tmp_out.y = 2*(int)ceil(objwidth) + 10;
		  //		    gsl_matrix_fprintf (stdout, drizzcoeffs, "%f");

		  gsl_matrix_set(drizzcoeffs, 0,10,0.0);
		  new_pos = get_drz_position_free(refwave_pos, drizzcoeffs, tmp_in, tmp_out);
		}

		drzprep_stamps = stamp_img_drzprep(opt_extr, pri_PET, sec_PET,
						   oblist[objindex]->beams[beamID].width,
						   -1000000.0, quant_cont, dimension,
						   drizzcoeffs, exptime, sky_cps,
						   (double)conf->rdnoise, bckmode);

		// store the count-extension
		sprintf (label, "BEAM_%d%c",oblist[objindex]->ID,
			 BEAM (oblist[objindex]->beams[beamID].ID));
		gsl_to_FITSimage_opened (drzprep_stamps->counts, DPP_ptr ,0,label);
		cards = drzinfo_to_FITScards(oblist[objindex],beamID,
					     outref, conf, drizzcoeffs,
					     trlength, relx, rely,objwidth,
					     refwave_pos, sky_cps,
					     drizzle_width, cdref, spcorr);
		put_FITS_cards_opened(DPP_ptr,cards);

		// store the error-extension
		sprintf (label, "ERR_%d%c",oblist[objindex]->ID,
			 BEAM (oblist[objindex]->beams[beamID].ID));
		gsl_to_FITSimage_opened (drzprep_stamps->error, DPP_ptr ,0,label);
		put_FITS_cards_opened(DPP_ptr,cards);

		// store the contamination-extension
		sprintf (label, "CONT_%d%c",oblist[objindex]->ID,
			 BEAM (oblist[objindex]->beams[beamID].ID));
		gsl_to_FITSimage_opened (drzprep_stamps->cont, DPP_ptr ,0,label);
		put_FITS_cards_opened(DPP_ptr,cards);

		//		  if (opt_extr)
		if (drzprep_stamps->model)
		  {
		    // store the model-extension
		    sprintf (label, "MOD_%d%c",oblist[objindex]->ID,
			     BEAM (oblist[objindex]->beams[beamID].ID));
		    gsl_to_FITSimage_opened (drzprep_stamps->model, DPP_ptr ,0,label);
		    put_FITS_cards_opened(DPP_ptr,cards);
		  }

		if (drzprep_stamps->vari)
		  {
		    // store the variance extension
		    sprintf (label, "VAR_%d%c",oblist[objindex]->ID,
			     BEAM (oblist[objindex]->beams[beamID].ID));
		    gsl_to_FITSimage_opened (drzprep_stamps->vari, DPP_ptr ,0,label);
		    put_FITS_cards_opened(DPP_ptr,cards);
		  }

		// release memory
		free_drzprep(drzprep_stamps);
		free_FITScards(cards);
		free_dispstruct(disp);
		free_dispstruct(outdisp);
		gsl_matrix_free(drizzcoeffs);

		fprintf (stdout, " Done.\n");
		i++;
	      }

	      if (pri_PET!=NULL) free(pri_PET);
	      if (sec_PET!=NULL) free(sec_PET);
	    }
	}
      if (oblist!=NULL) free_oblist (oblist);

      fits_close_file (DPP_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_DRZPREP: Could not" " close DPP file: %s\n", output_path);
	}

      fits_close_file (PET_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_DRZPREP: Could not" " close PET file: %s\n", PET_file_path);
	}

      if (bckmode && opt_extr)
	{
	  fits_close_file (SEC_ptr, &f_status);
	  if (f_status)
	    {
	      ffrprt (stderr, f_status);
	      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			   "aXe_DRZPREP: Could not" " close PET file: %s\n", SEC_file_path);
	    }
	}

      fprintf (stdout, "aXe_DRZPREP: %s Done...\n\n", output_path);
      //
      // End of working on one image
      //
      //
      //

    }

  free_axe_inputs(input_list);
  free_objectobs(allobjects);
  exit (0);
}


/******************************************************************************
*
* IMD -- The ITAP Molecular Dynamics Program
*
* Copyright 1996-2007 Institute for Theoretical and Applied Physics,
* University of Stuttgart, D-70550 Stuttgart
*
******************************************************************************/

/******************************************************************************
*
* imd.h -- Header file for all modules of IMD
*
******************************************************************************/

/******************************************************************************
* $Revision$
* $Date$
******************************************************************************/

/* C stuff */
#ifdef MEMALIGN
#define  _XOPEN_SOURCE 600
#endif
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>

//MYMOD
#include <stdbool.h>
#include "nn_interpol/nn.h"
#include "nn_interpol/delaunay.h"
//ENDOF MYMOD
//
//
#ifdef CBE
#define USE_WALLTIME
#endif

/* support for timers */
#ifndef MPI
#if defined(USE_RUSAGE) || defined(USE_WALLTIME)
#include <sys/time.h>
#include <sys/resource.h>
#else
#include <sys/times.h>
#include <sys/types.h>
#endif
#endif

/* Machine specific headers */
#if defined(MPI) || defined(NEB)
#include <mpi.h>
#ifdef MPE
#include <mpe.h>
#endif
#endif
#ifdef OMP
#include <omp.h>
#endif

/* FFT for diffraction patterns */
#ifdef DIFFPAT
#include <fftw3.h>
#endif

/* IMD version */
#include "version.h"

/* Configuration */
#include "config.h"

/* Data types */
#include "types.h"

/* Some constants */
#include "constants.h"

/* Some makros */
#include "makros.h"

/* Function Prototypes */
#include "prototypes.h"

/* Global Variables */
#include "globals.h"

/************
 * MY MOD   *
 ************/
#include <complex.h>

#ifdef COLRAD

// #include <gsl/gsl_sf_expint.h>       //FUER EXPONENTIAL INTEGRAL
#include <cvode/cvode.h>             /* prototypes for CVODE fcts., consts. */
#include <nvector/nvector_serial.h>  /* serial N_Vector types, fcts., macros */
#include <sunmatrix/sunmatrix_dense.h>
//#include <sundials/sundials_types.h> /* definition of type realtype */
#include "sunnonlinsol/sunnonlinsol_newton.h"
#define LAPACK  /* LAPACK SOLVER MULTO BENE*/
#ifdef LAPACK
#include <sunlinsol/sunlinsol_lapackdense.h>
#else
#include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver      */
#endif
#endif //ifdef COLRAD


#ifndef GLOBALS_H
#define GLOBALS_H

#define PROG_NAME "WVT ICs"
#define VERSION "1.0"

/* C std lib */
#include <stdlib.h>             // system
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <limits.h>
#include <complex.h>

/* GNU Scientifc Library */
#include <gsl/gsl_math.h>
#include <gsl/gsl_const_cgsm.h>
#include <gsl/gsl_const_num.h>
#include <gsl/gsl_rng.h>

#include <omp.h>

/* Global Prototypes */
#include "macro.h"
#include "peano.h"
#include "proto.h"

/* To eat PNG density files */
#ifdef EAT_PNG
#include "png.h"
#include "zlib.h"
#endif

#include "peanowalk.h"

/* Code parameters */

#define CHARBUFSIZE 512        // For any char buffer !
#define MAXTAGS 300            // In parameter file

#ifdef TWO_DIM
#ifdef SPH_CUBIC_SPLINE

#define DESNNGB 14         // SPH kernel weighted number of neighbours
#define NNGBDEV 0.01       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*16)  // size of neighbour list

#else

#ifdef SPH_WC2

#define DESNNGB 16         // SPH kernel weighted number of neighbours
#define NNGBDEV 0.01       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*8)  // size of neighbour list

#else

#define DESNNGB 44        // SPH kernel weighted number of neighbours
#define NNGBDEV 0.01       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*8)  // size of neighbour list

#endif // SPH_WC2
#endif // SPH_CUBIC_SPLINE
#else
#ifdef SPH_CUBIC_SPLINE

#define DESNNGB 50         // SPH kernel weighted number of neighbours
#define NNGBDEV 0.05       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*16)  // size of neighbour list

#else

#ifdef SPH_WC2

#define DESNNGB 64         // SPH kernel weighted number of neighbours
#define NNGBDEV 0.05       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*8)  // size of neighbour list

#else

#define DESNNGB 295        // SPH kernel weighted number of neighbours
#define NNGBDEV 0.05       // error tolerance in SPH kernel weighted neighb.
#define NGBMAX (DESNNGB*8)  // size of neighbour list

#endif // SPH_WC2
#endif // SPH_CUBIC_SPLINE
#endif // TWO_DIM

/* mathematical constants */
#define pi             M_PI
#define sqrt2        M_SQRT2
#define sqrt3       1.73205080756887719
#define fourpithird 4.18879032135009765

extern const double ULength, UMass, UVel; // Standard Gadget units (setup.c)

extern struct OpenMP_infos {
    int NThreads;          // Number of openMP threads
    int ThreadID;          // Thread ID of this thread
    unsigned short Seed[3]; // random number seed: erand48(Omp.Seed)
} Omp;
#pragma omp threadprivate(Omp)

extern struct Parameters {
    int Npart;
    int Maxiter;
    double MpsFraction; // move this fraction of the mean particle sep
    double StepReduction; // force convergence at this rate
    double BiasCorrection; // density bias correction factor
    double LimitMps[4]; // Convergence criterium for particle movement
    double MoveFractionMin; // move at least this fraction of particles during redistribution steps
    double MoveFractionMax; // move at most this fraction of particles during redistribution steps
    double ProbesFraction;
    int RedistributionFrequency;
    int LastMoveStep;
    int Problem_Flag;
    int Problem_Subflag;
} Param;

extern struct ProblemParameters {
    char Name[CHARBUFSIZE];
    double Mpart;
    double Boxsize[3]; // [0] is ALWAYS the largest one !
    double Rho_Max;
    bool Periodic[3];
} Problem;

#ifdef EAT_PNG
extern struct ImageProperties {
    char Name[CHARBUFSIZE];
    float *Density;
    int Xpix;
    int Ypix;
    int Zpix;
} Image;
#endif

extern struct ParticleData {
    double Pos[3];
    double Vel[3];
    int32_t ID;
    int Type;
    peanoKey Key;
    int Tree_Parent;
    bool Redistributed;
} *P;

extern struct GasParticleData {
    float U;
    float Rho;
    float Hsml;
    float VarHsmlFac;
    float ID;
    float Rho_Model;
    float Bfld[3];
} *SphP;

extern float ( *Density_Func_Ptr ) ( const int, const double );
extern float ( *U_Func_Ptr ) ( const int );
extern void ( *Velocity_Func_Ptr ) ( const int, float * );
extern void ( *Magnetic_Field_Func_Ptr ) ( const int, float * );
extern void ( *PostProcessing_Func_Ptr ) ();

double G; // gravitational constant in code units

#endif


#ifndef ALLVARS_H
#define ALLVARS_H

#include <stdio.h>
#include <gsl/gsl_rng.h>
#include "core_simulation.h"

#define ABORT(sigterm)                                                  \
do {                                                                \
  printf("Error in file: %s\tfunc: %s\tline: %i\n", __FILE__, __FUNCTION__, __LINE__); \
  myexit(sigterm);                                                \
} while(0)

#define  STEPS 10         // Number of integration intervals between two snapshots
#define  MAXGALFAC 1
#define  ALLOCPARAMETER 10.0
#define  MAX_NODE_NAME_LEN 50
#define  ABSOLUTEMAXSNAPS 1000

#define  GRAVITY     6.672e-8
#define  SOLAR_MASS  1.989e33
#define  SOLAR_LUM   3.826e33
#define  RAD_CONST   7.565e-15
#define  AVOGADRO    6.0222e23
#define  BOLTZMANN   1.3806e-16
#define  GAS_CONST   8.31425e7
#define  C           2.9979e10
#define  PLANCK      6.6262e-27
#define  CM_PER_MPC  3.085678e24
#define  PROTONMASS  1.6726e-24
#define  HUBBLE      3.2407789e-18   /* in h/sec */

#define  SEC_PER_MEGAYEAR   3.155e13
#define  SEC_PER_YEAR       3.155e7

// This structure contains the properties that are output
struct GALAXY_OUTPUT
{
  int   SnapNum;
  int   Type;

  long long   GalaxyIndex;
  long long   CentralGalaxyIndex;
  int   SAGEHaloIndex;
  int   SAGETreeIndex;
  long long   SimulationHaloIndex;

  int   mergeType;  //0=none; 1=minor merger; 2=major merger; 3=disk instability; 4=disrupt to ICS
  int   mergeIntoID;
  int   mergeIntoSnapNum;
  float dT;

  // (sub)halo properties
  float Pos[3];
  float Vel[3];
  float Spin[3];
  int   Len;
  float Mvir;
  float CentralMvir;
  float Rvir;
  float Vvir;
  float Vmax;
  float VelDisp;

  // baryonic reservoirs
  float ColdGas;
  float StellarMass;
  float BulgeMass;
  float HotGas;
  float EjectedMass;
  float BlackHoleMass;
  float ICS;

  // metals
  float MetalsColdGas;
  float MetalsStellarMass;
  float MetalsBulgeMass;
  float MetalsHotGas;
  float MetalsEjectedMass;
  float MetalsICS;

  // to calculate magnitudes
  float SfrDisk;
  float SfrBulge;
  float SfrDiskZ;
  float SfrBulgeZ;

  // misc
  float DiskScaleRadius;
  float Cooling;
  float Heating;
  float QuasarModeBHaccretionMass;
  float TimeOfLastMajorMerger;
  float TimeOfLastMinorMerger;
  float OutflowRate;

  // infall properties
  float infallMvir;
  float infallVvir;
  float infallVmax;
};

// This structure contains the properties used within the code
struct GALAXY
{
  int   SnapNum;
  int   Type;

  int   GalaxyNr;
  int   CentralGal;
  int   HaloNr;
  long long MostBoundID;

  int   mergeType;  //0=none; 1=minor merger; 2=major merger; 3=disk instability; 4=disrupt to ICS
  int   mergeIntoID;
  int   mergeIntoSnapNum;
  float dT;

  // (sub)halo properties
  float Pos[3];
  float Vel[3];
  int   Len;
  float Mvir;
  float deltaMvir;
  float CentralMvir;
  float Rvir;
  float Vvir;
  float Vmax;

  // baryonic reservoirs
  float ColdGas;
  float StellarMass;
  float BulgeMass;
  float HotGas;
  float EjectedMass;
  float BlackHoleMass;
  float ICS;

  // metals
  float MetalsColdGas;
  float MetalsStellarMass;
  float MetalsBulgeMass;
  float MetalsHotGas;
  float MetalsEjectedMass;
  float MetalsICS;

  // to calculate magnitudes
  float SfrDisk[STEPS];
  float SfrBulge[STEPS];
  float SfrDiskColdGas[STEPS];
  float SfrDiskColdGasMetals[STEPS];
  float SfrBulgeColdGas[STEPS];
  float SfrBulgeColdGasMetals[STEPS];

  // misc
  float DiskScaleRadius;
  float MergTime;
  double Cooling;
  double Heating;
  float r_heat;
  float QuasarModeBHaccretionMass;
  float TimeOfLastMajorMerger;
  float TimeOfLastMinorMerger;
  float OutflowRate;
  float TotalSatelliteBaryons;

  // infall properties
  float infallMvir;
  float infallVvir;
  float infallVmax;
}
*Gal, *HaloGal;

// auxiliary halo data
struct halo_aux_data
{
  int DoneFlag;
  int HaloFlag;
  int NGalaxies;
  int FirstGalaxy;
}
*HaloAux;

extern int    FirstFile;    // first and last file for processing
extern int    LastFile;

extern int    Ntrees;      // number of trees in current file
extern int    NumGals;     // Total number of galaxies stored for current tree
extern int    MaxGals;     // Maximum number of galaxies allowed for current tree
extern int    FoF_MaxGals;

extern int    GalaxyCounter;     // unique galaxy ID for main progenitor line in tree

extern int    LastSnapShotNr;

extern char   OutputDir[512];
extern char   FileNameGalaxies[512];
extern char   TreeName[512];
extern char   SimulationDir[512];
extern char   FileWithSnapList[512];

extern int    TotHalos;
extern int    TotGalaxies[ABSOLUTEMAXSNAPS];
extern int    *TreeNgals[ABSOLUTEMAXSNAPS];

extern int    *FirstHaloInSnap;

extern int    *TreeNHalos;
extern int    *TreeFirstHalo;

#ifdef MPI
extern int ThisTask, NTask, nodeNameLen;
extern char *ThisNode;
#endif

extern double Omega;
extern double OmegaLambda;
extern double PartMass;
extern double Hubble_h;
extern double EnergySNcode, EnergySN;
extern double EtaSNcode, EtaSN;

// recipe flags
extern int    ReionizationOn;
extern int    SupernovaRecipeOn;
extern int    DiskInstabilityOn;
extern int    AGNrecipeOn;
extern int    SFprescription;

// recipe parameters
extern double RecycleFraction;
extern double Yield;
extern double FracZleaveDisk;
extern double ReIncorporationFactor;
extern double ThreshMajorMerger;
extern double BaryonFrac;
extern double SfrEfficiency;
extern double FeedbackReheatingEpsilon;
extern double FeedbackEjectionEfficiency;
extern double RadioModeEfficiency;
extern double QuasarModeEfficiency;
extern double BlackHoleGrowthRate;
extern double Reionization_z0;
extern double Reionization_zr;
extern double ThresholdSatDisruption;

extern double UnitLength_in_cm,
  UnitTime_in_s,
  UnitVelocity_in_cm_per_s,
  UnitMass_in_g,
  RhoCrit,
  UnitPressure_in_cgs,
  UnitDensity_in_cgs,
  UnitCoolingRate_in_cgs,
  UnitEnergy_in_cgs,
  UnitTime_in_Megayears,
  G,
  Hubble,
  a0, ar;

extern int    ListOutputSnaps[ABSOLUTEMAXSNAPS];

extern double ZZ[ABSOLUTEMAXSNAPS];
extern double AA[ABSOLUTEMAXSNAPS];
extern double *Age;

extern int    MAXSNAPS;
extern int    NOUT;
extern int    Snaplistlen;

extern gsl_rng *random_generator;

extern int TreeID;
extern int FileNum;

#endif  // #ifndef ALLVARS_H


// Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
// LICENCE available at https://www.gnu.org/software/gsl/doc/html/gpl.html
#ifndef TRIDIAG_H_
#define TRIDIAG_H_

#include <gsl/gsl_errno.h>

#include <cstdlib>
#include <iostream>
#include <vector>

namespace GSL {

int solve_tridiag_nonsym(const std::vector<double>& diag, const std::vector<double>& abovediag,
                         const std::vector<double>& belowdiag, const std::vector<double>& rhs, std::vector<double>& x,
                         size_t N);

int gsl_linalg_solve_tridiag(const std::vector<double>& diag, const std::vector<double>& abovediag,
                             const std::vector<double>& belowdiag, const std::vector<double>& rhs,
                             std::vector<double>& solution);

}  // namespace GSL

#endif  // TRIDIAG_H_