#include #include "c_NonrecursiveDirectedCycle.h" c_err_t c_NonrecursiveTopological_Init(c_NonrecursiveTopological_t* self, c_Digraph_t* graph, c_Allocator_t* allocator) { if (!self || !graph) return C_ERR_PARAM; self->allocator = allocator ? *allocator : c_DefaultAllocator; self->has_order = C_FALSE; c_VertexIdList_Init(&self->order, 0, allocator); /* 1. Stack-safe Cycle Check: Ensure the graph is a DAG without relying on recursion stack */ c_NonrecursiveDirectedCycle_t cycle_detector; c_err_t err = c_NonrecursiveDirectedCycle_Init(&cycle_detector, graph, allocator); if (err != C_SUCCESS) return err; c_bool_t has_cycle = c_NonrecursiveDirectedCycle_HasCycle(&cycle_detector); c_NonrecursiveDirectedCycle_Destroy(&cycle_detector); if (has_cycle) { return C_SUCCESS; /* Contains a cycle, return gracefully with has_order = C_FALSE */ } /* 2. Kahn's Algorithm: Allocate a mutable working array copy of in-degrees */ c_size_t* working_indegree = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t)); if (!working_indegree) return C_ERR_NOMEM; /* A simple array-based queue bounded tightly by O(V) */ c_size_t* zero_in_degree_queue = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t)); if (!zero_in_degree_queue) { c_Allocator_Free(&self->allocator, working_indegree); return C_ERR_NOMEM; } c_size_t head = 0; c_size_t tail = 0; /* Initialize working in-degrees and seed the queue with source nodes (in-degree == 0) */ for (c_size_t v = 0; v < graph->V; ++v) { working_indegree[v] = c_Digraph_GetInDegree(graph, v); if (working_indegree[v] == 0) { zero_in_degree_queue[tail++] = v; } } /* 3. Non-recursive processing loop */ while (head < tail) { c_size_t u = zero_in_degree_queue[head++]; /* Append the processed node to the topological ordering stream */ c_VertexIdList_Append(&self->order, (c_uint_t)u); c_AdjList_t* adj = &graph->adj_list[u]; c_size_t size = (c_size_t)c_AdjList_GetSize(adj); /* Decrement in-degree for all downstream neighbors */ for (c_size_t i = 0; i < size; ++i) { c_uint_t target_value = 0; err = c_AdjList_Get(adj, i, &target_value); if (err == C_SUCCESS) { c_size_t w = (c_size_t)target_value; working_indegree[w]--; /* If all parent dependencies are cleared, enqueue the neighbor */ if (working_indegree[w] == 0) { zero_in_degree_queue[tail++] = w; } } } } /* Clean up local working tracking allocations */ c_Allocator_Free(&self->allocator, working_indegree); c_Allocator_Free(&self->allocator, zero_in_degree_queue); self->has_order = C_TRUE; return C_SUCCESS; } void c_NonrecursiveTopological_Destroy(c_NonrecursiveTopological_t* self) { if (!self) return; c_VertexIdList_Destroy(&self->order); self->has_order = C_FALSE; } c_err_t c_NonrecursiveTopological_GetOrder(c_NonrecursiveTopological_t* self, c_VertexIdList_t* out_order) { if (!self || !out_order) return C_ERR_PARAM; if (!self->has_order) return C_ERR_FAIL; /* High-speed block copy optimization */ return c_VertexIdList_Copy(out_order, &self->order); }