bitmark-grammar
Version:
861 lines (755 loc) • 22.7 kB
text/typescript
/*!
* Copyright 2016 The ANTLR Project. All rights reserved.
* Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information.
*/
import * as assert from "assert";
import * as util from "util";
import { MurmurHash } from "./MurmurHash";
/**
* Private empty array used to construct empty BitSets
*/
const EMPTY_DATA: Uint16Array = new Uint16Array(0);
/**
* Gets the word index of the `UInt16` element in `BitSet.data` containing the bit with the specified index.
*/
function getIndex(bitNumber: number) {
return bitNumber >>> 4;
}
/**
* Convert a word index into the bit index of the LSB of that word
*/
function unIndex(n: number) {
return n * 16;
}
/**
* Get's the bit number of the least signficant bit set LSB which is set in a word non-zero word;
* Bit numbers run from LSB to MSB starting with 0.
*/
function findLSBSet(word: number) {
let bit = 1;
for (let i = 0; i < 16; i++) {
if ((word & bit) !== 0) {
return i;
}
bit = (bit << 1) >>> 0;
}
throw new RangeError("No specified bit found");
}
function findMSBSet(word: number) {
let bit = (1 << 15) >>> 0;
for (let i = 15; i >= 0; i--) {
if ((word & bit) !== 0) {
return i;
}
bit = bit >>> 1;
}
throw new RangeError("No specified bit found");
}
/**
* Gets a 16-bit mask with bit numbers fromBit to toBit (inclusive) set.
* Bit numbers run from LSB to MSB starting with 0.
*/
function bitsFor(fromBit: number, toBit: number): number {
fromBit &= 0xF;
toBit &= 0xF;
if (fromBit === toBit) {
return (1 << fromBit) >>> 0;
}
return ((0xFFFF >>> (15 - toBit)) ^ (0xFFFF >>> (16 - fromBit)));
}
/**
* A lookup table for number of set bits in a 16-bit integer. This is used to quickly count the cardinality (number of unique elements) of a BitSet.
*/
const POP_CNT: Uint8Array = new Uint8Array(65536);
for (let i = 0; i < 16; i++) {
const stride = (1 << i) >>> 0;
let index = 0;
while (index < POP_CNT.length) {
// skip the numbers where the bit isn't set
index += stride;
// increment the ones where the bit is set
for (let j = 0; j < stride; j++) {
POP_CNT[index]++;
index++;
}
}
}
export class BitSet implements Iterable<number>{
private data: Uint16Array;
/**
* Creates a new bit set. All bits are initially `false`.
*/
constructor();
/**
* Creates a bit set whose initial size is large enough to explicitly represent bits with indices in the range `0`
* through `nbits-1`. All bits are initially `false`.
*/
constructor(nbits: number);
/**
* Creates a bit set from a iterable list of numbers (including another BitSet);
*/
constructor(numbers: Iterable<number>);
/*
** constructor implementation
*/
constructor(arg?: number | Iterable<number>) {
if (!arg) {
// covering the case of unspecified and nbits===0
this.data = EMPTY_DATA;
} else if (typeof arg === "number") {
if (arg < 0) {
throw new RangeError("nbits cannot be negative");
} else {
this.data = new Uint16Array(getIndex(arg - 1) + 1);
}
} else {
if (arg instanceof BitSet) {
this.data = arg.data.slice(0); // Clone the data
} else {
let max = -1;
for (let v of arg) {
if (max < v) {
max = v;
}
}
this.data = new Uint16Array(getIndex(max - 1) + 1);
for (let v of arg) {
this.set(v);
}
}
}
}
/**
* Performs a logical **AND** of this target bit set with the argument bit set. This bit set is modified so that
* each bit in it has the value `true` if and only if it both initially had the value `true` and the corresponding
* bit in the bit set argument also had the value `true`.
*/
public and(set: BitSet): void {
const data = this.data;
const other = set.data;
const words = Math.min(data.length, other.length);
let lastWord = -1; // Keep track of index of last non-zero word
for (let i = 0; i < words; i++) {
let value = data[i] &= other[i];
if (value !== 0) {
lastWord = i;
}
}
if (lastWord === -1) {
this.data = EMPTY_DATA;
}
if (lastWord < data.length - 1) {
this.data = data.slice(0, lastWord + 1);
}
}
/**
* Clears all of the bits in this `BitSet` whose corresponding bit is set in the specified `BitSet`.
*/
public andNot(set: BitSet): void {
const data = this.data;
const other = set.data;
const words = Math.min(data.length, other.length);
let lastWord = -1; // Keep track of index of last non-zero word
for (let i = 0; i < words; i++) {
let value = data[i] &= (other[i] ^ 0xFFFF);
if (value !== 0) {
lastWord = i;
}
}
if (lastWord === -1) {
this.data = EMPTY_DATA;
}
if (lastWord < data.length - 1) {
this.data = data.slice(0, lastWord + 1);
}
}
/**
* Returns the number of bits set to `true` in this `BitSet`.
*/
public cardinality(): number {
if (this.isEmpty) {
return 0;
}
const data = this.data;
const length = data.length;
let result = 0;
for (let i = 0; i < length; i++) {
result += POP_CNT[data[i]];
}
return result;
}
/**
* Sets all of the bits in this `BitSet` to `false`.
*/
public clear(): void;
/**
* Sets the bit specified by the index to `false`.
*
* @param bitIndex the index of the bit to be cleared
*
* @throws RangeError if the specified index is negative
*/
public clear(bitIndex: number): void;
/**
* Sets the bits from the specified `fromIndex` (inclusive) to the specified `toIndex` (exclusive) to `false`.
*
* @param fromIndex index of the first bit to be cleared
* @param toIndex index after the last bit to be cleared
*
* @throws RangeError if `fromIndex` is negative, or `toIndex` is negative, or `fromIndex` is larger than `toIndex`
*/
public clear(fromIndex: number, toIndex: number): void;
public clear(fromIndex?: number, toIndex?: number): void {
if (fromIndex == null) {
this.data.fill(0);
} else if (toIndex == null) {
this.set(fromIndex, false);
} else {
this.set(fromIndex, toIndex, false);
}
}
/**
* Sets the bit at the specified index to the complement of its current value.
*
* @param bitIndex the index of the bit to flip
*
* @throws RangeError if the specified index is negative
*/
public flip(bitIndex: number): void;
/**
* Sets each bit from the specified `fromIndex` (inclusive) to the specified `toIndex` (exclusive) to the complement
* of its current value.
*
* @param fromIndex index of the first bit to flip
* @param toIndex index after the last bit to flip
*
* @throws RangeError if `fromIndex` is negative, or `toIndex` is negative, or `fromIndex` is larger than `toIndex`
*/
public flip(fromIndex: number, toIndex: number): void;
public flip(fromIndex: number, toIndex?: number): void {
if (toIndex == null) {
toIndex = fromIndex;
}
if (fromIndex < 0 || toIndex < fromIndex) {
throw new RangeError();
}
let word = getIndex(fromIndex);
const lastWord = getIndex(toIndex);
if (word === lastWord) {
this.data[word] ^= bitsFor(fromIndex, toIndex);
} else {
this.data[word++] ^= bitsFor(fromIndex, 15);
while (word < lastWord) {
this.data[word++] ^= 0xFFFF;
}
this.data[word++] ^= bitsFor(0, toIndex);
}
}
/**
* Returns the value of the bit with the specified index. The value is `true` if the bit with the index `bitIndex`
* is currently set in this `BitSet`; otherwise, the result is `false`.
*
* @param bitIndex the bit index
*
* @throws RangeError if the specified index is negative
*/
public get(bitIndex: number): boolean;
/**
* Returns a new `BitSet` composed of bits from this `BitSet` from `fromIndex` (inclusive) to `toIndex` (exclusive).
*
* @param fromIndex index of the first bit to include
* @param toIndex index after the last bit to include
*
* @throws RangeError if `fromIndex` is negative, or `toIndex` is negative, or `fromIndex` is larger than `toIndex`
*/
public get(fromIndex: number, toIndex: number): BitSet;
public get(fromIndex: number, toIndex?: number): boolean | BitSet {
if (toIndex === undefined) {
return !!(this.data[getIndex(fromIndex)] & bitsFor(fromIndex, fromIndex));
} else {
// return a BitSet
let result = new BitSet(toIndex + 1);
for (let i = fromIndex; i <= toIndex; i++) {
result.set(i, this.get(i));
}
return result;
}
}
/**
* Returns true if the specified `BitSet` has any bits set to `true` that are also set to `true` in this `BitSet`.
*
* @param set `BitSet` to intersect with
*/
public intersects(set: BitSet): boolean {
let smallerLength = Math.min(this.length(), set.length());
if (smallerLength === 0) {
return false;
}
let bound = getIndex(smallerLength - 1);
for (let i = 0; i <= bound; i++) {
if ((this.data[i] & set.data[i]) !== 0) {
return true;
}
}
return false;
}
/**
* Returns true if this `BitSet` contains no bits that are set to `true`.
*/
get isEmpty(): boolean {
return this.length() === 0;
}
/**
* Returns the "logical size" of this `BitSet`: the index of the highest set bit in the `BitSet` plus one. Returns
* zero if the `BitSet` contains no set bits.
*/
public length(): number {
if (!this.data.length) {
return 0;
}
return this.previousSetBit(unIndex(this.data.length) - 1) + 1;
}
/**
* Returns the index of the first bit that is set to `false` that occurs on or after the specified starting index,
* If no such bit exists then `-1` is returned.
*
* @param fromIndex the index to start checking from (inclusive)
*
* @throws RangeError if the specified index is negative
*/
public nextClearBit(fromIndex: number): number {
if (fromIndex < 0) {
throw new RangeError("fromIndex cannot be negative");
}
const data = this.data;
const length = data.length;
let word = getIndex(fromIndex);
if (word > length) {
return -1;
}
let ignore = 0xFFFF ^ bitsFor(fromIndex, 15);
if ((data[word] | ignore) === 0xFFFF) {
word++;
ignore = 0;
for (; word < length; word++) {
if (data[word] !== 0xFFFF) {
break;
}
}
if (word === length) {
// Hit the end
return -1;
}
}
return unIndex(word) + findLSBSet((data[word] | ignore) ^ 0xFFFF);
}
/**
* Returns the index of the first bit that is set to `true` that occurs on or after the specified starting index.
* If no such bit exists then `-1` is returned.
*
* To iterate over the `true` bits in a `BitSet`, use the following loop:
*
* ```
* for (let i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
* // operate on index i here
* }
* ```
*
* @param fromIndex the index to start checking from (inclusive)
*
* @throws RangeError if the specified index is negative
*/
public nextSetBit(fromIndex: number): number {
if (fromIndex < 0) {
throw new RangeError("fromIndex cannot be negative");
}
const data = this.data;
const length = data.length;
let word = getIndex(fromIndex);
if (word > length) {
return -1;
}
let mask = bitsFor(fromIndex, 15);
if ((data[word] & mask) === 0) {
word++;
mask = 0xFFFF;
for (; word < length; word++) {
if (data[word] !== 0) {
break;
}
}
if (word >= length) {
return -1;
}
}
return unIndex(word) + findLSBSet(data[word] & mask);
}
/**
* Performs a logical **OR** of this bit set with the bit set argument. This bit set is modified so that a bit in it
* has the value `true` if and only if it either already had the value `true` or the corresponding bit in the bit
* set argument has the value `true`.
*/
public or(set: BitSet): void {
const data = this.data;
const other = set.data;
const minWords = Math.min(data.length, other.length);
const words = Math.max(data.length, other.length);
const dest = data.length === words ? data : new Uint16Array(words);
let lastWord = -1;
// Or those words both sets have in common
for (let i = 0; i < minWords; i++) {
let value = dest[i] = data[i] | other[i];
if (value !== 0) {
lastWord = i;
}
}
// Copy words from larger set (if there is one)
const longer = data.length > other.length ? data : other;
for (let i = minWords; i < words; i++) {
let value = dest[i] = longer[i];
if (value !== 0) {
lastWord = i;
}
}
if (lastWord === -1) {
this.data = EMPTY_DATA;
} else if (dest.length === lastWord + 1) {
this.data = dest;
} else {
this.data = dest.slice(0, lastWord);
}
}
/**
* Returns the index of the nearest bit that is set to `false` that occurs on or before the specified starting
* index. If no such bit exists, or if `-1` is given as the starting index, then `-1` is returned.
*
* @param fromIndex the index to start checking from (inclusive)
*
* @throws RangeError if the specified index is less than `-1`
*/
public previousClearBit(fromIndex: number): number {
if (fromIndex < 0) {
throw new RangeError("fromIndex cannot be negative");
}
const data = this.data;
const length = data.length;
let word = getIndex(fromIndex);
if (word >= length) {
word = length - 1;
}
let ignore = 0xFFFF ^ bitsFor(0, fromIndex);
if ((data[word] | ignore) === 0xFFFF) {
ignore = 0;
word--;
for (; word >= 0; word--) {
if (data[word] !== 0xFFFF) {
break;
}
}
if (word < 0) {
// Hit the end
return -1;
}
}
return unIndex(word) + findMSBSet((data[word] | ignore) ^ 0xFFFF);
}
/**
* Returns the index of the nearest bit that is set to `true` that occurs on or before the specified starting index.
* If no such bit exists, or if `-1` is given as the starting index, then `-1` is returned.
*
* To iterate over the `true` bits in a `BitSet`, use the following loop:
*
* ```
* for (let i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
* // operate on index i here
* }
* ```
*
* @param fromIndex the index to start checking from (inclusive)
*
* @throws RangeError if the specified index is less than `-1`
*/
public previousSetBit(fromIndex: number): number {
if (fromIndex < 0) {
throw new RangeError("fromIndex cannot be negative");
}
const data = this.data;
const length = data.length;
let word = getIndex(fromIndex);
if (word >= length) {
word = length - 1;
}
let mask = bitsFor(0, fromIndex);
if ((data[word] & mask) === 0) {
word--;
mask = 0xFFFF;
for (; word >= 0; word--) {
if (data[word] !== 0) {
break;
}
}
if (word < 0) {
return -1;
}
}
return unIndex(word) + findMSBSet(data[word] & mask);
}
/**
* Sets the bit at the specified index to `true`.
*
* @param bitIndex a bit index
*
* @throws RangeError if the specified index is negative
*/
public set(bitIndex: number): void;
/**
* Sets the bit at the specified index to the specified value.
*
* @param bitIndex a bit index
* @param value a boolean value to set
*
* @throws RangeError if the specified index is negative
*/
public set(bitIndex: number, value: boolean): void;
/**
* Sets the bits from the specified `fromIndex` (inclusive) to the specified `toIndex` (exclusive) to `true`.
*
* @param fromIndex index of the first bit to be set
* @param toIndex index after the last bit to be set
*
* @throws RangeError if `fromIndex` is negative, or `toIndex` is negative, or `fromIndex` is larger than `toIndex`
*/
public set(fromIndex: number, toIndex: number): void;
/**
* Sets the bits from the specified `fromIndex` (inclusive) to the specified `toIndex` (exclusive) to the specified
* value.
*
* @param fromIndex index of the first bit to be set
* @param toIndex index after the last bit to be set
* @param value value to set the selected bits to
*
* @throws RangeError if `fromIndex` is negative, or `toIndex` is negative, or `fromIndex` is larger than `toIndex`
*/
public set(fromIndex: number, toIndex: number, value: boolean): void;
public set(fromIndex: number, toIndex?: boolean | number, value?: boolean): void {
if (toIndex === undefined) {
toIndex = fromIndex;
value = true;
} else if (typeof toIndex === "boolean") {
value = toIndex;
toIndex = fromIndex;
}
if (value === undefined) {
value = true;
}
if (fromIndex < 0 || fromIndex > toIndex) {
throw new RangeError();
}
let word = getIndex(fromIndex);
let lastWord = getIndex(toIndex);
if (value && lastWord >= this.data.length) {
// Grow array "just enough" for bits we need to set
let temp = new Uint16Array(lastWord + 1);
this.data.forEach((value, index) => temp[index] = value);
this.data = temp;
} else if (!value) {
// But there is no need to grow array to clear bits.
if (word >= this.data.length) {
// Early exit
return;
}
if (lastWord >= this.data.length) {
// Adjust work to fit array
lastWord = this.data.length - 1;
toIndex = this.data.length * 16 - 1;
}
}
if (word === lastWord) {
this._setBits(word, value, bitsFor(fromIndex, toIndex));
} else {
this._setBits(word++, value, bitsFor(fromIndex, 15));
while (word < lastWord) {
this.data[word++] = value ? 0xFFFF : 0;
}
this._setBits(word, value, bitsFor(0, toIndex));
}
}
private _setBits(word: number, value: boolean, mask: number) {
if (value) {
this.data[word] |= mask;
} else {
this.data[word] &= 0xFFFF ^ mask;
}
}
/**
* Returns the number of bits of space actually in use by this `BitSet` to represent bit values. The maximum element
* in the set is the size - 1st element.
*/
get size(): number {
return this.data.byteLength * 8;
}
/**
* Returns a new byte array containing all the bits in this bit set.
*
* More precisely, if
* `let bytes = s.toByteArray();`
* then `bytes.length === (s.length()+7)/8` and `s.get(n) === ((bytes[n/8] & (1<<(n%8))) != 0)` for all
* `n < 8 * bytes.length`.
*/
// toByteArray(): Int8Array {
// throw new Error("NOT IMPLEMENTED");
// }
/**
* Returns a new integer array containing all the bits in this bit set.
*
* More precisely, if
* `let integers = s.toIntegerArray();`
* then `integers.length === (s.length()+31)/32` and `s.get(n) === ((integers[n/32] & (1<<(n%32))) != 0)` for all
* `n < 32 * integers.length`.
*/
// toIntegerArray(): Int32Array {
// throw new Error("NOT IMPLEMENTED");
// }
public hashCode(): number {
return MurmurHash.hashCode(this.data, 22);
}
/**
* Compares this object against the specified object. The result is `true` if and only if the argument is not
* `undefined` and is a `Bitset` object that has exactly the same set of bits set to `true` as this bit set. That
* is, for every nonnegative index `k`,
*
* ```
* ((BitSet)obj).get(k) == this.get(k)
* ```
*
* must be true. The current sizes of the two bit sets are not compared.
*/
public equals(obj: any): boolean {
if (obj === this) {
return true;
} else if (!(obj instanceof BitSet)) {
return false;
}
const len = this.length();
if (len !== obj.length()) {
return false;
}
if (len === 0) {
return true;
}
let bound = getIndex(len - 1);
for (let i = 0; i <= bound; i++) {
if (this.data[i] !== obj.data[i]) {
return false;
}
}
return true;
}
/**
* Returns a string representation of this bit set. For every index for which this `BitSet` contains a bit in the
* set state, the decimal representation of that index is included in the result. Such indices are listed in order
* from lowest to highest, separated by ", " (a comma and a space) and surrounded by braces, resulting in the usual
* mathematical notation for a set of integers.
*
* Example:
*
* BitSet drPepper = new BitSet();
*
* Now `drPepper.toString()` returns `"{}"`.
*
* drPepper.set(2);
*
* Now `drPepper.toString()` returns `"{2}"`.
*
* drPepper.set(4);
* drPepper.set(10);
*
* Now `drPepper.toString()` returns `"{2, 4, 10}"`.
*/
public toString(): string {
let result = "{";
let first = true;
for (let i = this.nextSetBit(0); i >= 0; i = this.nextSetBit(i + 1)) {
if (first) {
first = false;
} else {
result += ", ";
}
result += i;
}
result += "}";
return result;
}
// static valueOf(bytes: Int8Array): BitSet;
// static valueOf(buffer: ArrayBuffer): BitSet;
// static valueOf(integers: Int32Array): BitSet;
// static valueOf(data: Int8Array | Int32Array | ArrayBuffer): BitSet {
// throw new Error("NOT IMPLEMENTED");
// }
/**
* Performs a logical **XOR** of this bit set with the bit set argument. This bit set is modified so that a bit in
* it has the value `true` if and only if one of the following statements holds:
*
* * The bit initially has the value `true`, and the corresponding bit in the argument has the value `false`.
* * The bit initially has the value `false`, and the corresponding bit in the argument has the value `true`.
*/
public xor(set: BitSet): void {
const data = this.data;
const other = set.data;
const minWords = Math.min(data.length, other.length);
const words = Math.max(data.length, other.length);
const dest = data.length === words ? data : new Uint16Array(words);
let lastWord = -1;
// Xor those words both sets have in common
for (let i = 0; i < minWords; i++) {
let value = dest[i] = data[i] ^ other[i];
if (value !== 0) {
lastWord = i;
}
}
// Copy words from larger set (if there is one)
const longer = data.length > other.length ? data : other;
for (let i = minWords; i < words; i++) {
let value = dest[i] = longer[i];
if (value !== 0) {
lastWord = i;
}
}
if (lastWord === -1) {
this.data = EMPTY_DATA;
} else if (dest.length === lastWord + 1) {
this.data = dest;
} else {
this.data = dest.slice(0, lastWord + 1);
}
}
public clone() {
return new BitSet(this);
}
public [Symbol.iterator](): IterableIterator<number> {
return new BitSetIterator(this.data);
}
// Overrides formatting for nodejs assert etc.
public [(util.inspect as any).custom](): string {
return "BitSet " + this.toString();
}
}
class BitSetIterator implements IterableIterator<number>{
private index = 0;
private mask = 0xFFFF;
constructor(private data: Uint16Array) { }
public next() {
while (this.index < this.data.length) {
const bits = this.data[this.index] & this.mask;
if (bits !== 0) {
const bitNumber = unIndex(this.index) + findLSBSet(bits);
this.mask = bitsFor(bitNumber + 1, 15);
return { done: false, value: bitNumber };
}
this.index++;
this.mask = 0xFFFF;
}
return { done: true, value: -1 };
}
public [Symbol.iterator](): IterableIterator<number> { return this; }
}