#ifndef _PAIRWISE_ALIGNER_H_
#define _PAIRWISE_ALIGNER_H_

/**
 *    WHAM - high-throughput sequence aligner
 *    Copyright (C) 2011  WHAM Group, University of Wisconsin
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*	$Id: pair.h 157 2012-07-25 05:58:09Z yinan $ */

#include "sequence.h"
#include "short.h"
#include "rdtsc.h"
#include <math.h>

#define MAX_GAP 6

typedef struct int128 {
  int64 high;
  int64 low;
} int128;

typedef struct ErrorVector {
  unsigned char offset :4; //offset of matched target str
  unsigned char gap :4;
  signed char num;
  unsigned char len; //length of matched target str
  unsigned char qual;
  unsigned int vec;
} ErrorVector;

#define ERROR_MAX_ERR		127
#define ERROR_VECTOR_NUL	0
#define ERROR_VECTOR_INS	1
#define ERROR_VECTOR_DEL	2
#define ERROR_VECTOR_MIS	3
#define GET_ERROR(x, i) (((x) >> (32 - ((i) + 1) * 2)) & 0x3)
#define SET_ERROR(x, e) (x) = ((x) >> 2) | ((e) << 30)
//#define UPDATE_ERROR(x, e) (x) = (((x) & 0x3fffffff) | ((e) << 30))

extern unsigned long long timePairAlign;
extern unsigned long long numPairAlign;
extern unsigned long long numPairAlignFilter;
extern unsigned long long numPairAlignDBA;

class PairAligner {
public:
  static inline ErrorVector pairAlign(int64 * key, int64 * target, int length,
      int nError) {
    int64 diff[6];
    int64 cmp[2];
    ErrorVector error;

    diff[0] = target[0] ^ key[0];
    diff[1] = target[1] ^ key[1];
    diff[2] = target[2] ^ key[2];
    diff[3] = target[3] ^ key[3];
    diff[4] = target[4] ^ key[4];
    diff[5] = target[5] ^ key[5];

    //compact a 64-bit difference int into a 32-bit difference int
    cmp[0] = (diff[0] | (diff[0] >> 1) | (diff[0] >> 2)) & 0x2492492492492492LL;
    cmp[0] |= (diff[1] | (diff[1] >> 1) | (diff[1] >> 2) | (diff[0] << 63))
        & 0x4924924924924924LL;
    cmp[0] |= (diff[2] | (diff[2] >> 1) | (diff[2] >> 2) | (diff[1] << 63)
        | (diff[1] << 62)) & 0x9249249249249249LL;
    cmp[1] = (diff[3] | (diff[3] >> 1) | (diff[3] >> 2)) & 0x2492492492492492LL;
    cmp[1] |= (diff[4] | (diff[4] >> 1) | (diff[4] >> 2) | (diff[3] << 63))
        & 0x4924924924924924LL;
    cmp[1] |= (diff[5] | (diff[5] >> 1) | (diff[5] >> 2) | (diff[4] << 63)
        | (diff[4] << 62)) & 0x9249249249249249LL;

#if POPCNT
    cmp[0] = popcnt(cmp[0]);
    cmp[1] = popcnt(cmp[1]);
#else
    cmp[0] = BitCount(cmp[0]);
    cmp[1] = BitCount(cmp[1]);
#endif

    error.num = cmp[0] + cmp[1];
    error.offset = 0;
    error.gap = 0;
    error.len = length;
    error.vec = -1; //all mismatches

    return error;
  }

  static inline ErrorVector pairAlign(int64 * key, int64 * target, int length,
      int nError, int nGap) {
    unsigned long long time;
    ErrorVector errorVector;
    startTimer(&time);
#ifdef PAIRALIGN_BASIC
    PairAligner aligner;
    bool match = aligner.pairAlignEnum(key, target, length, nError, nGap);
    if (match)
    errorVector.num = 0;
    else
    errorVector.num = nError + 5;
    errorVector.len = length;
    errorVector.offset = 0;
#else
#ifdef PAIRALIGN_NW
    bool match = pairAlignDP(key, target, length, nError, nGap);
    if (match)
    errorVector.num = 0;
    else
    errorVector.num = nError + 5;
    errorVector.len = length;
    errorVector.offset = 0;
#else
#ifdef PAIRALIGN_BITVECTOR
    bool match = pairAlignBitVector(key, target, length, nError, nGap);
    if (match)
    errorVector.num = 0;
    else
    errorVector.num = nError + 5;
    errorVector.len = length;
    errorVector.offset = 0;
#else
    errorVector = pairAlignDBA(key, target, length, nError, nGap);
#endif
#endif
#endif

    stopTimer(&time);
    if (nGap != 0) {
      timePairAlign += time;
      numPairAlign++;
    }
    return errorVector;
  }

