#include <math.h>
#include "gbem_util.h"

void dgemm_(char *transa,char *transb,const int *m,const int *n,const int *k,
            double *alpha,double *a,const int *lda,double *b,const int *ldb,double *beta,double *c,const int *ldc);
void qsing(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r);
void qsepp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double* int_r);
void qsesp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r);
void nsing(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r);
void nsepp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r);
void nsesp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r);


void gbem_lap_iex_int(int elP, int elQ)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/***************************************************  Non-singular integration  ***************************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */

 
/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * elP == (input) Global index of the outer boundary element P
 * elQ == (input) Global index of the inner boundary element Q
 */

/* Global variables */
   extern const int mx_nd_elt;   /* mx_nd_elt == Maximum number of nodes per element */
   extern int nbnd;              /* nbnd == Total number of boundary nodes */
   extern int **belt;            /* belt[i][0:2] == Global indices of boundary nodes on the i-th element */
   extern int *pp, *qq;          /* pp[k] == Global index of the k-th node of the boundary element P */
                                 /* qq[k] == Global index of the k-th node of the boundary element Q */
   extern double pi;             /* pi = 3.1415926535897932 */
   extern double **bnd;          /* bnd[i][0:2] = Cartesian coordinates of the i-th boundary node */
   extern double **xeP, **xeQ;   /* xeP[k][0:2] == Cartesian coordinates of the k-th node of element P */
			         /* xeQ[k][0:2] == Cartesian coordinates of the k-th node of element Q */
   extern double DistPQ;         /* DistPQ == Distance between element P and element Q defined as
			                      minimum distance between vertices of element P and vertices of element Q */
   extern int PQmLocId[2];       /* PQmLocId[0] == Local index of the vertex of element P at which DistPQ is attained */
			         /* PQmLocId[1] == Local index of the vertex of element Q at which DistPQ is attained */
   extern double *EltDiam;       /* EltDiam[k] == Diameter of the k-th boundary element */
   extern double **LocCBasisElt; /* LocCBasisElt[k][0:8] == Local Companion Basis associated with the k-th boundary element */
   extern double **thetaElt;     /* thetaElt[k][i] == Angle at the i-th vertex of the k-th boundary element */
   extern double *int_p, *int_f; /* int_p == Local contribution for potential influence matrix */
				 /* int_f == Local contribution for flux influence matrix */
   extern double *int_r;         /* int_r == Local contribution for potential/flux influence matrix */
   extern double *h, *g;         /* h == Potential influence matrix, g == Flux influence matrix */

/* Local variables */
   int k,i,row,col,j,cl_i,cl_i_loc;
   int PminVertex,QminVertex;
   double e1P[3],e2P[3],e3P[3]; /* Orthonormal companion basis associated with element P */
   double e1Q[3],e2Q[3],e3Q[3]; /* Orthonormal companion basis associated with element Q */
   double e1[3],e2[3]; /* Orthonormal local basis */
   double aP,bP,cP; /* Geometric parameters of element P: bP = base, aP = height, 
 		       cP = relative position of the 3rd vertex of element P in the local basis {e1P,e2P,e3P} */
   double aQ,bQ,cQ; /* Geometric parameters of element Q: bQ = base, aQ = height,
 		       cQ = relative position of the 3rd vertex of element Q in the local basis {e1Q,e2Q,e3Q} */
   double QPTransitionMtrx[3][3],Diam;
   double vij[2][3],theta[3],alpha[3],alphaEltP[3],calpk,salpk,x,Dpq[3],DstPQ[3];



    PminVertex = PQmLocId[0]; QminVertex = PQmLocId[1]; /* The distance between element P and element Q is equal to the distance          */
						        /* between Vertex "PminVertex" of element P and  Vertex "QminVertex" of element Q */


/*** Geometry of the inner element Q */
    for (k=0; k<mx_nd_elt; k++) /* Loop over the nodes of element Q */
    {
       qq[k] = belt[elQ][k]; /* qq[k] == Global index of the k-th node of the inner boundary element Q */
       for (i=0; i<3; i++) /* Loop over all components */
          xeQ[k][i] = bnd[qq[k]][i]; /* xeQ[k][0:2] == Cartesian coordinates of the k-th node of element Q */
    }

     e1[0] = LocCBasisElt[elQ][0];  e1[1] = LocCBasisElt[elQ][1];  e1[2] = LocCBasisElt[elQ][2];
     e2[0] = LocCBasisElt[elQ][3];  e2[1] = LocCBasisElt[elQ][4];  e2[2] = LocCBasisElt[elQ][5];
    e3Q[0] = LocCBasisElt[elQ][6]; e3Q[1] = LocCBasisElt[elQ][7]; e3Q[2] = LocCBasisElt[elQ][8];

    theta[0] = thetaElt[elQ][0]; theta[1] = thetaElt[elQ][1]; theta[2] = thetaElt[elQ][2];

    alpha[0] = 0.0; alpha[1] = pi-theta[1]; alpha[2] = pi+theta[0];

/*** Reorder the node numbers of element Q so that the node with local index 0 coincides with the node of element Q at */
/*** which the distance DistPQ is attained, i.e., consider the permutation of node numbers of the inner element Q      */
     if (QminVertex == 0) /* {0,1,2} ==> {0,1,2} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {e1Q[k] = e1[k]; e2Q[k] = e2[k];}
     }
     else if(QminVertex == 1) /* {0,1,2} ==> {1,2,0} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {
	     x = xeQ[0][k];
	     xeQ[0][k] = xeQ[1][k];
	     xeQ[1][k] = xeQ[2][k];
	     xeQ[2][k] = x;
	  }
	  k = qq[0];     x = theta[0];
	  qq[0] = qq[1]; theta[0] = theta[1];
	  qq[1] = qq[2]; theta[1] = theta[2];
	  qq[2] = k;     theta[2] = x;

	calpk = cos(alpha[1]); salpk = sin(alpha[1]);

        for (k=0; k<3; k++) /* loop over all components */
	{
           e1Q[k] = calpk*e1[k] + salpk*e2[k];
           e2Q[k] = calpk*e2[k] - salpk*e1[k];
        }
     }
     else /* QminVertex == 2: {0,1,2} ==> {2,0,1} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {
	     x = xeQ[0][k];
	     xeQ[0][k] = xeQ[2][k];
	     xeQ[2][k] = xeQ[1][k];
	     xeQ[1][k] = x;
	  }
	  k = qq[0];     x = theta[0];
	  qq[0] = qq[2]; theta[0] = theta[2];
	  qq[2] = qq[1]; theta[2] = theta[1];
	  qq[1] = k;     theta[1] = x;

	calpk = cos(alpha[2]); salpk = sin(alpha[2]);

        for (k=0; k<3; k++) /* loop over all components */
	{
           e1Q[k] = calpk*e1[k] + salpk*e2[k];
           e2Q[k] = calpk*e2[k] - salpk*e1[k];
        }
     }

    for (k=0; k<3; k++) /* Loop over all components */
    {
       vij[0][k] = xeQ[1][k] - xeQ[0][k]; /* y2-y1 <== Side 1 of element Q */
       vij[1][k] = xeQ[2][k] - xeQ[0][k]; /* y3-y1 <== Side 3 of element Q */
    }

    bQ = vij[0][0]*e1Q[0] + vij[0][1]*e1Q[1] + vij[0][2]*e1Q[2]; /* bQ>0.0 always */
    aQ = vij[1][0]*e2Q[0] + vij[1][1]*e2Q[1] + vij[1][2]*e2Q[2]; /* aQ>0.0 always */
    cQ = vij[1][0]*e1Q[0] + vij[1][1]*e1Q[1] + vij[1][2]*e1Q[2];


/*** Geometry of the outer element P */
    for (k=0; k<mx_nd_elt; k++) /* Loop over the nodes of element P */
    {
       pp[k] = belt[elP][k]; /* pp[k] == Global index of the k-th node of the outer boundary element P */
       for (i=0; i<3; i++) /* Loop over all components */
	  xeP[k][i] = bnd[pp[k]][i]; /* xeP[k][0:2] == Cartesian coordinates of the k-th node of element P */
    }

     e1[0] = LocCBasisElt[elP][0];  e1[1] = LocCBasisElt[elP][1];  e1[2] = LocCBasisElt[elP][2];
     e2[0] = LocCBasisElt[elP][3];  e2[1] = LocCBasisElt[elP][4];  e2[2] = LocCBasisElt[elP][5];
    e3P[0] = LocCBasisElt[elP][6]; e3P[1] = LocCBasisElt[elP][7]; e3P[2] = LocCBasisElt[elP][8];

    alphaEltP[0] = 0.0; alphaEltP[1] = pi-thetaElt[elP][1]; alphaEltP[2] = pi+thetaElt[elP][0];

