[med-svn] [dascrubber] 01/01: Imported Upstream version 0~20160601

Afif Elghraoui afif at moszumanska.debian.org
Thu Oct 20 08:19:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

afif pushed a commit to branch master
in repository dascrubber.

commit b2bacc891f58950d4e09fcd8aecde21cdebec78a
Author: Afif Elghraoui <afif at ghraoui.name>
Date:   Thu Oct 20 00:18:15 2016 -0700

    Imported Upstream version 0~20160601
---
 DASqv.c   |  619 ++++++++
 DAStrim.c | 1866 ++++++++++++++++++++++
 DB.c      | 1733 +++++++++++++++++++++
 DB.h      |  417 +++++
 LICENSE   |   34 +
 Makefile  |   25 +
 QV.c      | 1387 +++++++++++++++++
 QV.h      |   96 ++
 README    |   47 +
 align.c   | 5132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 align.h   |  335 ++++
 11 files changed, 11691 insertions(+)

diff --git a/DASqv.c b/DASqv.c
new file mode 100644
index 0000000..131e3f1
--- /dev/null
+++ b/DASqv.c
@@ -0,0 +1,619 @@
+/*******************************************************************************************
+ *
+ *  Using overlap pile for each read compute estimated intrinisic quality values
+ *
+ *  Author:  Gene Myers
+ *  Date  :  September 2015
+ *
+ *******************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "DB.h"
+#include "align.h"
+
+#ifdef HIDE_FILES
+#define PATHSEP "/."
+#else
+#define PATHSEP "/"
+#endif
+
+#undef  QV_DEBUG
+
+static char *Usage = "[-v] -c<int> <source:db> <overlaps:las> ...";
+
+#define  MAXQV   50     //  Max QV score is 50
+#define  MAXQV1  51
+#define  MINCOV   2     //  To have a score must be covered >= MINCOV in each direction (must be >0)
+
+#define  PARTIAL .20    //  Partial terminal segments covering this percentage are scored
+
+static int     QV_DEEP; //  # of best diffs to average for QV score
+static int     VERBOSE;
+
+static int     TRACE_SPACING;  //  Trace spacing (from .las file)
+static int     TBYTES;         //  Bytes per trace segment (from .las file)
+
+static HITS_DB _DB, *DB  = &_DB;   //  Data base
+static int     DB_FIRST;           //  First read of DB to process
+static int     DB_LAST;            //  Last read of DB to process (+1)
+static int     DB_PART;            //  0 if all, otherwise block #
+
+static FILE   *QV_AFILE;   //  .qual.anno
+static FILE   *QV_DFILE;   //  .qual.data
+static int64   QV_INDEX;   //  Current index into .qual.data file
+
+//  Statistics
+
+static int64 nreads, totlen;
+static int64 qgram[MAXQV1], sgram[MAXQV1];
+
+
+//  For each pile, calculate QV scores of the aread at tick spacing TRACE_SPACING
+
+static void CALCULATE_QVS(int aread, Overlap *ovls, int novl)
+{ static int    nmax = 0;
+  static int   *hist = NULL;
+  static int   *cist = NULL;
+  static uint8 *qvec = NULL;
+  static int    partial;
+
+  int  alen, atick;
+  int *tick, *cick;
+  int  i;
+
+  alen  = DB->reads[aread].rlen;
+  atick = (alen + (TRACE_SPACING-1))/TRACE_SPACING;
+
+#if defined(QV_DEBUG)
+  printf("AREAD %d",aread);
+  if (novl == 0)
+    printf(" EMPTY");
+  printf("\n");
+#endif
+
+  //  QV SCORES
+  //       Allocate or expand data structures for qv calculation as needed
+
+  if (atick > nmax)
+    { nmax = atick*1.2 + 100;
+
+      hist = (int *) Realloc(hist,nmax*MAXQV1*sizeof(int),"Allocating histograms");
+      cist = (int *) Realloc(cist,nmax*MAXQV1*sizeof(int),"Allocating histograms");
+      qvec = (uint8 *) Realloc(qvec,nmax*sizeof(uint8),"Allocating QV vector");
+
+      if (hist == NULL || cist == NULL || qvec == NULL)
+        exit (1);
+
+      for (i = MAXQV1*nmax-1; i >= 0; i--)
+        hist[i] = cist[i] = 0;
+      partial = PARTIAL*TRACE_SPACING;
+    }
+
+  //  For every segment, fill histogram of match diffs for every one of the
+  //   atick intervals, building separate histograms, hist & cist, for forward
+  //   and reverse B-hits
+
+  for (i = 0; i < novl; i++)
+    { Path   *path;
+      uint16 *trace;
+      int    *ht;
+      int     tlen, abit;
+      int     a, b, x;
+
+      path  = &(ovls[i].path);
+      trace = (uint16 *) path->trace;
+      tlen  = path->tlen;
+
+      if (COMP(ovls[i].flags))
+        ht = cist;
+      else
+        ht = hist;
+
+      b = 0;
+      a = (path->abpos/TRACE_SPACING)*MAXQV1;
+
+      abit = (path->abpos % TRACE_SPACING);
+      if (abit != 0)
+        { a += MAXQV1;
+          b += 2;
+        }
+
+      abit = (path->aepos % TRACE_SPACING);
+      if (abit != 0)
+        tlen -= 2;
+
+      while (b < tlen)
+        { x = (int) ((200.*trace[b]) / (TRACE_SPACING + trace[b+1]));
+          if (x > MAXQV)
+            x = MAXQV;
+          ht[a + x] += 1;
+          a += MAXQV1;
+          b += 2;
+        }
+
+      if (path->aepos == alen && abit >= partial)
+        { x = (int) ((200.*trace[tlen]) / (abit + trace[tlen+1]));
+          if (x > MAXQV)
+            x = MAXQV;
+          ht[a + x] += 1;
+        }
+    }
+
+  //  For every segment, qv score is the maximum of the averages of the QV_DEEP lowest
+  //    in the forward and reverse directions (if each is QV_DEEP), or the average
+  //    of overlap scores (if between MINCOV and QV_DEEP-1), or MAXQV if no overlaps at all.
+  //    Reset histogram for segment to zeros.
+
+  tick = hist;
+  cick = cist;
+  for (i = 0; i < atick; i++)
+    { int  v, y;
+      int  qvn, qvc;
+      int  cntn, cntc;
+      int  sumn, sumc;
+
+#ifdef QV_DEBUG
+      { int min, max;
+
+        printf("   [%5d,%5d]:",i*TRACE_SPACING,(i+1)*TRACE_SPACING);
+
+        for (v = 0; v <= MAXQV; v++)
+          if (tick[v] > 0)
+            break;
+        min = v;
+
+        for (v = MAXQV; v >= 0; v--)
+          if (tick[v] > 0)
+            break;
+        max = v;
+
+        for (v = min; v <= max; v++)
+          if (tick[v] == 1)
+            printf(" %2d",v);
+          else if (tick[v] > 1)
+            printf(" %2d(%d)",v,tick[v]);
+
+        printf("\n                :");
+
+        for (v = 0; v <= MAXQV; v++)
+          if (cick[v] > 0)
+            break;
+        min = v;
+
+        for (v = MAXQV; v >= 0; v--)
+          if (cick[v] > 0)
+            break;
+        max = v;
+
+        for (v = min; v <= max; v++)
+          if (cick[v] == 1)
+            printf(" %2d",v);
+          else if (cick[v] > 1)
+            printf(" %2d(%d)",v,cick[v]);
+       }
+#endif
+
+      if (VERBOSE)
+        for (v = 0; v <= MAXQV; v++)
+          sgram[v] += tick[v] + cick[v];
+     
+      cntn = sumn = 0;
+      for (v = 0; v <= MAXQV; v++)
+        { y = tick[v];
+          tick[v] = 0;
+          cntn += y;
+          sumn += y*v;
+          if (cntn >= QV_DEEP)
+            { sumn -= (cntn-QV_DEEP)*v;
+              cntn  = QV_DEEP;
+              break;
+            }
+        }
+      for (v++; v <= MAXQV; v++)
+        tick[v] = 0;
+     
+      cntc = sumc = 0;
+      for (v = 0; v <= MAXQV; v++)
+        { y = cick[v];
+          cick[v] = 0;
+          cntc += y;
+          sumc += y*v;
+          if (cntc >= QV_DEEP)
+            { sumc -= (cntc-QV_DEEP)*v;
+              cntc  = QV_DEEP;
+              break;
+            }
+        }
+      for (v++; v <= MAXQV; v++)
+        cick[v] = 0;
+
+      if (cntn >= MINCOV)
+        qvn = sumn/cntn;
+      else
+        qvn = MAXQV;
+
+      if (cntc >= MINCOV)
+        qvc = sumc/cntc;
+      else
+        qvc = MAXQV;
+
+      if (qvn > qvc)
+        qvec[i] = (uint8) qvn;
+      else
+        qvec[i] = (uint8) qvc;
+
+      tick += MAXQV1;
+      cick += MAXQV1;
+
+#ifdef QV_DEBUG
+      printf(" >> %2d %2d = %2d <<\n",qvn,qvc,qvec[i]);
+#endif
+    }
+
+  //  Accumulate qv histogram (if VERBOSE) and append qv's to .qual file
+
+  if (VERBOSE)
+    { for (i = 0; i < atick; i++)
+        qgram[qvec[i]] += 1;
+      nreads += 1;
+      totlen += alen;
+    }
+
+  fwrite(qvec,sizeof(uint8),atick,QV_DFILE);
+  QV_INDEX += atick;
+  fwrite(&QV_INDEX,sizeof(int64),1,QV_AFILE);
+}
+
+  //  Read in each successive pile and call ACTION on it.  Read in the traces only if
+  //   "trace" is nonzero
+
+static int make_a_pass(FILE *input, void (*ACTION)(int, Overlap *, int), int trace)
+{ static Overlap *ovls = NULL;
+  static int      omax = 500;
+  static uint16  *paths = NULL;
+  static int      pmax = 100000;
+
+  int64 i, j, novl;
+  int   n, a;
+  int   pcur;
+  int   max;
+
+  if (ovls == NULL)
+    { ovls = (Overlap *) Malloc(sizeof(Overlap)*omax,"Allocating overlap buffer");
+      if (ovls == NULL)
+        exit (1);
+    }
+  if (trace && paths == NULL)
+    { paths = (uint16 *) Malloc(sizeof(uint16)*pmax,"Allocating path buffer");
+      if (paths == NULL)
+        exit (1);
+    }
+
+  rewind(input);
+  fread(&novl,sizeof(int64),1,input);
+  fread(&TRACE_SPACING,sizeof(int),1,input);
+  if (TRACE_SPACING <= TRACE_XOVR)
+    TBYTES = sizeof(uint8);
+  else
+    TBYTES = sizeof(uint16);
+
+  Read_Overlap(input,ovls);
+  if (trace)
+    { if (ovls[0].path.tlen > pmax)
+        { pmax  = 1.2*(ovls[0].path.tlen)+10000;
+          paths = (uint16 *) Realloc(paths,sizeof(uint16)*pmax,"Expanding path buffer");
+          if (paths == NULL) exit (1);
+        }
+      fread(paths,TBYTES,ovls[0].path.tlen,input);
+      if (TBYTES == 1)
+        { ovls[0].path.trace = paths;
+          Decompress_TraceTo16(ovls);
+        }
+    }
+  else
+    fseek(input,TBYTES*ovls[0].path.tlen,SEEK_CUR);
+
+  if (ovls[0].aread < DB_FIRST)
+    { fprintf(stderr,"%s: .las file overlaps don't correspond to reads in block %d of DB\n",
+                     Prog_Name,DB_PART);
+      exit (1);
+    }
+
+  pcur = 0;
+  n = max = 0;
+  for (j = DB_FIRST; j < DB_LAST; j++)
+    { ovls[0] = ovls[n];
+      a = ovls[0].aread;
+      if (a != j)
+        n = 0;
+      else
+        { if (trace)
+            memcpy(paths,paths+pcur,sizeof(uint16)*ovls[0].path.tlen);
+          n = 1;
+          pcur = ovls[0].path.tlen;
+          while (1)
+            { if (Read_Overlap(input,ovls+n) != 0)
+                { ovls[n].aread = INT32_MAX;
+                  break;
+                }
+              if (trace)
+                { if (pcur + ovls[n].path.tlen > pmax)
+                    { pmax = 1.2*(pcur+ovls[n].path.tlen)+10000;
+                      paths = (uint16 *) Realloc(paths,sizeof(uint16)*pmax,"Expanding path buffer");
+                      if (paths == NULL) exit (1);
+                    }
+                  fread(paths+pcur,TBYTES,ovls[n].path.tlen,input);
+                  if (TBYTES == 1)
+                    { ovls[n].path.trace = paths+pcur;
+                      Decompress_TraceTo16(ovls+n);
+                    }
+                }
+              else
+                fseek(input,TBYTES*ovls[n].path.tlen,SEEK_CUR);
+              if (ovls[n].aread != a)
+                break;
+              pcur += ovls[n].path.tlen;
+              n    += 1;
+              if (n >= omax)
+                { omax = 1.2*n + 100;
+                  ovls = (Overlap *) Realloc(ovls,sizeof(Overlap)*omax,"Expanding overlap buffer");
+                  if (ovls == NULL) exit (1);
+                }
+            }
+
+          if (n >= max)
+            max = n;
+          pcur = 0;
+          for (i = 0; i < n; i++)
+            { ovls[i].path.trace = paths+pcur;
+              pcur += ovls[i].path.tlen;
+            }
+        }
+      ACTION(j,ovls,n);
+    }
+
+  return (max);
+}
+
+int main(int argc, char *argv[])
+{ FILE  *input;
+  char  *root, *dpwd;
+  char  *las, *lpwd;
+  int64  novl;
+  int    c, COVERAGE;
+
+  //  Process arguments
+
+  { int  i, j, k;
+    int  flags[128];
+    char *eptr;
+
+    ARG_INIT("DASqv")
+
+    COVERAGE = -1;
+
+    j = 1;
+    for (i = 1; i < argc; i++)
+      if (argv[i][0] == '-')
+        switch (argv[i][1])
+        { default:
+            ARG_FLAGS("v")
+            break;
+          case 'c':
+            ARG_POSITIVE(COVERAGE,"Voting depth")
+            break;
+        }
+      else
+        argv[j++] = argv[i];
+    argc = j;
+
+    VERBOSE = flags['v'];
+
+    if (argc < 3)
+      { fprintf(stderr,"Usage: %s %s\n",Prog_Name,Usage);
+        exit (1);
+      }
+
+    if (COVERAGE < 0)
+      { fprintf(stderr,"%s: Must supply -c parameter\n",Prog_Name);
+        exit (1);
+      }
+    else
+      { if (COVERAGE >= 40)
+          QV_DEEP = COVERAGE/8;
+        else if (COVERAGE >= 20)
+          QV_DEEP = 5;
+        else if (COVERAGE >= 4)
+          QV_DEEP = COVERAGE/4;
+        else
+          { fprintf(stderr,"%s: Average coverage is too low (< 4X), cannot infer qv's\n",Prog_Name);
+            exit (1);
+          }
+      }
+  }
+
+  //  Open trimmed DB
+
+  { int status;
+
+    status = Open_DB(argv[1],DB);
+    if (status < 0)
+      exit (1);
+    if (status == 1)
+      { fprintf(stderr,"%s: Cannot be called on a .dam index: %s\n",Prog_Name,argv[1]);
+        exit (1);
+      }
+    if (DB->part)
+      { fprintf(stderr,"%s: Cannot be called on a block: %s\n",Prog_Name,argv[1]);
+        exit (1);
+      }
+    Trim_DB(DB);
+  }
+
+  //  Initialize statistics gathering
+
+  if (VERBOSE)
+    { int i;
+
+      nreads = 0;
+      totlen = 0;
+      for (i = 0; i <= MAXQV; i++)
+        qgram[i] = sgram[i] = 0;
+
+      printf("\nDASqv -c%d %s",COVERAGE,argv[1]);
+      for (i = 2; i < argc; i++)
+        printf(" %s",argv[i]);
+      printf("\n");
+    }
+
+  //  Determine if overlap block is being processed and if so get first and last read
+  //    from .db file
+
+  dpwd = PathTo(argv[1]);
+  root = Root(argv[1],".db");
+
+  for (c = 2; c < argc; c++)
+    { las  = Root(argv[c],".las");
+
+      { FILE *dbfile;
+        char  buffer[2*MAX_NAME+100];
+        char *p, *eptr;
+        int   i, part, nfiles, nblocks, cutoff, all, oindx;
+        int64 size;
+
+        DB_PART  = 0;
+        DB_FIRST = 0;
+        DB_LAST  = DB->nreads;
+
+        p = rindex(las,'.');
+        if (p != NULL)
+          { part = strtol(p+1,&eptr,10);
+            if (*eptr == '\0' && eptr != p+1)
+              { dbfile = Fopen(Catenate(dpwd,"/",root,".db"),"r");
+                if (dbfile == NULL)
+                  exit (1);
+                if (fscanf(dbfile,DB_NFILE,&nfiles) != 1)
+                  SYSTEM_ERROR
+                for (i = 0; i < nfiles; i++)
+                  if (fgets(buffer,2*MAX_NAME+100,dbfile) == NULL)
+                    SYSTEM_ERROR
+                if (fscanf(dbfile,DB_NBLOCK,&nblocks) != 1)
+                  SYSTEM_ERROR
+                if (fscanf(dbfile,DB_PARAMS,&size,&cutoff,&all) != 3)
+                  SYSTEM_ERROR
+                for (i = 1; i <= part; i++)
+                  if (fscanf(dbfile,DB_BDATA,&oindx,&DB_FIRST) != 2)
+                    SYSTEM_ERROR
+                if (fscanf(dbfile,DB_BDATA,&oindx,&DB_LAST) != 2)
+                  SYSTEM_ERROR
+                fclose(dbfile);
+                DB_PART = part;
+                *p = '\0';
+              }
+          }
+      }
+
+      //   Set up preliminary trimming track
+
+      if (DB_PART > 0)
+        { QV_AFILE = Fopen(Catenate(dpwd,PATHSEP,root,
+                                    Numbered_Suffix(".",DB_PART,".qual.anno")),"w");
+          QV_DFILE = Fopen(Catenate(dpwd,PATHSEP,root,
+                                    Numbered_Suffix(".",DB_PART,".qual.data")),"w");
+        }
+      else
+        { QV_AFILE = Fopen(Catenate(dpwd,PATHSEP,root,".qual.anno"),"w");
+          QV_DFILE = Fopen(Catenate(dpwd,PATHSEP,root,".qual.data"),"w");
+        }
+      if (QV_AFILE == NULL || QV_DFILE == NULL)
+        exit (1);
+
+      { int size, nreads;
+
+        nreads = DB_LAST - DB_FIRST;
+        size   = sizeof(int64);
+        fwrite(&nreads,sizeof(int),1,QV_AFILE);
+        fwrite(&size,sizeof(int),1,QV_AFILE);
+        QV_INDEX = 0;
+        fwrite(&QV_INDEX,sizeof(int64),1,QV_AFILE);
+      }
+
+      //  Open overlap file
+
+      lpwd = PathTo(argv[c]);
+      if (DB_PART > 0)
+        input = Fopen(Catenate(lpwd,"/",las,Numbered_Suffix(".",DB_PART,".las")),"r");
+      else
+        input = Fopen(Catenate(lpwd,"/",las,".las"),"r");
+      if (input == NULL)
+        exit (1);
+
+      free(lpwd);
+      free(las);
+
+      //  Get trace point spacing information
+
+      fread(&novl,sizeof(int64),1,input);
+      fread(&TRACE_SPACING,sizeof(int),1,input);
+
+      //  Process each read pile
+
+      make_a_pass(input,CALCULATE_QVS,1);
+
+      fclose(QV_AFILE);
+      fclose(QV_DFILE);
+    }
+
+  //  If verbose output statistics summary to stdout
+
+  if (VERBOSE)
+    { int   i;
+      int64 ssum, qsum;
+      int64 stotal, qtotal;
+
+      printf("\nInput:  ");
+      Print_Number(nreads,7,stdout);
+      printf("reads,  ");
+      Print_Number(totlen,12,stdout);
+      printf(" bases\n");
+
+      stotal = qtotal = 0;
+      for (i = 0; i <= MAXQV; i++)
+        { stotal += sgram[i];
+          qtotal += qgram[i];
+        }
+
+      printf("\nHistogram of q-values (average %d best)\n",2*QV_DEEP);
+      printf("\n                 Input                 QV\n");
+      qsum = qgram[MAXQV];
+      ssum = sgram[MAXQV];
+      printf("\n    %2d:  %9lld  %5.1f%%    %9lld  %5.1f%%\n\n",
+             MAXQV,sgram[MAXQV],(100.*ssum)/stotal,qgram[MAXQV],(100.*qsum)/qtotal);
+
+      qtotal -= qsum;
+      stotal -= ssum;
+      ssum = qsum = 0;
+      for (i = MAXQV-1; i >= 0; i--) 
+        if (qgram[i] > 0)
+          { ssum += sgram[i];
+            qsum += qgram[i];
+            printf("    %2d:  %9lld  %5.1f%%    %9lld  %5.1f%%\n",
+                   i,sgram[i],(100.*ssum)/stotal,
+                     qgram[i],(100.*qsum)/qtotal);
+          }
+    }
+
+  //  Clean up
+
+  free(dpwd);
+  free(root);
+
+  Close_DB(DB);
+  free(Prog_Name);
+
+  exit (0);
+}
diff --git a/DAStrim.c b/DAStrim.c
new file mode 100644
index 0000000..7932981
--- /dev/null
+++ b/DAStrim.c
@@ -0,0 +1,1866 @@
+/*******************************************************************************************
+ *
+ *  Using overlap pile for each read and intrinisic quality values, determine the
+ *    high quality segments with interspersed gaps.  Any unremoved
+ *    adaptemer sequences are dectected and the shorter side trimmed.
+ *
+ *  Author:  Gene Myers
+ *  Date  :  March 2015
+ *
+ *******************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "DB.h"
+#include "align.h"
+
+#ifdef HIDE_FILES
+#define PATHSEP "/."
+#else
+#define PATHSEP "/"
+#endif
+
+#undef   DEBUG_HQ_BLOCKS     //  Various DEBUG flags (normally all off)
+#undef   SHOW_EVENTS
+#undef   DEBUG_HOLE_FINDER
+#undef   DEBUG_GAP_STATUS
+#undef   SHOW_PAIRS
+#define  ANNOTATE
+
+
+//  Command format and global parameter variables
+
+static char *Usage = " [-v] [-l<int(1000)>] -g<int> -b<int> <source:db> <overlaps:las> ...";
+
+static int     BAD_QV;     //  qv >= and you are "bad"
+static int     GOOD_QV;    //  qv <= and you are "good"
+static int     MIN_LEN;    //  Minimum segment length to keep
+static int     VERBOSE;
+
+
+//  Good patch constant
+
+#define MIN_BLOCK  500    //  Minimum length of a good patch
+
+//  Gap constants
+
+#define  MIN_COVER   3   //  A coverage gap occurs at or below this level
+#define  COVER_LEN 400   //  An overlap covers a point if it extends COVER_LEN to either side.
+
+//  Wall Constants
+
+#define MIN_PNT 5        //  Minimum # of events in a wall
+#define MAX_SEP 25       //  Maximum separation between two events in a wall
+#define AVE_SEP 5.       //  Maximum average separation between two events in a wall
+
+
+//  Global Variables (must exist across the processing of each pile)
+
+  //  Read-only
+
+static int     TRACE_SPACING;  //  Trace spacing (from .las file)
+
+static HITS_DB _DB, *DB  = &_DB;   //  Data base
+static int     DB_FIRST;           //  First read of DB to process
+static int     DB_LAST;            //  Last read of DB to process (+1)
+static int     DB_PART;            //  0 if all, otherwise block #
+
+static int64  *QV_IDX;     //  qual track index
+static uint8  *QV;         //  qual track values
+
+  //  Read & Write
+
+#ifdef ANNOTATE
+
+static FILE   *HQ_AFILE;   //  .hq.anno
+static FILE   *HQ_DFILE;   //  .hq.data
+static int64   HQ_INDEX;   //  Current index into .hq.data file as it is being written
+
+static FILE   *HL_AFILE;   //  .hole.anno
+static FILE   *HL_DFILE;   //  .hole.data
+static int64   HL_INDEX;   //  Current index into .hole.data file as it is being written
+
+static FILE   *SN_AFILE;   //  .span.anno
+static FILE   *SN_DFILE;   //  .span.data
+static int64   SN_INDEX;   //  Current index into .span.data file as it is being written
+
+static FILE   *SP_AFILE;   //  .split.anno
+static FILE   *SP_DFILE;   //  .split.data
+static int64   SP_INDEX;   //  Current index into .split.data file as it is being written
+
+static FILE   *AD_AFILE;   //  .adapt.anno
+static FILE   *AD_DFILE;   //  .adapt.data
+static int64   AD_INDEX;   //  Current index into .adapt.data file as it is being written
+
+static FILE   *KP_AFILE;   //  .keep.anno
+static FILE   *KP_DFILE;   //  .keep.data
+static int64   KP_INDEX;   //  Current index into .keep.data file as it is being written
+
+#endif
+
+static int64 nreads, totlen;
+static int64 nelim, nelimbp;
+static int64 n5trm, n5trmbp;
+static int64 n3trm, n3trmbp;
+static int64 natrm, natrmbp;
+static int64 ngaps, ngapsbp;
+  static int64 nlowq, nlowqbp;
+  static int64 nspan, nspanbp;
+  static int64 nchim, nchimbp;
+
+//  Data Structures
+
+typedef struct   //  General read interval [beg..end]
+  { int beg;
+    int end;
+  } Interval;
+
+  //  Coverage events, type (one of 7 below) and position
+
+#define ADD  0  //  leftmost A-position of LA
+#define LFT  1  //  ADD position + COVER_LEN of LA (>= 2*COVER_LEN long)
+#define LGP  2  //  left end of an HQ-block
+#define CTR  3  //  A-center of LA < 2*COVER_LEN long
+#define RGP  4  //  right end of an HQ-block
+#define RGT  5  //  DEL position - COVER_LEN of LA
+#define DEL  6  //  rightmost A-position of LA
+
+#ifdef SHOW_EVENTS
+
+static char   Symbol[7] = { 'A', 'L', '[', 'C', ']', 'R', 'D' };
+
+#endif
+
+typedef struct
+  { int  type;   
+    int  pos;
+  } Event;
+
+  //  Wall: there are cnt LFT/RGT events ending in the interval [beg,end] going
+  //        from coverage depth cov up to cov+cnt
+
+typedef struct
+  { int beg;
+    int end;
+    int cnt;
+    int cov;
+  } Wall;
+
+/*******************************************************************************************
+ *
+ *  FIND ALL HIGH_QV BLOCKS OF EACH READ
+ *
+ ********************************************************************************************/
+
+  //  Find "good" blocks of trace point intervals:
+  //    0. A good block must begin and end with an interval <= GOOD_QV
+  //    1. Any stretch all < BAD_QV at least MIN_BLOCK long
+  //    2. Any stretch all <= GOOD_QV at least MIN_BLOCK-TRACE_SPACING long
+  //    3. Any stretch all <= GOOD_QV only 1 interval away from another good patch
+
+  //  Global Inputs:  QV, QV_IDX, GOOD_QV, BAD_QV 
+  //  HQ_BLOCKS[0..*nblk-1] contain the good patches in increase sequencing across aread.
+  //  Parameter aread is input-only, and p_nblk is output-only.
+
+static Interval *HQ_BLOCKS(int aread, int *p_nblk)
+{ int              nblk;
+  static int      *alive = NULL;
+  static Interval *block = NULL;
+
+  int    alen, atick;
+  uint8 *qvec;
+
+  alen  = DB->reads[aread].rlen;
+  atick = (alen + (TRACE_SPACING-1))/TRACE_SPACING;
+
+  if (alive == NULL)
+    { int max = DB->maxlen/TRACE_SPACING+2;
+      alive = (int *) Malloc(max*sizeof(int),"Allocating alive vector");
+      block = (Interval *) Malloc(max*sizeof(Interval),"Allocating block vector");
+      if (alive == NULL || block == NULL)
+        exit (1);
+    }
+
+  qvec = QV + QV_IDX[aread];
+  nblk = 0;
+
+  //  Find all blocks < BAD_QV with either len >= MIN_BLOCK or all <= GOOD_QV in block[0..nblk)
+  //    Mark those satisfying 1. or 2. as "alive" (.alv)
+
+  { int lmost = 0, rmost, thr;
+    int i, in;
+
+    thr = (MIN_BLOCK-1)/TRACE_SPACING;
+    in  = 0;
+    for (i = 0; i <= atick; i++)
+      { int q, alv;
+
+        if (i < atick)
+          q = qvec[i];
+        else
+          q = BAD_QV;
+        if (in)
+          { if (q >= BAD_QV)
+              { alv = (lmost-rmost >= thr);
+                if (alv)
+                  { block[nblk].beg = rmost; 
+                    block[nblk].end = lmost + 1;
+                    alive[nblk] = alv;
+                    nblk += 1;
+                  }
+                else
+                  { int j, k;
+
+                    for (j = rmost; j <= lmost; j = k)
+                      { for (k = j+1; k <= lmost; k++)
+                          if (qvec[k] > GOOD_QV)
+                            break;
+                        block[nblk].beg = j; 
+                        block[nblk].end = k;
+                        alive[nblk] = (k-j >= thr);
+                        nblk += 1;
+                        for ( ; k <= lmost; k++)
+                          if (qvec[k] <= GOOD_QV)
+                            break;
+                      }
+                  }
+                in = 0;
+              }
+            else if (q <= GOOD_QV)
+              lmost = i;
+          }
+        else
+          { if (q <= GOOD_QV)
+              { rmost = lmost = i;
+                in = 1;
+              }
+          }
+      }
+  }
+
+  //  Mark as alive all short, all-good blocks that satisfy 3.
+
+  { int i, j;
+
+    for (i = 0; i < nblk; i++)
+      if (alive[i])
+        { for (j = i-1; j >= 0 && ! alive[j]; j--)
+            if (block[j+1].beg - block[j].end == 1)
+              alive[j] = 1;  
+            else
+              break;
+          for (j = i+1; j < nblk && ! alive[j]; j++)
+            if (block[j].beg - block[j-1].end == 1)
+              alive[j] = 1;  
+            else
+              break;
+        }
+  }
+
+  //  Remove all blocks that are not alive
+
+  { int i, j;
+
+    j = 0;
+    for (i = 0; i < nblk; i++)
+      if (alive[i])
+        { block[j].beg = block[i].beg * TRACE_SPACING;
+          block[j].end = block[i].end * TRACE_SPACING;
+          j += 1;
+        }
+    nblk = j;
+    if (nblk > 0 && block[nblk-1].end > alen)
+      block[nblk-1].end = alen;
+  }
+
+#ifdef DEBUG_HQ_BLOCKS
+  { int i;
+
+    printf("         %3d:",nblk);
+    for (i = 0; i < nblk; i++)
+      printf(" [%5d,%5d]",block[i].beg,block[i].end);
+    printf("\n");
+  }
+#endif
+
+  *p_nblk = nblk;
+  return (block);
+}
+
+
+/*******************************************************************************************
+ *
+ *  WALL ANALYZER TO HELP AVOID REPEAT BOUNDARIES
+ *
+ ********************************************************************************************/
+
+   //  Find intervals of LFT/RGT events where no two events are separated by more than
+   //    MAX_SEP, the average arrival rate is AVE_SEP, and there are at least MIN_PNT
+   //    events in the interval.
+
+static Wall *wall_detector(int *ev, int b, int e, Wall *next)
+{ int idx;
+
+  { int    i, n, max;
+    double ave;
+
+    n = e-b;
+
+    if (n < MIN_PNT) return (next);  //  Too small: done
+
+    idx = b;
+    max = -1;                        //  Find the position of the largest separation between
+    for (i = b+1; i < e; i++)        //     two tips in ev[b..e)
+      if (ev[i] - ev[i-1] > max)
+        { max = ev[i] - ev[i-1];
+          idx = i;
+        }
+
+    ave = (ev[e-1] - ev[b]) / (n-1.);        //  Check if the current interval is a wall
+    if (ave <= AVE_SEP && max <= MAX_SEP)
+      { if (max <= 4.*(ave+1.))              //  Max separation < 4*average separation ?
+          { next->beg = b;
+            next->end = e;
+            next->cnt = n;
+            return (next+1);
+          }
+      }
+  }
+
+  next = wall_detector(ev,b,idx,next);    //  If not then split on the largest separation
+  next = wall_detector(ev,idx,e,next);    //    and recurse on the two parts
+  return (next);
+}
+
+   //  Find LFT/RGT event walls
+
+static Wall *find_walls(int novl, Event *queue, int *anum, int *dnum)
+{ static int   nmax = 0;
+
+  Wall        *aptr, *dptr;
+  static Wall *wall = NULL;
+
+  int          ntip;
+  static int  *adds = NULL;
+  static int  *dels;
+
+  if (novl == 0)
+    return (NULL);
+
+  if (novl > nmax)
+    { nmax = novl*1.2 + 1000; 
+      wall = (Wall *) Realloc(wall,sizeof(Wall)*(nmax/MIN_PNT),"Reallocating wall vector");
+      adds = (int *)  Realloc(adds,sizeof(int)*2*nmax,"Reallocating add+del vectors");
+      if (wall == NULL || adds == NULL)
+        exit (1);
+      dels = adds + nmax;
+    }
+
+  //  Make separate arrays of add and del tips (LFT and RGT events) in sorted order in
+  //    which to seek "walls".
+
+  { int i, j, x;
+
+    i = x = 0;                      //  A bit tricky: less than novl tips due to CTR events
+    for (j = 0; x < novl; j++)      //   that don't generate tips, so analyze events until
+      if (queue[j].type == CTR)     //   have counted all LA's.  Furthermore adds and dels
+        x += 1;                     //   are sorted because queue is sorted.
+      else if (queue[j].type == LFT)
+        { x += 1;
+          adds[i++] = queue[j].pos;
+        }
+    ntip = i;
+
+    i = 0;
+    for (j = 0; i < ntip; j++)
+      if (queue[j].type == RGT)
+        dels[i++] = queue[j].pos;
+  }
+
+  //  Find LFT walls and RGT walls in [walls,aptr) and [aptr,dptr)
+
+  aptr = wall_detector(adds,0,ntip,wall);
+  dptr = wall_detector(dels,0,ntip,aptr);
+
+  //  For each wall, determine the coverage of its base with a merged traversal
+  //    of the adds and dels arrays
+
+  { Wall *a, *d;
+    int   i, j, x;
+
+    x = 0;
+    a = wall;
+    d = aptr;;
+    i = j = 0;
+    while (j < ntip)
+      if (i < ntip && adds[i] < dels[j])
+        { if (a->beg == i)
+            a->cov = x;
+          else if (a->end == i+1)
+            { a += 1;
+              if (a >= aptr)
+                a -= 1;
+            }
+          x += 1;
+          i += 1;
+        }
+      else
+        { if (d->beg == j)
+            d->cov = x - d->cnt;
+          else if (d->end == j+1)
+            { d += 1;
+              if (d >= dptr)
+                d -= 1;
+            }
+          x -= 1;
+          j += 1;
+        }
+  }
+  
+  //  Sneaky, switch beg/end from an index into the adds or dels array, to the actually
+  //    coordinate of the event.
+
+  { Wall *a;
+
+    for (a = wall; a < aptr; a++)
+      { a->beg = adds[a->beg];
+        a->end = adds[a->end-1];
+      }
+    for (a = aptr; a < dptr; a++)
+      { a->beg = dels[a->beg];
+        a->end = dels[a->end-1];
+      }
+  }
+
+  *anum = aptr-wall;
+  *dnum = dptr-aptr;
+  return (wall);
+}
+
+
+/*******************************************************************************************
+ *
+ *  COVERAGE ANALYSIS TO FIND ALL HOLES (regions of very low coverage/support)
+ *
+ ********************************************************************************************/
+
+  //  Find intervals for which there are MIN_COVER or fewer LAs that project at least COVER_LEN
+  //    bases to the left and right of the interval.  These are called holes.
+  //  Holes are usually found between HQ-blocks.  However occasionally they intersect one or
+  //    more blocks and this requires the HQ-blocks be refined as follows:
+  //      a. Hole spans an HQ-block:
+  //           The block needs to be removed as HQ *if* it is not based 5 or more LA's
+  //           (this usually never happens, 10^-5 or less)
+  //      b. Hole is contained in an HQ-block:
+  //           The block needs to be split around the hole because one needs to verify that
+  //           the left and right regions on each side of a hole actually belong together
+  //           (this happens occasionaly, ~ 10^-3)
+  //      c. Hole overlaps an HQ-block:
+  //           If this happens, then the overlap is very small and the block is left unperturbed.
+  //           (this worries me a bit, but in all testing it remains so)
+  //  Given the above affects, the list of HQ-blocks can be modified by FIND_HOLES.
+
+static int ESORT(const void *l, const void *r)
+{ Event *x = (Event *) l;
+  Event *y = (Event *) r;
+
+  if (x->pos == y->pos)
+    return (x->type - y->type);
+  return (x->pos - y->pos);
+}
+
+static Interval *FIND_HOLES(int aread, Overlap *ovls, int novl,
+                            int *p_nhole, Interval **p_block, int *p_nblk)
+{ static int       nmax = 0;
+
+  int              nev;
+  static Event    *queue = NULL;  //  Event queue[0..nev)
+
+  int              nhole;
+  static Interval *holes = NULL;  //  Detected holes[0..nhole)
+
+  static int       pmax;
+  static Interval *cover = NULL;  //  Coverage at block ends [0..nblk)
+  static Interval *nwblk;         //  Modified block list [0..nblk')
+
+  int       nblk  = *p_nblk;      //  Initial HQ block list [0..nblk)
+  Interval *block = *p_block;
+
+  int   anum = 0, dnum = 0;       //  LFT and RGT walls, awall[0..anum) & dwall[0..dnum)
+  Wall *awall,   *dwall;
+
+  if (cover == NULL)
+    { pmax = DB->maxlen/TRACE_SPACING + 2;
+      cover = (Interval *) Malloc(2*pmax*sizeof(Interval),"Allocating patch vector");
+      nwblk = cover + pmax;
+    }
+
+  if (4*novl + pmax > nmax)
+    { nmax = 4.8*novl + pmax + 100;
+      queue = (Event *) Realloc(queue,(nmax+1)*sizeof(Event),"Allocating event queue");
+      holes = (Interval *) Realloc(holes,(nmax/4)*sizeof(Interval),"Allocating hole vector");
+      if (queue == NULL || holes == NULL)
+        exit (1);
+    }
+
+  { int i;
+
+    //  For each trimmed overlap: add its events to the queue
+
+    nev = 0;
+    for (i = 0; i < novl; i++)
+      { queue[nev].type = ADD;
+        queue[nev].pos  = ovls[i].path.abpos;
+        nev += 1;
+
+        queue[nev].type = DEL;
+        queue[nev].pos  = ovls[i].path.aepos;
+        nev += 1;
+
+        if (ovls[i].path.abpos + 2*COVER_LEN + 10 > ovls[i].path.aepos)
+          { queue[nev].type = CTR;
+            queue[nev].pos  = (ovls[i].path.abpos + ovls[i].path.aepos) / 2;
+            nev += 1;
+          }
+        else
+          { queue[nev].type = LFT;
+            queue[nev].pos  = ovls[i].path.abpos + COVER_LEN;
+            nev += 1;
+            queue[nev].type = RGT;
+            queue[nev].pos  = ovls[i].path.aepos - COVER_LEN;
+            nev += 1;
+          }
+      }
+
+    //  For each HQ-block: add its events to the queue
+
+    for (i = 0; i < nblk; i++)
+      { queue[nev].type = LGP;
+        queue[nev].pos  = block[i].beg;
+        nev += 1;
+        queue[nev].type = RGP;
+        queue[nev].pos  = block[i].end;
+        nev += 1;
+      }
+
+    queue[nev].pos = DB->reads[aread].rlen;
+  }
+
+  //  Sort the events
+
+  qsort(queue,nev,sizeof(Event),ESORT);
+
+  //  Find all LFT and RGT walls
+
+  awall = find_walls(novl,queue,&anum,&dnum);
+  dwall = awall + anum;
+
+#ifdef DEBUG_HOLE_FINDER
+  { int i;
+
+    printf("\n");
+    for (i = 0; i < anum; i++)
+      printf("  Add [%5d,%5d] %d %d\n",awall[i].beg,awall[i].end,awall[i].cnt,awall[i].cov);
+    for (i = 0; i < dnum; i++)
+      printf("  Del [%5d,%5d] %d %d\n",dwall[i].beg,dwall[i].end,dwall[i].cnt,dwall[i].cov);
+    printf("\n");
+  }
+#endif
+
+  //  Move through events in order keeping track of inc, dec, & cnf so that the
+  //    invariant stated below holds
+
+  { int  cnf, inc, dec;
+    int  cblk;
+
+    int   in;
+    int   nbeg, nend = 0;
+    int   first, last;
+
+    int  i;
+
+    in    = 1;
+    first = -1;
+    cblk  = 0;
+    nhole = 0;
+    inc = dec = cnf = 0;
+    for (i = 0; i < nev; i++)
+      { switch (queue[i].type)
+        { case ADD:
+            inc += 1;
+            break;
+          case LFT:
+            inc  -= 1;
+            cnf  += 1;
+            break;
+          case LGP:
+            cover[cblk].beg = cnf + inc + dec;  // = coverage depth at block[cblk].beg
+            continue;
+          case CTR:
+            inc -= 1;
+            dec += 1;
+            continue;
+          case RGP:
+            cover[cblk].end = cnf + inc + dec;  // = coverage depth at block[cblk].end
+            cblk += 1;
+            continue;
+          case RGT:
+            cnf  -= 1;
+            dec  += 1;
+            break;
+          case DEL:
+            dec -= 1;
+            break;
+        }
+
+        //  For position x = queue[i].pos:
+        //    inc = # of LA's between (ADD,LFT] positions
+        //    dec = # of LA's between (RGT,DEL] positions
+        //    cnf = # of LA's between (LFT,RGT] positions (= # of LAs tat project at least
+        //             COVER_LEN bases to the right and left of x!
+
+#ifdef SHOW_EVENTS
+        printf(" %5d %c: %3d<  %3d  >%3d  %3d\n",
+               queue[i].pos,Symbol[queue[i].type],inc,cnf,dec,dec-inc);
+#endif
+
+        //  When truncated coverage, cnf, transitions below MIN_COVER(3), note the fact (in = 1)
+        //    and record the index first of the event (must be a RGT) and the number of LA's
+        //    currently in their (RGT,DEL] interval
+
+        if (cnf <= MIN_COVER)
+          { if ( ! in)
+              { in = 1;
+                nend  = dec;
+                first = i;
+              }
+          }
+
+        //  When truncated coverage transitions above MIN_COVER, we declare it a hole
+        //    if interval below MIN_COVER is at least COVER_LEN long, there are at least
+        //    4 LA's that are "ending" at the left (i.e. in (RGT,DEL] interval, and
+        //    at least 4 LA's ending at the right.
+
+        else
+          { if (in && first >= 0 && queue[i].pos - queue[first].pos >= COVER_LEN &&
+                  nend >= 4 && inc >= 4)
+              { int lflank, rflank;
+                int dpos, apos;
+
+                nbeg = inc;
+                last = i;
+
+                //  Need to find the boundaries of the hole.  In principle, this is
+                //    [dpos + COVER_LEN, apos - COVER_LEN] where apos = queue[first].pos
+                //    and dpos = queue[last].pos, i.e. the entry and exit into the low
+                //    truncated cover interval.  However, walls induced by repeat boundaries
+                //    and/or uneveness in the end-points of LA's can cause the above to be
+                //    quite far off.  So ...
+
+
+                //  First try the average of the 2nd and 3rd quartile of the nend RGT events
+                //    before dpos.   The requisite number of events must exist by the definition
+                //    of nend.  While one is at it determine the index of the first of the
+                //    nend RGT events in lflank.
+
+                { int64 sum;
+                  int   q1, q3, n;
+                  int   a, d, k;
+                  int   acov, dcov;
+
+                  q1  = nend/4;
+                  q3  = (3*nend)/4;
+                  sum = 0;
+                  n   = 0;
+                  for (lflank = first; n < nend; lflank--)
+                    if (queue[lflank].type == RGT || queue[lflank].type == CTR)
+                      { if (n >= q1 && n < q3)
+                          sum += queue[lflank].pos;
+                        n += 1;
+                      }
+                  dpos = sum/(q3-q1);
+                  lflank += 1;
+
+#ifdef DEBUG_HOLE_FINDER
+                  printf("  Dev %5d-%3d-%5d -> %5d",queue[lflank].pos,nend,queue[first].pos,dpos);
+#endif
+
+                //  Second, look for the rightmost RGT-(LFT-)wall that overlaps the left (right)
+                //    flank, i.e. queue[lflank,first].pos (queue[last,rflank].pos), and if found
+                //    take the average position of the wall.
+
+                  for (d = dnum-1; d >= 0; d--)
+                    if (dwall[d].beg <= queue[first].pos)
+                      break;
+                  if (d >= 0 && dwall[d].end >= queue[lflank].pos)
+                    { sum = 0;
+                      n   = 0;
+                      for (k = first; k >= lflank; k--)
+                        if (queue[k].type == RGT || queue[k].type == CTR)
+                          { if (queue[k].pos < dwall[d].beg)
+                              break;
+                            if (queue[k].pos <= dwall[d].end)
+                              { sum += queue[k].pos;
+                                n += 1;
+                              }
+                          }
+                      dpos = sum/n;
+#ifdef DEBUG_HOLE_FINDER
+                      printf(" [%5d,%5d] -> %4d\n",dwall[d].beg,dwall[d].end,dpos);
+#endif
+                      dcov = dwall[d].cov + dwall[d].cnt;
+		      d -= 1;
+		    }
+                  else
+                    { dcov = nend + MIN_COVER;
+#ifdef DEBUG_HOLE_FINDER
+                      printf(" No wall mapping\n");
+#endif
+                    }
+
+                // First try on LFT events (replace nend with nbeg, RGT with LFT, before
+                //   with after, and dpos with apos, first with last, and lflank with rflank.
+
+                  q1  = nbeg/4;
+                  q3  = (3*nbeg)/4;
+                  sum = 0;
+                  n   = 0;
+                  for (rflank = last; n < nbeg; rflank++)
+                    if (queue[rflank].type == LFT || queue[rflank].type == CTR)
+                      { if (n >= q1 && n < q3)
+                          sum += queue[rflank].pos;
+                        n += 1;
+                      }
+                  apos = sum/(q3-q1);
+                  rflank -= 1;
+
+#ifdef DEBUG_HOLE_FINDER
+                  printf("  Aev %5d-%3d-%5d -> %5d",queue[i].pos,nbeg,queue[rflank].pos,apos);
+#endif
+
+                // Second look at LFT events.
+
+                  for (a = 0; a < anum; a++)
+                    if (awall[a].end >= queue[i].pos)
+                      break;
+                  if (a < anum && awall[a].beg <= queue[rflank].pos) 
+                    { sum = 0;
+                      n   = 0;
+                      for (k = i; k <= rflank; k++)
+                        if (queue[k].type == LFT || queue[k].type == CTR)
+                          { if (queue[k].pos > awall[a].end)
+                              break;
+                            if (queue[k].pos >= awall[a].beg)
+                              { sum += queue[k].pos;
+                                n += 1;
+                              }
+                          }
+                      apos = sum/n;
+#ifdef DEBUG_HOLE_FINDER
+                      printf(" [%5d,%5d] -> %4d\n",awall[a].beg,awall[a].end,apos);
+#endif
+                      acov = awall[a].cov + awall[a].cnt;
+                      a += 1;
+                    }
+                  else
+                    { acov = nbeg + MIN_COVER;
+#ifdef DEBUG_HOLE_FINDER
+                      printf(" No wall mapping\n");
+#endif
+                    }
+
+                // If apos and dpos are still so close that the implied hole boundaries
+                //   are out of order by 50 or more bases, then walk back through ascending
+                //   walls (if present) until this is no longer true or there are no more
+                //   more walls left.  If both left and right options exist, always take
+                //   the wall starting at the lower current height.
+
+                  while (apos - dpos < 2*COVER_LEN - 50)
+                    { if (d >= 0 && dwall[d].cov >= dcov)
+                        if (a < anum && awall[a].cov >= acov)
+                          { if (dcov < acov)
+                              { dcov = dwall[d].cov + dwall[d].cnt;
+                                dpos = dwall[d--].beg;
+#ifdef DEBUG_HOLE_FINDER
+                                printf("  <- %d\n",dpos);
+#endif
+                              }
+                            else
+                              { acov = awall[a].cov + awall[a].cnt;
+                                apos = awall[a++].end;
+#ifdef DEBUG_HOLE_FINDER
+                                printf("  -> %d\n",apos);
+#endif
+                              }
+                          }
+                        else
+                          { dcov = dwall[d].cov + dwall[d].cnt;
+                            dpos = dwall[d--].beg;
+#ifdef DEBUG_HOLE_FINDER
+                            printf("  <- %d\n",dpos);
+#endif
+                          }
+                      else
+                        if (a < anum && awall[a].cov >= acov)
+                          { acov = awall[a].cov + awall[a].cnt;
+                            apos = awall[a++].end;
+#ifdef DEBUG_HOLE_FINDER
+                            printf("  -> %d\n",apos);
+#endif
+                          }
+                        else
+                          {
+#ifdef DEBUG_HOLE_FINDER
+                            printf("  FAULT\n");
+#endif
+                            break;
+                          }
+                    }
+                }
+
+                // Finalize and record the hole boundaries.
+
+                holes[nhole].beg = dpos + COVER_LEN;
+                holes[nhole].end = apos - COVER_LEN;
+                nhole += 1;
+              }
+            in   = 0;
+          }
+      }
+  }
+
+  //  See if the holes remove or split any HQ-blocks and build the revised list
+  //    in newblk[0..q).
+
+  { int i, p, q, x;
+    int lhang, rhang;
+#ifdef DEBUG_HOLE_FINDER
+    int reverse;
+#endif
+
+    //  For each hole in left-to-right order
+
+    p = q = 0;
+    for (i = 0; i < nhole; i++)
+      { if (holes[i].beg > holes[i].end)
+          { x = holes[i].beg;
+            holes[i].beg = holes[i].end;
+            holes[i].end = x;
+#ifdef DEBUG_HOLE_FINDER
+            reverse = 1;
+#endif
+          }
+#ifdef DEBUG_HOLE_FINDER
+        else
+          reverse = 0;
+#endif
+
+        //  Advance to the next block p that intersects with or is to the right of hole
+        //    moving blocks being skipped over to the new block list
+
+        while (p < nblk && block[p].end <= holes[i].beg)
+          nwblk[q++] = block[p++];
+
+#ifdef DEBUG_HOLE_FINDER
+        printf("  HOLE: %5d [%5d,%5d]\n",
+               aread+1,holes[i].beg,holes[i].end);
+#endif
+      
+        //  While the current block intersects the current hole
+
+        while (p < nblk && block[p].beg < holes[i].end)
+          { lhang = (holes[i].beg < block[p].beg);
+            rhang = (holes[i].end > block[p].end);
+            if (lhang)
+              { if (rhang)
+
+                  //  Hole i contains block p: remove it if coverage <= 4 at both ends
+
+                  { if (block[p].end - block[p].beg >= MIN_BLOCK &&
+                          (cover[p].beg > 4 || cover[p].end > 4))
+                      nwblk[q++] = block[p];
+                    p += 1;
+#ifdef DEBUG_HOLE_FINDER
+                    printf("      INTERSECT %5d S [%5d,%5d] %3d %3d",
+                           aread+1,block[p-1].beg,block[p-1].end,cover[p-1].beg,cover[p-1].end);
+                    if (reverse)
+                      printf(" REV");
+                    printf("\n");
+#endif
+                  }
+
+                 //  Hole i intersect the left tip of block p: nothing to do
+
+                else
+                  {
+#ifdef DEBUG_HOLE_FINDER
+                    printf("      INTERSECT %5d Z %5d [..,%5d] %3d",
+                           aread+1,holes[i].end-block[p].beg,holes[i].end,cover[p].beg);
+                    if (reverse)
+                      printf(" REV");
+                    printf("\n");
+#endif
+                    break;
+                  }
+              }
+            else
+              if (rhang)
+
+                //  Hole i intersect the right tip of block p: move p to new block list
+
+                { nwblk[q++] = block[p++];
+#ifdef DEBUG_HOLE_FINDER
+                  printf("      INTERSECT %5d Z %5d [%5d,..] %3d",
+                         aread+1,block[p-1].end-holes[i].beg,holes[i].beg,cover[p-1].end);
+                  if (reverse)
+                    printf(" REV");
+                  printf("\n");
+#endif
+                }
+              else
+
+                //  Hole i is contained within block p:  Break block into two parts at
+                //    TRACE_SPACING ticks left and right of hole, and keep each piece
+                //    if they are greater than MIN_BLOCK long.
+
+                { int beg, end;
+
+#ifdef DEBUG_HOLE_FINDER
+                  printf("      INTERSECT %5d C %5d [%5d,%5d]",
+                         aread+1,holes[i].end-holes[i].beg,block[p].beg,block[p].end);
+                  if (reverse)
+                    printf(" REV");
+		  printf("\n");
+#endif
+                  beg = (holes[i].beg/TRACE_SPACING);
+                  end = (holes[i].end-1)/TRACE_SPACING+1;
+                  if (beg == end)
+                    { beg -= 1; end += 1; }
+                  beg *= TRACE_SPACING;
+                  end *= TRACE_SPACING;
+                  if (beg - block[p].beg >= MIN_BLOCK)
+                    { nwblk[q].beg = block[p].beg;
+                      nwblk[q++].end = beg;
+                    }
+                  if (block[p].end - end >= MIN_BLOCK)
+                    block[p].beg = end;
+                  else
+                    p += 1;
+                  break;
+                }
+          }
+      }
+
+    //  Remove any remaining blocks to the new list
+
+    while (p < nblk)
+      nwblk[q++] = block[p++];
+    nblk = q;
+  }
+
+  // Return the list of holes holes[0..nhole) and the new list of blocks, nwblk[0..nblk)
+
+  *p_nblk  = nblk;
+  *p_block = nwblk;
+  *p_nhole = nhole;
+  return (holes);
+}
+
+
+/*******************************************************************************************
+ *
+ *  FIND ANY UNREMOVED ADAPTER (OR POLYMERASE SWITCHES) AND TRIM SMALLER PARTS
+ *
+ ********************************************************************************************/
+
+static int GSORT(const void *l, const void *r)
+{ int x = *((int *) l);
+  int y = *((int *) r);
+
+  return (x - y);
+}
+
+#define LOWQ  0
+#define SPAN  1
+#define SPLIT 2
+#define ADAPT 3
+
+static int gap_status(Overlap *ovls, int novl, Interval *rblock)
+{ static int  nmax = 0;
+
+  static int *gsort  = NULL;       //  A-B delta for all B-reads spanning a gap
+  static int *asort  = NULL;       //  A-B delta for all B-reads spanning a gap
+
+  Interval *lblock = rblock-1;
+
+  int j;
+  int lft, rgt;
+  int lcv, rcv; 
+  int cnt;
+
+  if (novl > nmax)
+    { nmax = 1.2*novl + 500;
+      gsort = (int *) Realloc(gsort,nmax*sizeof(int),"Allocating gap vector");
+      asort = (int *) Realloc(asort,nmax*sizeof(int),"Allocating adaptemer position vector");
+      if (gsort == NULL || asort == NULL)
+        exit (1);
+    }
+
+  lft = lblock->end;
+  rgt = rblock->beg;
+  lcv = lft - COVER_LEN;
+  rcv = rgt + COVER_LEN;
+  if (lcv < lblock->beg)
+    lcv = lblock->beg;
+  if (rcv > rblock->end)
+    rcv = rblock->end;
+
+#ifdef DEBUG_GAP_STATUS
+  printf("  GAP [%5d,%5d]\n",lcv,rcv);
+#endif
+
+  cnt = 0;
+  for (j = 0; j < novl; j++)
+    if (ovls[j].path.abpos <= lcv && ovls[j].path.aepos >= rcv)
+      { cnt += 1;
+        if (cnt >= 10)
+          break;
+      }
+  if (cnt >= 10)
+    { 
+#ifdef DEBUG_GAP_STATUS
+      printf("    LOWQ\n");
+#endif
+      return (LOWQ);
+    }
+
+  { int bread, bcomp, blen;
+    int ab, ae;
+    int lcnt, rcnt, scnt, gcnt, acnt;
+    int lidx, ridx, sidx, cidx;
+    int k;
+
+    lcnt = rcnt = scnt = gcnt = acnt = 0;
+    for (j = 0; j < novl; j = k)
+      { bread = ovls[j].bread;
+        blen  = DB->reads[bread].rlen;
+        bcomp = COMP(ovls[j].flags);
+        if (bcomp)
+          cidx = j;
+
+        lidx = ridx = sidx = -1;
+        for (k = j; k < novl; k++)
+          { if (ovls[k].bread != bread)
+              break;
+            if (COMP(ovls[k].flags) != (uint32) bcomp)
+              { cidx  = k;
+                bcomp = COMP(ovls[k].flags);
+              }
+            ab = ovls[k].path.abpos;
+            ae = ovls[k].path.aepos;
+
+#ifdef SHOW_PAIRS
+            printf("\n %5d [%5d,%5d] %c",bread,ab,ae,COMP(ovls[k].flags)?'c':'n');
+            if (ab <= lcv && ae >= rcv)
+              printf("s");
+            else
+              printf(" ");
+#endif
+
+            if (ab <= lcv && ae >= rcv)
+              { sidx = k;
+                continue;
+              }
+
+            //  Duplicate left or right hits were due mainly to low complexity sequence
+            //     Just ignore, you want to lose the left or right have that is bad
+            //  Duplicate pairs are due entirely to unremoved adapters (palindromes)
+            //     Let the complement pair go through, both are equivalent
+
+#ifdef SHOW_PAIRS
+           if (ae >= rcv && ab <= rcv && ab - ovls[k].path.bbpos <= lft - MIN_LEN)
+             printf("r");
+           else
+             printf(" ");
+           if (ab <= lcv && ae >= lcv && ae + (blen-ovls[j].path.bepos) >= rgt + MIN_LEN)
+             printf("l");
+           else
+             printf(" ");
+#endif
+
+            if (ae >= rcv && ab <= rcv && ab - ovls[k].path.bbpos <= lft - MIN_LEN)
+              ridx = k;
+            if (ab <= lcv && ae >= lcv && ae + (blen-ovls[j].path.bepos) >= rgt + MIN_LEN)
+              lidx = k;
+          }
+        if (! bcomp)
+          cidx = k;
+
+#ifdef SHOW_PAIRS
+        printf(" =");
+        if (sidx >= 0)
+          printf(" S");
+        if (lidx >= 0)
+          printf(" L");
+        if (ridx >= 0)
+          printf(" R");
+        if (0 <= lidx && lidx < ridx && (ridx < cidx || lidx >= cidx))
+          printf(" G");
+        if ((0<=ridx && ridx<cidx && cidx<=lidx) || (0<=lidx && lidx<cidx && cidx<=ridx))
+          printf(" A");
+#endif
+
+        if (sidx >= 0)
+          scnt += 1;
+        if (lidx >= 0)
+          lcnt += 1;
+        if (ridx >= 0)
+          rcnt += 1;
+        if (0 <= lidx && lidx < ridx && (ridx < cidx || cidx <= lidx))
+          gsort[gcnt++] = (ovls[ridx].path.abpos - ovls[lidx].path.aepos)
+                        - (ovls[ridx].path.bbpos - ovls[lidx].path.bepos);
+        if ((0<=ridx && ridx<cidx && cidx<=lidx) || (0<=lidx && lidx<cidx && cidx<=ridx))
+          asort[acnt++] = (((blen-ovls[ridx].path.bbpos) - ovls[lidx].path.bepos)
+                        + (ovls[lidx].path.aepos + ovls[ridx].path.abpos))/2;
+      }
+
+#ifdef SHOW_PAIRS
+    printf("\n");
+#endif
+
+#ifdef DEBUG_GAP_STATUS
+    printf("    lcnt = %d  scnt = %d(%d)  rcnt = %d acnt = %d\n",lcnt,gcnt,scnt,rcnt,acnt);
+#endif
+
+    { int64 med, dev = 0;
+      int   std, low, hgh;
+
+      if (lcnt < rcnt)
+        rcnt = lcnt;
+  
+      if (acnt >= .4*rcnt && scnt+gcnt < .3*acnt) 
+        { qsort(asort,acnt,sizeof(int),GSORT);
+          med = asort[acnt/2];
+          low = acnt*.25;
+          hgh = acnt*.75;
+          for (j = low; j <= hgh; j++)
+            dev = (asort[j]-med)*(asort[j]-med);
+          std = sqrt((1.*dev)/acnt);
+          if (std > 200)
+            printf("  Warning: Read %d adaptemer test may be wrong\n",ovls[0].aread);
+#ifdef DEBUG_GAP_STATUS
+          printf("    ADAPT %3d\n",std);
+#endif
+          return (ADAPT);
+        }
+  
+      qsort(gsort,gcnt,sizeof(int),GSORT);
+      med = gsort[gcnt/2];
+      low = gcnt*.25;
+      hgh = gcnt*.75;
+      for (j = low; j <= hgh; j++)
+        dev = (gsort[j]-med)*(gsort[j]-med);
+      std = sqrt((1.*dev)/gcnt);
+  
+      if (std < 150 && scnt+gcnt >= 10 && (scnt+gcnt >= .4*rcnt || scnt+gcnt >= 20))
+        {
+#ifdef DEBUG_GAP_STATUS
+          printf("    SPAN %3d %5d\n",std,rgt-lft);
+#endif
+          return (SPAN);
+        }
+      else
+        {
+#ifdef DEBUG_GAP_STATUS
+          if (rcnt >= 20)
+            printf("    STRONG SPLIT\n");
+          else
+            printf("    WEAK SPLIT\n");
+          if (scnt + gcnt >= 10)
+            printf("  UNCERTAIN %5.1f %5d %3d\n",(scnt+gcnt)/(.01*rcnt),rgt-lft,scnt+gcnt);
+#endif
+          return (SPLIT);
+        }
+    }
+  }
+}
+
+static int *GAP_ANALYSIS(Overlap *ovls, int novl, Interval *block, int nblk)
+{ static int  bmax = 0;
+
+  static int *status = NULL;      //  Status of gaps between HQ_blocks
+
+  int i;
+
+  if (nblk > bmax)
+    { bmax = 1.2*nblk + 100;
+      status = (int *) Realloc(status,bmax*sizeof(int),"Allocating status vector");
+      if (status == NULL)
+        exit (1);
+    }
+
+#ifdef DEBUG_GAP_STATUS
+  printf("  GAPS\n");
+#endif
+  for (i = 1; i < nblk; i++)
+    status[i] = gap_status(ovls,novl,block+i);
+
+  return (status);
+}
+
+
+/*******************************************************************************************
+ *
+ *  SCRUB EACH PILE:
+ *     Trim low-quality tips of reads and patch low quality intervals within a sequence
+ *     Trim adapter (and associated redundant prefix or suffix)
+ *     Break chimers or all unscaffoldable no-coverage gaps of reads
+ *
+ ********************************************************************************************/
+
+//  Analyze all the gaps between the good patches found in the first pass.
+//  Consider a hole between two good intervals [lb,le] and [rb,re].  An overlap
+  //    is anchored to the left of the whole if abpos <= le-COVER_LEN and aepos >= rb+COVER_LEN
+
+static void GAPS(int aread, Overlap *ovls, int novl)
+{ int       alen;
+
+  int       nblk;
+  Interval *block;
+  int      *status;
+
+  int       nhole;
+  Interval *holes;
+
+#if defined(DEBUG_HQ_BLOCKS) || defined(SHOW_EVENTS) || defined(DEBUG_HOLE_FINDER) || defined(DEBUG_ADAPTER)
+  printf("\n");
+  printf("AREAD %d\n",aread);
+#endif
+
+  alen = DB->reads[aread].rlen;
+
+  if (VERBOSE)
+    { nreads += 1;
+      totlen += alen;
+    }
+
+  //  Partition into HQ-blocks
+
+  block = HQ_BLOCKS(aread,&nblk);
+
+  //  No blocks? ==> nothing to do
+
+  if (nblk <= 0)
+    { if (VERBOSE)
+        { nelim += 1;
+          nelimbp += alen;
+        }
+#ifdef ANNOTATE
+      fwrite(&HQ_INDEX,sizeof(int64),1,HQ_AFILE);
+      fwrite(&SN_INDEX,sizeof(int64),1,SN_AFILE);
+      fwrite(&SP_INDEX,sizeof(int64),1,SP_AFILE);
+      fwrite(&AD_INDEX,sizeof(int64),1,AD_AFILE);
+
+      fwrite(&HL_INDEX,sizeof(int64),1,HL_AFILE);
+
+      fwrite(&KP_INDEX,sizeof(int64),1,KP_AFILE);
+#endif
+      return;
+    }
+
+  //  Find holes and modify HQ-blocks if necessary
+
+  holes = FIND_HOLES(aread,ovls,novl,&nhole,&block,&nblk);
+
+  if (VERBOSE)
+    { if (block[0].beg > 0)
+        { n5trm += 1;
+          n5trmbp += block[0].beg;
+        }
+      if (block[nblk-1].end < alen)
+        { n3trm += 1;
+          n3trmbp += alen - block[nblk-1].end;
+        }
+    }
+
+  //  Determine the status of each gap between a pair of blocks
+
+  status = GAP_ANALYSIS(ovls,novl,block,nblk);
+
+#ifdef ANNOTATE
+  { int i;
+
+    for (i = 0; i < nblk; i++)
+      { fwrite(&(block[i].beg),sizeof(int),1,HQ_DFILE);
+        fwrite(&(block[i].end),sizeof(int),1,HQ_DFILE);
+        if (i > 0)
+          { if (status[i] == SPAN || status[i] == LOWQ)
+              { fwrite(&(block[i-1].end),sizeof(int),1,SN_DFILE);
+                fwrite(&(block[i].beg),sizeof(int),1,SN_DFILE);
+                SN_INDEX += 2*sizeof(int);
+              }
+            else if (status[i] == SPLIT)
+              { fwrite(&(block[i-1].end),sizeof(int),1,SP_DFILE);
+                fwrite(&(block[i].beg),sizeof(int),1,SP_DFILE);
+                SP_INDEX += 2*sizeof(int);
+              }
+            else //  status[i] == ADAPT 
+              { fwrite(&(block[i-1].end),sizeof(int),1,AD_DFILE);
+                fwrite(&(block[i].beg),sizeof(int),1,AD_DFILE);
+                AD_INDEX += 2*sizeof(int);
+              }
+          }
+      }
+    HQ_INDEX += 2*sizeof(int)*nblk;
+    fwrite(&HQ_INDEX,sizeof(int64),1,HQ_AFILE);
+    fwrite(&SN_INDEX,sizeof(int64),1,SN_AFILE);
+    fwrite(&SP_INDEX,sizeof(int64),1,SP_AFILE);
+    fwrite(&AD_INDEX,sizeof(int64),1,AD_AFILE);
+
+    for (i = 0; i < nhole; i++)
+      if (holes[i].end - holes[i].beg < 75)
+        { holes[i].end += 50;
+          holes[i].beg -= 50;
+          fwrite(&(holes[i].beg),sizeof(int),1,HL_DFILE);
+          fwrite(&(holes[i].end),sizeof(int),1,HL_DFILE);
+          holes[i].end -= 50;
+          holes[i].beg += 50;
+        }
+      else
+        { fwrite(&(holes[i].beg),sizeof(int),1,HL_DFILE);
+          fwrite(&(holes[i].end),sizeof(int),1,HL_DFILE);
+        }
+    HL_INDEX += 2*nhole*sizeof(int);
+    fwrite(&HL_INDEX,sizeof(int64),1,HL_AFILE);
+  }
+#endif
+
+  { int cmax, amax, abeg = 0, aend = 0;
+    int p, i;
+
+    amax = 0;
+    p = 0;
+    cmax = block[0].end-block[0].beg;
+    for (i = 1; i < nblk; i++)
+      if (status[i] == ADAPT)
+        { if (cmax > amax)
+            { amax = cmax;
+              abeg = p;
+              aend = i;
+            }
+          p = i;
+          cmax = block[i].end - block[i].beg; 
+        }
+      else if (status[i] != SPLIT)
+        cmax += block[i].end - block[i-1].end;
+      else
+        cmax += block[i].end - block[i].beg;
+    if (cmax > amax)
+      { amax = cmax;
+        abeg = p;
+        aend = nblk;
+      }
+ 
+#ifdef DEBUG_ADAPTER
+    printf("    Keeping %d-%d [%d,%d]\n",abeg,aend-1,block[abeg].beg,block[aend-1].end);
+#endif
+
+    if (VERBOSE)
+      { if (abeg > 0)
+          { natrm   += 1;
+            natrmbp += block[abeg].beg - block[0].beg;
+          }
+        if (aend < nblk)
+          { natrm   += 1;
+            natrmbp += (block[nblk-1].end - block[aend-1].end);
+          }
+        for (i = abeg+1; i < aend; i++)
+          { ngaps += 1;
+            ngapsbp += block[i].beg - block[i-1].end;
+            if (status[i] == LOWQ)
+              { nlowq   += 1;
+                nlowqbp += block[i].beg - block[i-1].end;
+              }
+            else if (status[i] == SPAN)
+              { nspan   += 1;
+                nspanbp += block[i].beg - block[i-1].end;
+              }
+            else // status[i] == SPLIT
+              { nchim   += 1;
+                nchimbp += block[i].beg - block[i-1].end;
+              }
+          }
+      }
+
+    nblk    = aend-abeg;
+    block  += abeg;
+    status += abeg;
+    holes  += abeg;
+  }
+
+#ifdef ANNOTATE
+  { int i;
+
+    fwrite(&(block[0].beg),sizeof(int),1,KP_DFILE);
+    for (i = 1; i < nblk; i++)
+      if (status[i] == SPLIT)
+        { fwrite(&(block[i-1].end),sizeof(int),1,KP_DFILE);
+          fwrite(&(block[i].beg),sizeof(int),1,KP_DFILE);
+          KP_INDEX += 2*sizeof(int);
+        }
+    fwrite(&(block[nblk-1].end),sizeof(int),1,KP_DFILE);
+    KP_INDEX += 2*sizeof(int);
+    fwrite(&KP_INDEX,sizeof(int64),1,KP_AFILE);
+  }
+#endif
+}
+
+
+  //  Read in each successive pile and call ACTION on it.  Read in the traces only if
+  //   "trace" is nonzero
+
+static int make_a_pass(FILE *input, void (*ACTION)(int, Overlap *, int), int trace)
+{ static Overlap *ovls = NULL;
+  static int      omax = 500;
+  static uint16  *paths = NULL;
+  static int      pmax = 100000;
+
+  int64 i, j, novl;
+  int   n, a;
+  int   pcur;
+  int   max;
+  int   tbytes;
+
+  if (ovls == NULL)
+    { ovls = (Overlap *) Malloc(sizeof(Overlap)*omax,"Allocating overlap buffer");
+      if (ovls == NULL)
+        exit (1);
+    }
+  if (trace && paths == NULL)
+    { paths = (uint16 *) Malloc(sizeof(uint16)*pmax,"Allocating path buffer");
+      if (paths == NULL)
+        exit (1);
+    }
+
+  rewind(input);
+  fread(&novl,sizeof(int64),1,input);
+  fread(&TRACE_SPACING,sizeof(int),1,input);
+  if (TRACE_SPACING <= TRACE_XOVR)
+    tbytes = sizeof(uint8);
+  else
+    tbytes = sizeof(uint16);
+
+  Read_Overlap(input,ovls);
+  if (trace)
+    { if (ovls[0].path.tlen > pmax)
+        { pmax  = 1.2*(ovls[0].path.tlen)+10000;
+          paths = (uint16 *) Realloc(paths,sizeof(uint16)*pmax,"Expanding path buffer");
+          if (paths == NULL) exit (1);
+        }
+      fread(paths,tbytes,ovls[0].path.tlen,input);
+      if (tbytes == 1)
+        { ovls[0].path.trace = paths;
+          Decompress_TraceTo16(ovls);
+        }
+    }
+  else
+    fseek(input,tbytes*ovls[0].path.tlen,SEEK_CUR);
+
+  if (ovls[0].aread < DB_FIRST)
+    { fprintf(stderr,"%s: .las file overlaps don't correspond to reads in block %d of DB\n",
+                     Prog_Name,DB_PART);
+      exit (1);
+    }
+
+  pcur = 0;
+  n = max = 0;
+  for (j = DB_FIRST; j < DB_LAST; j++)
+    { ovls[0] = ovls[n];
+      a = ovls[0].aread;
+      if (a != j)
+        n = 0;
+      else
+        { if (trace)
+            memcpy(paths,paths+pcur,sizeof(uint16)*ovls[0].path.tlen);
+          n = 1;
+          pcur = ovls[0].path.tlen;
+          while (1)
+            { if (Read_Overlap(input,ovls+n) != 0)
+                { ovls[n].aread = INT32_MAX;
+                  break;
+                }
+              if (trace)
+                { if (pcur + ovls[n].path.tlen > pmax)
+                    { pmax = 1.2*(pcur+ovls[n].path.tlen)+10000;
+                      paths = (uint16 *) Realloc(paths,sizeof(uint16)*pmax,"Expanding path buffer");
+                      if (paths == NULL) exit (1);
+                    }
+                  fread(paths+pcur,tbytes,ovls[n].path.tlen,input);
+                  if (tbytes == 1)
+                    { ovls[n].path.trace = paths+pcur;
+                      Decompress_TraceTo16(ovls+n);
+                    }
+                }
+              else
+                fseek(input,tbytes*ovls[n].path.tlen,SEEK_CUR);
+              if (ovls[n].aread != a)
+                break;
+              pcur += ovls[n].path.tlen;
+              n    += 1;
+              if (n >= omax)
+                { omax = 1.2*n + 100;
+                  ovls = (Overlap *) Realloc(ovls,sizeof(Overlap)*omax,"Expanding overlap buffer");
+                  if (ovls == NULL) exit (1);
+                }
+            }
+
+          if (n >= max)
+            max = n;
+          pcur = 0;
+          for (i = 0; i < n; i++)
+            { ovls[i].path.trace = paths+pcur;
+              pcur += ovls[i].path.tlen;
+            }
+        }
+      ACTION(j,ovls,n);
+    }
+
+  return (max);
+}
+
+int main(int argc, char *argv[])
+{ FILE       *input;
+  char       *root, *dpwd;
+  char       *las, *lpwd;
+  int64       novl;
+  HITS_TRACK *track;
+  int         c;
+
+  //  Process arguments
+
+  { int  i, j, k;
+    int  flags[128];
+    char *eptr;
+
+    ARG_INIT("DAStrim")
+
+    BAD_QV    = -1;
+    GOOD_QV   = -1;
+    MIN_LEN   = 1000;
+
+    j = 1;
+    for (i = 1; i < argc; i++)
+      if (argv[i][0] == '-')
+        switch (argv[i][1])
+        { default:
+            ARG_FLAGS("v")
+            break;
+          case 'b':
+            ARG_NON_NEGATIVE(BAD_QV,"Minimum QV score for being considered bad")
+            break;
+          case 'g':
+            ARG_NON_NEGATIVE(GOOD_QV,"Maximum QV score for being considered good")
+            break;
+          case 'l':
+            ARG_POSITIVE(MIN_LEN,"Minimum retained segment length")
+            break;
+        }
+      else
+        argv[j++] = argv[i];
+    argc = j;
+
+    VERBOSE = flags['v'];
+
+    if (argc < 3)
+      { fprintf(stderr,"Usage: %s %s\n",Prog_Name,Usage);
+        exit (1);
+      }
+
+    if (GOOD_QV < 0)
+      { fprintf(stderr,"%s: Must supply -g parameter\n",Prog_Name);
+        exit (1);
+      }
+    if (BAD_QV < 0)
+      { fprintf(stderr,"%s: Must supply -b parameter\n",Prog_Name);
+        exit (1);
+      }
+    if (GOOD_QV > BAD_QV)
+      { fprintf(stderr,"%s: Good QV threshold (%d) > Bad QV threshold (%d) ?\n",
+                       Prog_Name,GOOD_QV,BAD_QV);
+        exit (1);
+      }
+  }
+
+  //  Open trimmed DB and the prim-track
+
+  { int status;
+
+    status = Open_DB(argv[1],DB);
+    if (status < 0)
+      exit (1);
+    if (status == 1)
+      { fprintf(stderr,"%s: Cannot be called on a .dam index: %s\n",Prog_Name,argv[1]);
+        exit (1);
+      }
+    if (DB->part)
+      { fprintf(stderr,"%s: Cannot be called on a block: %s\n",Prog_Name,argv[1]);
+        exit (1);
+      }
+    Trim_DB(DB);
+  }
+
+  //  Initialize statistics gathering
+
+  if (VERBOSE)
+    { nreads  = 0;
+      totlen  = 0;
+      nelim   = 0;
+      n5trm   = 0;
+      n3trm   = 0;
+      natrm   = 0;
+      nelimbp = 0;
+      n5trmbp = 0;
+      n3trmbp = 0;
+      natrmbp = 0;
+
+      ngaps   = 0;
+      nlowq   = 0;
+      nspan   = 0;
+      nchim   = 0;
+      ngapsbp = 0;
+      nlowqbp = 0;
+      nspanbp = 0;
+      nchimbp = 0;
+
+      printf("\nDAStrim -g%d -b%d -l%d %s", GOOD_QV,BAD_QV,MIN_LEN,argv[1]);
+      for (c = 2; c < argc; c++)
+        printf(" %s",argv[c]);
+      printf("\n");
+    }
+
+  //  Determine if overlap block is being processed and if so get first and last read
+  //    from .db file
+
+  dpwd = PathTo(argv[1]);
+  root = Root(argv[1],".db");
+
+  for (c = 2; c < argc; c++)
+    { las  = Root(argv[c],".las");
+
+      { FILE *dbfile;
+        char  buffer[2*MAX_NAME+100];
+        char *p, *eptr;
+        int   i, part, nfiles, nblocks, cutoff, all, oindx;
+        int64 size;
+
+        DB_PART  = 0;
+        DB_FIRST = 0;
+        DB_LAST  = DB->nreads;
+
+        p = rindex(las,'.');
+        if (p != NULL)
+          { part = strtol(p+1,&eptr,10);
+            if (*eptr == '\0' && eptr != p+1)
+              { dbfile = Fopen(Catenate(dpwd,"/",root,".db"),"r");
+                if (dbfile == NULL)
+                  exit (1);
+                if (fscanf(dbfile,DB_NFILE,&nfiles) != 1)
+                  SYSTEM_ERROR
+                for (i = 0; i < nfiles; i++)
+                  if (fgets(buffer,2*MAX_NAME+100,dbfile) == NULL)
+                    SYSTEM_ERROR
+                if (fscanf(dbfile,DB_NBLOCK,&nblocks) != 1)
+                  SYSTEM_ERROR
+                if (fscanf(dbfile,DB_PARAMS,&size,&cutoff,&all) != 3)
+                  SYSTEM_ERROR
+                for (i = 1; i <= part; i++)
+                  if (fscanf(dbfile,DB_BDATA,&oindx,&DB_FIRST) != 2)
+                    SYSTEM_ERROR
+                if (fscanf(dbfile,DB_BDATA,&oindx,&DB_LAST) != 2)
+                  SYSTEM_ERROR
+                fclose(dbfile);
+                DB_PART = part;
+                *p = '\0';
+              }
+          }
+      }
+
+      track = Load_Track(DB,"qual");
+      if (track != NULL)
+        { QV_IDX = (int64 *) track->anno;
+          QV     = (uint8 *) track->data;
+        }
+      else
+        { fprintf(stderr,"%s: Must have a 'qual' track, run DASqv\n",Prog_Name);
+          exit (1);
+        }
+
+#ifdef ANNOTATE
+
+      //  Set up QV trimming track
+
+#define SETUP(AFILE,DFILE,INDEX,anno,data)					\
+{ int len, size;								\
+										\
+  if (DB_PART > 0)								\
+    { AFILE = Fopen(Catenate(dpwd,PATHSEP,root,					\
+                                Numbered_Suffix(".",DB_PART,anno)),"w");	\
+      DFILE = Fopen(Catenate(dpwd,PATHSEP,root,					\
+                                Numbered_Suffix(".",DB_PART,data)),"w");	\
+    }										\
+  else										\
+    { AFILE = Fopen(Catenate(dpwd,PATHSEP,root,anno),"w");			\
+      DFILE = Fopen(Catenate(dpwd,PATHSEP,root,data),"w");			\
+    }										\
+  if (AFILE == NULL || DFILE == NULL)						\
+    exit (1);									\
+										\
+  len  = DB_LAST - DB_FIRST;							\
+  size = 0;									\
+  fwrite(&len,sizeof(int),1,AFILE);						\
+  fwrite(&size,sizeof(int),1,AFILE);						\
+  INDEX = 0;									\
+  fwrite(&INDEX,sizeof(int64),1,AFILE);						\
+}
+
+      SETUP(HQ_AFILE,HQ_DFILE,HQ_INDEX,".hq.anno",".hq.data")
+      SETUP(SN_AFILE,SN_DFILE,SN_INDEX,".span.anno",".span.data")
+      SETUP(SP_AFILE,SP_DFILE,SP_INDEX,".split.anno",".split.data")
+      SETUP(AD_AFILE,AD_DFILE,AD_INDEX,".adapt.anno",".adapt.data")
+
+      SETUP(HL_AFILE,HL_DFILE,HL_INDEX,".hole.anno",".hole.data")
+
+      SETUP(KP_AFILE,KP_DFILE,KP_INDEX,".keep.anno",".keep.data")
+#endif
+
+      //  Open overlap file
+
+      lpwd = PathTo(argv[2]);
+      if (DB_PART)
+        input = Fopen(Catenate(lpwd,"/",las,Numbered_Suffix(".",DB_PART,".las")),"r");
+      else
+        input = Fopen(Catenate(lpwd,"/",las,".las"),"r");
+      if (input == NULL)
+        exit (1);
+
+      free(lpwd);
+      free(las);
+
+      //  Get trace point spacing information
+
+      fread(&novl,sizeof(int64),1,input);
+      fread(&TRACE_SPACING,sizeof(int),1,input);
+      MIN_LEN = (MIN_LEN  +(TRACE_SPACING-1))/TRACE_SPACING;
+
+      make_a_pass(input,GAPS,1);
+
+      //  Clean up
+
+
+#ifdef ANNOTATE
+      fclose(HQ_AFILE);
+      fclose(HQ_DFILE);
+
+      fclose(SN_AFILE);
+      fclose(SN_DFILE);
+
+      fclose(SP_AFILE);
+      fclose(SP_DFILE);
+
+      fclose(AD_AFILE);
+      fclose(AD_DFILE);
+
+      fclose(HL_AFILE);
+      fclose(HL_DFILE);
+
+      fclose(KP_AFILE);
+      fclose(KP_DFILE);
+#endif
+    }
+
+  //  If verbose output statistics summary to stdout
+
+  if (VERBOSE)
+    { printf("\nInput:    ");
+      Print_Number((int64) nreads,7,stdout);
+      printf(" (100.0%%) reads     ");
+      Print_Number(totlen,12,stdout);
+      printf(" (100.0%%) bases\n");
+
+      printf("Trimmed:  ");
+      Print_Number(nelim,7,stdout);
+      printf(" (%5.1f%%) reads     ",(100.*nelim)/nreads);
+      Print_Number(nelimbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*nelimbp)/totlen);
+
+      printf("5' trim:  ");
+      Print_Number(n5trm,7,stdout);
+      printf(" (%5.1f%%) reads     ",(100.*n5trm)/nreads);
+      Print_Number(n5trmbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*n5trmbp)/totlen);
+
+      printf("3' trim:  ");
+      Print_Number(n3trm,7,stdout);
+      printf(" (%5.1f%%) reads     ",(100.*n3trm)/nreads);
+      Print_Number(n3trmbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*n3trmbp)/totlen);
+
+      printf("Adapter:  ");
+      Print_Number(natrm,7,stdout);
+      printf(" (%5.1f%%) reads     ",(100.*natrm)/nreads);
+      Print_Number(natrmbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*natrmbp)/totlen);
+
+      printf("\n");
+
+      printf("Gaps:     ");
+      Print_Number(ngaps,7,stdout);
+      printf(" (%5.1f%%) gaps      ",(100.*(ngaps))/nreads);
+      Print_Number(ngapsbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(ngapsbp))/totlen);
+
+      printf("  Low QV: ");
+      Print_Number(nlowq,7,stdout);
+      printf(" (%5.1f%%) gaps      ",(100.*(nlowq))/nreads);
+      Print_Number(nlowqbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(nlowqbp))/totlen);
+
+      printf("  Span'd: ");
+      Print_Number(nspan,7,stdout);
+      printf(" (%5.1f%%) gaps      ",(100.*(nspan))/nreads);
+      Print_Number(nspanbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(nspanbp))/totlen);
+
+      printf("  Break:  ");
+      Print_Number(nchim,7,stdout);
+      printf(" (%5.1f%%) gaps      ",(100.*(nchim))/nreads);
+      Print_Number(nchimbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(nchimbp))/totlen);
+
+      printf("\n");
+
+      printf("Clipped:  ");
+      Print_Number(n5trm+n3trm+nelim+natrm+nchim,7,stdout);
+      printf(" clips              ");
+      Print_Number(n5trmbp+n3trmbp+nelimbp+natrmbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(n5trmbp+n3trmbp+nelimbp+natrmbp+nchimbp))/totlen);
+
+      printf("Patched:  ");
+      Print_Number(nlowq+nspan,7,stdout);
+      printf(" patches            ");
+      Print_Number(nlowqbp+nspanbp,12,stdout);
+      printf(" (%5.1f%%) bases\n",(100.*(nlowqbp+nspanbp))/totlen);
+    }
+
+  free(dpwd);
+  free(root);
+
+  Close_DB(DB);
+  free(Prog_Name);
+
+  exit (0);
+}
diff --git a/DB.c b/DB.c
new file mode 100644
index 0000000..b536536
--- /dev/null
+++ b/DB.c
@@ -0,0 +1,1733 @@
+/*******************************************************************************************
+ *
+ *  Compressed data base module.  Auxiliary routines to open and manipulate a data base for
+ *    which the sequence and read information are separated into two separate files, and the
+ *    sequence is compressed into 2-bits for each base.  Support for tracks of additional
+ *    information, and trimming according to the current partition.
+ *
+ *  Author :  Gene Myers
+ *  Date   :  July 2013
+ *  Revised:  April 2014
+ *
+ ********************************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "DB.h"
+
+#ifdef HIDE_FILES
+#define PATHSEP "/."
+#else
+#define PATHSEP "/"
+#endif
+
+
+/*******************************************************************************************
+ *
+ *  GENERAL UTILITIES
+ *
+ ********************************************************************************************/
+
+char *Prog_Name;
+
+#ifdef INTERACTIVE
+
+char Ebuffer[1000];
+
+#endif
+
+void *Malloc(int64 size, char *mesg)
+{ void *p;
+
+  if ((p = malloc(size)) == NULL)
+    { if (mesg == NULL)
+        EPRINTF(EPLACE,"%s: Out of memory\n",Prog_Name);
+      else
+        EPRINTF(EPLACE,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (p);
+}
+
+void *Realloc(void *p, int64 size, char *mesg)
+{ if (size <= 0)
+    size = 1;
+  if ((p = realloc(p,size)) == NULL)
+    { if (mesg == NULL)
+        EPRINTF(EPLACE,"%s: Out of memory\n",Prog_Name);
+      else
+        EPRINTF(EPLACE,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (p);
+}
+
+char *Strdup(char *name, char *mesg)
+{ char *s;
+
+  if (name == NULL)
+    return (NULL);
+  if ((s = strdup(name)) == NULL)
+    { if (mesg == NULL)
+        EPRINTF(EPLACE,"%s: Out of memory\n",Prog_Name);
+      else
+        EPRINTF(EPLACE,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (s);
+}
+
+FILE *Fopen(char *name, char *mode)
+{ FILE *f;
+
+  if (name == NULL || mode == NULL)
+    return (NULL);
+  if ((f = fopen(name,mode)) == NULL)
+    EPRINTF(EPLACE,"%s: Cannot open %s for '%s'\n",Prog_Name,name,mode);
+  return (f);
+}
+
+char *PathTo(char *name)
+{ char *path, *find;
+
+  if (name == NULL)
+    return (NULL);
+  if ((find = rindex(name,'/')) != NULL)
+    { *find = '\0';
+      path = Strdup(name,"Extracting path from");
+      *find = '/';
+    }
+  else
+    path = Strdup(".","Allocating default path");
+  return (path);
+}
+
+char *Root(char *name, char *suffix)
+{ char *path, *find, *dot;
+  int   epos;
+
+  if (name == NULL)
+    return (NULL);
+  find = rindex(name,'/');
+  if (find == NULL)
+    find = name;
+  else
+    find += 1;
+  if (suffix == NULL)
+    { dot = strchr(find,'.');
+      if (dot != NULL)
+        *dot = '\0';
+      path = Strdup(find,"Extracting root from");
+      if (dot != NULL)
+        *dot = '.';
+    }
+  else
+    { epos  = strlen(find);
+      epos -= strlen(suffix);
+      if (epos > 0 && strcasecmp(find+epos,suffix) == 0)
+        { find[epos] = '\0';
+          path = Strdup(find,"Extracting root from");
+          find[epos] = suffix[0];
+        }
+      else
+        path = Strdup(find,"Allocating root");
+    }
+  return (path);
+}
+
+char *Catenate(char *path, char *sep, char *root, char *suffix)
+{ static char *cat = NULL;
+  static int   max = -1;
+  int len;
+
+  if (path == NULL || root == NULL || sep == NULL || suffix == NULL)
+    return (NULL);
+  len =  strlen(path);
+  len += strlen(sep);
+  len += strlen(root);
+  len += strlen(suffix);
+  if (len > max)
+    { max = ((int) (1.2*len)) + 100;
+      if ((cat = (char *) realloc(cat,max+1)) == NULL)
+        { EPRINTF(EPLACE,"%s: Out of memory (Making path name for %s)\n",Prog_Name,root);
+          return (NULL);
+        }
+    }
+  sprintf(cat,"%s%s%s%s",path,sep,root,suffix);
+  return (cat);
+}
+
+char *Numbered_Suffix(char *left, int num, char *right)
+{ static char *suffix = NULL;
+  static int   max = -1;
+  int len;
+
+  if (left == NULL || right == NULL)
+    return (NULL);
+  len =  strlen(left);
+  len += strlen(right) + 40;
+  if (len > max)
+    { max = ((int) (1.2*len)) + 100;
+      if ((suffix = (char *) realloc(suffix,max+1)) == NULL)
+        { EPRINTF(EPLACE,"%s: Out of memory (Making number suffix for %d)\n",Prog_Name,num);
+          return (NULL);
+        }
+    }
+  sprintf(suffix,"%s%d%s",left,num,right);
+  return (suffix);
+}
+
+
+#define  COMMA  ','
+
+//  Print big integers with commas/periods for better readability
+
+void Print_Number(int64 num, int width, FILE *out)
+{ if (width == 0)
+    { if (num < 1000ll)
+        fprintf(out,"%lld",num);
+      else if (num < 1000000ll)
+        fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+      else if (num < 1000000000ll)
+        fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,
+                                           COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+      else
+        fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,
+                                                   COMMA,(num%1000000000ll)/1000000ll,
+                                                   COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+    }
+  else
+    { if (num < 1000ll)
+        fprintf(out,"%*lld",width,num);
+      else if (num < 1000000ll)
+        { if (width <= 4)
+            fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld",width-4,num/1000ll,COMMA,num%1000ll);
+        }
+      else if (num < 1000000000ll)
+        { if (width <= 8)
+            fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+                                               COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld%c%03lld",width-8,num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+                                                COMMA,num%1000ll);
+        }
+      else
+        { if (width <= 12)
+            fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,COMMA,
+                                                       (num%1000000000ll)/1000000ll,COMMA,
+                                                       (num%1000000ll)/1000ll,COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld%c%03lld%c%03lld",width-12,num/1000000000ll,COMMA,
+                                                        (num%1000000000ll)/1000000ll,COMMA,
+                                                        (num%1000000ll)/1000ll,COMMA,num%1000ll);
+        }
+    }
+}
+
+//  Return the number of digits, base 10, of num
+
+int  Number_Digits(int64 num)
+{ int digit;
+
+  digit = 0;
+  while (num >= 1)
+    { num /= 10;
+      digit += 1;
+    }
+  return (digit);
+}
+
+
+/*******************************************************************************************
+ *
+ *  READ COMPRESSION/DECOMPRESSION UTILITIES
+ *
+ ********************************************************************************************/
+
+//  Compress read into 2-bits per base (from [0-3] per byte representation
+
+void Compress_Read(int len, char *s)
+{ int   i;
+  char  c, d;
+  char *s0, *s1, *s2, *s3;
+
+  s0 = s;
+  s1 = s0+1;
+  s2 = s1+1;
+  s3 = s2+1;
+
+  c = s1[len];
+  d = s2[len];
+  s0[len] = s1[len] = s2[len] = 0;
+
+  for (i = 0; i < len; i += 4)
+    *s++ = (char ) ((s0[i] << 6) | (s1[i] << 4) | (s2[i] << 2) | s3[i]);
+
+  s1[len] = c;
+  s2[len] = d;
+}
+
+//  Uncompress read form 2-bits per base into [0-3] per byte representation
+
+void Uncompress_Read(int len, char *s)
+{ int   i, tlen, byte;
+  char *s0, *s1, *s2, *s3;
+  char *t;
+
+  s0 = s;
+  s1 = s0+1;
+  s2 = s1+1;
+  s3 = s2+1;
+
+  tlen = (len-1)/4;
+
+  t = s+tlen;
+  for (i = tlen*4; i >= 0; i -= 4)
+    { byte = *t--;
+      s0[i] = (char) ((byte >> 6) & 0x3);
+      s1[i] = (char) ((byte >> 4) & 0x3);
+      s2[i] = (char) ((byte >> 2) & 0x3);
+      s3[i] = (char) (byte & 0x3);
+    }
+  s[len] = 4;
+}
+
+//  Convert read in [0-3] representation to ascii representation (end with '\n')
+
+void Lower_Read(char *s)
+{ static char letter[4] = { 'a', 'c', 'g', 't' };
+
+  for ( ; *s != 4; s++)
+    *s = letter[(int) *s];
+  *s = '\0';
+}
+
+void Upper_Read(char *s)
+{ static char letter[4] = { 'A', 'C', 'G', 'T' };
+
+  for ( ; *s != 4; s++)
+    *s = letter[(int) *s];
+  *s = '\0';
+}
+
+//  Convert read in ascii representation to [0-3] representation (end with 4)
+
+void Number_Read(char *s)
+{ static char number[128] =
+    { 0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 1, 0, 0, 0, 2,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 3, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 1, 0, 0, 0, 2,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 3, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+    };
+
+  for ( ; *s != '\0'; s++)
+    *s = number[(int) *s];
+  *s = 4;
+}
+
+
+/*******************************************************************************************
+ *
+ *  DB OPEN, TRIM & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+
+// Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+//   a part # in it then just the part is opened.  The index array is allocated (for all or
+//   just the part) and read in.
+// Return status of routine:
+//    -1: The DB could not be opened for a reason reported by the routine to EPLACE
+//     0: Open of DB proceeded without mishap
+//     1: Open of DAM proceeded without mishap
+
+int Open_DB(char* path, HITS_DB *db)
+{ HITS_DB dbcopy;
+  char   *root, *pwd, *bptr, *fptr, *cat;
+  int     nreads;
+  FILE   *index, *dbvis;
+  int     status, plen, isdam;
+  int     part, cutoff, all;
+  int     ufirst, tfirst, ulast, tlast;
+
+  status = -1;
+  dbcopy = *db;
+
+  plen = strlen(path);
+  if (strcmp(path+(plen-4),".dam") == 0)
+    root = Root(path,".dam");
+  else
+    root = Root(path,".db");
+  pwd = PathTo(path);
+
+  bptr = rindex(root,'.');
+  if (bptr != NULL && bptr[1] != '\0' && bptr[1] != '-')
+    { part = strtol(bptr+1,&fptr,10);
+      if (*fptr != '\0' || part == 0)
+        part = 0;
+      else
+        *bptr = '\0';
+    }
+  else
+    part = 0;
+
+  isdam = 0;
+  cat = Catenate(pwd,"/",root,".db");
+  if (cat == NULL)
+    return (-1);
+  if ((dbvis = fopen(cat,"r")) == NULL)
+    { cat = Catenate(pwd,"/",root,".dam");
+      if (cat == NULL)
+        return (-1);
+      if ((dbvis = fopen(cat,"r")) == NULL)
+        { EPRINTF(EPLACE,"%s: Could not open database %s\n",Prog_Name,path);
+          goto error;
+        }
+      isdam = 1;
+    }
+
+  if ((index = Fopen(Catenate(pwd,PATHSEP,root,".idx"),"r")) == NULL)
+    goto error1;
+  if (fread(db,sizeof(HITS_DB),1,index) != 1)
+    { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+      goto error2;
+    }
+
+  { int   p, nblocks, nfiles;
+    int64 size;
+    char  fname[MAX_NAME], prolog[MAX_NAME];
+
+    nblocks = 0;
+    if (fscanf(dbvis,DB_NFILE,&nfiles) != 1)
+      { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+        goto error2;
+      }
+    for (p = 0; p < nfiles; p++)
+      if (fscanf(dbvis,DB_FDATA,&tlast,fname,prolog) != 3)
+        { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+          goto error2;
+        }
+    if (fscanf(dbvis,DB_NBLOCK,&nblocks) != 1)
+      if (part == 0)
+        { cutoff = 0;
+          all    = 1;
+        }
+      else
+        { EPRINTF(EPLACE,"%s: DB %s has not yet been partitioned, cannot request a block !\n",
+                         Prog_Name,root);
+          goto error2;
+        }
+    else
+      { if (fscanf(dbvis,DB_PARAMS,&size,&cutoff,&all) != 3)
+          { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+            goto error2;
+          }
+        if (part > nblocks)
+          { EPRINTF(EPLACE,"%s: DB %s has only %d blocks\n",Prog_Name,root,nblocks);
+            goto error2;
+          }
+      }
+
+    if (part > 0)
+      { for (p = 1; p <= part; p++)
+          if (fscanf(dbvis,DB_BDATA,&ufirst,&tfirst) != 2)
+            { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+              goto error2;
+            }
+        if (fscanf(dbvis,DB_BDATA,&ulast,&tlast) != 2)
+          { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+            goto error2;
+          }
+      }
+    else
+      { ufirst = tfirst = 0;
+        ulast  = db->ureads;
+        tlast  = db->treads;
+      }
+  }
+
+  db->trimmed = 0;
+  db->tracks  = NULL;
+  db->part    = part;
+  db->cutoff  = cutoff;
+  db->all     = all;
+  db->ufirst  = ufirst;
+  db->tfirst  = tfirst;
+
+  nreads = ulast-ufirst;
+  if (part <= 0)
+    { db->reads = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+2),"Allocating Open_DB index");
+      if (db->reads == NULL)
+        goto error2;
+      db->reads += 1;
+      if (fread(db->reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+        { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+          free(db->reads);
+          goto error2;
+        }
+    }
+  else
+    { HITS_READ *reads;
+      int        i, r, maxlen;
+      int64      totlen;
+
+      reads = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+2),"Allocating Open_DB index");
+      if (reads == NULL)
+        goto error2;
+      reads += 1;
+
+      fseeko(index,sizeof(HITS_READ)*ufirst,SEEK_CUR);
+      if (fread(reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+        { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+          free(reads);
+          goto error2;
+        }
+
+      totlen = 0;
+      maxlen = 0;
+      for (i = 0; i < nreads; i++)
+        { r = reads[i].rlen;
+          totlen += r;
+          if (r > maxlen)
+            maxlen = r;
+        }
+
+      db->maxlen = maxlen;
+      db->totlen = totlen;
+      db->reads  = reads;
+    }
+
+  ((int *) (db->reads))[-1] = ulast - ufirst;   //  Kludge, need these for DB part
+  ((int *) (db->reads))[-2] = tlast - tfirst;
+
+  db->nreads = nreads;
+  db->path   = Strdup(Catenate(pwd,PATHSEP,root,""),"Allocating Open_DB path");
+  if (db->path == NULL)
+    goto error2;
+  db->bases  = NULL;
+  db->loaded = 0;
+
+  status = isdam;
+
+error2:
+  fclose(index);
+error1:
+  fclose(dbvis);
+error:
+  if (bptr != NULL)
+    *bptr = '.';
+
+  free(pwd);
+  free(root);
+
+  if (status < 0)
+    *db = dbcopy;
+
+  return (status);
+}
+
+
+// Trim the DB or part thereof and all loaded tracks according to the cuttof and all settings
+//   of the current DB partition.  Reallocate smaller memory blocks for the information kept
+//   for the retained reads.
+
+void Trim_DB(HITS_DB *db)
+{ int         i, j, r;
+  int         allflag, cutoff;
+  int64       totlen;
+  int         maxlen, nreads;
+  HITS_TRACK *record;
+  HITS_READ  *reads;
+
+  if (db->trimmed) return;
+
+  if (db->cutoff <= 0 && db->all) return;
+
+  cutoff = db->cutoff;
+  if (db->all)
+    allflag = 0;
+  else
+    allflag = DB_BEST;
+
+  reads  = db->reads;
+  nreads = db->nreads;
+
+  for (record = db->tracks; record != NULL; record = record->next)
+    if (strcmp(record->name,". at qvs") == 0)
+      { uint16 *table = ((HITS_QV *) record)->table;
+
+        j = 0;
+        for (i = 0; i < db->nreads; i++)
+          if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+            table[j++] = table[i];
+      }
+    else
+      { int   *anno4, size;
+        int64 *anno8;
+        char  *anno, *data;
+
+        size = record->size;
+        data = (char *) record->data; 
+        if (data == NULL)
+          { anno = (char *) record->anno;
+            j = 0;
+            for (i = r = 0; i < db->nreads; i++, r += size)
+              if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+                { memmove(anno+j,anno+r,size);
+                  j += size;
+                }
+            memmove(anno+j,anno+r,size);
+          }
+        else if (size == 4)
+          { int ai;
+
+            anno4 = (int *) (record->anno);
+            j = anno4[0] = 0;
+            for (i = 0; i < db->nreads; i++)
+              if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+                { ai = anno4[i];
+                  anno4[j+1] = anno4[j] + (anno4[i+1]-ai);
+                  memmove(data+anno4[j],data+ai,anno4[i+1]-ai);
+                  j += 1;
+                }
+            record->data = Realloc(record->data,anno4[j],NULL);
+          }
+        else // size == 8
+          { int64 ai;
+
+            anno8 = (int64 *) (record->anno);
+            j = anno8[0] = 0;
+            for (i = 0; i < db->nreads; i++)
+              if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+                { ai = anno8[i];
+                  anno8[j+1] = anno8[j] + (anno8[i+1]-ai);
+                  memmove(data+anno8[j],data+ai,anno8[i+1]-ai);
+                  j += 1;
+                }
+            record->data = Realloc(record->data,anno8[j],NULL);
+          }
+        record->anno = Realloc(record->anno,record->size*(j+1),NULL);
+      }
+
+  totlen = maxlen = 0;
+  for (j = i = 0; i < nreads; i++)
+    { r = reads[i].rlen;
+      if ((reads[i].flags & DB_BEST) >= allflag && r >= cutoff)
+        { totlen += r;
+          if (r > maxlen)
+            maxlen = r;
+          reads[j++] = reads[i];
+        }
+    }
+  
+  db->totlen  = totlen;
+  db->maxlen  = maxlen;
+  db->nreads  = j;
+  db->trimmed = 1;
+
+  if (j < nreads)
+    { db->reads = Realloc(reads-1,sizeof(HITS_READ)*(j+2),NULL);
+      db->reads += 1;
+    }
+}
+
+// The DB has already been trimmed, but a track over the untrimmed DB needs to be loaded.
+//   Trim the track by rereading the untrimmed DB index from the file system.
+
+static int Late_Track_Trim(HITS_DB *db, HITS_TRACK *track, int ispart)
+{ int         i, j, r;
+  int         allflag, cutoff;
+  int         ureads;
+  char       *root;
+  HITS_READ   read;
+  FILE       *indx;
+
+  if (!db->trimmed) return (0);
+
+  if (db->cutoff <= 0 && db->all) return (0);
+
+  cutoff = db->cutoff;
+  if (db->all)
+    allflag = 0;
+  else
+    allflag = DB_BEST;
+
+  root = rindex(db->path,'/') + 2;
+  indx = Fopen(Catenate(db->path,"","",".idx"),"r");
+  fseeko(indx,sizeof(HITS_DB) + sizeof(HITS_READ)*db->ufirst,SEEK_SET);
+  if (ispart)
+    ureads = ((int *) (db->reads))[-1];
+  else
+    ureads = db->ureads;
+
+  if (strcmp(track->name,". at qvs") == 0)
+    { EPRINTF(EPLACE,"%s: Cannot load QV track after trimming\n",Prog_Name);
+      fclose(indx);
+      EXIT(1);
+    }
+
+  { int   *anno4, size;
+    int64 *anno8;
+    char  *anno, *data;
+
+    size = track->size;
+    data = (char *) track->data; 
+    if (data == NULL)
+      { anno = (char *) track->anno;
+        j = r = 0;
+        for (i = r = 0; i < ureads; i++, r += size)
+          { if (fread(&read,sizeof(HITS_READ),1,indx) != 1)
+              { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+                fclose(indx);
+                EXIT(1);
+              }
+            if ((read.flags & DB_BEST) >= allflag && read.rlen >= cutoff)
+              { memmove(anno+j,anno+r,size);
+                j += size;
+              }
+            r += size;
+          }
+        memmove(anno+j,anno+r,size);
+      }
+    else if (size == 4)
+      { int ai;
+
+        anno4 = (int *) (track->anno);
+        j = anno4[0] = 0;
+        for (i = 0; i < ureads; i++)
+          { if (fread(&read,sizeof(HITS_READ),1,indx) != 1)
+              { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+                fclose(indx);
+                EXIT(1);
+              }
+            if ((read.flags & DB_BEST) >= allflag && read.rlen >= cutoff)
+              { ai = anno4[i];
+                anno4[j+1] = anno4[j] + (anno4[i+1]-ai);
+                memmove(data+anno4[j],data+ai,anno4[i+1]-ai);
+                j += 1;
+              }
+          }
+        track->data = Realloc(track->data,anno4[j],NULL);
+      }
+    else // size == 8
+      { int64 ai;
+
+        anno8 = (int64 *) (track->anno);
+        j = anno8[0] = 0;
+        for (i = 0; i < ureads; i++)
+          { if (fread(&read,sizeof(HITS_READ),1,indx) != 1)
+              { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+                fclose(indx);
+                EXIT(1);
+              }
+            if ((read.flags & DB_BEST) >= allflag && read.rlen >= cutoff)
+              { ai = anno8[i];
+                anno8[j+1] = anno8[j] + (anno8[i+1]-ai);
+                memmove(data+anno8[j],data+ai,anno8[i+1]-ai);
+                j += 1;
+              }
+          }
+        track->data = Realloc(track->data,anno8[j],NULL);
+      }
+    track->anno = Realloc(track->anno,track->size*(j+1),NULL);
+  }
+
+  fclose(indx);
+  return (0);
+}
+
+// Shut down an open 'db' by freeing all associated space, including tracks and QV structures, 
+//   and any open file pointers.  The record pointed at by db however remains (the user
+//   supplied it and so should free it).
+
+void Close_DB(HITS_DB *db)
+{ HITS_TRACK *t, *p;
+
+  if (db->loaded)
+    free(((char *) (db->bases)) - 1);
+  else if (db->bases != NULL)
+    fclose((FILE *) db->bases);
+  if (db->reads != NULL)
+    free(db->reads-1);
+  free(db->path);
+
+  Close_QVs(db);
+
+  for (t = db->tracks; t != NULL; t = p)
+    { p = t->next;
+      free(t->anno);
+      free(t->data);
+      free(t);
+    }
+}
+
+
+// Return the size in bytes of the memory occupied by a given DB
+
+int64 sizeof_DB(HITS_DB *db)
+{ int64       s;
+  HITS_TRACK *t;
+
+  s = sizeof(HITS_DB)
+    + sizeof(HITS_READ)*(db->nreads+2)
+    + strlen(db->path)+1
+    + (db->totlen+db->nreads+4);
+
+  t = db->tracks;
+  if (t != NULL && strcmp(t->name,". at qvs") == 0)
+    { HITS_QV *q = (HITS_QV *) t;
+      s += sizeof(HITS_QV)
+         + sizeof(uint16) * db->nreads
+         + q->ncodes * sizeof(QVcoding)
+         + 6;
+      t = t->next;
+    }
+
+  for (; t != NULL; t = t->next)
+    { s += sizeof(HITS_TRACK)
+         + strlen(t->name)+1
+         + t->size * (db->nreads+1);
+      if (t->data != NULL)
+        { if (t->size == 8)
+            s += sizeof(int)*((int64 *) t->anno)[db->nreads];
+          else //  t->size == 4
+            s += sizeof(int)*((int *) t->anno)[db->nreads];
+        }
+    }
+
+  return (s);
+}
+
+
+/*******************************************************************************************
+ *
+ *  QV LOAD & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+HITS_DB *Active_DB = NULL;  //  Last db/qv used by "Load_QVentry"
+HITS_QV *Active_QV;         //    Becomes invalid after closing
+
+int Load_QVs(HITS_DB *db)
+{ FILE        *quiva, *istub, *indx;
+  char        *root;
+  uint16      *table;
+  HITS_QV     *qvtrk;
+  QVcoding    *coding, *nx;
+  int          ncodes = 0;
+
+  if (db->tracks != NULL && strcmp(db->tracks->name,". at qvs") == 0)
+    return (0);
+
+  if (db->trimmed)
+    { EPRINTF(EPLACE,"%s: Cannot load QVs after trimming the DB\n",Prog_Name);
+      EXIT(1);
+    }
+
+  if (db->reads[db->nreads-1].coff < 0)
+    { if (db->part > 0)
+        { EPRINTF(EPLACE,"%s: All QVs for this block have not been added to the DB!\n",Prog_Name);
+          EXIT(1);
+        }
+      else
+        { EPRINTF(EPLACE,"%s: All QVs for this DB have not been added!\n",Prog_Name);
+          EXIT(1);
+        }
+    }
+
+  //  Open .qvs, .idx, and .db files
+
+  quiva = Fopen(Catenate(db->path,"","",".qvs"),"r");
+  if (quiva == NULL)
+    return (-1);
+
+  istub  = NULL;
+  indx   = NULL; 
+  table  = NULL;
+  coding = NULL;
+  qvtrk  = NULL;
+
+  root = rindex(db->path,'/');
+  if (root[1] == '.')
+    { *root = '\0';
+      istub = Fopen(Catenate(db->path,"/",root+2,".db"),"r");
+      *root = '/';
+    }
+  else
+    istub = Fopen(Catenate(db->path,"","",".db"),"r");
+  if (istub == NULL)
+    goto error;
+
+  { int   first, last, nfiles;
+    char  prolog[MAX_NAME], fname[MAX_NAME];
+    int   i, j;
+
+    if (fscanf(istub,DB_NFILE,&nfiles) != 1)
+      { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+        goto error;
+      }
+
+    if (db->part > 0)
+      { int       pfirst, plast;
+        int       fbeg, fend;
+        int       n, k;
+        FILE     *indx;
+
+        //  Determine first how many and which files span the block (fbeg to fend)
+
+        pfirst = db->ufirst;
+        plast  = pfirst + db->nreads;
+
+        first = 0;
+        for (fbeg = 0; fbeg < nfiles; fbeg++)
+          { if (fscanf(istub,DB_FDATA,&last,fname,prolog) != 3)
+              { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+                goto error;
+              }
+            if (last > pfirst)
+              break;
+            first = last;
+          }
+        for (fend = fbeg+1; fend <= nfiles; fend++)
+          { if (last >= plast)
+              break;
+            if (fscanf(istub,DB_FDATA,&last,fname,prolog) != 3)
+              { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+                goto error;
+              }
+            first = last;
+          }
+
+        indx   = Fopen(Catenate(db->path,"","",".idx"),"r");
+        ncodes = fend-fbeg;
+        coding = (QVcoding *) Malloc(sizeof(QVcoding)*ncodes,"Allocating coding schemes");
+        table  = (uint16 *) Malloc(sizeof(uint16)*db->nreads,"Allocating QV table indices");
+        if (indx == NULL || coding == NULL || table == NULL)
+          { ncodes = 0;
+            goto error;
+          }
+
+        //  Carefully get the first coding scheme (its offset is most likely in a HITS_RECORD
+        //    in .idx that is *not* in memory).  Get all the other coding schemes normally and
+        //    assign the tables # for each read in the block in "tables".
+
+        rewind(istub);
+        (void) fscanf(istub,DB_NFILE,&nfiles);
+
+        first = 0;
+        for (n = 0; n < fbeg; n++)
+          { (void) fscanf(istub,DB_FDATA,&last,fname,prolog);
+            first = last;
+          }
+
+        for (n = fbeg; n < fend; n++)
+          { (void) fscanf(istub,DB_FDATA,&last,fname,prolog);
+
+            i = n-fbeg;
+            if (first < pfirst)
+              { HITS_READ read;
+
+                fseeko(indx,sizeof(HITS_DB) + sizeof(HITS_READ)*first,SEEK_SET);
+                if (fread(&read,sizeof(HITS_READ),1,indx) != 1)
+                  { EPRINTF(EPLACE,"%s: Index file (.idx) of %s is junk\n",Prog_Name,root);
+                    ncodes = i;
+                    goto error;
+                  }
+                fseeko(quiva,read.coff,SEEK_SET);
+                nx = Read_QVcoding(quiva);
+                if (nx == NULL)
+                  { ncodes = i;
+                    goto error;
+                  }
+                coding[i] = *nx;
+              }
+            else
+              { fseeko(quiva,db->reads[first-pfirst].coff,SEEK_SET);
+                nx = Read_QVcoding(quiva);
+                if (nx == NULL)
+                  { ncodes = i;
+                    goto error;
+                  }
+                coding[i] = *nx;
+                db->reads[first-pfirst].coff = ftello(quiva);
+              }
+
+            j = first-pfirst;
+            if (j < 0)
+              j = 0;
+            k = last-pfirst;
+            if (k > db->nreads)
+              k = db->nreads;
+            while (j < k)
+              table[j++] = (uint16) i;
+
+            first = last;
+	  }
+
+        fclose(indx);
+        indx = NULL;
+      }
+
+    else
+      { //  Load in coding scheme for each file, adjust .coff of first read in the file, and
+        //    record which table each read uses
+
+        ncodes = nfiles;
+        coding = (QVcoding *) Malloc(sizeof(QVcoding)*nfiles,"Allocating coding schemes");
+        table  = (uint16 *) Malloc(sizeof(uint16)*db->nreads,"Allocating QV table indices");
+        if (coding == NULL || table == NULL)
+          goto error;
+  
+        first = 0;
+        for (i = 0; i < nfiles; i++)
+          { if (fscanf(istub,DB_FDATA,&last,fname,prolog) != 3)
+              { EPRINTF(EPLACE,"%s: Stub file (.db) of %s is junk\n",Prog_Name,root);
+                goto error;
+              }
+  
+            fseeko(quiva,db->reads[first].coff,SEEK_SET);
+            nx = Read_QVcoding(quiva);
+            if (nx == NULL)
+              { ncodes = i;
+                goto error;
+              }
+            coding[i] = *nx;
+	    db->reads[first].coff = ftello(quiva);
+
+            for (j = first; j < last; j++)
+              table[j] = (uint16) i;
+
+            first = last;
+          }
+      }
+
+    //  Allocate and fill in the HITS_QV record and add it to the front of the
+    //    track list
+
+    qvtrk = (HITS_QV *) Malloc(sizeof(HITS_QV),"Allocating QV pseudo-track");
+    if (qvtrk == NULL)
+      goto error;
+    qvtrk->name   = Strdup(". at qvs","Allocating QV pseudo-track name");
+    if (qvtrk->name == NULL)
+      goto error;
+    qvtrk->next   = db->tracks;
+    db->tracks    = (HITS_TRACK *) qvtrk;
+    qvtrk->ncodes = ncodes;
+    qvtrk->table  = table;
+    qvtrk->coding = coding;
+    qvtrk->quiva  = quiva;
+  }
+
+  fclose(istub);
+  return (0);
+
+error:
+  if (qvtrk != NULL)
+    free(qvtrk);
+  if (table != NULL)
+    free(table);
+  if (coding != NULL)
+    { int i;
+      for (i = 0; i < ncodes; i++)
+        Free_QVcoding(coding+i);
+      free(coding);
+    }
+  if (indx != NULL)
+    fclose(indx);
+  if (istub != NULL)
+    fclose(istub);
+  fclose(quiva);
+  EXIT(1);
+}
+
+// Close the QV stream, free the QV pseudo track and all associated memory
+
+void Close_QVs(HITS_DB *db)
+{ HITS_TRACK *track;
+  HITS_QV    *qvtrk;
+  int         i;
+
+  Active_DB = NULL;
+
+  track = db->tracks;
+  if (track != NULL && strcmp(track->name,". at qvs") == 0)
+    { qvtrk = (HITS_QV *) track;
+      for (i = 0; i < qvtrk->ncodes; i++)
+        Free_QVcoding(qvtrk->coding+i);
+      free(qvtrk->coding);
+      free(qvtrk->table);
+      fclose(qvtrk->quiva);
+      db->tracks = track->next;
+      free(track);
+    }
+  return;
+}
+
+
+/*******************************************************************************************
+ *
+ *  TRACK LOAD & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+//  Return status of track:
+//     1: Track is for trimmed DB
+//     0: Track is for untrimmed DB
+//    -1: Track is not the right size of DB either trimmed or untrimmed
+//    -2: Could not find the track 
+
+int Check_Track(HITS_DB *db, char *track, int *kind)
+{ FILE       *afile;
+  int         tracklen, size, ispart;
+  int         ureads, treads;
+
+  afile = NULL;
+  if (db->part > 0)
+    { afile  = fopen(Catenate(db->path,Numbered_Suffix(".",db->part,"."),track,".anno"),"r");
+      ispart = 1;
+    }
+  if (afile == NULL)
+    { afile  = fopen(Catenate(db->path,".",track,".anno"),"r");
+      ispart = 0;
+    }
+  if (afile == NULL)
+    return (-2);
+
+  if (fread(&tracklen,sizeof(int),1,afile) != 1)
+    { fprintf(stderr,"%s: track files for %s are corrupted\n",Prog_Name,track);
+      exit (1);
+    }
+  if (fread(&size,sizeof(int),1,afile) != 1)
+    { fprintf(stderr,"%s: track files for %s are corrupted\n",Prog_Name,track);
+      exit (1);
+    }
+
+  if (size == 0)
+    *kind = MASK_TRACK;
+  else if (size > 0)
+    *kind = CUSTOM_TRACK;
+  else
+    { fprintf(stderr,"%s: track files for %s are corrupted\n",Prog_Name,track);
+      exit (1);
+    }
+  
+  fclose(afile);
+
+  if (ispart)
+    { ureads = ((int *) (db->reads))[-1];
+      treads = ((int *) (db->reads))[-2];
+    }
+  else
+    { ureads = db->ureads;
+      treads = db->treads;
+    }
+
+  if (tracklen == ureads)
+    return (0);
+  else if (tracklen == treads)
+    return (1);
+  else
+    return (-1);
+}
+
+// If track is not already in the db's track list, then allocate all the storage for it,
+//   read it in from the appropriate file, add it to the track list, and return a pointer
+//   to the newly created HITS_TRACK record.  If the track does not exist or cannot be
+//   opened for some reason, then NULL is returned.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track)
+{ FILE       *afile, *dfile;
+  int         tracklen, size;
+  int         nreads, ispart;
+  int         treads, ureads;
+  void       *anno;
+  void       *data;
+  char       *name;
+  HITS_TRACK *record;
+
+  if (track[0] == '.')
+    { EPRINTF(EPLACE,"%s: Track name, '%s', cannot begin with a .\n",Prog_Name,track);
+      EXIT(NULL);
+    }
+
+  for (record = db->tracks; record != NULL; record = record->next)
+    if (strcmp(record->name,track) == 0)
+      return (record);
+
+  afile = NULL;
+  if (db->part)
+    { afile  = fopen(Catenate(db->path,Numbered_Suffix(".",db->part,"."),track,".anno"),"r");
+      ispart = 1;
+    }
+  if (afile == NULL)
+    { afile = fopen(Catenate(db->path,".",track,".anno"),"r");
+      ispart = 0;
+    }
+  if (afile == NULL)
+    { EPRINTF(EPLACE,"%s: Track '%s' does not exist\n",Prog_Name,track);
+      return (NULL);
+    }
+
+  dfile  = NULL;
+  anno   = NULL;
+  data   = NULL;
+  record = NULL;
+
+  if (ispart)
+    name = Catenate(db->path,Numbered_Suffix(".",db->part,"."),track,".data");
+  else
+    name = Catenate(db->path,".",track,".data");
+  if (name == NULL)
+    goto error;
+  dfile = fopen(name,"r");
+
+  if (fread(&tracklen,sizeof(int),1,afile) != 1)
+    { EPRINTF(EPLACE,"%s: Track '%s' annotation file is junk\n",Prog_Name,track);
+      goto error;
+    }
+  if (fread(&size,sizeof(int),1,afile) != 1)
+    { EPRINTF(EPLACE,"%s: Track '%s' annotation file is junk\n",Prog_Name,track);
+      goto error;
+    }
+
+  if (size < 0)
+    { EPRINTF(EPLACE,"%s: Track '%s' annotation file is junk\n",Prog_Name,track);
+      goto error;
+    }
+  if (size == 0)
+    size = 8;
+
+  if (ispart)
+    { ureads = ((int *) (db->reads))[-1];
+      treads = ((int *) (db->reads))[-2];
+    }
+  else
+    { ureads = db->ureads;
+      treads = db->treads;
+    }
+
+  if (db->trimmed)
+    { if (tracklen != treads && tracklen != ureads)
+        { EPRINTF(EPLACE,"%s: Track '%s' not same size as database !\n",Prog_Name,track);
+          goto error;
+        }
+      if ( ! ispart && db->part > 0)
+        { if (tracklen == treads)
+            fseeko(afile,size*db->tfirst,SEEK_CUR);
+          else
+            fseeko(afile,size*db->ufirst,SEEK_CUR);
+        }
+    }
+  else
+    { if (tracklen != ureads)
+        { if (tracklen == treads)
+            EPRINTF(EPLACE,"%s: Track '%s' is for a trimmed DB !\n",Prog_Name,track);
+          else
+            EPRINTF(EPLACE,"%s: Track '%s' not same size as database !\n",Prog_Name,track);
+          goto error;
+        }
+      if ( ! ispart && db->part > 0)
+        fseeko(afile,size*db->ufirst,SEEK_CUR);
+    }
+  nreads = tracklen;
+
+  anno = (void *) Malloc(size*(nreads+1),"Allocating Track Anno Vector");
+  if (anno == NULL)
+    goto error;
+
+  if (dfile != NULL)
+    { int64 *anno8, off8, dlen;
+      int   *anno4, off4;
+      int    i;
+
+      if (fread(anno,size,nreads+1,afile) != (size_t) (nreads+1))
+        { EPRINTF(EPLACE,"%s: Track '%s' annotation file is junk\n",Prog_Name,track);
+          goto error;
+        }
+
+      if (size == 4)
+        { anno4 = (int *) anno;
+          off4  = anno4[0];
+          if (off4 != 0)
+            { for (i = 0; i <= nreads; i++)
+                anno4[i] -= off4;
+              fseeko(dfile,off4,SEEK_SET);
+            }
+          dlen = anno4[nreads];
+          data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+        }
+      else
+        { anno8 = (int64 *) anno;
+          off8  = anno8[0];
+          if (off8 != 0)
+            { for (i = 0; i <= nreads; i++)
+                anno8[i] -= off8;
+              fseeko(dfile,off8,SEEK_SET);
+            }
+          dlen = anno8[nreads];
+          data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+        }
+      if (data == NULL)
+        goto error;
+      if (dlen > 0)
+        { if (fread(data,dlen,1,dfile) != 1)
+            { EPRINTF(EPLACE,"%s: Track '%s' data file is junk\n",Prog_Name,track);
+              goto error;
+            }
+        }
+      fclose(dfile);
+      dfile = NULL;
+    }
+  else
+    { if (fread(anno,size,nreads,afile) != (size_t) nreads)
+        { EPRINTF(EPLACE,"%s: Track '%s' annotation file is junk\n",Prog_Name,track);
+          goto error;
+        }
+      data = NULL;
+    }
+
+  fclose(afile);
+
+  record = (HITS_TRACK *) Malloc(sizeof(HITS_TRACK),"Allocating Track Record");
+  if (record == NULL)
+    goto error;
+  record->name = Strdup(track,"Allocating Track Name");
+  if (record->name == NULL)
+    goto error;
+  record->data = data;
+  record->anno = anno;
+  record->size = size;
+
+  if (db->trimmed && tracklen != treads)
+    { if (Late_Track_Trim(db,record,ispart))
+        goto error;
+    }
+
+  if (db->tracks != NULL && strcmp(db->tracks->name,". at qvs") == 0)
+    { record->next     = db->tracks->next;
+      db->tracks->next = record;
+    }
+  else
+    { record->next = db->tracks;
+      db->tracks   = record;
+    }
+
+  return (record);
+
+error:
+  if (record != NULL)
+    free(record);
+  if (data != NULL)
+    free(data);
+  if (anno != NULL)
+    free(anno);
+  if (dfile != NULL)
+    fclose(dfile);
+  fclose(afile);
+  EXIT (NULL);
+}
+
+void Close_Track(HITS_DB *db, char *track)
+{ HITS_TRACK *record, *prev;
+
+  prev = NULL;
+  for (record = db->tracks; record != NULL; record = record->next)
+    { if (strcmp(record->name,track) == 0)
+        { free(record->anno);
+          free(record->data);
+          free(record->name);
+          if (prev == NULL)
+            db->tracks = record->next;
+          else
+            prev->next = record->next;
+          free(record);
+          return;
+        }
+      prev = record;
+    }
+  return;
+}
+
+
+/*******************************************************************************************
+ *
+ *  READ BUFFER ALLOCATION AND READ ACCESS
+ *
+ ********************************************************************************************/
+
+// Allocate and return a buffer big enough for the largest read in 'db', leaving room
+//   for an initial delimiter character
+
+char *New_Read_Buffer(HITS_DB *db)
+{ char *read;
+
+  read = (char *) Malloc(db->maxlen+4,"Allocating New Read Buffer");
+  if (read == NULL)
+    EXIT(NULL);
+  return (read+1);
+}
+
+// Load into 'read' the i'th read in 'db'.  As an upper case ASCII string if ascii is 2, as a
+//   lower-case ASCII string is ascii is 1, and as a numeric string over 0(A), 1(C), 2(G), and
+//   3(T) otherwise.
+//
+// **NB**, the byte before read will be set to a delimiter character!
+
+int Load_Read(HITS_DB *db, int i, char *read, int ascii)
+{ FILE      *bases  = (FILE *) db->bases;
+  int64      off;
+  int        len, clen;
+  HITS_READ *r = db->reads;
+
+  if (i >= db->nreads)
+    { EPRINTF(EPLACE,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+      EXIT(1);
+    }
+  if (bases == NULL)
+    { bases = Fopen(Catenate(db->path,"","",".bps"),"r");
+      if (bases == NULL)
+        EXIT(1);
+      db->bases = (void *) bases;
+    }
+
+  off = r[i].boff;
+  len = r[i].rlen;
+
+  if (ftello(bases) != off)
+    fseeko(bases,off,SEEK_SET);
+  clen = COMPRESSED_LEN(len);
+  if (clen > 0)
+    { if (fread(read,clen,1,bases) != 1)
+        { EPRINTF(EPLACE,"%s: Failed read of .bps file (Load_Read)\n",Prog_Name);
+          EXIT(1);
+        }
+    }
+  Uncompress_Read(len,read);
+  if (ascii == 1)
+    { Lower_Read(read);
+      read[-1] = '\0';
+    }
+  else if (ascii == 2)
+    { Upper_Read(read);
+      read[-1] = '\0';
+    }
+  else
+    read[-1] = 4;
+  return (0);
+}
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii)
+{ FILE      *bases  = (FILE *) db->bases;
+  int64      off;
+  int        len, clen;
+  int        bbeg, bend;
+  HITS_READ *r = db->reads;
+
+  if (i >= db->nreads)
+    { EPRINTF(EPLACE,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+      EXIT(NULL);
+    }
+  if (bases == NULL)
+    { bases = Fopen(Catenate(db->path,"","",".bps"),"r");
+      if (bases == NULL)
+        EXIT(NULL);
+      db->bases = (void *) bases;
+    }
+
+  bbeg = beg/4;
+  bend = (end-1)/4+1;
+
+  off = r[i].boff + bbeg;
+  len = end - beg;
+
+  if (ftello(bases) != off)
+    fseeko(bases,off,SEEK_SET);
+  clen = bend-bbeg;
+  if (clen > 0)
+    { if (fread(read,clen,1,bases) != 1)
+        { EPRINTF(EPLACE,"%s: Failed read of .bps file (Load_Read)\n",Prog_Name);
+          EXIT(NULL);
+        }
+    }
+  Uncompress_Read(4*clen,read);
+  read += beg%4;
+  read[len] = 4;
+  if (ascii == 1)
+    { Lower_Read(read);
+      read[-1] = '\0';
+    }
+  else if (ascii == 2)
+    { Upper_Read(read);
+      read[-1] = '\0';
+    }
+  else
+    read[-1] = 4;
+
+  return (read);
+}
+
+
+/*******************************************************************************************
+ *
+ *  QV BUFFER ALLOCATION QV READ ACCESS
+ *
+ ********************************************************************************************/
+
+// Allocate and return a buffer of 5 vectors big enough for the largest read in 'db'
+
+char **New_QV_Buffer(HITS_DB *db)
+{ char **entry;
+  char  *qvs;
+  int    i;
+
+  qvs   = (char *) Malloc(db->maxlen*5,"Allocating New QV Buffer");
+  entry = (char **) Malloc(sizeof(char *)*5,"Allocating New QV Buffer");
+  if (qvs == NULL || entry == NULL)
+    EXIT(NULL);
+  for (i = 0; i < 5; i++)
+    entry[i] = qvs + i*db->maxlen;
+  return (entry);
+}
+
+// Load into entry the QV streams for the i'th read from db.  The parameter ascii applies to
+//  the DELTAG stream as described for Load_Read.
+
+int Load_QVentry(HITS_DB *db, int i, char **entry, int ascii)
+{ HITS_READ *reads;
+  FILE      *quiva;
+  int        rlen;
+
+  if (db != Active_DB)
+    { if (db->tracks == NULL || strcmp(db->tracks->name,". at qvs") != 0)
+        { EPRINTF(EPLACE,"%s: QV's are not loaded (Load_QVentry)\n",Prog_Name);
+          EXIT(1);
+        }
+      Active_QV = (HITS_QV *) db->tracks;
+      Active_DB = db;
+    }
+  if (i >= db->nreads)
+    { EPRINTF(EPLACE,"%s: Index out of bounds (Load_QVentry)\n",Prog_Name);
+      EXIT(1);
+    }
+
+  reads = db->reads;
+  quiva = Active_QV->quiva;
+  rlen  = reads[i].rlen;
+
+  fseeko(quiva,reads[i].coff,SEEK_SET);
+  if (Uncompress_Next_QVentry(quiva,entry,Active_QV->coding+Active_QV->table[i],rlen))
+    EXIT(1);
+
+  if (ascii != 1)
+    { char *deltag = entry[1];
+
+      if (ascii != 2)
+        { char x = deltag[rlen];
+          deltag[rlen] = '\0';
+          Number_Read(deltag);
+          deltag[rlen] = x;
+        }
+      else
+        { int j;
+          int u = 'A'-'a';
+
+          for (j = 0; j < rlen; j++)
+            deltag[j] = (char) (deltag[j]+u);
+        }
+    }
+
+  return (0);
+}
+
+
+/*******************************************************************************************
+ *
+ *  BLOCK LOAD OF ALL READS (PRIMARILY FOR DALIGNER)
+ *
+ ********************************************************************************************/
+
+// Allocate a block big enough for all the uncompressed sequences, read them into it,
+//   reset the 'off' in each read record to be its in-memory offset, and set the
+//   bases pointer to point at the block after closing the bases file.  If ascii is
+//   non-zero then the reads are converted to ACGT ascii, otherwise the reads are left
+//   as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+
+int Read_All_Sequences(HITS_DB *db, int ascii)
+{ FILE      *bases;
+  int        nreads = db->nreads;
+  HITS_READ *reads = db->reads;
+  void     (*translate)(char *s);
+
+  char  *seq;
+  int64  o, off;
+  int    i, len, clen;
+
+  bases = Fopen(Catenate(db->path,"","",".bps"),"r");
+  if (bases == NULL)
+    EXIT(1);
+
+  seq = (char *) Malloc(db->totlen+nreads+4,"Allocating All Sequence Reads");
+  if (seq == NULL)
+    { fclose(bases);
+      EXIT(1);
+    }
+
+  *seq++ = 4;
+
+  if (ascii == 1)
+    translate = Lower_Read;
+  else
+    translate = Upper_Read;
+
+  o = 0;
+  for (i = 0; i < nreads; i++)
+    { len = reads[i].rlen;
+      off = reads[i].boff;
+      if (ftello(bases) != off)
+        fseeko(bases,off,SEEK_SET);
+      clen = COMPRESSED_LEN(len);
+      if (clen > 0)
+        { if (fread(seq+o,clen,1,bases) != 1)
+            { EPRINTF(EPLACE,"%s: Read of .bps file failed (Read_All_Sequences)\n",Prog_Name);
+              free(seq);
+              fclose(bases);
+              EXIT(1);
+            }
+        }
+      Uncompress_Read(len,seq+o);
+      if (ascii)
+        translate(seq+o);
+      reads[i].boff = o;
+      o += (len+1);
+    }
+  reads[nreads].boff = o;
+
+  fclose(bases);
+
+  db->bases  = (void *) seq;
+  db->loaded = 1;
+
+  return (0);
+}
+
+int List_DB_Files(char *path, void actor(char *path, char *extension))
+{ int            status, plen, rlen, dlen;
+  char          *root, *pwd, *name;
+  int            isdam;
+  DIR           *dirp;
+  struct dirent *dp;
+
+  status = 0;
+  pwd    = PathTo(path);
+  plen   = strlen(path);
+  if (strcmp(path+(plen-4),".dam") == 0)
+    root = Root(path,".dam");
+  else
+    root = Root(path,".db");
+  rlen = strlen(root);
+
+  if (root == NULL || pwd == NULL)
+    { free(pwd);
+      free(root);
+      EXIT(1);
+    }
+
+  if ((dirp = opendir(pwd)) == NULL)
+    { EPRINTF(EPLACE,"%s: Cannot open directory %s (List_DB_Files)\n",Prog_Name,pwd);
+      status = -1;
+      goto error;
+    }
+
+  isdam = 0;
+  while ((dp = readdir(dirp)) != NULL)     //   Get case dependent root name (if necessary)
+    { name = dp->d_name;
+      if (strcmp(name,Catenate("","",root,".db")) == 0)
+        break;
+      if (strcmp(name,Catenate("","",root,".dam")) == 0)
+        { isdam = 1;
+          break;
+        }
+      if (strcasecmp(name,Catenate("","",root,".db")) == 0)
+        { strncpy(root,name,rlen);
+          break;
+        }
+      if (strcasecmp(name,Catenate("","",root,".dam")) == 0)
+        { strncpy(root,name,rlen);
+          isdam = 1;
+          break;
+        }
+    }
+  if (dp == NULL)
+    { EPRINTF(EPLACE,"%s: Cannot find %s (List_DB_Files)\n",Prog_Name,pwd);
+      status = -1;
+      closedir(dirp);
+      goto error;
+    }
+
+  if (isdam)
+    actor(Catenate(pwd,"/",root,".dam"),"dam");
+  else
+    actor(Catenate(pwd,"/",root,".db"),"db");
+
+  rewinddir(dirp);                         //   Report each auxiliary file
+  while ((dp = readdir(dirp)) != NULL)
+    { name = dp->d_name;
+      dlen = strlen(name);
+#ifdef HIDE_FILES
+      if (name[0] != '.')
+        continue;
+      dlen -= 1;
+      name += 1;
+#endif
+      if (dlen < rlen+1)
+        continue;
+      if (name[rlen] != '.')
+        continue;
+      if (strncmp(name,root,rlen) != 0)
+        continue;
+      actor(Catenate(pwd,PATHSEP,name,""),name+(rlen+1));
+    }
+  closedir(dirp);
+
+error:
+  free(pwd);
+  free(root);
+  return (status);
+}
+
+void Print_Read(char *s, int width)
+{ int i;
+
+  if (s[0] < 4)
+    { for (i = 0; s[i] != 4; i++)
+        { if (i%width == 0 && i != 0)
+            printf("\n");
+          printf("%d",s[i]);
+        }
+      printf("\n");
+    }
+  else
+    { for (i = 0; s[i] != '\0'; i++)
+        { if (i%width == 0 && i != 0)
+            printf("\n");
+          printf("%c",s[i]);
+        }
+      printf("\n");
+    }
+}
diff --git a/DB.h b/DB.h
new file mode 100644
index 0000000..a7b8636
--- /dev/null
+++ b/DB.h
@@ -0,0 +1,417 @@
+/*******************************************************************************************
+ *
+ *  Compressed data base module.  Auxiliary routines to open and manipulate a data base for
+ *    which the sequence and read information are separated into two separate files, and the
+ *    sequence is compressed into 2-bits for each base.  Support for tracks of additional
+ *    information, and trimming according to the current partition.  Eventually will also
+ *    support compressed quality information. 
+ *
+ *  Author :  Gene Myers
+ *  Date   :  July 2013
+ *  Revised:  April 2014
+ *
+ ********************************************************************************************/
+
+#ifndef _HITS_DB
+
+#define _HITS_DB
+
+#include <stdio.h>
+
+#include "QV.h"
+
+#define HIDE_FILES          //  Auxiliary DB files start with a . so they are "hidden"
+                            //    Undefine if you don't want this
+
+//  For interactive applications where it is inappropriate to simply exit with an error
+//    message to standard error, define the constant INTERACTIVE.  If set, then error
+//    messages are put in the global variable Ebuffer and the caller of a DB routine
+//    can decide how to deal with the error.
+//
+//  DB, QV, or alignment routines that can encounter errors function as before in
+//    non-INTERACTIVE mode by exiting after printing an error message to stderr.  In
+//    INTERACTIVE mode the routines place a message at EPLACE and return an error
+//    value.  For such routines that were previously void, they are now int, and
+//    return 1 if an error occured, 0 otherwise.
+
+#ifdef INTERACTIVE
+
+#define EPRINTF sprintf
+#define EPLACE  Ebuffer
+#define EXIT(x) return (x)
+
+#else // BATCH
+
+#define EPRINTF fprintf
+#define EPLACE  stderr
+#define EXIT(x) exit (1)
+
+#endif
+
+typedef unsigned char      uint8;
+typedef unsigned short     uint16;
+typedef unsigned int       uint32;
+typedef unsigned long long uint64;
+typedef signed char        int8;
+typedef signed short       int16;
+typedef signed int         int32;
+typedef signed long long   int64;
+typedef float              float32;
+typedef double             float64;
+
+
+/*******************************************************************************************
+ *
+ *  COMMAND LINE INTERPRETATION MACROS
+ *
+ ********************************************************************************************/
+
+extern char *Prog_Name;   //  Name of program
+
+#ifdef INTERACTIVE
+
+extern char Ebuffer[];
+
+#endif
+
+#define SYSTEM_ERROR							\
+  { EPRINTF(EPLACE,"%s: System error, read failed!\n",Prog_Name);	\
+    exit (2);								\
+  }
+
+#define ARG_INIT(name)                  \
+  Prog_Name = Strdup(name,"");          \
+  for (i = 0; i < 128; i++)             \
+    flags[i] = 0;
+
+#define ARG_FLAGS(set)                                                                  \
+  for (k = 1; argv[i][k] != '\0'; k++)                                                  \
+    { if (index(set,argv[i][k]) == NULL)                                                \
+        { fprintf(stderr,"%s: -%c is an illegal option\n",Prog_Name,argv[i][k]);        \
+          exit (1);                                                                     \
+        }                                                                               \
+      flags[(int) argv[i][k]] = 1;                                                      \
+    }
+
+#define ARG_POSITIVE(var,name)                                                          \
+  var = strtol(argv[i]+2,&eptr,10);                                                     \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c '%s' argument is not an integer\n",			\
+                     Prog_Name,argv[i][1],argv[i]+2);      				\
+      exit (1);                                                                         \
+    }                                                                                   \
+  if (var <= 0)                                                                         \
+    { fprintf(stderr,"%s: %s must be positive (%d)\n",Prog_Name,name,var);              \
+      exit (1);                                                                         \
+    }
+
+#define ARG_NON_NEGATIVE(var,name)                                                      \
+  var = strtol(argv[i]+2,&eptr,10);                                                     \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c '%s' argument is not an integer\n",			\
+                     Prog_Name,argv[i][1],argv[i]+2);      				\
+      exit (1);                                                                         \
+    }                                                                                   \
+  if (var < 0)	                                                                        \
+    { fprintf(stderr,"%s: %s must be non-negative (%d)\n",Prog_Name,name,var);          \
+      exit (1);                                                                         \
+    }
+
+#define ARG_REAL(var)                                                                   \
+  var = strtod(argv[i]+2,&eptr);                                                        \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c '%s' argument is not a real number\n",			\
+                     Prog_Name,argv[i][1],argv[i]+2);      				\
+      exit (1);                                                                         \
+    }
+
+/*******************************************************************************************
+ *
+ *  UTILITIES
+ *
+ ********************************************************************************************/
+
+//  The following general utilities return NULL if any of their input pointers are NULL, or if they
+//    could not perform their function (in which case they also print an error to stderr).
+
+void *Malloc(int64 size, char *mesg);                    //  Guarded versions of malloc, realloc
+void *Realloc(void *object, int64 size, char *mesg);     //  and strdup, that output "mesg" to
+char *Strdup(char *string, char *mesg);                  //  stderr if out of memory
+
+FILE *Fopen(char *path, char *mode);     // Open file path for "mode"
+char *PathTo(char *path);                // Return path portion of file name "path"
+char *Root(char *path, char *suffix);    // Return the root name, excluding suffix, of "path"
+
+// Catenate returns concatenation of path.sep.root.suffix in a *temporary* buffer
+// Numbered_Suffix returns concatenation of left.<num>.right in a *temporary* buffer
+
+char *Catenate(char *path, char *sep, char *root, char *suffix);
+char *Numbered_Suffix(char *left, int num, char *right);
+
+
+// DB-related utilities
+
+void Print_Number(int64 num, int width, FILE *out);   //  Print readable big integer
+int  Number_Digits(int64 num);                        //  Return # of digits in printed number
+
+#define COMPRESSED_LEN(len)  (((len)+3) >> 2)
+
+void   Compress_Read(int len, char *s);   //  Compress read in-place into 2-bit form
+void Uncompress_Read(int len, char *s);   //  Uncompress read in-place into numeric form
+void      Print_Read(char *s, int width);
+
+void Lower_Read(char *s);     //  Convert read from numbers to lowercase letters (0-3 to acgt)
+void Upper_Read(char *s);     //  Convert read from numbers to uppercase letters (0-3 to ACGT)
+void Number_Read(char *s);    //  Convert read from letters to numbers
+
+
+/*******************************************************************************************
+ *
+ *  DB IN-CORE DATA STRUCTURES
+ *
+ ********************************************************************************************/
+
+#define DB_QV   0x03ff   //  Mask for 3-digit quality value
+#define DB_CSS  0x0400   //  This is the second or later of a group of reads from a given insert
+#define DB_BEST 0x0800   //  This is the longest read of a given insert (may be the only 1)
+
+typedef struct
+  { int     origin; //  Well #
+    int     rlen;   //  Length of the sequence (Last pulse = fpulse + rlen)
+    int     fpulse; //  First pulse
+    int64   boff;   //  Offset (in bytes) of compressed read in 'bases' file, or offset of
+                    //    uncompressed bases in memory block
+    int64   coff;   //  Offset (in bytes) of compressed quiva streams in 'quiva' file
+    int     flags;  //  QV of read + flags above
+  } HITS_READ;
+
+//  A track can be of 3 types:
+//    data == NULL: there are nreads 'anno' records of size 'size'.
+//    data != NULL && size == 4: anno is an array of nreads+1 int's and data[anno[i]..anno[i+1])
+//                                    contains the variable length data
+//    data != NULL && size == 8: anno is an array of nreads+1 int64's and data[anno[i]..anno[i+1])
+//                                    contains the variable length data
+
+typedef struct _track
+  { struct _track *next;  //  Link to next track
+    char          *name;  //  Symbolic name of track
+    int            size;  //  Size in bytes of anno records
+    void          *anno;  //  over [0,nreads]: read i annotation: int, int64, or 'size' records 
+    void          *data;  //     data[anno[i] .. anno[i+1]-1] is data if data != NULL
+  } HITS_TRACK;
+
+//  The information for accessing QV streams is in a HITS_QV record that is a "pseudo-track"
+//    named ". at qvs" and is always the first track record in the list (if present).  Since normal
+//    track names cannot begin with a . (this is enforced), this pseudo-track is never confused
+//    with a normal track.
+
+typedef struct
+  { struct _track *next;
+    char          *name;
+    int            ncodes;  //  # of coding tables
+    QVcoding      *coding;  //  array [0..ncodes-1] of coding schemes (see QV.h)
+    uint16        *table;   //  for i in [0,db->nreads-1]: read i should be decompressed with
+                            //    scheme coding[table[i]]
+    FILE          *quiva;   //  the open file pointer to the .qvs file
+  } HITS_QV;
+
+//  The DB record holds all information about the current state of an active DB including an
+//    array of HITS_READS, one per read, and a linked list of HITS_TRACKs the first of which
+//    is always a HITS_QV pseudo-track (if the QVs have been loaded).
+
+typedef struct
+  { int         ureads;     //  Total number of reads in untrimmed DB
+    int         treads;     //  Total number of reads in trimmed DB
+    int         cutoff;     //  Minimum read length in block (-1 if not yet set)
+    int         all;        //  Consider multiple reads from a given well
+    float       freq[4];    //  frequency of A, C, G, T, respectively
+
+    //  Set with respect to "active" part of DB (all vs block, untrimmed vs trimmed)
+
+    int         maxlen;     //  length of maximum read (initially over all DB)
+    int64       totlen;     //  total # of bases (initially over all DB)
+
+    int         nreads;     //  # of reads in actively loaded portion of DB
+    int         trimmed;    //  DB has been trimmed by cutoff/all
+    int         part;       //  DB block (if > 0), total DB (if == 0)
+    int         ufirst;     //  Index of first read in block (without trimming)
+    int         tfirst;     //  Index of first read in block (with trimming)
+
+       //  In order to avoid forcing users to have to rebuild all thier DBs to accommodate
+       //    the addition of fields for the size of the actively loaded trimmed and untrimmed
+       //    blocks, an additional read record is allocated in "reads" when a DB is loaded into
+       //    memory (reads[-1]) and the two desired fields are crammed into the first two
+       //    integer spaces of the record.
+
+    char       *path;       //  Root name of DB for .bps, .qvs, and tracks
+    int         loaded;     //  Are reads loaded in memory?
+    void       *bases;      //  file pointer for bases file (to fetch reads from),
+                            //    or memory pointer to uncompressed block of all sequences.
+    HITS_READ  *reads;      //  Array [-1..nreads] of HITS_READ
+    HITS_TRACK *tracks;     //  Linked list of loaded tracks
+  } HITS_DB; 
+
+
+/*******************************************************************************************
+ *
+ *  DB STUB FILE FORMAT = NFILE FDATA^nfile NBLOCK PARAMS BDATA^nblock
+ *
+ ********************************************************************************************/
+
+#define MAX_NAME 10000      //  Longest file name or fasta header line
+
+#define DB_NFILE  "files = %9d\n"   //  number of files
+#define DB_FDATA  "  %9d %s %s\n"   //  last read index + 1, fasta prolog, file name
+#define DB_NBLOCK "blocks = %9d\n"  //  number of blocks
+#define DB_PARAMS "size = %10lld cutoff = %9d all = %1d\n"  //  block size, len cutoff, all in well
+#define DB_BDATA  " %9d %9d\n"      //  First read index (untrimmed), first read index (trimmed)
+
+
+/*******************************************************************************************
+ *
+ *  DB ROUTINES
+ *
+ ********************************************************************************************/
+
+  // Suppose DB is the name of an original database.  Then there will be files .DB.idx, .DB.bps,
+  //    .DB.qvs, and files .DB.<track>.anno and DB.<track>.data where <track> is a track name
+  //    (not containing a . !).
+
+  // A DAM is basically a DB except that:
+  //    1. there are no QV's, instead .coff points the '\0' terminated fasta header of the read
+  //          in the file .<dam>.hdr file
+  //    2. .origin contains the contig # of the read within a fasta entry (assembly sequences
+  //          contain N-separated contigs), and .fpulse the first base of the contig in the
+  //          fasta entry
+
+  // Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+  //   a part # in it then just the part is opened.  The index array is allocated (for all or
+  //   just the part) and read in.
+  // Return status of routine:
+  //    -1: The DB could not be opened for a reason reported by the routine to EPLACE
+  //     0: Open of DB proceeded without mishap
+  //     1: Open of DAM proceeded without mishap
+
+int Open_DB(char *path, HITS_DB *db);
+
+  // Trim the DB or part thereof and all loaded tracks according to the cutoff and all settings
+  //   of the current DB partition.  Reallocate smaller memory blocks for the information kept
+  //   for the retained reads.
+
+void Trim_DB(HITS_DB *db);
+
+  // Shut down an open 'db' by freeing all associated space, including tracks and QV structures,
+  //   and any open file pointers.  The record pointed at by db however remains (the user
+  //   supplied it and so should free it).
+
+void Close_DB(HITS_DB *db);
+
+  // Return the size in bytes of the given DB
+
+int64 sizeof_DB(HITS_DB *db);
+
+  // If QV pseudo track is not already in db's track list, then load it and set it up.
+  //   The database must not have been trimmed yet.  -1 is returned if a .qvs file is not
+  //   present, and 1 is returned if an error (reported to EPLACE) occured and INTERACTIVE
+  //   is defined.  Otherwise a 0 is returned.
+
+int Load_QVs(HITS_DB *db);
+
+  // Remove the QV pseudo track, all space associated with it, and close the .qvs file.
+
+void Close_QVs(HITS_DB *db);
+
+  // Look up the file and header in the file of the indicated track.  Return:
+  //     1: Track is for trimmed DB
+  //     0: Track is for untrimmed DB
+  //    -1: Track is not the right size of DB either trimmed or untrimmed
+  //    -2: Could not find the track
+  // In addition, if opened (0 or 1 returned), then kind points at an integer indicating
+  //   the type of track as follows:
+  //      CUSTOM  0 => a custom track
+  //      MASK    1 => a mask track
+
+#define CUSTOM_TRACK 0
+#define   MASK_TRACK 1
+
+int Check_Track(HITS_DB *db, char *track, int *kind);
+
+  // If track is not already in the db's track list, then allocate all the storage for it,
+  //   read it in from the appropriate file, add it to the track list, and return a pointer
+  //   to the newly created HITS_TRACK record.  If the track does not exist or cannot be
+  //   opened for some reason, then NULL is returned if INTERACTIVE is defined.  Otherwise
+  //   the routine prints an error message to stderr and exits if an error occurs, and returns
+  //   with NULL only if the track does not exist.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track);
+
+  // If track is on the db's track list, then it is removed and all storage associated with it
+  //   is freed.
+
+void Close_Track(HITS_DB *db, char *track);
+
+  // Allocate and return a buffer big enough for the largest read in 'db'.
+  // **NB** free(x-1) if x is the value returned as *prefix* and suffix '\0'(4)-byte
+  // are needed by the alignment algorithms.  If cannot allocate memory then return NULL
+  // if INTERACTIVE is defined, or print error to stderr and exit otherwise.
+
+char *New_Read_Buffer(HITS_DB *db);
+
+  // Load into 'read' the i'th read in 'db'.  As a lower case ascii string if ascii is 1, an
+  //   upper case ascii string if ascii is 2, and a numeric string over 0(A), 1(C), 2(G), and 3(T)
+  //   otherwise.  A '\0' (or 4) is prepended and appended to the string so it has a delimeter
+  //   for traversals in either direction.  A non-zero value is returned if an error occured
+  //   and INTERACTIVE is defined.
+
+int  Load_Read(HITS_DB *db, int i, char *read, int ascii);
+
+  // Load into 'read' the subread [beg,end] of the i'th read in 'db' and return a pointer to the
+  //   the start of the subinterval (not necessarily = to read !!! ).  As a lower case ascii
+  //   string if ascii is 1, an upper case ascii string if ascii is 2, and a numeric string
+  //   over 0(A), 1(C), 2(G), and 3(T) otherwise.  A '\0' (or 4) is prepended and appended to
+  //   the string holding the substring so it has a delimeter for traversals in either direction.
+  //   A NULL pointer is returned if an error occured and INTERACTIVE is defined.
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii);
+
+  // Allocate a set of 5 vectors large enough to hold the longest QV stream that will occur
+  //   in the database.  If cannot allocate memory then return NULL if INTERACTIVE is defined,
+  //   or print error to stderr and exit otherwise.
+
+#define DEL_QV  0   //  The deletion QVs are x[DEL_QV] if x is the buffer returned by New_QV_Buffer
+#define DEL_TAG 1   //  The deleted characters
+#define INS_QV  2   //  The insertion QVs
+#define SUB_QV  3   //  The substitution QVs
+#define MRG_QV  4   //  The merge QVs
+
+char **New_QV_Buffer(HITS_DB *db);
+
+  // Load into 'entry' the 5 QV vectors for i'th read in 'db'.  The deletion tag or characters
+  //   are converted to a numeric or upper/lower case ascii string as per ascii.  Return with
+  //   a zero, except when an error occurs and INTERACTIVE is defined in which case return wtih 1.
+
+int   Load_QVentry(HITS_DB *db, int i, char **entry, int ascii);
+
+  // Allocate a block big enough for all the uncompressed sequences, read them into it,
+  //   reset the 'off' in each read record to be its in-memory offset, and set the
+  //   bases pointer to point at the block after closing the bases file.  If ascii is
+  //   1 then the reads are converted to lowercase ascii, if 2 then uppercase ascii, and
+  //   otherwise the reads are left as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+  //   Return with a zero, except when an error occurs and INTERACTIVE is defined in which
+  //   case return wtih 1.
+
+int Read_All_Sequences(HITS_DB *db, int ascii);
+
+  // For the DB or DAM "path" = "prefix/root.[db|dam]", find all the files for that DB, i.e. all
+  //   those of the form "prefix/[.]root.part" and call actor with the complete path to each file
+  //   pointed at by path, and the suffix of the path by extension.  The . proceeds the root
+  //   name if the defined constant HIDE_FILES is set.  Always the first call is with the
+  //   path "prefix/root.[db|dam]" and extension "db" or "dam".  There will always be calls for
+  //   "prefix/[.]root.idx" and "prefix/[.]root.bps".  All other calls are for *tracks* and
+  //   so this routine gives one a way to know all the tracks associated with a given DB.
+  //   -1 is returned if the path could not be found, and 1 is returned if an error (reported
+  //   to EPLACE) occured and INTERACTIVE is defined.  Otherwise a 0 is returned.
+
+int List_DB_Files(char *path, void actor(char *path, char *extension));
+
+#endif // _HITS_DB
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9aa819c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,34 @@
+
+  Copyright (c) 2014, Dr. Eugene W. Myers (EWM). All rights reserved.                
+                                                                                     
+  Redistribution and use in source and binary forms, with or without modification,   
+  are permitted provided that the following conditions are met:                      
+                                                                                     
+   · Redistributions of source code must retain the above copyright notice, this     
+     list of conditions and the following disclaimer.                                
+                                                                                     
+   · Redistributions in binary form must reproduce the above copyright notice, this  
+     list of conditions and the following disclaimer in the documentation and/or     
+     other materials provided with the distribution.                                 
+                                                                                     
+   · The name of EWM may not be used to endorse or promote products derived from     
+     this software without specific prior written permission.                        
+                                                                                     
+  THIS SOFTWARE IS PROVIDED BY EWM ”AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,    
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND       
+  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EWM BE LIABLE   
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  
+  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY      
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                      
+                                                                                     
+  For any issues regarding this software and its use, contact EWM at:                
+                                                                                     
+    Eugene W. Myers Jr.                                                              
+    Bautzner Str. 122e                                                               
+    01099 Dresden                                                                    
+    GERMANY                                                                          
+    Email: gene.myers at gmail.com                                                      
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..851972d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+DEST_DIR = ~/bin
+
+CFLAGS = -O3 -Wall -Wextra -Wno-unused-result -fno-strict-aliasing
+
+ALL = DASqv DAStrim
+
+all: $(ALL)
+
+DASqv: DASqv.c align.c align.h DB.c DB.h QV.c QV.h
+	gcc $(CFLAGS) -o DASqv DASqv.c align.c DB.c QV.c -lm
+
+DAStrim: DAStrim.c align.c align.h DB.c DB.h QV.c QV.h
+	gcc $(CFLAGS) -o DAStrim DAStrim.c align.c DB.c QV.c -lm
+
+clean:
+	rm -f $(ALL)
+	rm -fr *.dSYM
+	rm -f scrubber.tar.gz
+
+install:
+	cp $(ALL) $(DEST_DIR)
+
+package:
+	make clean
+	tar -zcf scrubber.tar.gz README Makefile *.h *.c
diff --git a/QV.c b/QV.c
new file mode 100644
index 0000000..cdb6a63
--- /dev/null
+++ b/QV.c
@@ -0,0 +1,1387 @@
+/*******************************************************************************************
+ *
+ *  Compressor/decompressor for .quiv files: customized Huffman codes for each stream based on
+ *    the histogram of values occuring in a given file.  The two low complexity streams
+ *    (deletionQV and substitutionQV) use a Huffman coding of the run length of the prevelant
+ *    character.
+ *
+ *  Author:   Gene Myers
+ *  Date:     Jan 18, 2014
+ *  Modified: July 25, 2014
+ *
+ ********************************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "DB.h"
+
+#undef DEBUG
+
+#define MIN_BUFFER 1000
+
+#define HUFF_CUTOFF  16   //  This cannot be larger than 16 !
+
+
+/*******************************************************************************************
+ *
+ *  Endian flipping routines
+ *
+ ********************************************************************************************/
+
+static int LittleEndian;  //  Little-endian machine ?
+                          //     Referred by: Decode & Decode_Run
+static int Flip;          //  Flip endian of all coded shorts and ints
+                          //     Referred by: Decode & Decode_Run & Read_Scheme
+
+static void Set_Endian(int flip)
+{ uint32 x = 3;
+  uint8 *b = (uint8 *) (&x);
+
+  Flip         = flip;
+  LittleEndian = (b[0] == 3);
+}
+
+static void Flip_Long(void *w)
+{ uint8 *v = (uint8 *) w;
+  uint8  x;
+
+  x    = v[0];
+  v[0] = v[3];
+  v[3] = x;
+  x    = v[1];
+  v[1] = v[2];
+  v[2] = x;
+}
+
+static void Flip_Short(void *w)
+{ uint8 *v = (uint8 *) w;
+  uint8  x;
+
+  x    = v[0];
+  v[0] = v[1];
+  v[1] = x;
+}
+
+
+/*******************************************************************************************
+ *
+ *  Routines for computing a Huffman Encoding Scheme
+ *
+ ********************************************************************************************/
+
+typedef struct
+  { int    type;             //  0 => normal, 1 => normal but has long codes, 2 => truncated
+    uint32 codebits[256];    //  If type = 2, then code 255 is the special code for
+    int    codelens[256];    //    non-Huffman exceptions
+    int    lookup[0x10000];  //  Lookup table (just for decoding)
+  } HScheme;
+
+typedef struct _HTree
+  { struct _HTree *lft, *rgt; 
+    uint64         count;
+  } HTree;
+
+  //  Establish heap property from node s down (1 is root, siblings of n are 2n and 2n+1)
+  //    assuming s is the only perturbation in the tree.
+
+static void Reheap(int s, HTree **heap, int hsize)
+{ int      c, l, r;
+  HTree   *hs, *hr, *hl;
+
+  c  = s;
+  hs = heap[s];
+  while ((l = 2*c) <= hsize)
+    { r  = l+1;
+      hl = heap[l];
+      hr = heap[r];
+      if (r > hsize || hr->count > hl->count)
+        { if (hs->count > hl->count)
+            { heap[c] = hl;
+              c = l;
+            }
+          else
+            break;
+        }
+      else
+        { if (hs->count > hr->count)
+            { heap[c] = hr;
+              c = r;
+            }
+          else
+            break;
+        }
+    }
+  if (c != s)
+    heap[c] = hs;
+}
+
+  //  Given Huffman tree build a table of codes from it, the low-order codelens[s] bits
+  //    of codebits[s] contain the code for symbol s.
+
+static void Build_Table(HTree *node, int code, int len, uint32 *codebits, int *codelens)
+{ if (node->rgt == NULL)
+    { uint64 symbol = (uint64) (node->lft);
+      codebits[symbol] = code;
+      codelens[symbol] = len;
+    }
+  else
+    { code <<= 1;
+      len   += 1;
+      Build_Table(node->lft,code,len,codebits,codelens);
+      Build_Table(node->rgt,code+1,len,codebits,codelens);
+    }
+}
+
+  // For the non-zero symbols in hist, compute a huffman tree over them, and then
+  //   build a table of the codes.  If inscheme is not NULL, then place all symbols
+  //   with code 255 or with more than HUFF_CUTOFF bits in the encoding by inscheme
+  //   as a single united entity, whose code signals that the value of these symbols
+  //   occur explicitly in 8 (values) or 16 (run lengths) bits following the code.
+  //   All the symbols in this class will have the same entry in the code table and
+  //   255 is always in this class.
+
+static HScheme *Huffman(uint64 *hist, HScheme *inscheme)
+{ HScheme *scheme;
+  HTree   *heap[259];
+  HTree    node[512];
+  int      hsize;
+  HTree   *lft, *rgt;
+  int      value, range;
+  int     i;
+
+  scheme = (HScheme *) Malloc(sizeof(HScheme),"Allocating Huffman scheme record");
+  if (scheme == NULL)
+    return (NULL);
+
+  hsize = 0;                        //  Load heap
+  value = 0;
+  if (inscheme != NULL)
+    { node[0].count = 0;
+      node[0].lft   = (HTree *) (uint64) 255;
+      node[0].rgt   = NULL;
+      heap[++hsize] = node+(value++);
+    }
+  for (i = 0; i < 256; i++)
+    if (hist[i] > 0)
+      { if (inscheme != NULL && (inscheme->codelens[i] > HUFF_CUTOFF || i == 255))
+          node[0].count += hist[i];
+        else
+          { node[value].count = hist[i];
+            node[value].lft   = (HTree *) (uint64) i;
+            node[value].rgt   = NULL;
+            heap[++hsize] = node+(value++);
+          }
+      }
+
+  for (i = hsize/2; i >= 1; i--)    //  Establish heap property
+    Reheap(i,heap,hsize);
+
+  range = value;                    //   Merge pairs with smallest count until have a tree
+  for (i = 1; i < value; i++)
+    { lft = heap[1];
+      heap[1] = heap[hsize--];
+      Reheap(1,heap,hsize);
+      rgt = heap[1];
+      node[range].lft = lft;
+      node[range].rgt = rgt;
+      node[range].count = lft->count + rgt->count;
+      heap[1] = node+(range++);
+      Reheap(1,heap,hsize);
+    }
+
+  for (i = 0; i < 256; i++)        //  Build the code table
+    { scheme->codebits[i] = 0;
+      scheme->codelens[i] = 0;
+    }
+
+  Build_Table(node+(range-1),0,0,scheme->codebits,scheme->codelens);
+
+  if (inscheme != NULL)            //  Set scheme type and if truncated (2), map truncated codes
+    { scheme->type = 2;            //    to code and length for 255
+      for (i = 0; i < 255; i++)
+        if (inscheme->codelens[i] > HUFF_CUTOFF || scheme->codelens[i] > HUFF_CUTOFF)
+          { scheme->codelens[i] = scheme->codelens[255];
+            scheme->codebits[i] = scheme->codebits[255];
+          }
+    }
+  else
+    { scheme->type = 0;
+      for (i = 0; i < 256; i++)
+        { if (scheme->codelens[i] > HUFF_CUTOFF)
+            scheme->type = 1;
+        }
+    }
+
+  return (scheme);
+}
+
+#ifdef DEBUG
+
+  //  For debug, show the coding table
+
+static void Print_Table(HScheme *scheme, uint64 *hist, int infosize)
+{ uint64 total_bits;
+  uint32 specval, mask, code, *bits;
+  int    speclen, clen, *lens;
+  int    i, k;
+
+  total_bits = 0;
+  bits = scheme->codebits;
+  lens = scheme->codelens;
+  if (scheme->type == 2)
+    { specval = bits[255];
+      speclen = lens[255];
+    }
+  else
+    specval = speclen = 0x7fffffff;
+
+  printf("\nCode Table:\n");
+  for (i = 0; i < 256; i++)
+    if (lens[i] > 0)
+      { clen = lens[i];
+        mask = (1 << clen);
+        code = bits[i];
+        printf(" %3d: %2d ",i,clen);
+        for (k = 0; k < clen; k++)
+          { mask >>= 1;
+            if (code & mask)
+              printf("1");
+            else
+              printf("0");
+          }
+        if (code == specval && clen == speclen)
+          { printf(" ***");
+            if (hist != NULL)
+              total_bits += (clen+infosize)*hist[i];
+          }
+        else if (hist != NULL)
+          total_bits += clen*hist[i];
+        printf("\n");
+      }
+  if (hist != NULL)
+    printf("\nTotal Bytes = %lld\n",(total_bits-1)/8+1);
+}
+
+  //  For debug, show the histogram
+
+static void Print_Histogram(uint64 *hist)
+{ int    i, low, hgh;
+  uint64 count;
+
+  for (hgh = 255; hgh >= 0; hgh--)
+    if (hist[hgh] != 0)
+      break;
+  for (low = 0; low < 256; low++)
+    if (hist[low] != 0)
+      break;
+  count = 0;
+  for (i = low; i <= hgh; i++)
+    count += hist[i];
+
+  for (i = hgh; i >= low; i--)
+    printf("    %3d: %8llu %5.1f%%\n",i,hist[i],(hist[i]*100.)/count);
+}
+
+#endif
+
+
+/*******************************************************************************************
+ *
+ *  Read and Write Huffman Schemes
+ *
+ ********************************************************************************************/
+
+  //  Write the code table to out.
+
+static void Write_Scheme(HScheme *scheme, FILE *out)
+{ int     i;
+  uint8   x;
+  uint32 *bits;
+  int    *lens;
+
+  lens = scheme->codelens;
+  bits = scheme->codebits;
+
+  x = (uint8) (scheme->type);
+  fwrite(&x,1,1,out);
+
+  for (i = 0; i < 256; i++)
+    { x = (uint8) (lens[i]);
+      fwrite(&x,1,1,out);
+      if (x > 0)
+        fwrite(bits+i,sizeof(uint32),1,out);
+    }
+}
+
+  //  Allocate and read a code table from in, and return a pointer to it.
+
+static HScheme *Read_Scheme(FILE *in)
+{ HScheme *scheme;
+  int     *look, *lens;
+  uint32  *bits, base;
+  int      i, j, powr;
+  uint8    x;
+
+  scheme = (HScheme *) Malloc(sizeof(HScheme),"Allocating Huffman scheme record");
+  if (scheme == NULL)
+    return (NULL);
+
+  lens = scheme->codelens;
+  bits = scheme->codebits;
+  look = scheme->lookup;
+
+  if (fread(&x,1,1,in) != 1)
+    { EPRINTF(EPLACE,"Could not read scheme type byte (Read_Scheme)\n");
+      free(scheme);
+      return (NULL);
+    }
+  scheme->type = x;
+  for (i = 0; i < 256; i++)
+    { if (fread(&x,1,1,in) != 1)
+        { EPRINTF(EPLACE,"Could not read length of %d'th code (Read_Scheme)\n",i);
+          return (NULL);
+        }
+      lens[i] = x;
+      if (x > 0)
+        { if (fread(bits+i,sizeof(uint32),1,in) != 1)
+            { EPRINTF(EPLACE,"Could not read bit encoding of %d'th code (Read_Scheme)\n",i);
+              free(scheme);
+              return (NULL);
+            }
+        }
+      else
+        bits[i] = 0;
+    }
+
+  if (Flip)
+    { for (i = 0; i < 256; i++)
+        Flip_Long(bits+i);
+    }
+
+  for (i = 0; i < 256; i++)
+    { if (lens[i] > 0)
+        { base = (bits[i] << (16-lens[i]));
+          powr = (1 << (16-lens[i]));
+          for (j = 0; j < powr; j++)
+            look[base+j] = i;
+        }
+    }
+
+  return (scheme);
+}
+
+
+/*******************************************************************************************
+ *
+ *  Encoders and Decoders
+ *
+ ********************************************************************************************/
+
+  //  Encode read[0..rlen-1] according to scheme and write to out
+
+static void Encode(HScheme *scheme, FILE *out, uint8 *read, int rlen)
+{ uint32  x, c, ocode;
+  int     n, k, olen, llen;
+  int    *nlens;
+  uint32 *nbits;
+  uint32  nspec;
+  int     nslen;
+
+  nlens = scheme->codelens;
+  nbits = scheme->codebits;
+
+  if (scheme->type == 2)
+    { nspec = nbits[255];
+      nslen = nlens[255];
+    }
+  else
+    nspec = nslen = 0x7fffffff;
+
+#define OCODE(L,C)				\
+{ int    len  = olen + (L);			\
+  uint32 code = (C);				\
+						\
+  llen = olen;					\
+  if (len >= 32)				\
+    { olen   = len-32;				\
+      ocode |= (code >> olen);			\
+      fwrite(&ocode,sizeof(uint32),1,out);	\
+      if (olen > 0)				\
+        ocode = (code << (32-olen));		\
+      else					\
+        ocode = 0;				\
+    } 						\
+  else						\
+    { olen   = len;				\
+      ocode |= (code << (32-olen));;		\
+    }						\
+}
+
+  llen  = 0;
+  olen  = 0;
+  ocode = 0;
+  for (k = 0; k < rlen; k++)
+    { x = read[k];
+      n = nlens[x];
+      c = nbits[x];
+      OCODE(n,c);
+      if (c == nspec && n == nslen)
+        OCODE(8,x);
+    }
+
+  if (olen > 0)                              //  Tricky: must pad so decoder does not read past
+    { fwrite(&ocode,sizeof(uint32),1,out);   //    last integer int the coded output.
+      if (llen > 16 && olen > llen)
+        fwrite(&ocode,sizeof(uint32),1,out);
+    }
+  else if (llen > 16)
+    fwrite(&ocode,sizeof(uint32),1,out);
+}
+
+  //  Encode read[0..rlen-1] according to non-rchar table neme, and run-length table reme for
+  //    runs of rchar characters.  Write to out.
+
+static void Encode_Run(HScheme *neme, HScheme *reme, FILE *out, uint8 *read, int rlen, int rchar)
+{ uint32  x, c, ocode;
+  int     n, h, k, olen, llen;
+  int    *nlens, *rlens;
+  uint32 *nbits, *rbits;
+  uint32  nspec, rspec;
+  int     nslen, rslen;
+
+  nlens = neme->codelens;
+  nbits = neme->codebits;
+  rlens = reme->codelens;
+  rbits = reme->codebits;
+
+  if (neme->type == 2)
+    { nspec = nbits[255];
+      nslen = nlens[255];
+    }
+  else
+    nspec = nslen = 0x7fffffff;
+
+  rspec = rbits[255];
+  rslen = rlens[255];
+
+  llen  = 0;
+  olen  = 0;
+  ocode = 0;
+  k     = 0;
+  while (k < rlen)
+    { h = k;
+      while (k < rlen && read[k] == rchar)
+        k += 1;
+      if (k-h >= 255)
+        x = 255;
+      else
+        x = k-h;
+      n = rlens[x];
+      c = rbits[x];
+      OCODE(n,c);
+      if (c == rspec && n == rslen)
+        OCODE(16,k-h);
+      if (k < rlen)
+        { x = read[k];
+          n = nlens[x];
+          c = nbits[x];
+          OCODE(n,c);
+          if (c == nspec && n == nslen)
+            OCODE(8,x);
+          k += 1;
+        }
+    }
+
+  if (olen > 0)
+    { fwrite(&ocode,sizeof(uint32),1,out);
+      if (llen > 16 && olen > llen)
+        fwrite(&ocode,sizeof(uint32),1,out);
+    }
+  else if (llen > 16)
+    fwrite(&ocode,sizeof(uint32),1,out);
+}
+
+  //  Read and decode from in, the next rlen symbols into read according to scheme
+
+static int Decode(HScheme *scheme, FILE *in, char *read, int rlen)
+{ int    *look, *lens;
+  int     signal, ilen;
+  uint64  icode;
+  uint32 *ipart;
+  uint16 *xpart;
+  uint8  *cpart;
+  int     j, n, c;
+
+  if (LittleEndian)
+    { ipart = ((uint32 *) (&icode));
+      xpart = ((uint16 *) (&icode)) + 2;
+      cpart = ((uint8  *) (&icode)) + 5;
+    }
+  else
+    { ipart = ((uint32 *) (&icode)) + 1;
+      xpart = ((uint16 *) (&icode)) + 1;
+      cpart = ((uint8  *) (&icode)) + 2;
+    }
+
+  if (scheme->type == 2)
+    signal  = 255;
+  else
+    signal  = 256;
+  lens = scheme->codelens;
+  look = scheme->lookup;
+
+#define GET								\
+  if (n > ilen)								\
+    { icode <<= ilen;							\
+      if (fread(ipart,sizeof(uint32),1,in) != 1)			\
+        { EPRINTF(EPLACE,"Could not read more bits (Decode)\n");	\
+          return (1);							\
+        }								\
+      ilen    = n-ilen;							\
+      icode <<= ilen;							\
+      ilen    = 32-ilen;						\
+    }									\
+  else									\
+    { icode <<= n;							\
+      ilen   -= n;							\
+    }
+
+#define GETFLIP								\
+  if (n > ilen)								\
+    { icode <<= ilen;							\
+      if (fread(ipart,sizeof(uint32),1,in) != 1)			\
+        { EPRINTF(EPLACE,"Could not read more bits (Decode)\n");	\
+          return (1);							\
+        }								\
+      Flip_Long(ipart);							\
+      ilen    = n-ilen;							\
+      icode <<= ilen;							\
+      ilen    = 32-ilen;						\
+    }									\
+  else									\
+    { icode <<= n;							\
+      ilen   -= n;							\
+    }
+
+  n     = 16;
+  ilen  = 0;
+  icode = 0;
+  if (Flip)
+    for (j = 0; j < rlen; j++)
+      { GETFLIP
+        c = look[*xpart];
+        n = lens[c];
+        if (c == signal)
+          { GETFLIP
+            c = *cpart;
+            n = 8;
+          }
+        read[j] = (char) c;
+      }
+  else
+    for (j = 0; j < rlen; j++)
+      { GET
+        c = look[*xpart];
+        n = lens[c];
+        if (c == signal)
+          { GET
+            c = *cpart;
+            n = 8;
+          }
+        read[j] = (char) c;
+      }
+
+  return (0);
+}
+
+  //  Read and decode from in, the next rlen symbols into read according to non-rchar scheme
+  //    neme, and the rchar runlength shceme reme
+
+static int Decode_Run(HScheme *neme, HScheme *reme, FILE *in, char *read,
+                      int rlen, int rchar)
+{ int    *nlook, *nlens;
+  int    *rlook, *rlens;
+  int     nsignal, ilen;
+  uint64  icode;
+  uint32 *ipart;
+  uint16 *xpart;
+  uint8  *cpart;
+  int     j, n, c, k;
+
+  if (LittleEndian)
+    { ipart = ((uint32 *) (&icode));
+      xpart = ((uint16 *) (&icode)) + 2;
+      cpart = ((uint8  *) (&icode)) + 5;
+    }
+  else
+    { ipart = ((uint32 *) (&icode)) + 1;
+      xpart = ((uint16 *) (&icode)) + 1;
+      cpart = ((uint8  *) (&icode)) + 2;
+    }
+
+  if (neme->type == 2)
+    nsignal = 255;
+  else
+    nsignal = 256;
+  nlens = neme->codelens;
+  nlook = neme->lookup;
+
+  rlens = reme->codelens;
+  rlook = reme->lookup;
+
+  n     = 16;
+  ilen  = 0;
+  icode = 0;
+  if (Flip)
+    for (j = 0; j < rlen; j++)
+      { GETFLIP
+        c = rlook[*xpart];
+        n = rlens[c];
+        if (c == 255)
+          { GETFLIP
+            c = *xpart;
+            n = 16;
+          }
+        for (k = 0; k < c; k++)
+          read[j++] = (char) rchar;
+
+        if (j < rlen)
+          { GETFLIP
+            c = nlook[*xpart];
+            n = nlens[c];
+            if (c == nsignal)
+              { GETFLIP
+                c = *cpart;
+                n = 8;
+              }
+            read[j] = (char) c;
+          }
+      }
+  else
+    for (j = 0; j < rlen; j++)
+      { GET
+        c = rlook[*xpart];
+        n = rlens[c];
+        if (c == 255)
+          { GET
+            c = *xpart;
+            n = 16;
+          }
+        for (k = 0; k < c; k++)
+          read[j++] = (char) rchar;
+
+        if (j < rlen)
+          { GET
+            c = nlook[*xpart];
+            n = nlens[c];
+            if (c == nsignal)
+              { GET
+                c = *cpart;
+                n = 8;
+              }
+            read[j] = (char) c;
+          }
+      }
+
+  return (0);
+}
+
+
+/*******************************************************************************************
+ *
+ *  Histogrammers
+ *
+ ********************************************************************************************/
+
+//  Histogram runlengths of symbol runChar in stream[0..rlen-1] into run.
+
+static void Histogram_Seqs(uint64 *hist, uint8 *stream, int rlen)
+{ int k;
+
+  for (k = 0; k < rlen; k++)
+    hist[stream[k]] += 1;
+}
+
+static void Histogram_Runs(uint64 *run, uint8 *stream, int rlen, int runChar)
+{ int k, h;
+
+  k = 0;
+  while (k < rlen)
+    { h = k;
+      while (k < rlen && stream[k] == runChar)
+        k += 1;
+      if (k-h >= 256)
+        run[255] += 1;
+      else
+        run[k-h] += 1;
+      if (k < rlen)
+        k += 1;
+    }
+}
+
+
+/*******************************************************************************************
+ *
+ *  Reader
+ *
+ ********************************************************************************************/
+
+static char  *Read = NULL;   //  Referred by:  QVentry, Read_Lines, QVcoding_Scan,
+static int    Rmax = -1;     //                Compress_Next_QVentry
+
+static int    Nline;         //  Referred by:  QVcoding_Scan
+
+char *QVentry()
+{ return (Read); }
+
+void Set_QV_Line(int line)
+{ Nline = line; }
+
+int Get_QV_Line()
+{ return (Nline); }
+
+//  If nlines == 1 trying to read a single header, nlines = 5 trying to read 5 QV/fasta lines
+//    for a sequence.  Place line j at Read+j*Rmax and the length of every line is returned
+//    unless eof occurs in which case return -1.  If any error occurs return -2.
+
+int Read_Lines(FILE *input, int nlines)
+{ int   i, rlen;
+  int   tmax;
+  char *tread;
+  char *other;
+
+  if (Read == NULL)
+    { tmax  = MIN_BUFFER;
+      tread = (char *) Malloc(5*tmax,"Allocating QV entry read buffer");
+      if (tread == NULL)
+        EXIT(-2);
+      Rmax = tmax;
+      Read = tread;
+    }
+
+  Nline += 1;
+  if (fgets(Read,Rmax,input) == NULL)
+    return (-1);
+
+  rlen = strlen(Read);
+  while (Read[rlen-1] != '\n')
+    { tmax  = ((int) 1.4*Rmax) + MIN_BUFFER;
+      tread = (char *) Realloc(Read,5*tmax,"Reallocating QV entry read buffer");
+      if (tread == NULL)
+        EXIT(-2);
+      Rmax = tmax;
+      Read = tread;
+      if (fgets(Read+rlen,Rmax-rlen,input) == NULL)
+        { EPRINTF(EPLACE,"Line %d: Last line does not end with a newline !\n",Nline);
+          EXIT(-2);
+        }
+      rlen += strlen(Read+rlen);
+    }
+  other = Read;
+  for (i = 1; i < nlines; i++)
+    { other += Rmax;
+      Nline += 1;
+      if (fgets(other,Rmax,input) == NULL)
+        { EPRINTF(EPLACE,"Line %d: incomplete last entry of .quiv file\n",Nline);
+          EXIT(-2);
+        }
+      if (rlen != (int) strlen(other))
+        { EPRINTF(EPLACE,"Line %d: Lines for an entry are not the same length\n",Nline);
+          EXIT(-2);
+        }
+    }
+  return (rlen-1);
+}
+
+
+/*******************************************************************************************
+ *
+ *  Tag compression and decompression routines
+ *
+ ********************************************************************************************/
+
+//  Keep only the symbols in tags[0..rlen-1] for which qvs[k] != rchar and
+//    return the # of symbols kept.
+
+static int Pack_Tag(char *tags, char *qvs, int rlen, int rchar)
+{ int j, k;
+
+  j = 0;
+  for (k = 0; k < rlen; k++)
+    if (qvs[k] != rchar)
+      tags[j++] = tags[k];
+  tags[j] = '\0';
+  return (j);
+}
+
+  //  Count the # of non-rchar symbols in qvs[0..rlen-1]
+
+static int Packed_Length(char *qvs, int rlen, int rchar)
+{ int k, clen;
+
+  clen = 0;
+  for (k = 0; k < rlen; k++)
+    if (qvs[k] != rchar)
+      clen += 1;
+  return (clen);
+}
+
+  //  Unpack tags by moving its i'th char to position k where qvs[k] is the i'th non-rchar
+  //    symbol in qvs.  All other chars are set to rchar.  rlen is the length of qvs and
+  //    the unpacked result, clen is the initial length of tags.
+
+static void Unpack_Tag(char *tags, int clen, char *qvs, int rlen, int rchar)
+{ int j, k;
+
+  j = clen-1;
+  for (k = rlen-1; k >= 0; k--)
+    { if (qvs[k] == rchar)
+        tags[k] = 'n';
+      else
+        tags[k] = tags[j--];
+    }
+}
+
+
+/*******************************************************************************************
+ *
+ *  Statistics Scan and Scheme creation and write
+ *
+ ********************************************************************************************/
+
+  // Read up to the next num entries or until eof from the .quiva file on input and record
+  //   frequency statistics.  Copy these entries to the temporary file temp if != NULL.
+  //   If there is an error then -1 is returned, otherwise the number of entries read.
+
+static uint64   delHist[256], insHist[256], mrgHist[256], subHist[256], delRun[256], subRun[256];
+static uint64   totChar;
+static int      delChar, subChar;
+
+  // Referred by:  QVcoding_Scan, Create_QVcoding
+
+int QVcoding_Scan(FILE *input, int num, FILE *temp)
+{ char *slash;
+  int   rlen;
+  int   i, r;
+
+  //  Zero histograms
+
+  bzero(delHist,sizeof(uint64)*256);
+  bzero(mrgHist,sizeof(uint64)*256);
+  bzero(insHist,sizeof(uint64)*256);
+  bzero(subHist,sizeof(uint64)*256);
+
+  for (i = 0; i < 256; i++)
+    delRun[i] = subRun[i] = 1;
+
+  totChar    = 0;
+  delChar    = -1;
+  subChar    = -1;
+
+  //  Make a sweep through the .quiva entries, histogramming the relevant things
+  //    and figuring out the run chars for the deletion and substition streams
+
+  r = 0;
+  for (i = 0; i < num; i++)
+    { int well, beg, end, qv;
+
+      rlen = Read_Lines(input,1);
+      if (rlen == -2)
+        EXIT(-1);
+      if (rlen < 0)
+        break;
+
+      if (rlen == 0 || Read[0] != '@')
+        { EPRINTF(EPLACE,"Line %d: Header in quiva file is missing\n",Nline);
+          EXIT(-1);
+        }
+      slash = index(Read+1,'/');
+      if (slash == NULL)
+  	{ EPRINTF(EPLACE,"%s: Line %d: Header line incorrectly formatted ?\n",
+                         Prog_Name,Nline);
+          EXIT(-1);
+        }
+      if (sscanf(slash+1,"%d/%d_%d RQ=0.%d\n",&well,&beg,&end,&qv) != 4)
+        { EPRINTF(EPLACE,"%s: Line %d: Header line incorrectly formatted ?\n",
+                         Prog_Name,Nline);
+          EXIT(-1);
+        }
+
+      if (temp != NULL)
+        fputs(Read,temp);
+
+      rlen = Read_Lines(input,5);
+      if (rlen < 0)
+        { if (rlen == -1)
+            EPRINTF(EPLACE,"Line %d: incomplete last entry of .quiv file\n",Nline);
+          EXIT(-1);
+        }
+
+      if (temp != NULL)
+        { fputs(Read,temp);
+          fputs(Read+Rmax,temp);
+          fputs(Read+2*Rmax,temp);
+          fputs(Read+3*Rmax,temp);
+          fputs(Read+4*Rmax,temp);
+        }
+
+      Histogram_Seqs(delHist,(uint8 *) (Read),rlen);
+      Histogram_Seqs(insHist,(uint8 *) (Read+2*Rmax),rlen);
+      Histogram_Seqs(mrgHist,(uint8 *) (Read+3*Rmax),rlen);
+      Histogram_Seqs(subHist,(uint8 *) (Read+4*Rmax),rlen);
+
+      if (delChar < 0)
+        { int   k;
+          char *del = Read+Rmax;
+
+          for (k = 0; k < rlen; k++)
+            if (del[k] == 'n' || del[k] == 'N')
+              { delChar = Read[k];
+                break;
+              }
+        }
+      if (delChar >= 0)
+        Histogram_Runs( delRun,(uint8 *) (Read),rlen,delChar);
+      totChar += rlen;
+      if (subChar < 0)
+        { if (totChar >= 100000)
+            { int k;
+
+              subChar = 0;
+              for (k = 1; k < 256; k++)
+                if (subHist[k] > subHist[subChar])
+                  subChar = k;
+            }
+        }
+      if (subChar >= 0)
+        Histogram_Runs( subRun,(uint8 *) (Read+4*Rmax),rlen,subChar);
+
+      r += 1;
+    }
+
+  return (r);
+}
+
+  //   Using the statistics in the global stat tables, create the Huffman schemes and write
+  //   them to output.  If lossy is set, then create a lossy table for the insertion and merge
+  //   QVs.
+
+QVcoding *Create_QVcoding(int lossy)
+{ static QVcoding coding;
+
+  HScheme *delScheme, *insScheme, *mrgScheme, *subScheme;
+  HScheme *dRunScheme, *sRunScheme;
+
+  delScheme  = NULL;
+  dRunScheme = NULL;
+  insScheme  = NULL;
+  mrgScheme  = NULL;
+  subScheme  = NULL;
+  sRunScheme = NULL;
+
+  //  Check whether using a subtitution run char is a win
+
+  if (totChar < 200000 || subHist[subChar] < .5*totChar)
+    subChar = -1;
+
+  //  If lossy encryption is enabled then scale insertions and merge QVs.
+
+  if (lossy)
+    { int k;
+
+      for (k = 0; k < 256; k += 2)
+        { insHist[k] += insHist[k+1];
+          insHist[k+1] = 0;
+        }
+
+      for (k = 0; k < 256; k += 4)
+        { mrgHist[k] += mrgHist[k+1];
+          mrgHist[k] += mrgHist[k+2];
+          mrgHist[k] += mrgHist[k+3];
+          mrgHist[k+1] = 0;
+          mrgHist[k+2] = 0;
+          mrgHist[k+3] = 0;
+        }
+    }
+
+  //  Build a Huffman scheme for each stream entity from the histograms
+
+#define SCHEME_MACRO(meme,hist,label,bits)	\
+  scheme = Huffman( (hist), NULL);		\
+  if (scheme == NULL)				\
+    goto error;					\
+  if (scheme->type)				\
+    { (meme) = Huffman( (hist), scheme);	\
+      free(scheme);				\
+    }						\
+  else						\
+    (meme) = scheme;
+
+#ifdef DEBUG
+
+#define MAKE_SCHEME(meme,hist,label,bits)	\
+  SCHEME_MACRO(meme,hist,label,bits)		\
+  printf("\n%s\n", (label) );			\
+  Print_Histogram( (hist));			\
+  Print_Table( (meme), (hist), (bits));	
+
+#else
+
+#define MAKE_SCHEME(meme,hist,label,bits)	\
+  SCHEME_MACRO(meme,hist,label,bits)
+
+#endif
+
+  { HScheme *scheme;
+
+    if (delChar < 0)
+      { MAKE_SCHEME(delScheme,delHist, "Hisotgram of Deletion QVs", 8);
+        dRunScheme = NULL;
+      }
+    else
+      { delHist[delChar] = 0;
+        MAKE_SCHEME(delScheme,delHist, "Hisotgram of Deletion QVs less run char", 8);
+        MAKE_SCHEME(dRunScheme,delRun, "Histogram of Deletion Runs QVs", 16);
+#ifdef DEBUG
+        printf("\nRun char is '%c'\n",delChar);
+#endif
+      }
+
+#ifdef DEBUG
+    { int    k;
+      uint64 count;
+
+      count = 0;
+      for (k = 0; k < 256; k++)
+        count += delHist[k];
+      printf("\nDelTag will require %lld bytes\n",count/4);
+    }
+#endif
+
+    MAKE_SCHEME(insScheme,insHist, "Hisotgram of Insertion QVs", 8);
+    MAKE_SCHEME(mrgScheme,mrgHist, "Hisotgram of Merge QVs", 8);
+
+    if (subChar < 0)
+      { MAKE_SCHEME(subScheme,subHist, "Hisotgram of Subsitution QVs", 8);
+        sRunScheme = NULL;
+      }
+    else
+      { subHist[subChar] = 0;
+        MAKE_SCHEME(subScheme,subHist, "Hisotgram of Subsitution QVs less run char", 8);
+        MAKE_SCHEME(sRunScheme,subRun, "Histogram of Substitution Run QVs", 16);
+#ifdef DEBUG
+        printf("\nRun char is '%c'\n",subChar);
+#endif
+      }
+  }
+
+  //  Setup endian handling
+
+  Set_Endian(0);
+
+  coding.delScheme  = delScheme;
+  coding.insScheme  = insScheme;
+  coding.mrgScheme  = mrgScheme;
+  coding.subScheme  = subScheme;
+  coding.dRunScheme = dRunScheme;
+  coding.sRunScheme = sRunScheme;
+  coding.delChar    = delChar;
+  coding.subChar    = subChar;
+  coding.prefix     = NULL;
+  coding.flip       = 0;
+
+  return (&coding);
+
+error:
+  if (delScheme != NULL)
+    free(delScheme);
+  if (dRunScheme != NULL)
+    free(dRunScheme);
+  if (insScheme != NULL)
+    free(insScheme);
+  if (mrgScheme != NULL)
+    free(mrgScheme);
+  if (subScheme != NULL)
+    free(subScheme);
+  if (sRunScheme != NULL)
+    free(sRunScheme);
+  EXIT(NULL);
+}
+
+  // Write the encoding scheme 'coding' to 'output'
+
+void Write_QVcoding(FILE *output, QVcoding *coding)
+{
+  //   Write out the endian key, run chars, and prefix (if not NULL)
+
+  { uint16 half;
+    int    len;
+
+    half = 0x33cc;
+    fwrite(&half,sizeof(uint16),1,output);
+
+    if (coding->delChar < 0)
+      half = 256;
+    else
+      half = (uint16) (coding->delChar);
+    fwrite(&half,sizeof(uint16),1,output);
+
+    if (coding->subChar < 0)
+      half = 256;
+    else
+      half = (uint16) (coding->subChar);
+    fwrite(&half,sizeof(uint16),1,output);
+
+    len = strlen(coding->prefix);
+    fwrite(&len,sizeof(int),1,output);
+    fwrite(coding->prefix,1,len,output);
+  }
+
+  //   Write out the scheme tables
+
+  Write_Scheme(coding->delScheme,output);
+  if (coding->delChar >= 0)
+    Write_Scheme(coding->dRunScheme,output);
+  Write_Scheme(coding->insScheme,output);
+  Write_Scheme(coding->mrgScheme,output);
+  Write_Scheme(coding->subScheme,output);
+  if (coding->subChar >= 0)
+    Write_Scheme(coding->sRunScheme,output);
+}
+
+  // Read the encoding scheme 'coding' to 'output'
+
+QVcoding *Read_QVcoding(FILE *input)
+{ static QVcoding coding;
+
+  // Read endian key, run chars, and short name common to all headers
+
+  { uint16 half;
+    int    len;
+
+    if (fread(&half,sizeof(uint16),1,input) != 1)
+      { EPRINTF(EPLACE,"Could not read flip byte (Read_QVcoding)\n");
+        EXIT(NULL);
+      }
+    coding.flip = (half != 0x33cc);
+
+    if (fread(&half,sizeof(uint16),1,input) != 1)
+      { EPRINTF(EPLACE,"Could not read deletion char (Read_QVcoding)\n");
+        EXIT(NULL);
+      }
+    if (coding.flip)
+      Flip_Short(&half);
+    coding.delChar = half;
+    if (coding.delChar >= 256)
+      coding.delChar = -1;
+
+    if (fread(&half,sizeof(uint16),1,input) != 1)
+      { EPRINTF(EPLACE,"Could not read substitution char (Read_QVcoding)\n");
+        EXIT(NULL);
+      }
+    if (coding.flip)
+      Flip_Short(&half);
+    coding.subChar = half;
+    if (coding.subChar >= 256)
+      coding.subChar = -1;
+
+    //  Read the short name common to all headers
+
+    if (fread(&len,sizeof(int),1,input) != 1)
+      { EPRINTF(EPLACE,"Could not read header name length (Read_QVcoding)\n");
+        EXIT(NULL);
+      }
+    if (coding.flip)
+      Flip_Long(&len);
+    coding.prefix = (char *) Malloc(len+1,"Allocating header prefix");
+    if (coding.prefix == NULL)
+      EXIT(NULL);
+    if (len > 0)
+      { if (fread(coding.prefix,len,1,input) != 1)
+          { EPRINTF(EPLACE,"Could not read header name (Read_QVcoding)\n");
+            EXIT(NULL);
+          }
+      }
+    coding.prefix[len] = '\0';
+  }
+
+  //  Setup endian handling
+
+  Set_Endian(coding.flip);
+
+  //  Read the Huffman schemes used to compress the data
+
+  coding.delScheme  = NULL;
+  coding.dRunScheme = NULL;
+  coding.insScheme  = NULL;
+  coding.mrgScheme  = NULL;
+  coding.subScheme  = NULL;
+  coding.sRunScheme = NULL;
+
+  coding.delScheme = Read_Scheme(input);
+  if (coding.delScheme == NULL)
+    goto error;
+  if (coding.delChar >= 0)
+    { coding.dRunScheme = Read_Scheme(input);
+      if (coding.dRunScheme == NULL)
+        goto error;
+    }
+  coding.insScheme = Read_Scheme(input);
+  if (coding.insScheme == NULL)
+    goto error;
+  coding.mrgScheme = Read_Scheme(input);
+  if (coding.mrgScheme == NULL)
+    goto error;
+  coding.subScheme = Read_Scheme(input);
+  if (coding.subScheme == NULL)
+    goto error;
+  if (coding.subChar >= 0)
+    { coding.sRunScheme = Read_Scheme(input);
+      if (coding.sRunScheme == NULL)
+        goto error;
+    }
+
+  return (&coding);
+
+error:
+  if (coding.delScheme != NULL)
+    free(coding.delScheme);
+  if (coding.dRunScheme != NULL)
+    free(coding.dRunScheme);
+  if (coding.insScheme != NULL)
+    free(coding.insScheme);
+  if (coding.mrgScheme != NULL)
+    free(coding.mrgScheme);
+  if (coding.subScheme != NULL)
+    free(coding.subScheme);
+  if (coding.sRunScheme != NULL)
+    free(coding.sRunScheme);
+  EXIT(NULL);
+}
+
+  //  Free all the auxilliary storage associated with the encoding argument
+
+void Free_QVcoding(QVcoding *coding)
+{ if (coding->subChar >= 0)
+    free(coding->sRunScheme);
+  free(coding->subScheme);
+  free(coding->mrgScheme);
+  free(coding->insScheme);
+  if (coding->delChar >= 0)
+    free(coding->dRunScheme);
+  free(coding->delScheme);
+  free(coding->prefix);
+}
+
+
+/*******************************************************************************************
+ *
+ *  Encode/Decode (w.r.t. coding) next entry from input and write to output
+ *
+ ********************************************************************************************/
+
+int Compress_Next_QVentry(FILE *input, FILE *output, QVcoding *coding, int lossy)
+{ int rlen, clen;
+
+  //  Get all 5 streams, compress each with its scheme, and output
+
+  rlen = Read_Lines(input,5);
+  if (rlen < 0)
+    { if (rlen == -1)
+        EPRINTF(EPLACE,"Line %d: incomplete last entry of .quiv file\n",Nline);
+      EXIT (-1);
+    }
+
+  if (coding->delChar < 0)
+    { Encode(coding->delScheme, output, (uint8 *) Read, rlen);
+      clen = rlen;
+    }
+  else
+    { Encode_Run(coding->delScheme, coding->dRunScheme, output,
+                 (uint8 *) Read, rlen, coding->delChar);
+      clen = Pack_Tag(Read+Rmax,Read,rlen,coding->delChar);
+    }
+  Number_Read(Read+Rmax);
+  Compress_Read(clen,Read+Rmax);
+  fwrite(Read+Rmax,1,COMPRESSED_LEN(clen),output);
+
+  if (lossy)
+    { uint8 *insert = (uint8 *) (Read+2*Rmax);
+      uint8 *merge  = (uint8 *) (Read+3*Rmax);
+      int    k;
+
+      for (k = 0; k < rlen; k++)
+        { insert[k] = (uint8) ((insert[k] >> 1) << 1);
+          merge[k]  = (uint8) (( merge[k] >> 2) << 2);
+        }
+    }
+
+  Encode(coding->insScheme, output, (uint8 *) (Read+2*Rmax), rlen);
+  Encode(coding->mrgScheme, output, (uint8 *) (Read+3*Rmax), rlen);
+  if (coding->subChar < 0)
+    Encode(coding->subScheme, output, (uint8 *) (Read+4*Rmax), rlen);
+  else
+    Encode_Run(coding->subScheme, coding->sRunScheme, output,
+               (uint8 *) (Read+4*Rmax), rlen, coding->subChar);
+
+  return (rlen);
+}
+
+int Uncompress_Next_QVentry(FILE *input, char **entry, QVcoding *coding, int rlen)
+{ int clen, tlen;
+
+  //  Decode each stream and write to output
+
+  if (coding->delChar < 0)
+    { if (Decode(coding->delScheme, input, entry[0], rlen))
+        EXIT(1);
+      clen = rlen;
+      tlen = COMPRESSED_LEN(clen);
+      if (tlen > 0)
+        { if (fread(entry[1],tlen,1,input) != 1)
+            { EPRINTF(EPLACE,"Could not read deletions entry (Uncompress_Next_QVentry\n");
+              EXIT(1);
+            }
+        }
+      Uncompress_Read(clen,entry[1]);
+      Lower_Read(entry[1]);
+    }
+  else
+    { if (Decode_Run(coding->delScheme, coding->dRunScheme, input,
+                     entry[0], rlen, coding->delChar))
+        EXIT(1);
+      clen = Packed_Length(entry[0],rlen,coding->delChar);
+      tlen = COMPRESSED_LEN(clen);
+      if (tlen > 0)
+        { if (fread(entry[1],tlen,1,input) != 1)
+            { EPRINTF(EPLACE,"Could not read deletions entry (Uncompress_Next_QVentry\n");
+              EXIT(1);
+            }
+        }
+      Uncompress_Read(clen,entry[1]);
+      Lower_Read(entry[1]);
+      Unpack_Tag(entry[1],clen,entry[0],rlen,coding->delChar);
+    }
+
+  if (Decode(coding->insScheme, input, entry[2], rlen))
+    EXIT(1);
+
+  if (Decode(coding->mrgScheme, input, entry[3], rlen))
+    EXIT(1);
+
+  if (coding->subChar < 0)
+    { if (Decode(coding->subScheme, input, entry[4], rlen))
+        EXIT(1);
+    }
+  else
+    { if (Decode_Run(coding->subScheme, coding->sRunScheme, input,
+                     entry[4], rlen, coding->subChar))
+        EXIT(1);
+    }
+
+  return (0);
+}
diff --git a/QV.h b/QV.h
new file mode 100644
index 0000000..532b2f4
--- /dev/null
+++ b/QV.h
@@ -0,0 +1,96 @@
+/*******************************************************************************************
+ *
+ *  Compressor/decompressor for .quiv files: customized Huffman codes for each stream based on
+ *    the histogram of values occuring in a given file.  The two low complexity streams
+ *    (deletionQV and substitutionQV) use a Huffman coding of the run length of the prevelant
+ *    character.
+ *
+ *  Author:   Gene Myers
+ *  Date:     Jan 18, 2014
+ *  Modified: July 25, 2014
+ *
+ ********************************************************************************************/
+
+#ifndef _QV_COMPRESSOR
+
+#include <stdio.h>
+
+#define _QV_COMPRESSOR
+
+  //  The defined constant INTERACTIVE (set in DB.h) determines whether an interactive or
+  //    batch version of the routines in this library are compiled.  In batch mode, routines
+  //    print an error message and exit.  In interactive mode, the routines place the error
+  //    message in EPLACE (also defined in DB.h) and return an error value, typically NULL
+  //    if the routine returns a pointer, and an unusual integer value if the routine returns
+  //    an integer.
+  //  Below when an error return is described, one should understand that this value is returned
+  //    only if the routine was compiled in INTERACTIVE mode.
+
+  //  A PacBio compression scheme
+
+typedef struct
+  { void    *delScheme;   //  Huffman scheme for deletion QVs
+    void    *insScheme;   //  Huffman scheme for insertion QVs
+    void    *mrgScheme;   //  Huffman scheme for merge QVs
+    void    *subScheme;   //  Huffman scheme for substitution QVs
+    void    *dRunScheme;  //  Huffman scheme for deletion run lengths (if delChar > 0)
+    void    *sRunScheme;  //  Huffman scheme for substitution run lengths (if subChar > 0)
+    int      delChar;     //  If > 0, run-encoded deletion value
+    int      subChar;     //  If > 0, run-encoded substitution value
+    int      flip;        //  Need to flip multi-byte integers
+    char    *prefix;      //  Header line prefix
+  } QVcoding;
+
+  // Read the next nlines of input, and QVentry returns a pointer to the first line if needed.
+  //   If end-of-input is encountered before any further input, -1 is returned.  If there is
+  //   an error than -2 is returned.  Otherwise the length of the line(s) read is returned.
+
+int       Read_Lines(FILE *input, int nlines);
+char     *QVentry();
+
+  // Get and set the line counter for error reporting
+
+void      Set_QV_Line(int line);
+int       Get_QV_Line();
+
+  // Read up to the next num entries or until eof from the .quiva file on input and record
+  //   frequency statistics.  Copy these entries to the temporary file temp if != NULL.
+  //   If there is an error then -1 is returned, otherwise the number of entries read.
+
+int       QVcoding_Scan(FILE *input, int num, FILE *temp);
+
+  // Given QVcoding_Scan has been called at least once, create an encoding scheme based on
+  //   the accumulated statistics and return a pointer to it.  The returned encoding object
+  //   is *statically allocated within the routine.  If lossy is set then use a lossy scaling
+  //   for the insertion and merge streams.  If there is an error, then NULL is returned.
+
+QVcoding *Create_QVcoding(int lossy);
+
+  //  Read/write a coding scheme to input/output.  The encoding object returned by the reader
+  //    is *statically* allocated within the routine.  If an error occurs while reading then
+  //    NULL is returned.
+
+QVcoding *Read_QVcoding(FILE *input);
+void      Write_QVcoding(FILE *output, QVcoding *coding);
+
+  //  Free all the auxiliary storage associated with coding (but not the object itself!)
+
+void      Free_QVcoding(QVcoding *coding);
+
+  //  Assuming the file pointer is positioned just beyond an entry header line, read the
+  //    next set of 5 QV lines, compress them according to 'coding', and output.  If lossy
+  //    is set then the scheme is a lossy one.  A negative value is returned if an error
+  //    occurred, and the sequence length otherwise.
+
+int      Compress_Next_QVentry(FILE *input, FILE *output, QVcoding *coding, int lossy);
+
+  //  Assuming the input is position just beyond the compressed encoding of an entry header,
+  //    read the set of compressed encodings for the ensuing 5 QV vectors, decompress them,
+  //    and place their decompressed values into entry which is a 5 element array of character
+  //    pointers.  The parameter rlen computed from the preceeding header line, critically
+  //    provides the length of each of the 5 vectors.  A non-zero value is return only if an
+  //    error occured.
+
+int      Uncompress_Next_QVentry(FILE *input, char **entry, QVcoding *coding, int rlen);
+
+#endif // _QV_COMPRESSOR
diff --git a/README b/README
new file mode 100644
index 0000000..b68a35f
--- /dev/null
+++ b/README
@@ -0,0 +1,47 @@
+
+
+
+*** PLEASE GO TO THE DAZZLER BLOG (https://dazzlerblog.wordpress.com) FOR TYPESET ***
+         DOCUMENTATION, EXAMPLES OF USE, AND DESIGN PHILOSOPHY.
+
+
+
+                The Dazzler Scrubbing Suite: DAS SCRUBBER
+
+                            Author:  Gene Myers
+                            First:   October 12, 2014
+                            Recent:  March 27, 2016
+
+  The goal of scrubbing is to produce a set of edited reads that are guaranteed to
+(a) be continuous stretches of the underlying genome (i.e. no unremoved adapters
+and not chimers), and (b) have no very low quality stretches (i.e. the error rate
+never exceeds some reasonable maximum, 20% or so in the case of Pacbio data).  The
+secondary goal of scrubbing is to do so with the minimum removal of data and splitting
+of reads.
+
+  The "DAS" suite will consist of a pipeline of several programs that will accomplish
+the task of scrubbing.  At this time, we are releasing the first program which assigns
+intrinsic quality values to every trace point interval of a read.
+
+1. DASqv [-v] -c<int> <source:db> <overlaps:las>
+
+   DASqv takes as input a database <source> and the local alignments, <overlaps>, for
+said database or a block thereof.  Note carefully that <source> must always refer to
+the entire DB, only <overlaps> can involve a block number.
+
+   Using the local alignment-pile for each A-read, DASqv produces a QV value for each
+complete segment of TRACE_SPACING bases (e.g. 100bp, the -s parameter to daligner).
+The quality value of the average percentile of the best 25-5-% alignment matches
+covering it depending on the coverage estimate -c.  One must supply the -c parameter
+to the expected coverage of the genome in question.  All quality values over 50 are
+clipped to 50.
+   
+   The quality values are written to a .qual track, that can be viewed by calling
+DBdump with the -i option set ("i" for "intrinsic QV").
+
+   The -v option prints out a histogram of the segment align matches, and the quality
+values produced.  This histgram is usefull in assessing, for a given data set, what
+constitutes the threshold -g and -b, to be used by down stream commands, for what is
+definitely a good segment and what is definitely a bad segment.
+
+2. DAStrim -- soon !
diff --git a/align.c b/align.c
new file mode 100644
index 0000000..82de72a
--- /dev/null
+++ b/align.c
@@ -0,0 +1,5132 @@
+/*******************************************************************************************
+ *
+ *  Fast alignment discovery and trace generation along with utilites for displaying alignments
+ *     Based on previously unpublished ideas from 2005, subsequently refined in 2013-14.  Basic
+ *     idea is to keep a dynamically selected interval of the f.r. waves from my 1986 O(nd) paper.
+ *     A recent cool idea is to not record all the details of an alignment while discovering it
+ *     but simply record trace points through which the optimal alignment passes every 100bp,
+ *     allowing rapid recomputation of the alignment details between trace points.
+ *
+ *  Author :  Gene Myers
+ *  First  :  June 2013
+ *  Current:  June 1, 2014
+ *
+ ********************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <limits.h>
+
+#include "DB.h"
+#include "align.h"
+
+#undef    DEBUG_PASSES     //  Show forward / backward extension termini for Local_Alignment
+#undef    DEBUG_POINTS     //  Show trace points
+#undef    DEBUG_WAVE       //  Show waves of Local_Alignment
+#undef     SHOW_MATCH_WAVE //  For waves of Local_Alignment also show # of matches
+#undef    SHOW_TRAIL       //  Show trace at the end of forward and reverse passes
+#undef    SHOW_TPS         //  Show trace points as they are encountered in a wave
+
+#undef  DEBUG_EXTEND       //  Show waves of Extend_Until_Overlap
+
+#undef  DEBUG_ALIGN        //  Show division points of Compute_Trace
+#undef  DEBUG_SCRIPT       //  Show trace additions for Compute_Trace
+#undef  DEBUG_AWAVE        //  Show F/R waves of Compute_Trace
+
+#undef  SHOW_TRACE         //  Show full trace for Print_Alignment
+
+#undef  WAVE_STATS
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  Working Storage Abstraction                                                           *
+*                                                                                        *
+\****************************************************************************************/
+
+typedef struct            //  Hidden from the user, working space for each thread
+  { int     vecmax;
+    void   *vector;
+    int     celmax;
+    void   *cells;
+    int     pntmax;
+    void   *points;
+    int     tramax;
+    void   *trace;
+  } _Work_Data;
+
+Work_Data *New_Work_Data()
+{ _Work_Data *work;
+  
+  work = (_Work_Data *) Malloc(sizeof(_Work_Data),"Allocating work data block");
+  if (work == NULL)
+    EXIT(NULL);
+  work->vecmax = 0;
+  work->vector = NULL;
+  work->pntmax = 0;
+  work->points = NULL;
+  work->tramax = 0;
+  work->trace  = NULL;
+  work->celmax = 0;
+  work->cells  = NULL;
+  return ((Work_Data *) work);
+}
+
+static int enlarge_vector(_Work_Data *work, int newmax)
+{ void *vec;
+  int   max;
+
+  max = ((int) (newmax*1.2)) + 10000;
+  vec = Realloc(work->vector,max,"Enlarging DP vector");
+  if (vec == NULL)
+    EXIT(1);
+  work->vecmax = max;
+  work->vector = vec;
+  return (0);
+}
+
+static int enlarge_points(_Work_Data *work, int newmax)
+{ void *vec;
+  int   max;
+
+  max = ((int) (newmax*1.2)) + 10000;
+  vec = Realloc(work->points,max,"Enlarging point vector");
+  if (vec == NULL)
+    EXIT(1);
+  work->pntmax = max;
+  work->points = vec;
+  return (0);
+}
+
+static int enlarge_trace(_Work_Data *work, int newmax)
+{ void *vec;
+  int   max;
+
+  max = ((int) (newmax*1.2)) + 10000;
+  vec = Realloc(work->trace,max,"Enlarging trace vector");
+  if (vec == NULL)
+    EXIT(1);
+  work->tramax = max;
+  work->trace  = vec;
+  return (0);
+}
+
+void Free_Work_Data(Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  if (work->vector != NULL)
+    free(work->vector);
+  if (work->cells != NULL)
+    free(work->cells);
+  if (work->trace != NULL)
+    free(work->trace);
+  if (work->points != NULL)
+    free(work->points);
+  free(work);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  ADAPTIVE PATH FINDING                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+  //  Absolute/Fixed Parameters
+
+#define BVEC  uint64     //  Can be uint32 if PATH_LEN <= 32
+
+#define TRIM_LEN    15   //  Report as the tip, the last wave maximum for which the last
+                         //     2*TRIM_LEN edits are prefix-positive at rate ave_corr*f(bias)
+                         //     (max value is 20)
+
+#define PATH_LEN    60   //  Follow the last PATH_LEN columns/edges (max value is 63)
+
+  //  Derivative fixed parameters
+
+#define PATH_TOP  0x1000000000000000ll   //  Must be 1 << PATH_LEN
+#define PATH_INT  0x0fffffffffffffffll   //  Must be PATH_TOP-1
+#define TRIM_MASK 0x7fff                 //  Must be (1 << TRIM_LEN) - 1
+#define TRIM_MLAG 200                    //  How far can last trim point be behind best point
+#define WAVE_LAG   30                    //  How far can worst point be behind the best point
+
+static double Bias_Factor[10] = { .690, .690, .690, .690, .780,
+                                  .850, .900, .933, .966, 1.000 };
+
+  //  Adjustable paramters
+
+typedef struct
+  { double ave_corr;
+    int    trace_space;
+    float  freq[4];
+    int    ave_path;
+    int16 *score;
+    int16 *table;
+  } _Align_Spec;
+ 
+/* Fill in bit table: TABLE[x] = 1 iff the alignment modeled by x (1 = match, 0 = mismatch)
+     has a non-negative score for every suffix of the alignment under the scoring scheme
+     where match = MATCH and mismatch = -1.  MATCH is set so that an alignment with TRIM_PCT
+     matches has zero score ( (1-TRIM_PCT) / TRIM_PCT ).                                     */
+
+#define FRACTION 1000  //  Implicit fractional part of scores, i.e. score = x/FRACTION
+
+typedef struct
+  { int    mscore;
+    int    dscore;
+    int16 *table;
+    int16 *score;
+  } Table_Bits;
+
+static void set_table(int bit, int prefix, int score, int max, Table_Bits *parms)
+{ if (bit >= TRIM_LEN)
+    { parms->table[prefix] = (int16) (score-max);
+      parms->score[prefix] = (int16) score;
+    }
+  else
+    { if (score > max)
+        max = score;
+      set_table(bit+1,(prefix<<1),score - parms->dscore,max,parms);
+      set_table(bit+1,(prefix<<1) | 1,score + parms->mscore,max,parms);
+    }
+}
+
+/* Create an alignment specification record including path tip tables & values */
+
+Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq)
+{ _Align_Spec *spec;
+  Table_Bits   parms;
+  double       match;
+  int          bias;
+
+  spec = (_Align_Spec *) Malloc(sizeof(_Align_Spec),"Allocating alignment specification");
+  if (spec == NULL)
+    EXIT(NULL);
+
+  spec->ave_corr    = ave_corr;
+  spec->trace_space = trace_space;
+  spec->freq[0]     = freq[0];
+  spec->freq[1]     = freq[1];
+  spec->freq[2]     = freq[2];
+  spec->freq[3]     = freq[3];
+
+  match = freq[0] + freq[3];
+  if (match > .5)
+    match = 1.-match;
+  bias = (int) ((match+.025)*20.-1.);
+  if (match < .2)
+    { fprintf(stderr,"Warning: Base bias worse than 80/20%% ! (New_Align_Spec)\n");
+      fprintf(stderr,"         Capping bias at this ratio.\n");
+      bias = 3; 
+    }
+
+  spec->ave_path = (int) (PATH_LEN * (1. - Bias_Factor[bias] * (1. - ave_corr)));
+  parms.mscore   = (int) (FRACTION * Bias_Factor[bias] * (1. - ave_corr));
+  parms.dscore   = FRACTION - parms.mscore;
+
+  parms.score = (int16 *) Malloc(sizeof(int16)*(TRIM_MASK+1)*2,"Allocating trim table");
+  if (parms.score == NULL)
+    { free(spec);
+      EXIT(NULL);
+    }
+  parms.table = parms.score + (TRIM_MASK+1);
+
+  set_table(0,0,0,0,&parms);
+
+  spec->table = parms.table;
+  spec->score = parms.score;
+
+  return ((Align_Spec *) spec);
+}
+
+void Free_Align_Spec(Align_Spec *espec)
+{ _Align_Spec *spec = (_Align_Spec *) espec;
+  free(spec->score);
+  free(spec);
+}
+
+double Average_Correlation(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->ave_corr); }
+
+int Trace_Spacing(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->trace_space); }
+
+float *Base_Frequencies(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->freq); }
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  LOCAL ALIGNMENT FINDER: forward_/reverse_wave and Local_Alignment                     *
+*                                                                                        *
+\****************************************************************************************/
+
+
+#ifdef WAVE_STATS
+
+static int64 MAX, TOT, NWV;
+static int64 RESTARTS;
+
+void Init_Stats()
+{ MAX = TOT = NWV = 0;
+  RESTARTS = 0;
+}
+
+void Print_Stats()
+{ printf("\nMax = %lld  Ave = %.1f  # = %lld\n",MAX,(1.*TOT)/NWV,NWV);
+  printf("\nRestarts = %lld\n",RESTARTS);
+}
+
+#endif
+
+
+#ifdef DEBUG_WAVE
+
+static void print_wave(int *V, int *M, int low, int hgh, int besta)
+{ int k, bestk;
+
+  (void) M;
+  printf("  [%6d,%6d]: ",low,hgh);
+  for (k = low; k <= hgh; k++)
+    { if (besta == V[k])
+        bestk = k;
+      // printf(" %3d",(V[k]+k)/2);
+      printf(" %3d",besta-V[k]);
+    }
+  printf(" : %d (%d,%d)\n",besta,(besta+bestk)/2,(besta-bestk)/2);
+#ifdef SHOW_MATCH_WAVE
+  printf("                   ");
+  for (k = low; k <= hgh; k++)
+    printf(" %3d",M[k]);
+  printf("\n");
+#endif
+  fflush(stdout);
+}
+
+#endif
+
+/* At each furthest reaching point, keep a-coordinate of point (V), bitvector
+     recording the last TRIM_LEN columns of the implied alignment (T), and the
+     # of matches (1-bits) in the bitvector (M).                               */
+
+typedef struct
+  { int ptr;
+    int diag;
+    int diff;
+    int mark;
+  } Pebble;
+
+static int VectorEl = 6*sizeof(int) + sizeof(BVEC);
+
+static int forward_wave(_Work_Data *work, _Align_Spec *spec, Alignment *align, Path *bpath,
+                        int *mind, int maxd, int mida, int minp, int maxp)
+{ char *aseq  = align->aseq;
+  char *bseq  = align->bseq;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     vlen, vmin, vmax;
+  int    *V, *M;
+  int    *_V, *_M;
+  BVEC   *T;
+  BVEC   *_T;
+
+  int    *HA, *HB;
+  int    *_HA, *_HB;
+  int    *NA, *NB;
+  int    *_NA, *_NB;
+  Pebble *cells;
+  int     avail, cmax, boff;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha, trimhb;
+  int     morea, morey, mored;
+  int     moreha, morehb;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  hgh = maxd;
+  low = *mind;
+  dif = 0;
+
+  { int span, wing;
+
+    span = (hgh-low)+1;
+    vlen = work->vecmax/VectorEl;
+    wing = (vlen - span)/2;
+    vmin = low - wing;
+    vmax = hgh + wing;
+
+    _V  = ((int *) work->vector);
+    _M  = _V + vlen;
+    _HA = _M + vlen;
+    _HB = _HA + vlen;
+    _NA = _HB + vlen;
+    _NB = _NA + vlen;
+    _T  = ((BVEC *) (_NB + vlen));
+
+    V  = _V-vmin;
+    M  = _M-vmin;
+    HA = _HA-vmin;
+    HB = _HB-vmin;
+    NA = _NA-vmin;
+    NB = _NB-vmin;
+    T  = _T-vmin;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+
+    if (COMP(align->flags))
+      boff = align->blen % TRACE_SPACE;
+    else
+      boff = 0;
+  }
+
+  /* Compute 0-wave starting from mid-line */
+
+  more  = 1;
+  aclip =  INT32_MAX;
+  bclip = -INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  trimhb = morehb = 1;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a  = aseq + hgh;
+    for (k = hgh; k >= low; k--)
+      { int     y, c, d;
+        int     ha, hb;
+        int     na, nb;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              EXIT(1);
+            work->celmax = cmax;
+            work->cells  = (void *) cells;
+          }
+
+        na = ((y+k)/TRACE_SPACE)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: %d,%d,0,%d\n",avail,-1,k,na); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = na;
+        ha  = avail++;
+        na += TRACE_SPACE;
+
+        nb = ((y+(TRACE_SPACE-boff))/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+        printf(" B %d: %d,%d,0,%d\n",avail,-1,k,nb); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = nb;
+        hb  = avail++;
+        nb += TRACE_SPACE;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip < k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y += 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k >= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = na;
+            ha  = avail++;
+            na += TRACE_SPACE;
+          }
+        while (y >= nb)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = hb;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = nb;
+            hb  = avail++;
+            nb += TRACE_SPACE;
+          }
+
+        if (c > besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+            trimhb = hb;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        HB[k] = hb;
+        NA[k] = na;
+        NB[k] = nb;
+
+        a -= 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (hgh >= aclip)
+        { hgh = aclip-1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+              morehb = HB[aclip];
+            }
+        }
+      if (low <= bclip)
+        { low = bclip+1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+              morehb = HB[bclip];
+            }
+        }
+      aclip =  INT32_MAX;
+      bclip = -INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nFORWARD WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  /* Compute successive waves until no furthest reaching points remain */
+
+  while (more && lasta >= besta - TRIM_MLAG)
+    { int     k, n;
+      int     ua, ub;
+      BVEC    t;
+      int     am, ac, ap;
+      char   *a;
+
+      low -= 1;
+      hgh += 1;
+
+      if (low <= vmin || hgh >= vmax)
+        { int   span, wing;
+          int64 move;
+          int64 vd, md, had, hbd, nad, nbd, td;
+
+          span = (hgh-low)+1;
+          if (.8*vlen < span)
+            { if (enlarge_vector(work,vlen*VectorEl))
+                EXIT(1);
+
+              move = ((void *) _V) - work->vector;
+              vlen = work->vecmax/VectorEl;
+
+              _V  = (int *) work->vector;
+              _M  = _V + vlen;
+              _HA = _M + vlen;
+              _HB = _HA + vlen;
+              _NA = _HB + vlen;
+              _NB = _NA + vlen;
+              _T  = ((BVEC *) (_NB + vlen));
+            }
+          else
+            move = 0;
+
+          wing = (vlen - span)/2;
+
+          vd  = ((void *) ( _V+wing)) - (((void *) ( V+low)) - move);
+          md  = ((void *) ( _M+wing)) - (((void *) ( M+low)) - move);
+          had = ((void *) (_HA+wing)) - (((void *) (HA+low)) - move);
+          hbd = ((void *) (_HB+wing)) - (((void *) (HB+low)) - move);
+          nad = ((void *) (_NA+wing)) - (((void *) (NA+low)) - move);
+          nbd = ((void *) (_NB+wing)) - (((void *) (NB+low)) - move);
+          td  = ((void *) ( _T+wing)) - (((void *) ( T+low)) - move);
+
+          if (vd < 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+          if (md < 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (had < 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (hbd < 0)
+            memmove(_HB+wing,  ((void *) (HB+low)) - move, span*sizeof(int));
+          if (nad < 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (nbd < 0)
+            memmove(_NB+wing,  ((void *) (NB+low)) - move, span*sizeof(int));
+          if (td < 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+
+          if (td > 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+          if (nbd > 0)
+            memmove(_NB+wing,  ((void *) (NB+low)) - move, span*sizeof(int));
+          if (nad > 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (hbd > 0)
+            memmove(_HB+wing,  ((void *) (HB+low)) - move, span*sizeof(int));
+          if (had > 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (md > 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (vd > 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+
+          vmin = low-wing;
+          vmax = hgh+wing;
+
+          V  =  _V-vmin;
+          M  =  _M-vmin;
+          HA = _HA-vmin;
+          HB = _HB-vmin;
+          NA = _NA-vmin;
+          NB = _NB-vmin;
+          T  =  _T-vmin;
+        }
+
+      if (low >= minp)
+        { NA[low] = NA[low+1];
+          NB[low] = NB[low+1];
+          V[low]  = -1;
+        }
+      else
+        low += 1;
+
+      if (hgh <= maxp)
+        { NA[hgh] = NA[hgh-1];
+          NB[hgh] = NB[hgh-1];
+          V[hgh]  = am = -1;
+        }
+      else
+        am = V[--hgh];
+
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = -1;
+      a  = aseq + hgh;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = ub = -1;
+      for (k = hgh; k >= low; k--)
+        { int     y, m;
+          int     ha, hb;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          ap = ac;
+          ac = am;
+          am = V[d = k-1];
+
+          if (ac < am)
+            if (am < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = am+1;
+                m  = M[d];
+                b  = T[d]; 
+                ha = HA[d];
+                hb = HB[d];
+              }
+          else
+            if (ac < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ac+2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+                hb = HB[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip < k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y += 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k >= NA[k])
+            { if (cells[ha].mark < NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] += TRACE_SPACE;
+            }
+
+          while (y >= NB[k])
+            { if (cells[hb].mark < NB[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = hb;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NB[k];
+                  hb = avail++;
+                }
+              NB[k] += TRACE_SPACE;
+            }
+
+          if (c > besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                        trimhb = hb;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          ub = HB[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+          HB[k] = hb;
+
+          a -= 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta-besty] != 4)
+            more = 1;
+          if (hgh >= aclip)
+            { hgh = aclip-1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                  morehb = HB[aclip];
+                }
+            }
+          if (low <= bclip)
+            { low = bclip+1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                  morehb = HB[bclip];
+                }
+            }
+          aclip =  INT32_MAX;
+          bclip = -INT32_MAX;
+        }
+
+      n = besta - WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] < n)
+          hgh -= 1;                               
+        else
+          { while (V[low] < n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    uint16 *btrace = (uint16 *) bpath->trace;
+    int     atlen, btlen;
+    int     trimx;
+    int     a, b, k, h;
+    int     d, e;
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+        trimhb = morehb;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = btlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = (mida-k)/2;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark - k;
+        d = cells[h].diff;
+        atrace[atlen++] = (uint16) (d-e);
+        atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,a-b); fflush(stdout);
+#endif
+        b = a;
+        e = d;
+      }
+    if (b+k != trimx)
+      { atrace[atlen++] = (uint16) (trimd-e);
+        atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimy)
+      { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+        atrace[atlen-2] = (uint16) (atrace[atlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+
+    a = -1;
+    for (h = trimhb; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = (mida+k)/2;
+    e = 0;
+    low = k;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,(mida-k)/2); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark + k;
+        d = cells[h].diff;
+        btrace[btlen++] = (uint16) (d-e);
+        btrace[btlen++] = (uint16) (a-b);  
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,a-b); fflush(stdout);
+#endif
+        b = a;
+        e = d;
+      }
+    if (b-k != trimy)
+      { btrace[btlen++] = (uint16) (trimd-e);
+        btrace[btlen++] = (uint16) (trimx-b);  
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimx)
+      { btrace[btlen-1] = (uint16) (btrace[btlen-1] + (trimx-b));
+        btrace[btlen-2] = (uint16) (btrace[btlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+      }
+
+    apath->aepos = trimx;
+    apath->bepos = trimy;
+    apath->diffs = trimd;
+    apath->tlen  = atlen;
+    if (COMP(align->flags))
+      { bpath->abpos = align->blen - apath->bepos;
+        bpath->bbpos = align->alen - apath->aepos;
+      }
+    else
+      { bpath->aepos = apath->bepos;
+        bpath->bepos = apath->aepos;
+      }
+    bpath->diffs = trimd;
+    bpath->tlen  = btlen;
+  }
+
+  *mind = low;
+  return (0);
+}
+
+/*** Reverse Wave ***/
+
+static int reverse_wave(_Work_Data *work, _Align_Spec *spec, Alignment *align, Path *bpath,
+                        int mind, int maxd, int mida, int minp, int maxp)
+{ char *aseq  = align->aseq - 1;
+  char *bseq  = align->bseq - 1;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     vlen, vmin, vmax;
+  int    *V, *M;
+  int    *_V, *_M;
+  BVEC   *T;
+  BVEC   *_T;
+
+  int    *HA, *HB;
+  int    *_HA, *_HB;
+  int    *NA, *NB;
+  int    *_NA, *_NB;
+  Pebble *cells;
+  int     avail, cmax, boff;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha, trimhb;
+  int     morea, morey, mored;
+  int     moreha, morehb;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  hgh = maxd;
+  low = mind;
+  dif = 0;
+
+  { int span, wing;
+
+    span = (hgh-low)+1;
+    vlen = work->vecmax/VectorEl;
+    wing = (vlen - span)/2;
+    vmin = low - wing;
+    vmax = hgh + wing;
+
+    _V  = ((int *) work->vector);
+    _M  = _V + vlen;
+    _HA = _M + vlen;
+    _HB = _HA + vlen;
+    _NA = _HB + vlen;
+    _NB = _NA + vlen;
+    _T  = ((BVEC *) (_NB + vlen));
+
+    V  = _V-vmin;
+    M  = _M-vmin;
+    HA = _HA-vmin;
+    HB = _HB-vmin;
+    NA = _NA-vmin;
+    NB = _NB-vmin;
+    T  = _T-vmin;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+
+    if (COMP(align->flags))
+      boff = align->blen % TRACE_SPACE;
+    else
+      boff = 0;
+  }
+
+  more  = 1;
+  aclip = -INT32_MAX;
+  bclip =  INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  trimhb = morehb = 1;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a = aseq + low;
+    for (k = low; k <= hgh; k++)
+      { int     y, c, d;
+        int     ha, hb;
+        int     na, nb;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              EXIT(1);
+            work->celmax = cmax;
+            work->cells  = (void *) cells;
+          }
+
+        na = ((y+k+TRACE_SPACE-1)/TRACE_SPACE-1)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: -1,%d,0,%d\n",avail,k,na+TRACE_SPACE); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = y+k;
+        ha  = avail++;
+
+        nb = ((y+(TRACE_SPACE-boff)-1)/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+        printf(" B %d: -1,%d,0,%d\n",avail,k,nb+TRACE_SPACE); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = y;
+        hb  = avail++;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip > k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y -= 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k <= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = na;
+            ha  = avail++;
+            na -= TRACE_SPACE;
+          }
+        while (y <= nb)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = hb;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = nb;
+            hb  = avail++;
+            nb -= TRACE_SPACE;
+          }
+
+        if (c < besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+            trimhb = hb;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        HB[k] = hb;
+        NA[k] = na;
+        NB[k] = nb;
+
+        a += 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (low <= aclip)
+        { low = aclip+1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+              morehb = HB[aclip];
+            }
+        }
+      if (hgh >= bclip)
+        { hgh = bclip-1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+              morehb = HB[bclip];
+            }
+        }
+      aclip = -INT32_MAX;
+      bclip =  INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nREVERSE WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  while (more && lasta <= besta + TRIM_MLAG)
+    { int    k, n;
+      int    ua, ub;
+      BVEC   t;
+      int    am, ac, ap;
+      char  *a;
+
+      low -= 1;
+      hgh += 1;
+
+      if (low <= vmin || hgh >= vmax)
+        { int   span, wing;
+          int64 move, vd, md, had, hbd, nad, nbd, td;
+
+          span = (hgh-low)+1;
+          if (.8*vlen < span)
+            { if (enlarge_vector(work,vlen*VectorEl))
+                EXIT(1);
+
+              move = ((void *) _V) - work->vector;
+              vlen = work->vecmax/VectorEl;
+
+              _V  = (int *) work->vector;
+              _M  = _V + vlen;
+              _HA = _M + vlen;
+              _HB = _HA + vlen;
+              _NA = _HB + vlen;
+              _NB = _NA + vlen;
+              _T  = ((BVEC *) (_NB + vlen));
+            }
+          else
+            move = 0;
+
+          wing = (vlen - span)/2;
+
+          vd  = ((void *) ( _V+wing)) - (((void *) ( V+low)) - move);
+          md  = ((void *) ( _M+wing)) - (((void *) ( M+low)) - move);
+          had = ((void *) (_HA+wing)) - (((void *) (HA+low)) - move);
+          hbd = ((void *) (_HB+wing)) - (((void *) (HB+low)) - move);
+          nad = ((void *) (_NA+wing)) - (((void *) (NA+low)) - move);
+          nbd = ((void *) (_NB+wing)) - (((void *) (NB+low)) - move);
+          td  = ((void *) ( _T+wing)) - (((void *) ( T+low)) - move);
+
+          if (vd < 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+          if (md < 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (had < 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (hbd < 0)
+            memmove(_HB+wing,  ((void *) (HB+low)) - move, span*sizeof(int));
+          if (nad < 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (nbd < 0)
+            memmove(_NB+wing,  ((void *) (NB+low)) - move, span*sizeof(int));
+          if (td < 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+
+          if (td > 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+          if (nbd > 0)
+            memmove(_NB+wing,  ((void *) (NB+low)) - move, span*sizeof(int));
+          if (nad > 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (hbd > 0)
+            memmove(_HB+wing,  ((void *) (HB+low)) - move, span*sizeof(int));
+          if (had > 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (md > 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (vd > 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+
+          vmin = low-wing;
+          vmax = hgh+wing;
+
+          V  =  _V-vmin;
+          M  =  _M-vmin;
+          HA = _HA-vmin;
+          HB = _HB-vmin;
+          NA = _NA-vmin;
+          NB = _NB-vmin;
+          T  =  _T-vmin;
+        }
+
+      if (low >= minp)
+        { NA[low] = NA[low+1];
+          NB[low] = NB[low+1];
+          V[low]  = ap = INT32_MAX;
+        }
+      else
+        ap = V[++low]; 
+
+      if (hgh <= maxp)
+        { NA[hgh] = NA[hgh-1];
+          NB[hgh] = NB[hgh-1];
+          V[hgh] = INT32_MAX;
+        }
+      else
+        hgh -= 1;
+
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = INT32_MAX;
+      a  = aseq + low;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = ub = -1;
+      for (k = low; k <= hgh; k++)
+        { int     y, m;
+          int     ha, hb;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          am = ac;
+          ac = ap;
+          ap = V[d = k+1];
+
+          if (ac > ap)
+            if (ap > am)
+              { c = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ap-1;
+                m  = M[d];
+                b  = T[d];
+                ha = HA[d];
+                hb = HB[d];
+              }
+          else
+            if (ac > am)
+              { c  = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ac-2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+                hb = HB[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip > k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y -= 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k <= NA[k])
+            { if (cells[ha].mark > NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] -= TRACE_SPACE;
+            }
+          while (y <= NB[k])
+            { if (cells[hb].mark > NB[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = hb;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NB[k];
+                  hb = avail++;
+                }
+              NB[k] -= TRACE_SPACE;
+            }
+
+          if (c < besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                        trimhb = hb;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          ub = HB[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+          HB[k] = hb;
+
+          a += 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+            more = 1;
+          if (low <= aclip)
+            { low = aclip+1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                  morehb = HB[aclip];
+                }
+            }
+          if (hgh >= bclip)
+            { hgh = bclip-1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                  morehb = HB[bclip];
+                }
+            }
+          aclip = -INT32_MAX;
+          bclip =  INT32_MAX;
+        }
+
+      n = besta + WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] > n)
+          hgh -= 1;                               
+        else
+          { while (V[low] > n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    uint16 *btrace = (uint16 *) bpath->trace;
+    int     atlen, btlen;
+    int     trimx;
+    int     a, b, k, h;
+    int     d, e;
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+        trimhb = morehb;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = btlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = cells[h].mark - k;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+    if ((b+k)%TRACE_SPACE != 0)
+      { h = cells[h].ptr;
+        if (h < 0)
+          { a = trimy;
+            d = trimd;
+          }
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            d = cells[h].diff;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+        if (apath->tlen == 0)
+          { atrace[--atlen] = (uint16) (b-a);
+            atrace[--atlen] = (uint16) (d-e);
+          }
+        else
+          { atrace[1] = (uint16) (atrace[1] + (b-a));
+            atrace[0] = (uint16) (atrace[0] + (d-e));
+          }
+        b = a;
+        e = d;
+      }
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            atrace[--atlen] = (uint16) (b-a);
+            d = cells[h].diff;
+            atrace[--atlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+            b = a;
+            e = d;
+          }
+        if (b+k != trimx)
+          { atrace[--atlen] = (uint16) (b-trimy);
+            atrace[--atlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+        else if (b != trimy)
+          { atrace[atlen+1] = (uint16) (atrace[atlen+1] + (b-trimy));
+            atrace[atlen]   = (uint16) (atrace[atlen]   + (trimd-e));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+      }
+
+    a = -1;
+    for (h = trimhb; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = cells[h].mark + k;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,b-k); fflush(stdout);
+#endif
+    if ((b-k)%TRACE_SPACE != boff)
+      { h = cells[h].ptr;
+        if (h < 0)
+          { a = trimx;
+            d = trimd;
+          } 
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+            d = cells[h].diff;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+        if (bpath->tlen == 0)
+          { btrace[--btlen] = (uint16) (b-a);
+            btrace[--btlen] = (uint16) (b-a);
+          }
+        else
+          { btrace[1] = (uint16) (btrace[1] + (b-a));
+            btrace[0] = (uint16) (btrace[0] + (d-e));
+          }
+        b = a;
+        e = d;
+      }
+
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+            btrace[--btlen] = (uint16) (b-a);
+            d = cells[h].diff;
+            btrace[--btlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+            b = a;
+            e = d;
+          }
+        if (b-k != trimy)
+          { btrace[--btlen] = (uint16) (b-trimx);
+            btrace[--btlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+          }
+        else if (b != trimx)
+          { btrace[btlen+1] = (uint16) (btrace[btlen+1] + (b-trimx));
+            btrace[btlen]   = (uint16) (btrace[btlen]   + (trimd-e));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+          }
+      }
+
+    apath->abpos = trimx;
+    apath->bbpos = trimy;
+    apath->diffs = apath->diffs + trimd;
+    apath->tlen  = apath->tlen  - atlen;
+    apath->trace = atrace + atlen;
+    if (COMP(align->flags))
+      { bpath->aepos = align->blen - apath->bbpos;
+        bpath->bepos = align->alen - apath->abpos;
+      }
+    else
+      { bpath->abpos = apath->bbpos;
+        bpath->bbpos = apath->abpos;
+      }
+    bpath->diffs = bpath->diffs + trimd;
+    bpath->tlen  = bpath->tlen  - btlen;
+    bpath->trace = btrace + btlen;
+  }
+
+  return (0);
+}
+
+
+/* Find the longest local alignment between aseq and bseq through (xcnt,ycnt)
+   See associated .h file for the precise definition of the interface.
+*/
+
+Path *Local_Alignment(Alignment *align, Work_Data *ework, Align_Spec *espec,
+                      int low, int hgh, int anti, int lbord, int hbord)
+{ _Work_Data  *work = ( _Work_Data *) ework;
+  _Align_Spec *spec = (_Align_Spec *) espec;
+
+  Path *apath, *bpath;
+  int   minp, maxp;
+  int   selfie;
+
+  { int alen, blen;
+    int maxtp, wsize;
+
+    alen = align->alen;
+    blen = align->blen;
+
+    if (hgh-low >= 7500)
+      wsize = VectorEl*(hgh-low+1);
+    else
+      wsize = VectorEl*10000;
+    if (wsize >= work->vecmax)
+      if (enlarge_vector(work,wsize))
+        EXIT(NULL);
+
+    if (alen < blen)
+      maxtp = 2*(blen/spec->trace_space+2);
+    else
+      maxtp = 2*(alen/spec->trace_space+2);
+    wsize = 4*maxtp*sizeof(uint16) + sizeof(Path);
+    if (wsize > work->pntmax)
+      if (enlarge_points(work,wsize))
+        EXIT(NULL);
+
+    apath = align->path;
+    bpath = (Path *) work->points;
+
+    apath->trace = ((uint16 *) (bpath+1)) + maxtp;
+    bpath->trace = ((uint16 *) apath->trace) +  2*maxtp;
+  }
+
+#ifdef DEBUG_PASSES
+  printf("\n");
+#endif
+
+  selfie = (align->aseq == align->bseq);
+   
+  if (lbord < 0)
+    { if (selfie && low >= 0)
+        minp = 1;
+      else
+        minp = -INT32_MAX;
+    }
+  else
+    minp = low-lbord;
+  if (hbord < 0)
+    { if (selfie && hgh <= 0)
+        maxp = -1;
+      else
+        maxp = INT32_MAX;
+    }
+  else
+    maxp = hgh+hbord;
+
+  if (forward_wave(work,spec,align,bpath,&low,hgh,anti,minp,maxp))
+    EXIT(NULL);
+
+#ifdef DEBUG_PASSES
+  printf("F1 (%d,%d) ~ %d => (%d,%d) %d\n",
+         (2*anti+(low+hgh))/4,(anti-(low+hgh))/4,hgh-low,
+         apath->aepos,apath->bepos,apath->diffs);
+#endif
+
+  if (reverse_wave(work,spec,align,bpath,low,low,anti,minp,maxp))
+    EXIT(NULL);
+
+#ifdef DEBUG_PASSES
+  printf("R1 (%d,%d) => (%d,%d) %d\n",
+         (anti+low)/2,(anti-low)/2,apath->abpos,apath->bbpos,apath->diffs);
+#endif
+
+  if (COMP(align->flags))
+    { uint16 *trace = (uint16 *) bpath->trace;
+      uint16  p;
+      int     i, j;
+
+      i = bpath->tlen-2;
+      j = 0;
+      while (j < i)
+        { p = trace[i];
+          trace[i] = trace[j];
+          trace[j] = p;
+          p = trace[i+1];
+          trace[i+1] = trace[j+1];
+          trace[j+1] = p;
+          i -= 2;
+          j += 2;
+        }
+    }
+
+#ifdef DEBUG_POINTS
+  { uint16 *trace = (uint16 *) apath->trace;
+    int     a, h;
+
+    printf("\nA-path (%d,%d)->(%d,%d)",apath->abpos,apath->bbpos,apath->aepos,apath->bepos);
+    printf(" %c\n",(COMP(align->flags) ? 'c' : 'n'));
+    a = apath->bbpos;
+    for (h = 1; h < apath->tlen; h += 2)
+      { int dif = trace[h-1];
+        int del = trace[h];
+        a += del;
+        printf("      %d / %d (%d)\n",dif,del,a);
+      }
+  }
+
+  { uint16 *trace = (uint16 *) bpath->trace;
+    int     a, h;
+
+    printf("\nB-path (%d,%d)->(%d,%d)",bpath->abpos,bpath->bbpos,bpath->aepos,bpath->bepos);
+    printf(" %c [%d,%d]\n",(COMP(align->flags) ? 'c' : 'n'),align->blen,align->alen);
+    a = bpath->bbpos;
+    for (h = 1; h < bpath->tlen; h += 2)
+      { int dif = trace[h-1];
+        int del = trace[h];
+        a += del;
+        printf("      %d / %d (%d)\n",dif,del,a);
+      }
+  }
+#endif
+
+  return (bpath);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  EXTENSION VERSION OF LOCAL ALIGNMENT                                                  *
+*                                                                                        *
+\****************************************************************************************/
+
+static int VectorEn = 4*sizeof(int) + sizeof(BVEC);
+
+static int forward_extend(_Work_Data *work, _Align_Spec *spec, Alignment *align,
+                          int midd, int mida, int minp, int maxp)
+{ char *aseq  = align->aseq;
+  char *bseq  = align->bseq;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     vlen, vmin, vmax;
+  int    *V, *M;
+  int    *_V, *_M;
+  BVEC   *T;
+  BVEC   *_T;
+
+  int    *HA, *NA;
+  int    *_HA, *_NA;
+  Pebble *cells;
+  int     avail, cmax;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha;
+  int     morea, morey, mored;
+  int     moreha;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  hgh = midd;
+  low = midd;
+  dif = 0;
+
+  { int span, wing;
+
+    span = (hgh-low)+1;
+    vlen = work->vecmax/VectorEn;
+    wing = (vlen - span)/2;
+    vmin = low - wing;
+    vmax = hgh + wing;
+
+    _V  = ((int *) work->vector);
+    _M  = _V + vlen;
+    _HA = _M + vlen;
+    _NA = _HA + vlen;
+    _T  = ((BVEC *) (_NA + vlen));
+
+    V  = _V-vmin;
+    M  = _M-vmin;
+    HA = _HA-vmin;
+    NA = _NA-vmin;
+    T  = _T-vmin;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+  }
+
+  /* Compute 0-wave starting from mid-line */
+
+  more  = 1;
+  aclip =  INT32_MAX;
+  bclip = -INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a  = aseq + hgh;
+    for (k = hgh; k >= low; k--)
+      { int     y, c, d;
+        int     ha, na;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              EXIT(1);
+            work->celmax = cmax;
+            work->cells  = (void *) cells;
+          }
+
+        na = ((y+k)/TRACE_SPACE)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: %d,%d,0,%d\n",avail,-1,k,na); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = na;
+        ha  = avail++;
+        na += TRACE_SPACE;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip < k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y += 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k >= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = na;
+            ha  = avail++;
+            na += TRACE_SPACE;
+          }
+
+        if (c > besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        NA[k] = na;
+
+        a -= 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (hgh >= aclip)
+        { hgh = aclip-1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+            }
+        }
+      if (low <= bclip)
+        { low = bclip+1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+            }
+        }
+      aclip =  INT32_MAX;
+      bclip = -INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nFORWARD WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  /* Compute successive waves until no furthest reaching points remain */
+
+  while (more && lasta >= besta - TRIM_MLAG)
+    { int     k, n;
+      int     ua;
+      BVEC    t;
+      int     am, ac, ap;
+      char   *a;
+
+      if (low <= vmin || hgh >= vmax)
+        { int   span, wing;
+          int64 move;
+          int64 vd, md, had, nad, td;
+
+          span = (hgh-low)+1;
+          if (.8*vlen < span)
+            { if (enlarge_vector(work,vlen*VectorEn))
+                EXIT(1);
+
+              move = ((void *) _V) - work->vector;
+              vlen = work->vecmax/VectorEn;
+
+              _V  = (int *) work->vector;
+              _M  = _V + vlen;
+              _HA = _M + vlen;
+              _NA = _HA + vlen;
+              _T  = ((BVEC *) (_NA + vlen));
+            }
+          else
+            move = 0;
+
+          wing = (vlen - span)/2;
+
+          vd  = ((void *) ( _V+wing)) - (((void *) ( V+low)) - move);
+          md  = ((void *) ( _M+wing)) - (((void *) ( M+low)) - move);
+          had = ((void *) (_HA+wing)) - (((void *) (HA+low)) - move);
+          nad = ((void *) (_NA+wing)) - (((void *) (NA+low)) - move);
+          td  = ((void *) ( _T+wing)) - (((void *) ( T+low)) - move);
+
+          if (vd < 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+          if (md < 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (had < 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (nad < 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (td < 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+
+          if (td > 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+          if (nad > 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (had > 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (md > 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (vd > 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+
+          vmin = low-wing;
+          vmax = hgh+wing;
+
+          V  =  _V-vmin;
+          M  =  _M-vmin;
+          HA = _HA-vmin;
+          NA = _NA-vmin;
+          T  =  _T-vmin;
+        }
+
+      if (low > minp)
+        { low -= 1;
+          NA[low] = NA[low+1];
+          V[low]  = -1;
+        }
+      if (hgh < maxp)
+        { hgh += 1;
+          NA[hgh] = NA[hgh-1];
+          V[hgh]  = am = -1;
+        }
+      else
+        am = V[hgh];
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = -1;
+      a  = aseq + hgh;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = -1;
+      for (k = hgh; k >= low; k--)
+        { int     y, m;
+          int     ha;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          ap = ac;
+          ac = am;
+          am = V[d = k-1];
+
+          if (ac < am)
+            if (am < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+              }
+            else
+              { c  = am+1;
+                m  = M[d];
+                b  = T[d]; 
+                ha = HA[d];
+              }
+          else
+            if (ac < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+              }
+            else
+              { c  = ac+2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip < k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y += 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k >= NA[k])
+            { if (cells[ha].mark < NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] += TRACE_SPACE;
+            }
+
+          if (c > besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+
+          a -= 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta-besty] != 4)
+            more = 1;
+          if (hgh >= aclip)
+            { hgh = aclip-1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                }
+            }
+          if (low <= bclip)
+            { low = bclip+1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                }
+            }
+          aclip =  INT32_MAX;
+          bclip = -INT32_MAX;
+        }
+
+      n = besta - WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] < n)
+          hgh -= 1;                               
+        else
+          { while (V[low] < n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    int     atlen;
+    int     trimx;
+    int     a, b, k, h;
+    int     d, e;
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = (mida-k)/2;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark - k;
+        d = cells[h].diff;
+        atrace[atlen++] = (uint16) (d-e);
+        atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,a-b); fflush(stdout);
+#endif
+        b = a;
+        e = d;
+      }
+    if (b+k != trimx)
+      { atrace[atlen++] = (uint16) (trimd-e);
+        atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimy)
+      { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+        atrace[atlen-2] = (uint16) (atrace[atlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+
+    apath->aepos = trimx;
+    apath->bepos = trimy;
+    apath->diffs = trimd;
+    apath->tlen  = atlen;
+  }
+
+  return (0);
+}
+
+static int reverse_extend(_Work_Data *work, _Align_Spec *spec, Alignment *align,
+                          int midd, int mida, int minp, int maxp)
+{ char *aseq  = align->aseq - 1;
+  char *bseq  = align->bseq - 1;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     vlen, vmin, vmax;
+  int    *V, *M;
+  int    *_V, *_M;
+  BVEC   *T;
+  BVEC   *_T;
+
+  int    *HA, *NA;
+  int    *_HA, *_NA;
+  Pebble *cells;
+  int     avail, cmax;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha;
+  int     morea, morey, mored;
+  int     moreha;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  hgh = midd;
+  low = midd;
+  dif = 0;
+
+  { int span, wing;
+
+    span = (hgh-low)+1;
+    vlen = work->vecmax/VectorEn;
+    wing = (vlen - span)/2;
+    vmin = low - wing;
+    vmax = hgh + wing;
+
+    _V  = ((int *) work->vector);
+    _M  = _V + vlen;
+    _HA = _M + vlen;
+    _NA = _HA + vlen;
+    _T  = ((BVEC *) (_NA + vlen));
+
+    V  = _V-vmin;
+    M  = _M-vmin;
+    HA = _HA-vmin;
+    NA = _NA-vmin;
+    T  = _T-vmin;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+  }
+
+  more  = 1;
+  aclip = -INT32_MAX;
+  bclip =  INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a = aseq + low;
+    for (k = low; k <= hgh; k++)
+      { int     y, c, d;
+        int     ha, na;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              EXIT(1);
+            work->celmax = cmax;
+            work->cells  = (void *) cells;
+          }
+
+        na = ((y+k+TRACE_SPACE-1)/TRACE_SPACE-1)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: -1,%d,0,%d\n",avail,k,na+TRACE_SPACE); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+        pb->diff = 0;
+        pb->mark = y+k;
+        ha  = avail++;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip > k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y -= 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k <= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  EXIT(1);
+                work->celmax = cmax;
+                work->cells  = (void *) cells;
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+            pb->diff = 0;
+            pb->mark = na;
+            ha  = avail++;
+            na -= TRACE_SPACE;
+          }
+
+        if (c < besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        NA[k] = na;
+
+        a += 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (low <= aclip)
+        { low = aclip+1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+            }
+        }
+      if (hgh >= bclip)
+        { hgh = bclip-1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+            }
+        }
+      aclip = -INT32_MAX;
+      bclip =  INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nREVERSE WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  while (more && lasta <= besta + TRIM_MLAG)
+    { int    k, n;
+      int    ua;
+      BVEC   t;
+      int    am, ac, ap;
+      char  *a;
+
+      if (low <= vmin || hgh >= vmax)
+        { int   span, wing;
+          int64 move, vd, md, had, nad, td;
+
+          span = (hgh-low)+1;
+          if (.8*vlen < span)
+            { if (enlarge_vector(work,vlen*VectorEn))
+                EXIT(1);
+
+              move = ((void *) _V) - work->vector;
+              vlen = work->vecmax/VectorEn;
+
+              _V  = (int *) work->vector;
+              _M  = _V + vlen;
+              _HA = _M + vlen;
+              _NA = _HA + vlen;
+              _T  = ((BVEC *) (_NA + vlen));
+            }
+          else
+            move = 0;
+
+          wing = (vlen - span)/2;
+
+          vd  = ((void *) ( _V+wing)) - (((void *) ( V+low)) - move);
+          md  = ((void *) ( _M+wing)) - (((void *) ( M+low)) - move);
+          had = ((void *) (_HA+wing)) - (((void *) (HA+low)) - move);
+          nad = ((void *) (_NA+wing)) - (((void *) (NA+low)) - move);
+          td  = ((void *) ( _T+wing)) - (((void *) ( T+low)) - move);
+
+          if (vd < 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+          if (md < 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (had < 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (nad < 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (td < 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+
+          if (td > 0)
+            memmove( _T+wing,  ((void *) ( T+low)) - move, span*sizeof(BVEC));
+          if (nad > 0)
+            memmove(_NA+wing,  ((void *) (NA+low)) - move, span*sizeof(int));
+          if (had > 0)
+            memmove(_HA+wing,  ((void *) (HA+low)) - move, span*sizeof(int));
+          if (md > 0)
+            memmove( _M+wing,  ((void *) ( M+low)) - move, span*sizeof(int));
+          if (vd > 0)
+            memmove( _V+wing,  ((void *) ( V+low)) - move, span*sizeof(int));
+
+          vmin = low-wing;
+          vmax = hgh+wing;
+
+          V  =  _V-vmin;
+          M  =  _M-vmin;
+          HA = _HA-vmin;
+          NA = _NA-vmin;
+          T  =  _T-vmin;
+        }
+
+      if (low > minp)
+        { low -= 1;
+          NA[low] = NA[low+1];
+          V[low]  = ap = INT32_MAX;
+        }
+      else
+        ap = V[low]; 
+      if (hgh < maxp)
+        { hgh += 1;
+          NA[hgh] = NA[hgh-1];
+          V[hgh] = INT32_MAX;
+        }
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = INT32_MAX;
+      a  = aseq + low;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = -1;
+      for (k = low; k <= hgh; k++)
+        { int     y, m;
+          int     ha;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          am = ac;
+          ac = ap;
+          ap = V[d = k+1];
+
+          if (ac > ap)
+            if (ap > am)
+              { c = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+              }
+            else
+              { c  = ap-1;
+                m  = M[d];
+                b  = T[d];
+                ha = HA[d];
+              }
+          else
+            if (ac > am)
+              { c  = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+              }
+            else
+              { c  = ac-2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip > k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y -= 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k <= NA[k])
+            { if (cells[ha].mark > NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        EXIT(1);
+                      work->celmax = cmax;
+                      work->cells  = (void *) cells;
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+                  pb->diff = dif;
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] -= TRACE_SPACE;
+            }
+
+          if (c < besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+
+          a += 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+            more = 1;
+          if (low <= aclip)
+            { low = aclip+1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                }
+            }
+          if (hgh >= bclip)
+            { hgh = bclip-1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                }
+            }
+          aclip = -INT32_MAX;
+          bclip =  INT32_MAX;
+        }
+
+      n = besta + WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] > n)
+          hgh -= 1;                               
+        else
+          { while (V[low] > n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    int     atlen;
+    int     trimx;
+    int     a, b, k, h;
+    int     d, e;
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+    k = cells[h].diag;
+    b = cells[h].mark - k;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+    if ((b+k)%TRACE_SPACE != 0)
+      { h = cells[h].ptr;
+        if (h < 0)
+          { a = trimy;
+            d = trimd;
+          }
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            d = cells[h].diff;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+        atrace[--atlen] = (uint16) (b-a);
+        atrace[--atlen] = (uint16) (d-e);
+        b = a;
+        e = d;
+      }
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            atrace[--atlen] = (uint16) (b-a);
+            d = cells[h].diff;
+            atrace[--atlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+            b = a;
+            e = d;
+          }
+        if (b+k != trimx)
+          { atrace[--atlen] = (uint16) (b-trimy);
+            atrace[--atlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+        else if (b != trimy)
+          { atrace[atlen+1] = (uint16) (atrace[atlen+1] + (b-trimy));
+            atrace[atlen]   = (uint16) (atrace[atlen]   + (trimd-e));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+      }
+
+    apath->abpos = trimx;
+    apath->bbpos = trimy;
+    apath->diffs = trimd;
+    apath->tlen  = - atlen;
+    apath->trace = atrace + atlen;
+  }
+
+  return (0);
+}
+
+
+/* Find the longest local alignment between aseq and bseq through (xcnt,ycnt)
+   See associated .h file for the precise definition of the interface.
+*/
+
+int Find_Extension(Alignment *align, Work_Data *ework, Align_Spec *espec,
+                   int diag, int anti, int lbord, int hbord, int prefix)
+{ _Work_Data  *work = ( _Work_Data *) ework;
+  _Align_Spec *spec = (_Align_Spec *) espec;
+
+  Path *apath;
+  int   minp, maxp;
+
+  { int alen, blen;
+    int maxtp, wsize;
+
+    alen = align->alen;
+    blen = align->blen;
+
+    wsize = VectorEn*10000;
+    if (wsize >= work->vecmax)
+      if (enlarge_vector(work,wsize))
+        EXIT(1);
+
+    if (alen < blen)
+      maxtp = 2*(blen/spec->trace_space+2);
+    else
+      maxtp = 2*(alen/spec->trace_space+2);
+    wsize = 2*maxtp*sizeof(uint16);
+    if (wsize > work->pntmax)
+      if (enlarge_points(work,wsize))
+        EXIT(1);
+
+    apath = align->path;
+    apath->trace = ((uint16 *) work->points) + maxtp;
+  }
+
+#ifdef DEBUG_PASSES
+  printf("\n");
+#endif
+
+  if (lbord < 0)
+    minp = -INT32_MAX;
+  else
+    minp = diag-lbord;
+  if (hbord < 0)
+    maxp = INT32_MAX;
+  else
+    maxp = diag+hbord;
+
+  if (prefix)
+    { if (reverse_extend(work,spec,align,diag,anti,minp,maxp))
+        EXIT(1);
+      apath->aepos = (anti-diag)/2;
+      apath->bepos = (anti+diag)/2;
+#ifdef DEBUG_PASSES
+      printf("E1 (%d,%d) => (%d,%d) %d\n",
+             (anti+diag)/2,(anti-diag)/2,apath->abpos,apath->bbpos,apath->diffs);
+#endif
+    }
+  else
+    { if (forward_extend(work,spec,align,diag,anti,minp,maxp))
+        EXIT(1);
+      apath->abpos = (anti-diag)/2;
+      apath->bbpos = (anti+diag)/2;
+#ifdef DEBUG_PASSES
+      printf("F1 (%d,%d) => (%d,%d) %d\n",
+             (anti+diag)/2,(anti-diag)/2,apath->aepos,apath->bepos,apath->diffs);
+#endif
+     }
+
+#ifdef DEBUG_POINTS
+  { uint16 *trace = (uint16 *) apath->trace;
+    int     a, h;
+
+    printf("\nA-path (%d,%d)->(%d,%d)",apath->abpos,apath->bbpos,apath->aepos,apath->bepos);
+    printf(" %c\n",(COMP(align->flags) ? 'c' : 'n'));
+    a = apath->bbpos;
+    for (h = 1; h < apath->tlen; h += 2)
+      { int dif = trace[h-1];
+        int del = trace[h];
+        a += del;
+        printf("      %d / %d (%d)\n",dif,del,a);
+      }
+  }
+#endif
+
+  return (0);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  OVERLAP MANIPULATION                                                                  *
+*                                                                                        *
+\****************************************************************************************/
+
+static int64 PtrSize   = sizeof(void *);
+static int64 OvlIOSize = sizeof(Overlap) - sizeof(void *);
+
+int Read_Overlap(FILE *input, Overlap *ovl)
+{ if (fread( ((char *) ovl) + PtrSize, OvlIOSize, 1, input) != 1)
+    return (1);
+  return (0);
+}
+
+int Read_Trace(FILE *input, Overlap *ovl, int tbytes)
+{ if (tbytes > 0 && ovl->path.tlen > 0)
+    { if (fread(ovl->path.trace, tbytes*ovl->path.tlen, 1, input) != 1)
+        return (1);
+    }
+  return (0);
+}
+
+void Write_Overlap(FILE *output, Overlap *ovl, int tbytes)
+{ fwrite( ((char *) ovl) + PtrSize, OvlIOSize, 1, output);
+  if (ovl->path.trace != NULL)
+    fwrite(ovl->path.trace,tbytes,ovl->path.tlen,output);
+}
+
+void Compress_TraceTo8(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+  uint8  *t8  = (uint8  *) ovl->path.trace;
+  int     j;
+
+  for (j = 0; j < ovl->path.tlen; j++)
+    t8[j] = (uint8) (t16[j]);
+}
+
+void Decompress_TraceTo16(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+  uint8  *t8  = (uint8  *) ovl->path.trace;
+  int     j;
+
+  for (j = ovl->path.tlen-1; j >= 0; j--)
+    t16[j] = t8[j];
+}
+
+void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent)
+{ int     i;
+
+  fprintf(output,"%*s%d vs. ",indent,"",ovl->aread);
+  if (COMP(ovl->flags))
+    fprintf(output,"c(%d)\n",ovl->bread);
+  else
+    fprintf(output,"%d\n",ovl->bread);
+  fprintf(output,"%*s  [%d,%d] vs [%d,%d] w. %d diffs\n",indent,"",
+                 ovl->path.abpos,ovl->path.aepos,ovl->path.bbpos,ovl->path.bepos,ovl->path.diffs);
+
+  if (tbytes == 1)
+    { uint8 *trace = (uint8 *) (ovl->path.trace);
+      if (trace != NULL)
+        { int p = ovl->path.bbpos + trace[1];
+          fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+          for (i = 3; i < ovl->path.tlen; i += 2)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %3d/%5d",trace[i-1],p);
+            }
+          fprintf(output,"\n");
+        }
+    }
+  else
+    { uint16 *trace = (uint16 *) (ovl->path.trace);
+      if (trace != NULL)
+        { int p = ovl->path.bbpos + trace[1];
+          fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+          for (i = 3; i < ovl->path.tlen; i += 2)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %3d/%5d",trace[i-1],p);
+            }
+          fprintf(output,"\n");
+        }
+    }
+}
+
+int Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname)
+{ int     i, p; 
+
+  if (((ovl->path.aepos-1)/tspace - ovl->path.abpos/tspace)*2 != ovl->path.tlen-2)
+    { if (verbose) 
+        EPRINTF(EPLACE,"  %s: Wrong number of trace points\n",fname);
+      return (1);
+    }         
+  p = ovl->path.bbpos;
+  if (tspace <= TRACE_XOVR)
+    { uint8 *trace8 = (uint8 *) ovl->path.trace;
+      for (i = 1; i < ovl->path.tlen; i += 2)
+        p += trace8[i];
+    }
+  else      
+    { uint16 *trace16 = (uint16 *) ovl->path.trace;
+      for (i = 1; i < ovl->path.tlen; i += 2)
+        p += trace16[i];
+    }
+  if (p != ovl->path.bepos)
+    { if (verbose)
+        EPRINTF(EPLACE,"  %s: Trace point sum != aligned interval\n",fname);
+      return (1); 
+    }         
+  return (0);
+}
+
+
+void Flip_Alignment(Alignment *align, int full)
+{ char *aseq  = align->aseq;
+  char *bseq  = align->bseq;
+  int   alen  = align->alen;
+  int   blen  = align->blen;
+  Path *path  = align->path;
+  int   comp  = COMP(align->flags);
+
+  int  *trace = (int *) path->trace;
+  int   tlen  = path->tlen;
+
+  int   i, j, p;
+
+  if (comp)
+    { p = path->abpos;
+      path->abpos = blen - path->bepos;
+      path->bepos = alen - p;
+      p = path->aepos;
+      path->aepos = blen - path->bbpos;
+      path->bbpos = alen - p;
+
+      if (full)
+        { alen += 2;
+          blen += 2;
+
+          for (i = 0; i < tlen; i++)
+            if ((p = trace[i]) < 0)
+              trace[i] = alen + p;
+            else
+              trace[i] = p - blen;
+
+          i = tlen-1;
+          j = 0;
+          while (j < i)
+            { p = trace[i];
+              trace[i] = trace[j];
+              trace[j] = p;
+              i -= 1;
+              j += 1;
+            }
+
+          alen -= 2;
+          blen -= 2;
+        }
+    }
+  else
+    { p = path->abpos;
+      path->abpos = path->bbpos;
+      path->bbpos = p;
+      p = path->aepos;
+      path->aepos = path->bepos;
+      path->bepos = p;
+
+      if (full)
+        for (i = 0; i < tlen; i++)
+          trace[i] = - (trace[i]);
+    }
+
+  align->aseq  = bseq;
+  align->bseq  = aseq;
+  align->alen  = blen;
+  align->blen  = alen;
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  ALIGNMENT PRINTING                                                                    *
+*                                                                                        *
+\****************************************************************************************/
+
+/* Complement the sequence in fragment aseq.  The operation does the
+   complementation/reversal in place.  Calling it a second time on a
+   given fragment restores it to its original state.                */
+
+void Complement_Seq(char *aseq, int len)
+{ char *s, *t;
+  int   c;
+
+  s = aseq;
+  t = aseq + (len-1);
+  while (s < t)
+    { c    = 3 - *s;
+      *s++ = (char) (3 - *t);
+      *t-- = (char) c;
+    }
+  if (s == t)
+    *s = (char) (3 - *s);
+}
+
+
+/* Print an alignment to file between a and b given in trace (unpacked).
+   Prefix gives the length of the initial prefix of a that is unaligned.  */
+
+static char ToL[8] = { 'a', 'c', 'g', 't', '.', '[', ']', '-' };
+static char ToU[8] = { 'A', 'C', 'G', 'T', '.', '[', ']', '-' };
+
+int Print_Alignment(FILE *file, Alignment *align, Work_Data *ework,
+                    int indent, int width, int border, int upper, int coord)
+{ _Work_Data *work  = (_Work_Data *) ework;
+  int        *trace = align->path->trace;
+  int         tlen  = align->path->tlen;
+
+  char *Abuf, *Bbuf, *Dbuf;
+  int   i, j, o;
+  char *a, *b;
+  char  mtag, dtag;
+  int   prefa, prefb;
+  int   aend, bend;
+  int   comp, blen;
+  int   sa, sb;
+  int   match, diff;
+  char *N2A;
+
+  if (trace == NULL) return (0);
+
+#ifdef SHOW_TRACE
+  fprintf(file,"\nTrace:\n");
+  for (i = 0; i < tlen; i++)
+    fprintf(file,"  %3d\n",trace[i]);
+#endif
+
+  o = sizeof(char)*3*(width+1);
+  if (o > work->vecmax)
+    if (enlarge_vector(work,o))
+      EXIT(1);
+
+  if (upper)
+    N2A = ToU;
+  else
+    N2A = ToL;
+
+  Abuf = (char *) work->vector;
+  Bbuf = Abuf + (width+1);
+  Dbuf = Bbuf + (width+1);
+
+  aend = align->path->aepos;
+  bend = align->path->bepos;
+
+  comp = COMP(align->flags);
+  blen = align->blen;
+
+  Abuf[width] = Bbuf[width] = Dbuf[width] = '\0';
+                                           /* buffer/output next column */
+#define COLUMN(x,y)							\
+{ int u, v;								\
+  if (o >= width)							\
+    { fprintf(file,"\n");						\
+      fprintf(file,"%*s",indent,"");					\
+      if (coord > 0)							\
+        { if (sa < aend)						\
+            fprintf(file," %*d",coord,sa);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %s\n",Abuf);					\
+          fprintf(file,"%*s %*s %s\n",indent,"",coord,"",Dbuf);		\
+          fprintf(file,"%*s",indent,"");				\
+          if (sb < bend)						\
+            if (comp)							\
+              fprintf(file," %*d",coord,blen-sb);			\
+            else							\
+              fprintf(file," %*d",coord,sb);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %s",Bbuf);					\
+        }								\
+      else								\
+        { fprintf(file," %s\n",Abuf);					\
+          fprintf(file,"%*s %s\n",indent,"",Dbuf);			\
+          fprintf(file,"%*s %s",indent,"",Bbuf);			\
+        }								\
+      fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));		\
+      o  = 0;								\
+      sa = i-1;								\
+      sb = j-1;								\
+      match = diff = 0;							\
+    }									\
+  u = (x);								\
+  v = (y);								\
+  if (u == 4 || v == 4)							\
+    Dbuf[o] = ' ';							\
+  else if (u == v)							\
+    Dbuf[o] = mtag;							\
+  else									\
+    Dbuf[o] = dtag;							\
+  Abuf[o] = N2A[u];							\
+  Bbuf[o] = N2A[v];							\
+  o += 1;								\
+}
+
+  a = align->aseq - 1;
+  b = align->bseq - 1;
+
+  o  = 0;
+  i = j = 1;
+
+  prefa = align->path->abpos;
+  prefb = align->path->bbpos;
+
+  if (prefa > border)
+    { i = prefa-(border-1);
+      prefa = border;
+    }
+  if (prefb > border)
+    { j = prefb-(border-1);
+      prefb = border;
+    }
+
+  sa   = i-1;
+  sb   = j-1;
+  mtag = ':';
+  dtag = ':';
+
+  while (prefa > prefb)
+    { COLUMN(a[i],4)
+      i += 1;
+      prefa -= 1;
+    }
+  while (prefb > prefa)
+    { COLUMN(4,b[j])
+      j += 1;
+      prefb -= 1;
+    }
+  while (prefa > 0)
+    { COLUMN(a[i],b[j])
+      i += 1;
+      j += 1;
+      prefa -= 1;
+    }
+
+  mtag = '[';
+  if (prefb > 0)
+    COLUMN(5,5)
+
+  mtag  = '|';
+  dtag  = '*';
+
+  match = diff = 0;
+
+  { int p, c;      /* Output columns of alignment til reach trace end */
+
+    for (c = 0; c < tlen; c++)
+      if ((p = trace[c]) < 0)
+        { p = -p;
+          while (i != p)
+            { COLUMN(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          COLUMN(7,b[j])
+          j += 1;
+          diff += 1;
+        }
+      else
+        { while (j != p)
+            { COLUMN(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          COLUMN(a[i],7)
+          i += 1;
+          diff += 1;
+        }
+    p = align->path->aepos;
+    while (i <= p)
+      { COLUMN(a[i],b[j])
+        if (a[i] == b[j])
+          match += 1;
+        else
+          diff += 1;
+        i += 1;
+        j += 1;
+      }
+  }
+
+  { int c;     /* Output remaining column including unaligned suffix */
+
+    mtag = ']';
+    if (a[i] != 4 && b[j] != 4 && border > 0)
+      COLUMN(6,6)
+
+    mtag = ':';
+    dtag = ':';
+
+    c = 0;
+    while (c < border && (a[i] != 4 || b[j] != 4))
+      { if (a[i] != 4)
+          if (b[j] != 4)
+            { COLUMN(a[i],b[j])
+              i += 1;
+              j += 1;
+            }
+          else
+            { COLUMN(a[i],4)
+              i += 1;
+            }
+        else
+          { COLUMN(4,b[j])
+            j += 1;
+          }
+        c += 1;
+      }
+  }
+
+  /* Print remainder of buffered col.s */
+
+  fprintf(file,"\n");
+  fprintf(file,"%*s",indent,"");
+  if (coord > 0)
+    { if (sa < aend)
+        fprintf(file," %*d",coord,sa);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+      fprintf(file,"%*s",indent,"");
+      if (sb < bend)
+        if (comp)
+          fprintf(file," %*d",coord,blen-sb);
+        else
+          fprintf(file," %*d",coord,sb);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s",o,Bbuf);
+    }
+  else
+    { fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+      fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+    }
+  if (diff+match > 0)
+    fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+  else
+    fprintf(file,"\n");
+
+  fflush(file);
+  return (0);
+}
+
+int Print_Reference(FILE *file, Alignment *align, Work_Data *ework,
+                    int indent, int block, int border, int upper, int coord)
+{ _Work_Data *work  = (_Work_Data *) ework;
+  int        *trace = align->path->trace;
+  int         tlen  = align->path->tlen;
+
+  char *Abuf, *Bbuf, *Dbuf;
+  int   i, j, o;
+  char *a, *b;
+  char  mtag, dtag;
+  int   prefa, prefb;
+  int   aend, bend;
+  int   comp, blen;
+  int   sa, sb, s0;
+  int   match, diff;
+  char *N2A;
+  int   vmax;
+
+  if (trace == NULL) return (0);
+
+#ifdef SHOW_TRACE
+  fprintf(file,"\nTrace:\n");
+  for (i = 0; i < tlen; i++)
+    fprintf(file,"  %3d\n",trace[i]);
+#endif
+
+  vmax = work->vecmax/3;
+  o = sizeof(char)*6*(block+1);
+  if (o > vmax)
+    { if (enlarge_vector(work,3*o))
+        EXIT(1);
+      vmax = work->vecmax/3;
+    }
+
+  Abuf = (char *) work->vector;
+  Bbuf = Abuf + vmax;
+  Dbuf = Bbuf + vmax;
+
+  if (upper)
+    N2A = ToU;
+  else
+    N2A = ToL;
+
+  aend = align->path->aepos;
+  bend = align->path->bepos;
+
+  comp = COMP(align->flags);
+  blen = align->blen;
+
+#define BLOCK(x,y)							\
+{ int u, v;								\
+  if (i%block == 1 && i != s0 && x < 4 && o > 0)			\
+    { fprintf(file,"\n");						\
+      fprintf(file,"%*s",indent,"");					\
+      if (coord > 0)							\
+        { if (sa < aend)						\
+            fprintf(file," %*d",coord,sa);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %.*s\n",o,Abuf);				\
+          fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);	\
+          fprintf(file,"%*s",indent,"");				\
+          if (sb < bend)						\
+            if (comp)							\
+              fprintf(file," %*d",coord,blen-sb);			\
+            else							\
+              fprintf(file," %*d",coord,sb);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %.*s",o,Bbuf);					\
+        }								\
+      else								\
+        { fprintf(file," %.*s\n",o,Abuf);				\
+          fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);			\
+          fprintf(file,"%*s %.*s",indent,"",o,Bbuf);			\
+        }								\
+      fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));		\
+      o  = 0;								\
+      sa = i-1;								\
+      sb = j-1;								\
+      match = diff = 0;							\
+    }									\
+  u = (x);								\
+  v = (y);								\
+  if (u == 4 || v == 4)							\
+    Dbuf[o] = ' ';							\
+  else if (u == v)							\
+    Dbuf[o] = mtag;							\
+  else									\
+    Dbuf[o] = dtag;							\
+  Abuf[o] = N2A[u];							\
+  Bbuf[o] = N2A[v];							\
+  o += 1;								\
+  if (o >= vmax)							\
+    { if (enlarge_vector(work,3*o))					\
+        EXIT(1);							\
+      vmax = work->vecmax/3;						\
+      memmove(work->vector+2*vmax,Dbuf,o);				\
+      memmove(work->vector+vmax,Bbuf,o);				\
+      memmove(work->vector,Abuf,o);					\
+      Abuf = (char *) work->vector;					\
+      Bbuf = Abuf + vmax;						\
+      Dbuf = Bbuf + vmax;						\
+    }									\
+}
+
+  a = align->aseq - 1;
+  b = align->bseq - 1;
+
+  o  = 0;
+  i = j = 1;
+
+  prefa = align->path->abpos;
+  prefb = align->path->bbpos;
+
+  if (prefa > border)
+    { i = prefa-(border-1);
+      prefa = border;
+    }
+  if (prefb > border)
+    { j = prefb-(border-1);
+      prefb = border;
+    }
+
+  s0   = i;
+  sa   = i-1;
+  sb   = j-1;
+  mtag = ':';
+  dtag = ':';
+
+  while (prefa > prefb)
+    { BLOCK(a[i],4)
+      i += 1;
+      prefa -= 1;
+    }
+  while (prefb > prefa)
+    { BLOCK(4,b[j])
+      j += 1;
+      prefb -= 1;
+    }
+  while (prefa > 0)
+    { BLOCK(a[i],b[j])
+      i += 1;
+      j += 1;
+      prefa -= 1;
+    }
+
+  mtag = '[';
+  if (prefb > 0)
+    BLOCK(5,5)
+
+  mtag  = '|';
+  dtag  = '*';
+
+  match = diff = 0;
+
+  { int p, c;      /* Output columns of alignment til reach trace end */
+
+    for (c = 0; c < tlen; c++)
+      if ((p = trace[c]) < 0)
+        { p = -p;
+          while (i != p)
+            { BLOCK(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          BLOCK(7,b[j])
+          j += 1;
+          diff += 1;
+        }
+      else
+        { while (j != p)
+            { BLOCK(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          BLOCK(a[i],7)
+          i += 1;
+          diff += 1;
+        }
+    p = align->path->aepos;
+    while (i <= p)
+      { BLOCK(a[i],b[j])
+        if (a[i] == b[j])
+		match += 1;
+	else
+          diff += 1;
+        i += 1;
+        j += 1;
+      }
+  }
+
+  { int c;     /* Output remaining column including unaligned suffix */
+
+    mtag = ']';
+    if (a[i] != 4 && b[j] != 4 && border > 0)
+      BLOCK(6,6)
+
+    mtag = ':';
+    dtag = ':';
+
+    c = 0;
+    while (c < border && (a[i] != 4 || b[j] != 4))
+      { if (a[i] != 4)
+          if (b[j] != 4)
+            { BLOCK(a[i],b[j])
+              i += 1;
+              j += 1;
+            }
+          else
+            { BLOCK(a[i],4)
+              i += 1;
+            }
+        else
+          { BLOCK(4,b[j])
+            j += 1;
+          }
+        c += 1;
+      }
+  }
+
+  /* Print remainder of buffered col.s */
+
+  fprintf(file,"\n");
+  fprintf(file,"%*s",indent,"");
+  if (coord > 0)
+    { if (sa < aend)
+        fprintf(file," %*d",coord,sa);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+      fprintf(file,"%*s",indent,"");
+      if (sb < bend)
+        if (comp)
+          fprintf(file," %*d",coord,blen-sb);
+        else
+          fprintf(file," %*d",coord,sb);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s",o,Bbuf);
+    }
+  else
+    { fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+      fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+    }
+  if (diff+match > 0)
+    fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+  else
+    fprintf(file,"\n");
+
+  fflush(file);
+  return (0);
+}
+
+/* Print an ASCII representation of the overlap in align between fragments
+   a and b to given file.                                                  */
+
+static inline void repchar(FILE *file, int symbol, int rep)
+{ while (rep-- > 0)
+    fputc(symbol,file);
+}
+
+void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord)
+{ int   alen = align->alen;
+  int   blen = align->blen;
+  Path *path = align->path;
+  int   comp = COMP(align->flags);
+  int   w;
+
+  fprintf(file,"%*s",indent,"");
+  if (path->abpos > 0)
+    fprintf(file,"    %*d ",coord,path->abpos);
+  else
+    fprintf(file,"%*s",coord+5,"");
+  if (path->aepos < alen)
+    fprintf(file,"%*s%d",coord+8,"",alen-path->aepos);
+  fprintf(file,"\n");
+
+  fprintf(file,"%*s",indent,"");
+  if (path->abpos > 0)
+    { fprintf(file,"A ");
+      w = Number_Digits((int64) path->abpos);
+      repchar(file,' ',coord-w);
+      repchar(file,'=',w+3);
+      fputc('+',file);
+      repchar(file,'-',coord+5);
+    }
+  else
+    { fprintf(file,"A %*s",coord+4,"");
+      repchar(file,'-',coord+5);
+    }
+
+  if (path->aepos < alen)
+    { fputc('+',file);
+      w = Number_Digits((int64) (alen-path->aepos));
+      repchar(file,'=',w+2);
+      fputc('>',file);
+      repchar(file,' ',w);
+    }
+  else
+    { fputc('>',file);
+      repchar(file,' ',coord+3);
+    }
+
+  { int asub, bsub;
+
+    asub = path->aepos - path->abpos;
+    bsub = path->bepos - path->bbpos;
+    fprintf(file,"   dif/(len1+len2) = %d/(%d+%d) = %5.2f%%\n",
+                 path->diffs,asub,bsub,(200.*path->diffs)/(asub+bsub));
+  }
+
+  { int   sym1e, sym2e;
+    int   sym1p, sym2p;
+
+    if (comp > 0)
+      { sym1p = '<'; sym2p = '-'; sym1e = '<'; sym2e = '='; }
+    else
+      { sym1p = '-'; sym2p = '>'; sym1e = '='; sym2e = '>'; }
+
+    fprintf(file,"%*s",indent,"");
+    if (path->bbpos > 0)
+      { fprintf(file,"B ");
+        w = Number_Digits((int64) path->bbpos);
+        repchar(file,' ',coord-w);
+        fputc(sym1e,file);
+        repchar(file,'=',w+2);
+        fputc('+',file);
+        repchar(file,'-',coord+5);
+      }
+    else
+      { fprintf(file,"B ");
+        repchar(file,' ',coord+3);
+        fputc(sym1p,file);
+        repchar(file,'-',coord+5);
+      }
+    if (path->bepos < blen)
+      { fprintf(file,"+");
+        w = Number_Digits((int64) (blen-path->bepos));
+        repchar(file,'=',w+2);
+        fprintf(file,"%c\n",sym2e);
+      }
+    else
+      fprintf(file,"%c\n",sym2p);
+  }
+
+  fprintf(file,"%*s",indent,"");
+  if (path->bbpos > 0)
+    fprintf(file,"    %*d ",coord,path->bbpos);
+  else
+    fprintf(file,"%*s",coord+5,"");
+  if (path->bepos < blen)
+    fprintf(file,"%*s%d",coord+8,"",blen-path->bepos);
+  fprintf(file,"\n");
+
+  fflush(file);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  O(ND) trace algorithm                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+
+#ifdef DEBUG_AWAVE
+
+static void print_awave(int *V, int low, int hgh)
+{ int k;
+
+  printf("  [%6d,%6d]: ",low,hgh);
+  for (k = low; k <= hgh; k++)
+    printf(" %3d",V[k]);
+  printf("\n");
+  fflush(stdout);
+}
+
+#endif
+
+#ifdef DEBUG_ALIGN
+
+static int depth = 0;
+
+#endif
+
+typedef struct
+  { int  *Stop;          //  Ongoing stack of alignment indels
+    char *Aabs, *Babs;   //  Absolute base of A and B sequences
+
+    int  **PVF, **PHF;   //  List of waves for iterative np algorithms
+    int   mida,  midb;   //  mid point division for mid-point algorithms
+
+    int   *VF,   *VB;    //  Forward/Reverse waves for nd algorithms
+                         //  (defunct: were used for O(nd) algorithms)
+  } Trace_Waves;
+
+static int dandc_nd(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int x, y;
+  int D;
+
+#ifdef DEBUG_ALIGN
+  printf("%*s %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+#endif
+
+  if (M <= 0)
+    { x = (wave->Aabs-A)-1;
+      for (y = 1; y <= N; y++)
+        { *wave->Stop++ = x;
+#ifdef DEBUG_SCRIPT
+          printf("%*s *I %ld(%ld)\n",depth,"",y+(B-wave->Babs),(A-wave->Aabs)+1);
+#endif
+        }
+      return (N);
+    }
+  if (N <= 0)
+    { y = (B-wave->Babs)+1;
+      for (x = 1; x <= M; x++)
+        { *wave->Stop++ = y;
+#ifdef DEBUG_SCRIPT
+          printf("%*s *D %ld(%ld)\n",depth,"",x+(A-wave->Aabs),(B-wave->Babs)+1);
+#endif
+        }
+      return (M);
+    }
+
+  { int  *VF = wave->VF;
+    int  *VB = wave->VB;
+    int   flow;  //  fhgh == D !
+    int   blow, bhgh;
+    char *a;
+
+    y = 0;
+    if (N < M)
+      while (y < N && B[y] == A[y])
+        y += 1;
+    else
+      { while (y < M && B[y] == A[y])
+          y += 1;
+        if (y >= M && N == M)
+          return (0);
+      }
+
+    flow   = 0;
+    VF[0]  = y;
+    VF[-1] = -2;
+
+    x = N-M;
+    a = A-x;
+    y = N-1;
+    if (N > M)
+      while (y >= x && B[y] == a[y])
+        y -= 1;
+    else
+      while (y >= 0 && B[y] == a[y])
+        y -= 1;
+
+    blow = bhgh = -x;
+    VB += x;
+    VB[blow]   = y;
+    VB[blow-1] = N+1;
+
+    for (D = 1; 1; D += 1)
+      { int   k, r;
+        int   am, ac, ap;
+
+        //  Forward wave
+
+        flow -= 1;
+        am = ac = VF[flow-1] = -2;
+
+        a = A + D;
+        x = M - D;
+        for (k = D; k >= flow; k--)
+          { ap = ac;
+            ac = am+1;
+            am = VF[k-1];
+
+            if (ac < am)
+              if (ap < am)
+                y  = am;
+              else
+                y = ap;
+            else
+              if (ap < ac)
+                y = ac;
+              else
+                y = ap;
+
+            if (blow <= k && k <= bhgh)
+              { r = VB[k];
+                if (y > r)
+                  { D = (D<<1)-1;
+                    if (ap > r)
+                      y = ap;
+                    else if (ac > r)
+                      y = ac;
+                    else
+                      y = r+1;
+                    x = k+y;
+                    goto OVERLAP2;
+                  }
+              }
+
+            if (N < x)
+              while (y < N && B[y] == a[y])
+                y += 1;
+            else
+              while (y < x && B[y] == a[y])
+                y += 1;
+            
+            VF[k] = y;
+            a -= 1;
+            x += 1;
+          }
+
+#ifdef DEBUG_AWAVE
+        print_awave(VF,flow,D);
+#endif
+
+        //  Reverse Wave
+
+        bhgh += 1;
+        blow -= 1;
+	am = ac = VB[blow-1] = N+1;
+
+        a = A + bhgh;
+        x = -bhgh;
+        for (k = bhgh; k >= blow; k--)
+          { ap = ac+1;
+            ac = am;
+            am = VB[k-1];
+
+            if (ac > am)
+              if (ap > am)
+                y  = am;
+              else
+                y = ap;
+            else
+              if (ap > ac)
+                y = ac;
+              else
+                y = ap;
+
+            if (flow <= k && k <= D)
+              { r = VF[k];
+	        if (y <= r)
+                  { D = (D << 1);
+                    if (ap <= r)
+                      y = ap;
+                    else if (ac <= r)
+                      y = ac;
+                    else
+                      y = r;
+                    x = k+y;
+                    goto OVERLAP2;
+                  }
+              }
+
+            y -= 1;
+            if (x > 0)
+              while (y >= x && B[y] == a[y])
+                y -= 1;
+            else
+              while (y >= 0 && B[y] == a[y])
+                y -= 1;
+
+            VB[k] = y;
+            a -= 1;
+            x += 1;
+          }
+
+#ifdef DEBUG_AWAVE
+        print_awave(VB,blow,bhgh);
+#endif
+      }
+  }
+
+OVERLAP2:
+
+#ifdef DEBUG_ALIGN
+  printf("%*s (%d,%d) @ %d\n",depth,"",x,y,D);
+  fflush(stdout);
+#endif
+  if (D > 1)
+    { 
+#ifdef DEBUG_ALIGN
+      depth += 2;
+#endif
+      dandc_nd(A,x,B,y,wave);
+      dandc_nd(A+x,M-x,B+y,N-y,wave);
+#ifdef DEBUG_ALIGN
+      depth -= 2;
+#endif
+    }
+  else if (D == 1)
+    { if (M > N)
+        { *wave->Stop++ = (B-wave->Babs)+y+1;
+#ifdef DEBUG_SCRIPT
+          printf("%*s  D %ld(%ld)\n",depth,"",(A-wave->Aabs)+x,(B-wave->Babs)+y+1);
+#endif
+        }
+      else if (M < N)
+        { *wave->Stop++ = (wave->Aabs-A)-x-1;
+#ifdef DEBUG_SCRIPT
+          printf("%*s  I %ld(%ld)\n",depth,"",(B-wave->Babs)+y,(A-wave->Aabs)+x+1);
+#endif
+        }
+#ifdef DEBUG_SCRIPT
+      else
+        printf("%*s  %ld S %ld\n",depth,"",(wave->Aabs-A)+x,(B-wave->Babs)+y);
+#endif
+    }
+
+  return (D);
+}
+
+
+static int Compute_Trace_ND_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  int   L, D;
+  int   asub, bsub;
+  Path *path;
+  int  *trace;
+
+  path = align->path;
+  asub = path->aepos-path->abpos;
+  bsub = path->bepos-path->bbpos;
+
+  if (asub < bsub)
+    L = bsub;
+  else
+    L = asub;
+  L *= sizeof(int);
+  if (L > work->tramax)
+    if (enlarge_trace(work,L))
+      EXIT(1);
+
+  trace = wave.Stop = ((int *) work->trace);
+
+  D = 2*(path->diffs + 4)*sizeof(int);
+  if (D > work->vecmax)
+    if (enlarge_vector(work,D))
+      EXIT(1);
+  
+  D = (path->diffs+3)/2;
+  wave.VF = ((int *) work->vector) + (D+1);
+  wave.VB = wave.VF + (2*D+1);
+
+  wave.Aabs = align->aseq;
+  wave.Babs = align->bseq;
+
+  path->diffs = dandc_nd(align->aseq+path->abpos,path->aepos-path->abpos,
+                         align->bseq+path->bbpos,path->bepos-path->bbpos,&wave);
+  path->trace = trace;
+  path->tlen  = wave.Stop - trace;
+  return (0);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  O(NP) tracing algorithms                                                              *
+*                                                                                        *
+\****************************************************************************************/
+
+/* Iterative O(np) algorithm for finding the alignment between two substrings (specified
+     by a Path record).  The variation includes handling substitutions and guarantees
+     to find left-most alignments so that low complexity runs are always aligned in
+     the same way.
+*/
+
+#ifdef DEBUG_ALIGN
+
+static int ToA[4] = { 'a', 'c', 'g', 't' };
+
+#endif
+
+static int iter_np(char *A, int M, char *B, int N, Trace_Waves *wave, int mode)
+{ int  **PVF = wave->PVF; 
+  int  **PHF = wave->PHF;
+  int    D;
+  int    del = M-N;
+
+  { int  *F0, *F1, *F2;
+    int  *HF;
+    int   low, hgh;
+    int   posl, posh;
+
+#ifdef DEBUG_ALIGN
+    printf("\n    BASE %ld,%ld: %d vs %d\n",A-wave->Aabs,B-wave->Babs,M,N);
+    printf("    A = ");
+    for (D = 0; D < M; D++)
+      printf("%c",ToA[(int) A[D]]);
+    printf("\n");
+    printf("    B = ");
+    for (D = 0; D < N; D++)
+      printf("%c",ToA[(int) B[D]]);
+    printf("\n");
+#endif
+
+    if (del >= 0)
+      { low = 0;
+        hgh = del;
+      }
+    else
+      { low = del;
+        hgh = 0;
+      }
+
+    posl = -INT32_MAX;
+    posh =  INT32_MAX;
+    if (wave->Aabs == wave->Babs)
+      { if (B == A)
+          { EPRINTF(EPLACE,"Error: self comparison starts on diagonal 0 (Compute_Trace)\n");
+            EXIT(-1);
+          }
+        else if (B < A)
+          posl = (B-A)+1;
+        else
+          posh = (B-A)-1;
+      }
+
+    F1 = PVF[-2];
+    F0 = PVF[-1];
+
+    for (D = low-1; D <= hgh+1; D++)
+      F1[D] = F0[D] = -2;
+    F0[0] = -1;
+
+    low += 1;
+    hgh -= 1;
+
+    for (D = 0; 1; D += 1)
+      { int   k, i, j;
+        int   am, ac, ap;
+        char *a;
+
+        F2 = F1;
+        F1 = F0;
+        F0 = PVF[D];
+        HF = PHF[D];
+
+        if ((D & 0x1) == 0)
+          { if (low > posl)
+              low -= 1;
+            if (hgh < posh)
+              hgh += 1;
+          }
+        F0[hgh+1] = F0[low-1] = -2;
+
+#define FS_MOVE(mdir,pdir)			\
+  ac = F1[k]+1;					\
+  if (ac < am)					\
+    if (ap < am)				\
+      { HF[k] = mdir;				\
+        j = am;					\
+      }						\
+    else					\
+      { HF[k] = pdir;				\
+        j = ap;					\
+      }						\
+  else						\
+    if (ap < ac)				\
+      { HF[k] = 0;				\
+        j = ac;					\
+      }						\
+    else					\
+      { HF[k] = pdir;				\
+        j = ap;					\
+      }						\
+						\
+  if (N < i)					\
+    while (j < N && B[j] == a[j])		\
+      j += 1;					\
+  else						\
+    while (j < i && B[j] == a[j])		\
+      j += 1;					\
+  F0[k] = j;
+
+        j = -2;
+        a = A + hgh;
+        i = M - hgh;
+        for (k = hgh; k > del; k--)
+          { ap = j+1;
+            am = F2[k-1];
+            FS_MOVE(-1,4)
+            a -= 1;
+            i += 1;
+          }
+
+        j = -2;
+        a = A + low;
+        i = M - low;
+        for (k = low; k < del; k++)
+          { ap = F2[k+1]+1;
+            am = j;
+            FS_MOVE(2,1)
+            a += 1;
+            i -= 1;
+          }
+
+        ap = F0[del+1]+1;
+        am = j;
+        FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+        print_awave(F0,low,hgh);
+        print_awave(HF,low,hgh);
+#endif
+
+        if (F0[del] >= N)
+          break;
+      }
+  }
+
+  { int   k, h, m, e, c;
+    int   ap = (wave->Aabs-A)-1;
+    int   bp = (B-wave->Babs)+1;
+
+    PHF[0][0] = 3;
+
+    c = N;
+    k = del;
+    e = PHF[D][k];
+    PHF[D][k] = 3;
+
+    if (mode == UPPERMOST)
+
+      while (e != 3)
+        { h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+
+          if (h < k)       // => e = -1 or 2,  UPPERMOST
+            { char *a;
+  
+              a = A + k;
+              if (k < 0)
+                m = -k;
+              else
+                m = 0;
+              if (PVF[D][h] <= c)
+                c = PVF[D][h]-1;
+              while (c >= m && a[c] == B[c])
+                c -= 1;
+              if (e == -1)  //  => edge is 2, others are 1, and 0
+                { if (c <= PVF[D+2][k+1])
+                    { e = 4;
+                      h = k+1;
+                      D = D+2;
+                    }
+                  else if (c == PVF[D+1][k])
+                    { e = 0;
+                      h = k;
+                      D = D+1;
+                    }
+                  else
+                    PVF[D][h] = c+1;
+                }
+              else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+                { if (k == del)
+                    m = D;
+                  else
+                    m = D-2;
+                  if (c <= PVF[m][k+1])
+                    { if (k == del)
+                        e = 4;
+                      else
+                        e = 1;
+                      h = k+1;
+                      D = m;
+                    }
+                  else if (c == PVF[D-1][k])
+                    { e = 0;
+                      h = k;
+                      D = D-1;
+                    }
+                  else
+                    PVF[D][h] = c+1;
+                }
+            }
+
+          m = PHF[D][h];
+          PHF[D][h] = e;
+          e = m;
+          k = h;
+        }
+
+    else if (mode == LOWERMOST)
+
+      while (e != 3)
+        { h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+
+          if (h > k)       // => e = 1 or 4,   LOWERMOST
+            { char *a;
+  
+              a = A + k;
+              if (k < 0)
+                m = -k;
+              else
+                m = 0;
+              if (PVF[D][h] < c)
+                c = PVF[D][h];
+              while (c >= m && a[c] == B[c])
+                c -= 1;
+              if (e == 1)  //  => edge is 2, others are 1, and 0
+                { if (c < PVF[D+2][k-1])
+                    { e = 2;
+                      h = k-1;
+                      D = D+2;
+                    }
+                  else if (c == PVF[D+1][k])
+                    { e = 0;
+                      h = k;
+                      D = D+1;
+                    }
+                  else
+                    PVF[D][h] = c--;
+                }
+              else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+                { if (k == del)
+                    m = D;
+                  else
+                    m = D-2;
+                  if (c < PVF[m][k-1])
+                    { if (k == del)
+                        e = 2;
+                      else
+                        e = -1;
+                      h = k-1;
+                      D = m;
+                    }
+                  else if (c == PVF[D-1][k])
+                    { e = 0;
+                      h = k;
+                      D = D-1;
+                    }
+                  else
+                    PVF[D][h] = c--;
+                }
+            }
+
+          m = PHF[D][h];
+          PHF[D][h] = e;
+          e = m;
+          k = h;
+        }
+
+    else //  mode == GREEDIEST
+
+      while (e != 3)
+        { h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+
+          m = PHF[D][h];
+          PHF[D][h] = e;
+          e = m;
+          k = h;
+        }
+
+    k = D = 0;
+    e = PHF[D][k];
+    while (e != 3)
+      { h = k-e;
+        c = PVF[D][k];
+        if (e > 1)
+          h += 3;
+        else if (e == 0)
+          D += 1;
+        else
+          D += 2;
+#ifdef DEBUG_SCRIPT
+        if (h > k)
+          printf("     D %d(%d)\n",(c-k)-(ap-1),c+bp);
+        else if (h < k)
+          printf("     I %d(%d)\n",c+(bp-1),(c+k)-ap);
+        else
+          printf("     %d S %d\n",(c+k)-(ap+1),c+(bp-1));
+#endif
+        if (h > k)
+          *wave->Stop++ = bp+c;
+        else if (h < k)
+          *wave->Stop++ = ap-(c+k);
+        k = h;
+        e = PHF[D][h];
+      }
+  }
+
+  return (D + abs(del));
+}
+
+static int middle_np(char *A, int M, char *B, int N, Trace_Waves *wave, int mode)
+{ int  **PVF = wave->PVF; 
+  int  **PHF = wave->PHF;
+  int    D;
+  int    del = M-N;
+
+  { int  *F0, *F1, *F2;
+    int  *HF;
+    int   low, hgh;
+    int   posl, posh;
+
+#ifdef DEBUG_ALIGN
+    printf("\n%*s BASE %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+    printf("%*s A = ",depth,"");
+    for (D = 0; D < M; D++)
+      printf("%c",ToA[(int) A[D]]);
+    printf("\n");
+    printf("%*s B = ",depth,"");
+    for (D = 0; D < N; D++)
+      printf("%c",ToA[(int) B[D]]);
+    printf("\n");
+#endif
+
+    if (del >= 0)
+      { low = 0;
+        hgh = del;
+      }
+    else
+      { low = del;
+        hgh = 0;
+      }
+
+    posl = -INT32_MAX;
+    posh =  INT32_MAX;
+    if (wave->Aabs == wave->Babs)
+      { if (B == A)
+          { EPRINTF(EPLACE,"Error: self comparison starts on diagonal 0 (Compute_Trace)\n");
+            EXIT(1);
+          }
+        else if (B < A)
+          posl = (B-A)+1;
+        else
+          posh = (B-A)-1;
+      }
+
+    F1 = PVF[-2];
+    F0 = PVF[-1];
+
+    for (D = low-1; D <= hgh+1; D++)
+      F1[D] = F0[D] = -2;
+    F0[0] = -1;
+
+    low += 1;
+    hgh -= 1;
+
+    for (D = 0; 1; D += 1)
+      { int   k, i, j;
+        int   am, ac, ap;
+        char *a;
+
+        F2 = F1;
+        F1 = F0;
+        F0 = PVF[D];
+        HF = PHF[D];
+
+        if ((D & 0x1) == 0)
+          { if (low > posl)
+              low -= 1;
+            if (hgh < posh)
+              hgh += 1;
+          }
+        F0[hgh+1] = F0[low-1] = -2;
+
+        j = -2;
+        a = A + hgh;
+        i = M - hgh;
+        for (k = hgh; k > del; k--)
+          { ap = j+1;
+            am = F2[k-1];
+            FS_MOVE(-1,4)
+            a -= 1;
+            i += 1;
+          }
+
+        j = -2;
+        a = A + low;
+        i = M - low;
+        for (k = low; k < del; k++)
+          { ap = F2[k+1]+1;
+            am = j;
+            FS_MOVE(2,1)
+            a += 1;
+            i -= 1;
+          }
+
+        ap = F0[del+1]+1;
+        am = j;
+        FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+        print_awave(F0,low,hgh);
+        print_awave(HF,low,hgh);
+#endif
+
+        if (F0[del] >= N)
+          break;
+      }
+  }
+
+  { int   k, h, m, e, c;
+    int   d, f;
+
+    d = D + abs(del);
+    c = N;
+    k = del;
+
+    if (mode == UPPERMOST)
+
+      for (f = d/2; d > f; d--)
+        { e = PHF[D][k];
+          h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+
+          if (h < k)       // => e = -1 or 2,  UPPERMOST
+            { char *a;
+  
+              a = A + k;
+              if (k < 0)
+                m = -k;
+              else
+                m = 0;
+              if (PVF[D][h] <= c)
+                c = PVF[D][h]-1;
+              while (c >= m && a[c] == B[c])
+                c -= 1;
+              if (e == -1)  //  => edge is 2, others are 1, and 0
+                { if (c <= PVF[D+2][k+1])
+                    { e = 4;
+                      h = k+1;
+                      D = D+2;
+                    }
+                  else if (c == PVF[D+1][k])
+                    { e = 0;
+                      h = k;
+                      D = D+1;
+                    }
+                  else
+                    PVF[D][h] = c+1;
+                }
+              else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+                { if (k == del)
+                    m = D;
+                  else
+                    m = D-2;
+                  if (c <= PVF[m][k+1])
+                    { if (k == del)
+                        e = 4;
+                      else
+                        e = 1;
+                      h = k+1;
+                      D = m;
+                    }
+                  else if (c == PVF[D-1][k])
+                    { e = 0;
+                      h = k;
+                      D = D-1;
+                    }
+                  else
+                    PVF[D][h] = c+1;
+                }
+            }
+
+          k = h;
+        }
+
+    else if (mode == LOWERMOST)
+
+      for (f = d/2; d > f; d--)
+        { e = PHF[D][k];
+          h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+
+          if (h > k)       // => e = 1 or 4,   LOWERMOST
+            { char *a;
+  
+              a = A + k;
+              if (k < 0)
+                m = -k;
+              else
+                m = 0;
+              if (PVF[D][h] < c)
+                c = PVF[D][h];
+              while (c >= m && a[c] == B[c])
+                c -= 1;
+              if (e == 1)  //  => edge is 2, others are 1, and 0
+                { if (c < PVF[D+2][k-1])
+                    { e = 2;
+                      h = k-1;
+                      D = D+2;
+                    }
+                  else if (c == PVF[D+1][k])
+                    { e = 0;
+                      h = k;
+                      D = D+1;
+                    }
+                  else
+                    PVF[D][h] = c--;
+                }
+              else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+                { if (k == del)
+                    m = D;
+                  else
+                    m = D-2;
+                  if (c < PVF[m][k-1])
+                    { if (k == del)
+                        e = 2;
+                      else
+                        e = -1;
+                      h = k-1;
+                      D = m;
+                    }
+                  else if (c == PVF[D-1][k])
+                    { e = 0;
+                      h = k;
+                      D = D-1;
+                    }
+                  else
+                    PVF[D][h] = c--;
+                }
+            }
+
+          k = h;
+        }
+
+    else //  mode == GREEDIEST
+
+      for (f = d/2; d > f; d--)
+        { e = PHF[D][k];
+          h = k+e;
+          if (e > 1)
+            h -= 3;
+          else if (e == 0)
+            D -= 1;
+          else
+            D -= 2;
+          k = h;
+        }
+
+    wave->midb = (B-wave->Babs) + PVF[D][k];
+    wave->mida = (A-wave->Aabs) + k + PVF[D][k];
+  }
+
+  return (0);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  COMPUTE_TRACE FLAVORS                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+int Compute_Trace_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path *path;
+  char *aseq, *bseq;
+  int   M, N, D;
+
+  path = align->path;
+  aseq = align->aseq;
+  bseq = align->bseq;
+
+  M = path->aepos-path->abpos;
+  N = path->bepos-path->bbpos;
+  
+  { int64 s;
+    int   d;
+    int   dmax;
+    int   **PVF, **PHF;
+
+    if (M < N)
+      s = N;
+    else
+      s = M;
+    s *= sizeof(int);
+    if (s > work->tramax)
+      if (enlarge_trace(work,s))
+        EXIT(1);
+
+    dmax = path->diffs - abs(M-N);
+
+    s = (dmax+3)*2*((M+N+3)*sizeof(int) + sizeof(int *));
+
+    if (s > 256000000)
+      return (Compute_Trace_ND_ALL(align,ework));
+
+    if (s > work->vecmax)
+      if (enlarge_vector(work,s))
+        EXIT(1);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = M+N+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (N+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = ((int *) work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  D = iter_np(aseq+path->abpos,M,bseq+path->bbpos,N,&wave,GREEDIEST);
+  if (D < 0)
+    EXIT(1);
+  path->diffs = D;
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+
+  return (0);
+}
+
+int Compute_Trace_PTS(Alignment *align, Work_Data *ework, int trace_spacing, int mode)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path   *path;
+  char   *aseq, *bseq;
+  uint16 *points;
+  int     tlen;
+  int     ab, bb;
+  int     ae, be;
+  int     diffs;
+
+  path   = align->path;
+  aseq   = align->aseq;
+  bseq   = align->bseq;
+  tlen   = path->tlen;
+  points = (uint16 *) path->trace;
+
+  { int64 s;
+    int   d;
+    int   M, N;
+    int   dmax, nmax;
+    int   **PVF, **PHF;
+
+    M = path->aepos-path->abpos;
+    N = path->bepos-path->bbpos;
+    if (M < N)
+      s = N*sizeof(int);
+    else
+      s = M*sizeof(int);
+    if (s > work->tramax)
+      if (enlarge_trace(work,s))
+        EXIT(1);
+
+    nmax = 0;
+    dmax = 0;
+    for (d = 1; d < tlen; d += 2)
+      { if (points[d-1] > dmax)
+          dmax = points[d-1];
+        if (points[d] > nmax)
+          nmax = points[d];
+      }
+    if (tlen <= 1)
+      nmax = N;
+
+    s = (dmax+3)*2*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+    if (s > work->vecmax)
+      if (enlarge_vector(work,s))
+        EXIT(1);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = trace_spacing+nmax+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = (int *) (work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  { int i, d;
+
+    diffs = 0;
+    ab = path->abpos;
+    ae = (ab/trace_spacing)*trace_spacing;
+    bb = path->bbpos;
+    tlen -= 2;
+    for (i = 1; i < tlen; i += 2)
+      { ae = ae + trace_spacing;
+        be = bb + points[i];
+        d  = iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave,mode);
+        if (d < 0)
+          EXIT(1);
+        diffs += d;
+        ab = ae;
+        bb = be;
+      }
+    ae = path->aepos;
+    be = path->bepos;
+    d  = iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave,mode);
+    if (d < 0)
+      EXIT(1);
+    diffs += d;
+  }
+
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+  path->diffs = diffs;
+
+  return (0);
+}
+
+int Compute_Trace_MID(Alignment *align, Work_Data *ework, int trace_spacing, int mode)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path   *path;
+  char   *aseq, *bseq;
+  uint16 *points;
+  int     tlen;
+  int     ab, bb;
+  int     ae, be;
+  int     diffs;
+
+  path   = align->path;
+  aseq   = align->aseq;
+  bseq   = align->bseq;
+  tlen   = path->tlen;
+  points = (uint16 *) path->trace;
+
+  { int64 s;
+    int   d;
+    int   M, N;
+    int   dmax, nmax;
+    int   **PVF, **PHF;
+
+    M = path->aepos-path->abpos;
+    N = path->bepos-path->bbpos;
+    if (M < N)
+      s = N*sizeof(int);
+    else
+      s = M*sizeof(int);
+    if (s > work->tramax)
+      if (enlarge_trace(work,s))
+        EXIT(1);
+
+    nmax = 0;
+    dmax = 0;
+    for (d = 1; d < tlen; d += 2)
+      { if (points[d-1] > dmax)
+          dmax = points[d-1];
+        if (points[d] > nmax)
+          nmax = points[d];
+      }
+    if (tlen <= 1)
+      nmax = N;
+
+    s = (dmax+3)*4*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+    if (s > work->vecmax)
+      if (enlarge_vector(work,s))
+        EXIT(1);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = trace_spacing+nmax+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = ((int *) work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  { int i, d;
+    int as, bs;
+    int af, bf;
+
+    diffs = 0;
+    ab = as = af = path->abpos;
+    ae = (ab/trace_spacing)*trace_spacing;
+    bb = bs = bf = path->bbpos;
+    tlen -= 2;
+    for (i = 1; i < tlen; i += 2) 
+      { ae = ae + trace_spacing;
+        be = bb + points[i];
+        if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave,mode))
+          EXIT(1);
+        af = wave.mida;
+        bf = wave.midb;
+        d  = iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave,mode);
+        if (d < 0)
+          EXIT(1);
+        diffs += d;
+        ab = ae;
+        bb = be;
+        as = af;
+        bs = bf;
+      }
+
+    ae = path->aepos;
+    be = path->bepos;
+
+    if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave,mode))
+      EXIT(1);
+    af = wave.mida;
+    bf = wave.midb;
+    d  = iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave,mode);
+    if (d < 0)
+      EXIT(1);
+    diffs += d;
+    as = af;
+    bs = bf;
+    
+    d += iter_np(aseq+af,ae-as,bseq+bf,be-bs,&wave,mode);
+    if (d < 0)
+      EXIT(1);
+    diffs += d;
+  }
+
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+  path->diffs = diffs;
+
+  return (0);
+}
+
+int Compute_Trace_IRR(Alignment *align, Work_Data *ework, int mode)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path   *path;
+  char   *aseq, *bseq;
+  uint16 *points;
+  int     tlen;
+  int     ab, bb;
+  int     ae, be;
+  int     diffs;
+
+  path   = align->path;
+  aseq   = align->aseq;
+  bseq   = align->bseq;
+  tlen   = path->tlen;
+  points = (uint16 *) path->trace;
+
+  { int64 s;
+    int   d;
+    int   M, N;
+    int   mmax, nmax, dmax;
+    int   **PVF, **PHF;
+
+    M = path->aepos-path->abpos;
+    N = path->bepos-path->bbpos;
+    if (M < N)
+      s = N*sizeof(int);
+    else
+      s = M*sizeof(int);
+    if (s > work->tramax)
+      if (enlarge_trace(work,s))
+        EXIT(1);
+
+    nmax = mmax = 0;
+    for (d = 0; d < tlen; d += 2)
+      { if (points[d] > mmax)
+          mmax = points[d];
+        if (points[d+1] > nmax)
+          nmax = points[d+1];
+      }
+    if (tlen <= 1)
+      { mmax = M;
+        nmax = N;
+      }
+    if (mmax > nmax)
+      dmax = nmax;
+    else
+      dmax = mmax;
+
+    s = (dmax+3)*2*((mmax+nmax+3)*sizeof(int) + sizeof(int *));
+
+    if (s > work->vecmax)
+      if (enlarge_vector(work,s))
+        EXIT(1);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = mmax+nmax+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = (int *) (work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  { int i, d;
+
+    diffs = 0;
+    ab = path->abpos;
+    bb = path->bbpos;
+    for (i = 0; i < tlen; i += 2)
+      { ae = ab + points[i];
+        be = bb + points[i+1];
+        d = iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave,mode);
+        if (d < 0)
+          EXIT(1);
+        diffs += d;
+        ab = ae;
+        bb = be;
+      }
+  }
+
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+  path->diffs = diffs;
+
+  return (0);
+}
diff --git a/align.h b/align.h
new file mode 100644
index 0000000..e937b68
--- /dev/null
+++ b/align.h
@@ -0,0 +1,335 @@
+/*******************************************************************************************
+ *
+ *  Local alignment module.  Routines for finding local alignments given a seed position,
+ *    representing such an l.a. with its interval and a set of pass-thru points, so that
+ *    a detailed alignment can be efficiently computed on demand.
+ *
+ *  All routines work on a numeric representation of DNA sequences, i.e. 0 for A, 1 for C,
+ *    2 for G, and 3 for T.
+ *
+ *  Author:  Gene Myers
+ *  Date  :  July 2013
+ *
+ ********************************************************************************************/
+
+#ifndef _A_MODULE
+
+#define _A_MODULE
+
+#include "DB.h"
+
+#define TRACE_XOVR 125   //  If the trace spacing is not more than this value, then can
+                         //    and do compress traces pts to 8-bit unsigned ints
+
+/*** INTERACTIVE vs BATCH version
+
+     The defined constant INTERACTIVE (set in DB.h) determines whether an interactive or
+       batch version of the routines in this library are compiled.  In batch mode, routines
+       print an error message and exit.  In interactive mode, the routines place the error
+       message in EPLACE (also defined in DB.h) and return an error value, typically NULL
+       if the routine returns a pointer, and an unusual integer value if the routine returns
+       an integer.
+     Below when an error return is described, one should understand that this value is returned
+       only if the routine was compiled in INTERACTIVE mode.
+
+***/
+
+
+/*** PATH ABSTRACTION:
+
+     Coordinates are *between* characters where 0 is the tick just before the first char,
+     1 is the tick between the first and second character, and so on.  Our data structure
+     is called a Path refering to its conceptualization in an edit graph.
+
+     A local alignment is specified by the point '(abpos,bbpos)' at which its path in
+     the underlying edit graph starts, and the point '(aepos,bepos)' at which it ends.
+     In otherwords A[abpos+1..aepos] is aligned to B[bbpos+1..bepos] (assuming X[1] is
+     the *first* character of X).
+
+     There are 'diffs' differences in an optimal local alignment between the beginning and
+     end points of the alignment (if computed by Compute_Trace), or nearly so (if computed
+     by Local_Alignment).  
+
+     Optionally, a Path can have additional information about the exact nature of the
+     aligned substrings if the field 'trace' is not NULL.  Trace points to either an
+     array of integers (if computed by a Compute_Trace routine), or an array of unsigned
+     short integers (if computed by Local_Alignment).
+
+     If computed by Local_Alignment 'trace' points at a list of 'tlen' (always even) short
+     values:
+
+            d_0, b_0, d_1, b_1, ... d_n-1, b_n-1, d_n, b_n
+
+     to be interpreted as follows.  The alignment from (abpos,bbpos) to (aepos,bepos)
+     passes through the n trace points for i in [1,n]:
+
+            (a_i,b_i) where a_i = floor(abpos/TS)*TS + i*TS
+                        and b_i = bbpos + (b_0 + b_1 + b_i-1)
+
+     where also let a_0,b_0 = abpos,bbpos and a_(n+1),b_(n+1) = aepos,bepos.  That is, the
+     interior (i.e. i != 0 and i != n+1) trace points pass through every TS'th position of
+     the aread where TS is the "trace spacing" employed when finding the alignment (see
+     New_Align_Spec).  Typically TS is 100.  Then d_i is the number of differences in the
+     portion of the alignment between (a_i,b_i) and (a_i+1,b_i+1).  These trace points allow
+     the Compute_Trace routines to efficiently compute the exact alignment between the two
+     reads by efficiently computing exact alignments between consecutive pairs of trace points.
+     Moreover, the diff values give one an idea of the quality of the alignment along every
+     segment of TS symbols of the aread.
+
+     If computed by a Compute_Trace routine, 'trace' points at a list of 'tlen' integers
+     < i1, i2, ... in > that encodes an exact alignment as follows.  A negative number j
+     indicates that a dash should be placed before A[-j] and a positive number k indicates
+     that a dash should be placed before B[k], where A and B are the two sequences of the
+     overlap.  The indels occur in the trace in the order in which they occur along the
+     alignment.  For a good example of how to "decode" a trace into an alignment, see the
+     code for the routine Print_Alignment.
+
+***/
+
+typedef struct
+  { void     *trace;
+    int       tlen;
+    int       diffs;
+    int       abpos, bbpos;
+    int       aepos, bepos;
+  } Path;
+
+
+/*** ALIGNMENT ABSTRACTION:
+
+     An alignment is modeled by an Alignment record, which in addition to a *pointer* to a
+     'path', gives pointers to the A and B sequences, their lengths, and indicates whether
+     the B-sequence needs to be complemented ('comp' non-zero if so).  The 'trace' pointer
+     of the 'path' subrecord can be either NULL, a list of pass-through points, or an exact
+     trace depending on what routines have been called on the record.
+
+     One can (1) compute a trace, with Compute_Trace, either from scratch if 'path.trace' = NULL,
+     or using the sequence of pass-through points in trace, (2) print an ASCII representation
+     of an alignment, or (3) reverse the roles of A and B, and (4) complement a sequence
+     (which is a reversible process).
+
+     If the alignment record shows the B sequence as complemented, *** THEN IT IS THE
+     RESPONSIBILITY OF THE CALLER *** to make sure that bseq points at a complement of
+     the sequence before calling Compute_Trace or Print_Alignment.  Complement_Seq complements
+     the sequence a of length n.  The operation does the complementation/reversal in place.
+     Calling it a second time on a given fragment restores it to its original state.
+***/
+
+#define COMP(x)  ((x) & 0x1)
+
+#define COMP_FLAG 0x1
+
+typedef struct
+  { Path   *path;
+    uint32  flags;        /* Pipeline status and complementation flags          */
+    char   *aseq;         /* Pointer to A sequence                              */
+    char   *bseq;         /* Pointer to B sequence                              */
+    int     alen;         /* Length of A sequence                               */
+    int     blen;         /* Length of B sequence                               */
+  } Alignment;
+
+void Complement_Seq(char *a, int n);
+
+  /* Many routines like Local_Alignment, Compute_Trace, and Print_Alignment need working
+     storage that is more efficiently reused with each call, rather than being allocated anew
+     with each call.  Each *thread* can create a Work_Data object with New_Work_Data and this
+     object holds and retains the working storage for routines of this module between calls
+     to the routines.  If enough memory for a Work_Data is not available then NULL is returned.
+     Free_Work_Data frees a Work_Data object and all working storage held by it.
+  */
+
+  typedef void Work_Data;
+
+  Work_Data *New_Work_Data();
+
+  void       Free_Work_Data(Work_Data *work);
+
+  /* Local_Alignment seeks local alignments of a quality determined by a number of parameters.
+     These are coded in an Align_Spec object that can be created with New_Align_Spec and
+     freed with Free_Align_Spec when no longer needed.  There are 4 essential parameters:
+
+     ave_corr:    the average correlation (1 - 2*error_rate) for the sought alignments.  For Pacbio
+                    data we set this to .70 assuming an average of 15% error in each read.
+     trace_space: the spacing interval for keeping trace points and segment differences (see
+                    description of 'trace' for Paths above)
+     freq[4]:     a 4-element vector where afreq[0] = frequency of A, f(A), freq[1] = f(C),
+                    freq[2] = f(G), and freq[3] = f(T).  This vector is part of the header
+                    of every HITS database (see db.h).
+
+     If an alignment cannot reach the boundary of the d.p. matrix with this condition (i.e.
+     overlap), then the last/first 30 columns of the alignment are guaranteed to be
+     suffix/prefix positive at correlation ave_corr * g(freq) where g is an empirically
+     measured function that increases from 1 as the entropy of freq decreases.  If memory is
+     unavailable or the freq distribution is too skewed then NULL is returned.
+
+     You can get back the original parameters used to create an Align_Spec with the simple
+     utility functions below.
+  */
+
+  typedef void Align_Spec;
+
+  Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq);
+
+  void        Free_Align_Spec(Align_Spec *spec);
+
+  int    Trace_Spacing      (Align_Spec *spec);
+  double Average_Correlation(Align_Spec *spec);
+  float *Base_Frequencies   (Align_Spec *spec);
+
+  /* Local_Alignment finds the longest significant local alignment between the sequences in
+     'align' subject to:
+
+       (a) the alignment criterion given by the Align_Spec 'spec',
+       (b) it passes through one of the points (anti+k)/2,(anti-k)/2 for k in [low,hgh] within
+             the underlying dynamic programming matrix (i.e. the points on diagonals low to hgh
+             on anti-diagonal anti or anti-1 (depending on whether the diagonal is odd or even)),
+       (c) if lbord >= 0, then the alignment is always above diagonal low-lbord, and
+       (d) if hbord >= 0, then the alignment is always below diagonal hgh+hbord.
+
+     The path record of 'align' has its 'trace' filled from the point of view of an overlap
+     between the aread and the bread.  In addition a Path record from the point of view of the
+     bread versus the aread is returned by the function, with this Path's 'trace' filled in
+     appropriately.  The space for the returned path and the two 'trace's are in the working
+     storage supplied by the Work_Data packet and this space is reused with each call, so if
+     one wants to retain the bread-path and the two trace point sequences, then they must be
+     copied to user-allocated storage before calling the routine again.  NULL is returned in
+     the event of an error.
+
+     Find_Extension is a variant of Local_Alignment that simply finds a local alignment that
+     either ends (if prefix is non-zero) or begins (if prefix is zero) at the point
+     (anti+diag)/2,(anti-diag)/2).  All other parameters are as before.  It returns a non-zero
+     value only when INTERACTIVE is on and it cannot allocate the memory it needs.
+     Only the path and trace with respect to the aread is returned.  This routine is experimental
+     and may not persist in later versions of the code.
+  */
+
+  Path *Local_Alignment(Alignment *align, Work_Data *work, Align_Spec *spec,
+                        int low, int hgh, int anti, int lbord, int hbord);
+
+  int   Find_Extension(Alignment *align, Work_Data *work, Align_Spec *spec,    //  experimental !!
+                       int diag, int anti, int lbord, int hbord, int prefix);
+
+  /* Given a legitimate Alignment object, Compute_Trace_X computes an exact trace for the alignment.
+     If 'path.trace' is non-NULL, then it is assumed to be a sequence of pass-through points
+     and diff levels computed by Local_Alignment.  In either case 'path.trace' is set
+     to point at an integer array within the storage of the Work_Data packet encoding an
+     exact optimal trace from the start to end points.  If the trace is needed beyond the
+     next call to a routine that sets it, then it should be copied to an array allocated
+     and managed by the caller.
+
+     Compute_Trace_ALL does not require a sequence of pass-through points, as it computes the
+     best alignment between (path->abpos,path->bbpos) and (path->aepos,path->bepos) in the
+     edit graph between the sequences.  Compute_Trace_PTS computes a trace by computing the
+     trace between successive pass through points.  It is much, much faster than Compute_Trace_ALL
+     but at the tradeoff of not necessarily being optimal as pass-through points are not all
+     perfect.  Compute_Trace_MID computes a trace by computing the trace between the mid-points
+     of alignments between two adjacent pairs of pass through points.  It is generally twice as
+     slow as Compute_Trace_PTS, but it produces nearer optimal alignments.  All these routines
+     return 1 if an error occurred and 0 otherwise.
+  */
+
+#define LOWERMOST -1   //   Possible modes for "mode" parameter below)
+#define GREEDIEST  0
+#define UPPERMOST  1
+
+  int Compute_Trace_ALL(Alignment *align, Work_Data *work);
+  int Compute_Trace_PTS(Alignment *align, Work_Data *work, int trace_spacing, int mode);
+  int Compute_Trace_MID(Alignment *align, Work_Data *work, int trace_spacing, int mode);
+
+  /* Compute_Trace_IRR (IRR for IRRegular) computes a trace for the given alignment where
+     it assumes the spacing between trace points between both the A and B read varies, and
+     futher assumes that the A-spacing is given in the short integers normally occupied by
+     the differences in the alignment between the trace points.  This routine is experimental
+     and may not persist in later versions of the code.
+  */
+
+  int Compute_Trace_IRR(Alignment *align, Work_Data *work, int mode);   //  experimental !!
+
+  /* Alignment_Cartoon prints an ASCII representation of the overlap relationhip between the
+     two reads of 'align' to the given 'file' indented by 'indent' space.  Coord controls
+     the display width of numbers, it must be not less than the width of any number to be
+     displayed.
+
+     If the alignment trace is an exact trace, then one can ask Print_Alignment to print an
+     ASCII representation of the alignment 'align' to the file 'file'.  Indent the display
+     by "indent" spaces and put "width" columns per line in the display.  Show "border"
+     characters of sequence on each side of the aligned region.  If upper is non-zero then
+     display bases in upper case.  If coord is greater than 0, then the positions of the
+     first character in A and B in the given row is displayed with a field width given by
+     coord's value.
+
+     Print_Reference is like Print_Alignment but rather than printing exaclty "width" columns
+     per segment, it prints "block" characters of the A sequence in each segment.  This results
+     in segments of different lengths, but is convenient when looking at two alignments involving
+     A as segments are guaranteed to cover the same interval of A in a segment.
+
+     Both Print routines return 1 if an error occurred (not enough memory), and 0 otherwise.
+
+     Flip_Alignment modifies align so the roles of A and B are reversed.  If full is off then
+     the trace is ignored, otherwise the trace must be to a full alignment trace and this trace
+     is also appropriately inverted.
+  */
+
+  void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord);
+
+  int  Print_Alignment(FILE *file, Alignment *align, Work_Data *work,
+                       int indent, int width, int border, int upper, int coord);
+
+  int  Print_Reference(FILE *file, Alignment *align, Work_Data *work,
+                       int indent, int block, int border, int upper, int coord);
+
+  void Flip_Alignment(Alignment *align, int full);
+
+
+/*** OVERLAP ABSTRACTION:
+
+     Externally, between modules an Alignment is modeled by an "Overlap" record, which
+     (a) replaces the pointers to the two sequences with their ID's in the HITS data bases,
+     (b) does not contain the length of the 2 sequences (must fetch from DB), and
+     (c) contains its path as a subrecord rather than as a pointer (indeed, typically the
+     corresponding Alignment record points at the Overlap's path sub-record).  The trace pointer
+     is always to a sequence of trace points and can be either compressed (uint8) or
+     uncompressed (uint16).  One can read and write binary records of an "Overlap".
+***/
+
+typedef struct {
+  Path    path;         /* Path: begin- and end-point of alignment + diffs    */
+  uint32  flags;        /* Pipeline status and complementation flags          */
+  int     aread;        /* Id # of A sequence                                 */
+  int     bread;        /* Id # of B sequence                                 */
+} Overlap;
+
+
+  /* Read_Overlap reads the next Overlap record from stream 'input', not including the trace
+     (if any), and without modifying 'ovl's trace pointer.  Read_Trace reads the ensuing trace
+     into the memory pointed at by the trace field of 'ovl'.  It is assumed to be big enough to
+     accommodate the trace where each value take 'tbytes' bytes (1 if uint8 or 2 if uint16).
+
+     Write_Overlap write 'ovl' to stream 'output' followed by its trace vector (if any) that
+     occupies 'tbytes' bytes per value.  
+
+     Print_Overlap prints an ASCII version of the contents of 'ovl' to stream 'output'
+     where the trace occupes 'tbytes' per value and the print out is indented from the left
+     margin by 'indent' spaces.
+
+     Compress_TraceTo8 converts a trace fo 16-bit values to 8-bit values in place, and
+     Decompress_TraceTo16 does the reverse conversion.
+
+     Check_Trace_Points checks that the number of trace points is correct and that the sum
+     of the b-read displacements equals the b-read alignment interval, assuming the trace
+     spacing is 'tspace'.  It reports an error message if there is a problem and 'verbose'
+     is non-zero.  The 'ovl' came from the file names 'fname'.
+  */
+
+  int Read_Overlap(FILE *input, Overlap *ovl);
+  int Read_Trace(FILE *innput, Overlap *ovl, int tbytes);
+
+  void Write_Overlap(FILE *output, Overlap *ovl, int tbytes);
+  void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent);
+
+  void Compress_TraceTo8(Overlap *ovl);
+  void Decompress_TraceTo16(Overlap *ovl);
+
+  int  Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname);
+
+#endif // _A_MODULE

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/dascrubber.git



More information about the debian-med-commit mailing list