Update examples for Zig (#287)
* update zig in chapters 1-6 * fix zig dijkstras algo * fix zig greedy algo * fix longest_common_subsequence in zig * cleanup * test: use testing allocator
This commit is contained in:
@@ -11,12 +11,13 @@ pub fn main() void {
|
||||
|
||||
fn binarySearch(comptime T: type, list: []const T, item: T) ?usize {
|
||||
var low: i32 = 0;
|
||||
var high: i32 = @intCast(i32, list.len) - 1;
|
||||
const u_high: u32 = @truncate(list.len);
|
||||
var high: i32 = @intCast(u_high - 1);
|
||||
|
||||
return while (low <= high) {
|
||||
var mid = @divTrunc((low + high), 2);
|
||||
var m = @intCast(usize, mid);
|
||||
var guess = list[m];
|
||||
const mid = @divTrunc((low + high), 2);
|
||||
const m: usize = @intCast(mid);
|
||||
const guess = list[m];
|
||||
if (guess == item) break m;
|
||||
if (guess > item) {
|
||||
high = mid - 1;
|
||||
|
||||
@@ -10,12 +10,12 @@ pub fn main() !void {
|
||||
}
|
||||
|
||||
fn selectionSort(comptime T: type, list: []T) void {
|
||||
for (list) |_, i| {
|
||||
for (0..list.len) |i| {
|
||||
var j = i + 1;
|
||||
while (j < list.len) : (j += 1) {
|
||||
if (list[i] > list[j]) {
|
||||
// swap
|
||||
var tmp = list[i];
|
||||
const tmp = list[i];
|
||||
list[i] = list[j];
|
||||
list[j] = tmp;
|
||||
}
|
||||
@@ -30,6 +30,6 @@ test "selectionSort" {
|
||||
selectionSort(i32, s[0..]);
|
||||
|
||||
try expect(s.len == exp.len);
|
||||
for (s) |e, i|
|
||||
for (s, 0..) |e, i|
|
||||
try expect(e == exp[i]);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ test "count" {
|
||||
var arr0 = [_]i32{};
|
||||
var arr1 = [_]i32{42};
|
||||
var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
var tests = [_]struct {
|
||||
const tests = [_]struct {
|
||||
arr: []i32,
|
||||
exp: i32,
|
||||
}{
|
||||
|
||||
@@ -17,7 +17,7 @@ fn sum(comptime T: type, arr: []T) T {
|
||||
test "sum" {
|
||||
var arr0 = [_]i32{ 1, 2, 3, 4 };
|
||||
var arr1 = [_]i32{};
|
||||
var tests = [_]struct {
|
||||
const tests = [_]struct {
|
||||
arr: []i32,
|
||||
exp: i32,
|
||||
}{
|
||||
@@ -32,7 +32,7 @@ test "sum" {
|
||||
};
|
||||
|
||||
for (tests) |t| {
|
||||
var n = sum(@TypeOf(t.exp), t.arr);
|
||||
const n = sum(@TypeOf(t.exp), t.arr);
|
||||
try expect(n == t.exp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fn sum(comptime T: type, list: []T) T {
|
||||
test "sum" {
|
||||
var arr0 = [_]i32{ 1, 2, 3, 4 };
|
||||
var arr1 = [_]i32{};
|
||||
var tests = [_]struct {
|
||||
const tests = [_]struct {
|
||||
arr: []i32,
|
||||
exp: i32,
|
||||
}{
|
||||
@@ -31,7 +31,7 @@ test "sum" {
|
||||
};
|
||||
|
||||
for (tests) |t| {
|
||||
var n = sum(@TypeOf(t.exp), t.arr);
|
||||
const n = sum(@TypeOf(t.exp), t.arr);
|
||||
try expect(n == t.exp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ test "count" {
|
||||
var arr0 = [_]i32{};
|
||||
var arr1 = [_]i32{42};
|
||||
var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
var tests = [_]struct {
|
||||
const tests = [_]struct {
|
||||
arr: []i32,
|
||||
exp: i32,
|
||||
}{
|
||||
|
||||
@@ -32,8 +32,8 @@ fn quicksort(comptime T: type, allocator: mem.Allocator, s: []const T) anyerror!
|
||||
}
|
||||
}
|
||||
|
||||
var low = try quicksort(T, allocator, lower.items);
|
||||
var high = try quicksort(T, allocator, higher.items);
|
||||
const low = try quicksort(T, allocator, lower.items);
|
||||
const high = try quicksort(T, allocator, higher.items);
|
||||
|
||||
var res = std.ArrayList(T).init(allocator);
|
||||
try res.appendSlice(low);
|
||||
@@ -44,13 +44,8 @@ fn quicksort(comptime T: type, allocator: mem.Allocator, s: []const T) anyerror!
|
||||
}
|
||||
|
||||
test "quicksort" {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer {
|
||||
arena.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
}
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const tests = [_]struct {
|
||||
s: []const u8,
|
||||
@@ -71,9 +66,9 @@ test "quicksort" {
|
||||
};
|
||||
|
||||
for (tests) |t| {
|
||||
var res = try quicksort(u8, arena.allocator(), t.s);
|
||||
const res = try quicksort(u8, arena.allocator(), t.s);
|
||||
try expect(res.len == t.exp.len);
|
||||
for (res) |e, i|
|
||||
for (res, 0..) |e, i|
|
||||
try expect(e == t.exp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,32 +4,31 @@ const expect = std.testing.expect;
|
||||
const heap = std.heap;
|
||||
const mem = std.mem;
|
||||
|
||||
pub const io_mode = .evented;
|
||||
|
||||
pub const Error = error{OutOfMemory};
|
||||
// pub const io_mode = .evented;
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
|
||||
var s = [_]u8{ 5, 3, 6, 2, 10 };
|
||||
var u = [_]u8{ 5, 3, 6, 2, 10 };
|
||||
|
||||
print("{d}\n", .{try quicksort(arena.allocator(), &s)});
|
||||
var s = try std.ArrayList(u8).initCapacity(arena.allocator(), u.len);
|
||||
try quicksort(u8, arena.allocator(), &u, &s);
|
||||
print("{d}\n", .{s.items});
|
||||
}
|
||||
|
||||
// NOTE: this async version cannot be generic because allocating a frame for a
|
||||
// generic function is not trivial.
|
||||
fn quicksort(allocator: mem.Allocator, s: []const u8) Error![]const u8 {
|
||||
if (s.len < 2) {
|
||||
return s;
|
||||
fn quicksort(comptime T: type, allocator: mem.Allocator, u: []const T, s: *std.ArrayList(T)) !void {
|
||||
if (u.len < 2) {
|
||||
try s.appendSlice(u);
|
||||
return;
|
||||
}
|
||||
|
||||
var lower = std.ArrayList(u8).init(allocator);
|
||||
var higher = std.ArrayList(u8).init(allocator);
|
||||
var lower = std.ArrayList(T).init(allocator);
|
||||
var higher = std.ArrayList(T).init(allocator);
|
||||
|
||||
const pivot = s[0];
|
||||
for (s[1..]) |item| {
|
||||
const pivot = u[0];
|
||||
for (u[1..]) |item| {
|
||||
if (item <= pivot) {
|
||||
try lower.append(item);
|
||||
} else {
|
||||
@@ -37,27 +36,41 @@ fn quicksort(allocator: mem.Allocator, s: []const u8) Error![]const u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const low_frame = try allocator.create(@Frame(quicksort));
|
||||
low_frame.* = async quicksort(allocator, lower.items);
|
||||
var high = try quicksort(allocator, higher.items);
|
||||
var low = try await low_frame;
|
||||
// NOTE: zig has temporary removed the async/await syntax since v0.11.0
|
||||
//
|
||||
// const low_frame = try allocator.create(@Frame(quicksort));
|
||||
// low_frame.* = async quicksort(allocator, lower.items);
|
||||
// const high = try quicksort(allocator, higher.items);
|
||||
// const low = try await low_frame;
|
||||
|
||||
var res = std.ArrayList(u8).init(allocator);
|
||||
try res.appendSlice(low);
|
||||
try res.append(pivot);
|
||||
try res.appendSlice(high);
|
||||
var low = try std.ArrayList(T).initCapacity(allocator, lower.items.len);
|
||||
var high = try std.ArrayList(T).initCapacity(allocator, higher.items.len);
|
||||
|
||||
return res.items;
|
||||
var low_handle = try std.Thread.spawn(
|
||||
.{},
|
||||
quicksort,
|
||||
.{ T, allocator, lower.items, &low },
|
||||
);
|
||||
var high_handle = try std.Thread.spawn(
|
||||
.{},
|
||||
quicksort,
|
||||
.{ T, allocator, higher.items, &high },
|
||||
);
|
||||
low_handle.join();
|
||||
high_handle.join();
|
||||
|
||||
const lows = try low.toOwnedSlice();
|
||||
const highs = try high.toOwnedSlice();
|
||||
try s.appendSlice(lows);
|
||||
try s.append(pivot);
|
||||
try s.appendSlice(highs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
test "quicksort" {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer {
|
||||
arena.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
}
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const tests = [_]struct {
|
||||
s: []const u8,
|
||||
@@ -78,9 +91,10 @@ test "quicksort" {
|
||||
};
|
||||
|
||||
for (tests) |t| {
|
||||
var res = try quicksort(arena.allocator(), t.s);
|
||||
try expect(res.len == t.exp.len);
|
||||
for (res) |e, i|
|
||||
try expect(e == t.exp[i]);
|
||||
var res = std.ArrayList(u8).init(arena.allocator());
|
||||
try quicksort(u8, arena.allocator(), t.s, &res);
|
||||
try expect(res.items.len == t.exp.len); // length not matching
|
||||
for (res.items, 0..) |e, i|
|
||||
try expect(e == t.exp[i]); // element not matching
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ fn search(
|
||||
var arena = heap.ArenaAllocator.init(allocator);
|
||||
defer arena.deinit();
|
||||
var searched = std.BufSet.init(arena.allocator());
|
||||
const Q = std.TailQueue([]const u8);
|
||||
const Q = std.DoublyLinkedList([]const u8);
|
||||
var queue = Q{};
|
||||
|
||||
var name_edges = graph.get(name);
|
||||
const name_edges = graph.get(name);
|
||||
if (name_edges) |edges| {
|
||||
var nodes = try arena.allocator().alloc(Q.Node, edges.len);
|
||||
var i: usize = 0;
|
||||
@@ -53,14 +53,15 @@ fn search(
|
||||
}
|
||||
|
||||
while (queue.len > 0) {
|
||||
var first = queue.popFirst();
|
||||
if (first) |person| {
|
||||
if (!searched.contains(person.data)) {
|
||||
const person = queue.popFirst() orelse unreachable; // we always have at least one node if len > 0
|
||||
if (searched.contains(person.data)) {
|
||||
continue;
|
||||
}
|
||||
if (personIsSeller(person.data)) {
|
||||
std.debug.print("{s} is a mango seller!\n", .{person.data});
|
||||
return;
|
||||
} else {
|
||||
var ee = graph.get(person.data);
|
||||
}
|
||||
const ee = graph.get(person.data);
|
||||
if (ee) |edges| {
|
||||
var nodes = try arena.allocator().alloc(Q.Node, edges.len);
|
||||
var i: usize = 0;
|
||||
@@ -73,9 +74,6 @@ fn search(
|
||||
}
|
||||
try searched.insert(person.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn personIsSeller(name: []const u8) bool {
|
||||
@@ -83,14 +81,9 @@ fn personIsSeller(name: []const u8) bool {
|
||||
}
|
||||
|
||||
test "search" {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
|
||||
var graph = std.StringHashMap([][]const u8).init(gpa.allocator());
|
||||
defer {
|
||||
graph.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
}
|
||||
const allocator = std.testing.allocator;
|
||||
var graph = std.StringHashMap([][]const u8).init(allocator);
|
||||
defer graph.deinit();
|
||||
|
||||
var you = [_][]const u8{ "alice", "bob", "claire" };
|
||||
var bob = [_][]const u8{ "anuj", "peggy" };
|
||||
@@ -110,5 +103,5 @@ test "search" {
|
||||
try graph.put("thom", &thom);
|
||||
try graph.put("jonny", &jonny);
|
||||
|
||||
try search(gpa.allocator(), &graph, "you");
|
||||
try search(allocator, &graph, "you");
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func main() {
|
||||
// Go through all the neighbors of this node.
|
||||
neighbors := graph[node]
|
||||
|
||||
for node, _ := range neighbors {
|
||||
for node := range neighbors {
|
||||
new_cost := cost + neighbors[node]
|
||||
// If it's cheaper to get to this neighbor by going through this node...
|
||||
if costs[node] > new_cost {
|
||||
@@ -71,7 +71,7 @@ func find_lowest_cost_node(costs map[string]float64) string {
|
||||
lowest_cost := math.Inf(1)
|
||||
lowest_cost_node := ""
|
||||
|
||||
for node, _ := range costs {
|
||||
for node := range costs {
|
||||
// fmt.Println("Node:", node, "Value:", value)
|
||||
cost := costs[node]
|
||||
// If it's the lowest cost so far and hasn't been processed yet...
|
||||
|
||||
@@ -6,109 +6,109 @@ pub fn main() !void {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator());
|
||||
var graph = std.StringHashMap(*std.StringHashMap(f32)).init(alloc);
|
||||
|
||||
var start = std.StringHashMap(f32).init(arena.allocator());
|
||||
var start = std.StringHashMap(f32).init(alloc);
|
||||
try start.put("a", 6);
|
||||
try start.put("b", 2);
|
||||
try start.put("c", 42);
|
||||
try graph.put("start", &start);
|
||||
|
||||
var a = std.StringHashMap(f32).init(arena.allocator());
|
||||
var a = std.StringHashMap(f32).init(alloc);
|
||||
try a.put("finish", 1);
|
||||
try graph.put("a", &a);
|
||||
|
||||
var b = std.StringHashMap(f32).init(arena.allocator());
|
||||
var b = std.StringHashMap(f32).init(alloc);
|
||||
try b.put("a", 3);
|
||||
try b.put("finish", 5);
|
||||
try graph.put("b", &b);
|
||||
|
||||
var fin = std.StringHashMap(f32).init(arena.allocator());
|
||||
var c = std.StringHashMap(f32).init(alloc);
|
||||
try c.put("finish", 42);
|
||||
try graph.put("c", &c);
|
||||
|
||||
var fin = std.StringHashMap(f32).init(alloc);
|
||||
try graph.put("finish", &fin);
|
||||
|
||||
var result = try dijkstra(arena.allocator(), &graph, "start", "finish");
|
||||
var costs, var path = try dijkstra(alloc, &graph, "start", "finish");
|
||||
|
||||
std.debug.print("Cost from the start to each node:\n", .{});
|
||||
var costs_it = result.costs.iterator();
|
||||
while (costs_it.next()) |cost| {
|
||||
std.debug.print("{s}: {d} ", .{ cost.key_ptr.*, cost.value_ptr.* });
|
||||
// Traverse the path hashmap backwards from finish to start and store the
|
||||
// steps in an ordered list.
|
||||
// The hashmap is unordered so there is no guarantee to print the path in
|
||||
// the correct order only by iterating through key/value(s).
|
||||
var dir = std.ArrayList([]const u8).init(alloc);
|
||||
|
||||
var v: []const u8 = "finish";
|
||||
try dir.append(v);
|
||||
var node = path.get(v);
|
||||
while (node) |n| : (node = path.get(v)) {
|
||||
try dir.append(n.?);
|
||||
v = n.?;
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("Path from start to finish:\n", .{});
|
||||
var path_it = result.path.iterator();
|
||||
while (path_it.next()) |parent| {
|
||||
std.debug.print("{s} = {?s}\n", .{ parent.key_ptr.*, parent.value_ptr.* });
|
||||
std.debug.print("start =(", .{});
|
||||
var i = dir.items.len - 2;
|
||||
var prev_cost: f32 = 0;
|
||||
while (i > 0) : (i -= 1) {
|
||||
const d = dir.items[i];
|
||||
const cost = costs.get(d).?;
|
||||
std.debug.print("{d})=> {s:<6}: {d}\n{s:<5} =(", .{ cost - prev_cost, d, cost, d });
|
||||
prev_cost = cost;
|
||||
}
|
||||
const fin_cost = costs.get("finish").?;
|
||||
std.debug.print("{d})=> finish: {d}\n", .{ fin_cost - prev_cost, fin_cost });
|
||||
}
|
||||
|
||||
/// this struct is needed because coercing an anonymous struct literal to an
|
||||
/// error union is not supported by zig yet. Once this is fixed (with the
|
||||
/// self-hosted compiler, see https://github.com/ziglang/zig/issues/11443), the
|
||||
/// dijkstra function could just return:
|
||||
/// ```zig
|
||||
/// return {
|
||||
/// .costs = costs,
|
||||
/// .path = parents,
|
||||
/// };
|
||||
/// ```
|
||||
const dijkstraResult = struct {
|
||||
costs: std.StringHashMap(f32),
|
||||
path: std.StringHashMap(?[]const u8),
|
||||
};
|
||||
|
||||
/// applies the dijkstra algorithm on the provided graph using
|
||||
/// the provided start anf finish nodes.
|
||||
/// applies the dijkstra algorithm on graph using start and finish nodes.
|
||||
/// Returns a tuple with the costs and the path.
|
||||
fn dijkstra(
|
||||
allocator: mem.Allocator,
|
||||
graph: *std.StringHashMap(*std.StringHashMap(f32)),
|
||||
start: []const u8,
|
||||
finish: []const u8,
|
||||
) !dijkstraResult {
|
||||
) !struct {
|
||||
std.StringHashMap(f32), // costs
|
||||
std.StringHashMap(?[]const u8), // path
|
||||
} {
|
||||
var costs = std.StringHashMap(f32).init(allocator);
|
||||
var parents = std.StringHashMap(?[]const u8).init(allocator);
|
||||
try costs.put(finish, std.math.inf_f32);
|
||||
try costs.put(finish, std.math.inf(f32));
|
||||
try parents.put(finish, null);
|
||||
|
||||
// initialize costs and parents maps for the nodes having start as parent
|
||||
var start_graph = graph.get(start);
|
||||
if (start_graph) |sg| {
|
||||
var it = sg.iterator();
|
||||
while (it.next()) |elem| {
|
||||
try costs.put(elem.key_ptr.*, elem.value_ptr.*);
|
||||
const start_graph = graph.get(start) orelse return error.MissingNode;
|
||||
var sg_it = start_graph.iterator();
|
||||
while (sg_it.next()) |elem| {
|
||||
try parents.put(elem.key_ptr.*, start);
|
||||
}
|
||||
try costs.put(elem.key_ptr.*, elem.value_ptr.*);
|
||||
}
|
||||
|
||||
var processed = std.BufSet.init(allocator);
|
||||
|
||||
var n = findCheapestNode(&costs, &processed);
|
||||
while (n) |node| : (n = findCheapestNode(&costs, &processed)) {
|
||||
var cost = costs.get(node).?;
|
||||
var neighbors = graph.get(node);
|
||||
if (neighbors) |nbors| {
|
||||
var it = nbors.iterator();
|
||||
const cost = costs.get(node).?;
|
||||
const neighbors = graph.get(node) orelse return error.MissingNode;
|
||||
var it = neighbors.iterator();
|
||||
while (it.next()) |neighbor| {
|
||||
var new_cost = cost + neighbor.value_ptr.*;
|
||||
const new_cost = cost + neighbor.value_ptr.*;
|
||||
if (costs.get(neighbor.key_ptr.*).? > new_cost) {
|
||||
// update maps if we found a cheaper path
|
||||
try costs.put(neighbor.key_ptr.*, new_cost);
|
||||
try parents.put(neighbor.key_ptr.*, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
try processed.insert(node);
|
||||
}
|
||||
|
||||
return dijkstraResult{
|
||||
.costs = costs,
|
||||
.path = parents,
|
||||
};
|
||||
return .{ costs, parents };
|
||||
}
|
||||
|
||||
/// finds the cheapest node among the not yet processed ones.
|
||||
fn findCheapestNode(costs: *std.StringHashMap(f32), processed: *std.BufSet) ?[]const u8 {
|
||||
var lowest_cost = std.math.inf_f32;
|
||||
var lowest_cost = std.math.inf(f32);
|
||||
var lowest_cost_node: ?[]const u8 = null;
|
||||
|
||||
var it = costs.iterator();
|
||||
@@ -123,39 +123,35 @@ fn findCheapestNode(costs: *std.StringHashMap(f32), processed: *std.BufSet) ?[]c
|
||||
}
|
||||
|
||||
test "dijkstra" {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer {
|
||||
arena.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
}
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator());
|
||||
var graph = std.StringHashMap(*std.StringHashMap(f32)).init(alloc);
|
||||
|
||||
var start = std.StringHashMap(f32).init(arena.allocator());
|
||||
var start = std.StringHashMap(f32).init(alloc);
|
||||
try start.put("a", 6);
|
||||
try start.put("b", 2);
|
||||
try graph.put("start", &start);
|
||||
|
||||
var a = std.StringHashMap(f32).init(arena.allocator());
|
||||
var a = std.StringHashMap(f32).init(alloc);
|
||||
try a.put("finish", 1);
|
||||
try graph.put("a", &a);
|
||||
|
||||
var b = std.StringHashMap(f32).init(arena.allocator());
|
||||
var b = std.StringHashMap(f32).init(alloc);
|
||||
try b.put("a", 3);
|
||||
try b.put("finish", 5);
|
||||
try graph.put("b", &b);
|
||||
|
||||
var fin = std.StringHashMap(f32).init(arena.allocator());
|
||||
var fin = std.StringHashMap(f32).init(alloc);
|
||||
try graph.put("finish", &fin);
|
||||
|
||||
var result = try dijkstra(arena.allocator(), &graph, "start", "finish");
|
||||
var costs, var path = try dijkstra(alloc, &graph, "start", "finish");
|
||||
|
||||
try std.testing.expectEqual(result.costs.get("a").?, 5);
|
||||
try std.testing.expectEqual(result.costs.get("b").?, 2);
|
||||
try std.testing.expectEqual(result.costs.get("finish").?, 6);
|
||||
try std.testing.expectEqual(result.path.get("b").?, "start");
|
||||
try std.testing.expectEqual(result.path.get("a").?, "b");
|
||||
try std.testing.expectEqual(result.path.get("finish").?, "a");
|
||||
try std.testing.expectEqual(costs.get("a").?, 5);
|
||||
try std.testing.expectEqual(costs.get("b").?, 2);
|
||||
try std.testing.expectEqual(costs.get("finish").?, 6);
|
||||
try std.testing.expectEqual(path.get("b").?, "start");
|
||||
try std.testing.expectEqual(path.get("a").?, "b");
|
||||
try std.testing.expectEqual(path.get("finish").?, "a");
|
||||
}
|
||||
|
||||
@@ -7,43 +7,45 @@ pub fn main() !void {
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
|
||||
var states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
|
||||
var states_needed = std.BufSet.init(arena.allocator());
|
||||
const ally = arena.allocator();
|
||||
const states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
|
||||
var states_needed = std.BufSet.init(ally);
|
||||
for (states_needed_array) |sn| {
|
||||
try states_needed.insert(sn);
|
||||
}
|
||||
|
||||
var stations = std.StringHashMap(*std.BufSet).init(arena.allocator());
|
||||
var stations = std.StringHashMap(*std.BufSet).init(ally);
|
||||
|
||||
var kone = std.BufSet.init(arena.allocator());
|
||||
try kone.insert("id");
|
||||
try kone.insert("nv");
|
||||
try kone.insert("ut");
|
||||
try stations.put("kone", &kone);
|
||||
var k_one = std.BufSet.init(ally);
|
||||
try k_one.insert("id");
|
||||
try k_one.insert("nv");
|
||||
try k_one.insert("ut");
|
||||
|
||||
var ktwo = std.BufSet.init(arena.allocator());
|
||||
try ktwo.insert("wa");
|
||||
try ktwo.insert("id");
|
||||
try ktwo.insert("mt");
|
||||
try stations.put("ktwo", &ktwo);
|
||||
var k_two = std.BufSet.init(ally);
|
||||
try k_two.insert("wa");
|
||||
try k_two.insert("id");
|
||||
try k_two.insert("mt");
|
||||
|
||||
var kthree = std.BufSet.init(arena.allocator());
|
||||
try kthree.insert("or");
|
||||
try kthree.insert("nv");
|
||||
try kthree.insert("ca");
|
||||
try stations.put("kthree", &kthree);
|
||||
var k_three = std.BufSet.init(ally);
|
||||
try k_three.insert("or");
|
||||
try k_three.insert("nv");
|
||||
try k_three.insert("ca");
|
||||
|
||||
var kfour = std.BufSet.init(arena.allocator());
|
||||
try kfour.insert("nv");
|
||||
try kfour.insert("ut");
|
||||
try stations.put("kfour", &kfour);
|
||||
var k_four = std.BufSet.init(ally);
|
||||
try k_four.insert("nv");
|
||||
try k_four.insert("ut");
|
||||
|
||||
var kfive = std.BufSet.init(arena.allocator());
|
||||
try kfive.insert("ca");
|
||||
try kfive.insert("az");
|
||||
try stations.put("kfive", &kfive);
|
||||
var k_five = std.BufSet.init(ally);
|
||||
try k_five.insert("ca");
|
||||
try k_five.insert("az");
|
||||
|
||||
var stations_covering = try setCovering(arena.allocator(), &stations, &states_needed);
|
||||
try stations.put("kone", &k_one);
|
||||
try stations.put("ktwo", &k_two);
|
||||
try stations.put("kthree", &k_three);
|
||||
try stations.put("kfour", &k_four);
|
||||
try stations.put("kfive", &k_five);
|
||||
|
||||
const stations_covering = try setCovering(ally, &stations, &states_needed);
|
||||
|
||||
for (stations_covering) |sc| {
|
||||
std.debug.print("{s}\n", .{sc});
|
||||
@@ -59,8 +61,8 @@ fn setCovering(allocator: mem.Allocator, stations: *std.StringHashMap(*std.BufSe
|
||||
|
||||
var it = stations.iterator();
|
||||
while (it.next()) |station| {
|
||||
var covered = &std.ArrayList([]const u8).init(allocator);
|
||||
try intersect(states_needed, station.value_ptr.*, covered);
|
||||
var covered = std.ArrayList([]const u8).init(allocator);
|
||||
try intersect(states_needed, station.value_ptr.*, &covered);
|
||||
if (covered.items.len > states_covered.len) {
|
||||
best_station = station.key_ptr.*;
|
||||
states_covered = covered.items;
|
||||
@@ -80,10 +82,61 @@ fn setCovering(allocator: mem.Allocator, stations: *std.StringHashMap(*std.BufSe
|
||||
return final_array.toOwnedSlice();
|
||||
}
|
||||
|
||||
test "setCovering" {
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const ally = arena.allocator();
|
||||
|
||||
const states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
|
||||
var states_needed = std.BufSet.init(ally);
|
||||
for (states_needed_array) |sn| {
|
||||
try states_needed.insert(sn);
|
||||
}
|
||||
|
||||
var stations = std.StringHashMap(*std.BufSet).init(ally);
|
||||
|
||||
var kone = std.BufSet.init(ally);
|
||||
try kone.insert("id");
|
||||
try kone.insert("nv");
|
||||
try kone.insert("ut");
|
||||
try stations.put("kone", &kone);
|
||||
|
||||
var ktwo = std.BufSet.init(ally);
|
||||
try ktwo.insert("wa");
|
||||
try ktwo.insert("id");
|
||||
try ktwo.insert("mt");
|
||||
try stations.put("ktwo", &ktwo);
|
||||
|
||||
var kthree = std.BufSet.init(ally);
|
||||
try kthree.insert("or");
|
||||
try kthree.insert("nv");
|
||||
try kthree.insert("ca");
|
||||
try stations.put("kthree", &kthree);
|
||||
|
||||
var kfour = std.BufSet.init(ally);
|
||||
try kfour.insert("nv");
|
||||
try kfour.insert("ut");
|
||||
try stations.put("kfour", &kfour);
|
||||
|
||||
var kfive = std.BufSet.init(ally);
|
||||
try kfive.insert("ca");
|
||||
try kfive.insert("az");
|
||||
try stations.put("kfive", &kfive);
|
||||
|
||||
const stations_covering = try setCovering(ally, &stations, &states_needed);
|
||||
|
||||
// The order of the keys in the hashmap affects the final result.
|
||||
// StringHashMap always produces the same order and we can assert over it.
|
||||
const expectedStations = &[_][]const u8{ "kfour", "ktwo", "kthree", "kfive" };
|
||||
for (stations_covering, 0..) |sc, i| {
|
||||
try std.testing.expectEqualStrings(expectedStations[i], sc);
|
||||
}
|
||||
}
|
||||
|
||||
fn intersect(left: *std.BufSet, right: *std.BufSet, intersection: *std.ArrayList([]const u8)) !void {
|
||||
var l_it = left.iterator();
|
||||
var r_it = right.iterator();
|
||||
while (l_it.next()) |l| {
|
||||
var r_it = right.iterator();
|
||||
while (r_it.next()) |r| {
|
||||
if (std.mem.eql(u8, l.*, r.*)) {
|
||||
try intersection.append(l.*);
|
||||
@@ -92,6 +145,54 @@ fn intersect(left: *std.BufSet, right: *std.BufSet, intersection: *std.ArrayList
|
||||
}
|
||||
}
|
||||
|
||||
test "intersect" {
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const ally = arena.allocator();
|
||||
|
||||
var left = std.BufSet.init(ally);
|
||||
try left.insert("banana");
|
||||
try left.insert("mango");
|
||||
try left.insert("papaya");
|
||||
|
||||
var right = std.BufSet.init(ally);
|
||||
try right.insert("banana");
|
||||
try right.insert("mango");
|
||||
try right.insert("avocado");
|
||||
|
||||
{
|
||||
// partial intersection
|
||||
const expected = &[2][]const u8{ "banana", "mango" };
|
||||
var actual = std.ArrayList([]const u8).init(ally);
|
||||
|
||||
try intersect(&left, &right, &actual);
|
||||
|
||||
for (actual.items, expected) |a, e| {
|
||||
try std.testing.expectEqualStrings(e, a);
|
||||
}
|
||||
}
|
||||
{
|
||||
// full intersection
|
||||
const expected = &[3][]const u8{ "banana", "mango", "papaya" };
|
||||
var actual = std.ArrayList([]const u8).init(ally);
|
||||
|
||||
try intersect(&left, &left, &actual);
|
||||
|
||||
for (actual.items, expected) |a, e| {
|
||||
try std.testing.expectEqualStrings(e, a);
|
||||
}
|
||||
}
|
||||
{
|
||||
// no intersection
|
||||
var empty = std.BufSet.init(ally);
|
||||
var actual = std.ArrayList([]const u8).init(ally);
|
||||
|
||||
try intersect(&left, &empty, &actual);
|
||||
|
||||
try std.testing.expect(actual.items.len == 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn difference(lessening: *std.BufSet, subtracting: [][]const u8) void {
|
||||
var less_it = lessening.iterator();
|
||||
|
||||
@@ -104,55 +205,52 @@ fn difference(lessening: *std.BufSet, subtracting: [][]const u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
test "setCovering" {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer {
|
||||
arena.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
test "difference" {
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const ally = arena.allocator();
|
||||
|
||||
{
|
||||
// partial diff
|
||||
var less = std.BufSet.init(ally);
|
||||
try less.insert("banana");
|
||||
try less.insert("mango");
|
||||
try less.insert("papaya");
|
||||
|
||||
var sub = [_][]const u8{ "banana", "mango" };
|
||||
|
||||
difference(&less, &sub);
|
||||
|
||||
try std.testing.expect(less.count() == 1);
|
||||
try std.testing.expect(less.contains("papaya"));
|
||||
}
|
||||
{
|
||||
// full diff
|
||||
var less = std.BufSet.init(ally);
|
||||
try less.insert("banana");
|
||||
try less.insert("mango");
|
||||
try less.insert("papaya");
|
||||
|
||||
var states_needed_array = [_][]const u8{ "mt", "wa", "or", "id", "nv", "ut", "ca", "az" };
|
||||
var states_needed = std.BufSet.init(arena.allocator());
|
||||
for (states_needed_array) |sn| {
|
||||
try states_needed.insert(sn);
|
||||
var sub = [_][]const u8{ "avocado", "kiwi", "ananas" };
|
||||
|
||||
difference(&less, &sub);
|
||||
|
||||
try std.testing.expect(less.count() == 3);
|
||||
try std.testing.expect(less.contains("banana"));
|
||||
try std.testing.expect(less.contains("mango"));
|
||||
try std.testing.expect(less.contains("papaya"));
|
||||
}
|
||||
{
|
||||
// no diff
|
||||
var less = std.BufSet.init(ally);
|
||||
try less.insert("banana");
|
||||
try less.insert("mango");
|
||||
try less.insert("papaya");
|
||||
|
||||
var stations = std.StringHashMap(*std.BufSet).init(arena.allocator());
|
||||
var sub = [_][]const u8{ "mango", "papaya", "banana" };
|
||||
|
||||
var kone = std.BufSet.init(arena.allocator());
|
||||
try kone.insert("id");
|
||||
try kone.insert("nv");
|
||||
try kone.insert("ut");
|
||||
try stations.put("kone", &kone);
|
||||
difference(&less, &sub);
|
||||
|
||||
var ktwo = std.BufSet.init(arena.allocator());
|
||||
try ktwo.insert("wa");
|
||||
try ktwo.insert("id");
|
||||
try ktwo.insert("mt");
|
||||
try stations.put("ktwo", &ktwo);
|
||||
|
||||
var kthree = std.BufSet.init(arena.allocator());
|
||||
try kthree.insert("or");
|
||||
try kthree.insert("nv");
|
||||
try kthree.insert("ca");
|
||||
try stations.put("kthree", &kthree);
|
||||
|
||||
var kfour = std.BufSet.init(arena.allocator());
|
||||
try kfour.insert("nv");
|
||||
try kfour.insert("ut");
|
||||
try stations.put("kfour", &kfour);
|
||||
|
||||
var kfive = std.BufSet.init(arena.allocator());
|
||||
try kfive.insert("ca");
|
||||
try kfive.insert("az");
|
||||
try stations.put("kfive", &kfive);
|
||||
|
||||
var stations_covering = try setCovering(arena.allocator(), &stations, &states_needed);
|
||||
|
||||
var expectedStations = &[_][]const u8{ "kone", "ktwo", "kfive", "kthree" };
|
||||
for (stations_covering) |sc, i| {
|
||||
try std.testing.expectEqualStrings(expectedStations[i], sc);
|
||||
try std.testing.expect(less.count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,20 @@ const std = @import("std");
|
||||
const heap = std.heap;
|
||||
const math = std.math;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
|
||||
var n = try subsequence(arena.allocator(), "fish", "fosh");
|
||||
std.debug.print("{d}\n", .{n});
|
||||
const n, const sub = try subsequence(arena.allocator(), "fish", "fosh");
|
||||
std.debug.print("{d}: {s}\n", .{ n, sub });
|
||||
}
|
||||
|
||||
fn subsequence(allocator: std.mem.Allocator, a: []const u8, b: []const u8) !u32 {
|
||||
fn subsequence(allocator: std.mem.Allocator, a: []const u8, b: []const u8) !struct { u32, []const u8 } {
|
||||
var grid = try allocator.alloc([]u32, a.len + 1);
|
||||
var subseq = try std.ArrayList(u8).initCapacity(allocator, @max(a.len, b.len));
|
||||
|
||||
for (grid) |*row| {
|
||||
row.* = try allocator.alloc(u32, b.len + 1);
|
||||
@@ -28,36 +30,34 @@ fn subsequence(allocator: std.mem.Allocator, a: []const u8, b: []const u8) !u32
|
||||
while (j <= b.len) : (j += 1) {
|
||||
if (a[i - 1] == b[j - 1]) {
|
||||
grid[i][j] = grid[i - 1][j - 1] + 1;
|
||||
try subseq.append(a[i - 1]);
|
||||
} else {
|
||||
grid[i][j] = math.max(grid[i][j - 1], grid[i - 1][j]);
|
||||
grid[i][j] = @max(grid[i][j - 1], grid[i - 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grid[a.len][b.len];
|
||||
const sub = try subseq.toOwnedSlice();
|
||||
return .{ grid[a.len][b.len], sub };
|
||||
}
|
||||
|
||||
test "subsequence" {
|
||||
var tests = [_]struct {
|
||||
const tests = [_]struct {
|
||||
a: []const u8,
|
||||
b: []const u8,
|
||||
exp: u32,
|
||||
expected: struct { u32, []const u8 },
|
||||
}{
|
||||
.{ .a = "abc", .b = "abcd", .exp = 3 },
|
||||
.{ .a = "pera", .b = "mela", .exp = 2 },
|
||||
.{ .a = "banana", .b = "kiwi", .exp = 0 },
|
||||
.{ .a = "abc", .b = "abcd", .expected = .{ 3, "abc" } },
|
||||
.{ .a = "pera", .b = "mela", .expected = .{ 2, "ea" } },
|
||||
.{ .a = "banana", .b = "kiwi", .expected = .{ 0, "" } },
|
||||
};
|
||||
|
||||
for (tests) |t| {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer {
|
||||
arena.deinit();
|
||||
const leaked = gpa.deinit();
|
||||
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
|
||||
}
|
||||
var arena = heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var n = try subsequence(arena.allocator(), t.a, t.b);
|
||||
try expect(n == t.exp);
|
||||
const actual = try subsequence(arena.allocator(), t.a, t.b);
|
||||
|
||||
try std.testing.expectEqualDeep(t.expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user