static char rcsid[] = "$Id$";
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Code is modified from the SIMDCompressionAndIntersection package,
   by Daniel Lemire, Leonid Boytsov, and Nathan Kurz, file
   intersection.cpp.  */

/**
 * This code is released under the
 * Apache License Version 2.0 http://www.apache.org/licenses/.
 *
 */

#include "assert.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>		/* For memcpy */
#include "mem.h"
#include "intersect-approx-simd.h"

#include "simd.h"

#if 0
#ifdef HAVE_SSE2
#include <emmintrin.h>
#endif
#ifdef HAVE_SSE4_1
#include <smmintrin.h>
#endif
#ifdef HAVE_AVX2
#include <immintrin.h>
#endif
#endif


#define INCLUDE_EXACT 1

#define EPI16_MAX 0x8000
#define EPI32_MAX 0x80000000


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

/* Intersect_approx_old */
#ifdef DEBUG9
#define debug9(x) x
#else
#define debug9(x)
#endif

/* Check SIMD against non-SIMD */
#ifdef DEBUG15
#define debug15(x) x
#else
#define debug15(x)
#endif

/* binary search */
#ifdef DEBUG20
#define debug20(x) x
#else
#define debug20(x)
#endif



#if defined(HAVE_AVX2) && defined(DEBUG)
static void
print_vector_signed (__m256i x) {
  printf("%10d %10d %10d %10d %10d %10d %10d %10d\n",
	 _mm256_extract_epi32(x,0) + EPI32_MAX,
	 _mm256_extract_epi32(x,1) + EPI32_MAX,
	 _mm256_extract_epi32(x,2) + EPI32_MAX,
	 _mm256_extract_epi32(x,3) + EPI32_MAX,
	 _mm256_extract_epi32(x,4) + EPI32_MAX,
	 _mm256_extract_epi32(x,5) + EPI32_MAX,
	 _mm256_extract_epi32(x,6) + EPI32_MAX,
	 _mm256_extract_epi32(x,7) + EPI32_MAX);
  return;
}

static void
print_vector_unsigned (__m256i x) {
  printf("%10u %10u %10u %10u %10u %10u %10u %10u\n",
	 _mm256_extract_epi32(x,0),
	 _mm256_extract_epi32(x,1),
	 _mm256_extract_epi32(x,2),
	 _mm256_extract_epi32(x,3),
	 _mm256_extract_epi32(x,4),
	 _mm256_extract_epi32(x,5),
	 _mm256_extract_epi32(x,6),
	 _mm256_extract_epi32(x,7));
  return;
}

static void
print_vector_hex (__m256i x) {
  printf("%08X %08X %08X %08X %08X %08X %08X %08X\n",
	 _mm256_extract_epi32(x,0),
	 _mm256_extract_epi32(x,1),
	 _mm256_extract_epi32(x,2),
	 _mm256_extract_epi32(x,3),
	 _mm256_extract_epi32(x,4),
	 _mm256_extract_epi32(x,5),
	 _mm256_extract_epi32(x,6),
	 _mm256_extract_epi32(x,7));
  return;
}
#endif


/* Fast scalar scheme designed by N. Kurz. */
/* Delta is required to be added to A to become equivalent with B */
/* Desired result should be A + diagtermA (== B + diagtermB) */
static unsigned int *
scalar (unsigned int **diagonals, unsigned int **endDiagonals, unsigned int *out,
	const unsigned int *A, const unsigned int *endA,
	const unsigned int *B, const unsigned int *endB, int deltaA,
	int diagtermA, int diagtermB, const unsigned int slop) {

  const unsigned int *B_save;
  int allocation, ndiagonals;
  unsigned int *more_diagonals;

  debug(printf("Entered scalar with %lu and %lu entries\n",endA - A,endB - B));

  while (A < endA) {
    while (B < endB && (*B) + slop < (*A) + deltaA) {
      B++;
    }

    B_save = B;
    debug(printf("Advancing B from %d up to %d\n",*B,(*A) + deltaA + slop));
    while (B < endB && (*B) <= (*A) + deltaA + slop) {
      debug(printf("(1) Generating output %u %u\n",(*A) + diagtermA,(*B) + diagtermB));
      if (out >= *endDiagonals) {
	allocation = (*endDiagonals) - (*diagonals);
	debug(printf("Reallocating from diagonals %p to end %p (allocation %d)\n",
		     *diagonals,*endDiagonals,allocation));
	more_diagonals = (unsigned int *) MALLOC(2 * allocation * sizeof(unsigned int));

	ndiagonals = out - (*diagonals);
	debug(printf("Copying from diagonals %p to out %p (ndiagonals %d)\n",
		     *diagonals,out,ndiagonals));
	memcpy(more_diagonals,*diagonals,ndiagonals * sizeof(unsigned int));

	*diagonals = more_diagonals;
	*endDiagonals = &(more_diagonals[2 * allocation]);
	out = &(more_diagonals[ndiagonals]);
      }
	
      assert(out + 1 < *endDiagonals);
#ifdef INCLUDE_EXACT
      *out++ = (*A) + diagtermA;
      *out++ = (*B) + diagtermB;
#else
      if ((*A) + deltaA != (*B)) {
	*out++ = (*A) + diagtermA;
	*out++ = (*B) + diagtermB;
      }
#endif
      B++;
    }
    B = B_save;

    A++;
  }

  return out;
}


