#include <geometry/basic_c.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <math.h>
#include <stdio.h>

// author:    Andrew Liew <liew@arch.ethz.ch>
// copyright: Copyright 2018, BLOCK Research Group - ETH Zurich
// license:   MIT License
// email:     liew@arch.ethz.ch

void drx_solver_c(
    double tol,    // Tolerance value.
    int steps,     // Maximum number of steps.
    int summary,   // Print summary at end (1:yes or 0:no).
    int m,         // Number of elements.
    int n,         // Number of nodes.
    int *u,        // Element start node.
    int *v,        // Element end node.
    double *X,     // Nodal co-ordinates.
    double *f0,    // Initial edge forces.
    double *l0,    // Initial edge lengths.
    double *k0,    // Initial edge axial stiffnesses.
    int *ind_c,    // Indices of compression only edges.
    int *ind_t,    // Indices of tension only edges.
    int ind_c_n,   // Length of ind_c.
    int ind_t_n,   // Length of ind_t.
    double *B,     // Constraint conditions Bx, By, Bz.
    double *P,     // Nodal loads Px, Py, Pz.
    double *S,     // Shear forces Sx, Sy, Sz.
    int *rows,     // Rows of Ct.
    int *cols,     // Columns of Ct.
    double *vals,  // Values of Ct.
    int nv,        // Length of rows/cols/vals.
    double *M,     // Mass matrix.
    double factor, // Convergence factor.
    double *V,     // Nodal velocities Vx, Vy, Vz.
    int *inds,     // Indices of beam element start nodes.
    int *indi,     // Indices of beam element intermediate nodes.
    int *indf,     // Indices of beam element finish nodes beams.
    double *EIx,   // Nodal EIx flexural stiffnesses.
    double *EIy,   // Nodal EIy flexural stiffnesses.
    int beams,     // Includes beams:1 or not:0.
    int nb)        // Length of inds/indi/indf.

    {

    int a;
    int b;
    int c;
    int i;
    int j;
    int k;
    int nans[nb];
    int ts;

    double alpha;
    double f[m];
    double fx[m];
    double fy[m];
    double fz[m];
    double frx[n];
    double fry[n];
    double frz[n];
    double kappa;
    double l;
    double La;
    double Lb;
    double Lc;
    double LQn;
    double Lmu;
    double Lc1;
    double Lc2;
    double Mi;
    double Ms;
    double q;
    double res;
    double Rx;
    double Ry;
    double Rz;
    double Rn;
    double Uo;
    double Un;
    double xd;
    double yd;
    double zd;

    gsl_vector *Xs = gsl_vector_alloc(3);
    gsl_vector *Xi = gsl_vector_alloc(3);
    gsl_vector *Xf = gsl_vector_alloc(3);
    gsl_vector *Qa = gsl_vector_alloc(3);
    gsl_vector *Qb = gsl_vector_alloc(3);
    gsl_vector *Qc = gsl_vector_alloc(3);
    gsl_vector *Qn = gsl_vector_alloc(3);
    gsl_vector *mu = gsl_vector_alloc(3);
    gsl_vector *ex = gsl_vector_alloc(3);
    gsl_vector *ey = gsl_vector_alloc(3);
    gsl_vector *ez = gsl_vector_alloc(3);
    gsl_vector *K  = gsl_vector_alloc(3);
    gsl_vector *Kx = gsl_vector_alloc(3);
    gsl_vector *Ky = gsl_vector_alloc(3);
    gsl_vector *Mc = gsl_vector_alloc(3);
    gsl_vector *ua = gsl_vector_alloc(3);
    gsl_vector *ub = gsl_vector_alloc(3);
    gsl_vector *c1 = gsl_vector_alloc(3);
    gsl_vector *c2 = gsl_vector_alloc(3);
    gsl_matrix *Sa = gsl_matrix_alloc(nb, 3);
    gsl_matrix *Sb = gsl_matrix_alloc(nb, 3);

    ts  = 0;
    Uo  = 0.;
    res = 1000. * tol;

    while (ts <= steps && res > tol)
    {
        #pragma omp parallel for private(i,j,k,xd,yd,zd,l,q)
        for (i = 0; i < m; i++)
        {
            j = 3 * v[i];
            k = 3 * u[i];
            xd = X[j + 0] - X[k + 0];
            yd = X[j + 1] - X[k + 1];
            zd = X[j + 2] - X[k + 2];
            l  = gsl_hypot3(xd, yd, zd);
            f[i] = f0[i] + k0[i] * (l - l0[i]);
            q = f[i] / l;
            fx[i] = xd * q;
            fy[i] = yd * q;
            fz[i] = zd * q;
        }

        if (ind_t_n > 0)
        {
            #pragma omp parallel for private(i)
            for (i = 0; i < ind_t_n; i++)
            {
                if (f[i] < 0)
                {
                    fx[i] = 0;
                    fy[i] = 0;
                    fz[i] = 0;
                }
            }
        }

        if (ind_c_n > 0)
        {
            #pragma omp parallel for private(i)
            for (i = 0; i < ind_c_n; i++)
            {
                if (f[i] > 0)
                {
                    fx[i] = 0;
                    fy[i] = 0;
                    fz[i] = 0;
                }
            }
        }

        if (beams)
        {
            #pragma omp parallel for private(i,j)
            for (i = 0; i < n; i++)
            {
                j = i * 3;
                S[j + 0] = 0.;
                S[j + 1] = 0.;
                S[j + 2] = 0.;
            }

            for (i = 0; i < nb; i++)
            {
                a = inds[i] * 3;
                b = indi[i] * 3;
                c = indf[i] * 3;

                vector_from_pointer(&X[a], Xs);
                vector_from_pointer(&X[b], Xi);
                vector_from_pointer(&X[c], Xf);
                subtract_vectors(Xi, Xs, Qa);
                subtract_vectors(Xf, Xi, Qb);
                subtract_vectors(Xf, Xs, Qc);
                cross_vectors(Qa, Qb, Qn);
                subtract_vectors(Xf, Xs, mu);
                scale_vector(mu, 0.5);

                La  = length_vector(Qa);
                Lb  = length_vector(Qb);
                Lc  = length_vector(Qc);
                LQn = length_vector(Qn);
                Lmu = length_vector(mu);
                alpha = acos((gsl_pow_2(La) + gsl_pow_2(Lb) - gsl_pow_2(Lc)) / (2. * La * Lb));
                kappa = 2. * sin(alpha) / Lc;

                gsl_vector_memcpy(ex, Qn);
                gsl_vector_memcpy(ez, mu);
                scale_vector(ex, 1./LQn);
                scale_vector(ez, 1./Lmu);
                cross_vectors(ez, ex, ey);
                gsl_vector_memcpy(K, Qn);
                scale_vector(K, kappa/LQn);
                gsl_vector_memcpy(Kx, ex);
                gsl_vector_memcpy(Ky, ey);
                scale_vector(Kx, dot_vectors(K, ex));
                scale_vector(Ky, dot_vectors(K, ey));
                scale_vector(Kx, EIx[i]);
                scale_vector(Ky, EIy[i]);
                add_vectors(Kx, Ky, Mc);
                cross_vectors(Mc, Qa, ua);
                cross_vectors(Mc, Qb, ub);
                normalize_vector(ua);
                normalize_vector(ub);
                cross_vectors(Qa, ua, c1);
                cross_vectors(Qb, ub, c2);

                Lc1 = length_vector(c1);
                Lc2 = length_vector(c2);
                Ms  = length_vector_squared(Mc);
                scale_vector(ua, Ms * Lc1 / (La * dot_vectors(Mc, c1)));
                scale_vector(ub, Ms * Lc2 / (Lb * dot_vectors(Mc, c2)));

                for (j = 0; j < 3; j++)
                {
                    gsl_matrix_set(Sa, i, j, gsl_vector_get(ua, j));
                    gsl_matrix_set(Sb, i, j, gsl_vector_get(ub, j));
                }
            }

            #pragma omp parallel for private(i,j)
            for (i = 0; i < nb; i++)
            {
                nans[i] = 0;
                for (j = 0; j < 3; j++)
                {
                    if (gsl_isnan(gsl_matrix_get(Sa, i, j)) || gsl_isnan(gsl_matrix_get(Sb, i, j)))
                    {
                        nans[i] = 1;
                        break;
                    }
                }
            }

            for (i = 0; i < nb; i++)
            {
                a = inds[i] * 3;
                b = indi[i] * 3;
                c = indf[i] * 3;

                if (nans[i] == 0)
                {
                    for (j = 0; j < 3; j++)
                    {
                        S[a + j] += gsl_matrix_get(Sa, i, j);
                        S[b + j] -= (gsl_matrix_get(Sa, i, j) + gsl_matrix_get(Sb, i, j));
                        S[c + j] += gsl_matrix_get(Sb, i, j);
                    }
                }
            }
        }

        #pragma omp parallel for private(i)
        for (i = 0; i < n; i++)
        {
            frx[i] = 0;
            fry[i] = 0;
            frz[i] = 0;
        }

        for (i = 0; i < nv; i++)
        {
            frx[rows[i]] += vals[i] * fx[cols[i]];
            fry[rows[i]] += vals[i] * fy[cols[i]];
            frz[rows[i]] += vals[i] * fz[cols[i]];
        }

        Un = 0.;
        Rn = 0.;

        #pragma omp parallel for private(i,j,Rx,Ry,Rz,Mi) reduction(+:Un,Rn)
        for (i = 0; i < n; i++)
        {
            j = 3 * i;
            Rx = (P[j + 0] - S[j + 0] - frx[i]) * B[j + 0];
            Ry = (P[j + 1] - S[j + 1] - fry[i]) * B[j + 1];
            Rz = (P[j + 2] - S[j + 2] - frz[i]) * B[j + 2];
            Rn += gsl_hypot3(Rx, Ry, Rz);
            Mi = M[i] * factor;
            V[j + 0] += Rx / Mi;
            V[j + 1] += Ry / Mi;
            V[j + 2] += Rz / Mi;
            Un += Mi * (gsl_pow_2(V[j + 0]) + gsl_pow_2(V[j + 1]) + gsl_pow_2(V[j + 2]));
        }

        if (Un < Uo)
        {
            #pragma omp parallel for private(i,j)
            for (i = 0; i < n; i++)
            {
                j = 3 * i;
                V[j + 0] = 0.;
                V[j + 1] = 0.;
                V[j + 2] = 0.;
            }
        }

        Uo = Un;

        #pragma omp parallel for private(i,j)
        for (i = 0; i < n; i++)
        {
            j = 3 * i;
            X[j + 0] += V[j + 0];
            X[j + 1] += V[j + 1];
            X[j + 2] += V[j + 2];
        }

        res = Rn / n;
        ts++;

    }

    if (summary == 1)
    {
        printf("Step: %i, Residual: %f\n", ts - 1, res);
    }

}


#pragma once

#include "BasicBox.h"
#include "BasicTypes.h"
#include "BoxTypes.h"
#include "General.h"
#include "NodeDim.h"
#include "NodeListDim.h"
#include <gsl.h>

