#ifndef TNet_Matrix_h
#define TNet_Matrix_h

#include <stddef.h>
#include <stdlib.h>
#include <stdexcept>
#include <iostream>

#ifdef HAVE_ATLAS
extern "C"{
  #include <cblas.h>
  #include <clapack.h>
}
#endif

#include "Common.h"
#include "MathAux.h"
#include "Types.h"
#include "Error.h"

//#define TRACE_MATRIX_OPERATIONS
#define CHECKSIZE

namespace TNet
{

  //  class matrix_error : public std::logic_error {};
  //  class matrix_sizes_error : public matrix_error {};

  // declare the class so the header knows about it
  template<typename _ElemT> class Vector;
  template<typename _ElemT> class SubVector;
  template<typename _ElemT> class Matrix;
  template<typename _ElemT> class SubMatrix;

  // we need to declare the friend << operator here
  template<typename _ElemT>
    std::ostream & operator << (std::ostream & rOut, const Matrix<_ElemT> & rM);

  // we need to declare the friend << operator here
  template<typename _ElemT>
    std::istream & operator >> (std::istream & rIn, Matrix<_ElemT> & rM);

  // we need to declare this friend function here
  template<typename _ElemT>
   _ElemT TraceOfProduct(const Matrix<_ElemT> &A, const Matrix<_ElemT> &B); // tr(A B)

  // we need to declare this friend function here
  template<typename _ElemT>
	 _ElemT TraceOfProductT(const Matrix<_ElemT> &A, const Matrix<_ElemT> &B); // tr(A B^T)==tr(A^T B)

  /** **************************************************************************
   ** **************************************************************************
   *  @brief Provides a matrix class
   *
   *  This class provides a way to work with matrices in TNet.
   *  It encapsulates basic operations and memory optimizations.
   *
   */
  template<typename _ElemT>
    class Matrix
    {
    public:
      /// defines a transpose type

      struct HtkHeader
      {
        INT_32    mNSamples;
        INT_32    mSamplePeriod;
        INT_16    mSampleSize;
        UINT_16   mSampleKind;
      };

      /**
       * @brief Extension of the HTK header
       */
      struct HtkHeaderExt
      {
        INT_32 mHeaderSize;
        INT_32 mVersion;
        INT_32 mSampSize;
      };

      /// defines a type of this
      typedef Matrix<_ElemT>    ThisType;

      // Constructors

      /// Empty constructor
      Matrix<_ElemT> ():
        mpData(NULL), mMCols(0), mMRows(0), mStride(0)
#ifdef STK_MEMALIGN_MANUAL
        , mpFreeData(NULL)
#endif
      {}

      /// Copy constructor
      Matrix<_ElemT> (const Matrix<_ElemT> & rM, MatrixTrasposeType trans=NO_TRANS):
        mpData(NULL)
      { if(trans==NO_TRANS){ Init(rM.mMRows, rM.mMCols); Copy(rM); } else { Init(rM.mMCols,rM.mMRows); Copy(rM,TRANS); } }

      /// Copy constructor from another type.
      template<typename _ElemU>
      explicit Matrix<_ElemT> (const Matrix<_ElemU> & rM, MatrixTrasposeType trans=NO_TRANS):
        mpData(NULL)
      { if(trans==NO_TRANS){ Init(rM.Rows(), rM.Cols()); Copy(rM); } else { Init(rM.Cols(),rM.Rows()); Copy(rM,TRANS); } }

      /// Basic constructor
      Matrix(const size_t r, const size_t c, bool clear=true)
      { mpData=NULL; Init(r, c, clear);  }

	  Matrix<_ElemT> &operator = (const Matrix <_ElemT> &other) { Init(other.Rows(), other.Cols()); Copy(other); return *this; } // Needed for inclusion in std::vector

      /// Destructor
      ~Matrix()
      { Destroy(); }

      /// Initializes matrix (if not done by constructor)
      ThisType &
      Init(const size_t r,
           const size_t c, bool clear=true);

      /**
       * @brief Dealocates the matrix from memory and resets the dimensions to (0, 0)
       */
      void
      Destroy();

      ThisType &
      Zero();

      ThisType &
      Unit(); // set to unit.

      /**
       * @brief Copies the contents of a matrix
       * @param rM Source data matrix
       * @return Returns reference to this
       */
      template<typename _ElemU> ThisType &
        Copy(const Matrix<_ElemU> & rM, MatrixTrasposeType Trans=NO_TRANS);

      /**
       * @brief Copies the elements of a vector row-by-row into a matrix
       * @param rV Source vector
       * @param nRows Number of rows of returned matrix
       * @param nCols Number of columns of returned matrix
       *
       * Note that rV.Dim() must equal nRows*nCols
       */
      ThisType &
      CopyVectorSplicedRows(const Vector<_ElemT> &rV, const size_t nRows, const size_t nCols);

      /**
       * @brief Returns @c true if matrix is initialized
       */
      bool
		IsInitialized() const
      { return mpData != NULL; }

      /// Returns number of rows in the matrix
      inline size_t
		Rows() const
      {
        return mMRows;
      }

      /// Returns number of columns in the matrix
      inline size_t
      Cols() const
      {
        return mMCols;
      }

      /// Returns number of columns in the matrix memory
      inline size_t
		Stride() const
      {
        return mStride;
      }

      /**
       *  @brief Gives access to a specified matrix row without range check
       *  @return Pointer to the const array
       */
      inline const _ElemT*  __attribute__((aligned(16)))
       pData () const
      {
        return mpData;
      }

      /**
       *  @brief Gives access to a specified matrix row without range check
       *  @return Pointer to the non-const data array
       */
      inline _ElemT* __attribute__((aligned(16)))
       pData ()
      {
        return mpData;
      }

      /**
       *  @brief pData_workaround is a workaround that allows SubMatrix to get a
       *  @return pointer to non-const data even though the Matrix is const...
       */
    protected:
      inline _ElemT*  __attribute__((aligned(16)))
       pData_workaround () const
      {
        return mpData;
      }
    public:

      /// Returns size of matrix in memory
      size_t
      MSize() const
      {
        return mMRows * mStride * sizeof(_ElemT);
      }

      /// Checks the content of the matrix for nan and inf values
      void
      CheckData(const std::string file = "") const
      {
        for(size_t row=0; row<Rows(); row++) {
          for(size_t col=0; col<Cols(); col++) {
            if(isnan((*this)(row,col)) || isinf((*this)(row,col))) {
              std::ostringstream os;
              os << "Invalid value: " << (*this)(row,col)
                 << " in matrix row: " << row
                 << " col: " << col
                 << " file: " << file;
              Error(os.str());
            }
          }
        }
      }

      /**
       *  **********************************************************************
       *  **********************************************************************
       *  @defgroup RESHAPE Matrix reshaping rutines
       *  **********************************************************************
       *  **********************************************************************
       * @{
       */

      /**
       *  @brief Removes one row from the matrix. The memory is not reallocated.
       */
      ThisType &
      RemoveRow(size_t i);

      /** @} */

      /**
       *  **********************************************************************
       *  **********************************************************************
       *  @defgroup ACCESS Access functions and operators
       *  **********************************************************************
       *  **********************************************************************
       * @{
       */

      /**
       *  @brief Gives access to a specified matrix row without range check
       *  @return Subvector object representing the row
       */
      inline const SubVector<_ElemT>
      operator []  (size_t i) const
      {
        assert(i < mMRows);
        return SubVector<_ElemT>(mpData + (i * mStride), Cols());
      }