/*** Reorder the node numbers of element P so that the node with local index 0 coincides with the node of element P at */
/*** which the distance DistPQ is attained, i.e., consider the permutation of node numbers of the outer element P      */
     if (PminVertex == 0) /* {0,1,2} ==> {0,1,2} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {e1P[k] = e1[k]; e2P[k] = e2[k];}
     }
     else if(PminVertex == 1) /* {0,1,2} ==> {1,2,0} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {
	     x = xeP[0][k];
	     xeP[0][k] = xeP[1][k];
	     xeP[1][k] = xeP[2][k];
	     xeP[2][k] = x;
	  }
	  k = pp[0];
	  pp[0] = pp[1];
	  pp[1] = pp[2];
	  pp[2] = k;

        calpk = cos(alphaEltP[1]); salpk = sin(alphaEltP[1]);

        for (k=0; k<3; k++) /* loop over all components */
	{
           e1P[k] = calpk*e1[k] + salpk*e2[k];
           e2P[k] = calpk*e2[k] - salpk*e1[k];
        }
     }
     else /* PminVertex == 2: {0,1,2} ==> {2,0,1} */
     {
	  for (k=0; k<3; k++) /* Loop over all components */
	  {
	     x = xeP[0][k];
	     xeP[0][k] = xeP[2][k];
	     xeP[2][k] = xeP[1][k];
	     xeP[1][k] = x;
	  }
	  k = pp[0];
	  pp[0] = pp[2];
	  pp[2] = pp[1];
	  pp[1] = k;

        calpk = cos(alphaEltP[2]); salpk = sin(alphaEltP[2]);

        for (k=0; k<3; k++) /* loop over all components */
	{
           e1P[k] = calpk*e1[k] + salpk*e2[k];
           e2P[k] = calpk*e2[k] - salpk*e1[k];
        }
     }

    for (k=0; k<3; k++) /* Loop over all components */
    {
       vij[0][k] = xeP[1][k] - xeP[0][k]; /* x2-x1 <== Side 1 of element P */
       vij[1][k] = xeP[2][k] - xeP[0][k]; /* x3-x1 <== Side 3 of element P */
    }

    bP = vij[0][0]*e1P[0] + vij[0][1]*e1P[1] + vij[0][2]*e1P[2]; /* bP>0.0 always */
    aP = vij[1][0]*e2P[0] + vij[1][1]*e2P[1] + vij[1][2]*e2P[2]; /* aP>0.0 always */
    cP = vij[1][0]*e1P[0] + vij[1][1]*e1P[1] + vij[1][2]*e1P[2];
    

/*** C_ij == Transition matrix from {e1Q,e2Q,e3Q} to {e1P,e2P,e3P} */
/*** Projection (Transformation) of e1P to coordinate system {e1Q,e2Q,e3Q} local to elQ */
          QPTransitionMtrx[0][0] = e1P[0]*e1Q[0] + e1P[1]*e1Q[1] + e1P[2]*e1Q[2]; /* C_11 */
          QPTransitionMtrx[0][1] = e1P[0]*e2Q[0] + e1P[1]*e2Q[1] + e1P[2]*e2Q[2]; /* C_21 */
	  QPTransitionMtrx[0][2] = e1P[0]*e3Q[0] + e1P[1]*e3Q[1] + e1P[2]*e3Q[2]; /* C_31 */

/*** Projection (Transformation) of e2P to coordinate system {e1Q,e2Q,e3Q} local to elQ */
          QPTransitionMtrx[1][0] = e2P[0]*e1Q[0] + e2P[1]*e1Q[1] + e2P[2]*e1Q[2]; /* C_12 */
          QPTransitionMtrx[1][1] = e2P[0]*e2Q[0] + e2P[1]*e2Q[1] + e2P[2]*e2Q[2]; /* C_22 */
	  QPTransitionMtrx[1][2] = e2P[0]*e3Q[0] + e2P[1]*e3Q[1] + e2P[2]*e3Q[2]; /* C_32 */

/*** Projection (Transformation) of e3P to coordinate system {e1Q,e2Q,e3Q} local to elQ */
          QPTransitionMtrx[2][0] = e3P[0]*e1Q[0] + e3P[1]*e1Q[1] + e3P[2]*e1Q[2]; /* C_13 */
          QPTransitionMtrx[2][1] = e3P[0]*e2Q[0] + e3P[1]*e2Q[1] + e3P[2]*e2Q[2]; /* C_23 */
	  QPTransitionMtrx[2][2] = e3P[0]*e3Q[0] + e3P[1]*e3Q[1] + e3P[2]*e3Q[2]; /* C_33 */

/*** Dpq = Vector characterizing the distance between element P and element Q in the Global Cartesian Coordinates */
    for (k=0; k<3; k++) /* Loop over all components */
       Dpq[k] = xeP[0][k] - xeQ[0][k];
    
/*** DstPQ = Vector characterizing the distance between element P and element Q in the local Coordinates associated with element Q */
    DstPQ[0] = Dpq[0]*e1Q[0] + Dpq[1]*e1Q[1] + Dpq[2]*e1Q[2];
    DstPQ[1] = Dpq[0]*e2Q[0] + Dpq[1]*e2Q[1] + Dpq[2]*e2Q[2];
    DstPQ[2] = Dpq[0]*e3Q[0] + Dpq[1]*e3Q[1] + Dpq[2]*e3Q[2];

    Diam = fmax(EltDiam[elP],EltDiam[elQ]); /* Diam = max Diameter of elements {P,Q} */


	if (DistPQ/Diam < 1.0)
           qsing(aP,bP,cP,aQ,bQ,cQ,theta,DstPQ,QPTransitionMtrx,int_p,int_f,int_r); /* Integration for Quasi-Singular elements */
	else
           nsing(aP,bP,cP,aQ,bQ,cQ,theta,DstPQ,QPTransitionMtrx,int_p,int_f,int_r); /* Integration for Well-separated elements */

        for (col=0; col<mx_nd_elt; col++) /* Loop over the nodes of element Q */
        {
           cl_i_loc = mx_nd_elt*col; /* cl_i_loc == Local Column stride */
           cl_i = nbnd*qq[col]; /* cl_i == Column stride */

           for (row=0; row<mx_nd_elt; row++) /* Loop over the nodes of element P */
	   {
              k = pp[row] + cl_i; /* Column-major order in h[nbnd][nbnd] and g[nbnd][nbnd] matrices */
	      i = row + cl_i_loc; /* Column-major order in int_p, int_f and int_r matrices viewed as mx_nd_elt by mx_nd_elt matrices */
	      h[k] += int_p[i];
	      g[k] += int_f[i];

/*** Reverse Kernel */
	      j = qq[col] + nbnd*pp[row]; /* nbnd*pp[row] == Column stride for Reverse kernel */
	      h[j] += int_r[i];
	      g[j] += int_f[i];
           }
        }

}


void qsing(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/**************************************  Quasi-singular integration for separated elements  ***************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi;           /* pi = 3.1415926535897932 */
   extern double gm_eps,f_eps; /* gm_eps == Tolerance on the geometry, f_eps == Tolerance on the argument of a function */
   extern int ng2;       /* ng2 == Number of Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double *xi2;   /* xi2 == Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double **wwg2; /* wwg2[i][k] = wg2[i]*wg2[k], where wg2[k] is the k-th Gauss-Legendre weight factor corresponding to xi2[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,th_id,cl_k,dabh,kp1;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c31,c12,c22,c32,c13,c23,c33,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,rh1s,rh2s,rh3s,p11s,p12s,p33s,ch33,ch31;
   double alpha2,alpha3,x,y,xh,zh,et,eth,ets,etx2,p11rh1,p31rh1,c13cf,c23cf,omega,cmb,xy,dlt,xww,xwpc,chis,chic,alp,bet;
   double cr11,cr12,zn1,zd1,cr22,cr23,zn2,zd2,cr31,cr33,zn3,zd3,p12rh2,p22rh2,p23rh3,p33rh3,theta0,ThetGam,etThetGam;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],rho[3],rhowv[3],qrhowv[3],chi[3],qchi[3],dchi[3],etchi[3],d[3],q[3],qs[3],qetx2[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt],intGamwv[mx_nd_elt],gamma[3];
   double intR[mx_nd_elt*2],intGN3tmp[smx_nd_elt]; /* Column-major order in intR[mx_nd_elt][2], intGN3tmp[mx_nd_elt][mx_nd_elt] */
   double intA[mx_nd_elt*3],intD[mx_nd_elt*3];     /* Column-major order in   intA[mx_nd_elt][3],   intD[mx_nd_elt][3] */
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt],intGam[smx_nd_elt]; /* intOme,intGam,intGK1,intGN1,intGK2,intGN2,intGK3,intGN3 are viewed as */
   double intGK1[smx_nd_elt],intGN1[smx_nd_elt]; /* Column-major ordered mx_nd_elt by mx_nd_elt matrices                  */
   double intGK2[smx_nd_elt],intGN2[smx_nd_elt];
   double intGK3[smx_nd_elt],intGN3[smx_nd_elt];
/* pt_tria_tst, gammai, asym_chi are external functions (see gbem_util.c) */



     c11 = QPTM[0][0]; c21 = QPTM[0][1]; c31 = QPTM[0][2];
     c12 = QPTM[1][0]; c22 = QPTM[1][1]; c32 = QPTM[1][2];
     c13 = QPTM[2][0]; c23 = QPTM[2][1]; c33 = QPTM[2][2];

     if ( ((fabs(c13) < gm_eps) && (fabs(c23) < gm_eps)) || ((fabs(c31) < gm_eps) && (fabs(c32) < gm_eps)) )
     {
          qsepp(a,b,c,aQ,bQ,cQ,theta,eps,QPTM,int_p,int_f,int_r); /* Integration for Quasi-Singular elements on parallel planes */
	  return;
     }

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */

     for (k=0; k<mx_nd_elt; k++) intGamwv[k] = 0.0;

     for (k=0; k<smx_nd_elt; k++)
     {
        intclN[k] = 0.0; intclM[k] = 0.0;   intA[k] = 0.0;
	intOme[k] = 0.0; intGam[k] = 0.0; intGK1[k] = 0.0; intGK2[k] = 0.0; intGK3[k] = 0.0; intGN3tmp[k] = 0.0; 
     }

     for (k=0; k<2*mx_nd_elt; k++) intR[k] = 0.0;

     for (i=0; i<ng2; i++) /* Loop over y = xi2[i] */
     {
	y = xi2[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh); eth = -(c31*xh + c32*zh);
	
        for (k=0; k<ng2; k++) /* Loop over x = xi2[k] */
	{
	   x = xi2[k];
           xww = x*wwg2[i][k];
	   
	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1]; et = x*eth-eps[2];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   ets = et*et; p11s = p11*p11; p12s = p12*p12; p33s = p33*p33; qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];

	   d[0] = qs[0] + ets; d[1] = qs[1] + ets; d[2] = qs[2] + ets;
	   
	   rh1s = p11s + d[0]; rh2s = p12s + d[0]; rh3s = p33s + d[2];
	   rho[0] = sqrt(rh1s); rho[1] = sqrt(rh2s); rho[2] = sqrt(rh3s);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y; etx2 = 2.0*et;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   if ( fabs(shc[0]) < gm_eps ) {shc[0] = 0.0; q[1] = 0.0;}
	   
	   if ( fabs(shc[1]) < gm_eps ) {shc[1] = 0.0; q[2] = 0.0;}
	   else if ( fabs(shc[1]-1.0) < gm_eps ) shc[1] = 1.0;
	   
	   if ( fabs(shc[2]) < gm_eps ) {shc[2] = 0.0; q[0] = 0.0;}
	   else if ( fabs(shc[2]-1.0) < gm_eps ) shc[2] = 1.0;

	   theta0 = pt_tria_tst(shc[0],shc[1],shc[2],theta,&th_id); /* Point in triangle test */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,d[2]);

	   for (j=0; j<mx_nd_elt; j++)
	   {xwp[j] = xww*psi[j]; dchi[j] = d[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j]; qchi[j] = q[j]*chi[j]; etchi[j] = et*chi[j];}
	   
	   omega = qchi[0] + qchi[1] + qchi[2];
	   
	   chis =          sncf[0]*chi[1] + sncf[1]*chi[2]; /* sncf[0] = sin(alpha2); sncf[1] = sin(alpha3) */
	   chic = chi[0] + cscf[0]*chi[1] + cscf[1]*chi[2]; /* cscf[0] = cos(alpha2); cscf[1] = cos(alpha3) */
	   
	   qetx2[0] = q[0]*etx2; qetx2[1] = q[1]*etx2; qetx2[2] = q[2]*etx2;

	   p11rh1 = p11*rho[0]; p12rh2 = p12*rho[1];
	   cr11 = qs[0]*rh1s - p11s*ets; cr12 = qs[0]*rh2s - p12s*ets;
	   zn1 = qetx2[0]*(p12rh2*cr11 - p11rh1*cr12); zd1 = cr11*cr12 + qetx2[0]*qetx2[0]*p11rh1*p12rh2;

	   p22rh2 = p22*rho[1]; p23rh3 = p23*rho[2];
	   cr22 = qs[1]*rh2s-p22*p22*ets; cr23 = qs[1]*rh3s-p23*p23*ets;
	   zn2 = qetx2[1]*(p23rh3*cr22 - p22rh2*cr23); zd2 = cr22*cr23 + qetx2[1]*qetx2[1]*p22rh2*p23rh3;

	   p33rh3 = p33*rho[2]; p31rh1 = p31*rho[0];
	   cr33 = qs[2]*rh3s - p33s*ets; cr31 = qs[2]*rh1s - p31*p31*ets;
	   zn3 = qetx2[2]*(p31rh1*cr33 - p33rh3*cr31); zd3 = cr31*cr33 + qetx2[2]*qetx2[2]*p31rh1*p33rh3;

	      gamma[0] = gammai(p11,p12,qetx2[0],cr11,cr12,zn1,zd1);
	      gamma[1] = gammai(p22,p23,qetx2[1],cr22,cr23,zn2,zd2);
	      gamma[2] = gammai(p33,p31,qetx2[2],cr33,cr31,zn3,zd3);

	   if ( et > 0.0 )
	      ThetGam = 0.5*(gamma[0]+gamma[1]+gamma[2]) + theta0;
	   else
	      ThetGam = 0.5*(gamma[0]+gamma[1]+gamma[2]) - theta0;

	   etThetGam = et*ThetGam;

           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      intGamwv[kk] = intGamwv[kk] + xwp[kk]*etThetGam;
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj = j + cl_k; /* Column-major order in intclN,intclM,intGN3tmp,intA,intOme,intGam,intGK1,intGK2,intGK3 matrices */
		 intclN[jj] += xwp[j]*qrhowv[kk];
		 intclM[jj] += xwp[j]*dchi[kk];

		 intGN3tmp[jj] += xwp[j]*etchi[kk];
		      intA[jj] += xwp[j]*qchi[kk];
		  
		 xwpc = xwp[j]*shc[kk];
		 intOme[jj] += xwpc*omega;
		 intGam[jj] += xwpc*etThetGam;
		  
		 intGK1[jj] += xwpc*chis;
		 intGK2[jj] -= xwpc*chic;
		 intGK3[jj] += xwpc*ThetGam;
	      }
           }
	   
           for (kk=0; kk<2; kk++)
	   {
              cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
	      kp1 = kk+1;
	      for (j=0; j<mx_nd_elt; j++)
	      {
		 jj = j + cl_k; /* Column-major order in intR matrix */
		 intR[jj] += xwp[j]*rhowv[kp1];
	      }
           }

        } /* End Loop over x */
     } /* End Loop over y */

     intD[0] = intA[0]; intD[1] = intA[1]; intD[2] = intA[2];
     for (k=0; k<2; k++)
     {
        kk = mx_nd_elt*k; /* kk == Local Column stride */
	cl_k = mx_nd_elt + kk; /* cl_k == Local Column stride */
	for (i=0; i<mx_nd_elt; i++)
	{
	   jj = i + kk; /* Column-major order in intR[mx_nd_elt][2] matrix */
	   j = i + cl_k; /* Column-major order in intD[mx_nd_elt][3] and intA[mx_nd_elt][3] matrices */
	   intR[jj] = sncf[k]*intR[jj];
	   intD[j] = intR[jj] + cscf[k]*intA[j];
	   intA[j] = sncf[k]*intA[j];
        }
     }

    tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intD,&mx_nd_elt,ah,&dabh,&bet,intGN1,&mx_nd_elt); /* intGN1 = matmul(intD,ah) */

    intGN2[0] = intR[0]*bh[1] + intR[3]*bh[2] + intA[3]*ah[1] + intA[6]*ah[2];
    intGN2[1] = intR[1]*bh[1] + intR[4]*bh[2] + intA[4]*ah[1] + intA[7]*ah[2];
    intGN2[2] = intR[2]*bh[1] + intR[5]*bh[2] + intA[5]*ah[1] + intA[8]*ah[2];
    
    intGN2[3] = intR[0]*bh[4] + intR[3]*bh[5] + intA[3]*ah[4] + intA[6]*ah[5];
    intGN2[4] = intR[1]*bh[4] + intR[4]*bh[5] + intA[4]*ah[4] + intA[7]*ah[5];
    intGN2[5] = intR[2]*bh[4] + intR[5]*bh[5] + intA[5]*ah[4] + intA[8]*ah[5];
    
    intGN2[6] = intR[0]*bh[7] + intR[3]*bh[8] + intA[3]*ah[7] + intA[6]*ah[8];
    intGN2[7] = intR[1]*bh[7] + intR[4]*bh[8] + intA[4]*ah[7] + intA[7]*ah[8];
    intGN2[8] = intR[2]*bh[7] + intR[5]*bh[8] + intA[5]*ah[7] + intA[8]*ah[8];

    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&mx_nd_elt,&alp,intGN3tmp,&mx_nd_elt,bh,&dabh,&bet,intGN3,&mx_nd_elt); /* intGN3 = matmul(intGN3tmp,bh) */

