#include "S.h"

#define	min(x,y)  ((x) < (y) ? (x) : (y))
#define	max(x,y)  ((x) > (y) ? (x) : (y))
#define	GAUSSIAN	1
#define SYMMETRIC	0

static int  *iv, liv, lv, tau;
static double	*v;

loess_raw(y, x, weights, robust, d, n, span, degree, nonparametric, 
	drop_square, sum_drop_sqr, cell, surf_stat, surface, parameter, a, 
	xi, vert, vval, diagonal, trL, one_delta, two_delta, setLf)
double	*y, *x, *weights, *robust, *span, *cell, *surface, *xi, *vert, 
	*vval, *diagonal, *trL, *one_delta, *two_delta;
int	*d, *n, *parameter, *a, *degree, *nonparametric, *drop_square, 
	*sum_drop_sqr, *setLf;
char	**surf_stat;
{
	int	zero = 0, one = 1, two = 2, nsing, i, k;
	double	*hat_matrix, *LL;

	*trL = 0;
	loess_workspace(d, n, span, degree, nonparametric, drop_square, 
		sum_drop_sqr, setLf);
        v[1] = *cell;
	if(!strcmp(*surf_stat, "interpolate/none")) {
		F77_SUB(lowesb)(x, y, robust, &zero, &zero, iv, &liv, &lv, v);
		F77_SUB(lowese)(iv, &liv, &lv, v, n, x, surface);
		loess_prune(parameter, a, xi, vert, vval);
	}			
	else if (!strcmp(*surf_stat, "direct/none")) {
		F77_SUB(lowesf)(x, y, robust, iv, &liv, &lv, v, n, x,
			&zero, &zero, surface);
	}
	else if (!strcmp(*surf_stat, "interpolate/1.approx")) {
		F77_SUB(lowesb)(x, y, weights, diagonal, &one, iv, &liv, &lv, v);
		F77_SUB(lowese)(iv, &liv, &lv, v, n, x, surface);
		nsing = iv[29];
		for(i = 0; i < (*n); i++) *trL = *trL + diagonal[i];
		F77_SUB(lowesa)(trL, n, d, &tau, &nsing, one_delta, two_delta);
		loess_prune(parameter, a, xi, vert, vval);
	}
        else if (!strcmp(*surf_stat, "interpolate/2.approx")) {
		F77_SUB(lowesb)(x, y, robust, &zero, &zero, iv, &liv, &lv, v);
		F77_SUB(lowese)(iv, &liv, &lv, v, n, x, surface);
		nsing = iv[29];
		F77_SUB(ehg196)(&tau, d, span, trL);
		F77_SUB(lowesa)(trL, n, d, &tau, &nsing, one_delta, two_delta);
		loess_prune(parameter, a, xi, vert, vval);
	}
	else if (!strcmp(*surf_stat, "direct/approximate")) {
		F77_SUB(lowesf)(x, y, weights, iv, &liv, &lv, v, n, x,
			diagonal, &one, surface);
		nsing = iv[29];
		for(i = 0; i < (*n); i++) *trL = *trL + diagonal[i];
		F77_SUB(lowesa)(trL, n, d, &tau, &nsing, one_delta, two_delta);
	}
	else if (!strcmp(*surf_stat, "interpolate/exact")) {
		hat_matrix = Calloc((*n)*(*n), double);
		LL = Calloc((*n)*(*n), double);
		F77_SUB(lowesb)(x, y, weights, diagonal, &one, iv, &liv, &lv, v);
		F77_SUB(lowesl)(iv, &liv, &lv, v, n, x, hat_matrix);
		F77_SUB(lowesc)(n, hat_matrix, LL, trL, one_delta, two_delta);
		F77_SUB(lowese)(iv, &liv, &lv, v, n, x, surface);
		loess_prune(parameter, a, xi, vert, vval);
		Free(hat_matrix);
		Free(LL);
	}
	else if (!strcmp(*surf_stat, "direct/exact")) {
		hat_matrix = Calloc((*n)*(*n), double);
		LL = Calloc((*n)*(*n), double);
		F77_SUB(lowesf)(x, y, weights, iv, liv, lv, v, n, x,
			hat_matrix, &two, surface);
		F77_SUB(lowesc)(n, hat_matrix, LL, trL, one_delta, two_delta);
                k = (*n) + 1;
		for(i = 0; i < (*n); i++)
			diagonal[i] = hat_matrix[i * k];
		Free(hat_matrix);
		Free(LL);
	}
	loess_free();
}

loess_dfit(y, x, x_evaluate, weights, span, degree, nonparametric, 
	drop_square, sum_drop_sqr, d, n, m, fit)
double	*y, *x, *x_evaluate, *weights, *span, *fit;
int	*degree, *nonparametric, *drop_square, *sum_drop_sqr, *d, *n, *m; 
{
	int	zero = 0, one = 1;
	
        loess_workspace(d, n, span, degree, nonparametric, drop_square,
                sum_drop_sqr, &zero);
	F77_SUB(lowesf)(x, y, weights, iv, &liv, &lv, v, m, x_evaluate,
			&zero, &zero, fit);
	loess_free();
}