namespace formulae{ namespace node {

/**
 *
 * @param width
 * @param node center node -> reference point, (must be Box0 or Rule !!!)
 * @param upList above center, enumerated from bottom to top
 * @param dnList below center, enumerated from top to bottom
 * @return
 */
template<typename VList1=vlist_t, typename VList2=vlist_t, typename Node=NodeRef>
box_t makeVBox(dist_t  width, Node && node,
               VList1 && upList, VList2 && dnList) {

  using general::revAppend;
  using general::transfer;

  auto h = vlistVsize(upList) + node.height();
  auto d = vlistVsize(dnList) + node.depth();
  std::remove_reference_t<VList1> emptyList;
  auto nodeList = revAppend(std::forward<VList1>(upList), emptyList);
  nodeList.push_back(std::forward<Node>(node));
  transfer(nodeList, std::forward<VList2>(dnList));

  return vbox(dim_t{width, h, d}, std::move(nodeList));
}
/**
 *
 * @param width
 * @param center center node -> reference point,
 * @param upList above center, enumerated from bottom to top
 * @return
 */
template <typename Box = box_t, typename VList = vlist_t>
box_t upVBox(dist_t  width, Box && center, VList&& upList) {
  std::remove_reference_t<VList> emptyList;
  return makeVBox(width, NodeRef{Box0(std::forward<Box>(center))},
                  std::forward<VList>(upList), emptyList);
}

/**
 *
 * @param width
 * @param center center node -> reference point
 * @param dnList below center, enumerated from top to bottom
 * @return
 */
template <typename Box = box_t, typename VList = vlist_t>
box_t dnVBox(dist_t  width, Box && center, VList && dnList) {
  std::remove_reference_t<VList> emptyList;
  return makeVBox(width, Box0(std::forward<Box>(center)), emptyList, std::forward<VList>(dnList));
}

/**
 *
 * @param n1
 * @param dist1 distance from bottom of n1 to baseline
 * @param dist  vertical distance between n1 and n2
 * @param n2
 * @return
 */
template <typename Node = NodeRef, typename DownNode=NodeRef>
NodeRef above(Node && n1, dist_t  dist1, dist_t  dist, DownNode && n2) {
  auto w = std::max(n1.vwidth(), n2.vwidth());
  auto h = n1.vsize() + dist1;
  auto d = n2.vsize() + dist - dist1;
  using E = std::remove_reference_t<Node>;
  list_t<E> nodeList;
  nodeList.push_back(std::forward<Node>(n1));
  nodeList.push_back(Kern_t::create(dist));
  nodeList.push_back(std::forward<DownNode>(n2));
  return Box0(vbox(dim_t{w, h, d}, std::move(nodeList)));
};

}
}


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

int main()
{
	printf("PI: %lf\n", M_PI);
}


#include <math.h>
#include <gsl/gsl_linalg.h>
#include "getNextIterate.h"

void getNextIterate(const grid *grd, const bool eos_func,
		    const wksp *w
#if AA_M > 0
		    , const unsigned long itCount, const long nHist,
		    double *residMax
#endif
		    ) {

  long i;
#if AA_M > 0
  long j, mk, colptr;
  gsl_matrix *resid;
  gsl_vector *tau, *llsresid, *aa_wgts, *constraint;
  gsl_vector_view constraint_view;
#endif

#if AA_M == 0
/* No Anderson acceleration, so just move temporaries into next
   guess */
  for (i=0; i<grd->nr; i++) {
    w->colNew[i] = w->colTmp[i];
    w->presNew_g[i+1] = gsl_vector_get(w->presTmp_g, i+1);
    if (eos_func==1)
      w->eIntNew[i] = w->eIntTmp[i];
  }
#else

  /* Anderson acceleration code */

  /* Set up pointers */
  mk = itCount < AA_M+1 ? itCount : AA_M+1;
  colptr = (itCount - 1) % (AA_M+1);

  /* Store the current guesses in the history array */
  for (i=0; i<grd->nr; i++) {
    w->colHist[i + colptr*grd->nr] = w->colTmp[i];
    w->presHist[i + colptr*grd->nr] = gsl_vector_get(w->presTmp_g, i+1);
    if (eos_func)
      w->eIntHist[i + colptr*grd->nr] = w->eIntTmp[i];
  }

  /* Compute residuals and store them in the residual matrix */
  for (i=0; i<grd->nr; i++) {
    w->colResid[i + colptr*grd->nr] =
      (w->colHist[i+colptr*grd->nr]-w->colNew[i]) /
      w->colHist[i+colptr*grd->nr];
    if (fabs(w->colResid[i + colptr*grd->nr]) > *residMax) {
      *residMax = fabs(w->colResid[i + colptr*grd->nr]);
    }
    w->presResid[i + colptr*grd->nr] =
      (w->presHist[i+colptr*grd->nr]-w->presNew_g[i+1]) /
      w->presHist[i+colptr*grd->nr];
    if (fabs(w->presResid[i + colptr*grd->nr]) > *residMax) {
      *residMax = fabs(w->presResid[i + colptr*grd->nr]);
    }
    if (eos_func) {
      w->eIntResid[i + colptr*grd->nr] =
	(w->eIntHist[i+colptr*grd->nr]-w->eIntNew[i]) /
	w->eIntHist[i+colptr*grd->nr];
      if (fabs(w->eIntResid[i + colptr*grd->nr]) > *residMax)
	*residMax = fabs(w->eIntResid[i + colptr*grd->nr]);
    }
  }

  /* If we're in the first iteration, set move temporaries to next
     guess */
  if (itCount == 1) {
    for (i=0; i<grd->nr; i++) {
      w->colNew[i] = w->colHist[i+colptr*grd->nr];
      w->presNew_g[i+1] = w->presHist[i+colptr*grd->nr];
      if (eos_func)
	w->eIntNew[i] = w->eIntHist[i+colptr*grd->nr];
    }
  } else {
    /* On subsequent iterations, solve the linear least squares
       optimization problem to get a weighted sum of previous
       guesses for the new guess */

    /* Allocate workspace for this iteration */
    resid = gsl_matrix_alloc(nHist+1, mk);
    llsresid = gsl_vector_alloc(nHist+1);
    aa_wgts = gsl_vector_alloc(mk);
    if (nHist+1 < mk) {
      tau = gsl_vector_alloc(nHist+1);
    } else {
      tau = gsl_vector_alloc(mk);
    }

    /* Build matrix of residuals */
    for (i=0; i<grd->nr; i++) {
      for (j=0; j<mk; j++) {
	gsl_matrix_set(resid, i, j, w->colResid[i+j*grd->nr]);
	gsl_matrix_set(resid, i+grd->nr, j, w->presResid[i+j*grd->nr]);
	if (eos_func)
	  gsl_matrix_set(resid, i+2*grd->nr, j, w->eIntResid[i+j*grd->nr]);
      }
    }

    /* Set up constraints on coefficients */
    for (j=0; j<mk; j++) gsl_matrix_set(resid, nHist, j, 1.0e3*(*residMax));
    constraint_view = gsl_vector_subvector(w->constraint, 0, nHist+1);
    constraint = &(constraint_view.vector);
    gsl_vector_set(constraint, nHist, 1e3*(*residMax));

    /* Solve linear least squares system by QR decomposition */
    gsl_linalg_QR_decomp(resid, tau);
    gsl_linalg_QR_lssolve(resid, tau, constraint, aa_wgts, llsresid);

    /* Build the new guess from the computed weights */
    for (i=0; i<grd->nr; i++) {
      w->colNew[i] =
	gsl_vector_get(aa_wgts, 0)*w->colHist[i];
      w->presNew_g[i+1] =
	gsl_vector_get(aa_wgts, 0)*w->presHist[i];
      if (eos_func)
	w->eIntNew[i] =
	  gsl_vector_get(aa_wgts, 0)*w->eIntHist[i];
    }
    for (j=1; j<mk; j++) {
      for (i=0; i<grd->nr; i++) {
	w->colNew[i] += gsl_vector_get(aa_wgts, j) *
	  w->colHist[i+j*grd->nr];
	w->presNew_g[i+1] += gsl_vector_get(aa_wgts, j) *
	  w->presHist[i+j*grd->nr];
	if (eos_func)
	  w->eIntNew[i] += gsl_vector_get(aa_wgts, j) *
	    w->eIntHist[i+j*grd->nr];
      }
    }

    /* Free memory for this iteration */
    gsl_vector_free(llsresid);
    gsl_vector_free(tau);
    gsl_vector_free(aa_wgts);
    gsl_matrix_free(resid);
  }

#endif
  /* End Anderson acceleration code */
}


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

#include <config.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_filter.h>
#include <gsl/gsl_poly.h>

/* maximum derivative order allowed for Gaussian filter */
#define GSL_FILTER_GAUSSIAN_MAX_ORDER     10

typedef double gaussian_type_t;
typedef double ringbuf_type_t;
#include "ringbuf.c"

typedef struct
{
  size_t n;        /* window size */
  double * window; /* linear array with current window */
  ringbuf * rbuf;  /* ring buffer storing current window */
} gaussian_state_t;

static size_t gaussian_size(const size_t n);
static int gaussian_init(const size_t n, void * vstate);
static int gaussian_insert(const gaussian_type_t x, void * vstate);
static int gaussian_delete(void * vstate);
static int gaussian_get(void * params, gaussian_type_t * result, const void * vstate);

static const gsl_movstat_accum gaussian_accum_type;

/*
gsl_filter_gaussian_alloc()
  Allocate a workspace for Gaussian filtering.

Inputs: K - number of samples in window; if even, it is rounded up to
            the next odd, to have a symmetric window

Return: pointer to workspace
*/

gsl_filter_gaussian_workspace *
gsl_filter_gaussian_alloc(const size_t K)
{
  const size_t H = K / 2;
  gsl_filter_gaussian_workspace *w;
  size_t state_size;

  w = calloc(1, sizeof(gsl_filter_gaussian_workspace));
  if (w == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
    }

  w->K = 2 * H + 1;

  w->kernel = malloc(w->K * sizeof(double));
  if (w->kernel == 0)
    {
      gsl_filter_gaussian_free(w);
      GSL_ERROR_NULL ("failed to allocate space for kernel", GSL_ENOMEM);
      return NULL;
    }

  state_size = gaussian_size(w->K);

  w->movstat_workspace_p = gsl_movstat_alloc_with_size(state_size, H, H);
  if (!w->movstat_workspace_p)
    {
      gsl_filter_gaussian_free(w);
      GSL_ERROR_NULL ("failed to allocate space for movstat workspace", GSL_ENOMEM);
    }

  return w;
}

void
gsl_filter_gaussian_free(gsl_filter_gaussian_workspace * w)
{
  if (w->kernel)
    free(w->kernel);

  if (w->movstat_workspace_p)
    gsl_movstat_free(w->movstat_workspace_p);

  free(w);
}

/*
gsl_filter_gaussian()
  Apply a Gaussian filter to an input vector:

G_{sigma}(x) = exp [ -x^2 / (2 sigma^2) ]

Inputs: alpha - number of standard deviations to include in Gaussian kernel
        order - derivative order of Gaussian
        x     - input vector, size n
        y     - (output) filtered vector, size n
        w     - workspace

Notes:
1) If alpha = 3, then the Gaussian kernel will be a Gaussian of +/- 3 standard deviations
*/

int
gsl_filter_gaussian(const gsl_filter_end_t endtype, const double alpha, const size_t order, const gsl_vector * x,
                    gsl_vector * y, gsl_filter_gaussian_workspace * w)
{
  if (x->size != y->size)
    {
      GSL_ERROR("input and output vectors must have same length", GSL_EBADLEN);
    }
  else if (alpha <= 0.0)
    {
      GSL_ERROR("alpha must be positive", GSL_EDOM);
    }
  else
    {
      int status;
      gsl_vector_view kernel = gsl_vector_view_array(w->kernel, w->K);

      /* construct Gaussian kernel of length K */
      gsl_filter_gaussian_kernel(alpha, order, 1, &kernel.vector);

      status = gsl_movstat_apply_accum(endtype, x, &gaussian_accum_type, (void *) w->kernel, y,
                                       NULL, w->movstat_workspace_p);

      return status;
    }
}

