/**
 * @file bblas_zaccuracy.c
 *
 * @brief BBLAS accuracy tests for double_Complex precision.
 *
 *  BBLAS is a software package provided by Univ. of Manchester,
 *  Univ. of Tennessee.
 *
 * @version 1.0.0
 * @author  Samuel  D. Relton
 * @author  Pedro   V. Lara
 * @author  Mawussi Zounon
 * @date    2016-02-20
 *
 **/

#ifndef DOXYGEN_SHOULD_SKIP_THIS
/**
 * Code generation
 * @precisions normal z -> c d s
 **/
#endif

#include "bblas_common.h"
#include <lapacke.h>
#include <cblas.h>

#define COMPLEX

/**
 * bblas_zgemm_tolerance computes the forward error
 * @f[\|C - C_{\mathrm{seq}}\|_{\infty}/((K|\alpha|\|A\|_{\infty}\|B\|_{\infty} +
 *           |\beta|\|C_{\mathrm{init}}\|_{\infty})\epsilon).@f]
 *
 **/

void bblas_zgemm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{

    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, An, Bm, Bn, M, K, N, ldc;
    int max_work_size = max(test->maxK,max(test->maxM, test->maxN));
    double Anorm, Bnorm, Rnorm, result;
    double eps = LAPACKE_dlamch_work('e');
    BBLAS_Complex64_t alpha =-1;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Temporary buffer to save (test-arrayC -C_final) */
    BBLAS_Complex64_t **C_diff;
    C_diff = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayC in C_diff */
    bblas_zcopy_Cinit(test, C_diff);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    M = test->M[batch_iter];
	    K   = test->K[batch_iter];
	    N   = test->N[batch_iter];
	    ldc = test->ldc[batch_iter];

	    if (test->transA[batch_iter] == BblasNoTrans) {
		Am = M; An = K;
	    } else {
		Am = K; An = M;
	    }
	    if (test->transB[batch_iter] == BblasNoTrans) {
		Bm = K; Bn = N;
	    } else {
		Bm = N; Bn = K;
	    }

	    /*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

	    /*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', M, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

	    /*Compute the infinity norm of B */
	    Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    result = Rnorm / ((K*cabs(test->alpha[batch_iter])*Anorm*Bnorm + cabs(test->beta[batch_iter])*test->Cinitnorm[batch_iter])*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}

    }else  if( test->batch_opts == BBLAS_FIXED ) //fixed  size
    {
	M   = test->M[first_index];
	K   = test->K[first_index];
	ldc = test->ldc[first_index];
	N   = test->N[first_index];

	if (test->transA[first_index] == BblasNoTrans) {
	    Am = M; An = K;
	} else {
	    Am = K; An = M;
	}
	if (test->transB[first_index] == BblasNoTrans) {
	    Bm = K; Bn = N;
	} else {
	    Bm = N; Bn = K;
	}

	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
		/*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);
	    /*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', M, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

	    /*Compute the infinity norm of B */
	    Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    /*Compute the relative error */
	    result = Rnorm / ((K*cabs(test->alpha[first_index])*Anorm*Bnorm + cabs(test->beta[first_index])*test->Cinitnorm[batch_iter])*eps);

	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal: Fixed, Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}
    }else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

/*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(C_diff[index]);
    }
    free(C_diff);
    free(work);
}

/**
 * bblas_zstarmm_tolerance computes the forward error
 * @f[\|C - C_{\mathrm{seq}}\|_{\infty}/((K|\alpha|\|A\|_{\infty}\|B\|_{\infty} +
 * |\beta|\|C_{\mathrm{init}}\|_{\infty})\epsilon).@f]
 *
 **/

void bblas_zstarmm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, Bm, Bn, N, ldc;
    int max_work_size = max(test->maxM, test->maxN);
    double Anorm, Bnorm, Rnorm, result;
    double eps = LAPACKE_dlamch_work('e');
    BBLAS_Complex64_t alpha =-1;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Temporary buffer to save (test-arrayC -C_final) */
    BBLAS_Complex64_t **C_diff;
    C_diff = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayC in C_diff */
    bblas_zcopy_Cinit(test, C_diff);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    N = test->N[batch_iter];
	    ldc = test->ldc[batch_iter];

	    if(test->side[batch_iter] == BblasLeft )
	    {
		Am = test->M[batch_iter];
	    } else {
		Am = test->N[batch_iter];
	    }
	    Bm = test->ldb[batch_iter];
	    Bn = test->N[batch_iter];
	    /*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

		/*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldc, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, Am, test->arrayA[batch_iter], Am, work);

	    /*Compute the infinity norm of B */
	    Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    result = Rnorm / ((N*cabs(test->alpha[batch_iter])*Anorm*Bnorm + cabs(test->beta[batch_iter])*test->Cinitnorm[batch_iter])*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}

    }else  if( test->batch_opts == BBLAS_FIXED ) //fixed  size
    {
	N = test->N[first_index];
	ldc = test->ldc[first_index];

	if(test->side[first_index] == BblasLeft )
	{
	    Am = test->M[first_index];
	} else {
	    Am = test->N[first_index];
	}
	Bm = test->ldb[first_index];
	Bn = test->N[first_index];

	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    /*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

	    /*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldc, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, Am, test->arrayA[batch_iter], Am, work);

	    /*Compute the infinity norm of B */
	    Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    /*Compute the relative error */
	    result = Rnorm / ((N*cabs(test->alpha[first_index])*Anorm*Bnorm + cabs(test->beta[first_index])*test->Cinitnorm[batch_iter])*eps);

	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal: Fixed, Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}
    }else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

/*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(C_diff[index]);
    }
    free(C_diff);
    free(work);
}

/**
 * bblas_zstark_tolerance computes the forward error
 * @f[\|C - C_{\mathrm{seq}}\|_{\infty}/((K|\alpha|\|A\|_{\infty}\|A\|_1 +
 * |\beta|\|C_{\mathrm{init}}\|_{\infty})\epsilon).@f]
 *
 **/

void bblas_zstark_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, An, N, K, ldc;
    char u;
    int max_work_size = max(test->maxK, test->maxN);
    double Anorm, ATnorm, Rnorm, result;
    double eps = LAPACKE_dlamch_work('e');
    double alpha_norm, beta_norm;
    BBLAS_Complex64_t alpha =-1;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Temporary buffer to save (test-arrayC -C_final) */
    BBLAS_Complex64_t **C_diff;
    C_diff = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayC in C_diff */
    bblas_zcopy_Cinit(test, C_diff);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    N = test->N[batch_iter];
	    K = test->K[batch_iter];
	    ldc = test->ldc[batch_iter];

	    if (test->trans[batch_iter] == BblasNoTrans) {
		Am = N; An = K;
	    } else {
		Am = K; An = N;
	    }

	    if(test->uplo[batch_iter] == BblasLower )
	      {
		u = 'L';
	      }else
	      {
		u = 'U';
	      }
	    /*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

	    /*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlanhe_work(LAPACK_COL_MAJOR, 'I', u, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

	    /*Compute the one norm of A */
	    ATnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Am, An, test->arrayA[batch_iter], Am, work);

	    if (test->routine == BBLAS_HERK)
	    {
		beta_norm   = cabs(test->beta_herk[batch_iter]);
		alpha_norm  = cabs(test->alpha_herk[batch_iter]);
	    }else{
		beta_norm  = cabs(test->beta[batch_iter]);
		alpha_norm  = cabs(test->alpha[batch_iter]);
	    }

	    result = Rnorm / ((K*alpha_norm*Anorm*ATnorm + beta_norm*test->Cinitnorm[batch_iter])*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}

    }else  if( test->batch_opts == BBLAS_FIXED ) //fixed  size
    {
	    N = test->N[first_index];
	    K = test->K[first_index];
	    ldc = test->ldc[first_index];

	    if (test->trans[first_index] == BblasNoTrans) {
		Am = N; An = K;
	    } else {
		Am = K; An = N;
	    }
	    if(test->uplo[first_index] == BblasLower )
	      {
		u = 'L';
	      }else
	      {
		u = 'U';
	      }

	    for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	      {
		/*Compute the error C - C_finial */
		cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

		Rnorm = LAPACKE_zlanhe_work(LAPACK_COL_MAJOR, 'I', u, N, C_diff[batch_iter], ldc, work);

		/*Compute the infinity norm of A */
		Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

		/*Compute the one norm of A */
		ATnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Am, An, test->arrayA[batch_iter], Am, work);

		if (test->routine == BBLAS_HERK)
		{
		    beta_norm   = cabs(test->beta_herk[first_index]);
		    alpha_norm  = cabs(test->alpha_herk[first_index]);
		}else{
		    beta_norm   = cabs(test->beta[first_index]);
		    alpha_norm  = cabs(test->alpha[first_index]);
		}

		/*Compute the relative error */
		result = Rnorm / ((K*alpha_norm*Anorm*ATnorm +
				   beta_norm*test->Cinitnorm[batch_iter])*eps);

		switch(test->target)
		{
		    case BBLAS_MKL:
			test->mkl_error[batch_iter] = result;
			break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		  test->device_error[batch_iter] = result;
		  break;

		    case BBLAS_OTHER:
			test->other_error[batch_iter] = result;
			break;
		    default:
			printf("In bblas_zcheck_Cfinal: Fixed, Target no defined\n");
			exit(EXIT_FAILURE);
		}
	    }
    }else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

/*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(C_diff[index]);
    }
    free(C_diff);
    free(work);
}

/**
 * bblas_zstar2k_tolerance computes the forward error
 * @f[\|C - C_{\mathrm{seq}}\|_{\infty}/((K|\alpha|\|A\|_{\infty}\|B\|_1 +
 * K|\alpha|\|B\|_{\infty}\|A\|_1  +
 * |\beta|\|C_{\mathrm{init}}\|_{\infty})\epsilon).@f]
 *
 **/

void bblas_zstar2k_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, An, Bn, Bm, N, K, ldc;
    int max_work_size = max(test->maxK, test->maxN);
    double Anorm, Bnorm, ATnorm, BTnorm,  Rnorm, result;
    double beta_norm;
    double eps = LAPACKE_dlamch_work('e');
    BBLAS_Complex64_t alpha =-1;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Temporary buffer to save (test-arrayC -C_final) */
    BBLAS_Complex64_t **C_diff;
    C_diff = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayC in C_diff */
    bblas_zcopy_Cinit(test, C_diff);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    N = test->N[batch_iter];
	    K = test->K[batch_iter];
	    ldc = test->ldc[batch_iter];

	    if (test->trans[batch_iter] == BblasNoTrans) {
		Am = N; An = K;
		Bn = K;
	    } else {
		Am = K; An = N;
		Bn = N;
	    }
	    Bm = test->ldb[batch_iter];

	    /*Compute the error C - C_finial */
	    cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

		/*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldc, N, C_diff[batch_iter], ldc, work);

	    /*Compute the infinity norm of A */
	    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

	    /*Compute the one norm of A */
	    ATnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Am, An, test->arrayA[batch_iter], Am, work);

	    /*Compute the infinity norm of B */
	    Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    /*Compute the one norm of B */
	    BTnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Bm, Bn, test->arrayB[batch_iter], Bm, work);

	    if(test->routine == BBLAS_HER2K)
	    {
		beta_norm = cabs(test->beta_herk[batch_iter]);
	    }else
	    {
		beta_norm = cabs(test->beta[batch_iter]);
	    }

	    result = Rnorm / ((K*cabs(test->alpha[batch_iter])*Anorm*BTnorm +
			       K*cabs(test->alpha[batch_iter])*Bnorm*ATnorm +
			       beta_norm*test->Cinitnorm[batch_iter])*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}

    }else  if( test->batch_opts == BBLAS_FIXED ) //fixed  size
    {
	N = test->N[first_index];
	K = test->K[first_index];
	ldc = test->ldc[first_index];

	if (test->trans[first_index] == BblasNoTrans) {
		Am = N; An = K;
		Bn = K;
	} else {
	    Am = K; An = N;
	    Bn = N;
	}
	Bm = test->ldb[first_index];

	    for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	    {
		/*Compute the error C - C_finial */
		cblas_zaxpy (ldc*N, CBLAS_SADDR(alpha), C_final[batch_iter], 1, C_diff[batch_iter], 1);

		/*Compute  the infinity norm associated  with the error */
		Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldc, N, C_diff[batch_iter], ldc, work);

		/*Compute the infinity norm of A */
		Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Am, An, test->arrayA[batch_iter], Am, work);

		/*Compute the one norm of A */
		ATnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Am, An, test->arrayA[batch_iter], Am, work);

		/*Compute the infinity norm of B */
		Bnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', Bm, Bn, test->arrayB[batch_iter], Bm, work);

		/*Compute the one norm of B */
		BTnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'O', Bm, Bn, test->arrayB[batch_iter], Bm, work);

		if(test->routine == BBLAS_HER2K)
		{
		    beta_norm = cabs(test->beta_herk[first_index]);
		}else
		{
		    beta_norm = cabs(test->beta[first_index]);
		}

		/*Compute the relative error */
		result = Rnorm / ((K*cabs(test->alpha[first_index])*Anorm*BTnorm +
				   K*cabs(test->alpha[first_index])*Bnorm*ATnorm +
				   beta_norm*test->Cinitnorm[batch_iter])*eps);

		switch(test->target)
		{
		case BBLAS_MKL:
		  test->mkl_error[batch_iter] = result;
		  break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		  test->device_error[batch_iter] = result;
		  break;

		case BBLAS_OTHER:
		  test->other_error[batch_iter] = result;
		  break;
		default:
		  printf("In bblas_zcheck_Cfinal: Fixed, Target no defined\n");
		  exit(EXIT_FAILURE);
		}
	    }
    }else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

