const std = @import("std");

const Str = []const u8;

const Md5 = std.crypto.hash.Md5;

pub fn first(allocator: std.mem.Allocator) !usize {
    _ = allocator;
    return first5Zero("yzbqklnj");
}

pub fn second(allocator: std.mem.Allocator) !usize {
    _ = allocator;
    return first6Zero("yzbqklnj");
}

fn first5Zero(comptime key: Str) !usize {
    var out: [Md5.digest_length]u8 = undefined;
    var buf: [16]u8 = undefined;

    var i: usize = 282000; // TODO: multithreading
    while (true) : (i += 1) {
        const input = try std.fmt.bufPrint(&buf, "{s}{d}", .{ key, i });
        Md5.hash(input, &out, .{});

        if (out[0] == 0 and out[1] == 0 and out[2] < 10) break;
    }

    return i;
}

fn first6Zero(comptime key: Str) !usize {
    var out: [Md5.digest_length]u8 = undefined;
    var buf: [16]u8 = undefined;

    var i: usize = 9960000; // TODO: multithreading
    while (true) : (i += 1) {
        const input = try std.fmt.bufPrint(&buf, "{s}{d}", .{ key, i });
        Md5.hash(input, &out, .{});

        if (out[0] == 0 and out[1] == 0 and out[2] == 0) break;
    }

    return i;
}

// test "Examples" {
//     try std.testing.expectEqual(@as(usize, 609043), try first5Zero("abcdef"));
//     try std.testing.expectEqual(@as(usize, 1048970), try first5Zero("pqrstuv"));
// }

test "day04a" {
    try std.testing.expectEqual(@as(usize, 282749), try first(std.testing.allocator));
}

test "day04b" {
    try std.testing.expectEqual(@as(usize, 9962624), try second(std.testing.allocator));
}