day 7 finished

This commit is contained in:
Moritz Gmeiner 2024-12-07 23:27:28 +01:00
commit f080dd64dd
10 changed files with 1866 additions and 153 deletions

View file

@ -1,27 +1,13 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const filename = "inputs/day1.txt";
pub fn main() !void { pub fn main() !void {
// // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
// std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
// // stdout is for the actual output of your application, for example if you
// // are implementing gzip, then only the compressed bytes should be sent to
// // stdout, not any debugging messages.
// const stdout_file = std.io.getStdOut().writer();
// var bw = std.io.bufferedWriter(stdout_file);
// const stdout = bw.writer();
// try stdout.print("Run `zig build test` to run the tests.\n", .{});
// try bw.flush(); // don't forget to flush!
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const filename = "inputs/day1.txt";
{ {
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
@ -158,20 +144,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 11); try std.testing.expectEqual(11, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day1.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 1506483); try std.testing.expectEqual(1506483, result);
} }
test "part2 example" { test "part2 example" {
@ -190,18 +174,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 31); try std.testing.expectEqual(31, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day1.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 23126924); try std.testing.expectEqual(23126924, result);
} }

View file

@ -1,6 +1,31 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const filename = "inputs/day2.txt";
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 2, part 1: {}\n", .{result});
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 2, part 2: {}\n", .{result});
}
}
fn Chain(comptime T: type) type { fn Chain(comptime T: type) type {
return struct { return struct {
@ -29,31 +54,6 @@ fn Chain(comptime T: type) type {
}; };
} }
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
const filename = "inputs/day2.txt";
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 2, part 1: {}\n", .{result});
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 2, part 2: {}\n", .{result});
}
}
fn check(report: []const i32) bool { fn check(report: []const i32) bool {
var window = std.mem.window(i32, report, 2, 1); var window = std.mem.window(i32, report, 2, 1);
@ -191,20 +191,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 2); try std.testing.expectEqual(2, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day2.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 549); try std.testing.expectEqual(549, result);
} }
test "part2 example" { test "part2 example" {
@ -223,18 +221,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 4); try std.testing.expectEqual(4, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day2.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 589); try std.testing.expectEqual(589, result);
} }

View file

@ -1,15 +1,15 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const isDigit = std.ascii.isDigit; const isDigit = std.ascii.isDigit;
const filename = "inputs/day3.txt";
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const filename = "inputs/day3.txt";
{ {
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
@ -240,20 +240,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 161); try std.testing.expectEqual(161, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day3.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 156388521); try std.testing.expectEqual(156388521, result);
} }
test "part2 example" { test "part2 example" {
@ -265,18 +263,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 48); try std.testing.expectEqual(48, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day3.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 75920122); try std.testing.expectEqual(75920122, result);
} }

View file

@ -1,13 +1,13 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const filename = "inputs/day4.txt";
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const filename = "inputs/day4.txt";
{ {
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
@ -183,20 +183,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 18); try std.testing.expectEqual(18, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day4.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 2593); try std.testing.expectEqual(2593, result);
} }
test "part2 example" { test "part2 example" {
@ -219,18 +217,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 9); try std.testing.expectEqual(9, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day4.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 1950); try std.testing.expectEqual(1950, result);
} }

View file

@ -1,15 +1,15 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const List = std.DoublyLinkedList(u8); const List = std.DoublyLinkedList(u8);
const filename = "inputs/day5.txt";
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const filename = "inputs/day5.txt";
{ {
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
@ -381,20 +381,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 143); try std.testing.expectEqual(143, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day5.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 5129); try std.testing.expectEqual(5129, result);
} }
test "part2 example" { test "part2 example" {
@ -435,18 +433,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 123); try std.testing.expectEqual(123, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day5.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 4077); try std.testing.expectEqual(4077, result);
} }

View file

@ -1,13 +1,13 @@
const std = @import("std"); const std = @import("std");
const utils = @import("utils.zig"); const utils = @import("lib/utils.zig");
const filename = "inputs/day6.txt";
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const filename = "inputs/day6.txt";
{ {
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
@ -403,20 +403,18 @@ test "part1 example" {
const result = try part1(alloc, stream.reader()); const result = try part1(alloc, stream.reader());
try std.testing.expect(result == 41); try std.testing.expectEqual(41, result);
} }
test "part1 input" { test "part1 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day6.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader()); const result = try part1(alloc, file_reader.reader());
try std.testing.expect(result == 4722); try std.testing.expectEqual(4722, result);
} }
test "part2 example" { test "part2 example" {
@ -439,18 +437,16 @@ test "part2 example" {
const result = try part2(alloc, stream.reader()); const result = try part2(alloc, stream.reader());
try std.testing.expect(result == 6); try std.testing.expectEqual(6, result);
} }
test "part2 input" { test "part2 input" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const filename = "inputs/day6.txt";
const file_reader = try utils.FileReader.init(alloc, filename); const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit(); defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader()); const result = try part2(alloc, file_reader.reader());
try std.testing.expect(result == 1602); try std.testing.expectEqual(1602, result);
} }

328
day7.zig Normal file
View file

@ -0,0 +1,328 @@
const std = @import("std");
const utils = @import("lib/utils.zig");
const spice = @import("lib/spice.zig");
const filename = "inputs/day7.txt";
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 7, part 1: {}\n", .{result});
}
{
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.io.getStdOut().writer().print("Day 7, part 1: {}\n", .{result});
}
}
fn checkEqnPlusMul(test_value: u64, cur_value: u64, eqn: []const u64) bool {
if (eqn.len == 0) {
return cur_value == test_value;
}
const x = eqn[0];
// +
{
const next_value = cur_value + x;
// if next_value > test_value: abort, can never get smaller
if (next_value <= test_value) {
if (checkEqnPlusMul(test_value, next_value, eqn[1..])) {
return true;
}
}
}
// *
{
const next_value = cur_value * x;
// if next_value > test_value: abort, can never get smaller
if (next_value <= test_value) {
if (checkEqnPlusMul(test_value, next_value, eqn[1..])) {
return true;
}
}
}
return false;
}
fn checkEqnPlusMulPar(
t: *spice.Task,
args: struct {
test_value: u64,
cur_value: u64,
eqn: []const u64,
},
) bool {
const test_value = args.test_value;
const cur_value = args.cur_value;
const eqn = args.eqn;
if (cur_value > test_value) {
return false;
}
if (eqn.len == 0) {
return cur_value == test_value;
}
const x = eqn[0];
var plus_fut = spice.Future(@TypeOf(args), bool).init();
// +
{
const next_value = cur_value + x;
// if next_value > test_value: abort, can never get smaller
plus_fut.fork(t, checkEqnPlusMulPar, .{
.test_value = test_value,
.cur_value = next_value,
.eqn = eqn[1..],
});
}
// *
{
const next_value = cur_value * x;
// if next_value > test_value: abort, can never get smaller
if (next_value <= test_value) {
if (t.call(bool, checkEqnPlusMulPar, .{ .test_value = test_value, .cur_value = next_value, .eqn = eqn[1..] })) {
_ = plus_fut.join(t);
return true;
}
}
}
if (plus_fut.join(t)) |b| {
return b;
} else {
const next_value = cur_value + x;
return t.call(bool, checkEqnPlusMulPar, .{ .test_value = test_value, .cur_value = next_value, .eqn = eqn[1..] });
}
}
fn checkEqnPlusMulConcat(test_value: u64, cur_value: u64, eqn: []u64) bool {
if (eqn.len == 0) {
return cur_value == test_value;
}
const x = eqn[0];
// +
{
const next_value = cur_value + x;
// if next_value > test_value: abort, can never get smaller
if (next_value <= test_value) {
if (checkEqnPlusMulConcat(test_value, next_value, eqn[1..])) {
return true;
}
}
}
if (cur_value == 0) {
// for first value only plus is posible
return false;
}
// *
{
const next_value = cur_value * x;
// if next_value > test_value: abort, can never get smaller
if (next_value <= test_value) {
if (checkEqnPlusMulConcat(test_value, next_value, eqn[1..])) {
return true;
}
}
}
// ||
{
const x_num_digits = utils.num_digits(x);
const shift = std.math.powi(u64, 10, x_num_digits) catch unreachable;
const new_value = shift * cur_value + x;
// check with concatted value
if (checkEqnPlusMulConcat(test_value, new_value, eqn[1..])) {
return true;
}
}
return false;
}
fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 {
var sum_calibration: u64 = 0;
// var thread_pool = spice.ThreadPool.init(alloc);
// defer thread_pool.deinit();
// thread_pool.start(.{});
var eqn = std.ArrayList(u64).init(alloc);
defer eqn.deinit();
var line_reader = utils.lineReader(alloc, reader);
defer line_reader.deinit();
while (try line_reader.next()) |line| {
const colon_idx = std.mem.indexOfScalar(u8, line, ':') orelse unreachable;
const test_value = try std.fmt.parseUnsigned(u64, line[0..colon_idx], 10);
eqn.clearRetainingCapacity();
var number_parser = utils.numberParser(u64, line[colon_idx + 1 ..]);
while (try number_parser.next()) |n| {
try eqn.append(n);
}
if (checkEqnPlusMul(test_value, 0, eqn.items)) {
sum_calibration += test_value;
}
// if (thread_pool.call(bool, checkEqnPlusMulPar, .{
// .test_value = test_value,
// .cur_value = 0,
// .eqn = eqn.items,
// })) {
// sum_calibration += test_value;
// }
}
return sum_calibration;
}
fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 {
var sum_calibration: u64 = 0;
// var thread_pool = spice.ThreadPool.init(alloc);
// defer thread_pool.deinit();
// thread_pool.start(.{});
var eqn = std.ArrayList(u64).init(alloc);
defer eqn.deinit();
var line_reader = utils.lineReader(alloc, reader);
defer line_reader.deinit();
while (try line_reader.next()) |line| {
const colon_idx = std.mem.indexOfScalar(u8, line, ':') orelse unreachable;
const test_value = try std.fmt.parseUnsigned(u64, line[0..colon_idx], 10);
eqn.clearRetainingCapacity();
var number_parser = utils.numberParser(u64, line[colon_idx + 1 ..]);
while (try number_parser.next()) |n| {
try eqn.append(n);
}
if (checkEqnPlusMulConcat(test_value, 0, eqn.items)) {
sum_calibration += test_value;
}
// if (thread_pool.call(bool, checkEqnPar, .{
// .test_value = test_value,
// .cur_value = 0,
// .eqn = eqn.items,
// })) {
// sum_calibration += test_value;
// }
}
return sum_calibration;
}
test "part1 example" {
const alloc = std.testing.allocator;
const example =
\\190: 10 19
\\3267: 81 40 27
\\83: 17 5
\\156: 15 6
\\7290: 6 8 6 15
\\161011: 16 10 13
\\192: 17 8 14
\\21037: 9 7 18 13
\\292: 11 6 16 20
;
var stream = std.io.fixedBufferStream(example);
const result = try part1(alloc, stream.reader());
try std.testing.expectEqual(3749, result);
}
test "part1 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part1(alloc, file_reader.reader());
try std.testing.expectEqual(1985268524462, result);
}
test "part2 example" {
const alloc = std.testing.allocator;
const example =
\\190: 10 19
\\3267: 81 40 27
\\83: 17 5
\\156: 15 6
\\7290: 6 8 6 15
\\161011: 16 10 13
\\192: 17 8 14
\\21037: 9 7 18 13
\\292: 11 6 16 20
;
var stream = std.io.fixedBufferStream(example);
const result = try part2(alloc, stream.reader());
try std.testing.expectEqual(11387, result);
}
test "part2 input" {
const alloc = std.testing.allocator;
const file_reader = try utils.FileReader.init(alloc, filename);
defer file_reader.deinit();
const result = try part2(alloc, file_reader.reader());
try std.testing.expectEqual(150077710195188, result);
}

