const stack = @import("./stack.zig"); const slices = @import("./slices.zig"); const std = @import("std"); pub fn Graph(comptime Payload: type) type { return struct { node_count: usize = 0, table: NodeTables, const NodeTables = stack.Parallel(struct { payload: Payload, edges: stack.Sequential(Node), is_occupied: bool = true, is_visited: bool = false, }); const Self = @This(); pub fn append(self: *Self, payload: Payload) std.mem.Allocator.Error!Node { const node = @as(Node, @enumFromInt(self.table.len())); try self.table.push_grow(.{ .payload = payload, .edges = .{.allocator = self.table.allocator}, }); self.node_count += 1; return node; } pub fn clear_edges(self: *Self) void { for (self.table.values.slice(.edges)) |*edges| { edges.clear(); } } pub fn deinit(self: *Self) void { for (self.table.values.slice(.edges)) |*edges| { edges.deinit(); } self.table.deinit(); self.* = undefined; } pub fn edge_nodes(self: Self, node: Node) ?[]const Node { if (!self.exists(node)) { return null; } return self.table.values.get_ptr(.edges, @intFromEnum(node)).?.values; } pub fn exists(self: Self, node: Node) bool { return self.table.values.get(.is_occupied, @intFromEnum(node)) orelse false; } pub fn get_ptr(self: Self, node: Node) ?*Payload { if (!self.exists(node)) { return null; } return self.table.values.get_ptr(.payload, @intFromEnum(node)).?; } pub fn init(allocator: std.mem.Allocator) Self { return .{ .table = .{.allocator = allocator}, }; } pub fn insert_edge(self: *Self, dependant_node: Node, edge_node: Node) std.mem.Allocator.Error!bool { if (!self.exists(edge_node)) { return false; } const edges = self.table.values.get_ptr(.edges, @intFromEnum(dependant_node)) orelse { return false; }; if (slices.index_of(edges.values, 0, edge_node) == null) { try edges.push_grow(edge_node); } return true; } pub fn is_empty(self: Self) bool { return self.node_count != 0; } pub fn nodes(self: *const Self) Nodes { return .{ .occupied_table = self.table.values.slice(.is_occupied), }; } pub fn mark_visited(self: *Self, node: Node) bool { if (!self.exists(node)) { return false; } std.debug.assert(self.table.values.set(.is_visited, @intFromEnum(node), true)); return true; } pub fn remove_node(self: *Self, node: Node) ?Payload { if (!self.exists(node)) { return null; } const node_index = @intFromEnum(node); self.table.values.get_ptr(.is_occupied, node_index).?.* = false; self.node_count -= 1; return self.table.values.get(.payload, node_index).?; } pub fn reset_visited(self: *Self) void { @memset(self.table.values.slice(.is_visited), false); } pub fn visited(self: Self, node: Node) ?bool { if (!self.exists(node)) { return null; } return self.table.values.get(.is_visited, @intFromEnum(node)).?; } }; } pub const Node = enum (usize) { _ }; pub const Nodes = struct { occupied_table: []const bool, iterations: usize = 0, pub fn next(self: *Nodes) ?Node { std.debug.assert(self.iterations <= self.occupied_table.len); while (self.iterations != self.occupied_table.len) { defer self.iterations += 1; if (self.occupied_table[self.iterations]) { return @enumFromInt(self.iterations); } } return null; } };