Home | History | Annotate | Download | only in misc
      1 /*
      2  * [The "BSD license"]
      3  *  Copyright (c) 2010 Terence Parr
      4  *  All rights reserved.
      5  *
      6  *  Redistribution and use in source and binary forms, with or without
      7  *  modification, are permitted provided that the following conditions
      8  *  are met:
      9  *  1. Redistributions of source code must retain the above copyright
     10  *      notice, this list of conditions and the following disclaimer.
     11  *  2. Redistributions in binary form must reproduce the above copyright
     12  *      notice, this list of conditions and the following disclaimer in the
     13  *      documentation and/or other materials provided with the distribution.
     14  *  3. The name of the author may not be used to endorse or promote products
     15  *      derived from this software without specific prior written permission.
     16  *
     17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 package org.antlr.misc;
     29 
     30 import org.antlr.analysis.Label;
     31 import org.antlr.tool.Grammar;
     32 
     33 import java.util.Collection;
     34 import java.util.Iterator;
     35 import java.util.List;
     36 import java.util.Map;
     37 
     38 /**A BitSet to replace java.util.BitSet.
     39  *
     40  * Primary differences are that most set operators return new sets
     41  * as opposed to oring and anding "in place".  Further, a number of
     42  * operations were added.  I cannot contain a BitSet because there
     43  * is no way to access the internal bits (which I need for speed)
     44  * and, because it is final, I cannot subclass to add functionality.
     45  * Consider defining set degree.  Without access to the bits, I must
     46  * call a method n times to test the ith bit...ack!
     47  *
     48  * Also seems like or() from util is wrong when size of incoming set is bigger
     49  * than this.bits.length.
     50  *
     51  * @author Terence Parr
     52  */
     53 public class BitSet implements IntSet, Cloneable {
     54     protected final static int BITS = 64;    // number of bits / long
     55     protected final static int LOG_BITS = 6; // 2^6 == 64
     56 
     57     /* We will often need to do a mod operator (i mod nbits).  Its
     58      * turns out that, for powers of two, this mod operation is
     59      * same as (i & (nbits-1)).  Since mod is slow, we use a
     60      * precomputed mod mask to do the mod instead.
     61      */
     62     protected final static int MOD_MASK = BITS - 1;
     63 
     64     /** The actual data bits */
     65     protected long bits[];
     66 
     67     /** Construct a bitset of size one word (64 bits) */
     68     public BitSet() {
     69         this(BITS);
     70     }
     71 
     72     /** Construction from a static array of longs */
     73     public BitSet(long[] bits_) {
     74         bits = bits_;
     75     }
     76 
     77     /** Construct a bitset given the size
     78      * @param nbits The size of the bitset in bits
     79      */
     80     public BitSet(int nbits) {
     81         bits = new long[((nbits - 1) >> LOG_BITS) + 1];
     82     }
     83 
     84     /** or this element into this set (grow as necessary to accommodate) */
     85 	@Override
     86     public void add(int el) {
     87         //System.out.println("add("+el+")");
     88         int n = wordNumber(el);
     89         //System.out.println("word number is "+n);
     90         //System.out.println("bits.length "+bits.length);
     91         if (n >= bits.length) {
     92             growToInclude(el);
     93         }
     94         bits[n] |= bitMask(el);
     95     }
     96 
     97 	@Override
     98     public void addAll(IntSet set) {
     99         if ( set instanceof BitSet ) {
    100             this.orInPlace((BitSet)set);
    101         }
    102 		else if ( set instanceof IntervalSet ) {
    103 			IntervalSet other = (IntervalSet)set;
    104 			// walk set and add each interval
    105 			for (Interval I : other.intervals) {
    106 				this.orInPlace(BitSet.range(I.a,I.b));
    107 			}
    108 		}
    109 		else {
    110 			throw new IllegalArgumentException("can't add "+
    111 											   set.getClass().getName()+
    112 											   " to BitSet");
    113 		}
    114     }
    115 
    116 	public void addAll(int[] elements) {
    117 		if ( elements==null ) {
    118 			return;
    119 		}
    120 		for (int i = 0; i < elements.length; i++) {
    121 			int e = elements[i];
    122 			add(e);
    123 		}
    124 	}
    125 
    126 	public void addAll(Iterable<Integer> elements) {
    127 		if ( elements==null ) {
    128 			return;
    129 		}
    130 		for (Integer element : elements) {
    131 			add(element);
    132 		}
    133 		/*
    134 		int n = elements.size();
    135 		for (int i = 0; i < n; i++) {
    136 			Object o = elements.get(i);
    137 			if ( !(o instanceof Integer) ) {
    138 				throw new IllegalArgumentException();
    139 			}
    140 			Integer eI = (Integer)o;
    141 			add(eI.intValue());
    142 		}
    143 		 */
    144 	}
    145 
    146 	@Override
    147     public IntSet and(IntSet a) {
    148         BitSet s = (BitSet)this.clone();
    149         s.andInPlace((BitSet)a);
    150         return s;
    151     }
    152 
    153     public void andInPlace(BitSet a) {
    154         int min = Math.min(bits.length, a.bits.length);
    155         for (int i = min - 1; i >= 0; i--) {
    156             bits[i] &= a.bits[i];
    157         }
    158         // clear all bits in this not present in a (if this bigger than a).
    159         for (int i = min; i < bits.length; i++) {
    160             bits[i] = 0;
    161         }
    162     }
    163 
    164     private static long bitMask(int bitNumber) {
    165         int bitPosition = bitNumber & MOD_MASK; // bitNumber mod BITS
    166         return 1L << bitPosition;
    167     }
    168 
    169     public void clear() {
    170         for (int i = bits.length - 1; i >= 0; i--) {
    171             bits[i] = 0;
    172         }
    173     }
    174 
    175     public void clear(int el) {
    176         int n = wordNumber(el);
    177         if (n >= bits.length) {	// grow as necessary to accommodate
    178             growToInclude(el);
    179         }
    180         bits[n] &= ~bitMask(el);
    181     }
    182 
    183 	@Override
    184     public Object clone() {
    185         BitSet s;
    186         try {
    187             s = (BitSet)super.clone();
    188             s.bits = new long[bits.length];
    189             System.arraycopy(bits, 0, s.bits, 0, bits.length);
    190         }
    191         catch (CloneNotSupportedException e) {
    192             throw new InternalError();
    193         }
    194         return s;
    195     }
    196 
    197 	@Override
    198     public int size() {
    199         int deg = 0;
    200         for (int i = bits.length - 1; i >= 0; i--) {
    201             long word = bits[i];
    202             if (word != 0L) {
    203                 for (int bit = BITS - 1; bit >= 0; bit--) {
    204                     if ((word & (1L << bit)) != 0) {
    205                         deg++;
    206                     }
    207                 }
    208             }
    209         }
    210         return deg;
    211     }
    212 
    213 	@Override
    214     public boolean equals(Object other) {
    215         if ( other == null || !(other instanceof BitSet) ) {
    216             return false;
    217         }
    218 
    219         BitSet otherSet = (BitSet)other;
    220 
    221         int n = Math.min(this.bits.length, otherSet.bits.length);
    222 
    223         // for any bits in common, compare
    224         for (int i=0; i<n; i++) {
    225             if (this.bits[i] != otherSet.bits[i]) {
    226                 return false;
    227             }
    228         }
    229 
    230         // make sure any extra bits are off
    231 
    232         if (this.bits.length > n) {
    233             for (int i = n+1; i<this.bits.length; i++) {
    234                 if (this.bits[i] != 0) {
    235                     return false;
    236                 }
    237             }
    238         }
    239         else if (otherSet.bits.length > n) {
    240             for (int i = n+1; i<otherSet.bits.length; i++) {
    241                 if (otherSet.bits[i] != 0) {
    242                     return false;
    243                 }
    244             }
    245         }
    246 
    247         return true;
    248     }
    249 
    250     /**
    251      * Grows the set to a larger number of bits.
    252      * @param bit element that must fit in set
    253      */
    254     public void growToInclude(int bit) {
    255         int newSize = Math.max(bits.length << 1, numWordsToHold(bit));
    256         long newbits[] = new long[newSize];
    257         System.arraycopy(bits, 0, newbits, 0, bits.length);
    258         bits = newbits;
    259     }
    260 
    261 	@Override
    262     public boolean member(int el) {
    263         int n = wordNumber(el);
    264         if (n >= bits.length) return false;
    265         return (bits[n] & bitMask(el)) != 0;
    266     }
    267 
    268     /** Get the first element you find and return it.  Return Label.INVALID
    269      *  otherwise.
    270      */
    271 	@Override
    272     public int getSingleElement() {
    273         for (int i = 0; i < (bits.length << LOG_BITS); i++) {
    274             if (member(i)) {
    275                 return i;
    276             }
    277         }
    278         return Label.INVALID;
    279     }
    280 
    281 	@Override
    282     public boolean isNil() {
    283         for (int i = bits.length - 1; i >= 0; i--) {
    284             if (bits[i] != 0) return false;
    285         }
    286         return true;
    287     }
    288 
    289     public IntSet complement() {
    290         BitSet s = (BitSet)this.clone();
    291         s.notInPlace();
    292         return s;
    293     }
    294 
    295 	@Override
    296     public IntSet complement(IntSet set) {
    297 		if ( set==null ) {
    298 			return this.complement();
    299 		}
    300         return set.subtract(this);
    301     }
    302 
    303     public void notInPlace() {
    304         for (int i = bits.length - 1; i >= 0; i--) {
    305             bits[i] = ~bits[i];
    306         }
    307     }
    308 
    309     /** complement bits in the range 0..maxBit. */
    310     public void notInPlace(int maxBit) {
    311         notInPlace(0, maxBit);
    312     }
    313 
    314     /** complement bits in the range minBit..maxBit.*/
    315     public void notInPlace(int minBit, int maxBit) {
    316         // make sure that we have room for maxBit
    317         growToInclude(maxBit);
    318         for (int i = minBit; i <= maxBit; i++) {
    319             int n = wordNumber(i);
    320             bits[n] ^= bitMask(i);
    321         }
    322     }
    323 
    324     private int numWordsToHold(int el) {
    325         return (el >> LOG_BITS) + 1;
    326     }
    327 
    328     public static BitSet of(int el) {
    329         BitSet s = new BitSet(el + 1);
    330         s.add(el);
    331         return s;
    332     }
    333 
    334     public static BitSet of(Collection<? extends Integer> elements) {
    335         BitSet s = new BitSet();
    336         for (Integer el : elements) {
    337             s.add(el);
    338         }
    339         return s;
    340     }
    341 
    342 	public static BitSet of(IntSet set) {
    343 		if ( set==null ) {
    344 			return null;
    345 		}
    346 
    347 		if ( set instanceof BitSet ) {
    348 			return (BitSet)set;
    349 		}
    350 		if ( set instanceof IntervalSet ) {
    351 			BitSet s = new BitSet();
    352 			s.addAll(set);
    353 			return s;
    354 		}
    355 		throw new IllegalArgumentException("can't create BitSet from "+set.getClass().getName());
    356 	}
    357 
    358     public static BitSet of(Map<? extends Integer, ?> elements) {
    359         return BitSet.of(elements.keySet());
    360     }
    361 
    362 	public static BitSet range(int a, int b) {
    363 		BitSet s = new BitSet(b + 1);
    364 		for (int i = a; i <= b; i++) {
    365 			int n = wordNumber(i);
    366 			s.bits[n] |= bitMask(i);
    367 		}
    368 		return s;
    369 	}
    370 
    371     /** return this | a in a new set */
    372 	@Override
    373     public IntSet or(IntSet a) {
    374 		if ( a==null ) {
    375 			return this;
    376 		}
    377         BitSet s = (BitSet)this.clone();
    378         s.orInPlace((BitSet)a);
    379         return s;
    380     }
    381 
    382     public void orInPlace(BitSet a) {
    383 		if ( a==null ) {
    384 			return;
    385 		}
    386         // If this is smaller than a, grow this first
    387         if (a.bits.length > bits.length) {
    388             setSize(a.bits.length);
    389         }
    390         int min = Math.min(bits.length, a.bits.length);
    391         for (int i = min - 1; i >= 0; i--) {
    392             bits[i] |= a.bits[i];
    393         }
    394     }
    395 
    396     // remove this element from this set
    397 	@Override
    398     public void remove(int el) {
    399         int n = wordNumber(el);
    400         if (n >= bits.length) {
    401             growToInclude(el);
    402         }
    403         bits[n] &= ~bitMask(el);
    404     }
    405 
    406     /**
    407      * Sets the size of a set.
    408      * @param nwords how many words the new set should be
    409      */
    410     private void setSize(int nwords) {
    411         long newbits[] = new long[nwords];
    412         int n = Math.min(nwords, bits.length);
    413         System.arraycopy(bits, 0, newbits, 0, n);
    414         bits = newbits;
    415     }
    416 
    417     public int numBits() {
    418         return bits.length << LOG_BITS; // num words * bits per word
    419     }
    420 
    421     /** return how much space is being used by the bits array not
    422      *  how many actually have member bits on.
    423      */
    424     public int lengthInLongWords() {
    425         return bits.length;
    426     }
    427 
    428     /**Is this contained within a? */
    429     public boolean subset(BitSet a) {
    430         if (a == null) return false;
    431         return this.and(a).equals(this);
    432     }
    433 
    434     /**Subtract the elements of 'a' from 'this' in-place.
    435      * Basically, just turn off all bits of 'this' that are in 'a'.
    436      */
    437     public void subtractInPlace(BitSet a) {
    438         if (a == null) return;
    439         // for all words of 'a', turn off corresponding bits of 'this'
    440         for (int i = 0; i < bits.length && i < a.bits.length; i++) {
    441             bits[i] &= ~a.bits[i];
    442         }
    443     }
    444 
    445 	@Override
    446     public IntSet subtract(IntSet a) {
    447         if (a == null || !(a instanceof BitSet)) return null;
    448 
    449         BitSet s = (BitSet)this.clone();
    450         s.subtractInPlace((BitSet)a);
    451         return s;
    452     }
    453 
    454 	@Override
    455 	public List<Integer> toList() {
    456 		throw new NoSuchMethodError("BitSet.toList() unimplemented");
    457 	}
    458 
    459     public int[] toArray() {
    460         int[] elems = new int[size()];
    461         int en = 0;
    462         for (int i = 0; i < (bits.length << LOG_BITS); i++) {
    463             if (member(i)) {
    464                 elems[en++] = i;
    465             }
    466         }
    467         return elems;
    468     }
    469 
    470     public long[] toPackedArray() {
    471         return bits;
    472     }
    473 
    474 	@Override
    475     public String toString() {
    476         return toString(null);
    477     }
    478 
    479     /** Transform a bit set into a string by formatting each element as an integer
    480      * separator The string to put in between elements
    481      * @return A commma-separated list of values
    482      */
    483 	@Override
    484     public String toString(Grammar g) {
    485         StringBuilder buf = new StringBuilder();
    486         String separator = ",";
    487 		boolean havePrintedAnElement = false;
    488 		buf.append('{');
    489 
    490         for (int i = 0; i < (bits.length << LOG_BITS); i++) {
    491             if (member(i)) {
    492                 if (i > 0 && havePrintedAnElement ) {
    493                     buf.append(separator);
    494                 }
    495                 if ( g!=null ) {
    496                     buf.append(g.getTokenDisplayName(i));
    497                 }
    498                 else {
    499                     buf.append(i);
    500                 }
    501 				havePrintedAnElement = true;
    502             }
    503         }
    504 		buf.append('}');
    505         return buf.toString();
    506     }
    507 
    508     /**Create a string representation where instead of integer elements, the
    509      * ith element of vocabulary is displayed instead.  Vocabulary is a Vector
    510      * of Strings.
    511      * separator The string to put in between elements
    512      * @return A commma-separated list of character constants.
    513      */
    514     public String toString(String separator, List<String> vocabulary) {
    515         if (vocabulary == null) {
    516             return toString(null);
    517         }
    518         String str = "";
    519         for (int i = 0; i < (bits.length << LOG_BITS); i++) {
    520             if (member(i)) {
    521                 if (str.length() > 0) {
    522                     str += separator;
    523                 }
    524                 if (i >= vocabulary.size()) {
    525                     str += "'" + (char)i + "'";
    526                 }
    527                 else if (vocabulary.get(i) == null) {
    528                     str += "'" + (char)i + "'";
    529                 }
    530                 else {
    531                     str += vocabulary.get(i);
    532                 }
    533             }
    534         }
    535         return str;
    536     }
    537 
    538     /**
    539      * Dump a comma-separated list of the words making up the bit set.
    540      * Split each 64 bit number into two more manageable 32 bit numbers.
    541      * This generates a comma-separated list of C++-like unsigned long constants.
    542      */
    543     public String toStringOfHalfWords() {
    544         StringBuilder s = new StringBuilder();
    545         for (int i = 0; i < bits.length; i++) {
    546             if (i != 0) s.append(", ");
    547             long tmp = bits[i];
    548             tmp &= 0xFFFFFFFFL;
    549             s.append(tmp);
    550 			s.append("UL");
    551             s.append(", ");
    552             tmp = bits[i] >>> 32;
    553             tmp &= 0xFFFFFFFFL;
    554 			s.append(tmp);
    555 			s.append("UL");
    556         }
    557 		return s.toString();
    558     }
    559 
    560     /**
    561      * Dump a comma-separated list of the words making up the bit set.
    562      * This generates a comma-separated list of Java-like long int constants.
    563      */
    564     public String toStringOfWords() {
    565 		StringBuilder s = new StringBuilder();
    566         for (int i = 0; i < bits.length; i++) {
    567             if (i != 0) s.append(", ");
    568             s.append(bits[i]);
    569 			s.append("L");
    570         }
    571         return s.toString();
    572     }
    573 
    574     public String toStringWithRanges() {
    575         return toString();
    576     }
    577 
    578     private static int wordNumber(int bit) {
    579         return bit >> LOG_BITS; // bit / BITS
    580     }
    581 }
    582