178 lines
5.9 KiB
Zig
178 lines
5.9 KiB
Zig
const std = @import("std");
|
|
const print = std.debug.print;
|
|
const util = @import("util.zig");
|
|
|
|
pub fn main() !void {
|
|
const output = try part_two();
|
|
print("{}\n", .{output});
|
|
}
|
|
|
|
pub fn part_one() !u32 {
|
|
const arrays = try parseInputFileToArrays();
|
|
const arr1 = arrays.first;
|
|
const arr2 = arrays.second;
|
|
|
|
// Everything below this is the actual logic
|
|
const stdout = std.io.getStdOut().writer();
|
|
std.mem.sort(u32, arr1, {}, comptime std.sort.asc(u32));
|
|
std.mem.sort(u32, arr2, {}, comptime std.sort.asc(u32));
|
|
|
|
var sum: u32 = 0;
|
|
for (0..arr1.len) |index| {
|
|
const val1 = arr1[index];
|
|
const val2 = arr2[index];
|
|
if (val1 > val2) {
|
|
sum += (val1 -% val2);
|
|
} else {
|
|
sum += (val2 -% val1);
|
|
}
|
|
}
|
|
try stdout.print("{}", .{sum});
|
|
// Only used for testing - but later we'll have this return to an actual higher-level `main` function and have
|
|
// _that_ do printing.
|
|
return sum;
|
|
}
|
|
|
|
pub fn part_two() !u32 {
|
|
const arrays = try parseInputFileToArrays();
|
|
const arr1 = arrays.first;
|
|
const arr2 = arrays.second;
|
|
print("DEBUG - arr1 is {any}\narr2 is {any}\n", .{arr1, arr2});
|
|
// Logic:
|
|
// * Iterate over each array
|
|
// * Count the number of occurrences
|
|
// * For each number, increment the sum by (number * count_1 * count_2)
|
|
// (Would technically be parallelizable if you wanted to get fancy, but JFC I'm not ready for that in Zig yet)
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
var counts1 = std.AutoHashMap(u32, u32).init(allocator);
|
|
var counts2 = std.AutoHashMap(u32, u32).init(allocator);
|
|
// Later, we'll need to iterate over the keys of `counts1` to sum up the final value.
|
|
// You'd think you could do this with:
|
|
// ```
|
|
// const iter = counts1.keyIterator();
|
|
// while (try iter.next()) |key| {
|
|
// ...
|
|
// ```
|
|
// But that gives an error that the iterator is a const pointer but `while` requires a mutable pointer.
|
|
// So instead I preserve the encountered keys in this array so that we can then loop over them.
|
|
// Ridiculous language.
|
|
var preservedKeys = std.ArrayList(u32).init(allocator);
|
|
for (arr1) |elem| {
|
|
if (!counts1.contains(elem)) {
|
|
try preservedKeys.append(elem);
|
|
print("Added {} to the preservedKeys list\n", .{elem});
|
|
}
|
|
try counts1.put(elem, (counts1.get(elem) orelse 0) + 1);
|
|
|
|
}
|
|
for (arr2) |elem| {
|
|
try counts2.put(elem, (counts2.get(elem) orelse 0) + 1);
|
|
}
|
|
|
|
print("Beginning the calculation", .{});
|
|
var output: u32 = 0;
|
|
for (preservedKeys.items) |key| {
|
|
print("Operating on {} - at this point the output value is {}\n", .{key, output});
|
|
const count1 = (counts1.get(key) orelse 0);
|
|
const count2 = (counts2.get(key) orelse 0);
|
|
const increment = key * count1 * count2;
|
|
print("The increment for key {} is {} (calculated based on counts {} and {})\n", .{key, increment, count1, count2});
|
|
output += increment;
|
|
}
|
|
return output;
|
|
|
|
}
|
|
|
|
fn parseInputFileToArrays() !struct { first: []u32, second: []u32 } {
|
|
// Wow this _sucks_. But there's no way (that I know of?) to find the number of lines in a file without parsing it,
|
|
// and we can't parse it without having created the array already, so... :shrug
|
|
const isTestCase = true;
|
|
const lengthOfArray: usize = if (isTestCase) 6 else 1000;
|
|
var arr1: [lengthOfArray]u32 = undefined;
|
|
var arr2: [lengthOfArray]u32 = undefined;
|
|
|
|
// Reading the input...
|
|
// Making use of example here: https://cookbook.ziglang.cc/01-01-read-file-line-by-line.html
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const path = try util.getInputFile("01", isTestCase);
|
|
std.debug.print("Path is {s}\n", .{path});
|
|
const file = try std.fs.cwd().openFile(path, .{});
|
|
defer file.close();
|
|
|
|
// Wrap the file reader in a buffered reader.
|
|
// Since it's usually faster to read a bunch of bytes at once.
|
|
var buf_reader = std.io.bufferedReader(file.reader());
|
|
const reader = buf_reader.reader();
|
|
|
|
var line = std.ArrayList(u8).init(allocator);
|
|
defer line.deinit();
|
|
|
|
const writer = line.writer();
|
|
var line_no: usize = 0;
|
|
|
|
while (reader.streamUntilDelimiter(writer, '\n', null)) {
|
|
// Clear the line so we can reuse it.
|
|
defer line.clearRetainingCapacity();
|
|
line_no += 1;
|
|
|
|
print("{d}--{s}\n", .{ line_no, line.items });
|
|
|
|
const values = parseLineToNumbers(line.items);
|
|
arr1[line_no - 1] = values.first;
|
|
arr2[line_no - 1] = values.second;
|
|
} else |err| switch (err) {
|
|
error.EndOfStream => { // end of file
|
|
if (line.items.len > 0) {
|
|
line_no += 1;
|
|
print("{d}--{s}\n", .{ line_no, line.items });
|
|
}
|
|
},
|
|
else => return err, // Propagate error
|
|
}
|
|
return .{ .first = &arr1, .second = &arr2 };
|
|
}
|
|
|
|
fn parseLineToNumbers(line: []u8) struct { first: u32, second: u32 } {
|
|
var first: u32 = 0;
|
|
var second: u32 = 0;
|
|
var isInFirst = true;
|
|
for (line) |char| {
|
|
if (char == ' ') {
|
|
print("found a space\n", .{});
|
|
isInFirst = false;
|
|
continue;
|
|
}
|
|
if (isInFirst) {
|
|
print("first\n", .{});
|
|
first += (char - 48);
|
|
print("{}\n", .{first});
|
|
first *= 10;
|
|
print("{}\n", .{first});
|
|
} else {
|
|
print("second\n", .{});
|
|
second += (char - 48);
|
|
print("{}\n", .{second});
|
|
second *= 10;
|
|
print("{}\n", .{second});
|
|
}
|
|
}
|
|
return .{ .first = first / 10, .second = second / 10 };
|
|
}
|
|
|
|
const expect = std.testing.expect;
|
|
|
|
test "part one" {
|
|
try expect(try part_one() == 11);
|
|
}
|
|
|
|
test "part two" {
|
|
try expect(try part_two() == 31);
|
|
}
|