/*
gsl_filter_gaussian_kernel()
  Construct Gaussian kernel with given sigma and order

Inputs: alpha     - number of standard deviations to include in window
        order     - kernel order (0 = gaussian, 1 = first derivative, ...)
        normalize - normalize so sum(G) = 1
        kernel    - (output) Gaussian kernel

Return: success/error

Notes:
1) If alpha = 3, then the output kernel will contain a Gaussian with +/- 3 standard deviations
*/

int
gsl_filter_gaussian_kernel(const double alpha, const size_t order, const int normalize, gsl_vector * kernel)
{
  const size_t N = kernel->size;

  if (alpha <= 0.0)
    {
      GSL_ERROR("alpha must be positive", GSL_EDOM);
    }
  else if (order > GSL_FILTER_GAUSSIAN_MAX_ORDER)
    {
      GSL_ERROR("derivative order is too large", GSL_EDOM);
    }
  else
    {
      const double half = 0.5 * (N - 1.0); /* (N - 1) / 2 */
      double sum = 0.0;
      size_t i;

      /* check for quick return */
      if (N == 1)
        {
          if (order == 0)
            gsl_vector_set(kernel, 0, 1.0);
          else
            gsl_vector_set(kernel, 0, 0.0);

          return GSL_SUCCESS;
        }

      for (i = 0; i < N; ++i)
        {
          double xi = ((double)i - half) / half;
          double yi = alpha * xi;
          double gi = exp(-0.5 * yi * yi);

          gsl_vector_set(kernel, i, gi);
          sum += gi;
        }

      /* normalize so sum(kernel) = 1 */
      if (normalize)
        gsl_vector_scale(kernel, 1.0 / sum);

      if (order > 0)
        {
          const double beta = -0.5 * alpha * alpha;
          double q[GSL_FILTER_GAUSSIAN_MAX_ORDER + 1];
          size_t k;

          /*
           * Need to calculate derivatives of the Gaussian window; define
           *
           * w(n) = C * exp [ p(n) ]
           *
           * p(n) = beta * n^2
           * beta = -1/2 * ( alpha / ((N-1)/2) )^2
           *
           * Then:
           *
           * d^k/dn^k w(n) = q_k(n) * w(n)
           *
           * where q_k(n) is a degree-k polynomial in n, which satisfies:
           *
           * q_k(n) = d/dn q_{k-1}(n) + q_{k-1}(n) * dp(n)/dn
           * q_0(n) = 1 / half^{order}
           */

          /* initialize q_0(n) = 1 / half^{order} */
          q[0] = 1.0 / gsl_pow_uint(half, order);
          for (i = 1; i <= GSL_FILTER_GAUSSIAN_MAX_ORDER; ++i)
            q[i] = 0.0;

          /* loop through derivative orders and calculate q_k(n) for k = 1,...,order */
          for (k = 1; k <= order; ++k)
            {
              double qm1 = q[0];

              q[0] = q[1];
              for (i = 1; i <= k; ++i)
                {
                  double tmp = q[i];
                  q[i] = (i + 1.0) * q[i + 1] + /* d/dn q_{k-1} */
                         2.0 * beta * qm1;      /* q_{k-1}(n) p'(n) */
                  qm1 = tmp;
                }
            }

          /* now set w(n) := q(n) * w(n) */
          for (i = 0; i < N; ++i)
            {
              double xi = ((double)i - half) / half;
              double qn = gsl_poly_eval(q, order + 1, xi);
              double *wn = gsl_vector_ptr(kernel, i);

              *wn *= qn;
            }
        }

      return GSL_SUCCESS;
    }
}

static size_t
gaussian_size(const size_t n)
{
  size_t size = 0;

  size += sizeof(gaussian_state_t);
  size += n * sizeof(gaussian_type_t);
  size += ringbuf_size(n);

  return size;
}

static int
gaussian_init(const size_t n, void * vstate)
{
  gaussian_state_t * state = (gaussian_state_t *) vstate;

  state->n = n;

  state->window = (gaussian_type_t *) ((unsigned char *) vstate + sizeof(gaussian_state_t));
  state->rbuf = (ringbuf *) ((unsigned char *) state->window + n * sizeof(gaussian_type_t));

  ringbuf_init(n, state->rbuf);

  return GSL_SUCCESS;
}

static int
gaussian_insert(const gaussian_type_t x, void * vstate)
{
  gaussian_state_t * state = (gaussian_state_t *) vstate;

  /* add new element to ring buffer */
  ringbuf_insert(x, state->rbuf);

  return GSL_SUCCESS;
}

static int
gaussian_delete(void * vstate)
{
  gaussian_state_t * state = (gaussian_state_t *) vstate;

  if (!ringbuf_is_empty(state->rbuf))
    ringbuf_pop_back(state->rbuf);

  return GSL_SUCCESS;
}

static int
gaussian_get(void * params, gaussian_type_t * result, const void * vstate)
{
  const gaussian_state_t * state = (const gaussian_state_t *) vstate;
  const double * kernel = (const double *) params;
  size_t n = ringbuf_copy(state->window, state->rbuf);
  double sum = 0.0;
  size_t i;

  for (i = 0; i < n; ++i)
    sum += state->window[i] * kernel[n - i - 1];

  *result = sum;

  return GSL_SUCCESS;
}

static const gsl_movstat_accum gaussian_accum_type =
{
  gaussian_size,
  gaussian_init,
  gaussian_insert,
  gaussian_delete,
  gaussian_get
};


// Framework for using a CBLAS high performance library:

// General functions defined:

// copy(dest, src, len)

// scal(X, len, alpha): X <- alpha * X

// addScal(X, Y, len, alpha): X <- X + alpha * Y

// matrix_multiply(optA, optB, A, B, C, rows_op_A, cols_op_B, cols_op_A): C <- op(A) * op(B)

#ifndef HIGH_PERF_H
#define HIGH_PERF_H

// To use a CBLAS library, e.g OpenBLAS:
#if defined _OPENBLAS
	#define CBLAS
#endif

#include "settings.h" // For 'Number' definition.

#if defined CBLAS

	#pragma message "OpenBLAS is being used."

	#include <cblas.h>

	#define ORDER CblasRowMajor // Do not modify.
	#define CONVERT_CST 111 // Do not modify, used for converting 'Trans' to 'CblasTrans'.

	#define leadingDimension(rows, cols) \
		(ORDER == CblasRowMajor ? cols : rows)

	// float/double implementation:

	#if defined _FLOAT

		#define _copy(dest, src, len) \
			cblas_scopy(len, src, 1, dest, 1)

		#define _scal(X, len, alpha) \
			cblas_sscal(len, alpha, X, 1)

		#define _axpy(X, Y, len, alpha) \
			cblas_saxpy(len, alpha, Y, 1, X, 1)

		#define _gemm(ORDER, optA, optB, rows_op_A, cols_op_B, cols_op_A, alpha, A, lda, B, ldb, beta, C, ldc) \
			cblas_sgemm(ORDER, optA, optB, rows_op_A, cols_op_B, cols_op_A, alpha, A, lda, B, ldb, beta, C, ldc)

	#elif defined _DOUBLE

		#define _copy(dest, src, len) \
			cblas_dcopy(len, src, 1, dest, 1)

		#define _scal(X, len, alpha) \
			cblas_dscal(len, alpha, X, 1)

		#define _axpy(X, Y, len, alpha) \
			cblas_daxpy(len, alpha, Y, 1, X, 1)

		#define _gemm(ORDER, optA, optB, rows_op_A, cols_op_B, cols_op_A, alpha, A, lda, B, ldb, beta, C, ldc) \
			cblas_dgemm(ORDER, optA, optB, rows_op_A, cols_op_B, cols_op_A, alpha, A, lda, B, ldb, beta, C, ldc)
	#endif

	// CBLAS framework:

	#define copy(dest, src, len) \
		copyVector(dest, src, len)
	// Not using CBLAS _copy here, since memcpy seems to be slightly faster...

	#define scal(X, len, alpha) \
		_scal(X, len, alpha)

	#define addScal(X, Y, len, alpha) \
		_axpy(X, Y, len, alpha)

	#define matrix_multiply(optA, optB, A, B, C, rows_op_A, cols_op_B, cols_op_A) \
	{ \
		CBLAS_TRANSPOSE cblas_optA = optA + CONVERT_CST; \
		CBLAS_TRANSPOSE cblas_optB = optB + CONVERT_CST; \
		int lda, ldb, ldc; \
		if (ORDER == CblasRowMajor) \
		{ \
			lda = cblas_optA == CblasNoTrans ? cols_op_A : rows_op_A; \
			ldb = cblas_optB == CblasNoTrans ? cols_op_B : cols_op_A; \
			ldc = cols_op_B; \
		} \
		else \
		{ \
			lda = cblas_optA == CblasNoTrans ? rows_op_A : cols_op_A; \
			ldb = cblas_optB == CblasNoTrans ? cols_op_A : cols_op_B; \
			ldc = rows_op_A; \
		} \
		_gemm(ORDER, cblas_optA, cblas_optB, rows_op_A, cols_op_B, cols_op_A, 1, A, lda, B, ldb, 0, C, ldc); \
	}

#else // Naive implementations:

	#pragma message "No high performance library is being used, expect slow learning."

	#define copy(dest, src, len) \
		copyVector(dest, src, len)

	#define scal(X, len, alpha) \
		naive_scal(X, len, alpha)

	#define addScal(X, Y, len, alpha) \
		naive_addScal(X, Y, len, alpha)

	#define matrix_multiply(optA, optB, A, B, C, rows_op_A, cols_op_B, cols_op_A) \
		naive_matrix_multiply(optA, optB, A, B, C, rows_op_A, cols_op_B, cols_op_A)
#endif

#endif


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

#include <config.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_filter.h>
#include <gsl/gsl_errno.h>

gsl_filter_median_workspace *
gsl_filter_median_alloc(const size_t K)
{
  gsl_filter_median_workspace *w;
  size_t H = K / 2;

  w = calloc(1, sizeof(gsl_filter_median_workspace));
  if (w == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
    }

  w->movstat_workspace_p = gsl_movstat_alloc(2*H + 1);
  if (w->movstat_workspace_p == NULL)
    {
      gsl_filter_median_free(w);
      GSL_ERROR_NULL ("failed to allocate space for movstat workspace", GSL_ENOMEM);
    }

  return w;
}

void
gsl_filter_median_free(gsl_filter_median_workspace * w)
{
  if (w->movstat_workspace_p)
    gsl_movstat_free(w->movstat_workspace_p);

  free(w);
}

/*
gsl_filter_median()
  Standard median filter

Inputs: endtype - end point handling
        x       - input vector
        y       - output vector
        w       - workspace
*/

int
gsl_filter_median(const gsl_filter_end_t endtype, const gsl_vector * x, gsl_vector * y, gsl_filter_median_workspace * w)
{
  int status = gsl_movstat_median(endtype, x, y, w->movstat_workspace_p);
  return status;
}


/*
 * Copyright (c) 2018, Aleksi Kurkela, Aleksas Mazeliauskas, Jean-Francois
 * Paquet, Soeren Schlichting and Derek Teaney
 * All rights reserved.
 *
 * KoMPoST is distributed under MIT license;
 * see the LICENSE file that should be present in the root
 * of the source distribution, or alternately available at:
 * https://github.com/KMPST/KoMPoST/
 */

#include <gsl/gsl_math.h>
#include <iostream>
#ifndef M_HBARC
#define M_HBARC 0.197326979
#endif