850
inputs/day7.txt Normal file
View file

@ -0,0 +1,850 @@
202998336: 686 9 7 62 2 673
19275222: 361 3 7 170 65 5 223
23101: 7 694 916 4 6
2042426: 6 34 2 423 3
40369523: 8 880 91 45 23
46629044796: 990 471 4 4 796
1839056: 3 42 2 4 3 258 703 4 8
26205: 2 9 5 9 9 4 3 7 44 5 8 7
2507629: 5 492 8 162 606
8643264: 5 1 5 7 8 8 1 33 654 1 59
2379228282: 264 3 5 7 5 7 39 457 2 9
2088: 8 6 6 7 234 705 2 6 8 14
28852: 70 7 688 31 36 3 193
6573214: 8 1 1 140 17 9 62 1 9 1 8
85235071: 3 4 67 6 17 6 58 7 3 6 4 6
11017177972: 683 68 3 489 797 4
7514454: 97 102 5 759 794 319
1562403920: 9 34 19 61 798 880
2094: 9 641 896 70 145 333
26449589: 25 50 2 92 9 592
150: 37 66 47
565228: 1 930 13 389 703 3 2
23863160: 321 4 28 95 662 46
43766400721: 49 1 97 94 96 699 23
10539180731: 48 15 1 1 89 7 3 869 9 2
3677868: 349 510 475 627 9
943524: 3 968 8 162 2 8 8 3 4 5 8
5676251693: 6 5 4 7 3 289 2 5 9 6 7 91
17516: 596 8 29
3266737179349: 3 4 5 7 7 413 42 5 9 7 7 7
220516272: 6 7 291 66 51 5 6 278 6
440606: 72 534 4 2 2 6 2 1 448 3
10529: 94 589 422 392 2 7 36
385881484801: 4 4 1 714 52 7 1 7 5 6 8 1
1756977: 9 48 43 148 3 991 489
26908592588: 1 6 336 5 8 277 92 588
11384071922: 78 82 3 2 3 5 57 52 6 2
5154: 2 44 55 414 9 45 3 93
1204800178: 16 495 9 10 1 6 6 73
4028289466: 62 53 52 733 919 6
855848: 67 456 8 3 3 28
5577: 52 567 9 4 2
1256906: 84 54 33 276 2
1489: 16 9 2 50 1 7 7 7 4 1 8
274139674: 411 9 2 87 524 324 3 2
7949: 890 3 2 5 7 2 8 9 663 1 5
960143: 1 42 2 876 14 3
661517824: 82 59 36 657 222 4
3639348: 6 10 655 6 4 6
100625: 26 1 5 982 44
89266577886: 7 1 5 119 92 73 7 7 886
135143059: 30 369 5 7 61 5 2 2 61
4818: 4 7 332 151 5 9
241986: 9 320 84 6 60
199082010: 2 47 831 3 50 98 849
116986: 556 607 79 9 597
4593051202: 42 7 333 466 732 7 8 6
43262715695: 1 5 2 7 7 1 6 674 1 6 6 94
3672789: 4 3 3 72 787
66241: 46 80 9 2 1
6558190292: 3 127 25 975 4 7 8 26 4
4130: 34 5 104 5 8 61
60867948960: 9 698 546 73 4 6 90
25113978462: 7 9 1 889 5 387 40 6 2
26892: 7 3 320 503 3 9 5
546749: 719 431 19 9 9 5 8 5 1 8
20796444564: 28 763 462 7 43 1 83 7
8249008814046: 6 19 6 931 39 913 67
8730429939: 8 86 587 39 15 923
736622586: 9 627 148 9 98 9
1885860: 9 2 93 72 5 58 60
1164746124: 25 668 719 392 97
41946: 771 1 9 43 6
38992: 261 68 2 97 91 44
1490: 251 7 3 710 6
98520531: 82 4 4 30 531
346525: 3 1 6 5 266 8 1 4 4 95 7 4
194065650: 508 23 423 36 24 9 9
136649: 1 1 7 3 8 9 41 9 983 9 3
1722816: 7 6 41 53 3 286
15476735: 552 664 7 502 34 4
979: 112 1 8 83
8467523846: 9 5 80 139 6 1 32 384 6
13136: 6 1 25 19 703 6 552
459: 6 8 37 1 9
239967904: 7 3 35 8 4 9 8 5 915 6 6 4
74099204: 6 8 2 43 2 8 49 6 5 204
34621719552: 90 4 6 4 291 1 40 8 24
5859557: 731 769 7 437 9 3 554
208505: 5 5 5 9 7 70 875 7 3 7 1
3571995333: 5 79 1 3 1 7 9 976 6 6 2
492132522671: 9 6 9 627 8 9 14 1 9 7 1
21120: 263 1 1 4 1 20
1123912968: 3 3 69 5 5 3 856 56 970
733: 8 85 51
8626131164: 2 167 805 73 1 508 80
7560: 1 725 3 2 305
7897807144: 9 9 927 46 964
183024: 6 2 9 5 4 9 7 661 1 3 94 6
62652233: 4 4 8 45 2 114 69 2 3 2
674752: 84 382 3 7 904
1422576199: 111 89 96 15 6 14 1
2653352: 64 78 110 6 52
403447: 168 1 6 4 7 2
53254: 44 50 1 53 6 926 3 9 1 1
151146: 9 9 70 1 148
9608: 2 54 7 2 6 3 796 2 8
21517536688: 16 4 5 3 8 572 64 67
3153752680452: 362 968 9 9 5 8 10 451
156088: 18 33 3 4 7 1 3 915 5 6
5377610: 97 20 56 888 1 21 35
31498040: 9 6 41 40 511
44: 2 6 30
371000: 873 982 4 5 10
578833633: 46 9 68 474 26
737: 23 459 255
242901094: 954 279 394 5 91 6
88101685: 122 9 63 263 6 4 80 85
11910: 117 70 7 5 128
987499: 986 79 6 705
8198251: 513 7 9 9 465 5 5 5 2 1 1
240310: 184 257 4 54 9
1836: 81 3 2 2 154
208356000: 5 2 756 45 53 17
5915: 8 6 496 9 3
85269: 52 5 499 93 51 18
276885499: 2 57 759 10 7 320 59
237728: 2 9 40 330 50 74 4
507805531: 49 9 57 115 32
221498: 2 3 8 23 7 9 1 5 6 9 8 314
355426819: 1 9 87 127 17 989 1
2390217: 1 5 11 2 596 772 214
418797031680: 669 16 64 886 599 2 9
66941210: 81 77 62 9 754 8 6 7 5 9
337036: 3 7 6 7 2 876 6 5 91 9 8 4
2126043264689: 7 13 46 66 8 44 645 4 7
2907365426: 3 43 7 467 97 66 50 48
31632440: 12 5 7 12 32 6 49 6 8 70
57833804: 57 8 33 748 58
175237918: 8 5 74 759 78
2783547851: 927 849 300 85 1
3910: 3 4 34 472 4
11393205: 2 5 3 9 86 1 200 535 3
2231811: 22 2 30 1 86 67 4 2 38
17687226: 6 9 4 6 8 1 3 79 1 2 24 2
4499404: 3 179 828 4 53 62 870
123758270900: 35 414 667 7 8 9 98 7 7
3434132: 38 3 54 7 56 621 35 2
40501693: 335 314 9 385 22 47 9
7674373: 8 351 18 917
24284940297: 562 46 8 789 791 8 9
115679880304: 1 9 3 14 3 2 345 6 295 8
218221558806: 65 57 6 180 71 840 7 3
24591: 408 1 5 751 5 1 21
30623434865: 306 23 434 838 30
6294644: 6 1 898 4 767 7 1 146
2228797860179: 32 277 802 21 19 41
98285947: 30 2 8 4 626 5 2 5 4 47
435023: 4 6 87 5 23
129850: 76 611 21 9 7
47235677511: 456 243 284 19 79 39
40216: 73 4 165 1 88
18603003: 7 9 6 57 7 6 1 7 90 39 3
872120: 34 53 20 13 98 6 1
4020580: 67 60 579 1
182694: 354 86 6 16 6 8
150106: 3 1 2 47 943 36 8 3 37 1
686: 7 629 19 32 1
97081: 3 815 118 82 408 67
296221: 28 32 24 321 901
176077987645: 3 27 96 7 3 8 611 764 4
107532: 45 8 766 2
55757: 11 86 678 70 549 40
9039605: 8 9 75 62 27 5
282175677057: 8 54 7 5 345 55 27 7 57
9939764518: 3 657 5 5 4 1 6 9 5 90 28
1939: 14 9 44 2 1 9
891: 5 2 25 4 67 30 7 87
5228370: 11 816 945 527 1 2
1266493205255: 9 7 8 5 4 905 68 7 525 5
25811913741: 323 7 886 8 9 3 742
70475: 5 80 5 4 628 5 6 6 599
29123378297: 8 6 39 416 6 7 4 9 1 2 9 7
650969606906: 6 8 6 3 7 3 4 8 40 4 6 908
146078: 9 2 5 885 3 5 3 28 8 8 1
2317385: 1 8 58 297 2 7 907
52750080: 6 46 69 6 3 15 964
159122940: 1 3 3 877 6 6 7 2 3 3 3 10
4471177: 5 7 47 9 8 5 31 6 3 1 64 9
46037791744049: 7 3 4 4 7 5 856 7 8 8 5 49
268: 72 102 94
24079158: 297 81 1 96 124 56
4147216379: 1 6 91 54 20 6 1 637 9
777: 67 6 6 26 6 1 7
768: 3 6 54 81 5
129978225: 166 783 13 6 88
643495198: 957 58 1 672 768
138381912: 94 9 172 951
3688: 5 2 5 44 6 5 65 385 63 2
14645582788: 648 70 3 73 783 4 9 8 7
157329: 10 81 6 72 3 4 8 59
7951406334: 5 1 471 65 92 2 3 3 189
107495: 7 2 8 59 7 1 53 8 2 2 415
14926586120: 4 799 54 736 597 70 7
3124665: 3 22 92 25 5 5 789 225
21191543290: 3 1 4 13 187 4 543 2 90
83781: 467 133 53 870 55 16
113: 21 93 2
44840144656: 8 4 631 355 2 2 5 4 6 5 6
48334308375: 128 890 628 861 75 5
28630176326: 168 81 212 8 329
1320869: 77 1 5 273 59 95
105265149167: 6 76 837 394 6 7 73 67
28557568: 911 4 2 75 5 619
2638: 5 3 3 3 891 2 6 478 97 5
46350: 9 51 1 86 702 8 3 3 4 8 6
383184551514: 957 96 4 55 151 4
5761960680: 55 66 3 73 16 23 99
368069184: 959 2 5 3 7 63 8 18 2
154813: 8 7 4 81 3
381615: 168 74 273 247 3
206: 26 89 3 8 80
231063956: 17 32 39 56 424 515 6
101024: 37 3 3 6 194 4 46 1 8
1795890: 35 9 5 8 93
81653384: 3 8 9 1 5 3 203 573 2 6 4
11547: 4 5 902 8 238 4 55 67 9
225768477: 225 768 35 8 4 45
84909: 93 6 6 68 9
24130695391439: 301 6 33 69 23 929 8 4
113841727942: 58 9 7 5 8 5 920 6 79 4 1
3314971539159: 36 4 688 666 4 7 2 478
595217: 29 9 7 5 692 1 8 9 1 77 7
28690096: 286 8 5 4 46 68 80
268249686: 8 79 8 419 89 4 2 84 4 1
2661776: 1 864 7 3 3 637 75 1 2
1276948233: 3 9 9 8 37 8 35 5 46 7 6
35844531: 56 2 6 103 527 6
1230: 5 88 92 641 57
56540407: 941 620 3 7 709 6 6 2 6
8137451: 511 1 49 6 9 2 9 9 5 9 3 7
116578459: 4 3 66 1 6 7 9 8 57 8 5 6
2794315200: 465 890 8 844
141268491349: 2 2 42 351 6 63 349
2302034228: 7 9 9 8 123 2 6 723 7 1 6
90320: 52 9 1 7 7 2 1 157 6 5 2 8
48029662: 79 6 6 296 63
7338712919: 8 103 363 5 701 7
1443480: 31 8 5 366 796 8 20
1015: 8 7 75 875 9
863938: 4 296 9 81 799
78309810: 892 81 1 1 804 2 10
496872015: 8 67 6 34 9 15 480 7 6
7196630: 702 6 317 5 98
930917: 849 9 66 6 917
221828636962: 1 26 60 436 7 1 56 205
3139541674: 1 1 24 2 50 5 7 6 6 68 4 1
928052161: 6 7 2 3 77 12 304 80 4 3
72810234647: 954 3 8 3 4 795 92 555
36576: 53 98 23 591 9
927127: 98 192 9 96 49
489060: 91 9 870 8 7 5 494
17048360: 434 16 35 1 499 8 70
384912: 4 1 135 38 713 4 9 6 2
7850: 5 5 6 39 5 2 7 4 11 2 61
253774361: 6 4 4 26 5 1 3 1 2 9 5 232
1411644490: 361 7 8 43 674 571 9 9
1599457863: 2 2 2 4 713 66 8 39 55 1
4206768246: 99 6 915 6 15 86 4 842
1582870: 718 8 701 1 49 720 5
8806706: 8 806 703
1412172: 86 422 76 93 26 60
67578114: 7 8 50 877 602
3674034: 2 1 5 171 22 471 1 5 39
576686: 718 802 782 67 1
554: 66 2 77 301 44
92371379217: 481 1 64 2 3 11 916 5 7
50328100: 9 1 699 72 1 100
1540: 55 526 77 88 794
2476840: 6 370 6 96 2 6 215 10 4
19926668: 4 1 7 8 9 6 8 4 2 335 6 7
2933411: 412 7 49 411
1573227346: 414 38 273 4 6
6555363048717: 755 87 1 998 4 8 7 18
2702927611: 73 370 8 184 761 1
43118460: 2 66 8 167 9 6 17 396
48497062: 68 29 7 3 7 77 92
2405: 4 2 8 15 692 753
2645592520: 453 73 80 72 520
671: 27 7 7 560 70
2679289: 147 25 81 9 214
1283872: 987 466 21 40 848
4734270: 983 962 3 9 8 809
6654: 1 1 8 552 387 7 5 1 1 3 9
1568488608045: 5 56 508 925 9 8 76 4 6
70638: 86 82 2 81 38
14396387239: 65 3 423 650 6 1 7 77
22329792: 309 54 944 2 8 6 9 7 1 5
2579656788: 222 29 11 4 5 65 2 88
851088: 7 2 4 2 8 113 5 9 6 6 168
94420605041: 55 429 6 4 1 1 512 6 4 3
67471145910: 5 4 88 838 6 3 3 9 70 9 3
16791: 1 248 7 54 2 9 8 555 37
1920: 99 10 27 247 5 2 90 4
9568569: 9 2 6 3 40 6 44 5 5 24 2 9
36767: 7 32 940 23 86
834606: 6 9 7 5 6 76 7 3 2 1 1 82
17399: 7 80 5 57 8 818 6 2 9 6 8
35160: 1 4 3 2 66 50
585688: 8 5 49 8 721 716
519858640: 78 49 49 2 2 340
27192883: 716 414 752 17 4 2 4 4
43200: 4 72 3 5 10
1740811060: 77 4 9 54 8 6 9 86 685 7
31026337: 3 912 6 6 5 63 2 4 57 34
47785521971: 2 7 1 1 3 3 6 971 51 9 7 1
12417265: 166 9 9 83 8 951 5
299266: 81 218 266
92943601032: 1 3 793 301 876 10 3 2
16349044: 87 87 9 24 4
262128077: 392 65 660 8 64 807 7
395845632808: 3 9 6 80 708 8 8 6 4 3 3 1
766841180: 16 5 28 535 71 412
20781318: 52 2 89 92 1 47 127 1
610516424: 76 30 68 5 7 52 23
165004: 8 55 4 45 6 9 8 9 6 8 5 7
30785: 8 9 76 48 886 7 4 7 9
3876026: 9 310 3 7 6 3 448 11
5362570: 5 1 72 392 38 5 5
597908736: 79 885 389 96 1 8 552
751: 437 305 9
667708: 14 4 1 486 70 7
353620: 9 4 6 1 736 5 4 5 3 1 8 4
21857: 25 2 671 31 175 44
1796: 998 427 57 189 121 4
45922959: 8 2 8 3 2 82 29 5 9
74810230: 31 1 89 5 76 69 914 5
9490: 543 89 3 2 5
77987894: 33 95 85 4 112 2 6 8 6 1
4539565074: 45 5 999 153 15 9 4 66
1420: 772 6 642
150702760: 835 8 752 49 5 3 2 490
8624: 53 3 9 15 86
20348212: 400 956 183 82 76
7566099: 60 479 360 52 8 7 4 5 4
3616: 3 1 452 2
28891452: 47 4 7 6 97 166 51 5 2
4880483527: 921 241 42 83 527
111996454926: 51 244 9 45 49 26
5501454: 1 2 7 3 3 8 8 9 832 4 5 6
34445161: 5 702 6 6 3 962 5 76 2 9
7640879724: 374 960 700 59 5 9 4 9
147568898: 2 4 5 817 6 786 8 9 7 2
3669375: 68 92 8 6 69 285
106487172: 7 857 7 9 4 197 2
3065304: 5 956 7 861 47 4 9 408
4766: 71 67 9
9144170: 3 8 9 62 2 7 2 8 1 45 22 1
839951: 5 1 834 9 53
53279425: 5 204 9 5 77 32 9 427
491703729: 8 36 453 28 9 307 7 4 9
872197928: 87 219 79 26
2217066984: 510 39 5 905 2 66 67
2137366: 712 45 3 9 6
183453566112: 9 98 7 7 76 1 2 6 4 228
25672: 76 4 40 9 8
3618384193940: 6 5 2 6 3 2 6 77 193 940
118692: 61 5 9 80 1 42
27433643760: 34 7 1 637 53 27 80
1561862: 86 68 3 89 436 10
900002589: 275 8 1 48 993 403 18
43626629648: 28 310 4 94 4 5 865 89
712428118: 93 93 69 5 8 477
236: 57 3 4 165 7
3932199: 8 7 512 512 39
63295056: 253 3 5 9 744 66
1689911423: 731 561 1 33 755 7
84309165951: 1 2 2 820 35 5 3 3 17 8 6
893906: 7 886 906
1479002477515: 7 488 5 3 1 5 79 2 7 5 16
973833: 512 8 840 716 73
10984: 964 18 1 574 9 4 1 7 1 1
36944182898: 91 53 766 289 8
29065149: 5 278 6 1 9 7 514 7
25994: 24 5 14 90 4
12914: 7 7 1 9 22 8 8 39 59 937
4972030: 7 7 7 10 8 11 3 4 210 1
273: 61 1 203 9
1127354776: 9 3 3 69 3 9 4 191 5 5 1
10827018: 488 7 66 34 8 583 6 1
28851024: 5 1 8 6 1 536 8 75 95 8
71789911205: 384 744 24 909 70 3
9855228: 4 819 3 87 75 3
60066: 592 30 33 80 723
498922280980: 8 8 9 59 81 3 1 9 355 4
33479: 87 374 645 245 51
7535428: 8 93 82 1 74 8
251418855: 50 40 348 82 979 7
1562449263: 9 3 77 9 74 579 6 3 1 6 4
136328861841: 932 57 8 140 611 27 3
622670265: 6 22 669 423 842
343372980: 3 4 33 727 5 4 226
12075: 4 5 5 4 5 5 560 881 8 6 7
5273: 45 3 8 3 43 6 17 50 900
354636568946: 36 3 92 4 33 927 6 84
44385: 19 151 9 2 9 5 71 8 7 1
449351: 826 544 1 3 4
519429760: 5 186 8 313 40
8765956: 8 6 63 84 7 2 4 4 1 464 8
10725630: 1 968 109 6 5
5036405220: 122 483 222 1 385
148523: 3 91 544 4 7
35013810659: 1 269 376 8 7 5 3 5 7 37
52456: 4 2 2 83 79
833001: 201 649 20 49 1
2171283408: 3 7 721 6 576 4 7 9 548
4376744812: 166 95 944 7 294 36
7481335851: 1 8 58 85 6 57 843
1439812587: 4 9 9 113 94 3 2 9 4 6 87
60063: 7 48 2 3 7 44 7 3 2 5 17 5
13923392048: 889 3 3 8 178 41 47 4 2
112200: 520 65 512 3 3 34
229593: 1 5 7 8 3 7 1 404 8 1 8 49
190450250: 283 7 241 545 5
1065: 35 61 1 9 6
36085: 1 5 6 4 5 60 2 83
758640: 758 59 4 43 3
305235895: 5 70 30 9 646 82 5 92 5
59688300: 7 822 9 8 300
513: 2 24 9 3 78
1518480634835: 14 3 5 5 296 1 6 3 4 835
458907: 6 15 69 4 8 7 70 8 5 6 7 4
408368283: 5 986 5 41 78 80 404
767592280269: 9 14 81 28 418 714 5 9
67611: 557 5 8 8 5 9 5 3 1 2 4 3
7253: 8 591 84 9 309 99 6
413869399: 67 1 1 8 76 2 6 68 2 715
669060: 2 159 32 77 354 7
879555: 66 622 1 9 28 1 9 6 5
144368332: 7 277 398 4 532 99 61
242361: 61 33 37 185 9
44185596: 1 85 6 91 941
610557: 6 10 5 5 7
8282952: 82 810 82 924 945
160: 7 9 7 9 5
1598192649402: 9 9 9 1 1 8 352 4 6 9 402
4295716554: 737 64 562 9 8 8 554
2185953: 4 7 4 5 3 312 8 69 1 7 1 5
5654: 33 28 6 1 1 108
684624416: 7 978 24 41 6
994: 17 5 973
69: 4 33 32
39140230551: 3 83 3 99 926 230 550
42835632: 2 2 517 7 2 734 658 8
346661286: 86 88 2 62 510 34 84 6
287042: 410 7 42
226835: 614 3 61 9 6 4 870 85
5101839: 48 8 2 1 123 3 6 33 36 9
12050106: 8 896 4 112 5 371 9 3 6
13072980827: 20 555 636 82 5
231990528: 9 610 9 5 845
44838434116: 9 762 92 939 519 15
226162860: 9 1 62 614 53 2 4 4 3 55
266400: 55 87 2 7 5 21 5 6 4 2 25
5427: 35 54 979 3 5 72
639: 58 77 7 3 213
1319509681006: 8 9 5 2 65 69 150 1 7 2 3
9116169: 3 5 5 871 314 43 260 2
448624: 816 1 6 34 167 1 88
11470372879: 532 4 3 27 38 8 8 697 7
222553: 8 9 1 5 403 4 9 5 6 2 9 2
13363961: 5 93 905 403 5 5 6 2 3 6
1130380: 3 374 4 428 297
1638669965109: 7 9 6 9 762 6 23 3 4 3 5 9
94144023787: 994 12 9 947 74 90
14050266: 47 550 68 5 542
56385450: 15 8 87 9 2 4 67 66 5 5 9
2591: 5 690 243 658 7 7 981
11335843: 56 1 4 950 9 6 939 7 43
339499: 484 89 7 76 1
50590800: 32 9 28 697 582 1 8 2 9
88685710: 6 795 677 5 5 2 24 9 10
1332298351: 331 409 8 5 402
5216655510: 9 2 9 842 1 93 1 59 946
1659853: 6 952 8 34 9 288 36 73
221772: 2 63 4 152 775
40561164: 3 3 5 7 3 4 8 526 1 14 2 9
23568090: 373 5 4 5 26 97
13154240: 221 1 16 488 44 74
101844827954: 193 811 69 8 943
569733777404: 6 56 603 19 55 148 1 2
5009649: 55 7 277 941 6
6099: 659 9 7 7 539 41 794
4940100131: 3 18 8 2 7 900 134
281957: 48 1 89 6 11 5
732142: 639 36 541 8 888 2
2602791942: 9 7 1 48 111 64 449 2 4
56636717: 566 123 199 45 17
970: 1 870 5 7 87 1
141997: 33 43 23 5 70
14103: 4 3 42 9 3 495
1380: 631 90 388 178 88 5
121458: 5 42 4 27 3 704 6 2 5 1
10563: 12 88 3
12076236: 6 71 2 23 87 3 9
407866358: 71 55 55 57 4 6
220520: 1 575 17 4 74 5
1136: 4 50 76 8 4
47253: 5 39 45 61 5 63 4
217056: 1 407 19 4 7
221152: 59 9 4 87 5 2
13785920220: 4 30 4 6 2 498 4 8 6 890
131874141126: 4 5 8 4 471 664 9 6 689
111356265089: 344 83 888 2 4 39 955
50790109540: 9 8 7 1 6 7 7 6 63 8 905
1832220: 28 39 4 1 6 1 9 90 3 78
12894792: 1 739 15 40 2 646 29
1953231: 496 6 8 82 975
27468: 14 6 405 29 1 136 42
207510: 190 2 543 746 424
7901694458: 7 90 1 694 458
14462447489: 469 9 81 47 39 9 55 34
466: 4 453 9
15487: 52 1 105 98 3
4016027: 5 93 959 290 9 1 1
575580136: 431 65 763 1 20 1 36
62390487: 6 1 37 8 35 4 4 4 4 5 8 9
5931562406: 8 1 443 8 3 548 8 2 40 7
2919799584: 327 412 904 39 552
10347578: 470 32 220 520 15
111597016: 6 2 34 757 5 9 78 2 69 5
55745928000: 36 490 46 100 687
35306328: 52 82 92 15 24 44 6
927133957: 95 9 7 66 966
1407447: 4 81 10 72 164
54563: 2 770 7 1 70 33
25347499: 633 63 5 4 2 93 3
15336: 8 6 41 160 5 8 955 2 9
1494711: 9 68 9 1 7 707 678 29 5
83914: 2 68 5 5 3 4 12 5 97
15581702: 357 219 1 1 3 2 5 6 15 4
691560: 549 2 32 1 612
5331867755: 532 2 9 86 7 752
87270: 83 4 1 91 79
4113216: 662 2 8 4 772
92741696518: 6 2 888 23 62 8 473 12
1052128262: 1 583 5 15 3 3 7 3 18
1353338: 33 22 7 400 4 431
415568: 66 320 5 804 152
6675614238: 11 12 60 23 6 8 35 4 3
9944957: 496 63 55 2 8 70 57 5
41348626: 6 2 17 87 1 4 2 87 6 1 94
893: 6 42 25 3 308 299 6
87913: 61 45 16 2 70 3
7169002681: 9 112 889 885 4
9502: 6 931 8 6 46
1343859: 2 669 5 774 85
90053666: 4 2 4 5 72 3 7 3 163 6 5
177: 1 54 41 8 74
169136269008: 7 302 20 1 2 2 3 4 25 4 8
32232: 3 2 22 8 6
14682648: 2 7 821 3 296 4 3 4 13 6
1048320: 287 25 8 35 6 2
218531768000: 518 5 65 20 854 76
27683792: 3 87 15 837 92
58809392926: 6 4 5 4 2 99 376 99 2 9
211079278502: 69 8 7 54 5 5 4 9 8 5 4 5
13246598290: 8 6 2 3 9 4 7 591 7 9 5 74
9376859: 9 376 831 1 27
2082539: 243 5 7 5 3 8 7 5 8 1 7 5
1400284: 89 5 7 2 4 40 9 927 2 7 4
226424872: 7 6 9 26 6 85 6 2 6 4 875
339176384270: 2 9 8 43 13 9 1 3 9 2 272
6516205: 3 8 21 6 57 5 7 8 43 7 3 7
6108: 9 9 1 9 5 5 50 94 1 964
153260: 2 8 3 5 343 8 7 8 2 27 4 2
12245: 61 66 95 56 124
42014: 670 5 297 6 2
816034509: 815 937 9 966 11
86314032: 40 62 322 73 36
34387080: 928 6 6 3 26 469
817: 8 34 9 3 580 90 93
168614531: 2 58 51 5 35 3 5 83 31
27105507: 7 360 9 7 6 779 4 83 24
8887196118: 759 510 5 9 94 4 1 4 1 7
1702560: 39 6 92 366 5 1 87 5 96
15546951434: 7 44 8 8 79 59 5 1 7 8 8 1
10416: 2 8 31 233 9 6 14
10610: 37 16 3 6 340 5
159259407: 1 284 41 558 3 626
1619: 30 9 4 357 989
4113024: 6 62 60 99 76 6 4 4
15861472119: 99 4 5 327 41 4 11 9
21606958622: 3 8 7 9 6 3 7 9 17 6 9 780
13349890272: 824 9 6 1 3 6 5 5 27 2
385229634: 3 70 68 69 4 77 33 514
99572054: 7 6 7 682 73 5 4
4296541: 41 40 717 5 607 5 5 41
83272: 4 297 290 9 56
139128713: 9 737 1 49 428 465
4028: 85 84 192 9 779
35364005: 5 8 37 4 1 810 7 9 7 1 9
4871348: 9 1 291 31 60 8
6377210: 899 22 8 5 6 1 7 322
920026739491: 888 7 505 3 5 7 1 41 80
664692813: 2 3 36 9 8 78 9 820 657
448787603: 448 787 603
2948: 862 74 3 51 89
2861689: 51 63 6 919 7 47
55: 33 3 3 9 7
277382607: 5 658 42 843 201
857871047: 324 445 850 7 47
66: 8 3 42
8829610: 6 9 317 3 1 3 543 96 10
98780: 89 4 92 85 95
1218692839027: 25 336 64 946 481 1
333850065: 370 9 1 4 4 28 9 6 9 6 73
5913270274: 49 674 7 9 2 95 5 3 9 1 4
1058945504413: 3 961 599 28 219 11
70200: 5 195 351
6313662: 4 23 8 66 4 956 71 164
7737319: 773 7 20 6 9 50
8890876: 7 7 5 6 42 9 2 16 23 8 4
702376: 557 17 128 375 1
1703: 727 17 1 8 948
764: 55 8 8 5 4
12838: 76 9 654 3 9 4
3017019296: 18 74 32 4 817 7 9 6 3 6
315: 10 59 246
7426403: 167 549 9 9 41 39
65918934: 9 965 15 441 456
1989: 3 6 20 917 884 9
14938235506: 2 67 9 4 3 7 6 5 3 9 8 503
2401280: 32 5 7 536 4
25048829: 5 5 48 7 2 1 5 59 81 7 2 9
34211: 965 39 2 34 5
2097: 71 63 3 12 960 43 680
4738230: 4 615 3 54 8 19 7
505325940064: 109 6 62 926 86 824
397: 320 1 77
24283672: 6 508 738 98 64 91 61
201570694: 411 3 5 7 7 2 7 7 901 2 7
262710: 6 8 59 5 599 9 565 5 6 7
234532856: 781 5 2 759 7 3 6 1 56 2
12183: 8 2 61 60 93
2853090: 6 2 4 51 9 31 9 7 305 2 9
12424702: 303 5 94 9 858 4
72081: 40 66 85 8
797: 368 421 8
7150889088: 247 718 3 3 4 64 9 56 8
408693600: 1 41 8 850 159 9
631864944414: 1 990 7 63 2 72 417
3156310699: 5 7 7 370 7 7 9 3 25 4 29
51034085: 30 1 17 340 85
180215165: 6 462 541 65
13656279: 13 650 6 56 79
76570107: 924 88 933 931 12
5959123: 5 68 5 1 175 198
172434636: 492 5 59 7 22 6 3 3
8263207829: 32 9 3 153 63 676 2 29
563920: 2 56 6 317 848
217867162: 2 1 893 8 8 5 1 8 5 759 7
88528440000: 874 1 52 67 60 484
276057738675: 4 7 4 3 9 2 698 7 4 75 9
455996860826: 8 288 39 246 2 55 826
97900: 90 871 7 84 4 821 56 8
1170566: 9 8 7 1 2 7 4 49 2 61 7
1071: 9 5 813 157 6
1295167590: 998 312 112 77 9 6 9
9425843: 533 8 131 133
948765321: 8 16 4 5 9 59 6 96 2 1 3 3
3773573: 94 33 4 3 71
7654134670: 838 9 5 6 467 10 217
3413788616: 254 336 4 2 8 616
2560: 54 37 470 58 34
16702: 6 3 1 9 8 7 2 8 3 7 4
310762155391: 5 639 7 1 8 334 9 6 52
3850516: 916 20 7 413 6
6686193958: 5 8 8 861 93 958
10864165: 1 782 365 3 2 99 38 3
796: 6 25 7 2 5 4
502: 44 3 9 8 54
4282: 8 478 442 9 7
14487: 5 4 61 228 5
1296: 3 56 747 338 43
5715: 83 1 6 35 9 786 1 6 70 1
14601634503: 5 9 6 3 40 43 8 6 3 7 2 5
17286066: 87 74 895 3 1 35
1055582489: 561 48 188 79 8
95925385: 958 6 4 60 970 416
22657320: 8 66 420 9 81
1039197692: 924 89 26 197 692
217322: 97 96 8 614 15 23
872: 1 7 72
66985792: 8 1 3 70 9 79 7 4 8 597 3
17112383: 8 9 83 3 3 7 5 81 5 8 1 64
6521202: 4 666 9 2 8 959
1835974: 25 41 6 6 683 96 6
35551710: 5 136 7 6 4 95 3 5 6 9 27
877173: 4 4 91 268 1 48 33
838145: 9 57 5 4 837 6 3 6 7 7 5
18944778: 17 621 7 707 6 6
44370833: 80 7 510 83 4
162305004887: 316 1 9 8 157 4 16 84 6
2136101: 76 7 3 304 820
1142: 6 18 85 34 8
10018: 909 84 3 86
4653: 6 31 680 275 4 89
9748: 7 5 72 98 88 4 85 3 6 9 1
950: 97 61 6 2
398525404786: 48 6 82 54 10 47 82 2
1379238: 597 330 9 15 7
1230344192: 5 7 2 567 8 64 517 8
13787542708: 492 4 12 2 7 51 4 906
152385944894: 28 68 8 2 405 26 8 8 9 2
15707756: 3 826 42 8 4 779
7753272691: 7 85 596 7 28 7 9 8 691
1708: 7 2 94 6 15
11845226295: 79 8 601 5 33 3 7 945
735050: 57 7 4 61 7 4 9 83 1 3 1 2
611123: 216 9 93 3 2 3
5615753409: 7 1 983 5 5 8 8 4 7 6 102
47952: 1 98 493 9 9
37889940: 300 7 85 22 66
21892494438: 84 2 26 494 43 5 2
580189817: 6 74 9 7 7 12 26 70 1
92459016045: 3 513 3 90 988 18 46
915252: 911 98 318 7 85
305766: 6 9 7 5 231 63 7 12 1 5
6449440: 3 61 4 94 40
92466197: 9 1 1 9 2 2 6 5 9 44 2 251
186845731583: 3 8 7 67 1 45 724 7 5 80
183702: 5 113 65 5 2 75
36586232: 45 4 238 854 872
1788196: 16 83 8 15 5 168 4
15343352929477: 2 2 9 98 3 5 9 623 8 7 71
487792: 13 574 7 83 1
142: 22 6 8
2779: 2 9 36 88 80 6 9 737 53
123532: 9 857 7 16 12
239544489: 177 9 4 8 22 683
542: 76 1 7 3
21242616: 3 42 444 7 1 6 4
1597155: 745 351 7 5 996 8 7 35
49642: 7 24 18 1 5 3 7 5 2
14083: 32 44 3
589683738732: 9 84 78 3 738 726 3
172847253: 122 463 510 5 2 5 3 1 6
23852288: 4 8 8 3 7 31 6 9 725 46 4
1648035: 4 8 33 5 312 172 503
170073923: 589 5 5 275 6 17 7 7 5
2685497: 597 87 4 109 83 81 9 5
5720: 94 85 85 45 17 467
15812857139: 479 8 379 6 71 27 257
18980157: 9 64 4 246 3 55 6 8 253
1209222918: 8 3 6 555 98 6 9 81 29
7496879: 7 930 8 816 63
1141455: 6 6 5 4 3 5 81 282 67 2 7
5529605684: 960 64 9 56 84
244490715: 3 540 1 7 7 77 4 8 31 5
6361489: 7 951 8 6 90
4548: 1 5 3 9 5 7 9 7 1 27 4 239
2270948411828: 6 60 375 93 63 11 83 1
5464: 4 19 45 615 8
635976: 3 15 320 177 799
937659501: 6 689 3 21 1 2 36 3 3 9 3
4879488: 7 602 8 6 59 7 813 81
11658: 2 5 6 5 31 8
36245304: 327 46 659 97 3 378
6424420: 803 8 420
268121844: 4 965 494 91 12 560 4
3878245224: 492 4 46 6 51 84
152195: 52 3 291
1590661: 9 5 4 4 1 5 85 9 77 226
9304680: 1 44 56 5 53 418 4
367057751: 2 2 3 23 4 1 3 73 2 43 1
91504: 90 419 1 533 551
75672050: 41 93 4 325 61 19 6
509068: 27 6 394 7 6 807 6 5 3
42718: 35 7 61 207 1 5 27 733
1866207563: 9 331 4 5 682 74 3
212: 18 108 12 2 72
1179911124230: 6 6 2 9 17 86 567 1 15 2
1313469: 35 8 33 5 384 9 189
16267691664897: 4 3 42 3 52 162 72 89 7
1245: 55 26 8 514 642
851925: 307 925 3
8002379: 6 4 7 3 589 38 1
21323704: 5 3 3 1 39 7 540 9 776
4788932613951: 1 2 57 7 9 3 26 1 3 95 1 1
8025705: 4 4 983 4 889 9 97 605
406158: 6 52 70 98 61
89397997394: 3 96 903 997 3 94
540540528: 1 3 7 3 66 30 3 6 1 15 5
641004065: 29 27 5 3 6 364 65
2464018: 246 3 7 11 309
32540: 28 1 76 46 3 2 2 717 5 4
1082: 66 2 955 6 53
1648383: 7 1 5 879 25 30 223 3
553368: 52 6 18 53 4 8
610848: 404 24 9 7
71050391: 38 3 7 319 89
2897: 1 27 833 88 3 53
1584602857: 90 584 80 62 4 474
9811393: 3 9 4 7 94 1 8 7 81 7 9 9
31048: 15 1 8 578 42 5 92
274: 1 81 3 26 2
105395693928: 3 67 61 6 791 4 906
234903318531: 10 8 980 9 9 2 2 3 1 411
6046371: 60 45 3 4 12 20 639
15554843649: 1 5 5 1 544 4 436 49
2593285843: 14 63 6 36 6 49 4 3
946737179: 946 72 93 78 61 20
1182: 991 5 62 8 9 6 1 1 9 86 6
496: 40 3 45 3 1 1
550094: 55 90 2 2 99 55 39
67808: 42 3 7 62 5 21
48854668: 7 2 7 2 75 6 426 1 5 6 70
299551812: 1 906 6 656 77 84
13631739: 878 2 671 187 47
1320498929: 8 7 9 53 7 4 41 8 89 2 2 8
34868: 54 5 652 48 741
5856382: 1 5 631 3 18 449 8 49
10440233176590: 42 242 669 511 5 402
1964450: 44 63 920 7 4 76
88562: 65 9 6 2 548 881 2
885545005: 877 8 2 4 768 4 9 8 2 5
443749232810: 6 288 9 63 14 7 9 56
436160700: 354 594 8 1 8 810 6 93
57136780: 1 9 97 909 8 18 1 4 95 3
174254199: 829 720 61 9 21
42509179: 9 2 461 5 1 92 177 2
4793: 8 230 1 940 8 4 8 3 32 6
3375756000: 723 5 4 4 1 9 2 25 46 9 5
59196060: 7 372 3 6 28 3 9 3 4 315
391933728: 54 26 753 9 47 57 6 9 9
2651184: 40 7 4 342 76 2
108123916802: 2 2 524 9 4 5 16 8 60 1
31188: 3 552 2 8 4 7 59 9 2 676
1023: 95 8 82 96 85
79008445: 2 16 8 85 457
396204: 71 77 327 7 822
1183000: 8 91 65 25 1
170526: 2 9 2 546 293
16687881: 556 3 72 97 49 531 4
290313: 51 8 1 6 982 6 4 8 1 6 6 9
22722971148979: 35 6 397 881 54 2 9 79
13723: 146 1 91 294 52
214646851: 21 46 464 4 51
578228: 57 789 33 1 7