  static void printTimePairAlign() {
    if (numPairAlign != 0) {
      printf("Average pair align time: %llu cycles\n",
          timePairAlign / numPairAlign);
      printf("Filter ratio: %llu/%llu = %.2f %%\n", numPairAlignFilter,
          numPairAlign, (double) numPairAlignFilter / numPairAlign * 100);
      printf("Filter pass ratio: %llu/%llu = %.2f %%\n", numPairAlignDBA,
          numPairAlignFilter,
          (double) numPairAlignDBA / numPairAlignFilter * 100);
    }
  }

  /**
   * the target sequence should be (nInsertion + nDelete) longer than the key sequence.
   */
  static inline ErrorVector pairAlignDBA(int64 * key, int64 * target,
      int length, int nError, int nGap) {
    int i, e;
    int64 diff[6];
    int128 d[10][2 * MAX_GAP + 1];int128
    bases[2 * MAX_GAP + 1];ErrorVector
    vector[2 * MAX_GAP + 1], _vector[2 * MAX_GAP + 1];
    int128 filter, mask;
    int64 num;

    /* quick path for non-indel pair alignment */
    if (nGap == 0) {
      return pairAlign(key, target, length, nError);
    }

    numPairAlignFilter++;

    filter.low = filter.high = -1;
    if (length < 64) {
      mask.high = 0;
      mask.low = (1ULL << length) - 1;
    } else {
      mask.high = (1ULL << (length - 64)) - 1;
      mask.low = -1;
    }

    for (i = 0; i <= 2 * nGap; i++) {
      //get the bit-difference vector
      if (i == 0) {
        diff[0] = key[0] ^ target[0];
        diff[1] = key[1] ^ target[1];
        diff[2] = key[2] ^ target[2];
        diff[3] = key[3] ^ target[3];
        diff[4] = key[4] ^ target[4];
        diff[5] = key[5] ^ target[5];
      } else {

        int i1 = i * 3;
        int i2 = 64 - i1;
        diff[0] = key[0] ^ (target[0] >> i1);
        diff[1] = key[1] ^ ((target[1] >> i1) | (target[0] << i2));
        diff[2] = key[2] ^ ((target[2] >> i1) | (target[1] << i2));
        diff[3] = key[3] ^ ((target[3] >> i1) | (target[2] << i2));
        diff[4] = key[4] ^ ((target[4] >> i1) | (target[3] << i2));
        diff[5] = key[5] ^ ((target[5] >> i1) | (target[4] << i2));
      }

      //use 1 bit to represent a character
      diff[5] = (diff[5] | (diff[5] >> 1) | (diff[5] >> 2) | (diff[4] << 63)
          | (diff[4] << 62)) & 0x9249249249249249LL;
      diff[4] = ((diff[4] >> 2) | (diff[4] >> 3) | (diff[4] >> 4)
          | (diff[3] << 63)) & 0x9249249249249249LL;
      diff[3] = ((diff[3] >> 1) | (diff[3] >> 2) | (diff[3] >> 3))
          & 0x9249249249249249LL;
      diff[2] = (diff[2] | (diff[2] >> 1) | (diff[2] >> 2) | (diff[1] << 63)
          | (diff[1] << 62)) & 0x9249249249249249LL;
      diff[1] = ((diff[1] >> 2) | (diff[1] >> 3) | (diff[1] >> 4)
          | (diff[0] << 63)) & 0x9249249249249249LL;
      diff[0] = ((diff[0] >> 1) | (diff[0] >> 2) | (diff[0] >> 3))
          & 0x9249249249249249LL;

      //push all valid bits to the rightmost
      diff[0] = BitPushRight(diff[0]);
      diff[1] = BitPushRight(diff[1]);
      diff[2] = BitPushRight(diff[2]);
      diff[3] = BitPushRight(diff[3]);
      diff[4] = BitPushRight(diff[4]);
      diff[5] = BitPushRight(diff[5]);

      d[0][2 * nGap - i].high = (diff[0] << 43) | (diff[1] << 22) | diff[2];
      d[0][2 * nGap - i].low = (diff[3] << 43) | (diff[4] << 22) | diff[5];

      /**
       * if we clear all errors and the number of indels is
       * less than nGap:
       */
      if (Bit128EqualZero(d[0][2 * nGap - i], length)) {
        _vector[i].num = 0;
        _vector[i].offset = 2 * nGap - i;
        _vector[i].len = length;
        return _vector[i];
      }

      bases[2 * nGap - i] = d[0][2 * nGap - i];
      vector[2 * nGap - i].len = 0;
      vector[2 * nGap - i].gap = 0;

      filter = Bit128And(filter, d[0][2 * nGap - i]);
    }

    //check filter
#ifndef PAIRALIGN_NO_FILTER
    filter = Bit128And(filter, mask);
    num = 0;
#if POPCNT
    num += popcnt(filter.high);
    num += popcnt(filter.low);
#else
    num += BitCount(filter.high);
    num += BitCount(filter.low);
#endif
    if (num > nError) {
      vector[0].num = ERROR_MAX_ERR;
      return vector[0];
    }
#endif

    numPairAlignDBA++;

    /**
     * we make a performance evaluation. Mainteining the status arrays
     * take long time to execute.
     * To be improved.
     */
    int128 cur;
    for (e = 1; e <= nError; e++) {
      /* -nGap, -nGap + 1, ..., -1, 0, 1, ..., nGap - 1, nGap */
      for (i = 0; i <= 2 * nGap; i++) {
        //apply mismatch
        d[e][i] = Bit128RemoveRightmost1(d[e - 1][i]);
        _vector[i] = vector[i];
        SET_ERROR(_vector[i].vec, ERROR_VECTOR_MIS);
        //apply deletion
        if (i < 2 * nGap) {
          cur = Bit128SmearRightmost1(d[e - 1][i + 1]);
          cur = Bit128And(bases[i], cur);
          if (Bit128CompareRightmost1(d[e][i], cur)) {
            d[e][i] = cur;
            _vector[i] = vector[i + 1];
            _vector[i].len++;
            _vector[i].gap++;
            SET_ERROR(_vector[i].vec, ERROR_VECTOR_DEL);
          }
        }
        //apply insert
        if (i > 0) {
          /**
           *  this identical to RemoveSmearRightmost1
           *  cur = Bit128LeftShift1(d[e - 1][i - 1]);
           *  cur = Bit128SmearRightmost1(cur);
           **/
          cur = Bit128RemoveSmearRightmost1(d[e - 1][i - 1]);
          cur = Bit128And(cur, bases[i]);
          if (Bit128CompareRightmost1(d[e][i], cur)) {
            d[e][i] = cur;
            _vector[i] = vector[i - 1];
            _vector[i].len--;
            _vector[i].gap++;
            SET_ERROR(_vector[i].vec, ERROR_VECTOR_INS);
          }
        }
        /**
         * if we clear all errors and the number of indels is
         * less than nGap:
         */
        if (Bit128EqualZero(d[e][i], length) && _vector[i].gap <= nGap) {
          _vector[i].num = e;
          _vector[i].offset = i;
          _vector[i].len += length;
          return _vector[i];
        }
      }
      for (i = 0; i <= 2 * nGap; i++)
        vector[i] = _vector[i];
    }

    vector[0].num = ERROR_MAX_ERR;
    return vector[0];
  }