      inline SubVector<_ElemT>
      operator []  (size_t i)
      {
        assert(i < mMRows);
        return SubVector<_ElemT>(mpData + (i * mStride), Cols());
      }

      /**
       *  @brief Gives access to a specified matrix row without range check
       *  @return pointer to the first field of the row
       */
      inline  _ElemT*
      pRowData(size_t i)
      {
        assert(i < mMRows);
        return mpData + i * mStride;
      }

      /**
       *  @brief Gives access to a specified matrix row without range check
       *  @return pointer to the first field of the row (const version)
       */
      inline const _ElemT*
      pRowData(size_t i) const
      {
        assert(i < mMRows);
        return mpData + i * mStride;
      }

      /**
       *  @brief Gives access to matrix elements (row, col)
       *  @return reference to the desired field
       */
      inline _ElemT&
		operator () (size_t r, size_t c)
      {
#ifdef PARANOID
        assert(r < mMRows && c < mMCols);
#endif
		return *(mpData + r * mStride + c);
	  }

      /**
       *  @brief Gives access to matrix elements (row, col)
       *  @return pointer to the desired field (const version)
       */
      inline const _ElemT
		operator () (size_t r, size_t c) const
      {
#ifdef PARANOID
        assert(r < mMRows && c < mMCols);
#endif
		return *(mpData + r * mStride + c);
	  }

      /**
       * @brief Returns a matrix sub-range
       * @param ro Row offset
       * @param r  Rows in range
       * @param co Column offset
       * @param c  Coluns in range
       * See @c SubMatrix class for details
       */
      SubMatrix<_ElemT>
      Range(const size_t    ro, const size_t    r,
            const size_t    co, const size_t    c)
      { return SubMatrix<_ElemT>(*this, ro, r, co, c); }

      const SubMatrix<_ElemT>
      Range(const size_t    ro, const size_t    r,
            const size_t    co, const size_t    c) const
      { return SubMatrix<_ElemT>(*this, ro, r, co, c); }
      /** @} */

      /**
       *  **********************************************************************
       *  **********************************************************************
       *  @defgroup MATH ROUTINES
       *  **********************************************************************
       *  **********************************************************************
       *  @{
       **/

      /**
       *  @brief Returns sum of all elements
       */
      _ElemT&
      Sum() const;

      ThisType &
      DotMul(const ThisType& a);

      ThisType &
      Scale(_ElemT alpha);

      ThisType &
      ScaleCols(const Vector<_ElemT> &scale); // Equivalent to (*this) = (*this) * diag(scale).

      ThisType &
      ScaleRows(const Vector<_ElemT> &scale); // Equivalent to (*this) = diag(scale) * (*this);

      /// Sum another matrix rMatrix with this matrix
      ThisType&
      Add(const Matrix<_ElemT>& rMatrix);

      /// Sum scaled matrix rMatrix with this matrix
      ThisType&
      AddScaled(_ElemT alpha, const Matrix<_ElemT>& rMatrix);

      /// Apply log to all items of the matrix
      ThisType&
      ApplyLog();

      /**
       * @brief Computes the determinant of this matrix
       * @return Returns the determinant of a matrix
       * @ingroup MATH
       *
       */
      _ElemT LogAbsDeterminant(_ElemT *DetSign=NULL);

      /**
       *  @brief Performs matrix inplace inversion
       */
      ThisType &
      Invert(_ElemT *LogDet=NULL, _ElemT *DetSign=NULL, bool inverse_needed=true);

      /**
       *  @brief Performs matrix inplace inversion in double precision, even if this object is not double precision.
       */
      ThisType &
      InvertDouble(_ElemT *LogDet=NULL, _ElemT *DetSign=NULL, bool inverse_needed=true){
        double LogDet_tmp, DetSign_tmp;
        Matrix<double> dmat(*this); dmat.Invert(&LogDet_tmp, &DetSign_tmp, inverse_needed); if(inverse_needed) (*this).Copy(dmat);
        if(LogDet) *LogDet = LogDet_tmp; if(DetSign) *DetSign = DetSign_tmp;
        return *this;
      }

      /**
       *  @brief Inplace matrix transposition. Applicable only to square matrices
       */
      ThisType &
      Transpose()
      {
        assert(Rows()==Cols());
        size_t M=Rows();
        for(size_t i=0;i<M;i++)
          for(size_t j=0;j<i;j++){
           _ElemT &a = (*this)(i,j), &b = (*this)(j,i);
		   std::swap(a,b);
        }
		return *this;
      }

      bool IsSymmetric(_ElemT cutoff = 1.0e-05) const;

      bool IsDiagonal(_ElemT cutoff = 1.0e-05) const;

      bool IsUnit(_ElemT cutoff = 1.0e-05) const;

      bool IsZero(_ElemT cutoff = 1.0e-05) const;

      _ElemT FrobeniusNorm() const; // sqrt of sum of square elements.

      _ElemT LargestAbsElem() const; // largest absolute value.

      friend _ElemT TNet::TraceOfProduct<_ElemT>(const Matrix<_ElemT> &A, const Matrix<_ElemT> &B); // tr(A B)
      friend _ElemT TNet::TraceOfProductT<_ElemT>(const Matrix<_ElemT> &A, const Matrix<_ElemT> &B); // tr(A B^T)==tr(A^T B)
      friend class SubMatrix<_ElemT>; // so it can get around const restrictions on the pointer to mpData.

      /** **********************************************************************
       *  **********************************************************************
       *  @defgroup BLAS_ROUTINES BLAS ROUTINES
       *  @ingroup MATH
       *  **********************************************************************
       *  **********************************************************************
       **/

      ThisType &
      BlasGer(const _ElemT alpha, const Vector<_ElemT>& rA, const Vector<_ElemT>& rB);

      ThisType &
	    Axpy(const _ElemT alpha, const Matrix<_ElemT> &rM, MatrixTrasposeType transA=NO_TRANS);

      ThisType &
      BlasGemm(const _ElemT alpha,
               const ThisType& rA, MatrixTrasposeType transA,
               const ThisType& rB, MatrixTrasposeType transB,
               const _ElemT beta = 0.0);

      /** @} */

      /** **********************************************************************
       *  **********************************************************************
       *  @defgroup IO Input/Output ROUTINES
       *  **********************************************************************
       *  **********************************************************************
       *  @{
       **/

      friend std::ostream &
      operator << <> (std::ostream & out, const ThisType & m);

      void PrintOut(char *file);
      void ReadIn(char *file);

      bool
      LoadHTK(const char* pFileName);

      /** @} */

    protected:
//      inline void swap4b(void *a);
//      inline void swap2b(void *a);

    protected:
      /// data memory area
      _ElemT*   mpData;

      /// these atributes store the real matrix size as it is stored in memory
      /// including memalignment
      size_t    mMCols;       ///< Number of columns
      size_t    mMRows;       ///< Number of rows
      size_t    mStride;      ///< true number of columns for the internal matrix.
                              ///< This number may differ from M_cols as memory
                              ///< alignment might be used

#ifdef STK_MEMALIGN_MANUAL
      /// data to be freed (in case of manual memalignment use, see Common.h)
      _ElemT*   mpFreeData;
#endif
    }; // class Matrix

    template<>  Matrix<float> &  Matrix<float>::Invert(float *LogDet, float *DetSign, bool inverse_needed); // state that we will implement separately for float and double.
    template<>  Matrix<double> &  Matrix<double>::Invert(double *LogDet, double *DetSign, bool inverse_needed);

