/* linalg/gsl_linalg.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2006, 2007, 2019 Gerard Jungman, Brian Gough, 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.
 */

#ifndef __GSL_LINALG_H__
#define __GSL_LINALG_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_mode.h>
#include <gsl/gsl_permutation.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_blas.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
#define __BEGIN_DECLS extern "C" {
#define __END_DECLS }
#else
#define __BEGIN_DECLS           /* empty */
#define __END_DECLS             /* empty */
#endif

__BEGIN_DECLS

typedef enum
  {
    GSL_LINALG_MOD_NONE = 0,
    GSL_LINALG_MOD_TRANSPOSE = 1,
    GSL_LINALG_MOD_CONJUGATE = 2
  }
gsl_linalg_matrix_mod_t;

/* Note: You can now use the gsl_blas_dgemm function instead of matmult */

/* Simple implementation of matrix multiply.
 * Calculates C = A.B
 *
 * exceptions: GSL_EBADLEN
 */
GSL_FUN int gsl_linalg_matmult (const gsl_matrix * A,
                        const gsl_matrix * B,
                        gsl_matrix * C);

/* Simple implementation of matrix multiply.
 * Allows transposition of either matrix, so it
 * can compute A.B or Trans(A).B or A.Trans(B) or Trans(A).Trans(B)
 *
 * exceptions: GSL_EBADLEN
 */
GSL_FUN int gsl_linalg_matmult_mod (const gsl_matrix * A,
                            gsl_linalg_matrix_mod_t modA,
                            const gsl_matrix * B,
                            gsl_linalg_matrix_mod_t modB,
                            gsl_matrix * C);

/* Calculate the matrix exponential by the scaling and
 * squaring method described in Moler + Van Loan,
 * SIAM Rev 20, 801 (1978). The mode argument allows
 * choosing an optimal strategy, from the table
 * given in the paper, for a given precision.
 *
 * exceptions: GSL_ENOTSQR, GSL_EBADLEN
 */
GSL_FUN int gsl_linalg_exponential_ss(
  const gsl_matrix * A,
  gsl_matrix * eA,
  gsl_mode_t mode
  );

/* Householder Transformations */

GSL_FUN double gsl_linalg_householder_transform (gsl_vector * v);
GSL_FUN double gsl_linalg_householder_transform2 (double * alpha, gsl_vector * v);
GSL_FUN gsl_complex gsl_linalg_complex_householder_transform (gsl_vector_complex * v);

GSL_FUN int gsl_linalg_householder_hm (double tau,
                               const gsl_vector * v,
                               gsl_matrix * A);

GSL_FUN int gsl_linalg_householder_mh (double tau,
                               const gsl_vector * v,
                               gsl_matrix * A);

GSL_FUN int gsl_linalg_householder_hv (double tau,
                               const gsl_vector * v,
                               gsl_vector * w);

GSL_FUN int gsl_linalg_householder_left(const double tau,
                                const gsl_vector * v,
                                gsl_matrix * A,
                                gsl_vector * work);

GSL_FUN int gsl_linalg_householder_right(const double tau,
                                 const gsl_vector * v,
                                 gsl_matrix * A,
                                 gsl_vector * work);

GSL_FUN int gsl_linalg_householder_hm1 (double tau,
                                gsl_matrix * A);

GSL_FUN int gsl_linalg_complex_householder_hm (gsl_complex tau,
                                       const gsl_vector_complex * v,
                                       gsl_matrix_complex * A);

GSL_FUN int gsl_linalg_complex_householder_mh (gsl_complex tau,
                                       const gsl_vector_complex * v,
                                       gsl_matrix_complex * A);

GSL_FUN int gsl_linalg_complex_householder_hv (gsl_complex tau,
                                       const gsl_vector_complex * v,
                                       gsl_vector_complex * w);

GSL_FUN int gsl_linalg_complex_householder_left (const gsl_complex tau,
                                         const gsl_vector_complex * v,
                                         gsl_matrix_complex * A,
                                         gsl_vector_complex * work);

/* Hessenberg reduction */

GSL_FUN int gsl_linalg_hessenberg_decomp(gsl_matrix *A, gsl_vector *tau);
GSL_FUN int gsl_linalg_hessenberg_unpack(gsl_matrix * H, gsl_vector * tau,
                                 gsl_matrix * U);
GSL_FUN int gsl_linalg_hessenberg_unpack_accum(gsl_matrix * H, gsl_vector * tau,
                                       gsl_matrix * U);
GSL_FUN int gsl_linalg_hessenberg_set_zero(gsl_matrix * H);
GSL_FUN int gsl_linalg_hessenberg_submatrix(gsl_matrix *M, gsl_matrix *A,
                                    size_t top, gsl_vector *tau);

/* Hessenberg-Triangular reduction */

GSL_FUN int gsl_linalg_hesstri_decomp(gsl_matrix * A, gsl_matrix * B,
                              gsl_matrix * U, gsl_matrix * V,
                              gsl_vector * work);

/* Singular Value Decomposition

 * exceptions:
 */

GSL_FUN int
gsl_linalg_SV_decomp (gsl_matrix * A,
                      gsl_matrix * V,
                      gsl_vector * S,
                      gsl_vector * work);

GSL_FUN int
gsl_linalg_SV_decomp_mod (gsl_matrix * A,
                          gsl_matrix * X,
                          gsl_matrix * V,
                          gsl_vector * S,
                          gsl_vector * work);

GSL_FUN int gsl_linalg_SV_decomp_jacobi (gsl_matrix * A,
                                 gsl_matrix * Q,
                                 gsl_vector * S);

GSL_FUN int
gsl_linalg_SV_solve (const gsl_matrix * U,
                     const gsl_matrix * Q,
                     const gsl_vector * S,
                     const gsl_vector * b,
                     gsl_vector * x);

GSL_FUN int gsl_linalg_SV_leverage(const gsl_matrix *U, gsl_vector *h);

/* LU Decomposition, Gaussian elimination with partial pivoting
 */

GSL_FUN int gsl_linalg_LU_decomp (gsl_matrix * A, gsl_permutation * p, int *signum);

GSL_FUN int gsl_linalg_LU_solve (const gsl_matrix * LU,
                         const gsl_permutation * p,
                         const gsl_vector * b,
                         gsl_vector * x);

GSL_FUN int gsl_linalg_LU_svx (const gsl_matrix * LU,
                       const gsl_permutation * p,
                       gsl_vector * x);

GSL_FUN int gsl_linalg_LU_refine (const gsl_matrix * A,
                          const gsl_matrix * LU,
                          const gsl_permutation * p,
                          const gsl_vector * b,
                          gsl_vector * x,
                          gsl_vector * work);

GSL_FUN int gsl_linalg_LU_invert (const gsl_matrix * LU,
                          const gsl_permutation * p,
                          gsl_matrix * inverse);
GSL_FUN int gsl_linalg_LU_invx (gsl_matrix * LU, const gsl_permutation * p);

GSL_FUN double gsl_linalg_LU_det (gsl_matrix * LU, int signum);
GSL_FUN double gsl_linalg_LU_lndet (gsl_matrix * LU);
GSL_FUN int gsl_linalg_LU_sgndet (gsl_matrix * lu, int signum);

/* Complex LU Decomposition */

GSL_FUN int gsl_linalg_complex_LU_decomp (gsl_matrix_complex * A,
                                  gsl_permutation * p,
                                  int *signum);

GSL_FUN int gsl_linalg_complex_LU_solve (const gsl_matrix_complex * LU,
                                 const gsl_permutation * p,
                                 const gsl_vector_complex * b,
                                 gsl_vector_complex * x);

GSL_FUN int gsl_linalg_complex_LU_svx (const gsl_matrix_complex * LU,
                               const gsl_permutation * p,
                               gsl_vector_complex * x);

GSL_FUN int gsl_linalg_complex_LU_refine (const gsl_matrix_complex * A,
                                  const gsl_matrix_complex * LU,
                                  const gsl_permutation * p,
                                  const gsl_vector_complex * b,
                                  gsl_vector_complex * x,
                                  gsl_vector_complex * work);

GSL_FUN int gsl_linalg_complex_LU_invert (const gsl_matrix_complex * LU,
                                  const gsl_permutation * p,
                                  gsl_matrix_complex * inverse);
GSL_FUN int gsl_linalg_complex_LU_invx (gsl_matrix_complex * LU, const gsl_permutation * p);

GSL_FUN gsl_complex gsl_linalg_complex_LU_det (gsl_matrix_complex * LU,
                                       int signum);

GSL_FUN double gsl_linalg_complex_LU_lndet (gsl_matrix_complex * LU);

GSL_FUN gsl_complex gsl_linalg_complex_LU_sgndet (gsl_matrix_complex * LU,
                                          int signum);

/* QR decomposition */

GSL_FUN int gsl_linalg_QR_decomp (gsl_matrix * A, gsl_vector * tau);

GSL_FUN int gsl_linalg_QR_decomp_r (gsl_matrix * A, gsl_matrix * T);

