#include #include "c_IndexMinPQ.h" #define PQ_SENTINEL ((c_size_t)-1) /* ================================================================================================================== */ /* Private Comparison Callback for c_IndexMinPQ_t */ static int c_Prim_WeightCompare(const void* a, const void* b, void* args) { double w_a = *(const double*)a; double w_b = *(const double*)b; (void)args; return (w_a > w_b) - (w_a < w_b); } /* ================================================================================================================== */ /* Eager Prim Scanning Subroutine Processing Functions */ static void c_Prim_Scan(c_PrimMST_t* self, const c_EdgeWeightedGraph_t* graph, c_size_t v, c_IndexMinPQ_t* pq) { self->marked[v] = C_TRUE; c_AdjList_t* adj = &graph->adj_list[v]; c_size_t size = (c_size_t)c_AdjList_GetSize(adj); for (c_size_t i = 0; i < size; ++i) { c_uint_t generic_edge_id = 0; c_err_t err = c_AdjList_Get(adj, i, &generic_edge_id); if (err == C_SUCCESS) { c_size_t edge_id = (c_size_t)generic_edge_id; c_Edge_t* edge = &graph->edges_pool[edge_id]; c_size_t w = (edge->v == v) ? edge->w : edge->v; if (self->marked[w]) continue; if (edge->weight < self->dist_to[w]) { self->dist_to[w] = edge->weight; self->edge_to[w] = edge_id; /* Leverage clean generic membership checking and modification mutations */ if (c_IndexMinPQ_Contains(pq, w)) { c_IndexMinPQ_Change(pq, w, &self->dist_to[w]); } else { c_IndexMinPQ_Push(pq, w, &self->dist_to[w]); } } } } } c_err_t c_PrimMST_Init(c_PrimMST_t* self, c_EdgeWeightedGraph_t* graph, c_Allocator_t* allocator) { if (!self || !graph) return C_ERR_PARAM; self->allocator = allocator ? *allocator : c_DefaultAllocator; self->weight = 0.0; self->V = graph->V; c_VertexIdList_Init(&self->mst_edges, 0, allocator); if (self->V == 0) return C_SUCCESS; /* 1. Allocate primary evaluation buffers */ self->marked = (c_bool_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_bool_t)); self->edge_to = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t)); self->dist_to = (double*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double)); if (!self->marked || !self->edge_to || !self->dist_to) { c_PrimMST_Destroy(self); return C_ERR_NOMEM; } /* Initialize metrics tracking layouts to positive infinity benchmarks */ for (c_size_t v = 0; v < self->V; ++v) { self->dist_to[v] = DBL_MAX; self->edge_to[v] = PQ_SENTINEL; } /* 2. Configure your formal generic Index Priority Queue sized exactly to V vertices */ c_IndexMinPQ_t pq; c_err_t err = c_IndexMinPQ_Init( &pq, self->V, sizeof(double), c_Prim_WeightCompare, NULL, allocator ); if (err != C_SUCCESS) { c_PrimMST_Destroy(self); return err; } /* 3. Run search loops across vertices to handle potential spanning forests */ for (c_size_t v = 0; v < self->V; ++v) { if (!self->marked[v]) { self->dist_to[v] = 0.0; c_IndexMinPQ_Push(&pq, v, &self->dist_to[v]); while (pq.size > 0) { c_size_t curr_v = 0; c_IndexMinPQ_Pop(&pq, &curr_v); /* Accumulate tree weights if it is not a structural forest root node */ if (self->edge_to[curr_v] != PQ_SENTINEL) { c_VertexIdList_Append(&self->mst_edges, (c_uint_t)self->edge_to[curr_v]); self->weight += self->dist_to[curr_v]; } c_Prim_Scan(self, graph, curr_v, &pq); } } } /* Clean up index priority queue allocations */ c_IndexMinPQ_Destroy(&pq); return C_SUCCESS; } void c_PrimMST_Destroy(c_PrimMST_t* self) { if (!self) return; if (self->marked) c_Allocator_Free(&self->allocator, self->marked); if (self->edge_to) c_Allocator_Free(&self->allocator, self->edge_to); if (self->dist_to) c_Allocator_Free(&self->allocator, self->dist_to); c_VertexIdList_Destroy(&self->mst_edges); self->marked = NULL; self->edge_to = NULL; self->dist_to = NULL; self->weight = 0.0; self->V = 0; } c_err_t c_PrimMST_GetEdges(c_PrimMST_t* self, c_VertexIdList_t* out_edges) { if (!self || !out_edges) return C_ERR_PARAM; return c_VertexIdList_Copy(out_edges, &self->mst_edges); }