159 lines
3.4 KiB
Zig
159 lines
3.4 KiB
Zig
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;
|
|
}
|
|
};
|