GSL_FUN int gsl_linalg_QR_solve (const gsl_matrix * QR, const gsl_vector * tau, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_solve_r (const gsl_matrix * QR, const gsl_matrix * T, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_svx (const gsl_matrix * QR, const gsl_vector * tau, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_lssolve (const gsl_matrix * QR, const gsl_vector * tau, const gsl_vector * b,
                           gsl_vector * x, gsl_vector * residual);

GSL_FUN int gsl_linalg_QR_lssolve_r (const gsl_matrix * QR, const gsl_matrix * T, const gsl_vector * b,
                             gsl_vector * x, gsl_vector * work);

GSL_FUN int gsl_linalg_QR_QRsolve (gsl_matrix * Q, gsl_matrix * R, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_Rsolve (const gsl_matrix * QR, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_Rsvx (const gsl_matrix * QR, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_update (gsl_matrix * Q, gsl_matrix * R, gsl_vector * w, const gsl_vector * v);

GSL_FUN int gsl_linalg_QR_QTvec (const gsl_matrix * QR, const gsl_vector * tau, gsl_vector * v);

GSL_FUN int gsl_linalg_QR_QTvec_r(const gsl_matrix * QR, const gsl_matrix * T, gsl_vector * b, gsl_vector * work);

GSL_FUN int gsl_linalg_QR_Qvec (const gsl_matrix * QR, const gsl_vector * tau, gsl_vector * v);

GSL_FUN int gsl_linalg_QR_QTmat (const gsl_matrix * QR, const gsl_vector * tau, gsl_matrix * A);

GSL_FUN int gsl_linalg_QR_QTmat_r(const gsl_matrix * QR, const gsl_matrix * T, gsl_matrix * B, gsl_matrix * work);

GSL_FUN int gsl_linalg_QR_matQ (const gsl_matrix * QR, const gsl_vector * tau, gsl_matrix * A);

GSL_FUN int gsl_linalg_QR_unpack (const gsl_matrix * QR, const gsl_vector * tau, gsl_matrix * Q, gsl_matrix * R);

GSL_FUN int gsl_linalg_QR_unpack_r(const gsl_matrix * QR, const gsl_matrix * T, gsl_matrix * Q, gsl_matrix * R);

GSL_FUN int gsl_linalg_R_solve (const gsl_matrix * R, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_R_svx (const gsl_matrix * R, gsl_vector * x);

GSL_FUN int gsl_linalg_QR_rcond(const gsl_matrix * QR, double * rcond, gsl_vector * work);

/* Q R P^T decomposition */

GSL_FUN int gsl_linalg_QRPT_decomp (gsl_matrix * A,
                            gsl_vector * tau,
                            gsl_permutation * p,
                            int *signum,
                            gsl_vector * norm);

GSL_FUN int gsl_linalg_QRPT_decomp2 (const gsl_matrix * A,
                             gsl_matrix * q, gsl_matrix * r,
                             gsl_vector * tau,
                             gsl_permutation * p,
                             int *signum,
                             gsl_vector * norm);

GSL_FUN int gsl_linalg_QRPT_solve (const gsl_matrix * QR,
                           const gsl_vector * tau,
                           const gsl_permutation * p,
                           const gsl_vector * b,
                           gsl_vector * x);

GSL_FUN int gsl_linalg_QRPT_lssolve (const gsl_matrix * QR,
                             const gsl_vector * tau,
                             const gsl_permutation * p,
                             const gsl_vector * b,
                             gsl_vector * x,
                             gsl_vector * residual);

GSL_FUN int gsl_linalg_QRPT_lssolve2 (const gsl_matrix * QR,
                              const gsl_vector * tau,
                              const gsl_permutation * p,
                              const gsl_vector * b,
                              const size_t rank,
                              gsl_vector * x,
                              gsl_vector * residual);

GSL_FUN int gsl_linalg_QRPT_svx (const gsl_matrix * QR,
                         const gsl_vector * tau,
                         const gsl_permutation * p,
                         gsl_vector * x);

GSL_FUN int gsl_linalg_QRPT_QRsolve (const gsl_matrix * Q,
                             const gsl_matrix * R,
                             const gsl_permutation * p,
                             const gsl_vector * b,
                             gsl_vector * x);

GSL_FUN int gsl_linalg_QRPT_Rsolve (const gsl_matrix * QR,
                             const gsl_permutation * p,
                             const gsl_vector * b,
                             gsl_vector * x);

GSL_FUN int gsl_linalg_QRPT_Rsvx (const gsl_matrix * QR,
                           const gsl_permutation * p,
                           gsl_vector * x);

GSL_FUN int gsl_linalg_QRPT_update (gsl_matrix * Q,
                            gsl_matrix * R,
                            const gsl_permutation * p,
                            gsl_vector * u,
                            const gsl_vector * v);

GSL_FUN size_t gsl_linalg_QRPT_rank (const gsl_matrix * QR, const double tol);

GSL_FUN int gsl_linalg_QRPT_rcond(const gsl_matrix * QR, double * rcond, gsl_vector * work);

/* triangle on top of rectangle QR decomposition */

GSL_FUN int gsl_linalg_QR_TR_decomp (gsl_matrix * S, gsl_matrix * A, gsl_matrix * T);

/* QL decomposition */

GSL_FUN int gsl_linalg_QL_decomp (gsl_matrix * A, gsl_vector * tau);

GSL_FUN int gsl_linalg_QL_unpack (const gsl_matrix * QL, const gsl_vector * tau, gsl_matrix * Q, gsl_matrix * L);

/* COD decomposition */

GSL_FUN int gsl_linalg_COD_decomp(gsl_matrix * A, gsl_vector * tau_Q, gsl_vector * tau_Z,
                          gsl_permutation * p, size_t * rank, gsl_vector * work);

GSL_FUN int gsl_linalg_COD_decomp_e(gsl_matrix * A, gsl_vector * tau_Q, gsl_vector * tau_Z,
                            gsl_permutation * p, double tol, size_t * rank, gsl_vector * work);

GSL_FUN int gsl_linalg_COD_lssolve (const gsl_matrix * QRZT, const gsl_vector * tau_Q, const gsl_vector * tau_Z,
                            const gsl_permutation * perm, const size_t rank, const gsl_vector * b,
                            gsl_vector * x, gsl_vector * residual);

GSL_FUN int
gsl_linalg_COD_lssolve2 (const double lambda, const gsl_matrix * QRZT, const gsl_vector * tau_Q, const gsl_vector * tau_Z,
                         const gsl_permutation * perm, const size_t rank, const gsl_vector * b,
                         gsl_vector * x, gsl_vector * residual, gsl_matrix * S, gsl_vector * work);

GSL_FUN int gsl_linalg_COD_unpack(const gsl_matrix * QRZT, const gsl_vector * tau_Q,
                          const gsl_vector * tau_Z, const size_t rank, gsl_matrix * Q,
                          gsl_matrix * R, gsl_matrix * Z);

GSL_FUN int gsl_linalg_COD_matZ(const gsl_matrix * QRZT, const gsl_vector * tau_Z, const size_t rank,
                        gsl_matrix * A, gsl_vector * work);

/* LQ decomposition */

GSL_FUN int gsl_linalg_LQ_decomp (gsl_matrix * A, gsl_vector * tau);

GSL_FUN int gsl_linalg_LQ_lssolve(const gsl_matrix * LQ, const gsl_vector * tau,
                          const gsl_vector * b, gsl_vector * x, gsl_vector * residual);

GSL_FUN int gsl_linalg_LQ_QTvec(const gsl_matrix * LQ, const gsl_vector * tau, gsl_vector * v);

GSL_FUN int gsl_linalg_LQ_solve_T (const gsl_matrix * LQ, const gsl_vector * tau,
			 const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_LQ_svx_T (const gsl_matrix * LQ, const gsl_vector * tau,
                         gsl_vector * x);

GSL_FUN int gsl_linalg_LQ_lssolve_T (const gsl_matrix * LQ, const gsl_vector * tau,
			   const gsl_vector * b, gsl_vector * x,
			   gsl_vector * residual);

GSL_FUN int gsl_linalg_LQ_Lsolve_T (const gsl_matrix * LQ, const gsl_vector * b,
			  gsl_vector * x);

GSL_FUN int gsl_linalg_LQ_Lsvx_T (const gsl_matrix * LQ, gsl_vector * x);

GSL_FUN int gsl_linalg_L_solve_T (const gsl_matrix * L, const gsl_vector * b,
			gsl_vector * x);

GSL_FUN int gsl_linalg_LQ_vecQ (const gsl_matrix * LQ, const gsl_vector * tau,
			gsl_vector * v);

GSL_FUN int gsl_linalg_LQ_vecQT (const gsl_matrix * LQ, const gsl_vector * tau,
			 gsl_vector * v);

GSL_FUN int gsl_linalg_LQ_unpack (const gsl_matrix * LQ, const gsl_vector * tau,
			  gsl_matrix * Q, gsl_matrix * L);

GSL_FUN int gsl_linalg_LQ_update (gsl_matrix * Q, gsl_matrix * R,
			  const gsl_vector * v, gsl_vector * w);
GSL_FUN int gsl_linalg_LQ_LQsolve (gsl_matrix * Q, gsl_matrix * L,
			   const gsl_vector * b, gsl_vector * x);

/* P^T L Q decomposition */

GSL_FUN int gsl_linalg_PTLQ_decomp (gsl_matrix * A, gsl_vector * tau,
			    gsl_permutation * p, int *signum,
			    gsl_vector * norm);

GSL_FUN int gsl_linalg_PTLQ_decomp2 (const gsl_matrix * A, gsl_matrix * q,
			     gsl_matrix * r, gsl_vector * tau,
			     gsl_permutation * p, int *signum,
			     gsl_vector * norm);

GSL_FUN int gsl_linalg_PTLQ_solve_T (const gsl_matrix * QR,
			   const gsl_vector * tau,
			   const gsl_permutation * p,
			   const gsl_vector * b,
			   gsl_vector * x);

GSL_FUN int gsl_linalg_PTLQ_svx_T (const gsl_matrix * LQ,
                           const gsl_vector * tau,
                           const gsl_permutation * p,
                           gsl_vector * x);

GSL_FUN int gsl_linalg_PTLQ_LQsolve_T (const gsl_matrix * Q, const gsl_matrix * L,
			     const gsl_permutation * p,
			     const gsl_vector * b,
			     gsl_vector * x);

GSL_FUN int gsl_linalg_PTLQ_Lsolve_T (const gsl_matrix * LQ,
			    const gsl_permutation * p,
			    const gsl_vector * b,
			    gsl_vector * x);

GSL_FUN int gsl_linalg_PTLQ_Lsvx_T (const gsl_matrix * LQ,
			  const gsl_permutation * p,
			  gsl_vector * x);

GSL_FUN int gsl_linalg_PTLQ_update (gsl_matrix * Q, gsl_matrix * L,
			    const gsl_permutation * p,
			    const gsl_vector * v, gsl_vector * w);

/* Cholesky Decomposition */

GSL_FUN int gsl_linalg_cholesky_decomp (gsl_matrix * A);
GSL_FUN int gsl_linalg_cholesky_decomp1 (gsl_matrix * A);

GSL_FUN int gsl_linalg_cholesky_solve (const gsl_matrix * cholesky,
                               const gsl_vector * b,
                               gsl_vector * x);
GSL_FUN int gsl_linalg_cholesky_solve_mat (const gsl_matrix * cholesky,
                                   const gsl_matrix * B,
                                   gsl_matrix * X);

GSL_FUN int gsl_linalg_cholesky_svx (const gsl_matrix * cholesky,
                             gsl_vector * x);
GSL_FUN int gsl_linalg_cholesky_svx_mat (const gsl_matrix * cholesky,
                                 gsl_matrix * X);

GSL_FUN int gsl_linalg_cholesky_invert(gsl_matrix * cholesky);

/* Cholesky decomposition with unit-diagonal triangular parts.
 *   A = L D L^T, where diag(L) = (1,1,...,1).
 *   Upon exit, A contains L and L^T as for Cholesky, and
 *   the diagonal of A is (1,1,...,1). The vector Dis set
 *   to the diagonal elements of the diagonal matrix D.
 */
GSL_FUN int gsl_linalg_cholesky_decomp_unit(gsl_matrix * A, gsl_vector * D);

GSL_FUN int gsl_linalg_cholesky_scale(const gsl_matrix * A, gsl_vector * S);

GSL_FUN int gsl_linalg_cholesky_scale_apply(gsl_matrix * A, const gsl_vector * S);

GSL_FUN int gsl_linalg_cholesky_decomp2(gsl_matrix * A, gsl_vector * S);

GSL_FUN int gsl_linalg_cholesky_svx2 (const gsl_matrix * LLT,
                              const gsl_vector * S,
                              gsl_vector * x);

GSL_FUN int gsl_linalg_cholesky_solve2 (const gsl_matrix * LLT,
                                const gsl_vector * S,
                                const gsl_vector * b,
                                gsl_vector * x);

GSL_FUN int gsl_linalg_cholesky_rcond (const gsl_matrix * LLT, double * rcond,
                               gsl_vector * work);

/* Complex Cholesky Decomposition */

GSL_FUN int gsl_linalg_complex_cholesky_decomp (gsl_matrix_complex * A);

GSL_FUN int gsl_linalg_complex_cholesky_solve (const gsl_matrix_complex * cholesky,
                                       const gsl_vector_complex * b,
                                       gsl_vector_complex * x);

GSL_FUN int gsl_linalg_complex_cholesky_svx (const gsl_matrix_complex * cholesky,
                                     gsl_vector_complex * x);

GSL_FUN int gsl_linalg_complex_cholesky_invert(gsl_matrix_complex * cholesky);

/* Pivoted Cholesky LDLT decomposition */

GSL_FUN int gsl_linalg_pcholesky_decomp (gsl_matrix * A, gsl_permutation * p);

GSL_FUN int gsl_linalg_pcholesky_solve(const gsl_matrix * LDLT,
                               const gsl_permutation * p,
                               const gsl_vector * b,
                               gsl_vector * x);

GSL_FUN int gsl_linalg_pcholesky_svx(const gsl_matrix * LDLT,
                             const gsl_permutation * p,
                             gsl_vector * x);

GSL_FUN int gsl_linalg_pcholesky_decomp2(gsl_matrix * A, gsl_permutation * p,
                                 gsl_vector * S);

GSL_FUN int gsl_linalg_pcholesky_solve2(const gsl_matrix * LDLT,
                                const gsl_permutation * p,
                                const gsl_vector * S,
                                const gsl_vector * b,
                                gsl_vector * x);

GSL_FUN int gsl_linalg_pcholesky_svx2(const gsl_matrix * LDLT,
                              const gsl_permutation * p,
                              const gsl_vector * S,
                              gsl_vector * x);

GSL_FUN int gsl_linalg_pcholesky_invert(const gsl_matrix * LDLT, const gsl_permutation * p,
                                gsl_matrix * Ainv);

GSL_FUN int gsl_linalg_pcholesky_rcond (const gsl_matrix * LDLT, const gsl_permutation * p,
                                double * rcond, gsl_vector * work);

/* Modified Cholesky decomposition */

GSL_FUN int gsl_linalg_mcholesky_decomp (gsl_matrix * A, gsl_permutation * p,
                                 gsl_vector * E);

GSL_FUN int gsl_linalg_mcholesky_solve(const gsl_matrix * LDLT,
                               const gsl_permutation * p,
                               const gsl_vector * b,
                               gsl_vector * x);

GSL_FUN int gsl_linalg_mcholesky_svx(const gsl_matrix * LDLT,
                             const gsl_permutation * p,
                             gsl_vector * x);

GSL_FUN int gsl_linalg_mcholesky_rcond (const gsl_matrix * LDLT, const gsl_permutation * p,
                                double * rcond, gsl_vector * work);

GSL_FUN int gsl_linalg_mcholesky_invert(const gsl_matrix * LDLT, const gsl_permutation * p,
                                gsl_matrix * Ainv);

/* Banded Cholesky decomposition */

GSL_FUN int gsl_linalg_cholesky_band_decomp(gsl_matrix * A);

GSL_FUN int gsl_linalg_cholesky_band_solve (const gsl_matrix * LLT, const gsl_vector * b,
                                    gsl_vector * x);

GSL_FUN int gsl_linalg_cholesky_band_svx (const gsl_matrix * LLT, gsl_vector * x);

GSL_FUN int gsl_linalg_cholesky_band_invert (const gsl_matrix * LLT, gsl_matrix * Ainv);

GSL_FUN int gsl_linalg_cholesky_band_unpack (const gsl_matrix * LLT, gsl_matrix * L);

GSL_FUN int gsl_linalg_cholesky_band_rcond (const gsl_matrix * LLT, double * rcond, gsl_vector * work);

/* L D L^T decomposition */

GSL_FUN int gsl_linalg_ldlt_decomp (gsl_matrix * A);

GSL_FUN int gsl_linalg_ldlt_solve (const gsl_matrix * LDLT, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_ldlt_svx (const gsl_matrix * LDLT, gsl_vector * x);

GSL_FUN int gsl_linalg_ldlt_rcond (const gsl_matrix * LDLT, double * rcond, gsl_vector * work);

/* Banded L D L^T decomposition */

GSL_FUN int gsl_linalg_ldlt_band_decomp (gsl_matrix * A);

GSL_FUN int gsl_linalg_ldlt_band_solve (const gsl_matrix * LDLT, const gsl_vector * b, gsl_vector * x);

GSL_FUN int gsl_linalg_ldlt_band_svx (const gsl_matrix * LDLT, gsl_vector * x);

GSL_FUN int gsl_linalg_ldlt_band_unpack (const gsl_matrix * LDLT, gsl_matrix * L, gsl_vector * D);

GSL_FUN int gsl_linalg_ldlt_band_rcond (const gsl_matrix * LDLT, double * rcond, gsl_vector * work);

/* Symmetric to symmetric tridiagonal decomposition */

GSL_FUN int gsl_linalg_symmtd_decomp (gsl_matrix * A,
                              gsl_vector * tau);

GSL_FUN int gsl_linalg_symmtd_unpack (const gsl_matrix * A,
                              const gsl_vector * tau,
                              gsl_matrix * Q,
                              gsl_vector * diag,
                              gsl_vector * subdiag);

GSL_FUN int gsl_linalg_symmtd_unpack_T (const gsl_matrix * A,
                                gsl_vector * diag,
                                gsl_vector * subdiag);

/* Hermitian to symmetric tridiagonal decomposition */

GSL_FUN int gsl_linalg_hermtd_decomp (gsl_matrix_complex * A,
                              gsl_vector_complex * tau);

GSL_FUN int gsl_linalg_hermtd_unpack (const gsl_matrix_complex * A,
                              const gsl_vector_complex * tau,
                              gsl_matrix_complex * U,
                              gsl_vector * diag,
                              gsl_vector * sudiag);

GSL_FUN int gsl_linalg_hermtd_unpack_T (const gsl_matrix_complex * A,
                                gsl_vector * diag,
                                gsl_vector * subdiag);

/* Linear Solve Using Householder Transformations

 * exceptions:
 */

GSL_FUN int gsl_linalg_HH_solve (gsl_matrix * A, const gsl_vector * b, gsl_vector * x);
GSL_FUN int gsl_linalg_HH_svx (gsl_matrix * A, gsl_vector * x);

/* Linear solve for a symmetric tridiagonal system.

 * The input vectors represent the NxN matrix as follows:
 *
 *     diag[0]  offdiag[0]             0    ...
 *  offdiag[0]     diag[1]    offdiag[1]    ...
 *           0  offdiag[1]       diag[2]    ...
 *           0           0    offdiag[2]    ...
 *         ...         ...           ...    ...
 */
GSL_FUN int gsl_linalg_solve_symm_tridiag (const gsl_vector * diag,
                                   const gsl_vector * offdiag,
                                   const gsl_vector * b,
                                   gsl_vector * x);

/* Linear solve for a nonsymmetric tridiagonal system.

 * The input vectors represent the NxN matrix as follows:
 *
 *       diag[0]  abovediag[0]              0    ...
 *  belowdiag[0]       diag[1]   abovediag[1]    ...
 *             0  belowdiag[1]        diag[2]    ...
 *             0             0   belowdiag[2]    ...
 *           ...           ...            ...    ...
 */
GSL_FUN int gsl_linalg_solve_tridiag (const gsl_vector * diag,
                                   const gsl_vector * abovediag,
                                   const gsl_vector * belowdiag,
                                   const gsl_vector * b,
                                   gsl_vector * x);

/* Linear solve for a symmetric cyclic tridiagonal system.

 * The input vectors represent the NxN matrix as follows:
 *
 *      diag[0]  offdiag[0]             0   .....  offdiag[N-1]
 *   offdiag[0]     diag[1]    offdiag[1]   .....
 *            0  offdiag[1]       diag[2]   .....
 *            0           0    offdiag[2]   .....
 *          ...         ...
 * offdiag[N-1]         ...
 */
GSL_FUN int gsl_linalg_solve_symm_cyc_tridiag (const gsl_vector * diag,
                                       const gsl_vector * offdiag,
                                       const gsl_vector * b,
                                       gsl_vector * x);

/* Linear solve for a nonsymmetric cyclic tridiagonal system.

 * The input vectors represent the NxN matrix as follows:
 *
 *        diag[0]  abovediag[0]             0   .....  belowdiag[N-1]
 *   belowdiag[0]       diag[1]  abovediag[1]   .....
 *              0  belowdiag[1]       diag[2]
 *              0             0  belowdiag[2]   .....
 *            ...           ...
 * abovediag[N-1]           ...
 */
GSL_FUN int gsl_linalg_solve_cyc_tridiag (const gsl_vector * diag,
                                  const gsl_vector * abovediag,
                                  const gsl_vector * belowdiag,
                                  const gsl_vector * b,
                                  gsl_vector * x);

/* Bidiagonal decomposition */

GSL_FUN int gsl_linalg_bidiag_decomp (gsl_matrix * A,
                              gsl_vector * tau_U,
                              gsl_vector * tau_V);

GSL_FUN int gsl_linalg_bidiag_unpack (const gsl_matrix * A,
                              const gsl_vector * tau_U,
                              gsl_matrix * U,
                              const gsl_vector * tau_V,
                              gsl_matrix * V,
                              gsl_vector * diag,
                              gsl_vector * superdiag);

GSL_FUN int gsl_linalg_bidiag_unpack2 (gsl_matrix * A,
                               gsl_vector * tau_U,
                               gsl_vector * tau_V,
                               gsl_matrix * V);

GSL_FUN int gsl_linalg_bidiag_unpack_B (const gsl_matrix * A,
                                gsl_vector * diag,
                                gsl_vector * superdiag);

/* Balancing */

GSL_FUN int gsl_linalg_balance_matrix (gsl_matrix * A, gsl_vector * D);
GSL_FUN int gsl_linalg_balance_accum (gsl_matrix * A, gsl_vector * D);
GSL_FUN int gsl_linalg_balance_columns (gsl_matrix * A, gsl_vector * D);

/* condition estimation */

GSL_FUN int gsl_linalg_tri_rcond(CBLAS_UPLO_t Uplo, const gsl_matrix * A, double * rcond, gsl_vector * work);
GSL_FUN int gsl_linalg_tri_upper_rcond(const gsl_matrix * A, double * rcond, gsl_vector * work);
GSL_FUN int gsl_linalg_tri_lower_rcond(const gsl_matrix * A, double * rcond, gsl_vector * work);
GSL_FUN int gsl_linalg_invnorm1(const size_t N,
                        int (* Ainvx)(CBLAS_TRANSPOSE_t TransA, gsl_vector * x, void * params),
                        void * params, double * Ainvnorm, gsl_vector * work);

/* triangular matrices */

GSL_FUN int gsl_linalg_tri_upper_invert(gsl_matrix * T);
GSL_FUN int gsl_linalg_tri_lower_invert(gsl_matrix * T);
GSL_FUN int gsl_linalg_tri_upper_unit_invert(gsl_matrix * T);
GSL_FUN int gsl_linalg_tri_lower_unit_invert(gsl_matrix * T);

GSL_FUN int gsl_linalg_tri_invert(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix * T);
GSL_FUN int gsl_linalg_complex_tri_invert(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix_complex * T);

GSL_FUN int gsl_linalg_tri_LTL(gsl_matrix * L);
GSL_FUN int gsl_linalg_tri_UL(gsl_matrix * LU);
GSL_FUN int gsl_linalg_complex_tri_LHL(gsl_matrix_complex * L);
GSL_FUN int gsl_linalg_complex_tri_UL(gsl_matrix_complex * LU);

GSL_FUN INLINE_DECL void gsl_linalg_givens (const double a, const double b,
                                    double *c, double *s);
GSL_FUN INLINE_DECL void gsl_linalg_givens_gv (gsl_vector * v, const size_t i,
                                       const size_t j, const double c,
                                       const double s);

#ifdef HAVE_INLINE

/* Generate a Givens rotation (cos,sin) which takes v=(x,y) to (|v|,0)
   From Golub and Van Loan, "Matrix Computations", Section 5.1.8 */
INLINE_FUN
void
gsl_linalg_givens (const double a, const double b, double *c, double *s)
{
  if (b == 0)
    {
      *c = 1;
      *s = 0;
    }
  else if (fabs (b) > fabs (a))
    {
      double t = -a / b;
      double s1 = 1.0 / sqrt (1 + t * t);
      *s = s1;
      *c = s1 * t;
    }
  else
    {
      double t = -b / a;
      double c1 = 1.0 / sqrt (1 + t * t);
      *c = c1;
      *s = c1 * t;
    }
} /* gsl_linalg_givens() */

INLINE_FUN
void
gsl_linalg_givens_gv (gsl_vector * v, const size_t i, const size_t j,
                      const double c, const double s)
{
  /* Apply rotation to vector v' = G^T v */

  double vi = gsl_vector_get (v, i);
  double vj = gsl_vector_get (v, j);
  gsl_vector_set (v, i, c * vi - s * vj);
  gsl_vector_set (v, j, s * vi + c * vj);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_LINALG_H__ */


/**
 *
 * @file testing_csymm.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated c Tue Jan  7 11:45:18 2014
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>
#include "testing_cmain.h"

static int check_solution(PLASMA_enum transA, PLASMA_enum transB, int M, int N,
                          PLASMA_Complex32_t alpha, PLASMA_Complex32_t *A, int LDA,
                          PLASMA_Complex32_t *B, int LDB,
                          PLASMA_Complex32_t beta, PLASMA_Complex32_t *Cref, PLASMA_Complex32_t *Cplasma, int LDC);

int testing_csymm(int argc, char **argv)
{
    /* Check for number of arguments*/
    if ( argc != 7 ){
        USAGE("SYMM", "alpha beta M N K LDA LDB LDC",
              "   - alpha : alpha coefficient \n"
              "   - beta : beta coefficient \n"
              "   - M : number of rows of matrices A and C \n"
              "   - N : number of columns of matrices B and C \n"
              "   - LDA : leading dimension of matrix A \n"
              "   - LDB : leading dimension of matrix B \n"
              "   - LDC : leading dimension of matrix C\n");
        return -1;
    }

    PLASMA_Complex32_t alpha = (PLASMA_Complex32_t) atol(argv[0]);
    PLASMA_Complex32_t beta  = (PLASMA_Complex32_t) atol(argv[1]);
    int M     = atoi(argv[2]);
    int N     = atoi(argv[3]);
    int LDA   = atoi(argv[4]);
    int LDB   = atoi(argv[5]);
    int LDC   = atoi(argv[6]);
    int MNmax = max(M, N);

    float eps;
    int info_solution;
    int i, j, s, u;
    int LDAxM = LDA*MNmax;
    int LDBxN = LDB*N;
    int LDCxN = LDC*N;

    PLASMA_Complex32_t *A      = (PLASMA_Complex32_t *)malloc(LDAxM*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *B      = (PLASMA_Complex32_t *)malloc(LDBxN*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *C      = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *Cinit  = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *Cfinal = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));

    /* Check if unable to allocate memory */
    if ((!A)||(!B)||(!Cinit)||(!Cfinal)){
        printf("Out of Memory \n ");
        return -2;
    }

    eps = LAPACKE_slamch_work('e');

    printf("\n");
    printf("------ TESTS FOR PLASMA CSYMM ROUTINE -------  \n");
    printf("            Size of the Matrix %d by %d\n", M, N);
    printf("\n");
    printf(" The matrix A is randomly generated for each test.\n");
    printf("============\n");
    printf(" The relative machine precision (eps) is to be %e \n",eps);
    printf(" Computational tests pass if scaled residuals are less than 10.\n");

    /*----------------------------------------------------------
    *  TESTING CSYMM
    */

    /* Initialize A */
    PLASMA_cplgsy( (float)0., MNmax, A, LDA, 51 );

    /* Initialize B */
    LAPACKE_clarnv_work(IONE, ISEED, LDBxN, B);

    /* Initialize C */
    LAPACKE_clarnv_work(IONE, ISEED, LDCxN, C);

    for (s=0; s<2; s++) {
        for (u=0; u<2; u++) {

            /* Initialize  Cinit / Cfinal */
            for ( i = 0; i < M; i++)
                for (  j = 0; j < N; j++)
                    Cinit[LDC*j+i] = C[LDC*j+i];
            for ( i = 0; i < M; i++)
                for (  j = 0; j < N; j++)
                    Cfinal[LDC*j+i] = C[LDC*j+i];

            /* PLASMA CSYMM */
            PLASMA_csymm(side[s], uplo[u], M, N, alpha, A, LDA, B, LDB, beta, Cfinal, LDC);

            /* Check the solution */
            info_solution = check_solution(side[s], uplo[u], M, N, alpha, A, LDA, B, LDB, beta, Cinit, Cfinal, LDC);

            if (info_solution == 0) {
                printf("***************************************************\n");
                printf(" ---- TESTING CSYMM (%5s, %5s) ....... PASSED !\n", sidestr[s], uplostr[u]);
                printf("***************************************************\n");
            }
            else {
                printf("************************************************\n");
                printf(" - TESTING CSYMM (%s, %s) ... FAILED !\n", sidestr[s], uplostr[u]);
                printf("************************************************\n");
            }
        }
    }

    free(A); free(B); free(C);
    free(Cinit); free(Cfinal);

    return 0;
}

/*--------------------------------------------------------------
 * Check the solution
 */

static int check_solution(PLASMA_enum side, PLASMA_enum uplo, int M, int N,
                   PLASMA_Complex32_t alpha, PLASMA_Complex32_t *A, int LDA,
                   PLASMA_Complex32_t *B, int LDB,
                   PLASMA_Complex32_t beta, PLASMA_Complex32_t *Cref, PLASMA_Complex32_t *Cplasma, int LDC)
{
    int info_solution, NrowA;
    float Anorm, Bnorm, Cinitnorm, Cplasmanorm, Clapacknorm, Rnorm;
    float eps;
    PLASMA_Complex32_t beta_const;
    float result;
    float *work = (float *)malloc(max(M, N)* sizeof(float));

    beta_const  = (PLASMA_Complex32_t)-1.0;

    NrowA = (side == PlasmaLeft) ? M : N;
    Anorm       = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), NrowA, NrowA, A,       LDA, work);
    Bnorm       = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M,     N,     B,       LDB, work);
    Cinitnorm   = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M,     N,     Cref,    LDC, work);
    Cplasmanorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M,     N,     Cplasma, LDC, work);

    cblas_csymm(CblasColMajor, (CBLAS_SIDE)side, (CBLAS_UPLO)uplo, M, N, CBLAS_SADDR(alpha), A, LDA, B, LDB, CBLAS_SADDR(beta), Cref, LDC);

    Clapacknorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Cref, LDC, work);

    cblas_caxpy(LDC * N, CBLAS_SADDR(beta_const), Cplasma, 1, Cref, 1);

    Rnorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Cref, LDC, work);

    eps = LAPACKE_slamch_work('e');

    printf("Rnorm %e, Anorm %e, Bnorm %e, Cinitnorm %e, Cplasmanorm %e, Clapacknorm %e\n",Rnorm,Anorm,Bnorm,Cinitnorm,Cplasmanorm,Clapacknorm);

    result = Rnorm / ((Anorm + Bnorm + Cinitnorm) * N * eps);

    printf("============\n");
    printf("Checking the norm of the difference against reference CSYMM \n");
    printf("-- ||Cplasma - Clapack||_oo/((||A||_oo+||B||_oo+||C||_oo).N.eps) = %e \n", result );

    if ( isinf(Clapacknorm) || isinf(Cplasmanorm) || isnan(result) || isinf(result) || (result > 10.0) ) {
        printf("-- The solution is suspicious ! \n");
        info_solution = 1;
    }
    else {
        printf("-- The solution is CORRECT ! \n");
        info_solution= 0 ;
    }
    free(work);
    return info_solution;
}


/*
 *  iaf_cond_exp_sfa_rr.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST 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 NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef IAF_COND_EXP_SFA_RR_H
#define IAF_COND_EXP_SFA_RR_H

// Generated includes:
#include "config.h"

#ifdef HAVE_GSL

// External includes:
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>

// Includes from nestkernel:
#include "archiving_node.h"
#include "connection.h"
#include "event.h"
#include "nest_types.h"
#include "recordables_map.h"
#include "ring_buffer.h"
#include "universal_data_logger.h"

/* BeginDocumentation
Name: iaf_cond_exp_sfa_rr - Simple conductance based leaky integrate-and-fire
                            neuron model.

Description:
iaf_cond_exp_sfa_rr is an iaf_cond_exp_sfa_rr i.e. an implementation of a
spiking neuron using IAF dynamics with conductance-based synapses,
with additional spike-frequency adaptation and relative refractory
mechanisms as described in Dayan+Abbott, 2001, page 166.

As for the iaf_cond_exp_sfa_rr, Incoming spike events induce a post-synaptic
change  of  conductance  modelled  by an  exponential  function.  The
exponential function is  normalised such that an event  of weight 1.0
results in a peak current of 1 nS.

Outgoing spike events induce a change of the adaptation and relative
refractory conductances by q_sfa and q_rr, respectively.  Otherwise
these conductances decay exponentially with time constants tau_sfa
and tau_rr, respectively.

Parameters:
The following parameters can be set in the status dictionary.

V_m        double - Membrane potential in mV
E_L        double - Leak reversal potential in mV.
C_m        double - Capacity of the membrane in pF
t_ref      double - Duration of refractory period in ms.
V_th       double - Spike threshold in mV.
V_reset    double - Reset potential of the membrane in mV.
E_ex       double - Excitatory reversal potential in mV.
E_in       double - Inhibitory reversal potential in mV.
g_L        double - Leak conductance in nS;
tau_syn_ex double - Time constant of the excitatory synaptic exponential
                    function in ms.
tau_syn_in double - Time constant of the inhibitory synaptic exponential
                    function in ms.
q_sfa      double - Outgoing spike activated quantal spike-frequency adaptation
                    conductance increase in nS.
q_rr       double - Outgoing spike activated quantal relative refractory
                    conductance increase in nS.
tau_sfa    double - Time constant of spike-frequency adaptation in ms.
tau_rr     double - Time constant of the relative refractory mechanism in ms.
E_sfa      double - spike-frequency adaptation conductance reversal potential in
                    mV.
E_rr       double - relative refractory mechanism conductance reversal potential
                    in mV.
I_e        double - an external stimulus current in pA.

Sends: SpikeEvent

Receives: SpikeEvent, CurrentEvent, DataLoggingRequest

References:

Meffin, H., Burkitt, A. N., & Grayden, D. B. (2004). An analytical
model for the large, fluctuating synaptic conductance state typical of
neocortical neurons in vivo. J.  Comput. Neurosci., 16, 159-175.

Dayan, P. and Abbott, L. F. (2001). Theoretical Neuroscience, MIT Press (p166)

Author: Sven Schrader, Eilif Muller

SeeAlso: iaf_cond_exp_sfa_rr, aeif_cond_alpha, iaf_psc_delta, iaf_psc_exp,
iaf_cond_alpha
*/

namespace nest
{
/**
 * Function computing right-hand side of ODE for GSL solver.
 * @note Must be declared here so we can befriend it in class.
 * @note Must have C-linkage for passing to GSL. Internally, it is
 *       a first-class C++ function, but cannot be a member function
 *       because of the C-linkage.
 * @note No point in declaring it inline, since it is called
 *       through a function pointer.
 * @param void* Pointer to model neuron instance.
 */
extern "C" int
iaf_cond_exp_sfa_rr_dynamics( double, const double*, double*, void* );

class iaf_cond_exp_sfa_rr : public Archiving_Node
{

public:
  iaf_cond_exp_sfa_rr();
  iaf_cond_exp_sfa_rr( const iaf_cond_exp_sfa_rr& );
  ~iaf_cond_exp_sfa_rr();

  /**
   * Import sets of overloaded virtual functions.
   * @see Technical Issues / Virtual Functions: Overriding, Overloading, and
   * Hiding
   */
  using Node::handle;
  using Node::handles_test_event;

  port send_test_event( Node&, rport, synindex, bool );

  void handle( SpikeEvent& );
  void handle( CurrentEvent& );
  void handle( DataLoggingRequest& );

  port handles_test_event( SpikeEvent&, rport );
  port handles_test_event( CurrentEvent&, rport );
  port handles_test_event( DataLoggingRequest&, rport );

  void get_status( DictionaryDatum& ) const;
  void set_status( const DictionaryDatum& );

private:
  void init_state_( const Node& proto );
  void init_buffers_();
  void calibrate();
  void update( Time const&, const long, const long );

  // END Boilerplate function declarations ----------------------------

  // Friends --------------------------------------------------------

  // make dynamics function quasi-member
  friend int
  iaf_cond_exp_sfa_rr_dynamics( double, const double*, double*, void* );

  // The next two classes need to be friends to access the State_ class/member
  friend class RecordablesMap< iaf_cond_exp_sfa_rr >;
  friend class UniversalDataLogger< iaf_cond_exp_sfa_rr >;

private:
  // ----------------------------------------------------------------

  //! Independent parameters
  struct Parameters_
  {
    double V_th_;    //!< Threshold Potential in mV
    double V_reset_; //!< Reset Potential in mV
    double t_ref_;   //!< Refractory period in ms
    double g_L;      //!< Leak Conductance in nS
    double C_m;      //!< Membrane Capacitance in pF
    double E_ex;     //!< Excitatory reversal Potential in mV
    double E_in;     //!< Inhibitory reversal Potential in mV
    double E_L;      //!< Leak reversal Potential (aka resting potential) in mV
    double tau_synE; //!< Synaptic Time Constant Excitatory Synapse in ms
    double tau_synI; //!< Synaptic Time Constant for Inhibitory Synapse in ms
    double I_e;      //!< Constant Current in pA
    double tau_sfa;  //!< spike-frequency adaptation (sfa) time constant
    double tau_rr;   //!< relative refractory (rr) time constant
    double E_sfa;    //!< spike-frequency adaptation (sfa) reversal Potential
                     //!< in mV
    double E_rr;     //!<  relative refractory (rr) reversal Potential in mV
    double q_sfa;    //!< spike-frequency adaptation (sfa) quantal conductance
                     //!< increase in nS
    double q_rr;     //!< relative refractory (rr) quantal conductance increase
                     //!< in nS

    Parameters_(); //!< Sets default parameter values

    void get( DictionaryDatum& ) const; //!< Store current values in dictionary
    void set( const DictionaryDatum& ); //!< Set values from dicitonary
  };

public:
  // ----------------------------------------------------------------

  /**
   * State variables of the model.
   * @note Copy constructor and assignment operator required because
   *       of C-style array.
   */
  struct State_
  {

    //! Symbolic indices to the elements of the state vector y
    enum StateVecElems
    {
      V_M = 0,
      G_EXC,
      G_INH,
      G_SFA,
      G_RR,
      STATE_VEC_SIZE
    };

    //! neuron state, must be C-array for GSL solver
    double y_[ STATE_VEC_SIZE ];
    int r_; //!< number of refractory steps remaining

    State_( const Parameters_& ); //!< Default initialization
    State_( const State_& );
    State_& operator=( const State_& );

    void get( DictionaryDatum& ) const;
    void set( const DictionaryDatum&, const Parameters_& );
  };

private:
  // ----------------------------------------------------------------

  /**
   * Buffers of the model.
   */
  struct Buffers_
  {
    Buffers_( iaf_cond_exp_sfa_rr& ); //!<Sets buffer pointers to 0
    //! Sets buffer pointers to 0
    Buffers_( const Buffers_&, iaf_cond_exp_sfa_rr& );

    //! Logger for all analog data
    UniversalDataLogger< iaf_cond_exp_sfa_rr > logger_;

    /** buffers and sums up incoming spikes/currents */
    RingBuffer spike_exc_;
    RingBuffer spike_inh_;
    RingBuffer currents_;

    /** GSL ODE stuff */
    gsl_odeiv_step* s_;    //!< stepping function
    gsl_odeiv_control* c_; //!< adaptive stepsize control function
    gsl_odeiv_evolve* e_;  //!< evolution function
    gsl_odeiv_system sys_; //!< struct describing system

    // IntergrationStep_ should be reset with the neuron on ResetNetwork,
    // but remain unchanged during calibration. Since it is initialized with
    // step_, and the resolution cannot change after nodes have been created,
    // it is safe to place both here.
    double step_;            //!< step size in ms
    double IntegrationStep_; //!< current integration time step, updated by GSL

    /**
     * Input current injected by CurrentEvent.
     * This variable is used to transport the current applied into the
     * _dynamics function computing the derivative of the state vector.
     * It must be a part of Buffers_, since it is initialized once before
     * the first simulation, but not modified before later Simulate calls.
     */
    double I_stim_;
  };

  // ----------------------------------------------------------------

  /**
   * Internal variables of the model.
   */
  struct Variables_
  {
    int RefractoryCounts_;
  };

  // Access functions for UniversalDataLogger -------------------------------

  //! Read out state vector elements, used by UniversalDataLogger
  template < State_::StateVecElems elem >
  double
  get_y_elem_() const
  {
    return S_.y_[ elem ];
  }

  // ----------------------------------------------------------------

  Parameters_ P_;
  State_ S_;
  Variables_ V_;
  Buffers_ B_;

  //! Mapping of recordables names to access functions
  static RecordablesMap< iaf_cond_exp_sfa_rr > recordablesMap_;
};

inline port
nest::iaf_cond_exp_sfa_rr::send_test_event( Node& target,
  rport receptor_type,
  synindex,
  bool )
{
  SpikeEvent e;
  e.set_sender( *this );
  return target.handles_test_event( e, receptor_type );
}

inline port
iaf_cond_exp_sfa_rr::handles_test_event( SpikeEvent&, rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return 0;
}

inline port
iaf_cond_exp_sfa_rr::handles_test_event( CurrentEvent&, rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return 0;
}

inline port
iaf_cond_exp_sfa_rr::handles_test_event( DataLoggingRequest& dlr,
  rport receptor_type )
{
  if ( receptor_type != 0 )
  {
    throw UnknownReceptorType( receptor_type, get_name() );
  }
  return B_.logger_.connect_logging_device( dlr, recordablesMap_ );
}

inline void
iaf_cond_exp_sfa_rr::get_status( DictionaryDatum& d ) const
{
  P_.get( d );
  S_.get( d );
  Archiving_Node::get_status( d );

  ( *d )[ names::recordables ] = recordablesMap_.get_list();
}

inline void
iaf_cond_exp_sfa_rr::set_status( const DictionaryDatum& d )
{
  Parameters_ ptmp = P_; // temporary copy in case of errors
  ptmp.set( d );         // throws if BadProperty
  State_ stmp = S_;      // temporary copy in case of errors
  stmp.set( d, ptmp );   // throws if BadProperty

  // We now know that (ptmp, stmp) are consistent. We do not
  // write them back to (P_, S_) before we are also sure that
  // the properties to be set in the parent class are internally
  // consistent.
  Archiving_Node::set_status( d );

  // if we get here, temporaries contain consistent set of properties
  P_ = ptmp;
  S_ = stmp;
}

} // namespace

#endif // HAVE_GSL
#endif // IAF_COND_EXP_SFA_RR_H


#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include <gsl/gsl_sf_gamma.h>

#include "allvars.h"
#include "proto.h"
#ifdef COSMIC_RAYS
#include "cosmic_rays.h"
#endif
#ifdef CS_MODEL
#include "cs_metals.h"
#endif

#ifdef MACHNUM
#ifdef COSMIC_RAYS
#define h  All.HubbleParam
#define cm (h/All.UnitLength_in_cm)
#define s  (h/All.UnitTime_in_s)
#define LightSpeed (2.9979e10*cm/s)
#define c2   ( LightSpeed * LightSpeed )
#endif
#endif

/*! \file init.c
 *  \brief code for initialisation of a simulation from initial conditions
 */

/*! This function reads the initial conditions, and allocates storage for the
 *  tree(s). Various variables of the particle data are initialised and An
 *  intial domain decomposition is performed. If SPH particles are present,
 *  the inial SPH smoothing lengths are determined.
 */
void init(void)
{
  int i, j;
  double a3, atime;
#ifdef COSMIC_RAYS
  int CRpop;
#endif

#if defined(COSMIC_RAYS) && defined(MACHNUM)
  double Pth1, PCR1[NUMCRPOP], rBeta[NUMCRPOP], C_phys[NUMCRPOP], q_phys[NUMCRPOP];
#endif
#ifdef CR_INITPRESSURE
  double cr_pressure, q_phys, C_phys[NUMCRPOP];
#endif
#ifdef BLACK_HOLES
  int count_holes = 0;
#endif
#ifdef CHEMISTRY
  int ifunc;
  double min_t_cool, max_t_cool;
  double min_t_elec, max_t_elec;
  double a_start, a_end;
#endif

#ifdef EULERPOTENTIALS
  double a0, a1, a2;
  double b0, b1, b2;
#endif

#ifdef START_WITH_EXTRA_NGBDEV
  double MaxNumNgbDeviationMerk;
#endif

#ifdef DISTORTIONTENSORPS
  int i1, i2;
#endif

  All.Time = All.TimeBegin;

  if(RestartFlag == 3 && RestartSnapNum < 0)
    {
      if(ThisTask == 0)
	printf("Need to give the snapshot number if FOF/SUBFIND is selected for output\n");
      endrun(0);
    }

  if(RestartFlag == 4 && RestartSnapNum < 0)
    {
      if(ThisTask == 0)
	printf("Need to give the snapshot number if snapshot should be converted\n");
      endrun(0);
    }

  switch (All.ICFormat)
    {
    case 1:
    case 2:
    case 3:
    case 4:
      if(RestartFlag >= 2 && RestartSnapNum >= 0)
	{
	  char fname[1000];

	  if(All.NumFilesPerSnapshot > 1)
	    sprintf(fname, "%s/snapdir_%03d/%s_%03d", All.OutputDir, RestartSnapNum, All.SnapshotFileBase,
		    RestartSnapNum);
	  else
	    sprintf(fname, "%s%s_%03d", All.OutputDir, All.SnapshotFileBase, RestartSnapNum);
	  read_ic(fname);
	  if(RestartFlag == 4)
	    {
	      sprintf(All.SnapshotFileBase, "%s_converted", All.SnapshotFileBase);
	      if(ThisTask == 0)
		printf("Start writing file %s\n", All.SnapshotFileBase);
	      printf("RestartSnapNum %d\n", RestartSnapNum);
	      savepositions(RestartSnapNum);
	      endrun(0);
	    }

	}
      else
	{
	  if(All.ICFormat == 4)
	    read_ic_cluster(All.InitCondFile);
	  else
	    read_ic(All.InitCondFile);
	}
      break;
    default:
      if(ThisTask == 0)
	printf("ICFormat=%d not supported.\n", All.ICFormat);
      endrun(0);
    }

  All.Time = All.TimeBegin;

#ifdef COOLING
#ifdef CS_MODEL
  if(RestartFlag == 0)
    XH = HYDROGEN_MASSFRAC;
#endif
  IonizeParams();
#endif

#ifdef CHEMISTRY
  InitChem();
#endif

  if(All.ComovingIntegrationOn)
    {
      All.Timebase_interval = (log(All.TimeMax) - log(All.TimeBegin)) / TIMEBASE;
      All.Ti_Current = 0;
      a3 = All.Time * All.Time * All.Time;
      atime = All.Time;
    }
  else
    {
      All.Timebase_interval = (All.TimeMax - All.TimeBegin) / TIMEBASE;
      All.Ti_Current = 0;
      a3 = 1;
      atime = 1;
    }

#ifdef RADTRANSFER
  All.Radiation_Ti_begstep = 0;
#endif

  set_softenings();

  All.NumCurrentTiStep = 0;	/* setup some counters */
  All.SnapshotFileCount = 0;
  if(RestartFlag == 2)
    {
      if(RestartSnapNum < 0)
	All.SnapshotFileCount = atoi(All.InitCondFile + strlen(All.InitCondFile) - 3) + 1;
      else
	All.SnapshotFileCount = RestartSnapNum + 1;
    }

#ifdef OUTPUTLINEOFSIGHT
  All.Ti_nextlineofsight = (int) (log(All.TimeFirstLineOfSight / All.TimeBegin) / All.Timebase_interval);
  if(RestartFlag == 2)
    endrun(78787);
#endif

  All.TotNumOfForces = 0;
  All.NumForcesSinceLastDomainDecomp = 0;
#if defined(MAGNETIC) && defined(BSMOOTH)
#ifdef SETMAINTIMESTEPCOUNT
  All.MainTimestepCounts = All.MainTimestepCountIni;
#else
  All.MainTimestepCounts = 0;
#endif
#endif

  All.TopNodeAllocFactor = 0.008;
  All.TreeAllocFactor = 0.7;

  All.Cadj_Cost = 1.0e-30;
  All.Cadj_Cpu = 1.0e-3;

  if(All.ComovingIntegrationOn)
    if(All.PeriodicBoundariesOn == 1)
      check_omega();

  All.TimeLastStatistics = All.TimeBegin - All.TimeBetStatistics;
#ifdef BLACK_HOLES
  All.TimeNextBlackHoleCheck = All.TimeBegin;
#endif

#ifdef BUBBLES
  if(All.ComovingIntegrationOn)
    All.TimeOfNextBubble = 1. / (1. + All.FirstBubbleRedshift);
  else
    All.TimeOfNextBubble = All.TimeBegin + All.BubbleTimeInterval / All.UnitTime_in_Megayears;
  if(ThisTask == 0)
    printf("Initial time: %g and first bubble time %g \n", All.TimeBegin, All.TimeOfNextBubble);

  if(RestartFlag == 2 && All.TimeBegin > All.TimeOfNextBubble)
    {
      printf("Restarting from the snapshot file with the wrong FirstBubbleRedshift! \n");
      endrun(0);
    }
#endif

#ifdef MULTI_BUBBLES
  if(All.ComovingIntegrationOn)
    All.TimeOfNextBubble = 1. / (1. + All.FirstBubbleRedshift);
  else
    All.TimeOfNextBubble = All.TimeBegin + All.BubbleTimeInterval / All.UnitTime_in_Megayears;
  if(ThisTask == 0)
    printf("Initial time: %g and time of the first bubbles %g \n", All.TimeBegin, All.TimeOfNextBubble);
  if(RestartFlag == 2 && All.TimeBegin > All.TimeOfNextBubble)
    {
      printf("Restarting from the snapshot file with the wrong FirstBubbleRedshift! \n");
      endrun(0);
    }
#endif

  if(All.ComovingIntegrationOn)	/*  change to new velocity variable */
    {
      for(i = 0; i < NumPart; i++)
	for(j = 0; j < 3; j++)
	  P[i].Vel[j] *= sqrt(All.Time) * All.Time;
    }

#ifdef REINIT_AT_TURNAROUND
  double turnaround_radius_local=0.0, turnaround_radius_global=0.0, v_part, r_part;

   /* get local turnaroundradius */
  for (i = 0; i < NumPart; i++)
    {
     r_part = sqrt(P[i].Pos[0]*P[i].Pos[0] + P[i].Pos[1]*P[i].Pos[1] + P[i].Pos[2]*P[i].Pos[2]);
     v_part = (P[i].Pos[0]*P[i].Vel[0]+P[i].Pos[1]*P[i].Vel[1]+P[i].Pos[2]*P[i].Vel[2]);
     if ((v_part < 0.0) && (r_part > turnaround_radius_local)) turnaround_radius_local = r_part;
    }

  /* find global turnaround radius by taking maximum of all CPUs */
  MPI_Allreduce(&turnaround_radius_local, &turnaround_radius_global, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);

  All.CurrentTurnaroundRadius = turnaround_radius_global;

  if (ThisTask == 0)
   {
    printf("REINIT_AT_TURNAROUND: current (initial) turnaround radius = %g\n", All.CurrentTurnaroundRadius);
    fflush(stdout);
   }
#endif

  for(i = 0; i < NumPart; i++)	/*  start-up initialization */
    {
      for(j = 0; j < 3; j++)
	P[i].g.GravAccel[j] = 0;

#ifdef DISTORTIONTENSORPS
      /*init tidal tensor for first output (this is not used for any calculation) */
      for(i1 = 0; i1 < 3; i1++)
	for(i2 = 0; i2 < 3; i2++)
	  P[i].tidal_tensorps[i1][i2] = 0.0;

      /* find caustics on the fly by sign analysis of configuration space distortion */
      P[i].last_stream_determinant = 1.0;
#ifdef DISTORTION_READALL
      MyDouble product_matrix[3][3];
      product_matrix[0][0] = P[i].distortion_tensorps[0][0] +
                             P[i].distortion_tensorps[0][3] * P[i].V_matrix[0][0] +
                             P[i].distortion_tensorps[0][4] * P[i].V_matrix[1][0] +
                             P[i].distortion_tensorps[0][5] * P[i].V_matrix[2][0];
      product_matrix[0][1] = P[i].distortion_tensorps[0][1] +
                             P[i].distortion_tensorps[0][3] * P[i].V_matrix[0][1] +
                             P[i].distortion_tensorps[0][4] * P[i].V_matrix[1][1] +
                             P[i].distortion_tensorps[0][5] * P[i].V_matrix[2][1];
      product_matrix[0][2] = P[i].distortion_tensorps[0][2] +
                             P[i].distortion_tensorps[0][3] * P[i].V_matrix[0][2] +
                             P[i].distortion_tensorps[0][4] * P[i].V_matrix[1][2] +
                             P[i].distortion_tensorps[0][5] * P[i].V_matrix[2][2];
      product_matrix[1][0] = P[i].distortion_tensorps[1][0] +
                             P[i].distortion_tensorps[1][3] * P[i].V_matrix[0][0] +
                             P[i].distortion_tensorps[1][4] * P[i].V_matrix[1][0] +
                             P[i].distortion_tensorps[1][5] * P[i].V_matrix[2][0];
      product_matrix[1][1] = P[i].distortion_tensorps[1][1] +
                             P[i].distortion_tensorps[1][3] * P[i].V_matrix[0][1] +
                             P[i].distortion_tensorps[1][4] * P[i].V_matrix[1][1] +
                             P[i].distortion_tensorps[1][5] * P[i].V_matrix[2][1];
      product_matrix[1][2] = P[i].distortion_tensorps[1][2] +
                             P[i].distortion_tensorps[1][3] * P[i].V_matrix[0][2] +
                             P[i].distortion_tensorps[1][4] * P[i].V_matrix[1][2] +
                             P[i].distortion_tensorps[1][5] * P[i].V_matrix[2][2];
      product_matrix[2][0] = P[i].distortion_tensorps[2][0] +
                             P[i].distortion_tensorps[2][3] * P[i].V_matrix[0][0] +
                             P[i].distortion_tensorps[2][4] * P[i].V_matrix[1][0] +
                             P[i].distortion_tensorps[2][5] * P[i].V_matrix[2][0];
      product_matrix[2][1] = P[i].distortion_tensorps[2][1] +
                             P[i].distortion_tensorps[2][3] * P[i].V_matrix[0][1] +
                             P[i].distortion_tensorps[2][4] * P[i].V_matrix[1][1] +
                             P[i].distortion_tensorps[2][5] * P[i].V_matrix[2][1];
      product_matrix[2][2] = P[i].distortion_tensorps[2][2] +
                             P[i].distortion_tensorps[2][3] * P[i].V_matrix[0][2] +
                             P[i].distortion_tensorps[2][4] * P[i].V_matrix[1][2] +
                             P[i].distortion_tensorps[2][5] * P[i].V_matrix[2][2];

      /* this determinant will change sign when we pass through a caustic -> criterion for caustics */
      P[i].last_stream_determinant = ((product_matrix[0][0]) * (product_matrix[1][1]) * (product_matrix[2][2]) +
	                              (product_matrix[0][1]) * (product_matrix[1][2]) * (product_matrix[2][0]) +
		                      (product_matrix[0][2]) * (product_matrix[1][0]) * (product_matrix[2][1]) -
		                      (product_matrix[0][2]) * (product_matrix[1][1]) * (product_matrix[2][0]) -
		                      (product_matrix[0][0]) * (product_matrix[1][2]) * (product_matrix[2][1]) -
	           	              (product_matrix[0][1]) * (product_matrix[1][0]) * (product_matrix[2][2]));

#endif
#ifdef ANNIHILATION_RADIATION
      /* integrated annihilation rate */
      P[i].annihilation = 0.0;

      P[i].rho_normed_cutoff_current = 1.0;
      P[i].rho_normed_cutoff_last = 1.0;

      P[i].stream_density = P[i].init_density;

      P[i].analytic_caustics = 0.0;
#endif

#ifdef REINIT_AT_TURNAROUND
      All.SIM_epsilon = REINIT_AT_TURNAROUND;
      /* no phase-space analysis for particles that are initially within the turnaround radius */
      if (sqrt(P[i].Pos[0]*P[i].Pos[0] + P[i].Pos[1]*P[i].Pos[1] + P[i].Pos[2]*P[i].Pos[2]) < All.CurrentTurnaroundRadius)
#ifdef DISTORTION_READALL
       P[i].turnaround_flag = 1;
#else
       P[i].turnaround_flag = -1;
#endif
      else
       P[i].turnaround_flag = 0;
#endif

#ifdef OUTPUT_LAST_CAUSTIC
      /* all entries zero -> no caustic yet */
      P[i].lc_Time       = 0.0;
      P[i].lc_Pos[0]     = 0.0;
      P[i].lc_Pos[1]     = 0.0;
      P[i].lc_Pos[2]     = 0.0;
      P[i].lc_Vel[0]     = 0.0;
      P[i].lc_Vel[1]     = 0.0;
      P[i].lc_Vel[2]     = 0.0;
      P[i].lc_rho_normed_cutoff = 0.0;

      P[i].lc_Dir_x[0]   = 0.0;
      P[i].lc_Dir_x[1]   = 0.0;
      P[i].lc_Dir_x[2]   = 0.0;
      P[i].lc_Dir_y[0]   = 0.0;
      P[i].lc_Dir_y[1]   = 0.0;
      P[i].lc_Dir_y[2]   = 0.0;
      P[i].lc_Dir_z[0]   = 0.0;
      P[i].lc_Dir_z[1]   = 0.0;
      P[i].lc_Dir_z[2]   = 0.0;

      P[i].lc_smear_x    = 0.0;
      P[i].lc_smear_y    = 0.0;
      P[i].lc_smear_z    = 0.0;
#endif

#ifdef PMGRID
      /* long range tidal field init */
      P[i].tidal_tensorpsPM[0][0] = 0;
      P[i].tidal_tensorpsPM[0][1] = 0;
      P[i].tidal_tensorpsPM[0][2] = 0;
      P[i].tidal_tensorpsPM[1][0] = 0;
      P[i].tidal_tensorpsPM[1][1] = 0;
      P[i].tidal_tensorpsPM[1][2] = 0;
      P[i].tidal_tensorpsPM[2][0] = 0;
      P[i].tidal_tensorpsPM[2][1] = 0;
      P[i].tidal_tensorpsPM[2][2] = 0;
#endif

#ifndef DISTORTION_READALL
      /*6D phase space distortion tensor equals unity in the beginning */

      /*
       * Distortion tensor D' (comoving) equals unity in the beginning
       * So the initial stream density is given by |det(D')|*mean density
       */

      /* cold dark matter => velocity dispersion=0 => d/dtD' = 0 => Z'=0 (comoving) */
      for(i1 = 0; i1 < 6; i1++)
	for(i2 = 0; i2 < 6; i2++)
	  {
	    if((i1 == i2))
	      {
		P[i].distortion_tensorps[i1][i2] = 1.0;
	      }
	    else
	      {
		P[i].distortion_tensorps[i1][i2] = 0.0;
	      }
	  }
#endif
#endif

#ifdef KEEP_DM_HSML_AS_GUESS
      if(RestartFlag != 1)
	P[i].DM_Hsml = -1;
#endif

#ifdef PMGRID
      for(j = 0; j < 3; j++)
	P[i].GravPM[j] = 0;
#endif
      P[i].Ti_begstep = 0;
      P[i].Ti_current = 0;
      P[i].TimeBin = 0;

      if(header.flag_ic_info != FLAG_SECOND_ORDER_ICS)
	P[i].OldAcc = 0;	/* Do not zero in 2lpt case as masses are stored here */

      P[i].GravCost = 1;
#if defined(EVALPOTENTIAL) || defined(COMPUTE_POTENTIAL_ENERGY)
      P[i].p.Potential = 0;
#endif
#ifdef STELLARAGE
      if(RestartFlag == 0)
	P[i].StellarAge = 0;
#endif

#ifdef METALS
#ifndef CS_MODEL
      if(RestartFlag == 0)
	P[i].Metallicity = 0;
#else
      if(RestartFlag == 0)
	{
	  for(j = 0; j < 12; j++)
	    {
	      P[i].Zm[j] = 0;
	      P[i].ZmReservoir[j] = 0;
	    }

	  P[i].Zm[6] = HYDROGEN_MASSFRAC * (P[i].Mass);
	  P[i].Zm[0] = (1 - HYDROGEN_MASSFRAC) * (P[i].Mass);
	  /*     Order of chemical elements:   He, Carbon,Mg,O,Fe,Si,H,N,Ne,S,Ca,Zn */

	  PPP[i].Hsml = 0;
#ifdef CS_FEEDBACK
	  PPP[i].EnergySN = 0;
	  PPP[i].EnergySNCold = 0;
#endif
	}
#endif

#endif

#ifdef BLACK_HOLES
      if(P[i].Type == 5)
	{
	  count_holes++;

	  if(RestartFlag == 0)
	    P[i].BH_Mass = All.SeedBlackHoleMass;
#ifdef BH_BUBBLES
	  if(RestartFlag == 0)
	    {
	      P[i].BH_Mass_bubbles = All.SeedBlackHoleMass;
	      P[i].BH_Mass_ini = All.SeedBlackHoleMass;
#ifdef UNIFIED_FEEDBACK
	      P[i].BH_Mass_radio = All.SeedBlackHoleMass;
#endif
	    }
#endif
	}
#endif
    }

#ifdef BLACK_HOLES
  MPI_Allreduce(&count_holes, &All.TotBHs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
#endif

  for(i = 0; i < TIMEBINS; i++)
    TimeBinActive[i] = 1;

  reconstruct_timebins();

#ifdef PMGRID
  All.PM_Ti_endstep = All.PM_Ti_begstep = 0;
#endif

#ifdef CONDUCTION
  All.Conduction_Ti_endstep = All.Conduction_Ti_begstep = 0;
#endif
#ifdef CR_DIFFUSION
  All.CR_Diffusion_Ti_endstep = All.CR_Diffusion_Ti_begstep = 0;
#endif

  for(i = 0; i < N_gas; i++)	/* initialize sph_properties */
    {
      for(j = 0; j < 3; j++)
	{
	  SphP[i].VelPred[j] = P[i].Vel[j];
	  SphP[i].a.HydroAccel[j] = 0;
	}

      SphP[i].e.DtEntropy = 0;

#ifdef CHEMISTRY
      SphP[i].Gamma = GAMMA;	/* set universal value */
      SphP[i].t_cool = 0;
      SphP[i].t_elec = 0;
#endif

#ifdef CS_MODEL
      SphP[i].DensityOld = 0;
#endif
#ifdef CS_FEEDBACK
      SphP[i].da.DensityAvg = 0;
      SphP[i].ea.EntropyAvg = 0;
      SphP[i].DensPromotion = 0;
      SphP[i].TempPromotion = 0;
      SphP[i].HotHsml = 0;
#endif

      if(RestartFlag == 0)
	{
#ifndef READ_HSML
	  PPP[i].Hsml = 0;
#endif
	  SphP[i].d.Density = -1;
#ifdef VOLUME_CORRECTION
	  SphP[i].DensityOld = 1;
#endif
#ifdef COOLING
	  SphP[i].Ne = 1.0;
#endif
	  SphP[i].v.DivVel = 0;
	}
#ifdef WINDS
      SphP[i].DelayTime = 0;
#endif
#ifdef SFR
      SphP[i].Sfr = 0;
#endif
#ifdef MAGNETIC
#ifdef BINISET
      SphP[i].BPred[0] = All.BiniX;
      SphP[i].BPred[1] = All.BiniY;
      SphP[i].BPred[2] = All.BiniZ;
#ifdef EULERPOTENTIALS
      if(All.BiniY == 0 && All.BiniZ == 0)
	{
	  a0 = 0;
	  a1 = All.BiniX;
	  a2 = 0;
	  b0 = 0;
	  b1 = 1;
	  b2 = 1;
	}
      else
	{
	  if(All.BiniX != 0 && (All.BiniY != 0 || All.BiniZ != 0))
	    {
	      b0 = -(All.BiniZ + All.BiniY) / All.BiniX;
	      b1 = 1;
	      b2 = 1;
	      a0 = 0;
	      a1 = -All.BiniZ / b0;
	      a2 = All.BiniY / b0;
	    }
	  else
	    {
	      a0 = a1 = a2 = b0 = b1 = b2 = 0;
	      if(ThisTask == 0)
		{
		  printf("Can not reconstruct Euler potentials from Bini values !\n");
		  endrun(6723);
		}
	    }
	}
      SphP[i].EulerA = (a0 * P[i].Pos[0] + a1 * P[i].Pos[1] + a2 * P[i].Pos[2]) * atime * All.HubbleParam;
      SphP[i].EulerB = (b0 * P[i].Pos[0] + b1 * P[i].Pos[1] + b2 * P[i].Pos[2]) * atime * All.HubbleParam;
#endif
#endif
#ifndef EULERPOTENTIALS
      for(j = 0; j < 3; j++)
	{
	  SphP[i].DtB[j] = 0;
	  SphP[i].B[j] = SphP[i].BPred[j];
	}
#endif
#ifdef TIME_DEP_MAGN_DISP
#ifdef HIGH_MAGN_DISP_START
      SphP[i].Balpha = All.ArtMagDispConst;
#else
      SphP[i].Balpha = All.ArtMagDispMin;
#endif
      SphP[i].DtBalpha = 0.0;
#endif
#ifdef DIVBCLEANING_DEDNER
      SphP[i].Phi = SphP[i].PhiPred = SphP[i].DtPhi = 0;
#endif
#endif

#ifdef TIME_DEP_ART_VISC
#ifdef HIGH_ART_VISC_START
      if(HIGH_ART_VISC_START == 0)
	SphP[i].alpha = All.ArtBulkViscConst;
      if(HIGH_ART_VISC_START > 0)
	if(P[i].Pos[0] > HIGH_ART_VISC_START)
	  SphP[i].alpha = All.ArtBulkViscConst;
	else
	  SphP[i].alpha = All.AlphaMin;
      if(HIGH_ART_VISC_START < 0)
	if(P[i].Pos[0] < -HIGH_ART_VISC_START)
	  SphP[i].alpha = All.ArtBulkViscConst;
	else
	  SphP[i].alpha = All.AlphaMin;
#else
      SphP[i].alpha = All.AlphaMin;
#endif
      SphP[i].Dtalpha = 0.0;
#endif

#if defined(BH_THERMALFEEDBACK) || defined(BH_KINETICFEEDBACK)
      SphP[i].i.Injected_BH_Energy = 0;
#endif
    }

#ifdef TWODIMS
  for(i = 0; i < NumPart; i++)
    {
      P[i].Pos[2] = 0;
      P[i].Vel[2] = 0;
    }
#endif

#ifdef ASSIGN_NEW_IDS
  assign_unique_ids();
#endif

#ifndef NOTEST_FOR_IDUNIQUENESS
  test_id_uniqueness();
#endif

  All.NumForcesSinceLastDomainDecomp = (long long) (1 + All.TotNumPart * All.TreeDomainUpdateFrequency);

  Flag_FullStep = 1;		/* to ensure that Peano-Hilber order is done */

  TreeReconstructFlag = 1;

  domain_Decomposition();	/* do initial domain decomposition (gives equal numbers of particles) */

  set_softenings();

  /* will build tree */
  ngb_treebuild();

  All.Ti_Current = 0;

#ifdef START_WITH_EXTRA_NGBDEV
  MaxNumNgbDeviationMerk = All.MaxNumNgbDeviation;
  All.MaxNumNgbDeviation = All.MaxNumNgbDeviationStart;
#endif

  if(RestartFlag != 3)
  setup_smoothinglengths();

#ifdef VORONOI
  for(i = 0; i < N_gas; i++)
    SphP[i].MaxDelaunayRadius = SphP[i].Hsml;

  voronoi_mesh();

#ifdef MESHRELAX_DENSITY_IN_INPUT
  if(RestartFlag == 0)
    for(i = 0; i < N_gas; i++)
      P[i].Mass *= SphP[i].Volume;
#endif

#ifdef VORONOI_MESHRELAX
  double mass, masstot, egy, egytot;
  for(i = 0, mass = 0, egy = 0; i < N_gas; i++)
    {
      mass += P[i].Mass;
      egy += P[i].Mass * SphP[i].Entropy;
      SphP[i].Pressure = GAMMA_MINUS1 * SphP[i].Entropy * P[i].Mass / SphP[i].Volume;
   }
  MPI_Allreduce(&mass, &masstot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
  MPI_Allreduce(&egy, &egytot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

  All.MeanMass = masstot / All.TotN_gas;
#ifdef TWODIMS
  All.MeanPressure = GAMMA_MINUS1 * egytot / (boxSize_X * boxSize_Y);
#else
  All.MeanPressure = GAMMA_MINUS1 * egytot / (boxSize_X * boxSize_Y * boxSize_Z);
#endif
#endif

  voronoi_density();

  myfree(List_P);
  myfree(ListExports);

  myfree(DT);
  myfree(DP - 5);
  myfree(VF);
#endif

#ifdef START_WITH_EXTRA_NGBDEV
  All.MaxNumNgbDeviation = MaxNumNgbDeviationMerk;
#endif

#ifdef HEALPIX
  //this should be readed in the parameterfile
  All.Nside=8;
  //
  if(ThisTask==0)
  printf(" First calculation of Healpix %i with %i \n",All.Nside,NSIDE2NPIX(All.Nside));
  // initialize the healpix array (just in case)
  All.healpixmap=(float *) malloc(NSIDE2NPIX(All.Nside) * sizeof(float));
  for(i=0;i<NSIDE2NPIX(All.Nside);i++)All.healpixmap[i]=0;

  healpix_halo(All.healpixmap);

#endif
  /* at this point, the entropy variable actually contains the
   * internal energy, read in from the initial conditions file.
   * Once the density has been computed, we can convert to entropy.
   */

  for(i = 0; i < N_gas; i++)	/* initialize sph_properties */
    {
      if(header.flag_entropy_instead_u == 0)
	{
	  if(ThisTask == 0 && i == 0)
	    printf("Converting u -> entropy !\n");
#ifndef EOS_DEGENERATE
#ifndef VORONOI_MESHRELAX
	  SphP[i].Entropy = GAMMA_MINUS1 * SphP[i].Entropy / pow(SphP[i].d.Density / a3, GAMMA_MINUS1);
#endif
#else
	  SphP[i].Entropy *= All.UnitEnergy_in_cgs;
	  /* call eos with physical units, energy and entropy are always stored in physical units */
#ifdef EOS_ENERGY
	  SphP[i].temp = 1.0;
	  eos_calc_egiven2(SphP[i].d.Density * All.UnitDensity_in_cgs, SphP[i].xnuc, SphP[i].Entropy,
			   &SphP[i].temp, &SphP[i].Pressure);
	  SphP[i].Pressure /= All.UnitPressure_in_cgs;
#else
	  SphP[i].u = SphP[i].Entropy;
	  eos_calc_egiven(SphP[i].d.Density * All.UnitDensity_in_cgs, SphP[i].xnuc, SphP[i].Entropy,
			  &SphP[i].temp, &SphP[i].Entropy);

	  /* get pressure */
	  eos_calc_sgiven(SphP[i].d.Density * All.UnitDensity_in_cgs, SphP[i].xnuc, SphP[i].Entropy,
			  &SphP[i].temp, &SphP[i].Pressure, &SphP[i].u);
	  SphP[i].Pressure /= All.UnitPressure_in_cgs;
#endif
#endif
	}
      SphP[i].e.DtEntropy = 0;

      SphP[i].v.DivVel = 0;

#ifdef MACHNUM
      SphP[i].Shock_MachNumber = 1.0;
#ifdef COSMIC_RAYS
      Pth1 = SphP[i].Entropy * pow(SphP[i].d.Density / a3, GAMMA);

#ifdef CR_IC_PHYSICAL
      for(CRpop = 0; CRpop < NUMCRPOP; CRpop++)
	{
	  C_phys[CRpop] = SphP[i].CR_C0[CRpop];
	  q_phys[CRpop] = SphP[i].CR_q0[CRpop];
	}
#else
      for(CRpop = 0; CRpop < NUMCRPOP; CRpop++)
	{
	  C_phys[CRpop] = SphP[i].CR_C0[CRpop] * pow(SphP[i].d.Density, (All.CR_Alpha[CRpop] - 1.0) / 3.0);
	  q_phys[CRpop] = SphP[i].CR_q0[CRpop] * pow(SphP[i].d.Density, 1.0 / 3.0);
	}
#endif
      SphP[i].PreShock_XCR = 0.0;
      for(CRpop = 0; CRpop < NUMCRPOP; CRpop++)
	{
	  rBeta[CRpop] = gsl_sf_beta((All.CR_Alpha[CRpop] - 2.0) * 0.5, (3.0 - All.CR_Alpha[CRpop]) * 0.5) *
	    gsl_sf_beta_inc((All.CR_Alpha[CRpop] - 2.0) * 0.5, (3.0 - All.CR_Alpha[CRpop]) * 0.5,
			    1.0 / (1.0 + q_phys[CRpop] * q_phys[CRpop]));

	  PCR1[CRpop] = C_phys[CRpop] * c2 * SphP[i].d.Density * rBeta[CRpop] / 6.0;
	  PCR1[CRpop] *= pow(atime, -3.0 * GAMMA);
	  SphP[i].PreShock_XCR += PCR1[CRpop] / Pth1;
	}

      SphP[i].PreShock_PhysicalDensity = SphP[i].d.Density / a3;
      SphP[i].PreShock_PhysicalEnergy =
	SphP[i].Entropy / GAMMA_MINUS1 * pow(SphP[i].d.Density / a3, GAMMA_MINUS1);

      SphP[i].Shock_DensityJump = 1.0001;
      SphP[i].Shock_EnergyJump = 1.0;
#endif /* COSMIC_RAYS */
#ifdef OUTPUT_PRESHOCK_CSND
      Pth1 = SphP[i].Entropy * pow(SphP[i].d.Density / a3, GAMMA);
      SphP[i].PreShock_PhysicalSoundSpeed =
	sqrt(GAMMA * Pth1 / SphP[i].d.Density) * pow(atime, -3. / 2. * GAMMA_MINUS1);
      SphP[i].PreShock_PhysicalDensity = SphP[i].d.Density / a3;
#endif /* OUTPUT_PRESHOCK_CSND */
#endif /* MACHNUM */

#ifdef REIONIZATION
      All.not_yet_reionized = 1;
#endif

#ifdef CR_IC_PHYSICAL
      /* Scale CR variables so that values from IC file are now the
       * physical values, not the adiabatic invariants
       */
      for(CRpop = 0; CRpop < NUMCRPOP; CRpop++)
	{
	  SphP[i].CR_C0[CRpop] *= pow(SphP[i].d.Density, (1.0 - All.CR_Alpha[CRpop]) / 3.0);
	  SphP[i].CR_q0[CRpop] *= pow(SphP[i].d.Density, -1.0 / 3.0);
	}
#endif

#ifdef CR_INITPRESSURE

      cr_pressure = CR_INITPRESSURE * SphP[i].Entropy * pow(SphP[i].d.Density / a3, GAMMA);
      SphP[i].Entropy *= (1 - CR_INITPRESSURE);
      q_phys = 1.685;

      for(CRpop = 0; CRpop < NUMCRPOP; CRpop++)
	{
	  C_phys[CRpop] =
	    cr_pressure / (SphP[i].d.Density / a3 * CR_Tab_Beta(q_phys, CRpop) * (C / All.UnitVelocity_in_cm_per_s) *
		       (C / All.UnitVelocity_in_cm_per_s) / 6.0);

	  SphP[i].CR_C0[CRpop] = C_phys[CRpop] * pow(SphP[i].d.Density, (1.0 - All.CR_Alpha[CRpop]) / 3.0);
	  SphP[i].CR_q0[CRpop] = q_phys * pow(SphP[i].d.Density, -1.0 / 3.0);
	}
#endif
    }

  if(RestartFlag == 3)
    {
#ifdef FOF
      fof_fof(RestartSnapNum);
#endif
      endrun(0);
    }

#ifdef CHEMISTRY
  if(ThisTask == 0)
    {
      printf("Initial abundances: \n");
      printf("HI=%g, HII=%g, HeI=%g, HeII=%g, HeIII=%g \n",
	     SphP[1].HI, SphP[1].HII, SphP[1].HeI, SphP[1].HeII, SphP[1].HeIII);

      printf("HM=%g, H2I=%g, H2II=%g, elec=%g, %d\n",
	     SphP[1].HM, SphP[1].H2I, SphP[1].H2II, SphP[1].elec, P[1].ID);

      printf("x=%g, y=%g, z=%g, vx=%g, vy=%g, vz=%g, density=%g, entropy=%g\n",
	     P[N_gas - 1].Pos[0], P[N_gas - 1].Pos[1], P[N_gas - 1].Pos[2], P[N_gas - 1].Vel[0],
	     P[N_gas - 1].Vel[1], P[N_gas - 1].Vel[2], SphP[N_gas - 1].Density, SphP[N_gas - 1].Entropy);
    }

  /* need predict the cooling time and elec_dot here */
  min_t_cool = min_t_elec = 1.0e30;
  max_t_cool = max_t_elec = -1.0e30;

  for(i = 0; i < N_gas; i++)
    {
      a_start = All.Time;
      a_end = All.Time + 0.001;	/* 0.001 as an arbitrary value */

      ifunc = compute_abundances(0, i, a_start, a_end);

      if(fabs(SphP[i].t_cool) < min_t_cool)
	min_t_cool = fabs(SphP[i].t_cool);
      if(fabs(SphP[i].t_cool) > max_t_cool)
	max_t_cool = fabs(SphP[i].t_cool);

      if(fabs(SphP[i].t_elec) < min_t_elec)
	min_t_elec = fabs(SphP[i].t_elec);
      if(fabs(SphP[i].t_elec) > max_t_elec)
	max_t_elec = fabs(SphP[i].t_elec);

    }

  fprintf(stdout, "PE %d t_cool min= %g, max= %g in yrs \n", ThisTask, min_t_cool, max_t_cool);
  fflush(stdout);
  fprintf(stdout, "PE %d t_elec min= %g, max= %g in yrs \n", ThisTask, min_t_elec, max_t_elec);
  fflush(stdout);

#endif

}

/*! This routine computes the mass content of the box and compares it to the
 * specified value of Omega-matter.  If discrepant, the run is terminated.
 */
void check_omega(void)
{
  double mass = 0, masstot, omega;
  int i;

  for(i = 0; i < NumPart; i++)
    mass += P[i].Mass;

  MPI_Allreduce(&mass, &masstot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

  omega =
    masstot / (All.BoxSize * All.BoxSize * All.BoxSize) / (3 * All.Hubble * All.Hubble / (8 * M_PI * All.G));
#ifdef TIMEDEPGRAV
  omega *= All.Gini / All.G;
#endif
  if(fabs(omega - All.Omega0) > 1.0e-3)
    {
      if(ThisTask == 0)
	{
	  printf("\n\nI've found something odd!\n");
	  printf
	    ("The mass content accounts only for Omega=%g,\nbut you specified Omega=%g in the parameterfile.\n",
	     omega, All.Omega0);
	  printf("\nI better stop.\n");

	  fflush(stdout);
	}
      endrun(1);
    }
}

/*! This function is used to find an initial smoothing length for each SPH
 *  particle. It guarantees that the number of neighbours will be between
 *  desired_ngb-MAXDEV and desired_ngb+MAXDEV. For simplicity, a first guess
 *  of the smoothing length is provided to the function density(), which will
 *  then iterate if needed to find the right smoothing length.
 */
void setup_smoothinglengths(void)
{
  int i, no, p;

  if(RestartFlag == 0)
    {
      for(i = 0; i < N_gas; i++)
	{
	  no = Father[i];

	  while(10 * All.DesNumNgb * P[i].Mass > Nodes[no].u.d.mass)
	    {
	      p = Nodes[no].u.d.father;

	      if(p < 0)
		break;

	      no = p;
	    }

#ifndef READ_HSML
#ifndef TWODIMS
	  PPP[i].Hsml =
	    pow(3.0 / (4 * M_PI) * All.DesNumNgb * P[i].Mass / Nodes[no].u.d.mass, 1.0 / 3) * Nodes[no].len;
#else
	  PPP[i].Hsml =
	    pow(1.0 / (M_PI) * All.DesNumNgb * P[i].Mass / Nodes[no].u.d.mass, 1.0 / 2) * Nodes[no].len;
#endif
	  if(PPP[i].Hsml > 200.0 * All.SofteningTable[0])
	    PPP[i].Hsml = All.SofteningTable[0];
#endif
	}
    }

#ifdef BLACK_HOLES
  if(RestartFlag == 0 || RestartFlag == 2)
    {
      for(i = 0; i < NumPart; i++)
	if(P[i].Type == 5)
	  PPP[i].Hsml = All.SofteningTable[5];
    }
#endif

#ifdef RADTRANSFER
  if(RestartFlag == 0 || RestartFlag == 2)
    {
      for(i = 0; i < NumPart; i++)
	if(P[i].Type == 4)
	  PPP[i].Hsml = All.SofteningTable[4];
    }
#endif

  density();

#if defined(MAGNETIC) && defined(BFROMROTA)
  if(RestartFlag == 0)
    {
      if(ThisTask == 0)
	printf("Converting: Vector Potential -> Bfield\n");
      rot_a();
    }
#endif

}

void assign_unique_ids(void)
{
  int i, *numpartlist;
  MyIDType idfirst;

  numpartlist = mymalloc(NTask * sizeof(int));

  MPI_Allgather(&NumPart, 1, MPI_INT, numpartlist, 1, MPI_INT, MPI_COMM_WORLD);

  idfirst = 1;

  for(i = 0; i < ThisTask; i++)
    idfirst += numpartlist[i];

  for(i = 0; i < NumPart; i++)
    {
      P[i].ID = idfirst;
      idfirst++;
    }

  myfree(numpartlist);
}

void test_id_uniqueness(void)
{
  int i;
  double t0, t1;
  MyIDType *ids, *ids_first;

  if(ThisTask == 0)
    {
      printf("Testing ID uniqueness...\n");
      fflush(stdout);
    }

  if(NumPart == 0)
    {
      printf("need at least one particle per cpu\n");
      endrun(8);
    }

  t0 = second();

#ifndef SPH_BND_PARTICLES
  ids = (MyIDType *) mymalloc(NumPart * sizeof(MyIDType));
  ids_first = (MyIDType *) mymalloc(NTask * sizeof(MyIDType));

  for(i = 0; i < NumPart; i++)
    ids[i] = P[i].ID;

  parallel_sort(ids, NumPart, sizeof(MyIDType), compare_IDs);

  for(i = 1; i < NumPart; i++)
    if(ids[i] == ids[i - 1])
      {
#ifdef LONGIDS
	printf("non-unique ID=%d%09d found on task=%d (i=%d NumPart=%d)\n",
	       (int) (ids[i] / 1000000000), (int) (ids[i] % 1000000000), ThisTask, i, NumPart);

#else
	printf("non-unique ID=%d found on task=%d   (i=%d NumPart=%d)\n", (int) ids[i], ThisTask, i, NumPart);
#endif
	endrun(12);
      }

  MPI_Allgather(&ids[0], sizeof(MyIDType), MPI_BYTE, ids_first, sizeof(MyIDType), MPI_BYTE, MPI_COMM_WORLD);

  if(ThisTask < NTask - 1)
    if(ids[NumPart - 1] == ids_first[ThisTask + 1])
      {
	printf("non-unique ID=%d found on task=%d\n", (int) ids[NumPart - 1], ThisTask);
	endrun(13);
      }

  myfree(ids_first);
  myfree(ids);
#endif

  t1 = second();

  if(ThisTask == 0)
    {
      printf("success.  took=%g sec\n", timediff(t0, t1));
      fflush(stdout);
    }
}

int compare_IDs(const void *a, const void *b)
{
  if(*((MyIDType *) a) < *((MyIDType *) b))
    return -1;

  if(*((MyIDType *) a) > *((MyIDType *) b))
    return +1;

  return 0;
}


#pragma once

#include "commons.h"
#include "Instancing.h"

#include <renderer/VertexSpecification.h>

#include <gsl/gsl>

namespace ad {

namespace vertex3D
{

struct Data
{
    Vec<3, GLfloat> mPosition;
    Vec<3, GLfloat> mNormal{0.f, 0.f, 1.f};
};

constexpr AttributeDescriptionList gDescription = {
    { 0, 3, offsetof(Data, mPosition), MappedGL<GLfloat>::enumerator},
    { 1, 3, offsetof(Data, mNormal), MappedGL<GLfloat>::enumerator},
};

inline Data operator*(const Data & aLhs, const Matrix<4> & aRhs)
{
    Vec<4> position{aLhs.mPosition.x(), aLhs.mPosition.y(), aLhs.mPosition.z(), 1.0f};
    Vec<4> normal{aLhs.mNormal.x(), aLhs.mNormal.y(), aLhs.mNormal.z(), 0.0f};

    position *= aRhs;
    normal *= aRhs;

    return {
        {position.x(), position.y(), position.z()},
        {normal.x(), normal.y(), normal.z()},
    };
}

inline const gsl::span<Data> & operator*=(const gsl::span<Data> & aLhs, const Matrix<4> & aRhs)
{
    std::transform(aLhs.begin(), aLhs.end(), aLhs.begin(),
                   [&aRhs](const Data & data){ return data * aRhs; });
    return aLhs;
}

} // namespace vertex3D

template <>
class BufferData_trait<vertex3D::Data>
{
public:
    static constexpr AttributeDescriptionList description_value = vertex3D::gDescription;
};

namespace ship3D {

static const std::array<vertex3D::Data, 4> gVertices = {
    vertex3D::Data{
        {-1.25f, -1.5f, 0.0f},
    },
    vertex3D::Data{
        { 3.75f,  0.0f, 0.0f},
    },
    vertex3D::Data{
        {-1.25f,  1.5f, 0.0f},
    },
    vertex3D::Data{
        { 0.0f,  0.0f,  0.0f},
    },
};

} // namespace ship3D

namespace cube {

static constexpr float cpi4 = 0.70710678f;

static const std::array<vertex3D::Data, 8*3> gVertices = {
    // Frontface
    vertex3D::Data{
        {-1.0f, -1.0f, 1.0f},
        {-cpi4,  0.0f, cpi4},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, 1.0f},
        {-cpi4,  0.0f, cpi4},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, 1.0f},
        { 0.0f,  cpi4, cpi4},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, 1.0f},
        { 0.0f,  cpi4, cpi4},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, 1.0f},
        { cpi4,  0.0f, cpi4},
    },
    vertex3D::Data{
        { 1.0f,  -1.0f, 1.0f},
        { cpi4,  0.0f,  cpi4},
    },
    vertex3D::Data{
        { 1.0f, -1.0f, 1.0f},
        { 0.0f, -cpi4, cpi4},
    },
    vertex3D::Data{
        {-1.0f, -1.0f, 1.0f},
        { 0.0f, -cpi4, cpi4},
    },

    // Backface
    vertex3D::Data{
        {-1.0f, -1.0f, -1.0f},
          {-cpi4,  0.0f, -cpi4},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, -1.0f},
        {-cpi4,  0.0f, -cpi4},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, -1.0f},
        { 0.0f,  cpi4, -cpi4},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, -1.0f},
        { 0.0f,  cpi4, -cpi4},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, -1.0f},
        { cpi4,  0.0f, -cpi4},
    },
    vertex3D::Data{
        { 1.0f,  -1.0f, -1.0f},
        { cpi4,   0.0f, -cpi4},
    },
    vertex3D::Data{
        { 1.0f, -1.0f, -1.0f},
        { 0.0f, -cpi4, -cpi4},
    },
    vertex3D::Data{
        {-1.0f, -1.0f, -1.0f},
        { 0.0f, -cpi4, -cpi4},
    },

    // Connect
    vertex3D::Data{
        {-1.0f, -1.0f, -1.0f},
        {-cpi4, -cpi4,  0.0f},
    },
    vertex3D::Data{
        {-1.0f, -1.0f, +1.0f},
        {-cpi4, -cpi4,  0.0f},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, -1.0f},
        {-cpi4,  cpi4,  0.0f},
    },
    vertex3D::Data{
        {-1.0f,  1.0f, +1.0f},
        {-cpi4,  cpi4,  0.0f},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, -1.0f},
        { cpi4,  cpi4,  0.0f},
    },
    vertex3D::Data{
        { 1.0f,  1.0f, +1.0f},
        { cpi4,  cpi4,  0.0f},
    },
    vertex3D::Data{
        { 1.0f,  -1.0f, -1.0f},
        { cpi4,  -cpi4,  0.0f},
    },
    vertex3D::Data{
        { 1.0f,  -1.0f, +1.0f},
        { cpi4,  -cpi4,  0.0f},
    },
};

} // namespace cube

