import Foundation func readInput(_ filePath: String) throws -> ([(Int, Int)], [[Int]]) { let content = try String(contentsOfFile: filePath, encoding: .ascii) var rules: [(Int, Int)] = [] var pages: [[Int]] = [] var readingRules = true for l in content.split(separator: "\n", omittingEmptySubsequences: false) { if l == "" { readingRules = false continue } if readingRules { let rule = l.split(separator: "|").map { Int($0)! } rules.append((rule[0], rule[1])) } else { pages.append(l.split(separator: ",").map { Int($0)! }) } } return (rules, pages) } func rulesMap(_ rulePairs: [(Int, Int)]) -> [Int: Set] { var rules: [Int: Set] = [:] for (from, to) in rulePairs { let current = rules[from, default: []] rules[from] = current.union([to]) } return rules } func goodPages(_ pages: [Int], against rules: [Int: Set]) -> Bool { var seen: Set = [] for page in pages { if !rules[page, default: []].isDisjoint(with: seen) { return false } seen.insert(page) } return true } func reorder(_ originalPages: [Int], conformTo rules: [(Int, Int)]) -> [Int] { let pages = Set(originalPages) var graph = rulesMap( rules.filter { from, to in pages.contains(from) && pages.contains(to) } ) // Topological sort var sorted: [Int] = [] while graph.count > 0 { let outNodes: Set = graph.reduce([], { s, i in s.union(i.value) }) let inNodes = graph.filter { !outNodes.contains($0.key) }.keys graph = graph.filter { !inNodes.contains($0.key) } sorted += inNodes } return sorted } let (rules, manuals) = try readInput(CommandLine.arguments[1]) let answer = manuals .filter { !goodPages($0, against: rulesMap(rules)) } .map { reorder($0, conformTo: rules) } .map { $0[$0.count/2] } .reduce(0, +) print(answer)