545
lib/spice.zig Normal file
View file

@ -0,0 +1,545 @@
const std = @import("std");
// The overall design of Spice is as follows:
// - ThreadPool spawns threads which acts as background workers.
// - A Worker, while executing, will share one piece of work (`shared_job`).
// - A Worker, while waiting, will look for shared jobs by other workers.
pub const ThreadPoolConfig = struct {
/// The number of background workers. If `null` this chooses a sensible
/// default based on your system (i.e. number of cores).
background_worker_count: ?usize = null,
/// How often a background thread is interrupted to find more work.
heartbeat_interval: usize = 100 * std.time.ns_per_us,
};
pub const ThreadPool = struct {
allocator: std.mem.Allocator,
mutex: std.Thread.Mutex = .{},
/// List of all workers.
workers: std.ArrayListUnmanaged(*Worker) = .{},
/// List of all background workers.
background_threads: std.ArrayListUnmanaged(std.Thread) = .{},
/// The background thread which beats.
heartbeat_thread: ?std.Thread = null,
/// A pool for the JobExecuteState, to minimize allocations.
execute_state_pool: std.heap.MemoryPool(JobExecuteState),
/// This is used to signal that more jobs are now ready.
job_ready: std.Thread.Condition = .{},
/// This is used to wait for the background workers to be available initially.
workers_ready: std.Thread.Semaphore = .{},
/// This is set to true once we're trying to stop.
is_stopping: bool = false,
/// A timer which we increment whenever we share a job.
/// This is used to prioritize always picking the oldest job.
time: usize = 0,
heartbeat_interval: usize,
pub fn init(allocator: std.mem.Allocator) ThreadPool {
return ThreadPool{
.allocator = allocator,
.execute_state_pool = std.heap.MemoryPool(JobExecuteState).init(allocator),
.heartbeat_interval = undefined,
};
}
/// Starts the thread pool. This should only be invoked once.
pub fn start(self: *ThreadPool, config: ThreadPoolConfig) void {
const actual_count = config.background_worker_count orelse (std.Thread.getCpuCount() catch @panic("getCpuCount error")) - 1;
self.heartbeat_interval = config.heartbeat_interval;
self.background_threads.ensureUnusedCapacity(self.allocator, actual_count) catch @panic("OOM");
self.workers.ensureUnusedCapacity(self.allocator, actual_count) catch @panic("OOM");
for (0..actual_count) |_| {
const thread = std.Thread.spawn(.{}, backgroundWorker, .{self}) catch @panic("spawn error");
self.background_threads.append(self.allocator, thread) catch @panic("OOM");
}
self.heartbeat_thread = std.Thread.spawn(.{}, heartbeatWorker, .{self}) catch @panic("spawn error");
// Wait for all of them to be ready:
for (0..actual_count) |_| {
self.workers_ready.wait();
}
}
pub fn deinit(self: *ThreadPool) void {
// Tell all background workers to stop:
{
self.mutex.lock();
defer self.mutex.unlock();
self.is_stopping = true;
self.job_ready.broadcast();
}
// Wait for background workers to stop:
for (self.background_threads.items) |thread| {
thread.join();
}
if (self.heartbeat_thread) |thread| {
thread.join();
}
// Free up memory:
self.background_threads.deinit(self.allocator);
self.workers.deinit(self.allocator);
self.execute_state_pool.deinit();
self.* = undefined;
}
fn backgroundWorker(self: *ThreadPool) void {
var w = Worker{ .pool = self };
var first = true;
self.mutex.lock();
defer self.mutex.unlock();
self.workers.append(self.allocator, &w) catch @panic("OOM");
// We don't bother removing ourselves from the workers list of exit since
// this only happens when the whole thread pool is destroyed anyway.
while (true) {
if (self.is_stopping) break;
if (self._popReadyJob()) |job| {
// Release the lock while executing the job.
self.mutex.unlock();
defer self.mutex.lock();
w.executeJob(job);
continue; // Go straight to another attempt of finding more work.
}
if (first) {
// Register that we are ready.
self.workers_ready.post();
first = false;
}
self.job_ready.wait(&self.mutex);
}
}
fn heartbeatWorker(self: *ThreadPool) void {
// We try to make sure that each worker is being heartbeat at the
// fixed interval by going through the workers-list one by one.
var i: usize = 0;
while (true) {
var to_sleep: u64 = self.heartbeat_interval;
{
self.mutex.lock();
defer self.mutex.unlock();
if (self.is_stopping) break;
const workers = self.workers.items;
if (workers.len > 0) {
i %= workers.len;
workers[i].heartbeat.store(true, .monotonic);
i += 1;
to_sleep /= workers.len;
}
}
std.time.sleep(to_sleep);
}
}
pub fn call(self: *ThreadPool, comptime T: type, func: anytype, arg: anytype) T {
// Create an one-off worker:
var worker = Worker{ .pool = self };
{
self.mutex.lock();
defer self.mutex.unlock();
self.workers.append(self.allocator, &worker) catch @panic("OOM");
}
defer {
self.mutex.lock();
defer self.mutex.unlock();
for (self.workers.items, 0..) |worker_ptr, idx| {
if (worker_ptr == &worker) {
_ = self.workers.swapRemove(idx);
break;
}
}
}
var t = worker.begin();
return t.call(T, func, arg);
}
/// The core logic of the heartbeat. Every executing worker invokes this periodically.
fn heartbeat(self: *ThreadPool, worker: *Worker) void {
@setCold(true);
self.mutex.lock();
defer self.mutex.unlock();
if (worker.shared_job == null) {
if (worker.job_head.shift()) |job| {
// Allocate an execute state for it:
const execute_state = self.execute_state_pool.create() catch @panic("OOM");
execute_state.* = .{
.result = undefined,
};
job.setExecuteState(execute_state);
worker.shared_job = job;
worker.job_time = self.time;
self.time += 1;
self.job_ready.signal(); // wake up one thread
}
}
worker.heartbeat.store(false, .monotonic);
}
/// Waits for (a shared) job to be completed.
/// This returns `false` if it turns out the job was not actually started.
fn waitForJob(self: *ThreadPool, worker: *Worker, job: *Job) bool {
const exec_state = job.getExecuteState();
{
self.mutex.lock();
defer self.mutex.unlock();
if (worker.shared_job == job) {
// This is the job we attempted to share with someone else, but before someone picked it up.
worker.shared_job = null;
self.execute_state_pool.destroy(exec_state);
return false;
}
// Help out by picking up more work if it's available.
while (!exec_state.done.isSet()) {
if (self._popReadyJob()) |other_job| {
self.mutex.unlock();
defer self.mutex.lock();
worker.executeJob(other_job);
} else {
break;
}
}
}
exec_state.done.wait();
return true;
}
/// Finds a job that's ready to be executed.
fn _popReadyJob(self: *ThreadPool) ?*Job {
var best_worker: ?*Worker = null;
for (self.workers.items) |other_worker| {
if (other_worker.shared_job) |_| {
if (best_worker) |best| {
if (other_worker.job_time < best.job_time) {
// Pick this one instead if it's older.
best_worker = other_worker;
}
} else {
best_worker = other_worker;
}
}
}
if (best_worker) |worker| {
defer worker.shared_job = null;
return worker.shared_job;
}
return null;
}
fn destroyExecuteState(self: *ThreadPool, exec_state: *JobExecuteState) void {
self.mutex.lock();
defer self.mutex.unlock();
self.execute_state_pool.destroy(exec_state);
}
};
pub const Worker = struct {
pool: *ThreadPool,
job_head: Job = Job.head(),
/// A job (guaranteed to be in executing state) which other workers can pick up.
shared_job: ?*Job = null,
/// The time when the job was shared. Used for prioritizing which job to pick up.
job_time: usize = 0,
/// The heartbeat value. This is set to `true` to signal we should do a heartbeat action.
heartbeat: std.atomic.Value(bool) = std.atomic.Value(bool).init(true),
pub fn begin(self: *Worker) Task {
std.debug.assert(self.job_head.isTail());
return Task{
.worker = self,
.job_tail = &self.job_head,
};
}
fn executeJob(self: *Worker, job: *Job) void {
var t = self.begin();
job.handler.?(&t, job);
}
};
pub const Task = struct {
worker: *Worker,
job_tail: *Job,
pub inline fn tick(self: *Task) void {
if (self.worker.heartbeat.load(.monotonic)) {
self.worker.pool.heartbeat(self.worker);
}
}
pub inline fn call(self: *Task, comptime T: type, func: anytype, arg: anytype) T {
return callWithContext(
self.worker,
self.job_tail,
T,
func,
arg,
);
}
};
// The following function's signature is actually extremely critical. We take in all of
// the task state (worker, last_heartbeat, job_tail) as parameters. The reason for this
// is that Zig/LLVM is really good at passing parameters in registers, but struggles to
// do the same for "fields in structs". In addition, we then return the changed value
// of last_heartbeat and job_tail.
fn callWithContext(
worker: *Worker,
job_tail: *Job,
comptime T: type,
func: anytype,
arg: anytype,
) T {
var t = Task{
.worker = worker,
.job_tail = job_tail,
};
t.tick();
return @call(.always_inline, func, .{
&t,
arg,
});
}
pub const JobState = enum {
pending,
queued,
executing,
};
// A job represents something which _potentially_ could be executed on a different thread.
// The jobs forms a doubly-linked list: You call `push` to append a job and `pop` to remove it.
const Job = struct {
handler: ?*const fn (t: *Task, job: *Job) void,
prev_or_null: ?*anyopaque,
next_or_state: ?*anyopaque,
// This struct gets placed on the stack in _every_ frame so we're very cautious
// about the size of it. There's three possible states, but we don't use a union(enum)
// since this would actually increase the size.
//
// 1. pending: handler is null. a/b is undefined.
// 2. queued: handler is set. prev_or_null is `prev`, next_or_state is `next`.
// 3. executing: handler is set. prev_or_null is null, next_or_state is `*JobExecuteState`.
/// Returns a new job which can be used for the head of a list.
fn head() Job {
return Job{
.handler = undefined,
.prev_or_null = null,
.next_or_state = null,
};
}
pub fn pending() Job {
return Job{
.handler = null,
.prev_or_null = undefined,
.next_or_state = undefined,
};
}
pub fn state(self: Job) JobState {
if (self.handler == null) return .pending;
if (self.prev_or_null != null) return .queued;
return .executing;
}
pub fn isTail(self: Job) bool {
return self.next_or_state == null;
}
fn getExecuteState(self: *Job) *JobExecuteState {
std.debug.assert(self.state() == .executing);
return @ptrCast(@alignCast(self.next_or_state));
}
pub fn setExecuteState(self: *Job, execute_state: *JobExecuteState) void {
std.debug.assert(self.state() == .executing);
self.next_or_state = execute_state;
}
/// Pushes the job onto a stack.
fn push(self: *Job, tail: **Job, handler: *const fn (task: *Task, job: *Job) void) void {
std.debug.assert(self.state() == .pending);
defer std.debug.assert(self.state() == .queued);
self.handler = handler;
tail.*.next_or_state = self; // tail.next = self
self.prev_or_null = tail.*; // self.prev = tail
self.next_or_state = null; // self.next = null
tail.* = self; // tail = self
}
fn pop(self: *Job, tail: **Job) void {
std.debug.assert(self.state() == .queued);
std.debug.assert(tail.* == self);
const prev: *Job = @ptrCast(@alignCast(self.prev_or_null));
prev.next_or_state = null; // prev.next = null
tail.* = @ptrCast(@alignCast(self.prev_or_null)); // tail = self.prev
self.* = undefined;
}
fn shift(self: *Job) ?*Job {
const job = @as(?*Job, @ptrCast(@alignCast(self.next_or_state))) orelse return null;
std.debug.assert(job.state() == .queued);
const next: ?*Job = @ptrCast(@alignCast(job.next_or_state));
// Now we have: self -> job -> next.
// If there is no `next` then it means that `tail` actually points to `job`.
// In this case we can't remove `job` since we're not able to also update the tail.
if (next == null) return null;
defer std.debug.assert(job.state() == .executing);
next.?.prev_or_null = self; // next.prev = self
self.next_or_state = next; // self.next = next
// Turn the job into "executing" state.
job.prev_or_null = null;
job.next_or_state = undefined;
return job;
}
};
const max_result_words = 4;
const JobExecuteState = struct {
done: std.Thread.ResetEvent = .{},
result: ResultType,
const ResultType = [max_result_words]u64;
fn resultPtr(self: *JobExecuteState, comptime T: type) *T {
if (@sizeOf(T) > @sizeOf(ResultType)) {
@compileError("value is too big to be returned by background thread");
}
const bytes = std.mem.sliceAsBytes(&self.result);
return std.mem.bytesAsValue(T, bytes);
}
};
pub fn Future(comptime Input: type, Output: type) type {
return struct {
const Self = @This();
job: Job,
input: Input,
pub inline fn init() Self {
return Self{ .job = Job.pending(), .input = undefined };
}
/// Schedules a piece of work to be executed by another thread.
/// After this has been called you MUST call `join` or `tryJoin`.
pub inline fn fork(
self: *Self,
task: *Task,
comptime func: fn (task: *Task, input: Input) Output,
input: Input,
) void {
const handler = struct {
fn handler(t: *Task, job: *Job) void {
const fut: *Self = @fieldParentPtr("job", job);
const exec_state = job.getExecuteState();
const value = t.call(Output, func, fut.input);
exec_state.resultPtr(Output).* = value;
exec_state.done.set();
}
}.handler;
self.input = input;
self.job.push(&task.job_tail, handler);
}
/// Waits for the result of `fork`.
/// This is only safe to call if `fork` was _actually_ called.
/// Use `tryJoin` if you conditionally called it.
pub inline fn join(
self: *Self,
task: *Task,
) ?Output {
std.debug.assert(self.job.state() != .pending);
return self.tryJoin(task);
}
/// Waits for the result of `fork`.
/// This function is safe to call even if you didn't call `fork` at all.
pub inline fn tryJoin(
self: *Self,
task: *Task,
) ?Output {
switch (self.job.state()) {
.pending => return null,
.queued => {
self.job.pop(&task.job_tail);
return null;
},
.executing => return self.joinExecuting(task),
}
}
fn joinExecuting(self: *Self, task: *Task) ?Output {
@setCold(true);
const w = task.worker;
const pool = w.pool;
const exec_state = self.job.getExecuteState();
if (pool.waitForJob(w, &self.job)) {
const result = exec_state.resultPtr(Output).*;
pool.destroyExecuteState(exec_state);
return result;
}
return null;
}
};
}

