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:
Paolo Grisoli
2024-12-07 14:29:48 +01:00
committed by GitHub
parent 177581a9a4
commit 8a13efde83
13 changed files with 357 additions and 260 deletions

View File

@@ -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;

View File

@@ -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]);
}

View File

@@ -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,
}{

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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,
}{

View File

@@ -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]);
}
}

View File

@@ -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
}
}

View File

@@ -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;
@@ -74,23 +75,15 @@ fn search(
try searched.insert(person.data);
}
}
}
}
}
fn personIsSeller(name: []const u8) bool {
return name[name.len - 1] == 'm';
}
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");
}

View File

@@ -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...

View File

@@ -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");
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}