140 lines
5.2 KiB
C
140 lines
5.2 KiB
C
#include "c_FloydWarshall.h"
|
|||
|
|
|
||
|
|
|
||
|
|
#define FW_SENTINEL ((c_size_t)-1)
|
||
|
|
|
||
|
|
static void c_FloydWarshall_FreeMatrices(c_FloydWarshall_t* self) {
|
||
|
|
if (self->dist_to) {
|
||
|
|
for (c_size_t i = 0; i < self->V; ++i) {
|
||
|
|
if (self->dist_to[i]) c_Allocator_Free(&self->allocator, self->dist_to[i]);
|
||
|
|
}
|
||
|
|
c_Allocator_Free(&self->allocator, self->dist_to);
|
||
|
|
self->dist_to = NULL;
|
||
|
|
}
|
||
|
|
if (self->next_vertex) {
|
||
|
|
for (c_size_t i = 0; i < self->V; ++i) {
|
||
|
|
if (self->next_vertex[i]) c_Allocator_Free(&self->allocator, self->next_vertex[i]);
|
||
|
|
}
|
||
|
|
c_Allocator_Free(&self->allocator, self->next_vertex);
|
||
|
|
self->next_vertex = NULL;
|
||
|
|
}
|
||
|
|
if (self->edge_to) {
|
||
|
|
for (c_size_t i = 0; i < self->V; ++i) {
|
||
|
|
if (self->edge_to[i]) c_Allocator_Free(&self->allocator, self->edge_to[i]);
|
||
|
|
}
|
||
|
|
c_Allocator_Free(&self->allocator, self->edge_to);
|
||
|
|
self->edge_to = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_FloydWarshall_Init(c_FloydWarshall_t* self, c_EdgeWeightedDigraph_t* graph, c_Allocator_t* allocator) {
|
||
|
|
if (!self || !graph) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
self->allocator = allocator ? *allocator : c_DefaultAllocator;
|
||
|
|
self->V = graph->V;
|
||
|
|
self->has_neg_cycle = C_FALSE;
|
||
|
|
|
||
|
|
self->dist_to = (double**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double*));
|
||
|
|
self->next_vertex = (c_size_t**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t*));
|
||
|
|
self->edge_to = (c_size_t**)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t*));
|
||
|
|
|
||
|
|
if (!self->dist_to || !self->next_vertex || !self->edge_to) {
|
||
|
|
c_FloydWarshall_FreeMatrices(self);
|
||
|
|
return C_ERR_NOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (c_size_t i = 0; i < self->V; ++i) {
|
||
|
|
self->dist_to[i] = (double*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(double));
|
||
|
|
self->next_vertex[i] = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
|
||
|
|
self->edge_to[i] = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
|
||
|
|
|
||
|
|
if (!self->dist_to[i] || !self->next_vertex[i] || !self->edge_to[i]) {
|
||
|
|
c_FloydWarshall_FreeMatrices(self);
|
||
|
|
return C_ERR_NOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (c_size_t j = 0; j < self->V; ++j) {
|
||
|
|
self->dist_to[i][j] = (i == j) ? 0.0 : DBL_MAX;
|
||
|
|
self->next_vertex[i][j] = FW_SENTINEL;
|
||
|
|
self->edge_to[i][j] = FW_SENTINEL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Step 1: Base adjacency mapping from graph edges pool */
|
||
|
|
for (c_size_t e = 0; e < graph->E; ++e) {
|
||
|
|
c_DirectedEdge_t* edge = &graph->edges_pool[e];
|
||
|
|
c_size_t u = edge->from;
|
||
|
|
c_size_t v = edge->to;
|
||
|
|
|
||
|
|
if (edge->weight < self->dist_to[u][v]) {
|
||
|
|
self->dist_to[u][v] = edge->weight;
|
||
|
|
self->next_vertex[u][v] = v;
|
||
|
|
self->edge_to[u][v] = e;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Step 2: Floyd-Warshall dynamic programming loops pass */
|
||
|
|
for (c_size_t k = 0; k < self->V; ++k) {
|
||
|
|
for (c_size_t i = 0; i < self->V; ++i) {
|
||
|
|
if (self->dist_to[i][k] == DBL_MAX) continue;
|
||
|
|
|
||
|
|
for (c_size_t j = 0; j < self->V; ++j) {
|
||
|
|
if (self->dist_to[k][j] == DBL_MAX) continue;
|
||
|
|
|
||
|
|
if (self->dist_to[i][j] > self->dist_to[i][k] + self->dist_to[k][j]) {
|
||
|
|
self->dist_to[i][j] = self->dist_to[i][k] + self->dist_to[k][j];
|
||
|
|
self->next_vertex[i][j] = self->next_vertex[i][k];
|
||
|
|
self->edge_to[i][j] = self->edge_to[i][k];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Step 3: Negative cycle interception */
|
||
|
|
if (self->dist_to[i][i] < 0.0) {
|
||
|
|
self->has_neg_cycle = C_TRUE;
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
void c_FloydWarshall_Destroy(c_FloydWarshall_t* self) {
|
||
|
|
if (!self) return;
|
||
|
|
c_FloydWarshall_FreeMatrices(self);
|
||
|
|
self->V = 0;
|
||
|
|
self->has_neg_cycle = C_FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ================================================================================================================== */
|
||
|
|
/* Exact Signature Re-implementation Match */
|
||
|
|
|
||
|
|
c_err_t c_FloydWarshall_Path(c_FloydWarshall_t* self, c_size_t u, c_size_t v, c_EdgeIdList_t* out_path) {
|
||
|
|
if (!self || !out_path || u >= self->V || v >= self->V) return C_ERR_PARAM;
|
||
|
|
if (self->has_neg_cycle || !c_FloydWarshall_HasPath(self, u, v)) return C_ERR_FAIL;
|
||
|
|
|
||
|
|
/* If origin equals target, the shortest path requires 0 edge steps */
|
||
|
|
if (u == v) return C_SUCCESS;
|
||
|
|
|
||
|
|
c_size_t curr_u = u;
|
||
|
|
c_err_t err = C_SUCCESS;
|
||
|
|
|
||
|
|
/* Step forward from origin node 'u' using the pre-computed next_vertex mappings */
|
||
|
|
while (curr_u != v) {
|
||
|
|
c_size_t next = self->next_vertex[curr_u][v];
|
||
|
|
if (next == FW_SENTINEL) return C_ERR_FAIL; /* Structural anomaly anchor guard */
|
||
|
|
|
||
|
|
c_size_t edge_id = self->edge_to[curr_u][v];
|
||
|
|
if (edge_id == FW_SENTINEL) return C_ERR_FAIL;
|
||
|
|
|
||
|
|
/* Append the resolved edge token into your sequential vertex list wrapper */
|
||
|
|
err = c_EdgeIdList_Append(out_path, (c_uint_t)edge_id);
|
||
|
|
if (err != C_SUCCESS) return err;
|
||
|
|
|
||
|
|
/* Move the sliding window origin forward to transition the trace */
|
||
|
|
curr_u = next;
|
||
|
|
}
|
||
|
|
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|