View file

@ -1,55 +1,5 @@
const std = @import("std"); const std = @import("std");
pub fn rangeComptime(comptime n: usize) [n]usize {
var array: [n]usize = undefined;
for (0.., &array) |i, *elem| {
elem.* = i;
}
return array;
}
pub fn range(alloc: std.mem.Allocator, n: usize) ![]usize {
var array = try alloc.alloc(usize, n);
for (0..n) |i| {
array[i] = i;
}
return array;
}
pub fn printSlice(comptime T: type, slice: []const T) void {
if (slice.len == 0) {
std.debug.print("[ ]", .{});
return;
}
std.debug.print("[ ", .{});
for (slice[0 .. slice.len - 1]) |x| {
std.debug.print("{}, ", .{x});
}
std.debug.print("{} ]", .{slice[slice.len - 1]});
}
pub fn printlnSlice(comptime T: type, slice: []const T) void {
if (slice.len == 0) {
std.debug.print("[ ]\n", .{});
return;
}
std.debug.print("[ ", .{});
for (slice[0 .. slice.len - 1]) |x| {
std.debug.print("{}, ", .{x});
}
std.debug.print("{} ]\n", .{slice[slice.len - 1]});
}
pub const FileReader = struct { pub const FileReader = struct {
const BufferedReader = std.io.BufferedReader(4096, std.fs.File.Reader); const BufferedReader = std.io.BufferedReader(4096, std.fs.File.Reader);
const Reader = std.io.Reader(*BufferedReader, std.fs.File.Reader.Error, BufferedReader.read); const Reader = std.io.Reader(*BufferedReader, std.fs.File.Reader.Error, BufferedReader.read);
@ -149,3 +99,81 @@ pub fn numberParser(comptime T: type, input: []const u8) NumberParser(T) {
pub fn numberParserWithDelimiter(comptime T: type, input: []const u8, delimiter: u8) NumberParser(T) { pub fn numberParserWithDelimiter(comptime T: type, input: []const u8, delimiter: u8) NumberParser(T) {
return NumberParser(T){ .token_it = std.mem.tokenizeScalar(u8, input, delimiter) }; return NumberParser(T){ .token_it = std.mem.tokenizeScalar(u8, input, delimiter) };
} }
pub fn rangeComptime(comptime n: usize) [n]usize {
var array: [n]usize = undefined;
for (0.., &array) |i, *elem| {
elem.* = i;
}
return array;
}
pub fn range(alloc: std.mem.Allocator, n: usize) ![]usize {
var array = try alloc.alloc(usize, n);
for (0..n) |i| {
array[i] = i;
}
return array;
}
pub fn printSlice(comptime T: type, slice: []const T) void {
if (slice.len == 0) {
std.debug.print("[ ]", .{});
return;
}
std.debug.print("[ ", .{});
for (slice[0 .. slice.len - 1]) |x| {
std.debug.print("{}, ", .{x});
}
std.debug.print("{} ]", .{slice[slice.len - 1]});
}
pub fn printlnSlice(comptime T: type, slice: []const T) void {
if (slice.len == 0) {
std.debug.print("[ ]\n", .{});
return;
}
std.debug.print("[ ", .{});
for (slice[0 .. slice.len - 1]) |x| {
std.debug.print("{}, ", .{x});
}
std.debug.print("{} ]\n", .{slice[slice.len - 1]});
}
pub fn num_digits(n: anytype) std.math.Log2Int(@TypeOf(n)) {
return std.math.log10_int(n) + 1;
}
test "num_digits" {
// try std.fmt.allocPrint(
// std.testing.allocator,
// "Expected {} to have 4 digit, but got {} instead",
// .{ n, num_digits },
// );
for (2..10) |n| {
try std.testing.expectEqual(num_digits(n), 1);
}
for (10..100) |n| {
try std.testing.expectEqual(num_digits(n), 2);
}
for (100..1_000) |n| {
try std.testing.expectEqual(num_digits(n), 3);
}
for (1_000..10_000) |n| {
try std.testing.expectEqual(num_digits(n), 4);
}
}