package org.basex.util; import static java.lang.Long.*; import java.util.*; /** * Bit array that grows when needed. The implementation is similar to * {@link BitSet}. * * @author BaseX Team 2005-17, BSD License * @author Dimitar Popov */ public final class BitArray { /** Number of bits needed to address a bit in a word; 2<sup>6</sup> = 64. */ private static final int WORD_POWER = 6; /** Size of a word = 2<sup>{@link #WORD_POWER}</sup>. */ static final int WORD_SIZE = 1 << WORD_POWER; /** A bit mask of 64 bits set to 1. */ private static final long WORD_MASK = -1L; /** Bit storage. */ private long[] words; /** Number of used bits. */ private int size; /** Construct a new bit array. */ public BitArray() { init(); } /** * Construct a new bit array with the specified number of bits. * @param capacity initial number of bits */ private BitArray(final int capacity) { init(new long[(Math.max(0, capacity - 1) >>> WORD_POWER) + 1], capacity); } /** * Construct a new bit array and an initial value. * @param capacity initial number of bits * @param set sets or clears all values */ public BitArray(final int capacity, final boolean set) { this(capacity); if(set) { final int p = Math.max(0, capacity - 1) >>> WORD_POWER; for(int i = 0; i < p; i++) words[i] = 0XFFFFFFFFFFFFFFFFL; for(int i = p << WORD_POWER; i < capacity; i++) set(i); } } /** * Construct a new bit array with the specified backing array. * @param a array with bits * @param l number of used bits */ public BitArray(final long[] a, final int l) { init(a, l); } /** Initialize the bit array with an empty array. */ void init() { init(new long[1], 0); } /** * Initialize the bit array with the specified backing array. * @param a array with bits * @param l number of used bits */ void init(final long[] a, final int l) { words = a; size = l; } /** * The word array used to store the bits. The array is shrunk to the last * word, where a bit is set. * @return array of longs */ public long[] toArray() { // find the last index of a word which is different from 0: int i = words.length; while(--i >= 0 && words[i] == 0); final long[] result = new long[++i]; System.arraycopy(words, 0, result, 0, i); return result; } /** * Returns the number of bits set to {@code true}. * @return number of bits set to {@code true} */ public int cardinality() { int sum = 0; final int inUse = size + WORD_SIZE - 1 >>> WORD_POWER; for(int i = 0; i < inUse; i++) sum += bitCount(words[i]); return sum; } /** * Get the value of the i<sup>th</sup> bit. * @param i index of the bit * @return {@code true} if the i<sup>th</sup> bit is set */ public boolean get(final int i) { if(i >= size) return false; // calculate the index of the word in the array: i div 2^6 = i >> 6 final int wi = i >>> WORD_POWER; // check if the ith bit is 1 return (words[wi] & 1L << i) != 0; } /** * Set the i<sup>th</sup> bit to 1. * @param i index of the bit */ public void set(final int i) { // calculate the index of the word in the array: i div 2^6 = i >> 6 final int wi = i >>> WORD_POWER; if(wi >= words.length) resize(wi + 1); words[wi] |= 1L << i; if(i >= size) size = i + 1; } /** * Set the i<sup>th</sup> bit to 0. * @param i index of the bit */ public void clear(final int i) { // calculate the index of the word in the array: i div 2^6 = i >> 6 final int wi = i >>> WORD_POWER; if(wi >= words.length) resize(wi + 1); words[wi] &= ~(1L << i); // it is not necessary to set the last used bit } /** * Get the next bit set to 0, starting from the i<sup>th</sup> bit. * @param i index from which to start the search (inclusive) * @return index of the next clear bit after the i<sup>th</sup> bit */ public int nextFree(final int i) { // calculate the index of the word in the array: i div 2^6 = i >> 6 int wi = i >>> WORD_POWER; // invert the word and skip the first i bits: long word = ~words[wi] & WORD_MASK << i; if(word != 0) { return (wi << WORD_POWER) + numberOfTrailingZeros(word); } final int wl = words.length; while(++wi < wl) { if((word = ~words[wi]) != 0) { return (wi << WORD_POWER) + numberOfTrailingZeros(word); } } // wi * 2^6: return wi << WORD_POWER; } /** * Get the next bit set to 1, starting from the i<sup>th</sup> bit. * @param i index from which to start the search (inclusive) * @return index of the next set bit after the i<sup>th</sup> bit */ public int nextSet(final int i) { if(i >= size) return -1; final int inUse = size + WORD_SIZE - 1 >>> WORD_POWER; int wi = i >>> WORD_POWER; long word = words[wi] & WORD_MASK << i; while(true) { if(word != 0) return (wi << WORD_POWER) + numberOfTrailingZeros(word); if(++wi == inUse) return -1; word = words[wi]; } } /** * Expand the {@link #words} array to the desired size. * @param s new size */ private void resize(final int s) { final long[] tmp = new long[Math.max(words.length << 1, s)]; System.arraycopy(words, 0, tmp, 0, words.length); words = tmp; } }