static inline unsigned int *
scalar_one_set (unsigned int **diagonals, unsigned int **endDiagonals, unsigned int *out,
		const unsigned int *A, const unsigned int *B, const unsigned int *endB,
		int deltaA, int diagtermA, int diagtermB, const unsigned int slop) {
  int allocation, ndiagonals;
  unsigned int *more_diagonals;

  debug(printf("Entered scalar_one_set with %lu entries\n",endB - B));

  debug(printf("Advancing B from %d up to %d\n",*B,(*A) + deltaA + slop));
  while (B < endB && (*B) <= (*A) + deltaA + slop) {
    debug(printf("(2) Generating output %u %u\n",(*A) + diagtermA,(*B) + diagtermB));

    if (out >= *endDiagonals) {
      allocation = (*endDiagonals) - (*diagonals);
      debug(printf("Reallocating from diagonals %p to end %p (allocation %d)\n",
		   *diagonals,*endDiagonals,allocation));
      more_diagonals = (unsigned int *) MALLOC(2 * allocation * sizeof(unsigned int));

      ndiagonals = out - (*diagonals);
	debug(printf("Copying from diagonals %p to out %p (ndiagonals %d)\n",
		     *diagonals,out,ndiagonals));
      memcpy(more_diagonals,*diagonals,ndiagonals * sizeof(unsigned int));

      *diagonals = more_diagonals;
      *endDiagonals = &(more_diagonals[2 * allocation]);
      out = &(more_diagonals[ndiagonals]);
    }

    assert(out + 1 < *endDiagonals);
#ifdef INCLUDE_EXACT
    *out++ = (*A) + diagtermA;
    *out++ = (*B) + diagtermB;
#else
    if ((*A) + deltaA != (*B)) {
      *out++ = (*A) + diagtermA;
      *out++ = (*B) + diagtermB;
    }
#endif
    B++;
  }

  return out;
}


#ifdef __GNUC__
#define COMPILER_LIKELY(x) __builtin_expect((x), 1)
#define COMPILER_RARELY(x) __builtin_expect((x), 0)
#else
#define COMPILER_LIKELY(x) x
#define COMPILER_RARELY(x) x
#endif

/**
 * Intersections scheme designed by N. Kurz that works very
 * well when intersecting an array with another where the density
 * differential is small (between 2 to 10).
 *
 * It assumes that lenRare <= lenFreq.
 *
 * Note that this is not symmetric: flipping the rare and freq pointers
 * as well as lenRare and lenFreq could lead to significant performance
 * differences.
 *
 * The matchOut pointer can safely be equal to the rare pointer.
 *
 */

static int match_start[256];
static int match_n[256];


static void
initialize_match_values () {
  int high_bit, low_bit, bit;
  int idx;

  for (idx = 0; idx < 256; idx++) {
    match_start[idx] = -1;
    match_n[idx] = 0;
  }

  for (high_bit = 0; high_bit < 8; high_bit++) {
    for (low_bit = 0; low_bit <= high_bit; low_bit++) {
      idx = 0;
      for (bit = low_bit; bit <= high_bit; bit++) {
	idx |= (1 << bit);
      }
      match_start[idx] = low_bit;
      match_n[idx] = high_bit - low_bit + 1;
      debug(printf("%d %d %d\n",idx,match_start[idx],match_n[idx]));
    }
  }
      
  return;
}