  /** **************************************************************************
   ** **************************************************************************
   *  @brief Sub-matrix representation
   *
   *  This class provides a way to work with matrix cutouts in STK.
   *
   *
   */
  template<typename _ElemT>
    class SubMatrix : public Matrix<_ElemT>
    {
    typedef SubMatrix<_ElemT>    ThisType;

    public:
      /// Constructor
      SubMatrix(const Matrix<_ElemT>& rT, // Input matrix cannot be const because SubMatrix can change its contents.
                const size_t    ro,
                const size_t    r,
                const size_t    co,
                const size_t    c);

      /// The destructor
      ~SubMatrix<_ElemT>()
      {
#ifndef STK_MEMALIGN_MANUAL
        Matrix<_ElemT>::mpData = NULL;
#else
        Matrix<_ElemT>::mpFreeData = NULL;
#endif
      }

      /// Assign operator
      ThisType& operator=(const ThisType& rSrc)
      {
        //std::cout << "[PERFORMing operator= SubMatrix&^2]" << std::flush;
        this->mpData = rSrc.mpData;
        this->mMCols = rSrc.mMCols;
        this->mMRows = rSrc.mMRows;
        this->mStride = rSrc.mStride;
        this->mpFreeData = rSrc.mpFreeData;
        return *this;
      }

      /// Initializes matrix (if not done by constructor)
      ThisType &
      Init(const size_t r,
           const size_t c, bool clear=true)
      { Error("Submatrix cannot do Init"); return *this; }

      /**
       * @brief Dealocates the matrix from memory and resets the dimensions to (0, 0)
       */
      void
      Destroy()
      { Error("Submatrix cannot do Destroy"); }

    };

  //Create useful shortcuts
  typedef Matrix<BaseFloat> BfMatrix;
  typedef SubMatrix<BaseFloat> BfSubMatrix;

  /**
   * Function for summing matrices of different types
   */
  template<typename _ElemT, typename _ElemU>
  void Add(Matrix<_ElemT>& rDst,  const Matrix<_ElemU>& rSrc) {
    assert(rDst.Cols() == rSrc.Cols());
    assert(rDst.Rows() == rSrc.Rows());

    for(size_t i=0; i<rDst.Rows(); i++) {
      const _ElemU* p_src = rSrc.pRowData(i);
      _ElemT* p_dst = rDst.pRowData(i);
      for(size_t j=0; j<rDst.Cols(); j++) {
        *p_dst++ += (_ElemT)*p_src++;
      }
    }
  }

  /**
   * Function for summing matrices of different types
   */
  template<typename _ElemT, typename _ElemU>
  void AddScaled(Matrix<_ElemT>& rDst, const Matrix<_ElemU>& rSrc, _ElemT scale) {
    assert(rDst.Cols() == rSrc.Cols());
    assert(rDst.Rows() == rSrc.Rows());

    Vector<_ElemT> tmp(rDst[0]);

    for(size_t i=0; i<rDst.Rows(); i++) {
      tmp.Copy(rSrc[i]);
      rDst[i].BlasAxpy(scale, tmp);

      /*
      const _ElemU* p_src = rSrc.pRowData(i);
      _ElemT* p_dst = rDst.pRowData(i);
      for(size_t j=0; j<rDst.Cols(); j++) {
        *p_dst++ += (_ElemT)(*p_src++) * scale;
      }
      */
    }
  }

} // namespace STK

//*****************************************************************************
//*****************************************************************************
// we need to include the implementation
#include "Matrix.tcc"
//*****************************************************************************
//*****************************************************************************

/******************************************************************************
 ******************************************************************************
 * The following section contains specialized template definitions
 * whose implementation is in Matrix.cc
 */

//#ifndef TNet_Matrix_h
#endif


/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * 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 2 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Sun Nov  7 20:44:25 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2real 14 */

/*
 * This function contains 62 FP additions, 38 FP multiplications,
 * (or, 60 additions, 36 multiplications, 2 fused multiply/add),
 * 24 stack variables, and 28 memory accesses
 */
static const fftw_real K445041867 = FFTW_KONST(+0.445041867912628808577805128993589518932711138);
static const fftw_real K1_801937735 = FFTW_KONST(+1.801937735804838252472204639014890102331838324);
static const fftw_real K1_246979603 = FFTW_KONST(+1.246979603717467061050009768008479621264549462);
static const fftw_real K867767478 = FFTW_KONST(+0.867767478235116240951536665696717509219981456);
static const fftw_real K1_949855824 = FFTW_KONST(+1.949855824363647214036263365987862434465571601);
static const fftw_real K1_563662964 = FFTW_KONST(+1.563662964936059617416889053348115500464669037);
static const fftw_real K2_000000000 = FFTW_KONST(+2.000000000000000000000000000000000000000000000);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2real_14(const fftw_real *real_input, const fftw_real *imag_input, fftw_real *output, int real_istride, int imag_istride, int ostride)
{
     fftw_real tmp3;
     fftw_real tmp13;
     fftw_real tmp6;
     fftw_real tmp14;
     fftw_real tmp26;
     fftw_real tmp35;
     fftw_real tmp23;
     fftw_real tmp34;
     fftw_real tmp12;
     fftw_real tmp16;
     fftw_real tmp20;
     fftw_real tmp33;
     fftw_real tmp9;
     fftw_real tmp15;
     fftw_real tmp1;
     fftw_real tmp2;
     ASSERT_ALIGNED_DOUBLE;
     tmp1 = real_input[0];
     tmp2 = real_input[7 * real_istride];
     tmp3 = tmp1 - tmp2;
     tmp13 = tmp1 + tmp2;
     {
	  fftw_real tmp4;
	  fftw_real tmp5;
	  fftw_real tmp24;
	  fftw_real tmp25;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp4 = real_input[2 * real_istride];
	  tmp5 = real_input[5 * real_istride];
	  tmp6 = tmp4 - tmp5;
	  tmp14 = tmp4 + tmp5;
	  tmp24 = imag_input[2 * imag_istride];
	  tmp25 = imag_input[5 * imag_istride];
	  tmp26 = tmp24 - tmp25;
	  tmp35 = tmp24 + tmp25;
     }
     {
	  fftw_real tmp21;
	  fftw_real tmp22;
	  fftw_real tmp10;
	  fftw_real tmp11;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp21 = imag_input[6 * imag_istride];
	  tmp22 = imag_input[imag_istride];
	  tmp23 = tmp21 - tmp22;
	  tmp34 = tmp21 + tmp22;
	  tmp10 = real_input[6 * real_istride];
	  tmp11 = real_input[real_istride];
	  tmp12 = tmp10 - tmp11;
	  tmp16 = tmp10 + tmp11;
     }
     {
	  fftw_real tmp18;
	  fftw_real tmp19;
	  fftw_real tmp7;
	  fftw_real tmp8;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp18 = imag_input[4 * imag_istride];
	  tmp19 = imag_input[3 * imag_istride];
	  tmp20 = tmp18 - tmp19;
	  tmp33 = tmp18 + tmp19;
	  tmp7 = real_input[4 * real_istride];
	  tmp8 = real_input[3 * real_istride];
	  tmp9 = tmp7 - tmp8;
	  tmp15 = tmp7 + tmp8;
     }
     {
	  fftw_real tmp38;
	  fftw_real tmp37;
	  fftw_real tmp29;
	  fftw_real tmp28;
	  ASSERT_ALIGNED_DOUBLE;
	  output[7 * ostride] = tmp3 + (K2_000000000 * (tmp6 + tmp9 + tmp12));
	  tmp38 = (K1_563662964 * tmp35) + (K1_949855824 * tmp33) + (K867767478 * tmp34);
	  tmp37 = tmp3 + (K1_246979603 * tmp6) - (K1_801937735 * tmp12) - (K445041867 * tmp9);
	  output[ostride] = tmp37 - tmp38;
	  output[13 * ostride] = tmp37 + tmp38;
	  {
	       fftw_real tmp40;
	       fftw_real tmp39;
	       fftw_real tmp36;
	       fftw_real tmp32;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp40 = (K867767478 * tmp33) + (K1_563662964 * tmp34) - (K1_949855824 * tmp35);
	       tmp39 = tmp3 + (K1_246979603 * tmp12) - (K1_801937735 * tmp9) - (K445041867 * tmp6);
	       output[5 * ostride] = tmp39 - tmp40;
	       output[9 * ostride] = tmp39 + tmp40;
	       tmp36 = (K1_563662964 * tmp33) - (K1_949855824 * tmp34) - (K867767478 * tmp35);
	       tmp32 = tmp3 + (K1_246979603 * tmp9) - (K445041867 * tmp12) - (K1_801937735 * tmp6);
	       output[11 * ostride] = tmp32 - tmp36;
	       output[3 * ostride] = tmp32 + tmp36;
	  }
	  output[0] = tmp13 + (K2_000000000 * (tmp14 + tmp15 + tmp16));
	  tmp29 = (K867767478 * tmp20) + (K1_563662964 * tmp23) - (K1_949855824 * tmp26);
	  tmp28 = tmp13 + (K1_246979603 * tmp16) - (K1_801937735 * tmp15) - (K445041867 * tmp14);
	  output[12 * ostride] = tmp28 - tmp29;
	  output[2 * ostride] = tmp28 + tmp29;
	  {
	       fftw_real tmp31;
	       fftw_real tmp30;
	       fftw_real tmp27;
	       fftw_real tmp17;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp31 = (K1_563662964 * tmp26) + (K1_949855824 * tmp20) + (K867767478 * tmp23);
	       tmp30 = tmp13 + (K1_246979603 * tmp14) - (K1_801937735 * tmp16) - (K445041867 * tmp15);
	       output[8 * ostride] = tmp30 - tmp31;
	       output[6 * ostride] = tmp30 + tmp31;
	       tmp27 = (K1_563662964 * tmp20) - (K1_949855824 * tmp23) - (K867767478 * tmp26);
	       tmp17 = tmp13 + (K1_246979603 * tmp15) - (K445041867 * tmp16) - (K1_801937735 * tmp14);
	       output[4 * ostride] = tmp17 - tmp27;
	       output[10 * ostride] = tmp17 + tmp27;
	  }
     }
}

