const std = @import("std"); const Error = error{IndexError}; const nPoints = 1000; const nConnections = 1000; const Point = [3]i64; const Connection = struct { d: i64, i: usize, j: usize, }; fn parsePoint(s: []u8) !Point { const i = std.mem.indexOfScalar(u8, s, ',') orelse return Error.IndexError; const tail = s[i + 1 ..]; const j = std.mem.indexOfScalar(u8, tail, ',') orelse return Error.IndexError; const x = try std.fmt.parseInt(i64, s[0..i], 10); const y = try std.fmt.parseInt(i64, tail[0..j], 10); const z = try std.fmt.parseInt(i64, tail[j + 1 ..], 10); return .{ x, y, z }; } fn dist(a: Point, b: Point) i64 { var result: i64 = 0; for (0..3) |i| { result += (a[i] - b[i]) * (a[i] - b[i]); } return result; } fn connectionAsc(_: void, a: Connection, b: Connection) bool { return a.d < b.d; } pub fn main() !void { const path: [:0]const u8 = std.mem.span(std.os.argv[1]); var file = try std.fs.cwd().openFile(path, .{}); defer file.close(); var buffer: [1024]u8 = undefined; var reader = file.reader(&buffer); var i: usize = 0; var k: usize = 0; var points = [_]Point{.{ 0, 0, 0 }} ** nPoints; var connections = [_]Connection{undefined} ** (nPoints * (nPoints - 1) / 2); while (reader.interface.peekDelimiterExclusive('\n')) |line| { reader.interface.toss(line.len + 1); points[i] = try parsePoint(line); for (0..i) |j| { const d = dist(points[i], points[j]); connections[k] = Connection{ .d = d, .i = i, .j = j }; k += 1; } i += 1; } else |err| switch (err) { error.EndOfStream => {}, else => |e| return e, } std.sort.block(Connection, &connections, void{}, connectionAsc); // each cluster is identified by its lowest member var counts = [_]usize{1} ** nPoints; var clusters = [_]usize{0} ** nPoints; for (0..clusters.len) |j| { clusters[j] = j; } for (&connections, 0..) |c, n| { if (clusters[c.i] != clusters[c.j]) { const cmin = @min(clusters[c.i], clusters[c.j]); const cmax = @max(clusters[c.i], clusters[c.j]); for (0..clusters.len) |j| { if (clusters[j] == cmax) { clusters[j] = cmin; } } counts[cmin] += counts[cmax]; counts[cmax] = 0; if (counts[cmin] == nPoints) { std.debug.print("part2: {}\n", .{points[c.i][0] * points[c.j][0]}); break; } } if (n + 1 == nConnections) { var copy = counts; std.sort.block(usize, ©, void{}, std.sort.desc(usize)); std.debug.print("part1: {}\n", .{copy[0] * copy[1] * copy[2]}); } } }