Skip to content

Commit 1865e6a

Browse files
committed
Bit Set improvements
1 parent 1d1c1bf commit 1865e6a

File tree

6 files changed

+760
-70
lines changed

6 files changed

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

0 commit comments

Comments
 (0)