fftw_codelet_desc fftw_hc2real_14_desc =
{
     "fftw_hc2real_14",
     (void (*)()) fftw_hc2real_14,
     14,
     FFTW_BACKWARD,
     FFTW_HC2REAL,
     323,
     0,
     (const int *) 0,
};


#include <math.h>
// Constants not defined in MSVC's math.h
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654746172
#endif
#ifndef M_2_SQRTPI
#define M_2_SQRTPI 1.12837916709551255856
#endif
#include <gsl/gsl_spline.h>
#include <galpy_potentials.h>
// ChandrasekharDynamicalFrictionForce: 8 arguments: amp,ms,rhm,gamma^2,
// lnLambda, minr^2, ro, rf
double ChandrasekharDynamicalFrictionForceAmplitude(double R,double z,
						    double phi,double t,
						    double r2,
						    struct potentialArg * potentialArgs,
						    double vR,double vT,
						    double vz){
  double sr,X,Xfactor,d_ind,forceAmplitude;
  double * args= potentialArgs->args;
  //Get args
  double amp= *args;
  double ms= *(args+9);
  double rhm= *(args+10);
  double gamma2= *(args+11);
  double lnLambda= *(args+12);
  double ro= *(args+14);
  double rf= *(args+15);
  double GMvs;
  double r= sqrt( r2 );
  double v2=  vR * vR + vT * vT + vz * vz;
  double v= sqrt( v2 );
  // Constant or variable Lambda
  if ( lnLambda < 0 ) {
    GMvs= ms/v/v;
    if ( GMvs < rhm )
      lnLambda= 0.5 * log ( 1. + r2 / gamma2 / rhm / rhm );
    else
      lnLambda= 0.5 * log ( 1. + r2 / gamma2 / GMvs / GMvs );
  }
  d_ind= (r-ro)/(rf-ro);
  d_ind= d_ind <  0 ? 0. : ( d_ind > 1 ? 1. : d_ind);
  sr= gsl_spline_eval(*potentialArgs->spline1d,d_ind,*potentialArgs->acc1d);
  X= M_SQRT1_2 * v / sr;
  Xfactor= erf ( X ) - M_2_SQRTPI * X * exp ( - X * X );
  forceAmplitude= - amp * Xfactor * lnLambda / v2 / v \
    * calcDensity(R,z,phi,t,potentialArgs->nwrapped,
		  potentialArgs->wrappedPotentialArg);
  // Caching
  *(args + 1)= R;
  *(args + 2)= z;
  *(args + 3)= phi;
  *(args + 4)= t;
  *(args + 5)= vR;
  *(args + 6)= vT;
  *(args + 7)= vz;
  *(args + 8)= forceAmplitude;
  return forceAmplitude;
}
double ChandrasekharDynamicalFrictionForceRforce(double R,double z, double phi,
						 double t,
						 struct potentialArg * potentialArgs,
						 double vR,double vT,
						 double vz){
  double forceAmplitude;
  double * args= potentialArgs->args;
  double r2=  R * R + z * z;
  if ( r2 < *(args+13) )  // r < minr, don't bother caching
    return 0.;
  //Get args
  double cached_R= *(args + 1);
  double cached_z= *(args + 2);
  double cached_phi= *(args + 3);
  double cached_t= *(args + 4);
  double cached_vR= *(args + 5);
  double cached_vT= *(args + 6);
  double cached_vz= *(args + 7);
  if ( R != cached_R || phi != cached_phi || z != cached_z || t != cached_t \
       || vR != cached_vR || vT != cached_vT || vz != cached_vz )
    forceAmplitude= ChandrasekharDynamicalFrictionForceAmplitude(R,z,phi,t,r2,
								 potentialArgs,
								 vR,vT,vz);
  else
    forceAmplitude= *(args + 8);
  return forceAmplitude * vR;
}
double ChandrasekharDynamicalFrictionForcezforce(double R,double z, double phi,
						 double t,
						 struct potentialArg * potentialArgs,
						 double vR,double vT,
						 double vz){
  double forceAmplitude;
  double * args= potentialArgs->args;
  double r2=  R * R + z * z;
  if ( r2 < *(args+13) )  // r < minr, don't bother caching
    return 0.;
  //Get args
  double cached_R= *(args + 1);
  double cached_z= *(args + 2);
  double cached_phi= *(args + 3);
  double cached_t= *(args + 4);
  double cached_vR= *(args + 5);
  double cached_vT= *(args + 6);
  double cached_vz= *(args + 7);
  if ( R != cached_R || phi != cached_phi || z != cached_z || t != cached_t \
       || vR != cached_vR || vT != cached_vT || vz != cached_vz )
    //LCOV_EXCL_START
    forceAmplitude= ChandrasekharDynamicalFrictionForceAmplitude(R,z,phi,t,r2,
								 potentialArgs,
								 vR,vT,vz);
    //LCOV_EXCL_STOP
  else
    forceAmplitude= *(args + 8);
  return forceAmplitude * vz;
}
double ChandrasekharDynamicalFrictionForcephiforce(double R,double z,
						   double phi,double t,
						   struct potentialArg * potentialArgs,
						   double vR,double vT,
						   double vz){
  double forceAmplitude;
  double * args= potentialArgs->args;
  double r2=  R * R + z * z;
  if ( r2 < *(args+13) )  // r < minr, don't bother caching
    return 0.;
  //Get args
  double cached_R= *(args + 1);
  double cached_z= *(args + 2);
  double cached_phi= *(args + 3);
  double cached_t= *(args + 4);
  double cached_vR= *(args + 5);
  double cached_vT= *(args + 6);
  double cached_vz= *(args + 7);
  if ( R != cached_R || phi != cached_phi || z != cached_z || t != cached_t \
       || vR != cached_vR || vT != cached_vT || vz != cached_vz )
    //LCOV_EXCL_START
    forceAmplitude= ChandrasekharDynamicalFrictionForceAmplitude(R,z,phi,t,r2,
								 potentialArgs,
								 vR,vT,vz);
    //LCOV_EXCL_STOP
  else
    forceAmplitude= *(args + 8);
  return forceAmplitude * vT * R;
}


