/*******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.ibm.wala.util.intset;

import java.io.Serializable;

/**
 * Abstract base class for implementations of bitvectors
 */
@SuppressWarnings("rawtypes")
abstract public class BitVectorBase<T extends BitVectorBase> implements Cloneable, Serializable {

  protected final static boolean DEBUG = false;

  protected final static int LOG_BITS_PER_UNIT = 5;

  protected final static int BITS_PER_UNIT = 32;

  protected final static int MASK = 0xffffffff;

  protected final static int LOW_MASK = 0x1f;

  protected int bits[];

  public abstract void set(int bit);

  public abstract void clear(int bit);

  public abstract boolean get(int bit);

  public abstract int length();

  public abstract void and(T other);

  public abstract void andNot(T other);

  public abstract void or(T other);

  public abstract void xor(T other);

  public abstract boolean sameBits(T other);

  public abstract boolean isSubset(T other);

  public abstract boolean intersectionEmpty(T other);

  /**
   * Convert bitIndex to a subscript into the bits[] array.
   */
  public static int subscript(int bitIndex) {
    return bitIndex >> LOG_BITS_PER_UNIT;
  }

  /**
   * Clears all bits.
   */
  public final void clearAll() {
    for (int i = 0; i < bits.length; i++) {
      bits[i] = 0;
    }
  }

  @Override
  public int hashCode() {
    int h = 1234;
    for (int i = bits.length - 1; i >= 0;) {
      h ^= bits[i] * (i + 1);
      i--;
    }
    return h;
  }

  /**
   * How many bits are set?
   */
  public final int populationCount() {
    int count = 0;
    for (int i = 0; i < bits.length; i++) {
      count += Bits.populationCount(bits[i]);
    }
    return count;
  }

  public boolean isZero() {
    int setLength = bits.length;
    for (int i = setLength - 1; i >= 0;) {
      if (bits[i] != 0)
        return false;
      i--;
    }
    return true;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Object clone() {
    BitVectorBase<T> result = null;
    try {
      result = (BitVectorBase<T>) super.clone();
    } catch (CloneNotSupportedException e) {
      // this shouldn't happen, since we are Cloneable
      throw new InternalError();
    }
    result.bits = new int[bits.length];
    System.arraycopy(bits, 0, result.bits, 0, result.bits.length);
    return result;
  }

  @Override
  public String toString() {
    StringBuffer buffer = new StringBuffer();
    boolean needSeparator = false;
    buffer.append('{');
    int limit = length();
    for (int i = 0; i < limit; i++) {
      if (get(i)) {
        if (needSeparator) {
          buffer.append(", ");
        } else {
          needSeparator = true;
        }
        buffer.append(i);
      }
    }
    buffer.append('}');
    return buffer.toString();
  }

  /*
   * @see com.ibm.wala.util.intset.IntSet#contains(int)
   */
  public boolean contains(int i) {
    return get(i);
  }

  private static final int[][] masks = new int[][] {
      { 0xFFFF0000 },
      { 0xFF000000, 0x0000FF00 },
      { 0xF0000000, 0x00F00000, 0x0000F000, 0x000000F0 },
      { 0xC0000000, 0x0C000000, 0x00C00000, 0x000C0000, 0x0000C000, 0x00000C00, 0x000000C0, 0x0000000C },
      { 0x80000000, 0x20000000, 0x08000000, 0x02000000, 0x00800000, 0x00200000, 0x00080000, 0x00020000, 0x00008000, 0x00002000,
          0x00000800, 0x00000200, 0x00000080, 0x00000020, 0x00000008, 0x00000002 } };

  public int max() {
    int lastWord = bits.length - 1;

    while (lastWord >= 0 && bits[lastWord] == 0)
      lastWord--;

    if (lastWord < 0)
      return -1;

    int count = lastWord * BITS_PER_UNIT;

    int top = bits[lastWord];

    int j = 0;
    for (int i = 0; i < masks.length; i++) {
      if ((top & masks[i][j]) != 0) {
        j <<= 1;
      } else {
        j <<= 1;
        j++;
      }
    }

    return count + (31 - j);
  }

  /**
   * @return min j >= start s.t get(j)
   */
  public int nextSetBit(int start) {
    if (start < 0) {
      throw new IllegalArgumentException("illegal start: " + start);
    }
    int word = subscript(start);
    int bit = (1 << (start & LOW_MASK));
    while (word < bits.length) {
      if (bits[word] != 0) {
        do {
          if ((bits[word] & bit) != 0)
            return start;
          bit <<= 1;
          start++;
        } while (bit != 0);
      } else {
        start += (BITS_PER_UNIT - (start & LOW_MASK));
      }

      word++;
      bit = 1;
    }

    return -1;
  }

  /**
   * Copies the values of the bits in the specified set into this set.
   * 
   * @param set the bit set to copy the bits from
   * @throws IllegalArgumentException if set is null
   */
  public void copyBits(BitVectorBase set) {
    if (set == null) {
      throw new IllegalArgumentException("set is null");
    }
    int setLength = set.bits.length;
    bits = new int[setLength];
    for (int i = setLength - 1; i >= 0;) {
      bits[i] = set.bits[i];
      i--;
    }
  }
}
