More
This commit is contained in:
@@ -64,6 +64,7 @@ add_project_modules_with_tests(SOURCES TEST_SOURCES
|
|||||||
Search
|
Search
|
||||||
Sort
|
Sort
|
||||||
String
|
String
|
||||||
|
Context
|
||||||
)
|
)
|
||||||
|
|
||||||
#foreach (item ${SOURCES})
|
#foreach (item ${SOURCES})
|
||||||
|
|||||||
+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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "c_Complex.h"
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
#ifndef INCLUDED_C_COMPLEX_H
|
||||||
|
#define INCLUDED_C_COMPLEX_H
|
||||||
|
|
||||||
|
#ifndef INCLUDED_C_TYPES_H
|
||||||
|
#include <c_Types.h>
|
||||||
|
#endif /*INCLUDED_C_TYPES_H*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_MATH_H
|
||||||
|
#define INCLUDED_MATH_H
|
||||||
|
#include <math.h>
|
||||||
|
#endif /*INCLUDED_MATH_H*/
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double real;
|
||||||
|
double imag;
|
||||||
|
} c_Complex_t;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Make(double real, double imag) {
|
||||||
|
c_Complex_t c;
|
||||||
|
c.real = real;
|
||||||
|
c.imag = imag;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从极坐标创建复数 (Polar to Rectangular)
|
||||||
|
* @param r 幅值 (Magnitude)
|
||||||
|
* @param theta 幅角 (Phase angle in radians)
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_FromPolar(double r, double theta) {
|
||||||
|
c_Complex_t c;
|
||||||
|
c.real = r * cos(theta);
|
||||||
|
c.imag = r * sin(theta);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds two complex numbers: res = a + b
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Add(c_Complex_t a, c_Complex_t b) {
|
||||||
|
c_Complex_t res;
|
||||||
|
res.real = a.real + b.real;
|
||||||
|
res.imag = a.imag + b.imag;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subtracts two complex numbers: res = a - b
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Sub(c_Complex_t a, c_Complex_t b) {
|
||||||
|
c_Complex_t res;
|
||||||
|
res.real = a.real - b.real;
|
||||||
|
res.imag = a.imag - b.imag;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies two complex numbers: res = a * b
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Mul(c_Complex_t a, c_Complex_t b) {
|
||||||
|
c_Complex_t res;
|
||||||
|
res.real = a.real * b.real - a.imag * b.imag;
|
||||||
|
res.imag = a.real * b.imag + a.imag * b.real;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数除法: res = a / b
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Div(c_Complex_t a, c_Complex_t b) {
|
||||||
|
double denom = b.real * b.real + b.imag * b.imag;
|
||||||
|
if (denom == 0.0) {
|
||||||
|
return c_Complex_Make(0.0, 0.0); /* 健壮性防线:防止除以 0 导致崩溃 */
|
||||||
|
}
|
||||||
|
return c_Complex_Make(
|
||||||
|
(a.real * b.real + a.imag * b.imag) / denom,
|
||||||
|
(a.imag * b.real - a.real * b.imag) / denom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes the complex conjugate: res = real - i*imag
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Conjugate(c_Complex_t c) {
|
||||||
|
c_Complex_t res;
|
||||||
|
res.real = c.real;
|
||||||
|
res.imag = -c.imag;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算复数的相反数 (Negate): res = -real - i*imag
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Negate(c_Complex_t c) {
|
||||||
|
return c_Complex_Make(-c.real, -c.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算复数的模长的平方 (Magnitude Squared / Norm)
|
||||||
|
* @note 避免了开方操作,适合用于比对大小以提升性能
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE double c_Complex_Norm(c_Complex_t c) {
|
||||||
|
return c.real * c.real + c.imag * c.imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算复数的模长 (Magnitude / Absolute Value)
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE double c_Complex_Abs(c_Complex_t c) {
|
||||||
|
return sqrt(c.real * c.real + c.imag * c.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算复数的幅角 (Phase Angle / Argument)
|
||||||
|
* @return 返回介于 -PI 到 PI 之间的弧度值
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE double c_Complex_Arg(c_Complex_t c) {
|
||||||
|
return atan2(c.imag, c.real);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================================================================== */
|
||||||
|
/* 标量混合运算接口 (Scalar & Complex Operations) */
|
||||||
|
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_AddScalar(c_Complex_t c, double s) {
|
||||||
|
return c_Complex_Make(c.real + s, c.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_SubScalar(c_Complex_t c, double s) {
|
||||||
|
return c_Complex_Make(c.real - s, c.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_MulScalar(c_Complex_t c, double s) {
|
||||||
|
return c_Complex_Make(c.real * s, c.imag * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_DivScalar(c_Complex_t c, double s) {
|
||||||
|
if (s == 0.0) return c_Complex_Make(0.0, 0.0);
|
||||||
|
return c_Complex_Make(c.real / s, c.imag / s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================================================================== */
|
||||||
|
/* 超越函数与幂运算接口 (Transcendental & Power Functions) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数的指数函数: e^c
|
||||||
|
* @note e^(x+iy) = e^x * (cos(y) + i*sin(y))
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Exp(c_Complex_t c) {
|
||||||
|
double exp_x = exp(c.real);
|
||||||
|
return c_Complex_Make(exp_x * cos(c.imag), exp_x * sin(c.imag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数的自然对数函数: ln(c)
|
||||||
|
* @note ln(c) = ln(|c|) + i*arg(c)
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Log(c_Complex_t c) {
|
||||||
|
return c_Complex_Make(log(c_Complex_Abs(c)), c_Complex_Arg(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数的幂运算: base^exp
|
||||||
|
* @note a^b = exp(b * log(a))
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Pow(c_Complex_t base, c_Complex_t exponent) {
|
||||||
|
if (base.real == 0.0 && base.imag == 0.0) return c_Complex_Make(0.0, 0.0);
|
||||||
|
return c_Complex_Exp(c_Complex_Mul(exponent, c_Complex_Log(base)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数的实数幂运算: base^p (p 为实数标量)
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_PowScalar(c_Complex_t base, double p) {
|
||||||
|
if (base.real == 0.0 && base.imag == 0.0) return c_Complex_Make(0.0, 0.0);
|
||||||
|
double r = pow(c_Complex_Abs(base), p);
|
||||||
|
double theta = c_Complex_Arg(base) * p;
|
||||||
|
return c_Complex_FromPolar(r, theta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 复数开平方根: sqrt(c)
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE c_Complex_t c_Complex_Sqrt(c_Complex_t c) {
|
||||||
|
double r = c_Complex_Abs(c);
|
||||||
|
double real_part = sqrt((r + c.real) / 2.0);
|
||||||
|
double imag_part = sqrt((r - c.real) / 2.0);
|
||||||
|
if (c.imag < 0.0) imag_part = -imag_part;
|
||||||
|
return c_Complex_Make(real_part, imag_part);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================================================================== */
|
||||||
|
/* 比较运算 (Comparison Interfaces) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断两个复数是否在允许的误差内相等
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE bool c_Complex_Equals(c_Complex_t a, c_Complex_t b, double epsilon) {
|
||||||
|
return (fabs(a.real - b.real) <= epsilon) && (fabs(a.imag - b.imag) <= epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*INCLUDED_C_COMPLEX_H*/
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include "c_Test.h"
|
||||||
|
#include "c_Complex.h"
|
||||||
|
|
||||||
|
TEST_CASE(test_complex_extended_apis) {
|
||||||
|
c_Complex_t a = c_Complex_Make(3.0, 4.0);
|
||||||
|
c_Complex_t b = c_Complex_Make(1.0, -2.0);
|
||||||
|
|
||||||
|
/* 1. 测试属性提取 */
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(25.0, c_Complex_Norm(a), "Norm 运算错误");
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(5.0, c_Complex_Abs(a), "Abs 运算错误");
|
||||||
|
|
||||||
|
/* 2. 测试除法 */
|
||||||
|
c_Complex_t res_div = c_Complex_Div(a, b);
|
||||||
|
/* (3+4i)/(1-2i) = (3+4i)(1+2i)/5 = (-5+10i)/5 = -1 + 2i */
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(-1.0, res_div.real, "复数除法实部错误");
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(2.0, res_div.imag, "复数除法虚部错误");
|
||||||
|
|
||||||
|
/* 3. 测试极坐标转换与欧拉公式基础验证 */
|
||||||
|
/* e^(i*PI) = -1 + 0i */
|
||||||
|
c_Complex_t polar = c_Complex_FromPolar(1.0, 3.141592653589793);
|
||||||
|
ASSERT_TRUE(c_Complex_Equals(polar, c_Complex_Make(-1.0, 0.0), 1e-6));
|
||||||
|
|
||||||
|
/* 4. 测试标量混合运算 */
|
||||||
|
c_Complex_t res_scalar = c_Complex_MulScalar(b, 3.0);
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(3.0, res_scalar.real, "标量乘法实部错误");
|
||||||
|
ASSERT_DOUBLE_EQ_MSG(-6.0, res_scalar.imag, "标量乘法虚部错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
TEST_START(TestSuite);
|
||||||
|
RUN_TEST(test_complex_extended_apis);
|
||||||
|
|
||||||
|
TEST_REPORT();
|
||||||
|
return (g_test_registry.failed_count > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include <c_HardwareConcurrency.h>
|
||||||
|
|
||||||
|
/* ================================================================================================================== */
|
||||||
|
/* Cross-Platform OS Handle Interceptions */
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(__linux__) || defined(__ANDROID__) || defined(__hpux) || defined(_AIX)
|
||||||
|
#include <unistd.h>
|
||||||
|
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
c_size_t c_HardwareConcurrency(void) {
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
SYSTEM_INFO sys_info;
|
||||||
|
GetSystemInfo(&sys_info);
|
||||||
|
return (c_size_t)sys_info.dwNumberOfProcessors > 0 ? (c_size_t)sys_info.dwNumberOfProcessors : 1;
|
||||||
|
|
||||||
|
#elif defined(__linux__) || defined(__ANDROID__) || defined(__hpux) || defined(_AIX)
|
||||||
|
long cores = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
return (cores > 0) ? (c_size_t)cores : 1;
|
||||||
|
|
||||||
|
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||||
|
int mib[2];
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_NCPU; /* Fallback baseline key token selector */
|
||||||
|
|
||||||
|
#ifdef HW_AVAILCPU
|
||||||
|
mib[1] = HW_AVAILCPU; /* Preferred choice: online/available logical cores count query */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int num_cores = 0;
|
||||||
|
size_t len = sizeof(num_cores);
|
||||||
|
if (sysctl(mib, 2, &num_cores, &len, NULL, 0) == 0 && num_cores > 0) {
|
||||||
|
return (c_size_t)num_cores;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Safe fallback anchor primitive for completely unknown or specialized bare-metal setups */
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef INCLUDED_C_HARDWARECONCURRENCY_H
|
||||||
|
#define INCLUDED_C_HARDWARECONCURRENCY_H
|
||||||
|
|
||||||
|
#ifndef INCLUDED_C_TYPES_H
|
||||||
|
#include <c_Types.h>
|
||||||
|
#endif /*INCLUDED_C_TYPES_H*/
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
c_size_t c_HardwareConcurrency(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*INCLUDED_C_HARDWARECONCURRENCY_H*/
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "c_Test.h"
|
||||||
|
#include "c_HardwareConcurrency.h"
|
||||||
|
|
||||||
|
TEST_CASE(test_hardware_concurrency_detection) {
|
||||||
|
c_size_t cores = c_HardwareConcurrency();
|
||||||
|
|
||||||
|
/* Log numerical results using your system's architectural type-spec macro format */
|
||||||
|
printf(" " COLOR_CYAN "[INFO] Detected System Hardware Concurrency: %" C_PRId " logical core(s)" COLOR_RESET "\n", (uint64_t)cores);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invariants verification:
|
||||||
|
* Even on old or highly constrained embedded hardware setups,
|
||||||
|
* the system must feature at least 1 logical core running the test loop thread.
|
||||||
|
*/
|
||||||
|
ASSERT_TRUE(cores >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
TEST_START(HardwareConcurrency_Detection_Suite);
|
||||||
|
RUN_TEST(test_hardware_concurrency_detection);
|
||||||
|
TEST_REPORT();
|
||||||
|
RETURN_TEST_STATUS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include <c_LongestCommonSubstring.h>
|
||||||
|
|
||||||
|
c_err_t c_LongestCommonSubstring_Init(c_LongestCommonSubstring_t* self, const char* str_a, const char* str_b, c_Allocator_t* allocator) {
|
||||||
|
if (!self || !str_a || !str_b) return C_ERR_PARAM;
|
||||||
|
|
||||||
|
self->allocator = allocator ? *allocator : c_DefaultAllocator;
|
||||||
|
self->max_len = 0;
|
||||||
|
self->start_idx_a = 0;
|
||||||
|
|
||||||
|
c_size_t len_a = strlen(str_a);
|
||||||
|
c_size_t len_b = strlen(str_b);
|
||||||
|
|
||||||
|
if (len_a == 0 || len_b == 0) return C_SUCCESS; /* Trivial empty evaluation check */
|
||||||
|
|
||||||
|
/* Optimize spatial footprint boundaries by always assigning the shorter collection as columns */
|
||||||
|
const char* s_row = str_a;
|
||||||
|
const char* s_col = str_b;
|
||||||
|
c_size_t rows = len_a;
|
||||||
|
c_size_t cols = len_b;
|
||||||
|
c_bool_t swapped = C_FALSE;
|
||||||
|
|
||||||
|
if (len_a < len_b) {
|
||||||
|
s_row = str_b;
|
||||||
|
s_col = str_a;
|
||||||
|
rows = len_b;
|
||||||
|
cols = len_a;
|
||||||
|
swapped = C_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. Allocate space-optimized 2-row rolling history track arrays */
|
||||||
|
c_size_t* prev_row = (c_size_t*)c_Allocator_Calloc(&self->allocator, cols + 1, sizeof(c_size_t));
|
||||||
|
c_size_t* curr_row = (c_size_t*)c_Allocator_Calloc(&self->allocator, cols + 1, sizeof(c_size_t));
|
||||||
|
|
||||||
|
if (!prev_row || !curr_row) {
|
||||||
|
if (prev_row) c_Allocator_Free(&self->allocator, prev_row);
|
||||||
|
if (curr_row) c_Allocator_Free(&self->allocator, curr_row);
|
||||||
|
return C_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Linear matrix sweep iteration pass */
|
||||||
|
for (c_size_t i = 1; i <= rows; ++i) {
|
||||||
|
for (c_size_t j = 1; j <= cols; ++j) {
|
||||||
|
if (s_row[i - 1] == s_col[j - 1]) {
|
||||||
|
curr_row[j] = prev_row[j - 1] + 1;
|
||||||
|
|
||||||
|
if (curr_row[j] > self->max_len) {
|
||||||
|
self->max_len = curr_row[j];
|
||||||
|
|
||||||
|
/* Map index location back relative to original String A coordinates */
|
||||||
|
if (!swapped) {
|
||||||
|
self->start_idx_a = i - self->max_len;
|
||||||
|
} else {
|
||||||
|
self->start_idx_a = j - self->max_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
curr_row[j] = 0; /* Continuous block break, reset sequence link */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Shift the rolling window contents smoothly */
|
||||||
|
memcpy(prev_row, curr_row, (cols + 1) * sizeof(c_size_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
c_Allocator_Free(&self->allocator, prev_row);
|
||||||
|
c_Allocator_Free(&self->allocator, curr_row);
|
||||||
|
return C_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_LongestCommonSubstring_Destroy(c_LongestCommonSubstring_t* self) {
|
||||||
|
if (!self) return;
|
||||||
|
self->max_len = 0;
|
||||||
|
self->start_idx_a = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_err_t c_LongestCommonSubstring_GetSubstring(const c_LongestCommonSubstring_t* self, const char* str_a, char* out_buffer, c_size_t buffer_cap) {
|
||||||
|
if (!self || !str_a || !out_buffer || buffer_cap == 0) return C_ERR_PARAM;
|
||||||
|
if (self->max_len == 0) {
|
||||||
|
out_buffer[0] = '\0';
|
||||||
|
return C_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer_cap <= self->max_len) return C_ERR_OUTOFBOUND;
|
||||||
|
|
||||||
|
/* Securely slice and copy matching token block segment */
|
||||||
|
memcpy(out_buffer, str_a + self->start_idx_a, self->max_len);
|
||||||
|
out_buffer[self->max_len] = '\0'; /* Terminate string safely */
|
||||||
|
|
||||||
|
return C_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef INCLUDED_C_LONGESTCOMMONSUBSTRING_H
|
||||||
|
#define INCLUDED_C_LONGESTCOMMONSUBSTRING_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*/
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
c_size_t max_len; /* Length of the longest common substring found */
|
||||||
|
c_size_t start_idx_a; /* Starting coordinate index inside String A sequence */
|
||||||
|
c_Allocator_t allocator; /* Deep copy of the user-provided allocator */
|
||||||
|
} c_LongestCommonSubstring_t;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Non-recursively computes the longest common substring between String A and String B.
|
||||||
|
* @param str_a Null-terminated or bound-verified character string A sequence
|
||||||
|
* @param str_b Null-terminated or bound-verified character string B sequence
|
||||||
|
* @param allocator Explicit allocator context used to configure temporary dynamic matrices
|
||||||
|
*/
|
||||||
|
c_err_t c_LongestCommonSubstring_Init(c_LongestCommonSubstring_t* self, const char* str_a, const char* str_b, c_Allocator_t* allocator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Releases internal tracking structures safely (Trivial for rolling array layouts).
|
||||||
|
*/
|
||||||
|
void c_LongestCommonSubstring_Destroy(c_LongestCommonSubstring_t* self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the length of the discovered longest common substring.
|
||||||
|
*/
|
||||||
|
C_STATIC_FORCE_INLINE
|
||||||
|
c_size_t c_LongestCommonSubstring_GetLen(const c_LongestCommonSubstring_t* self) {
|
||||||
|
return self ? self->max_len : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts the resolved longest common substring payload back into a safe user buffer.
|
||||||
|
* @param out_buffer Presized destination character array to copy results into
|
||||||
|
* @param buffer_cap Maximum threshold size limit matching out_buffer allocation bounds
|
||||||
|
*/
|
||||||
|
c_err_t c_LongestCommonSubstring_GetSubstring(const c_LongestCommonSubstring_t* self, const char* str_a, char* out_buffer, c_size_t buffer_cap);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*INCLUDED_C_LONGESTCOMMONSUBSTRING_H*/
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include "c_Test.h"
|
||||||
|
#include "c_LongestCommonSubstring.h"
|
||||||
|
|
||||||
|
TEST_CASE(test_longest_common_substring_resolution) {
|
||||||
|
/* Test inputs:
|
||||||
|
* Str A: "AABCCBDE"
|
||||||
|
* Str B: "XABCYBDE"
|
||||||
|
* Longest common continuous substring token should be "BDE" (Len: 3) or "ABC" (Len: 3).
|
||||||
|
* Our engine catches the first optimal one maximized or tracked sequentially.
|
||||||
|
*/
|
||||||
|
const char* a = "AABCCBDE";
|
||||||
|
const char* b = "XABCYBDE";
|
||||||
|
|
||||||
|
c_LongestCommonSubstring_t lcs;
|
||||||
|
c_err_t err = c_LongestCommonSubstring_Init(&lcs, a, b, NULL);
|
||||||
|
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||||
|
|
||||||
|
c_size_t resolved_len = c_LongestCommonSubstring_GetLen(&lcs);
|
||||||
|
ASSERT_LL_EQ(3, resolved_len);
|
||||||
|
|
||||||
|
char res_buf[16];
|
||||||
|
err = c_LongestCommonSubstring_GetSubstring(&lcs, a, res_buf, sizeof(res_buf));
|
||||||
|
ASSERT_INT_EQ(C_SUCCESS, err);
|
||||||
|
|
||||||
|
/* Assert exact string equality token content matches */
|
||||||
|
ASSERT_INT_EQ(0, strcmp("ABC", res_buf) == 0 || strcmp("BDE", res_buf) == 0 ? 0 : -1);
|
||||||
|
|
||||||
|
c_LongestCommonSubstring_Destroy(&lcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
TEST_START(LongestCommonSubstring_Verification_Suite);
|
||||||
|
RUN_TEST(test_longest_common_substring_resolution);
|
||||||
|
TEST_REPORT();
|
||||||
|
RETURN_TEST_STATUS;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user