Skip to content

Commit f839acb

Browse files
committed
Preliminary version of Bit Set
1 parent 962e0e9 commit f839acb

File tree

3 files changed

+249
-1
lines changed

3 files changed

+249
-1
lines changed

Bit Set/BitSet.swift

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*
2+
A fixed-size sequence of n bits. Bits have indices 0 to n-1.
3+
*/
4+
public struct BitSet {
5+
/* How many bits this object can hold. */
6+
private(set) public var size: Int
7+
8+
/*
9+
We store the bits in a list of unsigned 64-bit integers.
10+
The first entry is the LSB (least significant bit).
11+
*/
12+
private let N = 64
13+
public typealias Word = UInt64
14+
private(set) public var array: [Word]
15+
16+
/* Creates a bit set that can hold `size` bits. All bits are initially 0. */
17+
public init(size: Int) {
18+
precondition(size > 0)
19+
self.size = size
20+
21+
// Round up the count to the next multiple of 64.
22+
let n = (size + (N-1)) / N
23+
array = .init(count: n, repeatedValue: 0)
24+
}
25+
26+
/* Converts a bit index into an array index and a mask inside the word. */
27+
private func indexOf(i: Int) -> (Int, Word) {
28+
precondition(i >= 0)
29+
precondition(i < size)
30+
let o = i / N
31+
let m = Word(i - o*N)
32+
return (o, 1 << m)
33+
}
34+
35+
/*
36+
If the size is not a multiple of N, then we have to clear out the bits
37+
that we're not using, or the bitwise operations between two differently
38+
sized BitSets will go wrong.
39+
*/
40+
private mutating func clearUnusedBits() {
41+
let diff = array.count*N - size
42+
if diff > 0 {
43+
// First, set the highest bit that's still valid.
44+
var mask = 1 << Word(63 - diff)
45+
// Subtract 1 to turn it into a mask, and add the high bit back in.
46+
mask = mask | (mask - 1)
47+
// Finally, turn all the higher bits off.
48+
array[array.count - 1] &= mask
49+
}
50+
}
51+
52+
/* So you can write bitset[99] = ... */
53+
public subscript(i: Int) -> Bool {
54+
get { return isSet(i) }
55+
set { if newValue { set(i) } else { clear(i) } }
56+
}
57+
58+
/* Sets the bit at the specific index to 1. */
59+
public mutating func set(i: Int) {
60+
let (j, m) = indexOf(i)
61+
array[j] |= m
62+
}
63+
64+
/* Sets all the bits to 1. */
65+
public mutating func setAll() {
66+
let mask = ~Word()
67+
for i in 0..<array.count {
68+
array[i] = mask
69+
}
70+
clearUnusedBits()
71+
}
72+
73+
/* Sets the bit at the specific index to 0. */
74+
public mutating func clear(i: Int) {
75+
let (j, m) = indexOf(i)
76+
array[j] &= ~m
77+
}
78+
79+
/* Sets all the bits to 0. */
80+
public mutating func clearAll() {
81+
for i in 0..<array.count {
82+
array[i] = 0
83+
}
84+
}
85+
86+
/* Changes 0 into 1 and 1 into 0. Returns the new value of the bit. */
87+
public mutating func flip(i: Int) -> Bool {
88+
let (j, m) = indexOf(i)
89+
array[j] ^= m
90+
return (array[j] & m) != 0
91+
}
92+
93+
/* Determines whether the bit at the specific index is 1 (true) or 0 (false). */
94+
public func isSet(i: Int) -> Bool {
95+
let (j, m) = indexOf(i)
96+
return (array[j] & m) != 0
97+
}
98+
99+
/*
100+
Returns the number of bits that are 1. Time complexity is O(s) where s is
101+
the number of 1-bits.
102+
*/
103+
public var cardinality: Int {
104+
var count = 0
105+
for var x in array {
106+
while x != 0 {
107+
let y = x & ~(x - 1) // find lowest 1-bit
108+
x = x ^ y // and erase it
109+
++count
110+
}
111+
}
112+
return count
113+
}
114+
115+
/* Checks if all the bits are set. */
116+
public func all1() -> Bool {
117+
let mask = ~Word()
118+
for x in array {
119+
if x != mask { return false }
120+
}
121+
return true
122+
}
123+
124+
/* Checks if any of the bits are set. */
125+
public func any1() -> Bool {
126+
for x in array {
127+
if x != 0 { return true }
128+
}
129+
return false
130+
}
131+
132+
/* Checks if none of the bits are set. */
133+
public func all0() -> Bool {
134+
for x in array {
135+
if x != 0 { return false }
136+
}
137+
return true
138+
}
139+
}
140+
141+
// MARK: - Equality
142+
143+
extension BitSet: Equatable {
144+
}
145+
146+
public func ==(lhs: BitSet, rhs: BitSet) -> Bool {
147+
return lhs.array == rhs.array
148+
}
149+
150+
// MARK: - Hashing
151+
152+
extension BitSet: Hashable {
153+
/* Based on the hashing code from Java's BitSet. */
154+
public var hashValue: Int {
155+
var h = Word(1234)
156+
for i in array.count.stride(to: 0, by: -1) {
157+
h ^= array[i - 1] &* Word(i)
158+
}
159+
return Int((h >> 32) ^ h)
160+
}
161+
}
162+
163+
// MARK: - Bitwise operations
164+
165+
extension BitSet: BitwiseOperationsType {
166+
public static var allZeros: BitSet {
167+
return BitSet(size: 64)
168+
}
169+
}
170+
171+
/*
172+
Note: If lhs and rhs have different sizes, then we consider the higher-order
173+
bits to be 0.
174+
*/
175+
public func &(var lhs: BitSet, rhs: BitSet) -> BitSet {
176+
let n = min(lhs.array.count, rhs.array.count)
177+
for i in n..<lhs.array.count {
178+
lhs.array[i] = 0
179+
}
180+
for i in 0..<n {
181+
lhs.array[i] &= rhs.array[i]
182+
}
183+
return lhs
184+
}
185+
186+
/*
187+
Note: If lhs is smaller than rhs, then we drop the higher-order bits because
188+
BitSet cannot resize. It's OK if rhs has fewer bits than lhs.
189+
*/
190+
public func |(var lhs: BitSet, rhs: BitSet) -> BitSet {
191+
let n = min(lhs.array.count, rhs.array.count)
192+
for i in 0..<n {
193+
lhs.array[i] |= rhs.array[i]
194+
}
195+
lhs.clearUnusedBits()
196+
return lhs
197+
}
198+
199+
/*
200+
Note: If lhs is smaller than rhs, then we drop the higher-order bits because
201+
BitSet cannot resize. It's OK if rhs has fewer bits than lhs.
202+
*/
203+
public func ^(var lhs: BitSet, rhs: BitSet) -> BitSet {
204+
let n = min(lhs.array.count, rhs.array.count)
205+
for i in 0..<n {
206+
lhs.array[i] ^= rhs.array[i]
207+
}
208+
lhs.clearUnusedBits()
209+
return lhs
210+
}
211+
212+
prefix public func ~(var rhs: BitSet) -> BitSet {
213+
for i in 0..<rhs.array.count {
214+
rhs.array[i] = ~rhs.array[i]
215+
}
216+
rhs.clearUnusedBits()
217+
return rhs
218+
}
219+
220+
// MARK: - Debugging
221+
222+
extension UInt64 {
223+
/* Writes the bits in little-endian order, LSB first. */
224+
public func bitsToString() -> String {
225+
var s = ""
226+
var n = self
227+
for _ in 1...64 {
228+
s += ((n & 1 == 1) ? "1" : "0")
229+
n >>= 1
230+
}
231+
return s
232+
}
233+
}
234+
235+
extension BitSet: CustomStringConvertible {
236+
public var description: String {
237+
var s = ""
238+
for x in array {
239+
s += x.bitsToString() + " "
240+
}
241+
return s
242+
}
243+
}

Bit Set/README.markdown

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Bit Set
2+
3+
A fixed-size sequence of *n* bits. Also known as bit array or bit vector.
4+
5+
[More to come here.]

README.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Most of the time using just the built-in `Array`, `Dictionary`, and `Set` types
110110
### Variations on arrays
111111

112112
- [Array2D](Array2D/). A two-dimensional array with fixed dimensions. Useful for board games.
113+
- [Bit Set](Bit Set/). A fixed-size sequence of *n* bits.
113114
- [Fixed Size Array](Fixed Size Array/). When you know beforehand how large your data will be, it might be more efficient to use an old-fashioned array with a fixed size.
114115
- [Ordered Array](Ordered Array/). An array that is always sorted.
115116

@@ -148,7 +149,6 @@ Most of the time using just the built-in `Array`, `Dictionary`, and `Set` types
148149

149150
### Sets
150151

151-
- Bit Set
152152
- Bloom Filter
153153
- [Hash Set](Hash Set/). A set implemented using a hash table.
154154
- Multiset

0 commit comments

Comments
 (0)