  static inline unsigned char qual(int64 * key, int64 * target, int len,
      char * quals, ErrorVector * error, strand s) {
    /*
     if (error->num <= (int)(len * 0.25 + 0.5))
     return 37;
     else
     return 25;
     */
    int i, j, e, idx;
    int type;
    char keyStr[129], targetStr[129];
    double p = 1;
    unsigned char qual;

    //the maining is used for future improvement
    CompactSequence::decompose(keyStr, len, key);
    CompactSequence::decompose(targetStr, error->len, target);

    e = 0;
    for (i = 0, j = 0; i < len; i++, j++) {
      if (s == FORWARD
        )
        idx = i;
      else
        idx = len - i - 1;
      if (keyStr[i] == targetStr[j]) {
        p *= 1 - pow((double) 10, (quals[idx] - 33) / -10.0);
      } else {
        p *= pow((double) 10, (quals[idx] - 33) / -10.0);
        type = GET_ERROR(error->vec, e);
        if (type == ERROR_VECTOR_INS) {
          j--;
        } else if (type == ERROR_VECTOR_DEL) {
          i--;
        }
      }
      e++;
    }

    qual = (int) (-10 * log10(1 - p) + 0.5);
    return qual;
  }

  /*
   static inline int qual(int64 * key, int64 * target, int len, char * quals, strand s)
   {
   int64 diff[6];
   int64 cmp[6];
   int64 value;
   int qual = 0;
   int i, j, id;

   diff[0] = target[0] ^ key[0];
   diff[1] = target[1] ^ key[1];
   diff[2] = target[2] ^ key[2];
   diff[3] = target[3] ^ key[3];
   diff[4] = target[4] ^ key[4];
   diff[5] = target[5] ^ key[5];

   //compact a 64-bit difference int into a 32-bit difference int
   cmp[0] = (diff[0] | (diff[0] >> 1) | (diff[0] >> 2)) & 0x2492492492492492LL;
   cmp[1] = (diff[1] | (diff[1] >> 1) | (diff[1] >> 2) | (diff[0] << 63)) & 0x4924924924924924LL;
   cmp[2] = (diff[2] | (diff[2] >> 1) | (diff[2] >> 2) | (diff[1] << 63) | (diff[1] << 62)) & 0x9249249249249249LL;
   cmp[3] = (diff[3] | (diff[3] >> 1) | (diff[3] >> 2)) & 0x2492492492492492LL;
   cmp[4] = (diff[4] | (diff[4] >> 1) | (diff[4] >> 2) | (diff[3] << 63)) & 0x4924924924924924LL;
   cmp[5] = (diff[5] | (diff[5] >> 1) | (diff[5] >> 2) | (diff[4] << 63) | (diff[4] << 62)) & 0x9249249249249249LL;

   for (i = 0; i < 6; i++)
   {
   if (cmp[i] == 0)
   continue;

   for (j = 2 - i % 3; j < 64; j += 3)
   {
   id = len - 1 - ((6 - i) * 64 - j) / 3;
   //	id = i * (64/3) + j/3 - (128 - len);
   value = cmp[i] & (1LL << (63 - j));
   if (value != 0)
   {
   if (s == BACKWARD)
   id = len - 1 - id;
   qual += quals[id] - 33;
   }
   }
   }

   return qual + 33;
   }
   */