/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (j=0; j<mx_nd_elt; j++)
     {
	jj = mx_nd_elt*j; /* jj == Local Column stride */
	for (i=0; i<mx_nd_elt; i++)
	{
	   k = i + jj; /* Column-major order in int_f, intGN1 and intGN2 matrices viewed as mx_nd_elt by mx_nd_elt matrices */
	   int_f[k] = 0.5*int_f[k] + intOme[k] - intGam[k];
	   intGN1[k] -= intGamwv[i]*vecA[j];
	   intGN2[k] -= intGamwv[i]*vecB[j];
	}
     }
     
     c13cf = c13*dlt; c23cf = c23*dlt;

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f, int_p and int_r matrices viewed as mx_nd_elt by mx_nd_elt matrices */
	   int_f[kk] =   dlt*int_f[kk];
           int_p[kk] =  -dlt*(intGN3[kk]+intGK3[kk]);
	   int_r[kk] = c13cf*(intGN1[kk]+intGK1[kk]) + c23cf*(intGN2[kk]+intGK2[kk]) - c33*int_p[kk];
        }
     }

}


void qsepp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double* int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/*****************************  Quasi-singular integration for separated elements on parallel planes  *****************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi;           /* pi = 3.1415926535897932 */
   extern double gm_eps,f_eps; /* gm_eps == Tolerance on the geometry, f_eps == Tolerance on the argument of a function */
   extern int ng2;       /* ng2 == Number of Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double *xi2;   /* xi2 == Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double **wwg2; /* wwg2[i][k] = wg2[i]*wg2[k], where wg2[k] is the k-th Gauss-Legendre weight factor corresponding to xi2[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,th_id,cl_k,dabh;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c12,c22,c33,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,rh1s,rh2s,rh3s,p11s,p12s,p33s,ch33,ch31;
   double alpha2,alpha3,x,y,xh,zh,et,ets,etx2,p11rh1,p31rh1,omega,cmb,xy,dlt,xww,xwpc,etThetGam,alp,bet;
   double cr11,cr12,zn1,zd1,cr22,cr23,zn2,zd2,cr31,cr33,zn3,zd3,p12rh2,p22rh2,p23rh3,p33rh3,theta0,ThetGam;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],etchi[3],rho[3],rhowv[3],qrhowv[3],d[3],q[3],qs[3],chi[3],gamma[3],dchi[3],qetx2[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt],intGN3tmp[smx_nd_elt];
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt],intGam[smx_nd_elt]; /* intOme,intGam,intGK3,intGN3, intGN3tmp are viewed as */
   double intGK3[smx_nd_elt],intGN3[smx_nd_elt]; /* Column-major ordered mx_nd_elt by mx_nd_elt matrices */
/* pt_tria_tst, gammai, asym_chi are external functions (see gbem_util.c) */

 

     et = -eps[2];
     
     if (fabs(et) < gm_eps)
     {
        qsesp(a,b,c,aQ,bQ,cQ,theta,eps,QPTM,int_p,int_f,int_r); /* Integration for Quasi-Singular elements on the same plane */
	return;
     }

     ets = et*et; etx2 = 2.0*et;

     c11 = QPTM[0][0]; c21 = QPTM[0][1];
     c12 = QPTM[1][0]; c22 = QPTM[1][1];
     c33 = QPTM[2][2];

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */
     
     for (k=0; k<smx_nd_elt; k++)
     {intclN[k] = 0.0; intclM[k] = 0.0; intOme[k] = 0.0; intGK3[k] = 0.0; intGN3tmp[k] = 0.0; intGam[k] = 0.0;}

     for (i=0;i<ng2; i++) /* Loop over y = xi2[i] */
     {
	y = xi2[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh);
	
        for (k=0; k<ng2; k++) /* Loop over x = xi2[k] */
	{
	   x = xi2[k];
           xww = x*wwg2[i][k];
	   
	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   p11s = p11*p11; p12s = p12*p12; p33s = p33*p33; qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];

	   d[0] = qs[0] + ets; d[1] = qs[1] + ets; d[2] = qs[2] + ets;
	   
	   rh1s = p11s + d[0]; rh2s = p12s + d[0]; rh3s = p33s + d[2];
	   rho[0] = sqrt(rh1s); rho[1] = sqrt(rh2s); rho[2] = sqrt(rh3s);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   if ( fabs(shc[0]) < gm_eps ) {shc[0] = 0.0; q[1] = 0.0;}
	   
	   if ( fabs(shc[1]) < gm_eps ) {shc[1] = 0.0; q[2] = 0.0;}
	   else if ( fabs(shc[1]-1.0) < gm_eps ) shc[1] = 1.0;
	   
	   if ( fabs(shc[2]) < gm_eps ) {shc[2] = 0.0; q[0] = 0.0;}
	   else if ( fabs(shc[2]-1.0) < gm_eps ) shc[2] = 1.0;

	   theta0 = pt_tria_tst(shc[0],shc[1],shc[2],theta,&th_id); /* Point in triangle test */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,d[2]);

	   for (j=0; j<mx_nd_elt; j++)
	   {xwp[j] = xww*psi[j]; dchi[j] = d[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j]; etchi[j] = et*chi[j];}
	   
	   omega = q[0]*chi[0] + q[1]*chi[1] + q[2]*chi[2];
	   
	   qetx2[0] = q[0]*etx2; qetx2[1] = q[1]*etx2; qetx2[2] = q[2]*etx2;

	   p11rh1 = p11*rho[0]; p12rh2 = p12*rho[1];
	   cr11 = qs[0]*rh1s - p11s*ets; cr12 = qs[0]*rh2s - p12s*ets;
	   zn1 = qetx2[0]*(p12rh2*cr11 - p11rh1*cr12); zd1 = cr11*cr12 + qetx2[0]*qetx2[0]*p11rh1*p12rh2;

	   p22rh2 = p22*rho[1]; p23rh3 = p23*rho[2];
	   cr22 = qs[1]*rh2s-p22*p22*ets; cr23 = qs[1]*rh3s-p23*p23*ets;
	   zn2 = qetx2[1]*(p23rh3*cr22 - p22rh2*cr23); zd2 = cr22*cr23 + qetx2[1]*qetx2[1]*p22rh2*p23rh3;

	   p33rh3 = p33*rho[2]; p31rh1 = p31*rho[0];
	   cr33 = qs[2]*rh3s - p33s*ets; cr31 = qs[2]*rh1s - p31*p31*ets;
	   zn3 = qetx2[2]*(p31rh1*cr33 - p33rh3*cr31); zd3 = cr31*cr33 + qetx2[2]*qetx2[2]*p31rh1*p33rh3;

	      gamma[0] = gammai(p11,p12,qetx2[0],cr11,cr12,zn1,zd1);
	      gamma[1] = gammai(p22,p23,qetx2[1],cr22,cr23,zn2,zd2);
	      gamma[2] = gammai(p33,p31,qetx2[2],cr33,cr31,zn3,zd3);

	   if ( et > 0.0 )
	      ThetGam = 0.5*(gamma[0]+gamma[1]+gamma[2]) + theta0;
	   else
	      ThetGam = 0.5*(gamma[0]+gamma[1]+gamma[2]) - theta0;

	   etThetGam = et*ThetGam;

           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj=j+cl_k; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3], intGN3tmp[mx_nd_elt][mx_nd_elt],
		               intOme[mx_nd_elt][mx_nd_elt], intGam[mx_nd_elt][mx_nd_elt], intGK3[mx_nd_elt][mx_nd_elt] matrices */
		    intclN[jj] += xwp[j]*qrhowv[kk];
		    intclM[jj] += xwp[j]*dchi[kk];
		 intGN3tmp[jj] += xwp[j]*etchi[kk];
		  
		 xwpc = xwp[j]*shc[kk];
		 intOme[jj] += xwpc*omega;
		 intGam[jj] += xwpc*etThetGam;
		 intGK3[jj] += xwpc*ThetGam;
	      }
           }
	   
        } /* End Loop over x */
     } /* End Loop over y */
     
    tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&mx_nd_elt,&alp,intGN3tmp,&mx_nd_elt,bh,&dabh,&bet,intGN3,&mx_nd_elt); /* intGN3 = matmul(intGN3tmp,bh) */

