Index: trunk/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c
===================================================================
--- trunk/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c	(revision 36189)
+++ trunk/Ohana/src/dvopsps/src/insert_detections_dvopsps_catalog.c	(revision 36375)
@@ -202,5 +202,5 @@
 
   // PrintIOBuffer (buffer, "INSERT INTO dvoDetectionFull (imageID, ippDetectID, detectID, ippObjID, objID, photcode, flags, zp, zpErr, airMass, expTime, ra, dec_, raErr, decErr) VALUES \n");
-  PrintIOBuffer (buffer, "INSERT IGNORE INTO dvoDetectionFull (imageID, ippDetectID, detectID, ippObjID, objID, flags, zp, zpErr, airMass, expTime, ra, dec_, raErr, decErr) VALUES \n");
+  PrintIOBuffer (buffer, "INSERT INTO dvoDetectionFull (imageID, ippDetectID, detectID, ippObjID, objID, flags, zp, zpErr, airMass, expTime, ra, dec_, raErr, decErr) VALUES \n");
 
   return TRUE;
Index: trunk/Ohana/src/opihi/cmd.astro/fitplx.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.astro/fitplx.c	(revision 36189)
+++ trunk/Ohana/src/opihi/cmd.astro/fitplx.c	(revision 36375)
@@ -41,4 +41,8 @@
     remove_argument (N, &argc, argv);
     VERBOSE = TRUE;
+  }
+  if ((N = get_argument (argc, argv, "-vv"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = 2;
   }
 
@@ -145,5 +149,7 @@
 
   PlxFit fit;
-  FitPMandPar (&fit, X, dX, Y, dY, t, pX, pY, n, VERBOSE);
+  if (!FitPMandPar (&fit, X, dX, Y, dY, t, pX, pY, n, VERBOSE)) {
+    return FALSE;
+  }
 
   // fprintf (stderr, "Roff, Doff: %f, %f; dRo, dDo: %f, %f\n", fit.Ro, fit.Do, fit.dRo, fit.dDo);
@@ -204,9 +210,9 @@
   for (i = 0; i < Npts; i++) {
 
-    if (VERBOSE) fprintf (stderr, "%f %f : %f %f : %f : %f %f\n", X[i], dX[i], Y[i], dY[i], T[i], pR[i], pD[i]);
+    if (VERBOSE == 2) fprintf (stderr, "%f %f : %f %f : %f : %f %f\n", X[i], dX[i], Y[i], dY[i], T[i], pR[i], pD[i]);
 
     /* handle case where dX or dY = 0.0 */
-    wx = 1.0 / SQ(dX[i]);
-    wy = 1.0 / SQ(dY[i]);
+    wx = (fabs(dX[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dX[i]);
+    wy = (fabs(dY[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dY[i]);
 
     Wx += wx;
@@ -266,5 +272,17 @@
   B[4][0] = PRX + PDY;
 
-  dgaussjordan ((double **)A, (double **)B, 5, 1);
+  if (!dgaussjordan ((double **)A, (double **)B, 5, 1)) {
+    if (VERBOSE) fprintf (stderr, "error in fit\n");
+    if (VERBOSE == 2) {
+      int j;
+      for (i = 0; i < 5; i++) {
+	for (j = 0; j < 5; j++) {
+	  fprintf (stderr, "%e ", A[i][j]);
+	}
+	fprintf (stderr, " : %e\n", A[i][0]);
+      }
+    }
+    return FALSE;
+  }
 
   fit[0].Ro = B[0][0];
@@ -285,7 +303,9 @@
     Xf = fit[0].Ro + fit[0].uR*T[i] + fit[0].p*pR[i];
     Yf = fit[0].Do + fit[0].uD*T[i] + fit[0].p*pD[i];
-    chisq += SQ(X[i] - Xf) / SQ(dX[i]);
-    chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
-    if (VERBOSE) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
+    wx = (fabs(dX[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dX[i]);
+    wy = (fabs(dY[i]) < 0.0001) ? 1.0 : 1.0 / SQ(dY[i]);
+    chisq += SQ(X[i] - Xf) * wx;
+    chisq += SQ(Y[i] - Yf) * wy;
+    // if (VERBOSE) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
   }
   fit[0].Nfit = Npts;
Index: trunk/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 36189)
+++ trunk/Ohana/src/opihi/cmd.data/Makefile	(revision 36375)
@@ -64,4 +64,6 @@
 $(SRC)/imhist.$(ARCH).o	\
 $(SRC)/imsmooth.$(ARCH).o	\
+$(SRC)/imsmooth.generic.$(ARCH).o	\
+$(SRC)/imsmooth.2d.$(ARCH).o	\
 $(SRC)/integrate.$(ARCH).o	\
 $(SRC)/interpolate.$(ARCH).o	\
Index: trunk/Ohana/src/opihi/cmd.data/imsmooth.2d.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/imsmooth.2d.c	(revision 36375)
+++ trunk/Ohana/src/opihi/cmd.data/imsmooth.2d.c	(revision 36375)
@@ -0,0 +1,214 @@
+# include "data.h"
+
+# define NRAD_MAX 44
+static float radii2[NRAD_MAX] = {  0.0,  1.0,  2.0,  4.0,  5.0, 
+				   8.0,  9.0, 10.0, 13.0, 16.0, 
+				   17.0, 18.0, 20.0, 25.0, 26.0, 
+				   29.0, 32.0, 34.0, 36.0, 37.0, 
+				   40.0, 41.0, 45.0, 49.0, 50.0, 
+				   52.0, 53.0, 58.0, 61.0, 64.0,
+				   65.0, 68.0, 72.0, 73.0, 74.0, 
+				   80.0, 81.0, 82.0, 85.0, 89.0,
+				   90.0, 97.0, 98.0, 100.0
+};
+
+static int   radiiN[NRAD_MAX] = {    1,  4, 4,  4,  8,
+				     4,  4, 8,  8,  4,
+				     8,  4, 8, 12,  8, 
+				     8,  4, 8,  4,  8,
+				     8,  8, 8,  4, 12,
+				     8,  8, 8,  8,  4, 
+				     16, 8, 4,  8,  8, 
+				      8, 4, 8, 16,  8, 
+				      8, 8, 4, 12
+};
+
+/*
+ 0,  0 :  0 : 0  :   0
+ 0,  1 :  1 : 1  :   1
+ 1,  1 :  2 : 1  :   2
+ 0,  2 :  3 : 2  :   4
+ 1,  2 :  4 : 2  :   5
+ 2,  2 :  5 : 2  :   8
+ 0,  3 :  6 : 3  :   9
+ 1,  3 :  7 : 3  :  10
+ 2,  3 :  8 : 3  :  13
+ 0,  4 :  9 : 4  :  16
+ 1,  4 : 10 : 4  :  17
+ 3,  3 : 11 : 4  :  18
+ 2,  4 : 12 : 4  :  20
+ 0,  5 : 13 : 5  :  25
+ 3,  4 : 13 : 5  :  25
+ 1,  5 : 14 : 5  :  26
+ 2,  5 : 15 : 5  :  29
+ 4,  4 : 16 : 5  :  32
+ 3,  5 : 17 : 5  :  34
+ 0,  6 : 18 : 6  :  36
+ 1,  6 : 19 : 6  :  37
+ 2,  6 : 20 : 6  :  40
+ 4,  5 : 21 : 6  :  41
+ 3,  6 : 22 : 6  :  45
+ 0,  7 : 23 : 7  :  49
+ 1,  7 : 24 : 7  :  50 *
+ 5,  5 : 24 : 7  :  50 *
+ 4,  6 : 25 : 7  :  52
+ 2,  7 : 26 : 7  :  53
+ 3,  7 : 27 : 7  :  58
+ 5,  6 : 28 : 7  :  61
+ 0,  8 : 29 : 8  :  64
+ 1,  8 : 30 : 8  :  65 *
+ 4,  7 : 30 : 8  :  65 *
+ 2,  8 : 31 : 8  :  68
+ 6,  6 : 32 : 8  :  72
+ 3,  8 : 33 : 8  :  73
+ 5,  7 : 34 : 8  :  74
+ 4,  8 : 35 : 8  :  80
+ 0,  9 : 36 : 9  :  81
+ 1,  9 : 37 : 9  :  82
+ 2,  9 : 38 : 9  :  85 *
+ 6,  7 : 38 : 9  :  85 *
+ 5,  8 : 39 : 9  :  89
+ 3,  9 : 40 : 9  :  90
+ 4,  9 : 41 : 9  :  97
+ 7,  7 : 42 : 9  :  98
+ 0, 10 : 43 : 10 : 100 *
+ 6,  8 : 43 : 10 : 100 *
+ */
+
+# define ADD_AXIS(RAD,DD) s += radflux[RAD]*(vi[p - DD] + vi[p + DD] + vi[p - DD*Nx] + vi[p + DD*Nx]);
+# define ADD_DIAG(RAD,DD) s += radflux[RAD]*(vi[p - DD - DD*Nx] + vi[p + DD - DD*Nx] + vi[p - DD + DD*Nx] + vi[p + DD + DD*Nx]);
+# define ADD_RAND(RAD,DX,DY) s += radflux[RAD]*(vi[p - DX - DY*Nx] + vi[p + DX - DY*Nx] + vi[p - DX + DY*Nx] + vi[p + DX + DY*Nx] + \
+						vi[p - DY - DX*Nx] + vi[p + DY - DX*Nx] + vi[p - DY + DX*Nx] + vi[p + DY + DX*Nx]);
+
+int imsmooth_2d (int argc, char **argv) {
+  
+  int i, j;
+  float *vi, *vo;
+  Buffer *in;
+
+  if (argc != 4) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) sigma Nsigma\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  float  sigma = atof(argv[2]);
+  float Nsigma = atof(argv[3]);
+
+  int Ns = (int)(Nsigma * sigma);
+  Ns = MAX (3, MIN (Ns, 10));
+  int Ns2 = Ns * Ns;
+
+  // we are going to use a hard-wired radial profile
+  float *radflux = NULL;
+  ALLOCATE (radflux, float, NRAD_MAX);
+  float sum = 0.0;
+  for (i = 0; i < NRAD_MAX; i++) {
+    float z = radii2[i] / SQ(sigma);
+    radflux[i] = 1.0 / (1.0 + z + pow(z,1.666));
+    if (radii2[i] > Ns2) continue;
+    sum += radiiN[i] * radflux[i];
+  }
+  for (i = 0; i < NRAD_MAX; i++) {
+    radflux[i] = radflux[i] / sum;
+  }
+
+  int Nx = in[0].matrix.Naxis[0];
+  int Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (vo, float, Nx*Ny);
+  memset (vo, 0, Nx*Ny*sizeof(float));
+
+  /* smooth in X direction */
+  vi = (float *) in[0].matrix.buffer;
+  for (j = Ns; j < Ny - Ns; j++) {
+    for (i = Ns; i < Nx - Ns; i++) {
+
+      int p = i + j*Nx; // current pixel of interest
+      float s = radflux[0]*(vi[p]);
+
+      // r <= 1.0
+      ADD_AXIS (1,  1);      // r^2 = 1
+
+      // r <= 2.0
+      ADD_DIAG (2,  1);      // r^2 = 2
+      ADD_AXIS (3,  2);      // r^2 = 4
+
+      // r <= 3.0
+      ADD_RAND (4,  1,  2);  // r^2 = 5
+      ADD_DIAG (5,  2);      // r^2 = 8
+      ADD_AXIS (6,  3);      // r^2 = 9
+      if (Ns <= 3) goto finish;
+
+      // r <= 4.0
+      ADD_RAND (7,  1,  3);  // r^2 = 10
+      ADD_RAND (8,  2,  3);  // r^2 = 13
+      ADD_AXIS (9,  4);      // r^2 = 16
+      if (Ns <= 4) goto finish;
+
+      // r <= 5.0
+      ADD_RAND (10,  1,  4); // r^2 = 17
+      ADD_DIAG (11,  3);     // r^2 = 18
+      ADD_RAND (12,  2,  4); // r^2 = 20
+      ADD_RAND (13,  3,  4); // r^2 = 25
+      ADD_AXIS (13,  5);     // r^2 = 25
+      if (Ns <= 5) goto finish;
+
+      // r <= 6.0
+      ADD_RAND (14,  1,  5); // r^2 = 26
+      ADD_RAND (15,  2,  5); // r^2 = 29
+      ADD_DIAG (16,  4);     // r^2 = 32
+      ADD_RAND (17,  3,  5); // r^2 = 34
+      ADD_AXIS (18,  6);     // r^2 = 36
+      if (Ns <= 6) goto finish;
+
+      // r <= 7.0
+      ADD_RAND (19,  1,  6); // r^2 = 37
+      ADD_RAND (20,  2,  6); // r^2 = 40
+      ADD_RAND (21,  4,  5); // r^2 = 41
+      ADD_RAND (22,  3,  6); // r^2 = 45
+      ADD_AXIS (23,  7);     // r^2 = 49
+      if (Ns <= 7) goto finish;
+
+      // r <= 8.0
+      ADD_RAND (24,  1,  7); // r^2 = 50
+      ADD_DIAG (24,  5);     // r^2 = 50
+      ADD_RAND (25,  4,  6); // r^2 =   52
+      ADD_RAND (26,  2,  7); // r^2 =   53
+      ADD_RAND (27,  3,  7); // r^2 =   58
+      ADD_RAND (28,  5,  6); // r^2 =   61
+      ADD_AXIS (29,  8);     // r^2 =   64
+      if (Ns <= 8) goto finish;
+
+      ADD_RAND (30,  1,  8); // r^2 =   65 *
+      ADD_RAND (30,  4,  7); // r^2 =   65 *
+      ADD_RAND (31,  2,  8); // r^2 =   68
+      ADD_DIAG (32,  6);     // r^2 =   72
+      ADD_RAND (33,  3,  8); // r^2 =   73
+      ADD_RAND (34,  5,  7); // r^2 =   74
+      ADD_RAND (35,  4,  8); // r^2 =   80
+      ADD_AXIS (36,  9);     // r^2 =   81
+      if (Ns <= 9) goto finish;
+
+      ADD_RAND (37,  1,  9); // r^2 =   82
+      ADD_RAND (38,  2,  9); // r^2 =   85 *
+      ADD_RAND (38,  6,  7); // r^2 =   85 *
+      ADD_RAND (39,  5,  8); // r^2 =   89
+      ADD_RAND (40,  3,  9); // r^2 =   90
+      ADD_RAND (41,  4,  9); // r^2 =   97
+      ADD_DIAG (42,  7);     // r^2 =   98
+      ADD_RAND (43,  6,  8); // r^2 =  100 *
+      ADD_AXIS (43, 10);     // r^2 =  100 *
+      if (Ns <= 10) goto finish;
+
+    finish:
+      vo[p] = s;
+    }
+  }
+
+  free (radflux);
+  free (in[0].matrix.buffer);
+  in[0].matrix.buffer = (char *) vo;
+
+  return (TRUE);
+}
+
Index: trunk/Ohana/src/opihi/cmd.data/imsmooth.generic.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/imsmooth.generic.c	(revision 36375)
+++ trunk/Ohana/src/opihi/cmd.data/imsmooth.generic.c	(revision 36375)
@@ -0,0 +1,68 @@
+# include "data.h"
+
+int imsmooth_generic (int argc, char **argv) {
+  
+  int i, j, n;
+  float *vi, *vo;
+  float g, s;
+  Buffer *in;
+  Vector *vec;
+  float *temp;
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: imsmooth (input) vector\n");
+    return (FALSE);
+  }
+  
+  if ((in  = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);
+
+  if (vec[0].Nelements % 2 == 0) {
+    gprint (GP_ERR, "vector must have an odd number of elements\n");
+    return FALSE;
+  }
+
+  int Nx = in[0].matrix.Naxis[0];
+  int Ny = in[0].matrix.Naxis[1];
+  ALLOCATE (temp, float, Nx*Ny);
+
+  /* build a 1D gaussian */
+  int Ns = 0.5*(vec[0].Nelements - 1);
+  opihi_flt *smvec = &vec[0].elements.Flt[Ns];
+
+  /* smooth in X direction */
+  for (j = 0; j < Ny; j++) {
+    vi = (float *) in[0].matrix.buffer + j*Nx;
+    vo = &temp[j*Nx];
+    for (i = 0; i < Nx; i++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (i+n < 0) continue;
+	if (i+n >= Nx) continue;
+	s += smvec[n]*vi[i+n];
+	g += smvec[n];
+      }
+      vo[i] = s / g;
+    }
+  }
+
+  /* smooth in Y direction */
+  for (i = 0; i < Nx; i++) {
+    vi = &temp[i];
+    vo = (float *)in[0].matrix.buffer + i;
+    for (j = 0; j < Ny; j++) {
+      g = s = 0;
+      for (n = -Ns; n < Ns + 1; n++) {
+	if (j+n < 0) continue;
+	if (j+n >= Ny) continue;
+	s += smvec[n]*vi[(n+j)*Nx];
+	g += smvec[n];
+      }
+      vo[j*Nx] = s / g;
+    }
+  }
+
+  free (temp);
+  return (TRUE);
+}
+
Index: trunk/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/init.c	(revision 36189)
+++ trunk/Ohana/src/opihi/cmd.data/init.c	(revision 36375)
@@ -53,4 +53,6 @@
 int imhist           PROTO((int, char **));
 int imsmooth         PROTO((int, char **));
+int imsmooth_generic PROTO((int, char **));
+int imsmooth_2d      PROTO((int, char **));
 int integrate        PROTO((int, char **));
 int interpolate      PROTO((int, char **));
@@ -208,4 +210,6 @@
   {1, "imhistogram",  imhist,           "histogram of an image region"},
   {1, "imsmooth",     imsmooth,         "circular gaussian smoothing"},
+  {1, "imsmooth.generic", imsmooth_generic, "circular non-gaussian smoothing"},
+  {1, "imsmooth.2d",  imsmooth_2d,      "circular non-gaussian smoothing"},
   {1, "imstats",      imstats,          "statistics on a portion of an image"},
   {1, "integrate",    integrate,        "integrate a vector"},
Index: trunk/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36189)
+++ trunk/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 36375)
@@ -23,4 +23,5 @@
 // vector types
 enum {COLTYPE_NONE, COLTYPE_FLT, COLTYPE_INT, COLTYPE_TIME, COLTYPE_CHAR};
+static int FITS_TRANSPOSE;
 
 int read_vectors (int argc, char **argv) {
@@ -34,4 +35,10 @@
 
   char *buffer = NULL;
+
+  FITS_TRANSPOSE = FALSE;
+  if ((Narg = get_argument (argc, argv, "-transpose"))) {
+    remove_argument (Narg, &argc, argv);
+    FITS_TRANSPOSE = TRUE;
+  }
 
   /* auto-sense table type */
@@ -410,60 +417,122 @@
     }
 	
-    // define the multifield vector names
-    ALLOCATE (vec, Vector *, Nval);
-    for (j = 0; j < Nval; j++) {
-      if (Nval == 1) 
-	sprintf (name, "%s", argv[i]);
-      else
-	sprintf (name, "%s:%d", argv[i], j);
-      if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
-      ResetVector (vec[j], vecType, Ny);
-    }
-
-    if (!strcmp (type, "char")) {
-      char *Ptr = data;
+    if (!FITS_TRANSPOSE) {
+      // define the multifield vector names (Nval vectors x Ny elements)
+      ALLOCATE (vec, Vector *, Nval);
+      for (j = 0; j < Nval; j++) {
+	if (Nval == 1) 
+	  sprintf (name, "%s", argv[i]);
+	else
+	  sprintf (name, "%s:%d", argv[i], j);
+	if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+	ResetVector (vec[j], vecType, Ny);
+      }
+
+      if (!strcmp (type, "char")) {
+	char *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "short")) {
+	short *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int")) {
+	int *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int64_t")) {
+	int64_t *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Int[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "float")) {
+	float *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Flt[j] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "double")) {
+	double *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[k][0].elements.Flt[j] = *Ptr;
+	  }
+	}
+      }
+    } else {
+      // define the multifield vector names (Ny vectors x Nval elements)
+      ALLOCATE (vec, Vector *, Ny);
       for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "short")) {
-      short *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "int")) {
-      int *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "int64_t")) {
-      int64_t *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Int[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "float")) {
-      float *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Flt[j] = *Ptr;
-	}
-      }
-    }
-    if (!strcmp (type, "double")) {
-      double *Ptr = data;
-      for (j = 0; j < Ny; j++) {
-	for (k = 0; k < Nval; k++, Ptr++) {
-	  vec[k][0].elements.Flt[j] = *Ptr;
+	if (Ny == 1) 
+	  sprintf (name, "%s", argv[i]);
+	else
+	  sprintf (name, "%s:%d", argv[i], j);
+	if ((vec[j] = SelectVector (name, ANYVECTOR, TRUE)) == NULL) ESCAPE ("bad vector name");
+	ResetVector (vec[j], vecType, Nval);
+      }
+
+      if (!strcmp (type, "char")) {
+	char *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "short")) {
+	short *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int")) {
+	int *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "int64_t")) {
+	int64_t *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Int[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "float")) {
+	float *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Flt[k] = *Ptr;
+	  }
+	}
+      }
+      if (!strcmp (type, "double")) {
+	double *Ptr = data;
+	for (j = 0; j < Ny; j++) {
+	  for (k = 0; k < Nval; k++, Ptr++) {
+	    vec[j][0].elements.Flt[k] = *Ptr;
+	  }
 	}
       }