  int64 * g_key;
  int64 * g_target;
  int g_nError;
  int g_nInsert;
  int g_nDelete;
  int g_length;
  int numerror[3];
  int maxerror[3];
  int errorlist[10];
  int128 d[2 * MAX_GAP + 1];

bool  pairAlignEnum(int64 * key, int64 * target, int length, int nError, int nGap)
  {
    int i;
    int64 diff[4];

    int nInsert = nGap;
    int nDelete = nGap;
    /* quick path for non-indel pair alignment */
    if (nInsert == 0 && nDelete == 0)
    {
      ErrorVector error;
      error = pairAlign(key, target, length, nError);
      return error.num <= nError;
    }

    nGap = nInsert;
    for (i = 0; i <= 2 * nGap; i++)
    {
      //get the bit-difference vector
      if (i == 0)
      {
        diff[0] = key[0] ^ target[0];
        diff[1] = key[1] ^ target[1];
        diff[2] = key[2] ^ target[2];
        diff[3] = key[3] ^ target[3];
        diff[4] = key[4] ^ target[4];
        diff[5] = key[5] ^ target[5];
      }
      else
      {
        diff[0] = key[0] ^ (target[0] >> (i * 3));
        diff[1] = key[1] ^ ((target[1] >> (i * 3)) | (target[0] << (64 - i * 3)));
        diff[2] = key[2] ^ ((target[2] >> (i * 3)) | (target[1] << (64 - i * 3)));
        diff[3] = key[3] ^ ((target[3] >> (i * 3)) | (target[2] << (64 - i * 3)));
        diff[4] = key[4] ^ ((target[4] >> (i * 3)) | (target[3] << (64 - i * 3)));
        diff[5] = key[5] ^ ((target[5] >> (i * 3)) | (target[4] << (64 - i * 3)));
      }

      //use 1 bit to represent a character
      diff[5] = (diff[5] | (diff[5] >> 1) | (diff[5] >> 2) | (diff[4] << 63) | (diff[4] << 62)) & 0x9249249249249249LL;
      diff[4] = ((diff[4] >> 2) | (diff[4] >> 3) | (diff[4] >> 4) | (diff[3] << 63)) & 0x9249249249249249LL;
      diff[3] = ((diff[3] >> 1) | (diff[3] >> 2) | (diff[3] >> 3)) & 0x9249249249249249LL;
      diff[2] = (diff[2] | (diff[2] >> 1) | (diff[2] >> 2) | (diff[1] << 63) | (diff[1] << 62)) & 0x9249249249249249LL;
      diff[1] = ((diff[1] >> 2) | (diff[1] >> 3) | (diff[1] >> 4) | (diff[0] << 63)) & 0x9249249249249249LL;
      diff[0] = ((diff[0] >> 1) | (diff[0] >> 2) | (diff[0] >> 3)) & 0x9249249249249249LL;

      //push all valid bits to the rightmost
      diff[0] = BitPushRight(diff[0]);
      diff[1] = BitPushRight(diff[1]);
      diff[2] = BitPushRight(diff[2]);
      diff[3] = BitPushRight(diff[3]);
      diff[4] = BitPushRight(diff[4]);
      diff[5] = BitPushRight(diff[5]);

      d[2 * nGap - i].high = (diff[0] << 43) | (diff[1] << 22) | diff[2];
      d[2 * nGap - i].low = (diff[3] << 43) | (diff[4] << 22) | diff[5];

      /**
       * if we clear all errors and the number of indels is
       * less than nGap:
       */
      if (Bit128EqualZero(d[2 * nGap - i], length))
      {
        return true;
      }
    }

    g_key = key;
    g_target = target;
    g_length = length;
    g_nError = nError;
    g_nInsert = nInsert;
    g_nDelete = nDelete;

    numerror[0] = numerror[1] = numerror[2] = 0;
    maxerror[0] = nDelete;
    maxerror[1] = nError;
    maxerror[2] = nInsert;

    return errorPermutation(0);
  }

