blob: ab2928e185915f4f78972c16e728b9e984accb7d [file] [log] [blame]
/*
* [The "BSD license"]
* Copyright (c) 2010 Terence Parr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.misc;
import org.antlr.analysis.Label;
import org.antlr.tool.Grammar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**A BitSet to replace java.util.BitSet.
*
* Primary differences are that most set operators return new sets
* as opposed to oring and anding "in place". Further, a number of
* operations were added. I cannot contain a BitSet because there
* is no way to access the internal bits (which I need for speed)
* and, because it is final, I cannot subclass to add functionality.
* Consider defining set degree. Without access to the bits, I must
* call a method n times to test the ith bit...ack!
*
* Also seems like or() from util is wrong when size of incoming set is bigger
* than this.bits.length.
*
* @author Terence Parr
*/
public class BitSet implements IntSet, Cloneable {
protected final static int BITS = 64; // number of bits / long
protected final static int LOG_BITS = 6; // 2^6 == 64
/* We will often need to do a mod operator (i mod nbits). Its
* turns out that, for powers of two, this mod operation is
* same as (i & (nbits-1)). Since mod is slow, we use a
* precomputed mod mask to do the mod instead.
*/
protected final static int MOD_MASK = BITS - 1;
/** The actual data bits */
protected long bits[];
/** Construct a bitset of size one word (64 bits) */
public BitSet() {
this(BITS);
}
/** Construction from a static array of longs */
public BitSet(long[] bits_) {
bits = bits_;
}
/** Construct a bitset given the size
* @param nbits The size of the bitset in bits
*/
public BitSet(int nbits) {
bits = new long[((nbits - 1) >> LOG_BITS) + 1];
}
/** or this element into this set (grow as necessary to accommodate) */
public void add(int el) {
//System.out.println("add("+el+")");
int n = wordNumber(el);
//System.out.println("word number is "+n);
//System.out.println("bits.length "+bits.length);
if (n >= bits.length) {
growToInclude(el);
}
bits[n] |= bitMask(el);
}
public void addAll(IntSet set) {
if ( set instanceof BitSet ) {
this.orInPlace((BitSet)set);
}
else if ( set instanceof IntervalSet ) {
IntervalSet other = (IntervalSet)set;
// walk set and add each interval
for (Iterator iter = other.intervals.iterator(); iter.hasNext();) {
Interval I = (Interval) iter.next();
this.orInPlace(BitSet.range(I.a,I.b));
}
}
else {
throw new IllegalArgumentException("can't add "+
set.getClass().getName()+
" to BitSet");
}
}
public void addAll(int[] elements) {
if ( elements==null ) {
return;
}
for (int i = 0; i < elements.length; i++) {
int e = elements[i];
add(e);
}
}
public void addAll(Iterable elements) {
if ( elements==null ) {
return;
}
Iterator it = elements.iterator();
while (it.hasNext()) {
Object o = (Object) it.next();
if ( !(o instanceof Integer) ) {
throw new IllegalArgumentException();
}
Integer eI = (Integer)o;
add(eI.intValue());
}
/*
int n = elements.size();
for (int i = 0; i < n; i++) {
Object o = elements.get(i);
if ( !(o instanceof Integer) ) {
throw new IllegalArgumentException();
}
Integer eI = (Integer)o;
add(eI.intValue());
}
*/
}
public IntSet and(IntSet a) {
BitSet s = (BitSet)this.clone();
s.andInPlace((BitSet)a);
return s;
}
public void andInPlace(BitSet a) {
int min = Math.min(bits.length, a.bits.length);
for (int i = min - 1; i >= 0; i--) {
bits[i] &= a.bits[i];
}
// clear all bits in this not present in a (if this bigger than a).
for (int i = min; i < bits.length; i++) {
bits[i] = 0;
}
}
private final static long bitMask(int bitNumber) {
int bitPosition = bitNumber & MOD_MASK; // bitNumber mod BITS
return 1L << bitPosition;
}
public void clear() {
for (int i = bits.length - 1; i >= 0; i--) {
bits[i] = 0;
}
}
public void clear(int el) {
int n = wordNumber(el);
if (n >= bits.length) { // grow as necessary to accommodate
growToInclude(el);
}
bits[n] &= ~bitMask(el);
}
public Object clone() {
BitSet s;
try {
s = (BitSet)super.clone();
s.bits = new long[bits.length];
System.arraycopy(bits, 0, s.bits, 0, bits.length);
}
catch (CloneNotSupportedException e) {
throw new InternalError();
}
return s;
}
public int size() {
int deg = 0;
for (int i = bits.length - 1; i >= 0; i--) {
long word = bits[i];
if (word != 0L) {
for (int bit = BITS - 1; bit >= 0; bit--) {
if ((word & (1L << bit)) != 0) {
deg++;
}
}
}
}
return deg;
}
public boolean equals(Object other) {
if ( other == null || !(other instanceof BitSet) ) {
return false;
}
BitSet otherSet = (BitSet)other;
int n = Math.min(this.bits.length, otherSet.bits.length);
// for any bits in common, compare
for (int i=0; i<n; i++) {
if (this.bits[i] != otherSet.bits[i]) {
return false;
}
}
// make sure any extra bits are off
if (this.bits.length > n) {
for (int i = n+1; i<this.bits.length; i++) {
if (this.bits[i] != 0) {
return false;
}
}
}
else if (otherSet.bits.length > n) {
for (int i = n+1; i<otherSet.bits.length; i++) {
if (otherSet.bits[i] != 0) {
return false;
}
}
}
return true;
}
/**
* Grows the set to a larger number of bits.
* @param bit element that must fit in set
*/
public void growToInclude(int bit) {
int newSize = Math.max(bits.length << 1, numWordsToHold(bit));
long newbits[] = new long[newSize];
System.arraycopy(bits, 0, newbits, 0, bits.length);
bits = newbits;
}
public boolean member(int el) {
int n = wordNumber(el);
if (n >= bits.length) return false;
return (bits[n] & bitMask(el)) != 0;
}
/** Get the first element you find and return it. Return Label.INVALID
* otherwise.
*/
public int getSingleElement() {
for (int i = 0; i < (bits.length << LOG_BITS); i++) {
if (member(i)) {
return i;
}
}
return Label.INVALID;
}
public boolean isNil() {
for (int i = bits.length - 1; i >= 0; i--) {
if (bits[i] != 0) return false;
}
return true;
}
public IntSet complement() {
BitSet s = (BitSet)this.clone();
s.notInPlace();
return s;
}
public IntSet complement(IntSet set) {
if ( set==null ) {
return this.complement();
}
return set.subtract(this);
}
public void notInPlace() {
for (int i = bits.length - 1; i >= 0; i--) {
bits[i] = ~bits[i];
}
}
/** complement bits in the range 0..maxBit. */
public void notInPlace(int maxBit) {
notInPlace(0, maxBit);
}
/** complement bits in the range minBit..maxBit.*/
public void notInPlace(int minBit, int maxBit) {
// make sure that we have room for maxBit
growToInclude(maxBit);
for (int i = minBit; i <= maxBit; i++) {
int n = wordNumber(i);
bits[n] ^= bitMask(i);
}
}
private final int numWordsToHold(int el) {
return (el >> LOG_BITS) + 1;
}
public static BitSet of(int el) {
BitSet s = new BitSet(el + 1);
s.add(el);
return s;
}
public static BitSet of(Collection elements) {
BitSet s = new BitSet();
Iterator iter = elements.iterator();
while (iter.hasNext()) {
Integer el = (Integer) iter.next();
s.add(el.intValue());
}
return s;
}
public static BitSet of(IntSet set) {
if ( set==null ) {
return null;
}
if ( set instanceof BitSet ) {
return (BitSet)set;
}
if ( set instanceof IntervalSet ) {
BitSet s = new BitSet();
s.addAll(set);
return s;
}
throw new IllegalArgumentException("can't create BitSet from "+set.getClass().getName());
}
public static BitSet of(Map elements) {
return BitSet.of(elements.keySet());
}
public static BitSet range(int a, int b) {
BitSet s = new BitSet(b + 1);
for (int i = a; i <= b; i++) {
int n = wordNumber(i);
s.bits[n] |= bitMask(i);
}
return s;
}
/** return this | a in a new set */
public IntSet or(IntSet a) {
if ( a==null ) {
return this;
}
BitSet s = (BitSet)this.clone();
s.orInPlace((BitSet)a);
return s;
}
public void orInPlace(BitSet a) {
if ( a==null ) {
return;
}
// If this is smaller than a, grow this first
if (a.bits.length > bits.length) {
setSize(a.bits.length);
}
int min = Math.min(bits.length, a.bits.length);
for (int i = min - 1; i >= 0; i--) {
bits[i] |= a.bits[i];
}
}
// remove this element from this set
public void remove(int el) {
int n = wordNumber(el);
if (n >= bits.length) {
growToInclude(el);
}
bits[n] &= ~bitMask(el);
}
/**
* Sets the size of a set.
* @param nwords how many words the new set should be
*/
private void setSize(int nwords) {
long newbits[] = new long[nwords];
int n = Math.min(nwords, bits.length);
System.arraycopy(bits, 0, newbits, 0, n);
bits = newbits;
}
public int numBits() {
return bits.length << LOG_BITS; // num words * bits per word
}
/** return how much space is being used by the bits array not
* how many actually have member bits on.
*/
public int lengthInLongWords() {
return bits.length;
}
/**Is this contained within a? */
public boolean subset(BitSet a) {
if (a == null) return false;
return this.and(a).equals(this);
}
/**Subtract the elements of 'a' from 'this' in-place.
* Basically, just turn off all bits of 'this' that are in 'a'.
*/
public void subtractInPlace(BitSet a) {
if (a == null) return;
// for all words of 'a', turn off corresponding bits of 'this'
for (int i = 0; i < bits.length && i < a.bits.length; i++) {
bits[i] &= ~a.bits[i];
}
}
public IntSet subtract(IntSet a) {
if (a == null || !(a instanceof BitSet)) return null;
BitSet s = (BitSet)this.clone();
s.subtractInPlace((BitSet)a);
return s;
}
public List toList() {
throw new NoSuchMethodError("BitSet.toList() unimplemented");
}
public int[] toArray() {
int[] elems = new int[size()];
int en = 0;
for (int i = 0; i < (bits.length << LOG_BITS); i++) {
if (member(i)) {
elems[en++] = i;
}
}
return elems;
}
public long[] toPackedArray() {
return bits;
}
public String toString() {
return toString(null);
}
/** Transform a bit set into a string by formatting each element as an integer
* separator The string to put in between elements
* @return A commma-separated list of values
*/
public String toString(Grammar g) {
StringBuffer buf = new StringBuffer();
String separator = ",";
boolean havePrintedAnElement = false;
buf.append('{');
for (int i = 0; i < (bits.length << LOG_BITS); i++) {
if (member(i)) {
if (i > 0 && havePrintedAnElement ) {
buf.append(separator);
}
if ( g!=null ) {
buf.append(g.getTokenDisplayName(i));
}
else {
buf.append(i);
}
havePrintedAnElement = true;
}
}
buf.append('}');
return buf.toString();
}
/**Create a string representation where instead of integer elements, the
* ith element of vocabulary is displayed instead. Vocabulary is a Vector
* of Strings.
* separator The string to put in between elements
* @return A commma-separated list of character constants.
*/
public String toString(String separator, List vocabulary) {
if (vocabulary == null) {
return toString(null);
}
String str = "";
for (int i = 0; i < (bits.length << LOG_BITS); i++) {
if (member(i)) {
if (str.length() > 0) {
str += separator;
}
if (i >= vocabulary.size()) {
str += "'" + (char)i + "'";
}
else if (vocabulary.get(i) == null) {
str += "'" + (char)i + "'";
}
else {
str += (String)vocabulary.get(i);
}
}
}
return str;
}
/**
* Dump a comma-separated list of the words making up the bit set.
* Split each 64 bit number into two more manageable 32 bit numbers.
* This generates a comma-separated list of C++-like unsigned long constants.
*/
public String toStringOfHalfWords() {
StringBuffer s = new StringBuffer();
for (int i = 0; i < bits.length; i++) {
if (i != 0) s.append(", ");
long tmp = bits[i];
tmp &= 0xFFFFFFFFL;
s.append(tmp);
s.append("UL");
s.append(", ");
tmp = bits[i] >>> 32;
tmp &= 0xFFFFFFFFL;
s.append(tmp);
s.append("UL");
}
return s.toString();
}
/**
* Dump a comma-separated list of the words making up the bit set.
* This generates a comma-separated list of Java-like long int constants.
*/
public String toStringOfWords() {
StringBuffer s = new StringBuffer();
for (int i = 0; i < bits.length; i++) {
if (i != 0) s.append(", ");
s.append(bits[i]);
s.append("L");
}
return s.toString();
}
public String toStringWithRanges() {
return toString();
}
private final static int wordNumber(int bit) {
return bit >> LOG_BITS; // bit / BITS
}
}