const std = @import("std"); const Error = error{IndexError}; fn splitOnce(s: []u8, sep: u8) ![2][]u8 { const i = std.mem.indexOfScalar(u8, s, sep) orelse return Error.IndexError; return .{ s[0..i], s[i + 1 ..] }; } fn takeRange(r: *std.Io.Reader) ![]u8 { const s = r.takeDelimiterInclusive(',') catch |err| switch (err) { error.EndOfStream => try r.takeDelimiterInclusive('\n'), else => |e| return e, }; return s[0 .. s.len - 1]; } fn getDigits(i: usize) usize { const f: f64 = @floatFromInt(i); const digits = @floor(@log10(f)) + 1; return @intFromFloat(digits); } fn getMask(digits: usize, repeats: usize) usize { // examples: // - a number with 6 digits and 2 repeats has the form 100..999 * 1001 // - a number with 8 digits and 4 repeats has the form 10..99 * 1010101 var mask: usize = 1; for (1..repeats) |_| { mask = mask * std.math.pow(usize, 10, digits / repeats) + 1; } return mask; } 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: [64]u8 = undefined; var reader = file.reader(&buffer); var part1: usize = 0; var part2: usize = 0; while (takeRange(&reader.interface)) |line| { const s1, const s2 = try splitOnce(line, '-'); const start = try std.fmt.parseInt(usize, s1, 10); const end = try std.fmt.parseInt(usize, s2, 10); std.debug.assert(start <= end); for (getDigits(start)..getDigits(end) + 1) |digits| { for (2..digits + 1) |repeats| { if (digits % repeats != 0) { continue; } const mask = getMask(digits, repeats); const min = std.math.pow(usize, 10, digits / repeats - 1); const minValue = @max( min, try std.math.divCeil(usize, start, mask), ); const maxValue = @min( min * 10 - 1, try std.math.divFloor(usize, end, mask), ); valueloop: for (minValue..maxValue + 1) |value| { const result = value * mask; for (2..repeats) |prevRepeats| { if (result % getMask(digits, prevRepeats) == 0) { continue :valueloop; } } part2 += result; if (repeats == 2) { part1 += result; } } } } } else |err| switch (err) { error.EndOfStream => {}, else => |e| return e, } std.debug.print("part1: {}\n", .{part1}); std.debug.print("part2: {}\n", .{part2}); }