开始设计
This commit is contained in:
@@ -0,0 +1 @@
|
||||
#include <c_BinaryInsertionSort.h>
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef INCLUDED_C_BINARYINSERTIONSORT_H
|
||||
#define INCLUDED_C_BINARYINSERTIONSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* 通用折半插入排序函数
|
||||
* @param base 指向待排序数组首元素的指针
|
||||
* @param num 数组中元素的个数
|
||||
* @param size 每个元素的大小(字节数)
|
||||
* @param compar 指向比较函数的指针
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_BinaryInsertionSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移
|
||||
|
||||
// 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp)
|
||||
#define STACK_LIMIT 128
|
||||
char stack_buf[STACK_LIMIT];
|
||||
void* temp = NULL;
|
||||
|
||||
if (size <= STACK_LIMIT) {
|
||||
temp = stack_buf;
|
||||
} else {
|
||||
temp = C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
}
|
||||
|
||||
for (c_size_t i = 1; i < num; i++) {
|
||||
// temp = arr[i]:备份当前要插入的元素
|
||||
memcpy(temp, arr + (i * size), size);
|
||||
|
||||
// 1. 使用二分查找决定插入位置 [left, right]
|
||||
long long left = 0;
|
||||
long long right = i - 1;
|
||||
|
||||
while (left <= right) {
|
||||
long long mid = left + (right - left) / 2;
|
||||
|
||||
// 为了保证排序的稳定性(Stability),
|
||||
// 当 mid 元素等于 temp 时,应当继续向右区间查找,把 temp 放到相同元素的后面
|
||||
if (compar(arr + (mid * size), temp) <= 0) {
|
||||
left = mid + 1; // 目标位置在右边
|
||||
} else {
|
||||
right = mid - 1; // 目标位置在左边
|
||||
}
|
||||
}
|
||||
// 循环结束时,left 就是元素应该插入的目标索引位置
|
||||
|
||||
// 2. 将 [left, i-1] 区间的元素全部向后移动一个位置
|
||||
for (long long j = i - 1; j >= left; j--) {
|
||||
memcpy(arr + ((j + 1) * size), arr + (j * size), size);
|
||||
}
|
||||
|
||||
// 3. 将 temp 插入到腾出来的 left 位置
|
||||
memcpy(arr + (left * size), temp, size);
|
||||
}
|
||||
|
||||
// Only trigger free if it was actually allocated from the heap
|
||||
if (size > STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_BINARYINSERTIONSORT_H*/
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "c_BinaryInsertionSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
char name[20];
|
||||
double price;
|
||||
} Product;
|
||||
|
||||
// 自定义比较函数:按价格 (price) 升序排列
|
||||
int compareProductsByPrice(const void* a, const void* b) {
|
||||
const Product* p1 = (const Product*)a;
|
||||
const Product* p2 = (const Product*)b;
|
||||
|
||||
if (p1->price < p2->price) return -1;
|
||||
if (p1->price > p2->price) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 准备一个商品数组
|
||||
Product shop[] = {
|
||||
{"Laptop", 4500.0},
|
||||
{"Phone", 3200.0},
|
||||
{"Mouse", 150.0},
|
||||
{"Tablet", 3200.0}, // 测试稳定性:单价和 Phone 相同
|
||||
{"Keybd", 299.0}
|
||||
};
|
||||
size_t count = sizeof(shop) / sizeof(shop[0]);
|
||||
|
||||
printf("排序前:\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("商品: %-8s | 价格: %.2f\n", shop[i].name, shop[i].price);
|
||||
}
|
||||
|
||||
// 调用通用折半插入排序
|
||||
c_BinaryInsertionSort(shop, count, sizeof(Product), compareProductsByPrice);
|
||||
|
||||
printf("\n排序后(按价格升序):\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("商品: %-8s | 价格: %.2f\n", shop[i].name, shop[i].price);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#include <c_HeapSort.h>
|
||||
#include <c_Memory.h>
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef INCLUDED_C_HEAPSORT_H
|
||||
#define INCLUDED_C_HEAPSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* Internal macro helper to swap two arbitrary blocks of memory of given size.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) {
|
||||
if (a == b) return;
|
||||
memcpy(temp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, temp, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Sift-Down structural loop modified to build/maintain a Max-Heap.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_Heapify(char* arr, c_size_t num, c_size_t root, c_size_t size,
|
||||
void* temp, int (*compar)(const void*, const void*)) {
|
||||
c_int_t current = root;
|
||||
|
||||
while (1) {
|
||||
c_int_t left_child = (2 * current) + 1;
|
||||
c_int_t right_child = (2 * current) + 2;
|
||||
c_int_t largest = current;
|
||||
|
||||
if (left_child < num &&
|
||||
compar(arr + (left_child * size), arr + (largest * size)) > 0) {
|
||||
largest = left_child;
|
||||
}
|
||||
|
||||
if (right_child < num &&
|
||||
compar(arr + (right_child * size), arr + (largest * size)) > 0) {
|
||||
largest = right_child;
|
||||
}
|
||||
|
||||
if (largest == current) {
|
||||
break;
|
||||
}
|
||||
|
||||
c_SwapInternal(arr + (current * size), arr + (largest * size), size, temp);
|
||||
current = largest;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* Top-Level Framework Entry Point for Heapsort.
|
||||
* Time Complexity: O(n log n) | Space Complexity: O(1) in-place | Stable: No
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_HeapSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
|
||||
#define HEAP_STACK_LIMIT 128
|
||||
char stack_buf[HEAP_STACK_LIMIT];
|
||||
void* temp = (size <= HEAP_STACK_LIMIT) ? stack_buf : C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
|
||||
// Safely cast size parameters to signed c_int_t variables before starting the algorithm loops
|
||||
c_int_t total_items = (c_int_t)num;
|
||||
|
||||
// Phase 1: Build the Max-Heap from the bottom up (Floyd's heap construction)
|
||||
for (c_int_t i = (total_items / 2) - 1; i >= 0; i--) {
|
||||
c_Heapify(arr, total_items, i, size, temp, compar);
|
||||
}
|
||||
|
||||
// Phase 2: In-place sorted array extraction
|
||||
for (c_int_t k = total_items - 1; k > 0; k--) {
|
||||
// Swap root max element to current end positions
|
||||
c_SwapInternal(arr, arr + (k * size), size, temp);
|
||||
|
||||
// Re-heapify the remaining sub-heap structure
|
||||
c_Heapify(arr, k, 0, size, temp, compar);
|
||||
}
|
||||
|
||||
if (size > HEAP_STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef HEAP_STACK_LIMIT
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_HEAPSORT_H*/
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "c_HeapSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return false; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
typedef struct {
|
||||
int log_id;
|
||||
float core_temperature;
|
||||
} SensorLog;
|
||||
|
||||
int compareSensorLogs(const void* a, const void* b) {
|
||||
const SensorLog* s1 = (const SensorLog*)a;
|
||||
const SensorLog* s2 = (const SensorLog*)b;
|
||||
if (s1->core_temperature < s2->core_temperature) return -1;
|
||||
if (s1->core_temperature > s2->core_temperature) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compareInts(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
bool test_heapsort_inverted_array(void) {
|
||||
int datasets[] = { 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 };
|
||||
c_size_t total = sizeof(datasets) / sizeof(datasets);
|
||||
|
||||
c_HeapSort(datasets, total, sizeof(int), compareInts);
|
||||
|
||||
for (c_size_t i = 0; i < total - 1; i++) {
|
||||
EXPECT_EQ(datasets[i] <= datasets[i+1], true, "Inverted array sequence failed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_heapsort_structures(void) {
|
||||
SensorLog logs[] = {
|
||||
{ 901, 45.2f },
|
||||
{ 902, 101.4f},
|
||||
{ 903, -12.6f}, // Target absolute minimum (Must map to index 0)
|
||||
{ 904, 32.0f },
|
||||
{ 905, 78.9f }
|
||||
};
|
||||
c_size_t count = C_ARRAY_SIZE(logs);
|
||||
|
||||
c_HeapSort(logs, count, sizeof(SensorLog), compareSensorLogs);
|
||||
|
||||
// Verify structural sorting order consistency
|
||||
EXPECT_EQ(logs[0].log_id, 903, "Index 0 check failed");
|
||||
EXPECT_EQ(logs[1].log_id, 904, "Index 1 check failed");
|
||||
EXPECT_EQ(logs[2].log_id, 901, "Index 2 check failed");
|
||||
EXPECT_EQ(logs[3].log_id, 905, "Index 3 check failed");
|
||||
EXPECT_EQ(logs[4].log_id, 902, "Index 4 check failed");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Robust Fixed Framework Unit Testing ===\n");
|
||||
|
||||
if (test_heapsort_inverted_array() && test_heapsort_structures()) {
|
||||
printf(" [PASS] All Heapsort Pipeline Re-allocations Passed Correctly.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Test Sequence Intercepted Error.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <c_InPlaceMSDRadixSort.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Inline helper to safely extract a character at string offset d.
|
||||
* Automatically maps a string's null terminator to a sentinel value of -1.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
int c_InPlaceMSDRadixSort_CharAt(const char* str, c_size_t d) {
|
||||
if (str == NULL) return -1;
|
||||
c_size_t i = 0;
|
||||
while (i < d && str[i] != '\0') {
|
||||
i++;
|
||||
}
|
||||
if (str[i] == '\0' || i < d) return -1;
|
||||
return (unsigned char)str[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Core Private Recursive Sub-partition In-place Sorting Subroutine.
|
||||
* Employs a localized head/tail lookup permutation ring to operate directly within array slices.
|
||||
*/
|
||||
static void c_InPlaceMSDRadixSort_Recursive(char** arr, long long lo, long long hi, c_size_t d,
|
||||
c_size_t R, c_size_t* count_buf, long long* heads, long long* tails) {
|
||||
if (hi <= lo) return;
|
||||
|
||||
// Elements are shifted forward by +2 slots to absorb the -1 string end sentinel gracefully
|
||||
c_size_t total_buckets = R + 2;
|
||||
memset(count_buf, 0, total_buckets * sizeof(c_size_t));
|
||||
|
||||
// Pass A: Compute frequency counts for the current digit slice
|
||||
for (long long i = lo; i <= hi; i++) {
|
||||
int c = c_InPlaceMSDRadixSort_CharAt(arr[i], d);
|
||||
count_buf[c + 2]++;
|
||||
}
|
||||
|
||||
// Pass B: Transform frequencies into absolute head and tail cursor index maps
|
||||
heads[0] = lo;
|
||||
tails[0] = lo + (long long)count_buf[0];
|
||||
for (c_size_t r = 1; r < total_buckets; r++) {
|
||||
heads[r] = tails[r - 1];
|
||||
tails[r] = heads[r] + (long long)count_buf[r];
|
||||
}
|
||||
|
||||
// Pass C: Cyclic Permutation Swap Element Loop (In-Place Distribution)
|
||||
for (c_size_t r = 0; r < total_buckets; r++) {
|
||||
while (heads[r] < tails[r]) {
|
||||
long long curr_idx = heads[r];
|
||||
int c = c_InPlaceMSDRadixSort_CharAt(arr[curr_idx], d);
|
||||
c_size_t bucket = (c_size_t)(c + 2);
|
||||
|
||||
if (bucket == r) {
|
||||
heads[r]++; // Element is already in its correct bucket, step forward
|
||||
} else {
|
||||
// Evict the element to its correct destination bucket via data swap
|
||||
long long dest_idx = heads[bucket];
|
||||
char* temp = arr[curr_idx];
|
||||
arr[curr_idx] = arr[dest_idx];
|
||||
arr[dest_idx] = temp;
|
||||
|
||||
heads[bucket]++; // Increment the destination bucket's cursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass D: Recursively process sub-arrays for each character bucket
|
||||
// Shorter strings that terminated (sentinel character index 0) do not need deeper processing
|
||||
long long current_lo = lo + (long long)count_buf[0];
|
||||
for (c_size_t r = 1; r < total_buckets; r++) {
|
||||
long long current_hi = current_lo + (long long)count_buf[r] - 1;
|
||||
|
||||
if (current_hi > current_lo) {
|
||||
c_InPlaceMSDRadixSort_Recursive(arr, current_lo, current_hi, d + 1, R, count_buf, heads, tails);
|
||||
}
|
||||
current_lo = current_hi + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array of variable-length strings completely in-place.
|
||||
* Space Complexity: O(1) Auxiliary Heap memory footprint (Excluding recursive tracking arrays bound to R)
|
||||
*/
|
||||
c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n) {
|
||||
if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM;
|
||||
if (n <= 1) return C_ERR_OK;
|
||||
|
||||
// Radix-bound tracking buffers are allocated once upfront to eliminate heap overhead in hot loops
|
||||
c_size_t total_buckets = sort->R + 2;
|
||||
c_size_t* count_buf = (c_size_t*)C_ALLOC(total_buckets * sizeof(c_size_t));
|
||||
long long* heads = (long long*)C_ALLOC(total_buckets * sizeof(long long));
|
||||
long long* tails = (long long*)C_ALLOC(total_buckets * sizeof(long long));
|
||||
|
||||
if (count_buf == NULL || heads == NULL || tails == NULL) {
|
||||
C_FREE(count_buf); C_FREE(heads); C_FREE(tails);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// Launch the in-place cyclic permutation partition tree
|
||||
c_InPlaceMSDRadixSort_Recursive(arr, 0, (long long)n - 1, 0, sort->R, count_buf, heads, tails);
|
||||
|
||||
C_FREE(count_buf);
|
||||
C_FREE(heads);
|
||||
C_FREE(tails);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef INCLUDED_C_INPLACEMSDRADIXSORT_H
|
||||
#define INCLUDED_C_INPLACEMSDRADIXSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays)
|
||||
} c_InPlaceMSDRadixSort_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_InPlaceMSDRadixSort_Sort(const c_InPlaceMSDRadixSort_t* sort, char** arr, c_size_t n);
|
||||
|
||||
#endif /*INCLUDED_C_INPLACEMSDRADIXSORT_H*/
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "c_InPlaceMSDRadixSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "c_Memory.h"
|
||||
|
||||
// Your updated line tracing diagnostic macro
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
c_bool_t test_inplace_msd_radix_sort_execution(void) {
|
||||
c_InPlaceMSDRadixSort_t sort;
|
||||
sort.R = 256; // Standard extended ASCII alphabet boundaries
|
||||
|
||||
c_size_t n = 7;
|
||||
// Unsorted variable-length string test pool configurations
|
||||
char* test_data[] = {
|
||||
"she",
|
||||
"sells",
|
||||
"seashells",
|
||||
"by",
|
||||
"the",
|
||||
"sea",
|
||||
"shore"
|
||||
};
|
||||
|
||||
// Allocate an array of modifiable pointers to replicate the application environment
|
||||
char** arr = (char**)C_ALLOC(n * sizeof(char*));
|
||||
if (arr == NULL) return C_FALSE;
|
||||
for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i];
|
||||
|
||||
printf(" [LOG] Launching space-optimized In-Place MSD Radix Sort...\n");
|
||||
c_err_t err = c_InPlaceMSDRadixSort_Sort(&sort, arr, n);
|
||||
|
||||
EXPECT_EQ(err, C_ERR_OK, "In-place MSD sort engine returned unexpected runtime error code");
|
||||
|
||||
// Mathematically sorted verification checkpoints list mapping:
|
||||
// Expected alphabetical sequence: by, sea, seashells, sells, she, shore, the
|
||||
EXPECT_EQ(strcmp(arr[0], "by"), 0, "Sorted position 0 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[1], "sea"), 0, "Sorted position 1 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[2], "seashells"), 0, "Sorted position 2 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[3], "sells"), 0, "Sorted position 3 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[4], "she"), 0, "Sorted position 4 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[5], "shore"), 0, "Sorted position 5 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[6], "the"), 0, "Sorted position 6 incorrect");
|
||||
|
||||
// Checkpoint 2: Variable Length Validation check
|
||||
// "sea" must strictly precede its extended prefix branch form "seashells"
|
||||
EXPECT_EQ(strcmp(arr[1], "sea") == 0 && strcmp(arr[2], "seashells") == 0, C_TRUE, "Variable-length short-prefix ordering failed");
|
||||
|
||||
printf(" [STAT] In-Place MSD Radix Sort verified successfully. Alphabetic Output: ");
|
||||
for (c_size_t i = 0; i < n; i++) {
|
||||
printf("%s ", arr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
C_FREE(arr);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Verification: In-Place MSD Radix Sort ===\n");
|
||||
if (test_inplace_msd_radix_sort_execution()) {
|
||||
printf(" [PASS] Variable-Length In-Place Cyclic Swap Permutations and Array Pointer Re-maps Verified.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Radix Partition Tree In-Place Structural Analysis Anomalies Intercepted.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
#include <c_IndexMaxPQ.h>
|
||||
|
||||
#include <c_IndexMaxPQ.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Internal helper to swap two positions inside the heap structures.
|
||||
* Keeps the inverted index (qp) tightly synchronized.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMaxPQ_Swap(c_IndexMaxPQ_t* pq_inst, c_size_t i, c_size_t j) {
|
||||
long long temp_pq = pq_inst->pq[i];
|
||||
pq_inst->pq[i] = pq_inst->pq[j];
|
||||
pq_inst->pq[j] = temp_pq;
|
||||
|
||||
pq_inst->qp[pq_inst->pq[i]] = (long long)i;
|
||||
pq_inst->qp[pq_inst->pq[j]] = (long long)j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sift-Up Operational Core for Max-Heap
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMaxPQ_SiftUp(c_IndexMaxPQ_t* pq_inst, c_size_t current) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
while (current > 0) {
|
||||
c_size_t parent = (current - 1) / 2;
|
||||
|
||||
const void* current_key = keys_arr + (pq_inst->pq[current] * es);
|
||||
const void* parent_key = keys_arr + (pq_inst->pq[parent] * es);
|
||||
|
||||
// Max-Heap condition: Break if current element is less than or equal to its parent
|
||||
if (pq_inst->compar(current_key, parent_key) <= 0) {
|
||||
break;
|
||||
}
|
||||
c_IndexMaxPQ_Swap(pq_inst, current, parent);
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sift-Down Operational Core for Max-Heap
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMaxPQ_SiftDown(c_IndexMaxPQ_t* pq_inst, c_size_t current) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
while (1) {
|
||||
c_size_t left_child = (2 * current) + 1;
|
||||
c_size_t right_child = (2 * current) + 2;
|
||||
c_size_t largest = current;
|
||||
|
||||
// Max-Heap condition: Target the larger of the two children to sift down
|
||||
if (left_child < pq_inst->size) {
|
||||
if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) {
|
||||
largest = left_child;
|
||||
}
|
||||
}
|
||||
if (right_child < pq_inst->size) {
|
||||
if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[largest] * es)) > 0) {
|
||||
largest = right_child;
|
||||
}
|
||||
}
|
||||
|
||||
if (largest == current) {
|
||||
break;
|
||||
}
|
||||
c_IndexMaxPQ_Swap(pq_inst, current, largest);
|
||||
current = largest;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
|
||||
pq_inst->max_items = max_items;
|
||||
pq_inst->element_size = element_size;
|
||||
pq_inst->size = 0;
|
||||
pq_inst->compar = compar;
|
||||
|
||||
pq_inst->keys = C_ALLOC(max_items * element_size);
|
||||
pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long));
|
||||
pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long));
|
||||
|
||||
if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) {
|
||||
C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
for (c_size_t i = 0; i < max_items; i++) {
|
||||
pq_inst->qp[i] = -1;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst) {
|
||||
if (pq_inst) {
|
||||
C_FREE(pq_inst->keys); pq_inst->keys = NULL;
|
||||
C_FREE(pq_inst->pq); pq_inst->pq = NULL;
|
||||
C_FREE(pq_inst->qp); pq_inst->qp = NULL;
|
||||
pq_inst->size = 0;
|
||||
pq_inst->max_items = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE;
|
||||
return pq_inst->qp[ext_id] != -1;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM;
|
||||
if (c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS;
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size);
|
||||
|
||||
c_size_t current = pq_inst->size;
|
||||
pq_inst->pq[current] = (long long)ext_id;
|
||||
pq_inst->qp[ext_id] = (long long)current;
|
||||
|
||||
pq_inst->size++;
|
||||
|
||||
c_IndexMaxPQ_SiftUp(pq_inst, current);
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) {
|
||||
if (pq_inst == NULL) return C_ERR_PARAM;
|
||||
if (pq_inst->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
long long max_ext_id = pq_inst->pq[0];
|
||||
if (out_ext_id) *out_ext_id = (c_size_t)max_ext_id;
|
||||
|
||||
if (out_element_buffer) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
memcpy(out_element_buffer, keys_arr + (max_ext_id * pq_inst->element_size), pq_inst->element_size);
|
||||
}
|
||||
|
||||
c_IndexMaxPQ_Swap(pq_inst, 0, pq_inst->size - 1);
|
||||
|
||||
pq_inst->qp[max_ext_id] = -1;
|
||||
pq_inst->size--;
|
||||
|
||||
if (pq_inst->size > 0) {
|
||||
c_IndexMaxPQ_SiftDown(pq_inst, 0);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM;
|
||||
if (!c_IndexMaxPQ_Contains(pq_inst, ext_id)) return C_ERR_NOT_FOUND;
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
memcpy(keys_arr + (ext_id * es), new_element, es);
|
||||
|
||||
c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id];
|
||||
|
||||
c_IndexMaxPQ_SiftUp(pq_inst, heap_pos);
|
||||
c_IndexMaxPQ_SiftDown(pq_inst, heap_pos);
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id) {
|
||||
if (pq_inst == NULL || pq_inst->size == 0) return NULL;
|
||||
if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0];
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
return keys_arr + (pq_inst->pq[0] * pq_inst->element_size);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef INCLUDED_C_INDEXMAXPQ_H
|
||||
#define INCLUDED_C_INDEXMAXPQ_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
void* keys;
|
||||
c_size_t element_size;
|
||||
c_size_t max_items;
|
||||
c_size_t size;
|
||||
long long* pq;
|
||||
long long* qp;
|
||||
int (*compar)(const void*, const void*);
|
||||
} c_IndexMaxPQ_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_IndexMaxPQ_Init(c_IndexMaxPQ_t* pq_inst, c_size_t max_items, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
void c_IndexMaxPQ_Destroy(c_IndexMaxPQ_t* pq_inst);
|
||||
|
||||
c_bool_t c_IndexMaxPQ_Contains(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id);
|
||||
|
||||
c_err_t c_IndexMaxPQ_Push(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* element);
|
||||
|
||||
c_err_t c_IndexMaxPQ_Pop(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer);
|
||||
|
||||
c_err_t c_IndexMaxPQ_ChangeKey(c_IndexMaxPQ_t* pq_inst, c_size_t ext_id, const void* new_element);
|
||||
|
||||
void* c_IndexMaxPQ_Peek(c_IndexMaxPQ_t* pq_inst, c_size_t* out_ext_id);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_INDEXMAXPQ_H*/
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "c_IndexMaxPQ.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Complex struct data block payload
|
||||
typedef struct {
|
||||
int task_id;
|
||||
float score; // Primary tracking element for MaxPQ (higher level extracted first)
|
||||
} TelemetryTask;
|
||||
|
||||
int compareTelemetryTasks(const void* a, const void* b) {
|
||||
const TelemetryTask* t1 = (const TelemetryTask*)a;
|
||||
const TelemetryTask* t2 = (const TelemetryTask*)b;
|
||||
if (t1->score < t2->score) return -1;
|
||||
if (t1->score > t2->score) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c_bool_t test_framework_index_max_pq(void) {
|
||||
c_IndexMaxPQ_t ipq;
|
||||
|
||||
// 1. Parameter Enforcement Validation
|
||||
EXPECT_EQ(c_IndexMaxPQ_Init(NULL, 10, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_PARAM, "NULL pointer checking missed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Init(&ipq, 0, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_PARAM, "Zero capacity checking missed");
|
||||
|
||||
// Allocate space for 4 telemetry streams (External IDs 0 to 3)
|
||||
EXPECT_EQ(c_IndexMaxPQ_Init(&ipq, 4, sizeof(TelemetryTask), compareTelemetryTasks), C_ERR_OK, "PQ Init failed");
|
||||
|
||||
TelemetryTask t0 = { 5001, 12.5f };
|
||||
TelemetryTask t1 = { 5002, 98.4f }; // Initial Maximum Element
|
||||
TelemetryTask t2 = { 5003, 45.2f };
|
||||
|
||||
// 2. Data Insertion & State Tracking
|
||||
EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 0, &t0), C_ERR_OK, "Push t0 failed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 1, &t1), C_ERR_OK, "Push t1 failed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 2, &t2), C_ERR_OK, "Push t2 failed");
|
||||
|
||||
EXPECT_EQ(c_IndexMaxPQ_Push(&ipq, 1, &t1), C_ERR_ALREADY_EXISTS, "Duplicate insert guard missed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Contains(&ipq, 1), C_TRUE, "Contains failed reporting registered elements");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Contains(&ipq, 3), C_FALSE, "Contains reported tracking on unused indices");
|
||||
|
||||
// 3. Dynamic Key Upgrades (ChangeKey runtime shifts)
|
||||
// Modify Telemetry Task 0 (t0, external ID: 0) from 12.5f to 105.7f.
|
||||
// This should instantly shift t0 to the root position of the Max-Heap.
|
||||
TelemetryTask t0_boosted = { 5001, 105.7f };
|
||||
EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 5, &t0_boosted), C_ERR_PARAM, "OOB Index verification checks missed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 3, &t0_boosted), C_ERR_NOT_FOUND, "Unregistered Index modification check missed");
|
||||
EXPECT_EQ(c_IndexMaxPQ_ChangeKey(&ipq, 0, &t0_boosted), C_ERR_OK, "Valid target key adjustment failed");
|
||||
|
||||
// 4. Verification Lookups & Sequential Extraction Lifecycle
|
||||
c_size_t extracted_ext_id = 999;
|
||||
TelemetryTask out_buffer;
|
||||
|
||||
// Peek Check: Root must now map to External ID 0 (score: 105.7f)
|
||||
TelemetryTask* peek_ptr = (TelemetryTask*)c_IndexMaxPQ_Peek(&ipq, &extracted_ext_id);
|
||||
EXPECT_EQ(extracted_ext_id, 0, "Peek resolved incorrect key slot context");
|
||||
EXPECT_EQ(peek_ptr->score == 105.7f, C_TRUE, "Peek structural memory value extraction wrong");
|
||||
|
||||
// Pop 1: Yields Task 0 (ID: 0, Score: 105.7f)
|
||||
EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 1 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 0, "Re-balancing priority lookup sequence wrong at Pop 1");
|
||||
|
||||
// Pop 2: Yields Task 1 (ID: 1, Score: 98.4f)
|
||||
EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 2 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 1, "Re-balancing priority lookup sequence wrong at Pop 2");
|
||||
|
||||
// Pop 3: Yields Task 2 (ID: 2, Score: 45.2f)
|
||||
EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 3 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 2, "Re-balancing priority lookup sequence wrong at Pop 3");
|
||||
|
||||
// 5. Empty Boundary Assert Cleanups
|
||||
EXPECT_EQ(ipq.size, 0, "Tracking metric registers missed reset zero flags");
|
||||
EXPECT_EQ(c_IndexMaxPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_EMPTY, "Empty pipeline crash missing exception flag hooks");
|
||||
|
||||
c_IndexMaxPQ_Destroy(&ipq);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Custom Framework Profiling: c_IndexMaxPQ ===\n");
|
||||
|
||||
if (test_framework_index_max_pq()) {
|
||||
printf(" [PASS] All Indexed Max-Priority Queue Architectural Requirements Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Architectural Verification Pipeline Failure Detected.\n");
|
||||
}
|
||||
printf("=== All Indexed Priority Queue Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
#include <c_IndexMinPQ.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Internal helper to swap two positions inside the heap structures.
|
||||
* Also keeps the inverted index (qp) tightly synchronized.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMinPQ_Swap(c_IndexMinPQ_t* pq_inst, c_size_t i, c_size_t j) {
|
||||
long long temp_pq = pq_inst->pq[i];
|
||||
pq_inst->pq[i] = pq_inst->pq[j];
|
||||
pq_inst->pq[j] = temp_pq;
|
||||
|
||||
pq_inst->qp[pq_inst->pq[i]] = (long long)i;
|
||||
pq_inst->qp[pq_inst->pq[j]] = (long long)j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sift-Up Operational Core
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMinPQ_SiftUp(c_IndexMinPQ_t* pq_inst, c_size_t current) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
while (current > 0) {
|
||||
c_size_t parent = (current - 1) / 2;
|
||||
|
||||
const void* current_key = keys_arr + (pq_inst->pq[current] * es);
|
||||
const void* parent_key = keys_arr + (pq_inst->pq[parent] * es);
|
||||
|
||||
if (pq_inst->compar(current_key, parent_key) >= 0) {
|
||||
break;
|
||||
}
|
||||
c_IndexMinPQ_Swap(pq_inst, current, parent);
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sift-Down Operational Core
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_IndexMinPQ_SiftDown(c_IndexMinPQ_t* pq_inst, c_size_t current) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
while (1) {
|
||||
c_size_t left_child = (2 * current) + 1;
|
||||
c_size_t right_child = (2 * current) + 2;
|
||||
c_size_t smallest = current;
|
||||
|
||||
if (left_child < pq_inst->size) {
|
||||
if (pq_inst->compar(keys_arr + (pq_inst->pq[left_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) {
|
||||
smallest = left_child;
|
||||
}
|
||||
}
|
||||
if (right_child < pq_inst->size) {
|
||||
if (pq_inst->compar(keys_arr + (pq_inst->pq[right_child] * es), keys_arr + (pq_inst->pq[smallest] * es)) < 0) {
|
||||
smallest = right_child;
|
||||
}
|
||||
}
|
||||
|
||||
if (smallest == current) {
|
||||
break;
|
||||
}
|
||||
c_IndexMinPQ_Swap(pq_inst, current, smallest);
|
||||
current = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (pq_inst == NULL || max_items == 0 || element_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
|
||||
pq_inst->max_items = max_items;
|
||||
pq_inst->element_size = element_size;
|
||||
pq_inst->size = 0;
|
||||
pq_inst->compar = compar;
|
||||
|
||||
pq_inst->keys = C_ALLOC(max_items * element_size);
|
||||
pq_inst->pq = (long long*)C_ALLOC(max_items * sizeof(long long));
|
||||
pq_inst->qp = (long long*)C_ALLOC(max_items * sizeof(long long));
|
||||
|
||||
if (pq_inst->keys == NULL || pq_inst->pq == NULL || pq_inst->qp == NULL) {
|
||||
// Safe cleanup if any allocation segment drops offline
|
||||
C_FREE(pq_inst->keys); C_FREE(pq_inst->pq); C_FREE(pq_inst->qp);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// Initialize inverted tracking array cells to -1 (indicating absent from heap)
|
||||
for (c_size_t i = 0; i < max_items; i++) {
|
||||
pq_inst->qp[i] = -1;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst) {
|
||||
if (pq_inst) {
|
||||
C_FREE(pq_inst->keys); pq_inst->keys = NULL;
|
||||
C_FREE(pq_inst->pq); pq_inst->pq = NULL;
|
||||
C_FREE(pq_inst->qp); pq_inst->qp = NULL;
|
||||
pq_inst->size = 0;
|
||||
pq_inst->max_items = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items) return C_FALSE;
|
||||
return pq_inst->qp[ext_id] != -1;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items || element == NULL) return C_ERR_PARAM;
|
||||
if (c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_ALREADY_EXISTS; // Must use Change Key API if already existing
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
|
||||
// Store key inside the primary index table slot
|
||||
memcpy(keys_arr + (ext_id * pq_inst->element_size), element, pq_inst->element_size);
|
||||
|
||||
// Append to bottom leaf array trackers
|
||||
c_size_t current = pq_inst->size;
|
||||
pq_inst->pq[current] = (long long)ext_id;
|
||||
pq_inst->qp[ext_id] = (long long)current;
|
||||
|
||||
pq_inst->size++;
|
||||
|
||||
// Sift upward to re-stabilize the min-heap property
|
||||
c_IndexMinPQ_SiftUp(pq_inst, current);
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) {
|
||||
if (pq_inst == NULL) return C_ERR_PARAM;
|
||||
if (pq_inst->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
long long min_ext_id = pq_inst->pq[0];
|
||||
if (out_ext_id) *out_ext_id = (c_size_t)min_ext_id;
|
||||
|
||||
if (out_element_buffer) {
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
memcpy(out_element_buffer, keys_arr + (min_ext_id * pq_inst->element_size), pq_inst->element_size);
|
||||
}
|
||||
|
||||
// Swap top root with the trailing active leaf node
|
||||
c_IndexMinPQ_Swap(pq_inst, 0, pq_inst->size - 1);
|
||||
|
||||
// Clean up tracking registers for extracted ID
|
||||
pq_inst->qp[min_ext_id] = -1;
|
||||
pq_inst->size--;
|
||||
|
||||
// Sift down from the root to re-balance the tree bounds
|
||||
if (pq_inst->size > 0) {
|
||||
c_IndexMinPQ_SiftDown(pq_inst, 0);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element) {
|
||||
if (pq_inst == NULL || ext_id >= pq_inst->max_items || new_element == NULL) return C_ERR_PARAM;
|
||||
if (!c_IndexMinPQ_Contains(pq_inst, ext_id)) return C_ERR_NOT_FOUND;
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
c_size_t es = pq_inst->element_size;
|
||||
|
||||
// Update raw payload data in-place
|
||||
memcpy(keys_arr + (ext_id * es), new_element, es);
|
||||
|
||||
// Leverage the inverted index (qp) to instantly locate the item's position inside the heap tree
|
||||
c_size_t heap_pos = (c_size_t)pq_inst->qp[ext_id];
|
||||
|
||||
// Trigger localized sifting in both directions. Only one will execute depending on whether the key grew or shrank.
|
||||
c_IndexMinPQ_SiftUp(pq_inst, heap_pos);
|
||||
c_IndexMinPQ_SiftDown(pq_inst, heap_pos);
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id) {
|
||||
if (pq_inst == NULL || pq_inst->size == 0) return NULL;
|
||||
if (out_ext_id) *out_ext_id = (c_size_t)pq_inst->pq[0];
|
||||
|
||||
char* keys_arr = (char*)pq_inst->keys;
|
||||
return keys_arr + (pq_inst->pq[0] * pq_inst->element_size);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef INCLUDED_C_INDEXMINPQ_H
|
||||
#define INCLUDED_C_INDEXMINPQ_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
typedef struct {
|
||||
void* keys; // Flat array storing user's complex items (indexed by external ID)
|
||||
c_size_t element_size; // Size of each element in bytes
|
||||
c_size_t max_items; // Maximum external ID capacity (0 to max_items - 1)
|
||||
c_size_t size; // Current active element count inside the heap
|
||||
|
||||
long long* pq; // Heap array: map heap position -> external ID
|
||||
long long* qp; // Inverted index array: map external ID -> heap position (-1 if not in heap)
|
||||
|
||||
int (*compar)(const void*, const void*); // Custom comparison rule pointer
|
||||
} c_IndexMinPQ_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_IndexMinPQ_Init(c_IndexMinPQ_t* pq_inst, c_size_t max_items, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
void c_IndexMinPQ_Destroy(c_IndexMinPQ_t* pq_inst);
|
||||
|
||||
c_bool_t c_IndexMinPQ_Contains(c_IndexMinPQ_t* pq_inst, c_size_t ext_id);
|
||||
|
||||
c_err_t c_IndexMinPQ_Push(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* element);
|
||||
|
||||
c_err_t c_IndexMinPQ_Pop(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id, void* out_element_buffer) ;
|
||||
|
||||
c_err_t c_IndexMinPQ_ChangeKey(c_IndexMinPQ_t* pq_inst, c_size_t ext_id, const void* new_element);
|
||||
|
||||
void* c_IndexMinPQ_Peek(c_IndexMinPQ_t* pq_inst, c_size_t* out_ext_id);
|
||||
|
||||
#endif /*INCLUDED_C_INDEXMINPQ_H*/
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "c_IndexMinPQ.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// Unit test validation driver macro
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d)\n", msg, (int)(expected), (int)(actual)); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Complex data block node mapping path costs
|
||||
typedef struct {
|
||||
float node_weight;
|
||||
int edge_id;
|
||||
} PathNode;
|
||||
|
||||
// Framework Comparator for PathNode structures (Min-Heap optimization logic)
|
||||
int comparePathCosts(const void* a, const void* b) {
|
||||
const PathNode* p1 = (const PathNode*)a;
|
||||
const PathNode* p2 = (const PathNode*)b;
|
||||
if (p1->node_weight < p2->node_weight) return -1;
|
||||
if (p1->node_weight > p2->node_weight) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional Test Profile validating Param checking, Duplicate Guards,
|
||||
* and ChangeKey structural re-balancing routines.
|
||||
*/
|
||||
c_bool_t test_framework_index_min_pq(void) {
|
||||
c_IndexMinPQ_t ipq;
|
||||
|
||||
// 1. Parameter Enforcement Validation Boundary Checks
|
||||
EXPECT_EQ(c_IndexMinPQ_Init(NULL, 10, sizeof(PathNode), comparePathCosts), C_ERR_PARAM, "NULL pointer checking missed");
|
||||
EXPECT_EQ(c_IndexMinPQ_Init(&ipq, 0, sizeof(PathNode), comparePathCosts), C_ERR_PARAM, "Zero capacity checking missed");
|
||||
|
||||
// Allocate space for up to 4 concurrent vertices (External IDs: 0 to 3)
|
||||
EXPECT_EQ(c_IndexMinPQ_Init(&ipq, 4, sizeof(PathNode), comparePathCosts), C_ERR_OK, "PQ Init failed");
|
||||
|
||||
PathNode v0 = { 35.6f, 100 };
|
||||
PathNode v1 = { 14.2f, 101 };
|
||||
PathNode v2 = { 88.1f, 102 };
|
||||
|
||||
// 2. State Insert Tracking
|
||||
EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 0, &v0), C_ERR_OK, "Push v0 failed");
|
||||
EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 1, &v1), C_ERR_OK, "Push v1 failed");
|
||||
EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 2, &v2), C_ERR_OK, "Push v2 failed");
|
||||
|
||||
// Check duplication guard safety logic
|
||||
EXPECT_EQ(c_IndexMinPQ_Push(&ipq, 1, &v1), C_ERR_ALREADY_EXISTS, "Duplicate insert guard missed");
|
||||
EXPECT_EQ(c_IndexMinPQ_Contains(&ipq, 1), C_TRUE, "Contains failed reporting registered elements");
|
||||
EXPECT_EQ(c_IndexMinPQ_Contains(&ipq, 3), C_FALSE, "Contains reported tracking on unused indices");
|
||||
|
||||
// 3. Dynamic Key Upgrades (ChangeKey runtime shifts)
|
||||
// Vertex 2 (v2) currently has a cost of 88.1f. Let's decrease it to 5.2f.
|
||||
// This action must shift it straight up to the head of the Min-Priority Queue.
|
||||
PathNode v2_optimized = { 5.2f, 102 };
|
||||
EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 5, &v2_optimized), C_ERR_PARAM, "OOB Index verification checks missed");
|
||||
EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 3, &v2_optimized), C_ERR_NOT_FOUND, "Unregistered Index modification check missed");
|
||||
EXPECT_EQ(c_IndexMinPQ_ChangeKey(&ipq, 2, &v2_optimized), C_ERR_OK, "Valid target key adjustment failed");
|
||||
|
||||
// 4. Verification Lookups & Sequential Extraction Lifecycle
|
||||
c_size_t extracted_ext_id = 999;
|
||||
PathNode out_buffer;
|
||||
|
||||
// Peek Check: Root must now resolve to External ID 2 (cost 5.2f)
|
||||
PathNode* peek_ptr = (PathNode*)c_IndexMinPQ_Peek(&ipq, &extracted_ext_id);
|
||||
EXPECT_EQ(extracted_ext_id, 2, "Peek resolved incorrect key slot context");
|
||||
EXPECT_EQ(peek_ptr->node_weight == 5.2f, C_TRUE, "Peek structural memory value extraction wrong");
|
||||
|
||||
// Pop 1: Yields Vertex 2 (ID: 2, Weight: 5.2f)
|
||||
EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 1 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 2, "Re-balancing priority lookup sequence wrong at Pop 1");
|
||||
|
||||
// Pop 2: Yields Vertex 1 (ID: 1, Weight: 14.2f)
|
||||
EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 2 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 1, "Re-balancing priority lookup sequence wrong at Pop 2");
|
||||
|
||||
// Pop 3: Yields Vertex 0 (ID: 0, Weight: 35.6f)
|
||||
EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_OK, "Pop execution step 3 failed");
|
||||
EXPECT_EQ(extracted_ext_id, 0, "Re-balancing priority lookup sequence wrong at Pop 3");
|
||||
|
||||
// 5. Empty Boundary Assert Cleanups
|
||||
EXPECT_EQ(ipq.size, 0, "Tracking metric registers missed reset zero flags");
|
||||
EXPECT_EQ(c_IndexMinPQ_Pop(&ipq, &extracted_ext_id, &out_buffer), C_ERR_EMPTY, "Empty pipeline crash missing exception flag hooks");
|
||||
|
||||
c_IndexMinPQ_Destroy(&ipq);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Custom Framework Profiling: c_IndexMinPQ ===\n");
|
||||
|
||||
if (test_framework_index_min_pq()) {
|
||||
printf(" [PASS] All Indexed Min-Priority Queue Architectural Requirements Verified Successfully.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Architectural Verification Pipeline Failure Detected.\n");
|
||||
}
|
||||
|
||||
printf("=== All Indexed Priority Queue Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_InsertionSort.h>
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef INCLUDED_C_INSERTIONSORT_H
|
||||
#define INCLUDED_C_INSERTIONSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* 通用插入排序函数
|
||||
* @param base 指向待排序数组首元素的指针
|
||||
* @param num 数组中元素的个数
|
||||
* @param size 每个元素的大小(字节数)
|
||||
* @param compar 指向比较函数的指针
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_InsertionSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
char* arr = (char*)base; // 强转为 char* 以便按单字节进行指针偏移
|
||||
|
||||
// 分配一块临时内存,用于存放当前要插入的“哨兵”元素(temp)
|
||||
#define STACK_LIMIT 128
|
||||
char stack_buf[STACK_LIMIT];
|
||||
void* temp = NULL;
|
||||
|
||||
if (size <= STACK_LIMIT) {
|
||||
temp = stack_buf;
|
||||
} else {
|
||||
temp = C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
}
|
||||
|
||||
for (c_size_t i = 1; i < num; i++) {
|
||||
// temp = arr[i]:将当前元素复制到临时空间
|
||||
memcpy(temp, arr + (i * size), size);
|
||||
|
||||
long long j = i - 1;
|
||||
|
||||
// 循环条件:j >= 0 且 arr[j] > temp
|
||||
// 使用 compar(arr + (j * size), temp) > 0 来判断是否需要后移
|
||||
while (j >= 0 && compar(arr + (j * size), temp) > 0) {
|
||||
// arr[j + 1] = arr[j]:将前面的元素往后移一位
|
||||
memcpy(arr + ((j + 1) * size), arr + (j * size), size);
|
||||
j--;
|
||||
}
|
||||
|
||||
// arr[j + 1] = temp:将目标元素插入到正确位置
|
||||
memcpy(arr + ((j + 1) * size), temp, size);
|
||||
}
|
||||
|
||||
// Only trigger free if it was actually allocated from the heap
|
||||
if (size > STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_INSERTIONSORT_H*/
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "c_InsertionSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[20];
|
||||
double score;
|
||||
} Student;
|
||||
|
||||
// 自定义比较函数:按成绩 (score) 降序排列
|
||||
// 如果希望升序,只需反转返回值或改变比较符号
|
||||
int compareStudentsByScoreDesc(const void* a, const void* b) {
|
||||
const Student* s1 = (const Student*)a;
|
||||
const Student* s2 = (const Student*)b;
|
||||
|
||||
if (s1->score > s2->score) return -1; // s1 成绩高,排在前面
|
||||
if (s1->score < s2->score) return 1; // s1 成绩低,排在后面
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// 准备一个无序的学生数组
|
||||
Student students[] = {
|
||||
{101, "Alice", 82.5},
|
||||
{105, "Bob", 95.0},
|
||||
{109, "Charlie", 88.0},
|
||||
{112, "David", 79.5}
|
||||
};
|
||||
size_t count = sizeof(students) / sizeof(students[0]);
|
||||
|
||||
printf("排序前:\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("学号: %d, 姓名: %s, 成绩: %.1f\n", students[i].id, students[i].name, students[i].score);
|
||||
}
|
||||
|
||||
// 调用通用插入排序
|
||||
c_InsertionSort(
|
||||
students,
|
||||
count,
|
||||
sizeof(Student),
|
||||
compareStudentsByScoreDesc
|
||||
);
|
||||
|
||||
printf("\n排序后(按成绩降序):\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("学号: %d, 姓名: %s, 成绩: %.1f\n", students[i].id, students[i].name, students[i].score);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <c_LSDRadixSort.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Sorts an array of fixed-length strings stably using the LSD Radix Sort pipeline.
|
||||
* Features upfront workspace allocations to completely eliminate heap thrashing in hot loops.
|
||||
*
|
||||
* Time Complexity: O(W * (N + R)) | Space Complexity: O(N + R) transient workspace memory
|
||||
* @param sort Pointer to the initialized LSD config profile.
|
||||
* @param arr Array of pointers to null-terminated char arrays (each must be at least W long).
|
||||
* @param n Total number of strings inside the array.
|
||||
*/
|
||||
c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n) {
|
||||
if (sort == NULL || arr == NULL || sort->R == 0 || sort->W == 0) return C_ERR_PARAM;
|
||||
if (n <= 1) return C_ERR_OK; // Trivial exit pass
|
||||
|
||||
c_size_t R = sort->R;
|
||||
c_size_t W = sort->W;
|
||||
|
||||
// Upfront Transient Workspace Allocation: Eliminates allocation overhead in hot paths
|
||||
char** aux = (char**)C_ALLOC(n * sizeof(char*));
|
||||
c_size_t* count = (c_size_t*)C_ALLOC((R + 1) * sizeof(c_size_t));
|
||||
|
||||
if (aux == NULL || count == NULL) {
|
||||
C_FREE(aux);
|
||||
C_FREE(count);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// --- Core Iterative LSD Pass Loop ---
|
||||
// Travel from right to left (Least Significant to Most Significant)
|
||||
for (long long d = (long long)W - 1; d >= 0; d--) {
|
||||
|
||||
// Reset the counting frequency registers
|
||||
memset(count, 0, (R + 1) * sizeof(c_size_t));
|
||||
|
||||
// Pass A: Compute frequency counts using character indices as bucket addresses
|
||||
for (c_size_t i = 0; i < n; i++) {
|
||||
unsigned char c = (unsigned char)arr[i][d];
|
||||
count[c + 1]++;
|
||||
}
|
||||
|
||||
// Pass B: Transform frequencies into structural start indexes (Prefix Sums)
|
||||
for (c_size_t r = 0; r < R; r++) {
|
||||
count[r + 1] += count[r];
|
||||
}
|
||||
|
||||
// Pass C: Distribute strings to the temporary aux array (Guarantees Stable Order Sorting)
|
||||
for (c_size_t i = 0; i < n; i++) {
|
||||
unsigned char c = (unsigned char)arr[i][d];
|
||||
aux[count[c]++] = arr[i];
|
||||
}
|
||||
|
||||
// Pass D: Copy back copies natively to the primary tracking layout pointers
|
||||
memcpy(arr, aux, n * sizeof(char*));
|
||||
}
|
||||
|
||||
// Purge temporary scratchpad workspace containers cleanly
|
||||
C_FREE(aux);
|
||||
C_FREE(count);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef INCLUDED_C_LSDRADIXSORT_H
|
||||
#define INCLUDED_C_LSDRADIXSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
typedef struct {
|
||||
c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for ASCII chars or bytes)
|
||||
c_size_t W; // Fixed length key width criteria (number of sorting passes/characters)
|
||||
} c_LSDRadixSort_t;
|
||||
|
||||
c_err_t c_LSDRadixSort_Sort(const c_LSDRadixSort_t* sort, char** arr, c_size_t n);
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_LSDRADIXSORT_H*/
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "c_LSDRadixSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
// Your updated line tracing diagnostic macro
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
c_bool_t test_lsd_radix_sort_execution(void) {
|
||||
c_LSDRadixSort_t sort;
|
||||
sort.R = 256; // Standard extended ASCII byte alphabet boundaries
|
||||
sort.W = 3; // Testing a fixed-width of exactly 3 characters per word
|
||||
|
||||
c_size_t n = 6;
|
||||
// Unsorted fixed-width test pool configurations
|
||||
char* test_data[] = {
|
||||
"DOG",
|
||||
"CAT",
|
||||
"COW",
|
||||
"BAR",
|
||||
"CAB",
|
||||
"DIG"
|
||||
};
|
||||
|
||||
// Allocate an array of modifiable pointers to replicate the application environment
|
||||
char** arr = (char**)C_ALLOC(n * sizeof(char*));
|
||||
if (arr == NULL) return C_FALSE;
|
||||
for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i];
|
||||
|
||||
printf(" [LOG] Launching fixed-width Least-Significant-Digit Radix Sort...\n");
|
||||
c_err_t err = c_LSDRadixSort_Sort(&sort, arr, n);
|
||||
|
||||
EXPECT_EQ(err, C_ERR_OK, "LSD sort engine returned unexpected runtime error code");
|
||||
|
||||
// Mathematically sorted verification checkpoints list mapping:
|
||||
// Expected sorted sequence: BAR, CAB, CAT, COW, DIG, DOG
|
||||
EXPECT_EQ(strcmp(arr[0], "BAR"), 0, "Sorted position 0 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[1], "CAB"), 0, "Sorted position 1 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[2], "CAT"), 0, "Sorted position 2 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[3], "COW"), 0, "Sorted position 3 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[4], "DIG"), 0, "Sorted position 4 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[5], "DOG"), 0, "Sorted position 5 incorrect");
|
||||
|
||||
// Checkpoint 2: Validation check of stability bounds
|
||||
// Because "DIG" and "DOG" share character 'D', the sorted sequence must strictly respect
|
||||
// character 'I' vs 'O' sequence order boundaries.
|
||||
EXPECT_EQ(strcmp(arr[4], "DIG") == 0 && strcmp(arr[5], "DOG") == 0, C_TRUE, "Sorting stability check failed");
|
||||
|
||||
printf(" [STAT] LSD Radix Sort verified successfully. Chronological Output: ");
|
||||
for (c_size_t i = 0; i < n; i++) {
|
||||
printf("%s ", arr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
C_FREE(arr);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Verification: Least-Significant-Digit Radix Sort ===\n");
|
||||
if (test_lsd_radix_sort_execution()) {
|
||||
printf(" [PASS] Fixed-Width Stable Radix Sort Passes and Array Pointer Re-maps Verified.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Radix Sorting Pipe Matrix Evaluation Logic Anomaly Intercepted.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <c_MSDRadixSort.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Inline helper to safely extract a character at string offset d.
|
||||
* Automatically maps a string's null terminator to a sentinel value of -1.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
int c_MSDRadixSort_CharAt(const char* str, c_size_t d) {
|
||||
if (str == NULL) return -1;
|
||||
// Walk down to offset d without triggering a buffer overflow lookup violation
|
||||
c_size_t i = 0;
|
||||
while (i < d && str[i] != '\0') {
|
||||
i++;
|
||||
}
|
||||
if (str[i] == '\0' || i < d) return -1;
|
||||
return (unsigned char)str[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Core Private Recursive Sub-partition Sorting Subroutine.
|
||||
* Shares a single pre-allocated auxiliary buffer across stack frames to prevent heap allocation overhead.
|
||||
*
|
||||
* @param lo Lower boundary index of the target partition array slice (inclusive).
|
||||
* @param hi Upper boundary index of the target partition array slice (inclusive).
|
||||
* @param d The current character string evaluation offset cursor.
|
||||
*/
|
||||
static void c_MSDRadixSort_SortRecursive(char** arr, long long lo, long long hi, c_size_t d,
|
||||
c_size_t R, char** aux, c_size_t* count_buf) {
|
||||
if (hi <= lo) return;
|
||||
|
||||
// Cutoff to Insertion Sort for tiny sub-arrays can be added here for production fine-tuning.
|
||||
|
||||
// Calculate sub-slice width and initialize count registers
|
||||
c_size_t n = (c_size_t)(hi - lo + 1);
|
||||
// Elements are shifted forward by +2 slots to gracefully absorb the -1 string end sentinel
|
||||
memset(count_buf, 0, (R + 2) * sizeof(c_size_t));
|
||||
|
||||
// Pass A: Compute frequency buckets
|
||||
for (long long i = lo; i <= hi; i++) {
|
||||
int c = c_MSDRadixSort_CharAt(arr[i], d);
|
||||
count_buf[c + 2]++;
|
||||
}
|
||||
|
||||
// Pass B: Transform frequencies into structural start indexes (Prefix Sums)
|
||||
for (c_size_t r = 0; r < R + 1; r++) {
|
||||
count_buf[r + 1] += count_buf[r];
|
||||
}
|
||||
|
||||
// Pass C: Distribute strings stably to the temporary auxiliary workspace slice
|
||||
for (long long i = lo; i <= hi; i++) {
|
||||
int c = c_MSDRadixSort_CharAt(arr[i], d);
|
||||
aux[count_buf[c + 1]++] = arr[i];
|
||||
}
|
||||
|
||||
// Pass D: Copy back copies natively to the primary tracking layout pointers
|
||||
for (long long i = lo; i <= hi; i++) {
|
||||
arr[i] = aux[i - lo];
|
||||
}
|
||||
|
||||
// Recursively sort sub-arrays for each character bucket
|
||||
// Note: count_buf[0] handles strings that hit a terminal '\0' sentinel, so we skip it to prevent loops
|
||||
for (c_size_t r = 0; r < R; r++) {
|
||||
long long next_lo = lo + (long long)count_buf[r];
|
||||
long long next_hi = lo + (long long)count_buf[r + 1] - 1;
|
||||
|
||||
if (next_hi > next_lo) {
|
||||
c_MSDRadixSort_SortRecursive(arr, next_lo, next_hi, d + 1, R, aux, count_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array of variable-length strings using the MSD Radix Sort pipeline.
|
||||
* Guarantees zero runtime heap thrashing via upfront workspace pooling.
|
||||
*
|
||||
* Time Complexity: O(N * String_Length) optimal | Space Complexity: O(N + R) transient workspace memory
|
||||
*/
|
||||
c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n) {
|
||||
if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM;
|
||||
if (n <= 1) return C_ERR_OK;
|
||||
|
||||
// Upfront Transient Workspace Allocation: Eliminates allocation overhead in deep recursions
|
||||
char** aux = (char**)C_ALLOC(n * sizeof(char*));
|
||||
c_size_t* count_buf = (c_size_t*)C_ALLOC((sort->R + 2) * sizeof(c_size_t));
|
||||
|
||||
if (aux == NULL || count_buf == NULL) {
|
||||
C_FREE(aux);
|
||||
C_FREE(count_buf);
|
||||
return C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
// Launch the core string-wise recursive partition tree
|
||||
c_MSDRadixSort_SortRecursive(arr, 0, (long long)n - 1, 0, sort->R, aux, count_buf);
|
||||
|
||||
C_FREE(aux);
|
||||
C_FREE(count_buf);
|
||||
return C_ERR_OK;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef INCLUDED_C_MSDRADIXSORT_H
|
||||
#define INCLUDED_C_MSDRADIXSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
c_size_t R; // Alphabet size / Radix constraints (e.g., 256 for standard byte arrays)
|
||||
} c_MSDRadixSort_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n);
|
||||
|
||||
#endif /*INCLUDED_C_MSDRADIXSORT_H*/
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "c_MSDRadixSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "c_Memory.h"
|
||||
|
||||
// Your updated line tracing diagnostic macro
|
||||
#define EXPECT_EQ(actual, expected, msg) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
printf(" [X] Assert Failed: %s (Expected %d, got %d) %s:%d\n", msg, (int)(expected), (int)(actual), __FILE__, __LINE__); \
|
||||
return C_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
c_bool_t test_msd_radix_sort_execution(void) {
|
||||
c_MSDRadixSort_t sort;
|
||||
sort.R = 256; // Standard extended ASCII alphabet boundaries
|
||||
|
||||
c_size_t n = 7;
|
||||
// Unsorted variable-length string test pool configurations
|
||||
char* test_data[] = {
|
||||
"she",
|
||||
"sells",
|
||||
"seashells",
|
||||
"by",
|
||||
"the",
|
||||
"sea",
|
||||
"shore"
|
||||
};
|
||||
|
||||
// Allocate an array of modifiable pointers to replicate the application environment
|
||||
char** arr = (char**)C_ALLOC(n * sizeof(char*));
|
||||
if (arr == NULL) return C_FALSE;
|
||||
for (c_size_t i = 0; i < n; i++) arr[i] = test_data[i];
|
||||
|
||||
printf(" [LOG] Launching variable-length Most-Significant-Digit Radix Sort...\n");
|
||||
c_err_t err = c_MSDRadixSort_Sort(&sort, arr, n);
|
||||
|
||||
EXPECT_EQ(err, C_ERR_OK, "MSD sort engine returned unexpected runtime error code");
|
||||
|
||||
// Mathematically sorted verification checkpoints list mapping:
|
||||
// Expected alphabetical sequence: by, sea, seashells, sells, she, shore, the
|
||||
EXPECT_EQ(strcmp(arr[0], "by"), 0, "Sorted position 0 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[1], "sea"), 0, "Sorted position 1 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[2], "seashells"), 0, "Sorted position 2 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[3], "sells"), 0, "Sorted position 3 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[4], "she"), 0, "Sorted position 4 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[5], "shore"), 0, "Sorted position 5 incorrect");
|
||||
EXPECT_EQ(strcmp(arr[6], "the"), 0, "Sorted position 6 incorrect");
|
||||
|
||||
// Checkpoint 2: Variable Length Validation check
|
||||
// "sea" must strictly precede its extended prefix branch form "seashells"
|
||||
EXPECT_EQ(strcmp(arr[1], "sea") == 0 && strcmp(arr[2], "seashells") == 0, C_TRUE, "Variable-length short-prefix ordering failed");
|
||||
|
||||
printf(" [STAT] MSD Radix Sort verified successfully. Alphabetic Output: ");
|
||||
for (c_size_t i = 0; i < n; i++) {
|
||||
printf("%s ", arr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
C_FREE(arr);
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Verification: Most-Significant-Digit Radix Sort ===\n");
|
||||
if (test_msd_radix_sort_execution()) {
|
||||
printf(" [PASS] Variable-Length Stable MSD Sorting and Upfront Scratchpad Allocations Verified.\n");
|
||||
} else {
|
||||
printf(" [FAIL] Radix Partition Tree Structural Analysis Anomalies Intercepted.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#include <c_MaxPQ.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
|
||||
/**
|
||||
* Internal macro helper to swap two arbitrary blocks of memory.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) {
|
||||
if (idx1 == idx2) return;
|
||||
char* a = arr + (idx1 * size);
|
||||
char* b = arr + (idx2 * size);
|
||||
memcpy(temp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, temp, size);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
|
||||
pq->capacity = (initial_capacity > 0) ? initial_capacity : 4;
|
||||
pq->element_size = element_size;
|
||||
pq->size = 0;
|
||||
pq->compar = compar;
|
||||
pq->data = C_ALLOC(pq->capacity * element_size);
|
||||
|
||||
if (pq->data == NULL) return C_ERR_NOMEM;
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void c_MaxPQ_Destroy(c_MaxPQ_t* pq) {
|
||||
if (!pq) return;
|
||||
C_FREE(pq->data);
|
||||
pq->size = 0;
|
||||
pq->capacity = 0;
|
||||
}
|
||||
|
||||
c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element) {
|
||||
if (pq == NULL || element == NULL) return C_ERR_PARAM;
|
||||
|
||||
char* arr = (char*)pq->data;
|
||||
|
||||
// Capacity check: Scale memory boundary out if full
|
||||
if (pq->size >= pq->capacity) {
|
||||
c_size_t new_capacity = pq->capacity * 2;
|
||||
// Reallocate manually utilizing framework macros
|
||||
void* new_data = C_ALLOC(new_capacity * pq->element_size);
|
||||
if (new_data == NULL) return C_ERR_NOMEM; // Allocation failure block
|
||||
|
||||
memcpy(new_data, pq->data, pq->size * pq->element_size);
|
||||
C_FREE(pq->data);
|
||||
pq->data = new_data;
|
||||
pq->capacity = new_capacity;
|
||||
arr = (char*)pq->data;
|
||||
}
|
||||
|
||||
// Allocate stack cache buffer for object swapping routines
|
||||
#define PQ_STACK_LIMIT 128
|
||||
char stack_buf[PQ_STACK_LIMIT];
|
||||
void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size);
|
||||
if (temp == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Place new element at the bottom-most leaf slot of the max-heap tree
|
||||
c_size_t current = pq->size;
|
||||
memcpy(arr + (current * pq->element_size), element, pq->element_size);
|
||||
pq->size++;
|
||||
|
||||
// Sift-Up loop processing
|
||||
while (current > 0) {
|
||||
c_size_t parent = (current - 1) / 2;
|
||||
|
||||
// Max-heap rule tracking: If child <= parent, tree balancing properties are correct
|
||||
if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
c_HeapSwap(arr, current, parent, pq->element_size, temp);
|
||||
current = parent;
|
||||
}
|
||||
|
||||
if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp);
|
||||
#undef PQ_STACK_LIMIT
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer) {
|
||||
if (!pq) return C_ERR_PARAM;
|
||||
|
||||
if (pq->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
char* arr = (char*)pq->data;
|
||||
|
||||
// If a tracking output buffer pointer is supplied, export the maximum item
|
||||
if (output_buffer != NULL) {
|
||||
memcpy(output_buffer, arr, pq->element_size);
|
||||
}
|
||||
|
||||
// Shrink element count tracking early
|
||||
pq->size--;
|
||||
|
||||
if (pq->size > 0) {
|
||||
// Swap the last leaf node up to root position
|
||||
memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size);
|
||||
|
||||
#define PQ_STACK_LIMIT 128
|
||||
char stack_buf[PQ_STACK_LIMIT];
|
||||
void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size);
|
||||
if (temp == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Sift-Down balancing loop processing
|
||||
c_size_t current = 0;
|
||||
while (1) {
|
||||
c_size_t left_child = (2 * current) + 1;
|
||||
c_size_t right_child = (2 * current) + 2;
|
||||
c_size_t largest = current;
|
||||
|
||||
// Check if left child is larger than current node
|
||||
if (left_child < pq->size &&
|
||||
pq->compar(arr + (left_child * pq->element_size), arr + (largest * pq->element_size)) > 0) {
|
||||
largest = left_child;
|
||||
}
|
||||
|
||||
// Check if right child is larger than the currently tracked largest node
|
||||
if (right_child < pq->size &&
|
||||
pq->compar(arr + (right_child * pq->element_size), arr + (largest * pq->element_size)) > 0) {
|
||||
largest = right_child;
|
||||
}
|
||||
|
||||
// Balanced condition achieved
|
||||
if (largest == current) {
|
||||
break;
|
||||
}
|
||||
|
||||
c_HeapSwap(arr, current, largest, pq->element_size, temp);
|
||||
current = largest;
|
||||
}
|
||||
|
||||
if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp);
|
||||
#undef PQ_STACK_LIMIT
|
||||
}
|
||||
|
||||
return C_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void* c_MaxPQ_Peek(c_MaxPQ_t* pq) {
|
||||
if (pq == NULL || pq->size == 0) return NULL;
|
||||
return pq->data; // Root node is consistently maximum element
|
||||
}
|
||||
|
||||
c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq) {
|
||||
if (pq == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Simply reset the size to zero. The underlying buffer remains allocated.
|
||||
pq->size = 0;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually resize the memory allocation capacity of the Priority Queue.
|
||||
* @param pq Pointer to the Max Priority Queue instance.
|
||||
* @param new_capacity The desired number of element slots to allocate.
|
||||
* @return C_ERR_OK if successful, C_ERR_INVALID for bad arguments,
|
||||
* or C_ERR_NOMEM if memory allocation fails.
|
||||
*/
|
||||
c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity) {
|
||||
if (pq == NULL) return C_ERR_PARAM;
|
||||
|
||||
// Prevent shrinking below the current number of active elements inside the heap
|
||||
if (new_capacity < pq->size) return C_ERR_PARAM;
|
||||
|
||||
// If the capacity is already identical, skip processing to avoid memory overhead
|
||||
if (new_capacity == pq->capacity) return C_ERR_OK;
|
||||
|
||||
// Handle downsizing down to 0 safely if the queue is empty
|
||||
if (new_capacity == 0) {
|
||||
if (pq->data != NULL) {
|
||||
C_FREE(pq->data);
|
||||
pq->data = NULL;
|
||||
}
|
||||
pq->capacity = 0;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// Allocate a new memory block according to your framework specification
|
||||
void* new_data = C_ALLOC(new_capacity * pq->element_size);
|
||||
if (new_data == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// If there are existing active elements, move them to the newly allocated block
|
||||
if (pq->size > 0 && pq->data != NULL) {
|
||||
memcpy(new_data, pq->data, pq->size * pq->element_size);
|
||||
}
|
||||
|
||||
// Free the old array and bind the new tracking parameters
|
||||
if (pq->data != NULL) {
|
||||
C_FREE(pq->data);
|
||||
}
|
||||
pq->data = new_data;
|
||||
pq->capacity = new_capacity;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef INCLUDED_C_MAXPQ_H
|
||||
#define INCLUDED_C_MAXPQ_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
void* data; // Flat char pointer block tracking memory slots
|
||||
c_size_t element_size; // Size of each complex structure element in bytes
|
||||
c_size_t capacity; // Maximum allocated element capacity
|
||||
c_size_t size; // Current active element count inside the heap
|
||||
int (*compar)(const void*, const void*); // Custom comparison rule pointer
|
||||
} c_MaxPQ_t;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_MaxPQ_Init(c_MaxPQ_t* pq, c_size_t initial_capacity, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
void c_MaxPQ_Destroy(c_MaxPQ_t* pq);
|
||||
|
||||
c_err_t c_MaxPQ_Push(c_MaxPQ_t* pq, const void* element);
|
||||
c_err_t c_MaxPQ_Pop(c_MaxPQ_t* pq, void* output_buffer);
|
||||
void* c_MaxPQ_Peek(c_MaxPQ_t* pq);
|
||||
c_err_t c_MaxPQ_Clear(c_MaxPQ_t* pq);
|
||||
c_err_t c_MaxPQ_Resize(c_MaxPQ_t* pq, c_size_t new_capacity);
|
||||
|
||||
#endif /*INCLUDED_C_MAXPQ_H*/
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "c_MaxPQ.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#define EXPECT_EQ(val1, val2, msg) \
|
||||
do { \
|
||||
if ((val1) != (val2)) { printf(" [X] Failed: %s (Expected %d, got %d)\n", msg, (int)(val2), (int)(val1)); return false; } \
|
||||
} while(0)
|
||||
|
||||
// Test complex entity
|
||||
typedef struct {
|
||||
int thread_id;
|
||||
int priority_level; // Primary sorting metric for MaxPQ (higher level extract first)
|
||||
char tag[16];
|
||||
} ThreadTask;
|
||||
|
||||
// Max-Heap comparator: Returns positive if a > b
|
||||
int compareTasksByPriority(const void* a, const void* b) {
|
||||
const ThreadTask* t1 = (const ThreadTask*)a;
|
||||
const ThreadTask* t2 = (const ThreadTask*)b;
|
||||
return (t1->priority_level - t2->priority_level);
|
||||
}
|
||||
|
||||
// Test Case: Validates API Errors and Basic Operations
|
||||
bool test_maxpq_lifecycle_and_errors(void) {
|
||||
c_MaxPQ_t pq;
|
||||
|
||||
// 1. Test invalid parameters during initialization
|
||||
EXPECT_EQ(c_MaxPQ_Init(NULL, 10, sizeof(ThreadTask), compareTasksByPriority), C_ERR_PARAM, "NULL instance handle validation missing");
|
||||
EXPECT_EQ(c_MaxPQ_Init(&pq, 10, 0, compareTasksByPriority), C_ERR_PARAM, "Zero element size validation missing");
|
||||
EXPECT_EQ(c_MaxPQ_Init(&pq, 10, sizeof(ThreadTask), NULL), C_ERR_PARAM, "NULL comparator validation missing");
|
||||
|
||||
// 2. Correct initialization
|
||||
EXPECT_EQ(c_MaxPQ_Init(&pq, 2, sizeof(ThreadTask), compareTasksByPriority), C_ERR_OK, "Valid configuration failed init");
|
||||
|
||||
// 3. Test empty bounds lookups
|
||||
EXPECT_EQ(c_MaxPQ_Peek(&pq) == NULL, true, "Empty queue peek did not yield NULL");
|
||||
ThreadTask output;
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &output), C_ERR_EMPTY, "Empty queue extraction did not throw C_ERR_EMPTY");
|
||||
|
||||
// Clean up
|
||||
c_MaxPQ_Destroy(&pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test Case: Validates Max Extraction and Dynamic Scale Limits
|
||||
bool test_maxpq_functional_flow(void) {
|
||||
c_MaxPQ_t pq;
|
||||
// Initialize with a tiny capacity of 2 to guarantee scaling logic triggers
|
||||
c_MaxPQ_Init(&pq, 2, sizeof(ThreadTask), compareTasksByPriority);
|
||||
|
||||
ThreadTask tasks[] = {
|
||||
{ 401, 12, "Low Prio" },
|
||||
{ 402, 99, "Critical" },
|
||||
{ 403, 50, "High Prio" },
|
||||
{ 404, 75, "Urgent" }
|
||||
};
|
||||
|
||||
// Push structures
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[0]), C_ERR_OK, "Failed pushing task 0");
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[1]), C_ERR_OK, "Failed pushing task 1");
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[2]), C_ERR_OK, "Failed pushing task 2 (Scale Trigger)");
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &tasks[3]), C_ERR_OK, "Failed pushing task 3");
|
||||
|
||||
// Verify Peak Element maps to highest priority (99)
|
||||
ThreadTask* peeked = (ThreadTask*)c_MaxPQ_Peek(&pq);
|
||||
EXPECT_EQ(peeked != NULL && peeked->priority_level == 99, true, "Peek failed to return max item pointer");
|
||||
|
||||
ThreadTask extracted;
|
||||
|
||||
// Pop 1: Priority 99 ("Critical")
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 1 crashed");
|
||||
EXPECT_EQ(extracted.thread_id, 402, "Extracted sequence misaligned at item 1");
|
||||
|
||||
// Pop 2: Priority 75 ("Urgent")
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 2 crashed");
|
||||
EXPECT_EQ(extracted.thread_id, 404, "Extracted sequence misaligned at item 2");
|
||||
|
||||
// Pop 3: Priority 50 ("High Prio")
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 3 crashed");
|
||||
EXPECT_EQ(extracted.thread_id, 403, "Extracted sequence misaligned at item 3");
|
||||
|
||||
// Pop 4: Priority 12 ("Low Prio")
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &extracted), C_ERR_OK, "Extraction loop 4 crashed");
|
||||
EXPECT_EQ(extracted.thread_id, 401, "Extracted sequence misaligned at item 4");
|
||||
|
||||
// Confirm container has completely emptied out
|
||||
EXPECT_EQ(pq.size, 0, "Active queue tracking counter failed to reach zero index boundary");
|
||||
|
||||
c_MaxPQ_Destroy(&pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_maxpq_clear_and_reuse(void) {
|
||||
c_MaxPQ_t pq;
|
||||
// Initialize with a capacity of 4
|
||||
c_MaxPQ_Init(&pq, 4, sizeof(ThreadTask), compareTasksByPriority);
|
||||
|
||||
ThreadTask t1 = { 101, 10 };
|
||||
ThreadTask t2 = { 102, 50 };
|
||||
ThreadTask t3 = { 103, 30 };
|
||||
|
||||
// 1. Populate the priority queue
|
||||
c_MaxPQ_Push(&pq, &t1);
|
||||
c_MaxPQ_Push(&pq, &t2);
|
||||
c_MaxPQ_Push(&pq, &t3);
|
||||
EXPECT_EQ(pq.size, 3, "Queue size should be 3 before clearing");
|
||||
c_size_t saved_capacity = pq.capacity;
|
||||
|
||||
// 2. Execute Clear Protocol
|
||||
EXPECT_EQ(c_MaxPQ_Clear(NULL), C_ERR_PARAM, "Clearing NULL should yield C_ERR_PARAM");
|
||||
EXPECT_EQ(c_MaxPQ_Clear(&pq), C_ERR_OK, "Clearing active queue failed");
|
||||
|
||||
// 3. Assert structural properties after clearing
|
||||
EXPECT_EQ(pq.size, 0, "Queue size must be reset to 0 after clear");
|
||||
EXPECT_EQ(pq.capacity, saved_capacity, "Capacity should remain unchanged after clear");
|
||||
EXPECT_EQ(c_MaxPQ_Peek(&pq) == NULL, true, "Cleared queue peek should return NULL");
|
||||
|
||||
ThreadTask dummy;
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &dummy), C_ERR_EMPTY, "Cleared queue pop should return C_ERR_EMPTY");
|
||||
|
||||
// 4. Reuse and re-populate the same queue (Verifying memory reuse)
|
||||
ThreadTask t4 = { 201, 5 };
|
||||
ThreadTask t5 = { 202, 95 }; // This should become the new max root
|
||||
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &t4), C_ERR_OK, "Pushing to cleared queue failed");
|
||||
EXPECT_EQ(c_MaxPQ_Push(&pq, &t5), C_ERR_OK, "Pushing second item to cleared queue failed");
|
||||
EXPECT_EQ(pq.size, 2, "Size did not increment properly after reuse");
|
||||
|
||||
// Verify extraction works perfectly post-clear
|
||||
ThreadTask result;
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &result), C_ERR_OK, "Pop post-clear failed");
|
||||
EXPECT_EQ(result.thread_id, 202, "Max-Heap extraction broke after clearing and reusing queue");
|
||||
|
||||
c_MaxPQ_Destroy(&pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_maxpq_resize_behavior(void) {
|
||||
c_MaxPQ_t pq;
|
||||
// Initialize with a capacity of 4
|
||||
c_MaxPQ_Init(&pq, 4, sizeof(ThreadTask), compareTasksByPriority);
|
||||
|
||||
ThreadTask t1 = { 101, 10 };
|
||||
ThreadTask t2 = { 102, 50 };
|
||||
|
||||
c_MaxPQ_Push(&pq, &t1);
|
||||
c_MaxPQ_Push(&pq, &t2);
|
||||
EXPECT_EQ(pq.size, 2, "Initial push setup size should be 2");
|
||||
EXPECT_EQ(pq.capacity, 4, "Initial capacity should be 4");
|
||||
|
||||
// 1. Guard Check: Attempting to shrink capacity below the active item count (size=2) must fail
|
||||
EXPECT_EQ(c_MaxPQ_Resize(&pq, 1), C_ERR_PARAM, "Shrinking below current size did not fail safely");
|
||||
EXPECT_EQ(pq.capacity, 4, "Invalid resize operation altered internal capacity incorrectly");
|
||||
|
||||
// 2. Expansion Check: Expand capacity from 4 to 10
|
||||
EXPECT_EQ(c_MaxPQ_Resize(&pq, 10), C_ERR_OK, "Expanding valid memory blocks failed");
|
||||
EXPECT_EQ(pq.capacity, 10, "Capacity tracker failed to update to 10");
|
||||
EXPECT_EQ(pq.size, 2, "Active structural data sizes mutated during reallocation shift");
|
||||
|
||||
// 3. Contraction Check: Tighten memory footprints to fit data perfectly (shrink capacity to size=2)
|
||||
EXPECT_EQ(c_MaxPQ_Resize(&pq, 2), C_ERR_OK, "Clamping pool capacity limits down to active size failed");
|
||||
EXPECT_EQ(pq.capacity, 2, "Capacity tracker failed to collapse down to 2");
|
||||
|
||||
// 4. Operational Integrity Check: Verify heap extraction still works flawlessly post-resize
|
||||
ThreadTask result;
|
||||
EXPECT_EQ(c_MaxPQ_Pop(&pq, &result), C_ERR_OK, "Pop post-resize failed");
|
||||
EXPECT_EQ(result.thread_id, 102, "Max element tracking corrupted during pointer re-mapping operations");
|
||||
|
||||
c_MaxPQ_Destroy(&pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Custom Framework Testing for c_MaxPQ ===\n");
|
||||
|
||||
if (test_maxpq_lifecycle_and_errors()) printf(" [PASS] Test 1: Lifecycle Management & Error Guard Protocols Verified\n");
|
||||
if (test_maxpq_functional_flow()) printf(" [PASS] Test 2: Priority Tree Sifting Loops & Data Escalation Verified\n");
|
||||
if (test_maxpq_clear_and_reuse()) {
|
||||
printf(" [PASS] Test: Clear, Capacity Preservation, and Queue Reuse Verified Successfully\n");
|
||||
}
|
||||
if (test_maxpq_resize_behavior()) {
|
||||
printf(" [PASS] Test: Manual Resize, Boundary Guard Protection, and Data Persistence Verified\n");
|
||||
}
|
||||
printf("=== All Priority Queue Framework Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <c_MergeSort.h>
|
||||
|
||||
/**
|
||||
* Internal merging routine for top-down merge sort.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_MergeInternal(char* arr, c_size_t left, c_size_t mid, c_size_t right,
|
||||
c_size_t size, char* aux,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
c_size_t i = left;
|
||||
c_size_t j = mid + 1;
|
||||
c_size_t k = left;
|
||||
|
||||
// Copy the target segment into the auxiliary working buffer
|
||||
memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size);
|
||||
|
||||
// Merge back into the original array tracking sorted boundaries
|
||||
while (i <= mid && j <= right) {
|
||||
if (compar(aux + (i * size), aux + (j * size)) <= 0) {
|
||||
memcpy(arr + (k * size), aux + (i * size), size);
|
||||
i++;
|
||||
} else {
|
||||
memcpy(arr + (k * size), aux + (j * size), size);
|
||||
j++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
// Copy any remaining elements of the left sub-array if any
|
||||
while (i <= mid) {
|
||||
memcpy(arr + (k * size), aux + (i * size), size);
|
||||
i++;
|
||||
k++;
|
||||
}
|
||||
// Note: Remaining items on the right side are already natively sitting in the correct slots.
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive structural block splitting segments into halves.
|
||||
*/
|
||||
void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (left >= right) return;
|
||||
|
||||
c_size_t mid = left + (right - left) / 2;
|
||||
|
||||
c_MergeSortSub(arr, left, mid, size, aux, compar);
|
||||
c_MergeSortSub(arr, mid + 1, right, size, aux, compar);
|
||||
|
||||
// Optimization: If the array segment is already naturally sorted, skip the merge routine
|
||||
if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) {
|
||||
c_MergeInternal(arr, left, mid, right, size, aux, compar);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef INCLUDED_C_MERGESORT_H
|
||||
#define INCLUDED_C_MERGESORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_MergeSortSub(char* arr, c_size_t left, c_size_t right, c_size_t size, char* aux,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
/**
|
||||
* Top-Level Custom Framework Entry Point for Top-Down Merge Sort.
|
||||
* Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_MergeSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
c_size_t total_bytes = num * size;
|
||||
|
||||
// Optimization: Fallback to stack buffer allocation if overall payload fits locally
|
||||
#define MERGE_STACK_LIMIT 512
|
||||
char stack_buf[MERGE_STACK_LIMIT];
|
||||
char* aux = NULL;
|
||||
|
||||
if (total_bytes <= MERGE_STACK_LIMIT) {
|
||||
aux = stack_buf;
|
||||
} else {
|
||||
aux = (char*)C_ALLOC(total_bytes);
|
||||
if (aux == NULL) return; // Allocation safeguard
|
||||
}
|
||||
|
||||
// Execute recursive divide-and-conquer strategy
|
||||
c_MergeSortSub(arr, 0, num - 1, size, aux, compar);
|
||||
|
||||
if (total_bytes > MERGE_STACK_LIMIT) {
|
||||
C_FREE(aux);
|
||||
}
|
||||
#undef MERGE_STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_MERGESORT_H*/
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "c_MergeSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_TRUE(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \
|
||||
} while(0)
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Testing Datastructures & Comparison Helper Utilities
|
||||
// ----------------------------------------------------
|
||||
typedef struct {
|
||||
int primary_key; // Used for sorting
|
||||
int original_pos; // Used to test stable tracking properties
|
||||
char payload[64];
|
||||
} StableNode;
|
||||
|
||||
int compareStableNodes(const void* a, const void* b) {
|
||||
const StableNode* n1 = (const StableNode*)a;
|
||||
const StableNode* n2 = (const StableNode*)b;
|
||||
return (n1->primary_key > n2->primary_key) - (n1->primary_key < n2->primary_key);
|
||||
}
|
||||
|
||||
int compareIntegers(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Unit Test Sub-routines
|
||||
// ----------------------------------------------------
|
||||
|
||||
// Test 1: Guard limits on Null pointers and zero size footprints
|
||||
bool test_merge_edge_cases(void) {
|
||||
int* empty_ptr = NULL;
|
||||
c_MergeSort(empty_ptr, 0, sizeof(int), compareIntegers); // Safe escape pipeline
|
||||
|
||||
int singular[] = { 99 };
|
||||
c_MergeSort(singular, 1, sizeof(int), compareIntegers);
|
||||
EXPECT_TRUE(singular[0] == 99, "Singular item modified unexpectedly");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test 2: Core stability metrics (elements with matching keys preserve initial order)
|
||||
bool test_merge_sorting_stability(void) {
|
||||
StableNode items[] = {
|
||||
{ 10, 1, "First Ten" },
|
||||
{ 5, 2, "Five" },
|
||||
{ 10, 3, "Second Ten" },
|
||||
{ 1, 4, "One" },
|
||||
{ 10, 5, "Third Ten" }
|
||||
};
|
||||
c_size_t count = sizeof(items) / sizeof(items[0]);
|
||||
|
||||
c_MergeSort(items, count, sizeof(StableNode), compareStableNodes);
|
||||
|
||||
// Verify ordering sequence
|
||||
EXPECT_TRUE(items[0].primary_key == 1, "Lowest structural key error");
|
||||
EXPECT_TRUE(items[1].primary_key == 5, "Mid tier sorting position error");
|
||||
|
||||
// Stability checks: Primary keys at index 2, 3, and 4 are all '10'.
|
||||
// Their original_pos fields MUST step cleanly from 1 -> 3 -> 5.
|
||||
EXPECT_TRUE(items[2].original_pos == 1, "Stability Broken on duplicate subset element 1");
|
||||
EXPECT_TRUE(items[3].original_pos == 3, "Stability Broken on duplicate subset element 2");
|
||||
EXPECT_TRUE(items[4].original_pos == 5, "Stability Broken on duplicate subset element 3");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test 3: Large scaling array to bypass the MERGE_STACK_LIMIT (512 Bytes)
|
||||
bool test_merge_heap_allocation(void) {
|
||||
// 20 items * 72 bytes per StableNode = 1440 Bytes (Exceeds stack buffer threshold)
|
||||
StableNode massive_dataset[20];
|
||||
c_size_t total = 20;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
massive_dataset[i].primary_key = 20 - i; // Completely inverted structural setup
|
||||
massive_dataset[i].original_pos = i;
|
||||
}
|
||||
|
||||
c_MergeSort(massive_dataset, total, sizeof(StableNode), compareStableNodes);
|
||||
|
||||
// Scan through dataset confirming monotone strictly ascending continuity
|
||||
for (c_size_t i = 0; i < total - 1; i++) {
|
||||
EXPECT_TRUE(massive_dataset[i].primary_key <= massive_dataset[i+1].primary_key,
|
||||
"Continuous stream scaling breakdown under memory swap logic redirection");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Testing Execution Entry Driver
|
||||
// ----------------------------------------------------
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: Top-Down Merge Sort ===\n");
|
||||
|
||||
if (test_merge_edge_cases()) printf(" [PASS] Test 1: Empty Array & Edge Boundary Safety\n");
|
||||
if (test_merge_sorting_stability()) printf(" [PASS] Test 2: Sorting Structural Stability Preservation\n");
|
||||
if (test_merge_heap_allocation()) printf(" [PASS] Test 3: Heap Buffer Alloc Escalation (>512B Testing)\n");
|
||||
|
||||
printf("=== System Verification Sequence Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_MergeSortBU.h>
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef INCLUDED_C_MERGESORTBU_H
|
||||
#define INCLUDED_C_MERGESORTBU_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
/**
|
||||
* Internal merging routine for iterative blocks.
|
||||
* Re-used from the top-down logic to guarantee stability.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_MergeInternalBU(char* arr, c_size_t left, c_size_t mid, c_size_t right,
|
||||
c_size_t size, char* aux,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
c_size_t i = left;
|
||||
c_size_t j = mid + 1;
|
||||
c_size_t k = left;
|
||||
|
||||
// Snapshot target segment into tracking auxiliary zone
|
||||
memcpy(aux + (left * size), arr + (left * size), (right - left + 1) * size);
|
||||
|
||||
// Merge operational slots back sequentially
|
||||
while (i <= mid && j <= right) {
|
||||
if (compar(aux + (i * size), aux + (j * size)) <= 0) {
|
||||
memcpy(arr + (k * size), aux + (i * size), size);
|
||||
i++;
|
||||
} else {
|
||||
memcpy(arr + (k * size), aux + (j * size), size);
|
||||
j++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
// Flush remaining items sitting on the left slice
|
||||
while (i <= mid) {
|
||||
memcpy(arr + (k * size), aux + (i * size), size);
|
||||
i++;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-Level Iterative Framework Entry Point for Bottom-Up Merge Sort.
|
||||
* Time Complexity: O(n log n) | Space Complexity: O(n) | Stable: Yes | Call Stack: O(1)
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_MergeSortBottomUp(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
c_size_t total_bytes = num * size;
|
||||
|
||||
// Optimization: Fallback to stack buffer allocation if memory payload fits locally
|
||||
#define MERGE_BU_STACK_LIMIT 512
|
||||
char stack_buf[MERGE_BU_STACK_LIMIT];
|
||||
char* aux = NULL;
|
||||
|
||||
if (total_bytes <= MERGE_BU_STACK_LIMIT) {
|
||||
aux = stack_buf;
|
||||
} else {
|
||||
aux = (char*)C_ALLOC(total_bytes);
|
||||
if (aux == NULL) return;
|
||||
}
|
||||
|
||||
// Step-wise sub-array size doubling loop: 1, 2, 4, 8, 16...
|
||||
for (c_size_t width = 1; width < num; width *= 2) {
|
||||
// Iterate through segments by blocks of 2 * width
|
||||
for (c_size_t left = 0; left < num - width; left += 2 * width) {
|
||||
c_size_t mid = left + width - 1;
|
||||
c_size_t right = C_MIN(left + (2 * width) - 1, num - 1);
|
||||
|
||||
// Optimization: Skip merge phase if the adjacent sub-segments are already sequentially sorted
|
||||
if (compar(arr + (mid * size), arr + ((mid + 1) * size)) > 0) {
|
||||
c_MergeInternalBU(arr, left, mid, right, size, aux, compar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (total_bytes > MERGE_BU_STACK_LIMIT) {
|
||||
C_FREE(aux);
|
||||
}
|
||||
#undef MERGE_BU_STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_MERGESORTBU_H*/
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "c_MergeSortBU.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define EXPECT_TRUE(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \
|
||||
} while(0)
|
||||
|
||||
// Struct declaration enforcing alignment and tracking order metadata
|
||||
typedef struct {
|
||||
int key;
|
||||
int initial_order;
|
||||
char metadata[16];
|
||||
} TestNode;
|
||||
|
||||
int compareTestNodes(const void* a, const void* b) {
|
||||
const TestNode* n1 = (const TestNode*)a;
|
||||
const TestNode* n2 = (const TestNode*)b;
|
||||
return (n1->key > n2->key) - (n1->key < n2->key);
|
||||
}
|
||||
|
||||
// Test 1: Sorting arrays with sizes that are not powers of two (e.g., N = 7)
|
||||
bool test_bu_non_power_of_two(void) {
|
||||
int datasets[] = { 85, 24, 63, 45, 17, 31, 96 };
|
||||
c_size_t total = sizeof(datasets) / sizeof(datasets[0]);
|
||||
|
||||
// Simple integer comparison handler
|
||||
int cmp_int(const void* a, const void* b) { return (*(int*)a - *(int*)b); }
|
||||
|
||||
c_MergeSortBottomUp(datasets, total, sizeof(int), cmp_int);
|
||||
|
||||
for (c_size_t i = 0; i < total - 1; i++) {
|
||||
EXPECT_TRUE(datasets[i] <= datasets[i+1], "Irregular block boundary sorted sequence tracking crash");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test 2: Multi-field structural stability checkpoint (N = 6)
|
||||
bool test_bu_stability(void) {
|
||||
TestNode records[] = {
|
||||
{ 40, 0, "Alpha" },
|
||||
{ 20, 1, "Beta" },
|
||||
{ 40, 2, "Gamma" },
|
||||
{ 10, 3, "Delta" },
|
||||
{ 40, 4, "Zeta" },
|
||||
{ 20, 5, "Eta" }
|
||||
};
|
||||
c_size_t count = sizeof(records) / sizeof(records[0]);
|
||||
|
||||
c_MergeSortBottomUp(records, count, sizeof(TestNode), compareTestNodes);
|
||||
|
||||
// Assert key orders are strictly sorted
|
||||
EXPECT_TRUE(records[0].key == 10, "Base index misplacement");
|
||||
EXPECT_TRUE(records[1].key == 20 && records[2].key == 20, "Middle index sort tracking failed");
|
||||
EXPECT_TRUE(records[3].key == 40 && records[4].key == 40 && records[5].key == 40, "Tail elements misaligned");
|
||||
|
||||
// Assert stability: matching records must keep their initial relative positions
|
||||
EXPECT_TRUE(records[1].initial_order == 1 && records[2].initial_order == 5, "Stability broken on key value 20");
|
||||
EXPECT_TRUE(records[3].initial_order == 0 && records[4].initial_order == 2 && records[5].initial_order == 4,
|
||||
"Stability broken on key value 40");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test Driving Runner Subroutine
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: Bottom-Up Merge Sort ===\n");
|
||||
|
||||
if (test_bu_non_power_of_two()) printf(" [PASS] Test 1: Non-Power-of-Two Odd Dataset Counts Handles Securely\n");
|
||||
if (test_bu_stability()) printf(" [PASS] Test 2: Iterative Stable Record Processing Validated\n");
|
||||
|
||||
printf("=== System Verification Sequence Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#include <c_MinPQ.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
/**
|
||||
* Internal helper to swap two memory slots inside the heap array.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_MinPQ_HeapSwap(char* arr, c_size_t idx1, c_size_t idx2, c_size_t size, void* temp) {
|
||||
if (idx1 == idx2) return;
|
||||
char* a = arr + (idx1 * size);
|
||||
char* b = arr + (idx2 * size);
|
||||
memcpy(temp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, temp, size);
|
||||
}
|
||||
|
||||
c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (pq == NULL || element_size == 0 || compar == NULL) return C_ERR_PARAM;
|
||||
|
||||
pq->capacity = (initial_capacity > 0) ? initial_capacity : 4;
|
||||
pq->element_size = element_size;
|
||||
pq->size = 0;
|
||||
pq->compar = compar;
|
||||
pq->data = C_ALLOC(pq->capacity * element_size);
|
||||
|
||||
return (pq->data != NULL) ? C_ERR_OK : C_ERR_NOMEM;
|
||||
}
|
||||
|
||||
void c_MinPQ_Destroy(c_MinPQ_t* pq) {
|
||||
if (!pq) return;
|
||||
C_FREE(pq->data);
|
||||
pq->size = 0;
|
||||
pq->capacity = 0;
|
||||
}
|
||||
|
||||
c_err_t c_MinPQ_Clear(c_MinPQ_t* pq) {
|
||||
if (pq == NULL) return C_ERR_PARAM;
|
||||
pq->size = 0;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity) {
|
||||
if (pq == NULL || new_capacity < pq->size) return C_ERR_PARAM;
|
||||
if (new_capacity == pq->capacity) return C_ERR_OK;
|
||||
|
||||
if (new_capacity == 0) {
|
||||
if (pq->data != NULL) C_FREE(pq->data);
|
||||
pq->data = NULL;
|
||||
pq->capacity = 0;
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* new_data = C_ALLOC(new_capacity * pq->element_size);
|
||||
if (new_data == NULL) return C_ERR_NOMEM;
|
||||
|
||||
if (pq->size > 0 && pq->data != NULL) {
|
||||
memcpy(new_data, pq->data, pq->size * pq->element_size);
|
||||
}
|
||||
|
||||
if (pq->data != NULL) C_FREE(pq->data);
|
||||
pq->data = new_data;
|
||||
pq->capacity = new_capacity;
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element) {
|
||||
if (pq == NULL || element == NULL) return C_ERR_PARAM;
|
||||
|
||||
char* arr = (char*)pq->data;
|
||||
|
||||
// Handle dynamic capacity auto-doubling
|
||||
if (pq->size >= pq->capacity) {
|
||||
c_err_t err = c_MinPQ_Resize(pq, pq->capacity * 2);
|
||||
if (err != C_ERR_OK) return err;
|
||||
arr = (char*)pq->data;
|
||||
}
|
||||
|
||||
#define PQ_STACK_LIMIT 128
|
||||
char stack_buf[PQ_STACK_LIMIT];
|
||||
void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size);
|
||||
if (temp == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Place element at the next available leaf position
|
||||
c_size_t current = pq->size;
|
||||
memcpy(arr + (current * pq->element_size), element, pq->element_size);
|
||||
pq->size++;
|
||||
|
||||
// Sift-Up for Min-Heap: Move up while element < parent
|
||||
while (current > 0) {
|
||||
c_size_t parent = (current - 1) / 2;
|
||||
if (pq->compar(arr + (current * pq->element_size), arr + (parent * pq->element_size)) >= 0) {
|
||||
break;
|
||||
}
|
||||
c_MinPQ_HeapSwap(arr, current, parent, pq->element_size, temp);
|
||||
current = parent;
|
||||
}
|
||||
|
||||
if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp);
|
||||
#undef PQ_STACK_LIMIT
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer) {
|
||||
if (pq == NULL) return C_ERR_PARAM;
|
||||
if (pq->size == 0) return C_ERR_EMPTY;
|
||||
|
||||
char* arr = (char*)pq->data;
|
||||
|
||||
// Export root minimum element if buffer is provided
|
||||
if (output_buffer != NULL) {
|
||||
memcpy(output_buffer, arr, pq->element_size);
|
||||
}
|
||||
|
||||
pq->size--;
|
||||
|
||||
if (pq->size > 0) {
|
||||
// Move last leaf to root position
|
||||
memcpy(arr, arr + (pq->size * pq->element_size), pq->element_size);
|
||||
|
||||
#define PQ_STACK_LIMIT 128
|
||||
char stack_buf[PQ_STACK_LIMIT];
|
||||
void* temp = (pq->element_size <= PQ_STACK_LIMIT) ? stack_buf : C_ALLOC(pq->element_size);
|
||||
if (temp == NULL) return C_ERR_NOMEM;
|
||||
|
||||
// Sift-Down for Min-Heap: Swap with the smaller of the two children
|
||||
c_size_t current = 0;
|
||||
while (1) {
|
||||
c_size_t left_child = (2 * current) + 1;
|
||||
c_size_t right_child = (2 * current) + 2;
|
||||
c_size_t smallest = current;
|
||||
|
||||
if (left_child < pq->size &&
|
||||
pq->compar(arr + (left_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) {
|
||||
smallest = left_child;
|
||||
}
|
||||
if (right_child < pq->size &&
|
||||
pq->compar(arr + (right_child * pq->element_size), arr + (smallest * pq->element_size)) < 0) {
|
||||
smallest = right_child;
|
||||
}
|
||||
if (smallest == current) {
|
||||
break;
|
||||
}
|
||||
|
||||
c_MinPQ_HeapSwap(arr, current, smallest, pq->element_size, temp);
|
||||
current = smallest;
|
||||
}
|
||||
|
||||
if (pq->element_size > PQ_STACK_LIMIT) C_FREE(temp);
|
||||
#undef PQ_STACK_LIMIT
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void* c_MinPQ_Peek(c_MinPQ_t* pq) {
|
||||
if (pq == NULL || pq->size == 0) return NULL;
|
||||
return pq->data;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_C_MINPQ_H
|
||||
#define INCLUDED_C_MINPQ_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
c_size_t element_size;
|
||||
c_size_t capacity;
|
||||
c_size_t size;
|
||||
int (*compar)(const void*, const void*);
|
||||
} c_MinPQ_t;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_MinPQ_Init(c_MinPQ_t* pq, c_size_t initial_capacity, c_size_t element_size,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
void c_MinPQ_Destroy(c_MinPQ_t* pq);
|
||||
|
||||
c_err_t c_MinPQ_Clear(c_MinPQ_t* pq);
|
||||
c_err_t c_MinPQ_Resize(c_MinPQ_t* pq, c_size_t new_capacity);
|
||||
|
||||
c_err_t c_MinPQ_Push(c_MinPQ_t* pq, const void* element);
|
||||
c_err_t c_MinPQ_Pop(c_MinPQ_t* pq, void* output_buffer);
|
||||
void* c_MinPQ_Peek(c_MinPQ_t* pq);
|
||||
|
||||
#endif /*INCLUDED_C_MINPQ_H*/
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "c_MinPQ.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define EXPECT_EQ(val1, val2, msg) \
|
||||
do { \
|
||||
if ((val1) != (val2)) { printf(" [X] Failed: %s (Expected %d, got %d)\n", msg, (int)(val2), (int)(val1)); return false; } \
|
||||
} while(0)
|
||||
|
||||
// Complex element structural payload
|
||||
typedef struct {
|
||||
int packet_id;
|
||||
int latency_ms; // Primary sorting metric (smaller value = higher priority = popped first)
|
||||
} NetworkPacket;
|
||||
|
||||
// Min-Heap comparator: Returns negative if a < b
|
||||
int comparePacketsByLatency(const void* a, const void* b) {
|
||||
return (((NetworkPacket*)a)->latency_ms - ((NetworkPacket*)b)->latency_ms);
|
||||
}
|
||||
|
||||
// Validation Pipeline Function
|
||||
bool test_minpq_functional_flow(void) {
|
||||
c_MinPQ_t pq;
|
||||
// Initialize with a tiny capacity of 2 to verify dynamic allocation resize steps
|
||||
EXPECT_EQ(c_MinPQ_Init(&pq, 2, sizeof(NetworkPacket), comparePacketsByLatency), C_ERR_OK, "Initialization failed");
|
||||
|
||||
NetworkPacket packets[] = {
|
||||
{ 7001, 120 }, // High Latency
|
||||
{ 7002, 15 }, // Ultra-low Latency (Should be extracted first!)
|
||||
{ 7003, 45 }, // Mid Latency
|
||||
{ 7004, 80 } // High-mid Latency
|
||||
};
|
||||
|
||||
// 1. Push Operations
|
||||
EXPECT_EQ(c_MinPQ_Push(&pq, &packets[0]), C_ERR_OK, "Push 0 failed");
|
||||
EXPECT_EQ(c_MinPQ_Push(&pq, &packets[1]), C_ERR_OK, "Push 1 failed");
|
||||
EXPECT_EQ(c_MinPQ_Push(&pq, &packets[2]), C_ERR_OK, "Push 2 failed (Triggers automatic resize expansion)");
|
||||
EXPECT_EQ(c_MinPQ_Push(&pq, &packets[3]), C_ERR_OK, "Push 3 failed");
|
||||
EXPECT_EQ(pq.size, 4, "Active queue size layout tracker mismatched");
|
||||
|
||||
// 2. Peek Verification (Must target the absolute minimum latency element)
|
||||
NetworkPacket* peeked = (NetworkPacket*)c_MinPQ_Peek(&pq);
|
||||
EXPECT_EQ(peeked != NULL && peeked->latency_ms == 15, true, "Peek failed to find minimum item root pointer");
|
||||
|
||||
// 3. Sequential Extraction (Pop validation)
|
||||
NetworkPacket out;
|
||||
|
||||
// Pop 1: Latency 15 (ID 7002)
|
||||
EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 1 crashed");
|
||||
EXPECT_EQ(out.packet_id, 7002, "Min extraction sequence failed at item 1");
|
||||
|
||||
// Pop 2: Latency 45 (ID 7003)
|
||||
EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 2 crashed");
|
||||
EXPECT_EQ(out.packet_id, 7003, "Min extraction sequence failed at item 2");
|
||||
|
||||
// Pop 3: Latency 80 (ID 7004)
|
||||
EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 3 crashed");
|
||||
EXPECT_EQ(out.packet_id, 7004, "Min extraction sequence failed at item 3");
|
||||
|
||||
// Pop 4: Latency 120 (ID 7001)
|
||||
EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_OK, "Pop 4 crashed");
|
||||
EXPECT_EQ(out.packet_id, 7001, "Min extraction sequence failed at item 4");
|
||||
|
||||
// 4. Empty and Bounds Verification
|
||||
EXPECT_EQ(pq.size, 0, "Queue did not empty out properly");
|
||||
EXPECT_EQ(c_MinPQ_Pop(&pq, &out), C_ERR_EMPTY, "Pop on empty queue did not throw C_ERR_EMPTY");
|
||||
|
||||
c_MinPQ_Destroy(&pq);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("=== Starting Custom Framework Testing for c_MinPQ ===\n");
|
||||
|
||||
if (test_minpq_functional_flow()) {
|
||||
printf(" [PASS] Test: Min-Heap Push/Pop Sifting and Resizing Sequences Verified Successfully\n");
|
||||
}
|
||||
|
||||
printf("=== All Min-Priority Queue Framework Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <c_QuickSort.h>
|
||||
|
||||
|
||||
/**
|
||||
* Internal macro helper to swap two arbitrary blocks of memory of given size.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) {
|
||||
if (a == b) return;
|
||||
memcpy(temp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, temp, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses the median of left, center, and right elements as the pivot,
|
||||
* hides it at (right - 1), and returns a pointer to it.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void* c_MedianOfThree(char* arr, long long left, long long right, c_size_t size,
|
||||
void* temp, int (*compar)(const void*, const void*)) {
|
||||
long long center = left + (right - left) / 2;
|
||||
|
||||
// Order left, center, right
|
||||
if (compar(arr + (left * size), arr + (center * size)) > 0) {
|
||||
c_SwapInternal(arr + (left * size), arr + (center * size), size, temp);
|
||||
}
|
||||
if (compar(arr + (left * size), arr + (right * size)) > 0) {
|
||||
c_SwapInternal(arr + (left * size), arr + (right * size), size, temp);
|
||||
}
|
||||
if (compar(arr + (center * size), arr + (right * size)) > 0) {
|
||||
c_SwapInternal(arr + (center * size), arr + (right * size), size, temp);
|
||||
}
|
||||
|
||||
// Place pivot at position (right - 1)
|
||||
c_SwapInternal(arr + (center * size), arr + ((right - 1) * size), size, temp);
|
||||
return arr + ((right - 1) * size);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
// Optimization: Fallback to manual insertion sort for tiny arrays to prune deep recursion
|
||||
if (left + 10 > right) {
|
||||
// Basic Inline Insertion Sort boundary loop
|
||||
for (long long i = left + 1; i <= right; i++) {
|
||||
memcpy(temp, arr + (i * size), size);
|
||||
long long j = i;
|
||||
while (j > left && compar(arr + ((j - 1) * size), temp) > 0) {
|
||||
memcpy(arr + (j * size), arr + ((j - 1) * size), size);
|
||||
j--;
|
||||
}
|
||||
memcpy(arr + (j * size), temp, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve Median Pivot
|
||||
c_MedianOfThree(arr, left, right, size, temp, compar);
|
||||
|
||||
long long i = left;
|
||||
long long j = right - 1;
|
||||
|
||||
// Hoare Partitioning Loop
|
||||
while (1) {
|
||||
while (compar(arr + ((++i) * size), arr + ((right - 1) * size)) < 0);
|
||||
while (compar(arr + ((--j) * size), arr + ((right - 1) * size)) > 0);
|
||||
if (i < j) {
|
||||
c_SwapInternal(arr + (i * size), arr + (j * size), size, temp);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore pivot to its correct final slot
|
||||
c_SwapInternal(arr + (i * size), arr + ((right - 1) * size), size, temp);
|
||||
|
||||
// Recursively execute left and right segments
|
||||
c_QuickSortSub(arr, left, i - 1, size, temp, compar);
|
||||
c_QuickSortSub(arr, i + 1, right, size, temp, compar);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef INCLUDED_C_QUICKSORT_H
|
||||
#define INCLUDED_C_QUICKSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* Recursive partitioning sub-routine.
|
||||
*/
|
||||
|
||||
void c_QuickSortSub(char* arr, long long left, long long right, c_size_t size, void* temp,
|
||||
int (*compar)(const void*, const void*));
|
||||
|
||||
/**
|
||||
* Top-Level Framework Entry Point for Quicksort.
|
||||
* Time Complexity: O(n log n) average | Space Complexity: O(log n) call stack | Stable: No
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_QuickSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
|
||||
// Optimization: Use a local stack buffer if element size fits comfortably
|
||||
#define QSORT_STACK_LIMIT 128
|
||||
char stack_buf[QSORT_STACK_LIMIT];
|
||||
void* temp = NULL;
|
||||
|
||||
if (size <= QSORT_STACK_LIMIT) {
|
||||
temp = stack_buf;
|
||||
} else {
|
||||
temp = C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
}
|
||||
|
||||
// Invoke processing across signed range bounds safely
|
||||
c_QuickSortSub(arr, 0, (long long)num - 1, size, temp, compar);
|
||||
|
||||
if (size > QSORT_STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef QSORT_STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_QUICKSORT_H*/
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "c_QuickSort.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define EXPECT_TRUE(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \
|
||||
} while(0)
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
double performance_index;
|
||||
} TaskMetric;
|
||||
|
||||
int compareTasks(const void* a, const void* b) {
|
||||
const TaskMetric* t1 = (const TaskMetric*)a;
|
||||
const TaskMetric* t2 = (const TaskMetric*)b;
|
||||
if (t1->performance_index < t2->performance_index) return -1;
|
||||
if (t1->performance_index > t2->performance_index) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compareInts(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
// Test 1: Sorting completely pre-sorted lists (Guards against worst-case naive pivot selections)
|
||||
bool test_qsort_presorted(void) {
|
||||
int ordered[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
c_size_t total = sizeof(ordered) / sizeof(ordered);
|
||||
|
||||
c_QuickSort(ordered, total, sizeof(int), compareInts);
|
||||
|
||||
for (c_size_t i = 0; i < total - 1; i++) {
|
||||
EXPECT_TRUE(ordered[i] <= ordered[i+1], "Pre-sorted sequence tracking error occurred");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test 2: Dense duplicate key distribution array
|
||||
bool test_qsort_duplicates(void) {
|
||||
TaskMetric metrics[] = {
|
||||
{101, 9.5}, {102, 5.0}, {103, 9.5}, {104, 2.1}, {105, 5.0},
|
||||
{106, 9.5}, {107, 2.1}, {108, 9.5}, {109, 5.0}, {110, 2.1},
|
||||
{111, 5.0}, {112, 9.5}
|
||||
};
|
||||
c_size_t count = sizeof(metrics) / sizeof(metrics);
|
||||
|
||||
c_QuickSort(metrics, count, sizeof(TaskMetric), compareTasks);
|
||||
|
||||
for (c_size_t i = 0; i < count - 1; i++) {
|
||||
EXPECT_TRUE(metrics[i].performance_index <= metrics[i+1].performance_index,
|
||||
"Duplicate float element tracking collapsed under Hoare pointers");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Testing Execution Entry Driver
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: Quicksort ===\n");
|
||||
|
||||
if (test_qsort_presorted()) printf(" [PASS] Test 1: Pre-Sorted Array Pivot Anti-Degradation Passed\n");
|
||||
if (test_qsort_duplicates()) printf(" [PASS] Test 2: Highly Repetitive Element Distribution Sorted Successfully\n");
|
||||
|
||||
printf("=== System Verification Sequence Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_QuickSortIterative.h>
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef INCLUDED_C_QUICKSORTITERATIVE_H
|
||||
#define INCLUDED_C_QUICKSORTITERATIVE_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* Internal macro helper to swap two arbitrary blocks of memory.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SwapInternal(char* a, char* b, c_size_t size, void* temp) {
|
||||
if (a == b) return;
|
||||
memcpy(temp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, temp, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Lomuto or Hoare-style tracking partition loop.
|
||||
* Uses the rightmost element as the pivot for flat linear execution.
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
long long c_PartitionIterative(char* arr, long long left, long long right, c_size_t size,
|
||||
void* temp, int (*compar)(const void*, const void*)) {
|
||||
char* pivot = arr + (right * size);
|
||||
long long i = left - 1;
|
||||
|
||||
for (long long j = left; j < right; j++) {
|
||||
if (compar(arr + (j * size), pivot) <= 0) {
|
||||
i++;
|
||||
c_SwapInternal(arr + (i * size), arr + (j * size), size, temp);
|
||||
}
|
||||
}
|
||||
c_SwapInternal(arr + ((i + 1) * size), arr + (right * size), size, temp);
|
||||
return (i + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-Level Custom Framework Entry Point for Non-Recursive Quicksort.
|
||||
* Time Complexity: O(n log n) average | Space Complexity: O(log n) explicit stack | Stable: No
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_QuickSortIterative(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
|
||||
// Optimization: Stack buffer allocation for pivot element swaps
|
||||
#define QSORT_STACK_LIMIT 128
|
||||
char stack_buf[QSORT_STACK_LIMIT];
|
||||
void* temp = (size <= QSORT_STACK_LIMIT) ? stack_buf : C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
|
||||
// Allocate the explicit partition boundary stack.
|
||||
// Max required stack depth for range tracking is 2 * ceil(log2(num)) + 2.
|
||||
// For a 64-bit address space, 128 slots safely handles any possible array size.
|
||||
long long range_stack[128];
|
||||
long long top = -1;
|
||||
|
||||
// Push initial array boundaries onto the tracking stack
|
||||
range_stack[++top] = 0;
|
||||
range_stack[++top] = (long long)num - 1;
|
||||
|
||||
// Keep processing partitions until the explicit boundary stack is empty
|
||||
while (top >= 0) {
|
||||
// Pop right and left boundaries
|
||||
long long right = range_stack[top--];
|
||||
long long left = range_stack[top--];
|
||||
|
||||
// Execute linear pivot segmentation
|
||||
long long p = c_PartitionIterative(arr, left, right, size, temp, compar);
|
||||
|
||||
// If there are elements on the left side of the pivot, push their range to the stack
|
||||
if (p - 1 > left) {
|
||||
range_stack[++top] = left;
|
||||
range_stack[++top] = p - 1;
|
||||
}
|
||||
|
||||
// If there are elements on the right side of the pivot, push their range to the stack
|
||||
if (p + 1 < right) {
|
||||
range_stack[++top] = p + 1;
|
||||
range_stack[++top] = right;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > QSORT_STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef QSORT_STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_QUICKSORTITERATIVE_H*/
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "c_QuickSortIterative.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#define EXPECT_TRUE(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { printf(" [X] Failed Assertion: %s\n", msg); return false; } \
|
||||
} while(0)
|
||||
|
||||
typedef struct {
|
||||
int uid;
|
||||
int priority;
|
||||
} ThreadTask;
|
||||
|
||||
int compareTasks(const void* a, const void* b) {
|
||||
const ThreadTask* t1 = (const ThreadTask*)a;
|
||||
const ThreadTask* t2 = (const ThreadTask*)b;
|
||||
return (t1->priority - t2->priority);
|
||||
}
|
||||
|
||||
int comparePlainInts(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
// Test 1: Sorting a highly unsorted, random layout sequence
|
||||
bool test_iterative_qsort_random(void) {
|
||||
int datasets[] = { 42, 12, 89, 23, 7, 56, 34, 91, 15, 68 };
|
||||
c_size_t total = sizeof(datasets) / sizeof(datasets);
|
||||
|
||||
c_QuickSortIterative(datasets, total, sizeof(int), comparePlainInts);
|
||||
|
||||
for (c_size_t i = 0; i < total - 1; i++) {
|
||||
EXPECT_TRUE(datasets[i] <= datasets[i+1], "Iterative sorting sequence tracking error occurred");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test 2: Multi-field structure priority scheduling validation
|
||||
bool test_iterative_qsort_struct(void) {
|
||||
ThreadTask tasks[] = {
|
||||
{ 1001, 5 },
|
||||
{ 1002, 1 },
|
||||
{ 1003, 9 },
|
||||
{ 1004, 3 },
|
||||
{ 1005, 5 }
|
||||
};
|
||||
c_size_t count = sizeof(tasks) / sizeof(tasks);
|
||||
|
||||
c_QuickSortIterative(tasks, count, sizeof(ThreadTask), compareTasks);
|
||||
|
||||
for (c_size_t i = 0; i < count - 1; i++) {
|
||||
EXPECT_TRUE(tasks[i].priority <= tasks[i+1].priority,
|
||||
"Struct elements misaligned during iterative stack operations");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Testing Execution Entry Driver
|
||||
int main(void) {
|
||||
printf("=== Starting Framework Unit Testing: Iterative Quicksort ===\n");
|
||||
|
||||
if (test_iterative_qsort_random()) printf(" [PASS] Test 1: Random Disordered Data Array Sorted Successfully\n");
|
||||
if (test_iterative_qsort_struct()) printf(" [PASS] Test 2: Complex Struct Priority Segments Sorted Successfully\n");
|
||||
|
||||
printf("=== System Verification Sequence Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_SelectionSort.h>
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef INCLUDED_C_SELECTIONSORT_H
|
||||
#define INCLUDED_C_SELECTIONSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
/**
|
||||
* Generic Selection Sort Function
|
||||
* @param base Pointer to the first element of the array to be sorted
|
||||
* @param num Number of elements in the array
|
||||
* @param size Size of each element in bytes
|
||||
* @param compar Pointer to the comparison function
|
||||
*/
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_SelectionSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
// Avoid execution if array is empty or has only one element
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
|
||||
// OPTIMIZATION: Use stack allocation for small element sizes
|
||||
// to bypass heap overhead completely during force-inlining.
|
||||
#define STACK_LIMIT 128
|
||||
char stack_buf[STACK_LIMIT];
|
||||
void* temp = NULL;
|
||||
|
||||
if (size <= STACK_LIMIT) {
|
||||
temp = stack_buf;
|
||||
} else {
|
||||
temp = C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
}
|
||||
|
||||
for (c_size_t i = 0; i < num - 1; i++) {
|
||||
c_size_t minIndex = i;
|
||||
|
||||
for (c_size_t j = i + 1; j < num; j++) {
|
||||
if (compar(arr + (j * size), arr + (minIndex * size)) < 0) {
|
||||
minIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (minIndex != i) {
|
||||
memcpy(temp, arr + (i * size), size);
|
||||
memcpy(arr + (i * size), arr + (minIndex * size), size);
|
||||
memcpy(arr + (minIndex * size), temp, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Only trigger free if it was actually allocated from the heap
|
||||
if (size > STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef STACK_LIMIT
|
||||
}
|
||||
|
||||
#endif /*INCLUDED_C_SELECTIONSORT_H*/
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "c_SelectionSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
char title[50];
|
||||
int pubYear;
|
||||
double price;
|
||||
} Book;
|
||||
|
||||
// Custom comparison function: Sort by publication year ascending
|
||||
int compareBooksByYear(const void* a, const void* b) {
|
||||
const Book* b1 = (const Book*)a;
|
||||
const Book* b2 = (const Book*)b;
|
||||
|
||||
if (b1->pubYear < b2->pubYear) return -1;
|
||||
if (b1->pubYear > b2->pubYear) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize an unsorted library array
|
||||
Book library[] = {
|
||||
{"The C Programming Language", 1978, 42.50},
|
||||
{"Clean Code", 2008, 35.00},
|
||||
{"Introduction to Algorithms", 1990, 85.99},
|
||||
{"Design Patterns", 1994, 54.95}
|
||||
};
|
||||
size_t count = sizeof(library) / sizeof(library[0]);
|
||||
|
||||
printf("Before Sorting:\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("Year: %d | Title: %s\n", library[i].pubYear, library[i].title);
|
||||
}
|
||||
|
||||
// Call generic selection sort
|
||||
c_SelectionSort(library, count, sizeof(Book), compareBooksByYear);
|
||||
|
||||
printf("\nAfter Sorting (By Year Ascending):\n");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("Year: %d | Title: %s\n", library[i].pubYear, library[i].title);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <c_ShellSort.h>
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef INCLUDED_C_SHELLSORT_H
|
||||
#define INCLUDED_C_SHELLSORT_H
|
||||
|
||||
#ifndef INCLUDED_C_BASE_H
|
||||
#include <c_Base.h>
|
||||
#endif /*INCLUDED_C_BASE_H*/
|
||||
|
||||
#ifndef INCLUDED_C_MEMORY_H
|
||||
#include <c_Memory.h>
|
||||
#endif /*INCLUDED_C_MEMORY_H*/
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
void c_ShellSort(void* base, c_size_t num, c_size_t size,
|
||||
int (*compar)(const void*, const void*)) {
|
||||
if (base == NULL || num < 2 || size == 0) return;
|
||||
|
||||
char* arr = (char*)base;
|
||||
|
||||
// Stack optimization for memory swap space
|
||||
#define STACK_LIMIT 128
|
||||
char stack_buf[STACK_LIMIT];
|
||||
void* temp = NULL;
|
||||
|
||||
if (size <= STACK_LIMIT) {
|
||||
temp = stack_buf;
|
||||
} else {
|
||||
temp = C_ALLOC(size);
|
||||
if (temp == NULL) return;
|
||||
}
|
||||
|
||||
// Using Knuth's gap sequence: h = h * 3 + 1 (1, 4, 13, 40, 121, ...)
|
||||
c_size_t gap = 1;
|
||||
while (gap < num / 3) {
|
||||
gap = gap * 3 + 1;
|
||||
}
|
||||
|
||||
// Start with the largest gap and work down to a gap of 1
|
||||
while (gap > 0) {
|
||||
for (c_size_t i = gap; i < num; i++) {
|
||||
// temp = arr[i]
|
||||
memcpy(temp, arr + (i * size), size);
|
||||
|
||||
c_size_t j = i;
|
||||
|
||||
// Shift elements of the gap-sorted variant until the correct position is found
|
||||
// Loop guards prevent underflow on unsigned c_size_t subtraction (j >= gap)
|
||||
while (j >= gap && compar(arr + ((j - gap) * size), temp) > 0) {
|
||||
// arr[j] = arr[j - gap]
|
||||
memcpy(arr + (j * size), arr + ((j - gap) * size), size);
|
||||
j -= gap;
|
||||
}
|
||||
|
||||
// arr[j] = temp
|
||||
memcpy(arr + (j * size), temp, size);
|
||||
}
|
||||
// Reduce the gap
|
||||
gap /= 3;
|
||||
}
|
||||
|
||||
if (size > STACK_LIMIT) {
|
||||
C_FREE(temp);
|
||||
}
|
||||
#undef STACK_LIMIT
|
||||
}
|
||||
|
||||
|
||||
#endif /*INCLUDED_C_SHELLSORT_H*/
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "c_ShellSort.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// 单元测试断言宏
|
||||
#define EXPECT_TRUE(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { printf(" [X] 失败: %s\n", msg); return false; } \
|
||||
} while(0)
|
||||
|
||||
|
||||
// 复杂结构体元素:员工信息(用于小内存/栈分配测试)
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[16];
|
||||
int score;
|
||||
} Employee;
|
||||
|
||||
// 极其庞大的结构体元素(用于逼出堆分配测试,超过 STACK_LIMIT)
|
||||
typedef struct {
|
||||
int id;
|
||||
char massive_payload[256]; // 超过 128 字节限制
|
||||
} LargeTask;
|
||||
|
||||
// 1. 比较器:按员工积分 (score) 升序
|
||||
int compareEmpByScore(const void* a, const void* b) {
|
||||
const Employee* e1 = (const Employee*)a;
|
||||
const Employee* e2 = (const Employee*)b;
|
||||
return (e1->score > e2->score) - (e1->score < e2->score);
|
||||
}
|
||||
|
||||
// 2. 比较器:按大任务 ID 降序
|
||||
int compareTaskByIdDesc(const void* a, const void* b) {
|
||||
const LargeTask* t1 = (const LargeTask*)a;
|
||||
const LargeTask* t2 = (const LargeTask*)b;
|
||||
return (t2->id > t1->id) - (t2->id < t1->id);
|
||||
}
|
||||
|
||||
// 3. 比较器:普通整型升序
|
||||
int compareInt(const void* a, const void* b) {
|
||||
return (*(int*)a - *(int*)b);
|
||||
}
|
||||
|
||||
// 测试用例 1:极限边界(空数组或单元素数组不崩溃)
|
||||
bool test_boundary_cases() {
|
||||
int* empty_arr = NULL;
|
||||
c_ShellSort(empty_arr, 0, sizeof(int), compareInt); // 传 NULL 不应崩溃
|
||||
|
||||
int single_elem[] = { 42 };
|
||||
c_ShellSort(single_elem, 1, sizeof(int), compareInt); // 1个元素不处理
|
||||
EXPECT_TRUE(single_elem[0] == 42, "单元素数组值被篡改");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 测试用例 2:完全逆序数组的排序(触发大量 Gap 步进调整)
|
||||
bool test_reverse_array() {
|
||||
Employee emps[] = {
|
||||
{4, "Manager", 90},
|
||||
{3, "Leader", 80},
|
||||
{2, "Senior", 70},
|
||||
{1, "Junior", 60}
|
||||
};
|
||||
c_size_t num = sizeof(emps) / sizeof(emps[0]);
|
||||
|
||||
c_ShellSort(emps, num, sizeof(Employee), compareEmpByScore);
|
||||
|
||||
// 预期结果:按积分 60, 70, 80, 90 升序
|
||||
EXPECT_TRUE(emps[0].score == 60 && emps[3].score == 90, "逆序数组排序未完全生效");
|
||||
for(c_size_t i = 0; i < num - 1; i++) {
|
||||
EXPECT_TRUE(emps[i].score <= emps[i+1].score, "逆序序列排序后仍不满足单调递增");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 测试用例 3:包含大量相同主键的复杂数组(测试减治和覆盖分支)
|
||||
bool test_duplicate_keys() {
|
||||
Employee emps[] = {
|
||||
{1, "A", 100}, {2, "B", 50}, {3, "C", 100}, {4, "D", 50}, {5, "E", 100}
|
||||
};
|
||||
c_size_t num = sizeof(emps) / sizeof(emps[0]);
|
||||
|
||||
c_ShellSort(emps, num, sizeof(Employee), compareEmpByScore);
|
||||
|
||||
EXPECT_TRUE(emps[0].score == 50 && emps[1].score == 50, "相同项未能归拢到前半段");
|
||||
EXPECT_TRUE(emps[2].score == 100 && emps[4].score == 100, "相同项未能归拢到后半段");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 测试用例 4:大体积结构体(单元素 > 128 字节,强制触发 C_ALLOC 堆内存分配)
|
||||
bool test_large_struct_heap() {
|
||||
LargeTask tasks[] = {
|
||||
{10, "Payload A"}, {99, "Payload B"}, {5, "Payload C"}, {40, "Payload D"}
|
||||
};
|
||||
c_size_t num = sizeof(tasks) / sizeof(tasks[0]);
|
||||
|
||||
// 采用【降序】比较器
|
||||
c_ShellSort(tasks, num, sizeof(LargeTask), compareTaskByIdDesc);
|
||||
|
||||
// 预期结果:ID 降序排序 -> 99, 40, 10, 5
|
||||
EXPECT_TRUE(tasks[0].id == 99, "堆分配大结构体首位未命中最大值");
|
||||
EXPECT_TRUE(tasks[3].id == 5, "堆分配大结构体末位未命中最小值");
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("=== 开始 c_ShellSort 框架级单元测试 ===\n");
|
||||
|
||||
if (test_boundary_cases()) printf("[PASS] 用例 1: 极限边界条件测试通过\n");
|
||||
if (test_reverse_array()) printf("[PASS] 用例 2: 逆序复杂元素排序通过\n");
|
||||
if (test_duplicate_keys()) printf("[PASS] 用例 3: 密集重复键稳定性分支通过\n");
|
||||
if (test_large_struct_heap()) printf("[PASS] 用例 4: 堆分配(>128B)大元素排序通过\n");
|
||||
|
||||
printf("\n=== 所有测试执行完毕 ===\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user