/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f[mx_nd_elt][mx_nd_elt], int_p[mx_nd_elt][mx_nd_elt] and
	                  int_r[mx_nd_elt][mx_nd_elt] matrices */
	   int_f[kk] =  dlt*(0.5*int_f[kk] + intOme[kk] - intGam[kk]);
           int_p[kk] = -dlt*(intGN3[kk] + intGK3[kk]);
	   int_r[kk] = -c33*int_p[kk];
        }
     }

}


void qsesp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/*****************************  Quasi-singular integration for separated elements on the same plane  ******************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi, f_eps;    /* pi = 3.1415926535897932, f_eps == Tolerance on the argument of a function */
   extern int ng2;       /* ng2 == Number of Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double *xi2;   /* xi2 == Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
   extern double **wwg2; /* wwg2[i][k] = wg2[i]*wg2[k], where wg2[k] is the k-th Gauss-Legendre weight factor corresponding to xi2[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,cl_k,dabh;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c12,c22,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,alpha2,alpha3,x,y,xh,zh,omega,cmb,xy,dlt,xww,ch33,ch31,alp,bet;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],q[3],qs[3],rho[3],rhowv[3],qrhowv[3],chi[3],dchi[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt];
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt]; /* Column-major order in intOme[mx_nd_elt][mx_nd_elt] */
/* asym_chi is an external function (see gbem_util.c) */



     c11 = QPTM[0][0]; c21 = QPTM[0][1];
     c12 = QPTM[1][0]; c22 = QPTM[1][1];

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */
     
     for (k=0; k<smx_nd_elt; k++) {intclN[k] = 0.0; intclM[k] = 0.0; intOme[k] = 0.0;}

     for (i=0; i<ng2; i++) /* Loop over y = xi2[i] */
     {
	y = xi2[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh);

        for (k=0; k<ng2; k++) /* Loop over x = xi2[k] */
	{
	   x = xi2[k];
           xww = x*wwg2[i][k];
	   
	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];
	   
	   rho[0]= sqrt(p11*p11 + qs[0]); rho[1] = sqrt(p12*p12 + qs[0]); rho[2] = sqrt(p33*p33 + qs[2]);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,qs[2]);

	   for (j=0; j<mx_nd_elt; j++) {xwp[j] = xww*psi[j]; dchi[j] = qs[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j];}
	   
	   omega = q[0]*chi[0] + q[1]*chi[1] + q[2]*chi[2];
	   
           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj=j+cl_k; /* Column-major order in intclN[mx_nd_elt][3],intclM[mx_nd_elt][3],intOme[mx_nd_elt][mx_nd_elt] matrices */
		 intclN[jj] += xwp[j]*qrhowv[kk];
		 intclM[jj] += xwp[j]*dchi[kk];
		 intOme[jj] += xwp[j]*shc[kk]*omega;
	      }
           }

        } /* End Loop over x */
     } /* End Loop over y */
     
/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f[mx_nd_elt][mx_nd_elt], int_p[mx_nd_elt][mx_nd_elt] and
	                  int_r[mx_nd_elt][mx_nd_elt] matrices */
	   int_f[kk] = dlt*(0.5*int_f[kk] + intOme[kk]);
           int_p[kk] = 0.0;
	   int_r[kk] = 0.0;
        }
     }

}


void nsing(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/*************************************  Non-singular integration for well-separated elements  *************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi;           /* pi = 3.1415926535897932 */
   extern double gm_eps,f_eps; /* gm_eps == Tolerance on the geometry, f_eps == Tolerance on the argument of a function */
   extern int ng3;       /* ng3 == Number of Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double *xi3;   /* xi3 == Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double **wwg3; /* wwg3[i][k] = wg3[i]*wg3[k], where wg3[k] is the k-th Gauss-Legendre weight factor corresponding to xi3[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,cl_k,dabh,kp1;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c31,c12,c22,c32,c13,c23,c33,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,rh1s,rh2s,rh3s,p11s,p12s,p33s,ch33,ch31;
   double alpha2,alpha3,x,y,xh,zh,et,eth,ets,etx2,p11rh1,p31rh1,c13cf,c23cf,omega,cmb,xy,dlt,xww,xwpc,chis,chic,ThetGam,alp,bet;
   double cr11,cr12,zn1,zd1,cr22,cr23,zn2,zd2,cr31,cr33,zn3,zd3,p12rh2,p22rh2,p23rh3,p33rh3,etThetGam,S13n,S13d,S123n,S123d;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],qchi[3],etchi[3],qrhowv[3],d[3],q[3],qs[3],rho[3],rhowv[3],chi[3],dchi[3],qetx2[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt],intGamwv[mx_nd_elt];
   double intR[mx_nd_elt*2],intGN3tmp[smx_nd_elt]; /* Column-major order in intR[mx_nd_elt][2], intGN3tmp[mx_nd_elt][mx_nd_elt] */
   double intA[mx_nd_elt*3],intD[mx_nd_elt*3];     /* Column-major order in   intA[mx_nd_elt][3],   intD[mx_nd_elt][3] */
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt],intGam[smx_nd_elt]; /* intOme,intGam,intGK1,intGN1,intGK2,intGN2,intGK3,intGN3 are viewed as */
   double intGK1[smx_nd_elt],intGN1[smx_nd_elt]; /* Column-major ordered mx_nd_elt by mx_nd_elt matrices                  */
   double intGK2[smx_nd_elt],intGN2[smx_nd_elt];
   double intGK3[smx_nd_elt],intGN3[smx_nd_elt];
