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

void rgauleg(double x1,double x2, double *x, double *w, int n); /* Generate n Gauss-Legendre points and weights for integration */
								/* over a segment [x1,x2] (see rgauleg.c)                       */


void gbem_lap_init(void)
{

/* ===purpose=== */ 

/**********************************************************************************************************************************/
/**********************************  GALERKIN BEM for 3D LAPLACE EQUATION: SINGULAR FORMULATION  **********************************/
/************************************  3-NODED TRIANGULAR ELEMENT: SEMI-ANALYTIC INTEGRATION  *************************************/
/************************************************  PIECEWISE LINEAR APPROXIMATION  ************************************************/
/***********************************************  Initialize arrays and parameters  ***********************************************/
/**********************************************************************************************************************************/

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


/* Typical triangular element with vertices {y1,y2,y3}

*           y3
*	    *
*	   / \
*	  /   \
*	 /     \
*       /       \
*      /	 \
*     /		  \
*    *-------------*
*  y1               y2

* Oriented edge L1 = [y1,y2] <== Edge1
* Oriented edge L2 = [y2,y3] <== Edge2
* Oriented edge L3 = [y3,y1] <== Edge3
*/

 
/* 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 int nbelt;             /* nbelt == Total number of boundary elements */
   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,pix2,fac;    /* pi = 3.1415926535897932, pix2 = 2*pi, fac == Factor of the Green's function: fac = 1/(4*pi) */
   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 *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 **LocCBasisElt; /* LocCBasisElt[k][0:8] == Local Companion Basis associated with the k-th boundary element */
   extern double **xvElt,*zvElt; /* xvElt[k][0] == Base of the k-th boundary element */
				 /* xvElt[k][1] == Geometric parameter associated with the k-th boundary element */
				 /* zvElt[k] == Height of 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 *EltDiam;       /* EltDiam[k] == Diameter of the k-th boundary element */
   extern double *EltAreax2;     /* EltAreax2[k] = 2*Area of the k-th boundary element */
   extern double ***shfe1;       /* shfe1[i][k][l] == l-th shape function evaluated at Gauss point (k,i) for edge-adjacent case: E' */
   extern double ***shfe2;	 /* shfe2[i][k][l] == l-th shape function evaluated at Gauss point (k,i) for edge-adjacent case: E" */
   extern double ***shfv;	 /* shfv[i][k][l] == l-th shape function evaluated at Gauss point (k,i) for vertex-adjacent case */
   extern int ng1,ng2,ng3;       /* ng1 == Number of Gauss-Legendre points over a segment [0,1] for Singular integrations */
			         /* ng2 == Number of Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
			         /* ng3 == Number of Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double *xi1,*xi2,*xi3; /* xi1 == Gauss-Legendre points over a segment [0,1] for Singular integrations */
			         /* xi2 == Gauss-Legendre points over a segment [0,1] for Quasi-singular integration */
			         /* xi3 == Gauss-Legendre points over a segment [0,1] for Non-singular integration */
   extern double **wwg1; /* wwg1[i][k] = wg1[i]*wg1[k], where wg1[k] is the k-th Gauss-Legendre weight factor corresponding to xi1[k] */
   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] */
   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] */
   extern double **dwrk; /* Temporary working array, dwrk[mx_nd_elt][mx_nd_elt] */