loess_dfitse(y, x, x_evaluate, weights, robust, family, span, degree, 
	nonparametric, drop_square, sum_drop_sqr, d, n, m, fit, L)
double	*y, *x, *x_evaluate, *weights, *robust, *span, *fit, *L;
int	*family, *degree, *nonparametric, *drop_square, *sum_drop_sqr, 
	*d, *n, *m; 
{
	int	zero = 0, one = 1, two = 2;
	
        loess_workspace(d, n, span, degree, nonparametric, drop_square,
                sum_drop_sqr, &zero);
	if(*family == GAUSSIAN)
		F77_SUB(lowesf)(x, y, weights, iv, &liv, &lv, v, m, 
				x_evaluate, L, &two, fit);
	else if(*family == SYMMETRIC)
	{
		F77_SUB(lowesf)(x, y, weights, iv, &liv, &lv, v, m,
				x_evaluate, L, &two, fit);
		F77_SUB(lowesf)(x, y, robust, iv, &liv, &lv, v, m,
				x_evaluate, &zero, &zero, fit);
	}	
	loess_free();
}
loess_ifit(parameter, a, xi, vert, vval, m, x_evaluate, fit)
double	*xi, *vert, *vval, *x_evaluate, *fit;
int	*parameter, *a, *m;
{
	loess_grow(parameter, a, xi, vert, vval);
	F77_SUB(lowese)(iv, &liv, &lv, v, m, x_evaluate, fit);
	loess_free();
}

loess_ise(y, x, x_evaluate, weights, span, degree, nonparametric, 
	drop_square, sum_drop_sqr, cell, d, n, m, fit, L)
double	*y, *x, *x_evaluate, *weights, *span, *cell, *fit, *L;
int	*degree, *nonparametric, *drop_square, *sum_drop_sqr, *d, *n, *m; 
{
	int	zero = 0, one = 1;
	
        loess_workspace(d, n, span, degree, nonparametric, drop_square,
                sum_drop_sqr, &one);
	v[1] = *cell;
	F77_SUB(lowesb)(x, y, weights, &zero, &zero, iv, &liv, &lv, v);
	F77_SUB(lowesl)(iv, &liv, &lv, v, m, x_evaluate, L);
	loess_free();
}

loess_workspace(d, n, span, degree, nonparametric, drop_square, 
	sum_drop_sqr, setLf)
int	*d, *n, *degree, *nonparametric, *drop_square, *sum_drop_sqr, 
	*setLf;
double	*span;
{
	int	D, N, tau0, nvmax, nf, version = 106, i;

	D = *d;
	N = *n;
	nvmax = max(200, N);
        nf = min(N, floor(N * (*span)));
        tau0 = ((*degree) > 1) ? ((D + 2) * (D + 1) * 0.5) : (D + 1);
        tau = tau0 - (*sum_drop_sqr);
        lv = 50 + (3 * D + 3) * nvmax + N + (tau0 + 2) * nf;
	liv = 50 + ((int)pow((double)2, (double)D) + 4) * nvmax + 2 * N;
	if(*setLf) {
		lv = lv + (D + 1) * nf * nvmax;
		liv = liv + nf * nvmax;	
	}
        iv = Calloc(liv, int);
        v = Calloc(lv, double);

        F77_SUB(lowesd)(&version, iv, &liv, &lv, v, d, n, span, degree, 
			&nvmax, setLf);
        iv[32] = *nonparametric;
        for(i = 0; i < D; i++)
                iv[i + 40] = drop_square[i];
}

loess_prune(parameter, a, xi, vert, vval)
double	*xi, *vert, *vval;
int	*parameter, *a;
{
	int	d, vc, a1, v1, xi1, vv1, nc, nv, nvmax, i, j, k;
	
	d = iv[1];
	vc = iv[3] - 1;
	nc = iv[4];
	nv = iv[5];
	a1 = iv[6] - 1;
	v1 = iv[10] - 1;
	xi1 = iv[11] - 1;
	vv1 = iv[12] - 1;
	nvmax = iv[13];

	for(i = 0; i < 5; i++)
		parameter[i] = iv[i + 1];
	parameter[5] = iv[21] - 1;
	parameter[6] = iv[14] - 1;

	for(i = 0; i < d; i++){
		k = nvmax * i;
		vert[i] = v[v1 + k];
		vert[i + d] = v[v1 + vc + k];
	}
	for(i = 0; i < nc; i++) {
		xi[i] = v[xi1 + i];
		a[i] = iv[a1 + i];
	}
	k = (d + 1) * nv;
	for(i = 0; i < k; i++)
		vval[i] = v[vv1 + i];
}

