static char rcsid[] = "$Id: af884208a702983bdc24668d65c1f5c9f1fe48b5 $";
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef HAVE_MEMCPY
#define memcpy(d,s,n) bcopy((s),(d),(n))
#endif
#ifndef HAVE_MEMMOVE
#define memmove(d,s,n) bcopy((s),(d),(n))
#endif

#include "segment-search.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>		/* For memset() */
#include <math.h>
#include <ctype.h>		/* for tolower() */
#include "assert.h"
#include "mem.h"
#include "oligo.h"
#include "comp.h"

#include "list.h"
#include "intlist.h"
#include "complement.h"
#include "compress.h"
#include "genome_sites.h"
#include "maxent.h"
#include "maxent_hr.h"
#include "iitdef.h"
#include "univinterval.h"
#ifdef LARGE_GENOMES
#include "uint8list.h"
#else
#include "uintlist.h"
#endif
#include "univdiagdef.h"
#include "univdiag.h"
#include "univdiagpool.h"

#ifndef LARGE_GENOMES
#include "merge-diagonals-simd-uint4.h"
#elif defined(HAVE_AVX512) || defined(HAVE_AVX2)
#include "merge-diagonals-simd-uint8.h"
#else
#include "merge-diagonals-heap.h"
#endif

#ifdef LARGE_GENOMES
#include "intersect-large.h"
#endif
#include "intersect.h"
#include "sedgesort.h"
#include "path-solve.h"


#ifdef DEBUG
#define debug(x) x
#else
#define debug(x)
#endif

#ifdef DEBUG0
#define debug0(x) x
#else
#define debug0(x)
#endif

/* Records */
#ifdef DEBUG1
#define debug1(x) x
#else
#define debug1(x)
#endif

/* Record_overlap_p */
#ifdef DEBUG2
#define debug2(x) x
#else
#define debug2(x)
#endif

/* binary_search */
#ifdef DEBUG10
#define debug10(x) x
#else
#define debug10(x)
#endif

#ifdef DEBUG13
#define debug13(x) x
#else
#define debug13(x)
#endif


#define MAX_INDEX1INTERVAL 3
#define STAGE2_MIN_OLIGO 3	/* Actually 6, but we are adding index1interval to this */
#define MAX_ALLOCATION 200
#define SIGNIFICANT_COUNT 10

/* Was 100000.  Avoids cases where there are too many paths to check.
   Can rely upon paired_read_segment_search instead */
/* Limiting this to 100 can miss some alignments */
#define MAX_DIAGONALS 100000

/* Now checking for repetitive oligos instead of an absolute limit */
/* #define ABSOLUTE_LIMIT 5000 */


static Mode_T mode;
static int leftreadshift;
static Oligospace_T oligobase_mask; /* same as kmer_mask */

static int index1part;
static int index1interval;
static int max_anchors;

static int nchromosomes;
static Univ_IIT_T chromosome_iit;
static int circular_typeint;
static EF64_T chromosome_ef64;


static int
Record_qpos5_ascending_cmp (const void *a, const void *b) {
  Record_T x = * (Record_T *) a;
  Record_T y = * (Record_T *) b;

  if (x->qstart < y->qstart) {
    return -1;
  } else if (y->qstart < x->qstart) {
    return +1;
  } else {
    return 0;
  }
}

static int
Record_qpos3_ascending_cmp (const void *a, const void *b) {
  Record_T x = * (Record_T *) a;
  Record_T y = * (Record_T *) b;

  if (x->qend < y->qend) {
    return -1;
  } else if (y->qend < x->qend) {
    return +1;
  } else {
    return 0;
  }
}


static bool
Record_overlap_p (Record_T segment1, Record_T segment2) {
  int qpos5, qpos3;

  debug2(printf("Entered Record_overlap_p with %d..%d and %d..%d\n",
		segment1->qstart,segment1->qend,
		segment2->qstart,segment2->qend));

  if (segment1->qend < segment2->qstart) {
    debug2(printf("No overlap => false\n"));
    return false;
  } else if (segment2->qend < segment1->qstart) {
    debug2(printf("No overlap => false\n"));
    return false;
  } else {
    /* Previously, we took the smallest region, maximized qstart and minimized qend */
    if (segment1->qend < segment2->qend) {
      qpos3 = segment1->qend;
    } else {
      qpos3 = segment2->qend;
    }

    if (segment1->qstart > segment2->qstart) {
      qpos5 = segment1->qstart;
    } else {
      qpos5 = segment2->qstart;
    }

    debug2(printf("qpos5 %d, qpos3 %d\n",qpos5,qpos3));

    if (3 * (qpos3 - qpos5) > (segment1->qend - segment1->qstart)) {
      debug2(printf("Amount of overlap is significant compared to segment1\n"));
      return true;
    } else if (3 * (qpos3 - qpos5) > (segment2->qend - segment2->qstart)) {
      debug2(printf("Amount of overlap is significant compared to segment2\n"));
      return true;
    } else {
      debug2(printf("Amount of overlap is insignificant\n"));
      return false;
    }
  }
}


#if 0
/* Previously used for LARGE_GENOMES */
static int
binary_search_large (int lowi, int highi, unsigned char *positions_high, UINT4 *positions_low, Univcoord_T goal) {
  int middlei;
  Univcoord_T position;

  debug10(printf("entered binary search with lowi=%d, highi=%d, goal=%llu\n",
		 lowi,highi,(unsigned long long) goal));

  while (lowi < highi) {
    middlei = lowi + ((highi - lowi) / 2);
    position = ((Univcoord_T) positions_high[middlei] << 32) + positions_low[middlei];
    debug10(printf("  binary: %d:%llu %d:%llu %d:%llu   vs. %llu\n",
		   lowi,(unsigned long long) ((positions_high[lowi] << 32) + positions_low[lowi]),
		   middlei,(unsigned long long) position,
		   highi,(unsigned long long) ((positions_high[highi] << 32) + positions_low[highi]),
		   (unsigned long long) goal));
    if (goal < position) {
      highi = middlei;
    } else if (goal > position) {
      lowi = middlei + 1;
    } else {
      debug10(printf("binary search returns %d\n",middlei));
      return middlei;
    }
  }

  debug10(printf("binary search returns %d\n",highi));
  return highi;
}
#endif


#ifdef FAST_CHR_UPDATE
static int
binary_search (int lowi, int highi, Univcoord_T *positions, Univcoord_T goal) {
  int middlei;

  debug10(printf("entered binary search with lowi=%d, highi=%d, goal=%llu\n",
		 lowi,highi,(unsigned long long) goal));

  while (lowi < highi) {
    middlei = lowi + ((highi - lowi) / 2);
    debug10(printf("  binary: %d:%llu %d:%llu %d:%llu   vs. %llu\n",
		   lowi,(unsigned long long) positions[lowi],
		   middlei,(unsigned long long) positions[middlei],
		   highi,(unsigned long long) positions[highi],
		   (unsigned long long) goal));
    if (goal < positions[middlei]) {
      highi = middlei;
    } else if (goal > positions[middlei]) {
      lowi = middlei + 1;
    } else {
      debug10(printf("binary search returns %d\n",middlei));
      return middlei;
    }
  }

  debug10(printf("binary search returns %d\n",highi));
  return highi;
}
#endif



#define add_bounded(x,plusterm,highbound) ((x + (plusterm) >= highbound) ? (highbound - 1) : x + (plusterm))
#define subtract_bounded(x,minusterm,lowbound) ((x < lowbound + (minusterm)) ? lowbound : x - (minusterm))


