/* cubature.c: cubature rules for quadrilaterals and triangles */
/* Philippe Bekaert - Department of Computer Science, K. U. Leuven (Belgium)
 * Philippe.Bekaert@cs.kuleuven.ac.be
 * September, 5 1995 */

#include "cubature.h"

/* ********************* quadrilaterals: [-1,1]^2 ************************** */

/* degree 3, 4 points, 
 * Davis & Rabinowitz, Methods of Numerical Integration, 2nd edition 1984, 
 * p 367 */
#define u	0.81649658092772603272 	/* sqrt(2./3.) */
CUBARULE CRQ3 = {
	"quads degree 3, 4 points",
	3, 4,
        { u	, 0.	,-u	, 0. 	},
        { 0.	, u	, 0.	,-u 	},
        { 1.0	, 1.0	, 1.0	, 1.0	}
};
#undef u

/* degree 3, 4 points, product Gauss-Legendre formula, */
#define	u 0.57735026918962576450 	/* sqrt(1./3.) */
CUBARULE CRQ3PG = {
	"quads degree 3, 4 points, product Gauss formula",
	3 /* degree */, 4 /* points */,	
        { u	, u	,-u	,-u 	},  /* 1st coord. of abscissae */
        { u	,-u	, u	,-u 	},  /* 2nd coord. of abscissae */
        { 1.0	, 1.0	, 1.0	, 1.0	}   /* weights */
};
#undef u

/* degree 5, 7 points, Radon's rule
 * see e.g. Stroud '71. */
#define w1	8./7.
#define w2	5./9.
#define w3	20./63.
#define r	/* sqrt(14./15.) */	0.96609178307929588492
#define s	/* sqrt(1./3.) */	0.57735026918962573106
#define t	/* sqrt(3./5.) */	0.77459666924148340428
CUBARULE CRQ5 = {
	"quads degree 5, 7 points, Radon's rule",
	5 /* degree */, 7 /* points */, 
        { 0.0	, s	, s	,-s	,-s	, r	,-r	},
        { 0.0	, t	,-t	, t	,-t	, 0.0	, 0.0	},
        { w1	, w2	, w2	, w2	, w2	, w3	, w3	}
};
#undef w1
#undef w2
#undef w3
#undef r
#undef s
#undef t

/* degree 5, 9 points product Gauss-Legendre rule 
 * abscissae and weights computed using Stuff/gauleg.c */
#define x0	0.0
#define w0	8./9.
#define x1	0.7745966692414834
#define w1	5./9.
CUBARULE CRQ5PG = {
	"quads degree 5, 9 points product Gauss rule",
	5, 9,
        {-x1	,-x1	,-x1	, x0	, x0	, x0	, x1	, x1	, x1	},
        {-x1	, x0	, x1	,-x1	, x0	, x1	,-x1	, x0	, x1	},
        { w1*w1	, w1*w0	, w1*w1	, w0*w1	, w0*w0	, w0*w1	, w1*w1	, w1*w0	, w1*w1	}
};
#undef w1
#undef x1
#undef w0
#undef x0

/* degree 7, 12 points
 * from: Stroud, "Approximate Calculation of Multiple Integrals", 1971 */
#define r	0.92582009977255141919	/* sqrt(6./7.) */
#define s 	0.38055443320831561227	/* sqrt((114.-3.*sqrt(583.))/287.) */
#define t	0.80597978291859884159	/* sqrt((114.+3.*sqrt(583.))/287.) */
#define w1	0.24197530864197530631	/* 49./810.*4. */
#define w2	0.52059291666739448967	/* (178981.+2769.*sqrt(583.))/1888920.*4. */
#define w3	0.23743177469063023177	/* (178981.-2769.*sqrt(583.))/1888920.*4. */
CUBARULE CRQ7 = {
	"quads degree 7, 12 points",
	7 /* degree */, 12 /* points */, 
        { r	,-r	, 0.0	, 0.0	, s	, s	,-s	,-s	, t	, t	,-t	,-t	},
        { 0.0	, 0.0	, r	,-r	, s	,-s	, s	,-s	, t	,-t	, t	,-t	},
        { w1	, w1	, w1 	, w1	, w2	, w2	, w2	, w2	, w3	, w3	, w3	, w3	}
};
#undef r
#undef s
#undef t
#undef w1
#undef w2
#undef w3