/* 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__ */


#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


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

/*!
 * \file sim.c
 * \brief generate mocked 2d data.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_interp.h>
#include <mpi.h>

#include "brains.h"

/////////////////////////////////////////////////////////////////////////////////
#ifdef SpecAstro
/* baselines for 3C 273 dataset observed by the GRAVITY (Nature, 2020, 563, 657),
 * unit is meter */
int n_base_sa_3c273 = 24;
double base_sa_3c273[]={
  -39.847287,   18.757261,
  -72.901618,  -13.864206,
  -105.767452, -59.897942,
  -32.453674,  -32.669331,
  -64.714063,  -78.769286,
  -32.064961,  -46.108491,
  -53.774627,   21.045077,
  -71.062525,  -10.421538,
  -79.670158,  -55.520144,
  -16.867270,  -31.447448,
  -25.151512,  -76.555774,
   -8.846202,  -45.116789,
  -54.064283,   19.430318,
  -86.349699,  -12.827258,
  -115.711019, -58.525511,
  -32.484928,  -32.180585,
  -61.579109,  -77.881063,
  -29.231494,  -45.704607,
  -52.779207,   21.321411,
  -83.468788,  -12.802497,
  -107.827509, -59.908816,
  -28.601448,  -33.449429,
  -50.961674,  -81.626333,
  -23.699820,  -46.979455
};
#endif
//////////////////////////////////////////////////////////////////////////////////////

void *model;

void sim()
{
  if(thistask != roottask)
    return;

  FILE *fp;
  char fname[200];
  int i, j, incr;

  sim_init();

  double *pm = (double *)model, error, fcon;

  smooth_init(parset.n_vel_recon, TransV);

  if(parset.flag_dim == -1)
  {
    /* note that here use sigma_hat = sigma/sqrt(tau) */
    printf("sim with ln(sigma) = %f and  ln(taud) = %f.\n", var_param[1], var_param[2]);
    reconstruct_con_from_varmodel(exp(var_param[1]), exp(var_param[2]), 1.0, 0.0);
  }
  else
  {
    con_scale = 1.0;
    line_scale = 1.0;
    line_error_mean = con_error_mean = 0.01;
    /* arguments: sigma_hat, tau, alapha, and syserr */
    create_con_from_random(0.03, 45.0, 1.0, 0.0);
  }
  calculate_con_rm(model);

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

  for(i=0; i<parset.n_con_recon; i++)
  {
    fprintf(fp, "%e %e %e\n", Tcon[i]*(1.0+parset.redshift), Fcon[i]/con_scale, Fcerrs[i]/con_scale);
  }
  fclose(fp);

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

  if(parset.flag_dim != -2)
  {
    gsl_interp_init(gsl_linear, Tcon, Fcon, parset.n_con_recon);
    for(i=0; i<n_con_data; i++)
    {
      //fprintf(fp, "%f %f %f\n", Tcon[i], Fcon[i]/con_scale, Fcerrs[i]/con_scale);
      fcon = gsl_interp_eval(gsl_linear, Tcon, Fcon, Tcon_data[i], gsl_acc);
      fprintf(fp, "%e %e %e\n", Tcon_data[i]*(1.0+parset.redshift),
            (fcon+gsl_ran_ugaussian(gsl_r)*con_error_mean)/con_scale, con_error_mean/con_scale);
    }
  }
  else
  {
    incr = fmax(0.5/(Tcon[1]-Tcon[0]), 1.0); //cadence to be 0.5day or increasement to be 1
    for(i=0; i<parset.n_con_recon; i+=incr)
    {
      if(Tcon[i] >= 0.0)
      {
        fprintf(fp, "%e %e %e\n", Tcon[i]*(1.0+parset.redshift), Fcon[i]/con_scale, Fcerrs[i]/con_scale);
      }
    }
  }
  fclose(fp);

  gsl_interp_init(gsl_linear, Tcon, Fcon_rm, parset.n_con_recon);
  transfun_1d_cal(model, 0);
  calculate_line_from_blrmodel(model, Tline, Fline, parset.n_line_recon);

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

  if(parset.flag_dim == -1)
  {
    error = line_error_mean * sqrt(n_line_data) * (Vline_data[1] - Vline_data[0]);
  }
  else
  {
    error = line_error_mean;
  }
  for(i=0; i<parset.n_line_recon; i++)
  {
    fprintf(fp, "%e %e %e\n", Tline[i]*(1.0+parset.redshift), Fline[i]/line_scale + gsl_ran_ugaussian(gsl_r)*error/line_scale,
      error/line_scale);
  }

  // output transfer function.
  sprintf(fname, "%s/%s", parset.file_dir, parset.tran_out_file);
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s\n", fname);
    exit(-1);
  }

  for(i=0; i<parset.n_tau; i++)
  {
    fprintf(fp, "%e %e\n", TransTau[i], Trans1D[i]);
  }
  fclose(fp);

  transfun_2d_cal(model, TransV, Trans2D, parset.n_vel_recon, 1);
  calculate_line2d_from_blrmodel(model, Tline, TransV,
          Trans2D, Fline2d, parset.n_line_recon, parset.n_vel_recon);

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

  fprintf(fp, "# %d %d\n", parset.n_line_recon, parset.n_vel_recon);
  for(i=0; i<parset.n_line_recon; i++)
  {
    fprintf(fp, "# %f\n", Tline[i]*(1.0+parset.redshift));
    for(j=0; j<parset.n_vel_recon; j++)
    {
      fprintf(fp, "%e %e %e\n", TransW[j],
        (Fline2d[i*parset.n_vel_recon + j] + gsl_ran_ugaussian(gsl_r)*line_error_mean*0.3)/line_scale, line_error_mean/line_scale);
    }

    fprintf(fp, "\n");
  }
  fclose(fp);

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

  for(i=0; i<parset.n_line_recon; i++)
  {
    fprintf(fp, "%f %f\n", parset.InstRes * VelUnit, parset.InstRes_err * VelUnit);
  }
  fclose(fp);

  // output 2d transfer function
  sprintf(fname, "%s/%s", parset.file_dir, parset.tran2d_out_file);
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s\n", fname);
    exit(-1);
  }
  fprintf(fp, "# %d %d\n", parset.n_tau, parset.n_vel_recon);
  for(i=0; i<parset.n_tau; i++)
  {
    for(j=0; j<parset.n_vel_recon; j++)
    {
      fprintf(fp, "%e %e %e\n", TransV[j]*VelUnit, TransTau[i], Trans2D[i*parset.n_vel_recon + j]);
    }

    fprintf(fp, "\n");
  }
  fclose(fp);

