├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app └── BitSet.js ├── package.json └── spec ├── BitsetSpec.js └── support └── jasmine.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | api.md 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.12 4 | - 0.10 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mattkrick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast-bitset 2 | 3 | [![Build Status](https://travis-ci.org/mattkrick/fast-bitset.svg?branch=master)](https://travis-ci.org/mattkrick/fast-bitset) 4 | 5 | [![Join the chat at https://gitter.im/mattkrick/fast-bitset](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mattkrick/fast-bitset?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | A fast bitset with some nice methods. 8 | 9 | ##Features 10 | - Outperforms all other bitset packages in terms of speed and space 11 | - All bit operations execute in O(1) time (does not iterate through bits) 12 | - Useful methods for graph algorithms 13 | - Any array that stores booleans can safely be replaced by a bitset for improved speed 14 | - Uses 64x less space than a nontyped array 15 | 16 | ##Installation 17 | `npm install fast-bitset --save` 18 | 19 | ##License 20 | MIT 21 | 22 | ##API 23 | 24 | * [BitSet](#BitSet) 25 | * [new BitSet(nBitsOrKey)](#new_BitSet_new) 26 | * [.get(idx)](#BitSet+get) ⇒ boolean 27 | * [.set(idx)](#BitSet+set) ⇒ boolean 28 | * [.setRange(from, to)](#BitSet+setRange) ⇒ boolean 29 | * [.unset(idx)](#BitSet+unset) ⇒ boolean 30 | * [.unsetRange(from, to)](#BitSet+unsetRange) ⇒ boolean 31 | * [.toggle(idx)](#BitSet+toggle) ⇒ boolean 32 | * [.toggleRange(from, to)](#BitSet+toggleRange) ⇒ boolean 33 | * [.clear()](#BitSet+clear) ⇒ boolean 34 | * [.clone()](#BitSet+clone) ⇒ [BitSet](#BitSet) 35 | * [.dehydrate()](#BitSet+dehydrate) ⇒ string 36 | * [.and(bsOrIdx)](#BitSet+and) ⇒ [BitSet](#BitSet) 37 | * [.or(bsOrIdx)](#BitSet+or) ⇒ [BitSet](#BitSet) 38 | * [.xor(bsOrIdx)](#BitSet+xor) ⇒ [BitSet](#BitSet) 39 | * [.forEach(func)](#BitSet+forEach) 40 | * [.circularShift(number)](#BitSet+circularShift) ⇒ Bitset 41 | * [.getCardinality()](#BitSet+getCardinality) ⇒ number 42 | * [.getIndices()](#BitSet+getIndices) ⇒ Array 43 | * [.isSubsetOf(bs)](#BitSet+isSubsetOf) ⇒ Boolean 44 | * [.isEmpty()](#BitSet+isEmpty) ⇒ boolean 45 | * [.isEqual(bs)](#BitSet+isEqual) ⇒ boolean 46 | * [.toString()](#BitSet+toString) ⇒ string 47 | * [.ffs(_startWord)](#BitSet+ffs) ⇒ number 48 | * [.ffz(_startWord)](#BitSet+ffz) ⇒ number 49 | * [.fls(_startWord)](#BitSet+fls) ⇒ number 50 | * [.flz(_startWord)](#BitSet+flz) ⇒ number 51 | * [.nextSetBit(idx)](#BitSet+nextSetBit) ⇒ number 52 | * [.nextUnsetBit(idx)](#BitSet+nextUnsetBit) ⇒ number 53 | * [.previousSetBit(idx)](#BitSet+previousSetBit) ⇒ number 54 | * [.previousUnsetBit(idx)](#BitSet+previousUnsetBit) ⇒ number 55 | 56 | 57 | ### new BitSet(nBitsOrKey) 58 | Create a new bitset. Accepts either the maximum number of bits, or a dehydrated bitset 59 | 60 | 61 | | Param | Type | Description | 62 | | --- | --- | --- | 63 | | nBitsOrKey | number | string | Number of bits in the set or dehydrated bitset. For speed and space concerns, the initial number of bits cannot be increased. | 64 | 65 | 66 | ### bitSet.get(idx) ⇒ boolean 67 | Check whether a bit at a specific index is set 68 | 69 | **Kind**: instance method of [BitSet](#BitSet) 70 | **Returns**: boolean - true if bit is set, else false 71 | 72 | | Param | Type | Description | 73 | | --- | --- | --- | 74 | | idx | number | the position of a single bit to check | 75 | 76 | 77 | ### bitSet.set(idx) ⇒ boolean 78 | Set a single bit 79 | 80 | **Kind**: instance method of [BitSet](#BitSet) 81 | **Returns**: boolean - true if set was successful, else false 82 | 83 | | Param | Type | Description | 84 | | --- | --- | --- | 85 | | idx | number | the position of a single bit to set | 86 | 87 | 88 | ### bitSet.setRange(from, to) ⇒ boolean 89 | Set a range of bits 90 | 91 | **Kind**: instance method of [BitSet](#BitSet) 92 | **Returns**: boolean - true if set was successful, else false 93 | 94 | | Param | Type | Description | 95 | | --- | --- | --- | 96 | | from | number | the starting index of the range to set | 97 | | to | number | the ending index of the range to set | 98 | 99 | 100 | ### bitSet.unset(idx) ⇒ boolean 101 | Unset a single bit 102 | 103 | **Kind**: instance method of [BitSet](#BitSet) 104 | **Returns**: boolean - true if set was successful, else false 105 | 106 | | Param | Type | Description | 107 | | --- | --- | --- | 108 | | idx | number | the position of a single bit to unset | 109 | 110 | 111 | ### bitSet.unsetRange(from, to) ⇒ boolean 112 | Unset a range of bits 113 | 114 | **Kind**: instance method of [BitSet](#BitSet) 115 | **Returns**: boolean - true if set was successful, else false 116 | 117 | | Param | Type | Description | 118 | | --- | --- | --- | 119 | | from | number | the starting index of the range to unset | 120 | | to | number | the ending index of the range to unset | 121 | 122 | 123 | ### bitSet.toggle(idx) ⇒ boolean 124 | Toggle a single bit 125 | 126 | **Kind**: instance method of [BitSet](#BitSet) 127 | **Returns**: boolean - true if set was successful, else false 128 | 129 | | Param | Type | Description | 130 | | --- | --- | --- | 131 | | idx | number | the position of a single bit to toggle | 132 | 133 | 134 | ### bitSet.toggleRange(from, to) ⇒ boolean 135 | Toggle a range of bits 136 | 137 | **Kind**: instance method of [BitSet](#BitSet) 138 | **Returns**: boolean - true if set was successful, else false 139 | 140 | | Param | Type | Description | 141 | | --- | --- | --- | 142 | | from | number | the starting index of the range to toggle | 143 | | to | number | the ending index of the range to toggle | 144 | 145 | 146 | ### bitSet.clear() ⇒ boolean 147 | Clear an entire bitset 148 | 149 | **Kind**: instance method of [BitSet](#BitSet) 150 | **Returns**: boolean - true 151 | 152 | ### bitSet.clone() ⇒ [BitSet](#BitSet) 153 | Clone a bitset 154 | 155 | **Kind**: instance method of [BitSet](#BitSet) 156 | **Returns**: [BitSet](#BitSet) - an copy (by value) of the calling bitset 157 | 158 | ### bitSet.dehydrate() ⇒ string 159 | Turn the bitset into a comma separated string that skips leading & trailing 0 words. 160 | Ends with the number of leading 0s and MAX_BIT. 161 | Useful if you need the bitset to be an object key (eg dynamic programming). 162 | Can rehydrate by passing the result into the constructor 163 | 164 | **Kind**: instance method of [BitSet](#BitSet) 165 | **Returns**: string - representation of the bitset 166 | 167 | ### bitSet.and(bsOrIdx) ⇒ [BitSet](#BitSet) 168 | Perform a bitwise AND on 2 bitsets or 1 bitset and 1 index. 169 | Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 170 | 171 | **Kind**: instance method of [BitSet](#BitSet) 172 | **Returns**: [BitSet](#BitSet) - a new bitset that is the bitwise AND of the two 173 | 174 | | Param | Type | Description | 175 | | --- | --- | --- | 176 | | bsOrIdx | [BitSet](#BitSet) | Number | a bitset or single index to check (useful for LP, DP problems) | 177 | 178 | 179 | ### bitSet.or(bsOrIdx) ⇒ [BitSet](#BitSet) 180 | Perform a bitwise OR on 2 bitsets or 1 bitset and 1 index. 181 | Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 182 | 183 | **Kind**: instance method of [BitSet](#BitSet) 184 | **Returns**: [BitSet](#BitSet) - a new bitset that is the bitwise OR of the two 185 | 186 | | Param | Type | Description | 187 | | --- | --- | --- | 188 | | bsOrIdx | [BitSet](#BitSet) | Number | a bitset or single index to check (useful for LP, DP problems) | 189 | 190 | 191 | ### bitSet.xor(bsOrIdx) ⇒ [BitSet](#BitSet) 192 | Perform a bitwise XOR on 2 bitsets or 1 bitset and 1 index. 193 | Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 194 | 195 | **Kind**: instance method of [BitSet](#BitSet) 196 | **Returns**: [BitSet](#BitSet) - a new bitset that is the bitwise XOR of the two 197 | 198 | | Param | Type | Description | 199 | | --- | --- | --- | 200 | | bsOrIdx | [BitSet](#BitSet) | Number | a bitset or single index to check (useful for LP, DP problems) | 201 | 202 | 203 | ### bitSet.forEach(func) 204 | Run a custom function on every set bit. Faster than iterating over the entire bitset with a `get()` 205 | Source code includes a nice pattern to follow if you need to break the for-loop early 206 | 207 | **Kind**: instance method of [BitSet](#BitSet) 208 | 209 | | Param | Type | Description | 210 | | --- | --- | --- | 211 | | func | function | the function to pass the next set bit to | 212 | 213 | 214 | ### bitSet.circularShift(number) ⇒ Bitset 215 | Circular shift bitset by an offset 216 | 217 | **Kind**: instance method of [BitSet](#BitSet) 218 | **Returns**: Bitset - a new bitset that is rotated by the offset 219 | 220 | | Param | Type | Description | 221 | | --- | --- | --- | 222 | | number | Number | of positions that the bitset that will be shifted to the right. Using a negative number will result in a left shift. | 223 | 224 | 225 | ### bitSet.getCardinality() ⇒ number 226 | Get the cardinality (count of set bits) for the entire bitset 227 | 228 | **Kind**: instance method of [BitSet](#BitSet) 229 | **Returns**: number - cardinality 230 | 231 | ### bitSet.getIndices() ⇒ Array 232 | Get the indices of all set bits. Useful for debugging, uses `forEach` internally 233 | 234 | **Kind**: instance method of [BitSet](#BitSet) 235 | **Returns**: Array - Indices of all set bits 236 | 237 | ### bitSet.isSubsetOf(bs) ⇒ Boolean 238 | Checks if one bitset is subset of another. Same thing can be done using _and_ operation and equality check, 239 | but then new BitSet would be created, and if one is only interested in yes/no information it would be a waste of memory 240 | and additional GC strain. 241 | 242 | **Kind**: instance method of [BitSet](#BitSet) 243 | **Returns**: Boolean - `true` if provided bitset is a subset of this bitset, `false` otherwise 244 | 245 | | Param | Type | Description | 246 | | --- | --- | --- | 247 | | bs | [BitSet](#BitSet) | a bitset to check | 248 | 249 | 250 | ### bitSet.isEmpty() ⇒ boolean 251 | Quickly determine if a bitset is empty 252 | 253 | **Kind**: instance method of [BitSet](#BitSet) 254 | **Returns**: boolean - true if the entire bitset is empty, else false 255 | 256 | ### bitSet.isEqual(bs) ⇒ boolean 257 | Quickly determine if both bitsets are equal (faster than checking if the XOR of the two is === 0). 258 | Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 259 | 260 | **Kind**: instance method of [BitSet](#BitSet) 261 | **Returns**: boolean - true if the entire bitset is empty, else false 262 | 263 | | Param | Type | 264 | | --- | --- | 265 | | bs | [BitSet](#BitSet) | 266 | 267 | 268 | ### bitSet.toString() ⇒ string 269 | Get a string representation of the entire bitset, including leading 0s (useful for debugging) 270 | 271 | **Kind**: instance method of [BitSet](#BitSet) 272 | **Returns**: string - a base 2 representation of the entire bitset 273 | 274 | ### bitSet.ffs(_startWord) ⇒ number 275 | Find first set bit (useful for processing queues, breadth-first tree searches, etc.) 276 | 277 | **Kind**: instance method of [BitSet](#BitSet) 278 | **Returns**: number - the index of the first set bit in the bitset, or -1 if not found 279 | 280 | | Param | Type | Description | 281 | | --- | --- | --- | 282 | | _startWord | number | the word to start with (only used internally by nextSetBit) | 283 | 284 | 285 | ### bitSet.ffz(_startWord) ⇒ number 286 | Find first zero (unset bit) 287 | 288 | **Kind**: instance method of [BitSet](#BitSet) 289 | **Returns**: number - the index of the first unset bit in the bitset, or -1 if not found 290 | 291 | | Param | Type | Description | 292 | | --- | --- | --- | 293 | | _startWord | number | the word to start with (only used internally by nextUnsetBit) | 294 | 295 | 296 | ### bitSet.fls(_startWord) ⇒ number 297 | Find last set bit 298 | 299 | **Kind**: instance method of [BitSet](#BitSet) 300 | **Returns**: number - the index of the last set bit in the bitset, or -1 if not found 301 | 302 | | Param | Type | Description | 303 | | --- | --- | --- | 304 | | _startWord | number | the word to start with (only used internally by previousSetBit) | 305 | 306 | 307 | ### bitSet.flz(_startWord) ⇒ number 308 | Find last zero (unset bit) 309 | 310 | **Kind**: instance method of [BitSet](#BitSet) 311 | **Returns**: number - the index of the last unset bit in the bitset, or -1 if not found 312 | 313 | | Param | Type | Description | 314 | | --- | --- | --- | 315 | | _startWord | number | the word to start with (only used internally by previousUnsetBit) | 316 | 317 | 318 | ### bitSet.nextSetBit(idx) ⇒ number 319 | Find first set bit, starting at a given index 320 | 321 | **Kind**: instance method of [BitSet](#BitSet) 322 | **Returns**: number - the index of the next set bit >= idx, or -1 if not found 323 | 324 | | Param | Type | Description | 325 | | --- | --- | --- | 326 | | idx | number | the starting index for the next set bit | 327 | 328 | 329 | ### bitSet.nextUnsetBit(idx) ⇒ number 330 | Find first unset bit, starting at a given index 331 | 332 | **Kind**: instance method of [BitSet](#BitSet) 333 | **Returns**: number - the index of the next unset bit >= idx, or -1 if not found 334 | 335 | | Param | Type | Description | 336 | | --- | --- | --- | 337 | | idx | number | the starting index for the next unset bit | 338 | 339 | 340 | ### bitSet.previousSetBit(idx) ⇒ number 341 | Find last set bit, up to a given index 342 | 343 | **Kind**: instance method of [BitSet](#BitSet) 344 | **Returns**: number - the index of the next unset bit <= idx, or -1 if not found 345 | 346 | | Param | Type | Description | 347 | | --- | --- | --- | 348 | | idx | number | the starting index for the next unset bit (going in reverse) | 349 | 350 | 351 | ### bitSet.previousUnsetBit(idx) ⇒ number 352 | Find last unset bit, up to a given index 353 | 354 | **Kind**: instance method of [BitSet](#BitSet) 355 | **Returns**: number - the index of the next unset bit <= idx, or -1 if not found 356 | 357 | | Param | Type | Description | 358 | | --- | --- | --- | 359 | | idx | number | the starting index for the next unset bit (going in reverse) | 360 | -------------------------------------------------------------------------------- /app/BitSet.js: -------------------------------------------------------------------------------- 1 | //Matt Krick, matt.krick@gmail.com, MIT License 2 | 3 | //each bin holds bits 0 - 30, totaling 31 (sign takes up last bit) 4 | var BITS_PER_INT = 31; 5 | //used for ffs of a word in O(1) time. LUTs get a bad wrap, they are fast. 6 | var multiplyDeBruijnBitPosition = [0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 7 | 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9]; 8 | 9 | /** 10 | * 11 | * Create a new bitset. Accepts either the maximum number of bits, or a dehydrated bitset 12 | * @param {number|string} nBitsOrKey - Number of bits in the set or dehydrated bitset. 13 | * For speed and space concerns, the initial number of bits cannot be increased. 14 | * @constructor 15 | */ 16 | BitSet = function (nBitsOrKey) { 17 | var wordCount, arrVals, front, leadingZeros, i; 18 | if (typeof nBitsOrKey === 'number') { 19 | nBitsOrKey = nBitsOrKey || BITS_PER_INT; //default to 1 word 20 | wordCount = Math.ceil(nBitsOrKey / BITS_PER_INT); 21 | this.arr = new Uint32Array(wordCount); 22 | this.MAX_BIT = nBitsOrKey - 1; 23 | } else { 24 | arrVals = JSON.parse("[" + nBitsOrKey + "]"); 25 | this.MAX_BIT = arrVals.pop(); 26 | leadingZeros = arrVals.pop(); 27 | if (leadingZeros > 0) { 28 | front = []; 29 | for (i = 0; i < leadingZeros; i++) front[i] = 0; 30 | for (i = 0; i < arrVals.length; i++) front[leadingZeros + i] = arrVals[i]; 31 | arrVals = front; 32 | } 33 | wordCount = Math.ceil((this.MAX_BIT + 1) / BITS_PER_INT); 34 | this.arr = new Uint32Array(wordCount) 35 | this.arr.set(arrVals); 36 | } 37 | }; 38 | 39 | /** 40 | * Check whether a bit at a specific index is set 41 | * @param {number} idx the position of a single bit to check 42 | * @returns {boolean} true if bit is set, else false 43 | */ 44 | BitSet.prototype.get = function (idx) { 45 | var word = this._getWord(idx); 46 | return (word === -1) ? false : (((this.arr[word] >> (idx % BITS_PER_INT)) & 1) === 1); 47 | }; 48 | 49 | /** 50 | * Set a single bit 51 | * @param {number} idx the position of a single bit to set 52 | * @returns {boolean} true if set was successful, else false 53 | */ 54 | BitSet.prototype.set = function (idx) { 55 | var word = this._getWord(idx); 56 | if (word === -1) return false; 57 | this.arr[word] |= 1 << (idx % BITS_PER_INT); 58 | return true; 59 | }; 60 | 61 | /** 62 | * Set a range of bits 63 | * @param {number} from the starting index of the range to set 64 | * @param {number} to the ending index of the range to set 65 | * @returns {boolean} true if set was successful, else false 66 | */ 67 | BitSet.prototype.setRange = function (from, to) { 68 | return this._doRange(from, to, _setFunc); 69 | }; 70 | 71 | /** 72 | * Unset a single bit 73 | * @param {number} idx the position of a single bit to unset 74 | * @returns {boolean} true if set was successful, else false 75 | */ 76 | BitSet.prototype.unset = function (idx) { 77 | var word = this._getWord(idx); 78 | if (word === -1) return false; 79 | this.arr[word] &= ~(1 << (idx % BITS_PER_INT)); 80 | return true; 81 | }; 82 | 83 | /** 84 | * Unset a range of bits 85 | * @param {number} from the starting index of the range to unset 86 | * @param {number} to the ending index of the range to unset 87 | * @returns {boolean} true if set was successful, else false 88 | */ 89 | BitSet.prototype.unsetRange = function (from, to) { 90 | return this._doRange(from, to, _unsetFunc); 91 | }; 92 | 93 | /** 94 | * Toggle a single bit 95 | * @param {number} idx the position of a single bit to toggle 96 | * @returns {boolean} true if set was successful, else false 97 | */ 98 | BitSet.prototype.toggle = function (idx) { 99 | var word = this._getWord(idx); 100 | if (word === -1) return false; 101 | this.arr[word] ^= (1 << (idx % BITS_PER_INT)); 102 | return true; 103 | }; 104 | 105 | /** 106 | * Toggle a range of bits 107 | * @param {number} from the starting index of the range to toggle 108 | * @param {number} to the ending index of the range to toggle 109 | * @returns {boolean} true if set was successful, else false 110 | */ 111 | BitSet.prototype.toggleRange = function (from, to) { 112 | return this._doRange(from, to, _toggleFunc); 113 | }; 114 | 115 | /** 116 | * 117 | * Clear an entire bitset 118 | * @returns {boolean} true 119 | */ 120 | BitSet.prototype.clear = function () { 121 | for (var i = 0; i < this.arr.length; i++) { 122 | this.arr[i] = 0; 123 | } 124 | return true; 125 | }; 126 | 127 | /** 128 | * Clone a bitset 129 | * @returns {BitSet} an copy (by value) of the calling bitset 130 | */ 131 | BitSet.prototype.clone = function () { 132 | return new BitSet(this.dehydrate()); 133 | }; 134 | 135 | /** 136 | * 137 | * Turn the bitset into a comma separated string that skips leading & trailing 0 words. 138 | * Ends with the number of leading 0s and MAX_BIT. 139 | * Useful if you need the bitset to be an object key (eg dynamic programming). 140 | * Can rehydrate by passing the result into the constructor 141 | * @returns {string} representation of the bitset 142 | */ 143 | BitSet.prototype.dehydrate = function () { 144 | var i, lastUsedWord, s; 145 | var leadingZeros = 0; 146 | for (i = 0; i < this.arr.length; i++) { 147 | if (this.arr[i] !== 0) break; 148 | leadingZeros++; 149 | } 150 | for (i = this.arr.length - 1; i >= leadingZeros; i--) { 151 | if (this.arr[i] !== 0) { 152 | lastUsedWord = i; 153 | break; 154 | } 155 | } 156 | s = ''; 157 | for (i = leadingZeros; i <= lastUsedWord; i++) { 158 | s += (this.arr[i] + ','); 159 | } 160 | s += (leadingZeros + ',' + this.MAX_BIT); //leading 0s, stop numbers 161 | return s; 162 | }; 163 | 164 | /** 165 | * 166 | * Perform a bitwise AND on 2 bitsets or 1 bitset and 1 index. 167 | * Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 168 | * @param {BitSet | Number} bsOrIdx a bitset or single index to check (useful for LP, DP problems) 169 | * @returns {BitSet} a new bitset that is the bitwise AND of the two 170 | */ 171 | BitSet.prototype.and = function (bsOrIdx) { 172 | return this._op(bsOrIdx, _and); 173 | }; 174 | 175 | /** 176 | * 177 | * Perform a bitwise OR on 2 bitsets or 1 bitset and 1 index. 178 | * Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 179 | * @param {BitSet | Number} bsOrIdx a bitset or single index to check (useful for LP, DP problems) 180 | * @returns {BitSet} a new bitset that is the bitwise OR of the two 181 | */ 182 | BitSet.prototype.or = function (bsOrIdx) { 183 | return this._op(bsOrIdx, _or); 184 | }; 185 | 186 | /** 187 | * 188 | * Perform a bitwise XOR on 2 bitsets or 1 bitset and 1 index. 189 | * Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 190 | * @param {BitSet | Number} bsOrIdx a bitset or single index to check (useful for LP, DP problems) 191 | * @returns {BitSet} a new bitset that is the bitwise XOR of the two 192 | */ 193 | BitSet.prototype.xor = function (bsOrIdx) { 194 | return this._op(bsOrIdx, _xor); 195 | }; 196 | 197 | /** 198 | * Run a custom function on every set bit. Faster than iterating over the entire bitset with a `get()` 199 | * Source code includes a nice pattern to follow if you need to break the for-loop early 200 | * @param {Function} func the function to pass the next set bit to 201 | */ 202 | BitSet.prototype.forEach = function (func) { 203 | for (var i = this.ffs(); i !== -1; i = this.nextSetBit(i + 1)) { 204 | func(i); 205 | } 206 | }; 207 | 208 | /** 209 | * Circular shift bitset by an offset 210 | * @param {Number} number of positions that the bitset that will be shifted to the right. 211 | * Using a negative number will result in a left shift. 212 | * @returns {Bitset} a new bitset that is rotated by the offset 213 | */ 214 | 215 | BitSet.prototype.circularShift = function(offset) { 216 | offset = -offset; 217 | 218 | var S = this; // source BitSet (this) 219 | var MASK_SIGN = 0x7fffffff; 220 | var BITS = S.MAX_BIT+1; 221 | var WORDS = S.arr.length; 222 | var BITS_LAST_WORD = BITS_PER_INT - (WORDS*BITS_PER_INT - BITS); 223 | 224 | var T = new BitSet(BITS); // target BitSet (the shifted bitset) 225 | 226 | var s; var t = 0; // (s)ource and (t)arget word indices 227 | var i; var j = 0; // current bit indices for source (i) and target (j) words 228 | var z = 0; // bit index for entire sequence. 229 | 230 | offset = (BITS + (offset % BITS)) % BITS // positive, within length 231 | var s = ~~(offset / BITS_PER_INT) % WORDS 232 | var i = offset % BITS_PER_INT 233 | while (z < BITS){ 234 | var sourceWordLength = s === WORDS - 1 ? BITS_LAST_WORD : BITS_PER_INT 235 | var bits = S.arr[s] 236 | 237 | if (i > 0) { 238 | bits = bits >>> i; 239 | } 240 | if (j > 0) { 241 | bits = bits << j; 242 | } 243 | 244 | T.arr[t] = T.arr[t] | bits 245 | 246 | var bitsAdded = Math.min(BITS_PER_INT-j,sourceWordLength - i); 247 | z += bitsAdded; 248 | j += bitsAdded; 249 | if(j >= BITS_PER_INT){ 250 | T.arr[t] = T.arr[t] & MASK_SIGN 251 | j = 0; t++; 252 | } 253 | i += bitsAdded; 254 | if(i >= sourceWordLength){ i = 0; s++;} 255 | if(s >= WORDS){ s -= WORDS;} 256 | } 257 | T.arr[WORDS-1] = T.arr[WORDS-1] & (MASK_SIGN >>> (BITS_PER_INT-BITS_LAST_WORD)); 258 | return T; 259 | }; 260 | 261 | /** 262 | * Get the cardinality (count of set bits) for the entire bitset 263 | * @returns {number} cardinality 264 | */ 265 | BitSet.prototype.getCardinality = function () { 266 | var setCount = 0; 267 | for (var i = this.arr.length - 1; i >= 0; i--) { 268 | var j = this.arr[i]; 269 | j = j - ((j >> 1) & 0x55555555); 270 | j = (j & 0x33333333) + ((j >> 2) & 0x33333333); 271 | setCount += ((((j + (j >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); 272 | } 273 | return setCount; 274 | }; 275 | 276 | /** 277 | * Get the indices of all set bits. Useful for debugging, uses `forEach` internally 278 | * @returns {Array} Indices of all set bits 279 | */ 280 | BitSet.prototype.getIndices = function () { 281 | var indices = []; 282 | this.forEach(function (i) { 283 | indices.push(i); 284 | }); 285 | return indices; 286 | }; 287 | 288 | /** 289 | * Checks if one bitset is subset of another. Same thing can be done using _and_ operation and equality check, 290 | * but then new BitSet would be created, and if one is only interested in yes/no information it would be a waste of memory 291 | * and additional GC strain. 292 | * @param {BitSet} bs a bitset to check 293 | * @returns {Boolean} `true` if provided bitset is a subset of this bitset, `false` otherwise 294 | */ 295 | BitSet.prototype.isSubsetOf = function (bs) { 296 | var arr1 = this.arr; 297 | var arr2 = bs.arr; 298 | var len = arr1.length; 299 | for (var i = 0; i < len; i++) { 300 | if ((arr1[i] & arr2[i]) !== arr1[i]) { 301 | return false; 302 | } 303 | } 304 | return true; 305 | } 306 | 307 | /** 308 | * Quickly determine if a bitset is empty 309 | * @returns {boolean} true if the entire bitset is empty, else false 310 | */ 311 | BitSet.prototype.isEmpty = function () { 312 | var i, arr; 313 | arr = this.arr; 314 | for (i = 0; i < arr.length; i++) { 315 | if (arr[i]) { 316 | return false; 317 | } 318 | } 319 | return true; 320 | }; 321 | 322 | /** 323 | * 324 | * Quickly determine if both bitsets are equal (faster than checking if the XOR of the two is === 0). 325 | * Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 326 | * @param {BitSet} bs 327 | * @returns {boolean} true if the entire bitset is empty, else false 328 | */ 329 | BitSet.prototype.isEqual = function (bs) { 330 | var i; 331 | for (i = 0; i < this.arr.length; i++) { 332 | if (this.arr[i] !== bs.arr[i]) { 333 | return false; 334 | } 335 | } 336 | return true; 337 | }; 338 | 339 | /** 340 | * Get a string representation of the entire bitset, including leading 0s (useful for debugging) 341 | * @returns {string} a base 2 representation of the entire bitset 342 | */ 343 | BitSet.prototype.toString = function () { 344 | var i, str, fullString = ''; 345 | for (i = this.arr.length - 1; i >= 0; i--) { 346 | str = this.arr[i].toString(2); 347 | fullString += ('0000000000000000000000000000000' + str).slice(-BITS_PER_INT); 348 | } 349 | return fullString; 350 | }; 351 | 352 | /** 353 | * Find first set bit (useful for processing queues, breadth-first tree searches, etc.) 354 | * @param {number} _startWord the word to start with (only used internally by nextSetBit) 355 | * @returns {number} the index of the first set bit in the bitset, or -1 if not found 356 | */ 357 | BitSet.prototype.ffs = function (_startWord) { 358 | var setVal, i, fs = -1; 359 | _startWord = _startWord || 0; 360 | for (i = _startWord; i < this.arr.length; i++) { 361 | setVal = this.arr[i]; 362 | if (setVal === 0) continue; 363 | fs = _lsb(setVal) + i * BITS_PER_INT; 364 | break; 365 | } 366 | return fs <= this.MAX_BIT ? fs : -1; 367 | }; 368 | 369 | /** 370 | * Find first zero (unset bit) 371 | * @param {number} _startWord the word to start with (only used internally by nextUnsetBit) 372 | * @returns {number} the index of the first unset bit in the bitset, or -1 if not found 373 | */ 374 | BitSet.prototype.ffz = function (_startWord) { 375 | var i, setVal, fz = -1; 376 | _startWord = _startWord || 0; 377 | for (i = _startWord; i < this.arr.length; i++) { 378 | setVal = this.arr[i]; 379 | if (setVal === 0x7fffffff) continue; 380 | setVal ^= 0x7fffffff; 381 | fz = _lsb(setVal) + i * BITS_PER_INT; 382 | break; 383 | } 384 | return fz <= this.MAX_BIT ? fz : -1; 385 | }; 386 | 387 | /** 388 | * 389 | * Find last set bit 390 | * @param {number} _startWord the word to start with (only used internally by previousSetBit) 391 | * @returns {number} the index of the last set bit in the bitset, or -1 if not found 392 | */ 393 | BitSet.prototype.fls = function (_startWord) { 394 | var i, setVal, ls = -1; 395 | if (_startWord === undefined) _startWord = this.arr.length - 1; 396 | for (i = _startWord; i >= 0; i--) { 397 | setVal = this.arr[i]; 398 | if (setVal === 0) continue; 399 | ls = _msb(setVal) + i * BITS_PER_INT; 400 | break; 401 | } 402 | return ls; 403 | }; 404 | 405 | /** 406 | * 407 | * Find last zero (unset bit) 408 | * @param {number} _startWord the word to start with (only used internally by previousUnsetBit) 409 | * @returns {number} the index of the last unset bit in the bitset, or -1 if not found 410 | */ 411 | BitSet.prototype.flz = function (_startWord) { 412 | var i, setVal, ls = -1; 413 | if (_startWord === undefined) _startWord = this.arr.length - 1; 414 | for (i = _startWord; i >= 0; i--) { 415 | setVal = this.arr[i]; 416 | if (i === this.arr.length - 1) { 417 | var wordIdx = this.MAX_BIT % BITS_PER_INT; 418 | var unusedBitCount = BITS_PER_INT - wordIdx - 1; 419 | setVal |= ((1 << unusedBitCount) - 1) << (wordIdx + 1); 420 | } 421 | if (setVal === 0x7fffffff) continue; 422 | setVal ^= 0x7fffffff; 423 | ls = _msb(setVal) + i * BITS_PER_INT; 424 | break; 425 | } 426 | return ls; 427 | }; 428 | 429 | /** 430 | * Find first set bit, starting at a given index 431 | * @param {number} idx the starting index for the next set bit 432 | * @returns {number} the index of the next set bit >= idx, or -1 if not found 433 | */ 434 | BitSet.prototype.nextSetBit = function (idx) { 435 | var startWord = this._getWord(idx); 436 | if (startWord === -1) return -1; 437 | var wordIdx = idx % BITS_PER_INT; 438 | var len = BITS_PER_INT - wordIdx; 439 | var mask = ((1 << (len)) - 1) << wordIdx; 440 | var reducedWord = this.arr[startWord] & mask; 441 | if (reducedWord > 0) { 442 | return _lsb(reducedWord) + startWord * BITS_PER_INT; 443 | } 444 | return this.ffs(startWord + 1); 445 | }; 446 | 447 | /** 448 | * Find first unset bit, starting at a given index 449 | * @param {number} idx the starting index for the next unset bit 450 | * @returns {number} the index of the next unset bit >= idx, or -1 if not found 451 | */ 452 | BitSet.prototype.nextUnsetBit = function (idx) { 453 | var startWord = this._getWord(idx); 454 | if (startWord === -1) return -1; 455 | var mask = ((1 << (idx % BITS_PER_INT)) - 1); 456 | var reducedWord = this.arr[startWord] | mask; 457 | if (reducedWord === 0x7fffffff) { 458 | return this.ffz(startWord + 1); 459 | } 460 | return _lsb(0x7fffffff ^ reducedWord) + startWord * BITS_PER_INT; 461 | }; 462 | 463 | /** 464 | * Find last set bit, up to a given index 465 | * @param {number} idx the starting index for the next unset bit (going in reverse) 466 | * @returns {number} the index of the next unset bit <= idx, or -1 if not found 467 | */ 468 | BitSet.prototype.previousSetBit = function (idx) { 469 | var startWord = this._getWord(idx); 470 | if (startWord === -1) return -1; 471 | var mask = 0x7fffffff >>> (BITS_PER_INT - (idx % BITS_PER_INT) - 1); 472 | var reducedWord = this.arr[startWord] & mask; 473 | if (reducedWord > 0) { 474 | return _msb(reducedWord) + startWord * BITS_PER_INT; 475 | } 476 | return this.fls(startWord - 1); 477 | }; 478 | 479 | /** 480 | * Find last unset bit, up to a given index 481 | * @param {number} idx the starting index for the next unset bit (going in reverse) 482 | * @returns {number} the index of the next unset bit <= idx, or -1 if not found 483 | */ 484 | BitSet.prototype.previousUnsetBit = function (idx) { 485 | var startWord = this._getWord(idx); 486 | if (startWord === -1) return -1; 487 | var wordIdx = idx % BITS_PER_INT; 488 | var mask = ((1 << (BITS_PER_INT - wordIdx - 1)) - 1) << wordIdx + 1; 489 | var reducedWord = this.arr[startWord] | mask; 490 | if (reducedWord === 0x7fffffff) { 491 | return this.flz(startWord - 1); 492 | } 493 | return _msb(0x7fffffff ^ reducedWord) + startWord * BITS_PER_INT; 494 | }; 495 | 496 | /** 497 | * 498 | * @param {number} idx position of bit in bitset 499 | * @returns {number} the word where the index is located, or -1 if out of range 500 | * @private 501 | */ 502 | BitSet.prototype._getWord = function (idx) { 503 | return (idx < 0 || idx > this.MAX_BIT) ? -1 : ~~(idx / BITS_PER_INT); 504 | }; 505 | 506 | /** 507 | * Shared function for setting, unsetting, or toggling a range of bits 508 | * @param {number} from the starting index of the range to set 509 | * @param {number} to the ending index of the range to set 510 | * @param {Function} func function to run (set, unset, or toggle) 511 | * @returns {boolean} true if set was successful, else false 512 | * @private 513 | */ 514 | BitSet.prototype._doRange = function (from, to, func) { 515 | var i, curStart, curEnd, len; 516 | if (to < from) { 517 | to ^= from; 518 | from ^= to; 519 | to ^= from; 520 | } 521 | var startWord = this._getWord(from); 522 | var endWord = this._getWord(to); 523 | if (startWord === -1 || endWord === -1) return false; 524 | for (i = startWord; i <= endWord; i++) { 525 | curStart = (i === startWord) ? from % BITS_PER_INT : 0; 526 | curEnd = (i === endWord) ? to % BITS_PER_INT : BITS_PER_INT - 1; 527 | len = curEnd - curStart + 1; 528 | this.arr[i] = func(this.arr[i], len, curStart); 529 | 530 | } 531 | return true; 532 | }; 533 | 534 | /** 535 | * Both bitsets must have the same number of words, no length check is performed to prevent and overflow. 536 | * @param {BitSet | Number} bsOrIdx a bitset or single index to check (useful for LP, DP problems) 537 | * @param {Function} func the operation to perform (and, or, xor) 538 | * @returns {BitSet} a new bitset that is the bitwise operation of the two 539 | * @private 540 | */ 541 | BitSet.prototype._op = function (bsOrIdx, func) { 542 | var i, arr1, arr2, len, newBS, word; 543 | arr1 = this.arr; 544 | if (typeof bsOrIdx === 'number') { 545 | word = this._getWord(bsOrIdx); 546 | newBS = this.clone(); 547 | if (word !== -1) newBS.arr[word] = func(arr1[word], 1 << (bsOrIdx % BITS_PER_INT)); 548 | } else { 549 | arr2 = bsOrIdx.arr; 550 | len = arr1.length; 551 | newBS = new BitSet(this.MAX_BIT + 1); 552 | for (i = 0; i < len; i++) { 553 | newBS.arr[i] = func(arr1[i], arr2[i]); 554 | } 555 | } 556 | return newBS; 557 | }; 558 | 559 | /** 560 | * 561 | * Returns the least signifcant bit, or 0 if none set, so a prior check to see if the word > 0 is required 562 | * @param {number} word the current array 563 | * @returns {number} the index of the least significant bit in the current array 564 | * @private 565 | * 566 | */ 567 | function _lsb(word) { 568 | return multiplyDeBruijnBitPosition[(((word & -word) * 0x077CB531)) >>> 27]; 569 | } 570 | 571 | /** 572 | * Returns the least signifcant bit, or 0 if none set, so a prior check to see if the word > 0 is required 573 | * @param word the current array 574 | * @returns {number} the index of the most significant bit in the current array 575 | * @private 576 | */ 577 | function _msb(word) { 578 | word |= word >> 1; 579 | word |= word >> 2; 580 | word |= word >> 4; 581 | word |= word >> 8; 582 | word |= word >> 16; 583 | word = (word >> 1) + 1; 584 | return multiplyDeBruijnBitPosition[(word * 0x077CB531) >>> 27]; 585 | } 586 | 587 | function _toggleFunc(word, len, curStart) { 588 | var mask = (((1 << len) - 1) << curStart); 589 | return word ^ mask; 590 | } 591 | 592 | function _setFunc(word, len, curStart) { 593 | var mask = (((1 << len) - 1) << curStart); 594 | return word | mask; 595 | } 596 | 597 | function _unsetFunc(word, len, curStart) { 598 | var mask = 0x7fffffff ^ (((1 << len) - 1) << curStart); 599 | return word & mask; 600 | } 601 | 602 | function _and(word1, word2) { 603 | return word1 & word2; 604 | } 605 | 606 | function _or(word1, word2) { 607 | return word1 | word2; 608 | } 609 | 610 | function _xor(word1, word2) { 611 | return word1 ^ word2; 612 | } 613 | 614 | if (typeof define === 'function' && define['amd']) { 615 | define([], function () { 616 | return BitSet; 617 | }); 618 | } else if (typeof exports === 'object') { 619 | module['exports'] = BitSet; 620 | } 621 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-bitset", 3 | "version": "1.3.2", 4 | "description": "a fast bitset with some neat methods", 5 | "main": "./app/BitSet.js", 6 | "keywords": [ 7 | "bit", 8 | "bitset", 9 | "bitwise", 10 | "bit operations", 11 | "bitmap" 12 | ], 13 | "scripts": { 14 | "test": "./node_modules/.bin/jasmine-node spec", 15 | "docs": "jsdoc2md app/*.js > api.md" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/mattkrick/fast-bitset.git" 20 | }, 21 | "author": "Matt Krick ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/mattkrick/fast-bitset/issues" 25 | }, 26 | "homepage": "https://github.com/mattkrick/fast-bitset#readme", 27 | "devDependencies": { 28 | "jasmine-node": "^1.14.5", 29 | "jsdoc-to-markdown": "^1.1.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spec/BitsetSpec.js: -------------------------------------------------------------------------------- 1 | describe("BitSet", function () { 2 | var BitSet = require('../app/BitSet.js'); 3 | 4 | it('should create a bitset from a dehydrated string', function () { 5 | var dehydratedBS = '1073741824,2147483647,15,0,99'; 6 | var bs = new BitSet(dehydratedBS); 7 | expect(bs.dehydrate()).toBe(dehydratedBS); 8 | }); 9 | 10 | it('should set an individual bit', function () { 11 | var bs = new BitSet(100); 12 | bs.set(31); 13 | expect(bs.get(31)).toBe(true); 14 | }); 15 | 16 | it('should find first set', function () { 17 | var bs = new BitSet(100); 18 | bs.set(31); 19 | expect(bs.ffs()).toBe(31); 20 | }); 21 | 22 | it('should not be able to find first set in an empty bitset', function () { 23 | var bs = new BitSet(100); 24 | expect(bs.ffs()).toBe(-1); 25 | }); 26 | 27 | it('should find first zero', function () { 28 | var bs = new BitSet(100); 29 | bs.setRange(0,31); 30 | expect(bs.ffz()).toBe(32); 31 | }); 32 | 33 | it('should not be able to find first zero in a full bitset', function () { 34 | var bs = new BitSet('2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,255,0,224'); 35 | expect(bs.ffz()).toBe(-1); 36 | }); 37 | 38 | 39 | it('should set a range of len 1', function () { 40 | var bs = new BitSet(100); 41 | bs.setRange(31,31); 42 | expect(bs.dehydrate()).toBe('1,1,99'); 43 | }); 44 | 45 | it('should set a range of len 31', function () { 46 | var bs = new BitSet(100); 47 | bs.setRange(0,30); 48 | expect(bs.dehydrate()).toBe('2147483647,0,99'); 49 | }); 50 | 51 | it('should set a range that spans 3 words', function () { 52 | var bs = new BitSet(100); 53 | bs.setRange(30,65); 54 | expect(bs.dehydrate()).toBe('1073741824,2147483647,15,0,99'); 55 | }); 56 | 57 | it('should AND two bitsets', function () { 58 | var bs1 = new BitSet(100); 59 | var bs2 = new BitSet(100); 60 | bs1.setRange(1,10); 61 | bs2.setRange(10,33); 62 | var bs3 = bs1.and(bs2); 63 | expect(bs3.dehydrate()).toBe('1024,0,99'); 64 | }); 65 | 66 | it('should AND a bitset and an index', function () { 67 | var bs1 = new BitSet(100); 68 | bs1.setRange(1,10); 69 | var bs3 = bs1.and(1); 70 | expect(bs3.dehydrate()).toBe('2,0,99'); 71 | }); 72 | 73 | it('should OR two bitsets', function () { 74 | var bs1 = new BitSet(100); 75 | var bs2 = new BitSet(100); 76 | bs1.setRange(1,10); 77 | bs2.setRange(10,33); 78 | var bs3 = bs1.or(bs2); 79 | expect(bs3.dehydrate()).toBe('2147483646,7,0,99'); 80 | }); 81 | 82 | it('should XOR two bitsets', function () { 83 | var bs1 = new BitSet(100); 84 | var bs2 = new BitSet(100); 85 | bs1.setRange(1,10); 86 | bs2.setRange(10,33); 87 | var bs3 = bs1.xor(bs2); 88 | expect(bs3.dehydrate()).toBe('2147482622,7,0,99'); 89 | }); 90 | 91 | it('should detect empty arrays', function () { 92 | var bs = new BitSet(100); 93 | expect(bs.isEmpty()).toBe(true); 94 | bs.set(31); 95 | expect(bs.isEmpty()).toBe(false); 96 | }); 97 | 98 | it('should unset a bit', function () { 99 | var bs = new BitSet(100); 100 | bs.set(31); 101 | bs.unset(31); 102 | expect(bs.get(31)).toBe(false); 103 | }); 104 | 105 | it('should toggle a bit', function () { 106 | var bs = new BitSet(100); 107 | bs.toggle(31); 108 | expect(bs.get(31)).toBe(true); 109 | bs.toggle(31); 110 | expect(bs.get(31)).toBe(false); 111 | }); 112 | 113 | it('should toggle a range', function () { 114 | var bs = new BitSet(100); 115 | bs.toggleRange(31,35); 116 | bs.toggleRange(32,34); 117 | bs.toggleRange(33,33); 118 | expect(bs.dehydrate()).toBe('21,1,99'); 119 | }); 120 | 121 | it('should unset a range', function () { 122 | var bs = new BitSet(100); 123 | bs.setRange(29,59); 124 | bs.unsetRange(30,58); 125 | expect(bs.dehydrate()).toBe('536870912,268435456,0,99'); 126 | }); 127 | 128 | it('should clear a bitset', function () { 129 | var bs = new BitSet(100); 130 | bs.setRange(29,59); 131 | bs.clear(); 132 | expect(bs.isEmpty()).toBe(true); 133 | }); 134 | 135 | it('should check if one bitset is subset of another', function () { 136 | var bs = new BitSet(100); 137 | var bs2 = new BitSet(100); 138 | 139 | expect(bs.isSubsetOf(bs2)).toBe(true); 140 | 141 | bs.setRange(30, 60); 142 | bs2.setRange(30, 60); 143 | 144 | expect(bs2.isSubsetOf(bs)).toBe(true); 145 | 146 | bs2.clear(); 147 | bs2.setRange(31, 59); 148 | 149 | expect(bs2.isSubsetOf(bs)).toBe(true); 150 | expect(bs.isSubsetOf(bs2)).toBe(false); 151 | }) 152 | 153 | it('should check for equality', function () { 154 | var bs = new BitSet(100); 155 | bs.setRange(29,59); 156 | var bs2 = new BitSet(100); 157 | bs2.setRange(29,59); 158 | expect(bs.isEqual(bs2)).toBe(true); 159 | }); 160 | 161 | it('should find next set bit in the same word', function () { 162 | var bs = new BitSet(100); 163 | bs.setRange(10,30); 164 | expect(bs.nextSetBit(1)).toBe(10); 165 | }); 166 | 167 | it('should find next set bit the next word', function () { 168 | var bs = new BitSet(100); 169 | bs.setRange(66,99); 170 | expect(bs.nextSetBit(31)).toBe(66); 171 | }); 172 | 173 | it('should find next unset bit in the same word', function () { 174 | var bs = new BitSet(100); 175 | bs.setRange(10,30); 176 | expect(bs.nextUnsetBit(1)).toBe(1); 177 | }); 178 | 179 | it('should find next set bit the next word', function () { 180 | var bs = new BitSet(100); 181 | bs.setRange(10,30); 182 | expect(bs.nextUnsetBit(11)).toBe(31); 183 | }); 184 | 185 | it('should find the last set bit', function () { 186 | var bs = new BitSet(100); 187 | bs.setRange(10,30); 188 | expect(bs.fls()).toBe(30); 189 | }); 190 | 191 | it('should find the previous set bit', function () { 192 | var bs = new BitSet(100); 193 | bs.setRange(10,30); 194 | expect(bs.previousSetBit(90)).toBe(30); 195 | }); 196 | 197 | it('should find the last unset bit', function () { 198 | var bs = new BitSet(100); 199 | bs.setRange(60,99); 200 | expect(bs.flz()).toBe(59); 201 | }); 202 | 203 | it('should find the previous unset bit', function () { 204 | var bs = new BitSet(100); 205 | bs.setRange(60,99); 206 | expect(bs.previousUnsetBit(80)).toBe(59); 207 | }); 208 | 209 | it('should clone a bitset with only 1 word', function () { 210 | var bs = new BitSet(10); 211 | bs.setRange(6,9); 212 | bs2 = bs.clone(); 213 | expect(bs.dehydrate()).toBe(bs2.dehydrate()); 214 | }); 215 | 216 | it('should clone a bitset', function () { 217 | var bs = new BitSet(100); 218 | bs.setRange(60,99); 219 | bs2 = bs.clone(); 220 | expect(bs.dehydrate()).toBe(bs2.dehydrate()); 221 | }); 222 | 223 | it('should count number of bits set', function () { 224 | var bs = new BitSet(100); 225 | bs.setRange(60,99); 226 | expect(bs.getCardinality()).toBe(40); 227 | }); 228 | 229 | it('should return an array of set bits', function () { 230 | var bs = new BitSet(100); 231 | bs.set(30); 232 | bs.setRange(98,99); 233 | var range = [30,98,99]; 234 | expect(bs.getIndices()).toEqual(range); 235 | }); 236 | 237 | it('should set bit success which read from dehydrate string', function () { 238 | 239 | var bs = new BitSet('2147483646,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,2147483647,0,9999999'); 240 | expect(bs.get(899)).toBe(false); 241 | expect(bs.set(899, true)).toBe(true); 242 | expect(bs.get(899)).toBe(true); 243 | }); 244 | // 245 | it('should rotate a bitset', function () { 246 | 247 | var sizes = [10,34,70,500]; 248 | for(var i=0; i