const std = @import("std"); fn StackList(comptime T: type, comptime capacity_type: type, comptime capacity: capacity_type) type { return struct { const Self = @This(); mem: [capacity]T, length: capacity_type, fn add(self: *Self, value: T) void { self.mem[self.length] = value; self.length += 1; } fn has(self: *const Self, needle: T) bool { for (0..self.length) |i| { if (self.mem[i] == needle) { return true; } } return false; } fn getSlice(self: *const Self) []const T { return self.mem[0..self.length]; } fn reset(self: *Self) void { self.length = 0; } fn init() Self { return Self{ .mem = undefined, .length = 0, }; } }; } const StateStorage = StackList(u64, u32, 10_000); const State = struct { a: StateStorage = StateStorage.init(), b: StateStorage = StateStorage.init(), is_a_current: bool = true, fn getCurrent(self: *State) *StateStorage { return switch (self.is_a_current) { true => &self.a, false => &self.b, }; } fn getPrevious(self: *State) *StateStorage { return switch (self.is_a_current) { true => &self.b, false => &self.a, }; } fn add(self: *State, value: u64) void { self.getCurrent().*.add(value); } fn addMapping(self: *State, destination_start: u64, source_start: u64, range_length: u64) void { var current = self.getCurrent(); const previous = self.getPrevious().*; const source_end = source_start + range_length; std.debug.print("processing mapping from {d}-{d} (length {d}) to {d}\n", .{ source_start, source_end, range_length, destination_start }); for (previous.getSlice()) |value| { if (value >= source_start and value < source_end) { const new_value = (value - source_start) + destination_start; std.debug.print("adding new value {d}\n", .{new_value}); current.*.add(new_value); } } } fn swap(self: *State) void { self.is_a_current = !self.is_a_current; self.getCurrent().*.reset(); } fn getMinCurrentValue(self: *State) u64 { var current = self.getCurrent().*; var result: u32 = std.math.maxInt(u32); for (current.getSlice()) |value| { result = @min(result, value); } return result; } fn debug(self: *State) void { std.debug.print("Current state: {any}, previous state: {any}\n", .{ self.getCurrent().*.getSlice(), self.getPrevious().*.getSlice() }); } }; fn addDigit(result: anytype, digit: u8) void { result.* = (result.* * 10) + (digit - '0'); } fn parseFirstLine(line: []const u8) State { var result = State{}; var current_number: u64 = 0; var index: usize = 0; while (index < line.len) : (index += 1) { const char = line[index]; switch (char) { '0'...'9' => { addDigit(¤t_number, char); }, else => { if (current_number != 0) { result.add(current_number); current_number = 0; } }, } } if (current_number != 0) { result.add(current_number); } return result; } fn parseMappingLine(line: []const u8, state: *State) void { var destination_start: u64 = 0; var source_start: u64 = 0; var range_length: u64 = 0; var index: usize = 0; while (line[index] != ' ') : (index += 1) { addDigit(&destination_start, line[index]); } index += 1; while (line[index] != ' ') : (index += 1) { addDigit(&source_start, line[index]); } index += 1; while (index < line.len) : (index += 1) { addDigit(&range_length, line[index]); } state.addMapping(destination_start, source_start, range_length); } pub fn main() !void { const stdout = std.io.getStdOut().writer(); const raw_in = std.io.getStdIn(); var buffered_reader = std.io.bufferedReader(raw_in.reader()); var reader = buffered_reader.reader(); var line_buffer: [1000]u8 = undefined; const first_line = (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')).?; var state = parseFirstLine(first_line); state.debug(); _ = try reader.readUntilDelimiterOrEof(&line_buffer, '\n'); _ = try reader.readUntilDelimiterOrEof(&line_buffer, '\n'); state.swap(); state.debug(); while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| { if (line.len != 0) { parseMappingLine(line, &state); } else { state.debug(); _ = try reader.readUntilDelimiterOrEof(&line_buffer, '\n'); state.swap(); } } state.debug(); try stdout.print("{d}\n", .{state.getMinCurrentValue()}); }