671 lines
22 KiB
JavaScript
671 lines
22 KiB
JavaScript
"use strict";
|
|
/*!
|
|
* 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.
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.BitSet = void 0;
|
|
const util = require("util");
|
|
const MurmurHash_1 = require("./MurmurHash");
|
|
/**
|
|
* Private empty array used to construct empty BitSets
|
|
*/
|
|
const EMPTY_DATA = new Uint16Array(0);
|
|
/**
|
|
* Gets the word index of the `UInt16` element in `BitSet.data` containing the bit with the specified index.
|
|
*/
|
|
function getIndex(bitNumber) {
|
|
return bitNumber >>> 4;
|
|
}
|
|
/**
|
|
* Convert a word index into the bit index of the LSB of that word
|
|
*/
|
|
function unIndex(n) {
|
|
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) {
|
|
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) {
|
|
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, toBit) {
|
|
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 = 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++;
|
|
}
|
|
}
|
|
}
|
|
class BitSet {
|
|
/*
|
|
** constructor implementation
|
|
*/
|
|
constructor(arg) {
|
|
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`.
|
|
*/
|
|
and(set) {
|
|
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`.
|
|
*/
|
|
andNot(set) {
|
|
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`.
|
|
*/
|
|
cardinality() {
|
|
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;
|
|
}
|
|
clear(fromIndex, toIndex) {
|
|
if (fromIndex == null) {
|
|
this.data.fill(0);
|
|
}
|
|
else if (toIndex == null) {
|
|
this.set(fromIndex, false);
|
|
}
|
|
else {
|
|
this.set(fromIndex, toIndex, false);
|
|
}
|
|
}
|
|
flip(fromIndex, toIndex) {
|
|
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);
|
|
}
|
|
}
|
|
get(fromIndex, toIndex) {
|
|
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
|
|
*/
|
|
intersects(set) {
|
|
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() {
|
|
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.
|
|
*/
|
|
length() {
|
|
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
|
|
*/
|
|
nextClearBit(fromIndex) {
|
|
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
|
|
*/
|
|
nextSetBit(fromIndex) {
|
|
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`.
|
|
*/
|
|
or(set) {
|
|
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`
|
|
*/
|
|
previousClearBit(fromIndex) {
|
|
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`
|
|
*/
|
|
previousSetBit(fromIndex) {
|
|
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);
|
|
}
|
|
set(fromIndex, toIndex, value) {
|
|
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));
|
|
}
|
|
}
|
|
_setBits(word, value, mask) {
|
|
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() {
|
|
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");
|
|
// }
|
|
hashCode() {
|
|
return MurmurHash_1.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.
|
|
*/
|
|
equals(obj) {
|
|
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}"`.
|
|
*/
|
|
toString() {
|
|
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`.
|
|
*/
|
|
xor(set) {
|
|
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);
|
|
}
|
|
}
|
|
clone() {
|
|
return new BitSet(this);
|
|
}
|
|
[Symbol.iterator]() {
|
|
return new BitSetIterator(this.data);
|
|
}
|
|
// Overrides formatting for nodejs assert etc.
|
|
[util.inspect.custom]() {
|
|
return "BitSet " + this.toString();
|
|
}
|
|
}
|
|
exports.BitSet = BitSet;
|
|
class BitSetIterator {
|
|
constructor(data) {
|
|
this.data = data;
|
|
this.index = 0;
|
|
this.mask = 0xFFFF;
|
|
}
|
|
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 };
|
|
}
|
|
[Symbol.iterator]() { return this; }
|
|
}
|
|
//# sourceMappingURL=BitSet.js.map
|