/* Local variables */
   int i,k,elQ; /* elQ == Global index of the boundary element Q */
   double d12,d13,d23;
   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 {e1,e2,e3} */
   double x,y,xy,xyhlf,aQs,bmc;
   double vij[3][3],ym1hlf,yp1hlf,xm1,ym1;
   double e1[3],e2[3],e3[3]; /* Orthonormal local basis */
   double *wg1; /* Gauss-Legendre weight factors for Singular (Edge-adjacent and Vertex-adjacent) integrations */
   double *wg2; /* Gauss-Legendre weight factors for Quasi-singular integration */
   double *wg3; /* Gauss-Legendre weight factors for Non-singular integration */



    pi = 4.0*atan(1.0); /* pi = 3.1415926535897932 */
    pix2 = 2.0*pi; fac = 1.0/(4.0*pi); /* fac == Factor of the Green's function */

    if ( (qq = (int *)malloc(mx_nd_elt*sizeof(int))) == NULL) /* If qq is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"qq\" failed\n"); exit(1);}

    if ( (EltDiam = (double *)malloc(nbelt*sizeof(double))) == NULL) /* If EltDiam is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"EltDiam\" failed\n"); exit(1);}

    if ( (EltAreax2 = (double *)malloc(nbelt*sizeof(double))) == NULL) /* If EltAreax2 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"EltAreax2\" failed\n"); exit(1);}

    if ( (zvElt = (double *)malloc(nbelt*sizeof(double))) == NULL) /* If zvElt is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"zvElt\" failed\n"); exit(1);}

    if ( (xvElt = (double **)malloc(nbelt*sizeof(double *))) != NULL ) /* If xvElt is successfully allocated */
    {
        for (k=0; k<nbelt; k++) /* Loop over all boundary elements */
           if ( (xvElt[k] = (double *)malloc(2*sizeof(double))) == NULL ) /* If xvElt[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"xvElt[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"xvElt\" failed\n"); exit(1);}

    if ( (thetaElt = (double **)malloc(nbelt*sizeof(double *))) != NULL ) /* If thetaElt is successfully allocated */
    {
        for (k=0; k<nbelt; k++) /* Loop over all boundary elements */
           if ( (thetaElt[k] = (double *)malloc(3*sizeof(double))) == NULL ) /* If thetaElt[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"thetaElt[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"thetaElt\" failed\n"); exit(1);}

    if ( (LocCBasisElt = (double **)malloc(nbelt*sizeof(double *))) != NULL ) /* If LocCBasisElt is successfully allocated */
    {
        for (k=0; k<nbelt; k++) /* Loop over all boundary elements */
           if ( (LocCBasisElt[k] = (double *)malloc(9*sizeof(double))) == NULL ) /* If LocCBasisElt[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"LocCBasisElt[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"LocCBasisElt\" failed\n"); exit(1);}


/** Find Diameter, Companion Basis and Area of each boundary element ******************************/
 	  for (elQ=0; elQ<nbelt; elQ++) /* loop over all boundary elements */
	  {
 	     for (i=0; i<mx_nd_elt; i++) /* Loop over the nodes of element Q */
	        qq[i] = belt[elQ][i]; /* qq[i] == Global index of the i-th node (vertex) of the boundary element Q */

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

	  	d12 = vij[0][0]*vij[0][0] + vij[0][1]*vij[0][1] + vij[0][2]*vij[0][2];
	  	d23 = vij[1][0]*vij[1][0] + vij[1][1]*vij[1][1] + vij[1][2]*vij[1][2];
	  	d13 = vij[2][0]*vij[2][0] + vij[2][1]*vij[2][1] + vij[2][2]*vij[2][2];
		
	  	EltDiam[elQ] = sqrt(fmax(fmax(d12,d13),d23)); /* EltDiam[elQ] == Diameter of the boundary element Q */

/*** LocCBasisElt[elQ][0:8] = {e1,e2,e3} == Local orthonormal companion basis associated with the boundary element Q */
/*** {e1,e2} = Orthonormal System in the plane of the boundary element Q                                             */
/*** e3 = vector characterizing the unit "outward" normal to element Q                                               */
		ortho_comp_basis(vij[0],vij[2],e1,e2,e3); /* Deternime the orthonormal companion basis associated with element elQ */
 	  	LocCBasisElt[elQ][0] = e1[0]; LocCBasisElt[elQ][1] = e1[1]; LocCBasisElt[elQ][2] = e1[2];
 	  	LocCBasisElt[elQ][3] = e2[0]; LocCBasisElt[elQ][4] = e2[1]; LocCBasisElt[elQ][5] = e2[2];
 	  	LocCBasisElt[elQ][6] = e3[0]; LocCBasisElt[elQ][7] = e3[1]; LocCBasisElt[elQ][8] = e3[2];
		
 	  	bQ = vij[0][0]*e1[0] + vij[0][1]*e1[1] + vij[0][2]*e1[2]; /* bQ>0.0 always */
	        aQ = vij[2][0]*e2[0] + vij[2][1]*e2[1] + vij[2][2]*e2[2]; /* aQ>0.0 always */
		cQ = vij[2][0]*e1[0] + vij[2][1]*e1[1] + vij[2][2]*e1[2];

	  	xvElt[elQ][0] = bQ; xvElt[elQ][1] = cQ; zvElt[elQ] = aQ;
		
 	      EltAreax2[elQ] = bQ*aQ; /* EltAreax2[elQ] = 2*Area of element Q */

/*** thetaElt[elQ][i] == Angle at the i-th vertex of element Q */
	      bmc = bQ-cQ; aQs = aQ*aQ;
	      thetaElt[elQ][0] = acos(cQ/sqrt(cQ*cQ + aQs));
	      thetaElt[elQ][1] = acos(bmc/sqrt(bmc*bmc + aQs));
	      thetaElt[elQ][2] = pi - (thetaElt[elQ][0]+thetaElt[elQ][1]);
 	  }
/** End Find Diameter, Companion Basis and Area of each boundary element **************************/

/** Set Gauss-Legendre quadrature ********************************************************************************/
    if ( (xi1 = (double *)malloc(ng1*sizeof(double))) == NULL) /* If xi1 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"xi1\" failed\n"); exit(1);}

    if ( (wg1 = (double *)malloc(ng1*sizeof(double))) == NULL) /* If wg1 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"wg1\" failed\n"); exit(1);}

    if ( (xi2 = (double *)malloc(ng2*sizeof(double))) == NULL) /* If xi2 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"xi2\" failed\n"); exit(1);}

    if ( (wg2 = (double *)malloc(ng2*sizeof(double))) == NULL) /* If wg2 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"wg2\" failed\n"); exit(1);}

    if ( (xi3 = (double *)malloc(ng3*sizeof(double))) == NULL) /* If xi3 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"xi3\" failed\n"); exit(1);}

    if ( (wg3 = (double *)malloc(ng3*sizeof(double))) == NULL) /* If wg3 is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"wg3\" failed\n"); exit(1);}

       rgauleg(0.0,1.0,xi1,wg1,ng1); /* Generate ng1 Gauss-Legendre points and weights for integration over a segment [0,1] */
       rgauleg(0.0,1.0,xi2,wg2,ng2); /* Generate ng2 Gauss-Legendre points and weights for integration over a segment [0,1] */
       rgauleg(0.0,1.0,xi3,wg3,ng3); /* Generate ng3 Gauss-Legendre points and weights for integration over a segment [0,1] */
/** END Set Gauss-Legendre quadrature ****************************************************************************/


/** Initialize Gauss weights for Non-singular (i.e. Well-separated element pair) integration ************************/
    if ( (wwg3 = (double **)malloc(ng3*sizeof(double *))) != NULL ) /* If wwg3 is successfully allocated */
    {
        for (i=0; i<ng3; i++) /* Loop over all Gauss-Legendre points */
           if ( (wwg3[i] = (double *)malloc(ng3*sizeof(double))) != NULL ) /* If wwg3[i] is successfully allocated */
	   {
	      for (k=0; k<ng3; k++) /* Loop over all Gauss points */
	         wwg3[i][k] = wg3[i]*wg3[k];
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"wwg3[%d]\" failed\n",i); exit(1);}

	free(wg3);
    }
    else
    {printf("gbem_lap_init: Allocate array \"wwg3\" failed\n"); exit(1);}
/** End Initialize Gauss weights for Non-singular (i.e. Well-separated element pair) integration ********************/


/** Initialize Gauss weights for Quasi-singular integration ***************************************/
    if ( (wwg2 = (double **)malloc(ng2*sizeof(double *))) != NULL ) /* If wwg2 is successfully allocated */
    {
        for (i=0; i<ng2; i++) /* Loop over all Gauss-Legendre points */
           if ( (wwg2[i] = (double *)malloc(ng2*sizeof(double))) != NULL ) /* If wwg2[i] is successfully allocated */
	   {
	      for (k=0; k<ng2; k++) /* Loop over all Gauss points */
	         wwg2[i][k] = wg2[i]*wg2[k];
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"wwg2[%d]\" failed\n",i); exit(1);}

	free(wg2);
    }
    else
    {printf("gbem_lap_init: Allocate array \"wwg2\" failed\n"); exit(1);}
/** End Initialize Gauss weights for Quasi-singular integration ***********************************/


/** Initialize Gauss weights and shape functions for Singular integrations ************************/
    if ( (shfv = (double ***)malloc(ng1*sizeof(double **))) != NULL ) /* If shfv is successfully allocated */
    {
        for (i=0; i<ng1; i++) /* Loop over all Gauss points */
           if ( (shfv[i] = (double **)malloc(ng1*sizeof(double *))) != NULL ) /* If shfv[i] is successfully allocated */
	   {
	      for (k=0; k<ng1; k++) /* Loop over all Gauss points */
	      if ( (shfv[i][k] = (double *)malloc(mx_nd_elt*sizeof(double))) == NULL ) /* If shfv[i][k] is not successfully allocated */
	      {printf("gbem_lap_init: Allocate array \"shfv[%d][%d]\" failed\n",i,k); exit(1);}
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"shfv[%d]\" failed\n",i); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"shfv\" failed\n"); exit(1);}


    if ( (shfe1 = (double ***)malloc(ng1*sizeof(double **))) != NULL ) /* If shfe1 is successfully allocated */
    {
        for (i=0; i<ng1; i++) /* Loop over all Gauss points */
           if ( (shfe1[i] = (double **)malloc(ng1*sizeof(double *))) != NULL ) /* If shfe1[i] is successfully allocated */
	   {
	      for (k=0; k<ng1; k++) /* Loop over all Gauss points */
	      if ( (shfe1[i][k] = (double *)malloc(mx_nd_elt*sizeof(double))) == NULL ) /* If shfe1[i][k] is not successfully allocated */
	      {printf("gbem_lap_init: Allocate array \"shfe1[%d][%d]\" failed\n",i,k); exit(1);}
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"shfe1[%d]\" failed\n",i); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"shfe1\" failed\n"); exit(1);}


    if ( (shfe2 = (double ***)malloc(ng1*sizeof(double **))) != NULL ) /* If shfe2 is successfully allocated */
    {
        for (i=0; i<ng1; i++) /* Loop over all Gauss points */
           if ( (shfe2[i] = (double **)malloc(ng1*sizeof(double *))) != NULL ) /* If shfe2[i] is successfully allocated */
	   {
	      for (k=0; k<ng1; k++) /* Loop over all Gauss points */
	      if ( (shfe2[i][k] = (double *)malloc(mx_nd_elt*sizeof(double))) == NULL ) /* If shfe2[i][k] is not successfully allocated */
	      {printf("gbem_lap_init: Allocate array \"shfe2[%d][%d]\" failed\n",i,k); exit(1);}
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"shfe2[%d]\" failed\n",i); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"shfe2\" failed\n"); exit(1);}


    if ( (wwg1 = (double **)malloc(ng1*sizeof(double *))) != NULL ) /* If wwg1 is successfully allocated */
    {
        for (i=0; i<ng1; i++) /* Loop over all Gauss-Legendre points */
           if ( (wwg1[i] = (double *)malloc(ng1*sizeof(double))) != NULL ) /* If wwg1[i] is successfully allocated */
	   {
	      for (k=0; k<ng1; k++) /* Loop over all Gauss points */
	      {
	         wwg1[i][k] = wg1[i]*wg1[k];
	         x = xi1[k]; y = xi1[i];
	  	 ym1 = 1.0-y; xm1 = 1.0-x; xy = x*y;
	  	 xyhlf = 0.5*xy; ym1hlf = 0.5*ym1; yp1hlf = 0.5*(1.0+y);
		 shfv[i][k][0] = xm1; shfv[i][k][1] = x*ym1; shfv[i][k][2] = xy; /* shape functions for vertex-adjacent case */
		 shfe1[i][k][0]=xyhlf; shfe1[i][k][1]=xm1+xyhlf; shfe1[i][k][2]=x*ym1; /* shape functions for edge-adjacent case: E' */
		 shfe2[i][k][0]=1.0-x*yp1hlf; shfe2[i][k][1]=x*ym1hlf; shfe2[i][k][2]=xy; /* shape functions for edge-adjacent case: E" */
	      }
	   
	   }
	   else
	   {printf("gbem_lap_init: Allocate array \"wwg1[%d]\" failed\n",i); exit(1);}

	free(wg1);
    }
    else
    {printf("gbem_lap_init: Allocate array \"wwg1\" failed\n"); exit(1);}