#ifdef SpecAstro
  double *sa_pm;
  sa_pm = (double *)pm + num_params_blr;

  sa_smooth_init(parset.n_sa_vel_recon, vel_sa, parset.sa_InstRes);

  gen_sa_cloud_sample((void *)sa_pm, 3, 0);
  calculate_sa_sim_with_sample(pm, vel_sa, parset.n_sa_vel_recon, base_sa, parset.n_sa_base_recon,
                                   phase_sa, Fline_sa);

  sprintf(fname, "%s/%s", parset.file_dir, "data/sim_sa.txt");
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
    exit(0);
  }
  // output sa line
  fprintf(fp, "# %d %d %d\n", 1, parset.n_sa_vel_recon, parset.n_sa_base_recon);
  for(j=0; j<parset.n_sa_vel_recon; j++)
  {
    fprintf(fp, "%e %e %e\n", wave_sa[j], Fline_sa[j] + gsl_ran_ugaussian(gsl_r)*sa_line_error_mean, sa_line_error_mean);
  }
  fprintf(fp, "\n");
  for(i=0; i<parset.n_sa_base_recon; i++)
  {
    fprintf(fp, "# %f %f\n", base_sa[i*2], base_sa[i*2+1]);
    for(j=0; j<parset.n_sa_vel_recon; j++)
    {
      fprintf(fp, "%e %e %e\n", wave_sa[j],
       phase_sa[i*parset.n_sa_vel_recon + j]/(PhaseFactor * wave_sa[j]) + gsl_ran_ugaussian(gsl_r)*sa_phase_error_mean,
       sa_phase_error_mean);
    }
    fprintf(fp, "\n");
  }
  fclose(fp);
  sa_smooth_end();

#endif

  smooth_end();
  sim_end();
}

