#ifndef BITREAD_H_
#define BITREAD_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: bitread.h 162 2012-07-31 09:14:04Z yinan $ */

#include "lib.h"

#define BITS_PER_BASE 3
#define BITS_PER_BASE_LL 3LL
#define BITS_PER_BYTE 8
#define BITS_PER_WORD 32
#define BITS_PER_LONGWORD 64
#define BITS_LONGWORD_SHIFT 6
#define BITS_LONGWORD_MASK	0x000000000000003f
#define BITS_TWO_LONGWORD 128
#define BITS_THREE_LONGWORD 192
#define BITS_FOUR_LONGWORD 256
#define BITS_FIVE_LONGWORD 320
#define WORDS_PER_READ 6

class BitRead {
public:

  /*
   *	BitRead::extract
   *	extract a subsequence from the sequence. The subsequence
   *	starts from offset-th character of the sequence and has len
   *	characters.
   *
   *	This implementation is highly optimized for modern CPUs. All IF
   *	statements are implemented by arithmetic operations to eliminate the
   *	branch. It support up to 256-bit sequence (85 bases) that can be
   *	fitted into four 64-bit integers.
   */

  inline static void extract(int64 * a, int64 * b, int64 offset, int len) {
    int shift = ((offset) + (len)) & BITS_LONGWORD_MASK;
    int index = ((offset) + (len)) >> BITS_LONGWORD_SHIFT;
    int64 mask = 0LL - (shift != 0);
    int cshift = BITS_PER_LONGWORD - shift;

    b[0] = (mask & (a[index - 5] >> cshift)) | (a[index - 6] << shift);
    b[1] = (mask & (a[index - 4] >> cshift)) | (a[index - 5] << shift);
    b[2] = (mask & (a[index - 3] >> cshift)) | (a[index - 4] << shift);
    b[3] = (mask & (a[index - 2] >> cshift)) | (a[index - 3] << shift);
    b[4] = (mask & (a[index - 1] >> cshift)) | (a[index - 2] << shift);
    b[5] = (mask & (a[index] >> cshift)) | (a[index - 1] << shift);

    shift = BITS_PER_LONGWORD - ((len) & BITS_LONGWORD_MASK);
    b[0] = (0LL - (len > BITS_FIVE_LONGWORD)) & (b[0] << shift >> shift);
    cshift = (0LL - (len < BITS_FIVE_LONGWORD)) & shift;
    b[1] = b[1] << cshift >> cshift;
    b[1] = (0LL - ((len) > BITS_FOUR_LONGWORD)) & b[1];
    cshift = (0LL - (len < BITS_FOUR_LONGWORD)) & shift;
    b[2] = b[2] << cshift >> cshift;
    b[2] = (0LL - ((len) > BITS_THREE_LONGWORD)) & b[2];
    cshift = (0LL - (len < BITS_THREE_LONGWORD)) & shift;
    b[3] = b[3] << cshift >> cshift;

    b[3] = (0LL - ((len) > BITS_TWO_LONGWORD)) & b[3];
    cshift = (0LL - (len < BITS_TWO_LONGWORD)) & shift;
    b[4] = b[4] << cshift >> cshift;
    b[4] = (0LL - ((len) > BITS_PER_LONGWORD)) & b[4];
    cshift = (0LL - (len < BITS_PER_LONGWORD)) & shift;
    b[5] = b[5] << cshift >> cshift;
  }

  inline static void genHeadMask(int64 * mask, int l) {
    int64 tmp[WORDS_PER_READ];
    for (int i = 0; i < WORDS_PER_READ; i++)
      tmp[i] = -1;
    removeHead2(tmp, mask, l);
  }

  /*
   *	BitRead::removeHead
   *	This function is used to extract rightmost portion of sequence a
   *	into sequence b. The extracted portion has l bits.
   *
   *	This implementation is highly optimized for modern CPUs. All IF
   *	statements are implemented by arithmetic operations to eliminate the
   *	branch. It support up to 256-bit sequence (85 bases) that can be
   *	fitted into four 64-bit integers.
   */

  inline static void removeHead(int64 * a, int64 * b, int64 * mask) {
    b[0] = a[0] & mask[0];
    b[1] = a[1] & mask[1];
    b[2] = a[2] & mask[2];
    b[3] = a[3] & mask[3];
    b[4] = a[4] & mask[4];
    b[5] = a[5] & mask[5];
  }

