From f0ef386f43d944430654f3ef486464db5717b4c9 Mon Sep 17 00:00:00 2001
From: Daniel Drake <dsd@cs.manchester.ac.uk>
Date: Mon, 29 Oct 2007 23:37:28 +0000
Subject: [PATCH] More NBIS cleanups

---
 TODO                              |    2 +
 configure.ac                      |    2 +-
 libfprint/Makefile.am             |    7 +-
 libfprint/nbis/include/lfs.h      |   83 +-
 libfprint/nbis/mindtct/chaincod.c |  191 -----
 libfprint/nbis/mindtct/contour.c  |  460 +++++------
 libfprint/nbis/mindtct/detect.c   |  112 ++-
 libfprint/nbis/mindtct/getmin.c   |  155 ----
 libfprint/nbis/mindtct/isempty.c  |   94 ---
 libfprint/nbis/mindtct/link.c     | 1238 -----------------------------
 libfprint/nbis/mindtct/loop.c     |  151 ++++
 libfprint/nbis/mindtct/minutia.c  |   48 ++
 libfprint/nbis/mindtct/quality.c  |  302 +++----
 libfprint/nbis/mindtct/remove.c   | 1227 ----------------------------
 libfprint/nbis/mindtct/ridges.c   |  791 +++++++++---------
 libfprint/nbis/mindtct/xytreps.c  |  133 ----
 16 files changed, 1063 insertions(+), 3933 deletions(-)
 delete mode 100644 libfprint/nbis/mindtct/chaincod.c
 delete mode 100644 libfprint/nbis/mindtct/getmin.c
 delete mode 100644 libfprint/nbis/mindtct/isempty.c
 delete mode 100644 libfprint/nbis/mindtct/link.c
 delete mode 100644 libfprint/nbis/mindtct/xytreps.c

diff --git a/TODO b/TODO
index 519ee0e..d417f75 100644
--- a/TODO
+++ b/TODO
@@ -27,3 +27,5 @@ upekts/thinkfinger relicensing (GPL --> LGPL)
 make library optionally asynchronous and maybe thread-safe
 pkg-config file
 nbis cleanups
+track open devices, so we can close them during libfprint close
+free memory during libfprint close
diff --git a/configure.ac b/configure.ac
index 6c372a3..d6a0c4a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@ AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([libfprint/core.c])
 AM_CONFIG_HEADER([config.h])
 
-AC_PREREQ([2.61])
+AC_PREREQ([2.50])
 AC_PROG_CC
 AC_PROG_LIBTOOL
 AC_C_INLINE
diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am
index c537b76..f6e7e07 100644
--- a/libfprint/Makefile.am
+++ b/libfprint/Makefile.am
@@ -15,18 +15,14 @@ NBIS_SRC = \
 	nbis/bozorth3/bz_sort.c \
 	nbis/mindtct/binar.c \
 	nbis/mindtct/block.c \
-	nbis/mindtct/chaincod.c \
 	nbis/mindtct/contour.c \
 	nbis/mindtct/detect.c \
 	nbis/mindtct/dft.c \
 	nbis/mindtct/free.c \
-	nbis/mindtct/getmin.c \
 	nbis/mindtct/globals.c \
 	nbis/mindtct/imgutil.c \
 	nbis/mindtct/init.c \
-	nbis/mindtct/isempty.c \
 	nbis/mindtct/line.c \
-	nbis/mindtct/link.c \
 	nbis/mindtct/log.c \
 	nbis/mindtct/loop.c \
 	nbis/mindtct/maps.c \
@@ -38,8 +34,7 @@ NBIS_SRC = \
 	nbis/mindtct/ridges.c \
 	nbis/mindtct/shape.c \
 	nbis/mindtct/sort.c \
-	nbis/mindtct/util.c \
-	nbis/mindtct/xytreps.c
+	nbis/mindtct/util.c
 
 libfprint_la_CFLAGS = -fvisibility=hidden -Inbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(AM_CFLAGS)
 libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
diff --git a/libfprint/nbis/include/lfs.h b/libfprint/nbis/include/lfs.h
index f260c48..c184425 100644
--- a/libfprint/nbis/include/lfs.h
+++ b/libfprint/nbis/include/lfs.h
@@ -721,12 +721,9 @@ extern int find_valid_block(int *, int *, int *, int *, int *,
                      const int, const int);
 extern void set_margin_blocks(int *, const int, const int, const int);
 
-/* chaincod.c */
-extern int chain_code_loop(int **, int *, const int *, const int *, const int);
-extern int is_chain_clockwise(const int *, const int, const int);
-
 /* contour.c */
