Emulate java.util.BitSet

This emulation and its pretty thorough unit tests were originally
authored by Danny Daemonic <dannydaemonic@gmail.com> more than seven
years ago.

Many parts of the emulation have since been rewritten. It now uses a
dense int[] array instead of a sparse array to improve performance on
most modern JavaScript engines. Instead of full 32-bit integers, the
array holds 31-bit integers, which makes V8 treat them as "tagged small
integers" on 32-bit machines and improves performance dramatically.

The class does not have JSNI methods any more.

Most of the BitSet methods have been emulated. The two unimplemented
ones are:
  public static BitSet valueOf(ByteBuffer)
  public static BitSet valueOf(LongBuffer)

They rely on java.nio.ByteBuffer and java.nio.LongBuffer, which are not
implemented yet.

The unit tests are mostly kept as-is, besides small formatting fixes.

Authors: Danny Daemonic, Daniel Kurka, Michael Zhou, Andrei Korzhevskii
Bug: #3285
Bug-Link: https://github.com/gwtproject/gwt/issues/3285
Change-Id: I5569676ca5d3aaffe631d716b0ae2878374fc974
Review-Link: https://gwt-review.googlesource.com/#/c/5771/
diff --git a/user/super/com/google/gwt/emul/java/util/BitSet.java b/user/super/com/google/gwt/emul/java/util/BitSet.java
new file mode 100644
index 0000000..417850d
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/BitSet.java
@@ -0,0 +1,882 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package java.util;
+
+import static javaemul.internal.Coercions.ensureInt;
+import static javaemul.internal.InternalPreconditions.checkArraySize;
+
+import javaemul.internal.ArrayHelper;
+
+/**
+ * This implementation uses a dense array holding bit groups of size 31 to keep track of when bits
+ * are set to true or false. Using 31 bits keeps our implementation within the range of V8's
+ * "tagged small integer" and improves performance. Using a dense array also makes access faster on
+ * V8.
+ *
+ * Not yet implemented:
+ * public static BitSet valueOf(ByteBuffer)
+ * public static BitSet valueOf(LongBuffer)
+ */
+public class BitSet {
+  private static final int WORD_MASK = 0x7fffffff;
+  private static final int BITS_PER_WORD = 31;
+
+  private final int[] array;
+
+  public BitSet() {
+    array = new int[0];
+  }
+
+  public BitSet(int nbits) {
+    checkArraySize(nbits);
+    int length = wordIndex(nbits - 1) + 1;
+    array = new int[0];
+    ArrayHelper.setLength(array, length);
+  }
+
+  private BitSet(int[] array) {
+    this.array = array;
+  }
+
+  private static void checkIndex(int bitIndex) {
+    if (bitIndex < 0) {
+      throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
+    }
+  }
+
+  private static void checkRange(int fromIndex, int toIndex) {
+    if (fromIndex < 0 || toIndex < 0 || fromIndex > toIndex) {
+      throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + ", toIndex: " + toIndex);
+    }
+  }
+
+  /**
+   * Converts from a bit index to a word index.
+   *
+   * @param bitIndex The bit index.
+   * @return The index of the word (array entry) holding that bit.
+   */
+  private static int wordIndex(int bitIndex) {
+    return bitIndex / BITS_PER_WORD;
+  }
+
+  /**
+   * Converts from a word index to the lowest index of the bit stored in that word.
+   *
+   * @param wordIndex The word index.
+   * @return The lowest index of the bit stored in that word.
+   */
+  private static int bitIndex(int wordIndex) {
+    return wordIndex * BITS_PER_WORD;
+  }
+
+  /**
+   * Computes the offset within a word for a bit index. Within a word, the offset counts from
+   * right to left and caps at 31. This helps leaving the highest bit as zero to ensure that
+   * each word is a tagged small integer in V8.
+   *
+   * @param bitIndex The bit index.
+   * @return The offset of the bit within a word, counting from right to left.
+   */
+  private static int bitOffset(int bitIndex) {
+    return bitIndex % BITS_PER_WORD;
+  }
+
+  /**
+   * Sets all bits to true within the given range.
+   *
+   * @param fromIndex The lower bit index.
+   * @param toIndex The upper bit index.
+   */
+  private static void setInternal(int[] array, int fromIndex, int toIndex) {
+    int first = wordIndex(fromIndex);
+    int last = wordIndex(toIndex);
+
+    maybeGrowArrayToIndex(array, last);
+
+    int startBit = bitOffset(fromIndex);
+    int endBit = bitOffset(toIndex);
+
+    if (first == last) {
+      // Set the bits in between first and last.
+      maskInWord(array, first, startBit, endBit);
+    } else {
+      // Set the bits from fromIndex to the next 31 bit boundary.
+      maskInWord(array, first, startBit, BITS_PER_WORD);
+
+      // Set the bits from the last 31 bit boundary to toIndex.
+      maskInWord(array, last, 0, endBit);
+
+      // Set everything in between.
+      for (int i = first + 1; i < last; i++) {
+        array[i] = WORD_MASK;
+      }
+    }
+  }
+
+  private static void maybeGrowArrayToIndex(int[] array, int newMaxIndex) {
+    // TODO: This code has a potential problem:
+    // If we grow the array more than 1024 places the array will degenerate into a map,
+    // we can work around this by adding 0 to the arrays.
+    int newLength = newMaxIndex + 1;
+    if (newLength > array.length) {
+      ArrayHelper.setLength(array, newLength);
+    }
+  }
+
+  /**
+   * Returns the index of the last word containing a true bit in an array, or -1 if none.
+   *
+   * @param array The array.
+   * @return The index of the last word containing a true bit, or -1 if none.
+   */
+  private static int lastSetWordIndex(int[] array) {
+    int i = array.length - 1;
+    for (; i >= 0 && wordAt(array, i) == 0; --i) { }
+    return i;
+  }
+
+  /**
+   * Flips all bits in a word within the given range.
+   *
+   * @param array The array.
+   * @param index The word index.
+   * @param from The lower bit index.
+   * @param to The upper bit index.
+   */
+  private static void flipMaskedWord(int[] array, int index, int from, int to) {
+    if (from == to) {
+      return;
+    }
+
+    to = 32 - to;
+    int word = wordAt(array, index);
+    word ^= ((0xffffffff >>> from) << (from + to)) >>> to;
+    array[index] = word & WORD_MASK;
+  }
+
+  /**
+   * Sets all bits to true in a word within the given bit range.
+   *
+   * @param array The array.
+   * @param index The word index.
+   * @param from The lower bit index.
+   * @param to The upper bit index.
+   */
+  private static void maskInWord(int[] array, int index, int from, int to) {
+    if (from == to) {
+      return;
+    }
+
+    to = 32 - to;
+    int word = wordAt(array, index);
+    word |= ((0xffffffff >>> from) << (from + to)) >>> to;
+    array[index] = word & WORD_MASK;
+  }
+
+  /**
+   * Sets all bits to false in a word within the given bit range.
+   *
+   * @param array The array.
+   * @param index The word index.
+   * @param from The lower bit index.
+   * @param to The upper bit index.
+   */
+  private static void maskOutWord(int[] array, int index, int from, int to) {
+    if (from == to) {
+      return;
+    }
+
+    int word = wordAt(array, index);
+    if (word == 0) {
+      return;
+    }
+
+    int mask;
+    if (from != 0) {
+      mask = 0xffffffff >>> (32 - from);
+    } else {
+      mask = 0;
+    }
+    // Shifting by 32 is the same as shifting by 0.
+    if (to != 32) {
+      mask |= 0xffffffff << to;
+    }
+
+    word &= mask;
+    array[index] = word & WORD_MASK;
+  }
+
+  private static int wordAt(int[] array, int index) {
+    return ensureInt(array[index]);
+  }
+
+  // GWT emulates integer with double and doesn't overflow. Enfore it by a integer mask.
+  private static int enforceOverflow(int value) {
+    return value & 0xffffffff;
+  }
+
+  public void and(BitSet set) {
+    // a & a is just a.
+    if (this == set) {
+      return;
+    }
+
+    // Truth table
+    //
+    // Case | a     | b     | a & b | Change?
+    // 1    | false | false | false | a is already false
+    // 2    | false | true  | false | a is already false
+    // 3    | true  | false | false | set a to false
+    // 4    | true  | true  | true  | a is already true
+    //
+    // We only need to change something in case 3, so iterate over set a.
+    int limit = Math.min(array.length, set.array.length);
+    int index = 0;
+    for (; index < limit; index++) {
+      int word = wordAt(array, index);
+      if (word != 0) {
+        array[index] = word & wordAt(set.array, index);
+      }
+    }
+    Arrays.fill(array, index, array.length, 0);
+  }
+
+  public void andNot(BitSet set) {
+    // a & !a is false, and all falses result in an empty BitSet.
+    if (this == set) {
+      clear();
+      return;
+    }
+
+    // Truth table
+    //
+    // Case | a     | b     | !b    | a & !b | Change?
+    // 1    | false | false | true  | false  | a is already false
+    // 2    | false | true  | false | false  | a is already false
+    // 3    | true  | false | true  | true   | a is already true
+    // 4    | true  | true  | false | false  | set a to false
+    //
+    // We only need to change something in case 4. Whenever b is true, a should be false, so
+    // iterate over set b.
+    int limit = Math.min(array.length, set.array.length);
+    for (int index = 0; index < limit; index++) {
+      int otherWord = wordAt(set.array, index);
+      if (otherWord != 0) {
+        int word = wordAt(array, index);
+        if (word != 0) {
+          array[index] = word & (~otherWord & WORD_MASK);
+        }
+      }
+    }
+  }
+
+  public int cardinality() {
+    int count = 0;
+    for (int i = 0; i < array.length; i++) {
+      count += Integer.bitCount(wordAt(array, i));
+    }
+    return count;
+  }
+
+  public void clear() {
+    ArrayHelper.setLength(array, 0);
+  }
+
+  public void clear(int bitIndex) {
+    checkIndex(bitIndex);
+
+    int index = wordIndex(bitIndex);
+    if (index >= array.length) {
+      return;
+    }
+
+    int word = wordAt(array, index);
+    if (word != 0) {
+      array[index] = word & ~(1 << bitOffset(bitIndex)) & WORD_MASK;
+    }
+  }
+
+  public void clear(int fromIndex, int toIndex) {
+    checkRange(fromIndex, toIndex);
+
+    if (fromIndex == toIndex) {
+      return;
+    }
+
+    int first = wordIndex(fromIndex);
+    if (first >= array.length) {
+      return;
+    }
+
+    int last = wordIndex(toIndex);
+    if (last >= array.length) {
+      toIndex = length();
+      last = wordIndex(toIndex);
+    }
+
+    int startBit = bitOffset(fromIndex);
+    int endBit = bitOffset(toIndex);
+
+    if (first == last) {
+      // Clear the bits in between first and last.
+      maskOutWord(array, first, startBit, endBit);
+    } else {
+      // Clear the bits from fromIndex to the next 31 bit boundary.
+      maskOutWord(array, first, startBit, BITS_PER_WORD);
+
+      // Clear the bits from the last 31 bit boundary to the toIndex.
+      maskOutWord(array, last, 0, endBit);
+
+      Arrays.fill(array, first + 1, last, 0);
+    }
+  }
+
+  public Object clone() {
+    return new BitSet(Arrays.copyOf(array, array.length));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+
+    if (!(obj instanceof BitSet)) {
+      return false;
+    }
+
+    BitSet other = (BitSet) obj;
+
+    int lastIndex = lastSetWordIndex(array);
+    if (lastIndex != lastSetWordIndex(other.array)) {
+      return false;
+    }
+
+    for (int index = 0; index <= lastIndex; index++) {
+      if (wordAt(array, index) != wordAt(other.array, index)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void flip(int bitIndex) {
+    checkIndex(bitIndex);
+
+    int index = wordIndex(bitIndex);
+    int offset = bitOffset(bitIndex);
+
+    maybeGrowArrayToIndex(array, index);
+
+    int word = wordAt(array, index);
+    if (((word >>> offset) & 1) == 1) {
+      word = word & ~(1 << offset);
+    } else {
+      word = (word | (1 << offset));
+    }
+    array[index] = word & WORD_MASK;
+  }
+
+  public void flip(int fromIndex, int toIndex) {
+    checkRange(fromIndex, toIndex);
+
+    if (fromIndex == toIndex) {
+      return;
+    }
+
+    // If we are flipping bits beyond our length, we are setting them to true.
+    int length = length();
+    if (fromIndex >= length) {
+      setInternal(array, fromIndex, toIndex);
+      return;
+    }
+
+    if (toIndex >= length) {
+      setInternal(array, length, toIndex);
+      toIndex = length;
+    }
+
+    int first = wordIndex(fromIndex);
+    int last = wordIndex(toIndex);
+    int startBit = bitOffset(fromIndex);
+    int endBit = bitOffset(toIndex);
+
+    if (first == last) {
+      // Flip the bits in between first and last.
+      flipMaskedWord(array, first, startBit, endBit);
+
+    } else {
+      // Flip the bits from fromIndex to the next 31 bit boundary.
+      flipMaskedWord(array, first, startBit, BITS_PER_WORD);
+
+      // Flip the bits from the last 31 bit boundary to the toIndex.
+      flipMaskedWord(array, last, 0, endBit);
+
+      // Flip everything in between.
+      for (int i = first + 1; i < last; i++) {
+        array[i] = ~wordAt(array, i) & WORD_MASK;
+      }
+    }
+  }
+
+  public boolean get(int bitIndex) {
+    checkIndex(bitIndex);
+
+    int index = wordIndex(bitIndex);
+    // Shift and mask the bit out
+    return index < array.length && ((wordAt(array, index) >>> bitOffset(bitIndex)) & 1) == 1;
+  }
+
+  public BitSet get(int fromIndex, int toIndex) {
+    checkRange(fromIndex, toIndex);
+
+    int length = length();
+    if (length <= fromIndex || fromIndex == toIndex) {
+      return new BitSet(0);
+    }
+
+    toIndex = Math.min(toIndex, length);
+
+    // The bit shift offset for each group of bits
+    int rightShift = bitOffset(fromIndex);
+
+    if (rightShift == 0) {
+      int subFrom = wordIndex(fromIndex);
+      int subTo = wordIndex(toIndex + BITS_PER_WORD);
+      int[] subArray = Arrays.copyOfRange(array, subFrom, subTo);
+      int leftOvers = bitOffset(toIndex);
+      maskOutWord(subArray, subTo - subFrom - 1, leftOvers, BITS_PER_WORD);
+      return new BitSet(subArray);
+    }
+
+    int first = wordIndex(fromIndex);
+    int last = wordIndex(toIndex);
+
+    int[] subArray = new int[last - first + 1];
+    if (first == last) {
+      // Number of bits to cut from the end
+      int end = 32 - bitOffset(toIndex);
+      int word = wordAt(array, first);
+      subArray[0] = (word << end) >>> (rightShift + end);
+    } else {
+      // Fence post, carry over initial bits.
+      int word = wordAt(array, first);
+
+      // This holds the newly packed bits.
+      int current = word >>> rightShift;
+
+      // The raw index into the subset
+      int subIndex = 0;
+
+      // A left shift will be used to shift our bits to the top of "current".
+      int leftShift = BITS_PER_WORD - rightShift;
+
+      // Loop through everything in the middle.
+      for (int i = first + 1; i <= last; i++) {
+        word = wordAt(array, i);
+
+        // Shift out the bits from the top, OR them into current bits.
+        current |= word << leftShift;
+        subArray[subIndex++] = current & WORD_MASK;
+
+        // Carry over the unused bits.
+        current = word >>> rightShift;
+      }
+
+      // Fence post, flush out the extra bits, but don't go past the "end".
+      int end = 32 - bitOffset(toIndex);
+      current = (current << (rightShift + end)) >>> (rightShift + end);
+      subArray[subIndex] = current & WORD_MASK;
+    }
+
+    return new BitSet(subArray);
+  }
+
+  /**
+   * This hash is different than the one described in Sun's documentation. The
+   * described hash uses 64 bit integers and that's not practical in JavaScript.
+   */
+  @Override
+  public int hashCode() {
+    // FNV constants
+    final int fnvOffset = 0x811c9dc5;
+    final int fnvPrime = 0x1000193;
+
+    final int lastIndex = lastSetWordIndex(array);
+    int hash = fnvOffset ^ lastIndex;
+
+    for (int i = 0; i <= lastIndex; i++) {
+      int value = wordAt(array, i);
+      // Hash one byte at a time using FNV1 and make sure we don't overflow 32 bit int.
+      hash = enforceOverflow(hash * fnvPrime) ^ (value & 0xff);
+      hash = enforceOverflow(hash * fnvPrime) ^ ((value >>> 8) & 0xff);
+      hash = enforceOverflow(hash * fnvPrime) ^ ((value >>> 16) & 0xff);
+      hash = enforceOverflow(hash * fnvPrime) ^ (value >>> 24);
+    }
+
+    return hash;
+  }
+
+  public boolean intersects(BitSet set) {
+    if (this == set) {
+      // If it has any set bits then it intersects itself.
+      return length() > 0;
+    }
+
+    int limit = Math.min(array.length, set.array.length);
+    for (int index = 0; index < limit; index++) {
+      int word = wordAt(array, index);
+      if (word != 0 && (word & wordAt(set.array, index)) != 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean isEmpty() {
+    return length() == 0;
+  }
+
+  public int length() {
+    int lastIndex = lastSetWordIndex(array);
+    if (lastIndex == -1) {
+      return 0;
+    }
+
+    // Compute the bit index of the leftmost bit.
+    int word = wordAt(array, lastIndex);
+    return bitIndex(lastIndex) + (32 - Integer.numberOfLeadingZeros(word));
+  }
+
+  public int nextClearBit(int fromIndex) {
+    checkIndex(fromIndex);
+
+    int index = wordIndex(fromIndex);
+    int length = array.length;
+    if (index >= length) {
+      return fromIndex;
+    }
+
+    // Special case for the first word.
+    int word = ~wordAt(array, index) & WORD_MASK;
+    word &= (WORD_MASK << bitOffset(fromIndex));
+    while (word == 0) {
+      if (++index >= length) {
+        return bitIndex(index);
+      }
+      word = ~wordAt(array, index) & WORD_MASK;
+    }
+    return bitIndex(index) + Integer.numberOfTrailingZeros(word);
+  }
+
+  public int nextSetBit(int fromIndex) {
+    checkIndex(fromIndex);
+
+    int index = wordIndex(fromIndex);
+    int length = array.length;
+    if (index >= length) {
+      return -1;
+    }
+
+    // Special case for the first word.
+    int word = wordAt(array, index) & (WORD_MASK << bitOffset(fromIndex));
+    while (word == 0) {
+      if (++index >= length) {
+        return -1;
+      }
+      word = wordAt(array, index);
+    }
+    return bitIndex(index) + Integer.numberOfTrailingZeros(word);
+  }
+
+  public int previousClearBit(int fromIndex) {
+    if (fromIndex == -1) {
+      return -1;
+    }
+    checkIndex(fromIndex);
+
+    int index = wordIndex(fromIndex);
+    if (index >= array.length) {
+      return fromIndex;
+    }
+
+    // Special case for the first word.
+    int word = ~wordAt(array, index) & WORD_MASK;
+    word &= (WORD_MASK >>> (BITS_PER_WORD - bitOffset(fromIndex) - 1));
+    while (word == 0) {
+      if (--index < 0) {
+        return -1;
+      }
+      word = ~wordAt(array, index) & WORD_MASK;
+    }
+    return bitIndex(index) + (32 - Integer.numberOfLeadingZeros(word)) - 1;
+  }
+
+  public int previousSetBit(int fromIndex) {
+    if (fromIndex == -1) {
+      return -1;
+    }
+    checkIndex(fromIndex);
+
+    int index = wordIndex(fromIndex);
+    if (index >= array.length) {
+      return length() - 1;
+    }
+
+    // Special case for the first word.
+    int word = wordAt(array, index) & (WORD_MASK >>> (BITS_PER_WORD - bitOffset(fromIndex) - 1));
+    while (word == 0) {
+      if (--index < 0) {
+        return -1;
+      }
+      word = wordAt(array, index);
+    }
+    return bitIndex(index) + (32 - Integer.numberOfLeadingZeros(word)) - 1;
+  }
+
+  public void or(BitSet set) {
+    // a | a is just a.
+    if (this == set) {
+      return;
+    }
+
+    maybeGrowArrayToIndex(array, set.array.length - 1);
+
+    // Truth table
+    //
+    // Case | a     | b     | a | b | Change?
+    // 1    | false | false | false | a is already false
+    // 2    | false | true  | true  | set a to true
+    // 3    | true  | false | true  | a is already true
+    // 4    | true  | true  | true  | a is already true
+    //
+    // We only need to change something in case 2. Case 2 only happens when b is true, so iterate
+    // over set b
+    for (int index = 0; index < set.array.length; index++) {
+      int word = wordAt(set.array, index);
+      if (word != 0) {
+        array[index] = wordAt(array, index) | word;
+      }
+    }
+  }
+
+  public void set(int bitIndex) {
+    checkIndex(bitIndex);
+    int index = wordIndex(bitIndex);
+    maybeGrowArrayToIndex(array, index);
+    array[index] = wordAt(array, index) | (1 << bitOffset(bitIndex));
+  }
+
+  public void set(int bitIndex, boolean value) {
+    if (value) {
+      set(bitIndex);
+    } else {
+      clear(bitIndex);
+    }
+  }
+
+  public void set(int fromIndex, int toIndex) {
+    checkRange(fromIndex, toIndex);
+    if (fromIndex != toIndex) {
+      setInternal(array, fromIndex, toIndex);
+    }
+  }
+
+  public void set(int fromIndex, int toIndex, boolean value) {
+    if (value) {
+      set(fromIndex, toIndex);
+    } else {
+      clear(fromIndex, toIndex);
+    }
+  }
+
+  public int size() {
+    return array.length * 32;
+  }
+
+  @Override
+  public String toString() {
+    if (isEmpty()) {
+      return "{}";
+    }
+
+    StringBuilder sb = new StringBuilder("{");
+    int next = nextSetBit(0);
+    sb.append(next);
+
+    while ((next = nextSetBit(next + 1)) != -1) {
+      sb.append(", ");
+      sb.append(next);
+    }
+
+    sb.append("}");
+    return sb.toString();
+  }
+
+  public void xor(BitSet set) {
+    // a ^ a is all false, so return an empty BitSet.
+    if (this == set) {
+      clear();
+      return;
+    }
+
+    maybeGrowArrayToIndex(array, set.array.length - 1);
+
+    // Truth table
+    //
+    // Case | a     | b     | a ^ b | Change?
+    // 1    | false | false | false | a is already false
+    // 2    | false | true  | true  | set a to true
+    // 3    | true  | false | true  | a is already true
+    // 4    | true  | true  | false | set a to false
+    //
+    // We need to change something in cases 2 and 4. Cases 2 and 4 only happen when b is true,
+    // so iterate over set b.
+    for (int index = 0; index < set.array.length; index++) {
+      int word = wordAt(set.array, index);
+      if (word != 0) {
+        array[index] = wordAt(array, index) ^ word;
+      }
+    }
+  }
+
+  public byte[] toByteArray() {
+    int nbits = length();
+    int nbytes = nbits / Byte.SIZE;
+    if (nbits % Byte.SIZE != 0) {
+      nbytes++;
+    }
+    byte[] bytes = new byte[nbytes];
+    int bitOffset = 0;
+    for (int i = 0; i < nbytes; i++) {
+      bytes[i] = getByte(array, bitOffset);
+      bitOffset += Byte.SIZE;
+    }
+    return bytes;
+  }
+
+  public long[] toLongArray() {
+    int nbits = length();
+    int nlongs = nbits / Long.SIZE;
+    if (nbits % Long.SIZE != 0) {
+      nlongs++;
+    }
+    long[] longs = new long[nlongs];
+    int bitOffset = 0;
+    for (int i = 0; i < nlongs; i++) {
+      longs[i] = getLong(array, bitOffset);
+      bitOffset += Long.SIZE;
+    }
+    return longs;
+  }
+
+  private static byte getByte(int[] words, int bitIndex) {
+    int wordIndex = wordIndex(bitIndex);
+    if (wordIndex >= words.length) {
+      return 0;
+    }
+    int bitOffset = bitOffset(bitIndex);
+    int word = wordAt(words, wordIndex);
+    int b = (word >>> bitOffset);
+    int leftBits = Byte.SIZE - BITS_PER_WORD + bitOffset;
+    if (leftBits > 0 && wordIndex + 1 < words.length) {
+      word = wordAt(words, wordIndex + 1);
+      if (word != 0) {
+        word &= ~(WORD_MASK << leftBits);
+        word <<= (Byte.SIZE - leftBits);
+        b |= word;
+      }
+    }
+    return (byte) (b & 0xff);
+  }
+
+  private static int getInt(int[] words, int bitIndex) {
+    byte b1 = getByte(words, bitIndex);
+    byte b2 = getByte(words, bitIndex + 8);
+    byte b3 = getByte(words, bitIndex + 16);
+    byte b4 = getByte(words, bitIndex + 24);
+    return (b1 & 0xff) | ((b2 & 0xff) << 8) | ((b3 & 0xff) << 16) | ((b4 & 0xff) << 24);
+  }
+
+  private static long getLong(int[] words, int bitIndex) {
+    int low = getInt(words, bitIndex);
+    int high = getInt(words, bitIndex + 32);
+    return ((long) high << 32) | (low & 0xffff_ffffL);
+  }
+
+  public static BitSet valueOf(byte[] words) {
+    int len = words.length;
+    while (len > 0 && words[len - 1] == 0) {
+      len--;
+    }
+    int[] array = new int[len * Byte.SIZE];
+    int bitIndex = 0;
+    for (int i = 0; i < len; i++) {
+      addByte(array, words[i], bitIndex);
+      bitIndex += Byte.SIZE;
+    }
+    return new BitSet(array);
+  }
+
+  public static BitSet valueOf(long[] words) {
+    int len = words.length;
+    while (len > 0 && words[len - 1] == 0) {
+      len--;
+    }
+    int[] array = new int[len * Long.SIZE];
+    int bitIndex = 0;
+    for (int i = 0; i < len; i++) {
+      addLong(array, words[i], bitIndex);
+      bitIndex += Long.SIZE;
+    }
+    return new BitSet(array);
+  }
+
+  private static void addByte(int[] words, byte bits, int bitIndex) {
+    if (bits != 0) {
+      int wordIndex = wordIndex(bitIndex);
+      int bitOffset = bitOffset(bitIndex);
+      int first = ((bits & 0xff) << bitOffset) & WORD_MASK;
+      if (first != 0) {
+        words[wordIndex] = wordAt(words, wordIndex) | first;
+      }
+      int second = bitOffset == 0 ? 0 : (bits & 0xff) >>> (BITS_PER_WORD - bitOffset);
+      if (second != 0) {
+        words[wordIndex + 1] = wordAt(words, wordIndex + 1) | second;
+      }
+    }
+  }
+
+  private static void addInt(int[] words, int bits, int bitIndex) {
+    if (bits != 0) {
+      addByte(words, (byte) (bits & 0xff), bitIndex);
+      addByte(words, (byte) ((bits >> 8) & 0xff), bitIndex + Byte.SIZE);
+      addByte(words, (byte) ((bits >> 16) & 0xff), bitIndex + 2 * Byte.SIZE);
+      addByte(words, (byte) ((bits >> 24) & 0xff), bitIndex + 3 * Byte.SIZE);
+    }
+  }
+
+  private static void addLong(int[] words, long bits, int bitIndex) {
+    if (bits != 0) {
+      int low = (int) bits;
+      addInt(words, low, bitIndex);
+      int high = (int) (bits >>> 32);
+      addInt(words, high, bitIndex + Integer.SIZE);
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/CollectionsSuite.java b/user/test/com/google/gwt/emultest/CollectionsSuite.java
index 5f5e694..6ab5bfa 100644
--- a/user/test/com/google/gwt/emultest/CollectionsSuite.java
+++ b/user/test/com/google/gwt/emultest/CollectionsSuite.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.emultest.java.util.ArrayListTest;
 import com.google.gwt.emultest.java.util.ArraysTest;
+import com.google.gwt.emultest.java.util.BitSetTest;
 import com.google.gwt.emultest.java.util.CollectionsTest;
 import com.google.gwt.emultest.java.util.ComparatorTest;
 import com.google.gwt.emultest.java.util.DateTest;
@@ -49,6 +50,7 @@
     // $JUnit-BEGIN$
     suite.addTestSuite(ArrayListTest.class);
     suite.addTestSuite(ArraysTest.class);
+    suite.addTestSuite(BitSetTest.class);
     suite.addTestSuite(CollectionsTest.class);
     suite.addTestSuite(ComparatorTest.class);
     suite.addTestSuite(DateTest.class);
diff --git a/user/test/com/google/gwt/emultest/java/util/BitSetTest.java b/user/test/com/google/gwt/emultest/java/util/BitSetTest.java
new file mode 100644
index 0000000..071288e
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/BitSetTest.java
@@ -0,0 +1,1424 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * Tests BitSet class.
+ */
+public class BitSetTest extends EmulTestBase {
+
+  /**
+   * This class is used to describe numerical patterns.
+   */
+  private interface Pattern {
+    boolean contains(int i);
+  }
+
+  // count used for looping tests
+  private static final int TEST_SIZE = 509;
+
+  // this number has to be bigger than 100
+  private static final int BIG_NUMBER = (TEST_SIZE) * 10 + 101;
+
+  private static void assertTrue(BitSet set, int index) {
+    assertTrue("expected=true set[" + index + "]=false", set.get(index));
+  }
+
+  private static void assertFalse(BitSet set, int index) {
+    assertFalse("expected=false set[" + index + "]=true", set.get(index));
+  }
+
+  private static BitSet createSetOfMultiples(int multiple) {
+    BitSet set = new BitSet(TEST_SIZE);
+    for (int i = 0; i < TEST_SIZE; i += multiple) {
+      set.set(i);
+    }
+    return set;
+  }
+
+  // this checks to see the values given are true, and the others around are not
+  private static void checkValues(BitSet set, int... values) {
+    assertEquals(values.length, set.cardinality());
+
+    int highestIndex = values[values.length - 1];
+    boolean[] bits = new boolean[highestIndex + 1];
+    for (int value : values) {
+      bits[value] = true;
+    }
+
+    for (int index = 0; index < bits.length; index++) {
+      assertEquals(bits[index], set.get(index));
+    }
+  }
+
+  private static void checkEqualityTrue(BitSet setA, BitSet setB) {
+    assertTrue(setA.equals(setB));
+    assertTrue(setB.equals(setA));
+    assertTrue(setA.equals(setA));
+    assertTrue(setB.equals(setB));
+  }
+
+  private static void checkEqualityFalse(BitSet setA, BitSet setB) {
+    assertFalse(setA.equals(setB));
+    assertFalse(setB.equals(setA));
+  }
+
+  // Checks that the values in the given range are the only true values
+  private static void checkRange(BitSet set, int fromIndex, int toIndex) {
+    for (int i = fromIndex; i < toIndex; i++) {
+      assertTrue(set, i);
+    }
+    assertEquals(toIndex - fromIndex, set.cardinality());
+  }
+
+  // Checks that the values in the given range are the only true values
+  private static void checkRange(BitSet set, int fromIndex1, int toIndex1,
+      int fromIndex2, int toIndex2) {
+    for (int i = fromIndex1; i < toIndex1; i++) {
+      assertTrue(set, i);
+    }
+    for (int i = fromIndex2; i < toIndex2; i++) {
+      assertTrue(set, i);
+    }
+    assertEquals(toIndex1 - fromIndex1 + toIndex2 - fromIndex2,
+        set.cardinality());
+  }
+
+  private static void checkPattern(BitSet set, Pattern pattern) {
+    for (int i = 0; i < TEST_SIZE; i++) {
+      boolean contained = pattern.contains(i);
+      if (contained != set.get(i)) {
+        fail("expected=" + contained + " set[" + i + "]=" + !contained);
+      }
+    }
+  }
+
+  public void testConstructor() {
+    BitSet set = new BitSet();
+
+    // test what we know to be true of a new BitSet
+    assertTrue(set.isEmpty());
+    assertEquals(0, set.length());
+    assertEquals(0, set.cardinality());
+
+    // test exceptions
+    try {
+      set = new BitSet(-1);
+      fail("exception expected");
+    } catch (NegativeArraySizeException expected) {
+    }
+
+    set = new BitSet(0);
+    set = new BitSet(BIG_NUMBER);
+  }
+
+  public void testAnd() {
+    Pattern multiplesOf6 = new Pattern() {
+      @Override
+      public boolean contains(int i) {
+        return i % 6 == 0;
+      }
+    };
+
+    // setA will contain all multiples of 2
+    BitSet setA = createSetOfMultiples(2);
+
+    // setB will contain all multiples of 3
+    BitSet setB = createSetOfMultiples(3);
+
+    // and()ing the sets should give multiples of 6
+    setA.and(setB);
+
+    // verify by checking multiples of 6
+    checkPattern(setA, multiplesOf6);
+
+    // and()ing a set to itself should do nothing
+    setA.and(setA);
+
+    // verify by checking multiples of 6
+    checkPattern(setA, multiplesOf6);
+
+    // and()ing with a set identical to itself should do nothing
+    setA.and((BitSet) setA.clone());
+
+    // verify by checking multiples of 6
+    checkPattern(setA, multiplesOf6);
+
+    // and()ing with all trues should do nothing
+    BitSet trueSet = new BitSet(TEST_SIZE);
+    trueSet.set(0, TEST_SIZE);
+    setA.and(trueSet);
+
+    // verify by checking multiples of 6
+    checkPattern(setA, multiplesOf6);
+
+    // and()ing with all trues in a larger set should do nothing
+    trueSet.set(TEST_SIZE, TEST_SIZE * 2);
+    setA.and(trueSet);
+
+    // verify by checking multiples of 6
+    checkPattern(setA, multiplesOf6);
+    // there were "TEST_SIZE" extra trues, so lets verify those came out false
+    for (int i = TEST_SIZE; i < TEST_SIZE * 2; i++) {
+      assertFalse(setA.get(i));
+    }
+
+    // and()ing with an empty set should result in an empty set
+    setA.and(new BitSet());
+    assertEquals(0, setA.length());
+
+    // these close bits should not intersect
+    setB = new BitSet();
+    setA.set(0);
+    setB.set(1);
+    setA.and(setB);
+    assertTrue(setA.isEmpty());
+
+    // these bits should not intersect
+    setB = new BitSet();
+    setA.set(0);
+    setB.set(BIG_NUMBER);
+    setA.and(setB);
+    assertTrue(setA.isEmpty());
+    setA.set(0);
+    setB.and(setA);
+    assertTrue(setB.isEmpty());
+
+    setA = new BitSet();
+    setA.set(1);
+    setA.set(5);
+    setB = new BitSet();
+    setB.set(2);
+    setA.and(setB);
+    assertTrue(setA.isEmpty());
+
+    setA = new BitSet();
+    setA.set(1);
+    setA.set(5);
+    setB = new BitSet();
+    setB.set(5);
+    setA.and(setB);
+    assertTrue(setA.get(5));
+    assertEquals(1, setA.cardinality());
+
+    setA = new BitSet();
+    setA.set(2);
+    setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    setA.and(setB);
+    assertEquals(0, setA.cardinality());
+  }
+
+  public void testAndNot() {
+    Pattern multiplesOf2Not3 = new Pattern() {
+      @Override
+      public boolean contains(int i) {
+        return i % 2 == 0 && i % 3 != 0;
+      }
+    };
+
+    // setA will contain all multiples of 2
+    BitSet setA = createSetOfMultiples(2);
+
+    // setB will contain all multiples of 3
+    BitSet setB = createSetOfMultiples(3);
+
+    // andNot() the sets
+    setA.andNot(setB);
+
+    // verify by checking for multiples of 2 that are not multiples of 3
+    checkPattern(setA, multiplesOf2Not3);
+
+    // andNot()ing with an empty set should do nothing
+    setA.andNot(new BitSet());
+
+    // verify by checking for multiples of 2 that are not multiples of 3
+    checkPattern(setA, multiplesOf2Not3);
+
+    // andNot()ing with all trues should result in an empty set
+    BitSet trueSet = new BitSet(TEST_SIZE * 2);
+    trueSet.set(0, TEST_SIZE * 2);
+    setA.andNot(trueSet);
+    assertTrue(setA.isEmpty());
+
+    // save setB in setA
+    setA = (BitSet) setB.clone();
+
+    // andNot()ing a set to itself should result in an empty set
+    setB.andNot(setB);
+    assertTrue(setB.isEmpty());
+
+    // andNot()ing a set identical to itself should result in an empty set
+    setA.andNot((BitSet) setA.clone());
+    assertTrue(setA.isEmpty());
+
+    setA = new BitSet();
+    setA.set(2);
+    setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    setA.andNot(setB);
+    assertTrue(setA.get(2));
+    assertEquals(1, setA.cardinality());
+  }
+
+  public void testCardinality() {
+    BitSet set = new BitSet(TEST_SIZE);
+
+    // test the empty count
+    assertEquals(0, set.cardinality());
+
+    // test a count of 1
+    set.set(0);
+    assertEquals(1, set.cardinality());
+
+    // test a count of 2
+    set.set(BIG_NUMBER);
+    assertEquals(2, set.cardinality());
+
+    // clear them both and test again
+    set.clear(0);
+    set.clear(BIG_NUMBER);
+    assertEquals(0, set.cardinality());
+
+    // test different multiples
+    for (int multiple = 1; multiple < 33; multiple++) {
+      set = new BitSet();
+      set.set(BIG_NUMBER + multiple);
+      for (int i = 1; i < 33; i++) {
+        set.set(i * multiple);
+        assertEquals(i + 1, set.cardinality());
+      }
+    }
+
+    // test powers of 2
+    set = new BitSet();
+    int count = 0;
+    for (int i = 1; i < TEST_SIZE; i += i) {
+      set.set(i);
+      count++;
+    }
+    assertEquals(count, set.cardinality());
+
+    // test a long run
+    set = new BitSet();
+    for (int i = 0; i < TEST_SIZE; i++) {
+      set.set(i);
+      assertEquals(i + 1, set.cardinality());
+    }
+  }
+
+  public void testClear() {
+    BitSet set = new BitSet(TEST_SIZE);
+    for (int i = 0; i < TEST_SIZE; i++) {
+      set.set(i);
+    }
+
+    set.clear();
+    assertTrue(set.isEmpty());
+
+    set = new BitSet();
+    set.set(BIG_NUMBER);
+    set.clear();
+    assertFalse(set.get(BIG_NUMBER));
+  }
+
+  public void testClearInt() {
+    BitSet set = new BitSet();
+    set.set(0);
+    set.clear(0);
+    assertFalse(set.get(0));
+    set.set(BIG_NUMBER);
+    checkValues(set, BIG_NUMBER);
+    set.clear(BIG_NUMBER);
+    assertFalse(set.get(BIG_NUMBER));
+
+    set = new BitSet();
+    set.set(1);
+    set.set(2);
+    set.set(3);
+    set.set(18);
+    set.set(40);
+    set.clear(2);
+    checkValues(set, 1, 3, 18, 40);
+    set.clear(9);
+    checkValues(set, 1, 3, 18, 40);
+    set.clear(18);
+    checkValues(set, 1, 3, 40);
+    set.clear(7);
+    set.clear(6);
+    checkValues(set, 1, 3, 40);
+    set.clear(3);
+    checkValues(set, 1, 40);
+    set.clear(40);
+    checkValues(set, 1);
+    set.clear(1);
+    assertTrue(set.isEmpty());
+
+    // test exceptions
+    try {
+      set.clear(-1, 2);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.clear(3, 1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.clear(2, 2);
+  }
+
+  public void testClearIntIntAndSetIntInt() {
+    BitSet set = new BitSet();
+
+    set.set(7);
+    set.set(50);
+    set.set(BIG_NUMBER);
+    set.clear(0, BIG_NUMBER);
+    checkValues(set, BIG_NUMBER);
+    set.clear(0, BIG_NUMBER + 1);
+    assertTrue(set.isEmpty());
+
+    set.set(0, 65);
+    checkRange(set, 0, 65);
+    set.clear(0, 63);
+    checkRange(set, 63, 65);
+    set.clear(63, 65);
+    assertTrue(set.isEmpty());
+
+    set.set(0, 128);
+    set.clear(0, 64);
+    checkRange(set, 64, 128);
+    set.clear(0, 129);
+    assertTrue(set.isEmpty());
+
+    set.set(0, 65);
+    checkRange(set, 0, 65);
+
+    set.clear(0, 16);
+    checkRange(set, 16, 65);
+
+    set.set(0, 16);
+    checkRange(set, 0, 65);
+    set.clear(5, 5);
+    set.clear(7, 14);
+    set.clear(15, 42);
+    set.clear(43, 55);
+    set.clear(58, 62);
+    set.clear(BIG_NUMBER, BIG_NUMBER);
+    set.clear(BIG_NUMBER, BIG_NUMBER + 1);
+    checkValues(set, 0, 1, 2, 3, 4, 5, 6, 14, 42, 55, 56, 57, 62, 63, 64);
+    set.clear(0, 65);
+    assertTrue(set.isEmpty());
+
+    set.set(0, 33);
+    checkRange(set, 0, 33);
+    set.clear(0, 8);
+    checkRange(set, 8, 33);
+
+    for (int i = 0; i < 33; i++) {
+      // this shouldn't change anything
+      set.clear(i, i);
+      assertEquals(25, set.cardinality());
+      // nor should this
+      set.set(i, i);
+      assertEquals(25, set.cardinality());
+    }
+
+    for (int i = 0; i < 65; i++) {
+      set.set(0, 128);
+      set.clear(i, 128 - i);
+      checkRange(set, 0, i, 128 - i, 128);
+    }
+    set.clear(0, 128);
+    assertTrue(set.isEmpty());
+
+    set.set(7, 100);
+    checkRange(set, 7, 100);
+
+    set = new BitSet();
+    set.set(BIG_NUMBER, BIG_NUMBER);
+    assertEquals(0, set.cardinality());
+    set.set(BIG_NUMBER, BIG_NUMBER + 1);
+    checkRange(set, BIG_NUMBER, BIG_NUMBER + 1);
+    set.clear(BIG_NUMBER, BIG_NUMBER);
+    checkValues(set, BIG_NUMBER);
+    set.clear(BIG_NUMBER, BIG_NUMBER + 1);
+    assertEquals(0, set.cardinality());
+
+    set = new BitSet();
+    set.set(10, 12);
+    set.clear(11, 1000);
+    checkValues(set, 10);
+
+    set = new BitSet();
+    set.set(10, 12);
+    set.clear(0, 10);
+    set.set(BIG_NUMBER, BIG_NUMBER);
+    checkValues(set, 10, 11);
+    set.clear(10, 12);
+    assertTrue(set.isEmpty());
+
+    set = new BitSet();
+    set.set(1, 20);
+    set.clear(5, 10);
+    checkRange(set, 1, 5, 10, 20);
+
+    set = new BitSet();
+    set.set(1, 10);
+    set.clear(5, 15);
+    checkRange(set, 1, 5);
+
+    // test clear(int, int) exceptions
+    try {
+      set.clear(-1, 2);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.clear(3, 1);
+      fail("expected exception");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.clear(2, 2);
+
+    // test set(int, int) exceptions
+    try {
+      set.set(-1, 2);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.set(3, 1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.set(2, 2);
+  }
+
+  public void testClone() {
+    BitSet set = new BitSet();
+    set.set(2);
+    set.set(4);
+    set.set(32);
+    set.set(BIG_NUMBER);
+    BitSet clone = (BitSet) set.clone();
+    checkValues(clone, 2, 4, 32, BIG_NUMBER);
+    assertTrue(set.equals(clone));
+    assertEquals(4, clone.cardinality());
+  }
+
+  public void testEquals() {
+    BitSet setA = new BitSet();
+    BitSet setB = new BitSet();
+    checkEqualityTrue(setA, setB);
+
+    setA.set(0);
+    setB.set(0);
+    checkEqualityTrue(setA, setB);
+
+    setA.set(BIG_NUMBER);
+    setB.set(BIG_NUMBER);
+    checkEqualityTrue(setA, setB);
+
+    setA.clear(0);
+    setB.clear(0);
+    checkEqualityTrue(setA, setB);
+
+    setA.clear(BIG_NUMBER);
+    setB.clear(BIG_NUMBER);
+    checkEqualityTrue(setA, setB);
+
+    setA.set(0);
+    setB.set(1);
+    checkEqualityFalse(setA, setB);
+
+    setA.set(2);
+    setB.set(2);
+    checkEqualityFalse(setA, setB);
+
+    setA = new BitSet();
+    setB = new BitSet();
+    setA.set(Math.max(0, BIG_NUMBER - 8));
+    setB.set(BIG_NUMBER + 1);
+    checkEqualityFalse(setA, setB);
+
+    BitSet set1 = new BitSet();
+    set1.set(40);
+    BitSet set2 = new BitSet();
+    set2.set(1);
+    set2.set(40);
+    assertFalse(set1.equals(set2));
+
+    setA = new BitSet();
+    setA.set(2);
+    setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    checkEqualityFalse(setA, setB);
+  }
+
+  public void testFlipInt() {
+    BitSet set = new BitSet();
+    set.flip(0);
+    assertTrue(set.get(0));
+    set.flip(0);
+    assertFalse(set.get(0));
+    set.flip(BIG_NUMBER);
+    assertTrue(set.get(BIG_NUMBER));
+    set.flip(BIG_NUMBER);
+    assertFalse(set.get(BIG_NUMBER));
+
+    set = new BitSet();
+    set.flip(1);
+    set.flip(2);
+    set.flip(3);
+    set.flip(4);
+    set.flip(6);
+    set.flip(4);
+    set.flip(6);
+    set.flip(6);
+    set.flip(6);
+    set.flip(8);
+    set.flip(10);
+    set.flip(8);
+    set.flip(2);
+    set.flip(8);
+    checkValues(set, 1, 3, 8, 10);
+    set.flip(8);
+    checkValues(set, 1, 3, 10);
+    set.flip(3);
+    checkValues(set, 1, 10);
+    set.flip(10);
+    set.flip(11);
+    checkValues(set, 1, 11);
+    set.flip(1);
+    checkValues(set, 11);
+    set.flip(11);
+    assertTrue(set.isEmpty());
+
+    // test exceptions
+    try {
+      set.flip(-1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.flip(BIG_NUMBER);
+    set.flip(BIG_NUMBER);
+  }
+
+  public void testFlipIntInt() {
+    BitSet set = new BitSet();
+    set.flip(0, BIG_NUMBER);
+    checkRange(set, 0, BIG_NUMBER);
+    set.flip(0, BIG_NUMBER - 1);
+    checkValues(set, BIG_NUMBER - 1);
+    set.clear(0, BIG_NUMBER);
+    assertTrue(set.isEmpty());
+
+    set.flip(0, 33);
+    set.flip(0, 8);
+    checkRange(set, 8, 33);
+
+    // current state is set.set(8,33)
+    for (int i = 0; i < 33; i++) {
+      // this shouldn't change anything
+      set.flip(i, i);
+      assertEquals(25, set.cardinality());
+    }
+
+    // current state is set.set(8,33)
+    set.flip(0, 8);
+    set.flip(7, 21);
+    set.flip(22, 27);
+    checkValues(set, 0, 1, 2, 3, 4, 5, 6, 21, 27, 28, 29, 30, 31, 32);
+
+    set = new BitSet();
+    set.flip(10, 12);
+    set.flip(11, 1000);
+    checkRange(set, 10, 11, 12, 1000);
+
+    set = new BitSet();
+    set.flip(10, 12);
+    set.flip(0, 10);
+    checkRange(set, 0, 12);
+    set.flip(0, 12);
+    assertTrue(set.isEmpty());
+
+    set.flip(0, 64);
+    set.flip(0, 63);
+    checkValues(set, 63);
+    set.flip(63, 64);
+    assertTrue(set.isEmpty());
+
+    set.flip(0, 130);
+    checkRange(set, 0, 130);
+    set.flip(0, 66);
+    checkRange(set, 66, 130);
+    set.flip(65, 131);
+    checkRange(set, 65, 66, 130, 131);
+
+    set = new BitSet();
+    set.flip(1, 20);
+    set.flip(5, 10);
+    checkRange(set, 1, 5, 10, 20);
+
+    set = new BitSet();
+    set.flip(1, 10);
+    set.flip(5, 15);
+    checkRange(set, 1, 5, 10, 15);
+
+    // test exceptions
+    try {
+      set.flip(-1, 2);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.flip(3, 1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.flip(2, 2);
+  }
+
+  public void testGetIntAndSetInt() {
+    BitSet set = new BitSet();
+    set.set(0);
+    assertTrue(set.get(0));
+    assertFalse(set.get(1));
+    assertFalse(set.get(2));
+    assertFalse(set.get(100));
+
+    set.set(BIG_NUMBER);
+    assertFalse(set.get(BIG_NUMBER - 1));
+    assertTrue(set.get(BIG_NUMBER));
+    assertFalse(set.get(BIG_NUMBER + 1));
+
+    set = new BitSet();
+    set.set(0);
+    set.set(4);
+    set.set(7);
+    set.set(10);
+    set.set(31);
+    set.set(32);
+    set.set(33);
+    set.set(69);
+    checkValues(set, 0, 4, 7, 10, 31, 32, 33, 69);
+
+    // test get() exceptions
+    try {
+      set.get(-1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.get(BIG_NUMBER);
+
+    // test set() exceptions
+    try {
+      set.set(-1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.set(BIG_NUMBER);
+  }
+
+  public void testGetIntInt() {
+    BitSet set = new BitSet();
+
+    set.set(1);
+    assertFalse(set.get(1, 1).get(0));
+    assertTrue(set.get(1, 2).get(0));
+    assertTrue(set.get(0, 2).get(1));
+
+    set.set(32);
+    set.set(50);
+    set.set(BIG_NUMBER);
+
+    BitSet subSet = set.get(0, BIG_NUMBER);
+    checkValues(subSet, 1, 32, 50);
+
+    subSet = set.get(1, BIG_NUMBER);
+    checkValues(subSet, 0, 31, 49);
+
+    subSet = set.get(2, BIG_NUMBER + 1);
+    checkValues(subSet, 30, 48, BIG_NUMBER - 2);
+
+    subSet = set.get(32, BIG_NUMBER * 2);
+    checkValues(subSet, 0, 18, BIG_NUMBER - 32);
+    assertEquals(3, subSet.cardinality());
+
+    subSet = set.get(0, BIG_NUMBER + 1);
+    assertEquals(set, subSet);
+
+    set = new BitSet();
+    for (int i = 8; i < 33; i++) {
+      set.set(i);
+    }
+    for (int i = 0; i < 33; i++) {
+      assertTrue(set.get(i, i).isEmpty());
+    }
+
+    // test exceptions
+    try {
+      set.get(-1, 2);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.get(3, 1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.get(2, 2);
+  }
+
+  public void testHashCode() {
+    // Note: Oracle's hashCode() implementation creates many collisions when
+    // testSize is big, causing this test to fail in development mode.
+    // Since this is an "unimportant test" as suggested by the original author,
+    // we can just test with a small number.
+    int testSize = 25;
+
+    HashSet<Integer> hashValues = new HashSet<>();
+
+    // count the collisions
+    int collisions = 0;
+
+    // hash an empty set
+    hashValues.add(new BitSet().hashCode());
+
+    // hash the set of {TEST_SIZE + 1}
+    BitSet set = new BitSet();
+    set.set(testSize + 1);
+    assertTrue(hashValues.add(set.hashCode()));
+
+    // hash the set of {0, TEST_SIZE + 1}
+    set.set(0);
+    assertTrue(hashValues.add(set.hashCode()));
+
+    // hash the set of {0}
+    set = new BitSet();
+    set.set(0);
+    assertTrue(hashValues.add(set.hashCode()));
+
+    for (int multiple = 1; multiple < 33; multiple++) {
+      set = new BitSet();
+      set.set(0);
+
+      // fill a set with multiples
+      for (int i = multiple; i < testSize; i += multiple) {
+        set.set(i);
+        // hash the current set, except in the case of {0}
+        if (i != 0) {
+          if (hashValues.add(set.hashCode()) == false) {
+            collisions++;
+          }
+          set.set(testSize + 1);
+          if (hashValues.add(set.hashCode()) == false) {
+            collisions++;
+          }
+          set.clear(testSize + 1);
+        }
+      }
+    }
+
+    assertEquals(0, collisions);
+  }
+
+  public void testIntersects() {
+    final int prime = 37;
+    for (int multiple = 1; multiple < prime; multiple++) {
+      int size = prime * multiple + 1;
+
+      // setA will contain all multiples of "multiple" up to "size"
+      BitSet setA = new BitSet(size);
+      for (int i = multiple; i < size; i += multiple) {
+        setA.set(i);
+      }
+
+      // setB will contain all multiples of "prime" up to "size"
+      BitSet setB = new BitSet();
+      for (int i = prime; i < size; i += prime) {
+        setB.set(i);
+      }
+
+      // the two sets should only intersect on the very last bit
+      assertTrue(setA.intersects(setB));
+      setA.clear(size - 1);
+      assertFalse(setA.intersects(setB));
+
+      // the inverse of a set should not intersect itself
+      setB = new BitSet();
+      for (int i = 0; i < prime; i++) {
+        setB.set(i, !setA.get(i));
+      }
+      assertFalse(setA.intersects(setB));
+
+      // a set intersects itself if it has any bits set
+      assertTrue(setA.intersects(setA));
+      setA = new BitSet();
+      assertFalse(setA.intersects(setA));
+
+      // an empty set doesn't intersect itself
+      assertFalse(new BitSet().intersects(new BitSet()));
+    }
+
+    BitSet setA = new BitSet();
+    setA.set(2);
+    BitSet setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    assertFalse(setA.intersects(setB));
+  }
+
+  public void testIsEmpty() {
+    BitSet set = new BitSet();
+    assertTrue(set.isEmpty());
+    set.set(0);
+    assertFalse(set.isEmpty());
+    set = new BitSet();
+    set.set(BIG_NUMBER);
+    assertFalse(set.isEmpty());
+  }
+
+  public void testLength() {
+    BitSet set = new BitSet();
+    assertEquals(0, set.length());
+
+    set.set(30);
+    assertEquals(31, set.length());
+    set.clear(30);
+    set.set(31);
+    assertEquals(32, set.length());
+    set.clear(31);
+    set.set(100);
+    set.set(BIG_NUMBER);
+    assertEquals(BIG_NUMBER + 1, set.length());
+    set.clear(BIG_NUMBER);
+    assertEquals(101, set.length());
+    set.clear(100);
+    assertEquals(0, set.length());
+    set.set(0);
+    assertEquals(1, set.length());
+
+    set = new BitSet();
+    for (int i = 0; i < 640; i++) {
+      set.set(i);
+      assertEquals(i + 1, set.length());
+    }
+    for (int i = 0; i < 639; i++) {
+      set.clear(i);
+      assertEquals(640, set.length());
+    }
+    set.clear(639);
+    assertEquals(0, set.length());
+  }
+
+  public void testNextClearBit() {
+    BitSet set = new BitSet(10);
+
+    assertEquals(BIG_NUMBER, set.nextClearBit(BIG_NUMBER));
+
+    for (int i = 0; i < 10; i++) {
+      assertEquals(i, set.nextClearBit(i));
+    }
+    for (int i = 10; i < 100; i++) {
+      assertEquals(i, set.nextClearBit(i));
+    }
+
+    set.set(0, 9);
+    set.set(10, 50);
+    for (int i = 0; i <= 9; i++) {
+      assertEquals(9, set.nextClearBit(i));
+    }
+    for (int i = 10; i < 50; i++) {
+      assertEquals(50, set.nextClearBit(i));
+    }
+    for (int i = 50; i < 100; i++) {
+      assertEquals(i, set.nextClearBit(i));
+    }
+
+    set = new BitSet();
+    set.set(61);
+    for (int i = 0; i < 60; i++) {
+      set.set(i);
+      assertEquals(i + 1, set.nextClearBit(0));
+    }
+    set.clear(61);
+    for (int i = 60; i < 100; i++) {
+      set.set(i);
+      assertEquals(i + 1, set.nextClearBit(0));
+    }
+    assertEquals(BIG_NUMBER, set.nextClearBit(BIG_NUMBER));
+
+    try {
+      set.nextClearBit(-1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testNextSetBit() {
+    BitSet set = new BitSet();
+
+    assertEquals(-1, set.nextSetBit(0));
+    assertEquals(-1, set.nextSetBit(BIG_NUMBER));
+
+    set.set(0);
+    set.set(1);
+    set.set(3);
+    set.set(31);
+    set.set(32);
+    set.set(33);
+    assertEquals(0, set.nextSetBit(0));
+    assertEquals(1, set.nextSetBit(1));
+    assertEquals(3, set.nextSetBit(2));
+    assertEquals(3, set.nextSetBit(3));
+    assertEquals(31, set.nextSetBit(4));
+    assertEquals(31, set.nextSetBit(31));
+    assertEquals(32, set.nextSetBit(32));
+    assertEquals(33, set.nextSetBit(33));
+    assertEquals(-1, set.nextSetBit(34));
+
+    set = new BitSet();
+    set.set(BIG_NUMBER);
+    assertEquals(BIG_NUMBER, set.nextSetBit(0));
+    assertEquals(BIG_NUMBER, set.nextSetBit(BIG_NUMBER));
+    assertEquals(-1, set.nextSetBit(BIG_NUMBER + 1));
+
+    for (int i = 0; i < TEST_SIZE; i++) {
+      set.set(BIG_NUMBER + i);
+    }
+    set.set(TEST_SIZE / 2);
+    assertEquals(TEST_SIZE / 2, set.nextSetBit(0));
+
+    try {
+      set.nextSetBit(-1);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testPreviousClearBit() {
+    BitSet set = new BitSet();
+
+    assertEquals(-1, set.previousClearBit(-1));
+    assertEquals(0, set.previousClearBit(0));
+    assertEquals(100, set.previousClearBit(100));
+
+    set.set(0, 9);
+    set.set(10, 50);
+    for (int i = 100; i >= 50; i--) {
+      assertEquals(i, set.previousClearBit(i));
+    }
+    for (int i = 49; i >= 9; i--) {
+      assertEquals(9, set.previousClearBit(i));
+    }
+    for (int i = 8; i >= 0; i--) {
+      assertEquals(-1, set.previousClearBit(i));
+    }
+
+    try {
+      set.previousClearBit(-100);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testPreviousSetBit() {
+    BitSet set = new BitSet();
+
+    assertEquals(-1, set.previousSetBit(-1));
+    assertEquals(-1, set.previousSetBit(0));
+    assertEquals(-1, set.previousSetBit(100));
+
+    set.set(0, 9);
+    set.set(10, 50);
+    for (int i = 100; i >= 50; i--) {
+      assertEquals(49, set.previousSetBit(i));
+    }
+    for (int i = 49; i >= 10; i--) {
+      assertEquals(i, set.previousSetBit(i));
+    }
+    assertEquals(8, set.previousSetBit(9));
+    for (int i = 8; i >= 0; i--) {
+      assertEquals(i, set.previousSetBit(i));
+    }
+
+    try {
+      set.previousSetBit(-100);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testOr() {
+    Pattern multiplesOf2And5 = new Pattern() {
+      @Override
+      public boolean contains(int i) {
+        return i % 2 == 0 || i % 5 == 0;
+      }
+    };
+
+    // setA will contain all multiples of 2
+    BitSet setA = createSetOfMultiples(2);
+
+    // setB will contain all multiples of 5
+    BitSet setB = createSetOfMultiples(5);
+
+    // or() the two sets to get both multiples of 2 and 5
+    setA.or(setB);
+
+    // verify multiples of 2 and 5
+    checkPattern(setA, multiplesOf2And5);
+
+    // or()ing a set to itself should do nothing
+    setA.or(setA);
+
+    // verify multiples of 2 and 5
+    checkPattern(setA, multiplesOf2And5);
+
+    // or()ing with set identical to itself should do nothing
+    setA.or((BitSet) setA.clone());
+
+    // verify multiples of 2 and 5
+    checkPattern(setA, multiplesOf2And5);
+
+    // or()ing with an empty set (all falses) should do nothing
+    setA.or(new BitSet());
+
+    // verify multiples of 2 and 5
+    checkPattern(setA, multiplesOf2And5);
+
+    // or()ing with all trues should result in all trues
+    BitSet trueSet = new BitSet(TEST_SIZE * 2);
+    trueSet.set(0, TEST_SIZE * 2);
+    setA.or(trueSet);
+    assertEquals(TEST_SIZE * 2, setA.cardinality());
+
+    setA = new BitSet();
+    setA.set(2);
+    setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    setA.or(setB);
+    checkRange(setA, 1, 4);
+  }
+
+  public void testSetIntBoolean() {
+    BitSet set = new BitSet();
+    set.set(0, true);
+    assertTrue(set.get(0));
+    set.set(0, false);
+    assertFalse(set.get(0));
+    set.set(BIG_NUMBER, true);
+    assertTrue(set.get(BIG_NUMBER));
+    set.set(BIG_NUMBER, false);
+    assertFalse(set.get(BIG_NUMBER));
+
+    set = new BitSet();
+    set.set(1, true);
+    set.set(2, true);
+    set.set(3, true);
+    set.set(4, true);
+    set.set(6, true);
+    set.set(4, false);
+    set.set(6, true);
+    set.set(6, false);
+    set.set(6, false);
+    set.set(8, true);
+    set.set(10, true);
+    set.set(8, false);
+    set.set(2, false);
+    set.set(8, true);
+    checkValues(set, 1, 3, 8, 10);
+    set.set(8, false);
+    checkValues(set, 1, 3, 10);
+    set.set(3, false);
+    checkValues(set, 1, 10);
+    set.set(10, false);
+    set.set(11, true);
+    checkValues(set, 1, 11);
+    set.set(1, false);
+    checkValues(set, 11);
+    set.set(11, false);
+    assertTrue(set.isEmpty());
+  }
+
+  public void testSetIntIntBoolean() {
+    BitSet set = new BitSet();
+    set.set(0, BIG_NUMBER, true);
+    assertEquals(BIG_NUMBER, set.cardinality());
+    set.set(0, BIG_NUMBER - 1, false);
+    assertEquals(1, set.cardinality());
+    set.set(0, BIG_NUMBER, false);
+    assertTrue(set.isEmpty());
+
+    set.set(0, 32, true);
+    checkRange(set, 0, 32);
+    set.set(0, 8, false);
+    checkRange(set, 8, 32);
+    set.set(0, 8, true);
+    checkRange(set, 0, 32);
+    set.set(7, 21, false);
+    checkValues(set, 0, 1, 2, 3, 4, 5, 6, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
+    set.set(22, 27, false);
+    checkValues(set, 0, 1, 2, 3, 4, 5, 6, 21, 27, 28, 29, 30, 31);
+
+    set = new BitSet();
+    set.set(11, 1000, true);
+    set.set(10, 12, false);
+    checkRange(set, 12, 1000);
+    assertEquals(988, set.cardinality());
+
+    set = new BitSet();
+    set.set(10, 12, true);
+    set.set(0, 10, true);
+    checkRange(set, 0, 12);
+    set.set(0, 12, false);
+    assertTrue(set.isEmpty());
+
+    set = new BitSet();
+    set.set(1, 20, true);
+    set.set(5, 10, false);
+    checkRange(set, 1, 5, 10, 20);
+
+    set = new BitSet();
+    set.set(1, 10, true);
+    set.set(5, 10, false);
+    set.set(10, 15, true);
+    checkRange(set, 1, 5, 10, 15);
+
+    // test exceptions
+    try {
+      set.set(-1, 2, true);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.set(3, 1, true);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.set(2, 2, true);
+
+    try {
+      set.set(-1, 2, false);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    try {
+      set.set(3, 1, false);
+      fail("exception expected");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+
+    set.set(2, 2, false);
+  }
+
+  public void testSize() {
+    // this is an unimportant test
+
+    BitSet set = new BitSet(7);
+    assertTrue(set.size() >= 7);
+    set = new BitSet(BIG_NUMBER);
+    assertTrue(set.size() >= BIG_NUMBER);
+  }
+
+  public void testToString() {
+    BitSet set = new BitSet();
+    assertEquals("{}", set.toString());
+
+    set.set(32);
+    assertEquals("{32}", set.toString());
+
+    set.set(BIG_NUMBER);
+    assertEquals("{32, " + BIG_NUMBER + "}", set.toString());
+
+    set.set(1);
+    assertEquals("{1, 32, " + BIG_NUMBER + "}", set.toString());
+
+    set.set(2);
+    assertEquals("{1, 2, 32, " + BIG_NUMBER + "}", set.toString());
+  }
+
+  public void testXor() {
+    Pattern exclusiveMultiples = new Pattern() {
+      @Override
+      public boolean contains(int i) {
+        return (i % 2 == 0) ^ (i % 3 == 0);
+      }
+    };
+
+    // setA will contain all multiples of 2
+    BitSet setA = createSetOfMultiples(2);
+
+    // setB will contain all multiples of 3
+    BitSet setB = createSetOfMultiples(3);
+
+    // xor()ing the sets should give exclusive multiples of 2 and 3
+    setA.xor(setB);
+
+    // verify by checking for exclusive multiples of 2 and 3
+    checkPattern(setA, exclusiveMultiples);
+
+    // xor()ing a set to an empty set should do nothing
+    setA.xor(new BitSet());
+
+    // verify by checking for exclusive multiples of 2 and 3
+    checkPattern(setA, exclusiveMultiples);
+
+    // xor()ing a set with all trues should flip each bit
+    BitSet trueSet = new BitSet(TEST_SIZE * 2);
+    trueSet.set(0, TEST_SIZE * 2);
+    setA.xor(trueSet);
+
+    // verify by checking for !(exclusive multiples of 2 and 3)
+    checkPattern(setA, new Pattern() {
+      @Override
+      public boolean contains(int i) {
+        return !((i % 2 == 0) ^ (i % 3 == 0));
+      }
+    });
+    // there were "TEST_SIZE" extra trues, so verify those came out as true
+    for (int i = TEST_SIZE; i < TEST_SIZE * 2; i++) {
+      assertTrue(setA.get(i));
+    }
+
+    // xor()ing a set to itself should result in an empty set
+    setA.xor(setA);
+    assertTrue(setA.isEmpty());
+
+    // xor()ing a set identical to itself should result in an empty set
+    setB.xor((BitSet) setB.clone());
+    assertTrue(setB.isEmpty());
+
+    setA = new BitSet();
+    setA.set(2);
+    setB = new BitSet();
+    setB.set(1);
+    setB.set(3);
+    setA.xor(setB);
+    checkRange(setA, 1, 4);
+  }
+
+  public void testToByteArray() {
+    BitSet set = new BitSet();
+    assertEquals(0, set.toByteArray().length);
+
+    int[] bits = {7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 32, 33, 34, 35, 36, 37, 38,
+        47, 49, 51, 52, 53, 54, 55};
+    for (int bit : bits) {
+      set.set(bit);
+    }
+    byte[] expected = {(byte) -128, (byte) -1, (byte) 0, (byte) 31,
+        (byte) 127, (byte) 128, (byte) 250};
+    assertTrue(Arrays.equals(expected, set.toByteArray()));
+
+    set.clear();
+    assertEquals(0, set.toByteArray().length);
+  }
+
+  public void testToLongArray() {
+    BitSet set = new BitSet();
+    assertEquals(0, set.toLongArray().length);
+
+    int[] bits = {63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+        83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
+        104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+        122, 123, 124, 125, 126, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+        141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+        159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+        177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 287, 288, 289,
+        290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+        308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+        326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343,
+        344, 345, 346, 347, 348, 349, 350, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394,
+        395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412,
+        413, 414, 450, 453, 454, 514, 515, 516, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528,
+        529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546,
+        547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564,
+        565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 584, 640, 641, 642, 643, 644, 645,
+        646, 711};
+    for (int bit : bits) {
+      set.set(bit);
+    }
+    long[] expected = {Long.MIN_VALUE, Long.MAX_VALUE, -1, 0, Integer.MIN_VALUE,
+        Integer.MAX_VALUE, 0x7fffffff, 100, -100, 256, 127, 128};
+    assertTrue(Arrays.equals(expected, set.toLongArray()));
+
+    set.clear();
+    assertEquals(0, set.toLongArray().length);
+  }
+
+  public void testValueOfBytes() {
+    BitSet set = BitSet.valueOf(new byte[0]);
+    assertTrue(set.isEmpty());
+
+    set = BitSet.valueOf(new byte[]{(byte) -128, (byte) -1, (byte) 0, (byte) 31,
+        (byte) 127, (byte) 128, (byte) 250});
+    assertEquals("{7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 32, 33, 34, 35, 36, 37, 38, " +
+        "47, 49, 51, 52, 53, 54, 55}", set.toString());
+  }
+
+  public void testValueOfLongs() {
+    BitSet set = BitSet.valueOf(new long[0]);
+    assertTrue(set.isEmpty());
+
+    set = BitSet.valueOf(new long[]{Long.MIN_VALUE, Long.MAX_VALUE, -1, 0, Integer.MIN_VALUE,
+        Integer.MAX_VALUE, 0x7fffffff, 100, -100, 256, 127, 128});
+    assertEquals("{63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, " +
+        "83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, " +
+        "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, " +
+        "122, 123, 124, 125, 126, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, " +
+        "141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, " +
+        "159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, " +
+        "177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 287, 288, 289, " +
+        "290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, " +
+        "308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, " +
+        "326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, " +
+        "344, 345, 346, 347, 348, 349, 350, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, " +
+        "395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, " +
+        "413, 414, 450, 453, 454, 514, 515, 516, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, " +
+        "529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, " +
+        "547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, " +
+        "565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 584, 640, 641, 642, 643, 644, 645, " +
+        "646, 711}", set.toString());
+  }
+}