This commit is contained in:
2026-09-07 19:33:37 +08:00
parent c945aa211f
commit c7b6c90ec9
20 changed files with 1029 additions and 0 deletions
+105
View File
@@ -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;
}
+48
View File
@@ -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*/
+45
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
#include <c_FlowEdge.h>
+24
View File
@@ -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*/
+65
View File
@@ -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;
}
+39
View File
@@ -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*/
+96
View File
@@ -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));
}
+51
View File
@@ -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*/
+42
View File
@@ -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;
}