#ifndef UTIL_H
#define UTIL_H

#include <iostream>
#include <stdio.h>
#include <vector>
#include <Eigen/Core>
#include <Eigen/SparseCore>
#include <Eigen/Householder>
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include <random>
#include <atomic>
#ifdef USE_MKL
#include <mkl_cblas.h>
#include <mkl_lapacke.h>
#include <mkl_lapack.h>
#else
#include <cblas.h>
#include <lapacke.h>
#endif

typedef Eigen::SparseMatrix<double, 0, int> SpMat;
typedef Eigen::VectorBlock<Eigen::Matrix<double, -1,  1, 0, -1,  1>, -1> Segment;
typedef Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, -1, -1> MatrixBlock;

bool are_connected(Eigen::VectorXi &a, Eigen::VectorXi &b, SpMat &A);
bool should_be_disconnected(int lvl1, int lvl2, int sep1, int sep2);
double elapsed(timeval& start, timeval& end);
void swap2perm(Eigen::VectorXi* swap, Eigen::VectorXi* perm);
bool isperm(Eigen::VectorXi* perm);
SpMat symmetric_graph(SpMat& A);

typedef timeval timer;
timer wctime();

/* Concatenate the matrices vertically in the vector H */
void concatenate(std::vector<Eigen::MatrixXd*> H, Eigen::MatrixXd* V);
Eigen::MatrixXd Vconcatenate(std::vector<Eigen::MatrixXd*> H);
void rVconcatenate(std::vector<Eigen::MatrixXd*> H, Eigen::MatrixXd& Hc);

Eigen::VectorXd Vconcatenate(std::vector<Eigen::VectorXd*> H);

/* Concatenate the matrices horizontally in the vector H */
Eigen::MatrixXd Hconcatenate(std::vector<Eigen::MatrixXd*> H);
/* Reverse the concatenation*/
void rHconcatenate(std::vector<Eigen::MatrixXd*> H, Eigen::MatrixXd& Hc);

/**
 * C <- alpha A   * B   + beta C
 * C <- alpha A^T * B   + beta C
 * C <- alpha A   * B^T + beta C
 * C <- alpha A^T * B^T + beta C
 * Gemm
 */
void gemm(Eigen::MatrixXd* A, Eigen::MatrixXd* B, Eigen::MatrixXd* C, CBLAS_TRANSPOSE tA, CBLAS_TRANSPOSE tB, double alpha, double beta);

/** Return a new
 * C <- alpha A^(/T) * B^(/T)
 **/
Eigen::MatrixXd* gemm_new(Eigen::MatrixXd* A, Eigen::MatrixXd* B, CBLAS_TRANSPOSE tA, CBLAS_TRANSPOSE tB, double alpha);

/**
 * C <- C - A * A^T
 */
void syrk(Eigen::MatrixXd* A, Eigen::MatrixXd* C);

/**
 * A <- L, L L^T = A
 * Return != 0 if potf failed (not spd)
 */
int potf(Eigen::MatrixXd* A);

/**
 * A <- [L\U] (lower and upper)
 * p <- swap (NOT a permutation)
 * A[p] = L*U
 * L is unit diagonal
 * U is not
 * Return != 0 if getf failed (singular)
 */
int getf(Eigen::MatrixXd* A, Eigen::VectorXi* swap);

/**
 * Compute an estimated 1-norm condition number of A using its LU or Cholesky factorization
 */
double rcond_1_getf(Eigen::MatrixXd* A_LU, double A_1_norm);
double rcond_1_potf(Eigen::MatrixXd* A_LLT, double A_1_norm);

/**
 * B <- B * L^(-1)
 * B <- B * L^(-T)
 * B <- B * U^(-1)
 * B <- B * U^(-T)
 */
void trsm_right(Eigen::MatrixXd* L, Eigen::MatrixXd* B, CBLAS_UPLO uplo, CBLAS_TRANSPOSE trans, CBLAS_DIAG diag);

/**
 * B <- L^(-1) * B
 * B <- L^(-T) * B
 * B <- U^(-1) * B
 * B <- U^(-T) * B
 */
void trsm_left(Eigen::MatrixXd* L, Eigen::MatrixXd* B, CBLAS_UPLO uplo, CBLAS_TRANSPOSE trans, CBLAS_DIAG diag);

/**
 * x <- L^(-1) * x
 * x <- L^(-T) * x
 * x <- U^(-1) * x
 * x <- U^(-T) * x
 */
void trsv(Eigen::MatrixXd* L, Segment* x, CBLAS_UPLO uplo, CBLAS_TRANSPOSE trans, CBLAS_DIAG diag);

/**
 * x <- L^T * x
 */
void trmv_trans(Eigen::MatrixXd* L, Segment* x);

/**
 * A <- L^T * A
 */
void trmm_trans(Eigen::MatrixXd* L, Eigen::MatrixXd* A);

/**
 * x2 <- x2 - A21 * x1
 */
void gemv_notrans(Eigen::MatrixXd* A21, Segment* x1, Segment* x2);

/**
 * x2 <- x2 - A12^T * x1
 */
void gemv_trans(Eigen::MatrixXd* A12, Segment* x1, Segment* x2);

/**
 * AP = QR
 */
void geqp3(Eigen::MatrixXd* A, Eigen::VectorXi* jpvt, Eigen::VectorXd* tau);

/**
 * Form householder vector from x
 */
double house(Eigen::VectorXd& x);

/**
 * RRQR with truncation when the max R_ii < tol
 */
void rrqr(Eigen::MatrixXd* A, Eigen::VectorXi* jpvt, Eigen::VectorXd* tau, double& tol, int& rank);

// template<typename T>
// Eigen::MatrixXd get_gaussian(const int rows, const int cols, T* gen);
template<typename T>
Eigen::MatrixXd get_gaussian(const int rows, const int cols, T* gen) {
    std::normal_distribution<double> norm_dist(0.0, 1.0);
    Eigen::MatrixXd W(rows, cols);
    for(int j = 0; j < cols; j++){
        for(int i = 0; i < rows; i++){
            W(i,j) = norm_dist(*gen);
        }
    }
    Eigen::VectorXd col_norms = W.colwise().norm();
    assert(col_norms.size() == cols);
    assert(col_norms.minCoeff() >= 0);
    if (col_norms.minCoeff() > 0) {
        return W.normalized();
    }
    return W;
}

Eigen::MatrixXd get_uniform(const int rows, const int cols);
void random_rrqr(const Eigen::MatrixXd* A, Eigen::MatrixXd* v, Eigen::VectorXd* h,  double tol, std::function<Eigen::MatrixXd(int,int)> gen_gaussian);

// void laqps(Eigen::MatrixXd* A, Eigen::VectorXi* jpvt, Eigen::VectorXd* tau, double& tol, int block_size, int& rank);
void laqps(Eigen::MatrixXd* A, Eigen::VectorXi* jpvt, Eigen::VectorXd* tau, double& tol, int block_size, int& rank);
/**
 * A = U S U^T
 * Compute the full EVD, where if A is mxm, U is mxm and S is mxm
 */

void geevd(Eigen::MatrixXd* A, Eigen::MatrixXd* U, Eigen::VectorXd* S);

/**
 * A = U S VT
 * Compute the full SVD, where if A is mxn, U is mxm, V is nxn, and S is min(M,N)
 * VT is V^T, *not* V.
 */
void gesvd(Eigen::MatrixXd* A, Eigen::MatrixXd* U, Eigen::VectorXd* S, Eigen::MatrixXd* VT);

/**
 * Compute the singular values of a matrix A
 * A = m x n matrix
 * S = min(M,N) vector
 */
void gesvd_values(Eigen::MatrixXd* A,  Eigen::VectorXd* S);

/**
 * x <- Q * x
 * A <- Q * A
 */
void ormqr_notrans(Eigen::MatrixXd* v, Eigen::VectorXd* h, Segment* x);
void ormqr_notrans_left(Eigen::MatrixXd* v, Eigen::VectorXd* h, Eigen::MatrixXd* A);

/**
 * A <- A * Q
 * */
void ormqr_notrans_right(Eigen::MatrixXd* v, Eigen::VectorXd* h, Eigen::MatrixXd* A);
/**
 * x <- Q^T * x
 * A <- Q^T * A
 */
void ormqr_trans(Eigen::MatrixXd* v, Eigen::VectorXd* h, Segment* x);
void ormqr_trans_left(Eigen::MatrixXd* v, Eigen::VectorXd* h, Eigen::MatrixXd* A);

/**
 * A <- A   * Q
 * A <- A   * Q^T
 * A <- Q   * A
 * A <- Q^T * A
 */
void ormqr(Eigen::MatrixXd* v, Eigen::VectorXd* h, Eigen::MatrixXd* A, char side, char trans);

/**
 * Create the thin Q
 */
void orgqr(Eigen::MatrixXd* v, Eigen::VectorXd* h);

/* Finds upper triangular matrix T such that,
    H  =  I - V * T * V^T
    using Householder vectors V and their norms tau
    H = Householder reflector matrix
*/
void larft(Eigen::MatrixXd* V, Eigen::VectorXd* tau, Eigen::MatrixXd* T);

/* Apply householder vectors on a rectangular matrix
V = [1    *     *   * *
     v(1) 1     *   * *
     v(1) v(2)  *   * *
     v(1) v(2) v(3) * *];
H = H(1) H(2) H(3) ... H(k)
C <- H^T C
*/
void larfb(Eigen::MatrixXd* V, Eigen::MatrixXd* T, Eigen::MatrixXd* C);
void larfb(Eigen::MatrixXd* V, Eigen::MatrixXd* T, Eigen::VectorXd* C, char side = 'L', char trans = 'T', char direct = 'F', char storev = 'C');

/**
 * A = QR
 */
void geqrf(Eigen::MatrixXd* A, Eigen::VectorXd* tau);

int choose_rank(Eigen::VectorXd& s, double tol, bool rel = true);

std::size_t hashv(std::vector<size_t> vals);

// Hash function for Eigen matrix and vector.
// The code is from `hash_combine` function of the Boost library. See
// http://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine .
template<typename T>
struct matrix_hash : std::unary_function<T, size_t> {
  std::size_t operator()(T const& matrix) const {
    // Note that it is oblivious to the storage order of Eigen matrix (column- or
    // row-major). It will give you the same hash value for two different matrices if they
    // are the transpose of each other in different storage order.
    size_t seed = 0;
    for (size_t i = 0; i < matrix.size(); ++i) {
      auto elem = *(matrix.data() + i);
      seed ^= std::hash<typename T::Scalar>()(elem) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
    return seed;
  }
};

void block2dense(Eigen::VectorXi &rowval, Eigen::VectorXi &colptr, Eigen::VectorXd &nnzval, int i, int j, int li, int lj, Eigen::MatrixXd *dst, bool transpose);

Eigen::MatrixXd linspace_nd(int n, int dim);

// Returns A[p,p]
SpMat symm_perm(SpMat &A, Eigen::VectorXi &p);

// Permute the columns of non-square matrix A
SpMat col_perm(SpMat &A, Eigen::VectorXi &p);

// Random vector with seed
Eigen::VectorXd random(int size, int seed);

Eigen::MatrixXd random(int rows, int cols, int seed);

// Print vector
template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
    for(auto v_ : v) {
        os << v_ << " " ;
    }
    os << std::endl;
    return os;
}

#endif


/* specfunc/elljac.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Author:  G. Jungman */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include "gsl_sf_pow_int.h"
#include "gsl_sf_elljac.h"

/* See [Thompson, Atlas for Computing Mathematical Functions] */

