Finish day 13
This commit is contained in:
		
							parent
							
								
									3f063f931c
								
							
						
					
					
						commit
						0035850050
					
				| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					const Builder = @import("std").build.Builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn build(b: *Builder) void {
 | 
				
			||||||
 | 
					    // Standard target options allows the person running `zig build` to choose
 | 
				
			||||||
 | 
					    // what target to build for. Here we do not override the defaults, which
 | 
				
			||||||
 | 
					    // means any target is allowed, and the default is native. Other options
 | 
				
			||||||
 | 
					    // for restricting supported target set are available.
 | 
				
			||||||
 | 
					    const target = b.standardTargetOptions(.{});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Standard release options allow the person running `zig build` to select
 | 
				
			||||||
 | 
					    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
 | 
				
			||||||
 | 
					    const mode = b.standardReleaseOptions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const exe = b.addExecutable("day13", "src/main.zig");
 | 
				
			||||||
 | 
					    exe.setTarget(target);
 | 
				
			||||||
 | 
					    exe.setBuildMode(mode);
 | 
				
			||||||
 | 
					    exe.install();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const run_cmd = exe.run();
 | 
				
			||||||
 | 
					    run_cmd.step.dependOn(b.getInstallStep());
 | 
				
			||||||
 | 
					    if (b.args) |args| {
 | 
				
			||||||
 | 
					        run_cmd.addArgs(args);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const run_step = b.step("run", "Run the app");
 | 
				
			||||||
 | 
					    run_step.dependOn(&run_cmd.step);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					1005595
 | 
				
			||||||
 | 
					41,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,37,x,x,x,x,x,557,x,29,x,x,x,x,x,x,x,x,x,x,13,x,x,x,17,x,x,x,x,x,23,x,x,x,x,x,x,x,419,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,19
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,467 @@
 | 
				
			||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn main() anyerror!void {
 | 
				
			||||||
 | 
					    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
 | 
				
			||||||
 | 
					    defer arena.deinit();
 | 
				
			||||||
 | 
					    var gpa = &arena.allocator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var f = try std.fs.cwd().openFile("input", .{});
 | 
				
			||||||
 | 
					    var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u32));
 | 
				
			||||||
 | 
					    defer gpa.free(contents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var buses = std.ArrayList(Bus).init(gpa);
 | 
				
			||||||
 | 
					    defer buses.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var it = std.mem.tokenize(contents, "\n");
 | 
				
			||||||
 | 
					    var estimated_arrival_at_bus_station: u64 = 0;
 | 
				
			||||||
 | 
					    if (it.next()) |l| {
 | 
				
			||||||
 | 
					        estimated_arrival_at_bus_station = try std.fmt.parseUnsigned(u64, l, 10);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (it.next()) |l| {
 | 
				
			||||||
 | 
					        var bus_it = std.mem.tokenize(l, ",");
 | 
				
			||||||
 | 
					        var offset: u64 = 0;
 | 
				
			||||||
 | 
					        while (bus_it.next()) |b| : (offset += 1) {
 | 
				
			||||||
 | 
					            if (b[0] == 'x') {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var a = std.fmt.parseUnsigned(u64, b, 10) catch continue;
 | 
				
			||||||
 | 
					            var bus = Bus{.id = a, .offset = offset};
 | 
				
			||||||
 | 
					            std.log.debug("Bus: {}", .{bus});
 | 
				
			||||||
 | 
					            try buses.append(bus);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var earliest_bus = find_earliest_bus_after(buses.items, estimated_arrival_at_bus_station);
 | 
				
			||||||
 | 
					    var wait_time = earliest_bus.earliest_departure_after(estimated_arrival_at_bus_station) - estimated_arrival_at_bus_station;
 | 
				
			||||||
 | 
					    std.log.info("Earliest bus '{}' will depart at {}, a wait of {}.",
 | 
				
			||||||
 | 
					                 .{earliest_bus.id, earliest_bus.earliest_departure_after(estimated_arrival_at_bus_station), wait_time});
 | 
				
			||||||
 | 
					    std.log.info("Part one: {}", .{earliest_bus.id * wait_time});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Part 2
 | 
				
			||||||
 | 
					    // the challenge warns us it will "surely be" above 100000000000000
 | 
				
			||||||
 | 
					    //                                             18446744073709551615 <-- u64 max
 | 
				
			||||||
 | 
					    //var est_start : u64 = buses.items[0].earliest_departure_after(estimated_arrival_at_bus_station);
 | 
				
			||||||
 | 
					    //est_start = 100000733946084;
 | 
				
			||||||
 | 
					    est_start = 101775718927832;
 | 
				
			||||||
 | 
					    // From Est start 101775718927832
 | 
				
			||||||
 | 
					    // To 140354342739376 took 180.75m of CPU time,
 | 
				
			||||||
 | 
					    //    598411311431841 <-- sol'n after using sieve + lcm
 | 
				
			||||||
 | 
					    // It would have probably taken another (180.75)*3.26 = 9.82 hours to get there...
 | 
				
			||||||
 | 
					    // lcm + sieve: ~ 0.003s, and starts working from 0.
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    var act = std.os.Sigaction {
 | 
				
			||||||
 | 
					        .sigaction = handle_sigusr,
 | 
				
			||||||
 | 
					        //.sigaction = std.os.linux.SIG_IGN,
 | 
				
			||||||
 | 
					        .mask = std.os.empty_sigset,
 | 
				
			||||||
 | 
					        .flags = 0,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    std.os.sigaction(10, &act, null);
 | 
				
			||||||
 | 
					    // Brute force approach
 | 
				
			||||||
 | 
					    // while (prize_start == 0) : (est_start += buses.items[0].id) {
 | 
				
			||||||
 | 
					    //     var matches = true;
 | 
				
			||||||
 | 
					    //     for (buses.items[1..]) |b, k| {
 | 
				
			||||||
 | 
					    //         if (@mod(est_start + b.offset, b.id) % b.id != 0) {
 | 
				
			||||||
 | 
					    //             matches = false;
 | 
				
			||||||
 | 
					    //             //std.log.debug("Bus {} failed iteration starting at {}", .{b.id, est_start});
 | 
				
			||||||
 | 
					    //             break;
 | 
				
			||||||
 | 
					    //         }
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //     if (matches) {
 | 
				
			||||||
 | 
					    //         prize_start = est_start;
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses.items[0..], gpa);
 | 
				
			||||||
 | 
					    std.log.info("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var t_last_sigusr : i128 = 0;
 | 
				
			||||||
 | 
					var est_start : u64 = 0;
 | 
				
			||||||
 | 
					var last_start_value : u64 = 0;
 | 
				
			||||||
 | 
					fn handle_sigusr(sig: i32, info: *std.os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void {
 | 
				
			||||||
 | 
					    var values_per_second_avg : f64 = 0.0;
 | 
				
			||||||
 | 
					    if (last_start_value == 0) {
 | 
				
			||||||
 | 
					        std.log.warn("Last {}", .{est_start});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        var calcs_done : f64 = @intToFloat(f64, (est_start - last_start_value)/41);
 | 
				
			||||||
 | 
					        values_per_second_avg = calcs_done
 | 
				
			||||||
 | 
					            / @intToFloat(f64, std.time.nanoTimestamp() - t_last_sigusr);
 | 
				
			||||||
 | 
					        std.log.warn("Last {}, {}/s avg - {} calcs done",
 | 
				
			||||||
 | 
					                     .{est_start, values_per_second_avg * std.time.ns_per_s, calcs_done});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    last_start_value = est_start;
 | 
				
			||||||
 | 
					    t_last_sigusr = std.time.nanoTimestamp();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn find_earliest_bus_after(buses: []Bus, time: u64) Bus {
 | 
				
			||||||
 | 
					    var earliest_bus_index : u64 = 0;
 | 
				
			||||||
 | 
					    var earliest_departure_time : u64 = std.math.maxInt(u64);
 | 
				
			||||||
 | 
					    for (buses) |bus, k| {
 | 
				
			||||||
 | 
					        var departs_at : u64 = Bus.earliest_departure(bus.id, time);
 | 
				
			||||||
 | 
					        if (departs_at < earliest_departure_time) {
 | 
				
			||||||
 | 
					            earliest_bus_index = k;
 | 
				
			||||||
 | 
					            earliest_departure_time = departs_at;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return buses[earliest_bus_index];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Bus = struct {
 | 
				
			||||||
 | 
					    id: u64,
 | 
				
			||||||
 | 
					    offset: u64 = 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn earliest_departure_after(self: *Bus, time: u64) u64 {
 | 
				
			||||||
 | 
					        return Bus.earliest_departure(self.id, time);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn earliest_departure(id: u64, time: u64) u64 {
 | 
				
			||||||
 | 
					        var d : u64 = ((time / id)+1) * id;
 | 
				
			||||||
 | 
					        std.debug.assert(d >= time);
 | 
				
			||||||
 | 
					        return d;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn find_bus_offset_departure(buses: []Bus, a: *std.mem.Allocator) !u64 {
 | 
				
			||||||
 | 
					    var list_pos : usize = 0;
 | 
				
			||||||
 | 
					    var t : u64 = 0;
 | 
				
			||||||
 | 
					    var increment : u64 = buses[0].id;
 | 
				
			||||||
 | 
					    var bus_ids = try a.alloc(u64, buses.len);
 | 
				
			||||||
 | 
					    defer a.free(bus_ids);
 | 
				
			||||||
 | 
					    for (buses) |b, k| {
 | 
				
			||||||
 | 
					        bus_ids[k] = b.id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (list_pos < (buses.len-1)) : (t += increment) {
 | 
				
			||||||
 | 
					        // Check if (t+buses[list_pos+1].offset)%buses[list_pos+1].id == 0
 | 
				
			||||||
 | 
					        if (@mod(t+buses[list_pos+1].offset, buses[list_pos+1].id) == 0) {
 | 
				
			||||||
 | 
					            // Our bus arrives here, great.
 | 
				
			||||||
 | 
					            increment = try lcm_by_factorization(std.testing.allocator, bus_ids[0..list_pos+2]);
 | 
				
			||||||
 | 
					            std.log.debug("Bus {} (offset: {}) in pos {} at {}. New increment {}",
 | 
				
			||||||
 | 
					                           .{buses[list_pos+1].id, buses[list_pos+1].offset, list_pos+1, t, increment});
 | 
				
			||||||
 | 
					            list_pos += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // If we left the loop, we incremented, so decrement
 | 
				
			||||||
 | 
					    t -= increment;
 | 
				
			||||||
 | 
					    return t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "departure" {
 | 
				
			||||||
 | 
					    var a : u64 = 939;
 | 
				
			||||||
 | 
					    std.testing.expectEqual(Bus.earliest_departure(59, 939), 944);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_start" {
 | 
				
			||||||
 | 
					    // 7,13,x,x,59,x,31,19
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 7},
 | 
				
			||||||
 | 
					        Bus{.id = 13, .offset = 1},
 | 
				
			||||||
 | 
					        Bus{.id = 59, .offset = 4},
 | 
				
			||||||
 | 
					        Bus{.id = 31, .offset = 6},
 | 
				
			||||||
 | 
					        Bus{.id = 19, .offset = 7},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 1068781);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No LCM version, because the example's prize_start if based on the
 | 
				
			||||||
 | 
					    // arbitrary offset.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_ex1" {
 | 
				
			||||||
 | 
					    //17,x,13,19
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 17},
 | 
				
			||||||
 | 
					        Bus{.id = 13, .offset = 2},
 | 
				
			||||||
 | 
					        Bus{.id = 19, .offset = 3},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 3417);
 | 
				
			||||||
 | 
					    // LCM version
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 3417);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_ex2" {
 | 
				
			||||||
 | 
					    // 67,7,59,61
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 67},
 | 
				
			||||||
 | 
					        Bus{.id = 7, .offset = 1},
 | 
				
			||||||
 | 
					        Bus{.id = 59, .offset = 2},
 | 
				
			||||||
 | 
					        Bus{.id = 61, .offset = 3},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 754018);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // LCM version
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 754018);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_ex3" {
 | 
				
			||||||
 | 
					    // 67,x,7,59,61
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 67},
 | 
				
			||||||
 | 
					        Bus{.id = 7, .offset = 2},
 | 
				
			||||||
 | 
					        Bus{.id = 59, .offset = 3},
 | 
				
			||||||
 | 
					        Bus{.id = 61, .offset = 4},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 779210);
 | 
				
			||||||
 | 
					    // LCM version
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 779210);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_ex4" {
 | 
				
			||||||
 | 
					    // 67,7,x, 59,61
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 67},
 | 
				
			||||||
 | 
					        Bus{.id = 7, .offset = 1},
 | 
				
			||||||
 | 
					        Bus{.id = 59, .offset = 3},
 | 
				
			||||||
 | 
					        Bus{.id = 61, .offset = 4},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 1261476);
 | 
				
			||||||
 | 
					    // LCM version
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 1261476);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prize_ex5" {
 | 
				
			||||||
 | 
					    // 1789,37,47,1889
 | 
				
			||||||
 | 
					    var buses = [_]Bus {
 | 
				
			||||||
 | 
					        Bus{.id = 1789},
 | 
				
			||||||
 | 
					        Bus{.id = 37, .offset = 1},
 | 
				
			||||||
 | 
					        Bus{.id = 47, .offset = 2},
 | 
				
			||||||
 | 
					        Bus{.id = 1889, .offset = 3},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    est_start = 0;
 | 
				
			||||||
 | 
					    var prize_start : u64 = 0;
 | 
				
			||||||
 | 
					    while (prize_start == 0) : (est_start += buses[0].id) {
 | 
				
			||||||
 | 
					        var matches = true;
 | 
				
			||||||
 | 
					        for (buses[1..]) |b, k| {
 | 
				
			||||||
 | 
					            if ((est_start + b.offset) % b.id != 0) {
 | 
				
			||||||
 | 
					                matches = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (matches) {
 | 
				
			||||||
 | 
					            prize_start = est_start;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 1202161486);
 | 
				
			||||||
 | 
					    // LCM version
 | 
				
			||||||
 | 
					    prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(prize_start, 1202161486);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn lcm_by_factorization(allocator: *std.mem.Allocator, values: []u64) !u64 {
 | 
				
			||||||
 | 
					    var highest_powers = std.hash_map.AutoHashMap(u64, u64).init(allocator);
 | 
				
			||||||
 | 
					    defer highest_powers.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // For each item in the set, get the prime factorization of the item's value
 | 
				
			||||||
 | 
					    for (values) |v| {
 | 
				
			||||||
 | 
					        var list = try prime_factors(v, allocator);
 | 
				
			||||||
 | 
					        defer list.deinit();
 | 
				
			||||||
 | 
					        for (list.items) |factor| {
 | 
				
			||||||
 | 
					            std.log.warn("{}^{} is a prime factor of {}", .{factor.base, factor.power, v});
 | 
				
			||||||
 | 
					            if (highest_powers.get(factor.base)) |other_power| {
 | 
				
			||||||
 | 
					                if (factor.power > other_power) {
 | 
				
			||||||
 | 
					                    highest_powers.putAssumeCapacity(factor.base, factor.power);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                try highest_powers.put(factor.base, factor.power);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var lcm : u64 = 1;
 | 
				
			||||||
 | 
					    var it = highest_powers.iterator();
 | 
				
			||||||
 | 
					    while (it.next()) |entry| {
 | 
				
			||||||
 | 
					        std.log.warn("{}^{} is part of the LCM", .{entry.key, entry.value});
 | 
				
			||||||
 | 
					        lcm *= std.math.pow(u64, entry.key, entry.value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return lcm;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Factor = struct {
 | 
				
			||||||
 | 
					    base: u64,
 | 
				
			||||||
 | 
					    power: u64 = 1,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn prime_factors(value: u64, allocator: *std.mem.Allocator) !std.ArrayList(Factor) {
 | 
				
			||||||
 | 
					    var factors = std.ArrayList(Factor).init(allocator);
 | 
				
			||||||
 | 
					    // Using the "test and fail method"
 | 
				
			||||||
 | 
					    var current : u64 = value;
 | 
				
			||||||
 | 
					    var divisor : u64 = 2;
 | 
				
			||||||
 | 
					    if (is_prime(value)) {
 | 
				
			||||||
 | 
					        try factors.append(Factor{.base = value});
 | 
				
			||||||
 | 
					        return factors;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while(current != 1) {
 | 
				
			||||||
 | 
					        //std.log.warn("{} rem {} = {}", .{current, divisor, @rem(current, divisor)});
 | 
				
			||||||
 | 
					        if (@rem(current, divisor) == 0) {
 | 
				
			||||||
 | 
					            current = current / divisor;
 | 
				
			||||||
 | 
					            // Add divisor to the list of factors if it's not there, otherwise
 | 
				
			||||||
 | 
					            // increment the power
 | 
				
			||||||
 | 
					            var index: ?usize = null;
 | 
				
			||||||
 | 
					            for (factors.items) |v, k| {
 | 
				
			||||||
 | 
					                if (v.base == divisor) {
 | 
				
			||||||
 | 
					                    index = k;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (index) |i| {
 | 
				
			||||||
 | 
					                factors.items[i].power += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                try factors.append(Factor{.base = divisor});
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            // Out of this factor, increment the divisor until the next prime number
 | 
				
			||||||
 | 
					            // divisor is reached.
 | 
				
			||||||
 | 
					            divisor += 1;
 | 
				
			||||||
 | 
					            while(!is_prime(divisor)) : (divisor+=1) {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return factors;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn is_prime(value: u64) bool {
 | 
				
			||||||
 | 
					    if (value == 1) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var i: u64 = 2;
 | 
				
			||||||
 | 
					    while (i < (value-1)) : (i += 1) {
 | 
				
			||||||
 | 
					        if (@rem(value, i) == 0) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "is_prime_49" {
 | 
				
			||||||
 | 
					    std.testing.expect(is_prime(49) == false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "is_prime_1009" {
 | 
				
			||||||
 | 
					    std.testing.expect(is_prime(1009));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prime_factors_100" {
 | 
				
			||||||
 | 
					    var a = std.testing.allocator;
 | 
				
			||||||
 | 
					    var factors = try prime_factors(100, a);
 | 
				
			||||||
 | 
					    defer factors.deinit();
 | 
				
			||||||
 | 
					    // for (factors.items) |v| {
 | 
				
			||||||
 | 
					    //     std.log.warn("{}^{}  ", .{v.base, v.power});
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items.len, 2);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].base, 2);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].power, 2);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[1].base, 5);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].power, 2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test "prime_factors_1009" {
 | 
				
			||||||
 | 
					    var a = std.testing.allocator;
 | 
				
			||||||
 | 
					    var factors = try prime_factors(1009, a);
 | 
				
			||||||
 | 
					    defer factors.deinit();
 | 
				
			||||||
 | 
					    // for (factors.items) |v| {
 | 
				
			||||||
 | 
					    //     std.log.warn("{}^{}  ", .{v.base, v.power});
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items.len, 1);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].base, 1009);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].power, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is a pretty long test...
 | 
				
			||||||
 | 
					test "prime_factors_1289732" {
 | 
				
			||||||
 | 
					    var a = std.testing.allocator;
 | 
				
			||||||
 | 
					    var factors = try prime_factors(128973, a);
 | 
				
			||||||
 | 
					    defer factors.deinit();
 | 
				
			||||||
 | 
					    // for (factors.items) |v| {
 | 
				
			||||||
 | 
					    //     std.log.warn("{}^{}  ", .{v.base, v.power});
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items.len, 3);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].base, 3);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[0].power, 1);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[1].base, 13);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[1].power, 1);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[2].base, 3307);
 | 
				
			||||||
 | 
					    std.testing.expectEqual(factors.items[2].power, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue