#include #include /** * 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; }