  inline static void removeHead2(int64 * a, int64 * b, int l) {
    int shift;
    int lshift = BITS_PER_LONGWORD - ((l) & BITS_LONGWORD_MASK);

    shift = lshift;
    b[0] = ((int64) 0 - (l > BITS_FIVE_LONGWORD)) & (a[0] << shift >> shift);
    shift = ((int64) 0 - (l < BITS_FIVE_LONGWORD)) & lshift;
    b[1] = a[1] << shift >> shift;
    shift = lshift;
    b[1] = ((int64) 0 - ((l) > BITS_FOUR_LONGWORD)) & b[1];
    shift = ((int64) 0 - (l < BITS_FOUR_LONGWORD)) & lshift;
    b[2] = a[2] << shift >> shift;
    shift = lshift;
    b[2] = ((int64) 0 - ((l) > BITS_THREE_LONGWORD)) & b[2];
    shift = ((int64) 0 - ((l) < BITS_THREE_LONGWORD)) & lshift;
    b[3] = a[3] << shift >> shift;

    shift = lshift;
    b[3] = ((int64) 0 - ((l) > BITS_TWO_LONGWORD)) & b[3];
    shift = ((int64) 0 - ((l) < BITS_TWO_LONGWORD)) & lshift;
    b[4] = a[4] << shift >> shift;
    shift = lshift;
    b[4] = ((int64) 0 - ((l) > BITS_PER_LONGWORD)) & b[4];
    shift = ((int64) 0 - ((l) < BITS_PER_LONGWORD)) & lshift;
    b[5] = a[5] << shift >> shift;
  }

  /*
   *	BitRead::removeInterval
   *	This function is used to remove an interval segment from sequence a,
   *	and extract others into sequence b. The interval starts from x-th
   *	bit in the sequence and has len bits.
   *
   *	This implementation is highly optimized for modern CPUs. All IF
   *	statements are implemented by arithmetic operations to eliminate the
   *	branch. It support up to 256-bit sequence (85 bases) that can be
   *	fitted into four 64-bit integers.
   *
   *	This implementation requires that the sequence a is stored in the
   *	rightmost of seven 64-bit integers. All unoccupied bits in the seven
   *	64-bit integers are set to be 0s.
   */

  inline static void removeInterval(int64 * a, int64 * b, uint32 x, int p) {
    int index1 = WORDS_PER_READ - 1 - ((x) >> BITS_LONGWORD_SHIFT);
    int index2 = WORDS_PER_READ - 1 - (((x) + (p)) >> BITS_LONGWORD_SHIFT);
    int shift = (x) & BITS_LONGWORD_MASK;
    int shift2 = ((x) + (p)) & BITS_LONGWORD_MASK;
    int poffset = (p) >> BITS_LONGWORD_SHIFT;
    int pshift = (p) & BITS_LONGWORD_MASK;
    int64 mask = ((int64) 0 - (shift != 0))
        & ((int64) -1 >> (BITS_PER_LONGWORD - shift));
    int64 mask2 = (int64) 0 - (pshift != 0);
    int index3 = index1 - poffset;
    int rpshift = BITS_PER_LONGWORD - pshift;

    b[5] = a[5];
    b[4] = a[4];
    b[3] = a[3];
    b[2] = a[2];
    b[1] = a[1];
    b[index1] = (a[index1] & mask) | (a[index2] >> shift2 << shift);
    b[index1] |= ((int64) 0 - (shift2 > shift)) & ((a[index2 - 1] << rpshift));

    b[index1 - 1] = (a[index3 - 1] >> pshift)
        | (mask2 & (a[index3 - 2] << rpshift));
    b[index1 - 2] = (a[index3 - 2] >> pshift)
        | (mask2 & (a[index3 - 3] << rpshift));
    b[index1 - 3] = (a[index3 - 3] >> pshift)
        | (mask2 & (a[index3 - 4] << rpshift));
    b[index1 - 4] = (a[index3 - 4] >> pshift)
        | (mask2 & (a[index3 - 5] << rpshift));
    b[index1 - 5] = (a[index3 - 5] >> pshift)
        | (mask2 & (a[index3 - 6] << rpshift));
  }

  inline static void copy(int64 * a, int64 * b) {
    b[5] = a[5];
    b[4] = a[4];
    b[3] = a[3];
    b[2] = a[2];
    b[1] = a[1];
    b[0] = a[0];
  }

  inline static bool compare(int64 * a, int64 * b) {
    return (a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3])
        && (a[4] == b[4]) && (a[5] == b[5]);
  }

  inline static void leftShift(int64 * a, int64 * b) {
    b[0] = (a[0] << 3) | (a[1] >> 61);
    b[1] = (a[1] << 3) | (a[2] >> 61);
    b[2] = (a[2] << 3) | (a[3] >> 61);
    b[3] = (a[3] << 3) | (a[4] >> 61);
    b[4] = (a[4] << 3) | (a[5] >> 61);
    b[5] = (a[5] << 3);
  }

  inline static void rightShift(int64 * a, int64 * b) {
    b[0] = (a[0] >> 3);
    b[1] = (a[1] >> 3) | (a[0] << 61);
    b[2] = (a[2] >> 3) | (a[1] << 61);
    b[3] = (a[3] >> 3) | (a[2] << 61);
    b[4] = (a[4] >> 3) | (a[3] << 61);
    b[5] = (a[5] >> 3) | (a[4] << 61);
  }

};

#endif /* BITREAD_H_ */