loess_grow(parameter, a, xi, vert, vval)
double	*xi, *vert, *vval;
int	*parameter, *a;
{
	int	d, vc, nc, nv, a1, v1, xi1, vv1, i, j, k;

	d = parameter[0];
	vc = parameter[2];
	nc = parameter[3];
	nv = parameter[4];
	liv = parameter[5];
	lv = parameter[6];
	iv = Calloc(liv, int);
	v = Calloc(lv, double);

	iv[1] = d;
	iv[2] = parameter[1];
	iv[3] = vc;
	iv[5] = iv[13] = nv;
	iv[4] = iv[16] = nc;
	iv[6] = 50;
	iv[7] = iv[6] + nc;
	iv[8] = iv[7] + vc * nc;
	iv[9] = iv[8] + nc;
	iv[10] = 50;
	iv[12] = iv[10] + nv * d;
	iv[11] = iv[12] + (d + 1) * nv;
	iv[27] = 173;

	v1 = iv[10] - 1;
	xi1 = iv[11] - 1;
	a1 = iv[6] - 1;
	vv1 = iv[12] - 1;
	
        for(i = 0; i < d; i++) {
		k = nv * i;
		v[v1 + k] = vert[i];
		v[v1 + vc - 1 + k] = vert[i + d];
	}
        for(i = 0; i < nc; i++) {
                v[xi1 + i] = xi[i];
                iv[a1 + i] = a[i];
        }
	k = (d + 1) * nv;
	for(i = 0; i < k; i++)
		v[vv1 + i] = vval[i];

	F77_SUB(ehg169)(&d, &vc, &nc, &nc, &nv, &nv, v+v1, iv+a1,
			v+xi1, iv+iv[7]-1, iv+iv[8]-1, iv+iv[9]-1);
}

loess_free()
{
        Free(v);
        Free(iv);
}

/* begin ehg's FORTRAN-callable C-codes */

void
F77_SUB(ehg182)(i)
  int *i;
{
  char *mess, mess2[50];
    switch(*i){
case 100: mess="wrong version number in lowesd.  Probably typo in caller."; break;
case 101: mess="d>dMAX in ehg131.  Need to recompile with increased dimensions."; break;
case 102: mess="liv too small.   (Discovered by lowesd)"; break;
case 103: mess="lv too small.    (Discovered by lowesd)"; break;
case 104: mess="span too small.  fewer data values than degrees of freedom."; break;
case 105: mess="k>d2MAX in ehg136.  Need to recompile with increased dimensions."; break;
case 106: mess="lwork too small"; break;
case 107: mess="invalid value for kernel"; break;
case 108: mess="invalid value for ideg"; break;
case 109: mess="lowstt only applies when kernel=1."; break;
case 110: mess="not enough extra workspace for robustness calculation"; break;
case 120: mess="zero-width neighborhood. make span bigger"; break;
case 121: mess="all data on boundary of neighborhood. make span bigger"; break;
case 122: mess="extrapolation not allowed with blending"; break;
case 123: mess="ihat=1 (diag L) in l2fit only makes sense if z=x (eval=data)."; break;
case 171: mess="lowesd must be called first."; break;
case 172: mess="lowesf must not come between lowesb and lowese, lowesr, or lowesl."; break;
case 173: mess="lowesb must come before lowese, lowesr, or lowesl."; break;
case 174: mess="lowesb need not be called twice."; break;
case 175: mess="need setLf=.true. for lowesl."; break;
case 180: mess="nv>nvmax in cpvert."; break;
case 181: mess="nt>20 in eval."; break;
case 182: mess="svddc failed in l2fit."; break;
case 183: mess="didnt find edge in vleaf."; break;
case 184: mess="zero-width cell found in vleaf."; break;
case 185: mess="trouble descending to leaf in vleaf."; break;
case 186: mess="insufficient workspace for lowesf."; break;
case 187: mess="insufficient stack space"; break;
case 188: mess="lv too small for computing explicit L"; break;
case 191: mess="computed trace L was negative; something is wrong!"; break;
case 192: mess="computed delta was negative; something is wrong!"; break;
case 193: mess="workspace in loread appears to be corrupted"; break;
case 194: mess="trouble in l2fit/l2tr"; break;
case 195: mess="only constant, linear, or quadratic local models allowed"; break;
case 196: mess="degree must be at least 1 for vertex influence matrix"; break;
case 999: mess="not yet implemented"; break;
default: sprintf(mess=mess2,"Assert failed; error code %d\n",*i); break;
    }
    Recover(mess,NULL_ENTRY);  /* in /usr/s/current/src/qpe/debug.c */
}

void
F77_SUB(ehg183)(s,i,n,inc)
  char *s;
  int *i, *n, *inc;
{
  char mess[4000], num[20];
  int j;
  strcpy(mess,s);
  for (j=0; j<*n; j++) {
    sprintf(num," %d",i[j * *inc]);
    strcat(mess,num);
  }
  strcat(mess,"\n");
  Warning(mess,NULL_ENTRY);
}

void
F77_SUB(ehg184)(s,x,n,inc)
  char *s;
  double *x;
  int *n, *inc;
{
  char mess[4000], num[30];
  int j;
  strcpy(mess,s);
  for (j=0; j<*n; j++) {
    sprintf(num," %.5g",x[j * *inc]);
    strcat(mess,num);
  }
  strcat(mess,"\n");
  Warning(mess,NULL_ENTRY);
}
