Skip to content

Commit 94cf62b

Browse files
authored
Merge pull request #352 from vanvoorden/vanvoorden/tree-dictionary-keys-equality
[TreeDictionary][Keys] Add Equatable and Hashable Conformance to TreeDictionary.Keys
2 parents 97b1949 + c61e5e2 commit 94cf62b

File tree

4 files changed

+185
-0
lines changed

4 files changed

+185
-0
lines changed

Benchmarks/Sources/Benchmarks/ShareableDictionaryBenchmarks.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,5 +471,62 @@ extension Benchmark {
471471
blackHole(d)
472472
}
473473
}
474+
475+
self.add(
476+
title: "TreeDictionary<Int, Int> equality, unique",
477+
input: [Int].self
478+
) { input in
479+
let keysAndValues = input.map { ($0, 2 * $0) }
480+
let left = TreeDictionary(uniqueKeysWithValues: keysAndValues)
481+
let right = TreeDictionary(uniqueKeysWithValues: keysAndValues)
482+
return { timer in
483+
timer.measure {
484+
precondition(left == right)
485+
}
486+
}
487+
}
488+
489+
self.add(
490+
title: "TreeDictionary<Int, Int> equality, shared",
491+
input: [Int].self
492+
) { input in
493+
let keysAndValues = input.map { ($0, 2 * $0) }
494+
let left = TreeDictionary(uniqueKeysWithValues: keysAndValues)
495+
let right = left
496+
return { timer in
497+
timer.measure {
498+
precondition(left == right)
499+
}
500+
}
501+
}
502+
503+
self.add(
504+
title: "TreeDictionary<Int, Int>.Keys equality, unique",
505+
input: [Int].self
506+
) { input in
507+
let keysAndValues = input.map { ($0, 2 * $0) }
508+
let left = TreeDictionary(uniqueKeysWithValues: keysAndValues)
509+
let right = TreeDictionary(uniqueKeysWithValues: keysAndValues)
510+
return { timer in
511+
timer.measure {
512+
precondition(left.keys == right.keys)
513+
}
514+
}
515+
}
516+
517+
self.add(
518+
title: "TreeDictionary<Int, Int>.Keys equality, shared",
519+
input: [Int].self
520+
) { input in
521+
let keysAndValues = input.map { ($0, 2 * $0) }
522+
let left = TreeDictionary(uniqueKeysWithValues: keysAndValues)
523+
let right = left
524+
return { timer in
525+
timer.measure {
526+
precondition(left.keys == right.keys)
527+
}
528+
}
529+
}
530+
474531
}
475532
}

Sources/HashTreeCollections/TreeDictionary/TreeDictionary+Keys.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,40 @@ extension TreeDictionary.Keys {
288288
return d.keys
289289
}
290290
}
291+
292+
extension TreeDictionary.Keys: Equatable {
293+
/// Returns a Boolean value indicating whether two values are equal.
294+
///
295+
/// Equality is the inverse of inequality. For any values `a` and `b`,
296+
/// `a == b` implies that `a != b` is `false`.
297+
///
298+
/// - Parameter lhs: A value to compare.
299+
/// - Parameter rhs: Another value to compare.
300+
///
301+
/// - Complexity: Generally O(`count`), as long as`Element` properly
302+
/// implements hashing. That said, the implementation is careful to take
303+
/// every available shortcut to reduce complexity, e.g. by skipping
304+
/// comparing elements in shared subtrees.
305+
@inlinable
306+
public static func == (left: Self, right: Self) -> Bool {
307+
left._base._root.isEqualSet(to: right._base._root, by: { _, _ in true })
308+
}
309+
}
310+
311+
extension TreeDictionary.Keys: Hashable {
312+
/// Hashes the essential components of this value by feeding them into the
313+
/// given hasher.
314+
///
315+
/// Complexity: O(`count`)
316+
@inlinable
317+
public func hash(into hasher: inout Hasher) {
318+
let copy = hasher
319+
let seed = copy.finalize()
320+
321+
var hash = 0
322+
for member in self {
323+
hash ^= member._rawHashValue(seed: seed)
324+
}
325+
hasher.combine(hash)
326+
}
327+
}

Tests/HashTreeCollectionsTests/TreeDictionary.Keys Tests.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,81 @@ class TreeDictionaryKeysTests: CollectionTestCase {
105105
}
106106
}
107107
}
108+
109+
func test_isEqual_exhaustive() {
110+
withEverySubset("a", of: testItems) { a in
111+
let x = TreeDictionary<RawCollider, Int>(
112+
uniqueKeysWithValues: a.lazy.map { ($0, 2 * $0.identity) })
113+
let u = Set(a)
114+
expectEqualSets(x.keys, u)
115+
withEverySubset("b", of: testItems) { b in
116+
let y = TreeDictionary<RawCollider, Int>(
117+
uniqueKeysWithValues: b.lazy.map { ($0, -$0.identity - 1) })
118+
let v = Set(b)
119+
expectEqualSets(y.keys, v)
120+
121+
let reference = u == v
122+
print(reference)
123+
124+
expectEqual(x.keys == y.keys, reference)
125+
}
126+
}
127+
}
128+
129+
func test_Hashable() {
130+
let strings: [[[String]]] = [
131+
[
132+
[]
133+
],
134+
[
135+
["a"]
136+
],
137+
[
138+
["b"]
139+
],
140+
[
141+
["c"]
142+
],
143+
[
144+
["d"]
145+
],
146+
[
147+
["e"]
148+
],
149+
[
150+
["f"], ["f"],
151+
],
152+
[
153+
["g"], ["g"],
154+
],
155+
[
156+
["h"], ["h"],
157+
],
158+
[
159+
["i"], ["i"],
160+
],
161+
[
162+
["j"], ["j"],
163+
],
164+
[
165+
["a", "b"], ["b", "a"],
166+
],
167+
[
168+
["a", "d"], ["d", "a"],
169+
],
170+
[
171+
["a", "b", "c"], ["a", "c", "b"],
172+
["b", "a", "c"], ["b", "c", "a"],
173+
["c", "a", "b"], ["c", "b", "a"],
174+
],
175+
[
176+
["a", "d", "e"], ["a", "e", "d"],
177+
["d", "a", "e"], ["d", "e", "a"],
178+
["e", "a", "d"], ["e", "d", "a"],
179+
],
180+
]
181+
let keys = strings.map { $0.map { TreeDictionary(uniqueKeysWithValues: $0.map { ($0, Int.random(in: 1...100)) }).keys }}
182+
checkHashable(equivalenceClasses: keys)
183+
}
108184

109185
}

Tests/HashTreeCollectionsTests/TreeSet Tests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,21 @@ class TreeSetTests: CollectionTestCase {
681681
[
682682
["e"]
683683
],
684+
[
685+
["f"], ["f"],
686+
],
687+
[
688+
["g"], ["g"],
689+
],
690+
[
691+
["h"], ["h"],
692+
],
693+
[
694+
["i"], ["i"],
695+
],
696+
[
697+
["j"], ["j"],
698+
],
684699
[
685700
["a", "b"], ["b", "a"],
686701
],

0 commit comments

Comments
 (0)