void sim_init()
{
  int i, j, idx;
  double dT, Tspan;
  double *pm, Rblr, mbh;

  switch(parset.flag_blrmodel)
  {
    case -1:
      num_params_blr_model = num_params_MyTransfun2d;
      transfun_1d_cal = transfun_1d_cal_mytransfun;
      transfun_2d_cal = transfun_2d_cal_mytransfun;
      break;

    case 0:
      num_params_blr_model = num_params_MyBLRmodel2d;
      gen_cloud_sample = gen_cloud_sample_mymodel;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    case 1:
      num_params_blr_model = sizeof(BLRmodel1)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model1;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;
    case 2:
      num_params_blr_model = sizeof(BLRmodel2)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model2;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;
    case 3:
      num_params_blr_model = sizeof(BLRmodel3)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model3;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;
    case 4:
      num_params_blr_model = sizeof(BLRmodel4)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model4;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;
    case 5:
      num_params_blr_model = sizeof(BLRmodel5)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model5;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    case 6:
      num_params_blr_model = sizeof(BLRmodel6)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model6;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    case 7:
      num_params_blr_model = sizeof(BLRmodel7)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model7;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    case 8:
      num_params_blr_model = sizeof(BLRmodel8)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model8;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    case 9:
      num_params_blr_model = sizeof(BLRmodel9)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model9;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;

    default:
      num_params_blr_model = sizeof(BLRmodel1)/sizeof(double);
      gen_cloud_sample = gen_cloud_sample_model1;
      transfun_1d_cal = transfun_1d_cal_cloud;
      transfun_2d_cal = transfun_2d_cal_cloud;
      break;
  }

#ifdef SpecAstro
  set_sa_blr_model();
  /* SA */
  num_params_sa_blr = num_params_sa_blr_model + num_params_sa_extpar;
  num_params_sa = num_params_sa_blr;

#endif

  /* use epoch-independent broadening */
  if(parset.flag_InstRes > 1)
  {
    num_params_res = 1;
    parset.InstRes = 220.0/VelUnit;
    parset.InstRes_err = 0.0;
  }
  else
  {
    parset.InstRes /= VelUnit;
    parset.InstRes_err = 0.0;
  }

  if(parset.flag_narrowline > 1)
  {
    printf("# set flag_narrowline to 1.\n");
    parset.flag_narrowline = 1;
  }
  else if(parset.flag_narrowline == 0)
  {
    parset.width_narrowline = 0.0;
  }

  parset.flag_linecenter = 0;
  num_params_linecenter = 0;

  parset.num_particles = 1;
  which_particle_update = 0;
  force_update = 1;
  which_parameter_update = -1;

  num_params_blr = num_params_blr_model + num_params_nlr
                 + num_params_res + num_params_linecenter + 1; /* include line sys err */
  num_params_var = num_params_drw + num_params_trend + num_params_difftrend + num_params_resp;

  num_params_blr_tot = num_params_blr;
#ifdef SpecAstro
  num_params_blr_tot  += num_params_sa_blr;
#endif
  num_params = num_params_blr_tot + num_params_var + parset.n_con_recon;

  /* index of A and Ag */
  idx_resp = num_params_blr_tot + num_params_drw + num_params_trend;
  /* index of different trend */
  idx_difftrend = idx_resp + num_params_resp;

  model = malloc(num_params * sizeof(double));
  par_fix = (int *) malloc(num_params * sizeof(int));
  par_fix_val = (double *) malloc(num_params * sizeof(double));

  /* setup parameters */
  pm = (double *)model;
  if(parset.flag_blrmodel == -1)
  {
    set_par_value_mytransfun_sim(pm);
  }
  else
  {
    set_par_value_sim(pm, parset.flag_blrmodel);
    set_par_fix_blrmodel();
    for(i=0; i<num_params_blr_model; i++)
    {
      if(par_fix[i] == 1)
      {
        pm[i] = par_fix_val[i];
      }
    }
  }

  /* spectral broadening, note this is a deviation from the input value */
  pm[num_params_blr_model + num_params_nlr ] = 0.0;

  pm[idx_resp + 0] = log(1.0); //A
  pm[idx_resp + 1] = 0.0;      //Ag

#ifdef SpecAstro
  double *sa_model = pm + num_params_blr;
  set_idx_par_mutual();
  set_par_value_sim(sa_model, parset.flag_sa_blrmodel);
  set_par_fix_sa_blrmodel();
  for(i=0; i<num_params_sa_blr_model; i++)
  {
    if(par_fix[num_params_blr + i] == 1)
    {
      pm[num_params_blr + i] = par_fix_val[num_params_blr + i];
    }
  }

  /* set the same mbh and inc */
  sa_model[idx_sa_par_mutual[0]] = pm[idx_rm_par_mutual[0]]; //mbh
  sa_model[idx_sa_par_mutual[1]] = pm[idx_rm_par_mutual[1]]; //inc

  sa_model[num_params_sa_blr_model]     = log(550.0); //DA
  sa_model[num_params_sa_blr_model + 1] = 0.0;        //PA
  sa_model[num_params_sa_blr_model + 2] = 0.0;        //FA
  sa_model[num_params_sa_blr_model + 3] = 0.0;        //CO
#endif

  Fcon = malloc(parset.n_con_recon * sizeof(double));
  Fcon_rm = malloc(parset.n_con_recon * sizeof(double));

  idx = get_idx_mbh_from_blrmodel();
  mbh = exp(pm[idx]);

  if(parset.flag_blrmodel != 8) /* model 8 is particular */
  {
    Rblr = exp(pm[0]);
  }
  else
  {
    Rblr = exp(pm[9]);
  }

  if(parset.flag_dim == -1)
  {
    Tspan = Tcon_data[n_con_data -1] - Tcon_data[0];

  /* set time array for continuum */
    Tcon_min = Tcon_data[0] - time_back_set - 10.0;

    Tcon_max = fmax(Tcon_data[n_con_data-1], Tline_data[n_line_data-1]) + fmax(0.05*Tspan, 10.0);
    dT = (Tcon_max - Tcon_min)/(parset.n_con_recon -1);

    for(i=0; i<parset.n_con_recon; i++)
    {
      Tcon[i] = Tcon_min + i*dT;
    }
  }
  else
  {
    Tspan = Rblr*10.0;

    rcloud_min_set = 0.0;
    rcloud_max_set = Tspan/2.0;
    if(parset.rcloud_max > 0.0)
      rcloud_max_set = fmin(rcloud_max_set, parset.rcloud_max);
    printf("RM rcloud_min_max_set: %f %f\n", rcloud_min_set, rcloud_max_set);

    time_back_set = 2.0*rcloud_max_set;
    Tcon_min = 0.0 - time_back_set - 10.0;
    Tcon_max = Tspan + 10.0;

    dT = (Tcon_max - Tcon_min)/(parset.n_con_recon -1);

    for(i=0; i<parset.n_con_recon; i++)
    {
      Tcon[i] = Tcon_min + i*dT;
    }
  }

  /* set Larr_rec */
  for(i=0;i<parset.n_con_recon;i++)
  {
    Larr_rec[i*nq + 0]=1.0;
    for(j=1; j<nq; j++)
      Larr_rec[i*nq + j] = pow(Tcon[i], j);
  }

  if(parset.flag_dim == -1)
  {
    parset.n_line_recon = n_line_data;
    parset.n_vel_recon = n_vel_data;
  }

  TransTau = malloc(parset.n_tau * sizeof(double));
  TransV = malloc(parset.n_vel_recon * sizeof(double));
  TransW = malloc(parset.n_vel_recon * sizeof(double));
  Trans1D = malloc(parset.n_tau * sizeof(double));
  Trans2D = malloc(parset.n_tau * parset.n_vel_recon * sizeof(double));
  Tline = malloc(parset.n_line_recon * sizeof(double));
  Fline = malloc(parset.n_line_recon * sizeof(double));
  Fline2d = malloc(parset.n_line_recon * parset.n_vel_recon * sizeof(double));

  if(parset.flag_dim == -1)
  {
    memcpy(Tline, Tline_data, n_line_data * sizeof(double));
    memcpy(TransV, Vline_data, n_vel_data * sizeof(double));
    memcpy(TransW, Wline_data, n_vel_data * sizeof(double));
  }
  else
  {
    Tline_min = 0.0;
    Tline_max = Tcon_max - 1.0;

    dT = (Tline_max - Tline_min)/(parset.n_line_recon - 1);

    for(i=0; i<parset.n_line_recon; i++)
    {
      Tline[i] = Tline_min + i*dT;
    }

    double vel_max_set, vel_min_set;
    vel_max_set = sqrt(pow(2.0*sqrt(mbh/Rblr), 2.0) + pow(2.0*parset.InstRes, 2.0));
    vel_min_set = - vel_max_set;
    double dVel = (vel_max_set- vel_min_set)/(parset.n_vel_recon -1.0);

    for(i=0; i<parset.n_vel_recon; i++)
    {
      TransV[i] = vel_min_set + dVel*i;
      TransW[i] = (1.0 + TransV[i]/C_Unit) * parset.linecenter * (1.0+parset.redshift);
    }
  }

  clouds_tau = malloc(parset.n_cloud_per_task * sizeof(double));
  clouds_weight = malloc(parset.n_cloud_per_task * sizeof(double));
  clouds_vel = malloc(parset.n_cloud_per_task * parset.n_vel_per_cloud * sizeof(double));

  if(parset.flag_save_clouds && thistask == roottask)
  {
    if(parset.n_cloud_per_task <= 1000)
      icr_cloud_save = 1;
    else
      icr_cloud_save = parset.n_cloud_per_task/1000;

    char fname[200];
    sprintf(fname, "%s/%s", parset.file_dir, parset.cloud_out_file);
    fcloud_out = fopen(fname, "w");
    if(fcloud_out == NULL)
    {
      fprintf(stderr, "# Error: Cannot open file %s\n", fname);
      exit(-1);
    }
  }

#ifdef SpecAstro
  double saRblr;

  if(parset.flag_dim == -1)
  {
    parset.n_sa_vel_recon = n_vel_sa_data;
    parset.n_sa_base_recon = n_base_sa_data;
  }
  else
  {
    sa_flux_norm = 1.0;
    parset.n_sa_vel_recon = 40;
    if(parset.flag_gravity == 1)
      parset.n_sa_base_recon = n_base_sa_3c273;
    else
      parset.n_sa_base_recon = 20;

    sa_phase_error_mean = 0.01;
    sa_line_error_mean = 0.01;
  }

  vel_sa = malloc(parset.n_sa_vel_recon * sizeof(double));
  wave_sa = malloc(parset.n_sa_vel_recon * sizeof(double));
  Fline_sa = malloc(parset.n_sa_vel_recon * sizeof(double));
  base_sa = malloc(parset.n_sa_base_recon * 2 * sizeof(double));
  phase_sa = malloc(parset.n_sa_vel_recon * parset.n_sa_base_recon * sizeof(double));

  clouds_alpha = malloc(parset.n_cloud_per_task * sizeof(double));
  clouds_beta = malloc(parset.n_cloud_per_task * sizeof(double));

  workspace_phase = malloc(parset.n_sa_vel_recon * 3 * sizeof(double));

  if(parset.flag_sa_blrmodel != 8)
  {
    saRblr = exp(pm[num_params_blr]);
  }
  else
  {
    saRblr = exp(pm[num_params_blr + 9]);
  }
  rcloud_max_set = fmax(rcloud_max_set, saRblr * 5.0);
  printf("SA rcloud_min_max_set: %f %f\n", rcloud_min_set, rcloud_max_set);

  if(parset.flag_dim == -1)
  {
    memcpy(vel_sa, vel_sa_data, parset.n_sa_vel_recon * sizeof(double));
    memcpy(wave_sa, wave_sa_data, parset.n_sa_vel_recon * sizeof(double));

    memcpy(base_sa, base_sa_data, parset.n_sa_base_recon * 2 * sizeof(double));
  }
  else
  {
    double vel_max_set, vel_min_set;
    vel_max_set = sqrt(pow(2.0*sqrt(mbh/saRblr), 2.0) + pow(2.0*parset.sa_InstRes, 2.0));
    vel_min_set = - vel_max_set;
    double dVel = (vel_max_set- vel_min_set)/(parset.n_sa_vel_recon -1.0);
    for(i=0; i<parset.n_sa_vel_recon; i++)
    {
      vel_sa[i] = vel_min_set + dVel*i;
      wave_sa[i] = (1.0 + vel_sa[i]/C_Unit) * parset.sa_linecenter * (1.0+parset.redshift);
    }

    if(parset.flag_gravity == 1)
      memcpy(base_sa, base_sa_3c273, n_base_sa_3c273*2*sizeof(double));
    else
    {
      double phi;
      for(i=0; i<parset.n_sa_base_recon; i++)
      {
        phi = -PI/2.0 + PI/parset.n_sa_base_recon * i;
        base_sa[i*2+0] = 100.0*cos(phi);
        base_sa[i*2+1] = 100.0*sin(phi);
      }
    }

  }
#endif

  return;
}

