139 lines
4.6 KiB
C
139 lines
4.6 KiB
C
#include <c_PrimMST.h>
|
|||
|
|
#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);
|
||
|
|
}
|