namespace GreensFunctions {

class LowKArguments;

//////////////////////////////////////////////////

namespace EnergyPerturbations {

namespace FreeStreaming {
namespace CoordinateSpace {
double Gs(double dX, double dT);
double Gv(double dX, double dT);
double Gd(double dX, double dT);
double Gr(double dX, double dT);
}
} // FreeStreaming

namespace KineticTheory {
namespace CoordinateSpace {
double Gs(double dX, double dT, double ScalingVarOut);
double Gv(double dX, double dT, double ScalingVarOut);
double Gd(double dX, double dT, double ScalingVarOut);
double Gr(double dX, double dT, double ScalingVarOut);
}
} // KineticTheory

namespace LowKLimit {
namespace CoordinateSpace {
double Gs(double dX, double dT, const LowKArguments &in);
double Gv(double dX, double dT, const LowKArguments &in);
double Gd(double dX, double dT, const LowKArguments &in);
double Gr(double dX, double dT, const LowKArguments &in);
}
} // LowKLimit

} // EnergyPerturbations

//////////////////////////////////////////////////

namespace MomentumPerturbations {

namespace FreeStreaming {
namespace CoordinateSpace {
double Hv(double dX, double dT);
double Hd(double dX, double dT);
double Hr(double dX, double dT);
double Htd(double dX, double dT);
double Htm(double dX, double dT);
double Htr(double dX, double dT);
}
} // FreeStreaming

namespace KineticTheory {
namespace CoordinateSpace {
double Hv(double dX, double dT, double ScalingVarOut);
double Hd(double dX, double dT, double ScalingVarOut);
double Hr(double dX, double dT, double ScalingVarOut);
double Htd(double dX, double dT, double ScalingVarOut);
double Htm(double dX, double dT, double ScalingVarOut);
double Htr(double dX, double dT, double ScalingVarOut);
}
} // Kinetic Theory

} // MomentumPerturbations

//////////////////////////////////////////////////

void Setup(double Sigma, int NumberOfPoints, int ENERGY_PERTURBATIONS,
           int MOMENTUM_PERTURBATIONS);

void Output(int ENERGY_PERTURBATIONS, int MOMENTUM_PERTURBATIONS);

class LowKArguments {

  // private:
public:
  // Number of DOF
  const int NuG = 16;

  double tauF;  // Final time in fm
  double tauIn; // Initial time in fm

  double E0;
  double EF;

  double TXX0;
  double TXXF;

  double EtaByS;
  double SigmaBG; // Sigma in fm

public:
  LowKArguments(double tf, double tin, double e0, double ef, double txx0,
                double txxf, double etabys, double sigmabg)
      : tauF(tf), tauIn(tin), E0(e0), EF(ef), TXX0(txx0), TXXF(txxf),
        EtaByS(etabys), SigmaBG(sigmabg) {}

  double S(double dX, double dT) const {
    const double &s = SigmaBG;
    double rs = dX / s;
    return 1. / (M_PI * s * s) * exp(-rs * rs);
  }

  double DS(double dX, double dT) const {
    const double &s = SigmaBG;
    double rs = dX / s;
    return dT / (M_PI * s * s * s) * exp(-rs * rs) * (-2. * rs);
  }

  double DSOverR(double dX, double dT) const {
    const double &s = SigmaBG;
    double rs = dX / s;
    return dT * dT / (M_PI * s * s * s * s) * exp(-rs * rs) * (-2.);
  }

  double DDS(double dX, double dT) const {
    const double &s = SigmaBG;
    double rs = dX / s;
    return dT * dT / (M_PI * s * s * s * s) * exp(-rs * rs) *
           (4 * rs * rs - 2.);
  }

  double Ass() const {
    double dT = tauF - tauIn;
    double s2 = SigmaBG * SigmaBG / (dT * dT);
    return (-0.5 + s2 / 2.) * E0 / EF * (EF + TXXF) / (E0 + TXX0);
  }

  double Asv() const { return 0.5 * E0 / EF * (EF + TXXF) / (E0 + TXX0); }

  double ScalingVariableInverse() const {
    return 3. / 4. * EtaByS * Entropy(EF) * M_HBARC / (EF * tauF);
  }

  double Entropy(double e) const {
    double T = pow(30. / (M_PI * M_PI * NuG) * e, 0.25);
    return 4. / 3. * e / T;
  }
};

} // GreensFunctions


#ifndef libcv_ctf
#define libcv_ctf

#include "Array.h"
#import "Ellipse.h"
#include "MRC.h"
#include "Image.h"
#include "util.h"
#include "cvtypes.h"
#include <fftw3.h>
#include <math.h>
#include "geometry.h"
#include <gsl/gsl_math.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_multimin.h>

typedef struct CTFST {

	char * imgpath;

	f64 defocus_x;
	f64 defocus_y;
	f64 astig_angle;
	f64 amplitude;
	f64 volts;
	f64 lambda;
	f64 size;
	f64 apix;
	f64 scale;

	f64 * ctf1d;
	f64 * ctfsynth1d;
	f64 * background1d;
	f64 * envelope1d;
	f64 ctf1dscale;
	f64 ctf1dapix;
	u32 ctf1dsize;

	f64 * ctf2d;
	f64 * ctfsynth2d;
	f64 * background2d;
	f64 * envelope2d;
	f64 ctf2dapix;
	u32 ctf2drows;
	u32 ctf2dcols;

} * CTF;

@interface Array ( CTF_Functions )

-(id) ellipse1DAvg:(EllipseP)ellipse;

@end

void ctf2_calcv( f64 c[], f64 ctf[], u64 size );
f64 ctf_norm( f64 fit_data[], f64 ctf_p[], f64 ctf[], f64 norm[], u32 size );
f64 calculate_score(f64 c1[], f64 c2[], u32 lcut, u32 rcut );
ArrayP ctfNormalize( ArrayP fit_data, ArrayP ctf_params );
void estimateDefocus( ArrayP fit_data, ArrayP ctf_params );
void peakNormalize( f64 values[], u32 size );
ArrayP fitNoise( ArrayP fit_data );
ArrayP fitEnvelope( ArrayP fit_data );
f64 getTEMLambda( f64 volts );
f64 getTEMVoltage( f64 lambda );
f64 ctf_calc( f64 c[], f64 x );
void fit2DCTF( ArrayP image, f64 d1, f64 d2, f64 th, f64 apix, f64 kv, f64 cs, f64 ac );
f64 positionForPeak( f64 c[], u32 peak_pos );
f64 defocusForPeak( f64 c[], f64 peak_pos, u32 peak_num );
ArrayP generate2DCTF( f64 df1, f64 df2, f64 theta, u32 rows, u32 cols, f64 apix, f64 cs, f64 kv, f64 ac );
ArrayP g2DCTF( f64 df1, f64 df2, f64 theta, u32 rows, u32 cols, f64 apix, f64 cs, f64 kv, f64 ac );

#endif


/*
 * Copyright (c) 2015, Aleksas Mazeliauskas and Derek Teaney
 * All rights reserved.
 *
 * rnavier is distributed under MIT license;
 * see the LICENSE file that should be present in the root
 * of the source distribution, or alternately available at:
 * https://github.com/rnavier/rnavier/
 */
#ifndef RNAVIER_TBjFluctuation_h
#define RNAVIER_TBjFluctuation_h

#include <gsl/gsl_odeiv.h>
#include <memory>
#include "THModel3D.h"
#include "TIC3D.h"

class THydro3DBj ;
class TRNavier3DBj ;

class TBjFluctuation{

   private:

      THModel3D *fModel ;

      double fTau ;
      double fH ;
      double fKx;
      double fKy;
      double fKeta;

      static constexpr double fEpsAbs = 1.e-10;
      static constexpr double fEpsRel = 1.e-4  ;

      static const size_t fSize= 9 ;
      double fY[fSize] ;

      const gsl_odeiv_step_type *fType ;
      gsl_odeiv_step      *fStep ;
      gsl_odeiv_control   *fControl ;
      gsl_odeiv_evolve    *fEvolve ;
      gsl_odeiv_system    fSystem  ;

      void setY(const double &e0,
              const std::complex<double> &de,
              const std::complex<double> &gx,
              const std::complex<double> &gy,
              const std::complex<double> &gz, double y[]) ;

      void getY(const double y[], double &e0,
              std::complex<double> &de,
              std::complex<double> &gx,
              std::complex<double> &gy,
              std::complex<double> &gz) ;

      int step(double &t , const double &tf, double &h, double y[])  {
          return gsl_odeiv_evolve_apply(fEvolve, fControl, fStep,
                  &fSystem,  &t, tf, &h, y) ;
      }

      void eos_params(const double &e0,
             double &p0,
             double &cs0,
             double &s0,
             double &T0,
             double &eta0) ;

   public:

      TBjFluctuation(THModel3D *hmodel) ;
      ~TBjFluctuation() ;

      void setic(const double &t0,
              const double &kx, const double &ky, const double &keta,
              const double &e0,
              const std::complex<double> &de,
              const std::complex<double> &gx,
              const std::complex<double> &gy,
              const std::complex<double> &gz) ;

      void get_hydro_ic(const TICPoint3D &pnt, double &e, double &n, double *uI, double *piIJ_ns);

      int run(const double &t1, const bool &verbose = false) ;

      void get_timescales_cartesian(double &tauR, double &tau_sound, double &tau_damp) ;

      friend int tbjfluctuation_func (double t, const double y[], double f[],
         void *params) ;
} ;

int tbjfluctuation_func (double t, const double y[], double f[],
      void *params) ;

//////////////////////////////////////////////////////////////////////////////

class TICBjFluctuation : public TIC3D
{
   private:

      THModel3D *fModel ;

      double fTau0 ;
      double fLx;
      double fLy ;
      double fLeta;

      double fInitialS0;
      double fInitialE0;
      double fNKx;
      double fNKy;
      double fNKeta;
      double fDeOverE0;
      double fPhaseE;

      std::unique_ptr<TBjFluctuation> fFluctuation;

   public:

      TICBjFluctuation(TRNavier3DBj *rn, TGrid3D *gr) ;
      virtual void nextEvent(const double &bgen) override ;
      virtual void IC(const TICPoint3D &pnt, const TNDArray1D &auxi) override ;
      using TIC3D::fillGrid ;

      void evolve_to_time(const double &t)
      {
          fFluctuation->run(t) ;
      }
      void get_timescales_cartesian(double &tauR, double &tau_sound, double &tau_damp)
      {
          fFluctuation->get_timescales_cartesian(tauR, tau_sound, tau_damp) ;
      }

};

std::unique_ptr<TIC3D> make_ic_icbjfluctuation(THydro3DBj *hy, const std::string &icname) ;

#endif //RNAVIER_TBjFluctuation_h


/**
 * @file localization.h
 * @brief source localization
 * @author Ulrich Klee
 */

#ifndef LOCALIZATION_H
#define LOCALIZATION_H

#include <stdio.h>
#include <assert.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_fit.h>

#include "common/jexception.h"

gsl_vector* getSrpPhat(double delta_f, gsl_matrix_complex *mFramePerChannel, gsl_vector *searchRangeX, gsl_vector *searchRangeY, gsl_matrix *arrgeom, int zPos);

void calcDelays(int x, int y, int z, const gsl_matrix* mpos, gsl_vector* delays);

void calcDelaysOfLinearMicrophoneArray(float azimuth, const gsl_matrix* mpos, gsl_vector* delays);

void calcDelaysOfCircularMicrophoneArray(float azimuth, float elevation, const gsl_matrix* mpos, gsl_vector* delays);

gsl_vector* getDelays(double delta_f, gsl_matrix_complex* mFramePerChannel, gsl_vector* searchRange);

double getAzimuth(double delta_f, gsl_matrix_complex *mFramePerChannel, gsl_matrix *arrgeom, gsl_vector *delays);

double getPlaneWaveSrp(double delta_f, gsl_matrix_complex *mFramePerChannel, gsl_vector *searchRangeY, gsl_matrix *arrgeom);

