Built a dynamic long long array that grows and shrinks as needed. Supports quick sort and binary search, insert and delete. To/From file.
All from one script.
The hardest part; it built a complete test application.
Then had it convert that file to all; integer, unsigned inter and floating-point type arrays. Along with test apps.
Then had it build a dynamic string array from one script, along with complete test app. Still need to add split and join, and to/from text file.
All sort and binary search operations can use a custom or default callback.
This is just with the free online version, do have to open an account. Did have to feed a few errors back in, particularly on the floats. Ony takes a few seconds. Took a long time to build the scripts.
Goal, make C more BASIC like. String manipulation is going to be tough.
/*
* StrArr.h - Dynamic 8-bit string array
*
* This header provides a complete implementation of a dynamic array
* that stores pointers to null-terminated strings.
*
* Design:
* - Uses long long for indices and counts
* - Stores allocated strings in data array
* - Empty strings ("") are stored as NULL pointer (0)
* - All functions begin with 'StrArr' prefix to avoid conflicts
*/
#ifndef STRARR_H
#define STRARR_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Default compare callback - ASCII case sensitive */
int StrArrDefaultCompare(const char* a, const char* b);
/* Case insensitive compare callback */
int StrArrCaseInsensitiveCompare(const char* a, const char* b);
typedef struct {
char** data; /* Array of string pointers */
long long count; /* Number of strings currently stored */
long long capacity; /* Allocated capacity of data array */
} StrArrT;
/* Helper function declarations */
static int StrArrCompareWrapper(const void* a, const void* b);
/* Compare callback type */
typedef int (*StrArrCompareFunc)(const char*, const char*);
/* Global callback for qsort */
static StrArrCompareFunc g_compare_callback = NULL;
/* Wrapper for qsort */
static int StrArrCompareWrapper(const void* a, const void* b) {
const char* str_a = *(const char**)a;
const char* str_b = *(const char**)b;
/* Handle NULL pointers (empty strings) */
if (str_a == NULL) str_a = "";
if (str_b == NULL) str_b = "";
if (g_compare_callback) {
return g_compare_callback(str_a, str_b);
}
return StrArrDefaultCompare(str_a, str_b);
}
/* Initialize a string array structure */
void StrArrInit(StrArrT* arr) {
if (arr == NULL) return;
arr->data = NULL;
arr->count = 0;
arr->capacity = 0;
}
/* Free all memory used by the array and reset structure */
void StrArrFinal(StrArrT* arr) {
long long i;
if (arr == NULL) return;
/* Free each allocated string */
if (arr->data != NULL) {
for (i = 0; i < arr->count; i++) {
if (arr->data[i] != NULL) {
free(arr->data[i]);
}
}
free(arr->data);
}
arr->data = NULL;
arr->count = 0;
arr->capacity = 0;
}
/* Cleanup callback for when StrArrT is stored as handle in other structures */
void StrArrFinalCB(void* arr_ptr) {
StrArrT* arr = (StrArrT*)arr_ptr;
if (arr == NULL) return;
/* Free internal data but not the structure itself */
if (arr->data != NULL) {
long long i;
for (i = 0; i < arr->count; i++) {
if (arr->data[i] != NULL) {
free(arr->data[i]);
}
}
free(arr->data);
}
arr->data = NULL;
arr->count = 0;
arr->capacity = 0;
}
/* Clear all strings from array (same as StrArrFinal) */
void StrArrClear(StrArrT* arr) {
StrArrFinal(arr);
}
/* Ensure enough capacity for additional items */
int StrArrGrow(StrArrT* arr, long long items) {
long long new_capacity;
char** new_data;
if (arr == NULL) return 0;
if (arr->capacity >= arr->count + items) {
return 1; /* Already enough room */
}
/* Calculate new capacity: double the required size or at least 2*(count+items) */
new_capacity = 2 * (arr->count + items);
if (new_capacity < 4) new_capacity = 4;
new_data = (char**)realloc(arr->data, new_capacity * sizeof(char*));
if (new_data == NULL) return 0;
arr->data = new_data;
arr->capacity = new_capacity;
return 1;
}
/* Shrink array to exactly fit current count */
void StrArrShrink(StrArrT* arr) {
char** new_data;
if (arr == NULL || arr->data == NULL) return;
if (arr->capacity <= arr->count) return;
if (arr->count == 0) {
free(arr->data);
arr->data = NULL;
arr->capacity = 0;
return;
}
new_data = (char**)realloc(arr->data, arr->count * sizeof(char*));
if (new_data != NULL) {
arr->data = new_data;
arr->capacity = arr->count;
}
}
/* Return current number of strings */
long long StrArrGetCount(StrArrT* arr) {
if (arr == NULL) return 0;
return arr->count;
}
/* Set array size, growing or shrinking as needed */
int StrArrSetCount(StrArrT* arr, long long items) {
long long i;
if (arr == NULL) return 0;
if (items < 0) return 0;
if (items > arr->capacity) {
if (!StrArrGrow(arr, items - arr->count)) {
return 0;
}
}
/* If shrinking, free removed strings */
if (items < arr->count) {
for (i = items; i < arr->count; i++) {
if (arr->data[i] != NULL) {
free(arr->data[i]);
}
}
}
/* Initialize new elements to NULL */
for (i = arr->count; i < items; i++) {
arr->data[i] = NULL;
}
arr->count = items;
return 1;
}
/* Get string at index - always returns a valid string (never NULL) */
const char* StrArrGet(StrArrT* arr, long long index) {
if (arr == NULL) return "";
if (index < 0 || index >= arr->count) return "";
if (arr->data[index] == NULL) return "";
return arr->data[index];
}
/* Set string at index, freeing old string */
int StrArrSet(StrArrT* arr, long long index, const char* value) {
char* new_str;
if (arr == NULL) return 0;
if (index < 0 || index >= arr->count) return 0;
/* Free existing string if any */
if (arr->data[index] != NULL) {
free(arr->data[index]);
}
/* Store empty string as NULL */
if (value == NULL || value[0] == '\0') {
arr->data[index] = NULL;
return 1;
}
/* Allocate and copy the new string */
new_str = (char*)malloc((strlen(value) + 1) * sizeof(char));
if (new_str == NULL) return 0;
strcpy(new_str, value);
arr->data[index] = new_str;
return 1;
}
/* Add string to the end of the array */
int StrArrAdd(StrArrT* arr, const char* value) {
if (arr == NULL) return 0;
if (!StrArrGrow(arr, 1)) return 0;
arr->data[arr->count] = NULL;
arr->count++;
return StrArrSet(arr, arr->count - 1, value);
}
/* Insert string at specified index, shifting elements up */
int StrArrInsert(StrArrT* arr, long long index, const char* value) {
long long i;
if (arr == NULL) return 0;
if (index < 0 || index > arr->count) return 0;
if (!StrArrGrow(arr, 1)) return 0;
/* Shift elements up */
for (i = arr->count; i > index; i--) {
arr->data[i] = arr->data[i - 1];
}
arr->data[index] = NULL;
arr->count++;
return StrArrSet(arr, index, value);
}
/* Delete string at index, shifting elements down */
int StrArrDelete(StrArrT* arr, long long index) {
long long i;
if (arr == NULL) return 0;
if (index < 0 || index >= arr->count) return 0;
/* Free the string at index */
if (arr->data[index] != NULL) {
free(arr->data[index]);
}
/* Shift elements down */
for (i = index; i < arr->count - 1; i++) {
arr->data[i] = arr->data[i + 1];
}
arr->count--;
/* Shrink if capacity is more than double the count */
if (arr->capacity > 2 * arr->count) {
StrArrShrink(arr);
}
return 1;
}
/* Fast delete: swap with last element before deleting */
int StrArrFastDelete(StrArrT* arr, long long index) {
if (arr == NULL) return 0;
if (index < 0 || index >= arr->count) return 0;
/* Free the string at index */
if (arr->data[index] != NULL) {
free(arr->data[index]);
}
/* If not deleting the last element, copy last element to this position */
if (index != arr->count - 1) {
arr->data[index] = arr->data[arr->count - 1];
}
arr->count--;
/* Shrink if capacity is more than double the count */
if (arr->capacity > 2 * arr->count) {
StrArrShrink(arr);
}
return 1;
}
/* Push string onto stack (same as Add) */
int StrArrPush(StrArrT* arr, const char* value) {
return StrArrAdd(arr, value);
}
/* Peek at top string without removing */
const char* StrArrPeek(StrArrT* arr) {
if (arr == NULL) return "";
if (arr->count == 0) return "";
return StrArrGet(arr, arr->count - 1);
}
/* Pop string from stack (remove and return) */
/* IMPORTANT: Returns allocated memory that caller must free */
/* Returns NULL if error or empty */
char* StrArrPop(StrArrT* arr) {
char* result;
const char* str;
if (arr == NULL) return NULL;
if (arr->count == 0) return NULL;
str = StrArrGet(arr, arr->count - 1);
/* Make a copy of the result */
result = (char*)malloc((strlen(str) + 1) * sizeof(char));
if (result == NULL) return NULL;
strcpy(result, str);
/* Delete the last element */
if (arr->data[arr->count - 1] != NULL) {
free(arr->data[arr->count - 1]);
}
arr->count--;
/* Shrink if needed */
if (arr->capacity > 2 * arr->count) {
StrArrShrink(arr);
}
return result;
}
/* Swap two elements in the array */
int StrArrSwap(StrArrT* arr, long long a, long long b) {
char* temp;
if (arr == NULL) return 0;
if (a < 0 || a >= arr->count) return 0;
if (b < 0 || b >= arr->count) return 0;
if (a == b) return 1;
temp = arr->data[a];
arr->data[a] = arr->data[b];
arr->data[b] = temp;
return 1;
}
/* Sort the array using quicksort with callback */
int StrArrSort(StrArrT* arr, StrArrCompareFunc callback) {
if (arr == NULL) return 0;
if (arr->count <= 1) return 1;
g_compare_callback = callback;
qsort(arr->data, arr->count, sizeof(char*), StrArrCompareWrapper);
return 1;
}
/* Random sort - shuffles the array */
int StrArrRandomSort(StrArrT* arr) {
long long i, j;
if (arr == NULL) return 0;
if (arr->count <= 1) return 1;
srand((unsigned int)time(NULL));
/* Fisher-Yates shuffle */
for (i = arr->count - 1; i > 0; i--) {
j = rand() % (i + 1);
StrArrSwap(arr, i, j);
}
return 1;
}
/* Binary search for value in sorted array */
long long StrArrBinarySearch(StrArrT* arr, const char* value, StrArrCompareFunc callback) {
long long left = 0, right, mid;
int cmp;
if (arr == NULL) return -1;
if (arr->count == 0) return -1;
if (value == NULL) value = "";
right = arr->count - 1;
while (left <= right) {
mid = left + (right - left) / 2;
if (callback) {
cmp = callback(StrArrGet(arr, mid), value);
} else {
cmp = StrArrDefaultCompare(StrArrGet(arr, mid), value);
}
if (cmp < 0) {
left = mid + 1;
} else if (cmp > 0) {
right = mid - 1;
} else {
return mid; /* Found */
}
}
return -1; /* Not found */
}
/* Insert value at sorted position */
int StrArrBinaryInsert(StrArrT* arr, const char* value, StrArrCompareFunc callback) {
long long left = 0, right, mid;
int cmp;
long long insert_pos = 0;
if (arr == NULL) return 0;
if (value == NULL) value = "";
/* If empty, just add */
if (arr->count == 0) {
return StrArrAdd(arr, value);
}
/* Binary search for insertion position */
right = arr->count - 1;
while (left <= right) {
mid = left + (right - left) / 2;
if (callback) {
cmp = callback(StrArrGet(arr, mid), value);
} else {
cmp = StrArrDefaultCompare(StrArrGet(arr, mid), value);
}
if (cmp < 0) {
left = mid + 1;
insert_pos = left;
} else if (cmp > 0) {
right = mid - 1;
insert_pos = mid;
} else {
/* Equal values - insert after existing one */
insert_pos = mid + 1;
break;
}
}
return StrArrInsert(arr, insert_pos, value);
}
/* Binary search and delete value */
int StrArrBinaryDelete(StrArrT* arr, const char* value, StrArrCompareFunc callback) {
long long index;
if (arr == NULL) return 0;
if (value == NULL) value = "";
index = StrArrBinarySearch(arr, value, callback);
if (index == -1) return 1; /* Not found - not an error */
return StrArrDelete(arr, index);
}
/* Remove duplicates (array remains sorted) */
int StrArrUnique(StrArrT* arr, StrArrCompareFunc callback) {
long long i;
if (arr == NULL) return 0;
if (arr->count <= 1) return 1;
/* Sort first */
if (!StrArrSort(arr, callback)) return 0;
/* Remove duplicates from back to front */
for (i = arr->count - 1; i > 0; i--) {
int cmp;
if (callback) {
cmp = callback(StrArrGet(arr, i), StrArrGet(arr, i - 1));
} else {
cmp = StrArrDefaultCompare(StrArrGet(arr, i), StrArrGet(arr, i - 1));
}
if (cmp == 0) {
if (!StrArrDelete(arr, i)) return 0;
}
}
return 1;
}
/* Fast remove duplicates (array may not be sorted after) */
int StrArrFastUnique(StrArrT* arr, StrArrCompareFunc callback) {
long long i;
if (arr == NULL) return 0;
if (arr->count <= 1) return 1;
/* Sort first */
if (!StrArrSort(arr, callback)) return 0;
/* Remove duplicates from back to front using fast delete */
for (i = arr->count - 1; i > 0; i--) {
int cmp;
if (callback) {
cmp = callback(StrArrGet(arr, i), StrArrGet(arr, i - 1));
} else {
cmp = StrArrDefaultCompare(StrArrGet(arr, i), StrArrGet(arr, i - 1));
}
if (cmp == 0) {
if (!StrArrFastDelete(arr, i)) return 0;
}
}
return 1;
}
/* Reverse the array */
int StrArrReverse(StrArrT* arr) {
long long i, j;
if (arr == NULL) return 0;
for (i = 0, j = arr->count - 1; i < j; i++, j--) {
StrArrSwap(arr, i, j);
}
return 1;
}
/* Store array to binary file */
int StrArrFileStore(StrArrT* arr, const char* filename) {
FILE* file;
long long i;
size_t len;
if (arr == NULL) return 0;
if (filename == NULL) return 0;
file = fopen(filename, "wb");
if (file == NULL) return 0;
/* Write count */
if (fwrite(&arr->count, sizeof(long long), 1, file) != 1) {
fclose(file);
return 0;
}
/* Write each string */
for (i = 0; i < arr->count; i++) {
const char* str = StrArrGet(arr, i);
len = strlen(str) + 1; /* Include null terminator */
if (fwrite(&len, sizeof(size_t), 1, file) != 1) {
fclose(file);
return 0;
}
if (fwrite(str, sizeof(char), len, file) != len) {
fclose(file);
return 0;
}
}
fclose(file);
return 1;
}
/* Restore array from binary file */
int StrArrFileRestore(StrArrT* arr, const char* filename) {
FILE* file;
long long i, count;
size_t len;
char* buffer;
if (arr == NULL) return 0;
if (filename == NULL) return 0;
/* Clear existing data */
StrArrFinal(arr);
file = fopen(filename, "rb");
if (file == NULL) return 0;
/* Read count */
if (fread(&count, sizeof(long long), 1, file) != 1) {
fclose(file);
return 0;
}
/* Set count (allocate space) */
if (!StrArrSetCount(arr, count)) {
fclose(file);
return 0;
}
/* Read each string */
for (i = 0; i < count; i++) {
if (fread(&len, sizeof(size_t), 1, file) != 1) {
StrArrFinal(arr);
fclose(file);
return 0;
}
buffer = (char*)malloc(len * sizeof(char));
if (buffer == NULL) {
StrArrFinal(arr);
fclose(file);
return 0;
}
if (fread(buffer, sizeof(char), len, file) != len) {
free(buffer);
StrArrFinal(arr);
fclose(file);
return 0;
}
/* Store string */
if (len == 1 && buffer[0] == '\0') {
free(buffer);
arr->data[i] = NULL;
} else {
arr->data[i] = buffer;
}
}
fclose(file);
return 1;
}
/* Default compare function - ASCII case sensitive */
int StrArrDefaultCompare(const char* a, const char* b) {
if (a == NULL) a = "";
if (b == NULL) b = "";
return strcmp(a, b);
}
/* Case insensitive compare function */
int StrArrCaseInsensitiveCompare(const char* a, const char* b) {
if (a == NULL) a = "";
if (b == NULL) b = "";
return _stricmp(a, b); /* MSVC specific, use strcasecmp on other platforms */
}
#endif /* STRARR_H */
Deepseek is the cheapest KI, yet even "V4 Flash" can do any coding task and is 1/10 th of the price from the big prooviders.
In my tests it even beats Minimax M3 and is much cheaper.
In short: Deepseek is not the best but especially "V4 Flash" API is so cheap that this alone can put pressure on all other providers.
Imagine you have 2 Taxi's - one very compfortable and you pay $100 and a competition one with a hard seat and you pay $5.
The point is:
BOTH bring you from A to B. Which will most people take?