/*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(C_diff[index]);
    }
    free(C_diff);
    free(work);
}

/**
 * bblas_ztrmm_tolerance computes the forward error
 * @f[\|C - C_{\mathrm{seq}}\|_{\infty}/((|\alpha|\|A\|_{\infty}\|B\|_{\infty}N\epsilon).@f]
 *
 **/

void bblas_ztrmm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **B_final)
{
    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, An, ldb, N, M;
    int max_work_size = max(test->maxK, test->maxN);
    char u, diag;
    double Anorm, Rnorm, result;
    double eps = LAPACKE_dlamch_work('e');
    BBLAS_Complex64_t alpha =-1;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Temporary buffer to save (test-arrayB -B_final) */
    BBLAS_Complex64_t **B_diff;
    B_diff = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayB in B_diff */
    bblas_zcopy_Binit(test, B_diff);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    N = test->N[batch_iter];
	    M = test->M[batch_iter];
	    ldb = test->ldb[batch_iter];

	    Am = test->lda[batch_iter];

	    if(test->side[batch_iter] == BblasLeft )
	    {
		An = M;
	    }else
	    {
		An = N;
	    }

	    if(test->uplo[batch_iter] == BblasLower )
	    {
	       u = 'L';
	    }else
	    {
	       u = 'U';
	    }

	    if(test->diag[batch_iter] == BblasUnit )
	    {
		diag = 'U';
	    }else
	    {
		diag = 'N';
	    }

	    /*Compute the error B - B_finial */
	    cblas_zaxpy (ldb*N, CBLAS_SADDR(alpha), B_final[batch_iter], 1, B_diff[batch_iter], 1);

		/*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, B_diff[batch_iter], ldb, work);

	    /*Infinity norm of the triangular matrix A */
	    Anorm = LAPACKE_zlantr_work(LAPACK_COL_MAJOR, 'I', u, diag, Am,
					An, test->arrayA[batch_iter], Am, work);

		result = Rnorm/(N*cabs(test->alpha[batch_iter])*Anorm*test->Binitnorm[batch_iter]*eps);
	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }

	}
    }else  if( test->batch_opts == BBLAS_FIXED )
    {

	N = test->N[first_index];
	M = test->M[first_index];
	ldb = test->ldb[first_index];

	Am = test->lda[first_index];

	if(test->side[first_index] == BblasLeft )
	{
	    An = M;
	}else
	{
	    An = N;
	}

	if(test->uplo[first_index] == BblasLower )
	{
	    u = 'L';
	}else
	{
	    u = 'U';
	}

	if(test->diag[first_index] == BblasUnit )
	{
	    diag = 'U';
	}else
	{
	    diag = 'N';
	}

	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    /*Compute the error B - B_finial */
	    cblas_zaxpy (ldb*N, CBLAS_SADDR(alpha), B_final[batch_iter], 1, B_diff[batch_iter], 1);

		/*Compute  the infinity norm associated  with the error */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, B_diff[batch_iter], ldb, work);

	    /*Infinity norm of the triangular matrix A */
	    Anorm = LAPACKE_zlantr_work(LAPACK_COL_MAJOR, 'I', u, diag, Am,
					An, test->arrayA[batch_iter], Am, work);

	    result = Rnorm/(N*cabs(test->alpha[first_index])*Anorm*test->Binitnorm[batch_iter]*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}
    }
    else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

    /*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(B_diff[index]);
    }
    free(B_diff);
    free(work);
}

/**
 * bblas_ztrsm_tolerance computes the backward error
 * @f[\|\alpha B - AX \|_{\infty}/((\|A\|_{\infty}\|X\| + |\alpha|\|B\|_{\infty})N\epsilon).@f]
 *
 **/

void bblas_ztrsm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **B_final)
{
    /*Local variables */
    int batch_count = test->batch_count;
    int batch_iter, first_index = 0;
    int Am, An, ldb, N, M;
    int max_work_size = max(test->maxK, test->maxN);
    char u, diag;
    double Anorm, Rnorm, result, Xnorm;
    double eps = LAPACKE_dlamch_work('e');
    BBLAS_Complex64_t alpha;
    double *work = (double *)malloc(max_work_size*sizeof(double));

    /*Variable to save the residual alphaB -AX */
    BBLAS_Complex64_t **residual;
    residual = (BBLAS_Complex64_t **) malloc(batch_count*sizeof(BBLAS_Complex64_t *));

    /*Make a copy of test->arrayB (X) in residual */
    bblas_zcopy_Binit(test, residual);

    /*Variable size */
    if( test->batch_opts == BBLAS_VARIABLE )
    {
	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{
	    N = test->N[batch_iter];
	    M = test->M[batch_iter];
	    ldb = test->ldb[batch_iter];

	    Am = test->lda[batch_iter];

	    if(test->side[batch_iter] == BblasLeft )
	    {
		An = M;
	    }else
	    {
		An = N;
	    }

	    if(test->uplo[batch_iter] == BblasLower )
	    {
	       u = 'L';
	    }else
	    {
	       u = 'U';
	    }

	    if(test->diag[batch_iter] == BblasUnit )
	    {
		diag = 'U';
	    }else
	    {
		diag = 'N';
	    }

	    /*Compute residual <- AX  (ztrmm) */
	    alpha = 1;
	    cblas_ztrmm(BblasColMajor, test->side[batch_iter], test->uplo[batch_iter], test->transA[batch_iter],
			test->diag[batch_iter], M, N, CBLAS_SADDR(alpha), test->arrayA[batch_iter],
			test->lda[batch_iter], residual[batch_iter], ldb);

	    /*Compute Binit = alpha Binit */
	    for (int index=0;  index < ldb*N; index++)
	    {
		test->Binit[batch_iter][index] = test->alpha[batch_iter]*test->Binit[batch_iter][index];
	    }

	    /*Compute the  residual <- alpha B - Ax  */
	    alpha= -1;
	    cblas_zaxpy (ldb*N, CBLAS_SADDR(alpha), test->Binit[batch_iter], 1, residual[batch_iter], 1);
		/*Compute  the infinity norm associated  with the residual */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, residual[batch_iter], ldb, work);

	    /*Infinity norm of the triangular matrix A */
	    Anorm = LAPACKE_zlantr_work(LAPACK_COL_MAJOR, 'I', u, diag, Am,
					An, test->arrayA[batch_iter], Am, work);

	    /*Compute  the infinity norm associated  with X (B) */
	    Xnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, test->arrayB[batch_iter], ldb, work);

	    result = Rnorm/(Anorm*Xnorm*N*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }

	}
    }else  if( test->batch_opts == BBLAS_FIXED )
    {

	N = test->N[first_index];
	M = test->M[first_index];
	ldb = test->ldb[first_index];

	Am = test->lda[first_index];

	if(test->side[first_index] == BblasLeft )
	{
	    An = M;
	}else
	{
	    An = N;
	}

	if(test->uplo[first_index] == BblasLower )
	{
	    u = 'L';
	}else
	{
	    u = 'U';
	}

	if(test->diag[first_index] == BblasUnit )
	{
	    diag = 'U';
	}else
	{
	    diag = 'N';
	}

	for( batch_iter =0; batch_iter < batch_count ; batch_iter++)
	{

	    /*Compute residual <- AX  (ztrmm) */
	    alpha = 1;
	    cblas_ztrmm(BblasColMajor, test->side[first_index], test->uplo[first_index], test->transA[first_index],
			test->diag[first_index], M, N, CBLAS_SADDR(alpha), (const BBLAS_Complex64_t*) test->arrayA[batch_iter],
			test->lda[first_index], residual[batch_iter], ldb);

	    /*Compute Binit = alpha Binit */
	    for (int index=0;  index < ldb*N; index++)
	    {
		test->Binit[batch_iter][index] = test->alpha[first_index]*test->Binit[batch_iter][index];
	    }

	    /*Compute the  residual <- alpha B - Ax  */
	    alpha = -1;
	    cblas_zaxpy (ldb*N, CBLAS_SADDR(alpha), test->Binit[batch_iter], 1, residual[batch_iter], 1);

		/*Compute  the infinity norm associated  with the residual */
	    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, residual[batch_iter], ldb, work);

	    /*Infinity norm of the triangular matrix A */
	    Anorm = LAPACKE_zlantr_work(LAPACK_COL_MAJOR, 'I', u, diag, Am,
					An, test->arrayA[batch_iter], Am, work);

	    /*Compute  the infinity norm associated  with X (B) */
	    Xnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, 'I', ldb, N, test->arrayB[batch_iter], ldb, work);

	    result = Rnorm/(Anorm*Xnorm*N*eps);

	    /*Compute the relative error */
	    switch(test->target)
	    {
		case BBLAS_MKL:
		    test->mkl_error[batch_iter] = result;
		    break;

		case BBLAS_CUBLAS:
		case BBLAS_MAGMA:
		    test->device_error[batch_iter] = result;
		    break;

		case BBLAS_OTHER:
		    test->other_error[batch_iter] = result;
		    break;
		default:
		    printf("In bblas_zcheck_Cfinal, Variable: Target no defined\n");
		    exit(EXIT_FAILURE);
	    }
	}
    }
    else
    {
	bblas_error("bblas_ztesting.c", "wrong batch_opts value");
    }

    /*Free allocated memory */
    for(int index =0; index < batch_count; index++)
    {
	free(residual[index]);
    }
    free(residual);
    free(work);
}

/**
 * Calls bblas_zstar2k_tolerance.
 **/
#ifdef COMPLEX
void bblas_zher2k_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstar2k_tolerance(test, C_final);

}
#endif
/**
 * Calls bblas_zstar2k_tolerance.
 **/

void bblas_zsyr2k_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstar2k_tolerance(test, C_final);
}

/**
 * Calls bblas_zstark_tolerance.
 **/

#ifdef COMPLEX
void bblas_zherk_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstark_tolerance(test, C_final);
}
#endif
/**
 * Calls bblas_zstark_tolerance.
 **/

void bblas_zsyrk_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstark_tolerance(test, C_final);
}

/**
 * Calls bblas_zstarmm_tolerance.
 **/

#ifdef COMPLEX
void bblas_zhemm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstarmm_tolerance(test, C_final);
}
#endif
/**
 * Calls bblas_zstamm_tolerance.
 **/

void bblas_zsymm_tolerance(bblas_ztest_t *test, BBLAS_Complex64_t **C_final)
{
    bblas_zstarmm_tolerance(test, C_final);
}