#define SINGLETON 0
#define DUPLICATE -2
#define ADJACENT -1		/* Within overall_max_distance */


#ifdef DEBUG1
static char *
status_string (int status) {
  if (status == SINGLETON) {
    return "SINGLETON";
  } else if (status == ADJACENT) {
    return "ADJACENT";
  } else if (status == DUPLICATE) {
    return "DUPLICATE";
  } else {
    return "MULTIPLET";
  }
}
#endif


/* Returns status as count (if positive) or one of the flags SINGLETON, DUPLICATE, or ADJACENT */
static int *
compute_status_greedy (int *nanchors, int *nadjacent, Univcoord_T *univdiagonals, int nunivdiagonals,
		       Chrpos_T overall_max_distance) {
  int *status;
  int i, j, k, newi;
  int count_threshold, count, max_count = 0;

  debug(printf("Entering compute_status_greedy with %d univdiagonals\n",nunivdiagonals));

  status = (int *) CALLOC(nunivdiagonals,sizeof(int));
  *nanchors = *nadjacent = 0;

  /* Compute max_count */
  i = 0;
  while (i < nunivdiagonals) {
    j = i;
    while (j + 1 < nunivdiagonals && univdiagonals[j+1] == univdiagonals[i]) {
      j++;
    }
    newi = j+1;

    if (j > i) {  /* (count j - i + 1 > 1) */
      debug1(printf("For univdiagonal %u, count is %d\n",univdiagonals[i],j - i + 1));
      if ((count = j - i + 1) > max_count) {
	max_count = count;
      }
    }

    i = newi;
  }

  if (max_count <= 1) {
    /* Not enough alignment.  Skip modifying status */

  } else {
    if (max_count == 2) {
      /* Don't want counts of 1 since they are non-specific */
      count_threshold = 2;
    } else if (max_count >= SIGNIFICANT_COUNT) {
      /* Take anything that looks significant */
      count_threshold = SIGNIFICANT_COUNT;
    } else {
      count_threshold = max_count - 1;
    }

    i = 0;
    while (i < nunivdiagonals) {
      j = i;
      while (j + 1 < nunivdiagonals && univdiagonals[j+1] == univdiagonals[i]) {
	j++;
      }
      newi = j+1;

      if (j > i) {  /* (count j - i + 1 > 1) */
	/* Mark duplicates */
	for (k = i+1; k <= j; k++) {
	  debug1(printf("(1) Changing status of %u from %d (%s) to %d (DUPLICATE) => ",
			univdiagonals[k],status[k],status_string(status[k]),DUPLICATE));
	  if (status[k] == ADJACENT) {
	    (*nadjacent)--;
	  }
	  debug1(printf("nadjacent = %d\n",*nadjacent));
	  status[k] = DUPLICATE;
	}

	/* Previously had == max_count, but can have differences in counts due to mod 3 effects in indexdb */
	if ((count = j - i + 1) >= count_threshold) {
	  /* Anchor */
	  debug1(printf("Changing status of %u from %d (%s) to %d (MULTIPLET) => ",
			univdiagonals[i],status[i],status_string(status[i]),count));
	  if (status[i] == ADJACENT) {
	    (*nadjacent)--;
	  }
	  debug1(printf("nadjacent = %d\n",*nadjacent));
	  status[i] = count;
	  (*nanchors)++;
	
	  /* Mark segments ahead (adjacent is lowest status and might be overwritten) */
	  while (j + 1 < nunivdiagonals && univdiagonals[j+1] < univdiagonals[i] + overall_max_distance) {
	    debug1(printf("Changing status of %u from %d (%s) to %d (ADJACENT) => ",
			  univdiagonals[j+1],status[j+1],status_string(status[j+1]),ADJACENT));
	    if (status[++j] != ADJACENT) {
	      status[j] = ADJACENT;
	      (*nadjacent)++;
	    }
	    debug1(printf("nadjacent = %d\n",*nadjacent));
	  }
	
	  /* Mark segments behind */
	  j = i;
	  while (j - 1 >= 0 && univdiagonals[j-1] + overall_max_distance > univdiagonals[i]) {
	    debug1(printf("Changing status of %u from %d (%s) to %d (ADJACENT) => ",
			  univdiagonals[j-1],status[j-1],status_string(status[j-1]),ADJACENT));
	    if (status[--j] == SINGLETON) {
	      status[j] = ADJACENT;
	      (*nadjacent)++;
	    }
	    debug1(printf("nadjacent = %d\n",*nadjacent));
	  }
	}
      }

      i = newi;
    }
  }

#ifdef DEBUG1
  printf("Status for %d univdiagonals:\n",nunivdiagonals);
  for (i = 0; i < nunivdiagonals; i++) {
    printf("i=%d %d (%s) %u\n",i,status[i],status_string(status[i]),univdiagonals[i]);
  }
  printf("nanchors %d, nadjacent %d\n",*nanchors,*nadjacent);
#endif

#if 0
  nanchors_check = nadjacent_check = 0;
  for (i = 0; i < nunivdiagonals; i++) {
    if (status[i] > 0) {
      nanchors_check += 1;
    } else if (status[i] == ADJACENT) {
      nadjacent_check += 1;
    }
  }
  printf("nanchors_check %d, nadjacent_check %d\n",nanchors_check,nadjacent_check);

  if (*nanchors != nanchors_check) {
    abort();
  }
  if (*nadjacent != nadjacent_check) {
    abort();
  }
#endif

  return status;
}


/* Returns status as a positive count (if exceeds count_threshold) or
   one of the non-positive flags SINGLETON, DUPLICATE, or ADJACENT */
static int *
compute_status_threshold (int *nanchors, int *nadjacent, Univcoord_T *univdiagonals, int nunivdiagonals,
			  Chrpos_T overall_max_distance, int count_threshold) {
  int *status;
  int i, j, k, newi;
  int count;

  debug(printf("Entering compute_status_threshold\n"));

  status = (int *) CALLOC(nunivdiagonals,sizeof(int));
  *nanchors = *nadjacent = 0;

  i = 0;
  while (i < nunivdiagonals) {
    j = i;
    while (j + 1 < nunivdiagonals && univdiagonals[j+1] == univdiagonals[i]) {
      j++;
    }
    newi = j+1;

    if (j > i) {  /* (count j - i + 1 > 1) */
      /* Mark duplicates */
      for (k = i+1; k <= j; k++) {
	debug1(printf("Changing status of %u from %d (%s) to %d (DUPLICATE) => ",
		      univdiagonals[k],status[k],status_string(status[k]),DUPLICATE));
	if (status[k] == ADJACENT) {
	  (*nadjacent)--;
	}
	debug1(printf("nadjacent = %d\n",*nadjacent));
	status[k] = DUPLICATE;
      }

      if ((count = j - i + 1) >= count_threshold) {
	/* Anchor */
	debug1(printf("Changing status of %u from %d (%s) to %d (MULTIPLET) => ",
		      univdiagonals[i],status[i],status_string(status[i]),count));
	if (status[i] == ADJACENT) {
	  (*nadjacent)--;
	}
	debug1(printf("nadjacent = %d\n",*nadjacent));
	status[i] = count;
	(*nanchors)++;
	
	/* Mark segments ahead (adjacent is lowest status and might be overwritten) */
	while (j + 1 < nunivdiagonals && univdiagonals[j+1] < univdiagonals[i] + overall_max_distance) {
	  debug1(printf("Changing status of %u from %d (%s) to %d (ADJACENT) => ",
			univdiagonals[j+1],status[j+1],status_string(status[j+1]),ADJACENT));
	  if (status[++j] != ADJACENT) {
	    status[j] = ADJACENT;
	    (*nadjacent)++;
	  }
	  debug1(printf("nadjacent = %d\n",*nadjacent));
	}
	
	/* Mark segments behind */
	j = i;
	while (j - 1 >= 0 && univdiagonals[j-1] + overall_max_distance > univdiagonals[i]) {
	  debug1(printf("Changing status of %u from %d (%s) to %d (ADJACENT) => ",
			univdiagonals[j-1],status[j-1],status_string(status[j-1]),ADJACENT));
	  if (status[--j] == SINGLETON) {
	    status[j] = ADJACENT;
	    (*nadjacent)++;
	  }
	  debug1(printf("nadjacent = %d\n",*nadjacent));
	}
      }
    }

    i = newi;
  }

#ifdef DEBUG1
  printf("Status for %d univdiagonals:\n",nunivdiagonals);
  for (i = 0; i < nunivdiagonals; i++) {
    printf("i=%d %d (%s) %u\n",i,status[i],status_string(status[i]),univdiagonals[i]);
  }
  printf("nanchors %d, nadjacent %d\n",*nanchors,*nadjacent);
#endif

#if 0
  nanchors_check = nadjacent_check = 0;
  for (i = 0; i < nunivdiagonals; i++) {
    if (status[i] > 0) {
      nanchors_check += 1;
    } else if (status[i] == ADJACENT) {
      nadjacent_check += 1;
    }
  }
  printf("nanchors_check %d, nadjacent_check %d\n",nanchors_check,nadjacent_check);

  if (*nanchors != nanchors_check) {
    abort();
  }
  if (*nadjacent != nadjacent_check) {
    abort();
  }
#endif

  return status;
}