/** End Initialize Gauss weights and shape functions for Singular integrations ********************/


/*** Allocate global remaining arrays **************************************************************/
    if ( (pp = (int *)malloc(mx_nd_elt*sizeof(int))) == NULL) /* If pp is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"pp\" failed\n"); exit(1);}

    if ( (xeP = (double **)malloc(mx_nd_elt*sizeof(double *))) != NULL ) /* If xeP is successfully allocated */
    { /* xeP[mx_nd_elt][3] */
        for (k=0; k<mx_nd_elt; k++) /* Loop over all nodes of element P */
           if ( (xeP[k] = (double *)malloc(3*sizeof(double))) == NULL ) /* If xeP[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"xeP[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"xeP\" failed\n"); exit(1);}

    if ( (xeQ = (double **)malloc(mx_nd_elt*sizeof(double *))) != NULL ) /* If xeQ is successfully allocated */
    { /* xeQ[mx_nd_elt][3] */
        for (k=0; k<mx_nd_elt; k++) /* Loop over all nodes of element Q */
           if ( (xeQ[k] = (double *)malloc(3*sizeof(double))) == NULL ) /* If xeQ[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"xeQ[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"xeQ\" failed\n"); exit(1);}

    smx_nd_elt = mx_nd_elt*mx_nd_elt;
    if ( (int_p = (double *)malloc(smx_nd_elt*sizeof(double))) == NULL ) /* If int_p is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"int_p\" failed\n"); exit(1);} /* int_p[mx_nd_elt][mx_nd_elt] */

    if ( (int_f = (double *)malloc(smx_nd_elt*sizeof(double))) == NULL ) /* If int_f is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"int_f\" failed\n"); exit(1);} /* int_f[mx_nd_elt][mx_nd_elt] */

    if ( (int_r = (double *)malloc(smx_nd_elt*sizeof(double))) == NULL ) /* If int_r is not successfully allocated */
    {printf("gbem_lap_init: Allocate array \"int_r\" failed\n"); exit(1);} /* int_r[mx_nd_elt][mx_nd_elt] */

    if ( (dwrk = (double **)malloc(mx_nd_elt*sizeof(double *))) != NULL ) /* If dwrk is successfully allocated */
    { /* dwrk[mx_nd_elt][mx_nd_elt] */
        for (k=0; k<mx_nd_elt; k++) /* Loop over all nodes of element P */
           if ( (dwrk[k] = (double *)malloc(mx_nd_elt*sizeof(double))) == NULL ) /* If dwrk[k] is not successfully allocated */
	   {printf("gbem_lap_init: Allocate array \"dwrk[%d]\" failed\n",k); exit(1);}
    }
    else
    {printf("gbem_lap_init: Allocate array \"dwrk\" failed\n"); exit(1);}
/*** End Allocate global remaining arrays *********************************************************/

}
