Skip to content

Commit 128e6a1

Browse files
committed
Adds bitset.
1 parent c966d0b commit 128e6a1

File tree

6 files changed

+415
-0
lines changed

6 files changed

+415
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
// Bitwise operations
33+
34+
var a = BitSet(size: 4)
35+
var b = BitSet(size: 8)
36+
37+
a.setAll()
38+
a.cardinality // 4
39+
40+
a[1] = false
41+
a[2] = false
42+
a[3] = false
43+
44+
b[2] = true
45+
b[6] = true
46+
b[7] = true
47+
48+
print(a) // 1000000000000000000000000000000000000000000000000000000000000000
49+
print(b) // 0010001100000000000000000000000000000000000000000000000000000000
50+
51+
let c = a | b
52+
c.size // 8
53+
54+
print(c) // 1010001100000000000000000000000000000000000000000000000000000000
55+
56+
a.cardinality // 1
57+
b.cardinality // 3
58+
c.cardinality // 4
59+
60+
print("")
61+
print(~a) // 0111000000000000000000000000000000000000000000000000000000000000
62+
print(~b) // 1101110000000000000000000000000000000000000000000000000000000000
63+
print(~c) // 0101110000000000000000000000000000000000000000000000000000000000
64+
65+
(~a).cardinality // 3
66+
(~b).cardinality // 5
67+
(~c).cardinality // 4
68+
69+
var z = BitSet(size: 66)
70+
z.all0() // true
71+
z.all1() // false
72+
73+
z[45] = true
74+
z.any1() // true
75+
z.all0() // false
76+
77+
z[45] = false
78+
z.any1() // false
79+
z.all0() // true
80+
81+
z[65] = true
82+
z.any1() // true
83+
84+
z.setAll()
85+
z.all1() // true
86+
87+
z[65] = false
88+
z.all1() // false
89+
90+
//var bigBits = BitSet(size: 10000)
91+
//print(bigBits)
92+
93+
var smallBitSet = BitSet(size: 16)
94+
smallBitSet[5] = true
95+
smallBitSet[10] = true
96+
print(smallBitSet >> 3)
97+
print(smallBitSet << 6) // one bit shifts off the end
98+
99+
var bigBitSet = BitSet( size: 120 )
100+
bigBitSet[1] = true
101+
bigBitSet[3] = true
102+
bigBitSet[7] = true
103+
bigBitSet[32] = true
104+
bigBitSet[55] = true
105+
bigBitSet[64] = true
106+
bigBitSet[80] = true
107+
print(bigBitSet)
108+
print(bigBitSet << 32)
109+
print(bigBitSet << 64)
110+
print(bigBitSet >> 32)
111+
print(bigBitSet >> 64)
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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+
fileprivate let N = 64
13+
public typealias Word = UInt64
14+
fileprivate(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 = [Word](repeating: 0, count: n)
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 (Word)(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+
fileprivate 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 += 1
116+
}
117+
}
118+
return count
119+
}
120+
121+
/* Checks if all the bits are set. */
122+
public func all1() -> Bool {
123+
guard words.allSatisfy({ $0 == allOnes }) else { return false }
124+
return words[words.count - 1] == lastWordMask()
125+
}
126+
127+
/* Checks if any of the bits are set. */
128+
public func any1() -> Bool {
129+
guard words.allSatisfy({ $0 == 0 }) else { return true }
130+
return false
131+
}
132+
133+
/* Checks if none of the bits are set. */
134+
public func all0() -> Bool {
135+
guard words.allSatisfy({ $0 == 0 }) else { return false }
136+
return true
137+
}
138+
}
139+
140+
// MARK: - Equality
141+
142+
extension BitSet: Equatable {}
143+
144+
public func == (lhs: BitSet, rhs: BitSet) -> Bool {
145+
return lhs.words == rhs.words
146+
}
147+
148+
// MARK: - Hashing
149+
150+
extension BitSet: Hashable {
151+
/* Based on the hashing code from Java's BitSet. */
152+
public var hashValue: Int {
153+
var h = Word(1234)
154+
for i in stride(from: words.count, to: 0, by: -1) {
155+
h ^= words[i - 1] &* Word(i)
156+
}
157+
return Int((h >> 32) ^ h)
158+
}
159+
}
160+
161+
// MARK: - Bitwise operations
162+
163+
extension BitSet {
164+
public static var allZeros: BitSet {
165+
return BitSet(size: 64)
166+
}
167+
}
168+
169+
private func copyLargest(_ lhs: BitSet, _ rhs: BitSet) -> BitSet {
170+
return (lhs.words.count > rhs.words.count) ? lhs : rhs
171+
}
172+
173+
/*
174+
Note: In all of these bitwise operations, lhs and rhs are allowed to have a
175+
different number of bits. The new BitSet always has the larger size.
176+
The extra bits that get added to the smaller BitSet are considered to be 0.
177+
That will strip off the higher bits from the larger BitSet when doing &.
178+
*/
179+
180+
public func & (lhs: BitSet, rhs: BitSet) -> BitSet {
181+
let m = max(lhs.size, rhs.size)
182+
var out = BitSet(size: m)
183+
let n = min(lhs.words.count, rhs.words.count)
184+
for i in 0..<n {
185+
out.words[i] = lhs.words[i] & rhs.words[i]
186+
}
187+
return out
188+
}
189+
190+
public func | (lhs: BitSet, rhs: BitSet) -> BitSet {
191+
var out = copyLargest(lhs, rhs)
192+
let n = min(lhs.words.count, rhs.words.count)
193+
for i in 0..<n {
194+
out.words[i] = lhs.words[i] | rhs.words[i]
195+
}
196+
return out
197+
}
198+
199+
public func ^ (lhs: BitSet, rhs: BitSet) -> BitSet {
200+
var out = copyLargest(lhs, rhs)
201+
let n = min(lhs.words.count, rhs.words.count)
202+
for i in 0..<n {
203+
out.words[i] = lhs.words[i] ^ rhs.words[i]
204+
}
205+
return out
206+
}
207+
208+
prefix public func ~ (rhs: BitSet) -> BitSet {
209+
var out = BitSet(size: rhs.size)
210+
for i in 0..<rhs.words.count {
211+
out.words[i] = ~rhs.words[i]
212+
}
213+
out.clearUnusedBits()
214+
return out
215+
}
216+
217+
// MARK: - Bit shift operations
218+
219+
/*
220+
Note: For bitshift operations, the assumption is that any bits that are
221+
shifted off the end of the end of the declared size are not still set.
222+
In other words, we are maintaining the original number of bits.
223+
*/
224+
225+
public func << (lhs: BitSet, numBitsLeft: Int) -> BitSet {
226+
var out = lhs
227+
let offset = numBitsLeft / lhs.N
228+
let shift = numBitsLeft % lhs.N
229+
for i in 0..<lhs.words.count {
230+
out.words[i] = 0
231+
if i - offset >= 0 {
232+
out.words[i] = lhs.words[i - offset] << shift
233+
}
234+
if i - offset - 1 >= 0 {
235+
out.words[i] |= lhs.words[i - offset - 1] >> (lhs.N - shift)
236+
}
237+
}
238+
out.clearUnusedBits()
239+
return out
240+
}
241+
242+
public func >> (lhs: BitSet, numBitsRight: Int) -> BitSet {
243+
var out = lhs
244+
let offset = numBitsRight / lhs.N
245+
let shift = numBitsRight % lhs.N
246+
for i in 0..<lhs.words.count {
247+
out.words[i] = 0
248+
if i + offset < lhs.words.count {
249+
out.words[i] = lhs.words[i + offset] >> shift
250+
}
251+
if i + offset + 1 < lhs.words.count {
252+
out.words[i] |= lhs.words[i + offset + 1] << (lhs.N - shift)
253+
}
254+
}
255+
out.clearUnusedBits()
256+
return out
257+
}
258+
259+
// MARK: - Debugging
260+
261+
extension UInt64 {
262+
/* Writes the bits in little-endian order, LSB first. */
263+
public func bitsToString() -> String {
264+
var s = ""
265+
var n = self
266+
for _ in 1...64 {
267+
s += ((n & 1 == 1) ? "1" : "0")
268+
n >>= 1
269+
}
270+
return s
271+
}
272+
}
273+
274+
extension BitSet: CustomStringConvertible {
275+
public var description: String {
276+
var s = ""
277+
for x in words {
278+
s += x.bitsToString() + " "
279+
}
280+
return s
281+
}
282+
}

0 commit comments

Comments
 (0)