
#define MXPASS   4			     /*  define max number of passes to use */




#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

double sqrt(double x);
double fabs(double x);

inline double square(double x)
{
  return x*x;
}

#define MAX(a, b)   (((a) > (b)) ? (a) :  (b))
#define ABS(a)      (((a) > 0)   ? (a) : -(a))

int wlst[MXPASS];
int N;
typedef struct {double sigma; unsigned char wlst[MXPASS]; char passes;} PARAMS;

PARAMS *params;
float SCALE, MXSIGMA;
int MXWIDTH;

void approx(int pass, double sum, int last)
{
  int i, j;
  int neven;
  
  if (pass == MXPASS) {
    double sigma = sqrt(sum);
    int hash  = (int) (sigma * SCALE + 0.5);
    int passes;


    if (hash < 0  || hash >= N) {
      return;
    }

    neven = 0;
    for (passes = 0; passes < MXPASS  &&  wlst[passes] > 1; passes++) {
      neven += ((wlst[passes] & 0x1) == 0);  /* count # even width */
    }

    if ((neven & 0x1) == 1) {                /* odd # => can't make grd = 0 */
      return;
    }

    if ((passes <= 2  &&  sigma <  0.90  &&  sigma > 0.2)  || /* OK to use */
	(passes == 3  &&  sigma <= 15.0)  ||
	(passes == 4  &&  sigma >  10.0)) {
      int use_it = 0;
      
      if (params[hash].sigma != 0.0) {	     /* collision .. check which better */
	double mean1, mean2, var1, var2;

	mean1 = mean2 = 0.0;
	for (j = 0; j < passes; j++)
	  mean1 += (double) wlst[j];

	for (j = 0; j < params[hash].passes; j++)
	  mean2 += (double) params[hash].wlst[j];

	mean1 /= (double) passes;
	mean2 /= (double) params[hash].passes;

	var1 = var2 = 0.0;
	for (j = 0; j < passes; j++)
	  var1 += square((double) wlst[j] - mean1);

	for (j = 0; j < params[hash].passes; j++)
	  var2 += square((double) params[hash].wlst[j] - mean2);

	var1 /= (double) passes;
	var2 /= (double) params[hash].passes;
	use_it = (var1/mean1 < var2/mean2);  /* use if rel. variance less */
      }
      else {				     /* no collision => use it */
	use_it = 1;
      }

      if (use_it) {			     /* insert into table */
	params[hash].sigma = sigma;
	params[hash].passes = passes;
	for (j = 0; j < MXPASS; j++) {
	  params[hash].wlst[j] = wlst[j];
	}
      }
    }
  }
  else {
    int low = 1;

    if (pass > 0 && last - 20 > 1)	     /* for speed */
      low = last - 20;
    
    for (i = last; i >= low; i--) {
      wlst[pass] = i;
      approx(pass + 1, sum + (square(i) - 1.0) / 12.0, i);
    }
  }
}


print_params()
{
  int i, j;
  double mxerr = 0.0;


  printf("#define MXPASS  %d\n", MXPASS);
  printf("#define SCALE   %f\n", SCALE);
  printf("#define MXSIGMA %f\n", MXSIGMA);
  printf("#define MXWIDTH %d\n\n", MXWIDTH);
  printf("\ntypedef struct {float sigma; unsigned char wlst[MXPASS];} PARAMS;\n\n");
  printf("static PARAMS params[%d] = {\n", N);

  for (i = 0; i < N; i++) {
    printf("  {%15.10lf, {", params[i].sigma);

    for (j = 0; j < MXPASS-1; j++)
      printf("%3d, ", params[i].wlst[j]);

    printf("%3d}},", params[i].wlst[j]);
    printf("\t/* sigma = %7.3lf */\n", i / SCALE);
    if (i > SCALE)
      mxerr = MAX(mxerr, ABS(i/SCALE - params[i].sigma)/(i/SCALE));
  }

  printf("};\n\n/* mxerr = %7.3lf%% */\n\n", 100.0*mxerr);
  printf("static int nparams = %d;\n\n\n", N);
}


void fill_in()
{
  int i, j, k;

  
  for (i = 0; i < N; i++) {
    if (params[i].sigma == 0.0) {

      j = i - 1;
      for (k = i + 1; k < N  &&  params[k].sigma == 0.0; k++)
	;

      if (j >= 0  &&  k < N) {
	float s = (float) i / SCALE;
	params[i] = params[(fabs(params[k].sigma - s) > fabs(params[j].sigma - s))
			   ? j
			   : k];	  
      }
      else if (j < 0  &&  k >= N) {
	assert(0);
      }
      else {
	params[i] = params[(j >= 0) ? j : k];
      }
    }
  }
}


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

  
  if (argc != 3) {
    fprintf(stderr, "Usage: approx-table scale-factor max-sigma\n\n");
    return -1;
  }

  SCALE   = atof(argv[1]);
  MXSIGMA = atof(argv[2]);
  MXWIDTH = (int) (2.0 * MXSIGMA + 0.5);
  N = (int) (MXSIGMA*SCALE + 1.5);

 /* printf("scale = %f   mxsigma = %f   mxwidth = %d   N = %d\n\n",
    SCALE, MXSIGMA, MXWIDTH, N); */
  
  params = (PARAMS *) malloc(N * sizeof(PARAMS));
  assert(params != (PARAMS *) NULL);

  for (i = 0; i < N; i++) {
    params[i].sigma= 0.0;
  }
  
  approx(0, 0.0, MXWIDTH);
  fill_in();

#if 0
  { int i, j, mx, mn, dispar;

    dispar = 0;
    
    for (i = 0; i < N; i++) {
      if (params[i].sigma == 0.0)
	continue;

      mx = mn = params[i].wlst[0];
      for (j = 1; j < params[i].passes; j++) {
	if (params[i].wlst[j] > mx)
	  mx = params[i].wlst[j];
	else if (params[i].wlst[j] < mn)
	  mn = params[i].wlst[j];
      }

      if (mx - mn > dispar)
	dispar = mx - mn;
    }

    printf("dispar = %d\n\n", dispar);
  }
#endif
  
  print_params();

  return 0;
}