//void halfComplexPack(double* tgt, const gsl_vector_complex* src);

void getGCCRaw(const gsl_matrix_complex* spectralSample, double sampleRate, gsl_vector* gcc);

const gsl_vector* getGCC(gsl_matrix_complex *spectralSample, double sampleRate);
const gsl_vector* getWindowedGCC(gsl_matrix_complex *spectralSample, double sampleRate, double minDelay, double maxDelay);

const gsl_vector* getWindowedGCCratio(gsl_matrix_complex *spectralSample, double sampleRate, double minDelay, double maxDelay);

const gsl_vector* getWindowedGCCdirect(gsl_matrix_complex *spectralSample, double sampleRate, double minDelay, double maxDelay);

const gsl_vector* getWindowedGCCabs(gsl_matrix_complex *spectralSample, double sampleRate, double minDelay, double maxDelay);

const gsl_vector* getDynWindowedGCC(gsl_matrix_complex *spectralSample, double sampleRate, double minDelay, double maxDelay, double wMinDelay, double wMaxDelay, double threshold);

double getInterpolation(gsl_matrix *crossResult, int delayPos);

gsl_vector* get3DPosition(gsl_vector *yCoord, gsl_vector *azimuth1, gsl_vector *azimuth2, double xPos, double zPos);

gsl_vector* get3DPosition_T_shape(gsl_matrix *arrgeom1, int arrayNr1, gsl_matrix *arrgeom2, int arrayNr2,  gsl_matrix *arrgeom3, double azimuth1, double azimuth2, double azimuth3);

gsl_vector* getGCC_old(gsl_matrix_complex *spectralSample, double delta_f, gsl_vector *delays);

gsl_matrix* getLowerTriangMatrix(gsl_matrix* fullMatrix);

gsl_vector* getXi(gsl_matrix* D1_2);

class PhaseTransform {
public:
  PhaseTransform(unsigned sz);
  ~PhaseTransform();
private:
  gsl_vector_complex*					_crossSpectrum;
  double*						_crossCorrelation;
};

class NoisePowerSpectrum
{
 public:
  NoisePowerSpectrum(double alpha = 0.95);
  ~NoisePowerSpectrum() {
    if (powerSpectrum != NULL)
      gsl_vector_free(powerSpectrum);
  }
  void setAlpha(double alpha) {
    this->alpha = alpha;
    alpha1 = 1 - alpha;
  }
  double getAlpha() { return alpha; }
  void add(const gsl_vector_complex *noiseSpectrum, double timestamp);
  const gsl_vector *get() {
    return powerSpectrum;
  }
 private:
  gsl_vector *powerSpectrum;
  double alpha, alpha1;
  double timestamp;
};

class NoiseCrossSpectrum
{
 public:
  NoiseCrossSpectrum(double alpha = 0.95);
  ~NoiseCrossSpectrum() {
    if (crossSpectrum != NULL)
      gsl_vector_complex_free(crossSpectrum);
  }
  void setAlpha(double alpha) {
    this->alpha = alpha;
    alpha1 = 1 - alpha;
  }
  double getAlpha() { return alpha; }
  const gsl_vector_complex *get() {
    return crossSpectrum;
  }
  void add(const gsl_vector_complex *noiseSpectrum1, const gsl_vector_complex *noiseSpectrum2);
 private:
  gsl_vector_complex *crossSpectrum;
  double alpha, alpha1;
};

class GCC
{
 public:
  GCC(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true);
  ~GCC();
  void calculate(const gsl_vector_complex *spectralSample1, unsigned chan1, const gsl_vector_complex *spectralSample2, unsigned chan2, unsigned pair, double timestamp, bool sad = false, bool smooth = true);
  const gsl_vector* findMaximum(double minDelay = -HUGE_VAL, double maxDelay = HUGE_VAL);
  double getPeakDelay() { return delay; }
  double getPeakCorr() { return maxCorr; }
  double getRatio() { return ratio; }
  const gsl_vector* getNoisePowerSpectrum(unsigned chan) { return noisePowerSpectrum[chan].get(); }
  const gsl_vector_complex* getNoiseCrossSpectrum(unsigned pair) { return noiseCrossSpectrum[pair].get(); }
  const gsl_vector_complex* getCrossSpectrum() { return crossSpectrum; }
  const gsl_vector* getCrossCorrelation() { return crossCorrelation; }
  void setAlpha(double alpha) {
    for(unsigned i=0; i < pairs; i++)
      noiseCrossSpectrum[i].setAlpha(alpha);
    for(unsigned i=0; i < nChan; i++)
      noisePowerSpectrum[i].setAlpha(alpha);
  }
  double getAlpha() { return noiseCrossSpectrum[0].getAlpha(); }
 protected:
  virtual gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i) { throw jdimension_error("Not implemented!!!\n"); };

  //auxiliary variables
  gsl_complex x1, x2, Gx1x2, Gs1s2, G;
  double W1, W2, X1, X12, X2, X22;

  double sampleRate;
  unsigned fftLen;
  unsigned fftLen2;
  unsigned len;
  unsigned nChan;
  unsigned pairs;
  unsigned delayPos;
  double q, q1, q2, beta, beta1;
  double ratio;
  double delay;
  double maxCorr;
  double maxCorr2;
  gsl_vector *crossCorrelation;
  gsl_matrix *interpolValues;
  gsl_vector* retValues;
  gsl_vector *powerSpectrum1, *powerSpectrum2;
  gsl_vector_complex *cSpectrum;
  gsl_vector_complex *crossSpectrum;
  NoisePowerSpectrum* noisePowerSpectrum;
  NoiseCrossSpectrum* noiseCrossSpectrum;
  bool noisereduction;
  bool sad;
  bool interpolate;
};