static int
binary_search_uint4 (int lowi, int highi, const unsigned int *positions, unsigned int goal) {
  int middlei;

  debug20(printf("entered binary search with lowi=%d, highi=%d, goal=%u\n",lowi,highi,goal));

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

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


#ifdef HAVE_SSE2
static unsigned int *
v1 (unsigned int **diagonals, unsigned int **endDiagonals, unsigned int *out,
    const unsigned int *rare, int lenRare,
    const unsigned int *freq, int lenFreq, int delta_rare,
    int diagterm_rare, int diagterm_freq, const unsigned int slop) {

  const unsigned int *initRare = rare;
  const unsigned int *initFreq = freq;
  const unsigned int *endRare = &(rare[lenRare]);
  const unsigned int *endFreq = &(freq[lenFreq]);

  const unsigned int kRareSpace = 0;	/* 0. type was uint64_t */
  const unsigned int kFreqSpace = 2 * 4 * (0 + 1) - 1; /* 7. type was uint64_t */
  const unsigned int *stopRare = endRare - kRareSpace;
  const unsigned int *stopFreq = endFreq - kFreqSpace;

#ifndef INCLUDE_EXACT
  unsigned int valFreq;
#endif
  unsigned int valRare, outRare;
  const unsigned int kProbe = (0 + 1) * 2 * 4; /* 8. type was uint64_t */
  unsigned int goal;
  int npositions_rare, npositions_freq, j;

  int idx, i;
  const unsigned int slop_plus_1 = slop + 1; /* Because only cmpgt is available, not cmpge */
  int allocation, ndiagonals;
  unsigned int *more_diagonals;

#ifdef HAVE_AVX2
  __m256i Rare_low, Rare_high;
  __m256i F, F_save;
  __m256i M, M_above, M_below;
  __m256i _epi32_offset;
#else
  __m128i Rare_low, Rare_high;
  __m128i F0, F1, F0_save, F1_save;
  __m128i M, M_above, M_below;
  __m128i _epi32_offset;
#endif

#ifdef HAVE_AVX2
  _epi32_offset = _mm256_set1_epi32(EPI32_MAX);
#else
  _epi32_offset = _mm_set1_epi32(EPI32_MAX);
#endif


  assert(/*lenRare*/(endRare - initRare) <= /*lenFreq*/(endFreq - initFreq));

  valRare = (*rare) + delta_rare; /* For comparison */
  outRare = (*rare) + diagterm_rare; /* For output */

  /* Avoid issues where valRare - slop_plus_1 < 0 */
  while (COMPILER_RARELY(rare < stopRare && valRare < slop_plus_1)) {
    out = scalar_one_set(&(*diagonals), &(*endDiagonals), out,
			 rare, freq, endFreq,
			 delta_rare, diagterm_rare, diagterm_freq, slop);
    rare++;
    valRare = (*rare) + delta_rare; /* For comparison */
    outRare = (*rare) + diagterm_rare; /* For output */
  }

  if (COMPILER_LIKELY(rare < stopRare)) {
    /* Advance freq */
    npositions_rare = endRare - rare;
    npositions_freq = endFreq - freq;

    if (50 * npositions_rare > npositions_freq) {
      /* Use linear */
      while (freq < stopFreq && /*maxFreq*/freq[kProbe - 1] + slop < valRare) {
	debug(printf("Advancing freq by 8 because %u + slop < %u\n",freq[kProbe - 1],valRare));
	freq += kProbe/*8*/;
      }

    } else {
      /* Use galloping search */
      goal = valRare - slop;
      j = 1;
      while (j < npositions_freq && freq[j] < goal) {
	j <<= 1;		/* gallop by 2 */
      }
      if (j >= npositions_freq) {
	j = binary_search_uint4(j >> 1,npositions_freq,freq,goal);
      } else {
	j = binary_search_uint4(j >> 1,j,freq,goal);
      }
      freq += j;
    }
  }

#ifdef HAVE_AVX2
  Rare_low = _mm256_set1_epi32(valRare - slop_plus_1 - EPI32_MAX);
  Rare_high = _mm256_set1_epi32(valRare + slop_plus_1 - EPI32_MAX);

  F = _mm256_loadu_si256((const __m256i *)(freq));
  F = _mm256_sub_epi32(F, _epi32_offset);

#else
  Rare_low = _mm_set1_epi32(valRare - slop_plus_1 - EPI32_MAX);
  Rare_high = _mm_set1_epi32(valRare + slop_plus_1 - EPI32_MAX);

  F0 = _mm_lddqu_si128((const __m128i *)(freq));
  F0 = _mm_sub_epi32(F0, _epi32_offset);
  F1 = _mm_lddqu_si128((const __m128i *)(freq + 4));
  F1 = _mm_sub_epi32(F1, _epi32_offset);
#endif

  while (rare < stopRare && freq < stopFreq) {
    debug(printf("rare is at item %lu with value %u\n",rare - initRare,valRare));
    debug(printf("freq is at item %lu with values %u..%u\n",freq - initFreq,freq[0],freq[7]));

    const unsigned int *freq_save = freq;

    /* First iteration on freq */
#ifdef HAVE_AVX2
    F_save = F;

    debug(print_vector_signed(Rare_low));
    debug(print_vector_signed(F));
    debug(print_vector_signed(Rare_high));

    M_above = _mm256_cmpgt_epi32(F, Rare_low);
    M_below = _mm256_cmpgt_epi32(Rare_high, F);
    M = _mm256_and_si256(M_above,M_below);

    debug(print_vector_hex(M_above));
    debug(print_vector_hex(M_below));
    debug(print_vector_hex(M));

    idx = _mm256_movemask_ps((__m256) M);
#else
    F0_save = F0;
    F1_save = F1;

    M_above = _mm_cmpgt_epi32(F1, Rare_low);
    M_below = _mm_cmplt_epi32(F1, Rare_high);
    M = _mm_and_si128(M_above,M_below);
    idx = _mm_movemask_ps((__m128) M);

    M_above = _mm_cmpgt_epi32(F0, Rare_low);
    M_below = _mm_cmplt_epi32(F0, Rare_high);
    M = _mm_and_si128(M_above,M_below);
    idx = idx * 16 + _mm_movemask_ps((__m128) M);
#endif

    debug(printf("Got idx %d => %d x %d\n",idx,match_start[idx],match_n[idx]));

    if (out + 2*match_n[idx] >= *endDiagonals) {
      allocation = (*endDiagonals) - (*diagonals);
      debug(printf("Reallocating from diagonals %p to end %p (allocation %d)\n",
		   *diagonals,*endDiagonals,allocation));
      more_diagonals = (unsigned int *) MALLOC(2 * allocation * sizeof(unsigned int));
	
      ndiagonals = out - (*diagonals);
      debug(printf("Copying from diagonals %p to out %p (ndiagonals %d)\n",
		   *diagonals,out,ndiagonals));
      memcpy(more_diagonals,*diagonals,ndiagonals * sizeof(unsigned int));

      *diagonals = more_diagonals;
      *endDiagonals = &(more_diagonals[2 * allocation]);
      out = &(more_diagonals[ndiagonals]);
    }

    const unsigned int *freq_ptr = freq + match_start[idx];
    for (i = 0; i < match_n[idx]; i++) {
      debug(printf("(3) Generating output %u %u\n",outRare,(*freq_ptr) + diagterm_freq));

      assert(out + 1 < *endDiagonals);
#ifdef INCLUDE_EXACT
      *out++ = outRare;
      *out++ = *(freq_ptr++) + diagterm_freq;
#else
      if (valRare != (valFreq = *freq_ptr++)) {
	*out++ = outRare;
	*out++ = valFreq + diagterm_freq;
      }
#endif
    }

    /* Subsequent iterations on freq */
    while (freq + kProbe < stopFreq && /*minFreq*/freq[kProbe] <= valRare + slop) {
      freq += kProbe;
      
#ifdef HAVE_AVX2
      F = _mm256_loadu_si256((const __m256i *)(freq));
      F = _mm256_sub_epi32(F, _epi32_offset);
    
      debug(print_vector_signed(Rare_low));
      debug(print_vector_signed(F));
      debug(print_vector_signed(Rare_high));

      M_above = _mm256_cmpgt_epi32(F, Rare_low);
      M_below = _mm256_cmpgt_epi32(Rare_high, F);
      M = _mm256_and_si256(M_above,M_below);

      debug(print_vector_hex(M_above));
      debug(print_vector_hex(M_below));
      debug(print_vector_hex(M));

      idx = _mm256_movemask_ps((__m256) M);
#else
      F0 = _mm_lddqu_si128((const __m128i *)(freq));
      F0 = _mm_sub_epi32(F0, _epi32_offset);
      F1 = _mm_lddqu_si128((const __m128i *)(freq + 4));
      F1 = _mm_sub_epi32(F1, _epi32_offset);

      M_above = _mm_cmpgt_epi32(F1, Rare_low);
      M_below = _mm_cmplt_epi32(F1, Rare_high);
      M = _mm_and_si128(M_above,M_below);
      idx = _mm_movemask_ps((__m128) M);

      M_above = _mm_cmpgt_epi32(F0, Rare_low);
      M_below = _mm_cmplt_epi32(F0, Rare_high);
      M = _mm_and_si128(M_above,M_below);
      idx = idx * 16 + _mm_movemask_ps((__m128) M);
#endif

      debug(printf("Got idx %d => %d x %d\n",idx,match_start[idx],match_n[idx]));

      if (out + 2*match_n[idx] >= *endDiagonals) {
	allocation = (*endDiagonals) - (*diagonals);
	debug(printf("Reallocating from diagonals %p to end %p (allocation %d)\n",
		     *diagonals,*endDiagonals,allocation));
	more_diagonals = (unsigned int *) MALLOC(2 * allocation * sizeof(unsigned int));
	
	ndiagonals = out - (*diagonals);
	debug(printf("Copying from diagonals %p to out %p (ndiagonals %d)\n",
		     *diagonals,out,ndiagonals));
	memcpy(more_diagonals,*diagonals,ndiagonals * sizeof(unsigned int));

	*diagonals = more_diagonals;
	*endDiagonals = &(more_diagonals[2 * allocation]);
	out = &(more_diagonals[ndiagonals]);
      }

      const unsigned int *freq_ptr = freq + match_start[idx];
      for (i = 0; i < match_n[idx]; i++) {
	debug(printf("(4) Generating output %u %u\n",outRare,(*freq_ptr) + diagterm_freq));
	assert(out + 1 < *endDiagonals);
#ifdef INCLUDE_EXACT
	*out++ = outRare;
	*out++ = *(freq_ptr++) + diagterm_freq;
#else
	if (valRare != (valFreq = *freq_ptr++)) {
	  *out++ = outRare;
	  *out++ = valFreq + diagterm_freq;
	}
#endif
      }
    }
    /* End of iterations on freq */

    if (freq + kProbe >= stopFreq) {
      freq += kProbe;
      out = scalar_one_set(&(*diagonals), &(*endDiagonals), out,
			   rare, freq, endFreq,
			   delta_rare, diagterm_rare, diagterm_freq, slop);
    }

    if (++rare < stopRare) {
      valRare = (*rare) + delta_rare; /* For comparison */
      outRare = (*rare) + diagterm_rare; /* For output */

#ifdef HAVE_AVX2
      Rare_low = _mm256_set1_epi32(valRare - slop_plus_1 - EPI32_MAX);
      Rare_high = _mm256_set1_epi32(valRare + slop_plus_1 - EPI32_MAX);
      F = F_save;
#else
      Rare_low = _mm_set1_epi32(valRare - slop_plus_1 - EPI32_MAX);
      Rare_high = _mm_set1_epi32(valRare + slop_plus_1 - EPI32_MAX);
      F0 = F0_save;
      F1 = F1_save;
#endif

      /* Advance freq */
      freq = freq_save;

      npositions_rare = endRare - rare;
      npositions_freq = endFreq - freq;

      if (50 * npositions_rare > npositions_freq) {
	/* Use linear */
	while (freq < stopFreq && /*maxFreq*/freq[7] + slop < valRare) {
	  debug(printf("Advancing freq by 8 because %u + slop < %u\n",freq[7],valRare));
	  freq += /*kProbe*/8;
	}

      } else {
	/* Use galloping search */
	goal = valRare - slop;
	j = 1;
	while (j < npositions_freq && freq[j] < goal) {
	  j <<= 1;		/* gallop by 2 */
	}
	if (j >= npositions_freq) {
	  j = binary_search_uint4(j >> 1,npositions_freq,freq,goal);
	} else {
	  j = binary_search_uint4(j >> 1,j,freq,goal);
	}
	freq += j;
      }

      if (freq != freq_save) {
#ifdef HAVE_AVX2
	F = _mm256_loadu_si256((const __m256i *)(freq));
	F = _mm256_sub_epi32(F, _epi32_offset);
#else
	F0 = _mm_lddqu_si128((const __m128i *)(freq));
	F0 = _mm_sub_epi32(F0, _epi32_offset);
	F1 = _mm_lddqu_si128((const __m128i *)(freq + 4));
	F1 = _mm_sub_epi32(F1, _epi32_offset);
#endif
      }

      debug(printf("Advancing rare to %lu to compare with stop %lu\n",
		   rare - initRare,stopRare - initRare));
      debug(printf("Restoring freq to %lu\n",freq - initFreq));
    }
  }

  return scalar(&(*diagonals), &(*endDiagonals), out,
		rare, endRare, freq, endFreq,
		delta_rare, diagterm_rare, diagterm_freq, slop);
}
#endif


/**
 * Our main heuristic.
 *
 * The out pointer can be set1 if length1<=length2,
 * or else it can be set2 if length1>length2.
 */
/* set1 + diagterm1 == set2 + diagterm2 */
#ifdef HAVE_SSE2
unsigned int *
Intersect_approx (int *ndiagpairs,
		  const unsigned int *set1, const int length1, int diagterm1,
		  const unsigned int *set2, const int length2, int diagterm2,
		  const unsigned int slop) {
  unsigned int *diagonals, *endDiagonals, *out;
  int allocation;
#ifdef DEBUG15
  unsigned int *nonsimd_diagonals;
  int nonsimd_ndiagpairs, i, k;
#endif

  debug(printf("Entered Intersect_approx with %d and %d items\n",length1,length2));

#ifdef DEBUG
  int i;
  for (i = 0; i < length1; i++) {
    printf("%u\n",set1[i]);
  }
  printf("\n");
  for (i = 0; i < length2; i++) {
    printf("%u\n",set2[i]);
  }
  printf("\n");
#endif    

  if ((length1 == 0) || (length2 == 0)) {
    *ndiagpairs = 0;
    debug(printf("Exiting Intersect_approx with 0 items\n"));
    return (unsigned int *) NULL;

  } else {
    debug(printf("Using v1 method\n"));
    if (length1 <= length2) {
      if ((allocation = 2 * length1) < 16) {
	/* Needs to be greater than 2*match_n */
	allocation = 16;
      }
      out = diagonals = (unsigned int *) MALLOC(allocation * sizeof(unsigned int));
      endDiagonals = &(diagonals[allocation]);
      out = v1(&diagonals, &endDiagonals, out,
	       set1, length1, set2, length2,
	       /*delta_rare*/(diagterm1 - diagterm2),
	       /*diagterm_rare*/diagterm1, /*diagterm_freq*/diagterm2,
	       slop);
    } else {
      if ((allocation = 2 * length2) < 16) {
	allocation = 16;
      }
      out = diagonals = (unsigned int *) MALLOC(allocation * sizeof(unsigned int));
      endDiagonals = &(diagonals[allocation]);
      out = v1(&diagonals, &endDiagonals, out,
	       set2, length2, set1, length1,
	       /*delta_rare*/(diagterm2 - diagterm1),
	       /*diagterm_rare*/diagterm2, /*diagterm_freq*/diagterm1,
	       slop);
    }

    *ndiagpairs = (out - diagonals) / 2;
    debug(printf("Exiting Intersect_approx with %d items\n",*ndiagpairs));
#ifdef DEBUG
    int k;
    for (i = 0, k = 0; i < *ndiagpairs; i++, k += 2) {
      printf("%u %u\n",diagonals[k],diagonals[k+1]);
    }
#endif


#ifdef DEBUG15
    nonsimd_diagonals = Intersect_approx_old(&nonsimd_ndiagpairs,
					     set1,length1,diagterm1,
					     set2,length2,diagterm2,slop);
    if (*ndiagpairs != nonsimd_ndiagpairs) {
      printf("Number of diagpairs %d != %d\n",*ndiagpairs,nonsimd_ndiagpairs);

      for (i = 0, k = 0; i < *ndiagpairs; i++, k += 2) {
	printf("%u %u\n",diagonals[k],diagonals[k+1]);
      }
      printf("\n");
      for (i = 0, k = 0; i < nonsimd_ndiagpairs; i++, k += 2) {
	printf("%u %u\n",nonsimd_diagonals[k],nonsimd_diagonals[k+1]);
      }
      printf("\n");

      abort();

    } else {
#if 0
      for (i = 0, k = 0; i < *ndiagpairs; i++, k += 2) {
	printf("%u %u\n",diagonals[k],diagonals[k+1]);
      }
      printf("\n");
      for (i = 0, k = 0; i < nonsimd_ndiagpairs; i++, k += 2) {
	printf("%u %u\n",nonsimd_diagonals[k],nonsimd_diagonals[k+1]);
      }
      printf("\n");
#endif

      for (i = 0, k = 0; i < *ndiagpairs; i++, k += 2) {
	if (diagonals[k] != nonsimd_diagonals[k] ||
	    diagonals[k+1] != nonsimd_diagonals[k+1]) {
	  printf("%u %u != %u %u\n",diagonals[k],diagonals[k+1],nonsimd_diagonals[k],nonsimd_diagonals[k+1]);
	  abort();
	}
      }
    }

    FREE(nonsimd_diagonals);
#endif

    return diagonals;
  }
}

#else

unsigned int *
Intersect_approx (int *ndiagpairs,
		  const unsigned int *set1, const int length1, int diagterm1,
		  const unsigned int *set2, const int length2, int diagterm2,
		  const unsigned int slop) {
  unsigned int *diagonals, *endDiagonals, *out;
  int allocation;

  if ((length1 == 0) || (length2 == 0)) {
    *ndiagpairs = 0;
    return (unsigned int *) NULL;

  } else {
    if (length1 <= length2) {
      allocation = 2 * length1;
      out = diagonals = (unsigned int *) MALLOC(allocation * sizeof(unsigned int));
      endDiagonals = &(diagonals[allocation]);
      out = scalar(&diagonals, &endDiagonals, out,
		   set1, /*endA*/&(set1[length1]),
		   set2, /*endB*/&(set2[length2]),
		   /*deltaA*/(diagterm1 - diagterm2),
		   /*diagtermA*/diagterm1, /*diagtermB*/diagterm2,
		   slop);
    } else {
      allocation = 2 * length2;
      out = diagonals = (unsigned int *) MALLOC(allocation * sizeof(unsigned int));
      endDiagonals = &(diagonals[allocation]);
      out = scalar(&diagonals, &endDiagonals, out,
		   set2, /*endA*/&(set2[length2]),
		   set1, /*endB*/&(set1[length1]),
		   /*deltaA*/(diagterm2 - diagterm1),
		   /*diagtermA*/diagterm2, /*diagtermB*/diagterm1,
		   slop);
    }

    *ndiagpairs = (out - diagonals) / 2;
    return diagonals;
  }
}
#endif


#ifdef DEBUG15
/* LARGE_GENOMES still needs this to handle transcriptome */
/* Returns results as pairs of coordinates */
unsigned int *
Intersect_approx_old (
#ifdef INCLUDE_EXACT
		  bool *exactp,
#endif
		  int *ndiagpairs,
		  const unsigned int *positionsa, int npositionsa, int diagterma,
		  const unsigned int *positionsb, int npositionsb, int diagtermb,
		  const unsigned int maxdistance) {
  int ndiagonals = 0;
  unsigned int *diagonals, *more_diagonals, local_goal;
  unsigned int diagterm, delta;
  unsigned int *positions0, *positions1;
  unsigned int *ptr1, *start1;
  int npositions0, npositions1, j;
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
  unsigned int last_diagonal = 0U, this_diagonal;
#else
  int guess_allocation;
#endif

#ifdef INCLUDE_EXACT
  *exactp = false;
#endif

  if (npositionsa == 0 || npositionsb == 0) {
    debug9(printf("Intersect_approx: intersection is null because npositionsa %d or npositionsb %d is zero\n",npositionsa,npositionsb));
    *ndiagpairs = 0;
    return (unsigned int *) NULL;

  } else if (npositionsa <= npositionsb) {
    positions0 = positionsa;
    positions1 = positionsb;
    npositions0 = npositionsa;
    npositions1 = npositionsb;

    diagterm = (unsigned int) diagtermb;	/* local_goal based on larger list */
    delta = (unsigned int) (diagterma - diagtermb); /* list0 + (diagterm0 - diagterm1) = list1 */

  } else {
    positions0 = positionsb;
    positions1 = positionsa;
    npositions0 = npositionsb;
    npositions1 = npositionsa;

    diagterm = (unsigned int) diagterma;	/* local_goal based on larger list */
    delta = (unsigned int) (diagtermb - diagterma); /* list0 + (diagterm0 - diagterm1) = list1 */
  }

  start1 = positions1;
  /* end1 = &(positions1[npositions1]); */

  debug9(printf("Intersect_approx with %d positions <= %d positions.  diagterm %d\n",
	       npositions0,npositions1,(int) diagterm));
#ifdef DEBUG9
  for (ptr1 = positions0; ptr1 < &(positions0[npositions0]); ptr1++) {
    printf("%u + %d\n",*ptr1,diagterma);
  }
  printf("\n");

  for (ptr1 = positions1; ptr1 < &(positions1[npositions1]); ptr1++) {
    printf("%u\n",*ptr1);
  }
  printf("\n");
#endif

  ndiagonals = 0;
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
  diagonals = (unsigned int *) MALLOC(2 * npositions0 * sizeof(unsigned int));
#else
  guess_allocation = 2 * npositions0;
  diagonals = (unsigned int *) MALLOC(guess_allocation * sizeof(unsigned int));
#endif

#if 0
  /* Should not be necessary, since this procedure is called after Indexdb_ptr_with_diagterm */
  if (diagtermb - diagterma < 0) {
    while (npositions0 > 0 && (*positions0) < (Univcoord_T) -(diagtermb - diagterma)) {
      ++positions0;
      --npositions0;
    }
  }

  if (diagterma < 0) {
    while (npositions1 > 0 && (*positions1) < (Univcoord_T) -diagterma) {
      ++positions1;
      --npositions1;
    }
  }
#endif

  while (npositions0 > 0) {
    local_goal = (*positions0) + delta;
    debug9(printf("intersection list 0: %d:%u => local_goal %u.  Searching from npositions1 %d\n",
		 npositions0,*positions0,local_goal,npositions1));
    if (npositions1 > 0 && *positions1 < local_goal) {
      j = 1;
      while (j < npositions1 && positions1[j] < local_goal) {
	j <<= 1;		/* gallop by 2 */
      }
      if (j >= npositions1) {
	j = binary_search_uint4(j >> 1,npositions1,positions1,local_goal);
      } else {
	j = binary_search_uint4(j >> 1,j,positions1,local_goal);
      }
      positions1 += j;
      npositions1 -= j;
    }
#ifdef DEBUG9
    if (npositions1 > 0) {
      printf("Result of search is npositions1 %d, pointing to %u\n",npositions1,*positions1);
    }
#endif

    if (npositions1 <= 0) {
      /* Check backwards only */
      debug9(printf("    intersection list 1 at end  checking for approximate:"));
      ptr1 = &(positions1[-1]);
      if (ptr1 >= start1) {
	debug9(printf(" prev %d:%u?",npositions1-1,*ptr1));
	if ((*ptr1) + maxdistance >= local_goal && (*ptr1) <= local_goal + maxdistance) {
	  debug9(printf(" yes [%u+%d>=%u and %u<=%u+%d] ",
		       *ptr1,maxdistance,local_goal,*ptr1,local_goal,maxdistance));
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
	  if ((this_diagonal = local_goal + diagterm) != last_diagonal) {
	    /* Not a duplicate */
	    diagonals[ndiagonals++] = this_diagonal;
	    diagonals[ndiagonals++] = (*ptr1) + diagterm;
	    last_diagonal = this_diagonal;
	  }
#else
	  /* Want duplicates */
	  if (ndiagonals >= guess_allocation) {
	    more_diagonals = (unsigned int *) MALLOC(2 * guess_allocation * sizeof(unsigned int));
	    memcpy(more_diagonals,diagonals,guess_allocation*sizeof(unsigned int));
	    FREE(diagonals);
	    diagonals = more_diagonals;
	    guess_allocation = 2 * guess_allocation;
	  }
	  diagonals[ndiagonals++] = local_goal + diagterm;
	  diagonals[ndiagonals++] = (*ptr1) + diagterm;
#endif
	}
      }
      debug9(printf("\n"));

      /* Should be able to return at this point.  positions1[n-1] must be < *positions0 */
      *ndiagpairs = ndiagonals / 2; /* Number of pairs */
#ifdef DEBUG9
      printf("Returning %d diagpairs\n",*ndiagpairs);
      for (j = 0; j < ndiagonals; j++) {
	printf("%u\n",diagonals[j]);
      }
#endif
      return diagonals;

    } else if ((*positions1) == local_goal) {
      /* Found local goal.  Save and advance */
      debug9(printf("    intersection list 1: %d:%u  exact\n",npositions1,*positions1));
#ifdef INCLUDE_EXACT
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
      if ((this_diagonal = local_goal + diagterm) != last_diagonal) {
	/* Not a duplicate */
	diagonals[ndiagonals++] = this_diagonal;
	diagonals[ndiagonals++] = this_diagonal;
	last_diagonal = this_diagonal;
      }
#else
      if (ndiagonals >= guess_allocation) {
	more_diagonals = (unsigned int *) MALLOC(2 * guess_allocation * sizeof(unsigned int));
	memcpy(more_diagonals,diagonals,guess_allocation*sizeof(unsigned int));
	FREE(diagonals);
	diagonals = more_diagonals;
	guess_allocation = 2 * guess_allocation;
      }
      diagonals[ndiagonals++] = local_goal + diagterm;
      diagonals[ndiagonals++] = local_goal + diagterm;
#endif
      *exactp = true;
#endif
      ++positions1;
      --npositions1;

    } else {
      debug9(printf("    intersection list 1: %d:%u  checking for approximate:",npositions1,*positions1));
      ptr1 = &(positions1[-1]); /* closest position < local_goal */
      if (ptr1 >= start1) {
	debug9(printf(" prev %d:%u?",npositions1+1,*ptr1));
	if ((*ptr1) + maxdistance >= local_goal && (*ptr1) <= local_goal + maxdistance) {
	  debug9(printf(" yes [%u+%d>=%u and %u<=%u+%d] ",
		       *ptr1,maxdistance,local_goal,*ptr1,local_goal,maxdistance));
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
	  if ((this_diagonal = local_goal + diagterm) != last_diagonal) {
	    /* Not a duplicate */
	    diagonals[ndiagonals++] = this_diagonal;
	    diagonals[ndiagonals++] = (*ptr1) + diagterm;
	    last_diagonal = this_diagonal;
	  }
#else
	  if (ndiagonals >= guess_allocation) {
	    more_diagonals = (unsigned int *) MALLOC(2 * guess_allocation * sizeof(unsigned int));
	    memcpy(more_diagonals,diagonals,guess_allocation*sizeof(unsigned int));
	    FREE(diagonals);
	    diagonals = more_diagonals;
	    guess_allocation = 2 * guess_allocation;
	  }
	  diagonals[ndiagonals++] = local_goal + diagterm;
	  diagonals[ndiagonals++] = (*ptr1) + diagterm;
#endif
	}
      }

      ptr1 = &(positions1[0]); /* closest position > local_goal */
      debug9(printf(" at %d:%u?",npositions1,*ptr1));
      if ((*ptr1) + maxdistance >= local_goal && (*ptr1) <= local_goal + maxdistance) {
	debug9(printf(" yes [%u+%d>=%u and %u<=%u+%d] ",
		     *ptr1,maxdistance,local_goal,*ptr1,local_goal,maxdistance));
#ifdef REMOVE_DUPLICATES_OF_FIRST_DIAGONAL
	if ((this_diagonal = local_goal + diagterm) != last_diagonal) {
	  /* Not a duplicate */
	  diagonals[ndiagonals++] = this_diagonal;
	  diagonals[ndiagonals++] = (*ptr1) + diagterm;
	  last_diagonal = this_diagonal;
	}
#else
	if (ndiagonals >= guess_allocation) {
	  more_diagonals = (unsigned int *) MALLOC(2 * guess_allocation * sizeof(unsigned int));
	  memcpy(more_diagonals,diagonals,guess_allocation*sizeof(unsigned int));
	  FREE(diagonals);
	  diagonals = more_diagonals;
	  guess_allocation = 2 * guess_allocation;
	}
	diagonals[ndiagonals++] = local_goal + diagterm;
	diagonals[ndiagonals++] = (*ptr1) + diagterm;
#endif
      }
      debug9(printf("\n"));
    }

    ++positions0;
    --npositions0;
  }
  debug9(printf("\n"));

  *ndiagpairs = ndiagonals / 2; /* Number of pairs */
#ifdef DEBUG9
  printf("Returning %d diagpairs\n",*ndiagpairs);
  for (j = 0; j < ndiagonals; j++) {
    printf("%u\n",diagonals[j]);
  }
#endif
  return diagonals;
}

#endif


void
Intersect_approx_setup () {
  initialize_match_values();
  return;
}  