/* Create targets for intersection, and all_records */
static struct Record_T *
make_records (int *status, Univcoord_T *univdiagonals, int nunivdiagonals, int nrecords,
#ifdef LARGE_GENOMES
	      unsigned char **stream_high_alloc, UINT4 **stream_low_alloc,
#endif
	      Univcoord_T **streamptr_alloc, int *streamsize_alloc, int *diagterm_alloc,
	      int nstreams, bool streams_are_diagonals_p, int querylength) {
  struct Record_T *records;
  Record_T record;
  Univcoord_T *targets, univdiagonal, left;
  int queryoffset, diagterm;
  int *indices;
  int streami, nindices, i, k;

  Chrnum_T chrnum;
  Univcoord_T chroffset, chrhigh = 0, low, high;


  debug1(printf("*** Starting make_records ***\n"));

  records = (struct Record_T *) MALLOC(nrecords*sizeof(struct Record_T));
  targets = (Univcoord_T *) MALLOC(nrecords*sizeof(Univcoord_T));
  indices = MALLOC(nrecords*sizeof(int));

  k = 0;
  for (i = 0; i < nunivdiagonals; i++) {
    if (status[i] > 0) {
      /* status is a count */
      targets[k] = univdiagonals[i];
      records[k].univdiagonal = univdiagonals[i];
      records[k].qstart = -1; /* to indicate that it is not yet set */
      records[k].qend = -1; /* to indicate that it is not yet set */
      records[k].anchorp = true;
      k++;
    } else if (status[i] == ADJACENT) {
      targets[k] = univdiagonals[i];
      records[k].univdiagonal = univdiagonals[i];
      records[k].qstart = -1; /* to indicate that it is not yet set */
      records[k].qend = -1; /* to indicate that it is not yet set */
      records[k].anchorp = false;
      k++;
    } else {
      debug1(printf("Skipping univdiagonal %u\n",univdiagonals[i]));
    }
  }
  
  /* Assign query bounds, represented by streams, to each of the univdiagonals */
  for (streami = 0; streami < nstreams; streami++) {
    debug1(printf("Computing intersection on stream %d with %d positions\n",
		  streami,streamsize_alloc[streami]));
    if (streams_are_diagonals_p == true) {
      nindices = Intersect_exact_indices_univcoord(indices,streamptr_alloc[streami],streamsize_alloc[streami],
						   targets,nrecords);
    } else {
      diagterm = diagterm_alloc[streami];
#ifdef LARGE_GENOMES
      nindices = Intersect_exact_indices_large(indices,stream_high_alloc[streami],stream_low_alloc[streami],
					       streamsize_alloc[streami],diagterm,targets,nrecords);
#else
      nindices = Intersect_exact_indices_small(indices,streamptr_alloc[streami],streamsize_alloc[streami],
					       diagterm,targets,nrecords);
#endif
    }

    diagterm = diagterm_alloc[streami];
    queryoffset = querylength - diagterm;
    for (i = 0; i < nindices; i++) {
      k = indices[i];
      if (records[k].qstart == -1) {
	/* Take first (lowest) value */
	records[k].qstart = queryoffset;
      }
      /* Take last (highest) value */
      records[k].qend = queryoffset + index1part;
      debug1(printf("i=%d k=%d %u revised range: %d..%d\n",
		    i,k,records[k].univdiagonal,records[k].qstart,records[k].qend));
    }
    debug1(printf("\n"));
  }
  
#ifdef DEBUG1
  for (k = 0; k < nrecords; k++) {
    printf("k=%d %u final range: %d..%d\n",
	   k,records[k].univdiagonal,records[k].qstart,records[k].qend);
    assert(records[k].qstart >= 0);
  }
#endif

  FREE(indices);
  FREE(targets);

  chrnum = 1;
#ifdef USE_CHROMOSOME_IIT
  Univ_IIT_interval_bounds(&chroffset,&chrhigh,&chrlength,chromosome_iit,/*chrnum*/1,circular_typeint);
#else
  EF64_chrbounds(&chroffset,&chrhigh,chromosome_ef64,/*chrnum*/1);
#endif

  for (k = 0; k < nrecords; k++) {
    record = &(records[k]);
    univdiagonal = record->univdiagonal;
    left = univdiagonal - querylength;

    low = left + record->qstart;
    high = left + record->qend;

    if (low >= chrhigh) {
      debug(printf("Updated chrnum because low %u >= chrhigh %u.",low,chrhigh));
      chrnum = EF64_chrnum(&chroffset,&chrhigh,chromosome_ef64,low,univdiagonal);
      debug(printf("  New chrnum %d.  New chroffset %u.  New chrhigh %u.\n",
		   chrnum,chroffset,chrhigh));
    }

    if (chroffset > low) {
      /* Could happen because the records are sorted by univdiagonal, not lowpos */
      debug(printf("chroffset %u > left %u + querypos %d => Recomputed chrnum to be ",
		   chroffset,left,record->qstart));
#ifdef USE_CHROMOSOME_IIT
      chrnum = Univ_IIT_get_chrnum(&chroffset,&chrhigh,&chrlength,chromosome_iit,
				   /*low*/left+record->qstart,/*high*/left+record->qend,
				   circular_typeint);
#else
      chrnum = EF64_chrnum(&chroffset,&chrhigh,chromosome_ef64,
			   /*low*/left+record->qstart,/*high*/left+record->qend);
#endif
      debug(printf("%d\n",chrnum));
    }

    if (chroffset > low) {
      record->qstart = (int) (chroffset - left);
      debug(printf("Recomputed qstart to be %d\n",record->qstart));
    }
    if (high >= chrhigh) {
      record->qend = (int) (chrhigh - left);
      debug(printf("Recomputed qend to be %d\n",record->qstart));
    }

    debug1(printf("For univdiagonal %u, making record %d..%d at chrnum %d, chroffset %u, chrhigh %u\n",
		  univdiagonal,record->qstart,record->qend,chrnum,chroffset,chrhigh));
    assert(record->qstart < record->qend);

    record->chrnum = chrnum;
    record->chroffset = chroffset;
    record->chrhigh = chrhigh;
      
    record->lowpos = low;
    record->highpos = high;
  }

  return records;
}



