More
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
#include <c_FFT.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Private Helper: Verifies if an integer is a strict power of 2 */
|
||||
C_STATIC_FORCE_INLINE
|
||||
bool c_FFT_IsPowerOfTwo(c_size_t n) {
|
||||
return (n > 0) && ((n & (n - 1)) == 0);
|
||||
}
|
||||
|
||||
/* Private Helper: In-place O(N) bit-reversal permutation shuffling */
|
||||
static void c_FFT_BitReversePermutation(c_Complex_t* signal, c_size_t n) {
|
||||
c_size_t j = 0;
|
||||
for (c_size_t i = 0; i < n; ++i) {
|
||||
if (i < j) {
|
||||
c_Complex_t temp = signal[i];
|
||||
signal[i] = signal[j];
|
||||
signal[j] = temp;
|
||||
}
|
||||
c_size_t bit = n >> 1;
|
||||
while (j & bit) {
|
||||
j ^= bit;
|
||||
bit >>= 1;
|
||||
}
|
||||
j ^= bit;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_FFT_Init(c_FFT_t* self, c_size_t N, c_Allocator_t* allocator) {
|
||||
if (!self || N == 0) return C_ERR_PARAM;
|
||||
if (!c_FFT_IsPowerOfTwo(N)) return C_ERR_PARAM; /* Algorithm constraint check */
|
||||
|
||||
self->allocator = allocator ? *allocator : c_DefaultAllocator;
|
||||
self->N = N;
|
||||
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
void c_FFT_Destroy(c_FFT_t* self) {
|
||||
if (!self) return;
|
||||
self->N = 0;
|
||||
}
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Non-Recursive In-Place FFT Engine paired with your formal c_Complex_t APIs */
|
||||
|
||||
c_err_t c_FFT_Execute(const c_FFT_t* self, c_Complex_t* signal, c_bool_t inverse) {
|
||||
if (!self || !signal) return C_ERR_PARAM;
|
||||
if (self->N == 0) return C_SUCCESS;
|
||||
|
||||
c_size_t n = self->N;
|
||||
|
||||
/* 1. Execute the in-place bit-reversal shuffle up-front */
|
||||
c_FFT_BitReversePermutation(signal, n);
|
||||
|
||||
/* 2. Bottom-up butterfly merging loops */
|
||||
/* len represents the current sub-problem size (2, 4, 8, ..., N) */
|
||||
for (c_size_t len = 2; len <= n; len <<= 1) {
|
||||
double angle = 2.0 * M_PI / (double)len * (inverse ? 1.0 : -1.0);
|
||||
|
||||
/* Calculate principal twiddle factor step block using your standard constructor factory hook */
|
||||
c_Complex_t wlen = c_Complex_Make(cos(angle), sin(angle));
|
||||
|
||||
for (c_size_t i = 0; i < n; i += len) {
|
||||
c_Complex_t w = c_Complex_Make(1.0, 0.0);
|
||||
c_size_t half_len = len >> 1;
|
||||
|
||||
for (c_size_t j = 0; j < half_len; ++j) {
|
||||
c_size_t idx_u = i + j;
|
||||
c_size_t idx_v = i + j + half_len;
|
||||
|
||||
c_Complex_t u = signal[idx_u];
|
||||
|
||||
/* High-performance complex multiplication butterfly product segment: t = v * w */
|
||||
c_Complex_t t = c_Complex_Mul(signal[idx_v], w);
|
||||
|
||||
/* Core butterfly update mappings leveraging clean addition and subtraction interfaces */
|
||||
signal[idx_u] = c_Complex_Add(u, t);
|
||||
signal[idx_v] = c_Complex_Sub(u, t);
|
||||
|
||||
/* Advance intermediate rotation matrix step: w = w * wlen */
|
||||
w = c_Complex_Mul(w, wlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Scale Inverse IFFT vectors uniformly by 1/N using scalar operations helper */
|
||||
if (inverse) {
|
||||
double scale = 1.0 / (double)n;
|
||||
for (c_size_t i = 0; i < n; ++i) {
|
||||
signal[i] = c_Complex_MulScalar(signal[i], scale);
|
||||
}
|
||||
}
|
||||
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef INCLUDED_C_FFT_H
|
||||
#define INCLUDED_C_FFT_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
#ifndef INCLUDED_C_COMPLEX_H
|
||||
#include <c_Complex.h>
|
||||
#endif /*INCLUDED_C_COMPLEX_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef struct {
|
||||
c_size_t N; /* Length of the signal buffer (Must be a power of 2) */
|
||||
c_Allocator_t allocator; /* Deep copy of the user-provided allocator */
|
||||
} c_FFT_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief Initializes the FFT calculation engine context.
|
||||
* @param N Total number of signal sample points (Must be a strict power of 2)
|
||||
*/
|
||||
c_err_t c_FFT_Init(c_FFT_t* self, c_size_t N, c_Allocator_t* allocator);
|
||||
|
||||
/**
|
||||
* @brief Releases internal tracking engine allocations.
|
||||
*/
|
||||
void c_FFT_Destroy(c_FFT_t* self);
|
||||
|
||||
/**
|
||||
* @brief Non-recursively executes the forward or inverse FFT in-place.
|
||||
* @param signal Array of complex samples of size self->N (Modified in-place)
|
||||
* @param inverse Set to C_TRUE for Inverse FFT (IFFT), C_FALSE for Forward FFT
|
||||
*/
|
||||
c_err_t c_FFT_Execute(const c_FFT_t* self, c_Complex_t* signal, c_bool_t inverse);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_FFT_H*/
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "c_Test.h"
|
||||
#include "c_FFT.h"
|
||||
|
||||
TEST_CASE(test_fft_forward_and_inverse_precision) {
|
||||
c_size_t N = 8;
|
||||
c_FFT_t fft;
|
||||
c_err_t err = c_FFT_Init(&fft, N, NULL);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* Allocate and initialize a test complex input signal (e.g., a simple square pulse) */
|
||||
c_Complex_t signal[8] = {
|
||||
{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0},
|
||||
{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}
|
||||
};
|
||||
|
||||
/* Backup the original signal vector values for accuracy verification tests later */
|
||||
double original_real[8];
|
||||
for (c_size_t i = 0; i < N; ++i) original_real[i] = signal[i].real;
|
||||
|
||||
/* 1. Execute Forward Fourier Transformation */
|
||||
err = c_FFT_Execute(&fft, signal, C_FALSE);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* The DC component (index 0) must equal the sum of all elements = 4.0 */
|
||||
ASSERT_DOUBLE_EQ_MSG(4.0, signal[0].real, "Forward FFT DC component calculation wrong");
|
||||
|
||||
/* 2. Execute Inverse Fourier Transformation to reconstruct the original signal */
|
||||
err = c_FFT_Execute(&fft, signal, C_TRUE);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* Confirm that the output matches the original input wave precisely within allowed errors */
|
||||
for (c_size_t i = 0; i < N; ++i) {
|
||||
ASSERT_DOUBLE_EQ_MSG(original_real[i], signal[i].real, "FFT-IFFT signal precision mismatch");
|
||||
ASSERT_DOUBLE_EQ_MSG(0.0, signal[i].imag, "Residual imaginary noise caught in reconstructed signal");
|
||||
}
|
||||
|
||||
c_FFT_Destroy(&fft);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_START(FastFourierTransform_Engine_Suite);
|
||||
RUN_TEST(test_fft_forward_and_inverse_precision);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_FlowEdge.h>
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef INCLUDED_C_FLOWEDGE_H
|
||||
#define INCLUDED_C_FLOWEDGE_H
|
||||
|
||||
#ifndef INCLUDED_C_TYPES_H
|
||||
#include <c_Types.h>
|
||||
#endif /*INCLUDED_C_TYPES_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_size_t from; /* Source vertex of the directed edge */
|
||||
c_size_t to; /* Destination vertex of the directed edge */
|
||||
double capacity; /* Total flow capacity threshold limit */
|
||||
double flow; /* Current active allocated flow amount */
|
||||
} c_FlowEdge_t;
|
||||
|
||||
/**
|
||||
* @brief Resolves the companion residual edge ID using a high-speed bitwise XOR short-circuit
|
||||
*/
|
||||
#define C_FLOW_EDGE_REVERSE(edge_id) ((edge_id) ^ 1)
|
||||
|
||||
#endif /*INCLUDED_C_FLOWEDGE_H*/
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <c_FlowNetwork.h>
|
||||
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Flow Network Infrastructure API Primitives */
|
||||
|
||||
c_err_t c_FlowNetwork_Init(c_FlowNetwork_t* self, c_size_t V, c_Allocator_t* alloc) {
|
||||
if (!self) return C_ERR_PARAM;
|
||||
self->allocator = alloc ? *alloc : c_DefaultAllocator;
|
||||
self->V = V;
|
||||
self->E = 0;
|
||||
self->adj_list = NULL;
|
||||
self->edges_pool = NULL;
|
||||
self->edges_cap = 0;
|
||||
|
||||
if (V > 0) {
|
||||
self->adj_list = (c_UIntArray_t*)c_Allocator_Calloc(&self->allocator, V, sizeof(c_UIntArray_t));
|
||||
if (!self->adj_list) return C_ERR_NOMEM;
|
||||
for (c_size_t i = 0; i < V; ++i) c_UIntArray_Init(&self->adj_list[i], 0, alloc);
|
||||
}
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
void c_FlowNetwork_Destroy(c_FlowNetwork_t* self) {
|
||||
if (!self) return;
|
||||
if (self->adj_list) {
|
||||
for (c_size_t i = 0; i < self->V; ++i) c_UIntArray_Destroy(&self->adj_list[i]);
|
||||
c_Allocator_Free(&self->allocator, self->adj_list);
|
||||
}
|
||||
if (self->edges_pool) c_Allocator_Free(&self->allocator, self->edges_pool);
|
||||
memset(self, 0, sizeof(*self));
|
||||
}
|
||||
|
||||
c_err_t c_FlowNetwork_AddEdge(c_FlowNetwork_t* self, c_size_t from, c_size_t to, double capacity) {
|
||||
if (!self || from >= self->V || to >= self->V || capacity < 0.0) return C_ERR_PARAM;
|
||||
|
||||
/* Expand pool space to accommodate BOTH the forward edge and its residual twin (+2 entries) */
|
||||
if (self->E + 2 > self->edges_cap) {
|
||||
c_size_t old_cap = self->edges_cap;
|
||||
c_size_t new_cap = old_cap == 0 ? 8 : old_cap * 2;
|
||||
c_FlowEdge_t* new_pool = (c_FlowEdge_t*)c_Allocator_Realloc(
|
||||
&self->allocator, self->edges_pool, old_cap * sizeof(c_FlowEdge_t), new_cap * sizeof(c_FlowEdge_t)
|
||||
);
|
||||
if (!new_pool) return C_ERR_NOMEM;
|
||||
self->edges_pool = new_pool;
|
||||
self->edges_cap = new_cap;
|
||||
}
|
||||
|
||||
c_size_t forward_id = self->E;
|
||||
c_size_t residual_id = self->E + 1;
|
||||
|
||||
/* Push edge pairs sequentially to lock bitwise XOR reverse mapping property */
|
||||
self->edges_pool[forward_id] = (c_FlowEdge_t){ .from = from, .to = to, .capacity = capacity, .flow = 0.0 };
|
||||
self->edges_pool[residual_id] = (c_FlowEdge_t){ .from = to, .to = from, .capacity = 0.0, .flow = 0.0 };
|
||||
|
||||
/* Wire edge pointer handles into both adjacency lists to track backward residual flow channels */
|
||||
c_err_t err = c_UIntArray_Append(&self->adj_list[from], (c_uint_t)forward_id);
|
||||
if (err != C_SUCCESS) return err;
|
||||
err = c_UIntArray_Append(&self->adj_list[to], (c_uint_t)residual_id);
|
||||
if (err != C_SUCCESS) return err;
|
||||
|
||||
self->E += 2;
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef INCLUDED_C_FLOWNETWORK_H
|
||||
#define INCLUDED_C_FLOWNETWORK_H
|
||||
|
||||
#ifndef INCLUDED_C_FLOWEDGE_H
|
||||
#include <c_FlowEdge.h>
|
||||
#endif /*INCLUDED_C_FLOWEDGE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_UINTARRAY_H
|
||||
#include <c_UIntArray.h>
|
||||
#endif /*INCLUDED_C_UINTARRAY_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_ALLOCATOR_H
|
||||
#include <c_Allocator.h>
|
||||
#endif /*INCLUDED_C_ALLOCATOR_H*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_size_t V; /* Total number of vertices */
|
||||
c_size_t E; /* Total number of directed flow edges (including residual twins) */
|
||||
c_UIntArray_t* adj_list; /* Array of lists storing global edge_ids exiting/entering each vertex */
|
||||
c_FlowEdge_t* edges_pool; /* Global contiguous array pool housing raw flow edge objects */
|
||||
c_size_t edges_cap; /* Allocated edge pool tracking capacity boundary */
|
||||
c_Allocator_t allocator; /* Embedded custom allocator structure instance */
|
||||
} c_FlowNetwork_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_FlowNetwork_Init(c_FlowNetwork_t* self, c_size_t V, c_Allocator_t* alloc);
|
||||
void c_FlowNetwork_Destroy(c_FlowNetwork_t* self);
|
||||
c_err_t c_FlowNetwork_AddEdge(c_FlowNetwork_t* self, c_size_t from, c_size_t to, double capacity);
|
||||
|
||||
#endif /*INCLUDED_C_FLOWNETWORK_H*/
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <c_FordFulkerson.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FF_SENTINEL ((c_size_t)-1)
|
||||
#define C_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Edmonds-Karp Augmenting Path Finder Helper (Stack-Safe BFS Queue Engine) */
|
||||
|
||||
static c_bool_t c_FordFulkerson_HasAugmentingPath(c_FordFulkerson_t* self, c_FlowNetwork_t* graph, c_size_t s, c_size_t t, c_size_t* queue) {
|
||||
for (c_size_t v = 0; v < self->V; ++v) {
|
||||
self->marked[v] = C_FALSE;
|
||||
self->edge_to[v] = FF_SENTINEL;
|
||||
}
|
||||
|
||||
c_size_t head = 0, tail = 0;
|
||||
self->marked[s] = C_TRUE;
|
||||
queue[tail++] = s;
|
||||
|
||||
while (head < tail) {
|
||||
c_size_t v = queue[head++];
|
||||
c_UIntArray_t* adj = &graph->adj_list[v];
|
||||
c_size_t size = (c_size_t)c_UIntArray_GetSize(adj);
|
||||
|
||||
for (c_size_t i = 0; i < size; ++i) {
|
||||
c_uint_t generic_id = 0;
|
||||
if (c_UIntArray_Get(adj, i, &generic_id) != C_SUCCESS) continue;
|
||||
|
||||
c_size_t edge_id = (c_size_t)generic_id;
|
||||
c_FlowEdge_t* edge = &graph->edges_pool[edge_id];
|
||||
c_size_t w = edge->to;
|
||||
|
||||
/* Check residual capacity: forward edge capacity constraint or backward flow return buffer */
|
||||
double residual_capacity = edge->capacity - edge->flow;
|
||||
if (residual_capacity > 0.0 && !self->marked[w]) {
|
||||
self->edge_to[w] = edge_id;
|
||||
self->marked[w] = C_TRUE;
|
||||
queue[tail++] = w;
|
||||
if (w == t) return C_TRUE; /* Short-circuit early if sink is reached */
|
||||
}
|
||||
}
|
||||
}
|
||||
return self->marked[t];
|
||||
}
|
||||
|
||||
/* ================================================================================================================== */
|
||||
/* Core Ford-Fulkerson Solver Engine */
|
||||
|
||||
c_err_t c_FordFulkerson_Init(c_FordFulkerson_t* self, c_FlowNetwork_t* graph, c_size_t s, c_size_t t, c_Allocator_t* allocator) {
|
||||
if (!self || !graph || s >= graph->V || t >= graph->V || s == t) return C_ERR_PARAM;
|
||||
|
||||
self->allocator = allocator ? *allocator : c_DefaultAllocator;
|
||||
self->V = graph->V;
|
||||
self->max_flow = 0.0;
|
||||
|
||||
self->edge_to = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
|
||||
self->marked = (c_bool_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_bool_t));
|
||||
c_size_t* queue = (c_size_t*)c_Allocator_Calloc(&self->allocator, self->V, sizeof(c_size_t));
|
||||
|
||||
if (!self->edge_to || !self->marked || !queue) {
|
||||
if (queue) c_Allocator_Free(&self->allocator, queue);
|
||||
c_FordFulkerson_Destroy(self);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
/* Process Edmonds-Karp loop increments dynamically */
|
||||
while (c_FordFulkerson_HasAugmentingPath(self, graph, s, t, queue)) {
|
||||
|
||||
/* Step 1: Compute bottleneck bottleneck structural threshold along path tree link entries */
|
||||
double bottle = DBL_MAX;
|
||||
for (c_size_t v = t; v != s; v = graph->edges_pool[self->edge_to[v]].from) {
|
||||
c_FlowEdge_t* edge = &graph->edges_pool[self->edge_to[v]];
|
||||
bottle = C_MIN(bottle, edge->capacity - edge->flow);
|
||||
}
|
||||
|
||||
/* Step 2: Push bottleneck flow changes into forward and backward matching residual twins */
|
||||
for (c_size_t v = t; v != s; v = graph->edges_pool[self->edge_to[v]].from) {
|
||||
c_size_t forward_id = self->edge_to[v];
|
||||
c_size_t residual_id = C_FLOW_EDGE_REVERSE(forward_id);
|
||||
|
||||
graph->edges_pool[forward_id].flow += bottle;
|
||||
graph->edges_pool[residual_id].flow -= bottle; /* Reverse path flow compensation */
|
||||
}
|
||||
self->max_flow += bottle;
|
||||
}
|
||||
|
||||
c_Allocator_Free(&self->allocator, queue);
|
||||
return C_SUCCESS;
|
||||
}
|
||||
|
||||
void c_FordFulkerson_Destroy(c_FordFulkerson_t* self) {
|
||||
if (!self) return;
|
||||
if (self->edge_to) c_Allocator_Free(&self->allocator, self->edge_to);
|
||||
if (self->marked) c_Allocator_Free(&self->allocator, self->marked);
|
||||
memset(self, 0, sizeof(*self));
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef INCLUDED_C_FORDFULKERSON_H
|
||||
#define INCLUDED_C_FORDFULKERSON_H
|
||||
|
||||
#ifndef INCLUDED_C_FLOWNETWORK_H
|
||||
#include <c_FlowNetwork.h>
|
||||
#endif /*INCLUDED_C_FLOWNETWORK_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_size_t* edge_to; /* edge_to[v] = global edge_id on augmenting path to vertex v */
|
||||
c_bool_t* marked; /* marked[v] = C_TRUE if an augmenting path can reach vertex v */
|
||||
double max_flow; /* Cumulative total maximum value computed */
|
||||
c_size_t V; /* Vertex limit count cached locally */
|
||||
c_Allocator_t allocator; /* Deep copy of the user-provided allocator */
|
||||
} c_FordFulkerson_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* @brief Computes the maximum flow and minimum cut from source 's' to sink 't'.
|
||||
* @param allocator Explicit allocator context used to configure temporary path discovery buffers
|
||||
*/
|
||||
c_err_t c_FordFulkerson_Init(c_FordFulkerson_t* self, c_FlowNetwork_t* graph, c_size_t s, c_size_t t, c_Allocator_t* allocator);
|
||||
|
||||
/**
|
||||
* @brief Releases internal tracking buffers safely.
|
||||
*/
|
||||
void c_FordFulkerson_Destroy(c_FordFulkerson_t* self);
|
||||
|
||||
/**
|
||||
* @brief Returns the total maximum flow value computed.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
double c_FordFulkerson_GetValue(const c_FordFulkerson_t* self) {
|
||||
return self ? self->max_flow : 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if vertex 'v' is on the source side of the minimum cut.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
c_bool_t c_FordFulkerson_InCut(const c_FordFulkerson_t* self, c_size_t v) {
|
||||
if (!self || v >= self->V || !self->marked) return C_FALSE;
|
||||
return self->marked[v];
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_FORDFULKERSON_H*/
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "c_Test.h"
|
||||
#include "c_FordFulkerson.h"
|
||||
|
||||
TEST_CASE(test_ford_fulkerson_max_flow_and_min_cut) {
|
||||
c_FlowNetwork_t network;
|
||||
c_err_t err = c_FlowNetwork_Init(&network, 4, NULL);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* Construct a standard flow distribution diamond network layout:
|
||||
* 0 -> 1 (Capacity: 2.0)
|
||||
* 0 -> 2 (Capacity: 3.0)
|
||||
* 1 -> 3 (Capacity: 1.0)
|
||||
* 2 -> 3 (Capacity: 4.0)
|
||||
* 1 -> 2 (Capacity: 2.0, Cross diagonal distribution balance)
|
||||
*/
|
||||
c_FlowNetwork_AddEdge(&network, 0, 1, 2.0);
|
||||
c_FlowNetwork_AddEdge(&network, 0, 2, 3.0);
|
||||
c_FlowNetwork_AddEdge(&network, 1, 3, 1.0);
|
||||
c_FlowNetwork_AddEdge(&network, 2, 3, 4.0);
|
||||
c_FlowNetwork_AddEdge(&network, 1, 2, 2.0);
|
||||
|
||||
c_FordFulkerson_t ff;
|
||||
err = c_FordFulkerson_Init(&ff, &network, 0, 3, &network.allocator);
|
||||
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||
|
||||
/* Max-flow bottleneck saturation calculation verification must equal exactly 4.0 */
|
||||
ASSERT_DOUBLE_EQ_MSG(4.0, c_FordFulkerson_GetValue(&ff), "Maximum flow metric validation failed");
|
||||
|
||||
/* Min-Cut Verification: check side assignments */
|
||||
ASSERT_TRUE(c_FordFulkerson_InCut(&ff, 0)); /* Source must be on source side of cut */
|
||||
ASSERT_FALSE(c_FordFulkerson_InCut(&ff, 3)); /* Sink must be on sink side of cut */
|
||||
|
||||
c_FordFulkerson_Destroy(&ff);
|
||||
c_FlowNetwork_Destroy(&network);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
TEST_START(FordFulkerson_MaxFlow_Suite);
|
||||
RUN_TEST(test_ford_fulkerson_max_flow_and_min_cut);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
Reference in New Issue
Block a user