I’ve been participating regularly in Advent of Code for the past couple of years. It’s one of the highlights of my holiday season. The puzzles are fun, the stories are appropriately ridiculous, and it’s a neat way for me to keep the cobwebs brushed off some of the things I learned years ago that I don’t regularly use. Every year there are puzzles that take me a couple of minutes to solve, and puzzles that take me hours: I will forever curse the Intcode puzzles from 2019.
Last night’s puzzle was something new. The problem itself was pretty straight-forward (finding values that are common in multiple collections), but it resulted in a 45-minute debugging session that culminated in finding a bug in Swift’s implementation of Set.intersection(_:)
.
The nature of last night’s problem was that, when I had a bunch of inputs, I could expect that there was only a single common element between all of them. After getting lucky and solving the problem correctly, I started golfing my code to make it terser. That’s when I started noticing something odd.
My initial version of the code looked something like this:
let firstGroup: String = ...
let secondGroup: String = ...
let thirdGroup: String = ...
let uniqueLettersInFirstGroup = Set(firstGroup)
let uniqueLettersInSecondGroup = Set(secondGroup)
let uniqueLettersInThirdGroup = Set(thirdGroup)
let commonLetters = uniqueLettersInFirstGroup.intersection(uniqueLettersInSecondGroup).intersection(uniqueLettersInThirdGroup)
let commonLetter = commonLetters.first! // safe to unwrap, because if this crashes the input is bad
// ... do processing with the common letter
This worked great, but it’s also a lot of code. So I started combining things:
let firstGroup: String = ...
let secondGroup: String = ...
let thirdGroup: String = ...
let commonLetters = Set(firstGroup).intersection(secondGroup).intersection(thirdGroup)
let commonLetter = commonLetters.first!
Eventually I decided to make an extension, since this sort of algorithm (“find what’s in common between these groups of things”) is a pretty normal thing to encounter in Advent of Code:
extension Collection where Element: Collection, Element.Element: Hashable {
var commonElements: Set<Element.Element> {
// intersect all the elements
// return the final intersection
}
}
let firstGroup: String = ...
let secondGroup: String = ...
let thirdGroup: String = ...
let commonLetters = [firstGroup, secondGroup, thirdGroup].commonElements
let commonLetter = commonLetters.first!
It was about this point that I started noticing something weird: every time I ran my code, I’d get a different answer.
This is not how Advent of Code works. It is very deterministic: for each input, there is a single correct output. And as luck would have it, I’d already found the correct output. What I was getting now as everything except correct.
Questioning The Nature Of My Existence
After putting in a hefty number of log statements, I realized: my set of “common elements” would sometimes have more than one element in it.
Starting with gnmCjzwnmCPTPhBwPjzBgqPjllJJSWlhfhQD