namespace circle3D {

enum Type
{
    Circle,
    Disc,
};

template <GLsizei N_vertices, Type N_type = Circle>
std::vector<vertex3D::Data> makeVertices(GLfloat aRadius)
{
    std::vector<vertex3D::Data> result;
    if (N_type == Disc)
    {
        result.push_back(vertex3D::Data{
            {0.f, 0.f, 0.f},
        });
    }
    //result.reserve(N_vertices+2);
    for(int i=0; i<=N_vertices; ++i)
    {
        result.emplace_back(vertex3D::Data{{
                                                cos(2*pi<float>*i/N_vertices) * aRadius,
                                                sin(2*pi<float>*i/N_vertices) * aRadius,
                                                0.f,
                                           },
                                           {
                                               0.f,
                                               0.f,
                                               N_type == Disc ? -1.f : 1.0f,
                                           }});
    }
    return result;
}

} // namespace circle3D

namespace rectangle3D {

inline std::vector<vertex3D::Data> makeVertices(Size<2> aDimensions)
{
    return {
        { {+aDimensions.width()/2.f, +aDimensions.height()/2.f, 0.f} },
        { {-aDimensions.width()/2.f, +aDimensions.height()/2.f, 0.f} },
        { {-aDimensions.width()/2.f, -aDimensions.height()/2.f, 0.f} },
        { {+aDimensions.width()/2.f, -aDimensions.height()/2.f, 0.f} },
    };
}

} // namespace rectangle3D