/* asym_chi is an external function (see gbem_util.c) */
 


     c11 = QPTM[0][0]; c21 = QPTM[0][1]; c31 = QPTM[0][2];
     c12 = QPTM[1][0]; c22 = QPTM[1][1]; c32 = QPTM[1][2];
     c13 = QPTM[2][0]; c23 = QPTM[2][1]; c33 = QPTM[2][2];

     if ( ((fabs(c13) < gm_eps) && (fabs(c23) < gm_eps)) || ((fabs(c31) < gm_eps) && (fabs(c32) < gm_eps)) )
     {
          nsepp(a,b,c,aQ,bQ,cQ,theta,eps,QPTM,int_p,int_f,int_r); /* Integration for Well-separated elements on parallel planes */
	  return;
     }

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */
     
     for (k=0; k<mx_nd_elt; k++) intGamwv[k] = 0.0;

     for (k=0; k<smx_nd_elt; k++)
     {
        intclN[k] = 0.0; intclM[k] = 0.0;   intA[k] = 0.0;
	intOme[k] = 0.0; intGam[k] = 0.0; intGK1[k] = 0.0; intGK2[k] = 0.0; intGK3[k] = 0.0; intGN3tmp[k] = 0.0; 
     }

     for (k=0; k<2*mx_nd_elt; k++) intR[k] = 0.0;

     for (i=0; i<ng3; i++) /* Loop over y = xi3[i] */
     {
	y = xi3[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh); eth = -(c31*xh + c32*zh);
	
        for (k=0; k<ng3; k++) /* Loop over x = xi3[k] */
	{
	   x = xi3[k];
           xww = x*wwg3[i][k];

	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1]; et = x*eth-eps[2];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   ets = et*et; p11s = p11*p11; p12s = p12*p12; p33s = p33*p33; qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];

	   d[0] = qs[0] + ets; d[1] = qs[1] + ets; d[2] = qs[2] + ets;
	   
	   rh1s = p11s + d[0]; rh2s = p12s + d[0]; rh3s = p33s + d[2];
	   rho[0] = sqrt(rh1s); rho[1] = sqrt(rh2s); rho[2] = sqrt(rh3s);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y; etx2 = 2.0*et;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,d[2]);

	   for (j=0; j<mx_nd_elt; j++)
	   {xwp[j] = xww*psi[j]; dchi[j] = d[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j]; qchi[j] = q[j]*chi[j]; etchi[j] = et*chi[j];}
	   
	   omega = qchi[0] + qchi[1] + qchi[2];
	   
	   chis =          sncf[0]*chi[1] + sncf[1]*chi[2]; /* sncf[0] = sin(alpha2); sncf[1] = sin(alpha3) */
	   chic = chi[0] + cscf[0]*chi[1] + cscf[1]*chi[2]; /* cscf[0] = cos(alpha2); cscf[1] = cos(alpha3) */
	   
	   qetx2[0] = q[0]*etx2; qetx2[1] = q[1]*etx2; qetx2[2] = q[2]*etx2;

	   p11rh1 = p11*rho[0]; p12rh2 = p12*rho[1];
	   cr11 = qs[0]*rh1s - p11s*ets; cr12 = qs[0]*rh2s - p12s*ets;
	   zn1 = qetx2[0]*(p12rh2*cr11 - p11rh1*cr12); zd1 = cr11*cr12 + qetx2[0]*qetx2[0]*p11rh1*p12rh2;

	   p22rh2 = p22*rho[1]; p23rh3 = p23*rho[2];
	   cr22 = qs[1]*rh2s-p22*p22*ets; cr23 = qs[1]*rh3s-p23*p23*ets;
	   zn2 = qetx2[1]*(p23rh3*cr22 - p22rh2*cr23); zd2 = cr22*cr23 + qetx2[1]*qetx2[1]*p22rh2*p23rh3;

	   p33rh3 = p33*rho[2]; p31rh1 = p31*rho[0];
	   cr33 = qs[2]*rh3s - p33s*ets; cr31 = qs[2]*rh1s - p31*p31*ets;
	   zn3 = qetx2[2]*(p31rh1*cr33 - p33rh3*cr31); zd3 = cr31*cr33 + qetx2[2]*qetx2[2]*p31rh1*p33rh3;

           S13n = zn1*zd3 + zn3*zd1; S13d = zd1*zd3 - zn1*zn3;
           S123n = S13n*zd2 + S13d*zn2; S123d = S13d*zd2 - S13n*zn2;

	      ThetGam =  0.5*atan2(S123n,S123d);

	   etThetGam = et*ThetGam;

           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      intGamwv[kk] = intGamwv[kk] + xwp[kk]*etThetGam;
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj = j + cl_k; /* Column-major order in intclN,intclM,intGN3tmp,intA,intOme,intGam,intGK1,intGK2,intGK3 matrices */
		 intclN[jj] += xwp[j]*qrhowv[kk];
		 intclM[jj] += xwp[j]*dchi[kk];

		 intGN3tmp[jj] += xwp[j]*etchi[kk];
		      intA[jj] += xwp[j]*qchi[kk];
		  
		 xwpc = xwp[j]*shc[kk];
		 intOme[jj] += xwpc*omega;
		 intGam[jj] += xwpc*etThetGam;
		  
		 intGK1[jj] += xwpc*chis;
		 intGK2[jj] -= xwpc*chic;
		 intGK3[jj] += xwpc*ThetGam;
	      }
           }

           for (kk=0; kk<2; kk++)
	   {
              cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
	      kp1 = kk+1;
	      for (j=0; j<mx_nd_elt; j++)
	      {
		 jj = j + cl_k; /* Column-major order in intR matrix */
		 intR[jj] += xwp[j]*rhowv[kp1];
	      }
           }

        } /* End Loop over x */
     } /* End Loop over y */

     intD[0] = intA[0]; intD[1] = intA[1]; intD[2] = intA[2];
     for (k=0; k<2; k++)
     {
        kk = mx_nd_elt*k; /* kk == Local Column stride */
	cl_k = mx_nd_elt + kk; /* cl_k == Local Column stride */
	for (i=0; i<mx_nd_elt; i++)
	{
	   jj = i + kk; /* Column-major order in intR[mx_nd_elt][2] matrix */
	   j = i + cl_k; /* Column-major order in intD[mx_nd_elt][3] and intA[mx_nd_elt][3] matrices */
	   intR[jj] = sncf[k]*intR[jj];
	   intD[j] = intR[jj] + cscf[k]*intA[j];
	   intA[j] = sncf[k]*intA[j];
        }
     }

    tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intD,&mx_nd_elt,ah,&dabh,&bet,intGN1,&mx_nd_elt); /* intGN1 = matmul(intD,ah) */

    intGN2[0] = intR[0]*bh[1] + intR[3]*bh[2] + intA[3]*ah[1] + intA[6]*ah[2];
    intGN2[1] = intR[1]*bh[1] + intR[4]*bh[2] + intA[4]*ah[1] + intA[7]*ah[2];
    intGN2[2] = intR[2]*bh[1] + intR[5]*bh[2] + intA[5]*ah[1] + intA[8]*ah[2];
    
    intGN2[3] = intR[0]*bh[4] + intR[3]*bh[5] + intA[3]*ah[4] + intA[6]*ah[5];
    intGN2[4] = intR[1]*bh[4] + intR[4]*bh[5] + intA[4]*ah[4] + intA[7]*ah[5];
    intGN2[5] = intR[2]*bh[4] + intR[5]*bh[5] + intA[5]*ah[4] + intA[8]*ah[5];
    
    intGN2[6] = intR[0]*bh[7] + intR[3]*bh[8] + intA[3]*ah[7] + intA[6]*ah[8];
    intGN2[7] = intR[1]*bh[7] + intR[4]*bh[8] + intA[4]*ah[7] + intA[7]*ah[8];
    intGN2[8] = intR[2]*bh[7] + intR[5]*bh[8] + intA[5]*ah[7] + intA[8]*ah[8];

    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&mx_nd_elt,&alp,intGN3tmp,&mx_nd_elt,bh,&dabh,&bet,intGN3,&mx_nd_elt); /* intGN3 = matmul(intGN3tmp,bh) */

/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (j=0; j<mx_nd_elt; j++)
     {
	jj = mx_nd_elt*j; /* jj == Local Column stride */
	for (i=0; i<mx_nd_elt; i++)
	{
	   k = i + jj; /* Column-major order in int_f, intGN1 and intGN2 matrices viewed as mx_nd_elt by mx_nd_elt matrices */
	   int_f[k] = 0.5*int_f[k] + intOme[k] - intGam[k];
	   intGN1[k] -= intGamwv[i]*vecA[j];
	   intGN2[k] -= intGamwv[i]*vecB[j];
	}
     }

     c13cf = c13*dlt; c23cf = c23*dlt;

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f, int_p and int_r matrices viewed as mx_nd_elt by mx_nd_elt matrices */
	   int_f[kk] =   dlt*int_f[kk];
           int_p[kk] =  -dlt*(intGN3[kk]+intGK3[kk]);
	   int_r[kk] = c13cf*(intGN1[kk]+intGK1[kk]) + c23cf*(intGN2[kk]+intGK2[kk]) - c33*int_p[kk];
        }
     }

}