/* degree 7, 16 points product Gauss rule */
#define x1	0.86113631159405257522
#define x2	0.33998104358485626480	
#define w1	0.34785484513745385737
#define w2	0.65214515486254614263
CUBARULE CRQ7PG = {
	"quads degree 7, 16 points product Gauss rule",
	7, 16, 
        {-x1	,-x1	,-x1	,-x1	,-x2	,-x2	,-x2	,-x2	, x2	, x2	, x2	, x2	, x1	, x1	, x1	, x1	},
        {-x1	,-x2	, x2	, x1	,-x1	,-x2	, x2	, x1	,-x1	,-x2	, x2	, x1	,-x1	,-x2	, x2	, x1	},
        { w1*w1	, w1*w2	, w1*w2	, w1*w1	, w2*w1	, w2*w2	, w2*w2	, w2*w1	, w2*w1	, w2*w2	, w2*w2	, w2*w1	, w1*w1	, w1*w2	, w1*w2	, w1*w1	}
};
#undef w2
#undef w1
#undef x2
#undef x1


/* ****************** Triangles: barycentric coordinates ******************* */
/* weights sum to 1. instead of 0.5, which is the area of the triangle
 * 0 <= x+y <= 1, x,y >= 0 */

/* degree 3, 4 points, 
 * Stroud '71 p 308 */
CUBARULE CRT3 = {
	"triangles degree 3, 4 points",
	3, 4,
        {   1./3.,   0.2  ,   0.2  ,   0.6   },
        {   1./3.,   0.2  ,   0.6  ,   0.2   },
        { -9./16., 25./48., 25./48., 25./48. }
};

/* degree 5, 7 points,
 * Stroud '71 p 314 */
#define r	0.1012865073234563
#define s	0.7974269853530873
#define t	1./3.
#define u	0.4701420641051151
#define v	0.05971587178976981
#define A	0.225
#define B	0.1259391805448271
#define C	0.1323941527885062
CUBARULE CRT5 = {
	"triangles degree 5, 7 points",
	5, 7, 
        { t	, r	, r	, s	, u	, u	, v	},
        { t	, r	, s	, r	, u	, v	, u	},
        { A	, B	, B	, B	, C	, C	, C	}
};
#undef C
#undef B
#undef A
#undef v
#undef u
#undef t
#undef s
#undef r

/* degree 7, 12 nodes,
 * Gaterman, "The Construction of Symmetric Cubature Formulas for the 
 * Square and the triangle", Computing, 40, 229-240 (1988) */
#define w1	0.2651702815743450e-1 * 2.
#define a1	0.6238226509439084e-1
#define b1	0.6751786707392436e-1
#define c1	0.8700998678316848
#define w2	0.4388140871444811e-1 * 2.
#define a2	0.5522545665692000e-1
#define b2	0.3215024938520156
#define c2	0.6232720494910644
#define w3	0.2877504278497528e-1 * 2.
#define a3	0.3432430294509488e-1
#define b3	0.6609491961867980
#define c3	0.3047265008681072
#define w4	0.6749318700980879e-1 * 2.
#define a4	0.5158423343536001
#define b4	0.2777161669764050
#define c4	0.2064414986699949
CUBARULE CRT7 = {
	"triangles degree 7, 12 points",
	7, 12, 
        { a1	, b1	, c1	, a2	, b2	, c2	, 
	  a3	, b3	, c3	, a4	, b4	, c4	},
        { b1	, c1	, a1	, b2	, c2	, a2	,
	  b3	, c3	, a3	, b4	, c4	, a4	},
        { w1	, w1	, w1	, w2	, w2	, w2	,
	  w3	, w3	, w3	, w4	, w4	, w4	}
};
#undef c4
#undef b4
#undef a4
#undef w4
#undef c3
#undef b3
#undef a3
#undef w3
#undef c2
#undef b2
#undef a2
#undef w2
#undef c1
#undef b1
#undef a1
#undef w1

