const std = @import("std"); const PATH = "input/day14.txt"; const Str = []const u8; pub fn first(allocator: ?std.mem.Allocator) anyerror!usize { var memory = std.AutoHashMap(u36, u36).init(allocator.?); defer memory.deinit(); const file = @embedFile(PATH); var lines = std.mem.tokenize(u8, file, "\n"); var mask: Str = undefined; while (lines.next()) |line| { switch (line[1]) { // mask 'a' => { var parts = std.mem.tokenize(u8, line, "="); _ = parts.next(); mask = parts.next().?[1..]; }, // mem 'e' => { // parse memory data var parts = std.mem.tokenize(u8, line, "[]="); _ = parts.next(); // mem const address = try std.fmt.parseUnsigned(u36, parts.next().?, 0); _ = parts.next(); // emptry var value = try std.fmt.parseUnsigned(u36, parts.next().?[1..], 0); // enforce mask var idx: u6 = 36; for (mask) |item| { switch (item) { 'X' => {}, '1' => value |= @as(u36, 1) << idx - 1, '0' => value &= ~(@as(u36, 1) << idx - 1), else => unreachable, } idx -= 1; } // add to memory table try memory.put(address, value); }, else => unreachable, } } var sum: usize = 0; var it = memory.valueIterator(); while (it.next()) |item| { sum += item.*; } return sum; } pub fn second(allocator: ?std.mem.Allocator) anyerror!usize { var arena = std.heap.ArenaAllocator.init(allocator.?); defer arena.deinit(); var memory = std.AutoHashMap(u36, u36).init(arena.allocator()); defer memory.deinit(); const file = @embedFile(PATH); var lines = std.mem.tokenize(u8, file, "\n"); var mask: Str = undefined; while (lines.next()) |line| { switch (line[1]) { // mask 'a' => { var parts = std.mem.tokenize(u8, line, "="); _ = parts.next(); mask = parts.next().?[1..]; }, // mem 'e' => { var addresses = std.ArrayList(u36).init(arena.allocator()); defer addresses.deinit(); // parse memory data var parts = std.mem.tokenize(u8, line, "[]="); _ = parts.next(); // mem const adr = try std.fmt.parseUnsigned(u36, parts.next().?, 0); try addresses.append(adr); _ = parts.next(); // emptry const value = try std.fmt.parseUnsigned(u36, parts.next().?[1..], 0); // enforce mask var idx: u6 = 36; for (mask) |item| { defer idx -= 1; switch (item) { 'X' => { // This line below is _very_ important. Using append() would // arbitrary change array size, which makes .items pointers // unpredictable, resulting strange errors. try addresses.ensureTotalCapacity(addresses.capacity * 2); for (addresses.items) |*address| { const zero = address.* & ~(@as(u36, 1) << idx - 1); addresses.appendAssumeCapacity(zero); const one = address.* | (@as(u36, 1) << idx - 1); address.* = one; } }, '1' => { for (addresses.items) |*address| { address.* |= @as(u36, 1) << idx - 1; } }, '0' => {}, else => unreachable, } } // add to memory table for (addresses.items) |address| { try memory.put(address, value); } }, else => unreachable, } } var sum: usize = 0; var it = memory.valueIterator(); while (it.next()) |item| { sum += item.*; } return sum; } test "day14a" { try std.testing.expectEqual(@as(usize, 11327140210986), try first(std.testing.allocator)); } test "day14b" { try std.testing.expectEqual(@as(usize, 2308180581795), try second(std.testing.allocator)); } const test_input = \\mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X \\mem[8] = 11 \\mem[7] = 101 \\mem[8] = 0 ; const test_input2 = \\mask = 000000000000000000000000000000X1001X \\mem[42] = 100 \\mask = 00000000000000000000000000000000X0XX \\mem[26] = 1 ;