namespace instance3D {

struct Data
{
    Vec<2, GLfloat> mPosition;
    Matrix<4, GLfloat> mOrientationScale{Matrix<4, GLfloat>::Identity()};
};

constexpr AttributeDescriptionList gDescription = {
    {2, 2, offsetof(Data, mPosition),      MappedGL<GLfloat>::enumerator},
    {3, 4, offsetof(Data, mOrientationScale),   MappedGL<GLfloat>::enumerator},
    {4, 4, offsetof(Data, mOrientationScale) +  4*sizeof(GLfloat),   MappedGL<GLfloat>::enumerator},
    {5, 4, offsetof(Data, mOrientationScale) +  8*sizeof(GLfloat),   MappedGL<GLfloat>::enumerator},
    {6, 4, offsetof(Data, mOrientationScale) + 12*sizeof(GLfloat),   MappedGL<GLfloat>::enumerator},
};

} // namespace instance3D

template <>
class BufferData_trait<instance3D::Data>
{
public:
    static constexpr AttributeDescriptionList description_value = instance3D::gDescription;
};

} // namespace ad


#ifndef CLASS_H
#define CLASS_H

#include <iostream>
#include <ctime>
#include <fstream>
#include <string>
#include <algorithm>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_cdf.h>
//multivariate normal libraries
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>

