Exemple #1
0
/*
******** ell_3m_svd_d
**
** singular value decomposition:
** mat = uu * diag(sval) * vv
**
** singular values are square roots of eigenvalues of mat * mat^T
** columns of uu are eigenvectors of mat * mat^T
** rows of vv are eigenvectors of mat^T * mat
**
** returns info about singular values according to ellCubeRoot enum
**
** HEY: I think this does the wrong thing when given a symmetric
** matrix with negative eigenvalues . . .
*/
int
ell_3m_svd_d(double uu[9], double sval[3], double vv[9],
             const double mat[9], const int newton) {
  double trn[9], msqr[9], eval[3], evec[9];
  int roots;

  ELL_3M_TRANSPOSE(trn, mat);
  ELL_3M_MUL(msqr, mat, trn);
  roots = ell_3m_eigensolve_d(eval, evec, msqr, newton);
  sval[0] = sqrt(eval[0]);
  sval[1] = sqrt(eval[1]);
  sval[2] = sqrt(eval[2]);
  ELL_3M_TRANSPOSE(uu, evec);
  ELL_3M_MUL(msqr, trn, mat);
  _ell_3m_evecs_d(vv, eval, roots, msqr);

  return roots;
}
void
_gageVecAnswer(gageContext *ctx, gagePerVolume *pvl) {
  char me[]="_gageVecAnswer";
  double cmag, tmpMat[9], mgevec[9], mgeval[3];
  double symm[9], asym[9], tran[9], eval[3], tmpVec[3], norm;
  gage_t *vecAns, *normAns, *jacAns, *curlAns, *hesAns, *curlGradAns, 
         *helGradAns, *dirHelDirAns, *curlnormgradAns;
  /* int asw; */

  vecAns          = pvl->directAnswer[gageVecVector];
  normAns         = pvl->directAnswer[gageVecNormalized];
  jacAns          = pvl->directAnswer[gageVecJacobian];
  curlAns         = pvl->directAnswer[gageVecCurl];
  hesAns          = pvl->directAnswer[gageVecHessian];
  curlGradAns     = pvl->directAnswer[gageVecCurlGradient];
  curlnormgradAns = pvl->directAnswer[gageVecCurlNormGrad];
  helGradAns      = pvl->directAnswer[gageVecHelGradient];
  dirHelDirAns    = pvl->directAnswer[gageVecDirHelDeriv];

  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector)) {
    /* done if doV */
    if (ctx->verbose) {
      fprintf(stderr, "vec = ");
      ell_3v_PRINT(stderr, vecAns);
    }
  }
  /* done if doV 
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector{0,1,2})) {
  }
  */
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLength)) {
    pvl->directAnswer[gageVecLength][0] = AIR_CAST(gage_t, ELL_3V_LEN(vecAns));
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormalized)) {
    if (pvl->directAnswer[gageVecLength][0]) {
      ELL_3V_SCALE_TT(normAns, gage_t,
                      1.0/pvl->directAnswer[gageVecLength][0], vecAns);
    } else {
      ELL_3V_COPY(normAns, gageZeroNormal);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecJacobian)) {
    /* done if doD1 */
    /*
      0:dv_x/dx  1:dv_x/dy  2:dv_x/dz
      3:dv_y/dx  4:dv_y/dy  5:dv_y/dz
      6:dv_z/dx  7:dv_z/dy  8:dv_z/dz
    */
    if (ctx->verbose) {
      fprintf(stderr, "%s: jac = \n", me);
      ell_3m_PRINT(stderr, jacAns);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivergence)) {
    pvl->directAnswer[gageVecDivergence][0] = jacAns[0] + jacAns[4] + jacAns[8];
    if (ctx->verbose) {
      fprintf(stderr, "%s: div = %g + %g + %g  = %g\n", me,
              jacAns[0], jacAns[4], jacAns[8],
              pvl->directAnswer[gageVecDivergence][0]);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurl)) {
    ELL_3V_SET(curlAns,
               jacAns[7] - jacAns[5],
               jacAns[2] - jacAns[6],
               jacAns[3] - jacAns[1]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNorm)) {
    pvl->directAnswer[gageVecCurlNorm][0] =
      AIR_CAST(gage_t, ELL_3V_LEN(curlAns));  
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelicity)) {
    pvl->directAnswer[gageVecHelicity][0] = 
      ELL_3V_DOT(vecAns, curlAns);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormHelicity)) {
    cmag = ELL_3V_LEN(curlAns);
    pvl->directAnswer[gageVecNormHelicity][0] = 
      AIR_CAST(gage_t, cmag ? ELL_3V_DOT(normAns, curlAns)/cmag : 0);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLambda2)) {
      ELL_3M_TRANSPOSE(tran, jacAns);
      /* symmetric part */
      ELL_3M_SCALE_ADD2(symm, 0.5, jacAns,  0.5, tran);
      /* antisymmetric part */
      ELL_3M_SCALE_ADD2(asym, 0.5, jacAns, -0.5, tran);
      /* square symmetric part */
      ELL_3M_MUL(tmpMat, symm, symm);
      ELL_3M_COPY(symm, tmpMat);
      /* square antisymmetric part */
      ELL_3M_MUL(tmpMat, asym, asym);
      /* sum of both */
      ELL_3M_ADD2(symm, symm, tmpMat);
      /* get eigenvalues in sorted order */
      /* asw = */ ell_3m_eigenvalues_d(eval, symm, AIR_TRUE);
      pvl->directAnswer[gageVecLambda2][0] = AIR_CAST(gage_t, eval[1]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecImaginaryPart)) {
      pvl->directAnswer[gageVecImaginaryPart][0] =
        AIR_CAST(gage_t, gage_imaginary_part_eigenvalues(jacAns)); 
  }
  /* 2nd order vector derivative continued */ 
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHessian)) {
      /* done if doD2 */
      /* the ordering is induced by the scalar hessian computation :
         0:d2v_x/dxdx   1:d2v_x/dxdy   2:d2v_x/dxdz
         3:d2v_x/dydx   4:d2v_x/dydy   5:d2v_x/dydz
         6:d2v_x/dzdx   7:d2v_x/dzdy   8:d2v_x/dzdz
         9:d2v_y/dxdx       [...]
             [...]
        24:dv2_z/dzdx  25:d2v_z/dzdy  26:d2v_z/dzdz
      */
    if (ctx->verbose) {
      fprintf(stderr, "%s: hes = \n", me);
      ell_3m_PRINT(stderr, hesAns); /* ?? */
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivGradient)) {
      pvl->directAnswer[gageVecDivGradient][0] = hesAns[0] + hesAns[12] + hesAns[24];
      pvl->directAnswer[gageVecDivGradient][1] = hesAns[1] + hesAns[13] + hesAns[25];
      pvl->directAnswer[gageVecDivGradient][2] = hesAns[2] + hesAns[14] + hesAns[26];
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlGradient)) {
      pvl->directAnswer[gageVecCurlGradient][0] = hesAns[21]-hesAns[15];
      pvl->directAnswer[gageVecCurlGradient][1] = hesAns[22]-hesAns[16];
      pvl->directAnswer[gageVecCurlGradient][2] = hesAns[23]-hesAns[17];
      pvl->directAnswer[gageVecCurlGradient][3] = hesAns[ 6]-hesAns[18];
      pvl->directAnswer[gageVecCurlGradient][4] = hesAns[ 7]-hesAns[19];
      pvl->directAnswer[gageVecCurlGradient][5] = hesAns[ 8]-hesAns[20];
      pvl->directAnswer[gageVecCurlGradient][6] = hesAns[ 9]-hesAns[ 1];
      pvl->directAnswer[gageVecCurlGradient][7] = hesAns[10]-hesAns[ 2];
      pvl->directAnswer[gageVecCurlGradient][8] = hesAns[11]-hesAns[ 3];
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNormGrad)) {
      norm = 1./ELL_3V_LEN(curlAns);

      tmpVec[0] = hesAns[21] - hesAns[15];
      tmpVec[1] = hesAns[ 6] - hesAns[18];
      tmpVec[2] = hesAns[ 9] - hesAns[ 3];      
      pvl->directAnswer[gageVecCurlNormGrad][0]=
        AIR_CAST(gage_t, norm*ELL_3V_DOT(tmpVec, curlAns));

      tmpVec[0] = hesAns[22] - hesAns[16];
      tmpVec[1] = hesAns[ 7] - hesAns[19];
      tmpVec[2] = hesAns[10] - hesAns[ 4];      
      pvl->directAnswer[gageVecCurlNormGrad][1]=
        AIR_CAST(gage_t, norm*ELL_3V_DOT(tmpVec, curlAns));

      tmpVec[0] = hesAns[23] - hesAns[17];
      tmpVec[1] = hesAns[ 8] - hesAns[20];
      tmpVec[2] = hesAns[11] - hesAns[ 5];      
      pvl->directAnswer[gageVecCurlNormGrad][2]=
        AIR_CAST(gage_t,  norm*ELL_3V_DOT(tmpVec, curlAns));
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNCurlNormGrad)) {
      norm = 1./ELL_3V_LEN(curlnormgradAns);
      ELL_3V_SCALE_TT(pvl->directAnswer[gageVecNCurlNormGrad], gage_t,
                      norm, pvl->directAnswer[gageVecCurlNormGrad]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelGradient)) {
      pvl->directAnswer[gageVecHelGradient][0] = 
          jacAns[0]*curlAns[0]+
          jacAns[3]*curlAns[1]+
          jacAns[6]*curlAns[2]+
          curlGradAns[0]*vecAns[0]+
          curlGradAns[3]*vecAns[1]+
          curlGradAns[6]*vecAns[2];
      pvl->directAnswer[gageVecHelGradient][1] = 
          jacAns[1]*curlAns[0]+
          jacAns[4]*curlAns[1]+
          jacAns[7]*curlAns[2]+
          curlGradAns[1]*vecAns[0]+
          curlGradAns[4]*vecAns[1]+
          curlGradAns[7]*vecAns[2];
      pvl->directAnswer[gageVecHelGradient][0] = 
          jacAns[2]*curlAns[0]+
          jacAns[5]*curlAns[1]+
          jacAns[8]*curlAns[2]+
          curlGradAns[2]*vecAns[0]+
          curlGradAns[5]*vecAns[1]+
          curlGradAns[8]*vecAns[2];
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDirHelDeriv)) {
      pvl->directAnswer[gageVecDirHelDeriv][0] = 
        ELL_3V_DOT(normAns, helGradAns);          
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecProjHelGradient)) {
      pvl->directAnswer[gageVecDirHelDeriv][0] = 
          helGradAns[0]-dirHelDirAns[0]*normAns[0];
      pvl->directAnswer[gageVecDirHelDeriv][1] = 
          helGradAns[1]-dirHelDirAns[0]*normAns[1];
      pvl->directAnswer[gageVecDirHelDeriv][2] = 
          helGradAns[2]-dirHelDirAns[0]*normAns[2];
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient0)) {
    ELL_3V_SET(pvl->directAnswer[gageVecGradient0],
               jacAns[0],
               jacAns[1],
               jacAns[2]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient1)) {
    ELL_3V_SET(pvl->directAnswer[gageVecGradient1],
               jacAns[3],
               jacAns[4],
               jacAns[5]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient2)) {
    ELL_3V_SET(pvl->directAnswer[gageVecGradient2],
               jacAns[6],
               jacAns[7],
               jacAns[8]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMultiGrad)) {
    ELL_3M_IDENTITY_SET(pvl->directAnswer[gageVecMultiGrad]);
    ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad],
                      pvl->directAnswer[gageVecGradient0],
                      pvl->directAnswer[gageVecGradient0]);
    ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad],
                      pvl->directAnswer[gageVecGradient1],
                      pvl->directAnswer[gageVecGradient1]);
    ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad],
                      pvl->directAnswer[gageVecGradient2],
                      pvl->directAnswer[gageVecGradient2]);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGFrob)) {
    pvl->directAnswer[gageVecMGFrob][0] 
      = AIR_CAST(gage_t, ELL_3M_FROB(pvl->directAnswer[gageVecMultiGrad]));
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEval)) {
    ELL_3M_COPY(tmpMat, pvl->directAnswer[gageVecMultiGrad]);
    /* HEY: look at the return value for root multiplicity? */
    ell_3m_eigensolve_d(mgeval, mgevec, tmpMat, AIR_TRUE);
    ELL_3V_COPY_TT(pvl->directAnswer[gageVecMGEval], gage_t, mgeval);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEvec)) {
    ELL_3M_COPY_TT(pvl->directAnswer[gageVecMGEvec], gage_t, mgevec);
  }

  return;
}
Exemple #3
0
/* Calculates 2 new centroids (and a new segmentation) from distances
   between Q-balls and centroids, returns true if segmentation changed
*/
int
_tenCalccent2(const int gradcount, const double qpoints[],
              const double dists[], double centroid[6], unsigned int seg[]) {
#if 0
  /* HEY: Attempt to implement better line-adding by adding
     outerproducts of points and estimating major eigenvector
     afterwards */
  int i,changed=AIR_FALSE;
  double sum0[9],sum1[9],mat[9], eval[3],evec[9];

  ELL_3M_ZERO_SET( sum0 );
  ELL_3M_ZERO_SET( sum1 );
  for( i = 0; i < gradcount; i++ ) {
    if( dists[i] < dists[gradcount+i] ) {
      ELL_3MV_OUTER( mat, qpoints +3*i, qpoints +3*i );
      ELL_3M_ADD2( sum0, sum0, mat );
      changed = changed || (seg[i] != 0);
      seg[i] = 0;
    } else {
      ELL_3MV_OUTER( mat, qpoints +3*i +gradcount, qpoints +3*i +gradcount );
      ELL_3M_ADD2( sum1, sum1, mat );
      changed = changed || (seg[i] != 1);
      seg[i] = 1;
    }
  }

  ell_3m_eigensolve_d( eval, evec, sum0, 0 );
  ELL_3V_COPY( centroid, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) );
  /* ELL_3V_SCALE( centroid, ELL_3V_LEN( centroid ), centroid ); */


  ell_3m_eigensolve_d( eval, evec, sum1, 0 );
  ELL_3V_COPY( centroid +3, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) );
  /* ELL_3V_SCALE( centroid +3, ELL_3V_LEN( centroid ), centroid +3); Normalize */

  return changed;