int
gsl_sf_elljac_e(double u, double m, double * sn, double * cn, double * dn)
{
  if(fabs(m) > 1.0) {
    *sn = 0.0;
    *cn = 0.0;
    *dn = 0.0;
    GSL_ERROR ("|m| > 1.0", GSL_EDOM);
  }
  else if(fabs(m) < 2.0*GSL_DBL_EPSILON) {
    *sn = sin(u);
    *cn = cos(u);
    *dn = 1.0;
    return GSL_SUCCESS;
  }
  else if(fabs(m - 1.0) < 2.0*GSL_DBL_EPSILON) {
    *sn = tanh(u);
    *cn = 1.0/cosh(u);
    *dn = *cn;
    return GSL_SUCCESS;
  }
  else {
    int status = GSL_SUCCESS;
    const int N = 16;
    double   a[16];
    double   b[16];
    double   c[16];
    double phi[16];
    double psi[16]; /* psi[i] := phi[i] - Pi 2^{i-1} */
    double two_N;
    int n = 0;

    a[0] = 1.0;
    b[0] = sqrt(1.0 - m);
    c[0] = sqrt(m);

    while( fabs(c[n]) > 4.0 * GSL_DBL_EPSILON) {
      a[n+1] = 0.5 * (a[n] + b[n]);
      b[n+1] = sqrt(a[n] * b[n]);
      c[n+1] = 0.5 * (a[n] - b[n]);
      if(n >= N - 2) {
        status = GSL_EMAXITER;
	c[N-1] = 0.0;
	break;
      }
      ++n;
    }

    --n;
    two_N = (double)(1 << n ); /* 2^n */  /* gsl_sf_pow_int(2.0, n); */
    phi[n] = two_N * a[n] * u;
    psi[n] = two_N * (a[n]*u - 0.5*M_PI);

    while(n > 0) {
      const double psi_sgn = ( n == 1 ? -1.0 : 1.0 );
      const double phi_asin_arg = c[n] * sin(phi[n])/a[n];
      const double psi_asin_arg = c[n]/a[n] * psi_sgn * sin(psi[n]);
      const double phi_asin = asin(phi_asin_arg);
      const double psi_asin = asin(psi_asin_arg);
      phi[n-1] = 0.5 * (phi[n] + phi_asin);
      psi[n-1] = 0.5 * (psi[n] + psi_asin);
      --n;
    }

    *sn = sin(phi[0]);
    *cn = cos(phi[0]);
    {
      /* const double dn_method_1 = *cn / cos(phi[1] - phi[0]); */
      const double dn_method_2 = sin(psi[0])/sin(psi[1] - psi[0]);
      *dn = dn_method_2;
      /* printf("%18.16g  %18.16g\n", dn_method_1, dn_method_2); */
    }

    return status;
  }
}


/* rng/taus.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>

/* This is a maximally equidistributed combined Tausworthe
   generator. The sequence is,

   x_n = (s1_n ^ s2_n ^ s3_n)

   s1_{n+1} = (((s1_n & 4294967294) <<12) ^ (((s1_n <<13) ^ s1_n) >>19))
   s2_{n+1} = (((s2_n & 4294967288) << 4) ^ (((s2_n << 2) ^ s2_n) >>25))
   s3_{n+1} = (((s3_n & 4294967280) <<17) ^ (((s3_n << 3) ^ s3_n) >>11))

   computed modulo 2^32. In the three formulas above '^' means
   exclusive-or (C-notation), not exponentiation. Note that the
   algorithm relies on the properties of 32-bit unsigned integers (it
   is formally defined on bit-vectors of length 32). I have added a
   bitmask to make it work on 64 bit machines.

   We initialize the generator with s1_1 .. s3_1 = s_n MOD m, where
   s_n = (69069 * s_{n-1}) mod 2^32, and s_0 = s is the user-supplied
   seed.

   The theoretical value of x_{10007} is 2733957125. The subscript
   10007 means (1) seed the generator with s=1 (2) do six warm-up
   iterations, (3) then do 10000 actual iterations.

   The period of this generator is about 2^88.

   From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe
   Generators", Mathematics of Computation, 65, 213 (1996), 203--213.

   This is available on the net from L'Ecuyer's home page,

   http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps
   ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps

   Update: April 2002

   There is an erratum in the paper "Tables of Maximally
   Equidistributed Combined LFSR Generators", Mathematics of
   Computation, 68, 225 (1999), 261--269:
   http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps

        ... the k_j most significant bits of z_j must be non-
        zero, for each j. (Note: this restriction also applies to the
        computer code given in [4], but was mistakenly not mentioned in
        that paper.)

   This affects the seeding procedure by imposing the requirement
   s1 > 1, s2 > 7, s3 > 15.

   The generator taus2 has been added to satisfy this requirement.
   The original taus generator is unchanged.

   Update: November 2002

   There was a bug in the correction to the seeding procedure for s2.
   It affected the following seeds 254679140 1264751179 1519430319
   2274823218 2529502358 3284895257 3539574397 (s2 < 8).

*/

static inline unsigned long int taus_get (void *vstate);
static double taus_get_double (void *vstate);
static void taus_set (void *state, unsigned long int s);

typedef struct
  {
    unsigned long int s1, s2, s3;
  }
taus_state_t;

static inline unsigned long
taus_get (void *vstate)
{
  taus_state_t *state = (taus_state_t *) vstate;

#define MASK 0xffffffffUL
#define TAUSWORTHE(s,a,b,c,d) (((s &c) <<d) &MASK) ^ ((((s <<a) &MASK)^s) >>b)

  state->s1 = TAUSWORTHE (state->s1, 13, 19, 4294967294UL, 12);
  state->s2 = TAUSWORTHE (state->s2, 2, 25, 4294967288UL, 4);
  state->s3 = TAUSWORTHE (state->s3, 3, 11, 4294967280UL, 17);

  return (state->s1 ^ state->s2 ^ state->s3);
}

static double
taus_get_double (void *vstate)
{
  return taus_get (vstate) / 4294967296.0 ;
}

static void
taus_set (void *vstate, unsigned long int s)
{
  taus_state_t *state = (taus_state_t *) vstate;

  if (s == 0)
    s = 1;      /* default seed is 1 */

#define LCG(n) ((69069 * n) & 0xffffffffUL)
  state->s1 = LCG (s);
  state->s2 = LCG (state->s1);
  state->s3 = LCG (state->s2);

  /* "warm it up" */
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  return;
}

static void
taus2_set (void *vstate, unsigned long int s)
{
  taus_state_t *state = (taus_state_t *) vstate;

  if (s == 0)
    s = 1;      /* default seed is 1 */

#define LCG(n) ((69069 * n) & 0xffffffffUL)
  state->s1 = LCG (s);
  if (state->s1 < 2) state->s1 += 2UL;
  state->s2 = LCG (state->s1);
  if (state->s2 < 8) state->s2 += 8UL;
  state->s3 = LCG (state->s2);
  if (state->s3 < 16) state->s3 += 16UL;

  /* "warm it up" */
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  taus_get (state);
  return;
}

static const gsl_rng_type taus_type =
{"taus",                        /* name */
 0xffffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (taus_state_t),
 &taus_set,
 &taus_get,
 &taus_get_double};

const gsl_rng_type *gsl_rng_taus = &taus_type;

static const gsl_rng_type taus2_type =
{"taus2",                       /* name */
 0xffffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (taus_state_t),
 &taus2_set,
 &taus_get,
 &taus_get_double};

const gsl_rng_type *gsl_rng_taus2 = &taus2_type;


#include "openblas_utest.h"
#include <stdio.h>
#include <stdlib.h>
#include <cblas.h>

#define LAPACK_ROW_MAJOR               101
blasint LAPACKE_dgesvd( blasint matrix_layout, char jobu, char jobvt,
                           blasint m, blasint n, double* a,
                           blasint lda, double* s, double* u, blasint ldu,
                           double* vt, blasint ldvt, double* superb );

#define DATASIZE 100

double s[DATASIZE];
double u[DATASIZE*DATASIZE];
double vt[DATASIZE*DATASIZE];
double X[DATASIZE*DATASIZE];
double superb[DATASIZE];
double tmp[DATASIZE*DATASIZE];
double m[DATASIZE*DATASIZE];

CTEST(kernel_regress,skx_avx)
{
#ifdef BUILD_DOUBLE
    double norm;
    int i, j, info;
    srand(0);
	for (i = 0; i < DATASIZE*DATASIZE; i++) {
        m[i] = (rand()+0.0)/RAND_MAX * 10;
        tmp[i] = m[i];
    }

    info = LAPACKE_dgesvd( LAPACK_ROW_MAJOR, 'A', 'A', DATASIZE, DATASIZE, m, DATASIZE,
                        s, u, DATASIZE, vt, DATASIZE, superb);

	for (i = 0; i < DATASIZE; i++) {
	    for (j = 0; j < DATASIZE; j++) {
            u[i*DATASIZE+j] = u[i*DATASIZE+j]*s[j];
        }
    }
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
                DATASIZE, DATASIZE, DATASIZE, 1, u, DATASIZE, vt, DATASIZE, 0, X, DATASIZE);

	for (i = 0; i < DATASIZE*DATASIZE; i++) {
        X[i] = X[i] - tmp[i];
    }

    norm = cblas_dnrm2(DATASIZE*DATASIZE, X, 1);
    ASSERT_DBL_NEAR_TOL(0.0, norm, 1e-10);
#endif
}


/*
  NAME:
     normalize_row
  PURPOSE:
     normalize a row (or column) of a matrix given as logs
  CALLING SEQUENCE:
     normalize_row(gsl_matrix * q, int row,bool isrow,bool noweight,
     double weight)
  INPUT:
     q            - matrix
     row          - row to be normalized
     isrow        - is it a row or a column
     noweight    - add a weight to all of the values?
     weight       - weight to be added to all of the values
  OUTPUT:
     normalization factor (i.e. logsum)
  REVISION HISTORY:
     2008-09-21 - Written Bovy
     2010-04-01 - Added noweight and weight inputs to allow the qij to have
                  weights - Bovy
*/
#include <math.h>
#include <gsl/gsl_matrix.h>
#include "proj_gauss_mixtures.h"

double normalize_row(gsl_matrix * q, int row, bool isrow,
		     bool noweight, double weight){
  double loglike;
  if (isrow)
    loglike = logsum(q,row,true);
  else
    loglike = logsum(q,row,false);

  int dd;
  if (isrow)
    for (dd = 0; dd != q->size2; ++dd) {
      if ( noweight )
	gsl_matrix_set(q,row,dd,gsl_matrix_get(q,row,dd)-loglike);
      else
	gsl_matrix_set(q,row,dd,gsl_matrix_get(q,row,dd)-loglike+weight);
    }
  else
    for (dd = 0; dd != q->size1; ++dd) {
      if ( noweight )
	gsl_matrix_set(q,dd,row,gsl_matrix_get(q,dd,row)-loglike);
      else
	gsl_matrix_set(q,dd,row,gsl_matrix_get(q,dd,row)-loglike+weight);
    }
  if ( ! noweight ) loglike*= exp(weight);

  return loglike;
}


#include <stdio.h>
#include <math.h>
#include <gsl/gsl_statistics.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_randist.h>
#include <Python.h>

static char module_docstring[] = "GSL Statistics module";
static char t_test_docstring[] = "Perform an independent samples t-test";

double t_test(const double *data1, int n1, const double *data2, int n2)
{
    double mean1, mean2, var1, var2, num, sp, denom, t, dof, p;
    mean1 = gsl_stats_mean(data1, 1, n1);
    mean2 = gsl_stats_mean(data2, 1, n2);
    var1 = gsl_stats_variance(data1, 1, n1);
    var2 = gsl_stats_variance(data2, 1, n2);
    num = mean1 - mean2;
    sp = sqrt((((n1 - 1) * var1) + ((n2 - 1) * var2)) / (n1 + n2 - 2));
    denom = sp * sqrt((1 / (double) n1) + (1 / (double) n2));
    t = fabs(num / denom);
    dof = (double) n1 + n2 - 2;
    p = (1 - gsl_cdf_tdist_P(t, dof)) * 2;
    return p;
}

static double* PySequence_ToDoubleArray(PyObject* inp, int n)
{
    // Allocate array
    double *out;
    out = malloc(n * sizeof(double));
    for (int i = 0; i < n; i++)
    {
        PyObject *fitem;
        PyObject *item = PySequence_Fast_GET_ITEM(inp, i);
        if(!item) {
            Py_DECREF(inp);
            free(out);
            return NULL;
        }
        fitem = PyNumber_Float(item);
        if(!fitem) {
            Py_DECREF(inp);
            free(out);
            PyErr_SetString(PyExc_TypeError, "all items must be numbers");
            return NULL;
        }
        out[i] = PyFloat_AS_DOUBLE(fitem);
        Py_DECREF(fitem);
    }
    return out;
}

static PyObject *t_test_py(PyObject *self, PyObject *args)
{
    PyObject *inp1, *inp2;
    PyObject* data1;
    PyObject* data2;
    double *d1, *d2;
    int n1, n2;
    double res;

    if (!PyArg_ParseTuple(args, "OO", &inp1, &inp2))
        return 0;
    data1 = PySequence_Fast(inp1, "argument must be iterable");
    data2 = PySequence_Fast(inp2, "argument must be iterable");
    n1 = PySequence_Fast_GET_SIZE(data1);
    n2 = PySequence_Fast_GET_SIZE(data2);

    d1 = PySequence_ToDoubleArray(data1, n1);
    d2 = PySequence_ToDoubleArray(data2, n2);

    Py_DECREF(data1);
    Py_DECREF(data2);

    res = t_test(d1, n1, d2, n2);
    free(d1);
    free(d2);

    return Py_BuildValue("d", res);
}