using namespace std;
int poisson(double lambda);
double expTrunc(double, double);

class Parameters { //store all parameters in one class to stop them being everywhere. Will also make it easier to change parameters when needed.
  gsl_rng * rando;
  const gsl_rng_type * T;
  double lbda_original,v_to_h_original,sig_original; //original parameters stored so when changed from intervention still have in memory.
  public:
    Parameters(); //imports parameters from text file.
    Parameters(int); //imports parameters from text file and use index to seed random number generator.
    Parameters(double,double); //bite exposure gamma a, bite exposure gamma b
    Parameters(double,double,double); //bite exposure gamma a, bite exposure gamma b, v_to_h ratio.
    ~Parameters(void);
    //functions
    void setParameters(void);
    void importParameters(void);
    void setBR(bool intervention);
    void setVH(bool intervention);
    void setMu(bool intervention);
    double L3Uptake(double);
    double readParamLine(ifstream& myfile);
    double gamma_dist(double, double);
    double normal_dist(double, double);
    double uniform_dist(void);
    double expTrunc(double, double);
    int poisson_dist(double);
    int rmvnorm(const gsl_rng *, const int, const gsl_vector *, const gsl_matrix *, gsl_vector *);
    int setUB(double *, double *, double *);
    double immuneRK4Step(double,double);
    //parameters
    double a;
    double b;
    double shapeRisk; //shape of probability distribution for risk.
    double riskMu1; //mean of risk for age group less than 16
    double riskMu2; //mean of risk for age group  17-29
    double riskMu3; //mean of risk for age group  30+
    double mu; //death rate of worms
    double theta; //immune system response parameter. 0.112
    double gamma; //mf death rate
    double alpha; //mf birth rate
    double v_to_h; //vector to human ratio.
    double xi; //constant bite rate (5760.0 / 30.0)
    double kappas1; //vector uptake and development
    double r1; //vector uptake and development
    double tau;
    double z; //waning immunity
    double nu; //poly-monogamy parameter
    //shared state parameters
    double L3; //larvae density.
    double g; //Proportion of mosquitoes which pick up infection when biting an infected host
    double sig; //death rate of mosquitos
    double psi1; //Proportion of L3 leaving mosquito per bite
    double psi2; //Proportion of L3 leaving mosquito that enter host
    double lbda; //bite rate per mosquito per month
    double dt;
    double lbdaR, v_to_hR, sigR; //reduction in bite-rate and v_to_h ratio.
    int nMDA; //number of rounds of MDA.
    int mdaFreq;
    double covMDA;
    double s2;
    double mfPropMDA; //proportion of mf killed due to MDA
    double wPropMDA; //proportion of worms killed due to MDA
    double sysComp,u0Comp, sigComp; //parameters for systematic non-compliance
    double sysCompN, u0CompN, sigCompN; //parameters for systematic non-compliance of bed-nets.
    double covN; //coverage of bednets.
    double rhoCN; //correlation of non-compliance between bed-nets and MDA (Chemotherapy).
    int mosquitoSpecies; //species of mosquito 0 - Anopheles, 1 - Culex
    double rhoBU; //correlation between non-compliance and bite risk.
    int aWol; //using doxycycline as intervention 0- not used. 1- is used.
    double sN,dN; //probability of mosquito successfully biting given use of bed nets. prob of death due to bed net.
    int IDAControl; //switch to IDA after five rounds of normal MDA treatment. 0- not used, 1 - is used.
} ;