static CUBARULE *quadrule[3] = {&CRQ3, &CRQ5, &CRQ7};
static CUBARULE *quadprodrule[3] = {&CRQ3PG, &CRQ5PG, &CRQ7PG};
static CUBARULE *trianglerule[3] = {&CRT3, &CRT5, &CRT7};

/* This routine transforms a rule over [-1,1]^2 to the unit square [0,1]^2 */
static void TransformQuadRule(CUBARULE *rule)
{
	int k;

	for (k=0; k<rule->nrnodes; k++) {
		rule->u[k] = (rule->u[k]+1.)/2.;
		rule->v[k] = (rule->v[k]+1.)/2.;
		rule->w[k] /= 4.;
	}
}

/* This routine should be called during initialization of the program: it
 * transforms the rules over [-1,1]^2 to rules over the unit square [0,1]^2,
 * which we use to map to patches. */
void FixCubatureRules(void)
{
	int i;

	for (i=0; i<9; i++)
		TransformQuadRule(quadrule[i]);
	for (i=0; i<3; i++)
		TransformQuadRule(quadprodrule[i]);
}

#ifdef TEST
#include <stdio.h>

/* computes x^n with n a positive integer */
double power(double x, unsigned n)
{
	double pow = 1.;
	while (n>0) {
		pow *= x;
		n--;
	}
	return pow;
}

/* computes u^a * v^b for a,b >= 0*/
double f(double u, double v, int a, int b)
{
	return power(u, a) * power(v, b);
}

/* integrate u^a * v^b with given cubature rule */
double integrate(CUBARULE *rule, int a, int b)
{
	int k;
	double res = 0.;

	for (k=0; k<rule->nrnodes; k++)
		res += rule->w[k] * f(rule->u[k], rule->v[k], a, b);

	return res;
}

/* test all monomials u^a * v^b with 0 <= a+b <= maxdegree 
 * with given cubature rule */
void testmono(CUBARULE *rule, int maxdegree)
{
	int i, j;

	printf("a \\ b\t");
	for (j=0; j<=maxdegree; j++) 
		printf("\t     %d\t", j);
	printf("\n\n");
	
	for (i=0; i<=maxdegree; i++) {
		printf("%2d\t", i);
		for (j=0; j<=maxdegree; j++) {
			if (i+j > maxdegree)
				printf("\t     .\t");
			else
				printf("%14.14g\t", integrate(rule, i, j));
		}
		printf("\n");
	}
	printf("\n");
}

/* test all monomials u^a * v^b with 0 <= a,b <= maxdegree */
void testprod(CUBARULE *rule, int maxdegree)
{
	int i, j;

	printf("a \\ b\t");
	for (j=0; j<=maxdegree; j++) 
		printf("\t     %d\t", j);
	printf("\n\n");
	
	for (i=0; i<=maxdegree; i++) {
		printf("%2d\t", i);
		for (j=0; j<=maxdegree; j++) {
			printf("%14.14g\t", integrate(rule, i, j));
		}
		printf("\n");
	}
	printf("\n");
}

int main(int argc, char **argv)
{
	int i;

/* rules for qudrilaterals exact for monomials up to a certain degree */
	for (i=0; i<3; i++) {
		printf("%s\n", quadrule[i]->description);
		testmono(quadrule[i], quadrule[i]->degree);
	}

/* product Gauss rules for quadrilaterals */
	for (i=0; i<3; i++) {
		printf("%s\n", quadprodrule[i]->description);
		testprod(quadprodrule[i], quadprodrule[i]->degree);
	}

/* rules for triangles exact for monomials up to a certain degree, note that
 * the result for triangle rules is twice what it should be: the sum of the weights is
 * 1.0, not 0.5, which is the area of the triangle over which we integrate. */
	for (i=0; i<3; i++) {
		printf("%s\n", trianglerule[i]->description);
		testmono(trianglerule[i], trianglerule[i]->degree);
	}

	return 0;
}

#endif /*TEST*/