/************************************************************************//**
* @file time_evolution.c
*
* This file contains code to advance the solutions one step forward in time
*
* *****************************************************************************/

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

/* @cond GLOBAL_VARIABLES */
extern const double g;
/* @endcond */

/***************************************************************************//**
*
* Function to advance a channel structure one step forward in time using 2-stage
* RKDG scheme
* @param[in] Chan channel structure that we are currently working on
* @param[in] time current time of the simulation
* @param[in] dt size of the time step
* @param[in] channelNumber identity (number) of the channel structure that we
* are currently working on
*
* *********************************************************************************/
void oneTimeStep (struct channel *Chan, double time, double dt, int channelNumber)
{
	int TotalNumNodes = 2*Chan->NumNodes;
	double RHSA[TotalNumNodes];
	double RHSQ[TotalNumNodes];

	// Calculate wet/dry status of elements
	#ifdef WDON
	wetDryStatus1D(Chan);
	#endif

	// store A and Q
	gsl_vector *A = gsl_vector_alloc(TotalNumNodes);
	gsl_vector *Q = gsl_vector_alloc(TotalNumNodes);
	for (int i=0; i < TotalNumNodes; ++i)
	{
		gsl_vector_set(A,i,(*Chan).A[i]);
		gsl_vector_set(Q,i,(*Chan).Q[i]);
	}

	/******** For debugging print out RHSA and RHSQ)************/
	#ifdef DEBUG
	printf("Intial A and Q:\n");
	gsl_vector_fprintf(stdout, A, "%1.16f");
	printf("\n");
	gsl_vector_fprintf(stdout, Q, "%1.16f");
	printf("\n");
	#endif
	/***********************************************************/

	// Intermediate RK step
	computeL(Chan, time, channelNumber, RHSA, RHSQ);

	gsl_vector *gRHSA = gsl_vector_alloc(TotalNumNodes);
	gsl_vector *gRHSQ = gsl_vector_alloc(TotalNumNodes);
	for (int i=0; i<TotalNumNodes; ++i)
	{
		gsl_vector_set(gRHSA,i,RHSA[i]);
		gsl_vector_set(gRHSQ,i,RHSQ[i]);
	}

	/******** For debugging print out RHSA and RHSQ)************/
	#ifdef DEBUG
	printf("RHSA and RHSQ after first computeL:\n");
	gsl_vector_fprintf(stdout, RHSA, "%1.16f");
	printf("\n");
	gsl_vector_fprintf(stdout, RHSQ, "%1.16f");
	printf("\n");
	#endif
	/***********************************************************/

	// w_1 = w + dt*L
	gsl_vector *A_1 = gsl_vector_alloc(TotalNumNodes);
	gsl_vector *Q_1 = gsl_vector_alloc(TotalNumNodes);
	for (int i=0; i<TotalNumNodes; ++i)
	{
		gsl_vector_set(A_1,i,gsl_vector_get(A,i));
		gsl_vector_set(Q_1,i,gsl_vector_get(Q,i));
	}
	gsl_vector_scale(gRHSA,dt);
	gsl_vector_scale(gRHSQ,dt);
	gsl_vector_add(A_1,gRHSA);
	gsl_vector_add(Q_1,gRHSQ);

	/******** For debugging print out A_1 and Q_1************/
	#ifdef DEBUG
	printf("A_1 and Q_1\n");
	gsl_vector_fprintf(stdout, A_1, "%1.16f");
	printf("\n");
	gsl_vector_fprintf(stdout, Q_1, "%1.16f");
	printf("\n");
	#endif
	/***********************************************************/

	for (int i=0; i <TotalNumNodes; ++i)
	{
		Chan->A[i] = gsl_vector_get(A_1,i);
		Chan->Q[i] = gsl_vector_get(Q_1,i);
	}

	// Apply minmod slope limiter on w_1
	minmod(Chan);

	#ifdef WDON
	// apply the positive-depth operaton on w_1
	PDop1D(Chan);

	// Caculate wet/dry status again
	wetDryStatus1D(Chan);

	#endif

//	internal_BC();
	boundary_conditions();

	for (int i = 0; i<TotalNumNodes; ++i)
	{
		gsl_vector_set(A_1,i,Chan->A[i]);
		gsl_vector_set(Q_1,i,Chan->Q[i]);
	}

	/******** For debugging print out A and Q************/
/*	#ifdef DEBUG
	printf("A and Q after first minmod\n");
	gsl_vector_fprintf(stdout, A_1, "%e");
	printf("\n");
	gsl_vector_fprintf(stdout, Q_1, "%e");
	printf("\n");
	#endif
*/	/***********************************************************/

	// Compute w
	computeL(Chan, time, channelNumber, RHSA, RHSQ);

	for (int i=0; i<TotalNumNodes; ++i)
	{
		gsl_vector_set(gRHSA, i, RHSA[i]);
		gsl_vector_set(gRHSQ, i, RHSQ[i]);
	}

	/******** For debugging print out RHSA and RHSQ)************/
	#ifdef DEBUG
	printf("RHSA and RHSQ after second computeL:\n");
	gsl_vector_fprintf(stdout, RHSA, "%1.16f");
	printf("\n");
	gsl_vector_fprintf(stdout, RHSQ, "%1.16f");
	printf("\n");
	#endif
	/***********************************************************/

	// w_1 = w + w_1
	gsl_vector_add(A_1,A);
	gsl_vector_add(Q_1,Q);

	// w = (w1 + dt*L)/2
	gsl_vector_scale(A_1,0.5);
	gsl_vector_scale(Q_1,0.5);
	gsl_vector_scale(gRHSA,dt/2);
	gsl_vector_scale(gRHSQ,dt/2);

	gsl_vector_add(A_1,gRHSA);
	gsl_vector_add(Q_1,gRHSQ);

	for (int i=0; i < TotalNumNodes; ++i)
	{
		Chan->A[i] = gsl_vector_get(A_1,i);
		Chan->Q[i] = gsl_vector_get(Q_1,i);
	}

	/******** For debugging print out A and Q)************/
	#ifdef DEBUG
	printf("A and Q after second computeL:\n");
	gsl_vector_fprintf(stdout, A_1, "%1.16f");
	printf("\n");
	gsl_vector_fprintf(stdout, Q_1, "%1.16f");
	printf("\n");
	#endif
	/***********************************************************/

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

	#ifdef WDON
	PDop1D(Chan);
	#endif

	gsl_vector_free(A);
	gsl_vector_free(Q);
	gsl_vector_free(A_1);
	gsl_vector_free(Q_1);
	gsl_vector_free(gRHSA);
	gsl_vector_free(gRHSQ);

	boundary_conditions();
}

/***************************************************************************//**
*
* Function to advance a junction structure one step forward in time using 2-stage
* RKDG scheme
* @param[in] junc the junction tructure that we are currently working on
* @param[in] time current time of the simulation
* @param[in] dt size of the time step
*
* *********************************************************************************/