/* gplus_5 and gminus_3 */
struct Record_T *
Segment_identify_lower (int *nrecords,
#ifdef LARGE_GENOMES
			unsigned char **positions_high, UINT4 **positions_low,
#else
			Univcoord_T **positions,
#endif		  
			int *npositions, bool *validp, Oligospace_T *oligos,
			EF64_T repetitive_ef64,

			Univcoord_T **streamptr_alloc, int *streamsize_alloc,
			int *diagterm_alloc, Mergeinfo_T mergeinfo,
			Chrpos_T max_pairlength, Chrpos_T overall_max_distance, int querylength,
			Univcoord_T *ref_diagonals, int ref_ndiagonals, bool plusp) {
  struct Record_T *records;
  int query_lastpos = querylength - index1part;

  int total_npositions, used_npositions, nstreams, streami;

  int querypos, queryoffset;
  Univcoord_T *diagonals_alloc, *diagonals_used, *diagonals, *ptr;
  int *status;
  int ndiagonals, nanchors, nadjacent;


  debug1(printf("*** Starting Segment_identify_lower with plusp %d ***\n",plusp));

  total_npositions = 0;
  for (queryoffset = 0; queryoffset <= query_lastpos; queryoffset++) {
    querypos = (plusp == true) ? queryoffset : (query_lastpos - queryoffset);
    if (validp[querypos] == false) {
      debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		    querypos,npositions[querypos]));
    } else if (EF64_presentp(oligos[querypos],repetitive_ef64) == true) {
      debug1(printf("Skipping batch for querypos %d with %d, but is repetitive\n",
		    querypos,npositions[querypos]));
    } else {
      assert(npositions[querypos] >= 0);
      total_npositions += npositions[querypos];
    }
  }

  if (total_npositions == 0) {
    *nrecords = 0;
    records = (struct Record_T *) NULL;

  } else {
    /* Large allocation */
    debug1(printf("Total npositions = %d\n",total_npositions));
    ptr = diagonals_alloc = (Univcoord_T *) MALLOC(total_npositions * sizeof(Univcoord_T));
    
    used_npositions = 0;
    nstreams = 0;
    for (queryoffset = 0; queryoffset <= query_lastpos; queryoffset++) {
      querypos = (plusp == true) ? queryoffset : (query_lastpos - queryoffset);
      if (validp[querypos] == false) {
	debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		      querypos,npositions[querypos]));
      } else if (EF64_presentp(oligos[querypos],repetitive_ef64) == true) {
	debug1(printf("Skipping batch for querypos %d with %d, but is repetitive\n",
		      querypos,npositions[querypos]));
      } else if (npositions[querypos] <= 0) {
	debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		      querypos,npositions[querypos]));
      } else {
	debug1(printf("(1) Adding batch for querypos %d, plusp %d with %d positions",
		      querypos,plusp,npositions[querypos]));
	ndiagonals = Intersect_approx_lower(ptr,
#ifdef LARGE_GENOMES
					    positions_high[querypos],positions_low[querypos],
#else
					    positions[querypos],
#endif
					    npositions[querypos],/*diagterm*/querylength - queryoffset,
					    ref_diagonals,ref_ndiagonals,max_pairlength);
	if (ndiagonals <= 0) {
	  debug1(printf(" => ndiagonals %d, so skipping\n",ndiagonals));
	} else {
	  debug1(printf(" => stream %d\n",nstreams));
	  /* streamptr_alloc[nstreams] = ptr; -- assigned in positions_used */
	  used_npositions += streamsize_alloc[nstreams] = ndiagonals;
	  ptr += ndiagonals;
	  diagterm_alloc[nstreams++] = querylength - queryoffset;
	}
      }
    }
    debug1(printf("Used npositions = %d\n",used_npositions));
    
    
    if (nstreams == 0) {
      FREE(diagonals_alloc);
      *nrecords = 0;
      records = (struct Record_T *) NULL;
      
    } else {
      /* Move memory to a smaller allocation */
      ptr = diagonals_used = (Univcoord_T *) MALLOC(used_npositions * sizeof(Univcoord_T));
      memcpy(diagonals_used,diagonals_alloc,used_npositions*sizeof(Univcoord_T));
      FREE(diagonals_alloc);
      
      for (streami = 0; streami < nstreams; streami++) {
	streamptr_alloc[streami] = ptr;
	ptr += streamsize_alloc[streami];
      }
      
#ifdef LARGE_GENOMES
      diagonals = Merge_diagonals_uint8(&ndiagonals,streamptr_alloc,streamsize_alloc,nstreams,mergeinfo);
#else
      diagonals = Merge_diagonals_uint4(&ndiagonals,streamptr_alloc,streamsize_alloc,nstreams,mergeinfo);
#endif
      
      if (ndiagonals > MAX_DIAGONALS) {
	/* Skip */
	FREE(diagonals_used);
	*nrecords = 0;
	records = (struct Record_T *) NULL;

      } else {
	status = compute_status_threshold(&nanchors,&nadjacent,diagonals,ndiagonals,overall_max_distance,/*count_threshold*/2);
	if (nanchors == 0) {
	  FREE(diagonals_used);
	  *nrecords = 0;
	  records = (struct Record_T *) NULL;

	} else {
	  *nrecords = nanchors + nadjacent;
#ifdef LARGE_GENOMES
	  records = make_records(status,diagonals,ndiagonals,*nrecords,
				 /*stream_high_alloc*/NULL,/*stream_low_alloc*/NULL,
				 streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,
				 /*streams_are_diagonals_p*/true,querylength);
#else
	  records = make_records(status,diagonals,ndiagonals,*nrecords,
				 streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,
				 /*streams_are_diagonals_p*/true,querylength);
#endif
	  FREE(diagonals_used);
	}
      
	FREE(status);
      }
      
#ifndef LARGE_GENOMES
      FREE_ALIGN(diagonals);
#elif defined(HAVE_AVX512) || defined(HAVE_AVX2)
      FREE_ALIGN(diagonals);
#else
      FREE(diagonals);
#endif
    }
  }

  return records;
}


