Index: /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/include/dvo.h
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/include/dvo.h	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/include/dvo.h	(revision 36022)
@@ -696,4 +696,5 @@
 void sort_coords_index (double *X, double *Y, off_t *S, off_t N);
 void sort_coords_indexonly (double *X, double *Y, off_t *S, off_t N);
+void sort_IDs_indexonly (opihi_int *X, off_t *S, off_t N);
 void sort_regions (SkyRegion *region, off_t N);
 
Index: /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/src/dvosorts.c
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/src/dvosorts.c	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/libdvo/src/dvosorts.c	(revision 36022)
@@ -60,4 +60,19 @@
 
 /* sort a coordinate pair (X,Y) and the associated index (S) */
+void sort_IDs_indexonly (opihi_int *X, off_t *S, off_t N) {
+  
+# define SWAPFUNC(A,B){ off_t itmp; \
+  itmp = S[A]; S[A] = S[B]; S[B] = itmp; \
+}
+# define COMPARE(A,B)(X[S[A]] < X[S[B]])
+
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
+/* sort a coordinate pair (X,Y) and the associated index (S) */
 void sort_coords_indexonly (double *X, double *Y, off_t *S, off_t N) {
   
Index: /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/Makefile
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/Makefile	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/Makefile	(revision 36022)
@@ -66,4 +66,5 @@
 $(SRC)/integrate.$(ARCH).o	\
 $(SRC)/interpolate.$(ARCH).o	\
+$(SRC)/join.$(ARCH).o		\
 $(SRC)/jpeg.$(ARCH).o		\
 $(SRC)/kern.$(ARCH).o		\
Index: /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/init.c
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/init.c	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/init.c	(revision 36022)
@@ -55,4 +55,5 @@
 int integrate        PROTO((int, char **));
 int interpolate      PROTO((int, char **));
+int join             PROTO((int, char **));
 int jpeg             PROTO((int, char **));
 int kern             PROTO((int, char **));
@@ -210,4 +211,5 @@
   {1, "integrate",    integrate,        "integrate a vector"},
   {1, "interpolate",  interpolate,      "interpolate between vector pairs"},
+  {1, "join",         join,             "find the join of two ID vectors"},
   {1, "jpeg",         jpeg,             "convert display image to JPEG"},
   {1, "kern",         kern,             "convolve with 3x3 kernel"},
Index: /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/join.c
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/join.c	(revision 36022)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/join.c	(revision 36022)
@@ -0,0 +1,194 @@
+# include "data.h"
+
+int join_IDs_inner (Vector *ID1, Vector *ID2, Vector *index1, Vector *index2);
+int join_IDs_outer (Vector *ID1, Vector *ID2, Vector *index);
+
+// join (ID1) (ID2) [-index1 (index1)] [-index2 (index2)]
+// generate indexes for ID1 and ID2 to identify the matches of ID1 and ID2 (must be int?)
+int join (int argc, char **argv) {
+  
+  int N, OUTER;
+  Vector *ID1vec, *ID2vec;
+  Vector *index1, *index2;
+
+  if ((N = get_argument (argc, argv, "-h"))) goto usage;
+  if ((N = get_argument (argc, argv, "--help"))) goto usage;
+
+  OUTER = FALSE;
+  if ((N = get_argument (argc, argv, "-outer"))) {
+    remove_argument (N, &argc, argv);
+    OUTER = TRUE;
+  }
+
+  if ((N = get_argument (argc, argv, "-index1"))) {
+    remove_argument (N, &argc, argv);
+    if ((index1 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index1 = SelectVector ("index1", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if ((N = get_argument (argc, argv, "-index2"))) {
+    remove_argument (N, &argc, argv);
+    if ((index2 = SelectVector (argv[N], ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+    remove_argument (N, &argc, argv);
+  } else {
+    if ((index2 = SelectVector ("index2", ANYVECTOR, TRUE)) == NULL) return (FALSE);    
+  }
+
+  if (argc != 3) {
+    gprint (GP_ERR, "USAGE: join ID1 ID2 [-index1 (index1)] [-index2 (index2)] [-outer]\n");
+    gprint (GP_ERR, "  use -h or --help for more detail\n");
+  }
+
+  if ((ID1vec = SelectVector (argv[1], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+  if ((ID2vec = SelectVector (argv[2], OLDVECTOR, TRUE)) == NULL) return (FALSE);    
+
+  REQUIRE_VECTOR_INT (ID1vec, FALSE); 
+  REQUIRE_VECTOR_INT (ID2vec, FALSE); 
+
+  if (OUTER) {
+      join_IDs_outer (ID1vec, ID2vec, index1);
+      join_IDs_outer (ID2vec, ID1vec, index2);
+  } else {
+      join_IDs_inner (ID1vec, ID2vec, index1, index2);
+  }
+
+  return (TRUE);
+
+usage:
+  gprint (GP_ERR, "we have two modes of operation:\n\n");
+
+  gprint (GP_ERR, "without -outer, we are finding all matched ID pairs.  in this\n");
+  gprint (GP_ERR, "case, the two index vectors have the same length, one entry per matched pair.\n");
+  gprint (GP_ERR, "ID1[index1] matches to ID2[index2].\n\n");
+
+  gprint (GP_ERR, "with -outer selected, we are finding the matched element of set 1 for each of set 2\n");
+  gprint (GP_ERR, "and vice versa.  in this case, index1 is always the same length as ID1, while index2\n");
+  gprint (GP_ERR, "is the same length as ID2.  ID2[index1] matches ID1 while x1[ID2] matches ID1\n");
+
+  return FALSE;
+}
+
+// find the entries in ID1 which match ID2 (duplicates allows)
+int join_IDs_inner (Vector *ID1, Vector *ID2, Vector *index1, Vector *index2) {
+  
+  off_t i, j, first_j, I, J, *N1, *N2, Nmatch, NMATCH, DMATCH;
+  opihi_int dID;
+
+  NMATCH = MAX(MAX(0.01*ID1->Nelements, 0.01*ID2->Nelements), 100);
+  DMATCH = NMATCH;
+
+  ResetVector (index1, OPIHI_INT, NMATCH);
+  ResetVector (index2, OPIHI_INT, NMATCH);
+
+  ALLOCATE (N1, off_t, ID1->Nelements);
+  ALLOCATE (N2, off_t, ID2->Nelements);
+
+  for (i = 0; i < ID1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < ID2->Nelements; i++) { N2[i] = i; }
+
+  sort_IDs_indexonly (ID1->elements.Int, N1, ID1->Nelements);
+  sort_IDs_indexonly (ID2->elements.Int, N2, ID2->Nelements);
+
+  Nmatch = 0;
+  for (i = j = 0; (i < ID1->Nelements) && (j < ID2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+
+    if (dID < 0) { i++; continue; }
+    if (dID > 0) { j++; continue; }
+
+    // look for all matches of list2() to list1(i)
+    // this allows for multiple values of ID1 or ID2
+    first_j = j;
+    for (j = first_j; (dID == 0) && (j < ID2->Nelements); j++) {
+      J = N2[j];
+      dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+      if (dID == 0) {
+	index1->elements.Int[Nmatch] = I;
+	index2->elements.Int[Nmatch] = J;
+
+	Nmatch ++;
+	if (Nmatch >= NMATCH) {
+	  NMATCH += DMATCH;
+	  REALLOCATE (index1->elements.Int, opihi_int, NMATCH);
+	  REALLOCATE (index2->elements.Int, opihi_int, NMATCH);
+	}
+      }
+    }
+    j = first_j;
+    i++;
+  }
+  index1->Nelements = Nmatch;
+  index2->Nelements = Nmatch;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
+
+// find the elements of ID1 which match ID2 (-1 if no match)
+int join_IDs_outer (Vector *ID1, Vector *ID2, Vector *index) {
+  
+  off_t i, j, Jfirst, Ji, I, J, *N1, *N2, NMATCH;
+  opihi_int dID;
+
+  NMATCH = ID1->Nelements;
+  ResetVector (index, OPIHI_INT, NMATCH);
+
+  for (i = 0; i < index->Nelements; i++) { index->elements.Int[i] = -1; }
+
+  ALLOCATE (N1, off_t, ID1->Nelements);
+  ALLOCATE (N2, off_t, ID2->Nelements);
+
+  for (i = 0; i < ID1->Nelements; i++) { N1[i] = i; }
+  for (i = 0; i < ID2->Nelements; i++) { N2[i] = i; }
+
+  sort_IDs_indexonly (ID1->elements.Int, N1, ID1->Nelements);
+  sort_IDs_indexonly (ID2->elements.Int, N2, ID2->Nelements);
+
+  // find the closest entry in list 2 to the current entry in list 1:
+  for (i = j = 0; (i < ID1->Nelements) && (j < ID2->Nelements);) {
+    I = N1[i];
+    J = N2[j];
+
+    dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+
+    if (dID < 0) { 
+      // no match in list 2 to this entry
+      index->elements.Int[I] = -1;
+      i++; 
+      continue; 
+    }
+    if (dID > 0) { j++; continue; }
+
+    // look for closest matches of list2() to list1(i)
+    Jfirst = -1;
+    for (Ji = j; (Jfirst == -1) && (dID == 0) && (Ji < ID2->Nelements); Ji++) {
+      J = N2[Ji];
+      dID = ID1->elements.Int[I] - ID2->elements.Int[J];
+      if (dID == 0) {
+	Jfirst = J;
+      }
+    }
+
+    // no match in list 2 to this entry
+    if (Jfirst == -1) {
+      index->elements.Int[I] = -1;
+      i++;
+      continue;
+    }
+    index->elements.Int[I] = Jfirst;
+    i++;
+  }
+  index->Nelements = NMATCH;
+
+  free (N1);
+  free (N2);
+
+  return (TRUE);
+}
Index: /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/match2d.c
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/match2d.c	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/match2d.c	(revision 36022)
@@ -14,4 +14,7 @@
   Vector *index1, *index2;
 
+  if ((N = get_argument (argc, argv, "-h"))) goto usage;
+  if ((N = get_argument (argc, argv, "--help"))) goto usage;
+
   CLOSEST = FALSE;
   if ((N = get_argument (argc, argv, "-closest"))) {
@@ -38,6 +41,5 @@
   if (argc != 6) {
     gprint (GP_ERR, "USAGE: match2d X1 Y1 X2 Y2 Radius [-index1 (index1)] [-index2 (index2)] [-closest]\n");
-    gprint (GP_ERR, "  if -closest is provided, index1 & index2 will have the same length as X1 and X2 (respectively)\n");
-    gprint (GP_ERR, "    with either the index of the match or a value of -1 for non-matches\n");
+    gprint (GP_ERR, "  use -h or --help for more detail\n");
     return (FALSE);
   }
@@ -90,4 +92,21 @@
 
   return (TRUE);
+
+usage:
+  gprint (GP_ERR, "we have two modes of operation:\n\n");
+
+  gprint (GP_ERR, "without -closest, we are finding all matched pairs within the match radius.  in this\n");
+  gprint (GP_ERR, "case, the two index vectors have the same length, one entry per matched pair.\n");
+  gprint (GP_ERR, "x1[index1],y1[index1] matches to x2[index2],y2[index2].\n\n");
+
+  gprint (GP_ERR, "with -closest selected, we are finding the closest element of set 1 to each of set 2\n");
+  gprint (GP_ERR, "and vice versa.  in this case, index1 is always the same length as x1,y1, while index2\n");
+  gprint (GP_ERR, "is the same lengths as x2,y2.  x2[index1],y2[index1] matches x1,y1 while\n");
+  gprint (GP_ERR, "x1[index2],y1[index2] matches x2,y2\n\n");
+
+  gprint (GP_ERR, "if -index1 or -index2 is not supplied, the vectors are created with names index1 or index2\n");
+  gprint (GP_ERR, "use 'reindex' to generate new vectors based on these index vectors\n");
+
+  return FALSE;
 }
 
Index: /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/vlist.c
===================================================================
--- /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/vlist.c	(revision 36021)
+++ /branches/eam_branches/ipp-20130711/Ohana/src/opihi/cmd.data/vlist.c	(revision 36022)
@@ -19,4 +19,5 @@
     }
     INT = TRUE;
+    remove_argument (N, &argc, argv);
   }
 