static PyMethodDef module_methods[] = {
    {"t_test_py", t_test_py, METH_VARARGS, t_test_docstring},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef gslstatsmodule = {
    PyModuleDef_HEAD_INIT,
    "gslstats",
    module_docstring,
    -1,
    module_methods,
};

PyMODINIT_FUNC PyInit_gslstats(void)
{
    Py_Initialize();
    return PyModule_Create(&gslstatsmodule);
}


#include <stdio.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include "MeshAttributes.h"
#include "mathfunctions.h"
#include "oneTimeStep.h"

extern const double g;

void oneTimeStep2D (double time, double dt)
{

	// store H, Qx, Qy
	gsl_vector *gzeta = gsl_vector_alloc(3*NumEl);
	gsl_vector *gQx = gsl_vector_alloc(3*NumEl);
	gsl_vector *gQy = gsl_vector_alloc(3*NumEl);

	int j = 0;
	for (int i=0; i < NumEl; ++i) {
		for (int k=0; k < 3; ++k){
			gsl_vector_set(gzeta,j,zeta[index(i,k,3)]);
			gsl_vector_set(gQx,j, Qx[index(i,k,3)]);
			gsl_vector_set(gQy,j, Qy[index(i,k,3)]);
			++j;
		}
	}

	/************ print out zeta, Qx and  Qy for debugging ********************/
	#ifdef DEBUG
	printf("Initial values:\n zeta \n");
	gsl_vector_fprintf(stdout,gzeta,"%e");
	printf("\n Qx \n");
	gsl_vector_fprintf(stdout,gQx,"%e");
	printf("\n Qy \n");
	gsl_vector_fprintf(stdout,gQy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// Calculate the wet-dry status of the elements
//	wetDryStatus2D();

	// Intermediate RK step
	compute2DL(time);

	gsl_vector *gRHSZeta = gsl_vector_alloc(3*NumEl);
	gsl_vector *gRHSQx = gsl_vector_alloc(3*NumEl);
	gsl_vector *gRHSQy = gsl_vector_alloc(3*NumEl);

	j = 0;
	for (int i=0; i < NumEl; ++i) {
		for (int k=0; k < 3; ++k){
			gsl_vector_set(gRHSZeta,j,RHSZeta[index(i,k,3)]);
			gsl_vector_set(gRHSQx,j,RHSQx[index(i,k,3)]);
			gsl_vector_set(gRHSQy,j,RHSQy[index(i,k,3)]);
			++j;
		}
	}

	/************ print out RHSZeta, RHSQx and RHSQy for debugging ********************/
	#ifdef DEBUG
	printf("After first computeL:\n RHSZeta \n");
	gsl_vector_fprintf(stdout,gRHSZeta,"%e");
	printf("\n RHSQx \n");
	gsl_vector_fprintf(stdout,gRHSQx,"%e");
	printf("\n RHSQy \n");
	gsl_vector_fprintf(stdout,gRHSQy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// w_1 = w + dt*L;
	gsl_vector *gzeta_1 = gsl_vector_alloc(3*NumEl);
	gsl_vector *gQx_1 = gsl_vector_alloc(3*NumEl);
	gsl_vector *gQy_1 = gsl_vector_alloc(3*NumEl);
	for (int i=0; i<3*NumEl; ++i)
	{
		gsl_vector_set(gzeta_1,i,gsl_vector_get(gzeta,i));
		gsl_vector_set(gQx_1,i,gsl_vector_get(gQx,i));
		gsl_vector_set(gQy_1,i,gsl_vector_get(gQy,i));
	}

	gsl_vector_scale(gRHSZeta,dt);
	gsl_vector_scale(gRHSQx,dt);
	gsl_vector_scale(gRHSQy,dt);
	gsl_vector_add(gzeta_1,gRHSZeta);
	gsl_vector_add(gQx_1, gRHSQx);
	gsl_vector_add(gQy_1, gRHSQy);

	/************ print out zeta, Qx and  Qy for debugging ********************/
	#ifdef DEBUG
	printf("After first computeL:\n Zeta \n");
	gsl_vector_fprintf(stdout,gzeta_1,"%e");
	printf("\n Qx \n");
	gsl_vector_fprintf(stdout,gQx_1,"%e");
	printf("\n Qy \n");
	gsl_vector_fprintf(stdout,gQy_1,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	j = 0;
	for (int i=0; i<NumEl; ++i){
		for (int k =0; k < 3; ++k){
			zeta[index(i,k,3)] = gsl_vector_get(gzeta_1,j);
			Qx[index(i,k,3)] = gsl_vector_get(gQx_1,j);
			Qy[index(i,k,3)] = gsl_vector_get(gQy_1,j);
			++j;
		}
	}

/*	printf("zeta\n");
	for (int i=0; i < NumEl; ++i)
	{
		for (int k =0; k <3; ++k)
		{
			printf("%1.15f\t",junc->zeta[index(i,k,3)]);
		}
		printf("\n");

	}

	printf("Qx\n");
	for (int i=0; i < NumEl; ++i)
	{
		for (int k =0; k <3; ++k)
		{
			printf("%1.15f\t",junc->Qx[index(i,k,3)]);
		}
		printf("\n");

	}

	printf("Qy\n");
	for (int i=0; i < NumEl; ++i)
	{
		for (int k =0; k <3; ++k)
		{
			printf("%1.15f\t",junc->Qy[index(i,k,3)]);
		}
		printf("\n");

	}
*/
	// Apply minmod slope limiter on w_1
	SlopeLimiter();

	// Apply the positive-depth operator on w_1
//	PDop2D();

	j = 0;
	for (int i=0; i<NumEl; ++i)
	{
		for(int k=0; k <3; ++k)
		{
			gsl_vector_set(gzeta_1,j,zeta[index(i,k,3)]);
			gsl_vector_set(gQx_1,j,Qx[index(i,k,3)]);
			gsl_vector_set(gQy_1,j,Qy[index(i,k,3)]);
			++j;
		}
	}

	/************ print out zeta, Qx and  Qy for debugging ********************/
	#ifdef DEBUG
	printf("After first slope limiting:\n zeta \n");
	gsl_vector_fprintf(stdout,gzeta_1,"%e");
	printf("\n Qx \n");
	gsl_vector_fprintf(stdout,gQx_1,"%e");
	printf("\n Qy \n");
	gsl_vector_fprintf(stdout,gQy_1,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// w_1 = w_1 + w;
	gsl_vector_add(gzeta_1,gzeta);
	gsl_vector_add(gQx_1,gQx);
	gsl_vector_add(gQy_1,gQy);

	// Calcualte the wet-dry status of the elements again
//	wetDryStatus2D();

	// Compute w
	compute2DL(time);

	j = 0;
	for (int i=0; i < NumEl; ++i) {
		for (int k=0; k < 3; ++k){
			gsl_vector_set(gRHSZeta,j,RHSZeta[index(i,k,3)]);
			gsl_vector_set(gRHSQx,j,RHSQx[index(i,k,3)]);
			gsl_vector_set(gRHSQy,j,RHSQy[index(i,k,3)]);
			++j;
		}
	}

	/************ print out zeta, Qx and  Qy for debugging ********************/
	#ifdef DEBUG
	printf("After second computeL:\n RHSZeta \n");
	gsl_vector_fprintf(stdout,gRHSZeta,"%e");
	printf("\n RHSQx \n");
	gsl_vector_fprintf(stdout,gRHSQx,"%e");
	printf("\n RHSQy \n");
	gsl_vector_fprintf(stdout,gRHSQy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	gsl_vector_scale(gzeta_1,0.5);
	gsl_vector_scale(gQx_1,0.5);
	gsl_vector_scale(gQy_1,0.5);
	gsl_vector_scale(gRHSZeta,dt/2);
	gsl_vector_scale(gRHSQx,dt/2);
	gsl_vector_scale(gRHSQy,dt/2);

	gsl_vector_add(gzeta_1,gRHSZeta);
	gsl_vector_add(gQx_1,gRHSQx);
	gsl_vector_add(gQy_1,gRHSQy);

	j = 0;
	for (int i=0; i<NumEl; ++i){
		for (int k =0; k < 3; ++k){
			zeta[index(i,k,3)] = gsl_vector_get(gzeta_1,j);
			Qx[index(i,k,3)] = gsl_vector_get(gQx_1,j);
			Qy[index(i,k,3)] = gsl_vector_get(gQy_1,j);
			++j;
		}
	}

	// Apply minmod slope limiter to w
	SlopeLimiter();

	// Apply the positive depth operator on w
//	PDop2D();

	gsl_vector_free(gzeta);
	gsl_vector_free(gQx);
	gsl_vector_free(gQy);
	gsl_vector_free(gzeta_1);
	gsl_vector_free(gQx_1);
	gsl_vector_free(gQy_1);
	gsl_vector_free(gRHSZeta);
	gsl_vector_free(gRHSQx);
	gsl_vector_free(gRHSQy);

}

void time_evolution(double FinalTime)
{

	// output the file with x y and z information for the elements
	boundary_conditions();

	double time = 0;
	double dt = 1e-7;
	int Nstep = 0;
	int fileNumber = 1;

	// write out initial conditions
	FILE* Initfile1;
	FILE* Initfile2;
	FILE* Initfile3;
//	FILE* Initfile4;

	char flname1[100];
	char flname2[100];
	char flname3[100];
//	char flname4[100];
	sprintf(flname1, "000_zeta.dat");
	sprintf(flname2, "000_Qx.dat");
	sprintf(flname3, "000_Qy.dat");
//	sprintf(flname4, "000_Qdotn.dat");
	Initfile1 = fopen(flname1, "w");
	Initfile2 = fopen(flname2, "w");
	Initfile3 = fopen(flname3, "w");
//	Initfile4 = fopen(flname4, "w");

/*	fprintf(Initfile1, "H at time 0\n");
	fprintf(Initfile2, "Qx at time 0 \n");
	fprintf(Initfile3, "Qy at time 0 \n");
	fprintf(Initfile4, "Qdotn at time 0 \n");
*/
	int k;
	for (int j=0; j<NumEl; ++j)
	{
		for(k=0; k<3; ++k)
		{
			double zeta_val = zeta[index(j,k,3)];
			double zval = (z[EltoVert[j*3+k]]+z[EltoVert[j*3+((k+1)%3)]])/2;
			double H = zeta_val + zval;
			double Qx_val = Qx[index(j,k,3)];
			double Qy_val = Qy[index(j,k,3)];
		//	double Qdotn = Qx_val*normvecx[index(j,k,3)] + Qy_val*normvecy[index(j,k,3)];
			fprintf(Initfile1, "%1.15f\t\t",zeta_val);
			fprintf(Initfile2, "%1.15f\t\t",Qx_val);
			fprintf(Initfile3, "%1.15f\t\t",Qy_val);
		//	fprintf(Initfile4, "%1.15f\t\t",Qdotn);
		}
		fprintf(Initfile1, "\n");
		fprintf(Initfile2, "\n");
		fprintf(Initfile3, "\n");
//		fprintf(Initfile4, "\n");
	}
	fclose(Initfile1);
	fclose(Initfile2);
	fclose(Initfile3);
//	fclose(Initfile4);

	//wetDryStatus2D();
	while (time<FinalTime)
	{
		time = time + dt;
		printf("dt = %e\n", dt);
		printf("time = %e\n", time);

		oneTimeStep2D(time,dt);

		boundary_conditions();

		/********* Output data every twentienth step *************/
		Nstep = Nstep + 1;
		int NstepinSec = floor(1./dt);
		if (time == FinalTime)
	//	if (Nstep%500 == 0 || time == FinalTime)
//		if ((Nstep%(10*NstepinSec) == 0) || time == FinalTime)
		{

			FILE* file3;
			FILE* file4;
			FILE* file5;
//			FILE* file6;

			char filename1[100];
			char filename2[100];
			char filename3[100];
//			char filename4[100];

/*			sprintf(filename1, "Zeta%d", NumEl);
			sprintf(filename2, "Qx%d", NumEl);
			sprintf(filename3, "Qy%d", NumEl);
*/

			sprintf(filename1, "%3.3d_H.dat", fileNumber);
			sprintf(filename2, "%3.3d_Qx.dat", fileNumber);
			sprintf(filename3, "%3.3d_Qy.dat", fileNumber);

		//	sprintf(filename1, "%3.3d_H.dat", fileNumber);
		//	sprintf(filename2, "%3.3d_Qx.dat", fileNumber);
		//	sprintf(filename3, "%3.3d_Qy.dat", fileNumber);
//			sprintf(filename4, "%3.3d_Q.n.dat", fileNumber);
			file3 = fopen(filename1, "w");
			file4 = fopen(filename2, "w");
			file5 = fopen(filename3, "w");
//			file6 = fopen(filename4, "w");

			//fprintf(file3, "H at time %e\n", time);
			//fprintf(file4, "Qx at time %e \n", time);
			//fprintf(file5, "Qy at time %e \n", time);
//			fprintf(file6, "Qdotn at time %e \n", time);

			int k;
			for (int j=0; j<NumEl; ++j)
			{
				for(k=0; k<3; ++k)
				{
					double zeta_val = zeta[index(j,k,3)];
					double Qx_val = Qx[index(j,k,3)];
					double Qy_val = Qy[index(j,k,3)];
					double zval = (z[EltoVert[j*3+k]]+z[EltoVert[j*3+((k+1)%3)]])/2;
					double hval = zeta_val + zval;

//					double Qdotn = Qx_val*normvecx[index(j,k,3)] + Qy_val*normvecy[index(j,k,3)];
					fprintf(file3, "%1.15f\t\t",zeta_val);
					fprintf(file4, "%1.15f\t\t",Qx_val);
					fprintf(file5, "%1.15f\t\t",Qy_val);
//					fprintf(file6, "%1.15f\t\t",Qdotn);
				}
				fprintf(file3, "\n");
				fprintf(file4, "\n");
				fprintf(file5, "\n");
//				fprintf(file6, "\n");
			}
			fclose(file3);
			fclose(file4);
			fclose(file5);
//			fclose(file6);

			++fileNumber;
		}

		/**************** Set next time step ****************/

		// set next time step
	//	printf("max_lambda = %e\n", max_lambda);
	//	printf("minEdgLength = %e\n", minEdgLength);
		dt = 0.1*minEdgLength/max_lambda;
		if ((time + dt) > FinalTime)
			dt = FinalTime - time;

	}
}


/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

#include <math.h>
#include <cblas.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "PHY/TOOLS/defs.h"
#include "defs.h"

// NEW code with lookup table for sin/cos based on delay profile (TO BE TESTED)

double **cos_lut=NULL,**sin_lut=NULL;

//#if 1

int init_freq_channel(channel_desc_t *desc,uint16_t nb_rb,int16_t n_samples)
{

  double delta_f,freq;  // 90 kHz spacing
  double delay;
  int16_t f;
  uint8_t l;

  if ((n_samples&1)==0) {
    fprintf(stderr, "freq_channel_init: n_samples has to be odd\n");
    return(-1);
  }

  cos_lut = (double **)malloc(n_samples*sizeof(double*));
  sin_lut = (double **)malloc(n_samples*sizeof(double*));

  delta_f = nb_rb*180000/(n_samples-1);

  for (f=-(n_samples>>1); f<=(n_samples>>1); f++) {
    freq=delta_f*(double)f*1e-6;// due to the fact that delays is in mus

    cos_lut[f+(n_samples>>1)] = (double *)malloc((int)desc->nb_taps*sizeof(double));
    sin_lut[f+(n_samples>>1)] = (double *)malloc((int)desc->nb_taps*sizeof(double));

    for (l=0; l<(int)desc->nb_taps; l++) {
      if (desc->nb_taps==1)
        delay = desc->delays[l];
      else
        delay = desc->delays[l]+NB_SAMPLES_CHANNEL_OFFSET/desc->sampling_rate;

      cos_lut[f+(n_samples>>1)][l] = cos(2*M_PI*freq*delay);
      sin_lut[f+(n_samples>>1)][l] = sin(2*M_PI*freq*delay);
      //printf("values cos:%d, sin:%d\n", cos_lut[f][l], sin_lut[f][l]);

    }
  }

  return(0);
}

int freq_channel(channel_desc_t *desc,uint16_t nb_rb,int16_t n_samples)
{

  int16_t f,f2,d;
  uint8_t aarx,aatx,l;
  double *clut,*slut;
  static int freq_channel_init=0;
  static int n_samples_max=0;

  // do some error checking
  // n_samples has to be a odd number because we assume the spectrum is symmetric around the DC and includes the DC
  if ((n_samples&1)==0) {
    fprintf(stderr, "freq_channel: n_samples has to be odd\n");
    return(-1);
  }

  // printf("no of taps:%d,",(int)desc->nb_taps);

  if (freq_channel_init == 0) {
    // we are initializing the lut for the largets possible n_samples=12*nb_rb+1
    // if called with n_samples<12*nb_rb+1, we decimate the lut
    n_samples_max=12*nb_rb+1;
    if (init_freq_channel(desc,nb_rb,n_samples_max)==0)
      freq_channel_init=1;
    else
      return(-1);
  }

  d=(n_samples_max-1)/(n_samples-1);

  //printf("no_samples=%d, n_samples_max=%d, d=%d\n",n_samples,n_samples_max,d);

  start_meas(&desc->interp_freq);

  for (f=-n_samples_max/2,f2=-n_samples/2; f<n_samples_max/2; f+=d,f2++) {
    clut = cos_lut[n_samples_max/2+f];
    slut = sin_lut[n_samples_max/2+f];

    for (aarx=0; aarx<desc->nb_rx; aarx++) {
      for (aatx=0; aatx<desc->nb_tx; aatx++) {
        desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].x=0.0;
        desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].y=0.0;

        for (l=0; l<(int)desc->nb_taps; l++) {

          desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].x+=(desc->a[l][aarx+(aatx*desc->nb_rx)].x*clut[l]+
              desc->a[l][aarx+(aatx*desc->nb_rx)].y*slut[l]);
          desc->chF[aarx+(aatx*desc->nb_rx)][n_samples/2+f2].y+=(-desc->a[l][aarx+(aatx*desc->nb_rx)].x*slut[l]+
              desc->a[l][aarx+(aatx*desc->nb_rx)].y*clut[l]);
        }
      }
    }
  }

  stop_meas(&desc->interp_freq);

  return(0);
}

double compute_pbch_sinr(channel_desc_t *desc,
                         channel_desc_t *desc_i1,
                         channel_desc_t *desc_i2,
                         double snr_dB,double snr_i1_dB,
                         double snr_i2_dB,
                         uint16_t nb_rb)
{

  double avg_sinr,snr=pow(10.0,.1*snr_dB),snr_i1=pow(10.0,.1*snr_i1_dB),snr_i2=pow(10.0,.1*snr_i2_dB);
  uint16_t f;
  uint8_t aarx,aatx;
  double S;
  struct complex S_i1;
  struct complex S_i2;

  avg_sinr=0.0;

  //  printf("nb_rb %d\n",nb_rb);
  for (f=(nb_rb-6); f<(nb_rb+6); f++) {
    S = 0.0;
    S_i1.x =0.0;
    S_i1.y =0.0;
    S_i2.x =0.0;
    S_i2.y =0.0;

    for (aarx=0; aarx<desc->nb_rx; aarx++) {
      for (aatx=0; aatx<desc->nb_tx; aatx++) {
        S    += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc->chF[aarx+(aatx*desc->nb_rx)][f].x +
                 desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc->chF[aarx+(aatx*desc->nb_rx)][f].y);
        //  printf("%d %d chF[%d] => (%f,%f)\n",aarx,aatx,f,desc->chF[aarx+(aatx*desc->nb_rx)][f].x,desc->chF[aarx+(aatx*desc->nb_rx)][f].y);

        if (desc_i1) {
          S_i1.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x +
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y);
          S_i1.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y -
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x);
        }

        if (desc_i2) {
          S_i2.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x +
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y);
          S_i2.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y -
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x);
        }
      }
    }

    //    printf("snr %f f %d : S %f, S_i1 %f, S_i2 %f\n",snr,f-nb_rb,S,snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y),snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y));
    avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y)+snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y)));
  }

  //  printf("avg_sinr %f (%f,%f,%f)\n",avg_sinr/12.0,snr,snr_i1,snr_i2);
  return(10*log10(avg_sinr/12.0));
}