/* gplus_3 and gminus_5 */
struct Record_T *
Segment_identify_higher (int *nrecords,
#ifdef LARGE_GENOMES
			 unsigned char **positions_high, UINT4 **positions_low,
#else
			 Univcoord_T **positions,
#endif		  
			 int *npositions, bool *validp, Oligospace_T *oligos,
			 EF64_T repetitive_ef64,

			 Univcoord_T **streamptr_alloc, int *streamsize_alloc,
			 int *diagterm_alloc, Mergeinfo_T mergeinfo,
			 Chrpos_T max_pairlength, Chrpos_T overall_max_distance, int querylength,
			 Univcoord_T *ref_diagonals, int ref_ndiagonals, bool plusp) {
  struct Record_T *records;
  int query_lastpos = querylength - index1part;

  int total_npositions, used_npositions, nstreams, streami;
  int querypos, queryoffset;
  Univcoord_T *diagonals_alloc, *diagonals_used, *diagonals, *ptr;
  int *status;
  int ndiagonals, nanchors, nadjacent;


  debug1(printf("*** Starting Segment_identify_higher with plusp %d ***\n",plusp));

  total_npositions = 0;
  for (queryoffset = 0; queryoffset <= query_lastpos; queryoffset++) {
    querypos = (plusp == true) ? queryoffset : (query_lastpos - queryoffset);
    if (validp[querypos] == false) {
      debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		    querypos,npositions[querypos]));
    } else if (EF64_presentp(oligos[querypos],repetitive_ef64) == true) {
      debug1(printf("Skipping batch for querypos %d with %d positions, but is repetitive\n",
		    querypos,npositions[querypos]));
    } else {
      assert(npositions[querypos] >= 0);
      total_npositions += npositions[querypos];
    }
  }

  if (total_npositions == 0) {
    *nrecords = 0;
    records = (struct Record_T *) NULL;
  } else {
    /* Large allocation */
    ptr = diagonals_alloc = (Univcoord_T *) MALLOC(total_npositions * sizeof(Univcoord_T));
    
    used_npositions = 0;
    nstreams = 0;
    for (queryoffset = 0; queryoffset <= query_lastpos; queryoffset++) {
      querypos = (plusp == true) ? queryoffset : (query_lastpos - queryoffset);
      if (validp[querypos] == false) {
	debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		      querypos,npositions[querypos]));
      } else if (EF64_presentp(oligos[querypos],repetitive_ef64) == true) {
	debug1(printf("Skipping batch for querypos %d with %d positions, but is repetitive\n",
		      querypos,npositions[querypos]));
      } else if (npositions[querypos] <= 0) {
	debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		      querypos,npositions[querypos]));
      } else {
	debug1(printf("(2) Adding batch for querypos %d, plusp %d with %d positions",
		      querypos,plusp,npositions[querypos]));
	ndiagonals = Intersect_approx_higher(ptr,
#ifdef LARGE_GENOMES
					     positions_high[querypos],positions_low[querypos],
#else
					     positions[querypos],
#endif
					     npositions[querypos],/*diagterm*/querylength - queryoffset,
					     ref_diagonals,ref_ndiagonals,max_pairlength);
	if (ndiagonals <= 0) {
	  debug1(printf(" => ndiagonals %d, so skipping\n",ndiagonals));
	} else {
	  debug1(printf(" => stream %d\n",nstreams));
	  /* streamptr_alloc[nstreams] = ptr; -- assigned in positions_used */
	  used_npositions += streamsize_alloc[nstreams] = ndiagonals;
	  ptr += ndiagonals;
	  diagterm_alloc[nstreams++] = querylength - queryoffset;
	}
      }
    }
    debug1(printf("Used npositions = %d\n",used_npositions));
    
    
    if (nstreams == 0) {
      FREE(diagonals_alloc);
      *nrecords = 0;
      records = (struct Record_T *) NULL;
      
    } else {
      /* Move memory to a smaller allocation */
      ptr = diagonals_used = (Univcoord_T *) MALLOC(used_npositions * sizeof(Univcoord_T));
      memcpy(diagonals_used,diagonals_alloc,used_npositions*sizeof(Univcoord_T));
      FREE(diagonals_alloc);
      
      for (streami = 0; streami < nstreams; streami++) {
	streamptr_alloc[streami] = ptr;
	ptr += streamsize_alloc[streami];
      }
      
#ifdef LARGE_GENOMES
      diagonals = Merge_diagonals_uint8(&ndiagonals,streamptr_alloc,streamsize_alloc,nstreams,mergeinfo);
#else
      diagonals = Merge_diagonals_uint4(&ndiagonals,streamptr_alloc,streamsize_alloc,nstreams,mergeinfo);
#endif
      
      if (ndiagonals > MAX_DIAGONALS) {
	/* Skip */
	FREE(diagonals_used);
	*nrecords = 0;
	records = (struct Record_T *) NULL;

      } else {
	status = compute_status_threshold(&nanchors,&nadjacent,diagonals,ndiagonals,overall_max_distance,/*count_threshold*/2);
	if (nanchors == 0) {
	  FREE(diagonals_used);
	  *nrecords = 0;
	  records = (struct Record_T *) NULL;
	} else {
	  *nrecords = nanchors + nadjacent;
#ifdef LARGE_GENOMES
	  records = make_records(status,diagonals,ndiagonals,*nrecords,
				 /*stream_high_alloc*/NULL,/*stream_low_alloc*/NULL,
				 streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,
				 /*streams_are_diagonals_p*/true,querylength);
#else
	  records = make_records(status,diagonals,ndiagonals,*nrecords,
				 streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,
				 /*streams_are_diagonals_p*/true,querylength);
#endif
	  FREE(diagonals_used);
	}
	FREE(status);
      }

#ifndef LARGE_GENOMES
      FREE_ALIGN(diagonals);
#elif defined(HAVE_AVX512) || defined(HAVE_AVX2)
      FREE_ALIGN(diagonals);
#else
      FREE(diagonals);
#endif
    }
  }

  return records;
}


struct Record_T *
Segment_identify (int *nrecords,
#ifdef LARGE_GENOMES
		  unsigned char **positions_high,
#endif		  
		  UINT4 **positions, int *npositions, bool *validp, Oligospace_T *oligos,
		  EF64_T repetitive_ef64,

#ifdef LARGE_GENOMES
		  unsigned char **stream_high_alloc, UINT4 **stream_low_alloc,
#else
		  Univcoord_T **streamptr_alloc,
#endif
		  int *streamsize_alloc, int *diagterm_alloc, Mergeinfo_T mergeinfo,
		  Chrpos_T overall_max_distance, int querylength, int sizelimit,
		  bool plusp) {
  struct Record_T *records;
  int query_lastpos = querylength - index1part;

  int total_npositions = 0, nstreams;

  int querypos, queryoffset;
  Univcoord_T *diagonals;
  int ndiagonals;
  int *status;
  int nanchors, nadjacent;


  debug1(printf("*** Starting Segment_identify with plusp %d ***\n",plusp));

  nstreams = 0;
  for (queryoffset = 0; queryoffset <= query_lastpos; queryoffset++) {
    querypos = (plusp == true) ? queryoffset : (query_lastpos - queryoffset);
    if (sizelimit > 0 && npositions[querypos] > sizelimit) {
      debug1(printf("Skipping batch for querypos %d with %d positions, over sizelimit %d\n",
		    querypos,npositions[querypos],sizelimit));
#if 0
    } else if (npositions[querypos] > ABSOLUTE_LIMIT) {
      debug1(printf("Skipping batch for querypos %d with %d positions, over absolute_limit %d\n",
		    querypos,npositions[querypos],ABSOLUTE_LIMIT));
#endif
    } else if (validp[querypos] == false) {
      debug1(printf("Skipping batch for querypos %d with %d positions, but not valid\n",
		    querypos,npositions[querypos]));
    } else if (EF64_presentp(oligos[querypos],repetitive_ef64) == true) {
      debug1(printf("Skipping batch for querypos %d with %d positions, but is repetitive\n",
		    querypos,npositions[querypos]));
    } else if (npositions[querypos] > 0) {
      debug1(printf("(3) Adding batch for querypos %d, plusp %d with %d positions => stream %d\n",
		    querypos,plusp,npositions[querypos],nstreams));
      
#ifdef LARGE_GENOMES
      stream_high_alloc[nstreams] = positions_high[querypos];
      stream_low_alloc[nstreams] = positions[querypos];
#else
      streamptr_alloc[nstreams] = positions[querypos];
#endif      
      streamsize_alloc[nstreams] = npositions[querypos];
      diagterm_alloc[nstreams] = querylength - queryoffset;
	
      total_npositions += npositions[querypos];
      nstreams++;
    } else {
      /* debug1(printf("Not adding batch for querypos %d with %d positions\n",querypos,npositions[querypos])); */
    }
  }
  debug1(printf("Initial total_npositions = %d\n",total_npositions));

  if (nstreams == 0) {
    *nrecords = 0;
    records = (struct Record_T *) NULL;
  } else {
#ifdef LARGE_GENOMES
    diagonals = Merge_diagonals_large(&ndiagonals,stream_high_alloc,stream_low_alloc,
				      streamsize_alloc,diagterm_alloc,nstreams,mergeinfo);
#else
    diagonals = Merge_diagonals(&ndiagonals,streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,mergeinfo);
#endif

    if (ndiagonals > MAX_DIAGONALS) {
      /* Skip */
      *nrecords = 0;
      records = (struct Record_T *) NULL;
    } else {
      status = compute_status_greedy(&nanchors,&nadjacent,diagonals,ndiagonals,overall_max_distance);
      if (nanchors == 0) {
	*nrecords = 0;
	records = (struct Record_T *) NULL;
      } else {
	*nrecords = nanchors + nadjacent;
#ifdef LARGE_GENOMES
	records = make_records(status,diagonals,ndiagonals,*nrecords,
			       stream_high_alloc,stream_low_alloc,
			       /*streamptr_alloc*/NULL,streamsize_alloc,diagterm_alloc,nstreams,
			       /*streams_are_diagonals_p*/false,querylength);
#else
	records = make_records(status,diagonals,ndiagonals,*nrecords,
			       streamptr_alloc,streamsize_alloc,diagterm_alloc,nstreams,
			       /*streams_are_diagonals_p*/false,querylength);
#endif
      }
      FREE(status);
    }

#ifndef LARGE_GENOMES
    FREE_ALIGN(diagonals);
#elif defined(HAVE_AVX512) || defined(HAVE_AVX2)
    FREE_ALIGN(diagonals);
#else
    FREE(diagonals);
#endif
  }

  return records;
}