  bool errorPermutation(int k)
  {
    int offset;
    int128 m, u;

    if (k == g_nError)
    {
      //		for (int i = 0; i < k; i++)
      //			printf("%d ", errorlist[i]);
      //		printf("\n");

      offset = 0;
      u = d[offset];
      if (Bit128EqualZero(u, g_length))
      {
        return true;
      }

      for (int i = 0; i < k; i++)
      {
        if (errorlist[i] == 1)
        {
          u = Bit128RemoveRightmost1(u);
        }
        else if (errorlist[i] == 0)
        {
          //apply deletion
          if (i > 0)
          {
            offset --;
            m = Bit128SmearRightmost1(u);
            u = Bit128And(d[offset], m);
          }
          else
          return false;
        }
        else if (errorlist[i] == 2)
        {
          //apply insert
          if (i < g_nInsert + g_nDelete)
          {
            /**
             *  this identical to RemoveSmearRightmost1
             *  cur = Bit128LeftShift1(d[e - 1][i - 1]);
             *  cur = Bit128SmearRightmost1(cur);
             **/
            offset ++;
            m = Bit128RemoveSmearRightmost1(u);
            u = Bit128And(d[offset], m);
          }
          else
          return false;
        }
        /**
         * if we clear all errors and the number of indels is
         * less than nGap:
         */
        if (Bit128EqualZero(u, g_length))
        {
          return true;
        }
      }

      return false;
    }

    for (int i = 0; i <= 2; i++)
    {
      if (numerror[i] >= maxerror[i])
      continue;

      numerror[i]++;
      errorlist[k] = i;

      if (errorPermutation(k+1))
      return true;

      numerror[i]--;
    }

    return false;
  }

  static inline bool pairAlignDP(int64 * key, int64 * target, int length, int nError, int nGap, bool print = false)
  {
    int i, j, offset, min, tmp;
    int c[130][130];
    char x[130];
    char y[130];
    int start, end;
    bool done, ret;

    CompactSequence::decompose(y, length, key);
    CompactSequence::decompose(x, length + nGap * 2, target);

//		for (i = 0; i <= length; i++)
//			for (j = 0; j < length + nGap * 2; j++)
//				c[i][j] = 66;

    for (i = 0; i <= length; i++)
    c[i][0] = i;

    for (i = 0; i <= length + nGap * 2; i++)
    c[0][i] = 0;

    for (i = 1; i <= length + nGap * 2; i++)
    {
      if (i - nGap -nGap > 1)
      start = i - nGap - nGap;
      else
      start = 1;
      end = nError + i;

      done = true;
      for (j = start; j <= end; j++)
      {
        if (j > length)
        break;

        min = nError + 1;
        if (j > start || j == 1) {
          if (c[j-1][i] + 1 < min)
          min = c[j-1][i] + 1;
        }
        if (j < end || i == 1) {
          if (c[j][i-1] + 1 < min)
          min = c[j][i-1] + 1;
        }
        tmp = c[j-1][i-1] + 1;
        if (x[i - 1] == y[j - 1])
        tmp--;
        if (tmp < min)
        min = tmp;

        c[j][i] = min;
        if (c[j][i] <= nError)
        done = false;
      }
      if (end >= length && c[length][i] <= nError) {
        ret = true;
        break;
      }
      if (done) {
        ret = false;
        break;
      }
    }

    if (print) {
      printf("\n%s\n%s\n", x, y);
      printf("current i: %d\n", i);
      for (i = 0; i <= length; i++) {
        for (j = 0; j < length + nGap * 2; j++)
        printf("%2d ", c[i][j]);
        printf("\n");
      }

      getchar();
    }

    return ret;
  }