double compute_sinr(channel_desc_t *desc,
                    channel_desc_t *desc_i1,
                    channel_desc_t *desc_i2,
                    double snr_dB,double snr_i1_dB,
                    double snr_i2_dB,
                    uint16_t nb_rb)
{

  double avg_sinr,snr=pow(10.0,.1*snr_dB),snr_i1=pow(10.0,.1*snr_i1_dB),snr_i2=pow(10.0,.1*snr_i2_dB);
  uint16_t f;
  uint8_t aarx,aatx;
  double S;
  struct complex S_i1;
  struct complex S_i2;

  DevAssert( nb_rb > 0 );

  avg_sinr=0.0;

  //  printf("nb_rb %d\n",nb_rb);
  for (f=0; f<2*nb_rb; f++) {
    S = 0.0;
    S_i1.x =0.0;
    S_i1.y =0.0;
    S_i2.x =0.0;
    S_i2.y =0.0;

    for (aarx=0; aarx<desc->nb_rx; aarx++) {
      for (aatx=0; aatx<desc->nb_tx; aatx++) {
        S    += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc->chF[aarx+(aatx*desc->nb_rx)][f].x +
                 desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc->chF[aarx+(aatx*desc->nb_rx)][f].y);

        if (desc_i1) {
          S_i1.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x +
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y);
          S_i1.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].y -
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i1->chF[aarx+(aatx*desc->nb_rx)][f].x);
        }

        if (desc_i2) {
          S_i2.x += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x +
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y);
          S_i2.y += (desc->chF[aarx+(aatx*desc->nb_rx)][f].x*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].y -
                     desc->chF[aarx+(aatx*desc->nb_rx)][f].y*desc_i2->chF[aarx+(aatx*desc->nb_rx)][f].x);
        }
      }
    }

    //        printf("f %d : S %f, S_i1 %f, S_i2 %f\n",f-nb_rb,snr*S,snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y),snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y));
    avg_sinr += (snr*S/(desc->nb_tx+snr_i1*sqrt(S_i1.x*S_i1.x + S_i1.y*S_i1.y)+snr_i2*sqrt(S_i2.x*S_i2.x + S_i2.y*S_i2.y)));
  }

  //  printf("avg_sinr %f (%f,%f,%f)\n",avg_sinr/12.0,snr,snr_i1,snr_i2);
  return(10*log10(avg_sinr/(nb_rb*2)));
}

int pbch_polynomial_degree=6;
double pbch_awgn_polynomial[7]= {-7.2926e-05, -2.8749e-03, -4.5064e-02, -3.5301e-01, -1.4655e+00, -3.6282e+00, -6.6907e+00};

