diff --git a/.gitignore b/.gitignore index c79d826..0621ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ highlights/node_modules highlights/atom-language-perl6/ .DS_store highlights/package-lock.json +zig-cache # IDE specific .scala_dependencies @@ -41,4 +42,4 @@ highlights/package-lock.json /.env atlassian-ide-plugin.xml __pycache__ -.vscode \ No newline at end of file +.vscode diff --git a/01_introduction_to_algorithms/zig/binary-search.zig b/01_introduction_to_algorithms/zig/binary-search.zig new file mode 100644 index 0000000..f0e9123 --- /dev/null +++ b/01_introduction_to_algorithms/zig/binary-search.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; + +pub fn main() void { + const my_list = &[_]i8{ 1, 3, 5, 7, 9 }; + + print("{?}\n", .{binarySearch(i8, my_list, 3)}); + print("{?}\n", .{binarySearch(i8, my_list, -1)}); +} + +fn binarySearch(comptime T: type, list: []const T, item: T) ?usize { + var low: i32 = 0; + var high: i32 = @intCast(i32, list.len) - 1; + + return while (low <= high) { + var mid = @divTrunc((low + high), 2); + var m = @intCast(usize, mid); + var guess = list[m]; + if (guess == item) break m; + if (guess > item) { + high = mid - 1; + } else low = mid + 1; + } else null; +} + +test "binarySearch" { + const my_list = &[_]i8{ 1, 3, 5, 7, 9 }; + + var i = binarySearch(i8, my_list, 3); + try expect(i != null); + try expect(i.? == 1); + + i = binarySearch(i8, my_list, -1); + try expect(i == null); +} diff --git a/02_selection_sort/zig/selection_sort.zig b/02_selection_sort/zig/selection_sort.zig new file mode 100644 index 0000000..19a2c10 --- /dev/null +++ b/02_selection_sort/zig/selection_sort.zig @@ -0,0 +1,35 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; + +pub fn main() !void { + var s = [_]i32{ 5, 3, 6, 2, 10 }; + + selectionSort(i32, s[0..]); + print("{d}\n", .{s}); +} + +fn selectionSort(comptime T: type, list: []T) void { + for (list) |_, i| { + var j = i + 1; + while (j < list.len) : (j += 1) { + if (list[i] > list[j]) { + // swap + var tmp = list[i]; + list[i] = list[j]; + list[j] = tmp; + } + } + } +} + +test "selectionSort" { + var s = [_]i32{ 5, 3, 6, 2, 10 }; + const exp = [_]i32{ 2, 3, 5, 6, 10 }; + + selectionSort(i32, s[0..]); + + try expect(s.len == exp.len); + for (s) |e, i| + try expect(e == exp[i]); +} diff --git a/03_recursion/zig/01_countdown.zig b/03_recursion/zig/01_countdown.zig new file mode 100644 index 0000000..43fd2ee --- /dev/null +++ b/03_recursion/zig/01_countdown.zig @@ -0,0 +1,13 @@ +const print = @import("std").debug.print; + +fn countdown(comptime T: type, i: T) void { + print("{} ", .{i}); + if (i <= 0) { + print("\n", .{}); + return; + } else countdown(T, i - 1); +} + +pub fn main() void { + countdown(u32, 5); +} diff --git a/03_recursion/zig/02_greet.zig b/03_recursion/zig/02_greet.zig new file mode 100644 index 0000000..19b403d --- /dev/null +++ b/03_recursion/zig/02_greet.zig @@ -0,0 +1,20 @@ +const print = @import("std").debug.print; + +pub fn main() void { + greet("adit"); +} + +fn bye() void { + print("ok bye!\n", .{}); +} + +fn greet(name: []const u8) void { + print("hello, {s}!\n", .{name}); + greet2(name); + print("getting ready to say bye...\n", .{}); + bye(); +} + +fn greet2(name: []const u8) void { + print("how are you, {s}?\n", .{name}); +} diff --git a/03_recursion/zig/03_factorial.zig b/03_recursion/zig/03_factorial.zig new file mode 100644 index 0000000..6e015cb --- /dev/null +++ b/03_recursion/zig/03_factorial.zig @@ -0,0 +1,11 @@ +const print = @import("std").debug.print; + +fn fact(comptime T: type, x: T) T { + if (x == 1) { + return x; + } else return x * fact(T, x - 1); +} + +pub fn main() void { + print("{}\n", .{fact(i32, 5)}); +} diff --git a/03_recursion/zig/04_count.zig b/03_recursion/zig/04_count.zig new file mode 100644 index 0000000..914c4f2 --- /dev/null +++ b/03_recursion/zig/04_count.zig @@ -0,0 +1,40 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + var arr = [_]i32{ 4, 3, 2, 1 }; + print("{}\n", .{count(i32, arr[0..])}); +} + +fn count(comptime T: type, arr: []T) T { + if (arr.len == 0) { + return 0; + } else return 1 + count(T, arr[1..]); +} + +test "count" { + var arr0 = [_]i32{}; + var arr1 = [_]i32{42}; + var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var tests = [_]struct { + arr: []i32, + exp: i32, + }{ + .{ + .arr = &arr0, + .exp = 0, + }, + .{ + .arr = &arr1, + .exp = 1, + }, + .{ + .arr = &arr2, + .exp = 9, + }, + }; + + for (tests) |t| { + try expect(count(@TypeOf(t.exp), t.arr) == t.exp); + } +} diff --git a/03_recursion/zig/05_binary_search_recursive.zig b/03_recursion/zig/05_binary_search_recursive.zig new file mode 100644 index 0000000..99e521d --- /dev/null +++ b/03_recursion/zig/05_binary_search_recursive.zig @@ -0,0 +1,59 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + print("{}\n", .{binarySearch(i32, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2)}); +} + +fn binarySearch(comptime T: type, arr: []const T, target: T) bool { + switch (arr.len) { + 0 => return false, + 1 => return arr[0] == target, + else => { + const mid = arr.len / 2; + if (arr[mid] > target) { + return binarySearch(T, arr[0..mid], target); + } else { + return binarySearch(T, arr[mid..], target); + } + }, + } +} + +test "binary search recursive" { + const tests = [_]struct { + arr: []const i32, + target: i32, + exp: bool, + }{ + .{ + .arr = &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + .target = 7, + .exp = true, + }, + .{ + .arr = &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + .target = 42, + .exp = false, + }, + .{ + .arr = &[_]i32{42}, + .target = 42, + .exp = true, + }, + .{ + .arr = &[_]i32{1}, + .target = 42, + .exp = false, + }, + .{ + .arr = &[_]i32{}, + .target = 42, + .exp = false, + }, + }; + + for (tests) |t| { + try expect(binarySearch(@TypeOf(t.target), t.arr, t.target) == t.exp); + } +} diff --git a/03_recursion/zig/06_find_max.zig b/03_recursion/zig/06_find_max.zig new file mode 100644 index 0000000..58170ae --- /dev/null +++ b/03_recursion/zig/06_find_max.zig @@ -0,0 +1,47 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + print("{}\n", .{findMax(i32, &[_]i32{ 1, 2, 3, 4 })}); +} + +fn findMax(comptime T: type, arr: []const T) T { + switch (arr.len) { + 0 => return 0, + 1 => return arr[0], + else => { + const x = findMax(T, arr[1..]); + if (arr[0] > x) { + return arr[0]; + } else return x; + }, + } +} + +test "find max" { + const tests = [_]struct { + arr: []const i32, + exp: i32, + }{ + .{ + .arr = &[_]i32{ 1, 2, 3, 4 }, + .exp = 4, + }, + .{ + .arr = &[_]i32{ 8, 42, 3, 1 }, + .exp = 42, + }, + .{ + .arr = &[_]i32{42}, + .exp = 42, + }, + .{ + .arr = &[_]i32{}, + .exp = 0, + }, + }; + + for (tests) |t| { + try expect(findMax(@TypeOf(t.exp), t.arr) == t.exp); + } +} diff --git a/03_recursion/zig/07_sum_array.zig b/03_recursion/zig/07_sum_array.zig new file mode 100644 index 0000000..104c22d --- /dev/null +++ b/03_recursion/zig/07_sum_array.zig @@ -0,0 +1,38 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + print("{}\n", .{sumArray(i32, &[_]i32{ 1, 2, 3, 4 })}); +} + +fn sumArray(comptime T: type, arr: []const T) T { + switch (arr.len) { + 0 => return 0, + 1 => return arr[0], + else => return arr[0] + sumArray(T, arr[1..]), + } +} + +test "sum array" { + const tests = [_]struct { + arr: []const i32, + exp: i32, + }{ + .{ + .arr = &[_]i32{ 1, 2, 3, 4 }, + .exp = 10, + }, + .{ + .arr = &[_]i32{42}, + .exp = 42, + }, + .{ + .arr = &[_]i32{}, + .exp = 0, + }, + }; + + for (tests) |t| { + try expect(sumArray(@TypeOf(t.exp), t.arr) == t.exp); + } +} diff --git a/04_quicksort/zig/01_loop_sum.zig b/04_quicksort/zig/01_loop_sum.zig new file mode 100644 index 0000000..13c0d0a --- /dev/null +++ b/04_quicksort/zig/01_loop_sum.zig @@ -0,0 +1,38 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + var arr = [_]i32{ 1, 2, 3, 4 }; + print("{}\n", .{sum(i32, &arr)}); +} + +fn sum(comptime T: type, arr: []T) T { + var total: T = 0; + for (arr) |x| { + total += x; + } + return total; +} + +test "sum" { + var arr0 = [_]i32{ 1, 2, 3, 4 }; + var arr1 = [_]i32{}; + var tests = [_]struct { + arr: []i32, + exp: i32, + }{ + .{ + .arr = &arr0, + .exp = 10, + }, + .{ + .arr = &arr1, + .exp = 0, + }, + }; + + for (tests) |t| { + var n = sum(@TypeOf(t.exp), t.arr); + try expect(n == t.exp); + } +} diff --git a/04_quicksort/zig/02_recursive_sum.zig b/04_quicksort/zig/02_recursive_sum.zig new file mode 100644 index 0000000..a015ceb --- /dev/null +++ b/04_quicksort/zig/02_recursive_sum.zig @@ -0,0 +1,37 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + var list = [_]i32{ 1, 2, 3, 4 }; + print("{}\n", .{sum(i32, &list)}); +} + +fn sum(comptime T: type, list: []T) T { + if (list.len == 0) { + return 0; + } + return list[0] + sum(T, list[1..]); +} + +test "sum" { + var arr0 = [_]i32{ 1, 2, 3, 4 }; + var arr1 = [_]i32{}; + var tests = [_]struct { + arr: []i32, + exp: i32, + }{ + .{ + .arr = &arr0, + .exp = 10, + }, + .{ + .arr = &arr1, + .exp = 0, + }, + }; + + for (tests) |t| { + var n = sum(@TypeOf(t.exp), t.arr); + try expect(n == t.exp); + } +} diff --git a/04_quicksort/zig/03_recursive_count.zig b/04_quicksort/zig/03_recursive_count.zig new file mode 100644 index 0000000..914c4f2 --- /dev/null +++ b/04_quicksort/zig/03_recursive_count.zig @@ -0,0 +1,40 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + var arr = [_]i32{ 4, 3, 2, 1 }; + print("{}\n", .{count(i32, arr[0..])}); +} + +fn count(comptime T: type, arr: []T) T { + if (arr.len == 0) { + return 0; + } else return 1 + count(T, arr[1..]); +} + +test "count" { + var arr0 = [_]i32{}; + var arr1 = [_]i32{42}; + var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var tests = [_]struct { + arr: []i32, + exp: i32, + }{ + .{ + .arr = &arr0, + .exp = 0, + }, + .{ + .arr = &arr1, + .exp = 1, + }, + .{ + .arr = &arr2, + .exp = 9, + }, + }; + + for (tests) |t| { + try expect(count(@TypeOf(t.exp), t.arr) == t.exp); + } +} diff --git a/04_quicksort/zig/04_recursive_max.zig b/04_quicksort/zig/04_recursive_max.zig new file mode 100644 index 0000000..58170ae --- /dev/null +++ b/04_quicksort/zig/04_recursive_max.zig @@ -0,0 +1,47 @@ +const print = @import("std").debug.print; +const expect = @import("std").testing.expect; + +pub fn main() void { + print("{}\n", .{findMax(i32, &[_]i32{ 1, 2, 3, 4 })}); +} + +fn findMax(comptime T: type, arr: []const T) T { + switch (arr.len) { + 0 => return 0, + 1 => return arr[0], + else => { + const x = findMax(T, arr[1..]); + if (arr[0] > x) { + return arr[0]; + } else return x; + }, + } +} + +test "find max" { + const tests = [_]struct { + arr: []const i32, + exp: i32, + }{ + .{ + .arr = &[_]i32{ 1, 2, 3, 4 }, + .exp = 4, + }, + .{ + .arr = &[_]i32{ 8, 42, 3, 1 }, + .exp = 42, + }, + .{ + .arr = &[_]i32{42}, + .exp = 42, + }, + .{ + .arr = &[_]i32{}, + .exp = 0, + }, + }; + + for (tests) |t| { + try expect(findMax(@TypeOf(t.exp), t.arr) == t.exp); + } +} diff --git a/04_quicksort/zig/05_quicksort.zig b/04_quicksort/zig/05_quicksort.zig new file mode 100644 index 0000000..83c78e3 --- /dev/null +++ b/04_quicksort/zig/05_quicksort.zig @@ -0,0 +1,79 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; +const heap = std.heap; +const mem = std.mem; + +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 }; + + print("{d}\n", .{try quicksort(u8, arena.allocator(), &s)}); +} + +fn quicksort(comptime T: type, allocator: mem.Allocator, s: []const T) anyerror![]const T { + // base case, arrays with 0 or 1 element are already "sorted" + if (s.len < 2) { + return s; + } + + var lower = std.ArrayList(T).init(allocator); + var higher = std.ArrayList(T).init(allocator); + + const pivot = s[0]; + for (s[1..]) |item| { + if (item <= pivot) { + try lower.append(item); + } else { + try higher.append(item); + } + } + + var low = try quicksort(T, allocator, lower.items); + var high = try quicksort(T, allocator, higher.items); + + var res = std.ArrayList(T).init(allocator); + try res.appendSlice(low); + try res.append(pivot); + try res.appendSlice(high); + + return res.items; +} + +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 + } + + const tests = [_]struct { + s: []const u8, + exp: []const u8, + }{ + .{ + .s = &[_]u8{}, + .exp = &[_]u8{}, + }, + .{ + .s = &[_]u8{42}, + .exp = &[_]u8{42}, + }, + .{ + .s = &[_]u8{ 3, 2, 1 }, + .exp = &[_]u8{ 1, 2, 3 }, + }, + }; + + for (tests) |t| { + var res = try quicksort(u8, arena.allocator(), t.s); + try expect(res.len == t.exp.len); + for (res) |e, i| + try expect(e == t.exp[i]); + } +} diff --git a/04_quicksort/zig/06_quicksort_parallel.zig b/04_quicksort/zig/06_quicksort_parallel.zig new file mode 100644 index 0000000..8c4991a --- /dev/null +++ b/04_quicksort/zig/06_quicksort_parallel.zig @@ -0,0 +1,86 @@ +const std = @import("std"); +const print = std.debug.print; +const expect = std.testing.expect; +const heap = std.heap; +const mem = std.mem; + +pub const io_mode = .evented; + +pub const Error = error{OutOfMemory}; + +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 }; + + print("{d}\n", .{try quicksort(arena.allocator(), &s)}); +} + +// 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; + } + + var lower = std.ArrayList(u8).init(allocator); + var higher = std.ArrayList(u8).init(allocator); + + const pivot = s[0]; + for (s[1..]) |item| { + if (item <= pivot) { + try lower.append(item); + } else { + try higher.append(item); + } + } + + 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; + + var res = std.ArrayList(u8).init(allocator); + try res.appendSlice(low); + try res.append(pivot); + try res.appendSlice(high); + + return res.items; +} + +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 + } + + const tests = [_]struct { + s: []const u8, + exp: []const u8, + }{ + .{ + .s = &[_]u8{}, + .exp = &[_]u8{}, + }, + .{ + .s = &[_]u8{42}, + .exp = &[_]u8{42}, + }, + .{ + .s = &[_]u8{ 3, 2, 1 }, + .exp = &[_]u8{ 1, 2, 3 }, + }, + }; + + 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]); + } +} diff --git a/05_hash_tables/zig/check_voter.zig b/05_hash_tables/zig/check_voter.zig new file mode 100644 index 0000000..1cc7bc8 --- /dev/null +++ b/05_hash_tables/zig/check_voter.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const heap = std.heap; + +pub fn main() !void { + var gpa = heap.GeneralPurposeAllocator(.{}){}; + + var map = std.StringHashMap(void).init(gpa.allocator()); + defer map.deinit(); + + try checkVoter(&map, "tom"); + try checkVoter(&map, "mike"); + try checkVoter(&map, "mike"); +} + +fn checkVoter(voted: *std.StringHashMap(void), name: []const u8) !void { + if (voted.contains(name)) { + std.debug.print("kick them out!\n", .{}); + } else { + try voted.put(name, {}); + std.debug.print("let them vote!\n", .{}); + } +} diff --git a/05_hash_tables/zig/price_of_groceries.zig b/05_hash_tables/zig/price_of_groceries.zig new file mode 100644 index 0000000..91b42a2 --- /dev/null +++ b/05_hash_tables/zig/price_of_groceries.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const heap = std.heap; + +pub fn main() !void { + var gpa = heap.GeneralPurposeAllocator(.{}){}; + + var map = std.StringHashMap(f32).init(gpa.allocator()); + defer map.deinit(); + + try map.put("apple", 0.67); + try map.put("milk", 1.49); + try map.put("avocado", 1.49); + + var iterator = map.iterator(); + + while (iterator.next()) |entry| { + std.debug.print("{s}: {d:.2}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); + } +} diff --git a/06_breadth-first_search/zig/breadth_first_search.zig b/06_breadth-first_search/zig/breadth_first_search.zig new file mode 100644 index 0000000..2404c9e --- /dev/null +++ b/06_breadth-first_search/zig/breadth_first_search.zig @@ -0,0 +1,114 @@ +const std = @import("std"); +const mem = std.mem; +const heap = std.heap; + +pub fn main() !void { + var gpa = heap.GeneralPurposeAllocator(.{}){}; + + var graph = std.StringHashMap([][]const u8).init(gpa.allocator()); + defer graph.deinit(); + + var you = [_][]const u8{ "alice", "bob", "claire" }; + var bob = [_][]const u8{ "anuj", "peggy" }; + var alice = [_][]const u8{"peggy"}; + var claire = [_][]const u8{ "thom", "jonny" }; + var anuj = [_][]const u8{}; + var peggy = [_][]const u8{}; + var thom = [_][]const u8{}; + var jonny = [_][]const u8{}; + + try graph.put("you", &you); + try graph.put("bob", &bob); + try graph.put("alice", &alice); + try graph.put("claire", &claire); + try graph.put("anuj", &anuj); + try graph.put("peggy", &peggy); + try graph.put("thom", &thom); + try graph.put("jonny", &jonny); + + try search(gpa.allocator(), &graph, "you"); +} + +fn search( + allocator: mem.Allocator, + graph: *std.StringHashMap([][]const u8), + name: []const u8, +) !void { + var arena = heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + var searched = std.BufSet.init(arena.allocator()); + const Q = std.TailQueue([]const u8); + var queue = Q{}; + + var name_edges = graph.get(name); + if (name_edges) |edges| { + var nodes = try arena.allocator().alloc(Q.Node, edges.len); + var i: usize = 0; + while (i < edges.len) : (i += 1) { + nodes[i].data = edges[i]; + } + for (nodes) |*node| { + queue.append(node); + } + } + + while (queue.len > 0) { + var first = queue.popFirst(); + if (first) |person| { + if (!searched.contains(person.data)) { + if (personIsSeller(person.data)) { + std.debug.print("{s} is a mango seller!\n", .{person.data}); + return; + } else { + var ee = graph.get(person.data); + if (ee) |edges| { + var nodes = try arena.allocator().alloc(Q.Node, edges.len); + var i: usize = 0; + while (i < edges.len) : (i += 1) { + nodes[i].data = edges[i]; + } + for (nodes) |*node| { + queue.append(node); + } + } + 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 + } + + var you = [_][]const u8{ "alice", "bob", "claire" }; + var bob = [_][]const u8{ "anuj", "peggy" }; + var alice = [_][]const u8{"peggy"}; + var claire = [_][]const u8{ "thom", "jonny" }; + var anuj = [_][]const u8{}; + var peggy = [_][]const u8{}; + var thom = [_][]const u8{}; + var jonny = [_][]const u8{}; + + try graph.put("you", &you); + try graph.put("bob", &bob); + try graph.put("alice", &alice); + try graph.put("claire", &claire); + try graph.put("anuj", &anuj); + try graph.put("peggy", &peggy); + try graph.put("thom", &thom); + try graph.put("jonny", &jonny); + + try search(gpa.allocator(), &graph, "you"); +} diff --git a/07_dijkstras_algorithm/zig/dijkstras_algorithm.zig b/07_dijkstras_algorithm/zig/dijkstras_algorithm.zig new file mode 100644 index 0000000..2a038d0 --- /dev/null +++ b/07_dijkstras_algorithm/zig/dijkstras_algorithm.zig @@ -0,0 +1,161 @@ +const std = @import("std"); +const mem = std.mem; +const heap = std.heap; + +pub fn main() !void { + var gpa = heap.GeneralPurposeAllocator(.{}){}; + var arena = heap.ArenaAllocator.init(gpa.allocator()); + defer arena.deinit(); + + var graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator()); + + var start = std.StringHashMap(f32).init(arena.allocator()); + try start.put("a", 6); + try start.put("b", 2); + try graph.put("start", &start); + + var a = std.StringHashMap(f32).init(arena.allocator()); + try a.put("finish", 1); + try graph.put("a", &a); + + var b = std.StringHashMap(f32).init(arena.allocator()); + try b.put("a", 3); + try b.put("finish", 5); + try graph.put("b", &b); + + var fin = std.StringHashMap(f32).init(arena.allocator()); + try graph.put("finish", &fin); + + var result = try dijkstra(arena.allocator(), &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.* }); + } + 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.* }); + } +} + +/// 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. +fn dijkstra( + allocator: mem.Allocator, + graph: *std.StringHashMap(*std.StringHashMap(f32)), + start: []const u8, + finish: []const u8, +) !dijkstraResult { + var costs = std.StringHashMap(f32).init(allocator); + var parents = std.StringHashMap(?[]const u8).init(allocator); + 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.*); + try parents.put(elem.key_ptr.*, start); + } + } + + 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(); + while (it.next()) |neighbor| { + var 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, + }; +} + +/// 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_node: ?[]const u8 = null; + + var it = costs.iterator(); + while (it.next()) |node| { + if (node.value_ptr.* < lowest_cost and !processed.contains(node.key_ptr.*)) { + lowest_cost = node.value_ptr.*; + lowest_cost_node = node.key_ptr.*; + } + } + + return lowest_cost_node; +} + +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 graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator()); + + var start = std.StringHashMap(f32).init(arena.allocator()); + try start.put("a", 6); + try start.put("b", 2); + try graph.put("start", &start); + + var a = std.StringHashMap(f32).init(arena.allocator()); + try a.put("finish", 1); + try graph.put("a", &a); + + var b = std.StringHashMap(f32).init(arena.allocator()); + try b.put("a", 3); + try b.put("finish", 5); + try graph.put("b", &b); + + var fin = std.StringHashMap(f32).init(arena.allocator()); + try graph.put("finish", &fin); + + var result = try dijkstra(arena.allocator(), &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"); +} diff --git a/08_greedy_algorithms/zig/set_covering.zig b/08_greedy_algorithms/zig/set_covering.zig new file mode 100644 index 0000000..cf13ab3 --- /dev/null +++ b/08_greedy_algorithms/zig/set_covering.zig @@ -0,0 +1,158 @@ +const std = @import("std"); +const heap = std.heap; +const mem = std.mem; + +pub fn main() !void { + var gpa = heap.GeneralPurposeAllocator(.{}){}; + 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()); + for (states_needed_array) |sn| { + try states_needed.insert(sn); + } + + var stations = std.StringHashMap(*std.BufSet).init(arena.allocator()); + + 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 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); + + for (stations_covering) |sc| { + std.debug.print("{s}\n", .{sc}); + } +} + +fn setCovering(allocator: mem.Allocator, stations: *std.StringHashMap(*std.BufSet), states_needed: *std.BufSet) ![][]const u8 { + var final_stations = std.BufSet.init(allocator); + + while (states_needed.count() > 0) { + var best_station: []const u8 = undefined; + var states_covered: [][]const u8 = &[_][]const u8{}; + + var it = stations.iterator(); + while (it.next()) |station| { + 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; + } else covered.deinit(); + } + + difference(states_needed, states_covered); + try final_stations.insert(best_station); + } + + var final_array = std.ArrayList([]const u8).init(allocator); + var i = final_stations.iterator(); + while (i.next()) |key| { + try final_array.append(key.*); + } + + return final_array.toOwnedSlice(); +} + +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| { + while (r_it.next()) |r| { + if (std.mem.eql(u8, l.*, r.*)) { + try intersection.append(l.*); + } + } + } +} + +fn difference(lessening: *std.BufSet, subtracting: [][]const u8) void { + var less_it = lessening.iterator(); + + while (less_it.next()) |less| { + for (subtracting) |sub| { + if (std.mem.eql(u8, less.*, sub)) { + lessening.remove(less.*); + } + } + } +} + +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 + } + + 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 stations = std.StringHashMap(*std.BufSet).init(arena.allocator()); + + 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 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); + } +} diff --git a/09_dynamic_programming/zig/longest_common_subsequence.zig b/09_dynamic_programming/zig/longest_common_subsequence.zig new file mode 100644 index 0000000..b0d5909 --- /dev/null +++ b/09_dynamic_programming/zig/longest_common_subsequence.zig @@ -0,0 +1,63 @@ +const std = @import("std"); +const heap = std.heap; +const math = std.math; +const expect = std.testing.expect; + +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}); +} + +fn subsequence(allocator: std.mem.Allocator, a: []const u8, b: []const u8) !u32 { + var grid = try allocator.alloc([]u32, a.len + 1); + + for (grid) |*row| { + row.* = try allocator.alloc(u32, b.len + 1); + for (row.*) |*cell| { + cell.* = 0; + } + } + + var i: usize = 1; + while (i <= a.len) : (i += 1) { + var j: usize = 1; + while (j <= b.len) : (j += 1) { + if (a[i - 1] == b[j - 1]) { + grid[i][j] = grid[i - 1][j - 1] + 1; + } else { + grid[i][j] = math.max(grid[i][j - 1], grid[i - 1][j]); + } + } + } + + return grid[a.len][b.len]; +} + +test "subsequence" { + var tests = [_]struct { + a: []const u8, + b: []const u8, + exp: u32, + }{ + .{ .a = "abc", .b = "abcd", .exp = 3 }, + .{ .a = "pera", .b = "mela", .exp = 2 }, + .{ .a = "banana", .b = "kiwi", .exp = 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 n = try subsequence(arena.allocator(), t.a, t.b); + try expect(n == t.exp); + } +}