-
Notifications
You must be signed in to change notification settings - Fork 21
/
ranked-bit-array.ts
113 lines (106 loc) · 2.8 KB
/
ranked-bit-array.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import type { Bit } from "./utility-types.ts";
import { BitArray } from "./bit-array.ts";
import { getLSBIndex, popCount32 } from "./utilities.ts";
/**
* A bit array that supports constant time rank and O(logN) time select operations.
*
* @example
* const array = RankedBitArray.create(10);
* array.setBit(1).setBit(3).setBit(7);
* array.rank(2);
* //=> 1
* array.rank(7);
* //=> 2
* array.select(2);
* //=> 3
*/
export class RankedBitArray extends BitArray {
/**
* The amount of bits in the array.
*/
get size(): number {
return (this.length >> 1) << 5;
}
/**
* Returns the length of the underlying TypedArray required to hold the given amount of bits.
*
* @param size the amount of bits
* @return the required length
*/
static getLength(size: number): number {
return Math.ceil(size / 32) << 1;
}
/**
* Returns the rank of a bit at a given index.
*
* @param index the index
* @return the rank
*/
rank(index: number): number {
const { bucket, position } = this.getBitPosition(index);
const value = this[bucket];
// mask out following bits
const masked = value & ((1 << position) - 1);
const localRank = popCount32(masked);
const bucketRank = bucket ? this[(this.length >> 1) + bucket - 1] : 0;
return bucketRank + localRank;
}
/**
* Returns the select of a bit at a given index.
*
* @param index the index
* @return the select
*/
select(index: number): number {
const middle = this.length >> 1;
let left = middle;
let right = this.length - 1;
let bucketRankId = 0;
while (left <= right) {
bucketRankId = (right + left) >> 1;
if (index > this[bucketRankId]) {
left = bucketRankId + 1;
} else if (index < this[bucketRankId]) {
right = bucketRankId - 1;
} else if (index === this[bucketRankId - 1]) {
// preceded by a duplicate
right = bucketRankId - 1;
} else {
break;
}
}
bucketRankId = index === this[bucketRankId] ? bucketRankId : left;
if (bucketRankId >= this.length) return -1;
let rank = bucketRankId > middle ? this[bucketRankId - 1] : 0;
const bucket = bucketRankId - middle;
let value = this[bucket];
while (value) {
const position = getLSBIndex(value);
value &= value - 1;
rank++;
if (rank === index) {
return (bucket << 5) + position;
}
}
return -1;
}
/**
* Sets the bit at a given index.
*
* @param index the index
* @param value the value
* @return this
*/
setBit(index: number, value: Bit = 1): this {
super.setBit(index, value);
const change = value || -1;
for (
let i = (this.length >> 1) + this.lastPosition.bucket;
i < this.length;
i++
) {
this[i] += change;
}
return this;
}
}