void sim_end()
{
  free(model);
  free(par_fix);
  free(par_fix_val);
  free(Fcon);
  free(Fcon_rm);

  free(TransTau);
  free(TransV);
  free(TransW);
  free(Tline);
  free(Fline);
  free(Fline2d);
  free(Trans2D);
  free(Trans1D);

  free(clouds_tau);
  free(clouds_weight);
  free(clouds_vel);

  if(parset.flag_save_clouds && thistask == roottask)
  {
    fclose(fcloud_out);
  }

#ifdef SpecAstro

  free(vel_sa);
  free(wave_sa);
  free(Fline_sa);
  free(base_sa);
  free(phase_sa);

  free(clouds_alpha);
  free(clouds_beta);

  free(workspace_phase);
#endif
}

/*
 * set parameter values for either RM or SA
 */
void set_par_value_sim(double *pm, int flag_model)
{
  int i;
  switch(flag_model)
  {
    case 0:
      set_par_value_mymodel_sim(pm);
      break;

    case 1:
      i=0;
      pm[i++] = log(4.0);
      pm[i++] = 0.9;
      pm[i++] = 0.2;
      pm[i++] = cos(20.0/180.0*PI);
      pm[i++] = 40.0;
      pm[i++] = 0.0;
      pm[i++] = log(3.0);
      pm[i++] = 0.1;
      pm[i++] = 0.5;
      break;

    case 2:
      i=0;
      pm[i++] = log(4.0);
      pm[i++] = 0.9;
      pm[i++] = 0.2;
      pm[i++] = cos(20.0/180.0*PI);
      pm[i++] = 40.0;
      pm[i++] = 0.0;
      pm[i++] = log(3.0);
      pm[i++] = log(0.01);
      pm[i++] = log(0.1);
      break;

    case 3:
      i=0;
      pm[i++] = log(3.0);
      pm[i++] = log(5.0);
      pm[i++] = -1.0;
      pm[i++] = cos(20.0/180.0*PI);
      pm[i++] = 40.0;
      pm[i++] = 0.0;
      pm[i++] = log(3.0);
      pm[i++] = 0.5;
      pm[i++] = 0.5;
      break;

    case 4:
      i=0;
      pm[i++] = log(3.0);
      pm[i++] = log(5.0);
      pm[i++] = -1.0;
      pm[i++] = cos(20.0/180.0*PI);
      pm[i++] = 40.0;
      pm[i++] = 0.0;
      pm[i++] = log(3.0);
      pm[i++] = 0.5;
      pm[i++] = 0.5;
      break;

    case 5:
      i=0;
      pm[i++] = log(4.0);  //mu
      pm[i++] = 0.5;       //Fin
      pm[i++] = log(2.0);  //Fout
      pm[i++] = 1.5;       //alpha
      pm[i++] = cos(20.0/180.0*PI);      //inc
      pm[i++] = 40.0;      //opn
      pm[i++] = 0.5;       //k
      pm[i++] = 2.0;       //gam
      pm[i++] = 0.5;      //xi
      pm[i++] = log(2.0); //mbh
      pm[i++] = 0.5;      //fellip
      pm[i++] = 0.5;      //fflow
      pm[i++] = log(0.01);
      pm[i++] = log(0.1);
      pm[i++] = log(0.01);
      pm[i++] = log(0.1);
      pm[i++] = 0.0;      //theta_rot
      pm[i++] = log(0.001); //sig_turb
      break;

    case 6:
      i=0;
      pm[i++] = log(4.0);   // mu
      pm[i++] = 1.0;        // beta
      pm[i++] = 0.25;        // F
      pm[i++] = cos(20.0/180.0*PI); // inc
      pm[i++] = 40.0;       // opn
      pm[i++] = 0.0;        // kappa
      pm[i++] = 1.0;        // gamma
      pm[i++] = 1.0;        // obscuration
      pm[i++] = log(2.0);  //mbh
      pm[i++] = 0.5;       //fellip
      pm[i++] = 0.4;       //fflow
      pm[i++] = log(0.01); //
      pm[i++] = log(0.1);  //
      pm[i++] = log(0.01); //
      pm[i++] = log(0.1);  //
      pm[i++] = 0.0;       // theta_rot
      pm[i++] = log(0.001);  // sig_turb
      break;

    case 7:
      i=0;
      pm[i++] = log(4.0);  //mu
      pm[i++] = 0.8;       //beta
      pm[i++] = 0.1;       //F
      pm[i++] = cos(20.0/180.0*PI);//inc
      pm[i++] = 40.0;      //opn
      pm[i++] = 0.0;       //kappa
      pm[i++] = 1.0;       //gamma
      pm[i++] = 0.0;       //xi

      pm[i++] = 0.5;      //fsh
      pm[i++] = log(8.0); //mu_un
      pm[i++] = 1.2;      //beta_un
      pm[i++] = 0.1;      //F_un
      pm[i++] = 20.0;     //opn_un

      pm[i++] = log(2.0); //mbh
      pm[i++] = 0.5;      //fellip
      pm[i++] = 0.4;      //fflow
      pm[i++] = log(0.01);
      pm[i++] = log(0.1);
      pm[i++] = log(0.01);
      pm[i++] = log(0.1);
      pm[i++] = 0.0;      //theta_rot
      pm[i++] = 0.5;      //fellip_un
      pm[i++] = 0.4;      //fflow_un
      pm[i++] = log(0.001); // sig_turb
      break;

    case 8:
      i=0;
      pm[i++] = 50.0;   //theta_min
      pm[i++] = 20.0;   //dtheta_max
      pm[i++] = log(1.0); // r_min
      pm[i++] = 4.0;      // fr_max
      pm[i++] = 1.0;      // gamma
      pm[i++] = 1.0;      // alpha
      pm[i++] = -2.0;      // lambda
      pm[i++] = -0.5;      // k
      pm[i++] = 0.0;      // xi
      pm[i++] = log(30.0);     // Rv
      pm[i++] = log(20.0);     // Rblr, should larger than r_max=r_max * fr_max
      pm[i++] = cos(30.0/180.0*PI);     // inc
      pm[i++] = log(4.0); // mbh
      break;

    case 9:
      i=0;
      pm[i++] = log(4.0);   //mu
      pm[i++] = 1.0;        //beta
      pm[i++] = 0.2;        //F
      pm[i++] = cos(30.0/180.0*PI);     // inc
      pm[i++] = 30.0;       //opn
      pm[i++] = log(4.0);   // mbh
      break;
  }
  return;
}

/*
 * get index of mbh from a BLR model.
 *
 */
int get_idx_mbh_from_blrmodel()
{
  int idx = -1;
  switch(parset.flag_blrmodel)
  {
    case 0:
      idx = offsetof(MyBLRmodel, mbh);
      break;

    case 1:
      idx = offsetof(BLRmodel1, mbh);
      break;

    case 2:
      idx = offsetof(BLRmodel2, mbh);
      break;

    case 3:
      idx = offsetof(BLRmodel3, mbh);
      break;

    case 4:
      idx = offsetof(BLRmodel4, mbh);
      break;

    case 5:
      idx = offsetof(BLRmodel5, mbh);
      break;

    case 6:
      idx = offsetof(BLRmodel6, mbh);
      break;

    case 7:
      idx = offsetof(BLRmodel7, mbh);
      break;

    case 8:
      idx = offsetof(BLRmodel8, mbh);
      break;

    case 9:
      idx = offsetof(BLRmodel9, mbh);
      break;
  }
  return idx / sizeof(double);
}