void nsepp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/***************************  Non-singular integration for well-separated elements on parallel planes  ****************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi;           /* pi = 3.1415926535897932 */
   extern double gm_eps,f_eps; /* gm_eps == Tolerance on the geometry, f_eps == Tolerance on the argument of a function */
   extern int ng3;       /* ng3 == Number of Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double *xi3;   /* xi3 == Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double **wwg3; /* wwg3[i][k] = wg3[i]*wg3[k], where wg3[k] is the k-th Gauss-Legendre weight factor corresponding to xi3[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,cl_k,dabh;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c12,c22,c33,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,rh1s,rh2s,rh3s,p11s,p12s,p33s,ch33,ch31,alp;
   double alpha2,alpha3,x,y,xh,zh,et,ets,etx2,p11rh1,p31rh1,omega,cmb,xy,dlt,xww,xwpc,S13n,S13d,S123n,S123d,bet;
   double cr11,cr12,zn1,zd1,cr22,cr23,zn2,zd2,cr31,cr33,zn3,zd3,p12rh2,p22rh2,p23rh3,p33rh3,ThetGam,etThetGam;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],etchi[3],qrhowv[3],d[3],q[3],qs[3],rho[3],rhowv[3],chi[3],dchi[3],qetx2[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt],intGN3tmp[smx_nd_elt];
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt],intGam[smx_nd_elt]; /* intOme,intGam,intGK3,intGN3, intGN3tmp are viewed as */
   double intGK3[smx_nd_elt],intGN3[smx_nd_elt]; /* Column-major ordered mx_nd_elt by mx_nd_elt matrices */
/* asym_chi is an external function (see gbem_util.c) */

 

     et = -eps[2];
     
     if (fabs(et) < gm_eps) /* if et == 0 */
     {
        nsesp(a,b,c,aQ,bQ,cQ,theta,eps,QPTM,int_p,int_f,int_r); /* Integration for Well-separated elements on the same plane */
	return;
     }

     ets = et*et; etx2 = 2.0*et;

     c11 = QPTM[0][0]; c21 = QPTM[0][1];
     c12 = QPTM[1][0]; c22 = QPTM[1][1];
     c33 = QPTM[2][2];

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */
     
     for (k=0; k<smx_nd_elt; k++)
     {intclN[k] = 0.0; intclM[k] = 0.0; intOme[k] = 0.0; intGK3[k] = 0.0; intGN3tmp[k] = 0.0; intGam[k] = 0.0;}

     for (i=0; i<ng3; i++) /* Loop over y = xi3[i] */
     {
	y = xi3[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh);
	
        for (k=0; k<ng3; k++) /* Loop over x = xi3[k] */
	{
	   x = xi3[k];
           xww = x*wwg3[i][k];
	   
	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   p11s = p11*p11; p12s = p12*p12; p33s = p33*p33; qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];

	   d[0] = qs[0] + ets; d[1] = qs[1] + ets; d[2] = qs[2] + ets;
	   
	   rh1s = p11s + d[0]; rh2s = p12s + d[0]; rh3s = p33s + d[2];
	   rho[0] = sqrt(rh1s); rho[1] = sqrt(rh2s); rho[2] = sqrt(rh3s);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,d[2]);

	   for (j=0; j<mx_nd_elt; j++)
	   {xwp[j] = xww*psi[j]; dchi[j] = d[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j]; etchi[j] = et*chi[j];}
	   
	   omega = q[0]*chi[0] + q[1]*chi[1] + q[2]*chi[2];
	   
	   qetx2[0] = q[0]*etx2; qetx2[1] = q[1]*etx2; qetx2[2] = q[2]*etx2;
	   
	   p11rh1 = p11*rho[0]; p12rh2 = p12*rho[1];
	   cr11 = qs[0]*rh1s - p11s*ets; cr12 = qs[0]*rh2s - p12s*ets;
	   zn1 = qetx2[0]*(p12rh2*cr11 - p11rh1*cr12); zd1 = cr11*cr12 + qetx2[0]*qetx2[0]*p11rh1*p12rh2;

	   p22rh2 = p22*rho[1]; p23rh3 = p23*rho[2];
	   cr22 = qs[1]*rh2s-p22*p22*ets; cr23 = qs[1]*rh3s-p23*p23*ets;
	   zn2 = qetx2[1]*(p23rh3*cr22 - p22rh2*cr23); zd2 = cr22*cr23 + qetx2[1]*qetx2[1]*p22rh2*p23rh3;

	   p33rh3 = p33*rho[2]; p31rh1 = p31*rho[0];
	   cr33 = qs[2]*rh3s - p33s*ets; cr31 = qs[2]*rh1s - p31*p31*ets;
	   zn3 = qetx2[2]*(p31rh1*cr33 - p33rh3*cr31); zd3 = cr31*cr33 + qetx2[2]*qetx2[2]*p31rh1*p33rh3;

           S13n = zn1*zd3 + zn3*zd1; S13d = zd1*zd3 - zn1*zn3;
           S123n = S13n*zd2 + S13d*zn2; S123d = S13d*zd2 - S13n*zn2;

	      ThetGam =  0.5*atan2(S123n,S123d);

	   etThetGam = et*ThetGam;
	   
           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj=j+cl_k; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3], intGN3tmp[mx_nd_elt][mx_nd_elt],
		               intOme[mx_nd_elt][mx_nd_elt], intGam[mx_nd_elt][mx_nd_elt], intGK3[mx_nd_elt][mx_nd_elt] matrices */
		    intclN[jj] += xwp[j]*qrhowv[kk];
		    intclM[jj] += xwp[j]*dchi[kk];
		 intGN3tmp[jj] += xwp[j]*etchi[kk];
		  
		 xwpc = xwp[j]*shc[kk];
		 intOme[jj] += xwpc*omega;
		 intGam[jj] += xwpc*etThetGam;
		 intGK3[jj] += xwpc*ThetGam;
	      }
           }

        } /* End Loop over x */
     } /* End Loop over y */

    tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
    dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&mx_nd_elt,&alp,intGN3tmp,&mx_nd_elt,bh,&dabh,&bet,intGN3,&mx_nd_elt); /* intGN3 = matmul(intGN3tmp,bh) */

/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f[mx_nd_elt][mx_nd_elt], int_p[mx_nd_elt][mx_nd_elt] and
	                  int_r[mx_nd_elt][mx_nd_elt] matrices */
	   int_f[kk] =  dlt*(0.5*int_f[kk] + intOme[kk] - intGam[kk]);
           int_p[kk] = -dlt*(intGN3[kk] + intGK3[kk]);
	   int_r[kk] = -c33*int_p[kk];
        }
     }

}


void nsesp(double a, double b, double c, double aQ, double bQ, double cQ,
           double *theta, double *eps, double QPTM[][3], double *int_p, double *int_f, double *int_r)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/*******************************  EXact Inner integral with linear distribution on a flat triangle  *******************************/
/****************************  Non-singular integration for well-separated elements on the same plane  ****************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * a,b,c == (input) Geometric parameters of element P: b = base, a = height,
 *                  c = relative position of the 3rd vertex of element P
 * aQ,bQ,cQ == (input) Geometric parameters of element Q: bQ = base, aQ = height,
 *                     cQ = relative position of the 3rd vertex of element Q
 * theta[3] == (input): theta[i] == Angle at the i-th vertex of element Q
 * eps[3] == (input): Vector characterizing the distance between element P and element Q
 *                    in the local Coordinates associated with element Q
 * QPTM[3][3] == (input) Transition matrix from element Q to element P
 * int_p == (output) Singular contribution to potential influence matrix H
 * int_f == (output) Singular contribution to flux influence matrix G
 * int_r == (output) Singular contribution to potential influence matrix H: Reverse kernel
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int smx_nd_elt;      /* smx_nd_elt == mx_nd_elt*mx_nd_elt */
   extern double pi, f_eps;    /* pi = 3.1415926535897932, f_eps == Tolerance on the argument of a function */
   extern int ng3;       /* ng3 == Number of Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double *xi3;   /* xi3 == Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double **wwg3; /* wwg3[i][k] = wg3[i]*wg3[k], where wg3[k] is the k-th Gauss-Legendre weight factor corresponding to xi3[k] */

