UNPKG

typedfastbitset

Version:

Speed-optimized BitSet implementation for modern browsers and JavaScript engines, using typed arrays

677 lines 24.9 kB
"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