typedfastbitset
Version:
Speed-optimized BitSet implementation for modern browsers and JavaScript engines, using typed arrays
677 lines • 24.9 kB
JavaScript
"use strict";
/**
* TypedFastBitSet.js : a fast bit set implementation in JavaScript.
* (c) the authors
* Licensed under the Apache License, Version 2.0.
*
* Speed-optimized BitSet implementation for modern browsers and JavaScript engines.
*
* A BitSet is an ideal data structure to implement a Set when values being stored are
* reasonably small integers. It can be orders of magnitude faster than a generic set implementation.
* The FastBitSet implementation optimizes for speed, leveraging commonly available features
* like typed arrays.
*
* Simple usage :
* const b = new TypedFastBitSet();// initially empty
* // will throw exception if typed arrays are not supported
* b.add(1);// add the value "1"
* b.has(1); // check that the value is present! (will return true)
* b.add(2);
* console.log(""+b);// should display {1,2}
* b.add(10);
* b.array(); // would return [1,2,10]
*
* let c = new FastBitSet([1,2,3,10]); // create bitset initialized with values 1,2,3,10
* c.difference(b); // from c, remove elements that are in b (modifies c)
* c.difference2(b); // from c, remove elements that are in b (modifies b)
* c.change(b); // c will contain elements that are in b or in c, but not both
* const su = c.union_size(b);// compute the size of the union (bitsets are unchanged)
* c.union(b); // c will contain all elements that are in c and b
* const s1 = c.intersection_size(b);// compute the size of the intersection (bitsets are unchanged)
* c.intersection(b); // c will only contain elements that are in both c and b
* c = b.clone(); // create a (deep) copy of b and assign it to c.
* c.equals(b); // check whether c and b are equal
*
* See README.md file for a more complete description.
*
* You can install the library under node with the command line
* npm install typedfastbitset
*/
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypedFastBitSet = void 0;
var utils_1 = require("./utils");
function isIterable(obj) {
if (obj) {
return obj[Symbol.iterator] !== undefined;
}
return false;
}
/**
* you can provide an iterable
* an exception is thrown if typed arrays are not supported
*/
var TypedFastBitSet = /** @class */ (function () {
function TypedFastBitSet(iterable, words) {
var e_1, _a;
if (words === void 0) { words = new Uint32Array(8); }
this.words = words;
if (isIterable(iterable)) {
try {
for (var iterable_1 = __values(iterable), iterable_1_1 = iterable_1.next(); !iterable_1_1.done; iterable_1_1 = iterable_1.next()) {
var key = iterable_1_1.value;
this.add(key);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (iterable_1_1 && !iterable_1_1.done && (_a = iterable_1.return)) _a.call(iterable_1);
}
finally { if (e_1) throw e_1.error; }
}
}
}
/**
* @returns a new TypedFastBitset given a Uint32Array of words
*/
TypedFastBitSet.fromWords = function (words) {
return new TypedFastBitSet(undefined, words);
};
/**
* Add the value (Set the bit at index to true)
*/
TypedFastBitSet.prototype.add = function (index) {
this.resize(index);
this.words[index >>> 5] |= 1 << index;
};
/**
* If the value was not in the set, add it, otherwise remove it (flip bit at index)
*/
TypedFastBitSet.prototype.flip = function (index) {
this.resize(index);
this.words[index >>> 5] ^= 1 << index;
};
/**
* Remove all values, reset memory usage
*/
TypedFastBitSet.prototype.clear = function () {
this.words = new Uint32Array(8);
};
/**
* Set the bit at index to false
*/
TypedFastBitSet.prototype.remove = function (index) {
this.resize(index);
this.words[index >>> 5] &= ~(1 << index);
};
/**
* Set bits from start (inclusive) to end (exclusive)
*/
TypedFastBitSet.prototype.addRange = function (start, end) {
if (start >= end) {
return;
}
if (this.words.length << 5 <= end) {
this.resize(end);
}
var words = this.words;
var firstword = start >> 5;
var endword = (end - 1) >> 5;
if (firstword === endword) {
words[firstword] |= (~0 << start) & (~0 >>> -end);
return;
}
words[firstword] |= ~0 << start;
words.fill(~0, firstword + 1, endword);
words[endword] |= ~0 >>> -end;
};
/**
* Remove bits from start (inclusive) to end (exclusive)
*/
TypedFastBitSet.prototype.removeRange = function (start, end) {
var words = this.words;
start = Math.min(start, (words.length << 5) - 1);
end = Math.min(end, (words.length << 5) - 1);
if (start >= end) {
return;
}
var firstword = start >> 5;
var endword = (end - 1) >> 5;
if (firstword === endword) {
words[firstword] &= ~((~0 << start) & (~0 >>> -end));
return;
}
words[firstword] &= ~(~0 << start);
words.fill(0, firstword + 1, endword);
words[endword] &= ~(~0 >>> -end);
};
/**
* @returns true if no bit is set
*/
TypedFastBitSet.prototype.isEmpty = function () {
var words = this.words;
var c = words.length;
for (var i = 0; i < c; i++) {
if (words[i] !== 0)
return false;
}
return true;
};
/**
* Is the value contained in the set? Is the bit at index true or false?
*/
TypedFastBitSet.prototype.has = function (index) {
return (this.words[index >>> 5] & (1 << index)) !== 0;
};
/**
* Is any value of the (exclusive) range contained in the set?
*/
TypedFastBitSet.prototype.hasAnyInRange = function (start, end) {
if (start >= end)
return false;
var words = this.words;
start = Math.min(start, (words.length << 5) - 1);
end = Math.min(end, (words.length << 5) - 1);
var firstword = start >>> 5;
var endword = (end - 1) >> 5;
if (firstword === endword)
return (words[firstword] & ((~0 << start) & (~0 >>> -end))) !== 0;
if ((words[firstword] & (~0 << start)) !== 0)
return true;
for (var index = firstword + 1; index < endword - 1; index++)
if (words[index] !== 0)
return true;
return (words[endword] & (~0 >>> -end)) !== 0;
};
/**
* Tries to add the value (Set the bit at index to true)
*
* @returns 1 if the value was added, 0 if the value was already present
*/
TypedFastBitSet.prototype.checkedAdd = function (index) {
var words = this.words;
this.resize(index);
var word = words[index >>> 5];
var newword = word | (1 << index);
words[index >>> 5] = newword;
return ((newword ^ word) >>> index);
};
/**
* Reduce the memory usage to a minimum
*/
TypedFastBitSet.prototype.trim = function () {
var words = this.words;
var nl = words.length;
while (nl > 0 && words[nl - 1] === 0) {
nl--;
}
this.words = words.slice(0, nl);
};
/**
* Resize the bitset so that we can write a value at index
*/
TypedFastBitSet.prototype.resize = function (index) {
var words = this.words;
if (words.length << 5 > index)
return;
var count = (index + 32) >>> 5; // just what is needed
var newwords = new Uint32Array(count << 1);
newwords.set(words); // hopefully, this copy is fast
this.words = newwords;
};
/**
* @returns How many values stored in the set? How many set bits?
*/
TypedFastBitSet.prototype.size = function () {
var words = this.words;
var answer = 0;
var c = words.length;
var k = 0 | 0;
for (; k + 4 < c; k += 4) {
answer += (0, utils_1.hammingWeight4)(words[k] | 0, words[k + 1] | 0, words[k + 2] | 0, words[k + 3] | 0);
}
for (; k < c; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] | 0);
}
return answer;
};
/**
* @returns an array with the set bit locations (values)
*/
TypedFastBitSet.prototype.array = function () {
var words = this.words;
var answer = new Array(this.size());
var pos = 0 | 0;
var c = words.length;
for (var k = 0; k < c; ++k) {
var w = words[k];
while (w != 0) {
var t = w & -w;
answer[pos++] = (k << 5) + (0, utils_1.hammingWeight)((t - 1) | 0);
w ^= t;
}
}
return answer;
};
TypedFastBitSet.prototype.forEach = function (fnc) {
var words = this.words;
var c = words.length;
for (var k = 0; k < c; ++k) {
var w = words[k];
while (w != 0) {
var t = w & -w;
fnc(((k << 5) + (0, utils_1.hammingWeight)(t - 1)) | 0);
w ^= t;
}
}
};
/**
* Iterator of set bit locations (values)
*/
TypedFastBitSet.prototype[Symbol.iterator] = function () {
var _a;
var words = this.words;
var c = words.length;
var k = 0;
var w = words[k];
return _a = {},
_a[Symbol.iterator] = function () {
return this;
},
_a.next = function () {
while (k < c) {
if (w !== 0) {
var t = w & -w;
var value = (k << 5) + (0, utils_1.hammingWeight)((t - 1) | 0);
w ^= t;
return { done: false, value: value };
}
else {
k++;
if (k < c) {
w = words[k];
}
}
}
return { done: true, value: undefined };
},
_a;
};
/**
* @returns a copy of this bitmap
*/
TypedFastBitSet.prototype.clone = function () {
return TypedFastBitSet.fromWords(new Uint32Array(this.words));
};
/**
* Check if this bitset intersects with another one,
* no bitmap is modified
*/
TypedFastBitSet.prototype.intersects = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var newcount = Math.min(words.length, otherWords.length);
for (var k = 0 | 0; k < newcount; ++k) {
if ((words[k] & otherWords[k]) !== 0)
return true;
}
return false;
};
/**
* Computes the intersection between this bitset and another one,
* the current bitmap is modified (and returned by the function)
*/
TypedFastBitSet.prototype.intersection = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var newcount = Math.min(words.length, otherWords.length);
var k = 0 | 0;
for (; k + 7 < newcount; k += 8) {
words[k] &= otherWords[k];
words[k + 1] &= otherWords[k + 1];
words[k + 2] &= otherWords[k + 2];
words[k + 3] &= otherWords[k + 3];
words[k + 4] &= otherWords[k + 4];
words[k + 5] &= otherWords[k + 5];
words[k + 6] &= otherWords[k + 6];
words[k + 7] &= otherWords[k + 7];
}
for (; k < newcount; ++k) {
words[k] &= otherWords[k];
}
var c = words.length;
for (k = newcount; k < c; ++k) {
words[k] = 0;
}
return this;
};
/**
* Computes the size of the intersection between this bitset and another one
*/
TypedFastBitSet.prototype.intersection_size = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var newcount = Math.min(words.length, otherWords.length);
var answer = 0 | 0;
for (var k = 0 | 0; k < newcount; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] & otherWords[k]);
}
return answer;
};
/**
* Computes the intersection between this bitset and another one,
* a new bitmap is generated
*/
TypedFastBitSet.prototype.new_intersection = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var count = Math.min(words.length, otherWords.length);
var newWords = new Uint32Array(count);
var k = 0 | 0;
for (; k + 7 < count; k += 8) {
newWords[k] = words[k] & otherWords[k];
newWords[k + 1] = words[k + 1] & otherWords[k + 1];
newWords[k + 2] = words[k + 2] & otherWords[k + 2];
newWords[k + 3] = words[k + 3] & otherWords[k + 3];
newWords[k + 4] = words[k + 4] & otherWords[k + 4];
newWords[k + 5] = words[k + 5] & otherWords[k + 5];
newWords[k + 7] = words[k + 7] & otherWords[k + 7];
newWords[k + 6] = words[k + 6] & otherWords[k + 6];
}
for (; k < count; ++k) {
newWords[k] = words[k] & otherWords[k];
}
return new TypedFastBitSet(undefined, newWords);
};
/**
* Computes the intersection between this bitset and another one,
* the current bitmap is modified
*/
TypedFastBitSet.prototype.equals = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var mcount = Math.min(words.length, otherWords.length);
for (var k = 0 | 0; k < mcount; ++k) {
if (words[k] != otherWords[k])
return false;
}
if (words.length < otherWords.length) {
var c = otherWords.length;
for (var k = words.length; k < c; ++k) {
if (otherWords[k] != 0)
return false;
}
}
else if (otherWords.length < words.length) {
var c = words.length;
for (var k = otherWords.length; k < c; ++k) {
if (words[k] != 0)
return false;
}
}
return true;
};
/**
* Computes the difference between this bitset and another one,
* the current bitset is modified (and returned by the function)
*/
TypedFastBitSet.prototype.difference = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var newcount = Math.min(words.length, otherWords.length);
var k = 0 | 0;
for (; k + 7 < newcount; k += 8) {
words[k] &= ~otherWords[k];
words[k + 1] &= ~otherWords[k + 1];
words[k + 2] &= ~otherWords[k + 2];
words[k + 3] &= ~otherWords[k + 3];
words[k + 4] &= ~otherWords[k + 4];
words[k + 5] &= ~otherWords[k + 5];
words[k + 6] &= ~otherWords[k + 6];
words[k + 7] &= ~otherWords[k + 7];
}
for (; k < newcount; ++k) {
words[k] &= ~otherWords[k];
}
return this;
};
/**
* Computes the difference between this bitset and another one,
* the other bitset is modified (and returned by the function)
*
* (for this set A and other set B, this computes B = A - B and returns B)
*/
TypedFastBitSet.prototype.difference2 = function (otherbitmap) {
var mincount = Math.min(this.words.length, otherbitmap.words.length);
otherbitmap.resize((this.words.length << 5) - 1);
var words = this.words;
var otherWords = otherbitmap.words;
var k = 0 | 0;
for (; k + 7 < mincount; k += 8) {
otherWords[k] = words[k] & ~otherWords[k];
otherWords[k + 1] = words[k + 1] & ~otherWords[k + 1];
otherWords[k + 2] = words[k + 2] & ~otherWords[k + 2];
otherWords[k + 3] = words[k + 3] & ~otherWords[k + 3];
otherWords[k + 4] = words[k + 4] & ~otherWords[k + 4];
otherWords[k + 5] = words[k + 5] & ~otherWords[k + 5];
otherWords[k + 6] = words[k + 6] & ~otherWords[k + 6];
otherWords[k + 7] = words[k + 7] & ~otherWords[k + 7];
}
for (; k < mincount; ++k) {
otherWords[k] = this.words[k] & ~otherWords[k];
}
// remaining words are all part of difference
for (; k < this.words.length; ++k) {
otherWords[k] = this.words[k];
}
otherWords.fill(0, k);
return otherbitmap;
};
/**
* Computes the difference between this bitset and another one,
* a new bitmap is generated
*/
TypedFastBitSet.prototype.new_difference = function (otherbitmap) {
return this.clone().difference(otherbitmap); // should be fast enough
};
/**
* Computes the size of the difference between this bitset and another one
*/
TypedFastBitSet.prototype.difference_size = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var newcount = Math.min(words.length, otherWords.length);
var answer = 0 | 0;
var k = 0 | 0;
for (; k < newcount; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] & ~otherWords[k]);
}
var c = words.length;
for (; k < c; ++k) {
answer += (0, utils_1.hammingWeight)(words[k]);
}
return answer;
};
/**
* Computes the changed elements (XOR) between this bitset and another one,
* the current bitset is modified (and returned by the function)
*/
TypedFastBitSet.prototype.change = function (otherbitmap) {
var otherWords = otherbitmap.words;
var mincount = Math.min(this.words.length, otherWords.length);
this.resize((otherWords.length << 5) - 1);
var words = this.words;
var k = 0 | 0;
for (; k + 7 < mincount; k += 8) {
words[k] ^= otherWords[k];
words[k + 1] ^= otherWords[k + 1];
words[k + 2] ^= otherWords[k + 2];
words[k + 3] ^= otherWords[k + 3];
words[k + 4] ^= otherWords[k + 4];
words[k + 5] ^= otherWords[k + 5];
words[k + 6] ^= otherWords[k + 6];
words[k + 7] ^= otherWords[k + 7];
}
for (; k < mincount; ++k) {
words[k] ^= otherWords[k];
}
// remaining words are all part of change
for (; k < otherWords.length; ++k) {
words[k] = otherWords[k];
}
return this;
};
/**
* Computes the change between this bitset and another one,
* a new bitmap is generated
*/
TypedFastBitSet.prototype.new_change = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var count = Math.max(words.length, otherWords.length);
var newWords = new Uint32Array(count);
var mcount = Math.min(words.length, otherWords.length);
var k = 0;
for (; k + 7 < mcount; k += 8) {
newWords[k] = words[k] ^ otherWords[k];
newWords[k + 1] = words[k + 1] ^ otherWords[k + 1];
newWords[k + 2] = words[k + 2] ^ otherWords[k + 2];
newWords[k + 3] = words[k + 3] ^ otherWords[k + 3];
newWords[k + 4] = words[k + 4] ^ otherWords[k + 4];
newWords[k + 5] = words[k + 5] ^ otherWords[k + 5];
newWords[k + 6] = words[k + 6] ^ otherWords[k + 6];
newWords[k + 7] = words[k + 7] ^ otherWords[k + 7];
}
for (; k < mcount; ++k) {
newWords[k] = words[k] ^ otherWords[k];
}
var c = words.length;
for (k = mcount; k < c; ++k) {
newWords[k] = words[k];
}
var c2 = otherWords.length;
for (k = mcount; k < c2; ++k) {
newWords[k] = otherWords[k];
}
return new TypedFastBitSet(undefined, newWords);
};
/**
* Computes the number of changed elements between this bitset and another one
*/
TypedFastBitSet.prototype.change_size = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var mincount = Math.min(words.length, otherWords.length);
var answer = 0 | 0;
var k = 0 | 0;
for (; k < mincount; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] ^ otherWords[k]);
}
var longer = words.length > otherWords.length ? this : otherbitmap;
var c = longer.words.length;
for (; k < c; ++k) {
answer += (0, utils_1.hammingWeight)(longer.words[k]);
}
return answer;
};
/**
* @returns a string representation
*/
TypedFastBitSet.prototype.toString = function () {
return "{" + this.array().join(",") + "}";
};
/**
* Computes the union between this bitset and another one,
* the current bitset is modified (and returned by the function)
*/
TypedFastBitSet.prototype.union = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var mcount = Math.min(words.length, otherWords.length);
var k = 0 | 0;
for (; k + 7 < mcount; k += 8) {
words[k] |= otherWords[k];
words[k + 1] |= otherWords[k + 1];
words[k + 2] |= otherWords[k + 2];
words[k + 3] |= otherWords[k + 3];
words[k + 4] |= otherWords[k + 4];
words[k + 5] |= otherWords[k + 5];
words[k + 6] |= otherWords[k + 6];
words[k + 7] |= otherWords[k + 7];
}
for (; k < mcount; ++k) {
words[k] |= otherWords[k];
}
if (words.length < otherWords.length) {
this.resize((otherWords.length << 5) - 1);
words = this.words;
var c = otherWords.length;
for (k = mcount; k < c; ++k) {
words[k] = otherWords[k];
}
}
return this;
};
/**
* Computes the union between this bitset and another one,
* a new bitmap is generated
*/
TypedFastBitSet.prototype.new_union = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var count = Math.max(words.length, otherWords.length);
var newWords = new Uint32Array(count);
var mcount = Math.min(words.length, otherWords.length);
for (var k = 0; k < mcount; ++k) {
newWords[k] = words[k] | otherWords[k];
}
var c = words.length;
for (var k = mcount; k < c; ++k) {
newWords[k] = words[k];
}
var c2 = otherWords.length;
for (var k = mcount; k < c2; ++k) {
newWords[k] = otherWords[k];
}
return new TypedFastBitSet(undefined, newWords);
};
/**
* Computes the size union between this bitset and another one
*/
TypedFastBitSet.prototype.union_size = function (otherbitmap) {
var words = this.words;
var otherWords = otherbitmap.words;
var mcount = Math.min(words.length, otherWords.length);
var answer = 0 | 0;
for (var k = 0 | 0; k < mcount; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] | otherWords[k]);
}
if (words.length < otherWords.length) {
var c = otherWords.length;
for (var k = words.length; k < c; ++k) {
answer += (0, utils_1.hammingWeight)(otherWords[k] | 0);
}
}
else {
var c = words.length;
for (var k = otherWords.length; k < c; ++k) {
answer += (0, utils_1.hammingWeight)(words[k] | 0);
}
}
return answer;
};
return TypedFastBitSet;
}());
exports.TypedFastBitSet = TypedFastBitSet;
//# sourceMappingURL=TypedFastBitSet.js.map