void load_pbch_desc(FILE *pbch_file_fd)
{

  int i, ret;
  char dummy[25];

  ret = fscanf(pbch_file_fd,"%d",&pbch_polynomial_degree);

  if (ret < 0) {
    printf("fscanf failed: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
  }

  if (pbch_polynomial_degree>6) {
    printf("Illegal degree for pbch interpolation polynomial %d\n",pbch_polynomial_degree);
    exit(-1);
  }

  printf("PBCH polynomial : ");

  for (i=0; i<=pbch_polynomial_degree; i++) {
    ret = fscanf(pbch_file_fd,"%s",dummy);

    if (ret < 0) {
      printf("fscanf failed: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

    pbch_awgn_polynomial[i] = strtod(dummy,NULL);
    printf("%f ",pbch_awgn_polynomial[i]);
  }

  printf("\n");
}

double pbch_bler(double sinr)
{

  int i;
  double log10_bler=pbch_awgn_polynomial[pbch_polynomial_degree];
  double sinrpow=sinr;
  double bler=0.0;

  //  printf("log10bler %f\n",log10_bler);
  if (sinr<-10.0)
    bler= 1.0;
  else if (sinr>=0.0)
    bler=0.0;
  else  {
    for (i=1; i<=pbch_polynomial_degree; i++) {
      //    printf("sinrpow %f\n",sinrpow);
      log10_bler += (pbch_awgn_polynomial[pbch_polynomial_degree-i]*sinrpow);
      sinrpow *= sinr;
      //    printf("log10bler %f\n",log10_bler);
    }

    bler = pow(10.0,log10_bler);
  }

  //printf ("sinr %f bler %f\n",sinr,bler);
  return(bler);

}


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

/* Author:  J.-F. Caron
 *
 * This interpolation method is taken from
 * M.Steffen, "A simple method for monotonic interpolation in one dimension",
 * Astron. Astrophys. 239, 443-450 (1990).
 *
 * This interpolation method guarantees monotonic interpolation functions between
 * the given data points.  A consequence of this is that extremal values can only
 * occur at the data points.  The interpolating function and its first derivative
 * are guaranteed to be continuous, but the second derivative is not.
 *
 * The implementation is modelled on the existing Akima interpolation method
 * previously included in GSL by Gerard Jungman.
 */

#include <config.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include "integ_eval.h"
#include <gsl/gsl_interp.h>

typedef struct
{
  double * a;       /* eqs 2-5 of paper */
  double * b;
  double * c;
  double * d;

  double * y_prime; /* eq 11 of paper */
} steffen_state_t;

static void steffen_free (void * vstate);
static double steffen_copysign(const double x, const double y);

static void *
steffen_alloc (size_t size)
{
  steffen_state_t *state;

  state = (steffen_state_t *) calloc (1, sizeof (steffen_state_t));

  if (state == NULL)
    {
      GSL_ERROR_NULL("failed to allocate space for state", GSL_ENOMEM);
    }

  state->a = (double *) malloc (size * sizeof (double));

  if (state->a == NULL)
    {
      steffen_free(state);
      GSL_ERROR_NULL("failed to allocate space for a", GSL_ENOMEM);
    }

  state->b = (double *) malloc (size * sizeof (double));

  if (state->b == NULL)
    {
      steffen_free(state);
      GSL_ERROR_NULL("failed to allocate space for b", GSL_ENOMEM);
    }

  state->c = (double *) malloc (size * sizeof (double));

  if (state->c == NULL)
    {
      steffen_free(state);
      GSL_ERROR_NULL("failed to allocate space for c", GSL_ENOMEM);
    }

  state->d = (double *) malloc (size * sizeof (double));

  if (state->d == NULL)
    {
      steffen_free(state);
      GSL_ERROR_NULL("failed to allocate space for d", GSL_ENOMEM);
    }

  state->y_prime = (double *) malloc (size * sizeof (double));
  if (state->y_prime == NULL)
    {
      steffen_free(state);
      GSL_ERROR_NULL("failed to allocate space for y_prime", GSL_ENOMEM);
    }

  return state;
}

static int
steffen_init (void * vstate, const double x_array[],
              const double y_array[], size_t size)
{
  steffen_state_t *state = (steffen_state_t *) vstate;
  size_t i;
  double *a = state->a;
  double *b = state->b;
  double *c = state->c;
  double *d = state->d;
  double *y_prime = state->y_prime;

  /*
   * first assign the interval and slopes for the left boundary.
   * We use the "simplest possibility" method described in the paper
   * in section 2.2
   */
  double h0 = (x_array[1] - x_array[0]);
  double s0 = (y_array[1] - y_array[0]) / h0;

  y_prime[0] = s0;

  /* Now we calculate all the necessary s, h, p, and y' variables
     from 1 to N-2 (0 to size - 2 inclusive) */
  for (i = 1; i < (size - 1); i++)
    {
      double pi;

      /* equation 6 in the paper */
      double hi = (x_array[i+1] - x_array[i]);
      double him1 = (x_array[i] - x_array[i - 1]);

      /* equation 7 in the paper */
      double si = (y_array[i+1] - y_array[i]) / hi;
      double sim1 = (y_array[i] - y_array[i - 1]) / him1;

      /* equation 8 in the paper */
      pi = (sim1*hi + si*him1) / (him1 + hi);

      /* This is a C equivalent of the FORTRAN statement below eqn 11 */
      y_prime[i] = (steffen_copysign(1.0,sim1) + steffen_copysign(1.0,si)) *
                    GSL_MIN(fabs(sim1),
                            GSL_MIN(fabs(si), 0.5*fabs(pi)));
    }

  /*
   * we also need y' for the rightmost boundary; we use the
   * "simplest possibility" method described in the paper in
   * section 2.2
   *
   * y' = s_{n-1}
   */
  y_prime[size-1] = (y_array[size - 1] - y_array[size - 2]) /
                    (x_array[size - 1] - x_array[size - 2]);

  /* Now we can calculate all the coefficients for the whole range. */
  for (i = 0; i < (size - 1); i++)
    {
      double hi = (x_array[i+1] - x_array[i]);
      double si = (y_array[i+1] - y_array[i]) / hi;

      /* These are from equations 2-5 in the paper. */
      a[i] = (y_prime[i] + y_prime[i+1] - 2*si) / hi / hi;
      b[i] = (3*si - 2*y_prime[i] - y_prime[i+1]) / hi;
      c[i] = y_prime[i];
      d[i] = y_array[i];
    }

  return GSL_SUCCESS;
}

static void
steffen_free (void * vstate)
{
  steffen_state_t *state = (steffen_state_t *) vstate;

  RETURN_IF_NULL(state);

  if (state->a)
    free (state->a);

  if (state->b)
    free (state->b);

  if (state->c)
    free (state->c);

  if (state->d)
    free (state->d);

  if (state->y_prime)
    free (state->y_prime);

  free (state);
}

static int
steffen_eval (const void * vstate,
              const double x_array[], const double y_array[], size_t size,
              double x, gsl_interp_accel * a, double *y)
{
  const steffen_state_t *state = (const steffen_state_t *) vstate;

  size_t index;

  if (a != 0)
    {
      index = gsl_interp_accel_find (a, x_array, size, x);
    }
  else
    {
      index = gsl_interp_bsearch (x_array, x, 0, size - 1);
    }

  /* evaluate */
  {
    const double x_lo = x_array[index];
    const double delx = x - x_lo;
    const double a = state->a[index];
    const double b = state->b[index];
    const double c = state->c[index];
    const double d = state->d[index];
    /* Use Horner's scheme for efficient evaluation of polynomials. */
    /* *y = a*delx*delx*delx + b*delx*delx + c*delx + d; */
    *y = d + delx*(c + delx*(b + delx*a));

    return GSL_SUCCESS;
  }
}

static int
steffen_eval_deriv (const void * vstate,
                    const double x_array[], const double y_array[], size_t size,
                    double x, gsl_interp_accel * a, double *dydx)
{
  const steffen_state_t *state = (const steffen_state_t *) vstate;

  size_t index;

  /* DISCARD_POINTER(y_array); /\* prevent warning about unused parameter *\/ */

  if (a != 0)
    {
      index = gsl_interp_accel_find (a, x_array, size, x);
    }
  else
    {
      index = gsl_interp_bsearch (x_array, x, 0, size - 1);
    }

  /* evaluate */
  {
    double x_lo = x_array[index];
    double delx = x - x_lo;
    double a = state->a[index];
    double b = state->b[index];
    double c = state->c[index];
    /*double d = state->d[index];*/
    /* *dydx = 3*a*delx*delx*delx + 2*b*delx + c; */
    *dydx = c + delx*(2*b + delx*3*a);
    return GSL_SUCCESS;
  }
}

static int
steffen_eval_deriv2 (const void * vstate,
                     const double x_array[], const double y_array[], size_t size,
                     double x, gsl_interp_accel * a, double *y_pp)
{
  const steffen_state_t *state = (const steffen_state_t *) vstate;

  size_t index;

  /* DISCARD_POINTER(y_array); /\* prevent warning about unused parameter *\/ */

  if (a != 0)
    {
      index = gsl_interp_accel_find (a, x_array, size, x);
    }
  else
    {
      index = gsl_interp_bsearch (x_array, x, 0, size - 1);
    }

  /* evaluate */
  {
    const double x_lo = x_array[index];
    const double delx = x - x_lo;
    const double a = state->a[index];
    const double b = state->b[index];
    *y_pp = 6*a*delx + 2*b;
    return GSL_SUCCESS;
  }
}

static int
steffen_eval_integ (const void * vstate,
                    const double x_array[], const double y_array[], size_t size,
                    gsl_interp_accel * acc, double a, double b,
                    double * result)
{
  /* a and b are the boundaries of the integration. */

  const steffen_state_t *state = (const steffen_state_t *) vstate;

  size_t i, index_a, index_b;

  /* Find the data points in the x_array that are nearest to the desired */
  /* a and b integration boundaries. */

  if (acc != 0)
    {
      index_a = gsl_interp_accel_find (acc, x_array, size, a);
      index_b = gsl_interp_accel_find (acc, x_array, size, b);
    }
  else
    {
      index_a = gsl_interp_bsearch (x_array, a, 0, size - 1);
      index_b = gsl_interp_bsearch (x_array, b, 0, size - 1);
    }

  *result = 0.0;

  /* Iterate over all the segments between data points and sum the */
  /* contributions into result. */
  for(i=index_a; i<=index_b; i++)
    {
      const double x_hi = x_array[i + 1];
      const double x_lo = x_array[i];
      const double dx = x_hi - x_lo;
      if(dx != 0.0)
        {
          /*
           * check if we are at a boundary point, so take the
           * a and b parameters instead of the data points.
           */
          double x1 = (i == index_a) ? a-x_lo : 0.0;
          double x2 = (i == index_b) ? b-x_lo : x_hi-x_lo;

          *result += (1.0/4.0)*state->a[i]*(x2*x2*x2*x2 - x1*x1*x1*x1)
                    +(1.0/3.0)*state->b[i]*(x2*x2*x2 - x1*x1*x1)
                    +(1.0/2.0)*state->c[i]*(x2*x2 - x1*x1)
                    +state->d[i]*(x2-x1);
        }
      else /* if the interval was zero, i.e. consecutive x values in data. */
        {
          *result = 0.0;
          return GSL_EINVAL;
        }
    }

  return GSL_SUCCESS;
}

static double
steffen_copysign(const double x, const double y)
{
  if ((x < 0 && y > 0) || (x > 0 && y < 0))
    return -x;

  return x;
}

static const gsl_interp_type steffen_type =
{
  "steffen",
  3,
  &steffen_alloc,
  &steffen_init,
  &steffen_eval,
  &steffen_eval_deriv,
  &steffen_eval_deriv2,
  &steffen_eval_integ,
  &steffen_free
};

const gsl_interp_type * gsl_interp_steffen = &steffen_type;


/*
 *   1dcol.c
 *
 *   MPI code for performing parallel dense matrix-vector multiplication using 1D
 *   columnwise partitioning.
 *
 *   Written by cetinsamet -*- cetin.samet@metu.edu.tr
 *   April, 2019
 *
 */
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <cblas.h>

int main(int argc, char *argv[]) {

	int i, j;
    int rank, size;

    double minStart, maxEnd;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    int N 		= atoi(argv[1]);
    int pSize	= N / size;

    /* Initialize result vector */
    double* RESULT;
    RESULT 	= (double*) malloc(N * sizeof(double));

	/* Initialize local result vector */
	double* locResult;
	locResult = (double*) malloc(N * sizeof(double));

    /* Initialize matrix */
    double **matrix	= (double **) malloc(N * sizeof(double*));
	for(i = 0; i < N; i++) {
		matrix[i] = (double *) malloc(N * sizeof(double));
	}
	for (i = 0; i < N; ++i) {
		for (j = 0; j < N; ++j)
			matrix[i][j] = i + j;
	}

	/* Initialize vector */
	double* vector;
	vector = (double*) malloc(N * sizeof(double));
	for (i = 0; i < N; ++i)
		vector[i] = i;

	/* Initialize matrix */
	double **p_matrix	= (double **) malloc(N * sizeof(double*));
	for(i = 0; i < N; i++)
		p_matrix[i] = (double *) malloc(pSize * sizeof(double));

	for (j = 0; j < pSize; ++j) {
		for (i = 0; i < N; ++i)
			p_matrix[i][j] = matrix[i][(rank * pSize) + j];
	}

	/* Initialize local vector */
	double* p_vector;
	p_vector = (double*) malloc(N * sizeof(double));
	for (i = 0; i < pSize; ++i) {
		p_vector[i] = vector[(rank * pSize) + i];
	}

	double pStart = MPI_Wtime(); // <-- Start Time

	/* Calculate local dot product */
	for (i = 0; i < pSize; ++i) {
		for (j = 0; j < N; ++j) {
			locResult[j] += p_matrix[j][i] * p_vector[i];

	for (i = 0; i < N; ++i)
		MPI_Reduce(&locResult[i], &RESULT[i], 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

	double pEnd = MPI_Wtime(); // <-- Stop Time

	MPI_Reduce(&pStart,  &minStart, 1, MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD);
    MPI_Reduce(&pEnd, &maxEnd, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);

    /* Print result */
	if (rank == 0) {
		printf("RESULT of MATRIX-VECTOR MULTIPLICATION:\n");
		for (i = 0; i < N; ++i)
			printf("%f ", RESULT[i]);
		printf("\n");
		printf("Vector size: %d\tElapsed time: %f\n", N, maxEnd - minStart);
	}

	MPI_Finalize();
	return 0;
}


/*****************************************
Emitting C Generated Code
*******************************************/
#include <cblas.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
/**************** Snippet ****************/
void Snippet(int x0) {
  int x1[6] = { 1, 2, 3, 4, 5, 6 };
  int x2[6] = { 6, 5, 4, 3, 2, 1 };
  int* x3 = (int*)malloc(6 * sizeof(int));
  int x4 = 0;
  while (x4 != 6) {
    int x5 = x4;
    x3[x5] = x1[x5] + x2[x5];
    x4 = x4 + 1;
  }
  int x6 = 0;
  while (x6 != 6) {
    printf("%d ", x3[x6]);
    x6 = x6 + 1;
  }
  int* x7 = (int*)malloc(6 * sizeof(int));
  int x8 = 0;
  while (x8 != 6) {
    int x9 = x8;
    x7[x9] = x1[x9] - x2[x9];
    x8 = x8 + 1;
  }
  int x10 = 0;
  while (x10 != 6) {
    printf("%d ", x7[x10]);
    x10 = x10 + 1;
  }
  int* x11 = (int*)malloc(6 * sizeof(int));
  int x12 = 0;
  while (x12 != 6) {
    int x13 = x12;
    x11[x13] = x1[x13] * x2[x13];
    x12 = x12 + 1;
  }
  int x14 = 0;
  while (x14 != 6) {
    printf("%d ", x11[x14]);
    x14 = x14 + 1;
  }
  int* x15 = (int*)malloc(6 * sizeof(int));
  int x16 = 0;
  while (x16 != 6) {
    int x17 = x16;
    x15[x17] = x1[x17] / x2[x17];
    x16 = x16 + 1;
  }
  int x18 = 0;
  while (x18 != 6) {
    printf("%d ", x15[x18]);
    x18 = x18 + 1;
  }
}
/*****************************************
End of C Generated Code
*******************************************/
int main(int argc, char *argv[]) {
  if (argc != 2) {
    printf("usage: %s <arg>\n", argv[0]);
    return 0;
  }
  Snippet(atoi(argv[1]));
  return 0;
}


/* cdf/tdist.c
 *
 * Copyright (C) 2002 Jason H. Stover.
 *
 * 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.
 */

/*
 * Computes the Student's t cumulative distribution function using
 * the method detailed in
 *
 * W.J. Kennedy and J.E. Gentle, "Statistical Computing." 1980.
 * Marcel Dekker. ISBN 0-8247-6898-1.
 *
 * G.W. Hill and A.W. Davis. "Generalized asymptotic expansions
 * of Cornish-Fisher type." Annals of Mathematical Statistics,
 * vol. 39, 1264-1273. 1968.
 *
 * G.W. Hill. "Algorithm 395: Student's t-distribution," Communications
 * of the ACM, volume 13, number 10, page 617. October 1970.
 *
 * G.W. Hill, "Remark on algorithm 395: Student's t-distribution,"
 * Transactions on Mathematical Software, volume 7, number 2, page
 * 247. June 1982.
 */

#include <config.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>

#include "beta_inc.c"

static double
poly_eval (const double c[], unsigned int n, double x)
{
  unsigned int i;
  double y = c[0] * x;

  for (i = 1; i < n; i++)
    {
      y = x * (y + c[i]);
    }

  y += c[n];

  return y;
}

/*
 * Use the Cornish-Fisher asymptotic expansion to find a point u such
 * that gsl_cdf_gauss(y) = tcdf(t).
 *
 */

static double
cornish_fisher (double t, double n)
{
  const double coeffs6[10] = {
    0.265974025974025974026,
    5.449696969696969696970,
    122.20294372294372294372,
    2354.7298701298701298701,
    37625.00902597402597403,
    486996.1392857142857143,
    4960870.65,
    37978595.55,
    201505390.875,
    622437908.625
  };
  const double coeffs5[8] = {
    0.2742857142857142857142,
    4.499047619047619047619,
    78.45142857142857142857,
    1118.710714285714285714,
    12387.6,
    101024.55,
    559494.0,
    1764959.625
  };
  const double coeffs4[6] = {
    0.3047619047619047619048,
    3.752380952380952380952,
    46.67142857142857142857,
    427.5,
    2587.5,
    8518.5
  };
  const double coeffs3[4] = {
    0.4,
    3.3,
    24.0,
    85.5
  };

  double a = n - 0.5;
  double b = 48.0 * a * a;

  double z2 = a * log1p (t * t / n);
  double z = sqrt (z2);

  double p5 = z * poly_eval (coeffs6, 9, z2);
  double p4 = -z * poly_eval (coeffs5, 7, z2);
  double p3 = z * poly_eval (coeffs4, 5, z2);
  double p2 = -z * poly_eval (coeffs3, 3, z2);
  double p1 = z * (z2 + 3.0);
  double p0 = z;

  double y = p5;
  y = (y / b) + p4;
  y = (y / b) + p3;
  y = (y / b) + p2;
  y = (y / b) + p1;
  y = (y / b) + p0;

  if (t < 0)
    y *= -1;

  return y;
}

#if 0
/*
 * Series approximation for t > 4.0. This needs to be fixed;
 * it shouldn't subtract the result from 1.0. A better way is
 * to use two different series expansions. Figuring this out
 * means rummaging through Fisher's paper in Metron, v5, 1926,
 * "Expansion of Student's integral in powers of n^{-1}."
 */

#define MAXI 40

static double
normal_approx (const double x, const double nu)
{
  double y;
  double num;
  double diff;
  double q;
  int i;
  double lg1, lg2;

  y = 1 / sqrt (1 + x * x / nu);
  num = 1.0;
  q = 0.0;
  diff = 2 * GSL_DBL_EPSILON;
  for (i = 2; (i < MAXI) && (diff > GSL_DBL_EPSILON); i += 2)
    {
      diff = q;
      num *= y * y * (i - 1) / i;
      q += num / (nu + i);
      diff = q - diff;
    }
  q += 1 / nu;
  lg1 = gsl_sf_lngamma (nu / 2.0);
  lg2 = gsl_sf_lngamma ((nu + 1.0) / 2.0);

  diff = lg2 - lg1;
  q *= pow (y, nu) * exp (diff) / sqrt (M_PI);

  return q;
}
#endif

double
gsl_cdf_tdist_P (const double x, const double nu)
{
  double P;

  double x2 = x * x;

  if (nu > 30 && x2 < 10 * nu)
    {
      double u = cornish_fisher (x, nu);
      P = gsl_cdf_ugaussian_P (u);

      return P;
    }

  if (x2 < nu)
    {
      double u = x2 / nu;
      double eps = u / (1 + u);

      if (x >= 0)
        {
          P = beta_inc_AXPY (0.5, 0.5, 0.5, nu / 2.0, eps);
        }
      else
        {
          P = beta_inc_AXPY (-0.5, 0.5, 0.5, nu / 2.0, eps);
        }
    }
  else
    {
      double v = nu / (x * x);
      double eps = v / (1 + v);

      if (x >= 0)
        {
          P = beta_inc_AXPY (-0.5, 1.0, nu / 2.0, 0.5, eps);
        }
      else
        {
          P = beta_inc_AXPY (0.5, 0.0, nu / 2.0, 0.5, eps);
        }
    }

  return P;
}

double
gsl_cdf_tdist_Q (const double x, const double nu)
{
  double Q;

  double x2 = x * x;

  if (nu > 30 && x2 < 10 * nu)
    {
      double u = cornish_fisher (x, nu);
      Q = gsl_cdf_ugaussian_Q (u);

      return Q;
    }

  if (x2 < nu)
    {
      double u = x2 / nu;
      double eps = u / (1 + u);

      if (x >= 0)
        {
          Q = beta_inc_AXPY (-0.5, 0.5, 0.5, nu / 2.0, eps);
        }
      else
        {
          Q = beta_inc_AXPY (0.5, 0.5, 0.5, nu / 2.0, eps);
        }
    }
  else
    {
      double v = nu / (x * x);
      double eps = v / (1 + v);

      if (x >= 0)
        {
          Q = beta_inc_AXPY (0.5, 0.0, nu / 2.0, 0.5, eps);
        }
      else
        {
          Q = beta_inc_AXPY (-0.5, 1.0, nu / 2.0, 0.5, eps);
        }
    }

  return Q;
}


// align.h
//
// prototypes for align.cc
//
// dw, 12/12/3

#include <gsl/gsl_matrix.h>

#include "pdb.h"
#include "point_transformation.h"
#include <map>

// find rigid body transformation z=Rx+t minimizing
// least squares error ||y-z||**2
// find rigid body transformation z=Rx+t minimizing
// least squares error ||y-z||**2
void align(gsl_matrix_const_view , gsl_matrix_const_view, gsl_matrix *, gsl_vector *, double *);

double align(vector<atom>&, vector<atom>&, map<int, int>&, int, vector<atom>&,
    vector<atom>&, map<int, int>&, int);
double align(vector<vector<atom> >& P, vector<vector<atom> >& Q,
                map<pair<int,int>, vector<int> >& M, int sz, gsl_matrix *R, gsl_vector *t);
// Not only return the RMSD value of the alignment but it also returns a new
// vector with the modified x,y,z coordinates that make input align on target.
// As the method name states ALL atoms are aligned. It's expected that both
// collections are the same size.
// optimal_transformation is an optional output parameter that will copy
// the rotation matrix and translation used to obtain the alignment
// Note: The reason why there are 2 inputs is because the first one can be
// just c-alphas, for instance, and the latter one can be an all atom
// representation that needs to be transformed.
double align_all_and_transform(vector<atom>* input, vector<atom>* target,
    vector<atom>* input_to_transform, vector<atom>* transformed_output,
    point_transformation* optimal_transformation);


/* randrand - how random is rand(), mostly for comparison with the
 * random numbers signal/jitterbits.c produces. produces 256 different
 * random values which in an ideal world should be evenly distributed
 *
 *   ./randrand | r-fu equichisq
 *
 * even with gsl_rng_uniform_int() and the "maximally equidistributed"
 * Tausworthe generator (mark II), the p-values over 100 runs of this
 * version of the program on Mac OS X vary wildly, with a mean of 0.55
 * and standard deviation of 0.28 (and similar stats from OpenBSD) */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>

#include <gsl/gsl_rng.h>

#define TIMES 10000000

int Return_Value = EXIT_SUCCESS;

int main(void)
{
    unsigned long i;
    gsl_rng *gslrng;

#ifdef __OpenBSD__
    if (pledge("stdio", NULL) == -1)
        err(1, "pledge failed");
#endif

    if (!(gslrng = gsl_rng_alloc(gsl_rng_taus2)))
        err(EX_SOFTWARE, "call to gsl_rng_alloc() failed");
    gsl_rng_set(gslrng, arc4random());

    /* for instead rand() */
    //srandomdev();
    for (i = 0; i < TIMES; i++) {
        /* for instead rand() */
        //printf("%u\n", rand() % 256);

        /* for instead arc4random() */
        //printf("%u\n", arc4random() % 256);

        printf("%lu\n", gsl_rng_uniform_int(gslrng, 256));

        /* reseeding influences things? (no?) */
        //if (i % 100 == 0)
        //  gsl_rng_set(gslrng, arc4random());
    }

    exit(Return_Value);
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "fitsio.h"
#include <gsl/gsl_fit.h>
#include <gsl/gsl_multifit.h>

//The Function Declarations
int read_image(char[],float[],long[]);
int read_table(char[]);
int write_image(char[],float[],long[]);
int image_size(char[], long[]);
int fit(double[],double[],double[],float[],int);
int calc_pr(float[],float[],float,int,int,long[]);
void printerror(int);


/* multifit/gsl_multifit.h
 *
 * Copyright (C) 2000, 2007, 2010 Brian Gough
 * Copyright (C) 2013, 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_MULTIFIT_H__
#define __GSL_MULTIFIT_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_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_types.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 struct
{
  size_t nmax;         /* maximum number of observations */
  size_t pmax;         /* maximum number of parameters */
  size_t n;            /* number of observations in current SVD decomposition */
  size_t p;            /* number of parameters in current SVD decomposition */
  gsl_matrix * A;      /* least squares matrix for SVD, n-by-p */
  gsl_matrix * Q;
  gsl_matrix * QSI;
  gsl_vector * S;
  gsl_vector * t;
  gsl_vector * xt;
  gsl_vector * D;
  double rcond;        /* reciprocal condition number */
}
gsl_multifit_linear_workspace;

GSL_FUN gsl_multifit_linear_workspace *
gsl_multifit_linear_alloc (const size_t n, const size_t p);

GSL_FUN void
gsl_multifit_linear_free (gsl_multifit_linear_workspace * w);

GSL_FUN int
gsl_multifit_linear (const gsl_matrix * X,
                     const gsl_vector * y,
                     gsl_vector * c,
                     gsl_matrix * cov,
                     double * chisq,
                     gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_tsvd (const gsl_matrix * X,
                          const gsl_vector * y,
                          const double tol,
                          gsl_vector * c,
                          gsl_matrix * cov,
                          double * chisq,
                          size_t * rank,
                          gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_svd (const gsl_matrix * X,
                         gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_bsvd (const gsl_matrix * X,
                          gsl_multifit_linear_workspace * work);

GSL_FUN size_t
gsl_multifit_linear_rank(const double tol, const gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_solve (const double lambda,
                           const gsl_matrix * X,
                           const gsl_vector * y,
                           gsl_vector * c,
                           double *rnorm,
                           double *snorm,
                           gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_applyW(const gsl_matrix * X,
                           const gsl_vector * w,
                           const gsl_vector * y,
                           gsl_matrix * WX,
                           gsl_vector * Wy);

GSL_FUN int
gsl_multifit_linear_stdform1 (const gsl_vector * L,
                              const gsl_matrix * X,
                              const gsl_vector * y,
                              gsl_matrix * Xs,
                              gsl_vector * ys,
                              gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_wstdform1 (const gsl_vector * L,
                               const gsl_matrix * X,
                               const gsl_vector * w,
                               const gsl_vector * y,
                               gsl_matrix * Xs,
                               gsl_vector * ys,
                               gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_L_decomp (gsl_matrix * L, gsl_vector * tau);

GSL_FUN int
gsl_multifit_linear_stdform2 (const gsl_matrix * LQR,
                              const gsl_vector * Ltau,
                              const gsl_matrix * X,
                              const gsl_vector * y,
                              gsl_matrix * Xs,
                              gsl_vector * ys,
                              gsl_matrix * M,
                              gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_wstdform2 (const gsl_matrix * LQR,
                               const gsl_vector * Ltau,
                               const gsl_matrix * X,
                               const gsl_vector * w,
                               const gsl_vector * y,
                               gsl_matrix * Xs,
                               gsl_vector * ys,
                               gsl_matrix * M,
                               gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_genform1 (const gsl_vector * L,
                              const gsl_vector * cs,
                              gsl_vector * c,
                              gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_genform2 (const gsl_matrix * LQR,
                              const gsl_vector * Ltau,
                              const gsl_matrix * X,
                              const gsl_vector * y,
                              const gsl_vector * cs,
                              const gsl_matrix * M,
                              gsl_vector * c,
                              gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_wgenform2 (const gsl_matrix * LQR,
                               const gsl_vector * Ltau,
                               const gsl_matrix * X,
                               const gsl_vector * w,
                               const gsl_vector * y,
                               const gsl_vector * cs,
                               const gsl_matrix * M,
                               gsl_vector * c,
                               gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_lreg (const double smin, const double smax,
                          gsl_vector * reg_param);

GSL_FUN int
gsl_multifit_linear_lcurve (const gsl_vector * y,
                            gsl_vector * reg_param,
                            gsl_vector * rho, gsl_vector * eta,
                            gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_lcurvature (const gsl_vector * y,
                                const gsl_vector * reg_param,
                                const gsl_vector * rho,
                                const gsl_vector * eta,
                                gsl_vector * kappa,
                                gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_lcorner(const gsl_vector *rho,
                            const gsl_vector *eta,
                            size_t *idx);

GSL_FUN int
gsl_multifit_linear_lcorner2(const gsl_vector *reg_param,
                             const gsl_vector *eta,
                             size_t *idx);

GSL_FUN int
gsl_multifit_linear_Lk(const size_t p, const size_t k, gsl_matrix *L);

GSL_FUN int
gsl_multifit_linear_Lsobolev(const size_t p, const size_t kmax,
                             const gsl_vector *alpha, gsl_matrix *L,
                             gsl_multifit_linear_workspace *work);

GSL_FUN int
gsl_multifit_wlinear (const gsl_matrix * X,
                      const gsl_vector * w,
                      const gsl_vector * y,
                      gsl_vector * c,
                      gsl_matrix * cov,
                      double * chisq,
                      gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_wlinear_tsvd (const gsl_matrix * X,
                           const gsl_vector * w,
                           const gsl_vector * y,
                           const double tol,
                           gsl_vector * c,
                           gsl_matrix * cov,
                           double * chisq,
                           size_t * rank,
                           gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_wlinear_svd (const gsl_matrix * X,
                          const gsl_vector * w,
                          const gsl_vector * y,
                          double tol,
                          size_t * rank,
                          gsl_vector * c,
                          gsl_matrix * cov,
                          double *chisq,
                          gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_wlinear_usvd (const gsl_matrix * X,
                           const gsl_vector * w,
                           const gsl_vector * y,
                           double tol,
                           size_t * rank,
                           gsl_vector * c,
                           gsl_matrix * cov,
                           double *chisq,
                           gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_est (const gsl_vector * x,
                         const gsl_vector * c,
                         const gsl_matrix * cov, double *y, double *y_err);

GSL_FUN double
gsl_multifit_linear_rcond (const gsl_multifit_linear_workspace * w);

GSL_FUN int
gsl_multifit_linear_residuals (const gsl_matrix *X, const gsl_vector *y,
                               const gsl_vector *c, gsl_vector *r);

/* gcv.c */
GSL_FUN int
gsl_multifit_linear_gcv_init(const gsl_vector * y,
                             gsl_vector * reg_param,
                             gsl_vector * UTy,
                             double * delta0,
                             gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_gcv_curve(const gsl_vector * reg_param,
                              const gsl_vector * UTy,
                              const double delta0,
                              gsl_vector * G,
                              gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_gcv_min(const gsl_vector * reg_param,
                            const gsl_vector * UTy,
                            const gsl_vector * G,
                            const double delta0,
                            double * lambda,
                            gsl_multifit_linear_workspace * work);

GSL_FUN double
gsl_multifit_linear_gcv_calc(const double lambda,
                             const gsl_vector * UTy,
                             const double delta0,
                             gsl_multifit_linear_workspace * work);

GSL_FUN int
gsl_multifit_linear_gcv(const gsl_vector * y,
                        gsl_vector * reg_param,
                        gsl_vector * G,
                        double * lambda,
                        double * G_lambda,
                        gsl_multifit_linear_workspace * work);

typedef struct
{
  const char * name;     /* method name */
  int (*wfun)(const gsl_vector *r, gsl_vector *w);
  int (*psi_deriv)(const gsl_vector *r, gsl_vector *dpsi);
  double tuning_default; /* default tuning constant */
} gsl_multifit_robust_type;

typedef struct
{
  double sigma_ols;    /* OLS estimate of sigma */
  double sigma_mad;    /* MAD estimate of sigma */
  double sigma_rob;    /* robust estimate of sigma */
  double sigma;        /* final estimate of sigma */
  double Rsq;          /* R^2 coefficient of determination */
  double adj_Rsq;      /* degree of freedom adjusted R^2 */
  double rmse;         /* root mean squared error */
  double sse;          /* residual sum of squares */
  size_t dof;          /* degrees of freedom */
  size_t numit;        /* number of iterations */
  gsl_vector *weights; /* final weights */
  gsl_vector *r;       /* final residuals y - X c */
} gsl_multifit_robust_stats;

typedef struct
{
  size_t n;            /* number of observations */
  size_t p;            /* number of parameters */
  size_t numit;        /* number of iterations */
  size_t maxiter;      /* maximum iterations */
  const gsl_multifit_robust_type *type;
  double tune;         /* tuning parameter */

  gsl_vector *r;       /* residuals at current iteration */
  gsl_vector *weights; /* weights at current iteration */
  gsl_vector *c_prev;  /* coefficients from previous iteration */
  gsl_vector *resfac;  /* multiplicative factors for residuals */

  gsl_vector *psi;     /* psi(r) */
  gsl_vector *dpsi;    /* psi'(r) */

  gsl_matrix *QSI;     /* Q S^{-1} of original matrix X */
  gsl_vector *D;       /* balancing parameters of original matrix X */

  gsl_vector *workn;   /* workspace of length n */

  gsl_multifit_robust_stats stats; /* various statistics */

  gsl_multifit_linear_workspace *multifit_p;
} gsl_multifit_robust_workspace;

/* available types */
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_default;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_bisquare;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_cauchy;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_fair;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_huber;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_ols;
GSL_VAR const gsl_multifit_robust_type * gsl_multifit_robust_welsch;

GSL_FUN gsl_multifit_robust_workspace *gsl_multifit_robust_alloc(const gsl_multifit_robust_type *T,
                                                         const size_t n, const size_t p);
GSL_FUN void gsl_multifit_robust_free(gsl_multifit_robust_workspace *w);
GSL_FUN int gsl_multifit_robust_tune(const double tune,
                             gsl_multifit_robust_workspace *w);
GSL_FUN int gsl_multifit_robust_maxiter(const size_t maxiter,
                                gsl_multifit_robust_workspace *w);
GSL_FUN const char *gsl_multifit_robust_name(const gsl_multifit_robust_workspace *w);
GSL_FUN gsl_multifit_robust_stats gsl_multifit_robust_statistics(const gsl_multifit_robust_workspace *w);
GSL_FUN int gsl_multifit_robust_weights(const gsl_vector *r, gsl_vector *wts,
                                gsl_multifit_robust_workspace *w);
GSL_FUN int gsl_multifit_robust(const gsl_matrix * X, const gsl_vector * y,
                        gsl_vector * c, gsl_matrix *cov,
                        gsl_multifit_robust_workspace *w);
GSL_FUN int gsl_multifit_robust_est(const gsl_vector * x, const gsl_vector * c,
                            const gsl_matrix * cov, double *y, double *y_err);
GSL_FUN int gsl_multifit_robust_residuals(const gsl_matrix * X,
                                  const gsl_vector * y,
                                  const gsl_vector * c, gsl_vector * r,
                                  gsl_multifit_robust_workspace * w);

__END_DECLS

#endif /* __GSL_MULTIFIT_H__ */


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

int main()
{
	double bottom_tail = gsl_cdf_gaussian_P(-1.96,1);
	printf("Area between +-1.96: %g\n",1-2*bottom_tail);

	return 0;
}


/* A simple example program to square a number using GSL. */

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

int main(int argc, char **argv)
{
    double x;
    char *startptr, *endptr;

    if (argc != 2)
    {
        fputs("Expected exactly one command line argument\n", stderr);
        return EXIT_FAILURE;
    }

    startptr = argv[1];
    endptr = NULL;
    x = strtod(startptr, &endptr);

    if (startptr == endptr)
    {
        fputs("Expected command line argument to be a float\n", stderr);
        return EXIT_FAILURE;
    }

    x = gsl_pow_2(x);
    printf("%g\n", x);
    return EXIT_SUCCESS;
}


//
//  geru.h
//  Linear Algebra Template Library
//
//  Created by Rodney James on 12/23/11.
//  Copyright (c) 2011 University of Colorado Denver. All rights reserved.
//

#ifndef _geru_h
#define _geru_h

/// @file geru.h Performs complex vector outer product.

#include "latl.h"

namespace LATL
{
   /// @brief Performs a vector outer product of two complex vectors.
   ///
   /// For a complex matrix A, complex vectors x and y, and complex scalar alpha,
   ///
   ///        A := alpha * x * y.' + A
   ///
   /// is computed.
   /// @return 0 if success.
   /// @return -i if the ith argument is invalid.
   /// @tparam real_t Floating point type.
   /// @param m Specifies the number of rows of the matrix A.  m>=0
   /// @param n Specifies the number of coumns of the matrix A.  n>=0
   /// @param alpha Complex scalar.
   /// @param x Pointer to complex vector x.
   /// @param incx Increment of the vector x.  x!=0
   /// @param y Pointer to complex vector y.
   /// @param incy Increment of the vector y.  y!=0
   /// @param A Pointer to complex m-by-n matrix A.
   /// @param ldA Column length of matrix A.  ldA>=m.
   /// @ingroup BLAS

   template <typename real_t>
   int GERU(int_t m, int_t n, complex<real_t> alpha, complex<real_t> *x, int_t incx, complex<real_t> *y, int_t incy, complex<real_t> *A, int_t ldA)
   {
      using std::conj;

      const complex<real_t> zero(0.0,0.0);
      int_t i,j,kx,jy,ix;

      if(m<0)
         return -1;
      else if(n<0)
         return -2;
      else if(incx==0)
         return -5;
      else if(incy==0)
         return -7;
      else if(ldA<m)
         return -9;
      else if((m==0)||(n==0)||(alpha==zero))
         return 0;

      jy=(incy>0)?0:(1-n)*incy;
      kx=(incx>0)?0:(1-m)*incx;

      if(incx==1)
      {
         for(j=0;j<n;j++)
         {
            for(i=0;i<m;i++)
               A[i]+=x[i]*alpha*y[jy];
            jy+=incy;
            A+=ldA;
         }
      }
      else
      {
         for(j=0;j<n;j++)
         {
            ix=kx;
            for(i=0;i<m;i++)
            {
               A[i]+=x[ix]*alpha*y[jy];
               ix+=incx;
            }
            A+=ldA;
            jy+=incy;
         }
      }
      return 0;
   }
#ifdef __latl_cblas
#include <cblas.h>

   template <> int GERU<float>(int_t m, int_t n, complex<float> alpha, complex<float> *x, int_t incx, complex<float> *y, int_t incy, complex<float> *A, int_t ldA)
   {
      if(m<0)
         return -1;
      else if(n<0)
         return -2;
      else if(incx==0)
         return -5;
      else if(incy==0)
         return -7;
      else if(ldA<m)
         return -9;

      cblas_cgeru(CblasColMajor,m,n,&alpha,x,incx,y,incy,A,ldA);

      return 0;
   }

   template <> int GERU<double>(int_t m, int_t n, complex<double> alpha, complex<double> *x, int_t incx, complex<double> *y, int_t incy, complex<double> *A, int_t ldA)
   {
      if(m<0)
         return -1;
      else if(n<0)
         return -2;
      else if(incx==0)
         return -5;
      else if(incy==0)
         return -7;
      else if(ldA<m)
         return -9;

      cblas_cgeru(CblasColMajor,m,n,&alpha,x,incx,y,incy,A,ldA);

      return 0;
   }

#endif
}
#endif


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

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

#include <gsl/gsl_math.h>
#include <gsl/gsl_bst.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_test.h>

enum array_order
{
  ORD_RANDOM = 0,               /* random order */
  ORD_ASCENDING,                /* ascending order */
  ORD_DESCENDING,               /* descending order */
  ORD_BALANCED,                 /* balanced tree order */
  ORD_ZIGZAG,                   /* zig-zag order */
  ORD_ASCENDING_SHIFTED,        /* ascending from middle, then beginning */
  ORD_END_NODUP,                /* end of no-duplicate ordering */

  ORD_RANDOM_DUP                /* random order with duplicates */
};

/* fill array[] with random integers in [lower,upper] with duplicates allowed */
static void
random_integers(const size_t n, const int lower, const int upper,
                int array[], gsl_rng * r)
{
  size_t i;

  for (i = 0; i < n; ++i)
    array[i] = (int) ((upper - lower) * gsl_rng_uniform(r) + lower);
}

/* fills array[] with a random permutation of the integers between 0 and n - 1 */
static void
random_permuted_integers (const size_t n, int array[], gsl_rng * r)
{
  size_t i;

  for (i = 0; i < n; i++)
    array[i] = i;

  for (i = 0; i < n; i++)
    {
      size_t j = i + (unsigned) (gsl_rng_uniform(r) * (n - i));
      int t = array[j];
      array[j] = array[i];
      array[i] = t;
    }
}

static int
compare_ints(const void *pa, const void *pb, void *params)
{
  const int *a = pa;
  const int *b = pb;

  (void) params;

  return (*a < *b) ? -1 : (*a > *b);
}

/* Generates a list of integers that produce a balanced tree when
   inserted in order into a binary tree in the usual way.
   |min| and |max| inclusively bound the values to be inserted.
   Output is deposited starting at |*array|. */
static void
gen_balanced_tree (const int min, const int max, int **array)
{
  int i;

  if (min > max)
    return;

  i = (min + max + 1) / 2;
  *(*array)++ = i;
  gen_balanced_tree (min, i - 1, array);
  gen_balanced_tree (i + 1, max, array);
}

/* generates a permutation of the integers |0| to |n - 1| */
static void
gen_int_array (const size_t n, const enum array_order order, int array[], gsl_rng * r)
{
  size_t i;

  switch (order)
    {
      case ORD_RANDOM:
        random_permuted_integers (n, array, r);
        break;

      case ORD_ASCENDING:
        for (i = 0; i < n; i++)
          array[i] = i;
        break;

      case ORD_DESCENDING:
        for (i = 0; i < n; i++)
          array[i] = n - i - 1;
        break;

      case ORD_BALANCED:
        gen_balanced_tree (0, n - 1, &array);
        break;

      case ORD_ZIGZAG:
        for (i = 0; i < n; i++)
          {
            if (i % 2 == 0)
              array[i] = i / 2;
            else
              array[i] = n - i / 2 - 1;
          }
        break;

      case ORD_ASCENDING_SHIFTED:
        for (i = 0; i < n; i++)
          {
            array[i] = i + n / 2;
            if ((size_t) array[i] >= n)
              array[i] -= n;
          }
        break;

      case ORD_RANDOM_DUP:
        random_integers(n, -10, 10, array, r);
        break;

      default:
        assert (0);
    }
}

static void
check_traverser(const size_t n, const enum array_order order, gsl_bst_trav * trav, int data,
                const char *desc, const gsl_bst_workspace * w)
{
  int *prev, *cur, *next;

  prev = gsl_bst_trav_prev(trav);
  if (prev != NULL)
    {
      gsl_test(*prev > data, "bst %s[n=%zu,order=%d] %s traverser ahead of %d, but should be ahead of %d",
               gsl_bst_name(w), n, order, desc, *prev, data);
    }
  gsl_bst_trav_next(trav);

  cur = gsl_bst_trav_cur(trav);
  gsl_test(*cur != data, "bst %s[n=%zu,order=%d] %s traverser at %d, but should be at %d",
           gsl_bst_name(w), n, order, desc, *cur, data);

  next = gsl_bst_trav_next(trav);
  if (next != NULL)
    {
      gsl_test(*next < data, "bst %s[n=%zu,order=%d] %s traverser behind %d, but should be behind %d",
               gsl_bst_name(w), n, order, desc, *next, data);
    }
  gsl_bst_trav_prev(trav);
}

static void
test_bst_int(const size_t n, const gsl_bst_type * T, const enum array_order order, gsl_rng * r)
{
  int *data = malloc(n * sizeof(int));
  int *data_delete = malloc(n * sizeof(int));
  int *sorted_data = malloc(n * sizeof(int));
  gsl_bst_workspace * w = gsl_bst_alloc(T, NULL, compare_ints, NULL);
  gsl_bst_trav trav;
  int *p;
  int i;
  size_t nodes;

  /* generate data to be inserted in tree */
  gen_int_array(n, order, data, r);

  for (i = 0; i < (int) n; ++i)
    sorted_data[i] = data[i];

  gsl_sort_int(sorted_data, 1, n);

  if (order != ORD_RANDOM_DUP)
    {
      /* generate random order to delete data from tree */
      gen_int_array(n, ORD_RANDOM, data_delete, r);
    }
  else
    {
      for (i = 0; i < (int) n; ++i)
        data_delete[i] = sorted_data[i];
    }

  /* insert data */
  for (i = 0; i < (int) n; ++i)
    {
      p = gsl_bst_insert(&data[i], w);
      gsl_test(p != NULL, "bst_int %s[n=%zu,order=%d] insert i=%d", gsl_bst_name(w), n, order, i);
    }

  if (order != ORD_RANDOM_DUP)
    {
      nodes = gsl_bst_nodes(w);
      gsl_test(nodes != n, "bst_int %s[n=%zu,order=%d] after insertion count = %zu/%zu",
               gsl_bst_name(w), n, order, nodes, n);
    }

  /* test data was inserted and can be found */
  for (i = 0; i < (int) n; ++i)
    {
      p = gsl_bst_find(&data[i], w);
      gsl_test(*p != data[i], "bst_int %s[n=%zu,order=%d] find [%d,%d]",
               gsl_bst_name(w), n, order, *p, data[i]);

      p = gsl_bst_trav_find(&data[i], &trav, w);
      gsl_test(p == NULL, "bst_int %s[n=%zu,order=%d] trav_find unable to find item %d",
               gsl_bst_name(w), n, order, data[i]);

      check_traverser(n, order, &trav, data[i], "post-insertion", w);
    }

  /* traverse tree in-order */
  p = gsl_bst_trav_first(&trav, w);
  i = 0;
  while (p != NULL)
    {
      int *q = gsl_bst_trav_cur(&trav);

      gsl_test(*p != sorted_data[i], "bst_int %s[n=%zu,order=%d] traverse i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, sorted_data[i]);

      gsl_test(*p != *q, "bst_int %s[n=%zu,order=%d] traverse cur i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, *q);

      p = gsl_bst_trav_next(&trav);
      ++i;
    }

  gsl_test(i != (int) n, "bst_int %s[n=%zu,order=%d] traverse number=%d",
           gsl_bst_name(w), n, order, i);

  /* traverse tree in reverse order */
  p = gsl_bst_trav_last(&trav, w);
  i = n - 1;
  while (p != NULL)
    {
      int *q = gsl_bst_trav_cur(&trav);

      gsl_test(*p != sorted_data[i], "bst_int %s[n=%zu,order=%d] traverse reverse i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, sorted_data[i]);

      gsl_test(*p != *q, "bst_int %s[n=%zu,order=%d] traverse reverse cur i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, *q);

      p = gsl_bst_trav_prev(&trav);
      --i;
    }

  gsl_test(i != -1, "bst_int %s[n=%zu,order=%d] traverse reverse number=%d",
           gsl_bst_name(w), n, order, i);

  /* test traversal during tree modifications */
  for (i = 0; i < (int) n; ++i)
    {
      gsl_bst_trav x, y, z;

      gsl_bst_trav_find(&data[i], &x, w);
      check_traverser(n, order, &x, data[i], "pre-deletion", w);

      if (data[i] == data_delete[i])
        continue;

      p = gsl_bst_remove(&data_delete[i], w);
      gsl_test(*p != data_delete[i], "bst_int %s[n=%zu,order=%d] remove i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, data_delete[i]);

      p = gsl_bst_trav_copy(&y, &x);
      gsl_test(*p != data[i], "bst_int %s[n=%zu,order=%d] copy i=%d [%d,%d]",
               gsl_bst_name(w), n, order, i, *p, data[i]);

      /* re-insert item */
      p = gsl_bst_trav_insert(&data_delete[i], &z, w);

      check_traverser(n, order, &x, data[i], "post-deletion", w);
      check_traverser(n, order, &y, data[i], "copied", w);
      check_traverser(n, order, &z, data_delete[i], "insertion", w);

#if 0
      /* delete again */
      gsl_bst_remove(&data[i], w);
#endif
    }

  /* emmpty tree */
  gsl_bst_empty(w);

  nodes = gsl_bst_nodes(w);
  gsl_test(nodes != 0, "bst_int %s[n=%zu,order=%d] empty count = %zu",
           gsl_bst_name(w), n, order, nodes);

  gsl_bst_free(w);
  free(data);
  free(data_delete);
  free(sorted_data);
}

static void
test_bst(const gsl_bst_type * T, gsl_rng * r)
{
  enum array_order order;

  for (order = 0; order < ORD_END_NODUP; ++order)
    {
      test_bst_int(50, T, order, r);
      test_bst_int(100, T, order, r);
      test_bst_int(500, T, order, r);
    }
}

int
main(void)
{
  gsl_rng * r = gsl_rng_alloc(gsl_rng_default);

  test_bst(gsl_bst_avl, r);
  test_bst(gsl_bst_rb, r);

  gsl_rng_free(r);

  exit (gsl_test_summary());
}