#include #include "c_NonrecursiveDirectedDFS.h" c_err_t c_DirectedEulerianPath_Init(c_DirectedEulerianPath_t* self, c_Digraph_t* graph, c_Allocator_t* allocator) { if (!self || !graph) return C_ERR_PARAM; self->allocator = allocator ? *allocator : c_DefaultAllocator; c_VertexIdList_Init(&self->path, 0, allocator); /* Edge case: An empty graph with no edges trivially has an empty path */ if (graph->E == 0) return C_SUCCESS; /* 1. Degree tracking analysis to identify the unique start node candidate */ c_size_t start_vertex = graph->V; c_size_t end_vertex = graph->V; c_size_t start_nodes_count = 0; c_size_t end_nodes_count = 0; c_size_t fallback_start = graph->V; for (c_size_t v = 0; v < graph->V; ++v) { c_size_t out_deg = c_Digraph_GetOutDegree(graph, v); c_size_t in_deg = c_Digraph_GetInDegree(graph, v); if (out_deg > 0 && fallback_start == graph->V) { fallback_start = v; /* Used if the graph is a full cycle loop */ } if (out_deg > in_deg) { if (out_deg - in_deg > 1) return C_SUCCESS; /* Fails condition: open paths allow a max delta of 1 */ start_vertex = v; start_nodes_count++; } else if (in_deg > out_deg) { if (in_deg - out_deg > 1) return C_SUCCESS; end_vertex = v; end_nodes_count++; } } /* Validate structural structural configuration cases */ if (start_nodes_count == 0 && end_nodes_count == 0) { /* Case A: It's an Eulerian Cycle, choose the first non-isolated node as start */ start_vertex = fallback_start; } else if (start_nodes_count == 1 && end_nodes_count == 1) { /* Case B: It's an open Eulerian Path from start_vertex to end_vertex */ // start_vertex is already correctly captured here } else { return C_SUCCESS; /* Fails degree criteria layout, return with empty path */ } if (start_vertex == graph->V) return C_SUCCESS; /* 2. Strong connectivity check over edge elements using NonrecursiveDFS */ c_NonrecursiveDirectedDFS_t dfs; c_err_t err = c_NonrecursiveDirectedDFS_Init(&dfs, graph, start_vertex, allocator); if (err != C_SUCCESS) return err; for (c_size_t v = 0; v < graph->V; ++v) { if (c_Digraph_GetOutDegree(graph, v) > 0 && !c_NonrecursiveDirectedDFS_HasPathTo(&dfs, v, graph->V)) { c_NonrecursiveDirectedDFS_Destroy(&dfs); return C_SUCCESS; /* Edges are fragmented in isolated structures */ } } c_NonrecursiveDirectedDFS_Destroy(&dfs); /* 3. Hierholzer's Iterative Engine Routine */ c_size_t* edge_index_iterator = (c_size_t*)c_Allocator_Calloc(&self->allocator, graph->V, sizeof(c_size_t)); if (!edge_index_iterator) return C_ERR_NOMEM; c_size_t max_stack_cap = graph->E + 1; c_size_t* path_stack = (c_size_t*)c_Allocator_Calloc(&self->allocator, max_stack_cap, sizeof(c_size_t)); if (!path_stack) { c_Allocator_Free(&self->allocator, edge_index_iterator); return C_ERR_NOMEM; } c_size_t* output_stack = (c_size_t*)c_Allocator_Calloc(&self->allocator, max_stack_cap, sizeof(c_size_t)); if (!output_stack) { c_Allocator_Free(&self->allocator, edge_index_iterator); c_Allocator_Free(&self->allocator, path_stack); return C_ERR_NOMEM; } c_size_t path_stack_size = 0; c_size_t output_stack_size = 0; path_stack[path_stack_size++] = start_vertex; while (path_stack_size > 0) { c_size_t curr_v = path_stack[path_stack_size - 1]; c_AdjList_t* adj = &graph->adj_list[curr_v]; c_size_t out_degree = (c_size_t)c_UIntArray_GetSize(adj); if (edge_index_iterator[curr_v] < out_degree) { c_uint_t target_w = 0; err = c_UIntArray_Get(adj, edge_index_iterator[curr_v], &target_w); edge_index_iterator[curr_v]++; /* Consume edge track */ if (err == C_SUCCESS) { path_stack[path_stack_size++] = (c_size_t)target_w; } } else { output_stack[output_stack_size++] = curr_v; path_stack_size--; } } /* Unwind tracking queue directly to the VertexIdList matching forward travel steps */ while (output_stack_size > 0) { c_VertexIdList_Append(&self->path, (c_uint_t)output_stack[--output_stack_size]); } c_Allocator_Free(&self->allocator, edge_index_iterator); c_Allocator_Free(&self->allocator, path_stack); c_Allocator_Free(&self->allocator, output_stack); return C_SUCCESS; } void c_DirectedEulerianPath_Destroy(c_DirectedEulerianPath_t* self) { if (!self) return; c_VertexIdList_Destroy(&self->path); } c_err_t c_DirectedEulerianPath_GetPath(c_DirectedEulerianPath_t* self, c_VertexIdList_t* out_path) { if (!self || !out_path) return C_ERR_PARAM; if (!c_DirectedEulerianPath_HasPath(self)) return C_ERR_FAIL; c_size_t size = (c_size_t)c_VertexIdList_GetSize(&self->path); for (c_size_t i = 0; i < size; ++i) { c_uint_t val = 0; c_err_t err = c_VertexIdList_Get((c_VertexIdList_t*)&self->path, i, &val); if (err == C_SUCCESS) { c_err_t app_err = c_VertexIdList_Append(out_path, val); if (app_err != C_SUCCESS) return app_err; } } return C_SUCCESS; }