-extern int allocate_contour(int **, int **, int **, int **, const int);
+int allocate_contour(int **ocontour_x, int **ocontour_y,
+                     int **ocontour_ex, int **ocontour_ey, const int ncontour);
 extern void free_contour(int *, int *, int *, int *);
 extern int get_high_curvature_contour(int **, int **, int **, int **, int *,
                      const int, const int, const int, const int, const int,
@@ -741,11 +738,6 @@ extern int trace_contour(int **, int **, int **, int **, int *,
 extern int search_contour(const int, const int, const int,
                      const int, const int, const int, const int, const int,
                      unsigned char *, const int, const int);
-extern int next_contour_pixel(int *, int *, int *, int *,
-                     const int, const int, const int, const int, const int,
-                     unsigned char *, const int, const int);
-extern int start_scan_nbr(const int, const int, const int, const int);
-extern int next_scan_nbr(const int, const int);
 extern int min_contour_theta(int *, double *, const int, const int *,
                      const int *, const int);
 extern void contour_limits(int *, int *, int *, int *, const int *,
@@ -754,11 +746,11 @@ extern void fix_edge_pixel_pair(int *, int *, int *, int *,
                      unsigned char *, const int, const int);
 
 /* detect.c */
-extern int lfs_detect_minutiae_V2(MINUTIAE **,
-                     int **, int **, int **, int **, int *, int *,
-                     unsigned char **, int *, int *,
-                     unsigned char *, const int, const int,
-                     const LFSPARMS *);
+extern int get_minutiae(MINUTIAE **, int **, int **, int **,
+                 int **, int **, int *, int *,
+                 unsigned char **, int *, int *, int *,
+                 unsigned char *, const int, const int,
+                 const int, const double, const LFSPARMS *);
 
 /* dft.c */
 extern int dft_dir_powers(double **, unsigned char *, const int,
@@ -773,13 +765,6 @@ extern void free_dftwaves(DFTWAVES *);
 extern void free_rotgrids(ROTGRIDS *);
 extern void free_dir_powers(double **, const int);
 
-/* getmin.c */
-extern int get_minutiae(MINUTIAE **, int **, int **, int **,
-                 int **, int **, int *, int *,
-                 unsigned char **, int *, int *, int *,
-                 unsigned char *, const int, const int,
-                 const int, const double, const LFSPARMS *);
-
 /* imgutil.c */
 extern void bits_6to8(unsigned char *, const int, const int);
 extern void bits_8to6(unsigned char *, const int, const int);
@@ -804,33 +789,10 @@ extern int init_rotgrids(ROTGRIDS **, const int, const int, const int,
 extern int alloc_dir_powers(double ***, const int, const int);
 extern int alloc_power_stats(int **, double **, int **, double **, const int);
 
-/* isempty.c */
-extern int is_image_empty(int *, const int, const int);
-extern int is_qmap_empty(int *, const int, const int);
-
-
 /* line.c */
 extern int line_points(int **, int **, int *,
                      const int, const int, const int, const int);
 
-/* link.c */
-extern int link_minutiae(MINUTIAE *, unsigned char *, const int, const int,
-                     int *, const int, const int, const LFSPARMS *);
-extern int create_link_table(int **, int **, int **, int *, int *, int *,
-                     const int, const int, const MINUTIAE *, const int *,
-                     int *, const int, const int, unsigned char *,
-                     const int, const int, const LFSPARMS *);
-extern int update_link_table(int *, int *, int *, int *, int *, int *,
-                     const int, int *, int *, int *, int *,
-                     const int, const int, const int);
-extern int order_link_table(int *, int *, int *, const int, const int,
-                     const int, const int, const MINUTIAE *, const int);
-extern int process_link_table(const int *, const int *, const int *,
-                     const int, const int, const int, const int, MINUTIAE *,
-                     int *, unsigned char *, const int, const int,
-                     const LFSPARMS *);
-extern double link_score(const double, const double, const LFSPARMS *);
-
 /* loop.c */
 extern int get_loop_list(int **, MINUTIAE *, const int, unsigned char *,
                      const int, const int);
@@ -1022,6 +984,8 @@ extern int adjust_high_curvature_minutia_V2(int *, int *, int *,
                      int *, MINUTIAE *, const LFSPARMS *);
 extern int get_low_curvature_direction(const int, const int, const int,
                      const int);
+void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
+                          const MINUTIA *minutia, const int iw, const int ih);
 
 /* quality.c */
 extern int gen_quality_map(int **, int *, int *, int *, int *,
@@ -1029,12 +993,6 @@ extern int gen_quality_map(int **, int *, int *, int *, int *,
 extern int combined_minutia_quality(MINUTIAE *, int *, const int, const int,
                      const int, unsigned char *, const int, const int,
                      const int, const double);
-double grayscale_reliability(MINUTIA *, unsigned char *,
-                     const int, const int, const int);
-extern void get_neighborhood_stats(double *, double *, MINUTIA *,
-                     unsigned char *, const int, const int, const int);
-extern int reliability_fr_quality_map(MINUTIAE *, int *, const int,
-                     const int, const int, const int, const int);
 
 /* remove.c */
 extern int remove_false_minutia(MINUTIAE *,
@@ -1049,23 +1007,6 @@ extern int remove_false_minutia_V2(MINUTIAE *,
 extern int count_minutiae_ridges(MINUTIAE *,
                   unsigned char *, const int, const int,
                   const LFSPARMS *);
-extern int count_minutia_ridges(const int, MINUTIAE *,
-                  unsigned char *, const int, const int,
-                  const LFSPARMS *);
-extern int find_neighbors(int **, int *, const int, const int, MINUTIAE *);
-extern int update_nbr_dists(int *, double *, int *, const int,
-                  const int, const int, MINUTIAE *);
-extern int insert_neighbor(const int, const int, const double,
-                  int *, double *, int *, const int);
-extern int sort_neighbors(int *, const int, const int, MINUTIAE *);
-extern int ridge_count(const int, const int, MINUTIAE *,
-                  unsigned char *, const int, const int, const LFSPARMS *);
-extern int find_transition(int *, const int, const int,
-                  const int *, const int *, const int,
-                  unsigned char *, const int, const int);
-extern int validate_ridge_crossing(const int, const int,
-                  const int *, const int *, const int,
-                  unsigned char *, const int, const int, const int);
 
 /* shape.c */
 extern void free_shape(SHAPE *);
@@ -1094,12 +1035,6 @@ extern int line2direction(const int, const int, const int, const int,
                      const int);
 extern int closest_dir_dist(const int, const int, const int);
 
-/* xytreps.c */
-extern void lfs2nist_minutia_XYT(int *, int *, int *,
-                                const MINUTIA *, const int, const int);
-extern void lfs2m1_minutia_XYT(int *, int *, int *, const MINUTIA *);
-
-
 /*************************************************************************/
 /*        EXTERNAL GLOBAL VARIABLE DEFINITIONS                           */
 /*************************************************************************/
diff --git a/libfprint/nbis/mindtct/chaincod.c b/libfprint/nbis/mindtct/chaincod.c
deleted file mode 100644
index 006c996..0000000
--- a/libfprint/nbis/mindtct/chaincod.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*******************************************************************************
-
-License: 
-This software was developed at the National Institute of Standards and 
-Technology (NIST) by employees of the Federal Government in the course 
-of their official duties. Pursuant to title 17 Section 105 of the 
-United States Code, this software is not subject to copyright protection 
-and is in the public domain. NIST assumes no responsibility  whatsoever for 
-its use by other parties, and makes no guarantees, expressed or implied, 
-about its quality, reliability, or any other characteristic. 
-
-Disclaimer: 
-This software was developed to promote biometric standards and biometric
-technology testing for the Federal Government in accordance with the USA
-PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
-Specific hardware and software products identified in this software were used
-in order to perform the software development.  In no case does such
-identification imply recommendation or endorsement by the National Institute
-of Standards and Technology, nor does it imply that the products and equipment
-identified are necessarily the best available for the purpose.  
-
-*******************************************************************************/
-
-/***********************************************************************
-      LIBRARY: LFS - NIST Latent Fingerprint System
-
-      FILE:    CHAINCODE.C
-      AUTHOR:  Michael D. Garris
-      DATE:    05/11/1999
-
-      Contains routines responsible for generating and manipulating
-      chain codes as part of the NIST Latent Fingerprint System (LFS).
-
-***********************************************************************
-               ROUTINES:
-                        chain_code_loop()
-                        is_chain_clockwise()
-***********************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <lfs.h>
-
-/*************************************************************************
-**************************************************************************
-#cat: chain_code_loop - Converts a feature's contour points into an
-#cat:            8-connected chain code vector.  This encoding represents
-#cat:            the direction taken between each adjacent point in the
-#cat:            contour.  Chain codes may be used for many purposes, such
-#cat:            as computing the perimeter or area of an object, and they
-#cat:            may be used in object detection and recognition.
-
-   Input:
-      contour_x - x-coord list for feature's contour points
-      contour_y - y-coord list for feature's contour points
-      ncontour  - number of points in contour
-   Output:
-      ochain    - resulting vector of chain codes
-      onchain   - number of codes in chain
-                  (same as number of points in contour)
-   Return Code:
-      Zero      - chain code successful derived
-      Negative  - system error
-**************************************************************************/
-int chain_code_loop(int **ochain, int *onchain,
-               const int *contour_x, const int *contour_y, const int ncontour)
-{
-   int *chain;
-   int i, j, dx, dy;
-
-   /* If we don't have at least 3 points in the contour ... */
-   if(ncontour <= 3){
-      /* Then we don't have a loop, so set chain length to 0 */
-      /* and return without any allocations.                 */
-      *onchain = 0;
-      return(0);
-   }
-
-   /* Allocate chain code vector.  It will be the same length as the */
-   /* number of points in the contour.  There will be one chain code */
-   /* between each point on the contour including a code between the */
-   /* last to the first point on the contour (completing the loop).  */
-   chain = (int *)malloc(ncontour * sizeof(int));
-   /* If the allocation fails ... */
-   if(chain == (int *)NULL){
-      fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
-      return(-170);
-   }
-
-   /* For each neighboring point in the list (with "i" pointing to the */
-   /* previous neighbor and "j" pointing to the next neighbor...       */
-   for(i = 0, j=1; i < ncontour-1; i++, j++){
-      /* Compute delta in X between neighbors. */
-      dx = contour_x[j] - contour_x[i];
-      /* Compute delta in Y between neighbors. */
-      dy = contour_y[j] - contour_y[i];
-      /* Derive chain code index from neighbor deltas.                  */
-      /* The deltas are on the range [-1..1], so to use them as indices */
-      /* into the code list, they must first be incremented by one.     */
-      chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
-   }
-
-   /* Now derive chain code between last and first points in the */
-   /* contour list.                                              */
-   dx = contour_x[0] - contour_x[i];
-   dy = contour_y[0] - contour_y[i];
-   chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
-
-   /* Store results to the output pointers. */
-   *ochain = chain;
-   *onchain = ncontour;
-
-   /* Return normally. */
-   return(0);
-}   
-
-/*************************************************************************
-**************************************************************************
-#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
-#cat:            determines if the codes are ordered clockwise or
-#cat:            counter-clockwise.
-#cat:            The routine also requires a default return value be
-#cat:            specified in the case the the routine is not able to
-#cat:            definitively determine the chains direction.  This allows
-#cat:            the default response to be application-specific.
-
-   Input:
-      chain       - chain code vector
-      nchain      - number of codes in chain
-      default_ret - default return code (used when we can't tell the order)
-   Return Code:
-      TRUE      - chain determined to be ordered clockwise
-      FALSE     - chain determined to be ordered counter-clockwise
-      Default   - could not determine the order of the chain
-**************************************************************************/
-int is_chain_clockwise(const int *chain, const int nchain,
-                       const int default_ret)
-{
-   int i, j, d, sum;
-
-   /* Initialize turn-accumulator to 0. */
-   sum = 0;
-
-   /* Foreach neighboring code in chain, compute the difference in  */
-   /* direction and accumulate.  Left-hand turns increment, whereas */
-   /* right-hand decrement.                                         */
-   for(i = 0, j =1; i < nchain-1; i++, j++){
-      /* Compute delta in neighbor direction. */
-      d = chain[j] - chain[i];
-      /* Make the delta the "inner" distance. */
-      /* If delta >= 4, for example if chain_i==2 and chain_j==7 (which   */
-      /* means the contour went from a step up to step down-to-the-right) */
-      /* then 5=(7-2) which is >=4, so -3=(5-8) which means that the      */
-      /* change in direction is a righ-hand turn of 3 units).             */
-      if(d >= 4)
-         d -= 8;
-      /* If delta <= -4, for example if chain_i==7 and chain_j==2 (which  */
-      /* means the contour went from a step down-to-the-right to step up) */
-      /* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the    */
-      /* change in direction is a left-hand turn of 3 units).             */
-      else if (d <= -4)
-         d += 8;
-
-      /* The delta direction is then accumulated. */
-      sum += d;
-   }
-
-   /* Now we need to add in the final delta direction between the last */
-   /* and first codes in the chain.                                    */
-   d = chain[0] - chain[i];
-   if(d >= 4)
-      d -= 8;
-   else if (d <= -4)
-      d += 8;
-   sum += d;
-
-   /* If the final turn_accumulator == 0, then we CAN'T TELL the       */
-   /* direction of the chain code, so return the default return value. */
-   if(sum == 0)
-      return(default_ret);
-   /* Otherwise, if the final turn-accumulator is positive ... */
-   else if(sum > 0)
-      /* Then we had a greater amount of left-hand turns than right-hand     */
-      /* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
-      return(FALSE);
-   /* Otherwise, the final turn-accumulator is negative ... */
-   else
-      /* So we had a greater amount of right-hand turns than left-hand  */
-      /* turns, so the chain is in CLOCKWISE order, so return TRUE.     */
-      return(TRUE);
-}
diff --git a/libfprint/nbis/mindtct/contour.c b/libfprint/nbis/mindtct/contour.c
index ae43471..24d15c4 100644
--- a/libfprint/nbis/mindtct/contour.c
+++ b/libfprint/nbis/mindtct/contour.c
@@ -591,6 +591,236 @@ int get_centered_contour(int **ocontour_x, int **ocontour_y,
    return(0);
 }
 
+/*************************************************************************
+**************************************************************************
+#cat: start_scan_nbr - Takes a two pixel coordinates that are either
+#cat:            aligned north-to-south or east-to-west, and returns the
+#cat:            position the second pixel is in realtionship to the first.
+#cat:            The positions returned are based on 8-connectedness.
+#cat:            NOTE, this routine does NOT account for diagonal positions.
+
+   Input:
+      x_prev - x-coord of first point
+      y_prev - y-coord of first point
+      x_next - x-coord of second point
+      y_next - y-coord of second point
+   Return Code:
+      NORTH - second pixel above first
+      SOUTH - second pixel below first
+      EAST  - second pixel right of first
+      WEST  - second pixel left of first
+**************************************************************************/
+static int start_scan_nbr(const int x_prev, const int y_prev,
+                   const int x_next, const int y_next)
+{
+   if((x_prev==x_next) && (y_next > y_prev))
+      return(SOUTH);
+   else if ((x_prev==x_next) && (y_next < y_prev))
+      return(NORTH);
+   else if ((x_next > x_prev) && (y_prev==y_next))
+      return(EAST);
+   else if ((x_next < x_prev) && (y_prev==y_next))
+      return(WEST);
+
+   /* Added by MDG on 03-16-05 */
+   /* Should never reach here.  Added to remove compiler warning. */
+   return(INVALID_DIR); /* -1 */
+}
+
+/*************************************************************************
+**************************************************************************
+#cat: next_scan_nbr - Advances the given 8-connected neighbor index
+#cat:            on location in the specifiec direction (clockwise or
+#cat:            counter-clockwise). 
+
+   Input:
+      nbr_i      - current 8-connected neighbor index
+      scan_clock - direction in which the neighbor index is to be advanced
+   Return Code:
+      Next neighbor - 8-connected index of next neighbor
+**************************************************************************/
+static int next_scan_nbr(const int nbr_i, const int scan_clock)
+{
+   int new_i;
+
+   /* If scanning neighbors clockwise ... */
+   if(scan_clock == SCAN_CLOCKWISE)
+      /* Advance one neighbor clockwise. */
+      new_i = (nbr_i+1)%8;
+   /* Otherwise, scanning neighbors counter-clockwise ... */
+   else
+      /* Advance one neighbor counter-clockwise.         */
+      /* There are 8 pixels in the neighborhood, so to   */
+      /* decrement with wrapping from 0 around to 7, add */
+      /* the nieghbor index by 7 and mod with 8.         */
+      new_i = (nbr_i+7)%8;
+
+   /* Return the new neighbor index. */
+   return(new_i);
+}
+
+/*************************************************************************
+**************************************************************************
+#cat: next_contour_pixel - Takes a pixel coordinate of a point determined
+#cat:            to be on the interior edge of a feature (ridge or valley-
+#cat:            ending), and attempts to locate a neighboring pixel on the
+#cat:            feature's contour.  Neighbors of the current feature pixel
+#cat:            are searched in a specified direction (clockwise or counter-
+#cat:            clockwise) and the first pair of adjacent/neigboring pixels
+#cat:            found with the first pixel having the color of the feature
+#cat:            and the second the opposite color are returned as the next
+#cat:            point on the contour.  One exception happens when the new
+#cat:            point is on an "exposed" corner.
+
+   Input:
+      cur_x_loc  - x-pixel coord of current point on feature's
+                   interior contour
+      cur_y_loc  - y-pixel coord of current point on feature's
+                   interior contour
+      cur_x_edge - x-pixel coord of corresponding edge pixel
+                   (exterior to feature)
+      cur_y_edge - y-pixel coord of corresponding edge pixel
+                   (exterior to feature)
+      scan_clock - direction in which neighboring pixels are to be scanned
+                   for the next contour pixel
+      bdata      - binary image data (0==while & 1==black)
+      iw         - width (in pixels) of image
+      ih         - height (in pixels) of image
+   Output:
+      next_x_loc  - x-pixel coord of next point on feature's interior contour
+      next_y_loc  - y-pixel coord of next point on feature's interior contour
+      next_x_edge - x-pixel coord of corresponding edge (exterior to feature)
+      next_y_edge - y-pixel coord of corresponding edge (exterior to feature)
+   Return Code:
+      TRUE  - next contour point found and returned
+      FALSE - next contour point NOT found
+**************************************************************************/
+/*************************************************************************/
+static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
+                int *next_x_edge, int *next_y_edge,
+                const int cur_x_loc, const int cur_y_loc,
+                const int cur_x_edge, const int cur_y_edge,
+                const int scan_clock,
+                unsigned char *bdata, const int iw, const int ih)
+{
+   int feature_pix, edge_pix;
+   int prev_nbr_pix, prev_nbr_x, prev_nbr_y;
+   int cur_nbr_pix, cur_nbr_x, cur_nbr_y;
+   int ni, nx, ny, npix;
+   int nbr_i, i;
+
+   /* Get the feature's pixel value. */
+   feature_pix = *(bdata + (cur_y_loc * iw) + cur_x_loc);
+   /* Get the feature's edge pixel value. */
+   edge_pix = *(bdata + (cur_y_edge * iw) + cur_x_edge);
+
+   /* Get the nieghbor position of the feature's edge pixel in relationship */
+   /* to the feature's actual position.                                     */
+   /* REMEBER: The feature's position is always interior and on a ridge     */
+   /* ending (black pixel) or (for bifurcations) on a valley ending (white  */
+   /* pixel).  The feature's edge pixel is an adjacent pixel to the feature */
+   /* pixel that is exterior to the ridge or valley ending and opposite in  */
+   /* pixel value.                                                          */
+   nbr_i = start_scan_nbr(cur_x_loc, cur_y_loc, cur_x_edge, cur_y_edge);
+
+   /* Set current neighbor scan pixel to the feature's edge pixel. */
+   cur_nbr_x = cur_x_edge;
+   cur_nbr_y = cur_y_edge;
+   cur_nbr_pix = edge_pix;
+
+   /* Foreach pixel neighboring the feature pixel ... */
+   for(i = 0; i < 8; i++){
+
+      /* Set current neighbor scan pixel to previous scan pixel. */
+      prev_nbr_x = cur_nbr_x;
+      prev_nbr_y = cur_nbr_y;
+      prev_nbr_pix = cur_nbr_pix;
+
+      /* Bump pixel neighbor index clockwise or counter-clockwise. */
+      nbr_i = next_scan_nbr(nbr_i, scan_clock);
+
+      /* Set current scan pixel to the new neighbor.                   */
+      /* REMEMBER: the neighbors are being scanned around the original */
+      /* feature point.                                                */
+      cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
+      cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
+
+      /* If new neighbor is not within image boundaries... */
+      if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
+         (cur_nbr_y < 0) || (cur_nbr_y >= ih))
+         /* Return (FALSE==>Failure) if neighbor out of bounds. */
+         return(FALSE);
+
+      /* Get the new neighbor's pixel value. */
+      cur_nbr_pix = *(bdata + (cur_nbr_y * iw) + cur_nbr_x);
+
+      /* If the new neighbor's pixel value is the same as the feature's   */
+      /* pixel value AND the previous neighbor's pixel value is the same  */
+      /* as the features's edge, then we have "likely" found our next     */
+      /* contour pixel.                                                   */
+      if((cur_nbr_pix == feature_pix) && (prev_nbr_pix == edge_pix)){
+
+         /* Check to see if current neighbor is on the corner of the */
+         /* neighborhood, and if so, test to see if it is "exposed". */
+         /* The neighborhood corners have odd neighbor indicies.     */
+         if(nbr_i % 2){
+            /* To do this, look ahead one more neighbor pixel. */
+            ni = next_scan_nbr(nbr_i, scan_clock);
+            nx = cur_x_loc + nbr8_dx[ni];
+            ny = cur_y_loc + nbr8_dy[ni];
+            /* If new neighbor is not within image boundaries... */
+            if((nx < 0) || (nx >= iw) ||
+               (ny < 0) || (ny >= ih))
+               /* Return (FALSE==>Failure) if neighbor out of bounds. */
+               return(FALSE);
+            npix = *(bdata + (ny * iw) + nx);
+
+            /* If the next neighbor's value is also the same as the */
+            /* feature's pixel, then corner is NOT exposed...       */
+            if(npix == feature_pix){
+               /* Assign the current neighbor pair to the output pointers. */
+               *next_x_loc = cur_nbr_x;
+               *next_y_loc = cur_nbr_y;
+               *next_x_edge = prev_nbr_x;
+               *next_y_edge = prev_nbr_y;
+               /* Return TRUE==>Success. */
+               return(TRUE);
+            }
+            /* Otherwise, corner pixel is "exposed" so skip it. */
+            else{
+               /* Skip current corner neighbor by resetting it to the      */
+               /* next neighbor, which upon the iteration will immediately */
+               /* become the previous neighbor.                            */
+               cur_nbr_x = nx;
+               cur_nbr_y = ny;
+               cur_nbr_pix = npix;
+               /* Advance neighbor index. */
+               nbr_i = ni;
+               /* Advance neighbor count. */
+               i++;
+            }
+         }
+         /* Otherwise, current neighbor is not a corner ... */
+         else{
+            /* Assign the current neighbor pair to the output pointers. */
+            *next_x_loc = cur_nbr_x;
+            *next_y_loc = cur_nbr_y;
+            *next_x_edge = prev_nbr_x;
+            *next_y_edge = prev_nbr_y;
+            /* Return TRUE==>Success. */
+            return(TRUE);
+         }
+      }
+   }
+
+   /* If we get here, then we did not find the next contour pixel */
+   /* within the 8 neighbors of the current feature pixel so      */
+   /* return (FALSE==>Failure).                                   */
+   /* NOTE: This must mean we found a single isolated pixel.      */
+   /*       Perhaps this should be filled?                        */
+   return(FALSE);
+}
+
 /*************************************************************************
 **************************************************************************
 #cat: trace_contour - Takes the pixel coordinate of a detected minutia
@@ -816,236 +1046,6 @@ int search_contour(const int x_search, const int y_search,
    return(NOT_FOUND);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: next_contour_pixel - Takes a pixel coordinate of a point determined
-#cat:            to be on the interior edge of a feature (ridge or valley-
-#cat:            ending), and attempts to locate a neighboring pixel on the
-#cat:            feature's contour.  Neighbors of the current feature pixel
-#cat:            are searched in a specified direction (clockwise or counter-
-#cat:            clockwise) and the first pair of adjacent/neigboring pixels
-#cat:            found with the first pixel having the color of the feature
-#cat:            and the second the opposite color are returned as the next
-#cat:            point on the contour.  One exception happens when the new
-#cat:            point is on an "exposed" corner.
-
-   Input:
-      cur_x_loc  - x-pixel coord of current point on feature's
-                   interior contour
-      cur_y_loc  - y-pixel coord of current point on feature's
-                   interior contour
-      cur_x_edge - x-pixel coord of corresponding edge pixel
-                   (exterior to feature)
-      cur_y_edge - y-pixel coord of corresponding edge pixel
-                   (exterior to feature)
-      scan_clock - direction in which neighboring pixels are to be scanned
-                   for the next contour pixel
-      bdata      - binary image data (0==while & 1==black)
-      iw         - width (in pixels) of image
-      ih         - height (in pixels) of image
-   Output:
-      next_x_loc  - x-pixel coord of next point on feature's interior contour
-      next_y_loc  - y-pixel coord of next point on feature's interior contour
-      next_x_edge - x-pixel coord of corresponding edge (exterior to feature)
-      next_y_edge - y-pixel coord of corresponding edge (exterior to feature)
-   Return Code:
-      TRUE  - next contour point found and returned
-      FALSE - next contour point NOT found
-**************************************************************************/
-/*************************************************************************/
-int next_contour_pixel(int *next_x_loc, int *next_y_loc,
-                int *next_x_edge, int *next_y_edge,
-                const int cur_x_loc, const int cur_y_loc,
-                const int cur_x_edge, const int cur_y_edge,
-                const int scan_clock,
-                unsigned char *bdata, const int iw, const int ih)
-{
-   int feature_pix, edge_pix;
-   int prev_nbr_pix, prev_nbr_x, prev_nbr_y;
-   int cur_nbr_pix, cur_nbr_x, cur_nbr_y;
-   int ni, nx, ny, npix;
-   int nbr_i, i;
-
-   /* Get the feature's pixel value. */
-   feature_pix = *(bdata + (cur_y_loc * iw) + cur_x_loc);
-   /* Get the feature's edge pixel value. */
-   edge_pix = *(bdata + (cur_y_edge * iw) + cur_x_edge);
-
-   /* Get the nieghbor position of the feature's edge pixel in relationship */
-   /* to the feature's actual position.                                     */
-   /* REMEBER: The feature's position is always interior and on a ridge     */
-   /* ending (black pixel) or (for bifurcations) on a valley ending (white  */
-   /* pixel).  The feature's edge pixel is an adjacent pixel to the feature */
-   /* pixel that is exterior to the ridge or valley ending and opposite in  */
-   /* pixel value.                                                          */
-   nbr_i = start_scan_nbr(cur_x_loc, cur_y_loc, cur_x_edge, cur_y_edge);
-
-   /* Set current neighbor scan pixel to the feature's edge pixel. */
-   cur_nbr_x = cur_x_edge;
-   cur_nbr_y = cur_y_edge;
-   cur_nbr_pix = edge_pix;
-
-   /* Foreach pixel neighboring the feature pixel ... */
-   for(i = 0; i < 8; i++){
-
-      /* Set current neighbor scan pixel to previous scan pixel. */
-      prev_nbr_x = cur_nbr_x;
-      prev_nbr_y = cur_nbr_y;
-      prev_nbr_pix = cur_nbr_pix;
-
-      /* Bump pixel neighbor index clockwise or counter-clockwise. */
-      nbr_i = next_scan_nbr(nbr_i, scan_clock);
-
-      /* Set current scan pixel to the new neighbor.                   */
-      /* REMEMBER: the neighbors are being scanned around the original */
-      /* feature point.                                                */
-      cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
-      cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
-
-      /* If new neighbor is not within image boundaries... */
-      if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
-         (cur_nbr_y < 0) || (cur_nbr_y >= ih))
-         /* Return (FALSE==>Failure) if neighbor out of bounds. */
-         return(FALSE);
-
-      /* Get the new neighbor's pixel value. */
-      cur_nbr_pix = *(bdata + (cur_nbr_y * iw) + cur_nbr_x);
-
-      /* If the new neighbor's pixel value is the same as the feature's   */
-      /* pixel value AND the previous neighbor's pixel value is the same  */
-      /* as the features's edge, then we have "likely" found our next     */
-      /* contour pixel.                                                   */
-      if((cur_nbr_pix == feature_pix) && (prev_nbr_pix == edge_pix)){
-
-         /* Check to see if current neighbor is on the corner of the */
-         /* neighborhood, and if so, test to see if it is "exposed". */
-         /* The neighborhood corners have odd neighbor indicies.     */
-         if(nbr_i % 2){
-            /* To do this, look ahead one more neighbor pixel. */
-            ni = next_scan_nbr(nbr_i, scan_clock);
-            nx = cur_x_loc + nbr8_dx[ni];
-            ny = cur_y_loc + nbr8_dy[ni];
-            /* If new neighbor is not within image boundaries... */
-            if((nx < 0) || (nx >= iw) ||
-               (ny < 0) || (ny >= ih))
-               /* Return (FALSE==>Failure) if neighbor out of bounds. */
-               return(FALSE);
-            npix = *(bdata + (ny * iw) + nx);
-
-            /* If the next neighbor's value is also the same as the */
-            /* feature's pixel, then corner is NOT exposed...       */
-            if(npix == feature_pix){
-               /* Assign the current neighbor pair to the output pointers. */
-               *next_x_loc = cur_nbr_x;
-               *next_y_loc = cur_nbr_y;
-               *next_x_edge = prev_nbr_x;
-               *next_y_edge = prev_nbr_y;
-               /* Return TRUE==>Success. */
-               return(TRUE);
-            }
-            /* Otherwise, corner pixel is "exposed" so skip it. */
-            else{
-               /* Skip current corner neighbor by resetting it to the      */
-               /* next neighbor, which upon the iteration will immediately */
-               /* become the previous neighbor.                            */
-               cur_nbr_x = nx;
-               cur_nbr_y = ny;
-               cur_nbr_pix = npix;
-               /* Advance neighbor index. */
-               nbr_i = ni;
-               /* Advance neighbor count. */
-               i++;
-            }
-         }
-         /* Otherwise, current neighbor is not a corner ... */
-         else{
-            /* Assign the current neighbor pair to the output pointers. */
-            *next_x_loc = cur_nbr_x;
-            *next_y_loc = cur_nbr_y;
-            *next_x_edge = prev_nbr_x;
-            *next_y_edge = prev_nbr_y;
-            /* Return TRUE==>Success. */
-            return(TRUE);
-         }
-      }
-   }
-
-   /* If we get here, then we did not find the next contour pixel */
-   /* within the 8 neighbors of the current feature pixel so      */
-   /* return (FALSE==>Failure).                                   */
-   /* NOTE: This must mean we found a single isolated pixel.      */
-   /*       Perhaps this should be filled?                        */
-   return(FALSE);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: start_scan_nbr - Takes a two pixel coordinates that are either
-#cat:            aligned north-to-south or east-to-west, and returns the
-#cat:            position the second pixel is in realtionship to the first.
-#cat:            The positions returned are based on 8-connectedness.
-#cat:            NOTE, this routine does NOT account for diagonal positions.
-
-   Input:
-      x_prev - x-coord of first point
-      y_prev - y-coord of first point
-      x_next - x-coord of second point
-      y_next - y-coord of second point
-   Return Code:
-      NORTH - second pixel above first
-      SOUTH - second pixel below first
-      EAST  - second pixel right of first
-      WEST  - second pixel left of first
-**************************************************************************/
-int start_scan_nbr(const int x_prev, const int y_prev,
-                   const int x_next, const int y_next)
-{
-   if((x_prev==x_next) && (y_next > y_prev))
-      return(SOUTH);
-   else if ((x_prev==x_next) && (y_next < y_prev))
-      return(NORTH);
-   else if ((x_next > x_prev) && (y_prev==y_next))
-      return(EAST);
-   else if ((x_next < x_prev) && (y_prev==y_next))
-      return(WEST);
-
-   /* Added by MDG on 03-16-05 */
-   /* Should never reach here.  Added to remove compiler warning. */
-   return(INVALID_DIR); /* -1 */
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: next_scan_nbr - Advances the given 8-connected neighbor index
-#cat:            on location in the specifiec direction (clockwise or
-#cat:            counter-clockwise). 
-
-   Input:
-      nbr_i      - current 8-connected neighbor index
-      scan_clock - direction in which the neighbor index is to be advanced
-   Return Code:
-      Next neighbor - 8-connected index of next neighbor
-**************************************************************************/
-int next_scan_nbr(const int nbr_i, const int scan_clock)
-{
-   int new_i;
-
-   /* If scanning neighbors clockwise ... */
-   if(scan_clock == SCAN_CLOCKWISE)
-      /* Advance one neighbor clockwise. */
-      new_i = (nbr_i+1)%8;
-   /* Otherwise, scanning neighbors counter-clockwise ... */
-   else
-      /* Advance one neighbor counter-clockwise.         */
-      /* There are 8 pixels in the neighborhood, so to   */
-      /* decrement with wrapping from 0 around to 7, add */
-      /* the nieghbor index by 7 and mod with 8.         */
-      new_i = (nbr_i+7)%8;
-
-   /* Return the new neighbor index. */
-   return(new_i);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: min_contour_theta - Takes a contour list and analyzes it locating the
diff --git a/libfprint/nbis/mindtct/detect.c b/libfprint/nbis/mindtct/detect.c
index 3b05d21..214459b 100644
--- a/libfprint/nbis/mindtct/detect.c
+++ b/libfprint/nbis/mindtct/detect.c
@@ -36,6 +36,7 @@ identified are necessarily the best available for the purpose.
 ***********************************************************************
                ROUTINES:
                         lfs_detect_minutiae_V2()
+                        get_minutiae()
 
 ***********************************************************************/
 
@@ -80,7 +81,7 @@ identified are necessarily the best available for the purpose.
       Zero      - successful completion
       Negative  - system error
 **************************************************************************/
-int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
+static int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
                         int **odmap, int **olcmap, int **olfmap, int **ohcmap,
                         int *omw, int *omh,
                         unsigned char **obdata, int *obw, int *obh,
@@ -343,3 +344,112 @@ int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
    return(0);
 }
 
+/*************************************************************************
+**************************************************************************
+#cat:   get_minutiae - Takes a grayscale fingerprint image, binarizes the input
+#cat:                image, and detects minutiae points using LFS Version 2.
+#cat:                The routine passes back the detected minutiae, the
+#cat:                binarized image, and a set of image quality maps.
+
+   Input:
+      idata    - grayscale fingerprint image data
+      iw       - width (in pixels) of the grayscale image
+      ih       - height (in pixels) of the grayscale image
+      id       - pixel depth (in bits) of the grayscale image
+      ppmm     - the scan resolution (in pixels/mm) of the grayscale image
+      lfsparms - parameters and thresholds for controlling LFS
+   Output:
+      ominutiae         - points to a structure containing the
+                          detected minutiae
+      oquality_map      - resulting integrated image quality map
+      odirection_map    - resulting direction map
+      olow_contrast_map - resulting low contrast map
+      olow_flow_map     - resulting low ridge flow map
+      ohigh_curve_map   - resulting high curvature map
+      omap_w   - width (in blocks) of image maps
+      omap_h   - height (in blocks) of image maps
+      obdata   - points to binarized image data
+      obw      - width (in pixels) of binarized image
+      obh      - height (in pixels) of binarized image
+      obd      - pixel depth (in bits) of binarized image
+   Return Code:
+      Zero     - successful completion
+      Negative - system error
+**************************************************************************/
+int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
+                 int **odirection_map, int **olow_contrast_map,
+                 int **olow_flow_map, int **ohigh_curve_map,
+                 int *omap_w, int *omap_h,
+                 unsigned char **obdata, int *obw, int *obh, int *obd,
+                 unsigned char *idata, const int iw, const int ih,
+                 const int id, const double ppmm, const LFSPARMS *lfsparms)
+{
+   int ret;
+   MINUTIAE *minutiae;
+   int *direction_map, *low_contrast_map, *low_flow_map;
+   int *high_curve_map, *quality_map;
+   int map_w, map_h;
+   unsigned char *bdata;
+   int bw, bh;
+
+   /* If input image is not 8-bit grayscale ... */
+   if(id != 8){
+      fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
+      fprintf(stderr, "depth = %d != 8.\n", id);
+      return(-2);
+   }
+
+   /* Detect minutiae in grayscale fingerpeint image. */
+   if((ret = lfs_detect_minutiae_V2(&minutiae,
+                                   &direction_map, &low_contrast_map,
+                                   &low_flow_map, &high_curve_map,
+                                   &map_w, &map_h,
+                                   &bdata, &bw, &bh,
+                                   idata, iw, ih, lfsparms))){
+      return(ret);
+   }
+
+   /* Build integrated quality map. */
+   if((ret = gen_quality_map(&quality_map,
+                            direction_map, low_contrast_map,
+                            low_flow_map, high_curve_map, map_w, map_h))){
+      free_minutiae(minutiae);
+      free(direction_map);
+      free(low_contrast_map);
+      free(low_flow_map);
+      free(high_curve_map);
+      free(bdata);
+      return(ret);
+   }
+
+   /* Assign reliability from quality map. */
+   if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
+                                     lfsparms->blocksize,
+                                     idata, iw, ih, id, ppmm))){
+      free_minutiae(minutiae);
+      free(direction_map);
+      free(low_contrast_map);
+      free(low_flow_map);
+      free(high_curve_map);
+      free(quality_map);
+      free(bdata);
+      return(ret);
+   }
+
+   /* Set output pointers. */
+   *ominutiae = minutiae;
+   *oquality_map = quality_map;
+   *odirection_map = direction_map;
+   *olow_contrast_map = low_contrast_map;
+   *olow_flow_map = low_flow_map;
+   *ohigh_curve_map = high_curve_map;
+   *omap_w = map_w;
+   *omap_h = map_h;
+   *obdata = bdata;
+   *obw = bw;
+   *obh = bh;
+   *obd = id;
+
+   /* Return normally. */
+   return(0);
+}
diff --git a/libfprint/nbis/mindtct/getmin.c b/libfprint/nbis/mindtct/getmin.c
deleted file mode 100644
index 22404f1..0000000
--- a/libfprint/nbis/mindtct/getmin.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*******************************************************************************
-
-License: 
-This software was developed at the National Institute of Standards and 
-Technology (NIST) by employees of the Federal Government in the course 
-of their official duties. Pursuant to title 17 Section 105 of the 
-United States Code, this software is not subject to copyright protection 
-and is in the public domain. NIST assumes no responsibility  whatsoever for 
-its use by other parties, and makes no guarantees, expressed or implied, 
-about its quality, reliability, or any other characteristic. 
-
-Disclaimer: 
-This software was developed to promote biometric standards and biometric
-technology testing for the Federal Government in accordance with the USA
-PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
-Specific hardware and software products identified in this software were used
-in order to perform the software development.  In no case does such
-identification imply recommendation or endorsement by the National Institute
-of Standards and Technology, nor does it imply that the products and equipment
-identified are necessarily the best available for the purpose.  
-
-*******************************************************************************/
-
-/***********************************************************************
-      LIBRARY: LFS - NIST Latent Fingerprint System
-
-      FILE:    GETMIN.C
-      AUTHOR:  Michael D. Garris
-      DATE:    09/10/2004
-      UPDATED: 03/16/2005 by MDG
-
-      Takes an 8-bit grayscale fingerpinrt image and detects minutiae
-      as part of the NIST Latent Fingerprint System (LFS), returning
-      minutiae with final reliabilities and maps including a merged
-      quality map.
-
-***********************************************************************
-               ROUTINES:
-                        get_minutiae()
-
-***********************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <lfs.h>
-
-/*************************************************************************
-**************************************************************************
-#cat:   get_minutiae - Takes a grayscale fingerprint image, binarizes the input
-#cat:                image, and detects minutiae points using LFS Version 2.
-#cat:                The routine passes back the detected minutiae, the
-#cat:                binarized image, and a set of image quality maps.
-
-   Input:
-      idata    - grayscale fingerprint image data
-      iw       - width (in pixels) of the grayscale image
-      ih       - height (in pixels) of the grayscale image
-      id       - pixel depth (in bits) of the grayscale image
-      ppmm     - the scan resolution (in pixels/mm) of the grayscale image
-      lfsparms - parameters and thresholds for controlling LFS
-   Output:
-      ominutiae         - points to a structure containing the
-                          detected minutiae
-      oquality_map      - resulting integrated image quality map
-      odirection_map    - resulting direction map
-      olow_contrast_map - resulting low contrast map
-      olow_flow_map     - resulting low ridge flow map
-      ohigh_curve_map   - resulting high curvature map
-      omap_w   - width (in blocks) of image maps
-      omap_h   - height (in blocks) of image maps
-      obdata   - points to binarized image data
-      obw      - width (in pixels) of binarized image
-      obh      - height (in pixels) of binarized image
-      obd      - pixel depth (in bits) of binarized image
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
-                 int **odirection_map, int **olow_contrast_map,
-                 int **olow_flow_map, int **ohigh_curve_map,
-                 int *omap_w, int *omap_h,
-                 unsigned char **obdata, int *obw, int *obh, int *obd,
-                 unsigned char *idata, const int iw, const int ih,
-                 const int id, const double ppmm, const LFSPARMS *lfsparms)
-{
-   int ret;
-   MINUTIAE *minutiae;
-   int *direction_map, *low_contrast_map, *low_flow_map;
-   int *high_curve_map, *quality_map;
-   int map_w, map_h;
-   unsigned char *bdata;
-   int bw, bh;
-
-   /* If input image is not 8-bit grayscale ... */
-   if(id != 8){
-      fprintf(stderr, "ERROR : get_minutiae : input image pixel ");
-      fprintf(stderr, "depth = %d != 8.\n", id);
-      return(-2);
-   }
-
-   /* Detect minutiae in grayscale fingerpeint image. */
-   if((ret = lfs_detect_minutiae_V2(&minutiae,
-                                   &direction_map, &low_contrast_map,
-                                   &low_flow_map, &high_curve_map,
-                                   &map_w, &map_h,
-                                   &bdata, &bw, &bh,
-                                   idata, iw, ih, lfsparms))){
-      return(ret);
-   }
-
-   /* Build integrated quality map. */
-   if((ret = gen_quality_map(&quality_map,
-                            direction_map, low_contrast_map,
-                            low_flow_map, high_curve_map, map_w, map_h))){
-      free_minutiae(minutiae);
-      free(direction_map);
-      free(low_contrast_map);
-      free(low_flow_map);
-      free(high_curve_map);
-      free(bdata);
-      return(ret);
-   }
-
-   /* Assign reliability from quality map. */
-   if((ret = combined_minutia_quality(minutiae, quality_map, map_w, map_h,
-                                     lfsparms->blocksize,
-                                     idata, iw, ih, id, ppmm))){
-      free_minutiae(minutiae);
-      free(direction_map);
-      free(low_contrast_map);
-      free(low_flow_map);
-      free(high_curve_map);
-      free(quality_map);
-      free(bdata);
-      return(ret);
-   }
-
-   /* Set output pointers. */
-   *ominutiae = minutiae;
-   *oquality_map = quality_map;
-   *odirection_map = direction_map;
-   *olow_contrast_map = low_contrast_map;
-   *olow_flow_map = low_flow_map;
-   *ohigh_curve_map = high_curve_map;
-   *omap_w = map_w;
-   *omap_h = map_h;
-   *obdata = bdata;
-   *obw = bw;
-   *obh = bh;
-   *obd = id;
-
-   /* Return normally. */
-   return(0);
-}
diff --git a/libfprint/nbis/mindtct/isempty.c b/libfprint/nbis/mindtct/isempty.c
deleted file mode 100644
index da42cc3..0000000
--- a/libfprint/nbis/mindtct/isempty.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*******************************************************************************
-
-License: 
-This software was developed at the National Institute of Standards and 
-Technology (NIST) by employees of the Federal Government in the course 
-of their official duties. Pursuant to title 17 Section 105 of the 
-United States Code, this software is not subject to copyright protection 
-and is in the public domain. NIST assumes no responsibility  whatsoever for 
-its use by other parties, and makes no guarantees, expressed or implied, 
-about its quality, reliability, or any other characteristic. 
-
-Disclaimer: 
-This software was developed to promote biometric standards and biometric
-technology testing for the Federal Government in accordance with the USA
-PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
-Specific hardware and software products identified in this software were used
-in order to perform the software development.  In no case does such
-identification imply recommendation or endorsement by the National Institute
-of Standards and Technology, nor does it imply that the products and equipment
-identified are necessarily the best available for the purpose.  
-
-*******************************************************************************/
-
-/************************************************************************/
-/***********************************************************************
-      LIBRARY: LFS - NIST Latent Fingerprint System
-
-      FILE:           ISEMPTY.C
-      AUTHOR:         Michael D. Garris
-      DATE:           09/13/2004
-
-      Contains routines responsible for determining if a fingerprint
-      image is empty.
-
-***********************************************************************
-               ROUTINES:
-                        is_image_empty()
-                        is_qmap_empty()
-
-***********************************************************************/
-
-#include <lfs.h>
-
-/***********************************************************************
-************************************************************************
-#cat: is_image_empty - Routine determines if statistics passed indicate
-#cat:                  an empty image.
-
-   Input:
-      quality_map - quality map computed by NIST's Mindtct
-      map_w       - width of map
-      map_h       - height of map
-   Return Code:
-      True        - image determined empty
-      False       - image determined NOT empty
-************************************************************************/
-int is_image_empty(int *quality_map, const int map_w, const int map_h)
-{
-   /* This routine is designed to be expanded as more statistical */
-   /* tests are developed. */
-
-   if(is_qmap_empty(quality_map, map_w, map_h))
-      return(TRUE);
-   else
-      return(FALSE);
-}
-
-/***********************************************************************
-************************************************************************
-#cat: is_qmap_empty - Routine determines if quality map is all set to zero
-
-   Input:
-      quality_map - quality map computed by NIST's Mindtct
-      map_w       - width of map
-      map_h       - height of map
-   Return Code:
-      True        - quality map is empty
-      False       - quality map is NOT empty
-************************************************************************/
-int is_qmap_empty(int *quality_map, const int map_w, const int map_h)
-{
-   int i, maplen;
-   int *qptr;
-
-   qptr = quality_map;
-   maplen = map_w * map_h;
-   for(i = 0; i < maplen; i++){
-      if(*qptr++ != 0){
-         return(FALSE);
-      }
-   }
-   return(TRUE);
-}
-
diff --git a/libfprint/nbis/mindtct/link.c b/libfprint/nbis/mindtct/link.c
deleted file mode 100644
index 2a86452..0000000
--- a/libfprint/nbis/mindtct/link.c
+++ /dev/null
@@ -1,1238 +0,0 @@
-/*******************************************************************************
-
-License: 
-This software was developed at the National Institute of Standards and 
-Technology (NIST) by employees of the Federal Government in the course 
-of their official duties. Pursuant to title 17 Section 105 of the 
-United States Code, this software is not subject to copyright protection 
-and is in the public domain. NIST assumes no responsibility  whatsoever for 
-its use by other parties, and makes no guarantees, expressed or implied, 
-about its quality, reliability, or any other characteristic. 
-
-Disclaimer: 
-This software was developed to promote biometric standards and biometric
-technology testing for the Federal Government in accordance with the USA
-PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
-Specific hardware and software products identified in this software were used
-in order to perform the software development.  In no case does such
-identification imply recommendation or endorsement by the National Institute
-of Standards and Technology, nor does it imply that the products and equipment
-identified are necessarily the best available for the purpose.  
-
-*******************************************************************************/
-
-/***********************************************************************
-      LIBRARY: LFS - NIST Latent Fingerprint System
-
-      FILE:    LINK.C
-      AUTHOR:  Michael D. Garris
-      DATE:    08/02/1999
-      UPDATED: 10/04/1999 Version 2 by MDG
-      UPDATED: 03/16/2005 by MDG
-
-      Contains routines responsible for linking compatible minutiae
-      together as part of the NIST Latent Fingerprint System (LFS).
-
-***********************************************************************
-               ROUTINES:
-                        link_minutiae()
-                        create_link_table()
-                        update_link_table()
-                        order_link_table()
-                        process_link_table()
-                        link_score()
-***********************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <lfs.h>
-#include <log.h>
-
-/*************************************************************************
-**************************************************************************
-#cat: link_minutiae - Clusters minutiae that are sufficiently close to each
-#cat:                other and have compatible directions to be considered part
-#cat:                of the same ridge or valley and then links them together.
-#cat:                In linking two minutia, the respective minutia features
-#cat:                in the image are joined by drawing pixels and the points
-#cat:                are removed from the list.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      nmap      - IMAP ridge flow matrix with invalid, high-curvature,
-                  and no-valid-neighbor regions identified
-      mw        - width in blocks of the NMAP
-      mh        - height in blocks of the NMAP
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-      bdata     - edited binary image with breaks in ridges and valleys filled
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-int link_minutiae(MINUTIAE *minutiae,
-                 unsigned char *bdata, const int iw, const int ih,
-                 int *nmap, const int mw, const int mh,
-                 const LFSPARMS *lfsparms)
-{
-   int i, ret, *onloop;
-   MINUTIA *main_min; /* Renamed by MDG on 03-16-05 */
-   int main_x, main_y;
-   int *link_table, *x_axis, *y_axis, nx_axis, ny_axis, n_entries;
-
-   print2log("\nLINKING MINUTIA:\n");
-
-   /* Go through the list of minutiae and detect any small loops (ex. */
-   /* < 15 pixels in circumference), and remove any minutiae          */
-   /* (bifurcations) with bad contours.                               */
-   if((ret = get_loop_list(&onloop, minutiae, lfsparms->small_loop_len,
-                           bdata, iw, ih)))
-      return(ret);
-
-   i = 0;
-   /* Foreach minutia treated as a "main" minutia */
-   /* (except for the very last one) ...          */
-   while(i < minutiae->num-1){
-
-      /* Set current minutia to "main" minutia. */
-      main_min = minutiae->list[i];
-      main_x = main_min->x;
-      main_y = main_min->y;
-
-      /* If the main minutia is NOT on a small loop ... */
-      if(!onloop[i]){
-
-         /* Then minutia is a ridge-ending OR a bifurcation */
-         /* which is NOT to be skipped ...                  */
-
-         /* We now want to build a connected graph (link table) of    */
-         /* all those minutiae sufficiently close and complementing   */
-         /* each other that they potentially could be merged (linked) */
-         /* together. (Ex. table has max dimensions of 20).           */
-         if((ret = create_link_table(&link_table, &x_axis, &y_axis,
-                           &nx_axis, &ny_axis, &n_entries,
-                           lfsparms->link_table_dim,
-                           i, minutiae, onloop, nmap, mw, mh,
-                           bdata, iw, ih, lfsparms))){
-            /* Deallocate working memory. */
-            free(onloop);
-            /* Return error code. */
-            return(ret);
-         }
-
-         /* Put the link table in sorted order based on x and then y-axis  */
-         /* entries.  These minutia are sorted based on their point of     */
-         /* perpendicular intersection with a line running from the origin */
-         /* at an angle equal to the average direction of all entries in   */
-         /* the link table.                                                */
-         if((ret = order_link_table(link_table, x_axis, y_axis,
-                                   nx_axis, ny_axis, n_entries,
-                                   lfsparms->link_table_dim, minutiae,
-                                   lfsparms->num_directions))){
-            /* Deallocate working memories. */
-            free(link_table);
-            free(x_axis);
-            free(y_axis);
-            free(onloop);
-            /* Return error code. */
-            return(ret);
-         }
-
-         /* Process the link table deciding which minutia pairs in  */
-         /* the table should be linked (ie. joined in the image and */
-         /* removed from the minutiae list (and from onloop).        */
-         if((ret = process_link_table(link_table, x_axis, y_axis,
-                            nx_axis, ny_axis, n_entries,
-                            lfsparms->link_table_dim,
-                            minutiae, onloop, bdata, iw, ih, lfsparms))){
-            /* Deallocate working memories. */
-            free(link_table);
-            free(x_axis);
-            free(y_axis);
-            free(onloop);
-            /* Return error code. */
-            return(ret);
-         }
-
-         /* Deallocate link table buffers. */
-         free(link_table);
-         free(x_axis);
-         free(y_axis);
-      }
-      /* Otherwise, skip minutia on a small loop. */
-
-      /* Check to see if the current "main" minutia has been removed */
-      /* from the minutiae list.  If it has not, then we need to      */
-      /* advance to the next minutia in the list.  If it has been    */
-      /* removed, then we are pointing to the next minutia already.  */
-      if((minutiae->list[i]->x == main_x) &&
-         (minutiae->list[i]->y == main_y))
-         /* Advance to the next main feature in the list. */
-         i++;
-
-      /* At this point 'i' is pointing to the next main minutia to be */
-      /* processed, so continue.                                      */
-   }
-
-   free(onloop);
-   return(0);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: create_link_table - Builds a 2D minutia link table where each cell in the
-#cat:                    table represents a potential linking of 2 different
-#cat:                    minutia points.  Minutia IDs are stored on each axes
-#cat:                    and scores representing the degree of compatibility
-#cat:                    between 2 minutia are stored in each cell.  Note that
-#cat:                    the table is sparsely filled with scores.
-
-   Input:
-      tbldim    - dimension of each axes of the link table
-      start     - index position of starting minutia point in input list
-      minutiae  - list of minutia
-      onloop    - list of loop flags (on flag for each minutia point in list)
-      nmap      - IMAP ridge flow matrix with invalid, high-curvature,
-                  and no-valid-neighbor regions identified
-      mw        - width in blocks of the NMAP
-      mh        - height in blocks of the NMAP
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      olink_table - sparse 2D table containing scores of potentially
-                    linked minutia pairs
-      ox_axis     - minutia IDs registered along x-axis
-      oy_axis     - minutia IDs registered along y-axis
-      onx_axis    - number of minutia registered along x-axis
-      ony_axis    - number of minutia registered along y-axis
-      on_entries  - number of scores currently entered in the table
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int create_link_table(int **olink_table, int **ox_axis, int **oy_axis,
-           int *onx_axis, int *ony_axis, int *on_entries, const int tbldim, 
-           const int start, const MINUTIAE *minutiae, const int *onloop,
-           int *nmap, const int mw, const int mh,
-           unsigned char *bdata, const int iw, const int ih,
-           const LFSPARMS *lfsparms)
-{
-   int ret, first, second;
-   int full_ndirs, qtr_ndirs, half_ndirs, low_curve_min_deltadir, deltadir;
-   int *link_table, *x_axis, *y_axis, nx_axis, ny_axis, n_entries, tblalloc;
-   int *queue, *inqueue, head, tail;
-   MINUTIA *minutia1, *minutia2;
-   int xblk, yblk;
-   int nmapval, opp1dir, joindir, iscore;
-   double jointheta, joindist, dscore;
-
-   /* Compute number of directions on full circle. */
-   full_ndirs = lfsparms->num_directions<<1;
-   /* Compute number of directions in 45=(180/4) degrees. */
-   qtr_ndirs = lfsparms->num_directions>>2;
-   /* Compute number of directions in 90=(180/2) degrees. */
-   half_ndirs = lfsparms->num_directions>>1;
-
-   /* Minimum allowable deltadir to link minutia in low-curvature region.   */
-   /* (The closer the deltadir is to 180 degrees, the more likely the link. */
-   /* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees.      */
-   /* I chose to parameterize this threshold based on a fixed fraction of   */
-   /* 'ndirs' rather than on passing in a parameter in degrees and doing    */
-   /* the conversion.  I doubt the difference matters.                      */
-   low_curve_min_deltadir = (3 * qtr_ndirs) - 1;
-
-   /* Allocate and initialize link table buffers.                  */
-   /* Note: The use of "calloc" initializes all table values to 0. */
-   tblalloc = tbldim * tbldim;
-   link_table = (int *)calloc(tblalloc, sizeof(int));
-   if(link_table == (int *)NULL){
-      fprintf(stderr, "ERROR : create_link_table : calloc : link_table\n");
-      return(-330);
-   }
-   /* Allocate horizontal axis entries in table. */
-   x_axis = (int *)malloc(tbldim * sizeof(int));
-   if(x_axis == (int *)NULL){
-      free(link_table);
-      fprintf(stderr, "ERROR : create_link_table : malloc : x_axis\n");
-      return(-331);
-   }
-   /* Allocate vertical axis entries in table. */
-   y_axis = (int *)malloc(tbldim * sizeof(int));
-   if(y_axis == (int *)NULL){
-      free(link_table);
-      free(x_axis);
-      fprintf(stderr, "ERROR : create_link_table : malloc : y_axis\n");
-      return(-332);
-   }
-   nx_axis = 0;
-   ny_axis = 0;
-   n_entries = 0;
-
-   /* Allocate and initalize queue buffers.  As minutia are entered into   */
-   /* the link table they are placed in the queue for subsequent matching. */
-   queue = (int *)malloc(minutiae->num * sizeof(int));
-   if(queue == (int *)NULL){
-      free(link_table);
-      free(x_axis);
-      free(y_axis);
-      fprintf(stderr, "ERROR : create_link_table : malloc : queue\n");
-      return(-333);
-   }
-   /* List of flags to indicate if a manutia has been entered in the queue. */
-   /* Once a minutia "in queue" status is set to TRUE it will not be reset. */
-   /* This way a minutia will only ever be processed as a primary minutia   */
-   /* once when builing the link table.  Note that the calloc() initializes */
-   /* the flags to FALSE (as the queue is initially empty).                 */
-   inqueue = (int *)calloc(minutiae->num, sizeof(int));
-   if(inqueue == (int *)NULL){
-      free(link_table);
-      free(x_axis);
-      free(y_axis);
-      free(queue);
-      fprintf(stderr, "ERROR : create_link_table : calloc : inqueue\n");
-      return(-334);
-   }
-   /* Initialize head and tail to start of queue. */
-   head = 0;
-   tail = 0;
-
-   /* Push the index of the "main" manutia point onto the queue. */
-   queue[tail++] = start;
-   /* Set "main" minutia inqueue flag to TRUE. */
-   inqueue[start] = TRUE;
-
-   print2log("BUILD TABLE:\n");
-
-   /* While the queue is NOT empty ... */
-   while(head != tail){
-      /* Pop the next manutia point from the queue and refer to it as */
-      /* the primary (first) minutia.                                 */
-      first = queue[head++];
-      minutia1 = minutiae->list[first];
-
-      /* Look for those minutia points that potentially match the   */
-      /* "first" minutia and add them to the link table.  These     */
-      /* potentially matching minutia are secondary and refered to  */
-      /* as the "second" minutia.  Always restart the search at the */
-      /* original "main" manutia.                                   */
-      second = start+1;
-
-      /* While secondary manutae remain to be matched to the current */
-      /* first minutia...                                            */
-      while(second < minutiae->num){
-         /* Assign second minutia to temporary pointer. */
-         minutia2 = minutiae->list[second];
-
-         print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ",
-                   first, minutia1->x, minutia1->y, minutia1->type,
-                   second, minutia2->x, minutia2->y, minutia2->type);
-
-         /* 1. If y-delta from second to first minutia is small (ex. */
-         /*    <= 20 pixels) ...                                     */
-         if((minutia2->y - minutia1->y) <= lfsparms->max_link_dist){
-
-            print2log("1DY ");
-
-
-            /* 2. If first and second minutia are not the same point ... */
-            /*    (Remeber that the search for matching seconds starts   */
-            /*    all the way back to the starting "main" minutia.)      */
-            if(first != second){
-
-               print2log("2NE ");
-
-               /* 3. If first and second minutia are the same type ...    */
-               if(minutia1->type == minutia2->type){
-
-                  print2log("3TP ");
-
-                  /* 4. If |x-delta| between minutiae is small (ex. <= */
-                  /*    20 pixels) ...                                 */
-                  if(abs(minutia1->x - minutia2->x) <=
-                         lfsparms->max_link_dist){
-
-                     print2log("4DX ");
-
-                     /* 5. If second minutia is NOT on a small loop ... */
-                     if(!onloop[second]){
-
-                        print2log("5NL ");
-
-                        /* The second minutia is ridge-ending OR a */
-                        /* bifurcation NOT to be skipped ...       */
-
-                        /* Compute the "inner" distance between the */
-                        /* first and second minutia's directions.   */
-                        deltadir = closest_dir_dist(minutia1->direction,
-                                       minutia2->direction, full_ndirs);
-                        /* If the resulting direction is INVALID           */
-                        /* (this should never happen, but just in case)... */
-                        if(deltadir == INVALID_DIR){
-                           free(link_table);
-                           free(x_axis);
-                           free(y_axis);
-                           free(queue);
-                           free(inqueue);
-                           /* Then there is a problem. */
-                           fprintf(stderr,
-                           "ERROR : create_link_table : INVALID direction\n");
-                           return(-335);
-                        }
-
-                        /* Compute first minutia's block coords from */
-                        /* its pixel coords.                         */
-                        xblk = minutia1->x/lfsparms->blocksize;
-                        yblk = minutia1->y/lfsparms->blocksize;
-                        /* Get corresponding block's NMAP value. */
-                        /*    -3 == NO_VALID_NBRS                */
-                        /*    -2 == HIGH_CURVATURE               */
-                        /*    -1 == INVALID_DIR                  */
-                        /*     0 <= VALID_DIR                    */
-                        nmapval = *(nmap+(yblk*mw)+xblk);
-                        /* 6. CASE I: If block has VALID_DIR and deltadir    */
-                        /*            relatively close to 180 degrees (at    */
-                        /*            least 123.75 deg when ndirs==16)...    */
-                        /*    OR                                             */
-                        /*    CASE II: If block is HIGH_CURVATURE and        */
-                        /*            deltadir is at least 45 degrees...     */
-                        if(((nmapval >= 0) &&
-                            (deltadir >= low_curve_min_deltadir)) ||
-                           ((nmapval == HIGH_CURVATURE) &&
-                            (deltadir >= qtr_ndirs))){
-
-                           print2log("6DA ");
-
-                           /* Then compute direction of "joining" vector. */
-                           /* First, compute direction of line from first */
-                           /* to second minutia points.                   */
-                           joindir = line2direction(minutia1->x, minutia1->y,
-                                                    minutia2->x, minutia2->y,
-                                                    lfsparms->num_directions);
-
-                           /* Comptue opposite direction of first minutia. */
-                           opp1dir = (minutia1->direction+
-                                      lfsparms->num_directions)%full_ndirs;
-                           /* Take "inner" distance on full circle between */
-                           /* the first minutia's opposite direction and   */
-                           /* the joining direction.                       */
-                           joindir = abs(opp1dir - joindir);
-                           joindir = min(joindir, full_ndirs - joindir);
-                           /* 7. If join angle is <= 90 deg... */
-                           if(joindir <= half_ndirs){
-
-                              print2log("7JA ");
-
-                              /* Convert integer join direction to angle  */
-                              /* in radians on full circle.  Multiply     */
-                              /* direction by (2PI)/32==PI/16 radians per */
-                              /* unit direction and you get radians.      */
-                              jointheta = joindir *
-                                    (M_PI/(double)lfsparms->num_directions);
-                              /* Compute squared distance between frist   */
-                              /* and second minutia points.               */
-                              joindist = distance(minutia1->x, minutia1->y,
-                                                  minutia2->x, minutia2->y);
-                              /* 8. If the 2 minutia points are close enough */
-                              /*    (ex. thresh == 20 pixels)...             */
-                              if(joindist <= lfsparms->max_link_dist){
-
-                                 print2log("8JD ");
-
-                                 /* 9. Does a "free path" exist between the */
-                                 /*    2 minutia points?                    */
-                                 if(free_path(minutia1->x, minutia1->y,
-                                              minutia2->x, minutia2->y,
-                                              bdata, iw, ih, lfsparms)){
-
-                                    print2log("9FP ");
-
-                                    /* If the join distance is very small,   */
-                                    /* join theta will be unreliable, so set */
-                                    /* join theta to zero.                   */
-                                    /* (ex. thresh == 5 pixels)...           */
-                                    if(joindist < lfsparms->min_theta_dist)
-                                       /* Set the join theta to zero. */
-                                       jointheta = 0.0;
-                                    /* Combine the join theta and distance */
-                                    /* to compute a link score.            */
-                                    dscore = link_score(jointheta, joindist,
-                                                       lfsparms);
-                                    /* Round off the floating point score.  */
-                                    /* Need to truncate so answers are same */
-                                    /* on different computers.              */
-                                    dscore = trunc_dbl_precision(dscore,
-                                                             TRUNC_SCALE);
-                                    iscore = sround(dscore);
-                                    /* Add minutia pair and their link score */
-                                    /* to the Link Table.                    */
-
-                                    if(iscore > 0){
-                                       print2log("UPDATE");
-
-                                       if((ret = update_link_table(link_table,
-                                          x_axis, y_axis, &nx_axis, &ny_axis,
-                                          &n_entries, tbldim,
-                                          queue, &head, &tail, inqueue, 
-                                          first, second, iscore))){
-                                             /* If update ERROR, deallocate */
-                                             /* working memories.           */
-                                             free(link_table);
-                                             free(x_axis);
-                                             free(y_axis);
-                                             free(queue);
-                                             free(inqueue);
-                                             return(ret);
-                                       }
-                                    } /* Else score is <= 0, so skip second. */
-                                 } /* 9. Else no free path, so skip second. */
-                              } /* 8. Else joindist too big, so skip second. */
-                           } /* 7. Else joindir is too big, so skip second. */
-                        } /* 6. Else INVALID DIR or deltadir too small, */
-                          /* so skip second.                         */
-                     } /* 5. Else second minutia on small loop, so skip it. */
-                  } /* 4. Else X distance too big, so skip second. */
-               } /* 3. Else first and second NOT same type, so skip second. */
-            } /* 2. Else first and second ARE same point, so skip second. */
-
-            /* If we get here, we want to advance to the next secondary. */
-            second++;
-
-            print2log("\n");
-
-
-         }
-         /* 1. Otherwise, Y distnace too big, so we are done searching for */
-         /*    secondary matches to the current frist minutia.  It is time */
-         /*    to take the next minutia in the queue and begin matching    */
-         /*    secondaries to it.                                          */
-         else{
-
-            print2log("\n");
-
-            /* So, break out of the secondary minutiae while loop. */
-            break;
-         }
-      }
-      /* Done matching current first minutia to secondaries. */
-   }
-   /* Get here when queue is empty, and we have our complete link table. */
-
-   /* Deallocate working memories. */
-   free(queue);
-   free(inqueue);
-
-   /* Assign link table buffers and attributes to output pointers. */
-   *olink_table = link_table;
-   *ox_axis = x_axis;
-   *oy_axis = y_axis;
-   *onx_axis = nx_axis;
-   *ony_axis = ny_axis;
-   *on_entries = n_entries;
-
-   /* Return normally. */
-   return(0);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: update_link_table - Takes the indices of 2 minutia and their link
-#cat:                    compatibility score and updates the 2D link table.
-#cat:                    The input minutia are registered to positions along
-#cat:                    different axes, if they are not already in the table,
-#cat:                    and a queue is maintained so that a cluster of
-#cat:                    potentially linked points may be gathered.
-
-   Input:
-      link_table - sparse 2D table containing scores of potentially linked
-                   minutia pairs
-      x_axis     - minutia IDs registered along x-axis
-      y_axis     - minutia IDs registered along y-axis
-      nx_axis    - number of minutia registered along x-axis
-      ny_axis    - number of minutia registered along y-axis
-      n_entries  - number of scores currently entered in the table
-      tbldim     - dimension of each axes of the link table
-      queue      - list of clustered minutiae yet to be used to locate
-                   other compatible minutiae
-      head       - head of the queue
-      tail       - tail of the queue
-      inqueue    - flag for each minutia point in minutiae list to signify if
-                   it has been clustered with the points in this current link
-                   table
-      first      - index position of first minutia of current link pair
-      second     - index position of second minutia of current link pair
-      score      - degree of link compatibility of current link pair
-   Output:
-      link_table - updated sparse 2D table containing scores of potentially
-                   linked minutia pairs
-      x_axis     - updated minutia IDs registered along x-axis
-      y_axis     - updated minutia IDs registered along y-axis
-      nx_axis    - updated number of minutia registered along x-axis
-      ny_axis    - updated number of minutia registered along y-axis
-      n_entries  - updated number of scores currently entered in the table
-      queue      - updated list of clustered minutiae yet to be used to locate
-                   other compatible minutiae
-      tail       - updated tail of the queue
-      inqueue    - updated list of flags, one for each minutia point in
-                   minutiae list to signify if it has been clustered with
-                   the points in this current link table
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int update_link_table(int *link_table, int *x_axis, int *y_axis,
-                 int *nx_axis, int *ny_axis, int *n_entries, const int tbldim,
-                 int *queue, int *head, int *tail, int *inqueue,
-                 const int first, const int second, const int score)
-{
-   int x, y, *tptr;
-
-   /* If the link table is empty. */
-   if(*n_entries == 0){
-      /* Add first and second minutia to the table. */
-
-      /* If horizontal axis of table is full ... */
-      if(*nx_axis >= tbldim)
-         /* Then ignore the minutia pair and return normally. */
-         return(0);
-      /* Add first minutia to horizontal axis. */
-      x_axis[*nx_axis] = first;
-
-      /* If vertical axis of table is full ... */
-      if(*ny_axis >= tbldim)
-         /* Then ignore the minutia pair and return normally. */
-         return(0);
-      /* Add second minutia to vertical axis. */
-      y_axis[*ny_axis] = second;
-
-      /* Enter minutia pair score to the link table. */
-      tptr = link_table + ((*ny_axis)*tbldim) + (*nx_axis);
-      *tptr = score;
-      (*n_entries)++;
-
-      /* Bump number of entries in each axis. */
-      (*nx_axis)++;
-      (*ny_axis)++;
-
-      /* Add second minutia to queue (if not already in queue), so it */
-      /* can be processed to see who might link to it.                */
-      if(!inqueue[second]){
-         queue[*tail] = second;
-         (*tail)++;
-         inqueue[second] = TRUE;
-      }
-
-      /* Done, so return normally. */
-      return(0);
-   }
-
-   /* We are filling in the table with a "faimily" or "cluster" of  */
-   /* potentially inter-linked points.  Once the first entry is     */
-   /* made in the table, all subsequent updates will be based on    */
-   /* at least the first minutia already being in the table.        */
-
-   /* If first minutia already stored in horizontal axis */
-   /* of the link table.                                 */
-   if((x = in_int_list(first, x_axis, *nx_axis)) >= 0){
-      /* If second minutia already stored in vertical axis */
-      /* of the link table.                                */
-      if((y = in_int_list(second, y_axis, *ny_axis)) >= 0){
-         /* Entry may not be set or the new score may be larger. */
-         tptr = link_table + (y*tbldim) + x;
-         if(*tptr == 0)
-            /* Assign the minutia pair score to the table. */
-            *tptr = score;
-
-      }
-      /* Otherwise, second minutia not in vertical axis of link table. */
-      else{
-         /* Add the second minutia to the vertical axis and */
-         /* the minutia pair's score to the link table.     */
-
-         /* If vertical axis of table is full ... */
-         if(*ny_axis >= tbldim)
-            /* Then ignore the minutia pair and return normally. */
-            return(0);
-         /* Add second minutia to vertical axis. */
-         y_axis[*ny_axis] = second;
-
-         /* Enter minutia pair score to the link table. */
-         tptr = link_table + ((*ny_axis)*tbldim) + x;
-         *tptr = score;
-         (*n_entries)++;
-
-         /* Bump number of entries in vertical axis. */
-         (*ny_axis)++;
-
-         /* Add second minutia to queue (if not already in queue), so it */
-         /* can be processed to see who might link to it.                */
-         if(!inqueue[second]){
-            queue[*tail] = second;
-            (*tail)++;
-            inqueue[second] = TRUE;
-         }
-      }
-   }
-   /* Otherwise, first minutia not in horizontal axis of link table. */
-   else{
-      /* If first minutia already stored in vertical axis */
-      /* of the link table.                               */
-      if((y = in_int_list(first, y_axis, *ny_axis)) >= 0){
-         /* If second minutia already stored in horizontal axis */
-         /* of the link table.                                  */
-         if((x = in_int_list(second, x_axis, *nx_axis)) >= 0){
-            /* Entry may not be set or the new score may be larger. */
-            tptr = link_table + (y*tbldim) + x;
-            if(*tptr == 0)
-               /* Assign the minutia pair score to the table. */
-               *tptr = score;
-         }
-         /* Otherwise, second minutia not in horizontal axis of link table. */
-         else{
-            /* Add the second minutia to the horizontal axis and */
-            /* the minutia pair's score to the link table.       */
-
-            /* If horizontal axis of table is full ... */
-            if(*nx_axis >= tbldim)
-               /* Then ignore the minutia pair and return normally. */
-               return(0);
-            /* Add second minutia to vertical axis. */
-            x_axis[*nx_axis] = second;
-
-            /* Enter minutia pair score to the link table. */
-            tptr = link_table + (y*tbldim) + (*nx_axis);
-            *tptr = score;
-            (*n_entries)++;
-
-            /* Bump number of entries in horizontal axis. */
-            (*nx_axis)++;
-
-            /* Add second minutia to queue (if not already in queue), so it */
-            /* can be processed to see who might link to it.                */
-            if(!inqueue[second]){
-               queue[*tail] = second;
-               (*tail)++;
-               inqueue[second] = TRUE;
-            }
-         }
-      }
-      /* Otherwise, first minutia not in vertical or horizontal axis of */
-      /* link table.  This is an error, as this should only happen upon */
-      /* the first point being entered, which is already handled above. */
-      else{
-         fprintf(stderr,
-            "ERROR : update_link_table : first minutia not found in table\n");
-         return(-340);
-      }
-   }
-
-   /* Done, so return normally. */
-   return(0);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: order_link_table - Puts the link table in sorted order based on x and 
-#cat:                    then y-axis entries.  These minutia are sorted based
-#cat:                    on their point of perpendicular intersection with a
-#cat:                    line running from the origin at an angle equal to the
-#cat:                    average direction of all entries in the link table.
-
-   Input:
-      link_table - sparse 2D table containing scores of potentially linked
-                   minutia pairs
-      x_axis     - minutia IDs registered along x-axis
-      y_axis     - minutia IDs registered along y-axis
-      nx_axis    - number of minutia registered along x-axis
-      ny_axis    - number of minutia registered along y-axis
-      n_entries  - number of scores currently entered in the table
-      tbldim     - dimension of each axes of the link table
-      minutiae   - list of minutia
-      ndirs      - number of IMAP directions (in semicircle)
-   Output:
-      link_table - sorted sparse 2D table containing scores of potentially
-                   linked minutia pairs
-      x_axis     - sorted minutia IDs registered along x-axis
-      y_axis     - sorted minutia IDs registered along y-axis
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int order_link_table(int *link_table, int *x_axis, int *y_axis,
-                const int nx_axis, const int ny_axis, const int n_entries,
-                const int tbldim, const MINUTIAE *minutiae,
-                const int ndirs)
-{
-   int i, j, ret, sumdir, avrdir, *order;
-   double davrdir, avrtheta, pi_factor, cs, sn;
-   double *dlist;
-   MINUTIA *minutia;
-   int *tlink_table, *tx_axis, *ty_axis, tblalloc;
-   int *toptr, *frptr;
-
-   /* If the table is empty or if there is only one horizontal or */
-   /* vertical entry in the  table ...                            */
-   if((nx_axis <= 1) || (ny_axis <= 1))
-      /* Then don't reorder the table, just return normally. */
-      return(0);
-
-   /* Compute average direction (on semi-circle) of all minutia entered */
-   /* in the link table.  This gives an average ridge-flow direction    */
-   /* among all the potentially linked minutiae in the table.            */
-
-   /* Initialize direction accumulator to 0. */
-   sumdir = 0;
-
-   /* Accumulate directions (on semi-circle) of minutia entered in the */
-   /* horizontal axis of the link table.                               */
-   for(i = 0; i < nx_axis; i++)
-      sumdir += (minutiae->list[x_axis[i]]->direction % ndirs);
-
-   /* Accumulate directions of minutia entered in the vertical axis */
-   /* of the link table.                                            */
-   for(i = 0; i < ny_axis; i++)
-      sumdir += (minutiae->list[y_axis[i]]->direction % ndirs);
-
-   /* Compute the average direction and round off to integer. */
-   davrdir = (sumdir / (double)(nx_axis + ny_axis));
-   /* Need to truncate precision so that answers are consistent */
-   /* on different computer architectures when rounding doubles. */
-   davrdir = trunc_dbl_precision(davrdir, TRUNC_SCALE);
-   avrdir = sround(davrdir);
-
-   /* Conversion factor from integer directions to radians. */
-   pi_factor = M_PI / (double)ndirs;
-
-   /* Compute sine and cosine of average direction in radians. */
-   avrtheta = avrdir*pi_factor;
-   sn = sin(avrtheta);
-   cs = cos(avrtheta);
-
-   /* Allocate list to hold distances to be sorted on. */
-   dlist = (double *)malloc(tbldim * sizeof(double));
-   if(dlist == (double *)NULL){
-      fprintf(stderr, "ERROR : order_link_table : malloc : dlist\n");
-      return(-350);
-   }
-
-   /* Allocate and initialize temporary link table buffers. */
-   tblalloc = tbldim * tbldim;
-   tlink_table = (int *)calloc(tblalloc, sizeof(int));
-   if(tlink_table == (int *)NULL){
-      free(dlist);
-      fprintf(stderr, "ERROR : order_link_table : calloc : tlink_table\n");
-      return(-351);
-   }
-   tx_axis = (int *)malloc(tbldim * sizeof(int));
-   if(tx_axis == (int *)NULL){
-      free(dlist);
-      free(tlink_table);
-      fprintf(stderr, "ERROR : order_link_table : malloc : tx_axis\n");
-      return(-352);
-   }
-   ty_axis = (int *)malloc(tbldim * sizeof(int));
-   if(ty_axis == (int *)NULL){
-      free(dlist);
-      free(tlink_table);
-      free(tx_axis);
-      fprintf(stderr, "ERROR : order_link_table : malloc : ty_axis\n");
-      return(-353);
-   }
-
-   /* Compute distance measures for each minutia entered in the  */
-   /* horizontal axis of the link table.                         */
-   /* The measure is: dist = X*cos(avrtheta) + Y*sin(avrtheta)   */
-   /* which measures the distance from the origin along the line */
-   /* at angle "avrtheta" to the point of perpendicular inter-   */
-   /* section from the point (X,Y).                              */
-
-   /* Foreach minutia in horizontal axis of the link table ... */
-   for(i = 0; i < nx_axis; i++){
-      minutia = minutiae->list[x_axis[i]];
-      dlist[i] = (minutia->x * cs) + (minutia->y * sn);
-      /* Need to truncate precision so that answers are consistent */
-      /* on different computer architectures when rounding doubles. */
-      dlist[i] = trunc_dbl_precision(dlist[i], TRUNC_SCALE);
-   }
-
-   /* Get sorted order of distance for minutiae in horizontal axis. */
-   if((ret = sort_indices_double_inc(&order, dlist, nx_axis))){
-      free(dlist);
-      return(ret);
-   }
-
-   /* Store entries on y_axis into temporary list. */
-   memcpy(ty_axis, y_axis, ny_axis * sizeof(int));
-
-   /* For each horizontal entry in link table ... */
-   for(i = 0; i < nx_axis; i++){
-      /* Store next minutia in sorted order to temporary x_axis. */
-      tx_axis[i] = x_axis[order[i]];
-      /* Store corresponding column of scores into temporary table. */
-      frptr = link_table + order[i];
-      toptr = tlink_table + i;
-      for(j = 0; j < ny_axis; j++){
-         *toptr = *frptr;
-         toptr += tbldim;
-         frptr += tbldim;
-      }
-   }
-
-   /* Deallocate sorted order of distance measures. */
-   free(order);
-
-   /* Compute distance measures for each minutia entered in the  */
-   /* vertical axis of the temporary link table (already sorted  */
-   /* based on its horizontal axis entries.                      */
-
-   /* Foreach minutia in vertical axis of the link table ... */
-   for(i = 0; i < ny_axis; i++){
-      minutia = minutiae->list[y_axis[i]];
-      dlist[i] = (minutia->x * cs) + (minutia->y * sn);
-      /* Need to truncate precision so that answers are consistent */
-      /* on different computer architectures when rounding doubles. */
-      dlist[i] = trunc_dbl_precision(dlist[i], TRUNC_SCALE);
-   }
-
-   /* Get sorted order of distance for minutiae in vertical axis. */
-   if((ret = sort_indices_double_inc(&order, dlist, ny_axis))){
-      free(dlist);
-      return(ret);
-   }
-
-   /* Store entries in temporary x_axis. */
-   memcpy(x_axis, tx_axis, nx_axis * sizeof(int));
-
-   /* For each vertical entry in the temporary link table ... */
-   for(i = 0; i < ny_axis; i++){
-      /* Store next minutia in sorted order to y_axis. */
-      y_axis[i] = ty_axis[order[i]];
-      /* Store corresponding row of scores into link table. */
-      frptr = tlink_table + (order[i] * tbldim);
-      toptr = link_table + (i * tbldim);
-      for(j = 0; j < nx_axis; j++){
-         *toptr++ = *frptr++;
-      }
-   }
-
-   /* Deallocate sorted order of distance measures. */
-   free(order);
-
-   /* Link table is now sorted on x and y axes. */
-   /* Deallocate the working memories. */
-   free(dlist);
-   free(tlink_table);
-   free(tx_axis);
-   free(ty_axis);
-
-   /* Return normally. */
-   return(0);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: process_link_table - Processes the link table deciding which minutia
-#cat:                      pairs in the table should be linked (ie. joined in
-#cat:                      the image and removed from the minutiae list (and
-#cat:                      from onloop).
-
-   Input:
-      link_table - sparse 2D table containing scores of potentially linked
-                   minutia pairs
-      x_axis     - minutia IDs registered along x-axis
-      y_axis     - minutia IDs registered along y-axis
-      nx_axis    - number of minutia registered along x-axis
-      ny_axis    - number of minutia registered along y-axis
-      n_entries  - number of scores currently entered in the table
-      tbldim     - dimension of each axes of the link table
-      minutiae   - list of minutia
-      onloop     - list of flags signifying which minutia lie on small lakes
-      bdata      - binary image data (0==while & 1==black)
-      iw         - width (in pixels) of image
-      ih         - height (in pixels) of image
-      lfsparms   - parameters and thresholds for controlling LFS
-   Output:
-      minutiae   - list of pruned minutiae
-      onloop     - updated loop flags
-      bdata      - edited image with minutia features joined
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int process_link_table(const int *link_table,
-                 const int *x_axis, const int *y_axis,
-                 const int nx_axis, const int ny_axis, const int n_entries,
-                 const int tbldim, MINUTIAE *minutiae, int *onloop,
-                 unsigned char *bdata, const int iw, const int ih,
-                 const LFSPARMS *lfsparms)
-{
-   int i, j, ret, first, second;
-   MINUTIA *minutia1, *minutia2;
-   int rm1, rm2;
-   int *to_remove;
-   int n_lines, line_len, entry_incr, line_incr;
-   int line_i, entry_i;
-   int start, end;
-   int max_v, max_tbl_i, max_line_i, max_x, max_y;
-
-
-   print2log("LINKING FROM TABLE:\n");
-
-   /* If link table is empty, just return normally. */
-   if(n_entries == 0)
-      return(0);
-
-   /* If there is only 1 entry in the table, then join the minutia pair. */
-   if(n_entries == 1){
-      /* Join the minutia pair in the image. */
-      first = x_axis[0];
-      second = y_axis[0];
-      minutia1 = minutiae->list[first];
-      minutia2 = minutiae->list[second];
-
-      /* Connect the points with a line with specified radius (ex. 1 pixel). */
-      if((ret = join_minutia(minutia1, minutia2, bdata, iw, ih,
-                            WITH_BOUNDARY, lfsparms->join_line_radius)))
-         return(ret);
-
-      /* Need to remove minutiae from onloop first, as onloop is dependent */
-      /* on the length of the minutiae list.  We also need to remove the   */
-      /* highest index from the lists first or the indices will be off.   */
-      if(first > second){
-         rm1 = first;
-         rm2 = second;
-      }
-      else{
-         rm1 = second;
-         rm2 = first;
-      }
-      if((ret = remove_from_int_list(rm1, onloop, minutiae->num)))
-         return(ret);
-      if((ret = remove_from_int_list(rm2, onloop, minutiae->num-1)))
-         return(ret);
-
-      /* Now, remove the minutia from the minutiae list. */
-      if((ret = remove_minutia(rm1, minutiae)))
-         return(ret);
-      if((ret = remove_minutia(rm2, minutiae)))
-         return(ret);
-
-      /* Return normally. */
-      return(0);
-   }
-
-   /* Otherwise, we need to make decisions as to who links to who. */
-
-   /* Allocate list of minutia indices that upon completion of linking */
-   /* should be removed from the onloop and minutiae lists.  Note: That */
-   /* using "calloc" initializes the list to FALSE.                    */
-   to_remove = (int *)calloc(minutiae->num, sizeof(int));
-   if(to_remove == (int *)NULL){
-      fprintf(stderr, "process_link_table : calloc : to_remove\n");
-      return(-360);
-   }
-
-   /* If the number of horizontal entries is <= vertical entries ... */
-   if(nx_axis <= ny_axis){
-      /* Process columns in table as lines. */
-      n_lines = nx_axis;
-      /* Set length of each line to number of vertical entries. */
-      line_len = ny_axis;
-      /* Increment down column to next entry in line. */
-      entry_incr = tbldim;
-      /* Increment across row to next line. */
-      line_incr = 1;
-   }
-   /* Otherwise, the number of vertical entreis < horizontal entries ... */
-   else{
-      /* Process rows in table as lines. */
-      n_lines = ny_axis;
-      /* Set length of each line to number of horizontal entries. */
-      line_len = nx_axis;
-      /* Increment across row to next entry in line. */
-      entry_incr = 1;
-      /* Increment down column to next line. */
-      line_incr = tbldim;
-   }
-
-   /* Set start of next line index to origin of link table. */
-   line_i = 0;
-
-   /* Initialize the search limits for the line ... */
-   start = 0;
-   end = line_len - n_lines + 1;
-
-   /* Foreach line in table ... */
-   for(i = 0; i < n_lines; i++){
-
-      /* Find max score in the line given current search limits. */
-
-      /* Set table entry index to start of next line. */
-      entry_i = line_i;
-
-      /* Initialize running maximum with score in current line */
-      /* at offset 'start'.                                    */
-      entry_i += (start*entry_incr);
-
-      /* Set running maximum score. */
-      max_v = link_table[entry_i];
-      /* Set table index of maximum score. */
-      max_tbl_i = entry_i;
-      /* Set line index of maximum score. */
-      max_line_i = start;
-
-      /* Advance table entry index along line. */
-      entry_i += entry_incr;
-      /* Foreach successive entry in line up to line index 'end' ... */
-
-      for(j = start+1; j < end; j++){
-         /* If current entry > maximum score seen so far ... */
-         if(link_table[entry_i] >= max_v){
-            /* Store current entry as new maximum. */
-            max_v = link_table[entry_i];
-            max_tbl_i = entry_i;
-            max_line_i = j;
-         }
-         /* Advance table entry index along line. */
-         entry_i += entry_incr;
-      }
-
-      /* Convert entry index at maximum to table row and column indices. */
-      max_x = max_tbl_i % tbldim;
-      max_y = max_tbl_i / tbldim;
-
-      /* Set indices and pointers corresponding to minutia pair */
-      /* with maximum score this pass.                          */
-      first = x_axis[max_x];
-      second = y_axis[max_y];
-      minutia1 = minutiae->list[first];
-      minutia2 = minutiae->list[second];
-
-      /* Check to make sure the the maximum score found in the current */
-      /* line is > 0 (just to be safe)  AND                            */
-      /* If a "free path" exists between minutia pair ...              */
-      if( /* (max_v >0) && */
-         free_path(minutia1->x, minutia1->y, minutia2->x, minutia2->y,
-                   bdata, iw, ih, lfsparms)){
-
-         print2log("%d,%d to %d,%d LINK\n",
-                    minutia1->x, minutia1->y, minutia2->x, minutia2->y);
-
-         /* Join the minutia pair in the image. */
-         if((ret = join_minutia(minutia1, minutia2, bdata, iw, ih,
-                               WITH_BOUNDARY, lfsparms->join_line_radius))){
-            free(to_remove);
-            return(ret);
-         }
-
-         /* Set remove flags for minutia pair.  A minutia point may  */
-         /* be linked to more than one other minutia in this process */
-         /* so, just flag them to be removed for now and actually    */
-         /* conduct the removal after all linking is complete.       */
-         to_remove[first] = TRUE;
-         to_remove[second] = TRUE;
-
-      }
-      /* Set starting line index to one passed maximum found this pass. */
-      start = max_line_i + 1;
-      /* Bump ending line index. */
-      end++;
-
-      /* Advance start of line index to next line. */
-      line_i += line_incr;
-
-   } /* End for lines */
-
-   /* Now that all linking from the current table is complete,     */
-   /* remove any linked minutia from the onloop and minutiae lists. */
-   /* NOTE: Need to remove the minutia from their lists in reverse */
-   /*       order, otherwise, indices will be off.                 */
-   for(i = minutiae->num-1; i >= 0; i--){
-      /* If the current minutia index is flagged for removal ... */
-      if(to_remove[i]){
-         /* Remove the minutia from the onloop list before removing  */
-         /* from minutiae list as the length of onloop depends on the */
-         /* length of the minutiae list.                              */
-         if((ret = remove_from_int_list(i, onloop, minutiae->num))){
-            free(to_remove);
-            return(ret);
-         }
-         /* Now, remove the minutia from the minutiae list. */
-         if((ret = remove_minutia(i, minutiae))){
-            free(to_remove);
-            return(ret);
-         }
-      }
-   }
-
-   /* Deallocate remove list. */
-   free(to_remove);
-
-   /* Return normally. */
-   return(0);
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: link_score - Takes 2 parameters, a 'join angle' and a 'join distance'
-#cat:              computed between 2 minutia and combines these to compute
-#cat:              a score representing the degree of link compatibility
-#cat:              between the 2 minutiae.
-
-   Input:
-      jointheta - angle measured between 2 minutiae
-      joindist  - distance between 2 minutiae
-      lfsparms  - parameters and thresholds for controlling LFS
-   Return Code:
-      Score     - degree of link compatibility
-**************************************************************************/
-double link_score(const double jointheta, const double joindist,
-                  const LFSPARMS *lfsparms)
-{
-   double score, theta_factor, dist_factor;
-
-   /* Calculates a weighted score between the distance between the 1st &   */
-   /* 2nd features and the delta angle between the reflected 1st feature   */
-   /* and the "joining" vector to the 2nd feature.                         */
-   /*                                                                      */
-   /*                       SCORE_NUMERATOR                                */
-   /* SCORE =  ------------------------------------------                  */
-   /*                  THETA_FACTOR * DIST_FACTOR                          */
-   /*                                                                      */
-   /* THETA_FACTOR = exp((jointheta/SCORE_THETA_NORM)^2)                   */
-   /* DIST_FACTOR  = (1+exp(((joindist/SCORE_DIST_NORM)-1)                 */
-   /*                        *SCORE_DIST_WEIGHT))                          */
-   /*                                                                      */
-   /* For example:                                                         */
-   /*          SCORE_NUMERATOR   = 32000.0                                 */
-   /*          SCORE_THETA_NORM  =   15.0*(PI/180)                         */
-   /*          SCORE_DIST_NORM   =   10.0                                  */
-   /*          SCORE_DIST_WEIGHT =    4.0                                  */
-   /*                                                                      */
-   /*                                     3200.0                           */
-   /* SCORE =  ----------------------------------------------------------- */
-   /*          exp((jointheta/15.0)^2) * (1+exp(((joindist/10.0)-1.0)*4.0))*/
-   /*                                                                      */
-   /*         In this case, increases in distance drops off faster than    */
-   /*         in theta.                                                    */
-
-   /* Compute the THETA_FACTOR. */
-   theta_factor = jointheta / (lfsparms->score_theta_norm * DEG2RAD);
-   theta_factor = exp(theta_factor * theta_factor);
-   /* Compute the DIST_FACTOR. */
-   dist_factor = 1.0+exp(((joindist/lfsparms->score_dist_norm)-1.0) *
-                 lfsparms->score_dist_weight);
-
-   /* Compute the floating point score. */
-   score = lfsparms->score_numerator / (theta_factor * dist_factor);
-
-   /* Return the floating point score. */
-   return(score);
-}
-
diff --git a/libfprint/nbis/mindtct/loop.c b/libfprint/nbis/mindtct/loop.c
index 9e15e95..da5de0e 100644
--- a/libfprint/nbis/mindtct/loop.c
+++ b/libfprint/nbis/mindtct/loop.c
@@ -36,6 +36,8 @@ identified are necessarily the best available for the purpose.
 
 ***********************************************************************
                ROUTINES:
+                        chain_code_loop()
+                        is_chain_clockwise()
                         get_loop_list()
                         on_loop()
                         on_island_lake()
@@ -52,6 +54,155 @@ identified are necessarily the best available for the purpose.
 #include <stdlib.h>
 #include <lfs.h>
 
+/*************************************************************************
+**************************************************************************
+#cat: chain_code_loop - Converts a feature's contour points into an
+#cat:            8-connected chain code vector.  This encoding represents
+#cat:            the direction taken between each adjacent point in the
+#cat:            contour.  Chain codes may be used for many purposes, such
+#cat:            as computing the perimeter or area of an object, and they
+#cat:            may be used in object detection and recognition.
+
+   Input:
+      contour_x - x-coord list for feature's contour points
+      contour_y - y-coord list for feature's contour points
+      ncontour  - number of points in contour
+   Output:
+      ochain    - resulting vector of chain codes
+      onchain   - number of codes in chain
+                  (same as number of points in contour)
+   Return Code:
+      Zero      - chain code successful derived
+      Negative  - system error
+**************************************************************************/
+static int chain_code_loop(int **ochain, int *onchain,
+               const int *contour_x, const int *contour_y, const int ncontour)
+{
+   int *chain;
+   int i, j, dx, dy;
+
+   /* If we don't have at least 3 points in the contour ... */
+   if(ncontour <= 3){
+      /* Then we don't have a loop, so set chain length to 0 */
+      /* and return without any allocations.                 */
+      *onchain = 0;
+      return(0);
+   }
+
+   /* Allocate chain code vector.  It will be the same length as the */
+   /* number of points in the contour.  There will be one chain code */
+   /* between each point on the contour including a code between the */
+   /* last to the first point on the contour (completing the loop).  */
+   chain = (int *)malloc(ncontour * sizeof(int));
+   /* If the allocation fails ... */
+   if(chain == (int *)NULL){
+      fprintf(stderr, "ERROR : chain_code_loop : malloc : chain\n");
+      return(-170);
+   }
+
+   /* For each neighboring point in the list (with "i" pointing to the */
+   /* previous neighbor and "j" pointing to the next neighbor...       */
+   for(i = 0, j=1; i < ncontour-1; i++, j++){
+      /* Compute delta in X between neighbors. */
+      dx = contour_x[j] - contour_x[i];
+      /* Compute delta in Y between neighbors. */
+      dy = contour_y[j] - contour_y[i];
+      /* Derive chain code index from neighbor deltas.                  */
+      /* The deltas are on the range [-1..1], so to use them as indices */
+      /* into the code list, they must first be incremented by one.     */
+      chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
+   }
+
+   /* Now derive chain code between last and first points in the */
+   /* contour list.                                              */
+   dx = contour_x[0] - contour_x[i];
+   dy = contour_y[0] - contour_y[i];
+   chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
+
+   /* Store results to the output pointers. */
+   *ochain = chain;
+   *onchain = ncontour;
+
+   /* Return normally. */
+   return(0);
+}   
+
+/*************************************************************************
+**************************************************************************
+#cat: is_chain_clockwise - Takes an 8-connected chain code vector and
+#cat:            determines if the codes are ordered clockwise or
+#cat:            counter-clockwise.
+#cat:            The routine also requires a default return value be
+#cat:            specified in the case the the routine is not able to
+#cat:            definitively determine the chains direction.  This allows
+#cat:            the default response to be application-specific.
+
+   Input:
+      chain       - chain code vector
+      nchain      - number of codes in chain
+      default_ret - default return code (used when we can't tell the order)
+   Return Code:
+      TRUE      - chain determined to be ordered clockwise
+      FALSE     - chain determined to be ordered counter-clockwise
+      Default   - could not determine the order of the chain
+**************************************************************************/
+static int is_chain_clockwise(const int *chain, const int nchain,
+                       const int default_ret)
+{
+   int i, j, d, sum;
+
+   /* Initialize turn-accumulator to 0. */
+   sum = 0;
+
+   /* Foreach neighboring code in chain, compute the difference in  */
+   /* direction and accumulate.  Left-hand turns increment, whereas */
+   /* right-hand decrement.                                         */
+   for(i = 0, j =1; i < nchain-1; i++, j++){
+      /* Compute delta in neighbor direction. */
+      d = chain[j] - chain[i];
+      /* Make the delta the "inner" distance. */
+      /* If delta >= 4, for example if chain_i==2 and chain_j==7 (which   */
+      /* means the contour went from a step up to step down-to-the-right) */
+      /* then 5=(7-2) which is >=4, so -3=(5-8) which means that the      */
+      /* change in direction is a righ-hand turn of 3 units).             */
+      if(d >= 4)
+         d -= 8;
+      /* If delta <= -4, for example if chain_i==7 and chain_j==2 (which  */
+      /* means the contour went from a step down-to-the-right to step up) */
+      /* then -5=(2-7) which is <=-4, so 3=(-5+8) which means that the    */
+      /* change in direction is a left-hand turn of 3 units).             */
+      else if (d <= -4)
+         d += 8;
+
+      /* The delta direction is then accumulated. */
+      sum += d;
+   }
+
+   /* Now we need to add in the final delta direction between the last */
+   /* and first codes in the chain.                                    */
+   d = chain[0] - chain[i];
+   if(d >= 4)
+      d -= 8;
+   else if (d <= -4)
+      d += 8;
+   sum += d;
+
+   /* If the final turn_accumulator == 0, then we CAN'T TELL the       */
+   /* direction of the chain code, so return the default return value. */
+   if(sum == 0)
+      return(default_ret);
+   /* Otherwise, if the final turn-accumulator is positive ... */
+   else if(sum > 0)
+      /* Then we had a greater amount of left-hand turns than right-hand     */
+      /* turns, so the chain is in COUNTER-CLOCKWISE order, so return FALSE. */
+      return(FALSE);
+   /* Otherwise, the final turn-accumulator is negative ... */
+   else
+      /* So we had a greater amount of right-hand turns than left-hand  */
+      /* turns, so the chain is in CLOCKWISE order, so return TRUE.     */
+      return(TRUE);
+}
+
 /*************************************************************************
 **************************************************************************
 #cat: get_loop_list - Takes a list of minutia points and determines which
diff --git a/libfprint/nbis/mindtct/minutia.c b/libfprint/nbis/mindtct/minutia.c
index 5193fc8..6bcfaab 100644
--- a/libfprint/nbis/mindtct/minutia.c
+++ b/libfprint/nbis/mindtct/minutia.c
@@ -73,6 +73,7 @@ identified are necessarily the best available for the purpose.
                         adjust_high_curvature_minutia()
                         adjust_high_curvature_minutia_V2()
                         get_low_curvature_direction()
+                        lfs2nist_minutia_XYT()
 
 ***********************************************************************/
 
@@ -80,6 +81,8 @@ identified are necessarily the best available for the purpose.
 #include <stdlib.h>
 #include <lfs.h>
 
+
+
 /*************************************************************************
 **************************************************************************
 #cat: alloc_minutiae - Allocates and initializes a minutia list based on the
@@ -3461,3 +3464,48 @@ int get_low_curvature_direction(const int scan_dir, const int appearing,
    return(idir);
 }
 
+/*************************************************************************
+**************************************************************************
+#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
+#cat:        representation to NIST internal representation
+
+   Input:
+      minutia  - LFS minutia structure containing attributes to be converted
+   Output:
+      ox       - NIST internal based x-pixel coordinate
+      oy       - NIST internal based y-pixel coordinate
+      ot       - NIST internal based minutia direction/orientation
+   Return Code:
+      Zero     - successful completion
+      Negative - system error
+**************************************************************************/
+void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
+                          const MINUTIA *minutia, const int iw, const int ih)
+{
+   int x, y, t;
+   float degrees_per_unit;
+
+   /*       XYT's according to NIST internal rep:           */
+    /*      1. pixel coordinates with origin bottom-left    */
+   /*       2. orientation in degrees on range [0..360]     */
+   /*          with 0 pointing east and increasing counter  */
+   /*          clockwise (same as M1)                       */
+   /*       3. direction pointing out and away from the     */
+   /*             ridge ending or bifurcation valley        */
+   /*             (opposite direction from M1)              */
+
+   x = minutia->x;
+   y = ih - minutia->y;
+
+   degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
+
+   t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
+   if(t < 0){
+      t += 360;
+   }
+
+   *ox = x;
+   *oy = y;
+   *ot = t;
+}
+
diff --git a/libfprint/nbis/mindtct/quality.c b/libfprint/nbis/mindtct/quality.c
index 834767f..5dcabc2 100644
--- a/libfprint/nbis/mindtct/quality.c
+++ b/libfprint/nbis/mindtct/quality.c
@@ -39,7 +39,6 @@ identified are necessarily the best available for the purpose.
                         combined_minutia_quality()
                         grayscale_reliability()
                         get_neighborhood_stats()
-                        reliability_fr_quality_map()
 
 ***********************************************************************/
 
@@ -173,6 +172,120 @@ int gen_quality_map(int **oqmap, int *direction_map, int *low_contrast_map,
    return(0);
 }
 
+/***********************************************************************
+************************************************************************
+#cat: get_neighborhood_stats - Given a minutia point, computes the mean
+#cat:              and stdev of the 8-bit grayscale pixels values in a
+#cat:              surrounding neighborhood with specified radius.
+
+   Code originally written by Austin Hicklin for FBI ATU
+   Modified by Michael D. Garris (NIST) Sept. 25, 2000
+
+   Input:
+      minutia    - structure containing detected minutia
+      idata      - 8-bit grayscale fingerprint image
+      iw         - width (in pixels) of the image
+      ih         - height (in pixels) of the image
+      radius_pix - pixel radius of surrounding neighborhood
+   Output:
+      mean       - mean of neighboring pixels
+      stdev      - standard deviation of neighboring pixels
+************************************************************************/
+static void get_neighborhood_stats(double *mean, double *stdev, MINUTIA *minutia,
+                     unsigned char *idata, const int iw, const int ih,
+                     const int radius_pix)
+{
+   int i, x, y, rows, cols;
+   int n = 0, sumX = 0, sumXX = 0;
+   int histogram[256];
+
+   /* Zero out histogram. */
+   memset(histogram, 0, 256 * sizeof(int));
+
+   /* Set minutia's coordinate variables. */
+   x = minutia->x;
+   y = minutia->y;
+
+
+   /* If minutiae point is within sampleboxsize distance of image border, */
+   /* a value of 0 reliability is returned. */
+   if ((x < radius_pix) || (x > iw-radius_pix-1) || 
+       (y < radius_pix) || (y > ih-radius_pix-1)) {
+      *mean = 0.0;
+      *stdev = 0.0;
+      return;
+      
+   }
+
+   /* Foreach row in neighborhood ... */
+   for(rows = y - radius_pix;
+       rows <= y + radius_pix;
+       rows++){
+      /* Foreach column in neighborhood ... */
+      for(cols = x - radius_pix;
+          cols <= x + radius_pix;
+          cols++){
+         /* Bump neighbor's pixel value bin in histogram. */
+         histogram[*(idata+(rows * iw)+cols)]++;
+      }
+   }
+
+   /* Foreach grayscale pixel bin ... */
+   for(i = 0; i < 256; i++){
+      if(histogram[i]){
+         /* Accumulate Sum(X[i]) */
+         sumX += (i * histogram[i]);
+         /* Accumulate Sum(X[i]^2) */
+         sumXX += (i * i * histogram[i]);
+         /* Accumulate N samples */
+         n += histogram[i];
+      }
+   }
+
+   /* Mean = Sum(X[i])/N */
+   *mean = sumX/(double)n;
+   /* Stdev = sqrt((Sum(X[i]^2)/N) - Mean^2) */
+   *stdev = sqrt((sumXX/(double)n) - ((*mean)*(*mean)));
+}
+
+/***********************************************************************
+************************************************************************
+#cat: grayscale_reliability - Given a minutia point, computes a reliability
+#cat:              measure from the stdev and mean of its pixel neighborhood.
+
+   Code originally written by Austin Hicklin for FBI ATU
+   Modified by Michael D. Garris (NIST) Sept. 25, 2000
+
+   GrayScaleReliability - reasonable reliability heuristic, returns
+   0.0 .. 1.0 based on stdev and Mean of a localized histogram where
+   "ideal" stdev is >=64; "ideal" Mean is 127.  In a 1 ridge radius
+   (11 pixels), if the bytevalue (shade of gray) in the image has a
+   stdev of >= 64 & a mean of 127,  returns 1.0 (well defined
+   light & dark areas in equal proportions).
+
+   Input:
+      minutia    - structure containing detected minutia
+      idata      - 8-bit grayscale fingerprint image
+      iw         - width (in pixels) of the image
+      ih         - height (in pixels) of the image
+      radius_pix - pixel radius of surrounding neighborhood
+   Return Value:
+      reliability - computed reliability measure
+************************************************************************/
+static double grayscale_reliability(MINUTIA *minutia, unsigned char *idata,
+                             const int iw, const int ih, const int radius_pix)
+{
+   double mean, stdev;
+   double reliability;
+
+   get_neighborhood_stats(&mean, &stdev, minutia, idata, iw, ih, radius_pix);
+
+   reliability = min((stdev>IDEALSTDEV ? 1.0 : stdev/(double)IDEALSTDEV),
+                         (1.0-(fabs(mean-IDEALMEAN)/(double)IDEALMEAN)));
+
+   return(reliability);
+}
+
 /***********************************************************************
 ************************************************************************
 #cat: combined_minutia_quality - Combines quality measures derived from
@@ -277,191 +390,4 @@ int combined_minutia_quality(MINUTIAE *minutiae,
    /* Return normally. */
    return(0);
 }
-        
 
-/***********************************************************************
-************************************************************************
-#cat: grayscale_reliability - Given a minutia point, computes a reliability
-#cat:              measure from the stdev and mean of its pixel neighborhood.
-
-   Code originally written by Austin Hicklin for FBI ATU
-   Modified by Michael D. Garris (NIST) Sept. 25, 2000
-
-   GrayScaleReliability - reasonable reliability heuristic, returns
-   0.0 .. 1.0 based on stdev and Mean of a localized histogram where
-   "ideal" stdev is >=64; "ideal" Mean is 127.  In a 1 ridge radius
-   (11 pixels), if the bytevalue (shade of gray) in the image has a
-   stdev of >= 64 & a mean of 127,  returns 1.0 (well defined
-   light & dark areas in equal proportions).
-
-   Input:
-      minutia    - structure containing detected minutia
-      idata      - 8-bit grayscale fingerprint image
-      iw         - width (in pixels) of the image
-      ih         - height (in pixels) of the image
-      radius_pix - pixel radius of surrounding neighborhood
-   Return Value:
-      reliability - computed reliability measure
-************************************************************************/
-double grayscale_reliability(MINUTIA *minutia, unsigned char *idata,
-                             const int iw, const int ih, const int radius_pix)
-{
-   double mean, stdev;
-   double reliability;
-
-   get_neighborhood_stats(&mean, &stdev, minutia, idata, iw, ih, radius_pix);
-
-   reliability = min((stdev>IDEALSTDEV ? 1.0 : stdev/(double)IDEALSTDEV),
-                         (1.0-(fabs(mean-IDEALMEAN)/(double)IDEALMEAN)));
-
-   return(reliability);
-}
-
-
-/***********************************************************************
-************************************************************************
-#cat: get_neighborhood_stats - Given a minutia point, computes the mean
-#cat:              and stdev of the 8-bit grayscale pixels values in a
-#cat:              surrounding neighborhood with specified radius.
-
-   Code originally written by Austin Hicklin for FBI ATU
-   Modified by Michael D. Garris (NIST) Sept. 25, 2000
-
-   Input:
-      minutia    - structure containing detected minutia
-      idata      - 8-bit grayscale fingerprint image
-      iw         - width (in pixels) of the image
-      ih         - height (in pixels) of the image
-      radius_pix - pixel radius of surrounding neighborhood
-   Output:
-      mean       - mean of neighboring pixels
-      stdev      - standard deviation of neighboring pixels
-************************************************************************/
-void get_neighborhood_stats(double *mean, double *stdev, MINUTIA *minutia,
-                     unsigned char *idata, const int iw, const int ih,
-                     const int radius_pix)
-{
-   int i, x, y, rows, cols;
-   int n = 0, sumX = 0, sumXX = 0;
-   int histogram[256];
-
-   /* Zero out histogram. */
-   memset(histogram, 0, 256 * sizeof(int));
-
-   /* Set minutia's coordinate variables. */
-   x = minutia->x;
-   y = minutia->y;
-
-
-   /* If minutiae point is within sampleboxsize distance of image border, */
-   /* a value of 0 reliability is returned. */
-   if ((x < radius_pix) || (x > iw-radius_pix-1) || 
-       (y < radius_pix) || (y > ih-radius_pix-1)) {
-      *mean = 0.0;
-      *stdev = 0.0;
-      return;
-      
-   }
-
-   /* Foreach row in neighborhood ... */
-   for(rows = y - radius_pix;
-       rows <= y + radius_pix;
-       rows++){
-      /* Foreach column in neighborhood ... */
-      for(cols = x - radius_pix;
-          cols <= x + radius_pix;
-          cols++){
-         /* Bump neighbor's pixel value bin in histogram. */
-         histogram[*(idata+(rows * iw)+cols)]++;
-      }
-   }
-
-   /* Foreach grayscale pixel bin ... */
-   for(i = 0; i < 256; i++){
-      if(histogram[i]){
-         /* Accumulate Sum(X[i]) */
-         sumX += (i * histogram[i]);
-         /* Accumulate Sum(X[i]^2) */
-         sumXX += (i * i * histogram[i]);
-         /* Accumulate N samples */
-         n += histogram[i];
-      }
-   }
-
-   /* Mean = Sum(X[i])/N */
-   *mean = sumX/(double)n;
-   /* Stdev = sqrt((Sum(X[i]^2)/N) - Mean^2) */
-   *stdev = sqrt((sumXX/(double)n) - ((*mean)*(*mean)));
-}
-
-/***********************************************************************
-************************************************************************
-#cat: reliability_fr_quality_map - Takes a set of minutiae and assigns
-#cat:              each one a reliability measure based on 1 of 5 possible
-#cat:              quality levels from its location in a quality map.
-
-   Input:
-      minutiae    - structure contining the detected minutia
-      quality_map - map with blocks assigned 1 of 5 quality levels
-      map_w       - width (in blocks) of the map
-      map_h       - height (in blocks) of the map
-      blocksize   - size (in pixels) of each block in the map
-   Output:
-      minutiae    - updated reliability members
-   Return Code:
-      Zero       - successful completion
-      Negative   - system error
-************************************************************************/
-int reliability_fr_quality_map(MINUTIAE *minutiae,
-                   int *quality_map, const int mw, const int mh,
-                   const int iw, const int ih, const int blocksize)
-{
-   int ret, i, index;
-   int *pquality_map;
-   MINUTIA *minutia;
-
-   /* Expand block map values to pixel map. */
-   if((ret = pixelize_map(&pquality_map, iw, ih,
-                         quality_map, mw, mh, blocksize))){
-      return(ret);
-   }
-
-   /* Foreach minutiae detected ... */
-   for(i = 0; i < minutiae->num; i++){
-      /* Assign minutia pointer. */
-      minutia = minutiae->list[i];
-      /* Compute minutia pixel index. */
-      index = (minutia->y * iw) + minutia->x;
-      /* Switch on pixel's quality value ... */
-      switch(pquality_map[index]){
-         case 0:
-            minutia->reliability = 0.0;
-            break;
-         case 1:
-            minutia->reliability = 0.25;
-            break;
-         case 2:
-            minutia->reliability = 0.50;
-            break;
-         case 3:
-            minutia->reliability = 0.75;
-            break;
-         case 4:
-            minutia->reliability = 0.99;
-            break;
-         /* Error if quality value not in range [0..4]. */
-         default:
-            fprintf(stderr, "ERROR : reliability_fr_quality_map :");
-            fprintf(stderr, "unexpected quality value %d ",
-                             pquality_map[index]);
-            fprintf(stderr, "not in range [0..4]\n");
-            return(-2);
-      }
-   }
-
-   /* Deallocate pixelized quality map. */
-   free(pquality_map);
-
-   /* Return normally. */
-   return(0);
-}
diff --git a/libfprint/nbis/mindtct/remove.c b/libfprint/nbis/mindtct/remove.c
index 592cb72..029a0bc 100644
--- a/libfprint/nbis/mindtct/remove.c
+++ b/libfprint/nbis/mindtct/remove.c
@@ -38,17 +38,12 @@ identified are necessarily the best available for the purpose.
                         remove_false_minutia_V2()
                         remove_holes()
                         remove_hooks()
-                        remove_hooks_islands_lakes_overlaps()
                         remove_islands_and_lakes()
                         remove_malformations()
-                        remove_near_invblock()
                         remove_near_invblock_V2()
-                        remove_pointing_invblock()
                         remove_pointing_invblock_V2()
                         remove_overlaps()
-                        remove_pores()
                         remove_pores_V2()
-                        remove_or_adjust_side_minutiae()
                         remove_or_adjust_side_minutiae_V2()
 
 ***********************************************************************/
@@ -346,302 +341,6 @@ static int remove_hooks(MINUTIAE *minutiae,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: remove_hooks_islands_lakes_overlaps - Removes minutia points on hooks,
-#cat:                islands, lakes, and overlaps and fills in small small
-#cat:                loops in the binary image and joins minutia features in
-#cat:                the image on opposite sides of an overlap.  So, this
-#cat:                routine not only prunes minutia points but it edits the
-#cat:                binary input image as well.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-      bdata     - edited binary image with loops filled and overlaps removed
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-static int remove_hooks_islands_lakes_overlaps(MINUTIAE *minutiae,
-           unsigned char *bdata, const int iw, const int ih,
-           const LFSPARMS *lfsparms)
-{
-   int *to_remove;
-   int i, f, s, ret;
-   int delta_y, full_ndirs, qtr_ndirs, deltadir, min_deltadir;
-   int *loop_x, *loop_y, *loop_ex, *loop_ey, nloop;
-   MINUTIA *minutia1, *minutia2;
-   double dist;
-
-   print2log("\nREMOVING HOOKS, ISLANDS, LAKES, AND OVERLAPS:\n");
-
-   /* Allocate list of minutia indices that upon completion of testing */
-   /* should be removed from the minutiae lists.  Note: That using      */
-   /* "calloc" initializes the list to FALSE.                          */
-   to_remove = (int *)calloc(minutiae->num, sizeof(int));
-   if(to_remove == (int *)NULL){
-      fprintf(stderr,
-        "ERROR : remove_hooks_islands_lakes_overlaps : calloc : to_remove\n");
-      return(-300);
-   }
-
-   /* Compute number directions in full circle. */
-   full_ndirs = lfsparms->num_directions<<1;
-   /* Compute number of directions in 45=(180/4) degrees. */
-   qtr_ndirs = lfsparms->num_directions>>2;
-
-   /* Minimum allowable deltadir to consider joining minutia.               */
-   /* (The closer the deltadir is to 180 degrees, the more likely the join. */
-   /* When ndirs==16, then this value is 11=(3*4)-1 == 123.75 degrees.      */
-   /* I chose to parameterize this threshold based on a fixed fraction of   */
-   /* 'ndirs' rather than on passing in a parameter in degrees and doing    */
-   /* the conversion.  I doubt the difference matters.                      */
-   min_deltadir = (3 * qtr_ndirs) - 1;
-
-   f = 0;
-   /* Foreach primary (first) minutia (except for last one in list) ... */
-   while(f < minutiae->num-1){
-
-      /* If current first minutia not previously set to be removed. */
-      if(!to_remove[f]){
-
-         print2log("\n");
-
-         /* Set first minutia to temporary pointer. */
-         minutia1 = minutiae->list[f];
-         /* Foreach secondary (second) minutia to right of first minutia ... */
-         s = f+1;
-         while(s < minutiae->num){
-            /* Set second minutia to temporary pointer. */
-            minutia2 = minutiae->list[s];
-
-            print2log("1:%d(%d,%d)%d 2:%d(%d,%d)%d ",
-                      f, minutia1->x, minutia1->y, minutia1->type,
-                      s, minutia2->x, minutia2->y, minutia2->type);
-
-            /* The binary image is potentially being edited during each */
-            /* iteration of the secondary minutia loop, therefore       */
-            /* minutia pixel values may be changed.  We need to catch   */
-            /* these events by using the next 2 tests.                  */
-
-            /* If the first minutia's pixel has been previously changed... */
-            if(*(bdata+(minutia1->y*iw)+minutia1->x) != minutia1->type){
-               print2log("\n");
-               /* Then break out of secondary loop and skip to next first. */
-               break;
-            }
-
-            /* If the second minutia's pixel has been previously changed... */
-            if(*(bdata+(minutia2->y*iw)+minutia2->x) != minutia2->type)
-               /* Set to remove second minutia. */
-               to_remove[s] = TRUE;
-
-            /* If the second minutia not previously set to be removed. */
-            if(!to_remove[s]){
-
-               /* Compute delta y between 1st & 2nd minutiae and test. */
-               delta_y = minutia2->y - minutia1->y;
-               /* If delta y small enough (ex. < 8 pixels) ... */
-               if(delta_y <= lfsparms->max_rmtest_dist){
-
-                  print2log("1DY ");
-
-                  /* Compute Euclidean distance between 1st & 2nd mintuae. */
-                  dist = distance(minutia1->x, minutia1->y,
-                                  minutia2->x, minutia2->y);
-                  /* If distance is NOT too large (ex. < 8 pixels) ... */
-                  if(dist <= lfsparms->max_rmtest_dist){
-
-                     print2log("2DS ");
-
-                     /* Compute "inner" difference between directions on */
-                     /* a full circle and test.                          */
-                     if((deltadir = closest_dir_dist(minutia1->direction,
-                                    minutia2->direction, full_ndirs)) ==
-                                    INVALID_DIR){
-                        free(to_remove);
-                        fprintf(stderr,
-         "ERROR : remove_hooks_islands_lakes_overlaps : INVALID direction\n");
-                        return(-301);
-                     }
-                     /* If the difference between dirs is large enough ...  */
-                     /* (the more 1st & 2nd point away from each other the  */
-                     /* more likely they should be joined)                  */
-                     if(deltadir > min_deltadir){
-
-                        print2log("3DD ");
-
-                        /* If 1st & 2nd minutiae are NOT same type ... */
-                        if(minutia1->type != minutia2->type){
-                           /* Check to see if pair on a hook with contour */
-                           /* of specified length (ex. 15 pixels) ...     */
-                           ret = on_hook(minutia1, minutia2,
-                                         lfsparms->max_hook_len,
-                                         bdata, iw, ih);
-                           /* If hook detected between pair ... */
-                           if(ret == HOOK_FOUND){
-
-                              print2log("4HK RM\n");
-
-                              /* Set to remove first minutia. */
-                              to_remove[f] = TRUE;
-                              /* Set to remove second minutia. */
-                              to_remove[s] = TRUE;
-                           }
-                           /* If hook test IGNORED ... */
-                           else if (ret == IGNORE){
-
-                              print2log("RM\n");
-
-                              /* Set to remove first minutia. */
-                              to_remove[f] = TRUE;
-                              /* Skip to next 1st minutia by breaking out of */
-                              /* inner secondary loop.                       */
-                              break;
-                           }
-                           /* If system error occurred during hook test ... */
-                           else if (ret < 0){
-                              free(to_remove);
-                              return(ret);
-                           }
-                           /* Otherwise, no hook found, so skip to next */
-                           /* second minutia.                           */
-                           else
-                              print2log("\n");
-                        }
-                        /* Otherwise, pair is the same type, so test to see */
-                        /* if both are on an island or lake.                */
-                        else{
-                           /* Check to see if pair on a loop of specified */
-                           /* half length (ex. 15 pixels) ...             */
-                           ret = on_island_lake(&loop_x, &loop_y,
-                                    &loop_ex, &loop_ey, &nloop,
-                                    minutia1, minutia2,
-                                    lfsparms->max_half_loop,
-                                    bdata, iw, ih);
-                           /* If pair is on island/lake ... */
-                           if(ret == LOOP_FOUND){
-
-                              print2log("4IL RM\n");
-
-                              /* Fill the loop. */
-                              if((ret = fill_loop(loop_x, loop_y, nloop,
-                                                 bdata, iw, ih))){
-                                 free_contour(loop_x, loop_y,
-                                              loop_ex, loop_ey);
-                                 free(to_remove);
-                                 return(ret);
-                              }
-                              /* Set to remove first minutia. */
-                              to_remove[f] = TRUE;
-                              /* Set to remove second minutia. */
-                              to_remove[s] = TRUE;
-                              /* Deallocate loop contour. */
-                              free_contour(loop_x, loop_y, loop_ex, loop_ey);
-                           }
-                           /* If island/lake test IGNORED ... */
-                           else if (ret == IGNORE){
-
-                              print2log("RM\n");
-
-                              /* Set to remove first minutia. */
-                              to_remove[f] = TRUE;
-                              /* Skip to next 1st minutia by breaking out of */
-                              /* inner secondary loop.                       */
-                              break;
-                           }
-                           /* If ERROR while looking for island/lake ... */
-                           else if (ret < 0){
-                              free(to_remove);
-                              return(ret);
-                           }
-                           /* Otherwise, minutia pair not on island/lake, */
-                           /* but might be on an overlap.                 */
-                           else {
-                              /* If free path exists between pair ... */
-                              if(free_path(minutia1->x, minutia1->y,
-                                           minutia2->x, minutia2->y,
-                                           bdata, iw, ih, lfsparms)){
-
-                                 print2log("4OV RM\n");
-
-                                 /* Then assume overlap, so ...             */
-                                 /* Join first and second minutiae in image. */
-                                 if((ret = join_minutia(minutia1, minutia2,
-                                               bdata, iw, ih, NO_BOUNDARY,
-                                               JOIN_LINE_RADIUS))){
-                                    free(to_remove);
-                                    return(ret);
-                                 }
-                                 /* Set to remove first minutia. */
-                                 to_remove[f] = TRUE;
-                                 /* Set to remove second minutia. */
-                                 to_remove[s] = TRUE;
-                              }
-                              /* Otherwise, pair not on an overlap, so skip */
-                              /* to next second minutia.                    */
-                              else
-                                 print2log("\n");
-                           }/* End overlap test. */
-                        }/* End same type tests (island/lake & overlap). */
-                     }/* End deltadir test. */
-                     else
-                        print2log("\n");
-                  }/* End distance test. */
-                  else
-                     print2log("\n");
-               }
-               /* Otherwise, current 2nd too far below 1st, so skip to next */
-               /* 1st minutia.                                              */
-               else{
-
-                  print2log("\n");
-
-                  /* Break out of inner secondary loop. */
-                  break;
-               }/* End delta-y test. */
-
-            }/* End if !to_remove[s] */
-            else
-               print2log("\n");
-
-            /* Bump to next second minutia in minutiae list. */
-            s++;
-         }/* End secondary minutiae loop. */
-
-      }/* Otherwise, first minutia already flagged to be removed. */
-
-      /* Bump to next first minutia in minutiae list. */
-      f++;
-   }/* End primary minutiae loop. */
-
-   /* Now remove all minutiae in list that have been flagged for removal. */
-   /* NOTE: Need to remove the minutia from their lists in reverse       */
-   /*       order, otherwise, indices will be off.                       */
-   for(i = minutiae->num-1; i >= 0; i--){
-      /* If the current minutia index is flagged for removal ... */
-      if(to_remove[i]){
-         /* Remove the minutia from the minutiae list. */
-         if((ret = remove_minutia(i, minutiae))){
-            free(to_remove);
-            return(ret);
-         }
-      }
-   }
-
-   /* Deallocate flag list. */
-   free(to_remove);
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: remove_islands_and_lakes - Takes a list of true and false minutiae and
@@ -1104,237 +803,6 @@ static int remove_malformations(MINUTIAE *minutiae,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: remove_near_invblocks - Removes minutia points from the given list
-#cat:                that are sufficiently close to a block with invalid
-#cat:                ridge flow or to the edge of the image.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      nmap      - IMAP ridge flow matrix with invalid, high-curvature,
-                  and no-valid-neighbor regions identified
-      mw        - width in blocks of the NMAP
-      mh        - height in blocks of the NMAP
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-static int remove_near_invblock(MINUTIAE *minutiae, int *nmap,
-                         const int mw, const int mh, const LFSPARMS *lfsparms)
-{
-   int i, ret;
-   int ni, nbx, nby, nvalid;
-   int ix, iy, sbi, ebi;
-   int bx, by, px, py;
-   int removed;
-   MINUTIA *minutia;
-   int lo_margin, hi_margin;
-
-   /* MDG: The next 2 lookup tables are indexed by 'ix' and 'iy'. */
-   /*      When a feature pixel lies within a 6-pixel margin of a */
-   /*      block, this routine examines neighboring blocks to     */
-   /*      determine appropriate actions.                         */
-   /*         'ix' may take on values:                            */
-   /*              0 == x-pixel coord in leftmost margin          */
-   /*              1 == x-pixel coord in middle of block          */
-   /*              2 == x-pixel coord in rightmost margin         */
-   /*         'iy' may take on values:                            */
-   /*              0 == y-pixel coord in topmost margin           */
-   /*              1 == y-pixel coord in middle of block          */
-   /*              2 == y-pixel coord in bottommost margin        */
-   /*      Given (ix, iy):                                        */
-   /*         'startblk[ix][iy]' == starting neighbor index (sbi) */
-   /*         'endblk[ix][iy]'   == ending neighbor index (ebi)   */
-   /*         so that neighbors begin to be analized from index   */
-   /*         'sbi' to 'ebi'.                                     */
-   /*      Ex. (ix, iy) = (2, 0)                                  */
-   /*         ix==2 ==> x-pixel coord in rightmost margin         */
-   /*         iy==0 ==> y-pixel coord in topmost margin           */
-   /*         X - marks the region in the current block           */
-   /*             corresponding to (ix=2, iy=0).                  */
-   /*         sbi = 0 = startblk[2][0]                            */
-   /*         ebi = 2 = endblk[2][0]                              */
-   /*         so neighbors are analized on index range [0..2]     */
-   /*                                     |                       */
-   /*                      nbr block 0    |  nbr block 1          */
-   /*           --------------------------+------------           */
-   /*                top margin      | X  |                       */
-   /*           _._._._._._._._._._._._._.|                       */
-   /*                                |    |                       */
-   /*               current block    .r  m|  nbr block 2          */
-   /*                                |i  a|                       */
-   /*                                .g  g|                       */
-   /*                                |h  i|                       */
-   /*                                .t  n|                       */
-   /*                                |    |                       */
-
-   /* MDG: LUT for starting neighbor index given (ix, iy).        */
-   static int startblk[9] = { 6, 0, 0,
-                                  6,-1, 2,
-                                  4, 4, 2 };
-   /* MDG: LUT for ending neighbor index given (ix, iy).          */
-   static int endblk[9] =   { 8, 0, 2,
-                                  6,-1, 2,
-                                  6, 4, 4 };
-
-   /* MDG: Pixel coord offsets specifying the order in which neighboring */
-   /*      blocks are searched.  The current block is in the middle of   */
-   /*      8 surrounding neighbors.  The following illustrates the order */
-   /*      of neighbor indices.  (Note that 9 overlaps 1.)               */
-   /*                             8                                      */
-   /*                           7 0 1                                    */
-   /*                           6 C 2                                    */
-   /*                           5 4 3                                    */
-   /*                                                                    */
-   /*                       0  1  2  3  4  5  6  7  8                    */
-   static int blkdx[9] = {  0, 1, 1, 1, 0,-1,-1,-1, 0 };  /* Delta-X     */
-   static int blkdy[9] = { -1,-1, 0, 1, 1, 1, 0,-1,-1 };  /* Delta-Y     */
-
-   print2log("\nREMOVING MINUTIA NEAR INVALID BLOCKS:\n");
-
-   /* If the margin covers more than the entire block ... */
-   if(lfsparms->inv_block_margin > (lfsparms->blocksize>>1)){
-      /* Then treat this as an error. */
-      fprintf(stderr,
-        "ERROR : remove_near_invblock : margin too large for blocksize\n");
-      return(-270);
-   }
-
-   /* Compute the low and high pixel margin boundaries (ex. 6 pixels wide) */
-   /* in the block.                                                        */
-   lo_margin = lfsparms->inv_block_margin;
-   hi_margin = lfsparms->blocksize - lfsparms->inv_block_margin - 1;
-
-   i = 0;
-   /* Foreach minutia remaining in the list ... */
-   while(i < minutiae->num){
-      /* Assign temporary minutia pointer. */
-      minutia = minutiae->list[i];
-
-      /* Compute NMAP block coords from minutia's pixel location. */
-      bx = minutia->x/lfsparms->blocksize;
-      by = minutia->y/lfsparms->blocksize;
-
-      /* Compute pixel offset into the NMAP block corresponding to the  */
-      /* minutia's pixel location.                                      */
-      /* NOTE: The margins used here will necessarily correspond to the */
-      /* actual block, boundaries used to compute the NMAP values.      */
-      /* This will be true when the image width and/or height is not an */
-      /* even multiple of 'blocksize' and we are processing minutia     */
-      /* located in the right-most column (or bottom-most row) of       */
-      /* blocks.  I don't think this will pose a problem in practice.   */
-      px = minutia->x % lfsparms->blocksize;
-      py = minutia->y % lfsparms->blocksize;
-
-      /* Determine if x pixel offset into the block is in the margins. */
-      /* If x pixel offset is in left margin ... */
-      if(px < lo_margin)
-         ix = 0;
-      /* If x pixel offset is in right margin ... */
-      else if(px > hi_margin)
-         ix = 2;
-      /* Otherwise, x pixel offset is in middle of block. */
-      else
-         ix = 1;
-
-      /* Determine if y pixel offset into the block is in the margins. */
-      /* If y pixel offset is in top margin ... */
-      if(py < lo_margin)
-         iy = 0;
-      /* If y pixel offset is in bottom margin ... */
-      else if(py > hi_margin)
-         iy = 2;
-      /* Otherwise, y pixel offset is in middle of block. */
-      else
-         iy = 1;
-
-      /* Set remove flag to FALSE. */
-      removed = FALSE;
-
-      /* If one of the minutia's pixel offsets is in a margin ... */
-      if((ix != 1) || (iy != 1)){
-
-         /* Compute the starting neighbor block index for processing. */
-         sbi = *(startblk+(iy*3)+ix);
-         /* Compute the ending neighbor block index for processing. */
-         ebi = *(endblk+(iy*3)+ix);
-
-         /* Foreach neighbor in the range to be processed ... */
-         for(ni = sbi; ni <= ebi; ni++){
-            /* Compute the neighbor's NMAP block coords relative to */
-            /* the block the current minutia is in.                 */
-            nbx = bx + blkdx[ni];
-            nby = by + blkdy[ni];
-
-            /* If neighbor's block coords are outside of NMAP boundaries... */
-            if((nbx < 0) || (nbx >= mw) ||
-               (nby < 0) || (nby >= mh)){
-
-               print2log("%d,%d RM1\n", minutia->x, minutia->y);
-
-               /* Then the minutia is in a margin adjacent to the edge of */
-               /* the image.                                              */
-               /* NOTE: This is true when the image width and/or height   */
-               /* is an even multiple of blocksize.  When the image is not*/
-               /* an even multiple, then some minutia may not be detected */
-               /* as being in the margin of "the image" (not the block).  */
-               /* In practice, I don't think this will impact performance.*/
-               if((ret = remove_minutia(i, minutiae)))
-                  /* If system error occurred while removing minutia, */
-                  /* then return error code.                          */
-                  return(ret);
-               /* Set remove flag to TURE. */
-               removed = TRUE;
-               /* Break out of neighboring block loop. */
-               break;
-            }
-            /* If the neighboring NMAP block is INVALID ... */
-            else if (*(nmap+(nby*mw)+nbx) == INVALID_DIR){
-               /* Count the number of valid blocks neighboring */
-               /* the current neighbor.                        */
-               nvalid = num_valid_8nbrs(nmap, nbx, nby, mw, mh);
-               /* If the number of valid neighbors is < threshold */
-               /* (ex. 7)...                                      */
-               if(nvalid < lfsparms->rm_valid_nbr_min){
-
-                  print2log("%d,%d RM2\n", minutia->x, minutia->y);
-
-                  /* Then remove the current minutia from the list. */
-                  if((ret = remove_minutia(i, minutiae)))
-                     /* If system error occurred while removing minutia, */
-                     /* then return error code.                          */
-                     return(ret);
-                  /* Set remove flag to TURE. */
-                  removed = TRUE;
-                  /* Break out of neighboring block loop. */
-                  break;
-               }
-               /* Otherwise enough valid neighbors, so don't remove minutia */
-               /* based on this neighboring block.                          */
-            }
-            /* Otherwise neighboring block is VALID, HIGH_CURVATURE, or */
-            /* NO_VALID_NBRS, so don't remove minutia based on this     */
-            /* neighboring block.                                       */
-         }
-
-      } /* Otherwise not in margin, so skip to next minutia in list. */
-
-      /* If current minutia not removed ... */
-      if(!removed)
-         /* Advance to the next minutia in the list. */
-         i++;
-      /* Otherwise the next minutia has slid into the spot where current */
-      /* minutia was removed, so don't bump minutia index.               */
-   } /* End minutia loop */
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: remove_near_invblocks_V2 - Removes minutia points from the given list
@@ -1564,99 +1032,6 @@ static int remove_near_invblock_V2(MINUTIAE *minutiae, int *direction_map,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: remove_pointing_invblock - Removes minutia points that are relatively
-#cat:                close in the direction opposite the minutia to an NMAP
-#cat:                block with invalid ridge flow.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      nmap      - IMAP ridge flow matrix with invalid, high-curvature,
-                  and no-valid-neighbor regions identified
-      mw        - width in blocks of the NMAP
-      mh        - height in blocks of the NMAP
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-static int remove_pointing_invblock(MINUTIAE *minutiae,
-                             int *nmap, const int mw, const int mh,
-                             const LFSPARMS *lfsparms)
-{
-   int i, ret;
-   int delta_x, delta_y, nmapval;
-   int nx, ny, bx, by;
-   MINUTIA *minutia;
-   double pi_factor, theta;
-   double dx, dy;
-
-   print2log("\nREMOVING MINUTIA POINTING TO INVALID BLOCKS:\n");
-
-   /* Compute factor for converting integer directions to radians. */
-   pi_factor = M_PI / (double)lfsparms->num_directions;
-
-   i = 0;
-   /* Foreach minutia remaining in list ... */
-   while(i < minutiae->num){
-      /* Set temporary minutia pointer. */
-      minutia = minutiae->list[i];
-      /* Convert minutia's direction to radians. */
-      theta = minutia->direction * pi_factor;
-      /* Compute translation offsets (ex. 6 pixels). */
-      dx = sin(theta) * (double)lfsparms->trans_dir_pix;
-      dy = cos(theta) * (double)lfsparms->trans_dir_pix;
-      /* Need to truncate precision so that answers are consistent */
-      /* on different computer architectures when rounding doubles. */
-      dx = trunc_dbl_precision(dx, TRUNC_SCALE);
-      dy = trunc_dbl_precision(dy, TRUNC_SCALE);
-      delta_x = sround(dx);
-      delta_y = sround(dy);
-      /* Translate the minutia's coords. */
-      nx = minutia->x - delta_x;
-      ny = minutia->y + delta_y;
-      /* Convert pixel coords to NMAP block coords. */
-      bx = (int)(nx / lfsparms->blocksize);
-      by = (int)(ny / lfsparms->blocksize);
-      /* The translation could move the point out of image boundaries,    */
-      /* and therefore the corresponding block coords can be out of       */
-      /* IMAP boundaries, so limit the block coords to within boundaries. */
-      bx = max(0, bx);
-      bx = min(mw-1, bx);
-      by = max(0, by);
-      by = min(mh-1, by);
-
-      /* Get corresponding block's NMAP value. */
-      /*    -3 == NO_VALID_NBRS                */
-      /*    -2 == HIGH_CURVATURE               */
-      /*    -1 == INVALID_DIR                  */
-      /*     0 <= VALID_DIR                    */
-      nmapval = *(nmap+(by*mw)+bx);
-
-      /* If the NMAP value of translated minutia point is INVALID ... */
-      if(nmapval == INVALID_DIR){
-
-         print2log("%d,%d RM\n", minutia->x, minutia->y);
-
-         /* Remove the minutia from the minutiae list. */
-         if((ret = remove_minutia(i, minutiae))){
-            return(ret);
-         }
-         /* No need to advance because next minutia has slid into slot. */
-      }
-      else{
-         /* Advance to next minutia in list. */
-         i++;
-      }
-   }
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: remove_pointing_invblock_V2 - Removes minutia points that are relatively
@@ -1975,391 +1350,6 @@ static int remove_overlaps(MINUTIAE *minutiae,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: remove_pores - Attempts to detect and remove minutia points located on
-#cat:                pore-shaped valleys.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      nmap      - IMAP ridge flow matrix with invalid, high-curvature,
-                  and no-valid-neighbor regions identified
-      mw        - width in blocks of the NMAP
-      mh        - height in blocks of the NMAP
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-static int remove_pores(MINUTIAE *minutiae,
-                 unsigned char *bdata, const int iw, const int ih,
-                 int *nmap, const int mw, const int mh,
-                 const LFSPARMS *lfsparms)
-{
-   int i, ret;
-   int removed, blk_x, blk_y;
-   int rx, ry;
-   int px, py, pex, pey, bx, by, dx, dy;
-   int qx, qy, qex, qey, ax, ay, cx, cy;
-   MINUTIA *minutia;
-   double pi_factor, theta, sin_theta, cos_theta;
-   double ab2, cd2, ratio;
-   int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
-   double drx, dry;
-
-   /* MDG: This routine attempts to locate the following points on all */
-   /*      bifurcations within the feature list.                       */
-   /*      1. Compute R 3 pixels opposite the feature direction from   */
-   /*         feature point F.                                         */
-   /*      2. Find white pixel transitions P & Q within 12 steps from  */
-   /*         from R perpendicular to the feature's direction.         */
-   /*      3. Find points B & D by walking white edge from P.          */
-   /*      4. Find points A & C by walking white edge from Q.          */
-   /*      5. Measure squared distances between A-B and C-D.           */
-   /*      6. Compute ratio of squared distances and compare against   */
-   /*         threshold (2.25).  If A-B sufficiently larger than C-D,  */
-   /*         then assume NOT pore, otherwise flag the feature point F.*/
-   /*      If along the way, finding any of these points fails, then   */
-   /*      assume the feature is a pore and flag it.                   */
-   /*                                                                  */
-   /*                      A                                           */
-   /*                _____._                                           */
-   /*                       ----___     Q      C                       */
-   /*                ------____    ---_.________.___                   */
-   /*                          ---_                                    */
-   /*                (valley)    F.\   .R  (ridge)                     */
-   /*                          ____/                                   */
-   /*                ______----    ___-.--------.---                   */
-   /*                       ____---     P      D                       */
-   /*                -----.-                                           */
-   /*                      B                                           */
-   /*                                                                  */
-   /*              AB^2/CD^2 <= 2.25  then flag feature                */
-   /*                                                                  */
-
-
-   print2log("\nREMOVING PORES:\n");
-
-   /* Factor for converting integer directions into radians. */
-   pi_factor = M_PI/(double)lfsparms->num_directions;
-
-   /* Initialize to the beginning of the minutia list. */
-   i = 0;
-   /* Foreach minutia remaining in the list ... */
-   while(i < minutiae->num){
-      /* Set temporary minutia pointer. */
-      minutia = minutiae->list[i];
-
-      /* Initialize remove flag to FALSE. */
-      removed = FALSE;
-
-      /* If minutia is a bifurcation ... */
-      if(minutia->type == BIFURCATION){
-         /* Compute NMAP block coords from minutia point. */
-         blk_x = minutia->x / lfsparms->blocksize;
-         blk_y = minutia->y / lfsparms->blocksize;
-         /* If NMAP block has a VALID direction... */
-         if(*(nmap+(blk_y*mw)+blk_x) >= 0){
-            /* Compute radian angle from minutia direction. */
-            theta = (double)minutia->direction * pi_factor;
-            /* Compute sine and cosine factors of this angle. */
-            sin_theta = sin(theta);
-            cos_theta = cos(theta);
-            /* Translate the minutia point (ex. 3 pixels) in opposite */
-            /* direction minutia is pointing.  Call this point 'R'.   */
-            drx = (double)minutia->x -
-                        (sin_theta * (double)lfsparms->pores_trans_r);
-            dry = (double)minutia->y +
-                        (cos_theta * (double)lfsparms->pores_trans_r);
-            /* Need to truncate precision so that answers are consistent */
-            /* on different computer architectures when rounding doubles. */
-            drx = trunc_dbl_precision(drx, TRUNC_SCALE);
-            dry = trunc_dbl_precision(dry, TRUNC_SCALE);
-            rx = sround(drx);
-            ry = sround(dry);
-
-            /* If 'R' is on a black pixel ... */
-            if(*(bdata+(ry*iw)+rx) == 1){
-
-               /* Search a specified number of steps (ex. 12) from 'R' in a */
-               /* perpendicular direction from the minutia direction until  */
-               /* the first white pixel is found.  If a white pixel is      */
-               /* found within the specified number of steps, then call     */
-               /* this point 'P' (storing the point's edge pixel as well).  */
-               if(search_in_direction(&px, &py, &pex, &pey,
-                                      0, rx, ry, -cos_theta, -sin_theta,
-                                      lfsparms->pores_perp_steps,
-                                      bdata, iw, ih)){
-                  /* Trace contour from P's edge pixel in counter-clockwise  */
-                  /* scan and step along specified number of steps (ex. 10). */
-                  ret = trace_contour(&contour_x, &contour_y,
-                              &contour_ex, &contour_ey, &ncontour,
-                              lfsparms->pores_steps_fwd,
-                              px, py, px, py, pex, pey,
-                              SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
-
-                  /* If system error occurred during trace ... */
-                  if(ret < 0){
-                     /* Return error code. */
-                     return(ret);
-                  }
-
-                  /* If trace was not possible OR loop found OR */
-                  /* contour is incomplete ...                  */
-                  if((ret == IGNORE) ||
-                     (ret == LOOP_FOUND) ||
-                     (ncontour < lfsparms->pores_steps_fwd)){
-                     /* If contour allocated and returned ... */
-                     if((ret == LOOP_FOUND) ||
-                        (ncontour < lfsparms->pores_steps_fwd))
-                        /* Deallocate the contour. */
-                        free_contour(contour_x, contour_y,
-                                     contour_ex, contour_ey);
-
-
-                     print2log("%d,%d RMB\n", minutia->x, minutia->y);
-
-                     /* Then remove the minutia. */
-                     if((ret = remove_minutia(i, minutiae)))
-                        /* If system error, return error code. */
-                        return(ret);
-                     /* Set remove flag to TRUE. */
-                     removed = TRUE;
-                  }
-                  /* Otherwise, traced contour is complete. */
-                  else{
-                     /* Store last point in contour as point 'B'. */
-                     bx = contour_x[ncontour-1];
-                     by = contour_y[ncontour-1];
-                     /* Deallocate the contour. */
-                     free_contour(contour_x, contour_y,
-                                  contour_ex, contour_ey);
-
-                     /* Trace contour from P's edge pixel in clockwise scan */
-                     /* and step along specified number of steps (ex. 8).   */
-                     ret = trace_contour(&contour_x, &contour_y,
-                              &contour_ex, &contour_ey, &ncontour,
-                              lfsparms->pores_steps_bwd,
-                              px, py, px, py, pex, pey,
-                              SCAN_CLOCKWISE, bdata, iw, ih);
-
-                     /* If system error occurred during trace ... */
-                     if(ret < 0){
-                        /* Return error code. */
-                        return(ret);
-                     }
-
-                     /* If trace was not possible OR loop found OR */
-                     /* contour is incomplete ...                  */
-                     if((ret == IGNORE) ||
-                        (ret == LOOP_FOUND) ||
-                        (ncontour < lfsparms->pores_steps_bwd)){
-                        /* If contour allocated and returned ... */
-                        if((ret == LOOP_FOUND) ||
-                           (ncontour < lfsparms->pores_steps_bwd))
-                           /* Deallocate the contour. */
-                           free_contour(contour_x, contour_y,
-                                        contour_ex, contour_ey);
-
-                        print2log("%d,%d RMD\n", minutia->x, minutia->y);
-
-                        /* Then remove the minutia. */
-                        if((ret = remove_minutia(i, minutiae)))
-                           /* If system error, return error code. */
-                           return(ret);
-                        /* Set remove flag to TRUE. */
-                        removed = TRUE;
-                     }
-                     /* Otherwise, traced contour is complete. */
-                     else{
-                        /* Store last point in contour as point 'D'. */
-                        dx = contour_x[ncontour-1];
-                        dy = contour_y[ncontour-1];
-                        /* Deallocate the contour. */
-                        free_contour(contour_x, contour_y,
-                                     contour_ex, contour_ey);
-                        /* Search a specified number of steps (ex. 12) from */
-                        /* 'R' in opposite direction of that used to find   */
-                        /* 'P' until the first white pixel is found.  If a  */
-                        /* white pixel is found within the specified number */
-                        /* of steps, then call this point 'Q' (storing the  */
-                        /* point's edge pixel as well).                     */
-                        if(search_in_direction(&qx, &qy, &qex, &qey,
-                                     0, rx, ry, cos_theta, sin_theta,
-                                     lfsparms->pores_perp_steps,
-                                     bdata, iw, ih)){
-                           /* Trace contour from Q's edge pixel in clockwise */
-                           /* scan and step along specified number of steps  */
-                           /* (ex. 10).                                      */
-                           ret = trace_contour(&contour_x, &contour_y,
-                                    &contour_ex, &contour_ey, &ncontour,
-                                    lfsparms->pores_steps_fwd,
-                                    qx, qy, qx, qy, qex, qey,
-                                    SCAN_CLOCKWISE, bdata, iw, ih);
-
-                           /* If system error occurred during trace ... */
-                           if(ret < 0){
-                              /* Return error code. */
-                              return(ret);
-                           }
-
-                           /* If trace was not possible OR loop found OR */
-                           /* contour is incomplete ...                  */
-                           if((ret == IGNORE) ||
-                              (ret == LOOP_FOUND) ||
-                              (ncontour < lfsparms->pores_steps_fwd)){
-                              /* If contour allocated and returned ... */
-                              if((ret == LOOP_FOUND) ||
-                                 (ncontour < lfsparms->pores_steps_fwd))
-                                 /* Deallocate the contour. */
-                                 free_contour(contour_x, contour_y,
-                                              contour_ex, contour_ey);
-
-                              print2log("%d,%d RMA\n", minutia->x, minutia->y);
-
-                              /* Then remove the minutia. */
-                              if((ret = remove_minutia(i, minutiae)))
-                                 /* If system error, return error code. */
-                                 return(ret);
-                              /* Set remove flag to TRUE. */
-                              removed = TRUE;
-                           }
-                           /* Otherwise, traced contour is complete. */
-                           else{
-                              /* Store last point in contour as point 'A'. */
-                              ax = contour_x[ncontour-1];
-                              ay = contour_y[ncontour-1];
-                              /* Deallocate the contour. */
-                              free_contour(contour_x, contour_y,
-                                           contour_ex, contour_ey);
-
-                              /* Trace contour from Q's edge pixel in    */
-                              /* counter-clockwise scan and step along a */
-                              /* specified number of steps (ex. 8).      */
-                              ret = trace_contour(&contour_x, &contour_y,
-                                    &contour_ex, &contour_ey, &ncontour, 
-                                    lfsparms->pores_steps_bwd,
-                                    qx, qy, qx, qy, qex, qey,
-                                    SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
-
-                              /* If system error occurred during scan ... */
-                              if(ret < 0){
-                                 /* Return error code. */
-                                 return(ret);
-                              }
-
-                              /* If trace was not possible OR loop found OR */
-                              /* contour is incomplete ...                  */
-                              if((ret == IGNORE) ||
-                                 (ret == LOOP_FOUND) ||
-                                 (ncontour < lfsparms->pores_steps_bwd)){
-                                 /* If contour allocated and returned ... */
-                                 if((ret == LOOP_FOUND) ||
-                                    (ncontour < lfsparms->pores_steps_bwd))
-                                    /* Deallocate the contour. */
-                                    free_contour(contour_x, contour_y,
-                                                 contour_ex, contour_ey);
-
-                                 print2log("%d,%d RMC\n",
-                                           minutia->x, minutia->y);
-
-                                 /* Then remove the minutia. */
-                                 if((ret = remove_minutia(i, minutiae)))
-                                    /* If system error, return error code. */
-                                    return(ret);
-                                 /* Set remove flag to TRUE. */
-                                 removed = TRUE;
-                              }
-                              /* Otherwise, traced contour is complete. */
-                              else{
-                                 /* Store last point in contour as 'C'. */
-                                 cx = contour_x[ncontour-1];
-                                 cy = contour_y[ncontour-1];
-                                 /* Deallocate the contour. */
-                                 free_contour(contour_x, contour_y,
-                                              contour_ex, contour_ey);
-
-                                 /* Compute squared distance between points */
-                                 /* 'A' and 'B'.                            */
-                                 ab2 = squared_distance(ax, ay, bx, by);
-                                 /* Compute squared distance between points */
-                                 /* 'C' and 'D'.                            */
-                                 cd2 = squared_distance(cx, cy, dx, dy);
-                                 /* If CD distance is not near zero */
-                                 /* (ex. 0,5) ...                   */
-                                 if(cd2 > lfsparms->pores_min_dist2){
-                                    /* Compute ratio of squared distances. */
-                                    ratio = ab2 / cd2;
-                                    /* If ratio is small enough (ex. 2.25)...*/
-                                    if(ratio <= lfsparms->pores_max_ratio){
-
-                                       print2log("%d,%d ",
-                                                 minutia->x, minutia->y);
-      print2log("R=%d,%d P=%d,%d B=%d,%d D=%d,%d Q=%d,%d A=%d,%d C=%d,%d ",
-              rx, ry, px, py, bx, by, dx, dy, qx, qy, ax, ay, cx, cy);
-                                       print2log("RMRATIO\n");
-
-                                       /* Then assume pore & remove minutia. */
-                                       if((ret = remove_minutia(i, minutiae)))
-                                          /* If system error, return code. */
-                                          return(ret);
-                                       /* Set remove flag to TRUE. */
-                                       removed = TRUE;
-                                    }
-                                    /* Otherwise, ratio to big, so assume */
-                                    /* legitimate  bifurcation.           */
-                                 } /* Else, cd2 too small. */
-                              } /* Done with C. */
-                           } /* Done with A. */
-                        }
-                        /* Otherwise, Q not found ... */
-                        else{
-
-                           print2log("%d,%d RMQ\n", minutia->x, minutia->y);
-
-                           /* Then remove the minutia. */
-                           if((ret = remove_minutia(i, minutiae)))
-                              /* If system error, return error code. */
-                              return(ret);
-                           /* Set remove flag to TRUE. */
-                           removed = TRUE;
-                        } /* Done with Q. */
-                     } /* Done with D. */
-                  } /* Done with B. */
-               }
-               /* Otherwise, P not found ... */
-               else{
-
-                  print2log("%d,%d RMP\n", minutia->x, minutia->y);
-
-                  /* Then remove the minutia. */
-                  if((ret = remove_minutia(i, minutiae)))
-                     /* If system error, return error code. */
-                     return(ret);
-                  /* Set remove flag to TRUE. */
-                  removed = TRUE;
-               }
-            } /* Else, R is on white pixel. */
-         } /* Else NMAP is INVALID, HIGH_CURVATURE, or NO_VALID_NBRS. */
-      } /* Else, Ridge Ending. */
-
-      /* If current minutia not removed ... */
-      if(!removed)
-         /* Bump to next minutia in list. */
-         i++;
-      /* Otherwise, next minutia has slid into slot of current removed one. */
-
-   } /* End While minutia remaining in list. */
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: remove_pores_V2 - Attempts to detect and remove minutia points located on
@@ -2753,223 +1743,6 @@ static int remove_pores_V2(MINUTIAE *minutiae,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: remove_or_adjust_side_minutiae - Removes loops or minutia points that
-#cat:              are not on complete contours of specified length. If the
-#cat:              contour is complete, then the minutia is adjusted based
-#cat:              on a minmax analysis of the rotated y-coords of the contour.
-
-   Input:
-      minutiae  - list of true and false minutiae
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      lfsparms  - parameters and thresholds for controlling LFS
-   Output:
-      minutiae  - list of pruned minutiae
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-static int remove_or_adjust_side_minutiae(MINUTIAE *minutiae,
-                 unsigned char *bdata, const int iw, const int ih,
-                 const LFSPARMS *lfsparms)
-{
-   int i, j, ret;
-   MINUTIA *minutia;
-   double pi_factor, theta, sin_theta, cos_theta;
-   int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
-   int *rot_y, minloc;
-   int *minmax_val, *minmax_i, *minmax_type, minmax_alloc, minmax_num;
-   double drot_y;
-
-   print2log("\nADJUSTING SIDE MINUTIA:\n");
-
-   /* Allocate working memory for holding rotated y-coord of a */
-   /* minutia's contour.                                       */
-   rot_y = (int *)malloc(((lfsparms->side_half_contour<<1)+1) * sizeof(int));
-   if(rot_y == (int *)NULL){
-      fprintf(stderr,
-              "ERROR : remove_or_adjust_side_minutiae : malloc : rot_y\n");
-      return(-280);
-   }
-
-   /* Compute factor for converting integer directions to radians. */
-   pi_factor = M_PI / (double)lfsparms->num_directions;
-
-   i = 0;
-   /* Foreach minutia remaining in list ... */
-   while(i < minutiae->num){
-      /* Assign a temporary pointer. */
-      minutia = minutiae->list[i];
-
-      /* Extract a contour centered on the minutia point (ex. 7 pixels */
-      /* in both directions).                                          */
-      ret = get_centered_contour(&contour_x, &contour_y,
-                         &contour_ex, &contour_ey, &ncontour,
-                         lfsparms->side_half_contour,
-                         minutia->x, minutia->y, minutia->ex, minutia->ey,
-                         bdata, iw, ih);
-
-      /* If system error occurred ... */
-      if(ret < 0){
-         /* Deallocate working memory. */
-         free(rot_y);
-         /* Return error code. */
-         return(ret);
-      }
-
-      /* If we didn't succeed in extracting a complete contour for any */
-      /* other reason ...                                              */
-      if((ret == LOOP_FOUND) ||
-         (ret == IGNORE) ||
-         (ret == INCOMPLETE)){
-
-         print2log("%d,%d RM1\n", minutia->x, minutia->y);
-
-         /* Remove minutia from list. */
-         if((ret = remove_minutia(i, minutiae))){
-            /* Deallocate working memory. */
-            free(rot_y);
-            /* Return error code. */
-            return(ret);
-         }
-         /* No need to advance because next minutia has "slid" */
-         /* into position pointed to by 'i'.                   */
-      }
-      /* Otherwise, a complete contour was found and extracted ... */
-      else{
-         /* Rotate contour points by negative angle of feature's direction. */
-         /* The contour of a well-formed minutia point will form a bowl     */
-         /* shape concaved in the direction of the minutia.  By rotating    */
-         /* the contour points by the negative angle of feature's direction */
-         /* the bowl will be transformed to be concaved upwards and minima  */
-         /* and maxima of the transformed y-coords can be analyzed to       */
-         /* determine if the minutia is "well-formed" or not.  If well-     */
-         /* formed then the position of the minutia point is adjusted.  If  */
-         /* not well-formed, then the minutia point is removed altogether.  */
-
-         /* Normal rotation of T degrees around the origin of */
-         /*      the point (x,y):                             */
-         /*         rx = x*cos(T) - y*sin(T)                  */
-         /*         ry = x*cos(T) + y*sin(T)                  */
-         /*      The rotation here is for -T degrees:         */
-         /*         rx = x*cos(-T) - y*sin(-T)                */
-         /*         ry = x*cos(-T) + y*sin(-T)                */
-         /*      which can be written:                        */
-         /*         rx = x*cos(T) + y*sin(T)                  */
-         /*         ry = x*sin(T) - y*cos(T)                  */
-
-         /* Convert minutia's direction to radians. */
-         theta = (double)minutia->direction * pi_factor;
-         /* Compute sine and cosine values at theta for rotation. */
-         sin_theta = sin(theta);
-         cos_theta = cos(theta);
-
-         for(j = 0; j < ncontour; j++){
-             /* We only need to rotate the y-coord (don't worry     */
-             /* about rotating the x-coord or contour edge pixels). */
-             drot_y = ((double)contour_x[j] * sin_theta) -
-                               ((double)contour_y[j] * cos_theta);
-             /* Need to truncate precision so that answers are consistent */
-             /* on different computer architectures when rounding doubles. */
-             drot_y = trunc_dbl_precision(drot_y, TRUNC_SCALE);
-             rot_y[j] = sround(drot_y);
-         }
-
-         /* Locate relative minima and maxima in vector of rotated */
-         /* y-coords of current minutia's contour.                 */
-         if((ret = minmaxs(&minmax_val, &minmax_type, &minmax_i,
-                          &minmax_alloc, &minmax_num,
-                          rot_y, ncontour))){
-            /* If system error, then deallocate working memories. */
-            free(rot_y);
-            free_contour(contour_x, contour_y, contour_ex, contour_ey);
-            /* Return error code. */
-            return(ret);
-         }
-
-         /* If one and only one minima was found in rotated y-coord */
-         /* of contour ...                                          */
-         if((minmax_num == 1) &&
-            (minmax_type[0] == -1)){
-
-            print2log("%d,%d AD1 ", minutia->x, minutia->y);
-
-            /* Reset loation of minutia point to contour point at minima. */
-            minutia->x = contour_x[minmax_i[0]];
-            minutia->y = contour_y[minmax_i[0]];
-            minutia->ex = contour_ex[minmax_i[0]];
-            minutia->ey = contour_ey[minmax_i[0]];
-            /* Advance to the next minutia in the list. */
-            i++;
-
-            print2log("%d,%d\n", minutia->x, minutia->y);
-
-         }
-         /* If exactly 3 min/max found and they are min-max-min ... */
-         else if((minmax_num == 3) &&
-                 (minmax_type[0] == -1)){
-            /* Choose minima location with smallest rotated y-coord. */
-            if(minmax_val[0] < minmax_val[2])
-               minloc = minmax_i[0];
-            else
-               minloc = minmax_i[2];
-
-            print2log("%d,%d AD2 ", minutia->x, minutia->y);
-
-            /* Reset loation of minutia point to contour point at minima. */
-            minutia->x = contour_x[minloc];
-            minutia->y = contour_y[minloc];
-            minutia->ex = contour_ex[minloc];
-            minutia->ey = contour_ey[minloc];
-            /* Advance to the next minutia in the list. */
-            i++;
-
-            print2log("%d,%d\n", minutia->x, minutia->y);
-
-         }
-         /* Otherwise, ... */
-         else{
-
-            print2log("%d,%d RM2\n", minutia->x, minutia->y);
-
-            /* Remove minutia from list. */
-            if((ret = remove_minutia(i, minutiae))){
-               /* If system error, then deallocate working memories. */
-               free(rot_y);
-               free_contour(contour_x, contour_y, contour_ex, contour_ey);
-               if(minmax_alloc > 0){
-                  free(minmax_val);
-                  free(minmax_type);
-                  free(minmax_i);
-               }
-               /* Return error code. */
-               return(ret);
-            }
-            /* No need to advance because next minutia has "slid" */
-            /* into position pointed to by 'i'.                   */
-
-         }
-
-         /* Deallocate contour and min/max buffers. */
-         free_contour(contour_x, contour_y, contour_ex, contour_ey);
-         if(minmax_alloc > 0){
-            free(minmax_val);
-            free(minmax_type);
-            free(minmax_i);
-         }
-      } /* End else contour extracted. */
-   } /* End while not end of minutiae list. */
-
-   /* Deallocate working memory. */
-   free(rot_y);
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: remove_or_adjust_side_minutiae_V2 - Removes loops or minutia points that
diff --git a/libfprint/nbis/mindtct/ridges.c b/libfprint/nbis/mindtct/ridges.c
index 8cbfbe7..0fb2686 100644
--- a/libfprint/nbis/mindtct/ridges.c
+++ b/libfprint/nbis/mindtct/ridges.c
@@ -52,135 +52,156 @@ identified are necessarily the best available for the purpose.
 
 /*************************************************************************
 **************************************************************************
-#cat: count_minutiae_ridges - Takes a list of minutiae, and for each one,
-#cat:                determines its closest neighbors and counts the number
-#cat:                of interveining ridges between the minutia point and
-#cat:                each of its neighbors.
+#cat: insert_neighbor - Takes a minutia index and its squared distance to a
+#cat:               primary minutia point, and inserts them in the specified
+#cat:               position of their respective lists, shifting previously
+#cat:               stored values down and off the lists as necessary.
 
    Input:
-      minutiae  - list of minutiae
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      lfsparms  - parameters and thresholds for controlling LFS
+      pos       - postions where values are to be inserted in lists
+      nbr_index - index of minutia being inserted
+      nbr_dist2 - squared distance of minutia to its primary point
+      nbr_list  - current list of nearest neighbor minutia indices
+      nbr_sqr_dists - corresponding squared euclidean distance of each
+                  neighbor to the primary minutia point
+      nnbrs     - number of neighbors currently in the list
+      max_nbrs  - maximum number of closest neighbors to be returned
    Output:
-      minutiae  - list of minutiae augmented with neighbors and ridge counts
+      nbr_list - updated list of nearest neighbor indices
+      nbr_sqr_dists - updated list of nearest neighbor distances
+      nnbrs    - number of neighbors in the update lists
    Return Code:
-      Zero     - successful completion
-      Negative - system error
+      Zero      - successful completion
+      Negative  - system error
 **************************************************************************/
-int count_minutiae_ridges(MINUTIAE *minutiae,
-                      unsigned char *bdata, const int iw, const int ih,
-                      const LFSPARMS *lfsparms)
+static int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
+                    int *nbr_list, double *nbr_sqr_dists,
+                    int *nnbrs, const int max_nbrs)
 {
-   int ret;
    int i;
 
-   print2log("\nFINDING NBRS AND COUNTING RIDGES:\n");
-
-   /* Sort minutia points on x then y (column-oriented). */
-   if((ret = sort_minutiae_x_y(minutiae, iw, ih))){
-      return(ret);
+   /* If the desired insertion position is beyond one passed the last     */
+   /* neighbor in the lists OR greater than equal to the maximum ...      */
+   /* NOTE: pos is zero-oriented while nnbrs and max_nbrs are 1-oriented. */
+   if((pos > *nnbrs) ||
+      (pos >= max_nbrs)){
+      fprintf(stderr,
+              "ERROR : insert_neighbor : insertion point exceeds lists\n");
+      return(-480);
    }
 
-   /* Remove any duplicate minutia points from the list. */
-   if((ret = rm_dup_minutiae(minutiae))){
-      return(ret);
+   /* If the neighbor lists are NOT full ... */
+   if(*nnbrs < max_nbrs){
+      /* Then we have room to shift everything down to make room for new */
+      /* neighbor and increase the number of neighbors stored by 1.      */
+      i = *nnbrs-1;
+      (*nnbrs)++;
+   }
+   /* Otherwise, the neighbors lists are full ... */
+   else if(*nnbrs == max_nbrs)
+      /* So, we must bump the last neighbor in the lists off to make */
+      /* room for the new neighbor (ignore last neighbor in lists).  */
+      i = *nnbrs-2;
+   /* Otherwise, there is a list overflow error condition */
+   /* (shouldn't ever happen, but just in case) ...       */
+   else{
+      fprintf(stderr,
+              "ERROR : insert_neighbor : overflow in neighbor lists\n");
+      return(-481);
    }
 
-   /* Foreach remaining sorted minutia in list ... */
-   for(i = 0; i < minutiae->num-1; i++){
-      /* Located neighbors and count number of ridges in between. */
-      /* NOTE: neighbor and ridge count results are stored in     */
-      /*       minutiae->list[i].                                 */
-      if((ret = count_minutia_ridges(i, minutiae, bdata, iw, ih, lfsparms))){
-         return(ret);
-      }
+   /* While we havn't reached the desired insertion point ... */
+   while(i >= pos){
+      /* Shift the current neighbor down the list 1 positon. */
+      nbr_list[i+1] = nbr_list[i];
+      nbr_sqr_dists[i+1] = nbr_sqr_dists[i];
+      i--;
    }
 
+   /* We are now ready to put our new neighbor in the position where */
+   /* we shifted everything down from to make room.                  */
+   nbr_list[pos] = nbr_index;
+   nbr_sqr_dists[pos] = nbr_dist2;
+
    /* Return normally. */
    return(0);
 }
 
 /*************************************************************************
 **************************************************************************
-#cat: count_minutia_ridges - Takes a minutia, and determines its closest
-#cat:                neighbors and counts the number of interveining ridges
-#cat:                between the minutia point and each of its neighbors.
+#cat: update_nbr_dists - Takes the current list of neighbors along with a
+#cat:               primary minutia and a potential new neighbor, and
+#cat:               determines if the new neighbor is sufficiently close
+#cat:               to be added to the list of nearest neighbors.  If added,
+#cat:               it is placed in the list in its proper order based on
+#cat:               squared distance to the primary point.
 
    Input:
-      minutia   - input minutia
-      bdata     - binary image data (0==while & 1==black)
-      iw        - width (in pixels) of image
-      ih        - height (in pixels) of image
-      lfsparms  - parameters and thresholds for controlling LFS
+      nbr_list - current list of nearest neighbor minutia indices
+      nbr_sqr_dists - corresponding squared euclidean distance of each
+                 neighbor to the primary minutia point
+      nnbrs    - number of neighbors currently in the list
+      max_nbrs - maximum number of closest neighbors to be returned
+      first    - index of the primary minutia point
+      second   - index of the secondary (new neighbor) point
+      minutiae - list of minutiae
    Output:
-      minutiae  - minutia augmented with neighbors and ridge counts
+      nbr_list - updated list of nearest neighbor indices
+      nbr_sqr_dists - updated list of nearest neighbor distances
+      nnbrs    - number of neighbors in the update lists
    Return Code:
-      Zero     - successful completion
-      Negative - system error
+      Zero      - successful completion
+      Negative  - system error
 **************************************************************************/
-int count_minutia_ridges(const int first, MINUTIAE *minutiae,
-                      unsigned char *bdata, const int iw, const int ih,
-                      const LFSPARMS *lfsparms)
+static int update_nbr_dists(int *nbr_list, double *nbr_sqr_dists,
+                      int *nnbrs, const int max_nbrs,
+                      const int first, const int second, MINUTIAE *minutiae)
 {
-   int i, ret, *nbr_list, *nbr_nridges, nnbrs;
+   double dist2;
+   MINUTIA *minutia1, *minutia2;
+   int pos, last_nbr;
 
-   /* Find up to the maximum number of qualifying neighbors. */
-   if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
-                           first, minutiae))){
-      free(nbr_list);
-      return(ret);
-   }
+   /* Compute position of maximum last neighbor stored. */
+   last_nbr = max_nbrs - 1;
 
-   print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x,
-              minutiae->list[first]->y, nnbrs);
+   /* Assigne temporary minutia pointers. */
+   minutia1 = minutiae->list[first];
+   minutia2 = minutiae->list[second];
 
-   /* If no neighors found ... */
-   if(nnbrs == 0){
-      /* Then no list returned and no ridges to count. */
+   /* Compute squared euclidean distance between minutia pair. */
+   dist2 = squared_distance(minutia1->x, minutia1->y,
+                            minutia2->x, minutia2->y);
+
+   /* If maximum number of neighbors not yet stored in lists OR */
+   /* if the squared distance to current secondary is less      */
+   /* than the largest stored neighbor distance ...             */
+   if((*nnbrs < max_nbrs) ||
+      (dist2 < nbr_sqr_dists[last_nbr])){
+
+      /* Find insertion point in neighbor lists. */
+      pos = find_incr_position_dbl(dist2, nbr_sqr_dists, *nnbrs);
+      /* If the position returned is >= maximum list length (this should */
+      /* never happen, but just in case) ...                             */
+      if(pos >= max_nbrs){
+         fprintf(stderr, 
+         "ERROR : update_nbr_dists : illegal position for new neighbor\n");
+         return(-470);
+      }
+      /* Insert the new neighbor into the neighbor lists at the */
+      /* specified location.                                    */
+      if(insert_neighbor(pos, second, dist2,
+                         nbr_list, nbr_sqr_dists, nnbrs, max_nbrs))
+         return(-471);
+
+      /* Otherwise, neighbor inserted successfully, so return normally. */
       return(0);
    }
+   /* Otherwise, the new neighbor is not sufficiently close to be       */
+   /* added or inserted into the neighbor lists, so ignore the neighbor */
+   /* and return normally.                                              */
+   else
+      return(0);
 
-   /* Sort neighbors on delta dirs. */
-   if((ret = sort_neighbors(nbr_list, nnbrs, first, minutiae))){
-      free(nbr_list);
-      return(ret);
-   }
-
-   /* Count ridges between first and neighbors. */
-   /* List of ridge counts, one for each neighbor stored. */
-   nbr_nridges = (int *)malloc(nnbrs * sizeof(int));
-   if(nbr_nridges == (int *)NULL){
-      free(nbr_list);
-      fprintf(stderr, "ERROR : count_minutia_ridges : malloc : nbr_nridges\n");
-      return(-450);
-   }
-
-   /* Foreach neighbor found and sorted in list ... */
-   for(i = 0; i < nnbrs; i++){
-      /* Count the ridges between the primary minutia and the neighbor. */
-      ret = ridge_count(first, nbr_list[i], minutiae, bdata, iw, ih, lfsparms);
-      /* If system error ... */
-      if(ret < 0){
-         /* Deallocate working memories. */
-         free(nbr_list);
-         free(nbr_nridges);
-         /* Return error code. */
-         return(ret);
-      }
-
-      /* Otherwise, ridge count successful, so store ridge count to list. */
-      nbr_nridges[i] = ret;
-   }
-
-   /* Assign neighbor indices and ridge counts to primary minutia. */
-   minutiae->list[first]->nbrs = nbr_list;
-   minutiae->list[first]->ridge_counts = nbr_nridges;
-   minutiae->list[first]->num_nbrs = nnbrs;
-
-   /* Return normally. */
-   return(0);
 }
 
 /*************************************************************************
@@ -203,7 +224,7 @@ int count_minutia_ridges(const int first, MINUTIAE *minutiae,
       Zero      - successful completion
       Negative  - system error
 **************************************************************************/
-int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
+static int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
                    const int first, MINUTIAE *minutiae)
 {
    int ret, second, last_nbr;
@@ -291,160 +312,6 @@ int find_neighbors(int **onbr_list, int *onnbrs, const int max_nbrs,
    return(0);
 }
 
-/*************************************************************************
-**************************************************************************
-#cat: update_nbr_dists - Takes the current list of neighbors along with a
-#cat:               primary minutia and a potential new neighbor, and
-#cat:               determines if the new neighbor is sufficiently close
-#cat:               to be added to the list of nearest neighbors.  If added,
-#cat:               it is placed in the list in its proper order based on
-#cat:               squared distance to the primary point.
-
-   Input:
-      nbr_list - current list of nearest neighbor minutia indices
-      nbr_sqr_dists - corresponding squared euclidean distance of each
-                 neighbor to the primary minutia point
-      nnbrs    - number of neighbors currently in the list
-      max_nbrs - maximum number of closest neighbors to be returned
-      first    - index of the primary minutia point
-      second   - index of the secondary (new neighbor) point
-      minutiae - list of minutiae
-   Output:
-      nbr_list - updated list of nearest neighbor indices
-      nbr_sqr_dists - updated list of nearest neighbor distances
-      nnbrs    - number of neighbors in the update lists
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int update_nbr_dists(int *nbr_list, double *nbr_sqr_dists,
-                      int *nnbrs, const int max_nbrs,
-                      const int first, const int second, MINUTIAE *minutiae)
-{
-   double dist2;
-   MINUTIA *minutia1, *minutia2;
-   int pos, last_nbr;
-
-   /* Compute position of maximum last neighbor stored. */
-   last_nbr = max_nbrs - 1;
-
-   /* Assigne temporary minutia pointers. */
-   minutia1 = minutiae->list[first];
-   minutia2 = minutiae->list[second];
-
-   /* Compute squared euclidean distance between minutia pair. */
-   dist2 = squared_distance(minutia1->x, minutia1->y,
-                            minutia2->x, minutia2->y);
-
-   /* If maximum number of neighbors not yet stored in lists OR */
-   /* if the squared distance to current secondary is less      */
-   /* than the largest stored neighbor distance ...             */
-   if((*nnbrs < max_nbrs) ||
-      (dist2 < nbr_sqr_dists[last_nbr])){
-
-      /* Find insertion point in neighbor lists. */
-      pos = find_incr_position_dbl(dist2, nbr_sqr_dists, *nnbrs);
-      /* If the position returned is >= maximum list length (this should */
-      /* never happen, but just in case) ...                             */
-      if(pos >= max_nbrs){
-         fprintf(stderr, 
-         "ERROR : update_nbr_dists : illegal position for new neighbor\n");
-         return(-470);
-      }
-      /* Insert the new neighbor into the neighbor lists at the */
-      /* specified location.                                    */
-      if(insert_neighbor(pos, second, dist2,
-                         nbr_list, nbr_sqr_dists, nnbrs, max_nbrs))
-         return(-471);
-
-      /* Otherwise, neighbor inserted successfully, so return normally. */
-      return(0);
-   }
-   /* Otherwise, the new neighbor is not sufficiently close to be       */
-   /* added or inserted into the neighbor lists, so ignore the neighbor */
-   /* and return normally.                                              */
-   else
-      return(0);
-
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: insert_neighbor - Takes a minutia index and its squared distance to a
-#cat:               primary minutia point, and inserts them in the specified
-#cat:               position of their respective lists, shifting previously
-#cat:               stored values down and off the lists as necessary.
-
-   Input:
-      pos       - postions where values are to be inserted in lists
-      nbr_index - index of minutia being inserted
-      nbr_dist2 - squared distance of minutia to its primary point
-      nbr_list  - current list of nearest neighbor minutia indices
-      nbr_sqr_dists - corresponding squared euclidean distance of each
-                  neighbor to the primary minutia point
-      nnbrs     - number of neighbors currently in the list
-      max_nbrs  - maximum number of closest neighbors to be returned
-   Output:
-      nbr_list - updated list of nearest neighbor indices
-      nbr_sqr_dists - updated list of nearest neighbor distances
-      nnbrs    - number of neighbors in the update lists
-   Return Code:
-      Zero      - successful completion
-      Negative  - system error
-**************************************************************************/
-int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
-                    int *nbr_list, double *nbr_sqr_dists,
-                    int *nnbrs, const int max_nbrs)
-{
-   int i;
-
-   /* If the desired insertion position is beyond one passed the last     */
-   /* neighbor in the lists OR greater than equal to the maximum ...      */
-   /* NOTE: pos is zero-oriented while nnbrs and max_nbrs are 1-oriented. */
-   if((pos > *nnbrs) ||
-      (pos >= max_nbrs)){
-      fprintf(stderr,
-              "ERROR : insert_neighbor : insertion point exceeds lists\n");
-      return(-480);
-   }
-
-   /* If the neighbor lists are NOT full ... */
-   if(*nnbrs < max_nbrs){
-      /* Then we have room to shift everything down to make room for new */
-      /* neighbor and increase the number of neighbors stored by 1.      */
-      i = *nnbrs-1;
-      (*nnbrs)++;
-   }
-   /* Otherwise, the neighbors lists are full ... */
-   else if(*nnbrs == max_nbrs)
-      /* So, we must bump the last neighbor in the lists off to make */
-      /* room for the new neighbor (ignore last neighbor in lists).  */
-      i = *nnbrs-2;
-   /* Otherwise, there is a list overflow error condition */
-   /* (shouldn't ever happen, but just in case) ...       */
-   else{
-      fprintf(stderr,
-              "ERROR : insert_neighbor : overflow in neighbor lists\n");
-      return(-481);
-   }
-
-   /* While we havn't reached the desired insertion point ... */
-   while(i >= pos){
-      /* Shift the current neighbor down the list 1 positon. */
-      nbr_list[i+1] = nbr_list[i];
-      nbr_sqr_dists[i+1] = nbr_sqr_dists[i];
-      i--;
-   }
-
-   /* We are now ready to put our new neighbor in the position where */
-   /* we shifted everything down from to make room.                  */
-   nbr_list[pos] = nbr_index;
-   nbr_sqr_dists[pos] = nbr_dist2;
-
-   /* Return normally. */
-   return(0);
-}
-
 /*************************************************************************
 **************************************************************************
 #cat: sort_neighbors - Takes a list of primary minutia and its neighboring
@@ -464,7 +331,7 @@ int insert_neighbor(const int pos, const int nbr_index, const double nbr_dist2,
       Zero     - successful completion
       Negative - system error
 **************************************************************************/
-int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
+static int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
                    MINUTIAE *minutiae)
 {
    double *join_thetas, theta;
@@ -505,6 +372,174 @@ int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
    return(0);      
 }
 
+/*************************************************************************
+**************************************************************************
+#cat: find_transition - Takes a pixel trajectory and a starting index, and
+#cat:               searches forward along the trajectory until the specified
+#cat:               adjacent pixel pair is found, returning the index where
+#cat:               the pair was found (the index of the second pixel).
+
+   Input:
+      iptr  - pointer to starting pixel index into trajectory
+      pix1  - first pixel value in transition pair
+      pix2  - second pixel value in transition pair
+      xlist - x-pixel coords of line trajectory
+      ylist - y-pixel coords of line trajectory
+      num   - number of coords in line trajectory
+      bdata - binary image data (0==while & 1==black)
+      iw    - width (in pixels) of image
+      ih    - height (in pixels) of image
+   Output:
+      iptr  - points to location where 2nd pixel in pair is found
+   Return Code:
+      TRUE  - pixel pair transition found
+      FALSE - pixel pair transition not found
+**************************************************************************/
+static int find_transition(int *iptr, const int pix1, const int pix2,
+                    const int *xlist, const int *ylist, const int num,
+                    unsigned char *bdata, const int iw, const int ih)
+{
+   int i, j;
+
+   /* Set previous index to starting position. */
+   i = *iptr;
+   /* Bump previous index by 1 to get next index. */
+   j = i+1;
+
+   /* While not one point from the end of the trajectory .. */
+   while(i < num-1){
+      /* If we have found the desired transition ... */
+      if((*(bdata+(ylist[i]*iw)+xlist[i]) == pix1) &&
+         (*(bdata+(ylist[j]*iw)+xlist[j]) == pix2)){
+         /* Adjust the position pointer to the location of the */
+         /* second pixel in the transition.                    */
+         *iptr = j;
+
+         /* Return TRUE. */
+         return(TRUE);
+      }
+      /* Otherwise, the desired transition was not found in current */
+      /* pixel pair, so bump to the next pair along the trajector.  */
+      i++;
+      j++;
+   }
+
+   /* If we get here, then we exhausted the trajector without finding */
+   /* the desired transition, so set the position pointer to the end  */
+   /* of the trajector, and return FALSE.                             */
+   *iptr = num;
+   return(FALSE);
+}
+
+/*************************************************************************
+**************************************************************************
+#cat: validate_ridge_crossing - Takes a pair of points, a ridge start
+#cat:               transition and a ridge end transition, and walks the
+#cat:               ridge contour from thre ridge end points a specified
+#cat:               number of steps, looking for the ridge start point.
+#cat:               If found, then transitions determined not to be a valid
+#cat:               ridge crossing.
+
+   Input:
+      ridge_start - index into line trajectory of ridge start transition
+      ridge_end   - index into line trajectory of ridge end transition
+      xlist       - x-pixel coords of line trajectory
+      ylist       - y-pixel coords of line trajectory
+      num         - number of coords in line trajectory
+      bdata       - binary image data (0==while & 1==black)
+      iw          - width (in pixels) of image
+      ih          - height (in pixels) of image
+      max_ridge_steps  - number of steps taken in search in both
+                         scan directions
+   Return Code:
+      TRUE        - ridge crossing VALID
+      FALSE       - ridge corssing INVALID
+      Negative    - system error
+**************************************************************************/
+static int validate_ridge_crossing(const int ridge_start, const int ridge_end,
+                            const int *xlist, const int *ylist, const int num,
+                            unsigned char *bdata, const int iw, const int ih,
+                            const int max_ridge_steps)
+{
+   int ret;
+   int feat_x, feat_y, edge_x, edge_y;
+   int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
+
+   /* Assign edge pixel pair for contour trace. */
+   feat_x = xlist[ridge_end];
+   feat_y = ylist[ridge_end];
+   edge_x = xlist[ridge_end-1];
+   edge_y = ylist[ridge_end-1];
+
+   /* Adjust pixel pair if they neighbor each other diagonally. */
+   fix_edge_pixel_pair(&feat_x, &feat_y, &edge_x, &edge_y,
+                       bdata, iw, ih);
+
+   /* Trace ridge contour, starting at the ridge end transition, and */
+   /* taking a specified number of step scanning for edge neighbors  */
+   /* clockwise.  As we trace the ridge, we want to detect if we     */
+   /* encounter the ridge start transition.  NOTE: The ridge end     */
+   /* position is on the white (of a black to white transition) and  */
+   /* the ridge start is on the black (of a black to white trans),   */
+   /* so the edge trace needs to look for the what pixel (not the    */
+   /* black one) of the ridge start transition.                      */	
+   ret = trace_contour(&contour_x, &contour_y,
+                       &contour_ex, &contour_ey, &ncontour,
+                       max_ridge_steps,
+                       xlist[ridge_start-1], ylist[ridge_start-1],
+                       feat_x, feat_y, edge_x, edge_y,
+                       SCAN_CLOCKWISE, bdata, iw, ih);
+   /* If a system error occurred ... */
+   if(ret < 0)
+      /* Return error code. */
+      return(ret);
+
+   /* Otherwise, if the trace was not IGNORED, then a contour was */
+   /* was generated and returned.  We aren't interested in the    */
+   /* actual contour, so deallocate it.                           */
+   if(ret != IGNORE)
+      free_contour(contour_x, contour_y, contour_ex, contour_ey);
+
+   /* If the trace was IGNORED, then we had some sort of initialization */
+   /* problem, so treat this the same as if was actually located the    */
+   /* ridge start point (in which case LOOP_FOUND is returned).         */
+   /* So, If not IGNORED and ridge start not encounted in trace ...     */
+   if((ret != IGNORE) &&
+      (ret != LOOP_FOUND)){
+
+      /* Now conduct contour trace scanning for edge neighbors counter- */
+      /* clockwise.                                                     */
+      ret = trace_contour(&contour_x, &contour_y,
+                          &contour_ex, &contour_ey, &ncontour,
+                          max_ridge_steps,
+                          xlist[ridge_start-1], ylist[ridge_start-1],
+                          feat_x, feat_y, edge_x, edge_y,
+                          SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
+      /* If a system error occurred ... */
+      if(ret < 0)
+         /* Return error code. */
+         return(ret);
+
+      /* Otherwise, if the trace was not IGNORED, then a contour was */
+      /* was generated and returned.  We aren't interested in the    */
+      /* actual contour, so deallocate it.                           */
+      if(ret != IGNORE)
+         free_contour(contour_x, contour_y, contour_ex, contour_ey);
+
+      /* If trace not IGNORED and ridge start not encounted in 2nd trace ... */
+      if((ret != IGNORE) &&
+         (ret != LOOP_FOUND)){
+         /* If we get here, assume we have a ridge crossing. */
+         return(TRUE);
+      }
+      /* Otherwise, second trace returned IGNORE or ridge start found. */
+   }
+   /* Otherwise, first trace returned IGNORE or ridge start found. */
+   
+   /* If we get here, then we failed to validate a ridge crossing. */
+   return(FALSE);
+}
+
 /*************************************************************************
 **************************************************************************
 #cat: ridge_count - Takes a pair of minutiae, and counts the number of
@@ -523,7 +558,7 @@ int sort_neighbors(int *nbr_list, const int nnbrs, const int first,
       Zero or Positive - number of ridges counted
       Negative         - system error
 **************************************************************************/
-int ridge_count(const int first, const int second, MINUTIAE *minutiae,
+static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
                 unsigned char *bdata, const int iw, const int ih,
                 const LFSPARMS *lfsparms)
 {
@@ -664,168 +699,134 @@ int ridge_count(const int first, const int second, MINUTIAE *minutiae,
 
 /*************************************************************************
 **************************************************************************
-#cat: find_transition - Takes a pixel trajectory and a starting index, and
-#cat:               searches forward along the trajectory until the specified
-#cat:               adjacent pixel pair is found, returning the index where
-#cat:               the pair was found (the index of the second pixel).
+#cat: count_minutia_ridges - Takes a minutia, and determines its closest
+#cat:                neighbors and counts the number of interveining ridges
+#cat:                between the minutia point and each of its neighbors.
 
    Input:
-      iptr  - pointer to starting pixel index into trajectory
-      pix1  - first pixel value in transition pair
-      pix2  - second pixel value in transition pair
-      xlist - x-pixel coords of line trajectory
-      ylist - y-pixel coords of line trajectory
-      num   - number of coords in line trajectory
-      bdata - binary image data (0==while & 1==black)
-      iw    - width (in pixels) of image
-      ih    - height (in pixels) of image
+      minutia   - input minutia
+      bdata     - binary image data (0==while & 1==black)
+      iw        - width (in pixels) of image
+      ih        - height (in pixels) of image
+      lfsparms  - parameters and thresholds for controlling LFS
    Output:
-      iptr  - points to location where 2nd pixel in pair is found
+      minutiae  - minutia augmented with neighbors and ridge counts
    Return Code:
-      TRUE  - pixel pair transition found
-      FALSE - pixel pair transition not found
+      Zero     - successful completion
+      Negative - system error
 **************************************************************************/
-int find_transition(int *iptr, const int pix1, const int pix2,
-                    const int *xlist, const int *ylist, const int num,
-                    unsigned char *bdata, const int iw, const int ih)
+static int count_minutia_ridges(const int first, MINUTIAE *minutiae,
+                      unsigned char *bdata, const int iw, const int ih,
+                      const LFSPARMS *lfsparms)
 {
-   int i, j;
+   int i, ret, *nbr_list, *nbr_nridges, nnbrs;
 
-   /* Set previous index to starting position. */
-   i = *iptr;
-   /* Bump previous index by 1 to get next index. */
-   j = i+1;
-
-   /* While not one point from the end of the trajectory .. */
-   while(i < num-1){
-      /* If we have found the desired transition ... */
-      if((*(bdata+(ylist[i]*iw)+xlist[i]) == pix1) &&
-         (*(bdata+(ylist[j]*iw)+xlist[j]) == pix2)){
-         /* Adjust the position pointer to the location of the */
-         /* second pixel in the transition.                    */
-         *iptr = j;
-
-         /* Return TRUE. */
-         return(TRUE);
-      }
-      /* Otherwise, the desired transition was not found in current */
-      /* pixel pair, so bump to the next pair along the trajector.  */
-      i++;
-      j++;
+   /* Find up to the maximum number of qualifying neighbors. */
+   if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
+                           first, minutiae))){
+      free(nbr_list);
+      return(ret);
    }
 
-   /* If we get here, then we exhausted the trajector without finding */
-   /* the desired transition, so set the position pointer to the end  */
-   /* of the trajector, and return FALSE.                             */
-   *iptr = num;
-   return(FALSE);
+   print2log("NBRS FOUND: %d,%d = %d\n", minutiae->list[first]->x,
+              minutiae->list[first]->y, nnbrs);
+
+   /* If no neighors found ... */
+   if(nnbrs == 0){
+      /* Then no list returned and no ridges to count. */
+      return(0);
+   }
+
+   /* Sort neighbors on delta dirs. */
+   if((ret = sort_neighbors(nbr_list, nnbrs, first, minutiae))){
+      free(nbr_list);
+      return(ret);
+   }
+
+   /* Count ridges between first and neighbors. */
+   /* List of ridge counts, one for each neighbor stored. */
+   nbr_nridges = (int *)malloc(nnbrs * sizeof(int));
+   if(nbr_nridges == (int *)NULL){
+      free(nbr_list);
+      fprintf(stderr, "ERROR : count_minutia_ridges : malloc : nbr_nridges\n");
+      return(-450);
+   }
+
+   /* Foreach neighbor found and sorted in list ... */
+   for(i = 0; i < nnbrs; i++){
+      /* Count the ridges between the primary minutia and the neighbor. */
+      ret = ridge_count(first, nbr_list[i], minutiae, bdata, iw, ih, lfsparms);
+      /* If system error ... */
+      if(ret < 0){
+         /* Deallocate working memories. */
+         free(nbr_list);
+         free(nbr_nridges);
+         /* Return error code. */
+         return(ret);
+      }
+
+      /* Otherwise, ridge count successful, so store ridge count to list. */
+      nbr_nridges[i] = ret;
+   }
+
+   /* Assign neighbor indices and ridge counts to primary minutia. */
+   minutiae->list[first]->nbrs = nbr_list;
+   minutiae->list[first]->ridge_counts = nbr_nridges;
+   minutiae->list[first]->num_nbrs = nnbrs;
+
+   /* Return normally. */
+   return(0);
 }
 
 /*************************************************************************
 **************************************************************************
-#cat: validate_ridge_crossing - Takes a pair of points, a ridge start
-#cat:               transition and a ridge end transition, and walks the
-#cat:               ridge contour from thre ridge end points a specified
-#cat:               number of steps, looking for the ridge start point.
-#cat:               If found, then transitions determined not to be a valid
-#cat:               ridge crossing.
+#cat: count_minutiae_ridges - Takes a list of minutiae, and for each one,
+#cat:                determines its closest neighbors and counts the number
+#cat:                of interveining ridges between the minutia point and
+#cat:                each of its neighbors.
 
    Input:
-      ridge_start - index into line trajectory of ridge start transition
-      ridge_end   - index into line trajectory of ridge end transition
-      xlist       - x-pixel coords of line trajectory
-      ylist       - y-pixel coords of line trajectory
-      num         - number of coords in line trajectory
-      bdata       - binary image data (0==while & 1==black)
-      iw          - width (in pixels) of image
-      ih          - height (in pixels) of image
-      max_ridge_steps  - number of steps taken in search in both
-                         scan directions
+      minutiae  - list of minutiae
+      bdata     - binary image data (0==while & 1==black)
+      iw        - width (in pixels) of image
+      ih        - height (in pixels) of image
+      lfsparms  - parameters and thresholds for controlling LFS
+   Output:
+      minutiae  - list of minutiae augmented with neighbors and ridge counts
    Return Code:
-      TRUE        - ridge crossing VALID
-      FALSE       - ridge corssing INVALID
-      Negative    - system error
+      Zero     - successful completion
+      Negative - system error
 **************************************************************************/
-int validate_ridge_crossing(const int ridge_start, const int ridge_end,
-                            const int *xlist, const int *ylist, const int num,
-                            unsigned char *bdata, const int iw, const int ih,
-                            const int max_ridge_steps)
+int count_minutiae_ridges(MINUTIAE *minutiae,
+                      unsigned char *bdata, const int iw, const int ih,
+                      const LFSPARMS *lfsparms)
 {
    int ret;
-   int feat_x, feat_y, edge_x, edge_y;
-   int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
+   int i;
 
-   /* Assign edge pixel pair for contour trace. */
-   feat_x = xlist[ridge_end];
-   feat_y = ylist[ridge_end];
-   edge_x = xlist[ridge_end-1];
-   edge_y = ylist[ridge_end-1];
+   print2log("\nFINDING NBRS AND COUNTING RIDGES:\n");
 
-   /* Adjust pixel pair if they neighbor each other diagonally. */
-   fix_edge_pixel_pair(&feat_x, &feat_y, &edge_x, &edge_y,
-                       bdata, iw, ih);
-
-   /* Trace ridge contour, starting at the ridge end transition, and */
-   /* taking a specified number of step scanning for edge neighbors  */
-   /* clockwise.  As we trace the ridge, we want to detect if we     */
-   /* encounter the ridge start transition.  NOTE: The ridge end     */
-   /* position is on the white (of a black to white transition) and  */
-   /* the ridge start is on the black (of a black to white trans),   */
-   /* so the edge trace needs to look for the what pixel (not the    */
-   /* black one) of the ridge start transition.                      */	
-   ret = trace_contour(&contour_x, &contour_y,
-                       &contour_ex, &contour_ey, &ncontour,
-                       max_ridge_steps,
-                       xlist[ridge_start-1], ylist[ridge_start-1],
-                       feat_x, feat_y, edge_x, edge_y,
-                       SCAN_CLOCKWISE, bdata, iw, ih);
-   /* If a system error occurred ... */
-   if(ret < 0)
-      /* Return error code. */
+   /* Sort minutia points on x then y (column-oriented). */
+   if((ret = sort_minutiae_x_y(minutiae, iw, ih))){
       return(ret);
-
-   /* Otherwise, if the trace was not IGNORED, then a contour was */
-   /* was generated and returned.  We aren't interested in the    */
-   /* actual contour, so deallocate it.                           */
-   if(ret != IGNORE)
-      free_contour(contour_x, contour_y, contour_ex, contour_ey);
-
-   /* If the trace was IGNORED, then we had some sort of initialization */
-   /* problem, so treat this the same as if was actually located the    */
-   /* ridge start point (in which case LOOP_FOUND is returned).         */
-   /* So, If not IGNORED and ridge start not encounted in trace ...     */
-   if((ret != IGNORE) &&
-      (ret != LOOP_FOUND)){
-
-      /* Now conduct contour trace scanning for edge neighbors counter- */
-      /* clockwise.                                                     */
-      ret = trace_contour(&contour_x, &contour_y,
-                          &contour_ex, &contour_ey, &ncontour,
-                          max_ridge_steps,
-                          xlist[ridge_start-1], ylist[ridge_start-1],
-                          feat_x, feat_y, edge_x, edge_y,
-                          SCAN_COUNTER_CLOCKWISE, bdata, iw, ih);
-      /* If a system error occurred ... */
-      if(ret < 0)
-         /* Return error code. */
-         return(ret);
-
-      /* Otherwise, if the trace was not IGNORED, then a contour was */
-      /* was generated and returned.  We aren't interested in the    */
-      /* actual contour, so deallocate it.                           */
-      if(ret != IGNORE)
-         free_contour(contour_x, contour_y, contour_ex, contour_ey);
-
-      /* If trace not IGNORED and ridge start not encounted in 2nd trace ... */
-      if((ret != IGNORE) &&
-         (ret != LOOP_FOUND)){
-         /* If we get here, assume we have a ridge crossing. */
-         return(TRUE);
-      }
-      /* Otherwise, second trace returned IGNORE or ridge start found. */
    }
-   /* Otherwise, first trace returned IGNORE or ridge start found. */
-   
-   /* If we get here, then we failed to validate a ridge crossing. */
-   return(FALSE);
+
+   /* Remove any duplicate minutia points from the list. */
+   if((ret = rm_dup_minutiae(minutiae))){
+      return(ret);
+   }
+
+   /* Foreach remaining sorted minutia in list ... */
+   for(i = 0; i < minutiae->num-1; i++){
+      /* Located neighbors and count number of ridges in between. */
+      /* NOTE: neighbor and ridge count results are stored in     */
+      /*       minutiae->list[i].                                 */
+      if((ret = count_minutia_ridges(i, minutiae, bdata, iw, ih, lfsparms))){
+         return(ret);
+      }
+   }
+
+   /* Return normally. */
+   return(0);
 }
+
diff --git a/libfprint/nbis/mindtct/xytreps.c b/libfprint/nbis/mindtct/xytreps.c
deleted file mode 100644
index 65621ea..0000000
--- a/libfprint/nbis/mindtct/xytreps.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*******************************************************************************
-
-License: 
-This software was developed at the National Institute of Standards and 
-Technology (NIST) by employees of the Federal Government in the course 
-of their official duties. Pursuant to title 17 Section 105 of the 
-United States Code, this software is not subject to copyright protection 
-and is in the public domain. NIST assumes no responsibility  whatsoever for 
-its use by other parties, and makes no guarantees, expressed or implied, 
-about its quality, reliability, or any other characteristic. 
-
-Disclaimer: 
-This software was developed to promote biometric standards and biometric
-technology testing for the Federal Government in accordance with the USA
-PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
-Specific hardware and software products identified in this software were used
-in order to perform the software development.  In no case does such
-identification imply recommendation or endorsement by the National Institute
-of Standards and Technology, nor does it imply that the products and equipment
-identified are necessarily the best available for the purpose.  
-
-*******************************************************************************/
-
-/***********************************************************************
-      LIBRARY: LFS - NIST Latent Fingerprint System
-
-      FILE:    XYTREPS.C
-      AUTHOR:  Michael D. Garris
-      DATE:    09/16/2004
-
-      Contains routines useful in converting minutiae in LFS "native"
-      representation into other representations, such as
-      M1 (ANSI INCITS 378-2004) & NIST internal representations.
-
-***********************************************************************
-               ROUTINES:
-                        lfs2nist_minutia_XTY()
-                        lfs2m1_minutia_XTY()
-
-***********************************************************************/
-
-#include <lfs.h>
-#include <defs.h>
-
-/*************************************************************************
-**************************************************************************
-#cat: lfs2nist_minutia_XYT - Converts XYT minutiae attributes in LFS native
-#cat:        representation to NIST internal representation
-
-   Input:
-      minutia  - LFS minutia structure containing attributes to be converted
-   Output:
-      ox       - NIST internal based x-pixel coordinate
-      oy       - NIST internal based y-pixel coordinate
-      ot       - NIST internal based minutia direction/orientation
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-void lfs2nist_minutia_XYT(int *ox, int *oy, int *ot,
-                          const MINUTIA *minutia, const int iw, const int ih)
-{
-   int x, y, t;
-   float degrees_per_unit;
-
-   /*       XYT's according to NIST internal rep:           */
-    /*      1. pixel coordinates with origin bottom-left    */
-   /*       2. orientation in degrees on range [0..360]     */
-   /*          with 0 pointing east and increasing counter  */
-   /*          clockwise (same as M1)                       */
-   /*       3. direction pointing out and away from the     */
-   /*             ridge ending or bifurcation valley        */
-   /*             (opposite direction from M1)              */
-
-   x = minutia->x;
-   y = ih - minutia->y;
-
-   degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
-
-   t = (270 - sround(minutia->direction * degrees_per_unit)) % 360;
-   if(t < 0){
-      t += 360;
-   }
-
-   *ox = x;
-   *oy = y;
-   *ot = t;
-}
-
-/*************************************************************************
-**************************************************************************
-#cat: lfs2m1_minutia_XYT - Converts XYT minutiae attributes in LFS native
-#cat:        representation to M1 (ANSI INCITS 378-2004) representation
-
-   Input:
-      minutia  - LFS minutia structure containing attributes to be converted
-   Output:
-      ox       - M1 based x-pixel coordinate
-      oy       - M1 based y-pixel coordinate
-      ot       - M1 based minutia direction/orientation
-   Return Code:
-      Zero     - successful completion
-      Negative - system error
-**************************************************************************/
-void lfs2m1_minutia_XYT(int *ox, int *oy, int *ot, const MINUTIA *minutia)
-{
-   int x, y, t;
-   float degrees_per_unit;
-
-   /*       XYT's according to M1 (ANSI INCITS 378-2004):   */ 
-   /*       1. pixel coordinates with origin top-left       */
-   /*       2. orientation in degrees on range [0..179]     */
-   /*          with 0 pointing east and increasing counter  */
-   /*          clockwise                                    */
-   /*       3. direction pointing up the ridge ending or    */
-   /*             bifurcaiton valley                        */
-
-   x = minutia->x;
-   y = minutia->y;
-
-   degrees_per_unit = 180 / (float)NUM_DIRECTIONS;
-   t = (90 - sround(minutia->direction * degrees_per_unit)) % 360;
-   if(t < 0){
-      t += 360;
-   }
-
-   /* range of theta is 0..179 because angles are in units of 2 degress */
-   t = t / 2;
-
-   *ox = x;
-   *oy = y;
-   *ot = t;
-}