/* Generic, for either plus or minus genomic strand */
static void
solve_all (int *found_score, bool *found_transcriptp,
	   List_T *unextended_sense_paths, List_T *unextended_antisense_paths,
	   List_T *sense_paths, List_T *antisense_paths,
	   struct Record_T *records, int nrecords,
	   Shortread_T queryseq, char *queryptr, int querylength,
	   int *mismatch_positions_alloc,
	   Univcoord_T *novel_diagonals_alloc, unsigned short *localdb_alloc,
	   int streamspace_max_alloc, Univcoord_T *streamspace_alloc,
	   Univcoord_T **streamptr_alloc, int *streamsize_alloc, Mergeinfo_T mergeinfo,

	   Indelinfo_T indelinfo, Spliceinfo_T spliceinfo,
	   Knownsplicing_T knownsplicing, Knownindels_T knownindels,
	   Compress_T query_compress, Compress_T query_compress_fwd, Compress_T query_compress_rev,
	   int max_insertionlen, int max_deletionlen, int nmismatches_allowed,
	   Chrpos_T overall_max_distance, Chrpos_T overall_end_distance,
	   bool plusp, int genestrand, bool paired_end_p, bool first_read_p,
	   Univdiagpool_T univdiagpool, Intlistpool_T intlistpool,
	   Uintlistpool_T uintlistpool, Univcoordlistpool_T univcoordlistpool,
	   Listpool_T listpool, Pathpool_T pathpool, Transcriptpool_T transcriptpool,
	   Vectorpool_T vectorpool, Hitlistpool_T hitlistpool, Spliceendsgen_T spliceendsgen,
	   Method_T method, Pass_T pass) {

  Record_T anchor_segment, segment;
  int anchori, startk, endk, n, i, firstj, lastj, j, k;

  Univdiag_T middle_diagonal;
  List_T right_univdiags, left_univdiags;

  Chrnum_T chrnum;

  Record_T *sorted5, *sorted5_allocated, *sorted3, *sorted3_allocated;

  Univcoord_T univdiagonal;


  debug(printf("Entered solve_all, plusp %d, with %d records\n",plusp,nrecords));
  assert(nrecords > 0);

  sorted5_allocated = (Record_T *) MALLOC(nrecords*sizeof(Record_T));
  sorted3_allocated = (Record_T *) MALLOC(nrecords*sizeof(Record_T));


  for (anchori = 0; anchori < nrecords; anchori++) {
    if (records[anchori].anchorp == true) {
      anchor_segment = &(records[anchori]);
      univdiagonal = anchor_segment->univdiagonal;

      /* left is safer than anchor_segment->lowpos, in case trim goes further left than lowpos */
      chrnum = anchor_segment->chrnum;
    
      j = anchori - 1;
      while (j >= 0 && univdiagonal <= records[j].univdiagonal + overall_max_distance && records[j].chrnum == chrnum) {
	j--;
      }
      startk = j + 1;

      j = anchori + 1;
      while (j < nrecords && records[j].univdiagonal <= univdiagonal + overall_max_distance && records[j].chrnum == chrnum) {
	j++;
      }
      endk = j - 1;

      debug13(printf("Found records %d to %d inclusive for anchor #%d, %d..%d %u, chrnum %d, chrpos %u (range %u..%u)\n",
		     startk,endk,anchori,anchor_segment->qstart,anchor_segment->qend,
		     anchor_segment->univdiagonal,chrnum,anchor_segment->univdiagonal - anchor_segment->chroffset,
		     anchor_segment->lowpos,anchor_segment->highpos));
#ifdef DEBUG13
      for (k = startk; k <= endk; k++) {
	printf("  #%d: %d..%d %u (%u)\n",k,records[k].qstart,records[k].qend,
	       records[k].univdiagonal,records[k].univdiagonal - anchor_segment->chroffset);
      }
      printf("\n");
#endif

      n = endk - startk + 1;
      debug13(printf("n = %d\n",n));
      sorted5 = &(sorted5_allocated[startk]);
      sorted3 = &(sorted3_allocated[startk]);

      /* middle_path = (List_T) NULL; */
      right_univdiags = left_univdiags = (List_T) NULL;

      /* Add diagonals to left (low) side (qpos5) */
      for (k = startk, i = 0; k <= endk; k++) {
	sorted5[i++] = &(records[k]);
      }
      qsort(sorted5,n,sizeof(Record_T),Record_qpos5_ascending_cmp);
#ifdef DEBUG13
      printf("Sorted by qpos5\n");
      for (i = 0; i < n; i++) {
	printf("  #%d: %d..%d %u\n",i,sorted5[i]->qstart,sorted5[i]->qend,sorted5[i]->univdiagonal);
      }
#endif

      /* Skip obvious ones */
      lastj = 0;
      while (lastj < n && sorted5[lastj]->qstart < anchor_segment->qstart) {
	lastj++;
      }
#ifdef DEBUG13
      printf("On the qpos5 side, considering:\n");
      for (i = 0; i < lastj; i++) {
	printf("  #%d: %d..%d univdiagonal %u, range %u..%u\n",
	       i,sorted5[i]->qstart,sorted5[i]->qend,sorted5[i]->univdiagonal,sorted5[i]->lowpos,sorted5[i]->highpos);
      }
#endif

      /* Go leftward to favor shorter splices */
      debug13(printf("Trying to append to start of anchor segment\n"));
      for (j = lastj - 1; j >= 0; --j) {
	segment = sorted5[j];
	debug13(printf("ANCHOR SEGMENT %d..%d univdiagonal %u (range %u..%u)\n",
		       anchor_segment->qstart,anchor_segment->qend,anchor_segment->univdiagonal,anchor_segment->lowpos,anchor_segment->highpos));
	debug13(printf("(3) TRIAL SEGMENT ON 5' %d..%d univdiagonal %u (range %u..%u)\n",
		       segment->qstart,segment->qend,segment->univdiagonal,segment->lowpos,segment->highpos));
	
	if (segment->lowpos >= anchor_segment->lowpos + max_deletionlen) {
	  debug13(printf("(2) SKIPPING, SINCE TRIAL DOESN'T ADD NUCLEOTIDES TO THE LEFT OF ANCHOR\n"));
	  
	} else if (segment->lowpos >= anchor_segment->lowpos && segment->highpos <= anchor_segment->highpos) {
	  debug13(printf("(2) SKIPPING, SINCE ANCHOR SUBSUMES TRIAL (PROBABLE GENOMIC REPEAT)\n"));
	  
	} else if (anchor_segment->lowpos >= segment->lowpos && anchor_segment->highpos <= segment->highpos) {
	  debug13(printf("(2) SKIPPING, SINCE TRIAL SUBSUMES ANCHOR (PROBABLE GENOMIC REPEAT)\n"));
	  
#if 0
	} else if (segment->highpos < anchor_segment->chroffset) {
	  /* Now handled by path-solve, which accounts for the best chromosome for the anchor segment */
	  debug13(printf("Not allowing left diagonal that goes into next chromosome\n"));
#endif
	  
	} else {
	  debug13(printf("Candidate for start diagonal\n"));
	  if (segment->univdiagonal + segment->qstart >= univdiagonal + anchor_segment->qstart) {
	    debug13(printf("Not allowing left diagonal that doesn't advance min_genomepos\n"));
	  } else if ((segment->qend - anchor_segment->qstart) > (segment->qend - segment->qstart) / 2) {
	    debug13(printf("Not allowing left diagonal that mainly overlaps middle diagonal\n"));
	  } else if (Record_overlap_p(segment,anchor_segment) == true) {
	    debug13(printf("Not allowing left diagonal that overlaps anchor diagonal\n"));
	  } else {
	    debug13(printf("Left diagonal is allowable: %u..%u vs chromosome %u..%u\n",
			   segment->lowpos,segment->highpos,segment->chroffset,segment->chrhigh));
	    left_univdiags = Univdiagpool_push(left_univdiags,univdiagpool,
					       segment->qstart,segment->qend,/*nmismatches*/-1,
					       segment->univdiagonal
					       univdiagpool_trace(__FILE__,__LINE__));
	  }
	}
      }


      /* Add diagonals to right (high) side (qpos3) */
      for (k = startk, i = 0; k <= endk; k++) {
	sorted3[i++] = &(records[k]);
      }
      qsort(sorted3,n,sizeof(Record_T),Record_qpos3_ascending_cmp);
#ifdef DEBUG13
      printf("Sorted by qpos3\n");
      for (i = 0; i < n; i++) {
	printf("  #%d: %d..%d %u\n",i,sorted3[i]->qstart,sorted3[i]->qend,sorted3[i]->univdiagonal);
      }
#endif
      
      /* Skip obvious ones */
      firstj = n - 1;
      while (firstj >= 0 && sorted3[firstj]->qend > anchor_segment->qend) {
	firstj--;
      }
#ifdef DEBUG13
      printf("On the qpos3 side, considering:\n");
      for (j = firstj + 1; j < n; j++) {
	printf("  #%d: %d..%d univdiagonal %u, range %u..%u\n",
	       i,sorted3[j]->qstart,sorted3[j]->qend,sorted3[j]->univdiagonal,sorted3[j]->lowpos,sorted3[j]->highpos);
      }
#endif
      
      /* Go rightward to favor shorter splices */
      debug13(printf("Trying to append to end of anchor segment\n"));
      for (j = firstj + 1; j < n; j++) {
	segment = sorted3[j];
	debug13(printf("ANCHOR SEGMENT %d..%d univdiagonal %u (range %u..%u)\n",
		       anchor_segment->qstart,anchor_segment->qend,anchor_segment->univdiagonal,anchor_segment->lowpos,anchor_segment->highpos));
	debug13(printf("(4) TRIAL SEGMENT ON 3' %d..%d univdiagonal %u (range %u..%u)\n",
		       segment->qstart,segment->qend,segment->univdiagonal,segment->lowpos,segment->highpos));
	
	if (segment->highpos + max_deletionlen <= anchor_segment->highpos) {
	  debug13(printf("(4) SKIPPING, SINCE TRIAL DOESN'T ADD NUCLEOTIDES TO THE RIGHT OF ANCHOR\n"));
	  
	} else if (segment->lowpos >= anchor_segment->lowpos && segment->highpos <= anchor_segment->highpos) {
	  debug13(printf("(4) SKIPPING, SINCE ANCHOR SUBSUMES TRIAL (PROBABLE GENOMIC REPEAT)\n"));
	  
	} else if (anchor_segment->lowpos >= segment->lowpos && anchor_segment->highpos <= segment->highpos) {
	  debug13(printf("(4) SKIPPING, SINCE TRIAL SUBSUMES ANCHOR (PROBABLE GENOMIC REPEAT)\n"));
	  
#if 0
	} else if (segment->lowpos >= chrhigh) {
	  /* Now handled by path-solve, which accounts for the best chromosome for the anchor segment */
	  debug13(printf("Not allowing right diagonal that goes into next chromosome\n"));
#endif
	  
	} else {
	  debug13(printf("Candidate for end diagonal\n"));
	  if (segment->univdiagonal + segment->qend <= univdiagonal + anchor_segment->qend) {
	    debug13(printf("Not allowing right diagonal that doesn't advance max_genomepos\n"));
	  } else if ((anchor_segment->qend - segment->qstart) > (segment->qend - segment->qstart) / 2) {
	    debug13(printf("Not allowing right diagonal that mainly overlaps anchor diagonal\n"));
	  } else if (Record_overlap_p(segment,anchor_segment) == true) {
	    debug13(printf("Not allowing right diagonal that overlaps anchor diagonal\n"));
	  } else {
	    debug13(printf("Right diagonal is allowable: %u..%u vs chromosome %u..%u\n",
			   segment->lowpos,segment->highpos,segment->chroffset,segment->chrhigh));
	    right_univdiags = Univdiagpool_push(right_univdiags,univdiagpool,
						segment->qstart,segment->qend,/*nmismatches*/-1,
						segment->univdiagonal
						univdiagpool_trace(__FILE__,__LINE__));
	  }
	}
      }
      
      /* Compute middle diagonal */
      debug13(printf("Anchor diagonal %llu, querypos %d..%d\n",
		     (unsigned long long) anchor_segment->univdiagonal,anchor_segment->qstart,anchor_segment->qend));
      middle_diagonal = Univdiagpool_new_univdiag(univdiagpool,anchor_segment->qstart,anchor_segment->qend,/*nmismatches*/-1,
						  anchor_segment->univdiagonal
						  univdiagpool_trace(__FILE__,__LINE__));
      
      /* TODO: Use localdb to add left and right diagonals.  Then modify
	 Path_solve_from_diagonals to handle a path, and call that */
      debug13(printf("SOLVING A PATH WITH %d LEFT, ONE MIDDLE, and %d RIGHT DIAGONALS\n\n",
		     List_length(left_univdiags),List_length(right_univdiags)));
      
      Path_solve_from_diagonals(&(*found_score),&(*found_transcriptp),
				&(*unextended_sense_paths),&(*unextended_antisense_paths),
				&(*sense_paths),&(*antisense_paths),
				middle_diagonal->univdiagonal,middle_diagonal->qstart,middle_diagonal->qend,
				/*middle_nmismatches:unknown*/-1,
				/*qend_univdiags*/right_univdiags,/*qstart_univdiags*/left_univdiags,
				queryseq,queryptr,querylength,
				mismatch_positions_alloc,novel_diagonals_alloc,localdb_alloc,
				streamspace_max_alloc,streamspace_alloc,streamptr_alloc,streamsize_alloc,mergeinfo,
				indelinfo,spliceinfo,knownsplicing,knownindels,
				query_compress,query_compress_fwd,query_compress_rev,
				chrnum,anchor_segment->chroffset,anchor_segment->chrhigh,
				plusp,genestrand,max_insertionlen,max_deletionlen,nmismatches_allowed,
				overall_end_distance,paired_end_p,first_read_p,
				intlistpool,uintlistpool,univcoordlistpool,listpool,
				pathpool,transcriptpool,vectorpool,hitlistpool,spliceendsgen,method,pass);

      debug13(printf("DONE\n"));
      Univdiagpool_free_univdiag(&middle_diagonal,univdiagpool
				 univdiagpool_trace(__FILE__,__LINE__)); /* allocated by Univdiagpool_new_univdiag */
      Univdiagpool_gc(&right_univdiags,univdiagpool
		      univdiagpool_trace(__FILE__,__LINE__)); /* allocated by Univdiagpool_push */
      Univdiagpool_gc(&left_univdiags,univdiagpool
		      univdiagpool_trace(__FILE__,__LINE__)); /* allocated by Univdiagpool_push */
    }
  }

  FREE(sorted3_allocated);
  FREE(sorted5_allocated);

  debug(printf("Returning from solve_all\n"));

  return;
}