/* Local variables */
   char tr;
   int k,kk,i,j,jj,cl_k,dabh;
   double kQ2,kQ3; /* kQ2 == Slope of Edge2 of element Q, kQ3 == Slope of Edge3 of element Q */
   double c11,c21,c12,c22,p11,p11h,q1h,p12,p22,p23,p31,p33,x3,z3,alpha2,alpha3,x,y,xh,zh,omega,cmb,xy,dlt,xww,ch33,ch31,alp,bet;
   double ah[9],bh[9]; /* ah and bh are viewed as Column-major ordered 3 by 3 matrices, i.e. ah[3][3], bh[3][3] */
   double vecA[3],vecB[3],sncf[2],cscf[2],qrhowv[3],q[3],qs[3],rho[3],rhowv[3],chi[3],dchi[3];
   double psi[mx_nd_elt],shc[mx_nd_elt],xwp[mx_nd_elt];
   double intclN[mx_nd_elt*3],intclM[mx_nd_elt*3]; /* Column-major order in intclN[mx_nd_elt][3], intclM[mx_nd_elt][3] */
   double intOme[smx_nd_elt]; /* Column-major order in intOme[mx_nd_elt][mx_nd_elt] */
/* asym_chi is an external function (see gbem_util.c) */



     c11 = QPTM[0][0]; c21 = QPTM[0][1];
     c12 = QPTM[1][0]; c22 = QPTM[1][1];

     kQ2 = (cQ-bQ)/aQ; kQ3 = cQ/aQ; cmb = c-b; dlt = a*b;

     alpha2 = pi-theta[1]; alpha3 = pi+theta[0]; /* alpha1 = 0.0 */
     
     cscf[0] = cos(alpha2); sncf[0] = sin(alpha2); /* cscf(2) ; sncf(2) */
     cscf[1] = cos(alpha3); sncf[1] = sin(alpha3); /* cscf(3) ; sncf(3) */
     
     vecA[0] = -1.0/bQ; vecA[1] =  1.0/bQ; vecA[2] = 0.0;
     vecB[0] =  kQ2/bQ; vecB[1] = -kQ3/bQ; vecB[2] = 1.0/aQ;
     
     ah[0] = vecA[0]; ah[1] = (kQ2*sncf[0] - cscf[0])/bQ; ah[2] = (kQ2*sncf[1] - cscf[1])/bQ; /* ah(1,1) ; ah(2,1) ; ah(3,1) */
     ah[3] = vecA[1]; ah[4] = (cscf[0] - kQ3*sncf[0])/bQ; ah[5] = (cscf[1] - kQ3*sncf[1])/bQ; /* ah(1,2) ; ah(2,2) ; ah(3,2) */
     ah[6] = 0.0;     ah[7] = sncf[0]/aQ;                 ah[8] = sncf[1]/aQ; /* ah(1,3) ; ah(2,3) ; ah(3,3) */
     
     bh[0] = -vecB[0]; bh[1] = -(sncf[0] + kQ2*cscf[0])/bQ; bh[2] = -(sncf[1] + kQ2*cscf[1])/bQ; /* bh(1,1) ; bh(2,1) ; bh(3,1) */
     bh[3] = -vecB[1]; bh[4] =  (sncf[0] + kQ3*cscf[0])/bQ; bh[5] =  (sncf[1] + kQ3*cscf[1])/bQ; /* bh(1,2) ; bh(2,2) ; bh(3,2) */
     bh[6] = -vecB[2]; bh[7] = -cscf[0]/aQ;                 bh[8] = -cscf[1]/aQ; /* bh(1,3) ; bh(2,3) ; bh(3,3) */
     
     for (k=0; k<smx_nd_elt; k++) {intclN[k] = 0.0; intclM[k] = 0.0; intOme[k] = 0.0;}

     for (i=0; i<ng3; i++) /* Loop over y = xi3[i] */
     {
	y = xi3[i];

	xh = b+cmb*y; zh = a*y;
	p11h = -(c11*xh + c12*zh); q1h = -(c21*xh + c22*zh);
	
        for (k=0; k<ng3; k++) /* Loop over x = xi3[k] */
	{
	   x = xi3[k];
           xww = x*wwg3[i][k];
	   
	   p11 = x*p11h-eps[0]; q[0] = x*q1h-eps[1];
	   p31 = p11*cscf[1] + q[0]*sncf[1]; q[2] = q[0]*cscf[1] - p11*sncf[1]; /* cscf[1] = cos(alpha3); sncf[1] = sin(alpha3) */
	   p12 = bQ+p11; x3 = cQ+p11; z3 = aQ+q[0];
	   p22 = p12*cscf[0] + q[0]*sncf[0]; q[1] = q[0]*cscf[0] - p12*sncf[0]; /* cscf[0] = cos(alpha2); sncf[0] = sin(alpha2) */
	   p23 = x3*cscf[0] + z3*sncf[0]; p33 = x3*cscf[1] + z3*sncf[1];

	   qs[0] = q[0]*q[0]; qs[1] = q[1]*q[1]; qs[2] = q[2]*q[2];
	   
	   rho[0]= sqrt(p11*p11 + qs[0]); rho[1] = sqrt(p12*p12 + qs[0]); rho[2] = sqrt(p33*p33 + qs[2]);

	   rhowv[0] = rho[0]-rho[1]; rhowv[1] = rho[1]-rho[2]; rhowv[2] = rho[2]-rho[0];

           xy = x*y;
           psi[0] = 1.0-x; psi[1] = x-xy; psi[2] = xy; /* psi[0] = 1.0-x; psi[1] = x*(1.0-y); psi[2] = x*y */

           shc[1] = (kQ3*q[0] - p11)/bQ; shc[2] = -q[0]/aQ;
	   shc[0] = 1.0 - (shc[1]+shc[2]); /* shc[0] = 1.0 + (p11 - kQ2*q[0])/bQ */

	   chi[0] = log( (p11+rho[0])/(p12+rho[1]) ); chi[1] = log( (p22+rho[1])/(p23+rho[2]) );
	   ch31 = p31+rho[0]; ch33 = p33+rho[2];
	   if ( (ch31 >= f_eps) && (ch33 >= f_eps) )
	      chi[2] = -log(ch31/ch33);
	   else
	      chi[2] = asym_chi(p33,p31,qs[2]);

	   for (j=0; j<mx_nd_elt; j++) {xwp[j] = xww*psi[j]; dchi[j] = qs[j]*chi[j]; qrhowv[j] = q[j]*rhowv[j];}
	   
	   omega = q[0]*chi[0] + q[1]*chi[1] + q[2]*chi[2];
	   
           for (kk=0; kk<mx_nd_elt; kk++)
	   {
	      cl_k = mx_nd_elt*kk; /* cl_k == Local Column stride */
              for (j=0; j<mx_nd_elt; j++)
	      {
		 jj=j+cl_k; /* Column-major order in intclN[mx_nd_elt][3],intclM[mx_nd_elt][3],intOme[mx_nd_elt][mx_nd_elt] matrices */
		 intclN[jj] += xwp[j]*qrhowv[kk];
		 intclM[jj] += xwp[j]*dchi[kk];
		 intOme[jj] += xwp[j]*shc[kk]*omega;
	      }
           }

        } /* End Loop over x */
     } /* End Loop over y */
     
/*** int_f = matmul(intclN,ah) - matmul(intclM,bh) */
     tr = 'n'; dabh = 3; alp = 1.0; bet = 0.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclM,&mx_nd_elt,bh,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclM,bh) */
     bet = -1.0;
     dgemm_(&tr,&tr,&mx_nd_elt,&dabh,&dabh,&alp,intclN,&mx_nd_elt,ah,&dabh,&bet,int_f,&mx_nd_elt); /* int_f = matmul(intclN,ah) - int_f */

     for (i=0; i<mx_nd_elt; i++)
     {
        j = mx_nd_elt*i; /* j == Local Column stride */
	for (k=0; k<mx_nd_elt; k++)
	{
           kk = k + j; /* Column-major order in int_f[mx_nd_elt][mx_nd_elt], int_p[mx_nd_elt][mx_nd_elt] and
	                  int_r[mx_nd_elt][mx_nd_elt] matrices */
	   int_f[kk] = dlt*(0.5*int_f[kk] + intOme[kk]);
           int_p[kk] = 0.0;
	   int_r[kk] = 0.0;
        }
     }

}
