diff --git a/scratchpad/dag.c b/scratchpad/dag.c index 6a36c38..e9218e3 100644 --- a/scratchpad/dag.c +++ b/scratchpad/dag.c @@ -3,7 +3,6 @@ #include "../src/wapp/wapp.h" #include #include -#include #include wp_intern WpLogger _log = { .name = wpStr8LitRo("dag_man") }; @@ -65,8 +64,6 @@ typedef struct { u64 index; u64 generation; } PrNodeId; -typedef PrNodeId *PrNodeIdArray; - typedef struct { union { WpStr8 path; @@ -87,30 +84,31 @@ typedef struct PrGraphEdge PrGraphEdge; struct PrGraphEdge { PrGraphEdge *next_forward; PrGraphEdge *next_backward; - PrNodeId source; - PrNodeId target; + u64 source_idx; + u64 target_idx; }; -typedef PrGraphEdge *PrGraphEdgeArray; - /* --------------------------------------------------------------------------- - * Graph node — compact adjacency head (two pointers, no dead fields) + * Graph vertex — compact adjacency head (two pointers + active flag) * -------------------------------------------------------------------------*/ -typedef struct PrGraphNode PrGraphNode; -struct PrGraphNode { +typedef struct PrGraphVertex PrGraphVertex; +struct PrGraphVertex { PrGraphEdge *next_forward; PrGraphEdge *next_backward; + b8 active; }; -typedef PrGraphNode *PrGraphNodeArray; +typedef PrGraphVertex *PrGraphVertexArray; /* --------------------------------------------------------------------------- * PrGraph — owns only topology (edges + adjacency heads) * -------------------------------------------------------------------------*/ typedef struct { - PrPool edge_pool; - PrGraphNodeArray graph_nodes; - u64 capacity; + PrPool edge_pool; + PrGraphVertexArray vertices; + u64 capacity; + u64 max_vertex_ever; + u64 vertex_count; } PrGraph; /* --------------------------------------------------------------------------- @@ -118,12 +116,12 @@ typedef struct { * -------------------------------------------------------------------------*/ typedef struct { - PrNodeArray nodes; - PrGraph graph; - u64 capacity; - u64 max_count_ever; - u64 count; - u64 free_head; + PrNodeArray nodes; + PrGraph graph; + u64 capacity; + u64 max_count_ever; + u64 count; + u64 free_head; } PrNodeManager; /* --------------------------------------------------------------------------- @@ -137,13 +135,15 @@ wp_intern PrNodeId prNodeManagerGetNode(const PrNodeManager *mgr, u64 index); wp_intern PrNodeId prNodeManagerAddNode(PrNodeManager *mgr, PrNodeType type); wp_intern void prNodeManagerRemoveNode(PrNodeManager *mgr, PrNodeId id); wp_intern void prNodeManagerAddEdge(PrNodeManager *mgr, PrNodeId from, PrNodeId to); +wp_intern void prNodeManagerDumpGraph(const PrNodeManager *mgr); -wp_intern void prGraphInit(PrGraph *graph, WpAllocator *allocator, u64 capacity); -wp_intern void prGraphAddEdge(PrNodeManager *mgr, PrGraph *graph, u64 from_idx, u64 to_idx); -wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx); -wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const PrGraph *graph, - const WpAllocator *allocator); -wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph); +wp_intern void prGraphInit(PrGraph *graph, WpAllocator *allocator, u64 capacity); +wp_intern void prGraphAddVertex(PrGraph *graph, u64 idx); +wp_intern void prGraphRemoveVertex(PrGraph *graph, u64 idx); +wp_intern b8 prGraphAddEdge(PrGraph *graph, u64 from_idx, u64 to_idx); +wp_intern b8 prGraphEdgeExists(const PrGraph *graph, u64 from_idx, u64 to_idx); +wp_intern u64 prGraphVertexCount(const PrGraph *graph); +wp_intern WpU64Array prGraphTopologicalSort(const PrGraph *graph, const WpAllocator *allocator); /* --------------------------------------------------------------------------- * PrNodeManager implementation @@ -196,17 +196,18 @@ wp_intern PrNodeId prNodeManagerAddNode(PrNodeManager *mgr, PrNodeType type) { memset(&node->params, 0, sizeof(node->params)); mgr->count++; - if (idx + 1 > mgr->max_count_ever) { mgr->max_count_ever = idx + 1; } + prGraphAddVertex(&mgr->graph, idx); + return (PrNodeId){ .index = idx, .generation = node->generation }; } wp_intern void prNodeManagerRemoveNode(PrNodeManager *mgr, PrNodeId id) { if (!prNodeManagerIsActiveNode(mgr, id)) { return; } - /* Tear down all edges incident to this node */ - prGraphRemoveEdges(&mgr->graph, id.index); + /* Tear down all edges incident to this node and mark vertex inactive */ + prGraphRemoveVertex(&mgr->graph, id.index); /* Return node slot to free list with bumped generation */ PrNode *node = &mgr->nodes[id.index]; @@ -219,7 +220,8 @@ wp_intern void prNodeManagerRemoveNode(PrNodeManager *mgr, PrNodeId id) { wp_intern void prNodeManagerAddEdge(PrNodeManager *mgr, PrNodeId from, PrNodeId to) { if (!prNodeManagerIsActiveNode(mgr, from) || !prNodeManagerIsActiveNode(mgr, to)) { return; } if (from.index == to.index) { return; } - prGraphAddEdge(mgr, &mgr->graph, from.index, to.index); + if (prGraphEdgeExists(&mgr->graph, from.index, to.index)) { return; } + prGraphAddEdge(&mgr->graph, from.index, to.index); } /* --------------------------------------------------------------------------- @@ -227,30 +229,32 @@ wp_intern void prNodeManagerAddEdge(PrNodeManager *mgr, PrNodeId from, PrNodeId * -------------------------------------------------------------------------*/ wp_intern void prGraphInit(PrGraph *graph, WpAllocator *allocator, u64 capacity) { - graph->graph_nodes = wpArrayAllocCapacity(PrGraphNode, allocator, capacity, WP_ARRAY_INIT_FILLED); - graph->capacity = capacity; + graph->vertices = wpArrayAllocCapacity(PrGraphVertex, allocator, capacity, WP_ARRAY_INIT_FILLED); + graph->capacity = capacity; + graph->max_vertex_ever = 0; + graph->vertex_count = 0; - if (!graph->graph_nodes) { + if (!graph->vertices) { graph->capacity = 0; return; } prPoolInit(&graph->edge_pool, allocator, sizeof(PrGraphEdge)); - memset(graph->graph_nodes, 0, capacity * sizeof(PrGraphNode)); + memset(graph->vertices, 0, capacity * sizeof(PrGraphVertex)); } /* --- internal: unlink edge helpers (raw indices, caller guarantees validity) -- */ wp_intern void _unlinkForward(PrGraph *graph, u64 from_idx, PrGraphEdge *edge) { - PrGraphNode *gnode = &graph->graph_nodes[from_idx]; - PrGraphEdge *curr = gnode->next_forward; - PrGraphEdge *prev = NULL; + PrGraphVertex *vtx = &graph->vertices[from_idx]; + PrGraphEdge *curr = vtx->next_forward; + PrGraphEdge *prev = NULL; while (curr) { if (curr == edge) { if (prev) { prev->next_forward = curr->next_forward; } else { - gnode->next_forward = curr->next_forward; + vtx->next_forward = curr->next_forward; } return; } @@ -260,15 +264,15 @@ wp_intern void _unlinkForward(PrGraph *graph, u64 from_idx, PrGraphEdge *edge) { } wp_intern void _unlinkBackward(PrGraph *graph, u64 to_idx, PrGraphEdge *edge) { - PrGraphNode *gnode = &graph->graph_nodes[to_idx]; - PrGraphEdge *curr = gnode->next_backward; - PrGraphEdge *prev = NULL; + PrGraphVertex *vtx = &graph->vertices[to_idx]; + PrGraphEdge *curr = vtx->next_backward; + PrGraphEdge *prev = NULL; while (curr) { if (curr == edge) { if (prev) { prev->next_backward = curr->next_backward; } else { - gnode->next_backward = curr->next_backward; + vtx->next_backward = curr->next_backward; } return; } @@ -277,59 +281,86 @@ wp_intern void _unlinkBackward(PrGraph *graph, u64 to_idx, PrGraphEdge *edge) { } } -/* --- edge management -------------------------------------------------- */ +/* --- vertex lifecycle ------------------------------------------------- */ -wp_intern void prGraphAddEdge(PrNodeManager *mgr, PrGraph *graph, u64 from_idx, u64 to_idx) { - PrGraphEdge *edge = (PrGraphEdge *)prPoolAlloc(&graph->edge_pool); - if (!edge) { return; } - - PrNodeId from_id = prNodeManagerGetNode(mgr, from_idx); - PrNodeId to_id = prNodeManagerGetNode(mgr, to_idx); - - edge->source = from_id; - edge->target = to_id; - - /* Link into adjacency chains */ - PrGraphNode *src = &graph->graph_nodes[from_idx]; - PrGraphNode *dst = &graph->graph_nodes[to_idx]; - edge->next_forward = src->next_forward; - edge->next_backward = dst->next_backward; - src->next_forward = edge; - dst->next_backward = edge; - - /* Check whether the new edge created a cycle */ - WpAllocator scratch = wpMemArenaAllocatorInitZero(KiB(16)); - PrNodeIdArray sorted = prGraphTopologicalSort(mgr, graph, &scratch); - u64 sorted_cnt = sorted ? wpArrayCount(sorted) : 0; - if (sorted_cnt < mgr->count) { - _unlinkForward(graph, from_idx, edge); - _unlinkBackward(graph, to_idx, edge); - prPoolFree(&graph->edge_pool, edge); +wp_intern void prGraphAddVertex(PrGraph *graph, u64 idx) { + graph->vertices[idx].active = true; + graph->vertex_count++; + if (idx >= graph->max_vertex_ever) { + graph->max_vertex_ever = idx + 1; } } -wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx) { - PrGraphNode *gnode = &graph->graph_nodes[idx]; +wp_intern void prGraphRemoveVertex(PrGraph *graph, u64 idx) { + PrGraphVertex *vtx = &graph->vertices[idx]; + if (!vtx->active) { return; } /* Free outgoing edges: unlink from each target's backward list */ - PrGraphEdge *curr = gnode->next_forward; + PrGraphEdge *curr = vtx->next_forward; while (curr) { PrGraphEdge *next = curr->next_forward; - _unlinkBackward(graph, curr->target.index, curr); + _unlinkBackward(graph, curr->target_idx, curr); prPoolFree(&graph->edge_pool, curr); curr = next; } /* Free incoming edges: unlink from each source's forward list */ - curr = gnode->next_backward; + curr = vtx->next_backward; while (curr) { PrGraphEdge *next = curr->next_backward; - _unlinkForward(graph, curr->source.index, curr); + _unlinkForward(graph, curr->source_idx, curr); prPoolFree(&graph->edge_pool, curr); curr = next; } - gnode->next_forward = gnode->next_backward = NULL; + vtx->next_forward = NULL; + vtx->next_backward = NULL; + vtx->active = false; + graph->vertex_count--; +} + +/* --- edge management -------------------------------------------------- */ + +wp_intern b8 prGraphEdgeExists(const PrGraph *graph, u64 from_idx, u64 to_idx) { + PrGraphVertex *vtx = &graph->vertices[from_idx]; + PrGraphEdge *curr = vtx->next_forward; + while (curr) { + if (curr->target_idx == to_idx) { return true; } + curr = curr->next_forward; + } + return false; +} + +wp_intern b8 prGraphAddEdge(PrGraph *graph, u64 from_idx, u64 to_idx) { + PrGraphEdge *edge = (PrGraphEdge *)prPoolAlloc(&graph->edge_pool); + if (!edge) { return false; } + + edge->source_idx = from_idx; + edge->target_idx = to_idx; + + /* Link into adjacency chains */ + PrGraphVertex *src = &graph->vertices[from_idx]; + PrGraphVertex *dst = &graph->vertices[to_idx]; + edge->next_forward = src->next_forward; + edge->next_backward = dst->next_backward; + src->next_forward = edge; + dst->next_backward = edge; + + /* Check whether the new edge created a cycle */ + WpAllocator scratch = wpMemArenaAllocatorInitZero(KiB(16)); + WpU64Array sorted = prGraphTopologicalSort(graph, &scratch); + u64 sorted_n = sorted ? wpArrayCount(sorted) : 0; + if (sorted_n < graph->vertex_count) { + _unlinkForward(graph, from_idx, edge); + _unlinkBackward(graph, to_idx, edge); + prPoolFree(&graph->edge_pool, edge); + return false; + } + return true; +} + +wp_intern u64 prGraphVertexCount(const PrGraph *graph) { + return graph->vertex_count; } /* --------------------------------------------------------------------------- @@ -339,12 +370,11 @@ wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx) { * is less than mgr->count, the graph contains a cycle. * -------------------------------------------------------------------------*/ -wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const PrGraph *graph, - const WpAllocator *allocator) { - if (mgr->count == 0) { return NULL; } - if (!mgr->nodes || graph->capacity == 0) { return NULL; } +wp_intern WpU64Array prGraphTopologicalSort(const PrGraph *graph, const WpAllocator *allocator) { + if (graph->vertex_count == 0) { return NULL; } + if (!graph->vertices || graph->capacity == 0) { return NULL; } - PrNodeIdArray result = wpArrayAllocCapacity(PrNodeId, allocator, mgr->count, WP_ARRAY_INIT_NONE); + WpU64Array result = wpArrayAllocCapacity(u64, allocator, graph->vertex_count, WP_ARRAY_INIT_NONE); if (!result) { return NULL; } WpAllocator local_arena = wpMemArenaAllocatorInitZero(KiB(16)); @@ -353,23 +383,20 @@ wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const P if (!in_degree) { return result; } memset(in_degree, 0, wpArrayCapacity(in_degree) * sizeof(u64)); - for (u64 i = 0; i < mgr->max_count_ever; i++) { - PrNodeId id = prNodeManagerGetNode(mgr, i); - if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } + for (u64 i = 0; i < graph->max_vertex_ever; i++) { + if (!graph->vertices[i].active) { continue; } - PrGraphNode *gnode = &graph->graph_nodes[i]; - PrGraphEdge *curr = gnode->next_forward; + PrGraphEdge *curr = graph->vertices[i].next_forward; while (curr) { - in_degree[curr->target.index]++; + in_degree[curr->target_idx]++; curr = curr->next_forward; } } - WpQueue queue = wpQueueAlloc(u64, &local_arena, mgr->count); + WpQueue queue = wpQueueAlloc(u64, &local_arena, graph->vertex_count); - for (u64 i = 0; i < mgr->max_count_ever; i++) { - PrNodeId id = prNodeManagerGetNode(mgr, i); - if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } + for (u64 i = 0; i < graph->max_vertex_ever; i++) { + if (!graph->vertices[i].active) { continue; } if (in_degree[i] == 0) { wpQueuePush(u64, &queue, &i); } @@ -379,13 +406,11 @@ wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const P u64 *node_idx = wpQueuePop(u64, &queue); if (!node_idx) { break; } - PrNodeId id = prNodeManagerGetNode(mgr, *node_idx); - wpArrayAppendCapped(PrNodeId, result, &id); + wpArrayAppendCapped(u64, result, node_idx); - PrGraphNode *gnode = &graph->graph_nodes[*node_idx]; - PrGraphEdge *curr = gnode->next_forward; + PrGraphEdge *curr = graph->vertices[*node_idx].next_forward; while (curr) { - u64 target_idx = curr->target.index; + u64 target_idx = curr->target_idx; if (in_degree[target_idx] > 0) { in_degree[target_idx]--; if (in_degree[target_idx] == 0) { @@ -403,26 +428,24 @@ wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const P * Dump * -------------------------------------------------------------------------*/ -wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph) { +wp_intern void prNodeManagerDumpGraph(const PrNodeManager *mgr) { + const PrGraph *graph = &mgr->graph; printf("==============INPUTS==============\n"); for (u64 i = 0; i < mgr->max_count_ever; ++i) { PrNodeId id = prNodeManagerGetNode(mgr, i); if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } - PrGraphNode *gnode = &graph->graph_nodes[i]; - printf("%" PRIu64 ":", id.index + 1); - if (!(gnode->next_backward)) { + PrGraphEdge *curr = graph->vertices[i].next_backward; + if (!curr) { printf(" (none)"); } else { - PrGraphEdge *curr = gnode->next_backward; - while (curr) { - printf(" %" PRIu64, curr->source.index + 1); + do { + printf(" %" PRIu64, curr->source_idx + 1); curr = curr->next_backward; - } + } while (curr); } - printf("\n"); } @@ -431,20 +454,17 @@ wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph) { PrNodeId id = prNodeManagerGetNode(mgr, i); if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } - PrGraphNode *gnode = &graph->graph_nodes[i]; - printf("%" PRIu64 ":", id.index + 1); - if (!(gnode->next_forward)) { + PrGraphEdge *curr = graph->vertices[i].next_forward; + if (!curr) { printf(" (none)"); } else { - PrGraphEdge *curr = gnode->next_forward; - while (curr) { - printf(" %" PRIu64, curr->target.index + 1); + do { + printf(" %" PRIu64, curr->target_idx + 1); curr = curr->next_forward; - } + } while (curr); } - printf("\n"); } } @@ -476,24 +496,24 @@ i32 main(void) { prNodeManagerAddEdge(&mgr, n3, n4); prNodeManagerAddEdge(&mgr, n3, n5); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); prNodeManagerRemoveNode(&mgr, n3); printf("\n"); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); PrNodeId n6 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); prNodeManagerAddEdge(&mgr, n4, n6); printf("\n"); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); prNodeManagerRemoveNode(&mgr, n5); printf("\n"); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); PrNodeId n7 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); @@ -501,7 +521,7 @@ i32 main(void) { prNodeManagerAddEdge(&mgr, n2, n7); printf("\n"); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); PrNodeId n8 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); @@ -509,7 +529,7 @@ i32 main(void) { prNodeManagerAddEdge(&mgr, n7, n8); printf("\n"); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); printf("\n=== Try adding cycle 8→1 (should be rejected) ===\n"); prNodeManagerAddEdge(&mgr, n8, n1); @@ -520,20 +540,21 @@ i32 main(void) { printf("\n=== Try adding cycle 7→2 (should be rejected) ===\n\n"); prNodeManagerAddEdge(&mgr, n7, n2); - prGraphDump(&mgr, &mgr.graph); + prNodeManagerDumpGraph(&mgr); printf("\n=== Topological sort ===\n"); - PrNodeId *sorted = prGraphTopologicalSort(&mgr, &mgr.graph, &arena); + WpU64Array sorted = prGraphTopologicalSort(&mgr.graph, &arena); if (sorted) { u64 n = wpArrayCount(sorted); printf(" count: %llu / %llu active\n", (unsigned long long)n, (unsigned long long)mgr.count); for (u64 i = 0; i < n; i++) { + PrNodeId id = prNodeManagerGetNode(&mgr, sorted[i]); printf(" [%llu] idx=%llu gen=%llu\n", (unsigned long long)i, - (unsigned long long)sorted[i].index, - (unsigned long long)sorted[i].generation); + (unsigned long long)sorted[i], + (unsigned long long)id.generation); } }