         return hash
     }
     else {
-        var hash = ""
+        var hash = string.md5_2()
         autoreleasepool {
-            hash = string.md5_2()
             for _ in 0..<stretchCount {
-                hash = hash.md5_2().lowercased()
+                hash = hash.md5_2()
             }
         }
         hashDict[index] = hash

}

func findKeys(base: String, numberOfKeys: Int, stretchCount: Int) -> [Candidate] {
    
    var keys = [Candidate]()
    while keys.count != numberOfKeys {
        for salt in 0..<Int.max {
            let hash = hashFor(index: salt, or: "\(base)\(salt)", stretched: stretchCount)
            if let character = hash.tripletCharacter() {

                let quintuple = String([Character](repeatElement(character, count: 5)))
                for i in 1...1000 {
                    let checkHash = hashFor(index: salt+i, or: "\(base)\(salt+i)", stretched: stretchCount)
                    
                    if checkHash.contains(quintuple) {
                        let candidate = Candidate(index: salt, character: character, hash: hash)
                        keys.append(candidate)
                        print (String.init(format: "%02d [%@] %@", candidate.index, String(candidate.character), candidate.hash))
                        if keys.count == numberOfKeys {
                            return keys
                        }
                        break
                    }
                }
                
                // we can remove the checked hash, it's the first one and will never be checked again
            }
            hashDict.removeValue(forKey: salt)
        }
    }
    return keys

}

// Part Two
hashDict.removeAll()
measure {
    for (index, candidate) in findKeys(base: input, numberOfKeys: 64, stretchCount: 2016).sorted(by: { $0.index < $1.index }).enumerated() {
        print (String.init(format: "%02d [%@] %@", candidate.index, String(candidate.character), candidate.hash))