class GCCRaw : public GCC
{
 public:
  GCCRaw(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

class GCCGnnSub : public GCC
{
 public:
  GCCGnnSub(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

class GCCPhat : public GCC
{
 public:
  GCCPhat(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

class GCCGnnSubPhat : public GCC
{
 public:
  GCCGnnSubPhat(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

class GCCMLRRaw : public GCC
{
 public:
  GCCMLRRaw(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

class GCCMLRGnnSub : public GCC
{
 public:
  GCCMLRGnnSub(double sampleRate = 44100.0 , unsigned fftLen = 2048, unsigned nChan = 16, unsigned pairs = 6, double alpha = 0.95, double beta = 0.5, double q = 0.3, bool interpolate = true, bool noisereduction = true) : GCC(sampleRate, fftLen, nChan, pairs, alpha, beta, q, interpolate, noisereduction) {};
 protected:
  gsl_complex calcCrossSpectrumValue(gsl_complex x1, gsl_complex x2, const gsl_vector_complex* Gn1n2, const gsl_vector* N1, const gsl_vector* N2, unsigned i);
};

#endif


/**
 * File: nicback_utils.c
 * Subroutine for the NICMOS background subtraction
 *
 */
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spc_FITScards.h"
#include "spce_sect.h"
#include "spce_fitting.h"
#include "spce_is_in.h"
#include "spc_back.h"
#include "spce_pathlength.h"
#include "trfit_utils.h"
#include "nicback_utils.h"

/**
 * Function: make_nicmos_back
 * The subroutine computes and stores the background image for a specific
 * NICMOS grism image. The non-masked pixels in the grism image are
 * correlated with their corresponding values in the master background image.
 * A linear relation is fitted to the value pairs. Deviating points are
 * excluded using kappa-sigma clipping.
 * Using the slope of the final fit, the background image is computed
 * and stored as a fits image.
 *
 * Parameters:
 * @param obs         - the observation for the grism image
 * @param msk_name    - the full name of the mask file
 * @param master_bck  - the full name of the master sky background
 * @param bck_name    - the full name of the background image
 *
 * Returns:
 * @return -
 */
void
make_nicmos_back(const observation * const obs, const char *msk_name,
		 const char *master_bck, char bck_name[], char plist_name[],
		 const char corr_bck[])
{
  px_point npixels;

  double skypix_frac;

  gsl_matrix *msk_img;
  gsl_matrix *mbck_img;
  gsl_matrix *corr_img=NULL;
  gsl_matrix *grism_bck;

  gsl_vector *lfit;

  fitbck_data *fbck_data;
  //int f_status=0;

  FITScards *cards;

  char  ID[MAXCHAR];

  // fix the extension name
  sprintf (ID, "BCK");

  // load the master background image
  fprintf(stdout,"Loading DATA from: %s...", master_bck);
  mbck_img = FITSimage_to_gsl(master_bck, 1, 1);
  fprintf(stdout,". Done.\n");

  if (strlen(corr_bck) > 0)
    {
      // load the correction background image
      fprintf(stdout,"Loading DATA from: %s...", corr_bck);
      corr_img = FITSimage_to_gsl(corr_bck, 1, 1);
      fprintf(stdout,". Done.\n");
    }

  // load the mask image
  fprintf(stdout,"Loading DATA from: %s...", msk_name);
  msk_img = FITSimage_to_gsl(msk_name, 2, 1);
  fprintf(stdout,". Done.\n");

  // get the dimension of
  // the grism image
  npixels = get_npixel(obs);

  // allocate memory for the fit data
  fbck_data = alloc_fitbck_data(npixels.x * npixels.y);

  // fill the fit data into the structure
  fill_fbck_data(obs, msk_img, mbck_img, fbck_data, plist_name, corr_img);

  // make the fit, including
  // kappa-sigma iterations
  lfit = make_ksig_linfit(fbck_data);

  // report the result of the fit onto the screen
  fprintf(stdout, "\nFitting result: c0 = %f +- %f , c1 = %f +- %f , chi^2 = %f\n\n",
	  gsl_vector_get(lfit, 1), gsl_vector_get(lfit, 3),
	  gsl_vector_get(lfit, 2), gsl_vector_get(lfit, 4), gsl_vector_get(lfit, 6));

  // compute the background for the grism frame
  grism_bck = compute_nicmos_back(mbck_img, gsl_vector_get(lfit, 2),
				  gsl_vector_get(lfit, 1), corr_img);

  // write the background image to a file
  fprintf(stdout,"Writing data to: %s...", bck_name);
  gsl_to_FITSimage (grism_bck, bck_name, 1, ID);
  fprintf(stdout,". Done.\n");

  // determine the fraction of pixels
  // used in the background determination
  skypix_frac = get_skypix_frac(fbck_data);

  // create the fits keywords
  cards = nicbck_info_to_FITScards(skypix_frac, gsl_vector_get(lfit, 2),
				   gsl_vector_get(lfit, 1));

  // transfer the fits keywords
  // to the background image
  put_FITS_cards (bck_name, 2, cards);

  // release memory
  free_fitbck_data(fbck_data);
  gsl_matrix_free(mbck_img);
  if (corr_img != NULL)
    gsl_matrix_free(corr_img);
  gsl_matrix_free(msk_img);
  gsl_matrix_free(grism_bck);
  gsl_vector_free(lfit);
  free_FITScards(cards);
}

/**
 * Function: alloc_fitbck_data
 */
fitbck_data *
alloc_fitbck_data(const int n_data)
{
  fitbck_data *fbck_data;

  fbck_data = (fitbck_data *)malloc(sizeof(fitbck_data));

  fbck_data->x_values = (double *)malloc(n_data * sizeof(double));
  fbck_data->y_values = (double *)malloc(n_data * sizeof(double));
  fbck_data->e_values = (double *)malloc(n_data * sizeof(double));
  fbck_data->x_pos = (int *)malloc(n_data * sizeof(int));
  fbck_data->y_pos = (int *)malloc(n_data * sizeof(int));

  fbck_data->n_data = n_data;

  return fbck_data;
}

/**
 * Function: get_skypix_frac
 * The subroutine computes the fraction of image pixels which were used for
 * determining the scaling factor of the master background and the computation
 * of the bacground image. Pixels not used were masked out, since they are part
 * of an object beam or rejected in the kappa-sigma clipping.
 *
 * Parameters:
 * @param fbck_data - the data used for the fit
 *
 * Returns:
 * @return skypix_frac - fraction of pixels used for background determination
 */
double
get_skypix_frac(const fitbck_data *fbck_data)
{
  int index;
  int nvalues=0;

  double skypix_frac=0.0;

  // go over all data values
  for (index=0; index < fbck_data->n_data; index++)
    {
      // check whether the weight is not NULL
      if (fbck_data->e_values[index])
	// enhance the number of used pixels
	nvalues++;
    }

  // compute the fration of used pixels
  skypix_frac = (double)nvalues / (double)fbck_data->n_data;

  // return the fraction
  return skypix_frac;
}

/**
 * Function: compute_nicmos_back
 * The subroutine applies a scaling factor to a master background
 * in order to get the background image for a specific grism image.
 *
 * Parameters:
 * @param mbck_img - the master background pixel
 * @param factor   - the scaling factor
 *
 * Returns:
 * @return grism_bck - the background image as gsl-matrix
 */
gsl_matrix *
compute_nicmos_back(const gsl_matrix *mbck_img, const double factor,
		    const double offset, const gsl_matrix *corr_img)
{
  gsl_matrix *grism_bck;

  int ii, jj;

  // allocate the space for the background image
  grism_bck = gsl_matrix_alloc(mbck_img->size1, mbck_img->size2);

  // go over each row
  for (ii=0; ii < (int)mbck_img->size1; ii++)
    {
      // go over each column
      for (jj=0; jj < (int)mbck_img->size2; jj++)
	{
	  // compute and set the value in the background image
	  if (corr_img != NULL)
	    // using the pedestal image
	    gsl_matrix_set(grism_bck, ii, jj,
	        (gsl_matrix_get(mbck_img, ii, jj) + gsl_matrix_get(corr_img, ii, jj)) * factor);
	  else
	    // using the fit values only
	    gsl_matrix_set(grism_bck, ii, jj, gsl_matrix_get(mbck_img, ii, jj) * factor + offset);
	}
    }

  // return the background image
  return grism_bck;
}

/**
 * Function: compute_nicmos_back
 * The subroutine iteratively fits a linear relation to the pairs of
 * grism image - master background pixel values. After each fit,
 * pairs with large deviations are determined and excluded.
 * The result of the final fit is returned.
 *
 * Parameters:
 * @param fbck_data - the data used for the fits
 *
 * Returns:
 * @return lfit - the results of the final fit
 */
gsl_vector *
make_ksig_linfit(fitbck_data *fbck_data)
{
  gsl_vector *lfit=NULL;

  int index=0;

  int clipped=1;

  // check whether iterations still
  // must be done and whether
  // the data was changed from clipping
  while (index < N_KSIGBCK_ITER && clipped)
    {
      if (check_fbck_data(fbck_data))
	{
	  // make a non-weighted linear fit
	  lfit = det_vector_linear(fbck_data->x_values, fbck_data->y_values, fbck_data->e_values,
				   fbck_data->n_data, 0);

	  // make a clipping iteration
	  clipped = clipp_fbck_data(fbck_data, lfit, N_KSIGBCK_KAPPA);

	  // inhance the clipping counter
	  index++;
	}
      else
	{
	  // release the space
	  // for the old vector
	  if (lfit)
	    gsl_vector_free(lfit);

	  // get a new dummy vector
	  lfit = get_fbck_defaults();

	  break;
	}
    }

  // check whether the scale is within the boundaries
  if (gsl_vector_get(lfit, 2) < BCK_SCALE_MIN || gsl_vector_get(lfit, 2) > BCK_SCALE_MAX)
    {

      fprintf(stdout, "aXe_NICBACK: Background scale is out of bounds. It is re-adjusted.\n");
      // release the space
      // for the old vector
      gsl_vector_free(lfit);

      // get a new dummy vector
      lfit = get_fbck_defaults();
    }

  // return the fit result
  return lfit;
}

int
check_fbck_data(fitbck_data *fbck_data)
{
  int check=1;
  int ix_min=60000;
  int ix_max=0;
  int iy_min=60000;
  int iy_max=0;
  int index;
  double frac;
  double x_ext, y_ext;

  // get the fraction of background pixel
  frac =  get_skypix_frac(fbck_data);

  if (frac < FRAC_BPIX_MIN)
    {
      check = 0;
      fprintf(stdout, "aXe_NICBACK: Not enough background pixels. Fraction: %e\n", frac);
    }
  else
    {
      // go over all data values
      for (index=0; index < fbck_data->n_data; index++)
	{
	  // check whether the weight is not NULL
	  if (fbck_data->e_values[index])
	    {
	      if (fbck_data->x_pos[index] > ix_max)
		ix_max = fbck_data->x_pos[index];
	      if (fbck_data->x_pos[index] < ix_min)
		ix_min = fbck_data->x_pos[index];
	      if (fbck_data->y_pos[index] > iy_max)
		iy_max = fbck_data->y_pos[index];
	      if (fbck_data->y_pos[index] < iy_min)
		iy_min = fbck_data->y_pos[index];
	    }
	}
      x_ext = (float)(ix_max-ix_min) / NPIX_DIM1;
      y_ext = (float)(iy_max-iy_min) / NPIX_DIM2;

      if (x_ext < AREA_EXTEND_MIN || y_ext < AREA_EXTEND_MIN)
	{
	  check = 0;
	  fprintf(stdout, "aXe_NICBACK: Not enough coverage. xcover,ycover: %e,%e\n", x_ext, y_ext);
	}
    }

  return check;
}

gsl_vector*
get_fbck_defaults()
{
  gsl_vector *ret;

  ret = gsl_vector_alloc(10);

  /*
  gsl_vector_set(ret, 0, xean);
  gsl_vector_set(ret, 1, c0);
  gsl_vector_set(ret, 2, c1);
  gsl_vector_set(ret, 3, cov00);
  gsl_vector_set(ret, 4, cov01);
  gsl_vector_set(ret, 5, cov11);
  gsl_vector_set(ret, 6, chisq);
  */

  gsl_vector_set(ret, 0, 0.0);
  gsl_vector_set(ret, 1, 0.0);
  gsl_vector_set(ret, 2, 1.0);
  gsl_vector_set(ret, 3, 1.0);
  gsl_vector_set(ret, 4, 0.0);
  gsl_vector_set(ret, 5, 1.0);
  gsl_vector_set(ret, 6, 0.0);

  return ret;
}

/**
 * Function: clipp_fbck_data
 * The subroutine applies kappa-sigma clipping to a set of data points.
 * The difference between the measured values and the expected values
 * according to linear regression are determined.
 * Data which is outside of the accepted interval is excluded by
 * setting the weight to '0.0'. If at least one value was clipped,
 * '1' is returned, otherwise '0' (if all values are within the
 * accepted interval.
 *
 * Parameters:
 * @param fbck_data - the data used for the fits
 * @param lfit      - the result of the fit
 * @param kappa     - the kappa value
 *
 * Returns:
 * @return clipped - boolean to indicate clipped values
 */
int
clipp_fbck_data(fitbck_data *fbck_data, const gsl_vector *lfit, const float kappa)
{
  int clipped=0;
  int nclipps=0;

  int ndata=0;
  int index;

  double dy_mean=0.0;
  double stdev=0.0;
  double absdev;
  double dy_act;

  double *y_diff;

  // allocate memory for the tmp-vector
  y_diff = (double *) malloc(fbck_data->n_data * sizeof(double));

  // go over all data values
  for (index=0; index < fbck_data->n_data; index++)
    {
      // check whether the weight is not NULL
      if (fbck_data->e_values[index])
	{
	  // compute the difference from the data point
	  // to the prediction from the fit
	  dy_act = (fbck_data->x_values[index] - gsl_vector_get(lfit, 0)) * gsl_vector_get(lfit, 2)
	    + gsl_vector_get(lfit, 1)-fbck_data->y_values[index];

	  // fill the tmp vector
	  y_diff[ndata] = dy_act;

	  // pre-comute the mean
	  dy_mean += dy_act;

	  // enhance the counter
	  // of non-NULL values
	  ndata++;
	}
    }

  // compute the mean offset
  dy_mean = dy_mean / (double)ndata;

  // compute the standard deviation
  stdev = comp_stdev_from_array(y_diff, ndata, dy_mean);

  // get the maximum allowed
  // deviation
  absdev = kappa*stdev;

  // go over the data
  for (index=0; index < fbck_data->n_data; index++)
    {
      // check whether the data has weigth
      if (fbck_data->e_values[index])
	{
	  // compute the absolute difference from the data point
	  // to the prediction from the fit
	  dy_act = fabs((fbck_data->x_values[index] - gsl_vector_get(lfit, 0)) * gsl_vector_get(lfit, 2)
			+ gsl_vector_get(lfit, 1)-fbck_data->y_values[index]);

	  // check whether the difference
	  // is larger than allowed
	  if (dy_act > absdev)
	    {
	      // make the weight NULL
	      fbck_data->e_values[index] = 0.0;

	      // set the clipped flagg
	      clipped=1;
	      nclipps++;
	    }
	}
    }

  fprintf(stdout, "Number of clipped pixels: %i\n", nclipps);

  // release memory
  free(y_diff);

  // return the integer
  // indicating clipped values
  return clipped;
}

/**
 * Function: comp_stdev_from_array
 * The subroutine computes the standard deviation
 * of a data vector, using the known mean value.
 *
 * Parameters:
 * @param data  - the data vector
 * @param ndata - number of data points to use
 * @param mean  - mean value of the data points
 *
 * Returns:
 * @return clipped - boolean to indicate clipped values
 */
double
comp_stdev_from_array(const double *data, const int ndata, const double mean)
{
  int index=0;

  double stdev=0.0;

  // go over the data array
  for (index=0; index < ndata; index++)
    {
      // compute and add the square difference to the mean
      stdev += (data[index] - mean) * (data[index] - mean);
    }

  // compute the average square difference
  if (ndata > 1)
    stdev /= ((double)ndata-1);

  // compute the final
  // standard deviation
  stdev = sqrt(stdev);

  // return the stdev
  return stdev;
}

/**
 * Function: make_nicmos_back
 * The subroutine creates and fills a structure with pairs of correpsonding
 * from a grism image and a master sky background. Dq-masked pixels values
 * and pixels which are part of an object beam are used, but marked with
 * a zero weight.
 *
 * Parameters:
 * @param obs       - the observation for the grism image
 * @param msk_image - the maks image values
 * @param mbck_img  - the master background values
 * @param fbck_data    - structure with the data pairs
 *
 * Returns:
 * @return -
 */
void
fill_fbck_data(const observation * const obs, const gsl_matrix *msk_img,
	       const gsl_matrix *mbck_img, fitbck_data *fbck_data, char plist_name[],
	       const gsl_matrix *corr_img)
{
  int ix=0;
  int iy=0;
  int index=0;
  char Buffer[10240];
  FILE *fout;

  int px_min=20;
  int px_max=180;
  int py_min=50;
  int py_max=245;
  //int px_min=0;
  //int px_max=255;
  //int py_min=0;
  //int py_max=255;
  //10:240,15:245[10:180,20:245][20:180,50:245]

  // intitialize the index for
  // the fit_data structure
  index = 0;

  double diffval;

  fout = fopen(plist_name, "w");

  // go over all pixels
  // in the grism image
  for (ix=0; ix < (int)obs->grism->size1; ix++)
    {
      for (iy=0; iy < (int)obs->grism->size2; iy++)
	{
	  // set the master background pixel as independent variable
	  fbck_data->x_values[index] = gsl_matrix_get(mbck_img, ix, iy);

	  if (corr_img != NULL)
	    // subtract the correction value from the background value
	    diffval = gsl_matrix_get(obs->grism, ix, iy) - gsl_matrix_get(corr_img, ix, iy);
	  else
	    // use the naked background value
	    diffval = gsl_matrix_get(obs->grism, ix, iy);

	  // set the grism image pixel as dependent variable
	  //fbck_data->y_values[index] = gsl_matrix_get(obs->grism, ix, iy);
	  // set the difference as dependent variable
	  fbck_data->y_values[index] = diffval;

	  // set the x- and y-positions
	  fbck_data->x_pos[index] = ix;
	  fbck_data->y_pos[index] = iy;

	  // check whether the pixel was masked out
	  // or is part of an object
	  if (isnan(gsl_matrix_get(obs->grism, ix, iy))
		    || gsl_matrix_get(msk_img, ix, iy) < -9.0e+05)
	    // mask the pixel in the array
	    fbck_data->e_values[index] = 0.0;
	  else
	    {
	      // give the pixel full weight
	      //x,y,value_back,value_grism_imag
	      fbck_data->e_values[index] = 1.0;
	      sprintf (Buffer, "%i %i %e %e\n",ix, iy, gsl_matrix_get(obs->grism, ix, iy), gsl_matrix_get(mbck_img, ix, iy));
	      fputs (Buffer, fout);
	    }

	  // check whether the pixel is in the
	  // allowed are
	  if (ix < px_min || ix > px_max
	      || iy < py_min || iy > py_max)
	    // mask the pixel in the array
	    fbck_data->e_values[index] = 0.0;
	  // enhance the counter
	  index++;
	}
    }

  fclose (fout);
}

/*
 * Function: free_fitbck_data
 * The function releases the memory allocated in
 * a fit-data structure
 *
 * Parameters:
 * @param fbck_data - the allocated structure
 *
 * Returns:
 * @return -
 */
void
free_fitbck_data(fitbck_data *fbck_data)
{
  free(fbck_data->x_values);
  free(fbck_data->y_values);
  free(fbck_data->e_values);
  free(fbck_data->x_pos);
  free(fbck_data->y_pos);

  free(fbck_data);

  fbck_data = NULL;
}


#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_cblas.h>

#include "tests.h"

void
test_syrk (void) {
const double flteps = 1e-4, dbleps = 1e-6;
  {
   int order = 101;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha = -1.0f;
   float beta = 0.1f;
   float A[] = { 0.412f, -0.229f };
   int lda = 1;
   float C[] = { 0.628f, -0.664f, -0.268f, 0.096f };
   int ldc = 2;
   float C_expected[] = { -0.106944f, 0.027948f, -0.268f, -0.042841f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1566)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha = -1.0f;
   float beta = 0.1f;
   float A[] = { 0.101f, -0.653f };
   int lda = 2;
   float C[] = { 0.432f, 0.107f, -0.952f, -0.532f };
   int ldc = 2;
   float C_expected[] = { 0.032999f, 0.107f, -0.029247f, -0.479609f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1567)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha = 1.0f;
   float beta = 0.1f;
   float A[] = { 0.79f, 0.595f };
   int lda = 2;
   float C[] = { 0.257f, 0.183f, -0.021f, -0.053f };
   int ldc = 2;
   float C_expected[] = { 0.6498f, 0.48835f, -0.021f, 0.348725f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1568)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha = 1.0f;
   float beta = 0.1f;
   float A[] = { -0.181f, -0.654f };
   int lda = 1;
   float C[] = { -0.4f, 0.615f, 0.147f, -0.163f };
   int ldc = 2;
   float C_expected[] = { -0.007239f, 0.615f, 0.133074f, 0.411416f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1569)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha = 0.0f;
   float beta = -1.0f;
   float A[] = { -0.191f, 0.584f };
   int lda = 1;
   float C[] = { -0.719f, -0.681f, -0.003f, 0.544f };
   int ldc = 2;
   float C_expected[] = { 0.719f, -0.681f, 0.003f, -0.544f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1570)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha = 0.0f;
   float beta = -1.0f;
   float A[] = { 0.788f, 0.041f };
   int lda = 2;
   float C[] = { 0.029f, 0.365f, 0.739f, -0.769f };
   int ldc = 2;
   float C_expected[] = { -0.029f, -0.365f, 0.739f, 0.769f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1571)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha = -0.3f;
   float beta = -1.0f;
   float A[] = { 0.733f, 0.678f };
   int lda = 2;
   float C[] = { -0.941f, 0.96f, 0.07f, -0.295f };
   int ldc = 2;
   float C_expected[] = { 0.779813f, 0.96f, -0.219092f, 0.157095f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1572)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha = -0.3f;
   float beta = -1.0f;
   float A[] = { -0.87f, 0.675f };
   int lda = 1;
   float C[] = { -0.602f, -0.432f, -0.984f, 0.384f };
   int ldc = 2;
   float C_expected[] = { 0.37493f, 0.608175f, -0.984f, -0.520687f };
   cblas_ssyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], flteps, "ssyrk(case 1573)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha = 0.1;
   double beta = -0.3;
   double A[] = { 0.169, -0.875 };
   int lda = 1;
   double C[] = { 0.159, 0.277, 0.865, 0.346 };
   int ldc = 2;
   double C_expected[] = { -0.0448439, -0.0978875, 0.865, -0.0272375 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1574)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha = 0.1;
   double beta = -0.3;
   double A[] = { 0.536, -0.725 };
   int lda = 2;
   double C[] = { 0.154, -0.445, -0.841, -0.91 };
   int ldc = 2;
   double C_expected[] = { -0.0174704, -0.445, 0.21344, 0.3255625 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1575)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha = 0;
   double beta = -1;
   double A[] = { -0.07, 0.8 };
   int lda = 2;
   double C[] = { 0.823, -0.88, -0.136, 0.793 };
   int ldc = 2;
   double C_expected[] = { -0.823, 0.88, -0.136, -0.793 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1576)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha = 0;
   double beta = -1;
   double A[] = { -0.058, 0.649 };
   int lda = 1;
   double C[] = { -0.187, 0.294, -0.004, -0.933 };
   int ldc = 2;
   double C_expected[] = { 0.187, 0.294, 0.004, 0.933 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1577)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha = 1;
   double beta = -1;
   double A[] = { 0.263, -0.289 };
   int lda = 1;
   double C[] = { 0.554, -0.679, 0.993, 0.758 };
   int ldc = 2;
   double C_expected[] = { -0.484831, -0.679, -1.069007, -0.674479 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1578)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha = 1;
   double beta = -1;
   double A[] = { -0.265, -0.837 };
   int lda = 2;
   double C[] = { -0.994, 0.967, -0.34, -0.069 };
   int ldc = 2;
   double C_expected[] = { 1.064225, -0.745195, -0.34, 0.769569 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1579)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha = -0.3;
   double beta = 1;
   double A[] = { -0.464, 0.394 };
   int lda = 2;
   double C[] = { -0.45, -0.447, 0.649, 0.055 };
   int ldc = 2;
   double C_expected[] = { -0.5145888, -0.447, 0.7038448, 0.0084292 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1580)");
     }
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha = -0.3;
   double beta = 1;
   double A[] = { 0.815, 0.168 };
   int lda = 1;
   double C[] = { 0.817, -0.957, -0.395, -0.382 };
   int ldc = 2;
   double C_expected[] = { 0.6177325, -0.998076, -0.395, -0.3904672 };
   cblas_dsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[i], C_expected[i], dbleps, "dsyrk(case 1581)");
     }
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha[2] = {0.0f, 0.0f};
   float beta[2] = {-0.3f, 0.1f};
   float A[] = { 0.447f, -0.507f, -0.425f, 0.701f };
   int lda = 1;
   float C[] = { 0.16f, -0.245f, 0.922f, -0.437f, 0.24f, 0.008f, -0.095f, 0.749f };
   int ldc = 2;
   float C_expected[] = { -0.0235f, 0.0895f, -0.2329f, 0.2233f, 0.24f, 0.008f, -0.0464f, -0.2342f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1582) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1582) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha[2] = {0.0f, 0.0f};
   float beta[2] = {-0.3f, 0.1f};
   float A[] = { -0.421f, -0.435f, -0.914f, -0.493f };
   int lda = 2;
   float C[] = { -0.761f, -0.38f, 0.043f, -0.999f, 0.779f, 0.238f, 0.082f, 0.394f };
   int ldc = 2;
   float C_expected[] = { 0.2663f, 0.0379f, 0.043f, -0.999f, -0.2575f, 0.0065f, -0.064f, -0.11f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1583) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1583) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {-0.3f, 0.1f};
   float A[] = { 0.827f, -0.896f, 0.417f, 0.865f };
   int lda = 2;
   float C[] = { -0.349f, -0.31f, 0.972f, 0.794f, -0.906f, -0.595f, -0.089f, -0.333f };
   int ldc = 2;
   float C_expected[] = { 0.254587f, 1.54008f, -1.4909f, -0.482723f, -0.906f, -0.595f, 0.634336f, -0.63041f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1584) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1584) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {-0.3f, 0.1f};
   float A[] = { 0.607f, 0.747f, -0.889f, 0.333f };
   int lda = 1;
   float C[] = { 0.244f, 0.564f, 0.009f, 0.578f, -0.827f, 0.558f, -0.337f, 0.731f };
   int ldc = 2;
   float C_expected[] = { 0.05996f, -1.05166f, 0.009f, 0.578f, 0.980674f, 0.211852f, -0.651432f, 0.339074f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1585) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1585) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha[2] = {1.0f, 0.0f};
   float beta[2] = {0.0f, 1.0f};
   float A[] = { 0.784f, -0.281f, -0.88f, 0.479f };
   int lda = 1;
   float C[] = { 0.491f, 0.531f, 0.805f, -0.097f, 0.728f, 0.674f, -0.705f, -0.754f };
   int ldc = 2;
   float C_expected[] = { 0.004695f, 0.050392f, 0.805f, -0.097f, -1.22932f, 1.35082f, 1.29896f, -1.54804f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1586) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1586) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   float alpha[2] = {1.0f, 0.0f};
   float beta[2] = {0.0f, 1.0f};
   float A[] = { 0.272f, -0.146f, 0.155f, 0.038f };
   int lda = 2;
   float C[] = { 0.533f, -0.41f, -0.904f, 0.301f, -0.836f, 0.57f, -0.374f, -0.293f };
   int ldc = 2;
   float C_expected[] = { 0.462668f, 0.453576f, -0.253292f, -0.916294f, -0.836f, 0.57f, 0.315581f, -0.36222f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1587) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1587) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {-1.0f, 0.0f};
   float A[] = { -0.055f, -0.127f, -0.896f, -0.625f };
   int lda = 2;
   float C[] = { -0.619f, 0.511f, -0.877f, 0.557f, -0.801f, -0.437f, -0.922f, 0.332f };
   int ldc = 2;
   float C_expected[] = { 0.60503f, -0.524104f, -0.877f, 0.557f, 0.652833f, 0.406905f, -0.198f, 0.080191f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1588) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1588) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {-1.0f, 0.0f};
   float A[] = { -0.528f, 0.759f, -0.079f, 0.952f };
   int lda = 1;
   float C[] = { 0.775f, 0.855f, 0.786f, 0.525f, 0.85f, 0.044f, 0.658f, 0.947f };
   int ldc = 2;
   float C_expected[] = { 0.026504f, -1.1523f, -0.223383f, -1.20586f, 0.85f, 0.044f, -0.507584f, -1.84706f };
   cblas_csyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "csyrk(case 1589) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "csyrk(case 1589) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { -0.049, -0.687, -0.434, 0.294 };
   int lda = 1;
   double C[] = { 0.937, -0.113, 0.796, 0.293, 0.876, -0.199, -0.757, -0.103 };
   int ldc = 2;
   double C_expected[] = { 0.467432, -0.045674, 1.019244, 0.576752, 0.876, -0.199, -0.65508, -0.358192 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1590) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1590) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { 0.359, -0.364, 0.926, -0.69 };
   int lda = 2;
   double C[] = { 0.306, 0.249, 0.28, 0.229, 0.866, 0.092, 0.886, -0.283 };
   int ldc = 2;
   double C_expected[] = { 0.302385, -0.012352, 0.28, 0.229, 0.947274, -0.492774, 1.267376, -1.56088 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1591) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1591) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {0, 0};
   double A[] = { 0.607, 0.555, -0.85, 0.831 };
   int lda = 2;
   double C[] = { 0.069, 0.368, 0.551, -0.912, -0.243, -0.063, -0.924, 0.192 };
   int ldc = 2;
   double C_expected[] = { -0.0855042, -0.1960886, 0.2898798, -0.1075156, -0.243, -0.063, 0.1316883, 0.4270039 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1592) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1592) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 121;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {0, 0};
   double A[] = { 0.427, 0.86, -0.136, 0.002 };
   int lda = 1;
   double C[] = { 0.398, -0.47, 0.011, -0.547, -0.106, 0.016, 0.681, 0.246 };
   int ldc = 2;
   double C_expected[] = { 0.0937373, -0.2760591, 0.011, -0.547, 0.0295482, 0.0288526, -0.0054932, 0.0020124 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1593) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1593) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {1, 0};
   double A[] = { 0.718, 0.023, 0.355, -0.492 };
   int lda = 1;
   double C[] = { -0.637, -0.727, -0.475, -0.776, 0.802, -0.55, -0.837, 0.222 };
   int ldc = 2;
   double C_expected[] = { -0.7948013, -0.6854089, -0.475, -0.776, 0.7566473, -0.4198521, -0.7672563, 0.3151921 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1594) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1594) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 111;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {1, 0};
   double A[] = { 0.209, 0.139, -0.202, -0.223 };
   int lda = 2;
   double C[] = { -0.695, 0.524, 0.212, -0.88, -0.752, 0.291, 0.684, -0.124 };
   int ldc = 2;
   double C_expected[] = { -0.7081182, 0.5090054, 0.2228348, -0.8587166, -0.752, 0.291, 0.6776683, -0.1519201 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1595) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1595) imag");
     };
   };
  };

  {
   int order = 101;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {1, 0};
   double A[] = { -0.365, -0.624, 0.632, 0.348 };
   int lda = 2;
   double C[] = { 0.877, 0.927, -0.377, 0.967, 0.008, 0.292, -0.779, 0.794 };
   int ldc = 2;
   double C_expected[] = { 0.9082933, 0.7647289, -0.377, 0.967, 0.0641972, 0.4470636, -0.9064832, 0.6898704 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1596) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1596) imag");
     };
   };
  };

  {
   int order = 102;
   int uplo = 122;
   int trans = 112;
   int N = 2;
   int K = 1;
   double alpha[2] = {-0.3, 0.1};
   double beta[2] = {1, 0};
   double A[] = { -0.067, -0.586, 0.208, 0.331 };
   int lda = 1;
   double C[] = { 0.584, -0.454, 0.93, 0.782, 0.489, -0.278, 0.081, -0.919 };
   int ldc = 2;
   double C_expected[] = { 0.6778197, -0.5114479, 0.8903975, 0.8432225, 0.489, -0.278, 0.0871195, -0.9669385 };
   cblas_zsyrk(order, uplo, trans, N, K, alpha, A, lda, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 4; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zsyrk(case 1597) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zsyrk(case 1597) imag");
     };
   };
  };

}