Index: trunk/Ohana/src/opihi/dvo/Makefile
===================================================================
--- trunk/Ohana/src/opihi/dvo/Makefile	(revision 36189)
+++ trunk/Ohana/src/opihi/dvo/Makefile	(revision 36375)
@@ -40,4 +40,5 @@
 $(SRC)/cmpload.$(ARCH).o	  	\
 $(SRC)/cmpread.$(ARCH).o	  	\
+$(SRC)/coordimage.$(ARCH).o	  	\
 $(SRC)/ddmags.$(ARCH).o	  	\
 $(SRC)/detrend.$(ARCH).o	  	\
Index: trunk/Ohana/src/opihi/dvo/coordimage.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/coordimage.c	(revision 36375)
+++ trunk/Ohana/src/opihi/dvo/coordimage.c	(revision 36375)
@@ -0,0 +1,143 @@
+# include "dvoshell.h"
+
+int coordimage (int argc, char **argv) {
+
+  int N;
+  off_t i, j, Nimage, *subset, Nsubset;
+  time_t tzero;
+  double trange;
+  Buffer *bufX, *bufY;
+  SkyRegionSelection *selection;
+  Image *image;
+
+  if (!InitPhotcodes ()) return (FALSE);
+
+  // parse skyregion options
+  if ((selection = SetRegionSelection (&argc, argv)) == NULL) {
+    gprint (GP_ERR, "invalid sky region selection\n");
+    return FALSE;
+  }
+
+  int TimeSelect = FALSE;
+  if ((N = get_argument (argc, argv, "-time"))) {
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_time (argv[N], &tzero)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    if (!ohana_str_to_dtime (argv[N], &trange)) { 
+      gprint (GP_ERR, "syntax error\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+    TimeSelect = TRUE;
+    gprint (GP_ERR, "plotting in range %ds - %ds (%f seconds)\n", (int)tzero, (int)(tzero + trange), trange);
+  }
+
+  PhotCode *Photcode = NULL;
+  if ((N = get_argument (argc, argv, "-photcode"))) {
+    remove_argument (N, &argc, argv);
+    Photcode = GetPhotcodebyName (argv[N]);
+    if (Photcode == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+  if ((N = get_argument (argc, argv, "-Nphotcode"))) {
+    remove_argument (N, &argc, argv);
+    Photcode = GetPhotcodebyCode (atoi(argv[N]));
+    if (Photcode == NULL) {
+      gprint (GP_ERR, "photcode not found in photcode table\n");
+      return (FALSE);
+    }
+    remove_argument (N, &argc, argv);
+  }
+
+  char *Name = NULL;
+  if ((N = get_argument (argc, argv, "-name"))) {
+    remove_argument (N, &argc, argv);
+    Name = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int chipID = -1;
+  if ((N = get_argument (argc, argv, "-chip"))) {
+    remove_argument (N, &argc, argv);
+    chipID = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+  
+  if (argc != 5) goto syntax;
+
+  if ((bufX = SelectBuffer (argv[1], ANYBUFFER, TRUE)) == NULL) goto escape;
+  if ((bufY = SelectBuffer (argv[2], ANYBUFFER, TRUE)) == NULL) goto escape;
+  int Nx = atoi (argv[3]);
+  int Ny = atoi (argv[4]);
+
+  gfits_free_matrix (&bufX[0].matrix);
+  gfits_free_header (&bufX[0].header);
+  CreateBuffer (bufX, Nx, Ny, -32, 0.0, 1.0); // initialized to 0.0 here
+  strcpy (bufX[0].file, "(empty)");
+
+  gfits_free_matrix (&bufY[0].matrix);
+  gfits_free_header (&bufY[0].header);
+  CreateBuffer (bufY, Nx, Ny, -32, 0.0, 1.0); // initialized to 0.0 here
+  strcpy (bufY[0].file, "(empty)");
+
+  if ((image = LoadImagesDVO (&Nimage)) == NULL) return (FALSE);
+  image_subset (image, Nimage, &subset, &Nsubset, selection, tzero, trange, TimeSelect);
+
+  float Nsum = 0.0;
+  float *vX = (float *) bufX[0].matrix.buffer;
+  float *vY = (float *) bufY[0].matrix.buffer;
+
+  for (j = 0; j < Nsubset; j++) {
+    i = subset[j];
+    if (Name && (strstr (image[i].name, Name) == (char *) NULL)) continue;
+    if (Photcode) {
+      if (Photcode[0].type == PHOT_DEP) {
+	if (Photcode[0].code != image[i].photcode) continue;
+      } else {
+	if (Photcode[0].code != GetPhotcodeEquivCodebyCode (image[i].photcode)) continue;
+      }
+    }
+    if (chipID > -1) {
+      if (image[i].ccdnum != chipID) continue;
+    }
+
+    float dX = image[i].NX / (float) Nx;
+    float dY = image[i].NY / (float) Ny;
+
+    Coords coords = image[i].coords;
+    coords.Npolyterms = 0;
+
+    int ix, iy;
+    for (iy = 0; iy < Ny; iy++) {
+      double fy = iy * dY; // coordinate in full image
+      for (ix = 0; ix < Nx; ix++) {
+	double fx = ix * dX; // coordinate in full image
+
+	double Lo, Lm, dL;
+	double Mo, Mm, dM;
+
+	XY_to_LM (&Lo, &Mo, fx, fy, &coords);
+	XY_to_LM (&Lm, &Mm, fx, fy, &image[i].coords);
+	dL = Lo - Lm;
+	dM = Mo - Mm;
+
+	vX[ix + iy*Nx] += dL;
+	vY[ix + iy*Nx] += dM;
+      }
+    }
+    Nsum ++;
+  }
+  fprintf (stderr, "Nsum: %f\n", Nsum);
+  return TRUE;
+
+ syntax:
+  gprint (GP_ERR, "USAGE: coordimage buffX buffY Nx Ny\n");
+ escape:
+  return (FALSE);
+}
Index: trunk/Ohana/src/opihi/dvo/init.c
===================================================================
--- trunk/Ohana/src/opihi/dvo/init.c	(revision 36189)
+++ trunk/Ohana/src/opihi/dvo/init.c	(revision 36375)
@@ -13,4 +13,5 @@
 int cmpload         PROTO((int, char **));
 int cmpread         PROTO((int, char **));
+int coordimage      PROTO((int, char **));
 int ddmags          PROTO((int, char **));
 int detrend         PROTO((int, char **));
@@ -71,4 +72,5 @@
   {1, "cmpload",     cmpload,      "load cmp file into ?"},
   {1, "cmpread",     cmpread,      "read data from cmp format files"},
+  {1, "coordimage",  coordimage,   "generate a map of the transformation residuals"},
   {1, "ddmags",      ddmags,       "plot magnitude differences"},
   {1, "detrend",     detrend,      "extract from detrend database?"},
Index: trunk/Ohana/src/opihi/include/dvomath.h
===================================================================
--- trunk/Ohana/src/opihi/include/dvomath.h	(revision 36189)
+++ trunk/Ohana/src/opihi/include/dvomath.h	(revision 36375)
@@ -55,7 +55,35 @@
 } Buffer;
 
+typedef enum {
+  ST_NONE,
+  ST_LEFT,
+  ST_RIGHT,
+  ST_COMMA,
+  ST_TRINARY,
+  ST_OR,
+  ST_AND,
+  ST_LOGIC,
+  ST_BITWISE,
+  ST_ADD,
+  ST_TIMES,
+  ST_POWER,
+  ST_UNARY,
+  ST_BINARY,
+
+  ST_VALUE,
+  ST_SCALAR_INT,
+  ST_SCALAR_FLT,
+  ST_VECTOR,
+  ST_VECTOR_TMP,
+  ST_MATRIX,
+  ST_MATRIX_TMP,
+
+  ST_STRING,
+  ST_STRING_TMP,
+} StackVarType;
+
 typedef struct {			/* math stack structure */
   char   *name;
-  char    type;
+  StackVarType type;
   Buffer *buffer;
   Vector *vector;
@@ -77,5 +105,9 @@
 void          delete_stack	    PROTO((StackVar *stack, int Nstack));
 void          clear_stack 	    PROTO((StackVar *stack));
-void          assign_stack 	    PROTO((StackVar *stack, char *name, int type));
+void          assign_stack 	    PROTO((StackVar *stack, char *name, StackVarType type));
+
+int           SSS_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
+int           VVV_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
+int           MMM_trinary           PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op));
 
 int           VV_binary             PROTO((StackVar *OUT, StackVar *V1, StackVar *V2, char *op));
Index: trunk/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- trunk/Ohana/src/opihi/include/pcontrol.h	(revision 36189)
+++ trunk/Ohana/src/opihi/include/pcontrol.h	(revision 36375)
@@ -8,4 +8,5 @@
 /** job status values **/
 typedef enum {
+  PCONTROL_JOB_NONE, // XXX OK?
   PCONTROL_JOB_ALLJOBS,
   PCONTROL_JOB_PENDING,
@@ -309,4 +310,5 @@
 char  *GetJobStackName (int StackID);
 Stack *GetJobStackByName (char *name);
+int GetJobStackIDbyName (char *name);
 int    PutJob (Job *job, int StackID, int where);
 int    PutJobSetState (Job *job, int StackID, int where, int state);
Index: trunk/Ohana/src/opihi/lib.shell/check_stack.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/check_stack.c	(revision 36189)
+++ trunk/Ohana/src/opihi/lib.shell/check_stack.c	(revision 36375)
@@ -12,5 +12,5 @@
 
   for (i = 0; i < Nstack; i++) {
-    if (stack[i].type == 'X') {
+    if (stack[i].type == ST_VALUE) {
 
       /** if this is a number, put it on the list of scalars and move on.  assume value is
@@ -27,13 +27,13 @@
       stack[i].IntValue = strtol (stack[i].name, &c2, 0);
       if ((fabs(stack[i].FltValue) > MAX_INT) && (c1 == stack[i].name + strlen (stack[i].name))) {
-	stack[i].type  = 'S'; // 'S' == (float)
+	stack[i].type  = ST_SCALAR_FLT; // (float)
 	continue;
       } 
       if (c2 == stack[i].name + strlen (stack[i].name)) {
-	stack[i].type  = 's'; // 's' == (int)
+	stack[i].type  = ST_SCALAR_INT; // (int)
 	continue;
       } 
       if (c1 == stack[i].name + strlen (stack[i].name)) {
-	stack[i].type  = 'S'; // 'S' == (float)
+	stack[i].type  = ST_SCALAR_FLT; // (float)
 	continue;
       } 
@@ -42,5 +42,5 @@
       if (IsBuffer (stack[i].name)) {
 	stack[i].buffer = SelectBuffer (stack[i].name, OLDBUFFER, TRUE);
-	stack[i].type   = 'M';
+	stack[i].type   = ST_MATRIX;
 	if (Nx == -1) {
 	  Nx = stack[i].buffer[0].matrix.Naxis[0];
@@ -63,5 +63,5 @@
       if (IsVector (stack[i].name)) {
 	stack[i].vector = SelectVector (stack[i].name, OLDVECTOR, FALSE);
-	stack[i].type   = 'V';
+	stack[i].type   = ST_VECTOR;
 
 	if (Nv == -1) Nv = stack[i].vector[0].Nelements;
@@ -80,5 +80,5 @@
 
       /* this is not a scalar, vector, or matrix.  must be string */
-      stack[i].type  = 'W';
+      stack[i].type  = ST_STRING;
     }
   }
Index: trunk/Ohana/src/opihi/lib.shell/convert_to_RPN.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 36189)
+++ trunk/Ohana/src/opihi/lib.shell/convert_to_RPN.c	(revision 36375)
@@ -4,5 +4,5 @@
 StackVar *convert_to_RPN (int argc, char **argv, int *nstack) {
   
-  int type;
+ StackVarType type;
   int i, j, Nstack, Nop_stack, NSTACK;
   StackVar *stack, *op_stack;
@@ -21,78 +21,82 @@
     
     /* decide on priority of object */
-    type = 0;
+    type = ST_NONE;
+
+    /* trinary operations */
+    if (!strcmp (argv[i], "?"))      { type = ST_TRINARY; goto gotit; }
+    if (!strcmp (argv[i], ":"))      { type = ST_COMMA; goto gotit; }
+
     /* unary operations */
-    if (!strcmp (argv[i], "abs"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "int"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "exp"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ten"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "log"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ln"))     { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "sqrt"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "erf"))    { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "sinh"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "cosh"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "asinh"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "acosh"))  { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "sin"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "cos"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "tan"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dsin"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dcos"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dtan"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "asin"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "acos"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "atan"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dasin"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "dacos"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "datan"))  { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "lgamma")) { type = 9; goto gotit; }
-
-    if (!strcmp (argv[i], "rnd"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "xramp"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "yramp"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "ramp"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "zero"))   { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "--"))     { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "not"))    { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "isinf"))  { type = 9; goto gotit; }
-    if (!strcmp (argv[i], "isnan"))  { type = 9; goto gotit; }
+    if (!strcmp (argv[i], "abs"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "int"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "exp"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ten"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "log"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ln"))     { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sqrt"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "erf"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sinh"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "cosh"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "asinh"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "acosh"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "sin"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "cos"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "tan"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dsin"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dcos"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dtan"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "asin"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "acos"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "atan"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dasin"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "dacos"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "datan"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "lgamma")) { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "rnd"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "xramp"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "yramp"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "ramp"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "zero"))   { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "--"))     { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "not"))    { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "isinf"))  { type = ST_UNARY; goto gotit; }
+    if (!strcmp (argv[i], "isnan"))  { type = ST_UNARY; goto gotit; }
 
     /* binary operations */
