const std = @import("std"); const Allocator = std.mem.Allocator; const TokenIterator = std.mem.TokenIterator; const NUM_LINES = 5; pub fn run(_: Allocator, input: []u8) !struct { u64, u64 } { return .{ try part1(input), try part2(input) }; } fn part1(input: []u8) !u64 { const NUM_OPERANDS = NUM_LINES - 1; var lines = std.mem.tokenizeScalar(u8, input, '\n'); var operand_strings: [NUM_OPERANDS]TokenIterator(u8, .any) = undefined; for (0..NUM_OPERANDS) |i| { operand_strings[i] = std.mem.tokenizeAny(u8, lines.next().?, " "); } var operator: TokenIterator(u8, .any) = std.mem.tokenizeAny(u8, lines.next().?, " "); var answer1: u64 = 0; while (operator.next()) |op| { var operands: [NUM_OPERANDS]u64 = undefined; for (0..NUM_OPERANDS) |i| { operands[i] = try std.fmt.parseInt(u64, operand_strings[i].next().?, 10); } answer1 += switch (op[0]) { '+' => blk: { var result: u64 = 0; for (operands) |o| result += o; break :blk result; }, '*' => blk: { var result: u64 = 1; for (operands) |o| result *= o; break :blk result; }, else => unreachable, }; } return answer1; } fn part2(input: []u8) !u64 { const NUM_OPERAND_LINES = NUM_LINES - 1; var lines = std.mem.tokenizeScalar(u8, input, '\n'); var operand_lines: [NUM_OPERAND_LINES][]const u8 = undefined; for (0..NUM_OPERAND_LINES) |i| { operand_lines[i] = lines.next().?; } const operator = lines.next().?; var answer2: u64 = 0; var current_operator: u8 = undefined; var result: u64 = 0; for (0..operator.len) |i| { if (operator[i] != ' ') { answer2 += result; current_operator = operator[i]; result = switch (current_operator) { '+' => 0, '*' => 1, else => unreachable, }; } var operand_str: [NUM_OPERAND_LINES]u8 = undefined; for (0..NUM_OPERAND_LINES) |j| { operand_str[j] = operand_lines[j][i]; } const operand = std.fmt.parseUnsigned(u64, std.mem.trim(u8, operand_str[0..], " "), 10) catch continue; switch (current_operator) { '+' => result += operand, '*' => result *= operand, else => unreachable, } } answer2 += result; return answer2; }