void
Segment_search_all (int *found_score, bool *found_transcriptp,

		    List_T *unextended_sense_paths_gplus, List_T *unextended_sense_paths_gminus,
		    List_T *unextended_antisense_paths_gplus, List_T *unextended_antisense_paths_gminus,
		    
		    List_T *sense_paths_gplus, List_T *sense_paths_gminus,
		    List_T *antisense_paths_gplus, List_T *antisense_paths_gminus,

		    struct Record_T *plus_records, int plus_nrecords, 
		    struct Record_T *minus_records, int minus_nrecords,

		    Shortread_T queryseq, char *queryuc_ptr, char *queryrc, int querylength,
		    int *mismatch_positions_alloc, Univcoord_T *novel_diagonals_alloc, unsigned short *localdb_alloc,

		    Stage1_T stage1, Knownsplicing_T knownsplicing, Knownindels_T knownindels,
		    Compress_T query_compress_fwd, Compress_T query_compress_rev,
		    int max_insertionlen, int max_deletionlen, int nmismatches_allowed,
		    Chrpos_T overall_max_distance, Chrpos_T overall_end_distance,
		    int genestrand, bool paired_end_p, bool first_read_p,
		    Univdiagpool_T univdiagpool, Intlistpool_T intlistpool,
		    Uintlistpool_T uintlistpool, Univcoordlistpool_T univcoordlistpool,
		    Listpool_T listpool, Pathpool_T pathpool, Transcriptpool_T transcriptpool,
		    Vectorpool_T vectorpool, Hitlistpool_T hitlistpool, Spliceendsgen_T spliceendsgen,
		    Method_T method, Pass_T pass) {

  if (plus_records != NULL) {
    solve_all(&(*found_score),&(*found_transcriptp),
	      &(*unextended_sense_paths_gplus),&(*unextended_antisense_paths_gplus),
	      &(*sense_paths_gplus),&(*antisense_paths_gplus),
	      plus_records,plus_nrecords,
	      queryseq,/*queryptr*/queryuc_ptr,querylength,
	      mismatch_positions_alloc,novel_diagonals_alloc,localdb_alloc,
	      stage1->streamspace_max_alloc,stage1->streamspace_alloc,
	      stage1->streamptr_alloc,stage1->streamsize_alloc,stage1->mergeinfo,
	      stage1->indelinfo,stage1->spliceinfo,knownsplicing,knownindels,
	      /*query_compress*/query_compress_fwd,query_compress_fwd,query_compress_rev,
	      max_insertionlen,max_deletionlen,nmismatches_allowed,
	      overall_max_distance,overall_end_distance,
	      /*plusp*/true,genestrand,paired_end_p,first_read_p,
	      univdiagpool,intlistpool,uintlistpool,univcoordlistpool,listpool,
	      pathpool,transcriptpool,vectorpool,hitlistpool,spliceendsgen,method,pass);
  }
    
  if (minus_records != NULL) {
    solve_all(&(*found_score),&(*found_transcriptp),
	      &(*unextended_sense_paths_gminus),&(*unextended_antisense_paths_gminus),
	      &(*sense_paths_gminus),&(*antisense_paths_gminus),
	      minus_records,minus_nrecords,
	      queryseq,/*queryptr*/queryrc,querylength,
	      mismatch_positions_alloc,novel_diagonals_alloc,localdb_alloc,
	      stage1->streamspace_max_alloc,stage1->streamspace_alloc,
	      stage1->streamptr_alloc,stage1->streamsize_alloc,stage1->mergeinfo,
	      stage1->indelinfo,stage1->spliceinfo,knownsplicing,knownindels,
	      /*query_compress*/query_compress_rev,query_compress_fwd,query_compress_rev,
	      max_insertionlen,max_deletionlen,nmismatches_allowed,
	      overall_max_distance,overall_end_distance,
	      /*plusp*/false,genestrand,paired_end_p,first_read_p,
	      univdiagpool,intlistpool,uintlistpool,univcoordlistpool,listpool,
	      pathpool,transcriptpool,vectorpool,hitlistpool,spliceendsgen,method,pass);
  }

  return;
}


void
Segment_search_setup (int index1part_in, int index1interval_in,
		      int max_anchors_in, Univ_IIT_T chromosome_iit_in, int nchromosomes_in,
		      int circular_typeint_in, EF64_T chromosome_ef64_in, Mode_T mode_in) {

  index1part = index1part_in;
  index1interval = index1interval_in;
  max_anchors = max_anchors_in;

  nchromosomes = nchromosomes_in;
  chromosome_iit = chromosome_iit_in;
  circular_typeint = circular_typeint_in;
  chromosome_ef64 = chromosome_ef64_in;

  /* Univ_IIT_intervals_setup(&chroffsets,&chrhighs,&chrlengths,chromosome_iit,nchromosomes,circular_typeint); */

#ifdef HAVE_64_BIT
  leftreadshift = 64 - index1part - index1part;
  oligobase_mask = ~(~ (Oligospace_T) 0 << 2*index1part);
#else
  leftreadshift = 32 - index1part - index1part;
  oligobase_mask = ~(~ (Oligospace_T) 0 << 2*index1part);
#endif

  mode = mode_in;

  return;
}

void
Segment_search_cleanup () {
  return;
}
