diff rDiff/src/locfit/Source/liblfev.c @ 0:0f80a5141704

version 0.3 uploaded
author vipints
date Thu, 14 Feb 2013 23:38:36 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rDiff/src/locfit/Source/liblfev.c	Thu Feb 14 23:38:36 2013 -0500
@@ -0,0 +1,4355 @@
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+
+#include "mex.h"
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+extern void fitoptions();
+
+static double hmin, gmin, sig2, pen, vr, tb;
+static lfit *blf;
+static design *bdes;
+
+int procvbind(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ double s0, s1, bi;
+  int i, ii, k;
+  k = procv_var(des,lf,v);
+  wdiag(&lf->lfd, &lf->sp, des,des->wd,&lf->dv,0,1,0);
+  s0 = s1 = 0.0;
+  for (i=0; i<des->n; i++)
+  { ii = des->ind[i];
+    s0+= prwt(&lf->lfd,ii)*des->wd[i]*des->wd[i];
+    bi = prwt(&lf->lfd,ii)*fabs(des->wd[i]*ipower(dist(des,ii),deg(&lf->sp)+1));
+    s1+= bi*bi;
+  }
+  vr += s0;
+  tb += s1;
+  return(k);
+}
+
+double bcri(h,c,cri)
+double h;
+int c, cri;
+{ double num, den, res[10];
+  int (*pv)();
+  if (c==DALP)
+    blf->sp.nn = h;
+  else
+    blf->sp.fixh = h;
+  if ((cri&63)==BIND)
+  { pv = procvbind;
+    vr = tb = 0.0;
+  }
+  else pv = procvstd;
+  if (cri<64) startlf(bdes,blf,pv,0);
+  switch(cri&63)
+  { case BGCV:
+      ressumm(blf,bdes,res);
+      num = -2*blf->lfd.n*res[0];
+      den = blf->lfd.n-res[1];
+      return(num/(den*den));
+    case BCP:
+      ressumm(blf,bdes,res);
+      return(-2*res[0]/sig2-blf->lfd.n+pen*res[1]);
+    case BIND:
+      return(vr+pen*pen*tb);
+  } 
+  LERR(("bcri: unknown criterion"));
+  return(0.0);
+}
+
+void bsel2(h0,g0,ifact,c,cri)
+double h0, g0, ifact;
+int c, cri;
+{ int done, inc;
+  double h1, g1;
+  h1 = h0; g1 = g0;
+  done = inc = 0;
+  while (!done)
+  { h1 *= 1+ifact;
+    g0 = g1;
+    g1 = bcri(h1,c,cri);
+    if (g1<gmin) { hmin = h1; gmin = g1; }
+    if (g1>g0) inc++; else inc = 0;
+    switch(cri)
+    { case BIND:
+        done = (inc>=4) & (vr<blf->fp.nv);
+        break;
+      default:
+        done = (inc>=4);
+    }
+  }
+}
+
+void bsel3(h0,g0,ifact,c,cri)
+double h0, g0, ifact;
+int c, cri;
+{ double h1, g1;
+  int i;
+  hmin = h0; gmin = g0;
+  for (i=-1; i<=1; i++) if (i!=0)
+  { h1 = h0*(1+i*ifact);
+    g1 = bcri(h1,c,cri);
+    if (g1<gmin) { hmin = h1; gmin = g1; }
+  }
+  return;
+}
+
+void bselect(lf,des,c,cri,pn)
+lfit *lf;
+design *des;
+int c, cri;
+double pn;
+{ double h0, g0, ifact;
+  int i;
+  pen = pn;
+  blf = lf;
+  bdes = des;
+  if (cri==BIND) pen /= factorial(deg(&lf->sp)+1);
+  hmin = h0 = (c==DFXH) ? fixh(&lf->sp) : nn(&lf->sp);
+  if (h0==0) LERR(("bselect: initial bandwidth is 0"));
+  if (lf_error) return;
+  sig2 = 1.0;
+
+  gmin = g0 = bcri(h0,c,cri);
+  if (cri==BCP)
+  { sig2 = rv(&lf->fp);
+    g0 = gmin = bcri(h0,c,cri+64);
+  }
+  
+  ifact = 0.3;
+  bsel2(h0,g0,ifact,c,cri);
+
+  for (i=0; i<5; i++)
+  { ifact = ifact/2;
+    bsel3(hmin,gmin,ifact,c,cri);
+  }
+  if (c==DFXH)
+    fixh(&lf->sp) = hmin;
+  else
+    nn(&lf->sp) = hmin;
+  startlf(des,lf,procvstd,0);
+  ressumm(lf,des,lf->fp.kap);
+}
+
+double compsda(x,h,n)
+double *x, h;
+int n;
+/* n/(n-1) * int( fhat''(x)^2 dx ); bandwidth h */
+{ int i, j;
+  double ik, sd, z;
+  ik = wint(1,NULL,0,WGAUS);
+  sd = 0;
+
+  for (i=0; i<n; i++)
+    for (j=i; j<n; j++)
+    { z = (x[i]-x[j])/h;
+      sd += (2-(i==j))*Wconv4(z,WGAUS)/(ik*ik);
+    }
+  sd = sd/(n*(n-1)*h*h*h*h*h);
+  return(sd);
+}
+
+double widthsj(x,lambda,n)
+double *x, lambda;
+int n;
+{ double ik, a, b, td, sa, z, c, c1, c2, c3;
+  int i, j;
+  a = GFACT*0.920*lambda*exp(-log((double)n)/7)/SQRT2;
+  b = GFACT*0.912*lambda*exp(-log((double)n)/9)/SQRT2;
+  ik = wint(1,NULL,0,WGAUS);
+
+  td = 0;
+  for (i=0; i<n; i++)
+    for (j=i; j<n; j++)
+    { z = (x[i]-x[j])/b;
+      td += (2-(i==j))*Wconv6(z,WGAUS)/(ik*ik);
+    }
+
+  td = -td/(n*(n-1));
+  j = 2.0;
+  c1 = Wconv4(0.0,WGAUS);
+  c2 = wint(1,&j,1,WGAUS);
+  c3 = Wconv(0.0,WGAUS);  /* (2*c1/(c2*c3))^(1/7)=1.357 */
+  sa = compsda(x,a,n);
+  c = b*exp(log(c1*ik/(c2*c3*GFACT*GFACT*GFACT*GFACT)*sa/td)/7)*SQRT2;
+  return(c);
+}
+
+void kdecri(x,h,res,c,k,ker,n)
+double *x, h, *res, c;
+int k, ker, n;
+{ int i, j;
+  double degfree, dfd, pen, s, r0, r1, d0, d1, ik, wij;
+
+  if (h<=0) WARN(("kdecri, h = %6.4f",h));
+
+  res[0] = res[1] = 0.0;
+  ik = wint(1,NULL,0,ker);
+  switch(k)
+  { case 1: /* aic */
+      pen = 2.0;
+      for (i=0; i<n; i++)
+      { r0 = d0 = 0.0;
+        for (j=0; j<n; j++)
+        { s = (x[i]-x[j])/h;
+          r0 += W(s,ker);
+          d0 += s*s*Wd(s,ker);
+        }
+        d0 = -(d0+r0)/(n*h*h*ik);  /* d0 = d/dh fhat(xi) */
+        r0 /= n*h*ik;              /* r0 = fhat(xi) */
+        res[0] += -2*log(r0)+pen*W(0.0,ker)/(n*h*ik*r0);
+        res[1] += -2*d0/r0-pen*W(0.0,ker)/(n*h*ik*r0)*(d0/r0+1.0/h);
+      }
+      return;
+    case 2: /* ocv */
+      for (i=0; i<n; i++)
+      { r0 = 0.0; d0 = 0.0;
+        for (j=0; j<n; j++) if (i!=j)
+        { s = (x[i]-x[j])/h;
+          r0 += W(s,ker);
+          d0 += s*s*Wd(s,ker);
+        }
+        d0 = -(d0+r0)/((n-1)*h*h);
+        r0 = r0/((n-1)*h);
+        res[0] -= log(r0);
+        res[1] -= d0/r0;
+      }
+      return;
+    case 3: /* lscv */
+      r0 = r1 = d0 = d1 = degfree = 0.0;
+      for (i=0; i<n; i++)
+      { dfd = 0.0;
+        for (j=0; j<n; j++)
+        { s = (x[i]-x[j])/h;
+          wij = W(s,ker);
+          dfd += wij;
+/* 
+ *  r0 = \int fhat * fhat = sum_{i,j} W*W( (Xi-Xj)/h ) / n^2 h.
+ *  d0 is it's derivative wrt h.
+ *
+ *  r1 = 1/n sum( f_{-i}(X_i) ).
+ *  d1 is  it's derivative wrt h.
+ *
+ *  degfree = sum_i ( W_0 / sum_j W( (Xi-Xj)/h ) ) is fitted d.f.
+ */
+          r0 += Wconv(s,ker);
+          d0 += -s*s*Wconv1(s,ker);
+          if (i != j)
+          { r1 += wij;
+            d1 += -s*s*Wd(s,ker);
+          }
+        }
+        degfree += 1.0/dfd;
+      }
+      d1 -= r1;
+      d0 -= r0;
+      res[0] = r0/(n*n*h*ik*ik)   - 2*r1/(n*(n-1)*h*ik);
+      res[1] = d0/(n*n*h*h*ik*ik) - 2*d1/(n*(n-1)*h*h*ik);
+      res[2] = degfree;
+      return;
+    case 4: /* bcv */
+      r0 = d0 = 0.0;
+      for (i=0; i<n; i++)
+        for (j=i+1; j<n; j++)
+        { s = (x[i]-x[j])/h;
+          r0 += 2*Wconv4(s,ker);
+          d0 += 2*s*Wconv5(s,ker);
+        }
+      d0 = (-d0-r0)/(n*n*h*h*ik*ik);
+      r0 = r0/(n*n*h*ik*ik);
+      j = 2.0;
+      d1 = wint(1,&j,1,ker);
+      r1 = Wconv(0.0,ker);
+      res[0] = (d1*d1*r0/4+r1/(n*h))/(ik*ik);
+      res[1] = (d1*d1*d0/4-r1/(n*h*h))/(ik*ik);
+      return;
+    case 5: /* sjpi */
+      s = c*exp(5*log(h)/7)/SQRT2;
+      d0 = compsda(x,s,n);
+      res[0] = d0; /* this is S(alpha) in SJ */
+      res[1] = exp(log(Wikk(WGAUS,0)/(d0*n))/5)-h;
+      return;
+    case 6: /* gas-k-k */
+      s = exp(log(1.0*n)/10)*h;
+      d0 = compsda(x,s,n);
+      res[0] = d0;
+      res[1] = exp(log(Wikk(WGAUS,0)/(d0*n))/5)-h;
+      return;
+  }
+  LERR(("kdecri: what???"));
+  return;
+}
+
+double esolve(x,j,h0,h1,k,c,ker,n)
+double *x, h0, h1, c;
+int j, k, ker, n;
+{ double h[7], d[7], r[7], res[4], min, minh, fact;
+  int i, nc;
+  min = 1.0e30; minh = 0.0;
+  fact = 1.00001;
+  h[6] = h0; kdecri(x,h[6],res,c,j,ker,n);
+  r[6] = res[0]; d[6] = res[1];
+  if (lf_error) return(0.0);
+  nc = 0;
+  for (i=0; i<k; i++)
+  { h[5] = h[6]; r[5] = r[6]; d[5] = d[6];
+    h[6] = h0*exp((i+1)*log(h1/h0)/k);
+    kdecri(x,h[6],res,c,j,ker,n);
+    r[6] = res[0]; d[6] = res[1];
+    if (lf_error) return(0.0);
+    if (d[5]*d[6]<0)
+    { h[2] = h[0] = h[5]; d[2] = d[0] = d[5]; r[2] = r[0] = r[5];
+      h[3] = h[1] = h[6]; d[3] = d[1] = d[6]; r[3] = r[1] = r[6];
+      while ((h[3]>fact*h[2])|(h[2]>fact*h[3]))
+      { h[4] = h[3]-d[3]*(h[3]-h[2])/(d[3]-d[2]);
+        if ((h[4]<h[0]) | (h[4]>h[1])) h[4] = (h[0]+h[1])/2;
+        kdecri(x,h[4],res,c,j,ker,n);
+        r[4] = res[0]; d[4] = res[1];
+        if (lf_error) return(0.0);
+        h[2] = h[3]; h[3] = h[4];
+        d[2] = d[3]; d[3] = d[4];
+        r[2] = r[3]; r[3] = r[4];
+        if (d[4]*d[0]>0) { h[0] = h[4]; d[0] = d[4]; r[0] = r[4]; }
+                    else { h[1] = h[4]; d[1] = d[4]; r[1] = r[4]; }
+      }
+      if (j>=4) return(h[4]); /* first min for BCV etc */
+      if (r[4]<=min) { min = r[4]; minh = h[4]; }
+      nc++;
+    }
+  }
+  if (nc==0) minh = (r[5]<r[6]) ? h0 : h1;
+  return(minh);
+}
+
+void kdeselect(band,x,ind,h0,h1,meth,nm,ker,n)
+double h0, h1, *band, *x;
+int *ind, nm, ker, n, *meth;
+{ double scale, c;
+  int i, k;
+  k = n/4;
+  for (i=0; i<n; i++) ind[i] = i;
+  scale = kordstat(x,n+1-k,n,ind) - kordstat(x,k,n,ind);
+  c = widthsj(x,scale,n);
+  for (i=0; i<nm; i++)
+    band[i] = esolve(x,meth[i],h0,h1,10,c,ker,n);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ *   The function dens_integrate(lf,des,z) is used to integrate a density
+ *   estimate (z=1) or the density squared (z=2). This is used to renormalize
+ *   the estimate (function dens_renorm) or in the computation of LSCV
+ *   (in modlscv.c). The implementation is presently for d=1.
+ *
+ *   The computation orders the fit points selected by locfit, and
+ *   integrates analytically over each interval. For the log-link,
+ *   the interpolant used is peicewise quadratic (with one knot in
+ *   the middle of each interval); this differs from the cubic interpolant
+ *   used elsewhere in Locfit.
+ *
+ *   TODO: allow for xlim. What can be done simply in >=2 dimensions?
+ */
+
+#include "lfev.h"
+
+/*
+ * Finds the order of observations in the array x, and
+ * stores in integer array ind.
+ * At input, lset l=0 and r=length(x)-1.
+ * At output, x[ind[0]] <= x[ind[1]] <= ...
+ */
+void lforder(ind,x,l,r)
+int *ind, l, r;
+double *x;
+{ double piv;
+  int i, i0, i1;
+  piv = (x[ind[l]]+x[ind[r]])/2;
+  i0 = l; i1 = r;
+  while (i0<=i1)
+  { while ((i0<=i1) && (x[ind[i0]]<=piv)) i0++;
+    while ((i0<=i1) && (x[ind[i1]]>piv))  i1--;
+    if (i0<i1)
+    { ISWAP(ind[i0],ind[i1]);
+      i0++; i1--;
+    }
+  }
+  /* now, x[ind[l..i1]] <= piv < x[ind[i0..r]].
+     put the ties in the middle */
+  while ((i1>=l) && (x[ind[i1]]==piv)) i1--;
+  for (i=l; i<=i1; i++)
+    if (x[ind[i]]==piv)
+    { ISWAP(ind[i],ind[i1]);
+      while (x[ind[i1]]==piv) i1--;
+    }
+
+  if (l<i1) lforder(ind,x,l,i1);
+  if (i0<r) lforder(ind,x,i0,r);
+}
+
+/*
+ *  estdiv integrates the density between fit points x0 and x1.
+ *  f0, f1 are function values, d0, d1 are derivatives.
+ */
+double estdiv(x0,x1,f0,f1,d0,d1,lin)
+double x0, x1, f0, f1, d0, d1;
+int lin;
+{ double cf[4], I[2], dlt, e0, e1;
+
+  if (x0==x1) return(0.0);
+
+  if (lin==LIDENT)
+  {
+/* cf are integrals of hermite polynomials.
+ * Then adjust for x1-x0.
+ */
+    cf[0] = cf[1] = 0.5;
+    cf[2] = 1.0/12.0; cf[3] = -cf[2];
+    return( (cf[0]*f0+cf[1]*f1)*(x1-x0)
+          + (cf[2]*d0+cf[3]*d1)*(x1-x0)*(x1-x0) );
+  }
+
+/*
+ * this is for LLOG
+ */
+
+  dlt = (x1-x0)/2;
+  cf[0] = f0;
+  cf[1] = d0;
+  cf[2] = ( 2*(f1-f0) - dlt*(d1+3*d0) )/(4*dlt*dlt);
+  recurint(0.0,dlt,cf,I,0,WRECT);
+  e0 = I[0];
+
+  cf[0] = f1;
+  cf[1] = -d1;
+  cf[2] = ( 2*(f0-f1) + dlt*(d0+3*d1) )/( 4*dlt*dlt );
+  recurint(0.0,dlt,cf,I,0,WRECT);
+  e1 = I[0];
+
+  return(e0+e1);
+}
+
+/*
+ *   Evaluate the integral of the density estimate to the power z.
+ *   This would be severely messed up, if I ever implement parcomp
+ *   for densities.
+ */
+double dens_integrate(lf,des,z)
+lfit *lf;
+design *des;
+int z;
+{ int has_deriv, i, i0, i1, nv, *ind;
+  double *xev, *fit, *deriv, sum, term;
+  double d0, d1, f0, f1;
+  fitpt *fp;
+
+  fp = &lf->fp;
+
+  if (fp->d >= 2)
+  { WARN(("dens_integrate requires d=1"));
+    return(0.0);
+  }
+
+  has_deriv = (deg(&lf->sp) > 0); /* not right? */
+  fit = fp->coef;
+  if (has_deriv)
+    deriv = &fit[fp->nvm];
+  xev = evp(fp);
+
+  /*
+   * order the vertices
+   */
+  nv = fp->nv;
+  if (lf->lfd.n<nv) return(0.0);
+  ind = des->ind;
+  for (i=0; i<nv; i++) ind[i] = i;
+  lforder(ind,xev,0,nv-1);
+  sum = 0.0;
+
+  /*
+   * Estimate the contribution of the boundaries.
+   * should really check flim here.
+   */
+  i0 = ind[0]; i1 = ind[1];
+  f1 = fit[i0];
+  d1 = (has_deriv) ? deriv[i0] :
+         (fit[i1]-fit[i0])/(xev[i1]-xev[i0]);
+  if (d1 <= 0) WARN(("dens_integrate - ouch!"));
+  if (z==2)
+  { if (link(&lf->sp)==LLOG)
+    { f1 *= 2; d1 *= 2; }
+    else
+    { d1 = 2*d1*f1; f1 = f1*f1; }
+  }
+  term = (link(&lf->sp)==LIDENT) ? f1*f1/(2*d1) : exp(f1)/d1;
+  sum += term;
+
+  i0 = ind[nv-2]; i1 = ind[nv-1];
+  f0 = fit[i1];
+  d0 = (has_deriv) ? deriv[i1] :
+         (fit[i1]-fit[i0])/(xev[i1]-xev[i0]);
+  if (d0 >= 0) WARN(("dens_integrate - ouch!"));
+  if (z==2)
+  { if (link(&lf->sp)==LLOG)
+    { f0 *= 2; d0 *= 2; }
+    else
+    { d0 = 2*d0*f0; f0 = f0*f0; }
+  }
+  term = (link(&lf->sp)==LIDENT) ? -f0*f0/(2*d0) : exp(f0)/d0;
+  sum += term;
+  
+  for (i=1; i<nv; i++)
+  { i0 = ind[i-1]; i1 = ind[i];
+    f0 = fit[i0]; f1 = fit[i1];
+    d0 = (has_deriv) ? deriv[i0] :
+              (f1-f0)/(xev[i1]-xev[i0]);
+    d1 = (has_deriv) ? deriv[i1] : d0;
+    if (z==2)
+    { if (link(&lf->sp)==LLOG)
+      { f0 *= 2; f1 *= 2; d0 *= 2; d1 *= 2; }
+      else
+      { d0 *= 2*f0; d1 *= 2*f1; f0 = f0*f0; f1 = f1*f1; }
+    }
+    term = estdiv(xev[i0],xev[i1],f0,f1,d0,d1,link(&lf->sp));
+    sum += term;
+  }
+
+  return(sum);
+}
+
+void dens_renorm(lf,des)
+lfit *lf;
+design *des;
+{ int i;
+  double sum;
+  sum = dens_integrate(lf,des,1);
+  if (sum==0.0) return;
+  sum = log(sum);
+  for (i=0; i<lf->fp.nv; i++) lf->fp.coef[i] -= sum;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ *   This file contains functions for constructing and
+ *   interpolating the adaptive tree structure. This is
+ *   the default evaluation structure used by Locfit.
+ */
+
+#include "lfev.h"
+
+/*
+  Guess the number of fitting points.
+  Needs improving!
+*/
+void atree_guessnv(evs,nvm,ncm,vc,d,alp)
+evstruc *evs;
+double alp;
+int *nvm, *ncm, *vc, d;
+{ double a0, cu, ifl;
+  int i, nv, nc;
+
+  *ncm = 1<<30; *nvm = 1<<30;
+  *vc = 1 << d;
+
+  if (alp>0)
+  { a0 = (alp > 1) ? 1 : 1/alp;
+    if (cut(evs)<0.01)
+    { WARN(("guessnv: cut too small."));
+      cut(evs) = 0.01;
+    }
+    cu = 1;
+    for (i=0; i<d; i++) cu *= MIN(1.0,cut(evs));
+    nv = (int)((5*a0/cu)**vc);   /* this allows 10*a0/cu splits */
+    nc = (int)(10*a0/cu+1);      /* and 10*a0/cu cells */
+    if (nv<*nvm) *nvm = nv;
+    if (nc<*ncm) *ncm = nc;
+  }
+
+  if (*nvm == 1<<30) /* by default, allow 100 splits */
+  { *nvm = 102**vc;
+    *ncm = 201;
+  }
+
+  /* inflation based on mk */
+  ifl = mk(evs)/100.0;
+  *nvm = *vc+(int)(ifl**nvm);
+  *ncm = (int)(ifl**ncm);
+  
+}
+
+/*
+  Determine whether a cell in the tree needs splitting.
+  If so, return the split variable (0..d-1).
+  Otherwise, return -1.
+*/
+int atree_split(lf,ce,le,ll,ur)
+lfit *lf;
+int *ce;
+double *le, *ll, *ur;
+{ int d, vc, i, is;
+  double h, hmin, score[MXDIM];
+  d = lf->fp.d; vc = 1<<d;
+
+  hmin = 0.0;
+  for (i=0; i<vc; i++)
+  { h = lf->fp.h[ce[i]];
+    if ((h>0) && ((hmin==0)|(h<hmin))) hmin = h;
+  }
+
+  is = 0;
+  for (i=0; i<d; i++)
+  { le[i] = (ur[i]-ll[i])/lf->lfd.sca[i];
+    if ((lf->lfd.sty[i]==STCPAR) || (hmin==0))
+      score[i] = 2*(ur[i]-ll[i])/(lf->evs.fl[i+d]-lf->evs.fl[i]);
+    else
+      score[i] = le[i]/hmin;
+    if (score[i]>score[is]) is = i;
+  }
+  if (cut(&lf->evs)<score[is]) return(is);
+  return(-1);
+}
+
+/*
+  recursively grow the tree structure, begining with the parent cell.
+*/
+void atree_grow(des,lf,ce,ct,term,ll,ur)
+design *des;
+lfit *lf;
+int *ce, *ct, *term;
+double *ll, *ur;
+{ int nce[1<<MXDIM];
+  int i, i0, i1, d, vc, pv, tk, ns;
+  double le[MXDIM], z;
+  d = lf->fp.d; vc = 1<<d;
+
+  /* does this cell need splitting?
+     If not, wrap up and return.
+  */
+  ns = atree_split(lf,ce,le,ll,ur);
+  if (ns==-1)
+  { if (ct != NULL) /* reconstructing terminal cells */
+    { for (i=0; i<vc; i++) term[*ct*vc+i] = ce[i];
+      (*ct)++;
+    }
+    return;
+  }
+
+  /* split the cell at the midpoint on side ns */
+  tk = 1<<ns;
+  for (i=0; i<vc; i++)
+  { if ((i&tk)==0) nce[i] = ce[i];
+    else
+    { i0 = ce[i];
+      i1 = ce[i-tk];
+      pv = (lf->lfd.sty[i]!=STCPAR) &&
+           (le[ns] < (cut(&lf->evs)*MIN(lf->fp.h[i0],lf->fp.h[i1])));
+      nce[i] = newsplit(des,lf,i0,i1,pv);
+      if (lf_error) return;
+    }
+  }
+  z = ur[ns]; ur[ns] = (z+ll[ns])/2;
+  atree_grow(des,lf,nce,ct,term,ll,ur);
+  if (lf_error) return;
+  ur[ns] = z;
+  for (i=0; i<vc; i++)
+    nce[i] = ((i&tk)== 0) ? nce[i+tk] : ce[i];
+  z = ll[ns]; ll[ns] = (z+ur[ns])/2;
+  atree_grow(des,lf,nce,ct,term,ll,ur);
+  if (lf_error) return;
+  ll[ns] = z;
+}
+
+void atree_start(des,lf)
+design *des;
+lfit *lf;
+{ int d, i, j, k, vc, ncm, nvm;
+  double ll[MXDIM], ur[MXDIM];
+
+  if (lf_debug>1) mut_printf(" In atree_start\n");
+  d = lf->fp.d;
+  atree_guessnv(&lf->evs,&nvm,&ncm,&vc,d,nn(&lf->sp));
+  if (lf_debug>2) mut_printf(" atree_start: nvm %d ncm %d\n",nvm,ncm);
+  trchck(lf,nvm,ncm,vc);
+
+  /* Set the lower left, upper right limits. */
+  for (j=0; j<d; j++)
+  { ll[j] = lf->evs.fl[j];
+    ur[j] = lf->evs.fl[j+d];
+  }
+
+  /* Set the initial cell; fit at the vertices. */
+  for (i=0; i<vc; i++)
+  { j = i;
+    for (k=0; k<d; ++k)
+    { evptx(&lf->fp,i,k) = (j%2) ? ur[k] : ll[k];
+      j >>= 1;
+    }
+    lf->evs.ce[i] = i;
+    PROC_VERTEX(des,lf,i);
+    if (lf_error) return;
+    lf->evs.s[i] = 0;
+  }
+  lf->fp.nv = vc;
+
+  /* build the tree */
+  atree_grow(des,lf,lf->evs.ce,NULL,NULL,ll,ur);
+  lf->evs.nce = 1;
+}
+
+double atree_int(lf,x,what)
+lfit *lf;
+double *x;
+int what;
+{ double vv[64][64], *ll, *ur, h, xx[MXDIM];
+  int lo, tk, ns, nv, nc, d, i, vc;
+  int ce[64];
+  fitpt *fp;
+  evstruc *evs;
+
+  fp = &lf->fp;
+  evs= &lf->evs;
+  d = fp->d;
+  vc = 1<<d;
+
+  for (i=0; i<vc; i++)
+  { setzero(vv[i],vc);
+    nc = exvval(fp,vv[i],i,d,what,1);
+    ce[i] = evs->ce[i];
+  }
+  ns = 0;
+  while(ns!=-1)
+  { ll = evpt(fp,ce[0]); ur = evpt(fp,ce[vc-1]);
+    ns = atree_split(lf,ce,xx,ll,ur);
+    if (ns!=-1)
+    { tk = 1<<ns;
+      h = ur[ns]-ll[ns];
+      lo = (2*(x[ns]-ll[ns])) < h;
+      for (i=0; i<vc; i++) if ((tk&i)==0)
+      { nv = findpt(fp,evs,(int)ce[i],(int)ce[i+tk]);
+        if (nv==-1) LERR(("Descend tree problem"));
+        if (lf_error) return(0.0);
+        if (lo)
+        { ce[i+tk] = nv;
+          if (evs->s[nv]) exvvalpv(vv[i+tk],vv[i],vv[i+tk],d,ns,h,nc);
+                    else exvval(fp,vv[i+tk],nv,d,what,1);
+        }
+        else
+        { ce[i] = nv;
+          if (evs->s[nv]) exvvalpv(vv[i],vv[i],vv[i+tk],d,ns,h,nc);
+                    else exvval(fp,vv[i],nv,d,what,1);
+      } }
+  } }
+  ll = evpt(fp,ce[0]); ur = evpt(fp,ce[vc-1]);
+  return(rectcell_interp(x,vv,ll,ur,d,nc));
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+double linear_interp(h,d,f0,f1)
+double h, d, f0, f1;
+{ if (d==0) return(f0);
+  return( ( (d-h)*f0 + h*f1 ) / d );
+}
+
+void hermite2(x,z,phi)
+double x, z, *phi;
+{ double h;
+  if (z==0)
+  { phi[0] = 1.0; phi[1] = phi[2] = phi[3] = 0.0;
+    return;
+  }
+  h = x/z;
+  if (h<0)
+  { phi[0] = 1; phi[1] = 0;
+    phi[2] = h; phi[3] = 0;
+    return;
+  }
+  if (h>1)
+  { phi[0] = 0; phi[1] = 1;
+    phi[2] = 0; phi[3] = h-1;
+    return;
+  }
+  phi[1] = h*h*(3-2*h);
+  phi[0] = 1-phi[1];
+  phi[2] = h*(1-h)*(1-h);
+  phi[3] = h*h*(h - 1);
+}
+
+double cubic_interp(h,f0,f1,d0,d1)
+double h, f0, f1, d0, d1;
+{ double phi[4];
+  hermite2(h,1.0,phi);
+  return(phi[0]*f0+phi[1]*f1+phi[2]*d0+phi[3]*d1);
+}
+
+double cubintd(h,f0,f1,d0,d1)
+double h, f0, f1, d0, d1;
+{ double phi[4];
+  phi[1] = 6*h*(1-h);
+  phi[0] = -phi[1];
+  phi[2] = (1-h)*(1-3*h);
+  phi[3] = h*(3*h-2);
+  return(phi[0]*f0+phi[1]*f1+phi[2]*d0+phi[3]*d1);
+}
+
+/*
+  interpolate over a rectangular cell.
+    x = interpolation point. 
+    vv = array of vertex values.
+    ll = lower left corner.
+    ur = upper right corner.
+    d = dimension.
+    nc = no of coefficients.
+*/
+double rectcell_interp(x,vv,ll,ur,d,nc)
+double *x, vv[64][64], *ll, *ur;
+int d, nc;
+{ double phi[4];
+  int i, j, k, tk;
+
+  tk = 1<<d;
+  for (i=0; i<tk; i++) if (vv[i][0]==NOSLN) return(NOSLN);
+
+  /* no derivatives - use multilinear interpolation */
+  if (nc==1)
+  { for (i=d-1; i>=0; i--)
+    { tk = 1<<i;
+      for (j=0; j<tk; j++)
+        vv[j][0] = linear_interp(x[i]-ll[i],ur[i]-ll[i],vv[j][0],vv[j+tk][0]);
+    }
+    return(vv[0][0]);
+  }
+
+  /* with derivatives -- use cubic */
+  if (nc==d+1)
+  { for (i=d-1; i>=0; i--)
+    { hermite2(x[i]-ll[i],ur[i]-ll[i],phi);
+      tk = 1<<i;
+      phi[2] *= ur[i]-ll[i];
+      phi[3] *= ur[i]-ll[i];
+      for (j=0; j<tk; j++)
+      { vv[j][0] = phi[0]*vv[j][0] + phi[1]*vv[j+tk][0]
+                 + phi[2]*vv[j][i+1] + phi[3]*vv[j+tk][i+1];
+        for (k=1; k<=i; k++)
+          vv[j][k] = phi[0]*vv[j][k] + phi[1]*vv[j+tk][k];
+      }
+    }
+    return(vv[0][0]); 
+  }
+
+  /* with all coefs -- use multicubic */
+  for (i=d-1; i>=0; i--)
+  { hermite2(x[i]-ll[i],ur[i]-ll[i],phi);
+    tk = 1<<i;
+    phi[2] *= ur[i]-ll[i];
+    phi[3] *= ur[i]-ll[i];
+    for (j=0; j<tk; j++)
+      for (k=0; k<tk; k++)
+        vv[j][k] = phi[0]*vv[j][k] + phi[1]*vv[j+tk][k]
+                 + phi[2]*vv[j][k+tk] + phi[3]*vv[j+tk][k+tk];
+  }
+  return(vv[0][0]);
+}
+
+int exvval(fp,vv,nv,d,what,z)
+fitpt *fp;
+double *vv;
+int nv, d, z, what;
+{ int i, k;
+  double *values;
+
+  k = (z) ? 1<<d : d+1;
+  for (i=1; i<k; i++) vv[i] = 0.0;
+  switch(what)
+  { case PCOEF:
+      values = fp->coef;
+      break;
+    case PVARI:
+    case PNLX:
+      values = fp->nlx;
+      break;
+    case PT0:
+      values = fp->t0;
+      break;
+    case PBAND:
+      vv[0] = fp->h[nv];
+      return(1);
+    case PDEGR:
+      vv[0] = fp->deg[nv];
+      return(1);
+    case PLIK:
+      vv[0] = fp->lik[nv];
+      return(1);
+    case PRDF:
+      vv[0] = fp->lik[2*fp->nvm+nv];
+      return(1);
+    default:
+      LERR(("Invalid what in exvval"));
+      return(0);
+  }
+  vv[0] = values[nv];
+  if (!fp->hasd) return(1);
+  if (z)
+  { for (i=0; i<d; i++) vv[1<<i] = values[(i+1)*fp->nvm+nv];
+    return(1<<d);
+  }
+  else
+  { for (i=1; i<=d; i++) vv[i] = values[i*fp->nvm+nv];
+    return(d+1);
+  }
+}
+
+void exvvalpv(vv,vl,vr,d,k,dl,nc)
+double *vv, *vl, *vr, dl;
+int d, k, nc;
+{ int i, tk, td;
+  double f0, f1;
+  if (nc==1)
+  { vv[0] = (vl[0]+vr[0])/2;
+    return;
+  }
+  tk = 1<<k;
+  td = 1<<d;
+  for (i=0; i<td; i++) if ((i&tk)==0)
+  { f0 = (vl[i]+vr[i])/2 + dl*(vl[i+tk]-vr[i+tk])/8;
+    f1 = 1.5*(vr[i]-vl[i])/dl - (vl[i+tk]+vr[i+tk])/4;
+    vv[i] = f0;
+    vv[i+tk] = f1;
+  }
+} 
+
+double grid_int(fp,evs,x,what)
+fitpt *fp;
+evstruc *evs;
+double *x;
+int what;
+{ int d, i, j, jj, nc, sk, v[MXDIM], vc, z0, nce[1<<MXDIM], *mg;
+  double *ll, *ur, vv[64][64], z;
+
+  d = fp->d;
+  ll = evpt(fp,0); ur = evpt(fp,fp->nv-1);
+  mg = mg(evs);
+
+  z0 = 0; vc = 1<<d;
+  for (j=d-1; j>=0; j--)
+  { v[j] = (int)((mg[j]-1)*(x[j]-ll[j])/(ur[j]-ll[j]));
+    if (v[j]<0) v[j]=0;
+    if (v[j]>=mg[j]-1) v[j] = mg[j]-2;
+    z0 = z0*mg[j]+v[j];
+  }
+  nce[0] = z0; nce[1] = z0+1; sk = jj = 1; 
+  for (i=1; i<d; i++)
+  { sk *= mg[i-1];
+    jj<<=1;
+    for (j=0; j<jj; j++)
+      nce[j+jj] = nce[j]+sk;
+  }
+  for (i=0; i<vc; i++)
+    nc = exvval(fp,vv[i],nce[i],d,what,1);
+  ll = evpt(fp,nce[0]);
+  ur = evpt(fp,nce[vc-1]);
+  z = rectcell_interp(x,vv,ll,ur,d,nc);
+  return(z);
+}
+
+double fitp_int(fp,x,what,i)
+fitpt *fp;
+double *x;
+int what, i;
+{ double vv[1+MXDIM];
+  exvval(fp,vv,i,fp->d,what,0);
+  return(vv[0]);
+}
+
+double xbar_int(fp,x,what)
+fitpt *fp;
+double *x;
+int what;
+{ int i, nc;
+  double vv[1+MXDIM], f;
+  nc = exvval(fp,vv,0,fp->d,what,0);
+  f = vv[0];
+  if (nc>1)
+    for (i=0; i<fp->d; i++)
+      f += vv[i+1]*(x[i]-evptx(fp,0,i));
+  return(f);
+}
+
+double dointpoint(lf,x,what,ev,j)
+lfit *lf;
+double *x;
+int what, ev, j;
+{ double xf, f, link[LLEN];
+  int i, rt;
+  fitpt *fp;
+  evstruc *evs;
+
+  fp = &lf->fp; evs = &lf->evs;
+  for (i=0; i<fp->d; i++) if (lf->lfd.sty[i]==STANGL)
+  { xf = floor(x[i]/(2*PI*lf->lfd.sca[i]));
+    x[i] -= xf*2*PI*lf->lfd.sca[i];
+  }
+  if (what > 64)
+  { rt = what-64;
+    what = PCOEF;
+  }
+  else rt = 0;
+
+  switch(ev)
+  { case EGRID: f = grid_int(fp,evs,x,what); break;
+    case EKDTR: f = kdtre_int(fp,evs,x,what); break;
+    case ETREE: f = atree_int(lf,x,what); break;
+    case EPHULL: f = triang_int(lf,x,what); break;
+    case EFITP: f = fitp_int(fp,x,what,j); break;
+    case EXBAR: f = xbar_int(fp,x,what); break;
+    case ENONE: f = 0; break;
+    case ESPHR: f = sphere_int(lf,x,what); break;
+    default: LERR(("dointpoint: cannot interpolate structure %d",ev));
+  }
+  if (((what==PT0)|(what==PNLX)) && (f<0)) f = 0.0;
+  f += addparcomp(lf,x,what);
+
+  if (rt>0)
+  {
+    stdlinks(link,&lf->lfd,&lf->sp,j,f,rsc(fp));
+    f = resid(resp(&lf->lfd,j),prwt(&lf->lfd,j),f,fam(&lf->sp),rt,link);
+  }
+
+  return(f);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ *   Routines for building and interpolating the kd tree.
+ *   Initially, this started from the loess code.
+ *
+ *   Todo: EKDCE isn't working.
+ */
+
+#include "lfev.h"
+
+void newcell();
+static int nterm;
+
+void kdtre_guessnv(evs,nvm,ncm,vc,n,d,alp)
+evstruc *evs;
+double alp;
+int *nvm, *ncm, *vc, n, d;
+{ int k;
+  if (ev(evs) == EKDTR)
+  { nterm = (int)(cut(evs)/4 * n * MIN(alp,1.0) );
+    k = 2*n/nterm;
+    *vc = 1<<d;
+    *ncm = 2*k+1;
+    *nvm = (k+2)**vc/2;
+    return;
+  }
+  if (ev(evs) == EKDCE)
+  { nterm = (int)(n * alp);
+    *vc = 1;
+    *nvm = 1+(int)(2*n/nterm);
+    *ncm = 2**nvm+1;
+    return;
+  }
+  *nvm = *ncm = *vc = 0;
+  return;
+}
+
+/*
+  Split x[pi[l..r]] into two equal sized sets.
+
+  Let m=(l+r)/2.
+  At return,
+    x[pi[l..m]] < x[pi[m+1..r]].
+    Assuming no ties:
+      If l+r is odd, the sets have the same size.
+      If l+r is even, the low set is larger by 1.
+    If there are ties, all ties go in the low set.
+*/      
+int ksmall(l, r, m, x, pi)
+int l, r, m, *pi;
+double *x;
+{
+  int il, ir, jl, jr;
+  double t;
+
+
+  while(l<r)
+  { t = x[pi[m]];
+
+    /*
+      permute the observations so that
+        x[pi[l..il]] < t <= x[pi[ir..r]].
+    */
+    ir = l; il = r;
+    while (ir<il)
+    { while ((ir<=r) && (x[pi[ir]] < t)) ir++;
+      while ((il>=l) && (x[pi[il]]>= t)) il--;
+      if (ir<il) ISWAP(pi[ir],pi[il]);
+    }
+
+    /*
+      move  = t to the middle:
+        x[pi[l..il]]  < t
+        x[pi[jl..jr]] = t
+        x[pi[ir..r]] > t
+    */
+    jl = ir; jr = r;
+    while (ir<jr)
+    { while ((ir<=r)  && (x[pi[ir]]== t)) ir++;
+      while ((jr>=jl) && (x[pi[jr]] > t)) jr--;
+      if (ir<jr) ISWAP(pi[ir],pi[jr]);
+    }
+
+    /*
+      we're done if m is in the middle, jl <= m <= jr.
+    */
+    if ((jl<=m) & (jr>=m)) return(jr);
+
+    /*
+      update l or r.
+    */
+    if (m>=ir) l = ir;
+    if (m<=il) r = il;
+  }
+  if (l==r) return(l);
+  LERR(("ksmall failure"));
+  return(0);
+}
+
+int terminal(lf,p,pi,fc,d,m,split_val)
+lfit *lf;
+int p, d, fc, *m, *pi;
+double *split_val;
+{ int i, k, lo, hi, split_var;
+  double max, min, score, max_score, t;
+
+  /*
+    if there are fewer than fc points in the cell, this cell
+    is terminal.
+  */
+  lo = lf->evs.lo[p]; hi = lf->evs.hi[p];
+  if (hi-lo < fc) return(-1);
+
+  /* determine the split variable */
+  max_score = 0.0; split_var = 0;
+  for (k=0; k<d; k++)
+  { max = min = datum(&lf->lfd, k, pi[lo]);
+    for (i=lo+1; i<=hi; i++)
+    { t = datum(&lf->lfd,k,pi[i]);
+      if (t<min) min = t;
+      if (t>max) max = t;
+    }
+    score = (max-min) / lf->lfd.sca[k];
+    if (score > max_score)
+    { max_score = score;
+      split_var = k;
+    }
+  }
+  if (max_score==0) /* all points in the cell are equal */
+    return(-1);
+
+  *m = ksmall(lo,hi,(lo+hi)/2, dvari(&lf->lfd,split_var), pi);
+  *split_val = datum(&lf->lfd, split_var, pi[*m]);
+
+  if (*m==hi) /* all observations go lo */
+    return(-1);
+  return(split_var);
+}
+
+void kdtre_start(des,lf)
+design *des;
+lfit *lf;
+{
+  int i, j, vc, d, nc, nv, ncm, nvm, k, m, n, p, *pi;
+  double sv;
+  d = lf->lfd.d; n = lf->lfd.n; pi = des->ind;
+  kdtre_guessnv(&lf->evs,&nvm,&ncm,&vc,n,d,nn(&lf->sp));
+  trchck(lf,nvm,ncm,vc);
+
+  nv = 0;
+  if (ev(&lf->evs) != EKDCE)
+  { for (i=0; i<vc; i++)
+    { j = i;
+      for (k=0; k<d; ++k)
+      { evptx(&lf->fp,i,k) = lf->evs.fl[d*(j%2)+k];
+        j >>= 1;
+      }
+    }
+    nv = vc;
+    for (j=0; j<vc; j++) lf->evs.ce[j] = j;
+  }
+
+  for (i=0; i<n; i++) pi[i] = i;
+  p = 0; nc = 1;
+  lf->evs.lo[p] = 0; lf->evs.hi[p] = n-1;
+  lf->evs.s[p] = -1;
+  while (p<nc)
+  { k = terminal(lf,p,pi,nterm,d,&m,&sv);
+    if (k>=0)
+    {
+      if ((ncm<nc+2) | (2*nvm<2*nv+vc))
+      { WARN(("Insufficient space for full tree"));
+        lf->evs.nce = nc; lf->fp.nv = nv;
+        return;
+      }
+
+      /* new lo cell has obsn's lo[p]..m */
+      lf->evs.lo[nc] = lf->evs.lo[p];
+      lf->evs.hi[nc] = m;
+      lf->evs.s[nc] = -1;
+
+      /* new hi cell has obsn's m+1..hi[p] */
+      lf->evs.lo[nc+1] = m+1;
+      lf->evs.hi[nc+1] = lf->evs.hi[p];
+      lf->evs.s[nc+1] = -1;
+
+      /* cell p is split on variable k, value sv */
+      lf->evs.s[p] = k;
+      lf->evs.sv[p] = sv;
+      lf->evs.lo[p] = nc; lf->evs.hi[p] = nc+1;
+
+      nc=nc+2; i = nv;
+
+      /* now compute the new vertices. */
+      if (ev(&lf->evs) != EKDCE)
+        newcell(&nv,vc,evp(&lf->fp), d, k, sv,
+             &lf->evs.ce[p*vc], &lf->evs.ce[(nc-2)*vc], &lf->evs.ce[(nc-1)*vc]);
+
+    }
+    else if (ev(&lf->evs)==EKDCE) /* new vertex at cell center */
+    { sv = 0;
+      for (i=0; i<d; i++) evptx(&lf->fp,nv,i) = 0;
+      for (j=lf->evs.lo[p]; j<=lf->evs.hi[p]; j++)
+      { sv += prwt(&lf->lfd,(int)pi[j]);
+        for (i=0; i<d; i++)
+          evptx(&lf->fp,nv,i) += datum(&lf->lfd,i,pi[j])*prwt(&lf->lfd,(int)pi[j]);
+      }
+      for (i=0; i<d; i++) evptx(&lf->fp,nv,i) /= sv;
+      lf->lfd.n = lf->evs.hi[p] - lf->evs.lo[p] + 1;
+      des->ind = &pi[lf->evs.lo[p]]; /* why? */
+      PROC_VERTEX(des,lf,nv);
+      lf->lfd.n = n; des->ind = pi;
+      nv++;
+    }
+    p++;
+  }
+
+  /* We've built the tree. Now do the fitting. */
+  if (ev(&lf->evs)==EKDTR)
+    for (i=0; i<nv; i++) PROC_VERTEX(des,lf,i);
+
+  lf->evs.nce = nc; lf->fp.nv = nv;
+  return;
+}
+
+void newcell(nv,vc,xev, d, k, split_val, cpar, clef, crig)
+double *xev, split_val;
+int *cpar, *clef, *crig;
+int *nv, vc, d, k;
+{ int i, ii, j, j2, tk, match;
+  tk = 1<<k;
+  for (i=0; i<vc; i++)
+  { if ((i&tk) == 0)
+    { for (j=0; j<d; j++) xev[*nv*d+j] = xev[d*cpar[i]+j];
+      xev[*nv*d+k] = split_val;
+      match = 0; j = vc; /* no matches in first vc points */
+      while ((j<*nv) && (!match))
+      { j2 = 0;
+        while ((j2<d) && (xev[*nv*d+j2] == xev[j*d+j2])) j2++;
+        match = (j2==d);
+        if (!match) j++;
+      }
+      ii = i+tk;
+      clef[i] = cpar[i];
+      clef[ii]= crig[i] = j;
+      crig[ii]= cpar[ii];
+      if (!match) (*nv)++;
+    }
+  }
+  return;
+}
+
+extern void hermite2();
+
+double blend(fp,evs,s,x,ll,ur,j,nt,t,what)
+fitpt *fp;
+evstruc *evs;
+double s, *x, *ll, *ur;
+int j, nt, *t, what;
+{
+  int *ce, k, k1, m, nc, j0, j1;
+  double v0, v1, xibar, g0[3], g1[3], gg[4], gp[4], phi[4];
+  ce = evs->ce;
+  for (k=0; k<4; k++)  /* North South East West */
+  { k1 = (k>1);
+    v0 = ll[k1]; v1 = ur[k1];
+    j0 = ce[j+2*(k==0)+(k==2)];
+    j1 = ce[j+3-2*(k==1)-(k==3)];
+    xibar = (k%2==0) ? ur[k<2] : ll[k<2];
+    m = nt;
+    while ((m>=0) && ((evs->s[t[m]] != (k<=1)) | (evs->sv[t[m]] != xibar))) m--;
+    if (m >= 0)
+    { m = (k%2==1) ? evs->lo[t[m]] : evs->hi[t[m]];
+      while (evs->s[m] != -1)
+        m = (x[evs->s[m]] < evs->sv[m]) ? evs->lo[m] : evs->hi[m];
+      if (v0 < evptx(fp,ce[4*m+2*(k==1)+(k==3)],k1))
+      { j0 = ce[4*m+2*(k==1)+(k==3)];
+        v0 = evptx(fp,j0,k1);
+      }
+      if (evptx(fp,ce[4*m+3-2*(k==0)-(k==2)],k1) < v1)
+      { j1 = ce[4*m+3-2*(k==0)-(k==2)];
+        v1 = evptx(fp,j1,k1);
+      }
+    }
+    nc = exvval(fp,g0,j0,2,what,0);
+    nc = exvval(fp,g1,j1,2,what,0);
+    if (nc==1)
+      gg[k] = linear_interp((x[(k>1)]-v0),v1-v0,g0[0],g1[0]);
+    else
+    { hermite2(x[(k>1)]-v0,v1-v0,phi);
+      gg[k] = phi[0]*g0[0]+phi[1]*g1[0]+(phi[2]*g0[1+k1]+phi[3]*g1[1+k1])*(v1-v0);
+      gp[k] = phi[0]*g0[2-k1] + phi[1]*g1[2-k1];
+    }
+  }
+  s = -s;
+  if (nc==1)
+    for (k=0; k<2; k++)
+      s += linear_interp(x[k]-ll[k],ur[k]-ll[k],gg[3-2*k],gg[2-2*k]);
+    else
+    for (k=0; k<2; k++) /* EW NS */
+    { hermite2(x[k]-ll[k],ur[k]-ll[k],phi);
+      s += phi[0]*gg[3-2*k] + phi[1]*gg[2-2*k]
+          +(phi[2]*gp[3-2*k] + phi[3]*gp[2-2*k]) * (ur[k]-ll[k]);
+    }
+  return(s);
+}
+
+double kdtre_int(fp,evs,x,what)
+fitpt *fp;
+evstruc *evs;
+double *x;
+int what;
+{
+  int *ce, k, vc, t[20], nt, nc, j, d;
+  double *ll, *ur, ff, vv[64][64];
+  d = fp->d;
+  vc = 1<<d;
+  if (d > 6) { LERR(("d too large in kdint")); return(NOSLN); }
+
+  /* descend the tree to find the terminal cell */
+  nt = 0; t[nt] = 0; k = 0;
+  while (evs->s[k] != -1)
+  { nt++;
+    if (nt>=20) { LERR(("Too many levels in kdint")); return(NOSLN); }
+    k = t[nt] = (x[evs->s[k]] < evs->sv[k]) ? evs->lo[k] : evs->hi[k];
+  }
+
+  ce = &evs->ce[k*vc];
+  ll = evpt(fp,ce[0]);
+  ur = evpt(fp,ce[vc-1]);
+  nc = 0;
+  for (j=0; j<vc; j++) nc = exvval(fp,vv[j],(int)ce[j],d,what,0);
+  ff = rectcell_interp(x,vv,ll,ur,d,nc);
+
+  if (d==2) ff = blend(fp,evs,ff,x,ll,ur,k*vc,nt,t,what);
+  return(ff);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+/*
+ * convert eval. structure string to numeric code.
+ */
+#define NETYPE 11
+static char *etype[NETYPE]= { "tree",     "phull", "data", "grid", "kdtree",
+                          "kdcenter", "cross", "preset", "xbar", "none",
+                          "sphere" };
+static int   evals[NETYPE]= { ETREE, EPHULL, EDATA, EGRID, EKDTR,
+                          EKDCE, ECROS,  EPRES, EXBAR, ENONE, ESPHR };
+int lfevstr(char *z)
+{ return(pmatch(z, etype, evals, NETYPE, -1));
+}
+
+void evstruc_init(evs)
+evstruc *evs;
+{ int i;
+  ev(evs) = ETREE;
+  mk(evs) = 100;
+  cut(evs) = 0.8;
+  for (i=0; i<MXDIM; i++)
+  { evs->fl[i] = evs->fl[i+MXDIM] = 0.0;
+    evs->mg[i] = 10;
+  }
+  evs->nce = evs->ncm = 0;
+}
+
+int evstruc_reqi(nvm,ncm,vc)
+int nvm, ncm, vc;
+{ return(ncm*vc+3*MAX(ncm,nvm));
+}
+
+/* al=1: allows dynamic allocation.
+ * al=0: inhibit. use with caution.
+ */
+void evstruc_alloc(evs,nvm,ncm,vc,al)
+evstruc *evs;
+int nvm, ncm, vc, al;
+{ int rw, *k;
+
+  if (al)
+  { rw = evstruc_reqi(nvm,ncm,vc);
+    if (evs->liw<rw)
+    { evs->iwk = (int *)calloc(rw,sizeof(int));
+    if ( evs->iwk == NULL ) {
+      printf("Problem allocating memory for evs->iwk\n");fflush(stdout);
+    }
+      evs->liw = rw;
+    }
+  }
+  k = evs->iwk;
+  evs->ce = k; k += vc*ncm;
+  evs->s  = k; k += MAX(ncm,nvm);
+  evs->lo = k; k += MAX(ncm,nvm);
+  evs->hi = k; k += MAX(ncm,nvm);
+}
+
+void guessnv(evs,sp,mdl,n,d,lw,nvc)
+evstruc *evs;
+smpar *sp;
+module *mdl;
+int n, d, *lw, *nvc;
+{ int i, nvm, ncm, vc;
+
+  npar(sp) = calcp(sp,d);
+  switch(ev(evs))
+  { case ETREE:
+      atree_guessnv(evs,&nvm,&ncm,&vc,d,nn(sp));
+      break;
+    case EPHULL:
+      nvm = ncm = mk(evs)*d;
+      vc = d+1;
+      break;
+    case EDATA:
+    case ECROS:
+      nvm = n;
+      ncm = vc = 0;
+      break;
+    case EKDTR:
+    case EKDCE:
+      kdtre_guessnv(evs,&nvm,&ncm,&vc,n,d,nn(sp));
+      break;
+    case EGRID:
+      nvm = 1;
+      for (i=0; i<d; i++) nvm *= evs->mg[i];
+      ncm = 0;
+      vc = 1<<d;
+      break;
+    case EXBAR:
+    case ENONE:
+      nvm = 1;
+      ncm = vc = 0;
+      break;
+    case EPRES:
+      nvm = evs->mg[0];
+      ncm = vc = 0;
+      break;
+    default:
+      LERR(("guessnv: I don't know this evaluation structure."));
+      nvm = ncm = vc = 0;
+  }
+
+  lw[0] = des_reqd(n,npar(sp));
+  lw[1] = lfit_reqd(d,nvm,ncm,mdl);
+  lw[2] = evstruc_reqi(nvm,ncm,vc);
+  lw[6] = des_reqi(n,npar(sp));
+  lw[3] = pc_reqd(d,npar(sp));
+  lw[4] = mdl->keepv;
+  lw[5] = mdl->keepc;
+
+  if (nvc==NULL) return;
+  nvc[0] = nvm;
+  nvc[1] = ncm;
+  nvc[2] = vc;
+  nvc[3] = nvc[4] = 0;
+}
+
+/*
+ * trchck checks the working space on the lfit structure 
+ * has space for nvm vertices and ncm cells.
+ */
+void lfit_alloc(lf)
+lfit *lf;
+{ lf->fp.lwk = lf->fp.lev = lf->evs.liw = lf->pc.lwk = 0;
+  lf->lf_init_id = LF_INIT_ID;
+}
+int lfit_reqd(d,nvm,ncm,mdl)
+int d, nvm, ncm;
+module *mdl;
+{ int z;
+  z = mdl->keepv;
+  return(nvm*z+ncm);
+}
+
+void trchck(lf,nvm,ncm,vc)
+lfit *lf;
+int nvm, ncm, vc;
+{ int rw, d, *k;
+  double *z;
+
+  if (lf->lf_init_id != LF_INIT_ID) lfit_alloc(lf);
+
+  lf->fp.nvm = nvm; lf->evs.ncm = ncm;
+  d = lf->lfd.d;
+
+  if (lf->fp.lev < d*nvm)
+  { lf->fp.xev = (double *)calloc(d*nvm,sizeof(double));
+    if ( lf->fp.xev == NULL ) {
+      printf("Problem allocating memory for lf->fp.xev\n");fflush(stdout);
+    }
+    lf->fp.lev = d*nvm;
+  }
+
+  rw = lfit_reqd(d,nvm,ncm,&lf->mdl);
+  if (lf->fp.lwk < rw)
+  {
+    lf->fp.coef = (double *)calloc(rw,sizeof(double));
+    if ( lf->fp.coef == NULL ) {
+      printf("Problem allocating memory for lf->fp.coef\n");fflush(stdout);
+    }
+    lf->fp.lwk = rw;
+  }
+  z = lf->fp.wk = lf->fp.coef;
+
+  lf->fp.h = NULL;
+  if (!lf->mdl.isset) mut_printf("module not set.\n");
+  else
+  { if (lf->mdl.alloc!=NULL) lf->mdl.alloc(lf);
+    z += KEEPV(lf) * nvm;
+  }
+  lf->evs.sv = z; z += ncm;
+
+  evstruc_alloc(&lf->evs,nvm,ncm,vc,1);
+}
+
+void data_guessnv(nvm,ncm,vc,n)
+int *nvm, *ncm, *vc, n;
+{ *nvm = n;
+  *ncm = *vc = 0;
+}
+
+void dataf(des,lf)
+design *des;
+lfit *lf;
+{
+  int d, i, j, ncm, nv, vc;
+
+  d = lf->lfd.d;
+  data_guessnv(&nv,&ncm,&vc,lf->lfd.n);
+  trchck(lf,nv,ncm,vc);
+
+  for (i=0; i<nv; i++)
+    for (j=0; j<d; j++) evptx(&lf->fp,i,j) = datum(&lf->lfd,j,i);
+  for (i=0; i<nv; i++)
+  {
+    PROC_VERTEX(des,lf,i);
+    lf->evs.s[i] = 0;
+  }
+  lf->fp.nv = lf->fp.nvm = nv; lf->evs.nce = 0;
+}
+
+void xbar_guessnv(nvm,ncm,vc)
+int *nvm, *ncm, *vc;
+{ *nvm = 1;
+  *ncm = *vc = 0;
+  return;
+}
+
+void xbarf(des,lf)
+design *des;
+lfit *lf;
+{ int i, d, nvm, ncm, vc;
+  d = lf->lfd.d;
+  xbar_guessnv(&nvm,&ncm,&vc);
+  trchck(lf,1,0,0);
+  for (i=0; i<d; i++) evptx(&lf->fp,0,i) = lf->pc.xbar[i];
+  PROC_VERTEX(des,lf,0);
+  lf->evs.s[0] = 0;
+  lf->fp.nv = 1; lf->evs.nce = 0;
+}
+
+void preset(des,lf)
+design *des;
+lfit *lf;
+{ int i, nv;
+
+  nv = lf->fp.nvm;
+  trchck(lf,nv,0,0);
+  for (i=0; i<nv; i++)
+  { 
+    PROC_VERTEX(des,lf,i);
+    lf->evs.s[i] = 0;
+  }
+  lf->fp.nv = nv; lf->evs.nce = 0;
+}
+
+void crossf(des,lf)
+design *des;
+lfit *lf;
+{ int d, i, j, n, nv, ncm, vc;
+  double w;
+
+  n = lf->lfd.n; d = lf->lfd.d;
+  data_guessnv(&nv,&ncm,&vc,n);
+  trchck(lf,nv,ncm,vc);
+
+  if (lf->lfd.w==NULL) LERR(("crossf() needs prior weights"));
+  for (i=0; i<n; i++)
+    for (j=0; j<d; j++) evptx(&lf->fp,i,j) = datum(&lf->lfd,j,i);
+  for (i=0; i<n; i++)
+  { lf->evs.s[i] = 0;
+    w = prwt(&lf->lfd,i);
+    lf->lfd.w[i] = 0;
+    PROC_VERTEX(des,lf,i);
+    lf->lfd.w[i] = w;
+  }
+  lf->fp.nv = n; lf->evs.nce = 0;
+}
+
+void gridf(des,lf)
+design *des;
+lfit *lf;
+{ int d, i, j, nv, u0, u1, z;
+  nv = 1; d = lf->lfd.d;
+  for (i=0; i<d; i++)
+  { if (lf->evs.mg[i]==0)
+      lf->evs.mg[i] = 2+(int)((lf->evs.fl[i+d]-lf->evs.fl[i])/(lf->lfd.sca[i]*cut(&lf->evs)));
+    nv *= lf->evs.mg[i];
+  }
+  trchck(lf,nv,0,1<<d);
+  for (i=0; i<nv; i++)
+  { z = i;
+    for (j=0; j<d; j++)
+    { u0 = z%lf->evs.mg[j];
+      u1 = lf->evs.mg[j]-1-u0;
+      evptx(&lf->fp,i,j) = (lf->evs.mg[j]==1) ? lf->evs.fl[j] :
+                      (u1*lf->evs.fl[j]+u0*lf->evs.fl[j+d])/(lf->evs.mg[j]-1);
+      z = z/lf->evs.mg[j];
+    }
+    lf->evs.s[i] = 0;
+    PROC_VERTEX(des,lf,i);
+  }
+  lf->fp.nv = nv; lf->evs.nce = 0;
+}
+
+int findpt(fp,evs,i0,i1)
+fitpt *fp;
+evstruc *evs;
+int i0, i1;
+{ int i;
+  if (i0>i1) ISWAP(i0,i1);
+  for (i=i1+1; i<fp->nv; i++)
+    if ((evs->lo[i]==i0) && (evs->hi[i]==i1)) return(i);
+  return(-1);
+}
+
+/*
+  add a new vertex at the midpoint of (x[i0],x[i1]).
+  return the vertex number.
+*/
+int newsplit(des,lf,i0,i1,pv)
+design *des;
+lfit *lf;
+int i0, i1, pv;
+{ int i, nv;
+
+  i = findpt(&lf->fp,&lf->evs,i0,i1);
+  if (i>=0) return(i);
+
+  if (i0>i1) ISWAP(i0,i1);
+  nv = lf->fp.nv;
+  
+  /* the point is new. Now check we have space for the new point. */
+  if (nv>=lf->fp.nvm)
+  {
+    LERR(("newsplit: out of vertex space"));
+    return(-1);
+  }
+
+  /* compute the new point, and evaluate the fit */
+  lf->evs.lo[nv] = i0;
+  lf->evs.hi[nv] = i1;
+  for (i=0; i<lf->fp.d; i++)
+    evptx(&lf->fp,nv,i) = (evptx(&lf->fp,i0,i)+evptx(&lf->fp,i1,i))/2;
+  if (pv) /* pseudo vertex */
+  { lf->fp.h[nv] = (lf->fp.h[i0]+lf->fp.h[i1])/2;
+    lf->evs.s[nv] = 1; /* pseudo-vertex */
+  }
+  else /* real vertex */
+  {
+    PROC_VERTEX(des,lf,nv);
+    lf->evs.s[nv] = 0;
+  }
+  lf->fp.nv++;
+
+  return(nv);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ *   Functions for constructing the fit and
+ *   interpolating on the circle/sphere. d=2 only.
+ */
+
+#include "lfev.h"
+
+/*
+ * Guess the number of fitting points.
+ */
+void sphere_guessnv(nvm,ncm,vc,mg)
+int *nvm, *ncm, *vc, *mg;
+{ *nvm = mg[1]*(mg[0]+1);
+  *ncm = 0;
+  *vc = 0;
+}
+
+void sphere_start(des,lf)
+design *des;
+lfit *lf;
+{ int d, i, j, ct, nv, ncm, vc, *mg;
+  double rmin, rmax, *orig, r, th, c, s;
+
+  mg = mg(&lf->evs);
+  sphere_guessnv(&nv,&ncm,&vc,mg);
+  trchck(lf,nv,0,0);
+  d = lf->lfd.d;
+
+  rmin = lf->evs.fl[0];
+  rmax = lf->evs.fl[1];
+  orig = &lf->evs.fl[2];
+rmin = 0; rmax = 1; orig[0] = orig[1] = 0.0;
+
+  ct = 0;
+  for (i=0; i<mg[1]; i++)
+  { th = 2*PI*i/mg[1];
+    c = cos(th);
+    s = sin(th);
+    for (j=0; j<=mg[0]; j++)
+    { r = rmin + (rmax-rmin)*j/mg[0];
+      evptx(&lf->fp,ct,0) = orig[0] + r*c;
+      evptx(&lf->fp,ct,1) = orig[1] + r*s;
+      PROC_VERTEX(des,lf,ct);
+      ct++;
+    }
+  }
+  lf->fp.nv = ct;
+  lf->evs.nce = 0;
+}
+
+double sphere_int(lf,x,what)
+lfit *lf;
+double *x;
+int what;
+{ double rmin, rmax, *orig, dx, dy, r, th, th0, th1;
+  double v[64][64], c0, c1, s0, s1, r0, r1, d0, d1;
+  double ll[2], ur[2], xx[2];
+  int i0, j0, i1, j1, *mg, nc, ce[4];
+
+  rmin = lf->evs.fl[0];
+  rmax = lf->evs.fl[1];
+  orig = &lf->evs.fl[2];
+rmin = 0; rmax = 1; orig[0] = orig[1] = 0.0;
+  mg = mg(&lf->evs);
+
+  dx = x[0] - orig[0];
+  dy = x[1] - orig[1];
+  r = sqrt(dx*dx+dy*dy);
+  th = atan2(dy,dx); /* between -pi and pi */
+
+  i0 = (int)floor(mg[1]*th/(2*PI)) % mg[1];
+  j0 = (int)(mg[0]*(r-rmin)/(rmax-rmin));
+
+  i1 = (i0+1) % mg[1];
+  j1 = j0+1; if (j1>mg[0]) { j0 = mg[0]-1; j1 = mg[0]; }
+
+  ce[0] = i0*(mg[0]+1)+j0;
+  ce[1] = i0*(mg[0]+1)+j1;
+  ce[2] = i1*(mg[0]+1)+j0;
+  ce[3] = i1*(mg[0]+1)+j1;
+  nc = exvval(&lf->fp,v[0],ce[0],2,what,1);
+  nc = exvval(&lf->fp,v[1],ce[1],2,what,1);
+  nc = exvval(&lf->fp,v[2],ce[2],2,what,1);
+  nc = exvval(&lf->fp,v[3],ce[3],2,what,1);
+
+  th0 = 2*PI*i0/mg[1]; c0 = cos(th0); s0 = sin(th0);
+  th1 = 2*PI*i1/mg[1]; c1 = cos(th1); s1 = sin(th1);
+  r0 = rmin + j0*(rmax-rmin)/mg[0];
+  r1 = rmin + j1*(rmax-rmin)/mg[0];
+  
+  d0 = c0*v[0][1] + s0*v[0][2];
+  d1 = r0*(c0*v[0][2]-s0*v[0][1]);
+  v[0][1] = d0; v[0][2] = d1;
+
+  d0 = c0*v[1][1] + s0*v[1][2];
+  d1 = r1*(c0*v[1][2]-s0*v[1][1]);
+  v[1][1] = d0; v[1][2] = d1;
+
+  d0 = c1*v[2][1] + s1*v[2][2];
+  d1 = r0*(c1*v[2][2]-s1*v[2][1]);
+  v[2][1] = d0; v[2][2] = d1;
+
+  d0 = c1*v[3][1] + s1*v[3][2];
+  d1 = r1*(c1*v[3][2]-s1*v[3][1]);
+  v[3][1] = d0; v[3][2] = d1;
+
+  xx[0] = r; xx[1] = th;
+  ll[0] = r0; ll[1] = th0;
+  ur[0] = r1; ur[1] = th1;
+  return(rectcell_interp(xx,v,ll,ur,2,nc));
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+void solve(A,b,d) /* this is crude! A organized by column. */
+double *A, *b;
+int d;
+{ int i, j, k;
+  double piv;
+  for (i=0; i<d; i++)
+  { piv = A[(d+1)*i];
+    for (j=i; j<d; j++) A[j*d+i] /= piv;
+    b[i] /= piv;
+    for (j=0; j<d; j++) if (j != i)
+    { piv = A[i*d+j];
+      A[i*d+j] = 0;
+      for (k=i+1; k<d; k++)
+        A[k*d+j] -= piv*A[k*d+i];
+      b[j] -= piv*b[i];
+    }
+  }
+}
+
+void triang_guessnv(nvm,ncm,vc,d,mk)
+int *nvm, *ncm, *vc, d, mk;
+{ *nvm = *ncm = mk*d;
+  *vc = d+1;
+  return;         
+}
+
+int triang_split(lf,ce,le)
+lfit *lf;
+double *le;
+int *ce;
+{ int d, i, j, k, nts, vc;
+  double di, dfx[MXDIM];
+  nts = 0; d = lf->fp.d; vc = d+1;
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+    { for (k=0; k<d; k++)
+        dfx[k] = evptx(&lf->fp,ce[i],k)-evptx(&lf->fp,ce[j],k);
+      di = rho(dfx,lf->lfd.sca,d,KSPH,NULL);
+      le[i*vc+j] = le[j*vc+i] = di/MIN(lf->fp.h[ce[i]],lf->fp.h[ce[j]]);
+      nts = nts || le[i*vc+j]>cut(&lf->evs);
+    }
+  return(nts);
+}
+
+void resort(pv,xev,dig)
+double *xev;
+int *pv, *dig;
+{ double d0, d1, d2;
+  int i;
+  d0 = d1 = d2 = 0;
+  for (i=0; i<3; i++)
+  { d0 += (xev[3*pv[11]+i]-xev[3*pv[1]+i])*(xev[3*pv[11]+i]-xev[3*pv[1]+i]);
+    d1 += (xev[3*pv[ 7]+i]-xev[3*pv[2]+i])*(xev[3*pv[ 7]+i]-xev[3*pv[2]+i]);
+    d2 += (xev[3*pv[ 6]+i]-xev[3*pv[3]+i])*(xev[3*pv[ 6]+i]-xev[3*pv[3]+i]);
+  }
+  if ((d0<=d1) & (d0<=d2))
+  { dig[0] = pv[1]; dig[1] = pv[11];
+    dig[2] = pv[2]; dig[3] = pv[7];
+    dig[4] = pv[3]; dig[5] = pv[6];
+  }
+  else if (d1<=d2)
+  { dig[0] = pv[2]; dig[1] = pv[7];
+    dig[2] = pv[1]; dig[3] = pv[11];
+    dig[4] = pv[3]; dig[5] = pv[6];
+  }
+  else
+  { dig[0] = pv[3]; dig[1] = pv[6];
+    dig[2] = pv[2]; dig[3] = pv[7];
+    dig[4] = pv[1]; dig[5] = pv[11];
+  }
+}
+
+void triang_grow(des,lf,ce,ct,term)
+design *des;
+lfit *lf;
+int *ce, *ct, *term;
+{ double le[(1+MXDIM)*(1+MXDIM)], ml;
+  int d, i, j, im, jm, vc, pv[(1+MXDIM)*(1+MXDIM)], dig[6];
+  int nce[1+MXDIM];
+  if (lf_error) return;
+  d = lf->fp.d; vc = d+1;
+  if (!triang_split(lf,ce,le))
+  { if (ct != NULL)
+    { for (i=0; i<vc; i++) term[*ct*vc+i] = ce[i];
+      (*ct)++;
+    }
+    return;
+  }
+  if (d>3)
+  { ml = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<vc; j++)
+        if (le[i*vc+j]>ml) { ml = le[i*vc+j]; im = i; jm = j; }
+    pv[0] = newsplit(des,lf,(int)ce[im],(int)ce[jm],0);
+    for (i=0; i<vc; i++) nce[i] = ce[i];
+    nce[im] = pv[0]; triang_grow(des,lf,nce,ct,term); nce[im] = ce[im];
+    nce[jm] = pv[0]; triang_grow(des,lf,nce,ct,term);
+    return;
+  }
+
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+      pv[i*vc+j] = pv[j*vc+i]
+        = newsplit(des,lf,(int)ce[i],(int)ce[j],le[i*vc+j]<=cut(&lf->evs));
+  for (i=0; i<=d; i++) /* corners */
+  { for (j=0; j<=d; j++) nce[j] = (j==i) ? ce[i] : pv[i*vc+j];
+    triang_grow(des,lf,nce,ct,term);
+  }
+  
+  if (d==2) /* center for d=2 */
+  { nce[0] = pv[5]; nce[1] = pv[2]; nce[2] = pv[1];
+    triang_grow(des,lf,nce,ct,term);
+  }
+  if (d==3) /* center for d=3 */
+  { resort(pv,evp(&lf->fp),dig);
+    nce[0] = dig[0]; nce[1] = dig[1];
+    nce[2] = dig[2]; nce[3] = dig[4]; triang_grow(des,lf,nce,ct,term);
+    nce[2] = dig[5]; nce[3] = dig[3]; triang_grow(des,lf,nce,ct,term);
+    nce[2] = dig[2]; nce[3] = dig[5]; triang_grow(des,lf,nce,ct,term);
+    nce[2] = dig[4]; nce[3] = dig[3]; triang_grow(des,lf,nce,ct,term);
+  }
+}
+
+void triang_descend(tr,xa,ce)
+lfit *tr;
+double *xa;
+int *ce;
+{ double le[(1+MXDIM)*(1+MXDIM)], ml;
+  int d, vc, i, j, im, jm, pv[(1+MXDIM)*(1+MXDIM)];
+  design *des;
+  des = NULL;
+  if (!triang_split(tr,ce,le)) return;
+  d = tr->fp.d; vc = d+1;
+
+  if (d>3) /* split longest edge */
+  { ml = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<vc; j++)
+        if (le[i*vc+j]>ml) { ml = le[i*vc+j]; im = i; jm = j; }
+    pv[0] = newsplit(des,tr,(int)ce[im],(int)ce[jm],0);
+    if (xa[im]>xa[jm])
+    { xa[im] -= xa[jm]; xa[jm] *= 2; ce[jm] = pv[0]; }
+    else
+    { xa[jm] -= xa[im]; xa[im] *= 2; ce[im] = pv[0]; }
+    triang_descend(tr,xa,ce);
+    return;
+  }
+
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+      pv[i*vc+j] = pv[j*vc+i]
+        = newsplit(des,tr,(int)ce[i],(int)ce[j],le[i*d+j]<=cut(&tr->evs));
+  for (i=0; i<=d; i++) if (xa[i]>=0.5) /* in corner */
+  { for (j=0; j<=d; j++)
+    { if (i!=j) ce[j] = pv[i*vc+j];
+      xa[j] = 2*xa[j];
+    }
+    xa[i] -= 1;
+    triang_descend(tr,xa,ce);
+    return;
+  }
+  if (d==1) { LERR(("weights sum to < 1")); }
+  if (d==2) /* center */
+  { ce[0] = pv[5]; xa[0] = 1-2*xa[0];
+    ce[1] = pv[2]; xa[1] = 1-2*xa[1];
+    ce[2] = pv[1]; xa[2] = 1-2*xa[2];
+    triang_descend(tr,xa,ce);
+  }
+  if (d==3) /* center */
+  { double z; int dig[6];
+    resort(pv,evp(&tr->fp),dig);
+    ce[0] = dig[0]; ce[1] = dig[1];
+    xa[0] *= 2; xa[1] *= 2; xa[2] *= 2; xa[3] *= 2;
+    if (xa[0]+xa[2]>=1)
+    { if (xa[0]+xa[3]>=1)
+      { ce[2] = dig[2]; ce[3] = dig[4];
+        z = xa[0];
+        xa[3] += z-1; xa[2] += z-1; xa[0] = xa[1]; xa[1] = 1-z;
+      }
+      else
+      { ce[2] = dig[2]; ce[3] = dig[5];
+        z = xa[3]; xa[3] = xa[1]+xa[2]-1; xa[1] = z;
+        z = xa[2]; xa[2] += xa[0]-1; xa[0] = 1-z;
+    } }
+    else
+    { if (xa[1]+xa[2]>=1)
+      { ce[2] = dig[5]; ce[3] = dig[3];
+        xa[1] = 1-xa[1]; xa[2] -= xa[1]; xa[3] -= xa[1];
+      }
+      else
+      { ce[2] = dig[4]; ce[3] = dig[3];
+        z = xa[3]; xa[3] += xa[1]-1; xa[1] = xa[2];
+        xa[2] = z+xa[0]-1; xa[0] = 1-z;
+    } }
+    triang_descend(tr,xa,ce);
+} }
+
+void covrofdata(lfd,V,mn) /* covar of data; mean in mn */
+lfdata *lfd;
+double *V, *mn;
+{ int d, i, j, k;
+  double s;
+  s = 0; d = lfd->d;
+  for (i=0; i<d*d; i++) V[i] = 0;
+  for (i=0; i<lfd->n; i++)
+  { s += prwt(lfd,i);
+    for (j=0; j<d; j++)
+      for (k=0; k<d; k++)
+        V[j*d+k] += prwt(lfd,i)*(datum(lfd,j,i)-mn[j])*(datum(lfd,k,i)-mn[k]);
+  }
+  for (i=0; i<d*d; i++) V[i] /= s;
+}
+
+int intri(x,w,xev,xa,d) /* is x in triangle bounded by xd[0..d-1]? */
+double *x, *xev, *xa;
+int *w, d;
+{ int i, j;
+  double eps, *r, xd[MXDIM*MXDIM];
+  eps = 1.0e-10;
+  r = &xev[w[d]*d];
+  for (i=0; i<d; i++)
+  { xa[i] = x[i]-r[i];
+    for (j=0; j<d; j++) xd[i*d+j] = xev[w[i]*d+j]-r[j];
+  }
+  solve(xd,xa,d);
+  xa[d] = 1.0;
+  for (i=0; i<d; i++) xa[d] -= xa[i];
+  for (i=0; i<=d; i++) if ((xa[i]<-eps) | (xa[i]>1+eps)) return(0);
+  return(1);
+}
+
+void triang_start(des,lf) /* Triangulation with polyhedral start */
+design *des;
+lfit *lf;
+{
+  int i, j, k, n, d, nc, nvm, ncm, vc;
+  int *ce, ed[1+MXDIM];
+  double V[MXDIM*MXDIM], P[MXDIM*MXDIM], sigma, z[MXDIM], xa[1+MXDIM], *xev;
+  xev = evp(&lf->fp);
+  d = lf->lfd.d; n = lf->lfd.n;
+  lf->fp.nv = nc = 0;
+
+  triang_guessnv(&nvm,&ncm,&vc,d,mk(&lf->evs));
+  trchck(lf,nvm,ncm,vc);
+
+  ce = lf->evs.ce;
+  for (j=0; j<d; j++) xev[j] = lf->pc.xbar[j];
+  lf->fp.nv = 1;
+  covrofdata(&lf->lfd,V,xev); /* fix this with scaling */
+  eig_dec(V,P,d);
+
+  for (i=0; i<d; i++) /* add vertices +- 2sigma*eigenvect */
+  { sigma = sqrt(V[i*(d+1)]);
+    for (j=0; j<d; j++)
+      xev[lf->fp.nv*d+j] = xev[j]-2*sigma*P[j*d+i];
+    lf->fp.nv++;
+    for (j=0; j<d; j++)
+      xev[lf->fp.nv*d+j] = xev[j]+2*sigma*P[j*d+i];
+    lf->fp.nv++;
+  }
+
+  for (i=0; i<n; i++) /* is point i inside? */
+  { ed[0] = 0;
+    for (j=0; j<d; j++)
+    { z[j] = 0;
+      for (k=0; k<d; k++) z[j] += P[k*d+j]*(datum(&lf->lfd,k,i)-xev[k]);
+      ed[j+1] = 2*j+1+(z[j]>0);
+      for (k=0; k<d; k++) z[j] = datum(&lf->lfd,j,i);
+    }
+    k = intri(z,ed,xev,xa,d);
+    if (xa[0]<0)
+    { for (j=1; j<=d; j++)
+        for (k=0; k<d; k++)
+          xev[ed[j]*d+k] = xa[0]*xev[k]+(1-xa[0])*xev[ed[j]*d+k];
+    }
+  }
+
+  nc = 1<<d; /* create initial cells */
+  for (i=0; i<nc; i++)
+  { ce[i*vc] = 0; k = i;
+    for (j=0; j<d; j++)
+    { ce[i*vc+j+1] = 2*j+(k%2)+1;
+      k>>=1;
+    }
+  }
+
+  for (i=0; i<lf->fp.nv; i++)
+  { PROC_VERTEX(des,lf,i);
+    if (lf_error) return;
+    lf->evs.s[i] = 0;
+  }
+  for (i=0; i<nc; i++)
+    triang_grow(des,lf,&ce[i*vc],NULL,NULL);
+  lf->evs.nce = nc;
+}
+
+double triang_cubicint(v,vv,w,d,nc,xxa)
+double *v, *vv, *xxa;
+int *w, d, nc;
+{ double sa, lb, *vert0, *vert1, *vals0, *vals1, deriv0, deriv1;
+  int i, j, k;
+  if (nc==1) /* linear interpolate */
+  { sa = 0;
+    for (i=0; i<=d; i++) sa += xxa[i]*vv[i];
+    return(sa);
+  }
+  sa = 1.0;
+  for (j=d; j>0; j--)  /* eliminate v[w[j]] */
+  { lb = xxa[j]/sa;
+    for (k=0; k<j; k++) /* Interpolate edge v[w[k]],v[w[j]] */
+    { vert0 = &v[w[k]*d];
+      vert1 = &v[w[j]*d];
+      vals0 = &vv[k*nc];
+      vals1 = &vv[j*nc];
+      deriv0 = deriv1 = 0;
+      for (i=0; i<d; i++)
+      { deriv0 += (vert1[i]-vert0[i])*vals0[i+1];
+        deriv1 += (vert1[i]-vert0[i])*vals1[i+1];
+      }
+      vals0[0] = cubic_interp(lb,vals0[0],vals1[0],deriv0,deriv1);
+      for (i=1; i<=d; i++)
+        vals0[i] = (1-lb)*((1-lb)*vals0[i]+lb*vals1[i]);
+    }
+    sa -= xxa[j];
+    if (sa<=0) j = 0;
+  }
+  return(vals0[0]);
+}
+
+double triang_clotoch(xev,vv,ce,p,xxa)
+double *xev, *vv, *xxa;
+int *ce, p;
+{ double cfo[3], cfe[3], cg[9], *va, *vb, *vc,
+    l0, nm[3], na, nb, nc, *xl, *xr, *xz, d0, d1, lb, dlt, gam;
+  int i, w[3], cfl, cfr;
+  if (p==1)
+    return(xxa[0]*vv[0]+xxa[1]*vv[1]+xxa[2]*vv[2]);
+  if (xxa[2]<=MIN(xxa[0],xxa[1]))
+  { va = &xev[2*ce[0]]; vb = &xev[2*ce[1]]; vc = &xev[2*ce[2]];
+    w[0] = 0; w[1] = 3; w[2] = 6;
+  }
+  else
+  if (xxa[1]<xxa[0])
+  { w[0] = 0; w[1] = 6; w[2] = 3;
+    va = &xev[2*ce[0]]; vb = &xev[2*ce[2]]; vc = &xev[2*ce[1]];
+    lb = xxa[1]; xxa[1] = xxa[2]; xxa[2] = lb;
+  }
+  else
+  { w[0] = 6; w[1] = 3; w[2] = 0;
+    va = &xev[2*ce[2]]; vb = &xev[2*ce[1]]; vc = &xev[2*ce[0]];
+    lb = xxa[0]; xxa[0] = xxa[2]; xxa[2] = lb;
+  }
+  
+/* set cg to values and derivatives on standard triangle */
+  for (i=0; i<3; i++)
+  { cg[3*i] = vv[w[i]];
+    cg[3*i+1] = ((vb[0]-va[0])*vv[w[i]+1]
+                +(vb[1]-va[1])*vv[w[i]+2])/2;  /* df/dx */
+    cg[3*i+2] = ((2*vc[0]-vb[0]-va[0])*vv[w[i]+1]
+                +(2*vc[1]-vb[1]-va[1])*vv[w[i]+2])/2.0; /* sqrt{3} df/dy */
+  }
+  dlt = (vb[0]-va[0])*(vc[1]-va[1])-(vc[0]-va[0])*(vb[1]-va[1]);
+  /* Twice area; +ve if abc antic.wise  -ve is abc c.wise */
+  cfo[0] = (cg[0]+cg[3]+cg[6])/3;
+  cfo[1] = (2*cg[0]-cg[3]-cg[6])/4;
+  cfo[2] = (2*cg[3]-cg[0]-cg[6])/4;
+  na = -cg[1]+cg[2];  /* perp. deriv, rel. length 2 */
+  nb = -cg[4]-cg[5];
+  nc = 2*cg[7];
+  cfo[1] += (nb-nc)/16;
+  cfo[2] += (nc-na)/16;
+  na = -cg[1]-cg[2]/3.0;  /* derivatives back to origin */
+  nb =  cg[4]-cg[5]/3.0;
+  nc =        cg[8]/1.5;
+  cfo[0] -= (na+nb+nc)*7/54;
+  cfo[1] += 13*(nb+nc-2*na)/144;
+  cfo[2] += 13*(na+nc-2*nb)/144;
+  for (i=0; i<3; i++)
+  { /* Outward normals by linear interpolation on original triangle.
+       Convert to outward normals on standard triangle.
+       Actually, computed to opposite corner */
+    switch(i)
+    { case 0: xl = vc; xr = vb; xz = va; cfl = w[2]; cfr = w[1];
+              break;
+      case 1: xl = va; xr = vc; xz = vb; cfl = w[0]; cfr = w[2];
+              break;
+      case 2: xl = vb; xr = va; xz = vc; cfl = w[1]; cfr = w[0];
+              break;
+    }
+    na = xr[0]-xl[0]; nb = xr[1]-xl[1];
+    lb = na*na+nb*nb;
+    d0 = 1.5*(vv[cfr]-vv[cfl]) - 0.25*(na*(vv[cfl+1]+vv[cfr+1])
+         +nb*(vv[cfl+2]+vv[cfr+2]));
+    d1 = 0.5*( na*(vv[cfl+2]+vv[cfr+2])-nb*(vv[cfl+1]+vv[cfr+1]) );
+    l0 = (xz[0]-xl[0])*na+(xz[1]-xl[1])*nb-lb/2;
+    nm[i] = (d1*dlt-l0*d0)/lb;
+  }
+  cfo[0] -= (nm[0]+nm[1]+nm[2])*4/81;
+  cfo[1] += (2*nm[0]-nm[1]-nm[2])/27;
+  cfo[2] += (2*nm[1]-nm[0]-nm[2])/27;
+
+  gam = xxa[0]+xxa[1]-2*xxa[2];
+  if (gam==0) return(cfo[0]);
+  lb = (xxa[0]-xxa[2])/gam;
+  d0 = -2*cg[4]; d1 = -2*cg[1];
+  cfe[0] = cubic_interp(lb,cg[3],cg[0],d0,d1);
+  cfe[1] = cubintd(lb,cg[3],cg[0],d0,d1);
+  cfe[2] = -(1-lb)*(1-2*lb)*cg[5] + 4*lb*(1-lb)*nm[2] - lb*(2*lb-1)*cg[2];
+  d0 = 2*(lb*cfo[1]+(1-lb)*cfo[2]);
+  d1 = (lb-0.5)*cfe[1]+cfe[2]/3.0;
+  return(cubic_interp(gam,cfo[0],cfe[0],d0,d1));
+}
+
+int triang_getvertexvals(fp,evs,vv,i,what)
+fitpt *fp;
+evstruc *evs;
+double *vv;
+int i, what;
+{ double dx, P, le, vl[1+MXDIM], vh[1+MXDIM];
+  int d, il, ih, j, nc;
+  d = fp->d;
+  if (evs->s[i]==0) return(exvval(fp,vv,i,d,what,0));
+
+  il = evs->lo[i]; nc = triang_getvertexvals(fp,evs,vl,il,what);
+  ih = evs->hi[i]; nc = triang_getvertexvals(fp,evs,vh,ih,what);
+  vv[0] = (vl[0]+vh[0])/2;
+  if (nc==1) return(nc);
+  P = 1.5*(vh[0]-vl[0]);
+  le = 0.0;
+  for (j=0; j<d; j++)
+  { dx = evptx(fp,ih,j)-evptx(fp,il,j);
+    vv[0] += dx*(vl[j+1]-vh[j+1])/8;
+    vv[j+1] = (vl[j+1]+vh[j+1])/2;
+    P -= 1.5*dx*vv[j+1];
+    le += dx*dx;
+  }
+  for (j=0; j<d; j++)
+    vv[j+1] += P*(evptx(fp,ih,j)-evptx(fp,il,j))/le;
+  return(nc);
+}
+
+double triang_int(lf,x,what)
+lfit *lf;
+double *x;
+int what;
+{
+  int d, i, j, k, vc, nc;
+  int *ce, nce[1+MXDIM];
+  double xa[1+MXDIM], vv[(1+MXDIM)*(1+MXDIM)], lb;
+fitpt *fp;
+evstruc *evs;
+fp = &lf->fp;
+evs= &lf->evs;
+
+  d = fp->d; vc = d+1;
+  ce = evs->ce;
+  i = 0;
+  while ((i<evs->nce) && (!intri(x,&ce[i*vc],evp(fp),xa,d))) i++;
+  if (i==evs->nce) return(NOSLN);
+  i *= vc;
+  for (j=0; j<vc; j++) nce[j] = ce[i+j];
+  triang_descend(lf,xa,nce);
+
+  /* order the vertices -- needed for asymmetric interptr */
+  do
+  { k=0;
+    for (i=0; i<d; i++)
+      if (nce[i]>nce[i+1])
+      { j=nce[i]; nce[i]=nce[i+1]; nce[i+1]=j; k=1;
+        lb = xa[i]; xa[i] = xa[i+1]; xa[i+1] = lb;
+      }
+  } while(k);
+  nc = 0;
+  for (i=0; i<vc; i++)
+    nc =  triang_getvertexvals(fp,evs,&vv[i*nc],nce[i],what);
+  return((d==2) ? triang_clotoch(evp(fp),vv,nce,nc,xa) :
+                 triang_cubicint(evp(fp),vv,nce,d,nc,xa));
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ * Functions for computing residuals and fitted values from
+ * the locfit object.
+ *
+ * fitted(lf,fit,what,cv,ty) computes fitted values from the
+ *   fit structure in lf. 
+ * resid(y,c,w,th,mi,ty) converts fitted values to residuals
+*/
+
+#include "lfev.h"
+
+#define NRT 8
+static char *rtype[NRT] = { "deviance", "d2",    "pearson", "raw",
+                          "ldot",     "lddot", "fit",     "mean" };
+static int   rvals[NRT] = { RDEV, RDEV2, RPEAR, RRAW, RLDOT, RLDDT, RFIT, RMEAN};
+int restyp(z)
+char *z;
+{ int val;
+
+  val = pmatch(z, rtype, rvals, NRT, -1);
+  if (val==-1) LERR(("Unknown type = %s",z));
+  return(val);
+}
+
+double resid(y,w,th,fam,ty,res)
+int fam, ty;
+double y, w, th, *res;
+{ double raw;
+
+  fam = fam & 63;
+  if ((fam==TGAUS) | (fam==TROBT) | (fam==TCAUC))
+    raw = y-res[ZMEAN];
+  else
+    raw = y-w*res[ZMEAN];
+  switch(ty)
+  { case RDEV:
+      if (res[ZDLL]>0) return(sqrt(-2*res[ZLIK]));
+            else return(-sqrt(-2*res[ZLIK]));
+    case RPEAR:
+      if (res[ZDDLL]<=0)
+      { if (res[ZDLL]==0) return(0);
+        return(NOSLN);
+      }
+      return(res[ZDLL]/sqrt(res[ZDDLL]));
+    case RRAW:  return(raw);
+    case RLDOT: return(res[ZDLL]);
+    case RDEV2: return(-2*res[ZLIK]);
+    case RLDDT: return(res[ZDDLL]);
+    case RFIT:  return(th);
+    case RMEAN: return(res[ZMEAN]);
+    default: LERR(("resid: unknown residual type %d",ty));
+  }
+  return(0.0);
+}
+
+double studentize(res,inl,var,ty,link)
+double res, inl, var, *link;
+int ty;
+{ double den;
+  inl *= link[ZDDLL];
+  var = var*var*link[ZDDLL];
+  if (inl>1) inl = 1;
+  if (var>inl) var = inl;
+  den = 1-2*inl+var;
+  if (den<0) return(0.0);
+  switch(ty)
+  { case RDEV:
+    case RPEAR:
+    case RRAW:
+    case RLDOT:
+      return(res/sqrt(den));
+    case RDEV2:
+      return(res/den);
+    default: return(res);
+  }
+}
+
+void fitted(lf,fit,what,cv,st,ty)
+lfit *lf;
+double *fit;
+int what, cv, st, ty;
+{ int i, j, d, n, evo;
+  double xx[MXDIM], th, inl, var, link[LLEN];
+  n = lf->lfd.n;
+  d = lf->lfd.d;
+  evo = ev(&lf->evs);
+  cv &= (evo!=ECROS);
+  if ((evo==EDATA)|(evo==ECROS)) evo = EFITP;
+  setfamily(&lf->sp);
+
+  for (i=0; i<n; i++)
+  { for (j=0; j<d; j++) xx[j] = datum(&lf->lfd,j,i);
+    th = dointpoint(lf,xx,what,evo,i);
+    if ((what==PT0)|(what==PVARI)) th = th*th;
+    if (what==PCOEF)
+    {
+      th += base(&lf->lfd,i);
+      stdlinks(link,&lf->lfd,&lf->sp,i,th,rsc(&lf->fp));
+      if ((cv)|(st))
+      { inl = dointpoint(lf,xx,PT0,evo,i);
+        inl = inl*inl;
+        if (cv)
+        { th -= inl*link[ZDLL];
+          stdlinks(link,&lf->lfd,&lf->sp,i,th,rsc(&lf->fp));
+        }
+        if (st) var = dointpoint(lf,xx,PNLX,evo,i);
+      }
+      fit[i] = resid(resp(&lf->lfd,i),prwt(&lf->lfd,i),th,fam(&lf->sp),ty,link);
+      if (st) fit[i] = studentize(fit[i],inl,var,ty,link);
+    } else fit[i] = th;
+    if (lf_error) return;
+  }
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+extern double robscale;
+
+/* special version of ressumm to estimate sigma^2, with derivative estimation */
+void ressummd(lf)
+lfit *lf;
+{ int i;
+  double s0, s1;
+  s0 = s1 = 0.0;
+  if ((fam(&lf->sp)&64)==0)
+  { rv(&lf->fp) = 1.0;
+    return;
+  }
+  for (i=0; i<lf->fp.nv; i++)
+  { s0 += lf->fp.lik[2*lf->fp.nvm+i];
+    s1 += lf->fp.lik[i];
+  }
+  if (s0==0.0)
+    rv(&lf->fp) = 0.0;
+  else
+    rv(&lf->fp) = -2*s1/s0;
+}
+
+/*
+ * res[0] = log-likelihood.
+ * res[1] = df0 = tr(H)
+ * res[2] = df0 = tr(H'H)
+ * res[3] = s^2.
+ * res[5] = robustscale.
+ */
+void ressumm(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{ int i, j, evo, tg;
+  double *oy, pw, r1, r2, rdf, t0, t1, u[MXDIM], link[LLEN];
+  fitpt *fp;
+
+  res[0] = res[1] = res[2] = 0.0;
+
+  evo = ev(&lf->evs);
+  if ((evo==EKDCE) | (evo==EPRES))
+  { res[3] = 1.0;
+    return;
+  }
+  if (lf->dv.nd>0)
+  { ressummd(lf);
+    return;
+  }
+
+  r1 = r2 = 0.0;
+  if ((evo==EDATA) | (evo==ECROS)) evo = EFITP;
+
+  for (i=0; i<lf->lfd.n; i++)
+  { for (j=0; j<lf->lfd.d; j++) u[j] = datum(&lf->lfd,j,i);
+    fitv(des,i) = base(&lf->lfd,i)+dointpoint(lf,u,PCOEF,evo,i);
+    des->wd[i] = resp(&(lf->lfd),i) - fitv(des,i);
+    wght(des,i) = 1.0;
+    des->ind[i] = i;
+  }
+
+  tg = fam(&lf->sp);
+  res[5] = 1.0;
+  if ((tg==TROBT+64) | (tg==TCAUC+64)) /* global robust scale */
+  { oy = lf->lfd.y; lf->lfd.y = des->wd;
+    des->xev = lf->pc.xbar;
+    locfit(&lf->lfd,des,&lf->sp,1,0,0);
+    lf->lfd.y = oy;
+    res[5] = robscale;
+  }
+
+  for (i=0; i<lf->lfd.n; i++)
+  { for (j=0; j<lf->lfd.d; j++) u[j] = datum(&lf->lfd,j,i);
+    t0 = dointpoint(lf,u,PT0,evo,i);
+    t1 = dointpoint(lf,u,PNLX,evo,i);
+    stdlinks(link,&lf->lfd,&lf->sp,i,fitv(des,i),res[5]);
+    t1 = t1*t1*link[ZDDLL];
+    t0 = t0*t0*link[ZDDLL];
+    if (t1>1) t1 = 1;
+    if (t0>1) t0 = 1; /* no observation gives >1 deg.free */
+    res[0] += link[ZLIK];
+    res[1] += t0;
+    res[2] += t1;
+    pw = prwt(&lf->lfd,i);
+    if (pw>0)
+    { r1 += link[ZDLL]*link[ZDLL]/pw;
+      r2 += link[ZDDLL]/pw;
+    }
+  }
+
+  res[3] = 1.0;
+  if ((fam(&lf->sp)&64)==64) /* quasi family */
+  { rdf = lf->lfd.n-2*res[1]+res[2];
+    if (rdf<1.0)
+    { WARN(("Estimated rdf < 1.0; not estimating variance"));
+    }
+    else
+      res[3] = r1/r2 * lf->lfd.n / rdf;
+  }
+
+  /* try to ensure consistency for family="circ"! */
+  if (((fam(&lf->sp)&63)==TCIRC) & (lf->lfd.d==1))
+  { int *ind, nv;
+    double dlt, th0, th1;
+    ind = des->ind;
+    nv = fp->nv;
+    for (i=0; i<nv; i++) ind[i] = i;
+    lforder(ind,evp(fp),0,nv-1);
+    for (i=1; i<nv; i++)
+    { dlt = evptx(fp,ind[i],0)-evptx(fp,ind[i-1],0);
+      th0 = fp->coef[ind[i]]-dlt*fp->coef[ind[i]+nv]-fp->coef[ind[i-1]];
+      th1 = fp->coef[ind[i]]-dlt*fp->coef[ind[i-1]+nv]-fp->coef[ind[i-1]];
+      if ((th0>PI)&(th1>PI))
+      { for (j=0; j<i; j++)
+          fp->coef[ind[j]] += 2*PI;
+        i--;
+      }
+      if ((th0<(-PI))&(th1<(-PI)))
+      { for (j=0; j<i; j++)
+          fp->coef[ind[j]] -= 2*PI;
+        i--;
+      }
+    }
+  }
+
+}
+
+double rss(lf,des,df)
+lfit *lf;
+design *des;
+double *df;
+{ double ss, res[10];
+  ss = 0;
+  ressumm(lf,des,res);
+  *df = lf->lfd.n - 2*res[1] + res[2];
+  return(-2*res[0]);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ *
+ *   Derivative corrections. The local slopes are not the derivatives
+ *   of the local likelihood estimate; the function dercor() computes
+ *   the adjustment to get the correct derivatives under the assumption
+ *   that h is constant.
+ *
+ *   By differentiating the local likelihood equations, one obtains
+ *
+ *     d ^      ^       T      -1   T  d    .       ^
+ *    -- a   =  a  -  (X W V X)    X  -- W  l( Y, X a)
+ *    dx  0      1                    dx
+ */
+
+#include "lfev.h"
+extern double robscale;
+
+void dercor(lfd,sp,des,coef)
+lfdata *lfd;
+smpar *sp;
+design *des;
+double *coef;
+{ double s1, dc[MXDIM], wd, link[LLEN];
+  int i, ii, j, m, d, p;
+  if (fam(sp)<=THAZ) return;
+  if (ker(sp)==WPARM) return;
+
+  d = lfd->d;
+  p = des->p; m = des->n;
+
+  if (lf_debug>1) mut_printf("  Correcting derivatives\n");
+  fitfun(lfd, sp, des->xev,des->xev,des->f1,NULL);
+  jacob_solve(&des->xtwx,des->f1);
+  setzero(dc,d);
+
+  /* correction term is e1^T (XTWVX)^{-1} XTW' ldot. */
+  for (i=0; i<m; i++)
+  { s1 = innerprod(des->f1,d_xi(des,i),p);
+    ii = des->ind[i];
+    stdlinks(link,lfd,sp,ii,fitv(des,ii),robscale);
+    for (j=0; j<d; j++)
+    { wd = wght(des,ii)*weightd(datum(lfd,j,ii)-des->xev[j],lfd->sca[j],
+        d,ker(sp),kt(sp),des->h,lfd->sty[j],dist(des,ii));
+      dc[j] += s1*wd*link[ZDLL];
+    }
+
+  }
+  for (j=0; j<d; j++) coef[j+1] += dc[j];
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+void allocallcf(lf)
+lfit *lf;
+{ lf->fp.coef = VVEC(lf,0);
+  lf->fp.h    = VVEC(lf,NPAR(lf));
+}
+
+int procvallcf(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int i, p, lf_status;
+
+  lf_status = procv_nov(des,lf,v);
+  if (lf_error) return(lf_status);
+
+  p = NPAR(lf);
+  for (i=0; i<p; i++) VVAL(lf,v,i) = des->cf[i];
+  lf->fp.h[v] = des->h;
+
+  return(0);
+}
+
+void initallcf(lf)
+lfit *lf;
+{ PROCV(lf) = procvallcf;
+  ALLOC(lf) = allocallcf;
+  PPROC(lf) = NULL;
+  KEEPV(lf) = NPAR(lf)+1;
+  KEEPC(lf) = 0;
+  NOPC(lf)  = 1;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+void pprocgam(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{ int i, j, n, evo, op;
+  double *fv, *vr, df, t0, t1, u[MXDIM], link[LLEN];
+
+  n = lf->lfd.n;
+  fv = res;
+  vr = &res[n];
+  df = 0.0;
+
+  evo = ev(&lf->evs);
+  if (evo==EDATA) evo = EFITP;
+  
+  for (i=0; i<n; i++)
+  { for (j=0; j<lf->lfd.d; j++)  u[j] = datum(&lf->lfd,j,i);
+    fitv(des,i) = base(&lf->lfd,i)+dointpoint(lf,u,PCOEF,evo,i);
+    lf->lfd.y[i] -= fitv(des,i);
+    wght(des,i) = 1.0;
+    des->ind[i] = i;
+
+    t0 = dointpoint(lf,u,PT0,evo,i);
+    t1 = dointpoint(lf,u,PNLX,evo,i);
+    stdlinks(link,&lf->lfd,&lf->sp,i,fitv(des,i),1.0);
+    t0 = t0*t0*link[ZDDLL];
+    t1 = t1*t1*link[ZDDLL];
+    if (t0>1) t0 = 1; /* no observation gives >1 deg.free */
+    if (t1>1) t1 = 1; /* no observation gives >1 deg.free */
+    vr[i] = t1;
+    df += t0;
+  }
+
+  des->n = n;
+  deg(&lf->sp) = 1;
+  op = npar(&lf->sp);
+  npar(&lf->sp) = des->p = 1+lf->lfd.d;
+  des->xev = lf->pc.xbar;
+  locfit(&lf->lfd,des,&lf->sp,1,0,0);
+
+  for (i=0; i<n; i++) fv[i] = resp(&lf->lfd,i) - fitv(des,i);
+  for (i=0; i<=lf->lfd.d; i++)
+    lf->pc.coef[i] += des->cf[i];
+  res[2*n] = df-2;
+  npar(&lf->sp) = op;
+}
+
+void initgam(lf)
+lfit *lf;
+{ initstd(lf);
+  PPROC(lf) = pprocgam;
+  KEEPC(lf) = 2*NOBS(lf)+1;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+int procvhatm(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int k;
+  double *l;
+  l = &lf->fp.coef[v*lf->lfd.n];
+  if ((ker(&lf->sp)!=WPARM) | (!haspc(&lf->pc)))
+  { k = procv_nov(des,lf,v);
+    wdiag(&lf->lfd,&lf->sp,des,l,&lf->dv,0,1,1);
+  }
+  else
+    wdiagp(&lf->lfd,&lf->sp,des,l,&lf->pc,&lf->dv,0,1,1);
+  lf->fp.h[v] = des->h;
+  return(k);
+}
+
+void allochatm(lf)
+lfit *lf;
+{ lf->fp.coef = VVEC(lf,0);
+  lf->fp.h    = VVEC(lf,NOBS(lf));
+}
+
+void pprochatm(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{ transpose(lf->fp.coef,lf->fp.nvm,lf->lfd.n);
+}
+
+void inithatm(lf)
+lfit *lf;
+{ PROCV(lf) = procvhatm;
+  ALLOC(lf) = allochatm;
+  PPROC(lf) = pprochatm;
+  KEEPV(lf) = NOBS(lf)+1;
+  KEEPC(lf) = 1;
+  NOPC(lf) = 1; /* could use pc if mi[MKER] == WPARM */
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+static lfit *lf_scb;
+static lfdata *lfd_scb;
+static smpar  *scb_sp;
+static design *des_scb;
+
+int scbfitter(x,l,reqd)
+double *x, *l;
+int reqd;
+{
+  int m;
+  des_scb->xev = x;
+  if ((ker(scb_sp)!=WPARM) | (!haspc(&lf_scb->pc)))
+  { locfit(lfd_scb,des_scb,&lf_scb->sp,1,1,0);
+    m = wdiag(lfd_scb, scb_sp, des_scb,l,&lf_scb->dv,reqd,2,0);
+  }
+  else
+    m = wdiagp(lfd_scb, scb_sp, des_scb,l,&lf_scb->pc,&lf_scb->dv,reqd,2,0);
+  return(m);
+}
+
+int constants(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{
+  int d, m, nt;
+  double *wk;
+  evstruc *evs;
+
+  lf_scb = lf;
+  des_scb = des;
+  lfd_scb = &lf->lfd;
+  scb_sp  = &lf->sp;
+
+  evs = &lf->evs;
+  d = lfd_scb->d;
+  m = lfd_scb->n;
+  trchck(lf,0,0,0);
+
+  if (lf_error) return(0);
+  if ((ker(scb_sp) != WPARM) && (lf->sp.nn>0))
+    WARN(("constants are approximate for varying h"));
+  npar(scb_sp) = calcp(scb_sp,lf->lfd.d);
+  des_init(des,m,npar(scb_sp));
+  set_scales(&lf->lfd);
+  set_flim(&lf->lfd,&lf->evs);
+  compparcomp(des,&lf->lfd,&lf->sp,&lf->pc,ker(scb_sp)!=WPARM);
+  
+  wk = &res[d+1];
+  nt = tube_constants(scbfitter,d,m,ev(evs),mg(evs),evs->fl,
+    res,wk,(d>3) ? 4 : d+1,0);
+  lf->evs.nce = nt;   /* cheat way to return it to S. */
+  return(nt);
+}
+
+void initkappa(lf)
+lfit *lf;
+{ PROCV(lf) = NULL;
+  ALLOC(lf) = NULL;
+  PPROC(lf) = (void *)constants;
+  KEEPV(lf) = 0;
+  KEEPC(lf) = NVAR(lf)+1+k0_reqd(NVAR(lf),NOBS(lf),0);
+  NOPC(lf) = 0;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+/*  fix df computation for link=IDENT. */
+void pproclscv(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{ double df, fh, fh_cv, infl, z0, z1, x[MXDIM];
+  int i, n, j, evo;
+  z1 = df = 0.0;
+  evo = ev(&lf->evs);
+  n = lf->lfd.n;
+  if ((evo==EDATA) | (evo==ECROS)) evo = EFITP;
+
+  z0 = dens_integrate(lf,des,2);
+
+  for (i=0; i<n; i++)
+  { for (j=0; j<lf->lfd.d; j++) x[j] = datum(&lf->lfd,j,i);
+    fh = base(&lf->lfd,i)+dointpoint(lf,x,PCOEF,evo,i);
+    if (link(&lf->sp)==LLOG) fh = exp(fh);
+    infl = dointpoint(lf,x,PT0,evo,i);
+    infl = infl * infl;
+    if (infl>1) infl = 1;
+    fh_cv = (link(&lf->sp) == LIDENT) ?
+       (n*fh - infl) / (n-1.0) : fh*(1-infl)*n/(n-1.0);
+    z1 += fh_cv;
+    df += infl;
+  }
+
+  res[0] = z0-2*z1/n;
+  res[1] = df;
+}
+
+void initlscv(lf)
+lfit *lf;
+{ initstd(lf);
+  KEEPC(lf) = 2;
+  PPROC(lf) = pproclscv;
+  NOPC(lf) = 1;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+static double pen, sig2;
+
+void goldensec(f,des,tr,eps,xm,ym,meth)
+double (*f)(), eps, *xm, *ym;
+int meth;
+design *des;
+lfit *tr;
+{ double x[4], y[4], xx[11], yy[11];
+  int i, im;
+  xx[0] = tr->sp.fixh;
+  if (xx[0]<=0)
+  { LERR(("regband: initialize h>0"));
+    return;
+  }
+  for (i=0; i<=10; i++)
+  { if (i>0) xx[i] = (1+gold_rat)*xx[i-1];
+    yy[i] = f(xx[i],des,tr,meth);
+    if ((i==0) || (yy[i]<yy[im])) im = i;
+  }
+  if (im==0) im = 1;
+  if (im==10)im = 9;
+  x[0] = xx[im-1]; y[0] = yy[im-1];
+  x[1] = xx[im];   y[1] = yy[im];
+  x[3] = xx[im+1]; y[3] = yy[im+1];
+  x[2] = gold_rat*x[3]+(1-gold_rat)*x[0];
+  y[2] = f(x[2],des,tr,meth);
+  while (x[3]-x[0]>eps)
+  { if (y[1]<y[2])
+    { x[3] = x[2]; y[3] = y[2];
+      x[2] = x[1]; y[2] = y[1];
+      x[1] = gold_rat*x[0]+(1-gold_rat)*x[3];
+      y[1] = f(x[1],des,tr,meth);
+    }
+    else
+    { x[0] = x[1]; y[0] = y[1];
+      x[1] = x[2]; y[1] = y[2];
+      x[2] = gold_rat*x[3]+(1-gold_rat)*x[0];
+      y[2] = f(x[2],des,tr,meth);
+    }
+  }
+  im = 0;
+  for (i=1; i<4; i++) if (y[i]<y[im]) im = i;
+  *xm = x[im]; *ym = y[im];
+}
+
+double dnk(x,k)
+double x;
+int k;
+{ double f;
+  switch(k)
+  { case 0: f = 1; break;
+    case 1: f = -x; break;
+    case 2: f = x*x-1; break;
+    case 3: f = x*(x*x-3); break;
+    case 4: f = 3-x*x*(6-x*x); break;
+    case 5: f = -x*(15-x*x*(10-x*x)); break;
+    case 6: f = -15+x*x*(45-x*x*(15-x*x)); break;
+    default: LERR(("dnk: k=%d too large",k)); return(0.0);
+  }
+  return(f*exp(-x*x/2)/S2PI);
+}
+
+double locai(h,des,lf)
+double h;
+design *des;
+lfit *lf;
+{ double cp, res[10];
+  nn(&lf->sp) = h;
+  lf->mdl.procv = procvstd;
+  nstartlf(des,lf);
+  ressumm(lf,des,res);
+  cp = -2*res[0] + pen*res[1];
+  return(cp);
+}
+
+static int fc;
+
+double loccp(h,des,lf,m) /* m=1: cp    m=2: gcv */
+double h;
+design *des;
+lfit *lf;
+int m;
+{ double cp, res[10];
+  int dg, n;
+
+  n = lf->lfd.n;
+  nn(&lf->sp) = 0;
+  fixh(&lf->sp) = h;
+  lf->mdl.procv = procvstd;
+  nstartlf(des,lf);
+  ressumm(lf,des,res);
+  if (m==1)
+  { if (fc) sig2 = res[3]; /* first call - set sig2 */
+    cp = -2*res[0]/sig2 - n + 2*res[1];
+  }
+  else
+    cp = -2*n*res[0]/((n-res[1])*(n-res[1]));
+  fc = 0;
+  return(cp);
+}
+
+double cp(des,lf,meth)
+design *des;
+lfit *lf;
+int meth;
+{ double hm, ym;
+  fc = 1;
+  goldensec(loccp,des,lf,0.001,&hm,&ym,meth);
+  return(hm);
+}
+
+double gkk(des,lf)
+design *des;
+lfit *lf;
+{ double h, h5, nf, th;
+  int i, j, n, dg0, dg1;
+  ev(&lf->evs)= EDATA;
+  nn(&lf->sp) = 0;
+  n = lf->lfd.n;
+  dg0 = deg0(&lf->sp);     /* target degree */
+  dg1 = dg0+1+(dg0%2==0);  /* pilot degree */
+  nf = exp(log(1.0*n)/10); /* bandwidth inflation factor */
+  h = lf->sp.fixh;         /* start bandwidth */
+  for (i=0; i<=10; i++)
+  { deg(&lf->sp) = dg1;
+    lf->sp.fixh = h*nf;
+    lf->mdl.procv = procvstd;
+    nstartlf(des,lf);
+    th = 0;
+    for (j=10; j<n-10; j++)
+      th += lf->fp.coef[dg1*n+j]*lf->fp.coef[dg1*n+j];
+th *= n/(n-20.0);
+    h5 = sig2 * Wikk(ker(&lf->sp),dg0) / th;
+    h = exp(log(h5)/(2*dg1+1));
+    if (lf_error) return(0.0);
+/* mut_printf("pilot %8.5f  sel %8.5f\n",lf->sp.fixh,h); */
+  }
+  return(h);
+}
+
+double rsw(des,lf)
+design *des;
+lfit *lf;
+{ int i, j, k, nmax, nvm, n, mk, evo, dg0, dg1;
+  double rss[6], cp[6], th22, dx, d2, hh;
+  nmax = 5;
+  evo = ev(&lf->evs); ev(&lf->evs) = EGRID;
+  mk = ker(&lf->sp);  ker(&lf->sp) = WRECT;
+  dg0 = deg0(&lf->sp);
+  dg1 = 1 + dg0 + (dg0%2==0);
+  deg(&lf->sp) = 4;
+  for (k=nmax; k>0; k--)
+  { lf->evs.mg[0] = k;
+    lf->evs.fl[0] = 1.0/(2*k);
+    lf->evs.fl[1] = 1-1.0/(2*k);
+    nn(&lf->sp) = 0;
+    fixh(&lf->sp) = 1.0/(2*k);
+    lf->mdl.procv = procvstd;
+    nstartlf(des,lf);
+    nvm = lf->fp.nvm;
+    rss[k] = 0;
+    for (i=0; i<k; i++) rss[k] += -2*lf->fp.lik[i];
+  }
+  n = lf->lfd.n; k = 1;
+  for (i=1; i<=nmax; i++)
+  { /* cp[i] = (n-5*nmax)*rss[i]/rss[nmax]-(n-10*i); */
+    cp[i] = rss[i]/sig2-(n-10*i);
+    if (cp[i]<cp[k]) k = i;
+  }
+  lf->evs.mg[0] = k;
+  lf->evs.fl[0] = 1.0/(2*k);
+  lf->evs.fl[1] = 1-1.0/(2*k);
+  nn(&lf->sp) = 0;
+  fixh(&lf->sp) = 1.0/(2*k);
+  lf->mdl.procv = procvstd;
+  nstartlf(des,lf);
+  ker(&lf->sp) = mk; ev(&lf->evs) = evo;
+  nvm = lf->fp.nvm;
+  th22 = 0;
+  for (i=10; i<n-10; i++)
+  { j = floor(k*datum(&lf->lfd,0,i));
+    if (j>=k) j = k-1;
+    dx = datum(&lf->lfd,0,i)-evptx(&lf->fp,0,j);
+    if (dg1==2)
+      d2 = lf->fp.coef[2*nvm+j]+dx*lf->fp.coef[3*nvm+j]+dx*dx*lf->fp.coef[4*nvm+j]/2;
+    else d2 = lf->fp.coef[4*nvm+j];
+    th22 += d2*d2;
+  }
+  hh = Wikk(mk,dg0)*sig2/th22*(n-20.0)/n;
+  return(exp(log(hh)/(2*dg1+1)));
+}
+
+#define MAXMETH 10
+
+void rband(lf,des,hhat)
+lfit *lf;
+design *des;
+double *hhat;
+{ int i, dg, nmeth, meth[MAXMETH];
+  double h0, res[10];
+
+  nmeth = lf->dv.nd;
+  for (i=0; i<nmeth; i++) meth[i] = lf->dv.deriv[i];
+  lf->dv.nd = 0;
+
+/* first, estimate sigma^2.
+ * this is ridiculously bad. lf->sp.fixh = 0.05????
+ */
+/*  dg = deg(&lf->sp); deg(&lf->sp) = 2;
+  h0 = lf->sp.fixh;  lf->sp.fixh = 0.05;
+  lf->mdl.procv = procvstd;
+  nstartlf(des,lf);
+  ressumm(lf,des,res);
+  deg(&lf->sp) = dg; lf->sp.fixh = h0;
+  sig2 = res[3];  */
+
+  for (i=0; i<nmeth; i++)
+  {
+    switch(meth[i])
+    { case 0: hhat[i] = cp(des,lf,1);
+              break;
+      case 1: hhat[i] = cp(des,lf,2);
+              break;
+      case 2: hhat[i] = gkk(des,lf);
+              break;
+      case 3: hhat[i] = rsw(des,lf);
+              break;
+      default: hhat[i] = 0;
+              mut_printf("Unknown method %d\n",meth[i]);
+    }
+    if (lf_error) i = nmeth;
+  }
+  lf->dv.nd = nmeth;
+}
+
+void initrband(lf)
+lfit *lf;
+{
+  initstd(lf);
+  KEEPC(lf) = MAXMETH;
+  PROCV(lf) = NULL;
+  PPROC(lf) = rband;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+static double scb_crit, *x, c[10], kap[5], kaq[5], max_p2;
+static int side, type;
+design *scb_des;
+
+double covar_par(lf,des,x1,x2)
+lfit *lf;
+design *des;
+double x1, x2;
+{ double *v1, *v2, *wk;
+  paramcomp *pc;
+  int i, j, p, ispar;
+
+  v1 = des->f1; v2 = des->ss; wk = des->oc;
+  ispar = (ker(&lf->sp)==WPARM) && (haspc(&lf->pc));
+  p = npar(&lf->sp);
+
+/*  for parametric models, the covariance is
+ *  A(x1)^T (X^T W V X)^{-1} A(x2)
+ *  which we can find easily from the parametric component.
+ */
+  if (ispar)
+  { pc = &lf->pc;
+    fitfun(&lf->lfd, &lf->sp, &x1,pc->xbar,v1,NULL);
+    fitfun(&lf->lfd, &lf->sp, &x2,pc->xbar,v2,NULL);
+    jacob_hsolve(&lf->pc.xtwx,v1);
+    jacob_hsolve(&lf->pc.xtwx,v2);
+  }
+
+/*  for non-parametric models, we must use the cholseky decomposition
+ *  of M2 = X^T W^2 V X. Courtesy of lf_vcov caclulations, should have
+ *  des->P = M2^{1/2} M1^{-1}.
+ */
+  if (!ispar)
+  { fitfun(&lf->lfd, &lf->sp, &x1,des->xev,wk,NULL);
+    for (i=0; i<p; i++)
+    { v1[i] = 0;
+      for (j=0; j<p; j++) v1[i] += des->P[i*p+j]*wk[j];
+    }
+    fitfun(&lf->lfd, &lf->sp, &x2,des->xev,wk,NULL);
+    for (i=0; i<p; i++)
+    { v2[i] = 0;
+      for (j=0; j<p; j++) v2[i] += des->P[i*p+j]*wk[j];
+    }
+  }
+
+  return(innerprod(v1,v2,p));
+}
+
+void cumulant(lf,des,sd)
+lfit *lf;
+design *des;
+double sd;
+{ double b2i, b3i, b3j, b4i;
+  double ss, si, sj, uii, uij, ujj, k1;
+  int ii, i, j, jj;
+  for (i=1; i<10; i++) c[i] = 0.0;
+  k1 = 0;
+
+  /* ss = sd*sd; */
+  ss = covar_par(lf,des,des->xev[0],des->xev[0]);
+
+/*
+ * this isn't valid for nonparametric models. At a minimum,
+ * the sums would have to include weights. Still have to work
+ * out the right way.
+ */
+  for (i=0; i<lf->lfd.n; i++)
+  { ii = des->ind[i];
+    b2i = b2(fitv(des,ii),fam(&lf->sp),prwt(&lf->lfd,ii));
+    b3i = b3(fitv(des,ii),fam(&lf->sp),prwt(&lf->lfd,ii));
+    b4i = b4(fitv(des,ii),fam(&lf->sp),prwt(&lf->lfd,ii));
+    si = covar_par(lf,des,des->xev[0],datum(&lf->lfd,0,ii));
+    uii= covar_par(lf,des,datum(&lf->lfd,0,ii),datum(&lf->lfd,0,ii));
+    if (lf_error) return;
+
+    c[2] += b4i*si*si*uii;
+    c[6] += b4i*si*si*si*si;
+    c[7] += b3i*si*uii;
+    c[8] += b3i*si*si*si;
+    /* c[9] += b2i*si*si*si*si;
+       c[9] += b2i*b2i*si*si*si*si; */
+    k1 += b3i*si*(si*si/ss-uii);
+
+    /* i=j components */
+    c[1] += b3i*b3i*si*si*uii*uii;
+    c[3] += b3i*b3i*si*si*si*si*uii;
+    c[4] += b3i*b3i*si*si*uii*uii;
+
+    for (j=i+1; j<lf->lfd.n; j++)
+    { jj = des->ind[j];
+      b3j = b3(fitv(des,ii),fam(&lf->sp),prwt(&lf->lfd,jj));
+      sj = covar_par(lf,des,des->xev[0],datum(&lf->lfd,0,jj));
+      uij= covar_par(lf,des,datum(&lf->lfd,0,ii),datum(&lf->lfd,0,jj));
+      ujj= covar_par(lf,des,datum(&lf->lfd,0,jj),datum(&lf->lfd,0,jj));
+
+      c[1] += 2*b3i*b3j*si*sj*uij*uij;
+      c[3] += 2*b3i*b3j*si*si*sj*sj*uij;
+      c[4] += b3i*b3j*uij*(si*si*ujj+sj*sj*uii);
+      if (lf_error) return;
+    }
+  }
+  c[5] = c[1];
+  c[7] = c[7]*c[8];
+  c[8] = c[8]*c[8];
+
+  c[1] /= ss; c[2] /= ss; c[3] /= ss*ss; c[4] /= ss;
+  c[5] /= ss; c[6] /= ss*ss; c[7] /= ss*ss;
+  c[8] /= ss*ss*ss; c[9] /= ss*ss;
+
+/* constants used in p(x,z) computation */
+  kap[1] = k1/(2*sqrt(ss));
+  kap[2] = 1 + 0.5*(c[1]-c[2]+c[4]-c[7]) - 3*c[3] + c[6] + 1.75*c[8];
+  kap[4] = -9*c[3] + 3*c[6] + 6*c[8] + 3*c[9];
+
+/* constants used in q(x,u) computation */
+  kaq[2] = c[3] - 1.5*c[8] - c[5] - c[4] + 0.5*c[7] + c[6] - c[2];
+  kaq[4] = -3*c[3] - 6*c[4] - 6*c[5] + 3*c[6] + 3*c[7] - 3*c[8] + 3*c[9];
+}
+
+/* q2(u) := u+q2(x,u) in paper */
+double q2(u)
+double u;
+{ return(u-u*(36.0*kaq[2] + 3*kaq[4]*(u*u-3) + c[8]*((u*u-10)*u*u+15))/72.0);
+}
+
+/*  p2(u) := p2(x,u) in paper */
+double p2(u)
+double u;
+{ return( -u*( 36*(kap[2]-1+kap[1]*kap[1])
+     + 3*(kap[4]+4*kap[1]*sqrt(kap[3]))*(u*u-3)
+     + c[8]*((u*u-10)*u*u+15) ) / 72 );
+}
+
+extern int likereg();
+double gldn_like(a)
+double a;
+{ int err;
+
+  scb_des->fix[0] = 1;
+  scb_des->cf[0] = a;
+  max_nr(likereg, scb_des->cf, scb_des->oc, scb_des->res, scb_des->f1,
+    &scb_des->xtwx, scb_des->p, lf_maxit, 1.0e-6, &err); 
+  scb_des->fix[0] = 0;
+
+  return(scb_des->llk);
+}
+
+/* v1/v2 is correct for deg=0 only */
+void get_gldn(fp,des,lo,hi,v)
+fitpt *fp;
+design *des;
+double *lo, *hi;
+int v;
+{ double v1, v2, c, tlk;
+  int err;
+
+  v1 = fp->nlx[v];
+  v2 = fp->t0[v];
+  c = scb_crit * v1 / v2;
+  tlk = des->llk - c*c/2;
+mut_printf("v %8.5f %8.5f  c %8.5f  tlk %8.5f   llk %8.5f\n",v1,v2,c,tlk,des->llk);
+
+  /* want: { a : l(a) >= l(a-hat) - c*c/2 } */
+  lo[v] = fp->coef[v] - scb_crit*v1;
+  hi[v] = fp->coef[v] + scb_crit*v1;
+
+  err = 0;
+
+mut_printf("lo %2d\n",v);
+  lo[v] = solve_secant(gldn_like,tlk,lo[v],fp->coef[v],1e-8,BDF_EXPLEFT,&err);
+  if (err>0) mut_printf("solve_secant error\n");
+mut_printf("hi %2d\n",v);
+  hi[v] = solve_secant(gldn_like,tlk,fp->coef[v],hi[v],1e-8,BDF_EXPRIGHT,&err);
+  if (err>0) mut_printf("solve_secant error\n");
+}
+
+int procvscb2(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ double thhat, sd, *lo, *hi, u;
+  int err, st, tmp;
+  x = des->xev = evpt(&lf->fp,v);
+  tmp = haspc(&lf->pc);
+  /* if ((ker(&lf->sp)==WPARM) && (haspc(&lf->pc)))
+  { lf->coef[v] = thhat = addparcomp(lf,des->xev,PCOEF);
+    lf->nlx[v] = lf->t0[v] = sd = addparcomp(lf,des->xev,PNLX);
+  }
+  else */
+  { haspc(&lf->pc) = 0;
+    st = procvstd(des,lf,v);
+    thhat = lf->fp.coef[v];
+    sd = lf->fp.nlx[v];
+  }
+  if ((type==GLM2) | (type==GLM3) | (type==GLM4))
+  { if (ker(&lf->sp) != WPARM)
+      WARN(("nonparametric fit; correction is invalid"));
+    cumulant(lf,des,sd);
+  }
+  haspc(&lf->pc) = tmp;
+  lo = lf->fp.t0;
+  hi = &lo[lf->fp.nvm];
+  switch(type)
+  {
+    case GLM1:
+      return(st);
+    case GLM2: /* centered scr */
+      lo[v] = kap[1];
+      hi[v] = sqrt(kap[2]);
+      return(st);
+    case GLM3: /* corrected 2 */
+      lo[v] = solve_secant(q2,scb_crit,0.0,2*scb_crit,0.000001,BDF_NONE,&err);
+      return(st);
+    case GLM4: /* corrected 2' */
+      u = fabs(p2(scb_crit));
+      max_p2 = MAX(max_p2,u);
+      return(st);
+    case GLDN:
+      get_gldn(&lf->fp,des,lo,hi,v);
+      return(st);
+  }
+  LERR(("procvscb2: invalid type"));
+  return(st);
+}
+
+void scb(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{ double k1, k2, *lo, *hi, sig, thhat, nlx, rss[10];
+  int i, nterms;
+
+  scb_des= des;
+
+  npar(&lf->sp) = calcp(&lf->sp,lf->lfd.d);
+  des_init(des,lf->lfd.n,npar(&lf->sp));
+
+  type = geth(&lf->fp);
+
+  if (type >= 80) /* simultaneous */
+  {
+    nterms = constants(lf,des,res);
+    scb_crit = critval(0.05,res,nterms,lf->lfd.d,TWO_SIDED,0.0,GAUSS);
+    type -= 10;
+  }
+  else /* pointwise */
+  { res[0] = 1;
+    scb_crit = critval(0.05,res,1,lf->lfd.d,TWO_SIDED,0.0,GAUSS);
+  }
+
+  max_p2 = 0.0;
+  lf->mdl.procv = procvscb2;
+  nstartlf(des,lf);
+
+  if ((fam(&lf->sp)&64)==64)
+  { i = haspc(&lf->pc); haspc(&lf->pc) = 0;
+    ressumm(lf,des,rss);
+    haspc(&lf->pc) = i;
+    sig = sqrt(rss[3]);
+  }
+  else sig = 1.0;
+
+  lo = lf->fp.t0;
+  hi = &lo[lf->fp.nvm];
+  for (i=0; i<lf->fp.nv; i++)
+  { thhat = lf->fp.coef[i];
+    nlx = lf->fp.nlx[i];
+    switch(type)
+    {
+      case GLM1:  /* basic scb */
+        lo[i] = thhat - scb_crit * sig * nlx;
+        hi[i] = thhat + scb_crit * sig * nlx;
+        break;
+      case GLM2:
+        k1 = lo[i];
+        k2 = hi[i];
+        lo[i] = thhat - k1*nlx - scb_crit*nlx*k2;
+        hi[i] = thhat - k1*nlx + scb_crit*nlx*k2;
+        break;
+      case GLM3:
+        k1 = lo[i];
+        lo[i] = thhat - k1*nlx;
+        hi[i] = thhat + k1*nlx;
+      case GLM4:  /* corrected 2' */
+        lo[i] = thhat - (scb_crit-max_p2)*lf->fp.nlx[i];
+        hi[i] = thhat + (scb_crit-max_p2)*lf->fp.nlx[i];
+        break;
+      case GLDN:
+        break;
+    }
+  }
+}
+
+void initscb(lf)
+lfit *lf;
+{ initstd(lf);
+  PROCV(lf) = NULL;
+  KEEPC(lf) = NVAR(lf)+1+k0_reqd(NVAR(lf),NOBS(lf),0);
+  PPROC(lf) = scb;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+int procvsimple(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int lf_status;
+  lf_status = procv_nov(des,lf,v);
+  VVAL(lf,v,0) = des->cf[cfn(des,0)];
+  return(lf_status);
+}
+
+void allocsimple(lf)
+lfit *lf;
+{ lf->fp.coef = VVEC(lf,0);
+  lf->fp.h = NULL;
+}
+
+void initsimple(lf)
+lfit *lf;
+{
+  PROCV(lf) = procvsimple;
+  ALLOC(lf) = allocsimple;
+  PPROC(lf) = NULL;
+  KEEPV(lf) = 1;
+  KEEPC(lf) = 1;
+  NOPC(lf)  = 1;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+/* 3d+8 variables to keep:
+ * d+1 coef+derivs.
+ * d+1 sd's + derivs.
+ * d+1 infl + derivs.
+ *   3 likelihood and d.f's.
+ *   1 bandwidth h
+ *   1 degree.
+ */
+
+void allocstd(lf)
+lfit *lf;
+{ int d;
+  d = NVAR(lf);
+  lf->fp.coef = VVEC(lf,0);
+  lf->fp.nlx  = VVEC(lf,d+1);
+  lf->fp.t0   = VVEC(lf,2*d+2);
+  lf->fp.lik  = VVEC(lf,3*d+3);
+  lf->fp.h    = VVEC(lf,3*d+6);
+  lf->fp.deg  = VVEC(lf,3*d+7);
+}
+
+int procvstd(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int d, p, nvm, i, k;
+  double t0[1+MXDIM], vari[1+MXDIM];
+  k = procv_var(des,lf,v);
+  if (lf_error) return(k);
+   
+  d = lf->lfd.d;
+  p = npar(&lf->sp);
+  nvm = lf->fp.nvm;
+
+  if (k != LF_OK) lf_status_msg(k);
+
+  lf->fp.lik[v] = des->llk;
+  lf->fp.lik[nvm+v] = des->tr2;
+  lf->fp.lik[2*nvm+v] = des->tr0 - des->tr2;
+
+  for (i=0; i<des->ncoef; i++)
+    vari[i] = des->V[p*cfn(des,0) + cfn(des,i)];
+  vari[0] = sqrt(vari[0]);
+  if (vari[0]>0) for (i=1; i<des->ncoef; i++) vari[i] /= vari[0];
+
+  t0[0] = sqrt(des->f1[0]);
+  if (t0[0]>0) for (i=1; i<des->ncoef; i++) t0[i] = des->f1[i]/t0[0];
+
+  if (dc(&lf->fp)) dercor(&lf->lfd,&lf->sp,des,des->cf);
+  subparcomp(des,lf,des->cf);
+  for (i=0; i<des->ncoef; i++)
+    lf->fp.coef[i*lf->fp.nvm+v] =  des->cf[cfn(des,i)];
+
+  subparcomp2(des,lf,vari,t0);
+  for (i=0; i<des->ncoef; i++)
+  { lf->fp.nlx[i*nvm+v] = vari[i];
+    lf->fp.t0[i*nvm+v]  = t0[i];
+  }
+
+  lf->fp.deg[v] = deg(&lf->sp);
+  return(k);
+}
+
+void pprocstd(lf,des,res)
+lfit *lf;
+design *des;
+double *res;
+{
+  ressumm(lf,des,res);
+}
+
+void initstd(lf)
+lfit *lf;
+{ PROCV(lf) = procvstd;
+  ALLOC(lf) = allocstd;
+  PPROC(lf) = pprocstd;
+  KEEPV(lf) = 3*NVAR(lf) + 8;
+  KEEPC(lf) = 6;
+  NOPC(lf)  = 0;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+extern void procstd(), allocstd();
+extern double robscale;
+
+double vocri(lk,t0,t2,pen)
+double lk, t0, t2, pen;
+{ if (pen==0) return(-2*t0*lk/((t0-t2)*(t0-t2)));
+  return((-2*lk+pen*t2)/t0);
+}
+
+double intvo(des,lf,c0,c1,a,p,t0,t20,t21)
+design *des;
+lfit *lf;
+double *c0, *c1, a, t0, t20, t21;
+int p;
+{ double th, lk, link[LLEN];
+  int i, ii;
+  lk = 0;
+  for (i=0; i<des->n; i++)
+  { ii = des->ind[i];
+    th = (1-a)*innerprod(c0,d_xi(des,i),p) + a*innerprod(c1,d_xi(des,i),p);
+    stdlinks(link,&lf->lfd,&lf->sp,ii,th,robscale);
+    lk += wght(des,ii)*link[ZLIK];
+  }
+  des->llk = lk;
+  return(vocri(des->llk,t0,(1-a)*t20+a*t21,pen(&lf->sp)));
+}
+
+int procvvord(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ double tr[6], gcv, g0, ap, coef[4][10], t2[4], th, md;
+  int i, j, k, d1, i0, p1, ip;
+  des->xev = evpt(&lf->fp,v);
+
+  ap = pen(&lf->sp);
+  if ((ap==0) & ((fam(&lf->sp)&63)!=TGAUS)) ap = 2.0;
+  d1 = deg(&lf->sp); p1 = npar(&lf->sp);
+  for (i=0; i<p1; i++) coef[0][i] = coef[1][i] = coef[2][i] = coef[3][i] = 0.0;
+  i0 = 0; g0 = 0;
+  ip = 1;
+
+  for (i=deg0(&lf->sp); i<=d1; i++)
+  { deg(&lf->sp) = i;
+    des->p = npar(&lf->sp) = calcp(&lf->sp,lf->lfd.d);
+    k = locfit(&lf->lfd,des,&lf->sp,0, i==deg0(&lf->sp),0);
+
+    local_df(&lf->lfd,&lf->sp,des,tr);
+    gcv = vocri(des->llk,tr[0],tr[2],ap);
+    if ((i==deg0(&lf->sp)) || (gcv<g0)) { i0 = i; g0 = gcv; md = i; }
+
+    for (j=0; j<des->p; j++) coef[i][j] = des->cf[j];
+    t2[i] = tr[2];
+
+#ifdef RESEARCH
+    if ((ip) && (i>deg0(&lf->sp)))
+    { for (j=1; j<10; j++)
+      { gcv = intvo(des,lf,coef[i-1],coef[i],j/10.0,des->p,tr[0],t2[i-1],t2[i]);
+        if (gcv<g0) { g0 = gcv; md = i-1+j/10.0; }
+      }
+    }
+#endif
+  }
+  lf->fp.h[v] = des->h;
+  if (lf->fp.h[v]<=0) WARN(("zero bandwidth in procvvord"));
+
+  if (i0<d1) /* recompute the best fit */
+  { deg(&lf->sp) = i0;
+    des->p = npar(&lf->sp) = calcp(&lf->sp,lf->lfd.d);
+    k = locfit(&lf->lfd,des,&lf->sp,0,0,0);
+    for (i=npar(&lf->sp); i<p1; i++) des->cf[i] = 0.0;
+    i0 = md; if (i0==d1) i0--;
+    th = md-i0;
+    for (i=0; i<p1; i++) des->cf[i] = (1-th)*coef[i0][i]+th*coef[i0+1][i];
+    deg(&lf->sp) = d1; npar(&lf->sp) = p1;
+  }
+
+  for (i=0; i<p1; i++) lf->fp.coef[i*lf->fp.nvm+v] = des->cf[i];
+  lf->fp.deg[v] = md;
+  return(k);
+}
+
+void initvord(lf)
+lfit *lf;
+{ initstd(lf);
+  PROCV(lf) = procvvord;
+  ALLOC(lf) = allocstd;
+  PPROC(lf) = NULL;
+  KEEPC(lf) = 0;
+  NOPC(lf)  = 1;
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ * functions for computing and subtracting, adding the
+ * parametric component
+ */
+
+#include "lfev.h"
+
+int noparcomp(sp)
+smpar *sp;
+{ int tg;
+  if (ubas(sp)) return(1);
+  tg = fam(sp) & 63;
+  if (tg<=THAZ) return(1);
+  if (tg==TROBT) return(1);
+  if (tg==TCAUC) return(1);
+  if (tg==TQUANT) return(1);
+  return(0);
+}
+
+int pc_reqd(d,p)
+int d, p;
+{ return(d + 2*p + jac_reqd(p));
+}
+
+void pcchk(pc,d,p,lc)
+paramcomp *pc;
+int d, p, lc;
+{ int rw;
+  double *z;
+
+  rw = pc_reqd(d,p);
+  if (pc->lwk < rw)
+  { pc->wk = (double *)calloc(rw,sizeof(double));
+    if ( pc->wk == NULL ) {
+      printf("Problem allocating memory for pc->wk\n");fflush(stdout);
+    }
+    pc->lwk= rw;
+  }
+  z = pc->wk;
+
+  pc->xbar = z; z += d;
+  pc->coef = z; z += p;
+  pc->f    = z; z += p;
+
+  z = jac_alloc(&pc->xtwx,p,z);
+  pc->xtwx.p = p;
+}
+
+void compparcomp(des,lfd,sp,pc,nopc)
+design *des;
+lfdata *lfd;
+smpar *sp;
+paramcomp *pc;
+int nopc;
+{ int i, j, k, p;
+  double wt, sw;
+
+  if (lf_debug>1) mut_printf(" compparcomp:\n");
+  p = des->p;
+  pcchk(pc,lfd->d,p,1);
+
+  for (i=0; i<lfd->d; i++) pc->xbar[i] = 0.0;
+  sw = 0.0;
+  for (i=0; i<lfd->n; i++)
+  { 
+    wt = prwt(lfd,i);
+    sw += wt;
+    for (j=0; j<lfd->d; j++)
+      pc->xbar[j] += datum(lfd,j,i)*wt;
+    des->ind[i] = i;
+    wght(des,i) = 1.0;
+  }
+  for (i=0; i<lfd->d; i++) pc->xbar[i] /= sw;
+  if ((nopc) || noparcomp(sp))
+  { haspc(pc) = 0;
+    return;
+  }
+  haspc(pc) = 1;
+  des->xev = pc->xbar;
+  k = locfit(lfd,des,sp,0,0,0);
+  if (k != LF_OK) lf_status_msg(k);
+  if (lf_error) return;
+  switch(k)
+  { case LF_NOPT: return;
+    case LF_INFA: return;
+    case LF_NCON: return;
+    case LF_OOB: return;
+    case LF_NSLN: return;
+    case LF_PF:
+      WARN(("compparcomp: perfect fit"));
+    case LF_OK:
+    case LF_DONE:
+      for (i=0; i<p; i++)
+      { pc->coef[i] = des->cf[i];
+        pc->xtwx.dg[i] = des->xtwx.dg[i];
+        pc->xtwx.wk[i] = des->xtwx.wk[i];
+      }
+      for (i=0; i<p*p; i++)
+      { pc->xtwx.Z[i] = des->xtwx.Z[i];
+        pc->xtwx.Q[i] = des->xtwx.Q[i];
+      }
+      pc->xtwx.sm = des->xtwx.sm;
+      pc->xtwx.st = des->xtwx.st;
+      return;
+    default:
+      LERR(("compparcomp: locfit unknown return status %d",k));
+      return;
+  }
+}
+
+void subparcomp(des,lf,coef)
+design *des;
+lfit *lf;
+double *coef;
+{ int i, nd;
+  deriv *dv;
+  paramcomp *pc;
+
+  pc = &lf->pc;
+  if (!haspc(pc)) return;
+
+  dv = &lf->dv; nd = dv->nd;
+  fitfun(&lf->lfd, &lf->sp, des->xev,pc->xbar,des->f1,dv);
+  coef[0] -= innerprod(pc->coef,des->f1,pc->xtwx.p);
+  if (des->ncoef == 1) return;
+
+  dv->nd = nd+1;
+  for (i=0; i<lf->lfd.d; i++)
+  { dv->deriv[nd] = i;
+    fitfun(&lf->lfd, &lf->sp, des->xev,pc->xbar,des->f1,dv);
+    coef[i+1] -= innerprod(pc->coef,des->f1,pc->xtwx.p);
+  }
+  dv->nd = nd;
+}
+
+void subparcomp2(des,lf,vr,il)
+design *des;
+lfit *lf;
+double *vr, *il;
+{ double t0, t1;
+  int i, nd;
+  deriv *dv;
+  paramcomp *pc;
+
+  pc = &lf->pc;
+  if (!haspc(pc)) return;
+
+  dv = &lf->dv; nd = dv->nd;
+
+  fitfun(&lf->lfd, &lf->sp, des->xev,pc->xbar,des->f1,dv);
+  for (i=0; i<npar(&lf->sp); i++) pc->f[i] = des->f1[i];
+  jacob_solve(&pc->xtwx,des->f1);
+  t0 = sqrt(innerprod(pc->f,des->f1,pc->xtwx.p));
+  vr[0] -= t0;
+  il[0] -= t0;
+  if ((t0==0) | (des->ncoef==1)) return;
+
+  dv->nd = nd+1;
+  for (i=0; i<lf->lfd.d; i++)
+  { dv->deriv[nd] = i;
+    fitfun(&lf->lfd, &lf->sp, des->xev,pc->xbar,pc->f,dv);
+    t1 = innerprod(pc->f,des->f1,pc->xtwx.p)/t0;
+    vr[i+1] -= t1;
+    il[i+1] -= t1;
+  }
+  dv->nd = nd;
+}
+
+double addparcomp(lf,x,c)
+lfit *lf;
+double *x;
+int c;
+{ double y;
+  paramcomp *pc;
+
+  pc = &lf->pc;
+  if (!haspc(pc)) return(0.0);
+  fitfun(&lf->lfd, &lf->sp, x,pc->xbar,pc->f,&lf->dv);
+  if (c==PCOEF) return(innerprod(pc->coef,pc->f,pc->xtwx.p));
+  if ((c==PNLX)|(c==PT0)|(c==PVARI))
+  { y = sqrt(jacob_qf(&pc->xtwx,pc->f));
+    return(y);
+  }
+  return(0.0);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+/*
+  preplot():  interpolates the fit to a new set of points.
+  lf  -- the fit structure.
+  x   -- the points to predict at.
+  f   -- vector to return the predictions.
+  se  -- vector to return std errors (NULL if not req'd)
+  band-- char for conf band type. ('n'=none, 'g'=global etc.)
+  n   -- no of predictions (or vector of margin lengths for grid)
+  where -- where to predict:
+           1 = points in the array x.
+           2 = grid defined by margins in x.
+           3 = data points from lf (ignore x).
+           4 = fit points from lf (ignore x).
+  what -- what to predict.
+           (PCOEF etc; see lfcons.h file)
+
+*/
+
+#define NWH 8
+static char *whtyp[NWH] = { "coef", "nlx", "infl", "band",
+                          "degr", "like", "rdf", "vari" };
+static int   whval[NWH] = { PCOEF, PNLX, PT0, PBAND, PDEGR, PLIK, PRDF, PVARI };
+int ppwhat(z)
+char *z;
+{ int val;
+
+  val = pmatch(z, whtyp, whval, NWH, -1);
+  if (val==-1) LERR(("Unknown what = %s",z));
+  return(val);
+}
+
+static char cb;
+double *sef, *fit, sigmahat;
+
+void predptall(lf,x,what,ev,i)
+lfit *lf;
+double *x;
+int what, ev, i;
+{ double lik, rdf;
+  fit[i] = dointpoint(lf,x,what,ev,i);
+  if (cb=='n') return;
+  sef[i] = dointpoint(lf,x,PNLX,ev,i);
+  if (cb=='g')
+  { sef[i] *= sigmahat;
+    return;
+  }
+  if (cb=='l')
+  { lik = dointpoint(lf,x,PLIK,ev,i);
+    rdf = dointpoint(lf,x,PRDF,ev,i);
+    sef[i] *= sqrt(-2*lik/rdf);
+    return;
+  }
+  if (cb=='p')
+  { sef[i] = sigmahat*sqrt(1+sef[i]*sef[i]);
+    return;
+  }
+}
+
+void predptdir(lf,des,x,what,i)
+lfit *lf;
+design *des;
+double *x;
+int what, i;
+{ int needcv;
+  des->xev = x;
+  needcv = (what==PVARI) | (what==PNLX) | (what==PT0) | (what==PRDF);
+  locfit(&lf->lfd,des,&lf->sp,0,1,needcv);
+  switch(what)
+  { case PCOEF: fit[i] = des->cf[0]; break;
+    case PVARI: fit[i] = des->V[0]; break;
+    case PNLX:  fit[i] = sqrt(des->V[0]); break;
+    case PT0:   fit[i] = des->f1[0]; break;
+    case PBAND: fit[i] = des->h; break;
+    case PDEGR: fit[i] = deg(&lf->sp); break;
+    case PLIK:  fit[i] = des->llk; break;
+    case PRDF:  fit[i] = des->tr0 - des->tr2; break;
+    default: LERR(("unknown what in predptdir"));
+  }
+}
+
+void prepvector(lf,des,x,n,what,dir) /* interpolate a vector */
+lfit *lf;
+design *des;
+double **x;
+int n, what, dir;
+{ int i, j;
+  double xx[MXDIM];
+  for (i=0; i<n; i++)
+  { for (j=0; j<lf->fp.d; j++) xx[j] = x[j][i];
+    if (dir)
+      predptdir(lf,des,xx,what,i);
+    else
+      predptall(lf,xx,what,ev(&lf->evs),i);
+    if (lf_error) return;
+  }
+}
+
+void prepfitp(lf,what)
+lfit *lf;
+int what;
+{ int  i;
+  for (i=0; i<lf->fp.nv; i++)
+  { predptall(lf,evpt(&lf->fp,i),what,EFITP,i);
+    if (lf_error) return;
+  }
+}
+
+void prepgrid(lf,des,x,mg,n,what,dir) /* interpolate a grid given margins */
+lfit *lf;
+design *des;
+double **x;
+int *mg, dir, n, what;
+{ int i, ii, j, d;
+  double xv[MXDIM];
+  d = lf->fp.d;
+  for (i=0; i<n; i++)
+  { ii = i;
+    for (j=0; j<d; j++)
+    { xv[j] = x[j][ii%mg[j]];
+      ii /= mg[j];
+    }
+    if (dir)
+      predptdir(lf,des,xv,what,i);
+    else
+      predptall(lf,xv,what,ev(&lf->evs),i);
+    if (lf_error) return;
+  }
+}
+
+void preplot(lf,x,f,se,band,mg,where,what,dir)
+lfit *lf;
+double **x, *f, *se;
+int *mg, where, what, dir;
+char band;
+{ int d, i, n;
+  double *xx[MXDIM];
+  design ppdes;
+  d = lf->fp.d;
+  fit = f;
+  sef = se;
+  cb = band;
+  if (cb!='n') sigmahat = sqrt(rv(&lf->fp));
+  if (dir) des_init(&ppdes,lf->lfd.n,npar(&lf->sp));
+
+  switch(where)
+  { case 1: /* vector */
+      n = mg[0];
+      prepvector(lf,&ppdes,x,n,what,dir);
+      break;
+    case 2: /* grid */
+      n = 1;
+      for (i=0; i<d; i++) n *= mg[i];
+      prepgrid(lf,&ppdes,x,mg,n,what,dir);
+      break;
+    case 3: /* data */
+      n = lf->lfd.n;
+      if ((ev(&lf->evs)==EDATA) | (ev(&lf->evs)==ECROS))
+      { prepfitp(lf,what);
+        dir = 0;
+      }
+      else
+      { for (i=0; i<d; i++) xx[i] = dvari(&lf->lfd,i);
+        prepvector(lf,&ppdes,xx,n,what,dir);
+      }
+      break;
+    case 4: /* fit points */
+      n = lf->fp.nv; dir = 0;
+      prepfitp(lf,what);
+      break;
+    default:
+      LERR(("unknown where in preplot"));
+  }
+
+  if ((!dir) && ((what==PT0)|(what==PVARI)))
+    for (i=0; i<n; i++) f[i] = f[i]*f[i];
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+#include "lfev.h"
+
+int procv_nov(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int lf_status;
+
+  if (lf_debug>1) mut_printf(" procveraw: %d\n",v);
+  des->xev = evpt(&lf->fp,v);
+
+  if (acri(&lf->sp)==ANONE)
+    lf_status = locfit(&lf->lfd,des,&lf->sp,0,1,0);
+  else
+    lf_status = alocfit(&lf->lfd,&lf->sp,&lf->dv,des,0);
+  if (lf->fp.h != NULL) lf->fp.h[v] = des->h;
+
+  return(lf_status);
+}
+
+int procv_var(des,lf,v)
+design *des;
+lfit *lf;
+int v;
+{ int i, lf_status;
+
+  if (lf_debug>1) mut_printf(" procvraw: %d\n",v);
+  des->xev = evpt(&lf->fp,v);
+
+  if (acri(&lf->sp)==ANONE)
+    lf_status = locfit(&lf->lfd,des,&lf->sp,0,1,1);
+  else
+    lf_status = alocfit(&lf->lfd,&lf->sp,&lf->dv,des,1);
+  if (lf->fp.h != NULL) lf->fp.h[v] = des->h;
+
+  return(lf_status);
+}
+/*
+ * Copyright 1996-2006 Catherine Loader.
+ */
+/*
+ * startmodule(lf,des,mod,dir) -- the standard entry point.
+ *   des and lf are pointers to the design and fit structures.
+ *   mod - module name. Set to NULL if the module is already
+ *         initialized.
+ *   dir - for dynamic modules, the directory.
+ *
+ * initmodule(mdl,mod,dir,lf)
+ *   direct call for module initialization.
+ *
+ */
+
+#include "lfev.h"
+
+#ifdef WINDOWS
+
+#define DIRSEP '\\'
+#define PATHSEP ';'
+
+#else
+
+#define DIRSEP '/'
+#define PATHSEP ':'
+
+#endif
+
+
+#ifdef ALLOW_MODULES
+
+#ifdef WINDOWS
+#include <windows.h>
+#define DLEXT "dll"
+#define DLOPEN(x) LoadLibrary(x)
+#define DLSYM GetProcAddress
+
+#else
+
+#include <dlfcn.h>
+#define DLEXT "so"
+#define DLOPEN(x) dlopen(x,RTLD_LAZY)
+#define DLSYM dlsym
+#endif
+
+#endif
+
+static double fpkap[6];
+void fitpt_init(fp)
+fitpt *fp;
+{ 
+  dc(fp) = 0;
+  geth(fp) = GSTD;
+  fp->nv = fp->nvm = 0;
+  if (fp->kap==NULL) fp->kap = fpkap;
+}
+
+void lfit_init(lf)
+lfit *lf;
+{
+  lfdata_init(&lf->lfd);
+  evstruc_init(&lf->evs);
+  smpar_init(&lf->sp,&lf->lfd);
+  deriv_init(&lf->dv);
+  fitpt_init(&lf->fp);
+  lf->mdl.np = 0;
+}
+
+void fitdefault(lf)
+lfit *lf;
+{ WARN(("fitdefault deprecated -- use lfit_init()"));
+  lfit_init(lf);
+}
+
+void set_flim(lfd,evs)
+lfdata *lfd;
+evstruc *evs;
+{ int i, j, d, n;
+  double z, mx, mn, *bx;
+
+  if (ev(evs)==ESPHR) return;
+  d = lfd->d; n = lfd->n;
+  bx = evs->fl;
+  for (i=0; i<d; i++)
+    if (bx[i]==bx[i+d])
+    { if (lfd->sty[i]==STANGL)
+      { bx[i] = 0.0; bx[i+d] = 2*PI*lfd->sca[i];
+      }
+      else
+      { mx = mn = datum(lfd,i,0);
+        for (j=1; j<n; j++)
+        { mx = MAX(mx,datum(lfd,i,j));
+          mn = MIN(mn,datum(lfd,i,j));
+        }
+        if (lfd->xl[i]<lfd->xl[i+d]) /* user set xlim; maybe use them. */
+        { z = mx-mn;
+          if (mn-0.2*z < lfd->xl[i]) mn = lfd->xl[i];
+          if (mx+0.2*z > lfd->xl[i+d]) mx = lfd->xl[i+d];
+        }
+        bx[i] = mn;
+        bx[i+d] = mx;
+      }
+    }
+}
+
+double vvari(v,n)
+double *v;
+int n;
+{ int i;
+  double xb, s2;
+  xb = s2 = 0.0;
+  for (i=0; i<n; i++) xb += v[i];
+  xb /= n;
+  for (i=0; i<n; i++) s2 += SQR(v[i]-xb);
+  return(s2/(n-1));
+}
+
+void set_scales(lfd)
+lfdata *lfd;
+{ int i;
+  for (i=0; i<lfd->d; i++)
+    if (lfd->sca[i]<=0) /* set automatic scales */
+    { if (lfd->sty[i]==STANGL)
+        lfd->sca[i] = 1.0;
+      else lfd->sca[i] = sqrt(vvari(lfd->x[i],lfd->n));
+    }
+}
+
+void nstartlf(des,lf)
+design *des;
+lfit *lf;
+{ int i, d, n;
+
+  if (lf_debug>0) mut_printf("nstartlf\n");
+  n = lf->lfd.n;
+  d = lf->lfd.d;
+  npar(&lf->sp) = calcp(&lf->sp,d);
+
+  des_init(des,n,npar(&lf->sp));
+  set_scales(&lf->lfd);
+  set_flim(&lf->lfd,&lf->evs);
+  compparcomp(des,&lf->lfd,&lf->sp,&lf->pc,lf->mdl.nopc);
+  if (lf_error) return;
+  makecfn(&lf->sp,des,&lf->dv,lf->lfd.d);
+
+  lf->lfd.ord = 0;
+  if ((d==1) && (lf->lfd.sty[0]!=STANGL))
+  { i = 1;
+    while ((i<n) && (datum(&lf->lfd,0,i)>=datum(&lf->lfd,0,i-1))) i++;
+    lf->lfd.ord = (i==n);
+  }
+  for (i=0; i<npar(&lf->sp); i++) des->fix[i] = 0;
+
+  lf->fp.d = lf->lfd.d;
+  lf->fp.hasd = (des->ncoef==(1+lf->fp.d));
+  lf->fp.nv = lf->evs.nce = 0;
+
+  if (lf_debug>1) mut_printf("call eval structure %d\n",ev(&lf->evs));
+  switch(ev(&lf->evs))
+  { case EPHULL: triang_start(des,lf); break;
+    case EDATA:  dataf(des,lf); break;
+    case ECROS:  crossf(des,lf); break;
+    case EGRID:  gridf(des,lf); break;
+    case ETREE:  atree_start(des,lf); break;
+    case EKDCE:  kt(&lf->sp) = KCE;
+    case EKDTR:  kdtre_start(des,lf); break;
+    case EPRES:  preset(des,lf); break;
+    case EXBAR:  xbarf(des,lf); break;
+    case ENONE:  return;
+    case ESPHR:  sphere_start(des,lf); break;
+    case ESPEC:  lf->evs.espec(des,lf); break;
+    default: LERR(("startlf: Invalid evaluation structure %d",ev(&lf->evs)));
+  }
+
+  /* renormalize for family=density */
+  if ((de_renorm) && (fam(&lf->sp)==TDEN)) dens_renorm(lf,des);
+}
+
+/*
+ * getnextdir() gets the next dir from a string dirpath="dir1:dir2:dir3:..."
+ *   (;-separated on windows).
+ *   The directory is returned through dirnext, and the function returns
+ *   a pointer to the next string.
+ *   typical usage is recursive, dirpath = getnextdir(dirpath,dirnext).
+ *   with the above example, this sets dirnext="dir1" and dirpath="dir2:dir3:...".
+ * if the input dirpath has no :, then it is copied to dirnext, and return is "".
+ * if input dirpath is "", dirnext is set to "", and null pointer returned.
+ */
+char *getnextdir(dirpath,dirnext)
+char *dirpath, *dirnext;
+{ char *z;
+  if (strlen(dirpath)==0)
+  { sprintf(dirnext,"");
+    return(NULL);
+  }
+
+  z = strchr(dirpath,PATHSEP);
+  if (z==NULL)
+  { sprintf(dirnext,"%s%c",dirpath,DIRSEP);
+    return(&dirpath[strlen(dirnext)]);
+  }
+
+  *z = '\0';
+  sprintf(dirnext,"%s%c",dirpath,DIRSEP);
+  return(++z);
+}
+
+int initmodule(mdl, mod, dir, lf)
+module *mdl;
+lfit *lf;
+char *mod, *dir;
+{ int n, d, p;
+#ifdef ALLOW_MODULES
+#ifdef WINDOWS
+HINSTANCE res;
+typedef void (CALLBACK* DLLFN)();
+DLLFN init;
+#else
+void *res;
+void (*init)();
+#endif
+  char distname[500];
+#endif
+
+  n = lf->lfd.n;
+  d = lf->lfd.d;
+  p = npar(&lf->sp) = calcp(&lf->sp,d);
+
+  mdl->isset = 1;
+  PPROC(lf) = NULL;
+  if (strcmp(mod,"std")==0)    { initstd(lf); return(1); }
+  if (strcmp(mod,"simple")==0) { initsimple(lf); return(1); }
+  if (strcmp(mod,"allcf")==0)  { initallcf(lf); return(1); }
+  if (strcmp(mod,"hatm")==0)   { inithatm(lf); return(1); }
+  if (strcmp(mod,"kappa")==0)  { initkappa(lf); return(1); }
+  if (strcmp(mod,"lscv")==0)   { initlscv(lf); return(1); }
+  if (strcmp(mod,"gamf")==0)   { initgam(lf); return(1); }
+  if (strcmp(mod,"gamp")==0)   { initgam(lf); return(1); }
+  if (strcmp(mod,"rband")==0)  { initrband(lf); return(1); }
+  if (strcmp(mod,"scb")==0)    { initscb(lf); return(1); }
+  if (strcmp(mod,"vord")==0)   { initvord(lf); return(1); }
+
+#ifdef ALLOW_MODULES
+  while (dir != NULL)
+  {
+    dir = getnextdir(dir,distname);
+    sprintf(&distname[strlen(distname)],"mod%s.%s",mod,DLEXT);
+    res = DLOPEN(distname);
+    if (res==NULL)
+    {
+#ifdef WINDOWS
+      mut_printf("LoadLibrary failed: %s, %d\n",distname,GetLastError());
+#else
+      mut_printf("dlopen failed: %s\n",dlerror());
+#endif
+    }
+    else
+    {
+#ifdef WINDOWS
+      mut_printf("LoadLibrary success: %s\n",distname);
+#else
+      mut_printf("dlopen success: %s\n",distname);
+#endif
+      sprintf(distname,"init%s",mod);
+      init = (void *)DLSYM(res,distname);
+      if (init==NULL)
+      { mut_printf("I can't find %s() function.\n",distname);
+        mdl->isset = 0;
+        return(0);
+      }
+      init(lf);
+      return(1);
+    }
+  }
+#endif
+
+  mdl->isset = 0;
+  return(0);
+}
+
+/*
+ * startmodule is the entry point to launch the fit.
+ * if mod is provided, will first initialize the module.
+ * if mod==NULL, assumes the module has been initialized separately.
+ */
+void startmodule(lf,des,mod,dir)
+lfit *lf;
+design *des;
+char *mod, *dir;
+{ int z;
+
+  if (mod != NULL)
+  { z = initmodule(&lf->mdl,mod,dir,lf);
+    if (!z) return;
+  }
+
+  lf->fp.nv = lf->evs.nce = 0;
+  if (lf_error) return;
+  if (PROCV(lf) != NULL) nstartlf(des,lf);
+  if (lf_error) return;
+  if (PPROC(lf) != NULL) PPROC(lf)(lf,des,lf->fp.kap);
+}
+
+/* for compatability, more or less. */
+void startlf(des,lf,vfun,nopc)
+design *des;
+lfit *lf;
+int (*vfun)(), nopc;
+{ int z;
+  z = initmodule(&lf->mdl,"std",NULL,lf);
+  if (!z) return;
+  lf->mdl.procv = vfun;
+  lf->mdl.nopc = nopc;
+  nstartlf(des,lf);
+}