|
2 | 2 | // |
3 | 3 | // |
4 | 4 |
|
5 | | -// A bitset implementation, after that in java.util. Yes there |
6 | | -// already exist such things, but none implement next{Clear|Set}Bit or |
7 | | -// equivalent, and none involved me tooling about for an evening. |
8 | | - |
9 | 5 | 'use strict'; |
10 | 6 |
|
11 | | -function BitSet(size) { |
12 | | - if (size) { |
13 | | - var numWords = Math.ceil(size / 32); |
14 | | - this.words = new Array(numWords); |
| 7 | +/** |
| 8 | + * A bitset implementation, after that in java.util. Yes there |
| 9 | + * already exist such things, but none implement next{Clear|Set}Bit or |
| 10 | + * equivalent, and none involved me tooling about for an evening. |
| 11 | + */ |
| 12 | +class BitSet { |
| 13 | + /** |
| 14 | + * @param {number} [size] |
| 15 | + */ |
| 16 | + constructor(size) { |
| 17 | + if (size) { |
| 18 | + const numWords = Math.ceil(size / 32); |
| 19 | + this.words = new Array(numWords); |
| 20 | + } |
| 21 | + else { |
| 22 | + this.words = []; |
| 23 | + } |
| 24 | + this.wordsInUse = 0; // = number, not index |
15 | 25 | } |
16 | | - else { |
17 | | - this.words = []; |
| 26 | + |
| 27 | + /** |
| 28 | + * @param {number} numWords |
| 29 | + */ |
| 30 | + ensureSize(numWords) { |
| 31 | + const wordsPresent = this.words.length; |
| 32 | + if (wordsPresent < numWords) { |
| 33 | + this.words = this.words.concat(new Array(numWords - wordsPresent)); |
| 34 | + } |
18 | 35 | } |
19 | | - this.wordsInUse = 0; // = number, not index |
20 | | -} |
21 | 36 |
|
22 | | -var P = BitSet.prototype; |
| 37 | + /** |
| 38 | + * @param {number} bitIndex |
| 39 | + */ |
| 40 | + set(bitIndex) { |
| 41 | + const w = wordIndex(bitIndex); |
| 42 | + if (w >= this.wordsInUse) { |
| 43 | + this.ensureSize(w + 1); |
| 44 | + this.wordsInUse = w + 1; |
| 45 | + } |
| 46 | + const bit = 1 << bitIndex; |
| 47 | + this.words[w] |= bit; |
| 48 | + } |
23 | 49 |
|
24 | | -function wordIndex(bitIndex) { |
25 | | - return Math.floor(bitIndex / 32); |
26 | | -} |
| 50 | + /** |
| 51 | + * @param {number} bitIndex |
| 52 | + */ |
| 53 | + clear(bitIndex) { |
| 54 | + const w = wordIndex(bitIndex); |
| 55 | + if (w >= this.wordsInUse) return; |
| 56 | + const mask = ~(1 << bitIndex); |
| 57 | + this.words[w] &= mask; |
| 58 | + } |
27 | 59 |
|
28 | | -// Make sure we have at least numWords |
29 | | -P.ensureSize = function(numWords) { |
30 | | - var wordsPresent = this.words.length; |
31 | | - if (wordsPresent < numWords) { |
32 | | - this.words = this.words.concat(new Array(numWords - wordsPresent)); |
| 60 | + /** |
| 61 | + * @param {number} bitIndex |
| 62 | + */ |
| 63 | + get(bitIndex) { |
| 64 | + const w = wordIndex(bitIndex); |
| 65 | + if (w >= this.wordsInUse) return false; // >= since index vs size |
| 66 | + const bit = 1 << bitIndex; |
| 67 | + return !!(this.words[w] & bit); |
33 | 68 | } |
34 | | -} |
35 | 69 |
|
36 | | -P.set = function(bitIndex) { |
37 | | - var w = wordIndex(bitIndex); |
38 | | - if (w >= this.wordsInUse) { |
39 | | - this.ensureSize(w + 1); |
40 | | - this.wordsInUse = w + 1; |
| 70 | + /** |
| 71 | + * Give the next bit that is set on or after fromIndex, or -1 if no such bit |
| 72 | + * |
| 73 | + * @param {number} fromIndex |
| 74 | + */ |
| 75 | + nextSetBit(fromIndex) { |
| 76 | + let w = wordIndex(fromIndex); |
| 77 | + if (w >= this.wordsInUse) return -1; |
| 78 | + |
| 79 | + // the right-hand side is shifted to only test the bits of the first |
| 80 | + // word that are > fromIndex |
| 81 | + let word = this.words[w] & (0xffffffff << fromIndex); |
| 82 | + while (true) { |
| 83 | + if (word) return (w * 32) + trailingZeros(word); |
| 84 | + w++; |
| 85 | + if (w === this.wordsInUse) return -1; |
| 86 | + word = this.words[w]; |
| 87 | + } |
41 | 88 | } |
42 | | - var bit = 1 << bitIndex; |
43 | | - this.words[w] |= bit; |
44 | | -}; |
45 | 89 |
|
46 | | -P.clear = function(bitIndex) { |
47 | | - var w = wordIndex(bitIndex); |
48 | | - if (w >= this.wordsInUse) return; |
49 | | - var mask = ~(1 << bitIndex); |
50 | | - this.words[w] &= mask; |
51 | | -}; |
| 90 | + /** |
| 91 | + * @param {number} fromIndex |
| 92 | + */ |
| 93 | + nextClearBit(fromIndex) { |
| 94 | + let w = wordIndex(fromIndex); |
| 95 | + if (w >= this.wordsInUse) return fromIndex; |
52 | 96 |
|
53 | | -P.get = function(bitIndex) { |
54 | | - var w = wordIndex(bitIndex); |
55 | | - if (w >= this.wordsInUse) return false; // >= since index vs size |
56 | | - var bit = 1 << bitIndex; |
57 | | - return !!(this.words[w] & bit); |
| 97 | + let word = ~(this.words[w]) & (0xffffffff << fromIndex); |
| 98 | + while (true) { |
| 99 | + if (word) return (w * 32) + trailingZeros(word); |
| 100 | + w++; |
| 101 | + if (w == this.wordsInUse) return w * 32; |
| 102 | + word = ~(this.words[w]); |
| 103 | + } |
| 104 | + } |
58 | 105 | } |
59 | 106 |
|
| 107 | +/** |
| 108 | + * @param {number} bitIndex |
| 109 | + */ |
| 110 | +function wordIndex(bitIndex) { |
| 111 | + return Math.floor(bitIndex / 32); |
| 112 | +} |
| 113 | + |
| 114 | +/** |
| 115 | + * @param {number} i |
| 116 | + */ |
60 | 117 | function trailingZeros(i) { |
61 | 118 | // From Hacker's Delight, via JDK. Probably far less effective here, |
62 | 119 | // since bit ops are not necessarily the quick way to do things in |
63 | 120 | // JS. |
64 | 121 | if (i === 0) return 32; |
65 | | - var y, n = 31; |
| 122 | + let y, n = 31; |
66 | 123 | y = i << 16; if (y != 0) { n = n -16; i = y; } |
67 | 124 | y = i << 8; if (y != 0) { n = n - 8; i = y; } |
68 | 125 | y = i << 4; if (y != 0) { n = n - 4; i = y; } |
69 | 126 | y = i << 2; if (y != 0) { n = n - 2; i = y; } |
70 | 127 | return n - ((i << 1) >>> 31); |
71 | 128 | } |
72 | 129 |
|
73 | | -// Give the next bit that's set on or after fromIndex, or -1 if no such |
74 | | -// bit |
75 | | -P.nextSetBit = function(fromIndex) { |
76 | | - var w = wordIndex(fromIndex); |
77 | | - if (w >= this.wordsInUse) return -1; |
78 | | - |
79 | | - // the right-hand side is shifted to only test the bits of the first |
80 | | - // word that are > fromIndex |
81 | | - var word = this.words[w] & (0xffffffff << fromIndex); |
82 | | - while (true) { |
83 | | - if (word) return (w * 32) + trailingZeros(word); |
84 | | - w++; |
85 | | - if (w === this.wordsInUse) return -1; |
86 | | - word = this.words[w]; |
87 | | - } |
88 | | -}; |
89 | | - |
90 | | -P.nextClearBit = function(fromIndex) { |
91 | | - var w = wordIndex(fromIndex); |
92 | | - if (w >= this.wordsInUse) return fromIndex; |
93 | | - |
94 | | - var word = ~(this.words[w]) & (0xffffffff << fromIndex); |
95 | | - while (true) { |
96 | | - if (word) return (w * 32) + trailingZeros(word); |
97 | | - w++; |
98 | | - if (w == this.wordsInUse) return w * 32; |
99 | | - word = ~(this.words[w]); |
100 | | - } |
101 | | -}; |
102 | | - |
103 | 130 | module.exports.BitSet = BitSet; |
0 commit comments