@Override
  boolean processReadPair(Read r1, Read r2) {
    assert (r2 == null);
    final byte[] quals = r1.quality, bases = r1.bases;
    final byte[] match =
        (r1.match == null ? null : !r1.shortmatch() ? r1.match : Read.toLongMatchString(r1.match));
    if (match == null || quals == null || bases == null) {
      return false;
    }

    int subs = 0;
    int indels = 0;
    for (int qpos = 0, mpos = 0, last = quals.length - 1; mpos < match.length; mpos++) {

      final byte m = match[mpos];
      final byte mprev = match[Tools.max(mpos - 1, 0)];
      final byte mnext = match[Tools.min(mpos + 1, match.length - 1)];

      final byte q1 = quals[qpos];
      final byte b2 = bases[qpos];

      int sub = 0, indel = 0;
      if (m == 'S') {
        sub = 1;
      } else if (m == 'I') {
        indel = 1;
      } else if (m == 'm') {
        if (mprev == 'D' || mnext == 'D') {
          indel = 1;
        }
      } else if (m == 'D') {
        // do nothing
      } else if (m == 'C') {
        // do nothing
      } else {
        throw new RuntimeException(
            "Bad symbol m='"
                + ((char) m)
                + "'\n"
                + new String(match)
                + "\n"
                + new String(bases)
                + "\n");
      }
      subs += sub;
      indels += indel;
      if (q1 >= minq && q1 <= maxq) {
        if (sub > 0 || (indel > 0 && countIndels)) {
          return true;
        }
      }

      if (m != 'D') {
        qpos++;
      }
    }
    return keepPerfect && subs == 0 && indels == 0;
  }
  /** This will create a count consensus of the bases at each position in the cluster. */
  public int[][] baseCounts() {
    int maxLeft = -1, maxRight = -1;
    for (Read r : this) {
      long[] obj = (long[]) r.obj;
      int pos = (int) obj[1];
      maxLeft = Tools.max(maxLeft, pos);
      maxRight = Tools.max(maxRight, r.length() - pos);
    }
    final int width = maxLeft + maxRight;
    //		assert(size()==1) : "\nleft="+maxLeft+", right="+maxRight+", width="+width+",
    // "+k+"\n"+get(0).toFastq()+"\n"+get(size()-1).toFastq();

    //		System.err.println("\n\n");
    final int[][] counts = new int[4][width];
    for (Read r : this) {
      long[] obj = (long[]) r.obj;
      int pos = (int) obj[1];
      byte[] bases = r.bases, quals = r.quality;
      //			System.err.println("pos="+pos+", maxLeft="+maxLeft);
      for (int cloc = 0, rloc = maxLeft - pos; cloc < bases.length; cloc++, rloc++) {
        //				System.err.println("cloc="+cloc+"/"+bases.length+", rloc="+rloc+"/"+width);
        int x = AminoAcid.baseToNumber[bases[cloc]];
        if (x > -1) {
          int q = (quals == null ? 20 : quals[cloc]);
          counts[x][rloc] += q;
        }
      }
    }
    //		if(size()>0){//Looks correct.
    //			System.err.println(Arrays.toString(counts[0]));
    //			System.err.println(Arrays.toString(counts[1]));
    //			System.err.println(Arrays.toString(counts[2]));
    //			System.err.println(Arrays.toString(counts[3]));
    //		}
    return counts;
  }
  public void fill(Read r, long[] kmers) {
    final byte[] bases = r.bases;
    long kmer = 0;
    long rkmer = 0;
    int len = 0;

    if (bases == null || bases.length < k) {
      return;
    }

    long[] mods = local2.get();
    if (mods == null) {
      mods = new long[comparisons];
      local2.set(mods);
    }
    Arrays.fill(mods, -1);

    for (int i = 0; i < bases.length; i++) {
      byte b = bases[i];
      long x = Dedupe.baseToNumber[b];
      long x2 = Dedupe.baseToComplementNumber[b];
      kmer = ((kmer << 2) | x) & mask;
      rkmer = (rkmer >>> 2) | (x2 << shift2);
      if (b == 'N') {
        len = 0;
      } else {
        len++;
      }
      if (len >= k) {
        final long kmax = Tools.max(kmer, rkmer);
        for (int j = 0; j < comparisons; j++) {
          final long div = divisors[j];
          final long mod = kmax % div;
          if (mod > mods[j]) {
            mods[j] = mod;
            kmers[j] = kmax;
          }
        }
      }
    }
  }