class Host {

    //double dt; //time-step for tau.
    void initialise(void); //initialise state variables.

  public:

    Host(double);
    Host(void);
    void react(void);
    void evolve(double);
    double mfConc(void);
    double biteRate(void);
    double repRate(void);
    void updateRisk(bool);
    //state variables
    int W,WM,WF; //number of worms, number of male worms, number of female worms
    double I; //immune response (assumed to be deterministic)
    double M; //mf produced. //int
    double t; //time of host
    double a; //age of host.
    int aWol; //host is treated with Doxycycline.
    int bedNet; //host used bednet.
    //host parameters
    Parameters *param; //pointer to params.
    double b; //risk of infection
    double pRisk; //p-value for risk of infection (updates b)
    double uComp; //random parameter used to calculate compliance with MDA
    double uCompN; //random parameter used to calculate compliance with bed nets.
    //const double mu = 0.1/(30.0); //death rate of worms
    //double beta = 0.112; //immune system response parameter.
    //double gamma = 0.1/(30.0);
    //double alpha = 0.2/(30.0);
    int births;
    int deaths;
    //double xi = (5760.0 / 30.0) * 0.414 * 0.32 * 0.2;
    //double tau = 0.04 / (365.0); //0.02
    //double L3 = 1.0; //keeps track of L3 in total population.
} ;