/** @file */
#ifndef __CCL_CORE_H_INCLUDED__
#define __CCL_CORE_H_INCLUDED__

#include <stdbool.h>
#include <stdio.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>

CCL_BEGIN_DECLS

/**
 * Struct containing the parameters defining a cosmology
 */
typedef struct ccl_parameters {

  // Densities: CDM, baryons, total matter, neutrinos, curvature
  double Omega_c; /**< Density of CDM relative to the critical density*/
  double Omega_b; /**< Density of baryons relative to the critical density*/
  double Omega_m; /**< Density of all matter relative to the critical density*/
  double Omega_k; /**< Density of curvature relative to the critical density*/
  double sqrtk; /**< Square root of the magnitude of curvature, k */ //TODO check
  int k_sign; /**<Sign of the curvature k */

  // Dark Energy
  double w0;
  double wa;

  // Hubble parameters
  double H0;
  double h;

  // Neutrino properties

  double Neff; // Effective number of relativistic neutrino species in the early universe.
  int N_nu_mass; // Number of species of neutrinos which are nonrelativistic today
  double N_nu_rel;  // Number of species of neutrinos which are relativistic  today
  double *mnu;  // total mass of massive neutrinos (This is a pointer so that it can hold multiple masses.)
  double sum_nu_masses; // sum of the neutrino masses.
  double Omega_n_mass; // Omega_nu for MASSIVE neutrinos
  double Omega_n_rel; // Omega_nu for MASSLESS neutrinos

  //double Neff_partial[CCL_MAX_NU_SPECIES];
  //double mnu[CCL_MAX_NU_SPECIES];

  // Primordial power spectra
  double A_s;
  double n_s;

  // Radiation parameters
  double Omega_g;
  double T_CMB;

  // BCM baryonic model parameters
  double bcm_log10Mc;
  double bcm_etab;
  double bcm_ks;

  // Derived parameters
  double sigma8;
  double Omega_l;
  double z_star;

  //Modified growth rate
  bool has_mgrowth;
  int nz_mgrowth;
  double *z_mgrowth;
  double *df_mgrowth;
} ccl_parameters;