-    if (!strcmp (argv[i], "^"))      { type = 8; goto gotit; }
-
-    if (!strcmp (argv[i], "@"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "/"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "*"))      { type = 7; goto gotit; }
-    if (!strcmp (argv[i], "%"))      { type = 7; goto gotit; }
-
-    if (!strcmp (argv[i], "+"))      { type = 6; goto gotit; }
-    if (!strcmp (argv[i], "-"))      { type = 6; goto gotit; }
+    if (!strcmp (argv[i], "^"))      { type = ST_POWER; goto gotit; }
+
+    if (!strcmp (argv[i], "atan2"))  { type = ST_BINARY; goto gotit; }
+    if (!strcmp (argv[i], ","))      { type = ST_COMMA; goto gotit; }
+
+    if (!strcmp (argv[i], "@"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "/"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "*"))      { type = ST_TIMES; goto gotit; }
+    if (!strcmp (argv[i], "%"))      { type = ST_TIMES; goto gotit; }
+
+    if (!strcmp (argv[i], "+"))      { type = ST_ADD; goto gotit; }
+    if (!strcmp (argv[i], "-"))      { type = ST_ADD; goto gotit; }
 	
-    if (!strcmp (argv[i], "&"))      { type = 5; goto gotit; }
-    if (!strcmp (argv[i], "|"))      { type = 5; goto gotit; }
-
-    if (!strcmp (argv[i], "<"))      { type = 4; goto gotit; }
-    if (!strcmp (argv[i], ">"))      { type = 4; goto gotit; }
-    if (!strcmp (argv[i], "=="))     { type = 4; strcpy (argv[i], "E"); goto gotit; }
-    if (!strcmp (argv[i], "!="))     { type = 4; strcpy (argv[i], "N"); goto gotit; }
-    if (!strcmp (argv[i], "<="))     { type = 4; strcpy (argv[i], "L"); goto gotit; }
-    if (!strcmp (argv[i], ">="))     { type = 4; strcpy (argv[i], "G"); goto gotit; }
-    if (!strcmp (argv[i], ">>"))     { type = 4; strcpy (argv[i], "U"); goto gotit; }
-    if (!strcmp (argv[i], "<<"))     { type = 4; strcpy (argv[i], "D"); goto gotit; }
-
-    if (!strcmp (argv[i], "&&"))     { type = 3; strcpy (argv[i], "A"); goto gotit; }
-    if (!strcmp (argv[i], "||"))     { type = 3; strcpy (argv[i], "O"); goto gotit; }
-
-    if (!strcmp (argv[i], "("))      { type = 2; goto gotit; }
-    if (!strcmp (argv[i], ")"))      { type = 1; goto gotit; }
+    if (!strcmp (argv[i], "&"))      { type = ST_BITWISE; goto gotit; }
+    if (!strcmp (argv[i], "|"))      { type = ST_BITWISE; goto gotit; }
+
+    if (!strcmp (argv[i], "<"))      { type = ST_LOGIC; goto gotit; }
+    if (!strcmp (argv[i], ">"))      { type = ST_LOGIC; goto gotit; }
+    if (!strcmp (argv[i], "=="))     { type = ST_LOGIC; strcpy (argv[i], "E"); goto gotit; }
+    if (!strcmp (argv[i], "!="))     { type = ST_LOGIC; strcpy (argv[i], "N"); goto gotit; }
+    if (!strcmp (argv[i], "<="))     { type = ST_LOGIC; strcpy (argv[i], "L"); goto gotit; }
+    if (!strcmp (argv[i], ">="))     { type = ST_LOGIC; strcpy (argv[i], "G"); goto gotit; }
+    if (!strcmp (argv[i], ">>"))     { type = ST_LOGIC; strcpy (argv[i], "U"); goto gotit; }
+    if (!strcmp (argv[i], "<<"))     { type = ST_LOGIC; strcpy (argv[i], "D"); goto gotit; }
+
+    if (!strcmp (argv[i], "&&"))     { type = ST_AND; strcpy (argv[i], "A"); goto gotit; }
+    if (!strcmp (argv[i], "||"))     { type = ST_OR ; strcpy (argv[i], "O"); goto gotit; }
+
+    if (!strcmp (argv[i], "("))      { type = ST_LEFT;  goto gotit; }
+    if (!strcmp (argv[i], ")"))      { type = ST_RIGHT; goto gotit; }
 
   gotit:
     /* choose how to deal with object */
     switch (type) {
-      case 8:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
+      case ST_POWER:  /* exponentiation: 2^2^3 = 64 != 256 (precedence is right-to-left, not left-to-right!) */
 	/* pop previous, higher operators from OP stack to stack */
 	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type > type); j--) {
@@ -105,10 +109,13 @@
 	Nop_stack ++;
 	break;
-      case 9: /* unary OPs */
-      case 7: /* binary OPs */
-      case 6:
-      case 5: 
-      case 4: 
-      case 3: 
+      case ST_UNARY: 
+      case ST_BINARY: 
+      case ST_TRINARY:
+      case ST_TIMES:
+      case ST_ADD:
+      case ST_BITWISE: 
+      case ST_LOGIC: 
+      case ST_AND: 
+      case ST_OR: 
 	/* pop previous, higher or equal operators from OP stack to stack */
 	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type >= type); j--) {
@@ -121,5 +128,5 @@
 	Nop_stack ++;
 	break;
-      case 2:  
+      case ST_LEFT:  
 	/* push operator on OP stack */
 	assign_stack (&op_stack[Nop_stack], argv[i], type);
@@ -127,12 +134,12 @@
 	Nop_stack ++;
 	break;
-      case 1: 
+      case ST_RIGHT: 
 	/* pop rest of operators from OP stack to stack, looking for '(' */
-	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != 2); j--) {
-	  move_stack (&stack[Nstack], &op_stack[j]);
-	  Nstack ++;
-	  Nop_stack --;
-	}
-	if ((j == -1) || (op_stack[j].type != 2)) {
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != ST_LEFT); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	if ((j == -1) || (op_stack[j].type != ST_LEFT)) {
 	  push_error ("syntax error: mismatched parenthesis");
 	  Nstack = 0;
@@ -144,16 +151,29 @@
 	Nop_stack --;
 	break;
-      case 0:
+      case ST_COMMA: 
+	/* pop rest of operators from OP stack to stack, looking for '(' (but do not pop the '(')*/
+	for (j = Nop_stack - 1; (j >= 0) && (op_stack[j].type != ST_LEFT) && (op_stack[j].type != ST_TRINARY); j--) {
+	  move_stack (&stack[Nstack], &op_stack[j]);
+	  Nstack ++;
+	  Nop_stack --;
+	}
+	break;
+      case ST_NONE:
 	/* place the value (number or vector/matrix name) on stack */
 	/* value of 'X' is used as sentinel until we sort out values */
-	assign_stack (&stack[Nstack], argv[i], 'X');
+	assign_stack (&stack[Nstack], argv[i], ST_VALUE);
 	Nstack ++;
 	break;
+
+      default:
+	push_error ("invalid stack typ");
+	Nstack = 0;
+	goto cleanup;
     }
   }
 
