diff --git a/Makefile b/Makefile index 808fbbd..da527bd 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ BENCH_OBJ_DIR = bench_obj TESTS_SRC = tests -TARGET = usage TEST_V_TARGET = test_vector TEST_M_TARGET = test_map TEST_B_TARGET = test_bigint @@ -21,16 +20,12 @@ TEST_S_TARGET = test_string BENCH_TARGET = benchmark_datum LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/string.o -PROG_OBJS = $(OBJ_DIR)/usage.o -.PHONY: all clean +.PHONY: all clean examples -all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) +all: $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) examples bench: $(BENCH_TARGET) -$(TARGET): $(PROG_OBJS) $(LIB_OBJS) - $(CC) $(CFLAGS) -o $@ $^ - $(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o $(CC) $(CFLAGS) -o $@ $^ @@ -43,10 +38,10 @@ $(TEST_B_TARGET): $(OBJ_DIR)/test_bigint.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector $(TEST_S_TARGET): $(OBJ_DIR)/test_string.o $(OBJ_DIR)/string.o $(CC) $(CFLAGS) -o $@ $^ -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) - $(CC) $(CFLAGS) -c -o $@ $< +examples: $(LIB_OBJS) + $(MAKE) -C examples -$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR) +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c -o $@ $< $(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR) @@ -69,4 +64,5 @@ $(BENCH_OBJ_DIR): mkdir -p $(BENCH_OBJ_DIR) clean: - rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) + rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) + $(MAKE) -C examples clean diff --git a/README.md b/README.md index 42a0145..3544300 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ the standard library. It currently features: - [**Vector**](/docs/vector.md): a growable, contiguous array of homogenous generic data types; - [**Map**](/docs/map.md): an associative array of generic heterogenous data types; -- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers. +- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers; +- [**String**](/docs/string.md): an immutable, null-terminated string type with partial UTF-8 support. ## Usage At its simplest, you can use this library as follows: @@ -134,20 +135,44 @@ int main(void) { } ``` -For a more exhaustive example, refer to the `usage.c` file. There, you will find a program with proper error management -and a sample usage for every available method. To run it, first issue the following command: +### `String` usage +```c +#include + +#include "src/string.h" + +/* + * Compile with: gcc main.c src/string.c + * Output: Final string: "Hello,World,😀" Splitted: ["Hello" "World" "😀" ] + */ +int main(void) { + string_t *x = string_new(" Hello, ").value.string; + string_t *x_trm = string_trim(x).value.string; + + string_t *y = string_new("😀,dlroW").value.string; + string_t *y_rev = string_reverse(y).value.string; + + string_t *str = string_concat(x_trm, y_rev).value.string; + string_t **strings = string_split(str, ",").value.split.strings; + + printf("Final string: \"%s\" Splitted: [", str->data); + for (int idx = 0; idx < 3; idx++) { printf("\"%s\" ", strings[idx]->data); } + printf("]\n"); + + string_split_destroy(strings, 3); string_destroy(str); + string_destroy(x); string_destroy(y); + string_destroy(x_trm); string_destroy(y_rev); + + return 0; +} +``` + +For additional usage samples, refer to the [`examples/`](/examples) directory. You can compile these example programs with the following command: ```sh $ make clean all ``` -This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`. - -> [!NOTE] -> This project is primarily developed for learning purposes and was not created with industrial -> or production use in mind. As such, it is not intended to compete with any existing C library such as the -> GNU Multiple Precision Arithmetic Library (GMP). - ## Documentation For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs). diff --git a/docs/README.md b/docs/README.md index 9968106..1929cf6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,4 +7,5 @@ At the time being, this documentation includes the following pages: - [vector.md](vector.md): vector documentation; - [map.md](map.md): map documentation; -- [bigint.md](bigint.md): bigint documentation. +- [bigint.md](bigint.md): bigint documentation; +- [string.md](string.md): string documentation. diff --git a/docs/bigint.md b/docs/bigint.md index bd488e4..06abc9d 100644 --- a/docs/bigint.md +++ b/docs/bigint.md @@ -80,8 +80,6 @@ Each method that returns such type indicates whether the operation was successfu by setting the `status` field and by providing a descriptive message on the `message` field. If the operation was successful (that is, `status == BIGINT_OK`), you can either move on with the rest of the program or read the returned value from the sum data type. -Of course, you can choose to ignore the return value (if you're brave enough :D) as -illustrated on the first part of the README. The sum data type (i.e., the `value` union) defines four different variables. Each of them has an unique scope as described below: diff --git a/docs/map.md b/docs/map.md index c30ac33..7712802 100644 --- a/docs/map.md +++ b/docs/map.md @@ -72,5 +72,4 @@ typedef struct { Each method that returns such type indicates whether the operation was successful or not by setting the `status` field and by providing a descriptive message on the `message` field. If the operation was successful (that is, `status == MAP_OK`), you can either move on with the rest of the program or read -the returned value from the sum data type. Of course, you can choose to ignore the return value (if you're brave enough :D) as illustrated -on the first part of the README. +the returned value from the sum data type. diff --git a/docs/string.md b/docs/string.md new file mode 100644 index 0000000..60f7f51 --- /dev/null +++ b/docs/string.md @@ -0,0 +1,85 @@ +# String Technical Details +In this document you can find a quick overview of the technical aspects (internal design, memory layout, etc.) of the `String` data structure. + +`String` is an immutable, null-terminated string data type with partial UTF-8 support. +This means that methods return a new string instance rather than modifying the string in-place. +Internally, this data structure is represented by the following layout: + +```c +typedef struct { + char *data; + size_t byte_size; + size_t byte_capacity; + size_t char_count; +} string_t; +``` + +where the `data` field represent the actual string, `byte_size` +indicates the actual size (in bytes), `byte_capacity` represents the total number +of allocated memory (in bytes) and `char_count` represents the number of symbols. + +As mentioned earlier, this data type provides partial UTF-8 support. It is able +to recognize UTF-8 byte sequences as individual Unicode code points and has +full support for Unicode symbols such as emojis. However, it does not support +localization. In particular, it does not perform local-aware conversions. For instance, uppercase/lowercase transformations are limited to ASCII characters only. As a result, the German scharfes S (`ß`) is not convert to `SS`, the Spanish `Ñ` is not converted to `ñ` and the Italian `é` (and its variants) is not treated as a single symbol but rather as a base letter combined with an accent. + +At the time being, `String` supports the following methods: + +- `string_result_t string_new(c_str)`: create a new string; +- `string_result_t string_clone(str)`: clone an existing string; +- `string_result_t string_concat(x, y)`: concatenate two strings together; +- `string_result_t string_contains(haystack, needle)`: search whether the `haystack` string contains `needle`; +- `string_result_t string_slice(str, start, end)`: return a slice (a new string) from `str` between `start` and `end` indices (inclusive); +- `string_result_t string_eq(x, y, case_sensitive)`: check whether `x` and `y` are equal; +- `string_result_t string_get_at(str, position)`: get the UTF-8 symbol indexed by `position` from `str`; +- `string_result_t string_set_at(str, position, utf8_char)`: write a UTF-8 symbol into `str` at index `position`; +- `string_result_t string_to_lower(str)`: convert a string to lowercase; +- `string_result_t string_to_upper(str)`: convert a string to uppercase; +- `string_result_t string_reverse(str)`: reverse a string; +- `string_result_t string_trim(str)`: remove leading and trailing white space from a string; +- `string_result_t string_split(str, delim)`: split a string into an array of `string_t` by specifying a separator; +- `string_result_t string_destroy(str)`: remove a string from memory; +- `string_result_t string_split_destroy(split, count)`: remove an array of strings from memory; +- `size_t string_size(str)`: return string character count. + +As you can see from the previous function signatures, most methods that operate on the `String` +data type return a custom type called `string_result_t` which is defined as follows: + +```c +typedef enum { + STRING_OK = 0x0, + STRING_ERR_ALLOCATE, + STRING_ERR_INVALID, + STRING_ERR_INVALID_UTF8, + STRING_ERR_OVERFLOW +} string_status_t; + +typedef struct { + string_status_t status; + uint8_t message[RESULT_MSG_SIZE]; + union { + string_t *string; + char *symbol; + int64_t idx; + bool is_equ; + struct { + string_t **strings; + size_t count; + } split; + } value; +} string_result_t; +``` + +Each method that returns such type indicates whether the operation was successful or not +by setting the `status` field and by providing a descriptive message on the `message` +field. If the operation was successful (that is, `status == STRING_OK`) you can either +move on with the rest of your program or read the returned value from the sum data type. + +The sum data type (i.e., the `value` union) defines five different variables. +Each of them has an unique scope as described below: + +- `string`: result of `new`, `clone`, `slice`, `reverse` and `trim` functions; +- `symbol`: result of `get_at` function; +- `idx`: result of `contains` function; +- `is_eq`: result of `equ` function. It's true when two strings are equal, false otherwise; +- `split`: result of `split` function. It contains an array of `string_t` and its number of elements. \ No newline at end of file diff --git a/docs/vector.md b/docs/vector.md index 6ca7aeb..f99071e 100644 --- a/docs/vector.md +++ b/docs/vector.md @@ -65,8 +65,7 @@ typedef struct { Each method that returns such type indicates whether the operation was successful or not by setting the `status` field and by providing a descriptive message on the `message` field. If the operation was successful (that is, `status == VECTOR_OK`), you can either -move on with the rest of the program or read the returned value from the sum data type. Of course, you can choose to -ignore the return value (if you're brave enough :D) as illustrated on the first part of the README. +move on with the rest of the program or read the returned value from the sum data type. ## Functional methods `Vector` provides three functional methods called `map`, `filter` and `reduce` which allow the caller to apply a computation to the vector, diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..0e33572 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,37 @@ +CC = gcc +CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \ + -fsanitize=address -fsanitize=undefined -fstack-clash-protection \ + -Wwrite-strings -g -std=c99 + +SRC_DIR = ../src +OBJ_DIR = ../obj + +TARGETS = vector_basic vector_sorting vector_functional map_basic bigint_operations string_basic string_advanced + +.PHONY: all clean + +all: $(TARGETS) + +vector_basic: vector_basic.c $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + +vector_sorting: vector_sorting.c $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + +vector_functional: vector_functional.c $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + +map_basic: map_basic.c $(OBJ_DIR)/map.o + $(CC) $(CFLAGS) -o $@ $^ + +bigint_operations: bigint_operations.c $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + +string_basic: string_basic.c $(OBJ_DIR)/string.o + $(CC) $(CFLAGS) -o $@ $^ + +string_advanced: string_advanced.c $(OBJ_DIR)/string.o + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f $(TARGETS) \ No newline at end of file diff --git a/examples/bigint_operations.c b/examples/bigint_operations.c new file mode 100644 index 0000000..cdcc34b --- /dev/null +++ b/examples/bigint_operations.c @@ -0,0 +1,118 @@ +/* + * Bigint operations example. + */ + +#include +#include +#include + +#include "../src/bigint.h" + +int main(void) { + const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355"; + const char *y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937"; + const size_t x_len = strlen(x_origin); + const size_t y_len = strlen(y_origin); + const size_t large_x_size = x_len * 100 + 1; + const size_t large_y_size = y_len * 100 + 1; + + char *large_x = malloc(large_x_size); + char *large_y = malloc(large_y_size); + + if (large_x == NULL || large_y == NULL) { + printf("Error while allocating memory for strings\n"); + free(large_x); + free(large_y); + return 1; + } + + large_x[0] = '\0'; + large_y[0] = '\0'; + + // Concatenate 100 times + for (size_t idx = 0; idx < 100; idx++) { + strcat(large_x, x_origin); + strcat(large_y, y_origin); + } + + // Create two big integers from previous strings + bigint_result_t x_res = bigint_from_string(large_x); + if (x_res.status != BIGINT_OK) { + printf("Error while creating big number: %s\n", x_res.message); + return 1; + } + + bigint_result_t y_res = bigint_from_string(large_y); + if (x_res.status != BIGINT_OK) { + printf("Error while creating big number: %s\n", x_res.message); + return 1; + } + + bigint_t *x = x_res.value.number; + bigint_t *y = y_res.value.number; + + // Sum two big integers + bigint_result_t sum_res = bigint_add(x, y); + if (sum_res.status != BIGINT_OK) { + printf("Error while summing two big numbers: %s\n", sum_res.message); + return 1; + } + + bigint_t *sum = sum_res.value.number; + + // Print result + bigint_printf("Sum result = %B\n", sum); + + // Subtract two big integers + bigint_result_t diff_res = bigint_sub(x, y); + if (diff_res.status != BIGINT_OK) { + printf("Error while subtracting two big numbers: %s\n", diff_res.message); + return 1; + } + + bigint_t *diff = diff_res.value.number; + + // Print result + bigint_printf("difference result = %B\n", diff); + + // Multiply two big integers + bigint_result_t prod_res = bigint_prod(x, y); + if (prod_res.status != BIGINT_OK) { + printf("Error while multiplying two big numbers: %s\n", prod_res.message); + return 1; + } + + bigint_t *prod = prod_res.value.number; + + // Print result + bigint_printf("multiplication result = %B\n", prod); + + bigint_t *a = bigint_from_string(large_x).value.number; + bigint_t *b = bigint_from_string(y_origin).value.number; + + // Divide two big integers + bigint_result_t div_res = bigint_divmod(a, b); + if (div_res.status != BIGINT_OK) { + printf("Error while dividing two big numbers: %s\n", div_res.message); + return 1; + } + + bigint_t *quotient = div_res.value.division.quotient; + bigint_t *remainder = div_res.value.division.remainder; + + // Print result + bigint_printf( + "division result = %B\ + \nmod result = %B\n", + quotient, remainder); + + // Destroy big numbers and strings + bigint_destroy(x); bigint_destroy(y); + bigint_destroy(a); bigint_destroy(b); + bigint_destroy(sum); bigint_destroy(diff); + bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder); + free(large_x); free(large_y); + + return 0; +} + diff --git a/examples/map_basic.c b/examples/map_basic.c new file mode 100644 index 0000000..dabd5a8 --- /dev/null +++ b/examples/map_basic.c @@ -0,0 +1,101 @@ +/* + * Basic map operations example. + */ + +#include +#include + +#include "../src/map.h" + +int main(void) { + // Create a new map + map_result_t res = map_new(); + if (res.status != MAP_OK) { + printf("Error while creating map: %s\n", res.message); + return 1; + } + + map_t *map = res.value.map; + + // Add some values + const int x = 0xB00B5; + const char *y = "Hello"; + + map_result_t add_res = map_add(map, "x", (void*)&x); + if (add_res.status != MAP_OK) { + printf("Error while adding elements: %s\n", add_res.message); + return 1; + } + + add_res = map_add(map, "y", (void*)y); + if (add_res.status != MAP_OK) { + printf("Error while adding elements: %s\n", add_res.message); + return 1; + } + + // Print size and capacity + printf("Map size (should be 2): %zu\n", map_size(map)); + printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map)); + + // Retrieve keys + map_result_t get_res = map_get(map, "x"); + if (get_res.status != MAP_OK) { + printf("Cannot retrieve map element 'x': %s\n", get_res.message); + return 1; + } else { + const int *val = (const int*)get_res.value.element; + printf("Key 'x' contains (should be 'B00B5'): %X\n", *val); + } + + get_res = map_get(map, "y"); + if (get_res.status != MAP_OK) { + printf("Cannot retrieve map element 'y': %s\n", get_res.message); + return 1; + } else { + const char *val = (const char*)get_res.value.element; + printf("Key 'y' contains (should be 'Hello') : %s\n\n", val); + } + + // Update key + const int new_x = 0xC0FFEE; + map_result_t up_res = map_add(map, "x", (void*)&new_x); + + up_res = map_get(map, "x"); + if (get_res.status != MAP_OK) { + printf("Cannot retrieve map element 'x': %s\n", get_res.message); + return 1; + } else { + const int *val = (const int*)up_res.value.element; + printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val); + } + + // Remove an element + map_result_t rm_res = map_remove(map, "y"); + if (rm_res.status != MAP_OK) { + printf("Cannot remove map element 'y': %s\n", rm_res.message); + return 1; + } else { + printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map)); + } + + // Clear the map + map_result_t clear_res = map_clear(map); + if (clear_res.status != MAP_OK) { + printf("Cannot clear map: %s\n", clear_res.message); + return 1; + } else { + printf("Map cleared (size should be 0): %zu\n", map_size(map)); + } + + printf("\n"); + + // Delete the map + map_result_t del_res = map_destroy(map); + if (del_res.status != MAP_OK) { + printf("Error while destroying the map: %s\n", del_res.message); + return 1; + } + + return 0; +} + diff --git a/examples/string_advanced.c b/examples/string_advanced.c new file mode 100644 index 0000000..4e441cd --- /dev/null +++ b/examples/string_advanced.c @@ -0,0 +1,103 @@ +/* + * Advanced string manipulation example. + */ + +#include +#include + +#include "../src/string.h" + +int main(void) { + // Create a string for manipulation + string_result_t res = string_new("Hello, World! 😜"); + if (res.status != STRING_OK) { + printf("Error: %s\n", res.message); + return 1; + } + + string_t *str = res.value.string; + printf("Original string: \"%s\"\n\n", str->data); + + // Uppercase string + string_result_t res_upper = string_to_upper(str); + if (res_upper.status != STRING_OK) { + printf("Error: %s\n", res_upper.message); + return 1; + } + printf("Uppercase: \"%s\"\n", res_upper.value.string->data); + string_destroy(res_upper.value.string); + + // Lowercase string + string_result_t res_lower = string_to_lower(str); + if (res_lower.status != STRING_OK) { + printf("Error: %s\n", res_lower.message); + return 1; + } + printf("Lowercase: \"%s\"\n\n", res_lower.value.string->data); + string_destroy(res_lower.value.string); + + // Reverse string + string_result_t res_rev = string_reverse(str); + if (res_rev.status != STRING_OK) { + printf("Error: %s\n", res_rev.message); + return 1; + } + printf("Reversed: \"%s\"\n\n", res_rev.value.string->data); + string_destroy(res_rev.value.string); + + // Change first character of the string + string_result_t res_set = string_set_at(str, 0, "J"); + if (res_set.status != STRING_OK) { + printf("Error: %s\n", res_set.message); + return 1; + } + printf("Updated string: \"%s\"\n\n", res_set.value.string->data); + string_destroy(res_set.value.string); + + // Get character from string (the emoji) + string_result_t res_get = string_get_at(str, 14); + if (res_get.status != STRING_OK) { + printf("Error: %s\n", res_get.message); + return 1; + } + printf("Extracted symbol: \"%s\"\n", res_get.value.symbol); + free(res_get.value.symbol); + + // Trim string + string_t *to_trim = string_new(" foo ").value.string; + string_result_t res_trim = string_trim(to_trim); + if (res_trim.status != STRING_OK) { + printf("Error: %s\n", res_trim.message); + return 1; + } + + printf("Trimmed string: \"%s\"\n\n", res_trim.value.string->data); + string_destroy(to_trim); + string_destroy(res_trim.value.string); + + // Split string + string_t *to_split = string_new("foo/bar/biz").value.string; + string_result_t res_split = string_split(to_split, "/"); + if (res_split.status != STRING_OK) { + printf("Error: %s\n", res_split.message); + return 1; + } + + const size_t count = res_split.value.split.count; + string_t **strings = res_split.value.split.strings; + + printf("Original string: \"%s\"\nSplitted string: ", to_split->data); + for (size_t idx = 0; idx < count; idx++) { + printf("\"%s\" ", strings[idx]->data); + } + + printf("\n"); + + string_split_destroy(strings, count); + string_destroy(to_split); + + string_destroy(str); + + return 0; +} + diff --git a/examples/string_basic.c b/examples/string_basic.c new file mode 100644 index 0000000..53eae52 --- /dev/null +++ b/examples/string_basic.c @@ -0,0 +1,95 @@ +/* + * Basic string operations example. + */ + +#include +#include + +#include "../src/string.h" + +int main(void) { + // Create a new string + string_result_t res = string_new("Hello, "); + if (res.status != STRING_OK) { + printf("Error: %s\n", res.message); + return 1; + } + + string_t *str1 = res.value.string; + printf("Created string: \"%s\"\n", str1->data); + printf("Character count: %zu (%zu actual bytes)\n", string_size(str1), str1->byte_size); + + string_result_t res_clone = string_clone(str1); + if (res_clone.status != STRING_OK) { + printf("Error: %s\n", res.message); + return 1; + } + + string_t *cloned = res_clone.value.string; + printf("Cloned string: \"%s\"\n\n", cloned->data); + string_destroy(cloned); + + // Concatenation of strings + string_result_t res_suffix = string_new("World! 😜"); + if (res_suffix.status != STRING_OK) { + printf("Error: %s\n", res.message); + return 1; + } + + string_t *suffix = res_suffix.value.string; + printf("Created another string: \"%s\"\n", suffix->data); + printf("Character count: %zu (%zu actual bytes)\n\n", string_size(suffix), suffix->byte_size); + + string_result_t res_cat = string_concat(str1, suffix); + if (res_cat.status != STRING_OK) { + printf("Error: %s\n", res_cat.message); + return 1; + } + string_destroy(suffix); + + string_t *concat_str = res_cat.value.string; + printf("Concatenation result: \"%s\"\n\n", concat_str->data); + + // String contains + string_t *haystack = string_new("The quick brown fox jumps over the lazy dog.").value.string; + string_t *needle = string_new("brown fox").value.string; + + string_result_t res_contains = string_contains(haystack, needle); + if (res_contains.status != STRING_OK) { + printf("Error: %s\n", res_contains.message); + return 1; + } + + if (res_contains.value.idx != -1) { + printf("Substring found. Starting at index %ld\n\n", res_contains.value.idx); + } + + string_destroy(haystack); + string_destroy(needle); + + // String slicing + string_result_t res_slice = string_slice(concat_str, 7, 14); + if (res_slice.status != STRING_OK) { + printf("Error: %s\n", res_slice.message); + return 1; + } + + printf("Slice of string: \"%s\"\n\n", res_slice.value.string->data); + string_destroy(res_slice.value.string); + + // String equality + string_t *compare = string_new("hello, World! 😜").value.string; + string_result_t res_eq = string_eq(concat_str, compare, true); + if (res_eq.value.is_equ) { + printf("The two strings are equal\n\n"); + } else { + printf("The two strings are not equal\n\n"); + } + + string_destroy(compare); + string_destroy(concat_str); + string_destroy(str1); + + return 0; +} + diff --git a/examples/vector_basic.c b/examples/vector_basic.c new file mode 100644 index 0000000..6b09a4a --- /dev/null +++ b/examples/vector_basic.c @@ -0,0 +1,78 @@ +/* + * Basic vector operations example. + */ + +#include +#include + +#include "../src/vector.h" + +int main(void) { + // Create a vector of 3 integers + vector_result_t res = vector_new(3, sizeof(int)); + if (res.status != VECTOR_OK) { + printf("Error while creating vector: %s\n", res.message); + return 1; + } + + vector_t *vector = res.value.vector; + + // Push some values to trigger reallocation + for (int idx = 0; idx < 5; idx++) { + vector_result_t add_res = vector_push(vector, &idx); + if (add_res.status != VECTOR_OK) { + printf("Error while adding elements: %s\n", add_res.message); + return 1; + } + } + + // Print vector size and capacity + printf("Vector size (should be 5): %zu\n", vector_size(vector)); + printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector)); + + // Print the whole vector + size_t sz = vector_size(vector); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t get_res = vector_get(vector, idx); + if (get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message); + return 1; + } else { + int val = *(int *)get_res.value.element; + printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val); + } + } + + // Set an element at index 2 + int new_val = 0xBABE; + vector_result_t set_res = vector_set(vector, 2, &new_val); + if (set_res.status == VECTOR_OK) { + printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val); + } + + // Pop last element + vector_result_t pop_res = vector_pop(vector); + if (pop_res.status == VECTOR_OK) { + int val = *(int *)pop_res.value.element; + printf("Popped value (should be 5) : %d\n\n", val); + } + + // Clear vector + vector_result_t clear_res = vector_clear(vector); + if (clear_res.status != VECTOR_OK) { + printf("Cannot clear vector: %s\n", clear_res.message); + return 1; + } else { + printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector)); + } + + // Free vector + vector_result_t del_res = vector_destroy(vector); + if (del_res.status != VECTOR_OK) { + printf("Error while destroying the vector: %s\n", del_res.message); + return 1; + } + + return 0; +} + diff --git a/examples/vector_functional.c b/examples/vector_functional.c new file mode 100644 index 0000000..0a403e9 --- /dev/null +++ b/examples/vector_functional.c @@ -0,0 +1,118 @@ +/* + * Vector functional operations example. + */ + +#include +#include + +#include "../src/vector.h" + +#define UNUSED(X) (void)(X) + +static void square(void *element, void *env); +static int is_even(const void *element, void *env); +static void adder(void *accumulator, const void *element, void *env); + +int main(void) { + // Create a vector + vector_result_t res = vector_new(1, sizeof(int)); + if (res.status != VECTOR_OK) { + printf("Error while creating vector: %s\n", res.message); + return 1; + } + + vector_t *vector = res.value.vector; + + // Map vector elements + for (size_t idx = 1; idx <= 5; idx++) { + vector_result_t map_push_res = vector_push(vector, &idx); + if (map_push_res.status != VECTOR_OK) { + printf("Error while adding elements: %s\n", map_push_res.message); + return 1; + } + } + + size_t sz = vector_size(vector); + + // Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25] + vector_result_t map_res = vector_map(vector, square, NULL); + if (map_res.status != VECTOR_OK) { + printf("Error while mapping vector: %s\n", map_res.message); + return 1; + } + + printf("Squared vector: "); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t map_get_res = vector_get(vector, idx); + if (map_get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message); + return 1; + } else { + int *val = (int*)map_get_res.value.element; + printf("%d ", *val); + } + } + + printf("\n"); + + // Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16] + vector_result_t filter_res = vector_filter(vector, is_even, NULL); + if (filter_res.status != VECTOR_OK) { + printf("Error while filtering vector: %s\n", filter_res.message); + return 1; + } + + sz = vector_size(vector); + + printf("Filtered vector: "); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t map_get_res = vector_get(vector, idx); + if (map_get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message); + return 1; + } else { + int *val = (int*)map_get_res.value.element; + printf("%d ", *val); + } + } + + printf("\n"); + + // Reduce vector elements: [4, 16] -> 20 + int sum = 0; + vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL); + if (reduce_res.status != VECTOR_OK) { + printf("Error while reducing vector: %s\n", reduce_res.message); + return 1; + } + + printf("Sum of vector: %d\n", sum); + + // Free vector + vector_result_t del_res = vector_destroy(vector); + if (del_res.status != VECTOR_OK) { + printf("Error while destroying the vector: %s\n", del_res.message); + return 1; + } + + return 0; +} + +void square(void *element, void *env) { + UNUSED(env); + int *value = (int*)element; + *value = (*value) * (*value); +} + +int is_even(const void *element, void *env) { + UNUSED(env); + int value = *(int*)element; + + return (value % 2) == 0; +} + +void adder(void *accumulator, const void *element, void *env) { + UNUSED(env); + *(int*)accumulator += *(int*)element; +} + diff --git a/examples/vector_sorting.c b/examples/vector_sorting.c new file mode 100644 index 0000000..81e7124 --- /dev/null +++ b/examples/vector_sorting.c @@ -0,0 +1,110 @@ +/* + * Vector sorting example. + */ + +#include +#include + +#include "../src/vector.h" + +static vector_order_t cmp_int_asc(const void *x, const void *y); +static vector_order_t cmp_int_desc(const void *x, const void *y); + +int main(void) { + // Create a vector + vector_result_t res = vector_new(1, sizeof(int)); + if (res.status != VECTOR_OK) { + printf("Error while creating vector: %s\n", res.message); + return 1; + } + + vector_t *vector = res.value.vector; + + // Sort vector in ascending order + int values[] = {5, 10, -9, 3, 1, 0, 4}; + for (size_t idx = 0; idx < 7; idx++) { + vector_result_t sort_push_res = vector_push(vector, &values[idx]); + if (sort_push_res.status != VECTOR_OK) { + printf("Error while adding elements: %s\n", sort_push_res.message); + return 1; + } + } + + printf("Added new elements. Before sort: "); + + size_t sz = vector_size(vector); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t sort_get_res = vector_get(vector, idx); + if (sort_get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); + return 1; + } else { + const int *val = (int*)sort_get_res.value.element; + printf("%d ", *val); + } + } + printf("\n"); + + vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc); + if (sort_asc_res.status != VECTOR_OK) { + printf("Cannot sort array: %s\n", sort_asc_res.message); + return 1; + } + + printf("After sort in ascending order: "); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t sort_get_res = vector_get(vector, idx); + if (sort_get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); + return 1; + } else { + int *val = (int*)sort_get_res.value.element; + printf("%d ", *val); + } + } + printf("\n"); + + // Sort vector in descending order + vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc); + if (sort_desc_res.status != VECTOR_OK) { + printf("Cannot sort array: %s\n", sort_desc_res.message); + return 1; + } + + printf("After sort in descending order: "); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t sort_get_res = vector_get(vector, idx); + if (sort_get_res.status != VECTOR_OK) { + printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); + return 1; + } else { + int *val = (int*)sort_get_res.value.element; + printf("%d ", *val); + } + } + printf("\n\n"); + + // Free vector + vector_result_t del_res = vector_destroy(vector); + if (del_res.status != VECTOR_OK) { + printf("Error while destroying the vector: %s\n", del_res.message); + return 1; + } + + return 0; +} + +vector_order_t cmp_int_asc(const void *x, const void *y) { + int x_int = *(const int*)x; + int y_int = *(const int*)y; + + if (x_int < y_int) return VECTOR_ORDER_LT; + if (x_int > y_int) return VECTOR_ORDER_GT; + + return VECTOR_ORDER_EQ; +} + +vector_order_t cmp_int_desc(const void *x, const void *y) { + return cmp_int_asc(y, x); +} + diff --git a/src/string.c b/src/string.c index 1c22c54..6a381be 100644 --- a/src/string.c +++ b/src/string.c @@ -910,7 +910,7 @@ string_result_t string_destroy(string_t *str) { } /** - * string_split_destory + * string_split_destroy * @split: an array of pointers of String * @count: the number of elements * diff --git a/usage.c b/usage.c deleted file mode 100644 index 60f2b4a..0000000 --- a/usage.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Sample usage of the Datum library. - * - * This program is a complete example on how to use Datum - * with *verbose* error checking. For a more minimal usage, you may want to ignore - * return messages/codes and get straight to the actual result. See the early - * part of the README.md file for such example (use it at your own risk). - * - * Developed by Marco Cetica (c) 2025, - * - */ -#define SEP(SIZE) do { \ - for (size_t i = 0; i < SIZE; i++) { \ - printf("="); \ - }; \ - puts("\n"); \ -} while(0) - -#define UNUSED(X) (void)(X) - -#include -#include -#include - -#include "src/vector.h" -#include "src/map.h" -#include "src/bigint.h" - -static int vector_usage(void); -static int map_usage(void); -static int bigint_usage(void); - -static vector_order_t cmp_int_asc(const void *x, const void *y); -static vector_order_t cmp_int_desc(const void *x, const void *y); -static void square(void *element, void *env); -static int is_even(const void *element, void *env); -static void adder(void *accumulator, const void *element, void *env); - -int main(void) { - int st; - - st = vector_usage(); - if (st) { return st; } - - SEP(50); - - st = map_usage(); - if (st) { return st; } - - SEP(50); - - st = bigint_usage(); - if (st) { return st; } - - return 0; -} - -vector_order_t cmp_int_asc(const void *x, const void *y) { - int x_int = *(const int*)x; - int y_int = *(const int*)y; - - if (x_int < y_int) return VECTOR_ORDER_LT; - if (x_int > y_int) return VECTOR_ORDER_GT; - - return VECTOR_ORDER_EQ; -} - -vector_order_t cmp_int_desc(const void *x, const void *y) { - return cmp_int_asc(y, x); -} - -void square(void *element, void *env) { - UNUSED(env); - int *value = (int*)element; - *value = (*value) * (*value); -} - -int is_even(const void *element, void *env) { - UNUSED(env); - int value = *(int*)element; - - return (value % 2) == 0; -} - -void adder(void *accumulator, const void *element, void *env) { - UNUSED(env); - *(int*)accumulator += *(int*)element; -} - -int vector_usage(void) { - // Create a vector of 3 integers - vector_result_t res = vector_new(3, sizeof(int)); - if (res.status != VECTOR_OK) { - printf("Error while creating vector: %s\n", res.message); - - return 1; - } - - vector_t *vector = res.value.vector; - - // Push some values to trigger reallocation - for (int idx = 0; idx < 5; idx++) { - vector_result_t add_res = vector_push(vector, &idx); - if (add_res.status != VECTOR_OK) { - printf("Error while adding elements: %s\n", add_res.message); - - return 1; - } - } - - // Print vector size and capacity - printf("Vector size (should be 5): %zu\n", vector_size(vector)); - printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector)); - - // Print the whole vector - size_t sz = vector_size(vector); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t get_res = vector_get(vector, idx); - if (get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message); - - return 1; - } else { - int val = *(int *)get_res.value.element; - printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val); - } - } - - // Set an element at index 2 - int new_val = 0xBABE; - vector_result_t set_res = vector_set(vector, 2, &new_val); - if (set_res.status == VECTOR_OK) { - printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val); - } - - // Pop last element - vector_result_t pop_res = vector_pop(vector); - if (pop_res.status == VECTOR_OK) { - int val = *(int *)pop_res.value.element; - printf("Popped value (should be 5) : %d\n\n", val); - } - - // Clear vector - vector_result_t clear_res = vector_clear(vector); - if (clear_res.status != VECTOR_OK) { - printf("Cannot clear vector: %s\n", clear_res.message); - - return 1; - } else { - printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector)); - } - - // Sort vector in ascending order - int values[] = {5, 10, -9, 3, 1, 0, 4}; - for (size_t idx = 0; idx < 7; idx++) { - vector_result_t sort_push_res = vector_push(vector, &values[idx]); - if (sort_push_res.status != VECTOR_OK) { - printf("Error while adding elements: %s\n", sort_push_res.message); - - return 1; - } - } - - printf("Added new elements. Before sort: "); - - sz = vector_size(vector); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t sort_get_res = vector_get(vector, idx); - if (sort_get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); - - return 1; - } else { - const int *val = (int*)sort_get_res.value.element; - printf("%d ", *val); - } - } - printf("\n"); - - vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc); - if (sort_asc_res.status != VECTOR_OK) { - printf("Cannot sort array: %s\n", sort_asc_res.message); - - return 1; - } - - printf("After sort in ascending order: "); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t sort_get_res = vector_get(vector, idx); - if (sort_get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); - - return 1; - } else { - int *val = (int*)sort_get_res.value.element; - printf("%d ", *val); - } - } - printf("\n"); - - // Sort vector in descending order - vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc); - if (sort_desc_res.status != VECTOR_OK) { - printf("Cannot sort array: %s\n", sort_desc_res.message); - - return 1; - } - - printf("After sort in descending order: "); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t sort_get_res = vector_get(vector, idx); - if (sort_get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message); - - return 1; - } else { - int *val = (int*)sort_get_res.value.element; - printf("%d ", *val); - } - } - printf("\n\n"); - - vector_result_t map_clear_res = vector_clear(vector); - if (map_clear_res.status != VECTOR_OK) { - printf("Cannot clear vector: %s\n", map_clear_res.message); - - return 1; - } - - // Map vector elements - for (size_t idx = 1; idx <= 5; idx++) { - vector_result_t map_push_res = vector_push(vector, &idx); - if (map_push_res.status != VECTOR_OK) { - printf("Error while adding elements: %s\n", map_push_res.message); - - return 1; - } - } - - sz = vector_size(vector); - - // Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25] - vector_result_t map_res = vector_map(vector, square, NULL); - if (map_res.status != VECTOR_OK) { - printf("Error while mapping vector: %s\n", map_res.message); - - return 1; - } - - printf("Squared vector: "); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t map_get_res = vector_get(vector, idx); - if (map_get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message); - - return 1; - } else { - int *val = (int*)map_get_res.value.element; - printf("%d ", *val); - } - } - - printf("\n"); - - // Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16] - vector_result_t filter_res = vector_filter(vector, is_even, NULL); - if (filter_res.status != VECTOR_OK) { - printf("Error while filtering vector: %s\n", filter_res.message); - - return 1; - } - - sz = vector_size(vector); - - printf("Filtered vector: "); - for (size_t idx = 0; idx < sz; idx++) { - vector_result_t map_get_res = vector_get(vector, idx); - if (map_get_res.status != VECTOR_OK) { - printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message); - - return 1; - } else { - int *val = (int*)map_get_res.value.element; - printf("%d ", *val); - } - } - - printf("\n"); - - // Reduce vector elements: [4, 16] -> 20 - int sum = 0; - vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL); - if (reduce_res.status != VECTOR_OK) { - printf("Error while reducing vector: %s\n", reduce_res.message); - - return 1; - } - - printf("Sum of vector: %d\n\n", sum); - - // Free vector - vector_result_t del_res = vector_destroy(vector); - if (del_res.status != VECTOR_OK) { - printf("Error while destroying the vector: %s\n", del_res.message); - - return 1; - } - - return 0; -} - -int map_usage(void) { - // Create a new map - map_result_t res = map_new(); - if (res.status != MAP_OK) { - printf("Error while creating map: %s\n", res.message); - - return 1; - } - - map_t *map = res.value.map; - - // Add some values - const int x = 0xB00B5; - const char *y = "Hello"; - - map_result_t add_res = map_add(map, "x", (void*)&x); - if (add_res.status != MAP_OK) { - printf("Error while adding elements: %s\n", add_res.message); - - return 1; - } - - add_res = map_add(map, "y", (void*)y); - if (add_res.status != MAP_OK) { - printf("Error while adding elements: %s\n", add_res.message); - - return 1; - } - - // Print size and capacity - printf("Map size (should be 2): %zu\n", map_size(map)); - printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map)); - - // Retrieve keys - map_result_t get_res = map_get(map, "x"); - if (get_res.status != MAP_OK) { - printf("Cannot retrieve map element 'x': %s\n", get_res.message); - - return 1; - } else { - const int *val = (const int*)get_res.value.element; - printf("Key 'x' contains (should be 'B00B5'): %X\n", *val); - } - - get_res = map_get(map, "y"); - if (get_res.status != MAP_OK) { - printf("Cannot retrieve map element 'y': %s\n", get_res.message); - - return 1; - } else { - const char *val = (const char*)get_res.value.element; - printf("Key 'y' contains (should be 'Hello') : %s\n\n", val); - } - - // Update key - const int new_x = 0xC0FFEE; - map_result_t up_res = map_add(map, "x", (void*)&new_x); - - up_res = map_get(map, "x"); - if (get_res.status != MAP_OK) { - printf("Cannot retrieve map element 'x': %s\n", get_res.message); - - return 1; - } else { - const int *val = (const int*)up_res.value.element; - printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val); - } - - // Remove an element - map_result_t rm_res = map_remove(map, "y"); - if (rm_res.status != MAP_OK) { - printf("Cannot remove map element 'y': %s\n", rm_res.message); - - return 1; - } else { - printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map)); - } - - // Clear the map - map_result_t clear_res = map_clear(map); - if (clear_res.status != MAP_OK) { - printf("Cannot clear map: %s\n", clear_res.message); - - return 1; - } else { - printf("Map cleared (size should be 0): %zu\n", map_size(map)); - } - - printf("\n"); - - // Delete the map - map_result_t del_res = map_destroy(map); - if (del_res.status != MAP_OK) { - printf("Error while destroying the map: %s\n", del_res.message); - - return 1; - } - - return 0; -} - -int bigint_usage(void) { - const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355"; - const char *y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937"; - const size_t x_len = strlen(x_origin); - const size_t y_len = strlen(y_origin); - const size_t large_x_size = x_len * 100 + 1; - const size_t large_y_size = y_len * 100 + 1; - - char *large_x = malloc(large_x_size); - char *large_y = malloc(large_y_size); - - if (large_x == NULL || large_y == NULL) { - printf("Error while allocating memory for strings\n"); - free(large_x); - free(large_y); - - return 1; - } - - large_x[0] = '\0'; - large_y[0] = '\0'; - - // Concatenate 100 times - for (size_t idx = 0; idx < 100; idx++) { - strcat(large_x, x_origin); - strcat(large_y, y_origin); - } - - // Create two big integers from previous strings - bigint_result_t x_res = bigint_from_string(large_x); - if (x_res.status != BIGINT_OK) { - printf("Error while creating big number: %s\n", x_res.message); - - return 1; - } - - bigint_result_t y_res = bigint_from_string(large_y); - if (x_res.status != BIGINT_OK) { - printf("Error while creating big number: %s\n", x_res.message); - - return 1; - } - - bigint_t *x = x_res.value.number; - bigint_t *y = y_res.value.number; - - // Sum two big integers - bigint_result_t sum_res = bigint_add(x, y); - if (sum_res.status != BIGINT_OK) { - printf("Error while summing two big numbers: %s\n", sum_res.message); - - return 1; - } - - bigint_t *sum = sum_res.value.number; - - // Print result - bigint_printf("Sum result = %B\n", sum); - - // Subtract two big integers - bigint_result_t diff_res = bigint_sub(x, y); - if (diff_res.status != BIGINT_OK) { - printf("Error while subtracting two big numbers: %s\n", diff_res.message); - - return 1; - } - - bigint_t *diff = diff_res.value.number; - - // Print result - bigint_printf("difference result = %B\n", diff); - - // Multiply two big integers - bigint_result_t prod_res = bigint_prod(x, y); - if (prod_res.status != BIGINT_OK) { - printf("Error while multiplying two big numbers: %s\n", prod_res.message); - - return 1; - } - - bigint_t *prod = prod_res.value.number; - - // Print result - bigint_printf("multiplication result = %B\n", prod); - - bigint_t *a = bigint_from_string(large_x).value.number; - bigint_t *b = bigint_from_string(y_origin).value.number; - - // Divide two big integers - bigint_result_t div_res = bigint_divmod(a, b); - if (div_res.status != BIGINT_OK) { - printf("Error while dividing two big numbers: %s\n", div_res.message); - - return 1; - } - - bigint_t *quotient = div_res.value.division.quotient; - bigint_t *remainder = div_res.value.division.remainder; - - // Print result - bigint_printf( - "division result = %B\ - \nmod result = %B\n", - quotient, remainder); - - // Destroy big numbers and strings - bigint_destroy(x); bigint_destroy(y); - bigint_destroy(a); bigint_destroy(b); - bigint_destroy(sum); bigint_destroy(diff); - bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder); - free(large_x); free(large_y); - - return 0; -}