package soot.util;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2001 Felix Kwok
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

import java.util.NoSuchElementException;

/**
 * A fast enumerator for sparse bit sets. When the enumerator is created, it takes a snapshot of the underlying BitVector,
 * and iterates through the set bits. Note that this class almost implements the Iterator interface, but it doesn't because
 * the return type of next is int rather than Object.
 */
public class BitSetIterator {

  long[] bits; // Bits inherited from the underlying BitVector
  int index; // The 64-bit block currently being examined
  long save = 0; // A copy of the 64-bit block (for fast access)

  /*
   * Computes log_2(x) modulo 67. This uses the fact that 2 is a primitive root modulo 67
   */
  final static int[] lookup = { -1, 0, 1, 39, 2, 15, 40, 23, 3, 12, 16, 59, 41, 19, 24, 54, 4, -1, 13, 10, 17, 62, 60, 28,
      42, 30, 20, 51, 25, 44, 55, 47, 5, 32, -1, 38, 14, 22, 11, 58, 18, 53, -1, 9, 61, 27, 29, 50, 43, 46, 31, 37, 21, 57,
      52, 8, 26, 49, 45, 36, 56, 7, 48, 35, 6, 34, 33 };

  /** Creates a new BitSetIterator */
  BitSetIterator(long[] bits) {
    // this.bits = new long[bits.length];
    // System.arraycopy(bits,0,this.bits,0,bits.length);
    this.bits = bits;
    index = 0;

    /* Zip through empty blocks */
    while (index < bits.length && bits[index] == 0L) {
      index++;
    }
    if (index < bits.length) {
      save = bits[index];
    }
  }

  /** Returns true if there are more set bits in the BitVector; false otherwise. */
  public boolean hasNext() {
    return index < bits.length;
  }

  /**
   * Returns the index of the next set bit. Note that the return type is int, and not Object.
   */
  public int next() {
    if (index >= bits.length) {
      throw new NoSuchElementException();
    }

    long k = (save & (save - 1)); // Clears the last non-zero bit. save is guaranteed non-zero.
    long diff = save ^ k; // Finds out which bit it is. diff has exactly one bit set.
    save = k;

    // Computes the position of the set bit.
    int result = (diff < 0) ? 64 * index + 63 : 64 * index + lookup[(int) (diff % 67)];

    if (save == 0) {
      index++;
      while (index < bits.length && bits[index] == 0L) {
        index++;
      }
      if (index < bits.length) {
        save = bits[index];
      }
    }
    return result;
  }
}