-  /* dump remaining operators on stack, checking for ')' */
+  /* dump remaining operators on stack, checking for '(' */
   for (j = Nop_stack - 1; j >= 0; j--) {
-    if (op_stack[j].type == 2) {
+    if (op_stack[j].type == ST_LEFT) {
       push_error ("syntax error: mismatched parenthesis");
       Nstack = 0;
Index: trunk/Ohana/src/opihi/lib.shell/dvomath.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/dvomath.c	(revision 36189)
+++ trunk/Ohana/src/opihi/lib.shell/dvomath.c	(revision 36375)
@@ -80,5 +80,5 @@
 	sprintf (outname, "%s", stack[0].name);
       } else {
-	if (stack[0].type == 's') {
+	if (stack[0].type == ST_SCALAR_INT) {
 	  sprintf (outname, "%d", stack[0].IntValue);
 	} else {
Index: trunk/Ohana/src/opihi/lib.shell/evaluate_stack.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 36189)
+++ trunk/Ohana/src/opihi/lib.shell/evaluate_stack.c	(revision 36375)
@@ -2,11 +2,51 @@
 # define VERBOSE 0
 
-# define TWO_OP(A,B,FUNC) \
-  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
-    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 
-
-# define ONE_OP(A,FUNC) \
-  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
-    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 
+// all three operands must have the same type
+# define THREE_OP(A,FUNC) 						\
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+  if ((stack[i - 3].type == A+1) && (stack[i - 2].type == A+1) && (stack[i - 1].type == A+1)) { \
+    status = FUNC (&tmp_stack, &stack[i - 3], &stack[i - 2], &stack[i - 1], stack[i].name); \
+    goto got_three_op; } \
+
+// A & B value types all have 2 possible values
+# define TWO_OP(A,B,FUNC) {						\
+    if ((stack[i - 2].type == A) && (stack[i - 1].type == B)) {		\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A+1) && (stack[i - 1].type == B)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A) && (stack[i - 1].type == B+1)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; }						\
+    if ((stack[i - 2].type == A+1) && (stack[i - 1].type == B+1)) {	\
+      status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); \
+      goto got_two_op; } \
+  }
+
+# define ONE_OP(A,FUNC)						\
+  if (stack[i - 1].type == A) {					\
+    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name);   \
+    goto got_one_op; }
 
 int evaluate_stack (StackVar *stack, int *Nstack) {
@@ -20,9 +60,9 @@
 
   if (*Nstack == 1) {
-    if ((stack[0].type == 'S') || (stack[0].type == 's')) {
+    if ((stack[0].type == ST_SCALAR_INT) || (stack[0].type == ST_SCALAR_FLT)) {
       clear_stack (&tmp_stack);
       return (TRUE);
     }
-    if (stack[0].type == 'V') {
+    if (stack[0].type == ST_VECTOR) {
       /* need to make a copy so we set output value? */
       V_unary (&tmp_stack, &stack[0], "=");
@@ -30,5 +70,5 @@
       return (TRUE);
     }
-    if (stack[0].type == 'M') {
+    if (stack[0].type == ST_MATRIX) {
       /* need to make a copy so we set output value? */
       M_unary (&tmp_stack, &stack[0], "=");
@@ -56,75 +96,148 @@
     }
 
-    /***** binary operators *****/
-    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {
-
-      if (i < 2) {  /* need two variables to operate on */
-	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+    /***** trinary operators *****/
+    switch (stack[i].type) {
+      case ST_TRINARY:
+
+	if (i < 3) {  /* need two variables to operate on */
+	  snprintf (line, 512, "syntax error: trinary operator without three operands: %s\n", stack[i].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	status = FALSE;
+	THREE_OP (ST_MATRIX,MMM_trinary);
+	THREE_OP (ST_VECTOR,VVV_trinary);
+
+	THREE_OP (ST_SCALAR_FLT,SSS_trinary);
+	THREE_OP (ST_SCALAR_INT,SSS_trinary);
+
+	/* there are no valid unary string operators */
+	push_error ("invalid operands for trinary operator (mismatch types?)");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+
+      got_three_op:
+	if (!status) {
+	  snprintf (line, 512, "syntax error: invalid operand for binary operation: %s or %s or %s\n", stack[i-1].name, stack[i-2].name, stack[i-3].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+	move_stack (&stack[i-3], &tmp_stack);
+	delete_stack (&stack[i-2], 3);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j-3], &stack[j]);
+	}
+	*Nstack -= 3;
+	i -= 3;
+	init_stack (&tmp_stack);
+	continue;
+
+	/***** binary operators *****/
+      case ST_OR:
+      case ST_AND:
+      case ST_LOGIC:
+      case ST_BITWISE:
+      case ST_ADD:
+      case ST_TIMES:
+      case ST_POWER:
+      case ST_BINARY:
+
+	if (i < 2) {  /* need two variables to operate on */
+	  snprintf (line, 512, "syntax error: binary operator with one operand: %s\n", stack[i].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	status = FALSE;
+	TWO_OP (ST_MATRIX,ST_MATRIX,MM_binary);
+	TWO_OP (ST_MATRIX,ST_VECTOR,MV_binary);
+	TWO_OP (ST_MATRIX,ST_SCALAR_INT,MS_binary);
+
+	TWO_OP (ST_VECTOR,ST_MATRIX,VM_binary);
+	TWO_OP (ST_VECTOR,ST_VECTOR,VV_binary);
+	TWO_OP (ST_VECTOR,ST_SCALAR_INT,VS_binary);
+
+	TWO_OP (ST_SCALAR_INT,ST_MATRIX,SM_binary);
+	TWO_OP (ST_SCALAR_INT,ST_VECTOR,SV_binary);
+	TWO_OP (ST_SCALAR_INT,ST_SCALAR_INT,SS_binary);      
+
+	TWO_OP (ST_SCALAR_FLT,ST_MATRIX,SM_binary);
+	TWO_OP (ST_SCALAR_FLT,ST_VECTOR,SV_binary);
+	TWO_OP (ST_SCALAR_FLT,ST_SCALAR_INT,SS_binary);      
+
+	TWO_OP (ST_STRING,ST_STRING,WW_binary);      
+	TWO_OP (ST_STRING,ST_SCALAR_INT,WW_binary);      
+	TWO_OP (ST_SCALAR_INT,ST_STRING,WW_binary);      
+      
+      got_two_op:
+	if (!status) {
+	  snprintf (line, 512, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
+	  push_error (line);
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+	move_stack (&stack[i-2], &tmp_stack);
+	delete_stack (&stack[i-1], 2);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j - 2], &stack[j]);
+	}
+	*Nstack -= 2;
+	i -= 2;
+	init_stack (&tmp_stack);
+	continue;
+
+	/***** unary operators **/
+      case ST_UNARY:
+
+	if (i < 1) {  /* need one variable to operate on */
+	  push_error ("syntax error: unary operator with no operand");
+	  clear_stack (&tmp_stack);
+	  return (FALSE);
+	}
+
+	ONE_OP (ST_MATRIX, M_unary);
+	ONE_OP (ST_MATRIX_TMP, M_unary);
+
+	ONE_OP (ST_VECTOR, V_unary);
+	ONE_OP (ST_VECTOR_TMP, V_unary);
+
+	ONE_OP (ST_SCALAR_INT, S_unary);
+	ONE_OP (ST_SCALAR_FLT, S_unary);
+
+	/* there are no valid unary string operators */
+	push_error ("syntax error: no valid string unary ops");
+	clear_stack (&tmp_stack);
+	return (FALSE);
+
+      got_one_op:
+	move_stack (&stack[i-1], &tmp_stack);
+	delete_stack (&stack[i], 1);
+	for (j = i + 1; j < *Nstack; j++) {
+	  move_stack (&stack[j - 1], &stack[j]);
+	}
+	init_stack (&tmp_stack);
+	*Nstack -= 1;
+	i -= 1;
+	continue;
+
+      case ST_SCALAR_INT:
+      case ST_SCALAR_FLT:
+      case ST_VECTOR:
+      case ST_VECTOR_TMP:
+      case ST_MATRIX:
+      case ST_MATRIX_TMP:
+      case ST_STRING:
+	continue;
+
+      default:
+	snprintf (line, 512, "syntax error: unexpected operator type %s", stack[i].name);
 	push_error (line);
 	clear_stack (&tmp_stack);
 	return (FALSE);
-      }
-
-      status = FALSE;
-      TWO_OP ("M","M",MM_binary);
-      TWO_OP ("M","V",MV_binary);
-      TWO_OP ("M","S",MS_binary);
-      TWO_OP ("V","M",VM_binary);
-      TWO_OP ("V","V",VV_binary);
-      TWO_OP ("V","S",VS_binary);
-      TWO_OP ("S","M",SM_binary);
-      TWO_OP ("S","V",SV_binary);
-      TWO_OP ("S","S",SS_binary);      
-      TWO_OP ("W","W",WW_binary);      
-      TWO_OP ("W","S",WW_binary);      
-      TWO_OP ("S","W",WW_binary);      
-      
-      if (!status) {
-	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
-	push_error (line);
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-      move_stack (&stack[i-2], &tmp_stack);
-      delete_stack (&stack[i-1], 2);
-      for (j = i + 1; j < *Nstack; j++) {
-	move_stack (&stack[j - 2], &stack[j]);
-      }
-      *Nstack -= 2;
-      i -= 2;
-      init_stack (&tmp_stack);
-      continue;
-    }
-
-    /***** unary operators **/
-    if (stack[i].type == 9) {
-
-      if (i < 1) {  /* need one variable to operate on */
-	push_error ("syntax error: unary operator with no operand");
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-
-      ONE_OP ("M", M_unary);
-      ONE_OP ("V", V_unary);
-      ONE_OP ("S", S_unary);
-
-      /* there are no valid unary string operators */
-      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
-	push_error ("syntax error: no valid string unary ops");
-	clear_stack (&tmp_stack);
-	return (FALSE);
-      }
-
-      move_stack (&stack[i-1], &tmp_stack);
-      delete_stack (&stack[i], 1);
-      for (j = i + 1; j < *Nstack; j++) {
-	move_stack (&stack[j - 1], &stack[j]);
-      }
-      init_stack (&tmp_stack);
-      *Nstack -= 1;
-      i -= 1;
-      continue;
-    } 
+    }
   }
   clear_stack (&tmp_stack);
@@ -160,5 +273,5 @@
 
   for (i = 0; i < Nstack; i++) {
-    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
+    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == ST_MATRIX_TMP)) {
       if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
       free (stack[i].buffer[0].header.buffer);
@@ -167,5 +280,5 @@
       stack[i].buffer = NULL;
     }	
-    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
+    if (IsVectorPtr (stack[i].vector) && (stack[i].type == ST_VECTOR_TMP)) {
       if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
       free (stack[i].vector[0].elements.Ptr);
@@ -193,5 +306,5 @@
 }
 
-void assign_stack (StackVar *stack, char *name, int type) {
+void assign_stack (StackVar *stack, char *name, StackVarType type) {
   stack->name = strcreate (name);
   stack->type = type;
Index: trunk/Ohana/src/opihi/lib.shell/stack_math.c
===================================================================
--- trunk/Ohana/src/opihi/lib.shell/stack_math.c	(revision 36189)
+++ trunk/Ohana/src/opihi/lib.shell/stack_math.c	(revision 36375)
@@ -6,9 +6,46 @@
 */
 
-// XXX we temporarily drop the concept of using one of the temporary input vectors for 
-// the output vector (thus saving an ALLOC): we have to juggle the size of the input vectors 
-// as well as their temporary state
-
-int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+int SSS_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
+
+  char line[512]; // this is only used to report an error 
+  
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define SSS_FUNC(OP) {						\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_FLT) && (V3->type == ST_SCALAR_FLT)) { \
+      opihi_flt M1  =  V1[0].FltValue;			\
+      opihi_flt M2  =  V2[0].FltValue;			\
+      opihi_flt M3  =  V3[0].FltValue;			\
+      OUT[0].type = ST_SCALAR_FLT;			\
+      OUT[0].FltValue = OP;							\
+      break;								\
+    }									\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT) && (V3->type == ST_SCALAR_INT)) { \
+      opihi_int M1  =  V1[0].IntValue;			\
+      opihi_int M2  =  V2[0].IntValue;			\
+      opihi_int M3  =  V3[0].IntValue;			\
+      OUT[0].type = ST_SCALAR_INT;			\
+      OUT[0].IntValue = OP;							\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) {
+    case '?': SSS_FUNC(M1 ? M2: M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef SSS_FUNC
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+int VVV_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
 
   int i, Nx;
@@ -19,4 +56,7 @@
     return (FALSE);
   }
+  if (V1[0].vector[0].Nelements != V3[0].vector[0].Nelements) {
+    return (FALSE);
+  }
 
   Nx = V1[0].vector[0].Nelements;
@@ -24,5 +64,154 @@
   // create the output vector guaranteed to be temporary until the very end
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';
+  OUT[0].type = ST_VECTOR_TMP;
+
+  // set up the possible operations : int OP int -> int, all else yield float
+  // OP is the operation performed on *M1 and *M2
+# define VVV_FUNC(OP) {						\
+    if ((V1->vector->type == OPIHI_FLT) && (V2->vector->type == OPIHI_FLT) && (V3->vector->type == OPIHI_FLT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
+      opihi_flt *M2  =  V2[0].vector[0].elements.Flt;			\
+      opihi_flt *M3  =  V3[0].vector[0].elements.Flt;			\
+      opihi_flt *out = OUT[0].vector[0].elements.Flt;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++, M3++) {		\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+    if ((V1->vector->type == OPIHI_INT) && (V2->vector->type == OPIHI_INT) && (V3->vector->type == OPIHI_INT)) { \
+      CopyVector (OUT[0].vector, V1[0].vector);				\
+      opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
+      opihi_int *M2  =  V2[0].vector[0].elements.Int;			\
+      opihi_int *M3  =  V3[0].vector[0].elements.Int;			\
+      opihi_int *out = OUT[0].vector[0].elements.Int;			\
+      for (i = 0; i < Nx; i++, out++, M1++, M2++, M3++) {		\
+  	*out = OP;							\
+      }									\
+      break;								\
+    }									\
+  }
+
+  switch (op[0]) {
+    case '?': VVV_FUNC(*M1 ? *M2: *M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef VVV_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == ST_VECTOR_TMP) {
+    free (V1[0].vector[0].elements.Ptr);
+    free (V1[0].vector);
+  }
+  if (V2[0].type == ST_VECTOR_TMP) {
+    free (V2[0].vector[0].elements.Ptr);
+    free (V2[0].vector);
+  }
+  if (V3[0].type == ST_VECTOR_TMP) {
+    free (V3[0].vector[0].elements.Ptr);
+    free (V3[0].vector);
+  }
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+int MMM_trinary (StackVar *OUT, StackVar *V1, StackVar *V2, StackVar *V3, char *op) {
+
+  int i, Nx, Ny;
+  float *out, *M1, *M2, *M3;
+  char line[512]; // this is only used to report an error 
+  
+  Nx = V1[0].buffer[0].matrix.Naxis[0];
+  Ny = V1[0].buffer[0].matrix.Naxis[1];
+
+  if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
+    OUT[0].buffer = V1[0].buffer;
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
+  } else {
+    if (V2[0].type == ST_MATRIX_TMP) { /** use V2 as temp buffer, but header of V1 **/
+      OUT[0].buffer = V2[0].buffer;
+      V2[0].type = ST_MATRIX; /* prevent it from being freed below */
+    } else {  /* no spare temp buffer */
+      OUT[0].buffer = InitBuffer ();
+      CopyBuffer (OUT[0].buffer, V1[0].buffer);
+    }
+  }
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
+
+  M1  = (float *)V1[0].buffer[0].matrix.buffer;
+  M2  = (float *)V2[0].buffer[0].matrix.buffer;
+  M3  = (float *)V3[0].buffer[0].matrix.buffer;
+  out = (float *)OUT[0].buffer[0].matrix.buffer;
+
+# define MMM_FUNC(OP)					\
+  for (i = 0; i < Nx*Ny; i++, out++, M1++, M2++, M3++) {	\
+    *out = OP;						\
+  }							\
+  break; 
+
+  switch (op[0]) {
+    case '?': MMM_FUNC(*M1 ? *M2: *M3);
+    default:
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
+      push_error (line);
+      return (FALSE);
+  }
+# undef MMM_FUNC
+
+  /** free up any temporary buffers: **/
+
+  if (V1[0].type == ST_MATRIX_TMP) {
+    free (V1[0].buffer[0].header.buffer);
+    free (V1[0].buffer[0].matrix.buffer);
+    free (V1[0].buffer);
+  }
+  if (V2[0].type == ST_MATRIX_TMP) {
+    free (V2[0].buffer[0].header.buffer);
+    free (V2[0].buffer[0].matrix.buffer);
+    free (V2[0].buffer);
+  }
+  if (V3[0].type == ST_MATRIX_TMP) {
+    free (V3[0].buffer[0].header.buffer);
+    free (V3[0].buffer[0].matrix.buffer);
+    free (V3[0].buffer);
+  }
+
+  /* at the end, V1 and V2 are deleted only if they were temporary */
+
+  clear_stack (V1);
+  clear_stack (V2);
+  clear_stack (V3);
+  return (TRUE);
+
+}
+
+// XXX we temporarily drop the concept of using one of the temporary input vectors for 
+// the output vector (thus saving an ALLOC): we have to juggle the size of the input vectors 
+// as well as their temporary state
+
+int VV_binary (StackVar *OUT, StackVar *V1, StackVar *V2, char *op) {
+
+  int i, Nx;
+  char line[512]; // this is only used to report an error 
+  
+  // the vectors have to match in length
+  if (V1[0].vector[0].Nelements != V2[0].vector[0].Nelements) {
+    return (FALSE);
+  }
+
+  Nx = V1[0].vector[0].Nelements;
+
+  // create the output vector guaranteed to be temporary until the very end
+  OUT[0].vector = InitVector ();
+  OUT[0].type = ST_VECTOR_TMP;
 
   // set up the possible operations : int OP int -> int, all else yield float
@@ -59,5 +248,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->vector->type != OPIHI_FLT) && (V2->vector->type != OPIHI_FLT)) { \
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -82,25 +271,26 @@
 
   switch (op[0]) {
-    case '+': VV_FUNC('s', *M1 + *M2);
-    case '-': VV_FUNC('s', *M1 - *M2);
-    case '*': VV_FUNC('s', *M1 * *M2);
-    case '/': VV_FUNC('S', *M1 / (opihi_flt) *M2);
-    case '%': VV_FUNC('s', (long long)*M1 % (long long)*M2);
-    case '^': VV_FUNC('S', pow (*M1, *M2));
-    case '@': VV_FUNC('S', DEG_RAD*atan2 (*M1, *M2));
-    case 'D': VV_FUNC('s', MIN (*M1, *M2));
-    case 'U': VV_FUNC('s', MAX (*M1, *M2));
-    case '<': VV_FUNC('s', (*M1 < *M2) ? 1 : 0);
-    case '>': VV_FUNC('s', (*M1 > *M2) ? 1 : 0);
-    case '&': VV_FUNC('s', ((long long)*M1 & (long long)*M2));
-    case '|': VV_FUNC('s', ((long long)*M1 | (long long)*M2));
-    case 'E': VV_FUNC('s', (*M1 == *M2) ? 1 : 0);
-    case 'N': VV_FUNC('s', (*M1 != *M2) ? 1 : 0);
-    case 'L': VV_FUNC('s', (*M1 <= *M2) ? 1 : 0);
-    case 'G': VV_FUNC('s', (*M1 >= *M2) ? 1 : 0);
-    case 'A': VV_FUNC('s', (*M1 && *M2) ? 1 : 0);
-    case 'O': VV_FUNC('s', (*M1 || *M2) ? 1 : 0);
+    case '+': VV_FUNC(ST_SCALAR_INT, *M1 + *M2);
+    case '-': VV_FUNC(ST_SCALAR_INT, *M1 - *M2);
+    case '*': VV_FUNC(ST_SCALAR_INT, *M1 * *M2);
+    case '/': VV_FUNC(ST_SCALAR_FLT, *M1 / (opihi_flt) *M2);
+    case '%': VV_FUNC(ST_SCALAR_INT, (long long)*M1 % (long long)*M2);
+    case '^': VV_FUNC(ST_SCALAR_FLT, pow (*M1, *M2));
+    case '@': VV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, *M2));
+    case 'a': VV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, *M2));
+    case 'D': VV_FUNC(ST_SCALAR_INT, MIN (*M1, *M2));
+    case 'U': VV_FUNC(ST_SCALAR_INT, MAX (*M1, *M2));
+    case '<': VV_FUNC(ST_SCALAR_INT, (*M1 < *M2) ? 1 : 0);
+    case '>': VV_FUNC(ST_SCALAR_INT, (*M1 > *M2) ? 1 : 0);
+    case '&': VV_FUNC(ST_SCALAR_INT, ((long long)*M1 & (long long)*M2));
+    case '|': VV_FUNC(ST_SCALAR_INT, ((long long)*M1 | (long long)*M2));
+    case 'E': VV_FUNC(ST_SCALAR_INT, (*M1 == *M2) ? 1 : 0);
+    case 'N': VV_FUNC(ST_SCALAR_INT, (*M1 != *M2) ? 1 : 0);
+    case 'L': VV_FUNC(ST_SCALAR_INT, (*M1 <= *M2) ? 1 : 0);
+    case 'G': VV_FUNC(ST_SCALAR_INT, (*M1 >= *M2) ? 1 : 0);
+    case 'A': VV_FUNC(ST_SCALAR_INT, (*M1 && *M2) ? 1 : 0);
+    case 'O': VV_FUNC(ST_SCALAR_INT, (*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -110,9 +300,9 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
   }
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -134,10 +324,10 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP;   /*** <<--- says this is a temporary matrix ***/
 
   // set up the possible operations : int OP int -> int, all else yield float
   // OP is the operation performed on *M1 and *M2
 # define SV_FUNC(FTYPE,OP) {						\
-    if ((V1->type == 'S') && (V2->vector->type == OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_flt  M1  =  V1[0].FltValue;					\
@@ -149,5 +339,5 @@
       break;								\
     }									\
-    if ((V1->type == 'S') && (V2->vector->type != OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->vector->type != OPIHI_FLT)) {		\
       MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
       opihi_flt  M1  =  V1[0].FltValue;					\
@@ -159,5 +349,5 @@
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->vector->type == OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_INT) && (V2->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -169,5 +359,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->type == 's') && (V2->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT) && (V2->vector->type != OPIHI_FLT)) { \
       MatchVector (OUT[0].vector, V2[0].vector, OPIHI_FLT);		\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -179,5 +369,5 @@
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->vector->type != OPIHI_FLT)) {		\
+    if ((V1->type == ST_SCALAR_INT) && (V2->vector->type != OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V2[0].vector);				\
       opihi_int  M1  =  V1[0].IntValue;					\
@@ -192,25 +382,26 @@
 
   switch (op[0]) { 
-    case '+': SV_FUNC('s', M1 + *M2);
-    case '-': SV_FUNC('s', M1 - *M2);
-    case '*': SV_FUNC('s', M1 * *M2);
-    case '/': SV_FUNC('S', M1 / (opihi_flt) *M2);
-    case '%': SV_FUNC('s', (long long) M1 % (long long) *M2);
-    case '^': SV_FUNC('S', pow (M1, *M2));
-    case '@': SV_FUNC('S', DEG_RAD*atan2 (M1, *M2));
-    case 'D': SV_FUNC('s', MIN (M1, *M2));
-    case 'U': SV_FUNC('s', MAX (M1, *M2));
-    case '<': SV_FUNC('s', (M1 < *M2) ? 1 : 0);
-    case '>': SV_FUNC('s', (M1 > *M2) ? 1 : 0);
-    case '&': SV_FUNC('s', ((long long)M1 & (long long)*M2));
-    case '|': SV_FUNC('s', ((long long)M1 | (long long)*M2));
-    case 'E': SV_FUNC('s', (M1 == *M2) ? 1 : 0);
-    case 'N': SV_FUNC('s', (M1 != *M2) ? 1 : 0);
-    case 'L': SV_FUNC('s', (M1 <= *M2) ? 1 : 0);
-    case 'G': SV_FUNC('s', (M1 >= *M2) ? 1 : 0);
-    case 'A': SV_FUNC('s', (M1 && *M2) ? 1 : 0);
-    case 'O': SV_FUNC('s', (M1 || *M2) ? 1 : 0);
+    case '+': SV_FUNC(ST_SCALAR_INT, M1 + *M2);
+    case '-': SV_FUNC(ST_SCALAR_INT, M1 - *M2);
+    case '*': SV_FUNC(ST_SCALAR_INT, M1 * *M2);
+    case '/': SV_FUNC(ST_SCALAR_FLT, M1 / (opihi_flt) *M2);
+    case '%': SV_FUNC(ST_SCALAR_INT, (long long) M1 % (long long) *M2);
+    case '^': SV_FUNC(ST_SCALAR_FLT, pow (M1, *M2));
+    case '@': SV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, *M2));
+    case 'a': SV_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, *M2));
+    case 'D': SV_FUNC(ST_SCALAR_INT, MIN (M1, *M2));
+    case 'U': SV_FUNC(ST_SCALAR_INT, MAX (M1, *M2));
+    case '<': SV_FUNC(ST_SCALAR_INT, (M1 < *M2) ? 1 : 0);
+    case '>': SV_FUNC(ST_SCALAR_INT, (M1 > *M2) ? 1 : 0);
+    case '&': SV_FUNC(ST_SCALAR_INT, ((long long)M1 & (long long)*M2));
+    case '|': SV_FUNC(ST_SCALAR_INT, ((long long)M1 | (long long)*M2));
+    case 'E': SV_FUNC(ST_SCALAR_INT, (M1 == *M2) ? 1 : 0);
+    case 'N': SV_FUNC(ST_SCALAR_INT, (M1 != *M2) ? 1 : 0);
+    case 'L': SV_FUNC(ST_SCALAR_INT, (M1 <= *M2) ? 1 : 0);
+    case 'G': SV_FUNC(ST_SCALAR_INT, (M1 >= *M2) ? 1 : 0);
+    case 'A': SV_FUNC(ST_SCALAR_INT, (M1 && *M2) ? 1 : 0);
+    case 'O': SV_FUNC(ST_SCALAR_INT, (M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -219,5 +410,5 @@
 
   /** free up any temporary buffers: **/
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -240,10 +431,10 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v';   /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP;   /*** <<--- says this is a temporary matrix ***/
 
   // set up the possible operations : int OP int -> int, all else yield float
   // OP is the operation performed on *M1 and *M2
 # define VS_FUNC(FTYPE,OP) {						\
-    if ((V2->type == 'S') && (V1->vector->type == OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_FLT) && (V1->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
@@ -255,5 +446,5 @@
       break;								\
     }									\
-    if ((V2->type == 'S') && (V1->vector->type != OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_FLT) && (V1->vector->type != OPIHI_FLT)) {		\
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);		\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -265,5 +456,5 @@
       break;								\
     }									\
-    if ((V2->type == 's') && (V1->vector->type == OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_INT) && (V1->vector->type == OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_flt *M1  =  V1[0].vector[0].elements.Flt;			\
@@ -275,5 +466,5 @@
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V2->type == 's') && (V1->vector->type != OPIHI_FLT)) { \
+    if ((FTYPE == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_INT) && (V1->vector->type != OPIHI_FLT)) { \
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -285,5 +476,5 @@
       break;								\
     }									\
-    if ((V2->type == 's') && (V1->vector->type != OPIHI_FLT)) {		\
+    if ((V2->type == ST_SCALAR_INT) && (V1->vector->type != OPIHI_FLT)) {		\
       CopyVector (OUT[0].vector, V1[0].vector);				\
       opihi_int *M1  =  V1[0].vector[0].elements.Int;			\
@@ -298,25 +489,26 @@
 
   switch (op[0]) { 
-    case '+': VS_FUNC('s', *M1 + M2);
-    case '-': VS_FUNC('s', *M1 - M2);
-    case '*': VS_FUNC('s', *M1 * M2);
-    case '/': VS_FUNC('S', *M1 / (opihi_flt) M2);
-    case '%': VS_FUNC('s', (long long) *M1 % (long long) M2);
-    case '^': VS_FUNC('S', pow (*M1, M2));
-    case '@': VS_FUNC('S', DEG_RAD*atan2 (*M1, M2));
-    case 'D': VS_FUNC('s', MIN (*M1, M2));
-    case 'U': VS_FUNC('s', MAX (*M1, M2));
-    case '<': VS_FUNC('s', (*M1 < M2) ? 1 : 0);
-    case '>': VS_FUNC('s', (*M1 > M2) ? 1 : 0);
-    case '&': VS_FUNC('s', ((long long)*M1 & (long long)M2));
-    case '|': VS_FUNC('s', ((long long)*M1 | (long long)M2));
-    case 'E': VS_FUNC('s', (*M1 == M2) ? 1 : 0);
-    case 'N': VS_FUNC('s', (*M1 != M2) ? 1 : 0);
-    case 'L': VS_FUNC('s', (*M1 <= M2) ? 1 : 0);
-    case 'G': VS_FUNC('s', (*M1 >= M2) ? 1 : 0);
-    case 'A': VS_FUNC('s', (*M1 && M2) ? 1 : 0);
-    case 'O': VS_FUNC('s', (*M1 || M2) ? 1 : 0);
+    case '+': VS_FUNC(ST_SCALAR_INT, *M1 + M2);
+    case '-': VS_FUNC(ST_SCALAR_INT, *M1 - M2);
+    case '*': VS_FUNC(ST_SCALAR_INT, *M1 * M2);
+    case '/': VS_FUNC(ST_SCALAR_FLT, *M1 / (opihi_flt) M2);
+    case '%': VS_FUNC(ST_SCALAR_INT, (long long) *M1 % (long long) M2);
+    case '^': VS_FUNC(ST_SCALAR_FLT, pow (*M1, M2));
+    case '@': VS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, M2));
+    case 'a': VS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (*M1, M2));
+    case 'D': VS_FUNC(ST_SCALAR_INT, MIN (*M1, M2));
+    case 'U': VS_FUNC(ST_SCALAR_INT, MAX (*M1, M2));
+    case '<': VS_FUNC(ST_SCALAR_INT, (*M1 < M2) ? 1 : 0);
+    case '>': VS_FUNC(ST_SCALAR_INT, (*M1 > M2) ? 1 : 0);
+    case '&': VS_FUNC(ST_SCALAR_INT, ((long long)*M1 & (long long)M2));
+    case '|': VS_FUNC(ST_SCALAR_INT, ((long long)*M1 | (long long)M2));
+    case 'E': VS_FUNC(ST_SCALAR_INT, (*M1 == M2) ? 1 : 0);
+    case 'N': VS_FUNC(ST_SCALAR_INT, (*M1 != M2) ? 1 : 0);
+    case 'L': VS_FUNC(ST_SCALAR_INT, (*M1 <= M2) ? 1 : 0);
+    case 'G': VS_FUNC(ST_SCALAR_INT, (*M1 >= M2) ? 1 : 0);
+    case 'A': VS_FUNC(ST_SCALAR_INT, (*M1 && M2) ? 1 : 0);
+    case 'O': VS_FUNC(ST_SCALAR_INT, (*M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -326,5 +518,5 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
@@ -353,7 +545,7 @@
 
   /* if possible, use V1 as temp buffer, otherwise create new one */
-  if (V1[0].type == 'm') {  
+  if (V1[0].type == ST_MATRIX_TMP) {  
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {  
     /* do buffer.matrix.buffer and buffer.header.buffer get correctly zeroed? */
@@ -361,5 +553,5 @@
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float     *M1  = (float *) V1[0].buffer[0].matrix.buffer;
@@ -395,4 +587,5 @@
     case '^': MV_FUNC(pow (*M1, *M2));
     case '@': MV_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': MV_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': MV_FUNC(MIN (*M1, *M2));
     case 'U': MV_FUNC(MAX (*M1, *M2));
@@ -408,5 +601,5 @@
     case 'O': MV_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -416,10 +609,10 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
     free (V1[0].buffer);
   }
-  if (V2[0].type == 'v') {
+  if (V2[0].type == ST_VECTOR_TMP) {
     free (V2[0].vector[0].elements.Ptr);
     free (V2[0].vector);
@@ -446,12 +639,12 @@
 
   /* if possible, use V2 as temp buffer, otherwise create new one */
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V2[0].buffer;
-    V2[0].type = 'M'; /* prevent it from being freed below */
+    V2[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {  /* no spare temp buffer */
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V2[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float     *M2  = (float *) V2[0].buffer[0].matrix.buffer;
@@ -487,4 +680,5 @@
     case '^': VM_FUNC(pow (*M1, *M2));
     case '@': VM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': VM_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': VM_FUNC(MIN (*M1, *M2));
     case 'U': VM_FUNC(MAX (*M1, *M2));
@@ -500,5 +694,5 @@
     case 'O': VM_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -508,9 +702,9 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
   }
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -535,11 +729,11 @@
   Ny = V1[0].buffer[0].matrix.Naxis[1];
 
-  if (V1[0].type == 'm') {  /** use V1 as temp buffer **/
+  if (V1[0].type == ST_MATRIX_TMP) {  /** use V1 as temp buffer **/
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
-    if (V2[0].type == 'm') { /** use V2 as temp buffer, but header of V1 **/
+    if (V2[0].type == ST_MATRIX_TMP) { /** use V2 as temp buffer, but header of V1 **/
       OUT[0].buffer = V2[0].buffer;
-      V2[0].type = 'M'; /* prevent it from being freed below */
+      V2[0].type = ST_MATRIX; /* prevent it from being freed below */
     } else {  /* no spare temp buffer */
       OUT[0].buffer = InitBuffer ();
@@ -547,5 +741,5 @@
     }
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   M1  = (float *)V1[0].buffer[0].matrix.buffer;
@@ -567,4 +761,5 @@
     case '^': MM_FUNC(pow (*M1, *M2));
     case '@': MM_FUNC(DEG_RAD*atan2 (*M1, *M2));
+    case 'a': MM_FUNC(DEG_RAD*atan2 (*M1, *M2));
     case 'D': MM_FUNC(MIN (*M1, *M2));
     case 'U': MM_FUNC(MAX (*M1, *M2));
@@ -580,5 +775,5 @@
     case 'O': MM_FUNC((*M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -588,10 +783,10 @@
   /** free up any temporary buffers: **/
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
     free (V1[0].buffer);
   }
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -615,12 +810,12 @@
 
   /* if possible, use V1 as temp buffer, otherwise create new one */
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP;      /*** <<--- says this is a temporary matrix ***/
 
   float *M1    = (float *)V1[0].buffer[0].matrix.buffer;
@@ -628,5 +823,5 @@
 
 # define MS_FUNC(OP) {					\
-    if (V2->type == 'S')  {				\
+    if (V2->type == ST_SCALAR_FLT)  {				\
       opihi_flt M2 = V2[0].FltValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
@@ -635,5 +830,5 @@
       break;						\
     }							\
-    if (V2->type == 's')  {				\
+    if (V2->type == ST_SCALAR_INT)  {				\
       opihi_int M2 = V2[0].IntValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M1++) {	\
@@ -652,4 +847,5 @@
     case '^': MS_FUNC(pow (*M1, M2));
     case '@': MS_FUNC(DEG_RAD*atan2 (*M1, M2));
+    case 'a': MS_FUNC(DEG_RAD*atan2 (*M1, M2));
     case 'D': MS_FUNC(MIN (*M1, M2));
     case 'U': MS_FUNC(MAX (*M1, M2));
@@ -665,5 +861,5 @@
     case 'O': MS_FUNC((*M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -671,5 +867,5 @@
 # undef MS_FUNC
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
@@ -691,12 +887,12 @@
   Ny = V2[0].buffer[0].matrix.Naxis[1];
 
-  if (V2[0].type == 'm') {  /* V2[0] is NOT temporary, we can't use it for storage */
+  if (V2[0].type == ST_MATRIX_TMP) {  /* V2[0] is NOT temporary, we can't use it for storage */
     OUT[0].buffer = V2[0].buffer;
-    V2[0].type = 'M'; /* prevent it from being freed below */
+    V2[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V2[0].buffer);
   }
-  OUT[0].type = 'm'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP; /*** <<--- says this is a temporary matrix ***/
 
   float *M2    = (float *)V2[0].buffer[0].matrix.buffer;
@@ -704,5 +900,5 @@
 
 # define SM_FUNC(OP) {					\
-    if (V1->type == 'S')  {				\
+    if (V1->type == ST_SCALAR_FLT)  {				\
       opihi_flt M1 = V1[0].FltValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
@@ -711,5 +907,5 @@
       break;						\
     }							\
-    if (V1->type == 's')  {				\
+    if (V1->type == ST_SCALAR_INT)  {				\
       opihi_int M1 = V1[0].IntValue;			\
       for (i = 0; i < Nx*Ny; i++, out++, M2++) {	\
@@ -728,4 +924,5 @@
     case '^': SM_FUNC(pow (M1, *M2));
     case '@': SM_FUNC(DEG_RAD*atan2 (M1, *M2));
+    case 'a': SM_FUNC(DEG_RAD*atan2 (M1, *M2));
     case 'D': SM_FUNC(MIN (M1, *M2));
     case 'U': SM_FUNC(MAX (M1, *M2));
@@ -741,5 +938,5 @@
     case 'O': SM_FUNC((M1 || *M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -747,5 +944,5 @@
 # undef SM_FUNC
 
-  if (V2[0].type == 'm') {
+  if (V2[0].type == ST_MATRIX_TMP) {
     free (V2[0].buffer[0].header.buffer);
     free (V2[0].buffer[0].matrix.buffer);
@@ -764,36 +961,36 @@
 
 # define SS_FUNC(FTYPE,OP) {						\
-    if ((V1->type == 'S') && (V2->type == 'S')) {			\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_FLT)) {			\
       opihi_flt M1 = V1[0].FltValue;					\
       opihi_flt M2 = V2[0].FltValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 'S') && (V2->type == 's')) {			\
+    if ((V1->type == ST_SCALAR_FLT) && (V2->type == ST_SCALAR_INT)) {			\
       opihi_flt M1 = V1[0].FltValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->type == 'S')) {			\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_FLT)) {			\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_flt M2 = V2[0].FltValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((FTYPE == 'S') && (V1->type == 's') && (V2->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT)) {	\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 'S';						\
+      OUT[0].type = ST_SCALAR_FLT;						\
       OUT[0].FltValue = OP;						\
       break;								\
     }									\
-    if ((V1->type == 's') && (V2->type == 's')) {			\
+    if ((V1->type == ST_SCALAR_INT) && (V2->type == ST_SCALAR_INT)) {			\
       opihi_int M1 = V1[0].IntValue;					\
       opihi_int M2 = V2[0].IntValue;					\
-      OUT[0].type = 's';						\
+      OUT[0].type = ST_SCALAR_INT;						\
       OUT[0].IntValue = OP;						\
       break;								\
@@ -802,25 +999,26 @@
 
   switch (op[0]) { 
-    case '+': SS_FUNC('s', M1 + M2);
-    case '-': SS_FUNC('s', M1 - M2);
-    case '*': SS_FUNC('s', M1 * M2);
-    case '/': SS_FUNC('S', M1 / (opihi_flt) M2);
-    case '%': SS_FUNC('s', (long long) M1 % (long long) M2);
-    case '^': SS_FUNC('S', pow (M1, M2));
-    case '@': SS_FUNC('S', DEG_RAD*atan2 (M1, M2));
-    case 'D': SS_FUNC('s', MIN (M1, M2));
-    case 'U': SS_FUNC('s', MAX (M1, M2));
-    case '<': SS_FUNC('s', (M1 < M2) ? 1 : 0);
-    case '>': SS_FUNC('s', (M1 > M2) ? 1 : 0);
-    case '&': SS_FUNC('s', ((long long)M1 & (long long)M2));
-    case '|': SS_FUNC('s', ((long long)M1 | (long long)M2));
-    case 'E': SS_FUNC('s', (M1 == M2) ? 1 : 0);
-    case 'N': SS_FUNC('s', (M1 != M2) ? 1 : 0);
-    case 'L': SS_FUNC('s', (M1 <= M2) ? 1 : 0);
-    case 'G': SS_FUNC('s', (M1 >= M2) ? 1 : 0);
-    case 'A': SS_FUNC('s', (M1 && M2) ? 1 : 0);
-    case 'O': SS_FUNC('s', (M1 || M2) ? 1 : 0);
+    case '+': SS_FUNC(ST_SCALAR_INT, M1 + M2);
+    case '-': SS_FUNC(ST_SCALAR_INT, M1 - M2);
+    case '*': SS_FUNC(ST_SCALAR_INT, M1 * M2);
+    case '/': SS_FUNC(ST_SCALAR_FLT, M1 / (opihi_flt) M2);
+    case '%': SS_FUNC(ST_SCALAR_INT, (long long) M1 % (long long) M2);
+    case '^': SS_FUNC(ST_SCALAR_FLT, pow (M1, M2));
+    case '@': SS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, M2));
+    case 'a': SS_FUNC(ST_SCALAR_FLT, DEG_RAD*atan2 (M1, M2));
+    case 'D': SS_FUNC(ST_SCALAR_INT, MIN (M1, M2));
+    case 'U': SS_FUNC(ST_SCALAR_INT, MAX (M1, M2));
+    case '<': SS_FUNC(ST_SCALAR_INT, (M1 < M2) ? 1 : 0);
+    case '>': SS_FUNC(ST_SCALAR_INT, (M1 > M2) ? 1 : 0);
+    case '&': SS_FUNC(ST_SCALAR_INT, ((long long)M1 & (long long)M2));
+    case '|': SS_FUNC(ST_SCALAR_INT, ((long long)M1 | (long long)M2));
+    case 'E': SS_FUNC(ST_SCALAR_INT, (M1 == M2) ? 1 : 0);
+    case 'N': SS_FUNC(ST_SCALAR_INT, (M1 != M2) ? 1 : 0);
+    case 'L': SS_FUNC(ST_SCALAR_INT, (M1 <= M2) ? 1 : 0);
+    case 'G': SS_FUNC(ST_SCALAR_INT, (M1 >= M2) ? 1 : 0);
+    case 'A': SS_FUNC(ST_SCALAR_INT, (M1 && M2) ? 1 : 0);
+    case 'O': SS_FUNC(ST_SCALAR_INT, (M1 || M2) ? 1 : 0);
     default:
-      sprintf (line, "error: op %c not defined!", op[0]);
+      snprintf (line, 512, "error: op %c not defined!", op[0]);
       push_error (line);
       return (FALSE);
@@ -844,5 +1042,5 @@
 
   if ((op[0] != 'N') && (op[0] != 'E')) {
-    sprintf (line, "error: op %c not defined for string operations!", op[0]);
+    snprintf (line, 512, "error: op %c not defined for string operations!", op[0]);
     push_error (line);
     return (FALSE);
@@ -853,9 +1051,17 @@
      thus: string == number -> false */
 
-  if (!strncasecmp (&V1[0].type, "S", 1)) {
+  if (V1[0].type == ST_SCALAR_INT) {
     value = (op[0] == 'N');
     goto escape;
   }
-  if (!strncasecmp (&V2[0].type, "S", 1)) {
+  if (V1[0].type == ST_SCALAR_FLT) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (V2[0].type == ST_SCALAR_INT) {
+    value = (op[0] == 'N');
+    goto escape;
+  }
+  if (V2[0].type == ST_SCALAR_FLT) {
     value = (op[0] == 'N');
     goto escape;
@@ -870,5 +1076,5 @@
     break; 
     default:
-      sprintf (line, "error: op %c not defined for string operations!", op[0]);
+      snprintf (line, 512, "error: op %c not defined for string operations!", op[0]);
       push_error (line);
       return (FALSE);
@@ -877,5 +1083,5 @@
 escape:
   OUT[0].FltValue = value;
-  OUT[0].type = 'S';
+  OUT[0].type = ST_SCALAR_FLT;
 
   clear_stack (V1);
@@ -891,21 +1097,21 @@
   
 # define S_FUNC(OP,FTYPE) {			\
-    if (V1->type == 'S') {			\
+    if (V1->type == ST_SCALAR_FLT) {			\
       opihi_flt M1  = V1[0].FltValue;		\
-      OUT[0].type = 'S';			\
+      OUT[0].type = ST_SCALAR_FLT;			\
       OUT[0].FltValue = OP;			\
       clear_stack (V1);				\
       return (TRUE);				\
     }						\
-    if ((FTYPE == 'S') && (V1->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_FLT) && (V1->type == ST_SCALAR_INT)) {	\
       opihi_int M1  = V1[0].IntValue;		\
-      OUT[0].type = 'S';			\
+      OUT[0].type = ST_SCALAR_FLT;			\
       OUT[0].FltValue = OP;			\
       clear_stack (V1);				\
       return (TRUE);				\
     }						\
-    if ((FTYPE == 's') && (V1->type == 's')) {	\
+    if ((FTYPE == ST_SCALAR_INT) && (V1->type == ST_SCALAR_INT)) {	\
       opihi_int M1  = V1[0].IntValue;		\
-      OUT[0].type = 's';			\
+      OUT[0].type = ST_SCALAR_INT;			\
       OUT[0].IntValue = OP;			\
       clear_stack (V1);				\
@@ -914,40 +1120,40 @@
   }
 
-  if (!strcmp (op, "="))      S_FUNC(M1, 's');
-  if (!strcmp (op, "abs"))    S_FUNC(fabs(M1), 's');
-  if (!strcmp (op, "int"))    S_FUNC((long long)(M1), 's');
-  if (!strcmp (op, "exp"))    S_FUNC(exp (M1), 'S');
-  if (!strcmp (op, "ten"))    S_FUNC(pow (10.0,M1), 'S');
-  if (!strcmp (op, "log"))    S_FUNC(log10 (M1), 'S');
-  if (!strcmp (op, "ln"))     S_FUNC(log (M1), 'S');
-  if (!strcmp (op, "sqrt"))   S_FUNC(sqrt (M1), 'S');
-  if (!strcmp (op, "erf"))    S_FUNC(erf (M1), 'S');
-  if (!strcmp (op, "sinh"))   S_FUNC(sinh (M1), 'S');
-  if (!strcmp (op, "cosh"))   S_FUNC(cosh (M1), 'S');
-  if (!strcmp (op, "asinh"))  S_FUNC(asinh (M1), 'S');
-  if (!strcmp (op, "acosh"))  S_FUNC(acosh (M1), 'S');
-  if (!strcmp (op, "lgamma")) S_FUNC(lgamma (M1), 'S');
-  if (!strcmp (op, "sin"))    S_FUNC(sin (M1), 'S');
-  if (!strcmp (op, "cos"))    S_FUNC(cos (M1), 'S');
-  if (!strcmp (op, "tan"))    S_FUNC(tan (M1), 'S');
-  if (!strcmp (op, "dsin"))   S_FUNC(sin (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dcos"))   S_FUNC(cos (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dtan"))   S_FUNC(tan (M1*RAD_DEG), 'S');
-  if (!strcmp (op, "asin"))   S_FUNC(asin (M1), 'S');
-  if (!strcmp (op, "acos"))   S_FUNC(acos (M1), 'S');
-  if (!strcmp (op, "atan"))   S_FUNC(atan (M1), 'S');
-  if (!strcmp (op, "dasin"))  S_FUNC(asin (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "dacos"))  S_FUNC(acos (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "datan"))  S_FUNC(atan (M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "rnd"))    S_FUNC(M1*0.0 + drand48(), 'S');
-  if (!strcmp (op, "not"))    S_FUNC(!(M1), 's');
-  if (!strcmp (op, "--"))     S_FUNC(-1*M1, 's'); // NOTE: opihi_int is signed, 
-  if (!strcmp (op, "isinf"))  S_FUNC(!finite(M1), 'S'); // XXX modify in future 
-  if (!strcmp (op, "isnan"))  S_FUNC(isnan(M1), 'S'); // XXX modify in future   
+  if (!strcmp (op, "="))      S_FUNC(M1, ST_SCALAR_INT);
+  if (!strcmp (op, "abs"))    S_FUNC(fabs(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "int"))    S_FUNC((long long)(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "exp"))    S_FUNC(exp (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ten"))    S_FUNC(pow (10.0,M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "log"))    S_FUNC(log10 (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ln"))     S_FUNC(log (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sqrt"))   S_FUNC(sqrt (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "erf"))    S_FUNC(erf (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sinh"))   S_FUNC(sinh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cosh"))   S_FUNC(cosh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "asinh"))  S_FUNC(asinh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acosh"))  S_FUNC(acosh (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "lgamma")) S_FUNC(lgamma (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sin"))    S_FUNC(sin (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cos"))    S_FUNC(cos (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "tan"))    S_FUNC(tan (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dsin"))   S_FUNC(sin (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dcos"))   S_FUNC(cos (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dtan"))   S_FUNC(tan (M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "asin"))   S_FUNC(asin (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acos"))   S_FUNC(acos (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "atan"))   S_FUNC(atan (M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dasin"))  S_FUNC(asin (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "dacos"))  S_FUNC(acos (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "datan"))  S_FUNC(atan (M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "rnd"))    S_FUNC(M1*0.0 + drand48(), ST_SCALAR_FLT);
+  if (!strcmp (op, "not"))    S_FUNC(!(M1), ST_SCALAR_INT);
+  if (!strcmp (op, "--"))     S_FUNC(-1*M1, ST_SCALAR_INT); // NOTE: opihi_int is signed, 
+  if (!strcmp (op, "isinf"))  S_FUNC(!finite(M1), ST_SCALAR_FLT); // XXX modify in future 
+  if (!strcmp (op, "isnan"))  S_FUNC(isnan(M1), ST_SCALAR_FLT); // XXX modify in future   
 
 # undef S_FUNC
 
   clear_stack (V1);
-  sprintf (line, "error: op %s not defined!", op);
+  snprintf (line, 512, "error: op %s not defined!", op);
   push_error (line);
   return (FALSE);
@@ -962,5 +1168,5 @@
 
   OUT[0].vector = InitVector ();
-  OUT[0].type = 'v'; /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_VECTOR_TMP; /*** <<--- says this is a temporary matrix ***/
 
 # define V_FUNC(OP,FTYPE) {					\
@@ -974,5 +1180,5 @@
       goto escape;						\
     }								\
-    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 'S')) {	\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == ST_SCALAR_FLT)) {	\
       MatchVector (OUT[0].vector, V1[0].vector, OPIHI_FLT);	\
       opihi_int *M1  = V1[0].vector[0].elements.Int;		\
@@ -983,5 +1189,5 @@
       goto escape;						\
     }								\
-    if ((V1->vector->type == OPIHI_INT) && (FTYPE == 's')) {	\
+    if ((V1->vector->type == OPIHI_INT) && (FTYPE == ST_SCALAR_INT)) {	\
       CopyVector (OUT[0].vector, V1[0].vector);			\
       opihi_int *M1  = V1[0].vector[0].elements.Int;		\
@@ -993,39 +1199,39 @@
     } }							
 
-  if (!strcmp (op, "="))      V_FUNC(*M1, 's');
-  if (!strcmp (op, "abs"))    V_FUNC(fabs(*M1), 's');
-  if (!strcmp (op, "int"))    V_FUNC((long long)(*M1), 's');
-  if (!strcmp (op, "exp"))    V_FUNC(exp(*M1), 'S');
-  if (!strcmp (op, "ten"))    V_FUNC(pow(10.0,*M1), 'S');
-  if (!strcmp (op, "log"))    V_FUNC(log10(*M1), 'S');
-  if (!strcmp (op, "ln"))     V_FUNC(log(*M1), 'S');
-  if (!strcmp (op, "sqrt"))   V_FUNC(sqrt(*M1), 'S');
-  if (!strcmp (op, "erf"))    V_FUNC(erf(*M1), 'S');
-  if (!strcmp (op, "sinh"))   V_FUNC(sinh(*M1), 'S');
-  if (!strcmp (op, "cosh"))   V_FUNC(cosh(*M1), 'S');
-  if (!strcmp (op, "asinh"))  V_FUNC(asinh(*M1), 'S');
-  if (!strcmp (op, "acosh"))  V_FUNC(acosh(*M1), 'S');
-  if (!strcmp (op, "lgamma")) V_FUNC(lgamma(*M1), 'S');
-  if (!strcmp (op, "sin"))    V_FUNC(sin(*M1), 'S');
-  if (!strcmp (op, "cos"))    V_FUNC(cos(*M1), 'S');
-  if (!strcmp (op, "tan"))    V_FUNC(tan(*M1), 'S');
-  if (!strcmp (op, "dsin"))   V_FUNC(sin(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dcos"))   V_FUNC(cos(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "dtan"))   V_FUNC(tan(*M1*RAD_DEG), 'S');
-  if (!strcmp (op, "asin"))   V_FUNC(asin(*M1), 'S');
-  if (!strcmp (op, "acos"))   V_FUNC(acos(*M1), 'S');
-  if (!strcmp (op, "atan"))   V_FUNC(atan(*M1), 'S');
-  if (!strcmp (op, "dasin"))  V_FUNC(asin(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "dacos"))  V_FUNC(acos(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "datan"))  V_FUNC(atan(*M1)*DEG_RAD, 'S');
-  if (!strcmp (op, "rnd"))    V_FUNC(drand48(), 'S');
-  if (!strcmp (op, "ramp"))   V_FUNC(i, 's');
-  if (!strcmp (op, "zero"))   V_FUNC(0, 's');
-  if (!strcmp (op, "not"))    V_FUNC(!(*M1), 's');
-  if (!strcmp (op, "--"))     V_FUNC(-1*(*M1), 's'); // NOTE: opihi_int is signed
-  if (!strcmp (op, "isinf"))  V_FUNC(!finite(*M1), 'S');
-  if (!strcmp (op, "isnan"))  V_FUNC(isnan(*M1), 'S');
-  if (!strcmp (op, "xramp"))  V_FUNC(i, 's');
-  if (!strcmp (op, "yramp"))  V_FUNC(0, 's');
+  if (!strcmp (op, "="))      V_FUNC(*M1, ST_SCALAR_INT);
+  if (!strcmp (op, "abs"))    V_FUNC(fabs(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "int"))    V_FUNC((long long)(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "exp"))    V_FUNC(exp(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ten"))    V_FUNC(pow(10.0,*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "log"))    V_FUNC(log10(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "ln"))     V_FUNC(log(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sqrt"))   V_FUNC(sqrt(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "erf"))    V_FUNC(erf(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sinh"))   V_FUNC(sinh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cosh"))   V_FUNC(cosh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "asinh"))  V_FUNC(asinh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acosh"))  V_FUNC(acosh(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "lgamma")) V_FUNC(lgamma(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "sin"))    V_FUNC(sin(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "cos"))    V_FUNC(cos(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "tan"))    V_FUNC(tan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dsin"))   V_FUNC(sin(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dcos"))   V_FUNC(cos(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "dtan"))   V_FUNC(tan(*M1*RAD_DEG), ST_SCALAR_FLT);
+  if (!strcmp (op, "asin"))   V_FUNC(asin(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "acos"))   V_FUNC(acos(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "atan"))   V_FUNC(atan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "dasin"))  V_FUNC(asin(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "dacos"))  V_FUNC(acos(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "datan"))  V_FUNC(atan(*M1)*DEG_RAD, ST_SCALAR_FLT);
+  if (!strcmp (op, "rnd"))    V_FUNC(drand48(), ST_SCALAR_FLT);
+  if (!strcmp (op, "ramp"))   V_FUNC(i, ST_SCALAR_INT);
+  if (!strcmp (op, "zero"))   V_FUNC(0, ST_SCALAR_INT);
+  if (!strcmp (op, "not"))    V_FUNC(!(*M1), ST_SCALAR_INT);
+  if (!strcmp (op, "--"))     V_FUNC(-1*(*M1), ST_SCALAR_INT); // NOTE: opihi_int is signed
+  if (!strcmp (op, "isinf"))  V_FUNC(!finite(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "isnan"))  V_FUNC(isnan(*M1), ST_SCALAR_FLT);
+  if (!strcmp (op, "xramp"))  V_FUNC(i, ST_SCALAR_INT);
+  if (!strcmp (op, "yramp"))  V_FUNC(0, ST_SCALAR_INT);
   /* xramp and yramp above only make sense for matrices. for vectors, xramp = ramp, yramp = zero */
 
@@ -1034,5 +1240,5 @@
 escape:
 
-  if (V1[0].type == 'v') {
+  if (V1[0].type == ST_VECTOR_TMP) {
     free (V1[0].vector[0].elements.Ptr);
     free (V1[0].vector);
@@ -1053,12 +1259,12 @@
   Ny = V1[0].buffer[0].matrix.Naxis[1];
 
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     OUT[0].buffer = V1[0].buffer;
-    V1[0].type = 'M'; /* prevent it from being freed below */
+    V1[0].type = ST_MATRIX; /* prevent it from being freed below */
   } else {
     OUT[0].buffer = InitBuffer ();
     CopyBuffer (OUT[0].buffer, V1[0].buffer);
   }
-  OUT[0].type = 'm';      /*** <<--- says this is a temporary matrix ***/
+  OUT[0].type = ST_MATRIX_TMP;      /*** <<--- says this is a temporary matrix ***/
   M1  = (float *) V1[0].buffer[0].matrix.buffer;
   out = (float *)OUT[0].buffer[0].matrix.buffer;
@@ -1116,5 +1322,5 @@
   }
   
-  if (V1[0].type == 'm') {
+  if (V1[0].type == ST_MATRIX_TMP) {
     free (V1[0].buffer[0].header.buffer);
     free (V1[0].buffer[0].matrix.buffer);
Index: trunk/Ohana/src/opihi/pantasks/CheckController.c
===================================================================
--- trunk/Ohana/src/opihi/pantasks/CheckController.c	(revision 36189)
+++ trunk/Ohana/src/opihi/pantasks/CheckController.c	(revision 36375)
@@ -29,10 +29,23 @@
   if (p != NULL) goto escape;
 
+  /** skip past any leading garbage? ***/
+
+  /** find "Njobs: **/
+  p = memstr (buffer.buffer, "BEGIN BLOCK Njobs:", MIN(buffer.Nbuffer, 256));
+  p += strlen("BEGIN BLOCK Njobs:");
+
   /** parse job list **/
-  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  status = sscanf (p, "%d", &Njobs);
   if (status != 1) goto escape;
   if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(&start, TRUE));
 
-  p = buffer.buffer;
+  /* output looks like:
+     BEGIN BLOCK Njobs: NN\n
+     ID name machine\n
+     ID name machine\n
+  */
+
+  // p is currently pointing at "BEGIN BLOCK Njobs"
+
   for (i = 0; i < Njobs; i++) {
     q = strchr (p, '\n');
@@ -67,10 +80,23 @@
   if (p != NULL) goto escape;
 
+  /** skip past any leading garbage? ***/
+
+  /** find "Njobs: **/
+  p = memstr (buffer.buffer, "BEGIN BLOCK Njobs:", MIN(buffer.Nbuffer, 256));
+  p += strlen("BEGIN BLOCK Njobs:");
+
   /** parse job list **/
-  status = sscanf (buffer.buffer, "%*s %d", &Njobs);
+  status = sscanf (p, "%d", &Njobs);
   if (status != 1) goto escape;
   if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(&start, TRUE)); 
 
-  p = buffer.buffer;
+  /* output looks like:
+     BEGIN BLOCK Njobs: NN\n
+     ID name machine\n
+     ID name machine\n
+  */
+
+  // p is currently pointing at "BEGIN BLOCK Njobs"
+
   for (i = 0; i < Njobs; i++) {
     q = strchr (p, '\n');
Index: trunk/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 36189)
+++ trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 36375)
@@ -59,4 +59,16 @@
   pcontrol_exit (53);
   return (NULL);
+}
+
+int GetJobStackIDbyName (char *name) {
+  if (!strcasecmp (name, "ALLJOBS"))  return PCONTROL_JOB_ALLJOBS;
+  if (!strcasecmp (name, "PENDING"))  return PCONTROL_JOB_PENDING;
+  if (!strcasecmp (name, "BUSY"))     return PCONTROL_JOB_BUSY   ;
+  if (!strcasecmp (name, "RESP"))     return PCONTROL_JOB_RESP   ;
+  if (!strcasecmp (name, "DONE"))     return PCONTROL_JOB_DONE   ;
+  if (!strcasecmp (name, "KILL"))     return PCONTROL_JOB_KILL   ;
+  if (!strcasecmp (name, "EXIT"))     return PCONTROL_JOB_EXIT   ;
+  if (!strcasecmp (name, "CRASH"))    return PCONTROL_JOB_CRASH  ;
+  return (PCONTROL_JOB_NONE);
 }
 
Index: trunk/Ohana/src/opihi/pcontrol/jobstack.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/jobstack.c	(revision 36189)
+++ trunk/Ohana/src/opihi/pcontrol/jobstack.c	(revision 36375)
@@ -22,5 +22,5 @@
   /* print list */
   LockStack (stack);
-  gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
+  gprint (GP_LOG, "BEGIN BLOCK Njobs: %d\n", stack[0].Nobject);
   for (i = 0; i < stack[0].Nobject; i++) {
     job = stack[0].object[i];
Index: trunk/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/status.c	(revision 36189)
+++ trunk/Ohana/src/opihi/pcontrol/status.c	(revision 36375)
@@ -1,16 +1,93 @@
 # include "pcontrol.h"
 
-int PrintJobStack (int Nstack);
+int PrintJobStack (int Nstack, char *command, char *hostname, int state, float age);
 int PrintHostStack (int Nstack);
 
 int status (int argc, char **argv) {
 
-  PrintJobStack (PCONTROL_JOB_ALLJOBS);
-  PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+  int N;
+
+  if (get_argument (argc, argv, "-h")) goto usage;
+  if (get_argument (argc, argv, "-help")) goto usage;
+  if (get_argument (argc, argv, "--help")) goto usage;
+
+  /* I would like to add the following options:
+   * strsub on argv[0]
+   * strsub on hostname, realhost
+   * list hostname and realhost
+   * filter by state
+   * filter by dtime
+  */
+
+  // -cmd (cmd)
+  // -host (hostname)
+  // -state (busy, pending, done, kill, exit, crash, resp, hung
+  // -age (seconds?) (minutes?)
+  // -nohost
+
+  char *COMMAND = NULL;
+  if ((N = get_argument (argc, argv, "-cmd"))) {
+    remove_argument (N, &argc, argv);
+    COMMAND = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  char *HOSTNAME = NULL;
+  if ((N = get_argument (argc, argv, "-host"))) {
+    remove_argument (N, &argc, argv);
+    HOSTNAME = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int STATE = PCONTROL_JOB_ALLJOBS;
+  if ((N = get_argument (argc, argv, "-state"))) {
+    remove_argument (N, &argc, argv);
+    STATE = GetJobStackIDbyName (argv[N]);
+    remove_argument (N, &argc, argv);
+    if (STATE == PCONTROL_JOB_NONE) goto usage;
+  }
+
+  float AGE = 0.0;
+  if ((N = get_argument (argc, argv, "-age"))) {
+    remove_argument (N, &argc, argv);
+    AGE = atof (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
+  int SHOWHOST = TRUE;
+  if ((N = get_argument (argc, argv, "-nohost"))) {
+    remove_argument (N, &argc, argv);
+    SHOWHOST = FALSE;
+  }
+  if ((N = get_argument (argc, argv, "+jobs"))) {
+    remove_argument (N, &argc, argv);
+    SHOWHOST = FALSE;
+  }
+
+  int SHOWJOBS = TRUE;
+  if ((N = get_argument (argc, argv, "-nojobs"))) {
+    remove_argument (N, &argc, argv);
+    SHOWJOBS = FALSE;
+  }
+  if ((N = get_argument (argc, argv, "+host"))) {
+    remove_argument (N, &argc, argv);
+    SHOWJOBS = FALSE;
+  }
+
+  if (SHOWJOBS) {
+    PrintJobStack (PCONTROL_JOB_ALLJOBS, COMMAND, HOSTNAME, STATE, AGE);
+  }
+  if (SHOWHOST) {
+    PrintHostStack (PCONTROL_HOST_ALLHOSTS);
+  }
 
   return (TRUE);
+
+usage:
+  gprint (GP_ERR, "USAGE: status [-cmd command] [-host hostname] [-state state] [-age seconds] [+jobs,-nohost] [+host,-nojobs]\n");
+  return FALSE;
 }
 
-int PrintJobStack (int Nstack) {
+int PrintJobStack (int Nstack, char *command, char *hostname, int state, float age) {
 
   int i, j, Nobject;
@@ -30,10 +107,6 @@
     job = stack[0].object[i];
     ASSERT (job != NULL, "programming error");
-    if (job[0].realhost == NULL) {
-	gprint (GP_LOG, "%3d %9s ", i, job[0].hostname);
-    } else {
-	gprint (GP_LOG, "%3d %9s ", i, job[0].realhost);
-    }
-    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+
+    char *thishost = (job[0].realhost == NULL) ? job[0].hostname : job[0].realhost;
 
     switch (job[0].state) {
@@ -45,5 +118,4 @@
 	gettimeofday (&now, (void *) NULL);
 	dtime = DTIME (now, job[0].start);
-	gprint (GP_LOG, "%8.2f ", dtime);
 	break;
 
@@ -55,7 +127,29 @@
       default:
 	dtime = DTIME (job[0].stop, job[0].start);
-	gprint (GP_LOG, "%8.2f ", dtime);
 	break;
     }
+
+    // check on the filters
+    if (command) {
+      if (!strstr (job[0].argv[0], command)) continue;
+    }
+    if (hostname) {
+      if (!strstr (thishost, hostname)) continue;
+    }
+    if (age > 0.0) {
+      if (dtime < age) continue;
+    }
+    if (state != PCONTROL_JOB_ALLJOBS) {
+      // allow PCONTROL_JOB_RESP == BUSY
+      int validState = FALSE;
+      validState |= (state == PCONTROL_JOB_RESP) && (job[0].state == PCONTROL_JOB_BUSY);
+      validState |= (state == PCONTROL_JOB_BUSY) && (job[0].state == PCONTROL_JOB_RESP);
+      validState |= (state == job[0].state);
+      if (!validState) continue;
+    }
+
+    gprint (GP_LOG, "%3d %9s ", i, thishost);
+    gprint (GP_LOG, "%7s  ", GetJobStackName (job[0].state));
+    gprint (GP_LOG, "%8.2f ", dtime);
 
     PrintID (GP_LOG, job[0].JobID);
