#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#include "../src/config.h"
#include <check.h>

#include <maxwell.h>
#include <ctl.h>

/* return a random number in [0,1]: */
double mydrand(void)
{
     double d = rand();
     return (d / (double) RAND_MAX);
}

#define MAX_NSQ_PTS 72
#define NUM_PLANES 100000

#define K_PI 3.141592653589793238462643383279502884197

/* return the angle, in degrees, between two unit-normalized vectors */
double angle(vector3 v1, vector3 v2)
{
     return 180/K_PI * acos(vector3_dot(v1,v2));
}

/* return a random unit vector, uniformly distributed over a sphere */
vector3 random_unit_vector3(void)
{
     double z, t, r;
     vector3 v;

     z = 2*mydrand() - 1;
     t = 2*K_PI*mydrand();
     r = sqrt(1 - z*z);
     v.x = r * cos(t);
     v.y = r * sin(t);
     v.z = z;
     return v;
}

int main(void)
{
     int i, j, nsq, num_sq_pts[] = { 12, 50, 72 };
     real x[MAX_NSQ_PTS], y[MAX_NSQ_PTS], z[MAX_NSQ_PTS], w[MAX_NSQ_PTS];

     srand(time(NULL));

     printf("Testing spherical quadratures to find normals to %d surfaces.\n",
	    NUM_PLANES);

     for (nsq = 0; nsq < sizeof(num_sq_pts) / sizeof(int); ++nsq) {
	  double err_mean, err_std;
	  double min_angle = 360;

	  spherical_quadrature_points(x,y,z,w, num_sq_pts[nsq]);

	  /* compute the minimum angle between pairs of points: */
	  for (i = 0; i < num_sq_pts[nsq]; ++i)
	       for (j = i + 1; j < num_sq_pts[nsq]; ++j) {
		    vector3 v1, v2;
		    double a;
		    v1.x = x[i]; v1.y = y[i]; v1.z = z[i];
		    v2.x = x[j]; v2.y = y[j]; v2.z = z[j];
		    a = angle(v1,v2);
		    if (a < min_angle)
			 min_angle = a;
	       }
	  printf("%d-point formula: minimum angle is %g degrees.\n",
		 num_sq_pts[nsq], min_angle);

	  /* Normals to planes: */
	  err_mean = err_std = 0.0;
	  for (i = 0; i < NUM_PLANES; ++i) {
	       vector3 n, nsum = {0,0,0};
	       double d;
	       
	       n = random_unit_vector3();
	       d = mydrand();
	       for (j = 0; j < num_sq_pts[nsq]; ++j) {
		    vector3 v;
		    real val;
		    v.x = x[j]; v.y = y[j]; v.z = z[j];
		    val = vector3_dot(v,n) >= d ? 12.0 : 1.0;
		    val *= w[j];
		    nsum = vector3_plus(nsum, vector3_scale(val, v));
	       }
	       nsum =  unit_vector3(nsum);
	       {  /* stable one-pass formula for mean and std. deviation: */
		    double e, dev;
		    e = angle(n, nsum);
		    dev = (e - err_mean) / (i + 1);
		    err_mean += dev;
		    err_std += i*(i+1) * dev*dev;
	       }
	  }
	  err_std = sqrt(err_std / (NUM_PLANES - 1));	  
	  printf("planes: mean error angle for %d-pt formula = "
		 "%g +/- %g degrees\n", 
		 num_sq_pts[nsq], err_mean, err_std);

	  /* Normals to spheres: */
	  err_mean = err_std = 0.0;
	  for (i = 0; i < NUM_PLANES; ++i) {
	       vector3 n, nsum = {0,0,0}, c;
	       double r, d;
	       int j;
	       
	       n = random_unit_vector3();
	       d = mydrand();
	       do {
		    r = mydrand() * 10; /* radius of the sphere */
	       } while (r + d < 1.0); /* require sphere to intersect surface */
	       c = vector3_scale(r + d, n);  /* center of the sphere */
	       for (j = 0; j < num_sq_pts[nsq]; ++j) {
		    vector3 v;
		    real val;
		    v.x = x[j]; v.y = y[j]; v.z = z[j];
		    val = vector3_norm(vector3_minus(c,v)) <= r ? 12.0 : 1.0;
		    val *= w[j];
		    nsum = vector3_plus(nsum, vector3_scale(val, v));
	       }
	       nsum =  unit_vector3(nsum);
	       {  /* stable one-pass formula for mean and std. deviation: */
		    double e, dev;
		    e = angle(n, nsum);
		    dev = (e - err_mean) / (i + 1);
		    err_mean += dev;
		    err_std += i*(i+1) * dev*dev;
	       }
	  }
	  err_std = sqrt(err_std / (NUM_PLANES - 1));	  
	  printf("spheres: mean error angle for %d-pt formula = "
		 "%g +/- %g degrees\n", 
		 num_sq_pts[nsq], err_mean, err_std);
     }

     return EXIT_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1