/**
 * Struct containing references to gsl splines for distance and acceleration calculations
 */
typedef struct ccl_data{
  // These are all functions of the scale factor a.

  // Distances are defined in Mpc
  double growth0;
  gsl_spline * chi;
  gsl_spline * growth;
  gsl_spline * fgrowth;
  gsl_spline * E;
  gsl_spline * achi;

  // All these splines use the same accelerator so that
  // if one calls them successively with the same a value
  // they will be much faster.
  gsl_interp_accel *accelerator;
  gsl_interp_accel *accelerator_achi;
  gsl_interp_accel *accelerator_m;
  gsl_interp_accel *accelerator_d;
  //TODO: it seems like we're not really using this accelerator, and we should
  gsl_interp_accel *accelerator_k;

  // Function of Halo mass M

  gsl_spline * logsigma;
  gsl_spline * dlnsigma_dlogm;

  // splines for halo mass function
  gsl_spline * alphahmf;
  gsl_spline * betahmf;
  gsl_spline * gammahmf;
  gsl_spline * phihmf;
  gsl_spline * etahmf;

  // These are all functions of the wavenumber k and the scale factor a.
  gsl_spline2d * p_lin;
  gsl_spline2d * p_nl;
  double k_min_lin; //k_min  [1/Mpc] <- minimum wavenumber that the power spectrum has been computed to
  double k_min_nl;
  double k_max_lin;
  double k_max_nl;
} ccl_data;

/**
 * Sturct containing references to instances of the above structs, and boolean flags of precomputed values.
 */
typedef struct ccl_cosmology
{
  ccl_parameters    params;
  ccl_configuration config;
  ccl_data          data;

  bool computed_distances;
  bool computed_growth;
  bool computed_power;
  bool computed_sigma;
  bool computed_hmfparams;

  int status;
  //this is optional - less tedious than tracking all numerical values for status in error handler function
  char status_message[500];

  // other flags?
} ccl_cosmology;

// Label for whether you are passing a pointer to a sum of neutrino masses or a pointer to a list of 3 masses.
typedef enum ccl_mnu_convention {
  ccl_mnu_list=0,   // you pass a list of three neutrino masses
  ccl_mnu_sum =1,  // sum, defaults to splitting with normal hierarchy
  ccl_mnu_sum_inverted = 2, //sum, split with inverted hierarchy
  ccl_mnu_sum_equal = 3, //sum, split into equal masses
  // More options could be added here
} ccl_mnu_convention;

// Initialization and life cycle of objects
void ccl_cosmology_read_config(void);
ccl_cosmology * ccl_cosmology_create(ccl_parameters params, ccl_configuration config);

/* Internal function to set the status message safely. */
void ccl_cosmology_set_status_message(ccl_cosmology * cosmo, const char * status_message, ...);

// User-facing creation routines
/**
 * Create a cosmology
 * @param Omega_c Omega_c
 * @param Omega_b Omega_b
 * @param Omega_k Omega_k
 * @param Neff Number of relativistic neutrino species in the early universe
 * @param mnu neutrino mass, either sum or list of length 3
 * @param mnu_type determines neutrino mass convention (ccl_mnu_list, ccl_mnu_sum, ccl_mnu_sum_inverted, ccl_mnu_sum_equal)
 * @param w0 Dark energy EoS parameter
 * @param wa Dark energy EoS parameter
 * @param h Hubble constant in units of 100 km/s/Mpc
 * @param norm_pk the normalization of the power spectrum, either A_s or sigma8
 * @param n_s the power-law index of the power spectrum
 * @param bcm_log10Mc log10 cluster mass, one of the parameters of the BCM model
 * @param bcm_etab ejection radius parameter, one of the parameters of the BCM model
 * @param bcm_ks wavenumber for the stellar profile, one of the parameters of the BCM model
 * @param nz_mgrowth the number of redshifts where the modified growth is provided
 * @param zarr_mgrowth the array of redshifts where the modified growth is provided
 * @param dfarr_mgrowth the modified growth function vector provided
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * For specific cases see documentation for ccl_error.c
 * @return void
 */
ccl_parameters ccl_parameters_create(double Omega_c, double Omega_b, double Omega_k,
                     double Neff, double* mnu, ccl_mnu_convention mnu_type,
                     double w0, double wa, double h, double norm_pk,
                     double n_s, double bcm_log10Mc, double bcm_etab, double bcm_ks,
                     int nz_mgrowth,double *zarr_mgrowth,
                     double *dfarr_mgrowth, int *status);

/* ------- ROUTINE: ccl_parameters_create_flat_lcdm --------
INPUT: some cosmological parameters needed to create a flat LCDM model
TASK: call ccl_parameters_create to produce an LCDM model
*/
ccl_parameters ccl_parameters_create_flat_lcdm(double Omega_c, double Omega_b, double h,
					       double norm_pk, double n_s, int *status);

/**
 * Free a parameters struct
 * @param params ccl_parameters struct
 * @return void
 */
void ccl_parameters_free(ccl_parameters * params);

/**
 * Write a cosmology parameters object to a file in yaml format, .
 * @param params Cosmological parameters
 * @param filename Name of file to create and write
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * @return void
 */
void ccl_parameters_write_yaml(ccl_parameters * params, const char * filename, int * status);

/**
 * Read a cosmology parameters object from a file in yaml format, .
 * @param filename Name of existing file to read from
 * @param status Status flag. 0 if there are no errors, nonzero otherwise.
 * @return cosmo Cosmological parameters
 */
ccl_parameters ccl_parameters_read_yaml(const char * filename, int *status);

/**
 * Free a cosmology struct
 * @param cosmo Cosmological parameters
 * @return void
 */
void ccl_cosmology_free(ccl_cosmology * cosmo);

CCL_END_DECLS

#endif