1const std = @import("std");
2
3pub fn pt1(comptime input: []const u8) u64 {
4 const r = input[0 .. input.len - 1];
5 var ranges = std.mem.splitScalar(u8, r, ',');
6
7 var count: u64 = 0;
8 while (ranges.next()) |l| {
9 if (l.len == 0) {
10 return count;
11 }
12
13 var range = parseRange(l);
14 while (range.next()) |v| {
15 if (isRange(v)) {
16 count += v;
17 }
18 }
19 }
20 return count;
21}
22
23pub fn pt2(comptime input: []const u8) u64 {
24 const r = input[0 .. input.len - 1];
25 var ranges = std.mem.splitScalar(u8, r, ',');
26
27 var count: u64 = 0;
28 while (ranges.next()) |l| {
29 if (l.len == 0) {
30 return count;
31 }
32
33 var range = parseRange(l);
34 while (range.next()) |v| {
35 if (isRangeN(v)) {
36 count += v;
37 }
38 }
39 }
40 return count;
41}
42
43var buf: [20]u8 = undefined;
44
45fn isRangeN(v: u64) bool {
46 const str = std.fmt.bufPrint(&buf, "{d}", .{v}) catch unreachable;
47 for (1..str.len) |s| {
48 // We can't check if this batch can not evenly chunked.
49 if (@mod(str.len, s) != 0) {
50 continue;
51 }
52
53 const c = str.len / s; // size on the chunk
54 const p = str[0..s]; // first chunk as reference for eql
55 var m = true; // flag that says if something went wrong (if wrong set false)
56
57
58 for (1..c) |i| {
59 const sub_str = str[i * s .. ((1 + i) * s)];
60 if (!std.mem.eql(u8, p, sub_str)) {
61 m = false;
62 break;
63 }
64 }
65
66 if (m) return true;
67 }
68
69 return false;
70}
71
72fn isRange(v: u64) bool {
73 const str = std.fmt.bufPrint(&buf, "{d}", .{v}) catch unreachable;
74
75 if ((str.len % 2) != 0) {
76 return false;
77 }
78
79 const mid = str.len / 2;
80
81 for (0..mid) |i| {
82 if (str[i] != str[i + mid]) {
83 return false;
84 }
85 }
86
87 return true;
88}
89
90const Range = struct {
91 from: u64 = 0,
92 to: u64 = 0,
93 index: u64 = 0,
94
95 pub fn next(self: *Range) ?u64 {
96 const to = self.from + self.index;
97 if (to > self.to) {
98 return null;
99 }
100
101 self.index += 1;
102 return to;
103 }
104};
105
106fn parseRange(range_str: []const u8) Range {
107 var range: Range = .{};
108 var iter = std.mem.splitScalar(u8, range_str, '-');
109
110 var i: u64 = 0;
111 while (iter.next()) |v| {
112 if (i == 0) {
113 range.from = std.fmt.parseInt(u64, v, 10) catch unreachable;
114 } else if (i == 1) {
115 range.to = std.fmt.parseInt(u64, v, 10) catch unreachable;
116 } else {
117 break;
118 }
119
120 i += 1;
121 }
122
123 return range;
124}
125
126test "Day 2 part 1" {
127 const res = pt1(@embedFile("./input/day2"));
128 try std.testing.expect(res == 55916882972);
129}
130
131test "Day 2 part 2" {
132 const res = pt2(@embedFile("./input/day2"));
133 try std.testing.expect(res == 76169125915);
134}