void oneTimeStep2D (struct junction *junc, double time, double dt)
{

	int NumEl = junc->NumEl;
	int NumEdges = 3*NumEl;
	double RHSZeta[NumEdges], RHSQx[NumEdges], RHSQy[NumEdges];

	#ifdef WDON
	wetDryStatus2D(junc);
	#endif

	// store H, Qx, Qy
	gsl_vector *zeta = gsl_vector_alloc(NumEdges);
	gsl_vector *Qx = gsl_vector_alloc(NumEdges);
	gsl_vector *Qy = gsl_vector_alloc(NumEdges);

	int j = 0;
	for (int i=0; i < NumEl; ++i) {
		for (int k=0; k < 3; ++k){
			gsl_vector_set(zeta,j,junc->zeta[index(i,k,3)]);
			gsl_vector_set(Qx,j, junc->Qx[index(i,k,3)]);
			gsl_vector_set(Qy,j, junc->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,zeta,"%e");
	printf("\n Qx \n");
	gsl_vector_fprintf(stdout,Qx,"%e");
	printf("\n Qy \n");
	gsl_vector_fprintf(stdout,Qy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// Intermediate RK step
	compute2DL(junc, time, RHSZeta, RHSQx, RHSQy);

	gsl_vector *gRHSZeta = gsl_vector_alloc(NumEdges);
	gsl_vector *gRHSQx = gsl_vector_alloc(NumEdges);
	gsl_vector *gRHSQy = gsl_vector_alloc(NumEdges);

	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,RHSZeta,"%e");
	printf("\n RHSQx \n");
	gsl_vector_fprintf(stdout,RHSQx,"%e");
	printf("\n RHSQy \n");
	gsl_vector_fprintf(stdout,RHSQy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// w_1 = w + dt*L;
	gsl_vector *zeta_1 = gsl_vector_alloc(NumEdges);
	gsl_vector *Qx_1 = gsl_vector_alloc(NumEdges);
	gsl_vector *Qy_1 = gsl_vector_alloc(NumEdges);
	for (int i=0; i<NumEdges; ++i)
	{
		gsl_vector_set(zeta_1,i,gsl_vector_get(zeta,i));
		gsl_vector_set(Qx_1,i,gsl_vector_get(Qx,i));
		gsl_vector_set(Qy_1,i,gsl_vector_get(Qy,i));
	}

	gsl_vector_scale(gRHSZeta,dt);
	gsl_vector_scale(gRHSQx,dt);
	gsl_vector_scale(gRHSQy,dt);
	gsl_vector_add(zeta_1,gRHSZeta);
	gsl_vector_add(Qx_1, gRHSQx);
	gsl_vector_add(Qy_1, gRHSQy);

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

	j = 0;
	for (int i=0; i<NumEl; ++i){
		for (int k =0; k < 3; ++k){
			junc->zeta[index(i,k,3)] = gsl_vector_get(zeta_1,j);
			junc->Qx[index(i,k,3)] = gsl_vector_get(Qx_1,j);
			junc->Qy[index(i,k,3)] = gsl_vector_get(Qy_1,j);
			++j;
		}
	}

	// Apply minmod slope limiter on w_1
	SlopeLimiter(junc);
//	internal_BC();

	#ifdef WDON
	PDop2D(junc);
	wetDryStatus2D(junc);
	#endif

	j = 0;
	for (int i=0; i<NumEl; ++i)
	{
		for(int k=0; k <3; ++k)
		{
			gsl_vector_set(zeta_1,j,junc->zeta[index(i,k,3)]);
			gsl_vector_set(Qx_1,j,junc->Qx[index(i,k,3)]);
			gsl_vector_set(Qy_1,j,junc->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,zeta_1,"%e");
	printf("\n Qx \n");
	gsl_vector_fprintf(stdout,Qx_1,"%e");
	printf("\n Qy \n");
	gsl_vector_fprintf(stdout,Qy_1,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	// w_1 = w_1 + w;
	gsl_vector_add(zeta_1,zeta);
	gsl_vector_add(Qx_1, Qx);
	gsl_vector_add(Qy_1,Qy);

	// Compute w
	compute2DL(junc, time, RHSZeta, RHSQx, RHSQy);

	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,RHSZeta,"%e");
	printf("\n RHSQx \n");
	gsl_vector_fprintf(stdout,RHSQx,"%e");
	printf("\n RHSQy \n");
	gsl_vector_fprintf(stdout,RHSQy,"%e");
	printf("\n");
	#endif
	/***************************************************************************/

	gsl_vector_scale(zeta_1,0.5);
	gsl_vector_scale(Qx_1,0.5);
	gsl_vector_scale(Qy_1,0.5);
	gsl_vector_scale(gRHSZeta,dt/2);
	gsl_vector_scale(gRHSQx,dt/2);
	gsl_vector_scale(gRHSQy,dt/2);

	gsl_vector_add(zeta_1,gRHSZeta);
	gsl_vector_add(Qx_1,gRHSQx);
	gsl_vector_add(Qy_1,gRHSQy);

	j = 0;
	for (int i=0; i<NumEl; ++i){
		for (int k =0; k < 3; ++k){
			junc->zeta[index(i,k,3)] = gsl_vector_get(zeta_1,j);
			junc->Qx[index(i,k,3)] = gsl_vector_get(Qx_1,j);
			junc->Qy[index(i,k,3)] = gsl_vector_get(Qy_1,j);
			++j;
		}
	}

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

	#ifdef WDON
	PDop2D(junc);
	#endif

	gsl_vector_free(zeta);
	gsl_vector_free(Qx);
	gsl_vector_free(Qy);
	gsl_vector_free(zeta_1);
	gsl_vector_free(Qx_1);
	gsl_vector_free(Qy_1);
	gsl_vector_free(gRHSZeta);
	gsl_vector_free(gRHSQx);
	gsl_vector_free(gRHSQy);

}

/*******************************************************************//**
*
* Function to evolve the channels as well as the junctions through time
* until the final simulation time. It also outputs the data at a certain
* time
*
* *********************************************************************/
void time_evolution(double FinalTime)
{

	//Output initial conditions
	FILE* file1;
	FILE* file2;

	for (int i=0; i<NumChannels; ++i)
	{
		char filename1[100];
		char filename2[100];
		sprintf(filename1, "000_Zeta%d.dat", i);
		sprintf(filename2, "000_Q%d.dat", i);
		file1 = fopen(filename1, "w");
		file2 = fopen(filename2, "w");

		fprintf(file1, "Height at time 0 for Channel %d\n", i);
		fprintf(file2, "Q at time 0 for Channel %d\n", i);

		int NumNodes = ChannelList[i]->NumNodes;

		for (int j = 0; j<NumNodes; ++j)
		{
			double B;
			double z;

			B = ChannelList[i]->b[j];
			z = ChannelList[i]->z[j];
			double x_val = ChannelList[i]->x[j];
			double y_val = ChannelList[i]->y[j];
			for (int k=0; k<2; ++k)
			{

				double A = ChannelList[i]->A[index(j,k,2)];
				double Q = ChannelList[i]->Q[index(j,k,2)];
				double H = A/B;
				double zeta = H - z;
				fprintf(file1, "%e\t%e\t%e\t%e\n", x_val, y_val, -z, H);
				fprintf(file2, "%e\t%e\t%e\t%e\n", x_val, y_val, -z, Q);
			}
		}
		fclose(file1);
		fclose(file2);
	}

	// output the file with x y and z information for the elements
/*	FILE *juncMesh;
	for (int i=0; i <NumJunctions; ++i)
	{
		char filename1[100];
		sprintf(filename1, "Junction%dMesh.dat",i);
		juncMesh = fopen(filename1, "w");
		int NumEl = JunctionList[i]->NumEl;
		int NumNodes = JunctionList[i]->NumNodes;
		int NumEdges = JunctionList[i]->TotalNumEdges;

		fprintf(juncMesh, "NodeNum \t x \t\t y \t\t z\n");
		for (int j = 0; j < NumNodes; ++j)
		{
			double xcor = JunctionList[i]->x[j];
			double ycor = JunctionList[i]->y[j];
			double zcor = JunctionList[i]->z[j];
			fprintf(juncMesh, "%d\t%e\t%e\t%e\n",j+1, xcor, ycor, zcor);
		}

		fprintf(juncMesh, "\n EltoVert \n");
		for (int j = 0; j < NumEl; ++j)
		{
			int n1 = JunctionList[i]->EltoVert[j*3]+1;
			int n2 = JunctionList[i]->EltoVert[j*3+1]+1;
			int n3 = JunctionList[i]->EltoVert[j*3+2]+1;
			fprintf(juncMesh, "%d\t3\t%d\t%d\t%d\n", j+1, n1, n2,n3);

		}

		fprintf(juncMesh, "\n EdgtoVert \n");
		for (int j =0; j < NumEdges; ++j)
		{
			int n1 = JunctionList[i]->EdgtoVert[j*2]+1;
			int n2 = JunctionList[i]->EdgtoVert[j*2+1]+1;
			fprintf(juncMesh, "%d\t%d\n",n1,n2);

		}

		fprintf(juncMesh, "\n EdgtoEls \n");
		for (int j =0; j < NumEdges; ++j)
		{
			int n1 = JunctionList[i]->EdgtoEls[j*2];
			int n2 = JunctionList[i]->EdgtoEls[j*2+1];
			fprintf(juncMesh, "%d\t%d\n",n1,n2);

		}

		fclose(juncMesh);

	}
*/
	boundary_conditions();

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

	double minLength = 99999999;
	for (int i = 0; i < NumChannels; ++i)
	{
		minLength = fmin(minLength, ChannelList[i]->mindh);
	}

	for (int i = 0; i < NumJunctions; ++i)
	{
		minLength = fmin(minLength, JunctionList[i]->minEdgLength);
	}

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

		double maxLambda = 0;

		// step forward in time
		for(int i=0; i<NumChannels; ++i)
		{
			oneTimeStep(ChannelList[i],time,dt,i);
			maxLambda = fmax(maxLambda, ChannelList[i]->max_lambda);

		}

		internal_BC();

		for(int i=0; i<NumJunctions; ++i)
		{
			oneTimeStep2D(JunctionList[i],time,dt);
			maxLambda = fmax(maxLambda, JunctionList[i]->max_lambda);
		}
		internal_BC();

		boundary_conditions();

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

			//Files for the channels
			FILE* file1;
			FILE* file2;

			for (int i=0; i<NumChannels; ++i)
			{
				char filename1[100];
				char filename2[100];
				sprintf(filename1, "%3.3d_Zeta%d.dat", fileNumber,i);
				sprintf(filename2, "%3.3d_Q%d.dat", fileNumber,i);
				file1 = fopen(filename1, "w");
				file2 = fopen(filename2, "w");

				fprintf(file1, "Height at time %e for Channel %d\n", time, i);
				fprintf(file2, "Q at time %e for Channel %d\n", time,i);

				int NumNodes = ChannelList[i]->NumNodes;

				for (int j = 0; j<NumNodes; ++j)
				{
					double B;
					double z;

					B = ChannelList[i]->b[j];
					z = ChannelList[i]->z[j];
					double x_val = ChannelList[i]->x[j];
					double y_val = ChannelList[i]->y[j];

					int k;
					for (k=0; k<2; ++k)
					{

						double A = ChannelList[i]->A[index(j,k,2)];
						double Q = ChannelList[i]->Q[index(j,k,2)];
						double H = A/B;
						double zeta = H - z;
						fprintf(file1, "%e\t%e\t%e\t%e\n", x_val, y_val, -z, H);
						fprintf(file2, "%e\t%e\t%e\t%e\n", x_val, y_val, -z, Q);
					}

				}
				fclose(file1);
				fclose(file2);
			}

			FILE* file3;
			FILE* file4;
			FILE* file5;

			for (int i=0; i<NumJunctions;++i)
			{
				char filename1[100];
				char filename2[100];
				char filename3[100];
				sprintf(filename1, "%3.3d_Height_junc%d.dat", fileNumber,i);
				sprintf(filename2, "%3.3d_Qx_junc%d.dat", fileNumber,i);
				sprintf(filename3, "%3.3d_Qy_junc%d.dat", fileNumber,i);
				file3 = fopen(filename1, "w");
				file4 = fopen(filename2, "w");
				file5 = fopen(filename3, "w");

				fprintf(file3, "Zeta at time %e for Junction %d\n", time, i);
				fprintf(file4, "Qx at time %e for Junction %d\n", time, i);
				fprintf(file5, "Qy at time %e for Junction %d\n", time,i);

				int j;
				int k;
				for (j=0; j<JunctionList[i]->NumEl; ++j)
				{
					for(k=0; k<3; ++k)
					{
						double zeta = JunctionList[i]->zeta[index(j,k,3)];
						double Qx = JunctionList[i]->Qx[index(j,k,3)];
						double Qy = JunctionList[i]->Qy[index(j,k,3)];
						double z1 = JunctionList[i]->z[JunctionList[i]->EltoVert[j*3+k]];
						double z2 = JunctionList[i]->z[JunctionList[i]->EltoVert[j*3+(k+1)%3]];
						double z = 0.5*(z1+z2);
						double height = zeta + z;
						fprintf(file3, "%1.15f\t\t",height);
						fprintf(file4, "%1.15f\t\t",Qx);
						fprintf(file5, "%1.15f\t\t",Qy);
					}
					fprintf(file3, "\n");
					fprintf(file4, "\n");
					fprintf(file5, "\n");
				}
				fclose(file3);
				fclose(file4);
				fclose(file5);
			}

			++fileNumber;
		}

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

		// set next time step
		//printf("max_lambda = %e\n", maxlambda);
		//printf("mindx = %e\n", mindx);
		dt = 0.4*minLength/maxLambda;
		if ((time + dt) > FinalTime)
			dt = FinalTime - time;

	}
}


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

int
main (void)
{
  double x = 1, oldsum = 0, sum = 0;
  int i = 0;

  gsl_ieee_env_setup (); /* read GSL_IEEE_MODE */

  do
    {
      i++;

      oldsum = sum;
      sum += x;
      x = x / i;

      printf ("i=%2d sum=%.18f error=%g\n",
              i, sum, sum - M_E);

      if (i > 30)
         break;
    }
  while (sum != oldsum);

  return 0;
}


#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_eigen.h>

double f (double px, void * par) {

            double ep=-.9, ed=.0, tsp=1.63, tpd=1.13, tpp=.2;

            struct my_f_params {  double x; double y; double z; double v;};
            struct my_f_params * params  = (struct my_f_params *)par;

             double T2 = (params->x);
             double es = (params->y);
             double eF = (params->z);
             double py = (params->v);

                    double xi,argument;
                    double sx=2.*sin(px/2.),sy=2.*sin(py/2.);

                    double data[] = {   ed ,  0.   ,  tpd*sx   ,  -tpd*sy,
                                    0., es    ,  tsp*sx   ,   tsp*sy,
                                tpd*sx, tsp*sx,  ep       ,  -tpp*sx*sy,
                               -tpd*sy, tsp*sy, -tpp*sx*sy,   ep };

               gsl_matrix_view m = gsl_matrix_view_array (data, 4, 4);
               gsl_vector *eval = gsl_vector_alloc (4);
               gsl_matrix *evec = gsl_matrix_alloc (4, 4);
               gsl_eigen_symmv_workspace * w = gsl_eigen_symmv_alloc (4);
               gsl_eigen_symmv (&m.matrix, eval, evec, w);
               gsl_eigen_symmv_free (w);
               gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_VAL_ASC);

                     double e = gsl_vector_get (eval, 2);
                     gsl_vector_view evec_i   = gsl_matrix_column (evec, 2);
                     xi = gsl_vector_get(&evec_i.vector,0)*gsl_vector_get(&evec_i.vector,1);

                gsl_vector_free (eval);
                gsl_matrix_free (evec);

                double eeF=e-eF;
                if ( eeF == 0 )
                    argument=1./T2;
                else
                    argument=(tanh(eeF/T2))/eeF;
                    argument*=xi*xi;

	return argument;
}

double g(double py, void * p)
{
                struct my_f_params { double a; double b; double c; };
                struct my_f_params * params  = (struct my_f_params *)p;

                double T2 = (params->a);
                double es = (params->b);
                double eF = (params->c);

                //printf("%g %g %g\n",a,b,c);
                struct your_f_params { double x; double y; double z; double v; };

                struct your_f_params parametri = { T2, es , eF , py };

                double result, error;
                gsl_integration_workspace * w = gsl_integration_workspace_alloc (5000000);
                gsl_function F;

                F.function = &f;
                F.params = &parametri;

                gsl_integration_qags (&F, 0, 2*M_PI, 0, 1e-7, 5000000, w, &result, &error);
                gsl_integration_workspace_free (w);

return result;
}

double dvoenintegral(double T2, double es, double eF)
{
                double dvoenintegral, error;
                struct my_f_params { double a; double b; double c; };

                gsl_integration_workspace * v = gsl_integration_workspace_alloc (5000000);

                gsl_function F;
                struct my_f_params params = {T2, es , eF};

                F.function = &g;
                F.params = &params;

                gsl_integration_qags (&F, 0, 2*M_PI, 0, 1e-7, 5000000, v, &dvoenintegral, &error);
                gsl_integration_workspace_free (v);

return (dvoenintegral)/(2*M_PI*M_PI);
}

double optim(double L, void * pp)
{

                struct my_f_params { double a; double b; double c; };
                struct my_f_params * params  = (struct my_f_params *)pp;

                double Jsd = (params->a);
                double es = (params->b);
                double eF = (params->c);

        double TT=2.*exp(-L);
        double tmp=dvoenintegral(TT,es,eF);

return Jsd*tmp-1;
}

double nameri_nula( double Jsd, double es, double eF)
{
       int status;
       double Kelvin=1/11604.;
       struct my_f_params { double a; double b; double c; };
       struct my_f_params params = { Jsd, es , eF};
       int iter = 0, max_iter = 100;
       const gsl_root_fsolver_type *T;
       gsl_root_fsolver *s;
       double r = 0;
       double x_lo = 1E-6, x_hi = 7;
       gsl_function F;

       F.function = &optim;
       F.params = &params;

       T = gsl_root_fsolver_brent;
       s = gsl_root_fsolver_alloc (T);
       gsl_root_fsolver_set (s, &F, x_lo, x_hi);

       //printf ("using %s method\n", gsl_root_fsolver_name (s));

       //printf ("%5s [%9s, %9s] %9s %10s\n", "iter", "lower", "upper", "root", "err");

       do
         {
           iter++;
           status = gsl_root_fsolver_iterate (s);
           r = gsl_root_fsolver_root (s);
           x_lo = gsl_root_fsolver_x_lower (s);
           x_hi = gsl_root_fsolver_x_upper (s);
           status = gsl_root_test_interval (x_lo, x_hi, 0, 0.001);

           //if (status == GSL_SUCCESS)
           //printf ("Converged:\n");

           //printf ("%5d [%.7f, %.7f] %.7f %+.7f\n", iter, x_lo, x_hi, r, x_hi - x_lo);
         }
       while (status == GSL_CONTINUE && iter < max_iter);

	//printf("T_c=%g\n",exp(-r)/Kelvin);

       gsl_root_fsolver_free (s);

       return (2*exp(-r)/Kelvin);
}

double granichni_tochki(double a, double b,double es)
{

    double sx=2.*sin(a/2.);
    double sy=2.*sin(b/2.);

    double ep=-.9, ed=.0, tsp=1.63, tpd=1.13, tpp=.2;

    double data[] = {   ed ,  0.   ,  tpd*sx   ,  -tpd*sy,
                            0., es    ,  tsp*sx   ,   tsp*sy,
                        tpd*sx, tsp*sx,  ep       ,  -tpp*sx*sy,
                       -tpd*sy, tsp*sy, -tpp*sx*sy,   ep };

       gsl_matrix_view m = gsl_matrix_view_array (data, 4, 4);
       gsl_vector *eval = gsl_vector_alloc (4);
       gsl_matrix *evec = gsl_matrix_alloc (4, 4);
       gsl_eigen_symmv_workspace * w = gsl_eigen_symmv_alloc (4);
       gsl_eigen_symmv (&m.matrix, eval, evec, w);
       gsl_eigen_symmv_free (w);
       gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_VAL_ASC);

         gsl_vector_free (eval);
         gsl_matrix_free (evec);

         return ( gsl_vector_get (eval, 2) );
}

double pd(double e, double es)
{
    double ep=-.9, ed=.0, tsp=1.63, tpd=1.13, tpp=.2;
    double A,B,C,xd,eep,eed,ees,ppd;

eep=e-ep;
eed=e-ed;
ees=e-es;

A=16.*(4.*tpd*tpd*tsp*tsp + 2.*tsp*tsp*tpp*eed-2.*tpd*tpd*tpp*ees-tpp*tpp*eed*ees);
B=-4.*eep*(tsp*tsp*eed+tpd*tpd*ees);
C=ees*eep*eep*eed;
xd=(-B+sqrt(B*B-A*C))/A;
//printf("xd=%g\n",xd);
ppd=2*asin(sqrt(xd));
//printf("xd=%g ppd=%g e=%g es=%g\n",xd,ppd,e,es);
return ppd;
}

double fermi(double px, void * p)
{
                struct my_f_params { double a; double b; };
                struct my_f_params * params  = (struct my_f_params *)p;

                double eF = (params->a);
                double es = (params->b);

    double A,B,C,eep,eed,ees,e,F;
    double ep=-.9, ed=.0, tsp=1.63, tpd=1.13, tpp=.2;

    e=eF;
eep=e-ep;
eed=e-ed;
ees=e-es;

A=16.*(4.*tpd*tpd*tsp*tsp + 2.*tsp*tsp*tpp*eed-2.*tpd*tpd*tpp*ees-tpp*tpp*eed*ees);
B=-4.*eep*(tsp*tsp*eed+tpd*tpd*ees);
C=ees*eep*eep*eed;

double x=sin(px/2.)*sin(px/2.);
F=(px-2*asin(sqrt(- (C+B*x)/(A*x+B)  )));

return F;
}

double ffactor( double eF , double es )
{
                double res, error;
                struct my_f_params { double a; double b; };

                gsl_integration_workspace * v = gsl_integration_workspace_alloc (1000);

                gsl_function F;
                struct my_f_params params = {eF , es};

                F.function = &fermi;
                F.params = &params;

                gsl_integration_qag (&F, pd(eF,es) , M_PI , 0, 1e-7, 1000, 6 , v, &res, &error);
                gsl_integration_workspace_free (v);

return  (8*res)/(4*M_PI*M_PI) ;
}

double ffactornula( double eF , void * p )
{
                double res, error;
                double es = *(double *) p;

                struct my_f_params { double a; double b; };

                gsl_integration_workspace * v = gsl_integration_workspace_alloc (100000);

                gsl_function F;
                struct my_f_params params = {eF , es};

                F.function = &fermi;
                F.params = &params;

                gsl_integration_qags (&F, pd(eF,es) , M_PI , 0, 1e-7, 100000,  v, &res, &error);
                gsl_integration_workspace_free (v);

return  (8*res)/(4*M_PI*M_PI) - 0.66;
}

double ferminula( double evhs, double etop, double es)
{
    int status;

       int iter = 0, max_iter = 100;
       const gsl_root_fsolver_type *T;
       gsl_root_fsolver *s;
       double r = 0;
       double x_lo = evhs+1E-7, x_hi = etop-1E-7;
       gsl_function F;

       F.function = &ffactornula;
       F.params = &es;

       T = gsl_root_fsolver_brent;
       s = gsl_root_fsolver_alloc (T);
       gsl_root_fsolver_set (s, &F, x_lo, x_hi);

       //printf ("using %s method\n", gsl_root_fsolver_name (s));

       //printf ("%5s [%9s, %9s] %9s %10s\n", "iter", "lower", "upper", "root", "err");

       do
         {
           iter++;
           status = gsl_root_fsolver_iterate (s);
           r = gsl_root_fsolver_root (s);
           x_lo = gsl_root_fsolver_x_lower (s);
           x_hi = gsl_root_fsolver_x_upper (s);
           status = gsl_root_test_interval (x_lo, x_hi, 0, 0.001);

           //if (status == GSL_SUCCESS)
             //printf ("Converged:\n");

           //printf ("%5d [%.7f, %.7f] %.7f %+.7f\n", iter, x_lo, x_hi, r, x_hi - x_lo);
         }
       while (status == GSL_CONTINUE && iter < max_iter);

       gsl_root_fsolver_free (s);

       return r;
}

int main(void)
{
    double Kelvin=1/11604.;
    double es=6.0, eF=1.301531002,tsp=1.63,ep=-.9,tpd=1.13;
    double Jsd,Tc,evhs,etop,s,ss;
    //double tmp;
/*
    Jsd=1/dvoenintegral(90*Kelvin,es,eF);
    printf ("Jsd= %g\n",Jsd);
    Tc=nameri_nula(Jsd,es,eF);
    printf ("Tc= %g\n",Tc);
    evhs=granichni_tochki(0,M_PI,es);
    printf("evhs=%g\n",evhs);
    etop=granichni_tochki(M_PI,M_PI,es);
    printf("etop=%g\n",etop);
    ff=ffactor(evhs, es);
    printf("filling factor(evhs)=%g\n",ff);
    printf("%g\n",ferminula(evhs,etop,es));
*/
    Jsd=1/dvoenintegral(90*Kelvin,es,eF);

    for(es=5;es<8.5;es+=0.1)
    {
    evhs=granichni_tochki(0,M_PI,es);
    etop=granichni_tochki(M_PI,M_PI,es);
    eF=ferminula(evhs,etop,es);
    Tc=nameri_nula(Jsd,es,eF);
    //s=((es-eF)*(eF-ep))/(4*tsp*tsp);
    ss=((es-eF)*(eF-ep))/(4*tsp*tpd);
    //printf("Tc=%g es=%g eF=%g\n",Tc,es,eF);
    printf("%g %g\n",ss,log(Tc));
    //printf("%g %g\n",1/(2*(1+s)),Tc );
    }

    return 0;
}


//Gets line spectral frequencies (LSFs).
//from the autoregressive (AR) coefficients along cols or rows of X.

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <cblas.h>
#include <lapacke.h>
#include "cmp_ascend.c"

#ifdef __cplusplus
namespace ov {
extern "C" {
#endif

int ar2lsf_s (float *Y, const float *X, const int iscolmajor, const int R, const int C, const int dim);
int ar2lsf_d (double *Y, const double *X, const int iscolmajor, const int R, const int C, const int dim);
int ar2lsf_c (float *Y, const float *X, const int iscolmajor, const int R, const int C, const int dim);
int ar2lsf_z (double *Y, const double *X, const int iscolmajor, const int R, const int C, const int dim);

int ar2lsf_s (float *Y, const float *X, const int iscolmajor, const int R, const int C, const int dim)
{
    const float z = 0.0f, o = 1.0f;
    const int P = (dim==0) ? R : C;
    const int job = 'E', compz = 'N';  //eigenvalues only
    const lapack_int ldh = P+1, n = P+1, ldz = 1;
    const lapack_int ilo = 1, ihi = n;  //avoids balancing
    lapack_int info;
    float *poly, *prev, *omegas, *compan, *wr, *wi, zz[1];
    int r, c;

    //Checks
    if (R<1) { fprintf(stderr,"error in ar2lsf_s: nrows X must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in ar2lsf_s: ncols X must be positive\n"); return 1; }
    if (P<1) { fprintf(stderr,"error in ar2lsf_s: P (length of polynomial coeffs including a0=1) must be positive\n"); return 1; }

    //Allocate
    if (!(poly=(float *)malloc((size_t)(n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }
    if (!(prev=(float *)malloc((size_t)(n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }
    if (!(omegas=(float *)malloc((size_t)(2*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wr=(float *)malloc((size_t)(n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wi=(float *)malloc((size_t)(n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }
    if (!(compan=(float *)malloc((size_t)(n*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_s: problem with malloc. "); perror("malloc"); return 1; }

    if (dim==0)
    {
        for (c=0; c<C; c++)
        {
            if (iscolmajor) { cblas_scopy(n,&X[c*R],1,poly,1); }
            else { cblas_scopy(n,&X[c],C,poly,1); }
            poly[P] = z; prev[P] = -o;
            for (r=0; r<P; r++) { prev[r] = poly[P-r-1]; }
            for (r=0; r<n; r++) { poly[r] -= prev[r]; }
            cblas_scopy(n*n,&z,0,compan,1); cblas_scopy(P,&o,0,&compan[1],n+1); cblas_scopy(n,poly,1,compan,n);
            info = LAPACKE_shseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_s: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[r] = (wi[r]>10.0f*FLT_EPSILON) ? atan2f(wi[r],wr[r]) : 0.0f; poly[r] += prev[r] + prev[r]; }
            cblas_scopy(n*n,&z,0,compan,1); cblas_scopy(P,&o,0,&compan[1],n+1); cblas_scopy(n,poly,1,compan,n);
            info = LAPACKE_shseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_s: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[n+r] = (wi[r]>10.0f*FLT_EPSILON) ? atan2f(wi[r],wr[r]) : 0.0f; }
            qsort(omegas,(size_t)(2*n),sizeof(float),cmp_ascend_s);
            if (iscolmajor) { cblas_scopy(P,&omegas[n+1],1,&Y[c*P],1); }
            else { cblas_scopy(P,&omegas[n+1],1,&Y[c],C); }
        }
    }
    else if (dim==1)
    {
        for (r=0; r<R; r++)
        {
            if (iscolmajor) { cblas_scopy(n,&X[r],R,poly,1); }
            else { cblas_scopy(n,&X[r*C],1,poly,1); }
            poly[P] = z; prev[P] = -o;
            for (c=0; c<P; c++) { prev[c] = poly[P-c-1]; }
            for (c=0; c<n; c++) { poly[c] -= prev[c]; }
            cblas_scopy(n*n,&z,0,compan,1); cblas_scopy(P,&o,0,&compan[1],n+1); cblas_scopy(n,poly,1,compan,n);
            info = LAPACKE_shseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_s: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[c] = (wi[c]>10.0f*FLT_EPSILON) ? atan2f(wi[c],wr[c]) : 0.0f; poly[c] += prev[c] + prev[c]; }
            cblas_scopy(n*n,&z,0,compan,1); cblas_scopy(P,&o,0,&compan[1],n+1); cblas_scopy(n,poly,1,compan,n);
            info = LAPACKE_shseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_s: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[n+c] = (wi[c]>10.0f*FLT_EPSILON) ? atan2f(wi[c],wr[c]) : 0.0f; }
            qsort(omegas,(size_t)(2*n),sizeof(float),cmp_ascend_s);
            if (iscolmajor) { cblas_scopy(P,&omegas[n+1],1,&Y[r],R); }
            else { cblas_scopy(P,&omegas[n+1],1,&Y[r*P],1); }
        }
    }
    else
    {
        fprintf(stderr,"error in ar2lsf_s: dim must be 0 or 1.\n"); return 1;
    }

    //Exit
    free(compan); free(wr); free(wi); free(poly); free(prev); free(omegas);
    return 0;
}

int ar2lsf_d (double *Y, const double *X, const int iscolmajor, const int R, const int C, const int dim)
{
    const double z = 0.0, o = 1.0;
    const int P = (dim==0) ? R : C;
    const int job = 'E', compz = 'N';  //eigenvalues only
    const lapack_int ldh = P+1, n = P+1, ldz = 1;
    const lapack_int ilo = 1, ihi = n;  //avoids balancing
    lapack_int info;
    double *poly, *prev, *omegas, *compan, *wr, *wi, zz[1];
    int r, c;

    //Checks
    if (R<1) { fprintf(stderr,"error in ar2lsf_d: nrows X must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in ar2lsf_d: ncols X must be positive\n"); return 1; }
    if (P<1) { fprintf(stderr,"error in ar2lsf_d: P (length of polynomial coeffs including a0=1) must be positive\n"); return 1; }

    //Allocate
    if (!(poly=(double *)malloc((size_t)(n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }
    if (!(prev=(double *)malloc((size_t)(n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }
    if (!(omegas=(double *)malloc((size_t)(2*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wr=(double *)malloc((size_t)(n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wi=(double *)malloc((size_t)(n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }
    if (!(compan=(double *)malloc((size_t)(n*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_d: problem with malloc. "); perror("malloc"); return 1; }

    if (dim==0)
    {
        for (c=0; c<C; c++)
        {
            if (iscolmajor) { cblas_dcopy(n,&X[c*R],1,poly,1); }
            else { cblas_dcopy(n,&X[c],C,poly,1); }
            poly[P] = z; prev[P] = -o;
            for (r=0; r<P; r++) { prev[r] = poly[n-r]; }
            for (r=0; r<n; r++) { poly[r] -= prev[r]; }
            cblas_dcopy(n*n,&z,0,compan,1); cblas_dcopy(P,&o,0,&compan[1],n+1); cblas_dcopy(n,poly,1,compan,n);
            info = LAPACKE_dhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_d: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[r] = (wi[r]>10.0*DBL_EPSILON) ? atan2(wi[r],wr[r]) : 0.0; poly[r] += prev[r] + prev[r]; }
            cblas_dcopy(n*n,&z,0,compan,1); cblas_dcopy(P,&o,0,&compan[1],n+1); cblas_dcopy(n,poly,1,compan,n);
            info = LAPACKE_dhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_d: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[n+r] = (wi[r]>10.0*DBL_EPSILON) ? atan2(wi[r],wr[r]) : 0.0; }
            qsort(omegas,(size_t)(2*n),sizeof(double),cmp_ascend_d);
            if (iscolmajor) { cblas_dcopy(P,&omegas[n+1],1,&Y[c*P],1); }
            else { cblas_dcopy(P,&omegas[n+1],1,&Y[c],C); }
        }
    }
    else if (dim==1)
    {
        for (r=0; r<R; r++)
        {
            if (iscolmajor) { cblas_dcopy(n,&X[r],R,poly,1); }
            else { cblas_dcopy(n,&X[r*C],1,poly,1); }
            poly[P] = z; prev[P] = -o;
            for (c=0; c<P; c++) { prev[c] = poly[n-c]; }
            for (c=0; c<n; c++) { poly[c] -= prev[c]; }
            cblas_dcopy(n*n,&z,0,compan,1); cblas_dcopy(P,&o,0,&compan[1],n+1); cblas_dcopy(n,poly,1,compan,n);
            info = LAPACKE_dhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_d: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[c] = (wi[c]>10.0*DBL_EPSILON) ? atan2(wi[c],wr[c]) : 0.0; poly[c] += prev[c] + prev[c]; }
            cblas_dcopy(n*n,&z,0,compan,1); cblas_dcopy(P,&o,0,&compan[1],n+1); cblas_dcopy(n,poly,1,compan,n);
            info = LAPACKE_dhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,compan,ldh,wr,wi,zz,ldz);  //eig
            if (info) { fprintf(stderr,"error in ar2lsf_d: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[n+c] = (wi[c]>10.0*DBL_EPSILON) ? atan2(wi[c],wr[c]) : 0.0; }
            qsort(omegas,(size_t)(2*n),sizeof(double),cmp_ascend_d);
            if (iscolmajor) { cblas_dcopy(P,&omegas[n+1],1,&Y[r],R); }
            else { cblas_dcopy(P,&omegas[n+1],1,&Y[r*P],1); }
        }
    }
    else
    {
        fprintf(stderr,"error in ar2lsf_d: dim must be 0 or 1.\n"); return 1;
    }

    //Exit
    free(compan); free(wr); free(wi); free(poly); free(prev); free(omegas);
    return 0;
}

int ar2lsf_c (float *Y, const float *X, const int iscolmajor, const int R, const int C, const int dim)
{
    const float z[2] =  {0.0f,0.0f}, o[2] =  {1.0f,0.0f};
    const int P = (dim==0) ? R : C;
    const int job = 'E', compz = 'N';  //eigenvalues only
    const lapack_int ldh = P, n = P, ldz = 1;
    const lapack_int ilo = 1, ihi = n;  //avoids balancing
    lapack_int info;
    float *poly, *prev, *omegas, *compan, *wc, zz[2];
    int r, c;

    //Checks
    if (R<1) { fprintf(stderr,"error in ar2lsf_c: nrows X must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in ar2lsf_c: ncols X must be positive\n"); return 1; }
    if (P<1) { fprintf(stderr,"error in ar2lsf_c: P (length of polynomial coeffs including a0=1) must be positive\n"); return 1; }

    //Allocate
    if (!(poly=(float *)malloc((size_t)(2*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_c: problem with malloc. "); perror("malloc"); return 1; }
    if (!(prev=(float *)malloc((size_t)(2*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_c: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wc=(float *)malloc((size_t)(2*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_c: problem with malloc. "); perror("malloc"); return 1; }
    if (!(omegas=(float *)malloc((size_t)(2*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_c: problem with malloc. "); perror("malloc"); return 1; }
    if (!(compan=(float *)malloc((size_t)(4*n*n)*sizeof(float)))) { fprintf(stderr,"error in ar2lsf_c: problem with malloc. "); perror("malloc"); return 1; }

    if (dim==0)
    {
        for (c=0; c<C; c++)
        {
            if (iscolmajor) { cblas_ccopy(n,&X[2*c*R],1,poly,1); }
            else { cblas_ccopy(n,&X[2*c],C,poly,1); }
            poly[2*P] = poly[2*P+1] = prev[2*P+1] = 0.0f; prev[2*P] = -1.0f;
            for (r=0; r<P; r++) { prev[2*r] = poly[2*(P-r-1)]; prev[2*r+1] = poly[2*(P-r-1)+1]; }
            for (r=0; r<n; r++) { poly[2*r] -= prev[2*r]; poly[2*r+1] -= prev[2*r+1]; }
            cblas_ccopy(n*n,z,0,compan,1); cblas_ccopy(P,o,0,&compan[2],n+1); cblas_ccopy(n,poly,1,compan,n);
            info = LAPACKE_chseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_float *)compan,ldh,(lapack_complex_float *)wc,(lapack_complex_float *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_c: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[r] = atan2f(wc[2*r+1],wc[2*r]); poly[2*r] += prev[2*r] + prev[2*r]; poly[2*r+1] += prev[2*r+1] + prev[2*r+1]; }
            cblas_ccopy(n*n,z,0,compan,1); cblas_ccopy(P,o,0,&compan[2],n+1); cblas_ccopy(n,poly,1,compan,n);
            info = LAPACKE_chseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_float *)compan,ldh,(lapack_complex_float *)wc,(lapack_complex_float *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_c: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[n+r] = atan2f(wc[2*r+1],wc[2*r]); }
            qsort(omegas,(size_t)(2*n),sizeof(float),cmp_ascend_s);
            if (iscolmajor) { cblas_scopy(2*n,omegas,1,&Y[2*c*n],1); }
            else { cblas_scopy(2*n,omegas,1,&Y[c],C); }
        }
    }
    else if (dim==1)
    {
        for (r=0; r<R; r++)
        {
            if (iscolmajor) { cblas_ccopy(n,&X[2*r],R,poly,1); }
            else { cblas_ccopy(n,&X[2*r*C],1,poly,1); }
            poly[2*P] = poly[2*P+1] = prev[2*P+1] = 0.0f; prev[2*P] = -1.0f;
            for (c=0; c<P; c++) { prev[2*c] = poly[2*(P-c-1)]; prev[2*c+1] = poly[2*(P-c-1)+1]; }
            for (c=0; c<n; c++) { poly[2*c] -= prev[2*c]; poly[2*c+1] -= prev[2*c+1]; }
            cblas_ccopy(n*n,z,0,compan,1); cblas_ccopy(P,o,0,&compan[2],n+1); cblas_ccopy(n,poly,1,compan,n);
            info = LAPACKE_chseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_float *)compan,ldh,(lapack_complex_float *)wc,(lapack_complex_float *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_c: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[c] = atan2f(wc[2*c+1],wc[2*c]); poly[2*c] += prev[2*c] + prev[2*c]; poly[2*c+1] += prev[2*c+1] + prev[2*c+1]; }
            cblas_ccopy(n*n,z,0,compan,1); cblas_ccopy(P,o,0,&compan[2],n+1); cblas_ccopy(n,poly,1,compan,n);
            info = LAPACKE_chseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_float *)compan,ldh,(lapack_complex_float *)wc,(lapack_complex_float *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_c: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[n+c] = atan2f(wc[2*c+1],wc[2*c]); }
            qsort(omegas,(size_t)(2*n),sizeof(float),cmp_ascend_s);
            if (iscolmajor) { cblas_scopy(2*n,omegas,1,&Y[r],R); }
            else { cblas_scopy(2*n,omegas,1,&Y[2*r*n],1); }
        }
    }
    else
    {
        fprintf(stderr,"error in ar2lsf_c: dim must be 0 or 1.\n"); return 1;
    }

    //Exit
    free(compan); free(wc); free(poly); free(prev); free(omegas);
    return 0;
}

int ar2lsf_z (double *Y, const double *X, const int iscolmajor, const int R, const int C, const int dim)
{
    const double z[2] =  {0.0,0.0}, o[2] =  {1.0,0.0};
    const int P = (dim==0) ? R : C;
    const int job = 'E', compz = 'N';  //eigenvalues only
    const lapack_int ldh = P, n = P, ldz = 1;
    const lapack_int ilo = 1, ihi = n;  //avoids balancing
    lapack_int info;
    double *poly, *prev, *omegas, *compan, *wc, zz[2];
    int r, c;

    //Checks
    if (R<1) { fprintf(stderr,"error in ar2lsf_z: nrows X must be positive\n"); return 1; }
    if (C<1) { fprintf(stderr,"error in ar2lsf_z: ncols X must be positive\n"); return 1; }
    if (P<1) { fprintf(stderr,"error in ar2lsf_z: P (length of polynomial coeffs including a0=1) must be positive\n"); return 1; }

    //Allocate
    if (!(poly=(double *)malloc((size_t)(2*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_z: problem with malloc. "); perror("malloc"); return 1; }
    if (!(prev=(double *)malloc((size_t)(2*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_z: problem with malloc. "); perror("malloc"); return 1; }
    if (!(wc=(double *)malloc((size_t)(2*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_z: problem with malloc. "); perror("malloc"); return 1; }
    if (!(omegas=(double *)malloc((size_t)(2*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_z: problem with malloc. "); perror("malloc"); return 1; }
    if (!(compan=(double *)malloc((size_t)(4*n*n)*sizeof(double)))) { fprintf(stderr,"error in ar2lsf_z: problem with malloc. "); perror("malloc"); return 1; }

    if (dim==0)
    {
        for (c=0; c<C; c++)
        {
            if (iscolmajor) { cblas_zcopy(n,&X[2*c*R],1,poly,1); }
            else { cblas_zcopy(n,&X[2*c],C,poly,1); }
            poly[2*P] = poly[2*P+1] = prev[2*P+1] = 0.0; prev[2*P] = -1.0;
            for (r=0; r<P; r++) { prev[2*r] = poly[2*(P-r-1)]; prev[2*r+1] = poly[2*(P-r-1)+1]; }
            for (r=0; r<n; r++) { poly[2*r] -= prev[2*r]; poly[2*r+1] -= prev[2*r+1]; }
            cblas_zcopy(n*n,z,0,compan,1); cblas_zcopy(P,o,0,&compan[2],n+1); cblas_zcopy(n,poly,1,compan,n);
            info = LAPACKE_zhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_double *)compan,ldh,(lapack_complex_double *)wc,(lapack_complex_double *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_z: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[r] = atan2(wc[2*r+1],wc[2*r]); poly[2*r] += prev[2*r] + prev[2*r]; poly[2*r+1] += prev[2*r+1] + prev[2*r+1]; }
            cblas_zcopy(n*n,z,0,compan,1); cblas_zcopy(P,o,0,&compan[2],n+1); cblas_zcopy(n,poly,1,compan,n);
            info = LAPACKE_zhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_double *)compan,ldh,(lapack_complex_double *)wc,(lapack_complex_double *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_z: lapacke decomposition failed\n"); return 1; }
            for (r=0; r<n; r++) { omegas[n+r] = atan2(wc[2*r+1],wc[2*r]); }
            qsort(omegas,(size_t)(2*n),sizeof(double),cmp_ascend_d);
            if (iscolmajor) { cblas_dcopy(2*n,omegas,1,&Y[2*c*n],1); }
            else { cblas_dcopy(2*n,omegas,1,&Y[c],C); }
        }
    }
    else if (dim==1)
    {
        for (r=0; r<R; r++)
        {
            if (iscolmajor) { cblas_zcopy(n,&X[2*r],R,poly,1); }
            else { cblas_zcopy(n,&X[2*r*C],1,poly,1); }
            poly[2*P] = poly[2*P+1] = prev[2*P+1] = 0.0; prev[2*P] = -1.0;
            for (c=0; c<P; c++) { prev[2*c] = poly[2*(P-c-1)]; prev[2*c+1] = poly[2*(P-c-1)+1]; }
            for (c=0; c<n; c++) { poly[2*c] -= prev[2*c]; poly[2*c+1] -= prev[2*c+1]; }
            cblas_zcopy(n*n,z,0,compan,1); cblas_zcopy(P,o,0,&compan[2],n+1); cblas_zcopy(n,poly,1,compan,n);
            info = LAPACKE_zhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_double *)compan,ldh,(lapack_complex_double *)wc,(lapack_complex_double *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_z: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[c] = atan2(wc[2*c+1],wc[2*c]); poly[2*c] += prev[2*c] + prev[2*c]; poly[2*c+1] += prev[2*c+1] + prev[2*c+1]; }
            cblas_zcopy(n*n,z,0,compan,1); cblas_zcopy(P,o,0,&compan[2],n+1); cblas_zcopy(n,poly,1,compan,n);
            info = LAPACKE_zhseqr(LAPACK_COL_MAJOR,job,compz,n,ilo,ihi,(lapack_complex_double *)compan,ldh,(lapack_complex_double *)wc,(lapack_complex_double *)zz,ldz);
            if (info) { fprintf(stderr,"error in ar2lsf_z: lapacke decomposition failed\n"); return 1; }
            for (c=0; c<n; c++) { omegas[n+c] = atan2(wc[2*c+1],wc[2*c]); }
            qsort(omegas,(size_t)(2*n),sizeof(double),cmp_ascend_d);
            if (iscolmajor) { cblas_dcopy(2*n,omegas,1,&Y[r],R); }
            else { cblas_dcopy(2*n,omegas,1,&Y[2*r*n],1); }
        }
    }
    else
    {
        fprintf(stderr,"error in ar2lsf_z: dim must be 0 or 1.\n"); return 1;
    }

    //Exit
    free(compan); free(wc); free(poly); free(prev); free(omegas);
    return 0;
}

#ifdef __cplusplus
}
}
#endif


/* min/gsl_min.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007, 2009 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 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_MIN_H__
#define __GSL_MIN_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_types.h>
#include <gsl/gsl_math.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
  {
    const char *name;
    size_t size;
    int (*set) (void *state, gsl_function * f, double x_minimum, double f_minimum, double x_lower, double f_lower, double x_upper, double f_upper);
    int (*iterate) (void *state, gsl_function * f, double * x_minimum, double * f_minimum, double * x_lower, double * f_lower, double * x_upper, double * f_upper);
  }
gsl_min_fminimizer_type;

typedef struct
  {
    const gsl_min_fminimizer_type * type;
    gsl_function * function ;
    double x_minimum ;
    double x_lower ;
    double x_upper ;
    double f_minimum, f_lower, f_upper;
    void *state;
  }
gsl_min_fminimizer;

GSL_FUN gsl_min_fminimizer *
gsl_min_fminimizer_alloc (const gsl_min_fminimizer_type * T) ;

GSL_FUN void gsl_min_fminimizer_free (gsl_min_fminimizer * s);

GSL_FUN int gsl_min_fminimizer_set (gsl_min_fminimizer * s,
                            gsl_function * f, double x_minimum,
                            double x_lower, double x_upper);

GSL_FUN int gsl_min_fminimizer_set_with_values (gsl_min_fminimizer * s,
                                        gsl_function * f,
                                        double x_minimum, double f_minimum,
                                        double x_lower, double f_lower,
                                        double x_upper, double f_upper);

GSL_FUN int gsl_min_fminimizer_iterate (gsl_min_fminimizer * s);

GSL_FUN const char * gsl_min_fminimizer_name (const gsl_min_fminimizer * s);

GSL_FUN double gsl_min_fminimizer_x_minimum (const gsl_min_fminimizer * s);
GSL_FUN double gsl_min_fminimizer_x_lower (const gsl_min_fminimizer * s);
GSL_FUN double gsl_min_fminimizer_x_upper (const gsl_min_fminimizer * s);
GSL_FUN double gsl_min_fminimizer_f_minimum (const gsl_min_fminimizer * s);
GSL_FUN double gsl_min_fminimizer_f_lower (const gsl_min_fminimizer * s);
GSL_FUN double gsl_min_fminimizer_f_upper (const gsl_min_fminimizer * s);

/* Deprecated, use x_minimum instead */
GSL_FUN double gsl_min_fminimizer_minimum (const gsl_min_fminimizer * s);

GSL_FUN int
gsl_min_test_interval (double x_lower, double x_upper, double epsabs, double epsrel);

GSL_VAR const gsl_min_fminimizer_type  * gsl_min_fminimizer_goldensection;
GSL_VAR const gsl_min_fminimizer_type  * gsl_min_fminimizer_brent;
GSL_VAR const gsl_min_fminimizer_type  * gsl_min_fminimizer_quad_golden;

typedef
int (*gsl_min_bracketing_function)(gsl_function *f,
                                   double *x_minimum,double * f_minimum,
                                   double *x_lower, double * f_lower,
                                   double *x_upper, double * f_upper,
                                   size_t eval_max);

GSL_FUN int
gsl_min_find_bracket(gsl_function *f,double *x_minimum,double * f_minimum,
                     double *x_lower, double * f_lower,
                     double *x_upper, double * f_upper,
                     size_t eval_max);

__END_DECLS

#endif /* __GSL_MIN_H__ */


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

#define qh_QHimport
#include <libqhull_r/qhull_ra.h>

void print_summary(qhT *qh);
double findDelaunay(qhT *qh, int dim, coordT *TargetCoord);
double findNearestVertices(qhT *qh, int dim, coordT *TargetCoord);
double findNearestCentrum(qhT *qh, int dim, coordT *TargetCoord);

void obtain_convex_h_qhull(int n_data, double *ef2cvh, int *x, double *y, int NCatDim, int *nCat_min, int *nCat_max, double *E0, char *qhullflags) {

    int i, j, k, l, nid, Ndim;
    double tmp;

    Ndim = NCatDim - 1;     // because nV is dependent on other nCats
    printf("Convex hull is created using qhull.\n");
    printf("Input data\n");
    printf("---------------------------\n");
    printf("  nL   nN   nM   nC   nV    Ef\n");
    for (i=0;i<n_data;i++) {
        for (j=0;j<NCatDim;j++)
            printf("%4d ",x[NCatDim*i+j]);
        printf("  % .4f\n",y[i]);
    }
    printf("---------------------------\n");

    // Convex Hull by calling qhull
    int DIM;
    int SIZEcube, SIZEdiamond, TOTpoints;
    int dim;            /* dimension of points */
    int numpoints;      /* number of points */
    DIM = Ndim + 1;     /* because of ef as one additional dimension */
    dim = DIM;
    TOTpoints = n_data;
    numpoints= n_data;

//    coordT points[(DIM+1)*TOTpoints]; /* array of coordinates for each point */
    coordT points[DIM*TOTpoints]; /* array of coordinates for each point */
    coordT *rows[TOTpoints];
    boolT ismalloc= False;    /* True if qhull should free points in qh_freeqhull() or reallocation */
    char flags[250];          /* option flags for qhull, see qh-quick.htm */
    FILE *outfile= stdout;    /* output from qh_produce_output()
                               use NULL to skip qh_produce_output() */
    FILE *errfile= stderr;    /* error messages from qhull code */
    int exitcode;             /* 0 if no error from qhull */
    facetT *facet;            /* set by FORALLfacets */
    int curlong, totlong;     /* memory remaining after qh_memfreeshort */

    qhT qh_qh;                /* Qhull's data structure.  First argument of most calls */
    qhT *qh= &qh_qh;

    QHULL_LIB_CHECK

    qh_zero(qh, errfile);

    printf( "\ncompute %d-d convexhull, options:%s\n", dim, qhullflags);
//    sprintf(flags, "qhull o Qx ");     // Error generated
    sprintf(flags, "qhull o %s ",qhullflags);
//    sprintf(flags, "qhull o QJ ");
//    sprintf(flags, "qhull o C-0 ");     // Error generated
//    sprintf(flags, "qhull o C-n ");     // Error generated
//    sprintf(flags, "qhull o Cn ");     // Error generated
//    sprintf(flags, "qhull o Qt ");     // Error generated
//    sprintf(flags, "qhull o Qx C-n ");     // Error generated
    for (i=0;i<n_data;i++) {    //size of points is n_data by (Ndim+1)
        for (j=0;j<Ndim;j++)    //size of x is n_data by Ndim
            points[dim*i+j] = x[NCatDim*i+j];
        points[dim*i+Ndim] = y[i];
    }
    for (i=0;i<numpoints;i++)   //size of rows is n_data x (Ndim+1)
        rows[i]= points+dim*i;
//    for (i=numpoints; i--; )
//        rows[i]= points+dim*i;
    qh_printmatrix(qh, outfile, "input", rows, numpoints, dim);
    exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
                           flags, outfile, errfile);
    if (!exitcode) {                  /* if no error */
        print_summary(qh);
        printf( "\nThe relative Ef w.r.t. the convex hull:\n", dim);
        exitcode= setjmp(qh->errexit);
        if (!exitcode) {
            /* Trap Qhull errors in findDelaunay().  Without the setjmp(), Qhull
             will exit() after reporting an error */
            qh->NOerrexit= False;
            for (i=0;i<numpoints;i++) {
//                ef2cvh[i] = findDelaunay(qh, DIM, rows[i]);
//                ef2cvh[i] = findNearestVertices(qh, DIM, rows[i]);
                ef2cvh[i] = findNearestCentrum(qh, DIM, rows[i]);
                printf("ef2cvh[%d] = % .4e\n", i, ef2cvh[i]);
            }
        }
        qh->NOerrexit= True;
    }
    qh_freeqhull(qh, !qh_ALL);                 /* free long memory */
    qh_memfreeshort(qh, &curlong, &totlong);  /* free short memory and memory allocator */
    if (curlong || totlong)
        fprintf(errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
}

void print_summary(qhT *qh) {
    facetT *facet;
    vertexT *vertex;
    int k;

    printf("\n%d facets with normals:\n", qh->num_facets);
    FORALLfacets {
        for (k=0; k < qh->hull_dim; k++) {
            printf("%6.2g ", facet->normal[k]);
//            printf("", facet->vertices);
        }
        printf("\n");
    }
    printf("\n%d vertices:\n",qh->num_vertices);
    FORALLvertices {
        qh_printvertex(qh, qh->fout, vertex);
    }
}

double findDelaunay(qhT *qh, int dim, coordT *TargetCoord) {
    int j, k;
    coordT tmppoint[ 100];
    boolT isoutside;
    realT bestdist;
    facetT *facet;
    vertexT *vertex, **vertexp;

    for (k= 0; k < dim; k++)
        tmppoint[k]= TargetCoord[k];
    qh_setdelaunay(qh, dim+1, 1, tmppoint);
    facet= qh_findbestfacet(qh, tmppoint, qh_ALL, &bestdist, &isoutside);
    if (facet->tricoplanar) {
        fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
                facet->id);
        qh_errexit(qh, qh_ERRqhull, facet, NULL);
    }

    for (k= 0; k < dim-1; k++)
        printf("% .3f ",tmppoint[k]);
    printf("% .3f, ",tmppoint[dim-1]);
    printf("facet id: %d\n",facet->id);
/*    FOREACHvertex_(facet->vertices) {
        for (k=0; k < dim; k++)
            printf("%5.3f ", vertex->point[k]);
        printf("vertex id: %d, point id: %d\n", vertex->id, qh_pointid(qh, vertex->point));
    }*/
    FOREACHvertex_(facet->vertices) {
        printf("vertex id: %d, point id: %d\n", vertex->id, qh_pointid(qh, vertex->point));
    }

    gsl_matrix * A = gsl_matrix_alloc (dim,dim);
    gsl_matrix * V = gsl_matrix_alloc (dim,dim);
    gsl_vector * S = gsl_vector_alloc (dim);
    gsl_vector * work = gsl_vector_alloc (dim);
    gsl_vector * b = gsl_vector_alloc (dim);
    gsl_vector * x = gsl_vector_alloc (dim);

    j= 0;
    FOREACHvertex_(facet->vertices) {
        for (k= 0; k < dim; k++)
            gsl_matrix_set (A,k,j, vertex->point[k]);
        gsl_vector_set(b,j,tmppoint[j]);
        j++;
    }
    double ef[dim];
    // dismiss the last row corresponding to Ef
    // & apply the constriction on the coefficients sum_x_i = 1
    for (k= 0; k < dim; k++) {
        ef[k] = gsl_matrix_get (A,dim-1,k);
        gsl_matrix_set (A,dim-1,k,1.0);
    }
    gsl_vector_set(b,dim-1,2.0);

    gsl_linalg_SV_decomp(A, V, S, work);
    gsl_linalg_SV_solve (A, V, S, b, x);

    double ef_cvh = 0.0;
    for (k= 0; k < dim; k++)
        ef_cvh += gsl_vector_get(x,k)*ef[k];

    gsl_matrix_free(A);
    gsl_matrix_free(V);
    gsl_vector_free(S);
    gsl_vector_free(work);
    gsl_vector_free(b);
    gsl_vector_free(x);

    return(TargetCoord[dim-1]-ef_cvh);
}

double findNearestVertices(qhT *qh, int dim, coordT *TargetCoord) {
    int i, j, k;
    int nid;
    double ef_cvh;
    double tmp;
    double tmppoint[dim];
    boolT isoutside;
    realT bestdist;
    facetT *facet;
    vertexT *vertex, **vertexp;

    nid = qh->num_vertices;
    for (k= 0; k < dim; k++)
        tmppoint[k]= TargetCoord[k];

    double verticespoint[nid][dim];
    j = 0;
    FORALLvertices {
        for (k= 0; k < dim; k++)
            verticespoint[j][k] = vertex->point[k];
        j++;
    }

    double dij2[nid];
    int ids[nid];
    for (j= 0; j< nid; j++) {
        tmp = 0.0;
        for (k= 0; k < dim-1; k++) {    //ef is excluded for configurational coordinate
            tmp += pow(verticespoint[j][k] - tmppoint[k],2);
        }
        dij2[j] = tmp;
        ids[j] = j;
    }
    sort_array_ids(dij2,ids,nid);

    gsl_matrix * A = gsl_matrix_alloc (dim,dim);
    gsl_matrix * V = gsl_matrix_alloc (dim,dim);
    gsl_vector * S = gsl_vector_alloc (dim);
    gsl_vector * work = gsl_vector_alloc (dim);
    gsl_vector * b = gsl_vector_alloc (dim);
    gsl_vector * x = gsl_vector_alloc (dim);

    if (dij2[0]==0.0)
        ef_cvh = 0.0;
    else {
        for (j= 0; j< dim; j++) {
            for (k= 0; k < dim; k++)
                gsl_matrix_set (A,k,j,verticespoint[ids[j]][k]);
            gsl_vector_set(b,j,tmppoint[j]);
        }
        for (k= 0; k < dim; k++) {      //dismiss the last row corresponding to ef
            gsl_matrix_set (A,dim-1,k,1.0);
        }
        gsl_vector_set(b,dim-1,2.0);    //dismiss the last row corresponding to ef
        gsl_linalg_SV_decomp(A, V, S, work);
        gsl_linalg_SV_solve (A, V, S, b, x);
        tmp = 0.0;
        for (j= 0; j< dim; j++)
            tmp += gsl_vector_get(x,j)*verticespoint[ids[j]][dim-1];
        ef_cvh = tmppoint[dim-1]-tmp;
    }

    gsl_matrix_free(A);
    gsl_matrix_free(V);
    gsl_vector_free(S);
    gsl_vector_free(work);
    gsl_vector_free(b);
    gsl_vector_free(x);

    return(ef_cvh);
}

double findNearestCentrum(qhT *qh, int dim, coordT *TargetCoord) {
    int i, j, k;
    int nf, nv;
    double ef_cvh;
    double tmp;
    double tmppoint[dim];
    boolT isoutside;
    realT bestdist;
    facetT *facet;
    vertexT *vertex, **vertexp;
    pointT *centrum;

    nf = qh->num_facets;
    nv = qh->num_vertices;

    for (k= 0; k < dim; k++)
        tmppoint[k]= TargetCoord[k];

    double facet_normal[nf][dim];
    double facet_center[nf][dim];
    int facet_id[nf];
    int num_lowerhullfacets;

    j = 0;
    FORALLfacets {
        if (facet->normal[dim-1] < 0.0) {//is the slope in ef direction negative?
            for (k= 0; k < dim; k++) {
                facet_normal[j][k] = facet->normal[k];
                if (qh->CENTERtype == qh_AScentrum)
                    centrum= facet->center;
                else
                    centrum= qh_getcentrum(qh, facet);
                facet_center[j][k] = centrum[k];
                facet_id[j] = facet->id;
            }
            j++;
        }
    }
    num_lowerhullfacets = j;

    double dij2[num_lowerhullfacets];
    int ids[num_lowerhullfacets];
    for (j= 0; j< num_lowerhullfacets; j++) {
        tmp = 0.0;
        for (k= 0; k < dim-1; k++) {    //ef is excluded for configurational coordinate
            tmp += pow(facet_center[j][k] - tmppoint[k],2);
        }
        dij2[j] = tmp;
        ids[j] = j;
    }
    sort_array_ids(dij2,ids,num_lowerhullfacets);

    int projectedfacet = facet_id[ids[0]];
    double vertices_point[dim][dim];
    FORALLfacets {
        if (facet->id == projectedfacet) {
            j = 0;
            FOREACHvertex_(facet->vertices) {
                for (k=0; k < dim; k++)
                    vertices_point[j][k] = vertex->point[k];
                j++;
            }
        }
    }

    gsl_matrix * A = gsl_matrix_alloc (dim,dim);
    gsl_matrix * V = gsl_matrix_alloc (dim,dim);
    gsl_vector * S = gsl_vector_alloc (dim);
    gsl_vector * work = gsl_vector_alloc (dim);
    gsl_vector * b = gsl_vector_alloc (dim);
    gsl_vector * x = gsl_vector_alloc (dim);

    for (j= 0; j< dim; j++) {
        for (k= 0; k < dim; k++)
            gsl_matrix_set (A,k,j,vertices_point[j][k]);
        gsl_vector_set(b,j,tmppoint[j]);
    }
    for (k= 0; k < dim; k++) {      //dismiss the last row corresponding to ef
        gsl_matrix_set (A,dim-1,k,1.0);
    }
    gsl_vector_set(b,dim-1,1.0);    //dismiss the last row corresponding to ef

    printf("A:\n");
    for (j= 0; j< dim; j++) {
        for (k= 0; k < dim; k++)
            printf("%g ", gsl_matrix_get (A,j,k));
        printf("\n");
    }
    printf("b:\n");
    for (j= 0; j< dim; j++)
        printf("%g\n", gsl_vector_get(b,j));

    gsl_linalg_SV_decomp(A, V, S, work);
    gsl_linalg_SV_solve (A, V, S, b, x);
    printf("solution:\n");
    for (j= 0; j< dim; j++)
        printf("%g\n", gsl_vector_get(x,j));
    // need to check if the components of x is in [0,2]
    for (j= 0; j< dim; j++) {
        tmp = gsl_vector_get(x,j);
        if (tmp < -0.1 || tmp > 1.0) {
            printf("\n%d-component of x is %f, out of range\n", j, tmp);
            exit(-1);
        }
    }
    tmp = 0.0;
    for (j= 0; j< dim; j++)
        tmp += gsl_vector_get(x,j)*vertices_point[j][dim-1];
    ef_cvh = tmppoint[dim-1]-tmp;

    gsl_matrix_free(A);
    gsl_matrix_free(V);
    gsl_vector_free(S);
    gsl_vector_free(work);
    gsl_vector_free(b);
    gsl_vector_free(x);

    return(ef_cvh);
}