class Model {

    Host *host_pop;
    int n;
    bool aWolInt; //undergoing aWol intervention.
    int bedNetInt; //undergoing bednet intervention.
  public:
    Model(int,double,double);
    Model(int, Parameters*);
    Parameters *param;
    void react(void);
    void evolve(double);
    void save(string);
    void outputs(void);
    void saveOngoing(string); //appends WM,WF,M to the bottom of filename
    void evolveAndSaves(double tot_t, string filename); //functionally same as evolve, but saves outputs at regular intervals.
    double L3(void);
    void MDAEvent(void);
    void aWolEvent(bool);
    void bedNetEvent(void);

} ;

#endif


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

#include <math.h>
#include <libgen.h>

#include <options/options.h>
#include "../csm/csm_all.h"

void purify(LDP ld, double threshold_min, double threshold_max);

struct ld_purify_params {
	double threshold_min, threshold_max;
	const char* file_input;
	const char* file_output;
};

int main(int argc, const char * argv[]) {
	sm_set_program_name(argv[0]);

	options_banner("ld_purify: Makes sure that the file format is valid. \n * Sets valid=0 if reading is outside interval ");

	struct ld_purify_params p;

	struct option* ops = options_allocate(20);
	options_double(ops, "threshold_min", &p.threshold_min, 0.01,
		"Sets valid=0 if readings are less than this threshold.");
	options_double(ops, "threshold_max", &p.threshold_max, 79.0,
		"Sets valid=0 if readings are more than this threshold.");

	options_string(ops, "in", &p.file_input, "stdin", "Input file ");
	options_string(ops, "out", &p.file_output, "stdout", "Output file ");

	if(!options_parse_args(ops, argc, argv)) {
		options_print_help(ops, stderr);
		return -1;
	}

	FILE * in = open_file_for_reading(p.file_input);
	if(!in) return -3;

	FILE * out = open_file_for_writing(p.file_output);
	if(!out) return -2;

	LDP ld; int count = -1;
	while( (ld = ld_from_json_stream(in))) {

		purify(ld, p.threshold_min, p.threshold_max);

		if(!ld_valid_fields(ld))  {
			sm_error("Wait, we didn't purify enough  (#%d in file)\n", count);
			continue;
		}

		ld_write_as_json(ld, out);
		ld_free(ld);
	}

	return 0;
}

void purify(LDP ld, double threshold_min, double threshold_max) {
	for(int i=0;i<ld->nrays;i++) {
		if(!ld->valid[i]) continue;

		double rho = ld->readings[i];
		if( is_nan(rho) | (rho < threshold_min) | (rho > threshold_max) ) {
			ld->readings[i] = GSL_NAN;
			ld->valid[i] = 0;
			ld->alpha[i] = GSL_NAN;
			ld->alpha_valid[i] = 0;
		}

	}

}