forked from kodecocodes/swift-algorithm-club
-
Notifications
You must be signed in to change notification settings - Fork 1
/
BitSet.swift
247 lines (213 loc) · 5.88 KB
/
BitSet.swift
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/*
A fixed-size sequence of n bits. Bits have indices 0 to n-1.
*/
public struct BitSet {
/* How many bits this object can hold. */
private(set) public var size: Int
/*
We store the bits in a list of unsigned 64-bit integers.
The first entry, `words[0]`, is the least significant word.
*/
private let N = 64
public typealias Word = UInt64
fileprivate(set) public var words: [Word]
private let allOnes = ~Word()
/* Creates a bit set that can hold `size` bits. All bits are initially 0. */
public init(size: Int) {
precondition(size > 0)
self.size = size
// Round up the count to the next multiple of 64.
let n = (size + (N-1)) / N
words = [Word](repeating: 0, count: n)
}
/* Converts a bit index into an array index and a mask inside the word. */
private func indexOf(_ i: Int) -> (Int, Word) {
precondition(i >= 0)
precondition(i < size)
let o = i / N
let m = Word(i - o*N)
return (o, 1 << m)
}
/* Returns a mask that has 1s for all bits that are in the last word. */
private func lastWordMask() -> Word {
let diff = words.count*N - size
if diff > 0 {
// Set the highest bit that's still valid.
let mask = 1 << Word(63 - diff)
// Subtract 1 to turn it into a mask, and add the high bit back in.
return mask | (mask - 1)
} else {
return allOnes
}
}
/*
If the size is not a multiple of N, then we have to clear out the bits
that we're not using, or bitwise operations between two differently sized
BitSets will go wrong.
*/
fileprivate mutating func clearUnusedBits() {
words[words.count - 1] &= lastWordMask()
}
/* So you can write bitset[99] = ... */
public subscript(i: Int) -> Bool {
get { return isSet(i) }
set { if newValue { set(i) } else { clear(i) } }
}
/* Sets the bit at the specified index to 1. */
public mutating func set(_ i: Int) {
let (j, m) = indexOf(i)
words[j] |= m
}
/* Sets all the bits to 1. */
public mutating func setAll() {
for i in 0..<words.count {
words[i] = allOnes
}
clearUnusedBits()
}
/* Sets the bit at the specified index to 0. */
public mutating func clear(_ i: Int) {
let (j, m) = indexOf(i)
words[j] &= ~m
}
/* Sets all the bits to 0. */
public mutating func clearAll() {
for i in 0..<words.count {
words[i] = 0
}
}
/* Changes 0 into 1 and 1 into 0. Returns the new value of the bit. */
public mutating func flip(_ i: Int) -> Bool {
let (j, m) = indexOf(i)
words[j] ^= m
return (words[j] & m) != 0
}
/* Determines whether the bit at the specific index is 1 (true) or 0 (false). */
public func isSet(_ i: Int) -> Bool {
let (j, m) = indexOf(i)
return (words[j] & m) != 0
}
/*
Returns the number of bits that are 1. Time complexity is O(s) where s is
the number of 1-bits.
*/
public var cardinality: Int {
var count = 0
for var x in words {
while x != 0 {
let y = x & ~(x - 1) // find lowest 1-bit
x = x ^ y // and erase it
count += 1
}
}
return count
}
/* Checks if all the bits are set. */
public func all1() -> Bool {
for i in 0..<words.count - 1 {
if words[i] != allOnes { return false }
}
return words[words.count - 1] == lastWordMask()
}
/* Checks if any of the bits are set. */
public func any1() -> Bool {
for x in words {
if x != 0 { return true }
}
return false
}
/* Checks if none of the bits are set. */
public func all0() -> Bool {
for x in words {
if x != 0 { return false }
}
return true
}
}
// MARK: - Equality
extension BitSet: Equatable {
}
public func == (lhs: BitSet, rhs: BitSet) -> Bool {
return lhs.words == rhs.words
}
// MARK: - Hashing
extension BitSet: Hashable {
/* Based on the hashing code from Java's BitSet. */
public var hashValue: Int {
var h = Word(1234)
for i in stride(from: words.count, to: 0, by: -1) {
h ^= words[i - 1] &* Word(i)
}
return Int((h >> 32) ^ h)
}
}
// MARK: - Bitwise operations
extension BitSet: BitwiseOperations {
public static var allZeros: BitSet {
return BitSet(size: 64)
}
}
private func copyLargest(_ lhs: BitSet, _ rhs: BitSet) -> BitSet {
return (lhs.words.count > rhs.words.count) ? lhs : rhs
}
/*
Note: In all of these bitwise operations, lhs and rhs are allowed to have a
different number of bits. The new BitSet always has the larger size.
The extra bits that get added to the smaller BitSet are considered to be 0.
That will strip off the higher bits from the larger BitSet when doing &.
*/
public func & (lhs: BitSet, rhs: BitSet) -> BitSet {
let m = max(lhs.size, rhs.size)
var out = BitSet(size: m)
let n = min(lhs.words.count, rhs.words.count)
for i in 0..<n {
out.words[i] = lhs.words[i] & rhs.words[i]
}
return out
}
public func | (lhs: BitSet, rhs: BitSet) -> BitSet {
var out = copyLargest(lhs, rhs)
let n = min(lhs.words.count, rhs.words.count)
for i in 0..<n {
out.words[i] = lhs.words[i] | rhs.words[i]
}
return out
}
public func ^ (lhs: BitSet, rhs: BitSet) -> BitSet {
var out = copyLargest(lhs, rhs)
let n = min(lhs.words.count, rhs.words.count)
for i in 0..<n {
out.words[i] = lhs.words[i] ^ rhs.words[i]
}
return out
}
prefix public func ~ (rhs: BitSet) -> BitSet {
var out = BitSet(size: rhs.size)
for i in 0..<rhs.words.count {
out.words[i] = ~rhs.words[i]
}
out.clearUnusedBits()
return out
}
// MARK: - Debugging
extension UInt64 {
/* Writes the bits in little-endian order, LSB first. */
public func bitsToString() -> String {
var s = ""
var n = self
for _ in 1...64 {
s += ((n & 1 == 1) ? "1" : "0")
n >>= 1
}
return s
}
}
extension BitSet: CustomStringConvertible {
public var description: String {
var s = ""
for x in words {
s += x.bitsToString() + " "
}
return s
}
}