  static inline bool pairAlignBitVector64(int64 * key, int64 * target, int patlen, int dif, int ngap)
  {
    register unsigned long long P, M, X, U, Y;
    unsigned long long Ebit, One;
    int i, p, num, base, Cscore;
    int a;
    unsigned long long Pc[4];
    char buf[130], query[130];

    CompactSequence::decompose(query, patlen, key);
    CompactSequence::decompose(buf, patlen + ngap * 2, target);

    Pc[0] = Pc[1] = Pc[2] = Pc[3] = 0;

    One = 1;
    for (p = 0; p < patlen; p++)
    {
      if (query[p] == 'A')
      Pc[0] |= One;
      else if (query[p] == 'C')
      Pc[1] |= One;
      else if (query[p] == 'G')
      Pc[2] |= One;
      else if (query[p] == 'T')
      Pc[3] |= One;
      One <<= 1;
    }

    One = 1;
    Ebit = (One << (patlen-1));
    P = -1;
    M = 0;

    Cscore = patlen;
    for (i = 0; i < patlen + 2 * ngap; i++)
    {
      if (buf[i] == 'A')
      U = Pc[0];
      else if (buf[i] == 'C')
      U = Pc[1];
      else if (buf[i] == 'G')
      U = Pc[2];
      else if (buf[i] == 'T')
      U = Pc[3];
      else
      U = 0;

      X = (((U & P) + P) ^ P) | U;
      U |= M;

      Y = P;
      P = M | ~ (X | Y);
      M = Y & X;

      if (P & Ebit)
      Cscore += 1;
      else if (M & Ebit)
      Cscore -= 1;

      Y = P << 1;
      P = (M << 1) | ~ (U | Y);
      M = Y & U;

      if (Cscore <= dif) {
        return true;
      }

    }
    return false;
  }

  static inline bool pairAlignBitVector(int64 * key, int64 * target, int patlen, int dif, int ngap)
  {
    int128 P, M, X, U, Y;
    int128 Ebit, One;
    int i, p, num, base, Cscore;
    int a;
    int128 Pc[4];
    char buf[130], query[130];

    CompactSequence::decompose(query, patlen, key);
    CompactSequence::decompose(buf, patlen + ngap * 2, target);

    Pc[0].high = Pc[0].low = Pc[1].high = Pc[1].low = 0;
    Pc[2].high = Pc[2].low = Pc[3].high = Pc[3].low = 0;

    One.high = 0;
    One.low = 1;
    for (p = 0; p < patlen; p++)
    {
      if (query[p] == 'A')
      Pc[0] = Bit128Or(Pc[0], One);
      else if (query[p] == 'C')
      Pc[1] = Bit128Or(Pc[1], One);
      else if (query[p] == 'G')
      Pc[2] = Bit128Or(Pc[2], One);
      else if (query[p] == 'T')
      Pc[3] = Bit128Or(Pc[3], One);

      One = Bit128LeftShift1(One);
    }

    One.high = 0;
    One.low = 1;
    P.high = -1;
    P.low = -1;
    M.high = 0;
    M.low = 0;
    if (patlen - 1 < 64) {
      Ebit.high = 0;
      Ebit.low = 1ULL << (patlen - 1);
    } else {
      Ebit.high = 1ULL << (patlen - 1 - 64);
      Ebit.low = 0;
    }

    Cscore = patlen;
    for (i = 0; i < patlen + 2 * ngap; i++)
    {
      if (buf[i] == 'A')
      U = Pc[0];
      else if (buf[i] == 'C')
      U = Pc[1];
      else if (buf[i] == 'G')
      U = Pc[2];
      else if (buf[i] == 'T')
      U = Pc[3];
      else
      U.high = U.low = 0;

      X = Bit128Or(Bit128Xor(Bit128Plus(Bit128And(U, P), P), P), U);
      U = Bit128Or(U, M);

      Y = P;
      P = Bit128Or(M, Bit128Neg(Bit128Or(X, Y)));
      M = Bit128And(Y, X);

      if (Bit128True(Bit128And(P, Ebit)))
      Cscore += 1;
      else if (Bit128True(Bit128And(M, Ebit)))
      Cscore -= 1;

      Y = Bit128LeftShift1(P);
      P = Bit128Or(Bit128LeftShift1(M), Bit128Neg(Bit128Or(U, Y)));
      M = Bit128And(Y, U);

      if (Cscore <= dif) {
        return true;
      }

    }
    return false;
  }

  static inline int64 popcnt(int64 v)
  {
    int64 ret;
#if defined(__i386__) || defined(__x86_64__)
    __asm__ __volatile__ ("popcnt %0, %1" : "=r"(ret) : "r"(v));
#else
#error "POPCNT is only supported in Intel X86/IA-64 architectures"
#endif
    return ret;
  }

  static inline int64 BitCount(int64 x)
  {
    x = ((x) & 0x5555555555555555LL) + (((x) >> 1) & 0x5555555555555555LL);
    x = ((x) & 0x3333333333333333LL) + (((x) >> 2) & 0x3333333333333333LL);
    x = ((x) & 0x0F0F0F0F0F0F0F0FLL) + (((x) >> 4) & 0x0F0F0F0F0F0F0F0FLL);
    x = ((x) & 0x00FF00FF00FF00FFLL) + (((x) >> 8) & 0x00FF00FF00FF00FFLL);
    x = ((x) & 0x0000FFFF0000FFFFLL) + (((x) >> 16) & 0x0000FFFF0000FFFFLL);
    x = ((x) & 0x00000000FFFFFFFFLL) + (((x) >> 32) & 0x00000000FFFFFFFFLL);
    return x;
  }