#endif

  int i, sign, seg0count=0, seg1count=0, changed=AIR_FALSE;
  double oldcentroid[6], diff[3], sum[3];

  memcpy( oldcentroid, centroid, 6 * sizeof( double ));

  for( i = 0; i < gradcount; i++ ) {
    if( dists[ 0*gradcount +i] < dists[1*gradcount +i] ) {
      /* Try to resolve sign so that centroid do not end up as all 0 */
      /* Choose signs so that the point lies "on the same side" as */
      /* the previous centroid. */
      diff[0] = oldcentroid[0] - qpoints[3*i +0];
      diff[1] = oldcentroid[1] - qpoints[3*i +1];
      diff[2] = oldcentroid[2] - qpoints[3*i +2];
      sum[0] = oldcentroid[0] + qpoints[3*i +0];
      sum[1] = oldcentroid[1] + qpoints[3*i +1];
      sum[2] = oldcentroid[2] + qpoints[3*i +2];
      sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) <
        (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1;

      changed = changed || (seg[i] != 0);

      seg[i] = 0;
      centroid[0] += sign * qpoints[3*i +0];
      centroid[1] += sign * qpoints[3*i +1];
      centroid[2] += sign * qpoints[3*i +2];
      seg0count++;
    } else {
      diff[0] = oldcentroid[3+0] - qpoints[3*i +0];
      diff[1] = oldcentroid[3+1] - qpoints[3*i +1];
      diff[2] = oldcentroid[3+2] - qpoints[3*i +2];
      sum[0] = oldcentroid[3+0] + qpoints[3*i +0];
      sum[1] = oldcentroid[3+1] + qpoints[3*i +1];
      sum[2] = oldcentroid[3+2] + qpoints[3*i +2];
      sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) <
        (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1;

      changed = changed || (seg[i] != 1);

      seg[i] = 1;
      centroid[3+0] += sign * qpoints[3*i +0];
      centroid[3+1] += sign * qpoints[3*i +1];
      centroid[3+2] += sign * qpoints[3*i +2];
      seg1count++;
    }
  }
  centroid[0] /= seg0count;
  centroid[1] /= seg0count;
  centroid[2] /= seg0count;
  centroid[3+0] /= seg1count;
  centroid[3+1] /= seg1count;
  centroid[3+2] /= seg1count;

  /* printf("cent = %f %f %f %f %f %f\n", centroid[0],centroid[1],centroid[2],centroid[3],centroid[4],centroid[5] ); */

  /*
    Should give error if any segment contains less than 6 elements,
    i.e. if( seg0count < 6 || seg1count < 6 ), since that would
    imply that a tensor cannot be computed for that segment.
  */

  return changed;
}
Exemple #4
0
void
_gageSclAnswer (gageContext *ctx, gagePerVolume *pvl) {
  char me[]="_gageSclAnswer";
  double gmag=0, *hess, *norm, *gvec, *gten, *k1, *k2, curv=0, 
    sHess[9]={0,0,0,0,0,0,0,0,0};
  double tmpMat[9], tmpVec[3], hevec[9], heval[3];
  double len, gp1[3], gp2[3], *nPerp, ncTen[9], nProj[9]={0,0,0,0,0,0,0,0,0};
  double alpha = 0.5;
  double beta = 0.5;
  double gamma = 5;
  double cc = 1e-6;
#define FD_MEDIAN_MAX 16
  int fd, nidx, xi, yi, zi;
  double *fw, iv3wght[2*FD_MEDIAN_MAX*FD_MEDIAN_MAX*FD_MEDIAN_MAX],
    wghtSum, wght;

  /* convenience pointers for work below */
  hess = pvl->directAnswer[gageSclHessian];
  gvec = pvl->directAnswer[gageSclGradVec];
  norm = pvl->directAnswer[gageSclNormal];
  nPerp = pvl->directAnswer[gageSclNPerp];
  gten = pvl->directAnswer[gageSclGeomTens];
  k1 = pvl->directAnswer[gageSclK1];
  k2 = pvl->directAnswer[gageSclK2];
  
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclValue)) {
    /* done if doV */
    if (ctx->verbose) {
      fprintf(stderr, "%s: val = % 15.7f\n", me, 
              (double)(pvl->directAnswer[gageSclValue][0]));
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradVec)) {
    /* done if doD1 */
    if (ctx->verbose) {
      fprintf(stderr, "%s: gvec = ", me);
      ell_3v_print_d(stderr, gvec);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGradMag)) {
    /* this is the true value of gradient magnitude */
    gmag = pvl->directAnswer[gageSclGradMag][0] = sqrt(ELL_3V_DOT(gvec, gvec));
  }

  /* NB: it would seem that gageParmGradMagMin is completely ignored ... */

  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNormal)) {
    if (gmag) {
      ELL_3V_SCALE(norm, 1/gmag, gvec);
      /* polishing ... 
      len = sqrt(ELL_3V_DOT(norm, norm));
      ELL_3V_SCALE(norm, 1/len, norm);
      */
    } else {
      ELL_3V_COPY(norm, gageZeroNormal);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclNPerp)) {
    /* nPerp = I - outer(norm, norm) */
    /* NB: this sets both nPerp and nProj */
    ELL_3MV_OUTER(nProj, norm, norm);
    ELL_3M_SCALE(nPerp, -1, nProj);
    nPerp[0] += 1;
    nPerp[4] += 1;
    nPerp[8] += 1;
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessian)) {
    /* done if doD2 */
    if (ctx->verbose) {
      fprintf(stderr, "%s: hess = \n", me);
      ell_3m_print_d(stderr, hess);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclLaplacian)) {
    pvl->directAnswer[gageSclLaplacian][0] = hess[0] + hess[4] + hess[8];
    if (ctx->verbose) {
      fprintf(stderr, "%s: lapl = %g + %g + %g  = %g\n", me,
              hess[0], hess[4], hess[8], 
              pvl->directAnswer[gageSclLaplacian][0]);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessFrob)) {
    pvl->directAnswer[gageSclHessFrob][0] = ELL_3M_FROB(hess);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEval)) {
    /* HEY: look at the return value for root multiplicity? */
    ell_3m_eigensolve_d(heval, hevec, hess, AIR_TRUE);
    ELL_3V_COPY(pvl->directAnswer[gageSclHessEval], heval);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessEvec)) {
    ELL_3M_COPY(pvl->directAnswer[gageSclHessEvec], hevec);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessRidgeness)) {
    double A, B, S;
    if (heval[1] >0 || heval[2]>0) {
      pvl->directAnswer[gageSclHessRidgeness][0] = 0;
    }
    else if (AIR_ABS(heval[1])<1e-10 || AIR_ABS(heval[2])<1e-10) {
      pvl->directAnswer[gageSclHessRidgeness][0] = 0;
    }
    else {
      double *ans;
      A = AIR_ABS(heval[1])/AIR_ABS(heval[2]);
      B = AIR_ABS(heval[0])/sqrt(AIR_ABS(heval[1]*heval[2]));
      S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]);
      ans = pvl->directAnswer[gageSclHessRidgeness];
      ans[0] = (1-exp(-A*A/(2*alpha*alpha))) *
        exp(-B*B/(2*beta*beta)) *
        (1-exp(-S*S/(2*gamma*gamma))) *
        exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[2]*heval[2]));
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessValleyness)) {
    double A, B, S;
    if (heval[0] <0 || heval[1]<0) {
      pvl->directAnswer[gageSclHessValleyness][0] = 0;
    }
    else if (AIR_ABS(heval[0])<1e-10 || AIR_ABS(heval[1])<1e-10) {
      pvl->directAnswer[gageSclHessValleyness][0] = 0;
    }
    else {
      double *ans;
      A = AIR_ABS(heval[1])/AIR_ABS(heval[0]);
      B = AIR_ABS(heval[2])/sqrt(AIR_ABS(heval[1]*heval[0]));
      S = sqrt(heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2]);
      ans = pvl->directAnswer[gageSclHessValleyness];
      ans[0] = (1-exp(-A*A/(2*alpha*alpha))) *
        exp(-B*B/(2*beta*beta)) *
        (1-exp(-S*S/(2*gamma*gamma))) *
        exp(-2*cc*cc/(AIR_ABS(heval[1])*heval[0]*heval[0]));
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclHessMode)) {
    pvl->directAnswer[gageSclHessMode][0] = airMode3_d(heval);
  }

  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageScl2ndDD)) {
    ELL_3MV_MUL(tmpVec, hess, norm);
    pvl->directAnswer[gageScl2ndDD][0] = ELL_3V_DOT(norm, tmpVec);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclGeomTens)) {
    if (gmag > ctx->parm.gradMagCurvMin) {
      /* parm.curvNormalSide applied here to determine the sense of the
         normal when doing all curvature calculations */
      ELL_3M_SCALE(sHess, -(ctx->parm.curvNormalSide)/gmag, hess);
      
      /* gten = nPerp * sHess * nPerp */
      ELL_3M_MUL(tmpMat, sHess, nPerp);
      ELL_3M_MUL(gten, nPerp, tmpMat);

      if (ctx->verbose) {
        fprintf(stderr, "%s: gten: \n", me);
        ell_3m_print_d(stderr, gten);
        ELL_3MV_MUL(tmpVec, gten, norm);
        len = ELL_3V_LEN(tmpVec);
        fprintf(stderr, "%s: should be small: %30.15f\n", me, (double)len);
        ell_3v_perp_d(gp1, norm);
        ELL_3MV_MUL(tmpVec, gten, gp1);
        len = ELL_3V_LEN(tmpVec);
        fprintf(stderr, "%s: should be bigger: %30.15f\n", me, (double)len);
        ELL_3V_CROSS(gp2, gp1, norm);
        ELL_3MV_MUL(tmpVec, gten, gp2);
        len = ELL_3V_LEN(tmpVec);
        fprintf(stderr, "%s: should (also) be bigger: %30.15f\n",
                me, (double)len);
      }
    } else {
      ELL_3M_ZERO_SET(gten);
    }
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclTotalCurv)) {
    curv = pvl->directAnswer[gageSclTotalCurv][0] = ELL_3M_FROB(gten);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclShapeTrace)) {
    pvl->directAnswer[gageSclShapeTrace][0] = (curv
                                               ? ELL_3M_TRACE(gten)/curv
                                               : 0);
  }
  if ( (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclK1)) ||
       (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclK2)) ){
    double T, N, D;
    T = ELL_3M_TRACE(gten);
    N = curv;
    D = 2*N*N - T*T;
    /*
    if (D < -0.0000001) {
      fprintf(stderr, "%s: %g %g\n", me, T, N);
      fprintf(stderr, "%s: !!! D curv determinant % 22.10f < 0.0\n", me, D);
      fprintf(stderr, "%s: gten: \n", me);
      ell_3m_print_d(stderr, gten);
    }
    */
    D = AIR_MAX(D, 0);
    D = sqrt(D);
    k1[0] = 0.5*(T + D);
    k2[0] = 0.5*(T - D);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclMeanCurv)) {
    pvl->directAnswer[gageSclMeanCurv][0] = (*k1 + *k2)/2;
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclGaussCurv)) {
    pvl->directAnswer[gageSclGaussCurv][0] = (*k1)*(*k2);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query,  gageSclShapeIndex)) {
    pvl->directAnswer[gageSclShapeIndex][0] = 
      -(2/AIR_PI)*atan2(*k1 + *k2, *k1 - *k2);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir1)) {
    /* HEY: this only works when K1, K2, 0 are all well mutually distinct,
       since these are the eigenvalues of the geometry tensor, and this
       code assumes that the eigenspaces are all one-dimensional */
    ELL_3M_COPY(tmpMat, gten);
    ELL_3M_DIAG_SET(tmpMat, gten[0] - *k1, gten[4]- *k1, gten[8] - *k1);
    ell_3m_1d_nullspace_d(tmpVec, tmpMat);
    ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir1], tmpVec);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclCurvDir2)) {
    /* HEY: this only works when K1, K2, 0 are all well mutually distinct,
       since these are the eigenvalues of the geometry tensor, and this
       code assumes that the eigenspaces are all one-dimensional */
    ELL_3M_COPY(tmpMat, gten);
    ELL_3M_DIAG_SET(tmpMat, gten[0] - *k2, gten[4] - *k2, gten[8] - *k2);
    ell_3m_1d_nullspace_d(tmpVec, tmpMat);
    ELL_3V_COPY(pvl->directAnswer[gageSclCurvDir2], tmpVec);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclFlowlineCurv)) {
    if (gmag >= ctx->parm.gradMagCurvMin) {
      /* because of the gageSclGeomTens prerequisite, sHess, nPerp, and
         nProj are all already set */
      /* ncTen = nPerp * sHess * nProj */
      ELL_3M_MUL(tmpMat, sHess, nProj);
      ELL_3M_MUL(ncTen, nPerp, tmpMat);
    } else {
      ELL_3M_ZERO_SET(ncTen);
    }
    /* there used to be a wrong extra sqrt() here */
    pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB(ncTen);
  }
  if (GAGE_QUERY_ITEM_TEST(pvl->query, gageSclMedian)) {
    /* this item is currently a complete oddball in that it does not
       benefit from anything done in the "filter" stage, which is in
       fact a waste of time if the query consists only  of this item */
    fd = 2*ctx->radius;
    if (fd > FD_MEDIAN_MAX) {
      fprintf(stderr, "%s: PANIC: current filter diameter = %d "
              "> FD_MEDIAN_MAX = %d\n", me, fd, FD_MEDIAN_MAX);
      exit(1);
    }
    fw = ctx->fw + fd*3*gageKernel00;
    /* HEY: this needs some optimization help */
    wghtSum = 0;
    nidx = 0;
    for (xi=0; xi<fd; xi++) {
      for (yi=0; yi<fd; yi++) {
        for (zi=0; zi<fd; zi++) {
          iv3wght[0 + 2*nidx] = pvl->iv3[nidx];
          iv3wght[1 + 2*nidx] = fw[xi + 0*fd]*fw[yi + 1*fd]*fw[zi + 2*fd];
          wghtSum += iv3wght[1 + 2*nidx];
          nidx++;
        }
      }
    }
    qsort(iv3wght, fd*fd*fd, 2*sizeof(double), nrrdValCompare[nrrdTypeDouble]);
    wght = 0;
    for (nidx=0; nidx<fd*fd*fd; nidx++) {
      wght += iv3wght[1 + 2*nidx];
      if (wght > wghtSum/2) {
        break;
      }
    }
    pvl->directAnswer[gageSclMedian][0] = iv3wght[0 + 2*nidx];
  }
  return;
}