  /**
   *  push all valid bits in a int64 to the rightmost of the int64
   *  00X00X00X00X00X00X -> 000000000000XXXXXX
   */
  static inline int64 BitPushRight(int64 x)
  {
    if (x != 0)
    {
      x = (x | (x >> 2)) & 0x30c30c30c30c30c3LL;
      x = (x | (x >> 4)) & 0xf00f00f00f00f00fLL;
      x = (x | (x >> 8)) & 0x00ff0000ff0000ffLL;
      x = (x | (x >> 16) | (x >> 32)) & 0x0000000000ffffffLL;
    }
    return x;
  }

  static inline int128 Bit128And(int128 x, int128 y)
  {
    int128 tmp;
    tmp.low = x.low & y.low;
    tmp.high = x.high & y.high;
    return tmp;
  }

  static inline int128 Bit128Or(int128 x, int128 y)
  {
    int128 tmp;
    tmp.low = x.low | y.low;
    tmp.high = x.high | y.high;
    return tmp;
  }

  static inline int128 Bit128Xor(int128 x, int128 y)
  {
    int128 tmp;
    tmp.low = x.low ^ y.low;
    tmp.high = x.high ^ y.high;
    return tmp;
  }

  static inline int128 Bit128Neg(int128 x)
  {
    int128 tmp;
    tmp.low = ~x.low;
    tmp.high = ~x.high;
    return tmp;
  }

  static inline int128 Bit128Plus(int128 x, int128 y)
  {
    int128 tmp;
    tmp.low = x.low + y.low;
    tmp.high = x.high + x.high;
    //low overflow
    if (tmp.low < x.low && tmp.low < y.low)
    tmp.high++;
    return tmp;
  }

  static inline bool Bit128True(int128 x)
  {
    return (x.low != 0) || (x.high != 0);
  }

  /**
   * remove the rightmost 1 in the 128-bit integer
   */
  static inline int128 Bit128RemoveRightmost1(int128 x)
  {
    int128 tmp;
    tmp.low = x.low & (x.low - 1);
    /**
     * the following line is identical to the code block:
     * if (tmp.low == x.low)	//the low 64 bits has no 1.
     *	 tmp.high = x.high & (x.high - 1);
     * else
     *	 tmp.high = x.high;
     **/
    tmp.high = x.high & (x.high - (tmp.low == x.low));
    return tmp;
  }

  /**
   * smear the rightmost 1 to the left in the 128-bit integer
   */
  static inline int128 Bit128SmearRightmost1(int128 x)
  {
    int128 tmp;
    tmp.low = x.low | -x.low;
    /**
     * the following line is identical to the code block:
     * if (x.low == 0) // the low 64 bits have no 1.
     * 	tmp.high = x.high | - x.high;
     * else
     * 	tmp.high = -1LL;
     **/
    tmp.high = x.high | - x.high | (0LL - (x.low != 0));
    return tmp;
  }

  /**
   * remove and smear the rightmost 1 to the left in the
   * 128-bit integer
   * NEED TO BE MODIFIED
   */
  static inline int128 Bit128RemoveSmearRightmost1(int128 x)
  {
    int128 tmp;
    tmp.low = x.low ^ -x.low;
    /**
     * the following line is identical to the code block:
     * if (x.low == 0) // the low 64 bits have no 1.
     * 	tmp.high = x.high ^ - x.high;
     * else
     * 	tmp.high = -1LL;
     **/
    tmp.high = x.high ^ - x.high | (0LL - (x.low != 0));
    return tmp;
  }

  /**
   * left shit the 128-bit integar
   */
  static inline int128 Bit128LeftShift1(int128 x)
  {
    int128 tmp;

    tmp.high = (x.high << 1) | (x.low >> 63);
    tmp.low = (x.low << 1);

    return tmp;
  }

  /**
   * right shit the 128-bit integar
   */
  static inline int128 Bit128RightShift1(int128 x)
  {
    int128 tmp;

    tmp.high = (x.high >> 1);
    tmp.low = (x.high << 63) | (x.low >> 1);

    return tmp;
  }

  static inline bool Bit128EqualZero(int128 x, int len)
  {
    int128 tmp;

    if (len >= 64)
    {
      tmp.high = (x.high << (128 - len)) | ( x.low >> (len - 64));
      tmp.low = x.low << (128 - len);
    }
    else
    {
      tmp.high = x.low << (64- len);
      tmp.low = 0;
    }

    return (tmp.high == 0) && (tmp.low == 0);
  }

  static inline bool Bit128CompareRightmost1(int128 x, int128 y)
  {
    int128 tmp1, tmp2;
    tmp1 = Bit128SmearRightmost1(x);
    tmp2 = Bit128SmearRightmost1(y);
    tmp1.low = ~tmp1.low;
    tmp1.high = ~tmp1.high;
    tmp2.low = ~tmp2.low;
    tmp2.high = ~tmp2.high;
    if (tmp1.high == tmp2.high)
    return tmp1.low < tmp2.low;
    else
    return tmp1.high < tmp2.high;
  }
};

#endif

/*
 static inline int pairAlign(int64 * orgkey, int64 * entrykey, int length, int nError, int nGap)
 {
 int i, e;
 int64 diff[4];
 int128 d[10][10];
 int128 offset[10];

 for (i = - nGap; i <= nGap; i++)
 {
 //get the bit-difference vector
 if (i == 0)
 {
 diff[0] = orgkey[0] ^ (entrykey[0] >> (i * 3));
 diff[1] = orgkey[1] ^ (entrykey[1] >> (i * 3));
 diff[2] = orgkey[2] ^ (entrykey[2] >> (i * 3));
 diff[3] = orgkey[3] ^ (entrykey[3] >> (i * 3));
 }
 else if (i < 0)
 {
 diff[0] = orgkey[0] ^ (entrykey[0] >> (-i * 3));
 diff[1] = orgkey[1] ^ ((entrykey[1] >> (-i * 3)) | (entrykey[0] << (64 + i * 3)));
 diff[2] = orgkey[2] ^ ((entrykey[2] >> (-i * 3)) | (entrykey[1] << (64 + i * 3)));
 diff[3] = orgkey[3] ^ ((entrykey[3] >> (-i * 3)) | (entrykey[2] << (64 + i * 3)));
 }
 else if (i > 0)
 {
 diff[0] = orgkey[0] ^ ((entrykey[0] << (i * 3)) | (entrykey[1] >> (64 - i * 3)));
 diff[1] = orgkey[1] ^ ((entrykey[1] << (i * 3)) | (entrykey[2] >> (64 - i * 3)));
 diff[2] = orgkey[2] ^ ((entrykey[2] << (i * 3)) | (entrykey[3] >> (64 - i * 3)));
 diff[3] = orgkey[3] ^ (entrykey[3] << (i * 3));
 }

 //use 1 bit to represent a character
 diff[3] = (diff[3] | (diff[3] >> 1) | (diff[3] >> 2) | (diff[2] << 63) | (diff[2] << 62)) & 0x9249249249249249LL;
 diff[2] = ((diff[2] >> 2) | (diff[2] >> 3) | (diff[2] >> 4) | (diff[1] << 63)) & 0x9249249249249249LL;
 diff[1] = ((diff[1] >> 1) | (diff[1] >> 2) | (diff[1] >> 3)) & 0x9249249249249249LL;
 diff[0] = (diff[0] | (diff[0] >> 1) | (diff[0] >> 2)) & 0x9249249249249249LL;

 //push all valid bits to the rightmost
 diff[0] = BitPushRight(diff[0]);
 diff[1] = BitPushRight(diff[1]);
 diff[2] = BitPushRight(diff[2]);
 diff[3] = BitPushRight(diff[3]);

 d[0][nGap + i].high = diff[0];
 d[0][nGap + i].low = (diff[1] << 44) | (diff[2] << 22) | diff[3];
 offset[nGap + i] = d[0][nGap + i];
 }

 int128 cur;
 for (e = 1; e <= nError; e++)
 {
 // -nGap, -nGap + 1, ..., -1, 0, 1, ..., nGap - 1, nGap
 for (i = 0; i <= 2 * nGap; i++)
 {
 //apply mismatch
 d[e][i] = Bit128RemoveRightmost1(d[e - 1][i]);
 //apply deletion
 if (i < 2 * nGap)
 {
 cur = Bit128SmearRightmost1(d[e - 1][i + 1]);
 cur = Bit128And(offset[i], cur);
 if (Bit128CompareRightmost1(d[e][i], cur))
 d[e][i] = cur;
 }
 //apply insert
 if (i > 0)
 {
 cur = Bit128LeftShift1(d[e - 1][i - 1]);
 cur = Bit128SmearRightmost1(cur);
 cur = Bit128And(cur, offset[i]);
 if (Bit128CompareRightmost1(d[e][i], cur))
 d[e][i] = cur;
 }
 if (Bit128EqualZero(d[e][i], length))
 return e;
 }
 }

 return 100;
 }
 */

