diff --git a/.github/workflows/clang-build.yml b/.github/workflows/clang-build.yml new file mode 100644 index 0000000..1e7b286 --- /dev/null +++ b/.github/workflows/clang-build.yml @@ -0,0 +1,19 @@ +name: clang-build +on: [push,pull_request,workflow_dispatch] + +jobs: + clang-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Clang + run: sudo apt update && sudo apt install -y clang + + - name: Build Datum + run: | + make clean all CC=clang + + - name: Run unit tests + run: | + ./test_vector && ./test_map && ./test_bigint \ No newline at end of file diff --git a/.github/workflows/datum.yml b/.github/workflows/gcc-build.yml similarity index 59% rename from .github/workflows/datum.yml rename to .github/workflows/gcc-build.yml index 4f25837..b277691 100644 --- a/.github/workflows/datum.yml +++ b/.github/workflows/gcc-build.yml @@ -1,17 +1,16 @@ -name: datum -on: - push: - branch: [master] - workflow_dispatch: +name: gcc-build +on: [push,pull_request,workflow_dispatch] jobs: - build: + gcc-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Build Datum run: | make clean all + - name: Run unit tests run: | - ./test_vector && ./test_map \ No newline at end of file + ./test_vector && ./test_map && ./test_bigint \ No newline at end of file diff --git a/Makefile b/Makefile index 0b1e840..b0152f4 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,14 @@ TESTS_SRC = tests TARGET = usage TEST_V_TARGET = test_vector TEST_M_TARGET = test_map +TEST_B_TARGET = test_bigint -LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o +LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o PROG_OBJS = $(OBJ_DIR)/usage.o .PHONY: all clean -all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) +all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TARGET): $(PROG_OBJS) $(LIB_OBJS) $(CC) $(CFLAGS) -o $@ $^ @@ -27,6 +28,9 @@ $(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o $(TEST_M_TARGET): $(OBJ_DIR)/test_map.o $(OBJ_DIR)/map.o $(CC) $(CFLAGS) -o $@ $^ +$(TEST_B_TARGET): $(OBJ_DIR)/test_bigint.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c -o $@ $< @@ -40,4 +44,4 @@ $(OBJ_DIR): mkdir -p $(OBJ_DIR) clean: - rm -rf $(OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) + rm -rf $(OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) diff --git a/README.md b/README.md index 412afa9..95b1dc5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@

Datum

Collection of dynamic and generic data structures.
-[![](https://github.com/ceticamarco/datum/actions/workflows/datum.yml/badge.svg)](https://github.com/ceticamarco/datum/actions/workflows/datum.yml) +[![](https://github.com/ceticamarco/datum/actions/workflows/gcc-build.yml/badge.svg)](https://github.com/ceticamarco/datum/actions/workflows/gcc-build.yml) +[![](https://github.com/ceticamarco/datum/actions/workflows/clang-build.yml/badge.svg)](https://github.com/ceticamarco/datum/actions/workflows/clang-build.yml) Datum is a collection of dynamic and generic data structures implemented from scratch in C with no external dependencies beyond @@ -10,11 +11,12 @@ 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 that handles generic heterogenous data types; +- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers. ## Usage At its simplest, you can use this library as follows: -### `Vector`'s usage +### `Vector` usage ```c #include @@ -51,7 +53,7 @@ int main(void) { } ``` -### `Map`'s usage +### `Map` usage ```c #include @@ -95,6 +97,35 @@ int main(void) { } ``` +### `BigInt` usage +```c +#include "src/bigint.h" + +/* + * Compile with: gcc -O3 main.c src/bigint.c src/vector.c + * Output: 20000! = 1819206320230345134827641... + * Time: 4.01s user 0.00s system 99% cpu 4.021 total + */ +int main(void) { + const int n = 20000; + bigint_t *fact = bigint_from_int(1).value.number; + + for (int idx = 2; idx <= n; idx++) { + bigint_t *big_idx = bigint_from_int(idx).value.number; + bigint_t *partial_fact = bigint_prod(fact, big_idx).value.number; + + bigint_destroy(fact); + bigint_destroy(big_idx); + fact = partial_fact; + } + + bigint_printf("%d! = %B\n", n, fact); + + bigint_destroy(fact); + return 0; +} +``` + 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: @@ -109,12 +140,13 @@ For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs). ## Unit tests -Datum provides some unit tests for both the `Vector` and the `Map` data types. To run them, you can issue the following commands: +Datum provides some unit tests for `Vector`, `Map` and `BigInt`. To run them, you can issue the following commands: ```sh $ make clean all $ ./test_vector $ ./test_map +$ ./test_bigint ``` ## License diff --git a/docs/README.md b/docs/README.md index 6483877..f7e6320 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,5 +7,5 @@ At the time being, this documentation includes the following pages: - [vector.md](vector.md): vector documentation; - [map.md](map.md): map documentation; -- [sort.md](sort.md): how to use the `vector_sort` method. - +- [sort.md](sort.md): how to use the `vector_sort` method; +- [bigint.md](bigint.md): bigint documentation. diff --git a/docs/bigint.md b/docs/bigint.md new file mode 100644 index 0000000..3f07731 --- /dev/null +++ b/docs/bigint.md @@ -0,0 +1,92 @@ +# BigInt Technical Details +In this document you can find a quick overview of the technical +aspects (internal design, memory layout, etc.) of the `BigInt` data structure. + +`BigInt` is a data type for arbitrary precision arithmetic that supports addition, +subtraction, multiplication, division and modulo operations on signed integers of unlimited size. Internally, it uses +the `Vector` data structure to represent big numbers using the following layout: + +``` +Number: 2485795518678991171206065 +Internally: [ 171206065, 518678991, 2485795 ] + / | \ + / | \ + digit[0] digit[1] digit[2] + (LSB) (MSB) +``` + +That is, each element of the vector stores 9 digits in base $10^9$ using +**little-endian order**. Each such digits can therefore store values from `0` up to +`999,999,999`. + +This scheme maps to the following structure: + +```c +typedef struct { + vector_t *digits; + bool is_negative; +} bigint_t; +``` + +where the `digits` array stores the representation in base $10^9$ of the big integer +and the boolean `is_negative` variable denotes its sign. + +The `BigInt` data structure supports the following methods: + +- `bigint_result_t bigint_from_int(value)`: create a big integer from a primitive `int` type; +- `bigint_result_t bigint_from_string(string_num)`: create a big integer from a C string; +- `bigint_result_t bigint_to_string(number)`: convert a big integer to a C string; +- `bigint_result_t bigint_clone(number)`: clone a big integer; +- `bigint_result_t bigint_compare(x, y)`: compare two big integers, returning either `-1`, `0` or `1` if the first is less than, equal than or greater than the second, respectively; +- `bigint_result_t bigint_add(x, y)`: add two big integers together in $\mathcal{O}(n)$; +- `bigint_result_t bigint_sub(x, y)`: subtract two big integers in $\mathcal{O}(n)$; +- `bigint_result_t bigint_prod(x, y)`: multiply two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$; +- `bigint_result_t bigint_divmod(x, y)`: divide two big integers using *long division* algorithm in $\mathcal{O}(n^2)$, returning both the quotient and the remainder; +- `bigint_result_t bigint_mod(x, y)`: computes modulo of two big integers using *long division* algorithm in $\mathcal{O}(n^2)$; +- `bigint_result_t bigint_destroy(number)`: delete the big number; +- `bigint_result_t bigint_printf(format, ...)`: `printf` wrapper that introduces the `%B` placeholder to print big numbers. It supports variadic parameters. + +As you can see by the previous function signatures, methods that operate on the +`BigInt` data type return a custom type called `bigint_result_t` which is defined as +follows: + +```c +typedef enum { + BIGINT_OK = 0x0, + BIGINT_ERR_ALLOCATE, + BIGINT_ERR_DIV_BY_ZERO, + BIGINT_ERR_INVALID +} bigint_status_t; + +typedef struct { + bigint_t *quotient; + bigint_t *remainder; +} div_result_t; + +typedef struct { + bigint_status_t status; + uint8_t message[RESULT_MSG_SIZE]; + union { + bigint_t *number; + div_result_t division; + int8_t compare_status; + char *string_num; + } value; +} bigint_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 == 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 in 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: + +- `number`: result of arithmetical, cloning and creating functions; +- `division`: result of `bigint_divmod`; +- `compare_status`: result of `bigint_compare`; +- `string_num`: result of `bigint_to_string`. + diff --git a/docs/map.md b/docs/map.md index f061d10..02471b1 100644 --- a/docs/map.md +++ b/docs/map.md @@ -71,5 +71,5 @@ 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 +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 in the first part of the README. \ No newline at end of file diff --git a/docs/sort.md b/docs/sort.md index a76c3c3..5f2f900 100644 --- a/docs/sort.md +++ b/docs/sort.md @@ -42,7 +42,7 @@ vector_order_t cmp_int_desc(const void *x, const void *y) { } /* - * Compile with: gcc main.c src/vector.h + * Compile with: gcc main.c src/vector.c * Output: Before sorting: -8 20 -10 125 34 9 * After sorting (ascending order): -10 -8 9 20 34 125 * After sorting (descending order): 125 34 20 9 -8 -10 @@ -55,9 +55,11 @@ int main(void) { vector_push(v, &values[idx]); } + const size_t sz = vector_size(v); + // Print unsorted array printf("Before sorting: "); - for (size_t idx = 0; idx < vector_size(v); idx++) { + for (size_t idx = 0; idx < sz; idx++) { printf("%d ", *(int*)vector_get(v, idx).value.element); } @@ -66,7 +68,7 @@ int main(void) { // Print sorted array printf("\nAfter sorting (ascending order): "); - for (size_t idx = 0; idx < vector_size(v); idx++) { + for (size_t idx = 0; idx < sz; idx++) { printf("%d ", *(int*)vector_get(v, idx).value.element); } @@ -75,7 +77,7 @@ int main(void) { // Print sorted array printf("\nAfter sorting (descending order): "); - for (size_t idx = 0; idx < vector_size(v); idx++) { + for (size_t idx = 0; idx < sz; idx++) { printf("%d ", *(int*)vector_get(v, idx).value.element); } @@ -124,7 +126,7 @@ vector_order_t cmp_person_by_name(const void *x, const void *y) { } /* - * Compile with: gcc main.c src/vector.h + * Compile with: gcc main.c src/vector.c * Output: Sort by age: * Name: Marco, Age: 25 * Name: Alice, Age: 28 @@ -149,9 +151,11 @@ int main(void) { // Sort array by age vector_sort(employees, cmp_person_by_age); + const size_t sz = vector_size(employees); + // Print sorted array printf("Sort by age:\n"); - for (size_t idx = 0; idx < vector_size(employees); idx++) { + for (size_t idx = 0; idx < sz; idx++) { Employee *p = (Employee*)vector_get(employees, idx).value.element; printf("Name: %s, Age: %d\n", p->name, p->age); } @@ -161,7 +165,7 @@ int main(void) { // Print sorted array printf("\nSort by name:\n"); - for (size_t idx = 0; idx < vector_size(employees); idx++) { + for (size_t idx = 0; idx < sz; idx++) { Employee *p = (Employee*)vector_get(employees, idx).value.element; printf("Name: %s, Age: %d\n", p->name, p->age); } diff --git a/docs/vector.md b/docs/vector.md index 477b97a..a271eef 100644 --- a/docs/vector.md +++ b/docs/vector.md @@ -64,7 +64,7 @@ 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 == 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 in the first part of the README. +ignore the return value (if you're brave enough :D) as illustrated in the first part of the README. The documentation for the `vector_sort(map, cmp)` method can be found in [the following document](/docs/sort.md). \ No newline at end of file diff --git a/src/bigint.c b/src/bigint.c new file mode 100644 index 0000000..e7835b5 --- /dev/null +++ b/src/bigint.c @@ -0,0 +1,1863 @@ +#define SET_MSG(result, msg) \ + do { \ + snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \ + } while (0) + +#define COPY_MSG(result, msg) \ + do { \ + strncpy((char *)(result).message, (const char *)(msg), RESULT_MSG_SIZE - 1); \ + (result).message[RESULT_MSG_SIZE - 1] = '\0'; \ + } while (0) + +#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9') + +#include +#include +#include +#include + +#include "bigint.h" +#include "vector.h" + +// Internal methods +static bigint_result_t bigint_trim_zeros(bigint_t *number); +static bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y); +static bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y); +static bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y); +static bigint_result_t bigint_shift_left(const bigint_t *num, size_t n); +static bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, bigint_t **low); +static bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y); +static bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y); +static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y); + +/** + * bigint_from_int + * @value: an integer value + * + * Takes an integer and convert it to a big integer + * + * Returns a big_int_result_t data type containing a new big integer + */ +bigint_result_t bigint_from_int(long long value) { + bigint_result_t result = {0}; + + bigint_t *number = malloc(sizeof(bigint_t)); + if (number == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(4, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(number); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + number->digits = vec_res.value.vector; + number->is_negative = (value < 0); + + // Discard the sign since we don't need it anymore + unsigned long long abs_val = value < 0 ? -(unsigned long long)value : (unsigned long long)value; + + if(abs_val == 0) { + int zero = 0; + vector_result_t push_res = vector_push(number->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy(number->digits); + free(number); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } else { + while (abs_val != 0) { + int digit = abs_val % BIGINT_BASE; + vector_result_t push_res = vector_push(number->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(number->digits); + free(number); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + + abs_val /= BIGINT_BASE; + } + } + + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully created"); + result.value.number = number; + + return result; +} + +/** + * bigint_from_string + * @string_num: an array of chars representing a number + * + * Takes a string containing a number and convert it to big integer + * + * Returns a bigint_result_t data type containing a new big integer + */ +bigint_result_t bigint_from_string(const char *string_num) { + bigint_result_t result = {0}; + + if (string_num == NULL || *string_num == 0) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid string"); + + return result; + } + + bigint_t *number = malloc(sizeof(bigint_t)); + if (number == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(4, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(number); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + number->digits = vec_res.value.vector; + + number->is_negative = false; + if (*string_num == '-') { + number->is_negative = true; + string_num++; + } else if (*string_num == '+') { + string_num++; + } + + // Check whether the integer is valid or not + if (*string_num == '\0') { + vector_destroy(number->digits); + free(number); + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Invalid integer"); + + return result; + } + + // Check whether characters are digits + for (const char *p = string_num; *p; ++p) { + if (!IS_DIGIT((unsigned char)*p)) { + vector_destroy(number->digits); + free(number); + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid integer"); + + return result; + } + } + + // Skip leading zeros + while (*string_num == '0' && *(string_num + 1) != '\0') { + string_num++; + } + + const size_t number_len = strlen(string_num); + + // Process digits from right to left by chunks of the representation base + for (int i = number_len; i > 0; i -= BIGINT_BASE_DIGITS) { + const int start = (i - BIGINT_BASE_DIGITS > 0) ? i - BIGINT_BASE_DIGITS : 0; + const int chunk_len = (i - start); + + int digit = 0; + for (int j = 0; j < chunk_len; j++) { + digit = digit * 10 + (string_num[start + j] - '0'); + } + + vector_result_t push_res = vector_push(number->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(number->digits); + free(number); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, push_res.message); + + return result; + } + } + + bigint_result_t trim_res = bigint_trim_zeros(number); + if (trim_res.status != BIGINT_OK) { + vector_destroy(number->digits); + free(number); + + return trim_res; + } + + result.value.number = number; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully created"); + + return result; +} + +/** + * bigint_to_string + * @number: a valid non-null big number + * + * Converts a big integer to a C string + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_to_string(const bigint_t *number) { + bigint_result_t result = {0}; + + if (number == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + const size_t size = vector_size(number->digits); + const size_t max_len = (size * BIGINT_BASE_DIGITS) + 2; // +2 for sign and terminator + + char *str = malloc(max_len); + if (str == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for string"); + + return result; + } + + char *ptr = str; + if (number->is_negative) { + *ptr++ = '-'; + } + + // Print MSB without leading zeros + vector_result_t msb_res = vector_get(number->digits, size - 1); + if (msb_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, msb_res.message); + + return result; + } + + int *msb = (int*)msb_res.value.element; + ptr += sprintf(ptr, "%d", *msb); + + // Print remaining digits with leading zeros + for (int idx = size - 2; idx >= 0; idx--) { + vector_result_t digit_res = vector_get(number->digits, idx); + if (digit_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, digit_res.message); + + return result; + } + + int *digit = (int*)digit_res.value.element; + ptr += sprintf(ptr, "%09d", *digit); + } + + result.value.string_num = str; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully converted"); + + return result; +} + +/** + * bigint_clone + * @number: a valid non-null big integer + * + * Clones a big integer + * + * Returns a bigint_result_t data type containing the new big integer + */ +bigint_result_t bigint_clone(const bigint_t *number) { + bigint_result_t result = {0}; + + if (number == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + bigint_t *cloned = malloc(sizeof(bigint_t)); + if (cloned == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(number->digits), sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(cloned); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + cloned->digits = vec_res.value.vector; + cloned->is_negative = number->is_negative; + + // Copy digits + const size_t sz = vector_size(number->digits); + for (size_t idx = 0; idx < sz; idx++) { + vector_result_t get_res = vector_get(number->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(cloned->digits); + free(cloned); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + + vector_result_t push_res = vector_push(cloned->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(cloned->digits); + free(cloned); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + result.value.number = cloned; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully cloned"); + + return result; +} + +/** + * bigint_trim_zeros + * @number: a non-null big integer + * + * Helper function to remove leading zeros + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_trim_zeros(bigint_t *number) { + bigint_result_t result = {0}; + + size_t number_len = vector_size(number->digits); + + while (number_len > 1) { + vector_result_t get_res = vector_get(number->digits, number_len - 1); + if (get_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *last = (int*)get_res.value.element; + if (*last != 0) { + break; + } + + vector_result_t pop_res = vector_pop(number->digits); + if (pop_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + number_len--; + } + + if (number_len == 1) { + vector_result_t get_res = vector_get(number->digits, number_len - 1); + if (get_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *first = (int*)get_res.value.element; + if (*first == 0) { + number->is_negative = false; + } + } + + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully trimmed"); + + return result; + +} + +/** + * bigint_compare_abs + * @x: a non-null big integer + * @y: a non-null big integer + * + * Compares absolute value of two big integers + * if |x| < |y| => -1 + * if |x| == |y| => 0 + * if |x| > |y| => 1 + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + if (x_size != y_size) { + result.value.compare_status = (x_size > y_size) ? 1 : -1; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + + // Start to compare from the MSB + for (int idx = (int)(x_size - 1); idx >= 0; idx--) { + vector_result_t x_get = vector_get(x->digits, idx); + if (x_get.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, x_get.message); + + return result; + } + + vector_result_t y_get = vector_get(y->digits, idx); + if (y_get.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_get.message); + + return result; + } + + int *x_digit = (int*)x_get.value.element; + int *y_digit = (int*)y_get.value.element; + + if (*x_digit != *y_digit) { + result.value.compare_status = (*x_digit > *y_digit) ? 1 : -1; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + } + + result.value.compare_status = 0; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; +} + +/** + * bigint_compare + * @x: a valid non-null big integer + * @y: a valid non-null big integer + * + * Compares two big integers + * if x < y => -1 + * if x == y => 0 + * if x > y => 1 + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + if (x->is_negative != y->is_negative) { + result.value.compare_status = x->is_negative ? -1 : 1; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + + bigint_result_t cmp_res = bigint_compare_abs(x, y); + if (cmp_res.status != BIGINT_OK) { + return cmp_res; + } + + const int8_t abs_cmp = cmp_res.value.compare_status; + + result.value.compare_status = x->is_negative ? -abs_cmp : abs_cmp; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; +} + +/** + * bigint_add_abs + * @x: a non-null big integer + * @y: a non-null big integer + * + * Adds two absolute values together + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_t *sum = malloc(sizeof(bigint_t)); + if (sum == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Cannot allocate memory for big integer"); + + return result; + } + + const size_t max_size = vector_size(x->digits) > vector_size(y->digits) ? + vector_size(x->digits) : vector_size(y->digits); + + vector_result_t vec_res = vector_new(max_size + 1, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + sum->digits = vec_res.value.vector; + sum->is_negative = false; + + long long carry = 0; + size_t idx = 0; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + while (idx < x_size || idx < y_size || carry) { + long long partial_sum = carry; + + if (idx < x_size) { + vector_result_t get_res = vector_get(x->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *x_digit = (int*)get_res.value.element; + partial_sum += *x_digit; + } + + if (idx < y_size) { + vector_result_t get_res = vector_get(y->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *y_digit = (int*)get_res.value.element; + partial_sum += *y_digit; + } + + int digit = partial_sum % BIGINT_BASE; + carry = partial_sum / BIGINT_BASE; + + vector_result_t push_res = vector_push(sum->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + idx++; + } + + bigint_result_t trim_res = bigint_trim_zeros(sum); + if (trim_res.status != BIGINT_OK) { + vector_destroy(sum->digits); + free(sum); + + return trim_res; + } + + result.value.number = sum; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully added"); + + return result; +} + +/** + * bigint_sub_abs + * @x: a non-null big integer + * @y: a non-null big integer + * + * Subtracts two absolute values assuming that |x| >= |y| + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_t *difference = malloc(sizeof(bigint_t)); + if (difference == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Cannot allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + difference->digits = vec_res.value.vector; + difference->is_negative = false; + + long long borrow = 0; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + for (size_t idx = 0; idx < x_size; idx++) { + vector_result_t x_get_res = vector_get(x->digits, idx); + if (x_get_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, x_get_res.message); + + return result; + } + + int *x_digit = (int*)x_get_res.value.element; + long long partial_difference = *x_digit - borrow; + + if (idx < y_size) { + vector_result_t y_get_res = vector_get(y->digits, idx); + if (y_get_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_get_res.message); + + return result; + } + + int *y_digit = (int*)y_get_res.value.element; + partial_difference -= *y_digit; + } + + if (partial_difference < 0) { + partial_difference += BIGINT_BASE; + borrow = 1; + } else { + borrow = 0; + } + + int digit = partial_difference; + vector_result_t push_res = vector_push(difference->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + bigint_result_t trim_res = bigint_trim_zeros(difference); + if (trim_res.status != BIGINT_OK) { + vector_destroy(difference->digits); + free(difference); + + return trim_res; + } + + result.value.number = difference; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully subtracted"); + + return result; +} + +/** + * bigint_add + * @x: a non-null big integer + * @y: a non-null big integer + * + * Adds two big integers together + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integers"); + + return result; + } + + // Same sign: add absolute values + if (x->is_negative == y->is_negative) { + bigint_result_t sum_res = bigint_add_abs(x, y); + if (sum_res.status != BIGINT_OK) { + return sum_res; + } + + bigint_t *sum = sum_res.value.number; + if (sum) { + sum->is_negative = x->is_negative; + } + + result.value.number = sum; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully added"); + + return result; + } + + // Different signs: subtract smaller from larger + bigint_result_t cmp_res = bigint_compare_abs(x, y); + if (cmp_res.status != BIGINT_OK) { + return cmp_res; + } + + const int8_t cmp = cmp_res.value.compare_status; + if (cmp == 0) { + return bigint_from_int(0); + } else if (cmp > 0) { + bigint_result_t sub_res = bigint_sub_abs(x, y); + if (sub_res.status != BIGINT_OK) { + return sub_res; + } + + bigint_t *sub = sub_res.value.number; + if (sub) { + sub->is_negative = x->is_negative; + } + + result.value.number = sub; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully added"); + } else { + bigint_result_t sub_res = bigint_sub_abs(y, x); + if (sub_res.status != BIGINT_OK) { + return sub_res; + } + + bigint_t *sub = sub_res.value.number; + if (sub) { + sub->is_negative = y->is_negative; + } + + result.value.number = sub; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully added"); + } + + return result; +} + +/** + * bigint_sub + * @x: a non-null big integer + * @y: a non-null big integer + * + * Subtracts two big integers together + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integers"); + + return result; + } + + /* To subtract two big integers we can consider + * the following equivalence: + * x - y = x + (-y) + */ + bigint_result_t neg_y_res = bigint_clone(y); + if (neg_y_res.status != BIGINT_OK) { + return neg_y_res; + } + + bigint_t *neg_y = neg_y_res.value.number; + neg_y->is_negative = !neg_y->is_negative; + + bigint_result_t difference_res = bigint_add(x, neg_y); + if (difference_res.status != BIGINT_OK) { + bigint_destroy(neg_y); + + return difference_res; + } + + bigint_destroy(neg_y); + bigint_t *difference = difference_res.value.number; + + result.value.number = difference; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully subtracted"); + + return result; +} + +/** + * bigint_prod + * @x: a non-null big integer + * @y: a non-null big integer + * + * Perform a multiplication between @a and @b + * using Karatsuba's algorithm + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integers"); + + return result; + } + + bigint_result_t product_res = bigint_karatsuba(x, y); + if (product_res.status != BIGINT_OK) { + return product_res; + } + + bigint_t *product = product_res.value.number; + product->is_negative = (x->is_negative != y->is_negative); + + bigint_result_t trim_res = bigint_trim_zeros(product); + if (trim_res.status != BIGINT_OK) { + bigint_destroy(product); + + return trim_res; + } + + result.value.number = product; + result.status = BIGINT_OK; + SET_MSG(result, "Product between big integers was successful"); + + return result; +} + + + +/** + * bigint_divmod + * @x: a valid non-null big integer + * @y: a valid non-null big integer + * + * Computes division with remainder + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + bigint_result_t tmp_res = {0}; + + // Intermediate results + bigint_t *quotient = NULL; + bigint_t *y_times_q = NULL; + bigint_t *remainder = NULL; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big numbers"); + + return result; + } + + // Check for division by zero + const size_t y_size = vector_size(y->digits); + if (y_size == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Division by zero"); + + return result; + } + + if (y_size == 1) { + vector_result_t y_val_res = vector_get(y->digits, 0); + if (y_val_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_val_res.message); + + return result; + } + + int *y_val = (int*)y_val_res.value.element; + if (*y_val == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Division by zero"); + + return result; + } + } + + // |x| < |y| then quotient is 0 and remainder is x + tmp_res = bigint_compare_abs(x, y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + if (tmp_res.value.compare_status < 0) { + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + quotient = tmp_res.value.number; + + tmp_res = bigint_clone(x); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + remainder = tmp_res.value.number; + + result.value.division.quotient = quotient; + result.value.division.remainder = remainder; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + tmp_res = bigint_div(x, y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + quotient = tmp_res.value.number; + + // Compute r = x - y * q + tmp_res = bigint_prod(y, quotient); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + y_times_q = tmp_res.value.number; + + tmp_res = bigint_sub(x, y_times_q); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + remainder = tmp_res.value.number; + + // Ensure that remainder has correct sign (i.e., same as dividend x) + // In C-style division, sign(remainder) = sign(dividend) + remainder->is_negative = x->is_negative; + + tmp_res = bigint_trim_zeros(remainder); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + result.value.division.quotient = quotient; + result.value.division.remainder = remainder; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + bigint_destroy(y_times_q); + + return result; + +cleanup: + if (quotient) { bigint_destroy(quotient); } + if (y_times_q) { bigint_destroy(y_times_q); } + if (remainder) { bigint_destroy(remainder); } + + return result; +} + +/** + * bigint_mod + * @x: a valid non-null big integer + * @y: a valid non-null big integer + * + * Computes @x mod @y + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_mod(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big numbers"); + + return result; + } + + bigint_result_t div_res = bigint_divmod(x, y); + if (div_res.status != BIGINT_OK) { return div_res; } + + bigint_t* const quotient = div_res.value.division.quotient; + bigint_t* const remainder = div_res.value.division.remainder; + + // Discard quotient + bigint_destroy(quotient); + + result.value.number = remainder; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; +} + +/** + * bigint_shift_left + * @num: a non-null big integer + * @n: number of digits to shift + * + * Shifts left by @n digits (i.e., multiply by BASE^n) + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { + bigint_result_t result = {0}; + + if (n == 0) { + return bigint_clone(num); + } + + bigint_t *shifted = malloc(sizeof(bigint_t)); + if (shifted == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(num->digits) + n, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(shifted); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + shifted->digits = vec_res.value.vector; + shifted->is_negative = num->is_negative; + + // Add 'n' zeros by starting from the LSB + int zero = 0; + for (size_t idx = 0; idx < n; idx++) { + vector_result_t push_res = vector_push(shifted->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // Copy back original digits + const size_t num_size = vector_size(num->digits); + for (size_t idx = 0; idx < num_size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push(shifted->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + result.value.number = shifted; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer shifted successfully"); + + return result; +} + +/** + * bigint_split + * @num: a non-null big integers + * @m: the pivot/position where to split + * @high: digits \in [0, m) + * @low: digits \in [m, size) + * + * Splits number into @high and @low parts at position @m + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, bigint_t **low) { + bigint_result_t result = {0}; + + const size_t size = vector_size(num->digits); + + // Low part: digits \in [0, m) + *low = malloc(sizeof(bigint_t)); + if (*low == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t low_res = vector_new(m ? m : 1, sizeof(int)); + if (low_res.status != VECTOR_OK) { + free(*low); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, low_res.message); + + return result; + } + + (*low)->digits = low_res.value.vector; + (*low)->is_negative = false; + + for (size_t idx = 0; idx < m && idx < size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push((*low)->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + if (vector_size((*low)->digits) == 0) { + int zero = 0; + vector_result_t push_res = vector_push((*low)->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // First pass of zero trimming + bigint_result_t first_trim_res = bigint_trim_zeros(*low); + if (first_trim_res.status != BIGINT_OK) { + vector_destroy((*low)->digits); + free(*low); + + return first_trim_res; + } + + // High part: digits \in [m, size) + *high = malloc(sizeof(bigint_t)); + if (*high == NULL) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t high_res = vector_new(size > m ? (size - m) : 1, sizeof(int)); + if (high_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, low_res.message); + + return result; + } + + (*high)->digits = high_res.value.vector; + (*high)->is_negative = false; + + if (size > m) { + for (size_t idx = m; idx < size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push((*high)->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + } else { + int zero = 0; + vector_result_t push_res = vector_push((*high)->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // Second pass of zero trimming + bigint_result_t second_trim_res = bigint_trim_zeros(*high); + if (second_trim_res.status != BIGINT_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + return second_trim_res; + } + + result.status = BIGINT_OK; + SET_MSG(result, "Big number successfully splitted"); + + return result; +} + +/** + * bigint_karatsuba_base + * @x: a non-null big integer + * @y: a non-null big integer + * + * Base case of the Karatsuba recursive algorithm + * which uses a "grade school" multiplication. + * Its complexity is O(n^2) + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_result_t prod_res = bigint_from_int(0); + if (prod_res.status != BIGINT_OK) { + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, prod_res.message); + + return result; + } + + bigint_t *product = prod_res.value.number; + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + for (size_t i = 0; i < x_size; i++) { + long long carry = 0; + + vector_result_t get_res = vector_get(x->digits, i); + if (get_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *x_digit = (int*)get_res.value.element; + for (size_t j = 0; j < y_size || carry; j++) { + int *y_digit = NULL; + int *curr = NULL; + + if (j < y_size) { + vector_result_t y_res = vector_get(y->digits, j); + if (y_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_res.message); + + return result; + } + + y_digit = (int*)y_res.value.element; + } + + if ((i + j) < vector_size(product->digits)) { + vector_result_t curr_res = vector_get(product->digits, i + j); + if (curr_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, curr_res.message); + + return result; + } + + curr = (int*)curr_res.value.element; + } + + long long partial_prod = carry; + if (curr) { partial_prod += *curr; } + if (y_digit) { partial_prod += (long long)(*x_digit) * (*y_digit); } + + int new_digit =(int)(partial_prod % BIGINT_BASE); + carry = partial_prod / BIGINT_BASE; + + if (curr) { + vector_result_t set_res = vector_set(product->digits, i + j, &new_digit); + if (set_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, set_res.message); + + return result; + } + } else { + vector_result_t push_res = vector_push(product->digits, &new_digit); + if (push_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + } + } + + bigint_result_t trim_res = bigint_trim_zeros(product); + if (trim_res.status != BIGINT_OK) { + bigint_destroy(product); + + return trim_res; + } + + result.value.number = product; + result.status = BIGINT_OK; + SET_MSG(result, "Product between big integers was successful"); + + return result; +} + +/** + * bigint_karatusba + * @x: a non-null big integer + * @y: a non-null big integer + * + * Perform a multiplication using Karatsuba recursive algorithm + * in O(n^{\log_2 3}) \approx O(n^{1.585}) + */ +bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + bigint_result_t tmp_res = {0}; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integers"); + + return result; + } + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + // Base case using "grade school" quadratic algorithm + if (x_size <= 32 || y_size <= 32) { + return bigint_karatsuba_base(x, y); + } + + // Split the big integer at approximately half the size of the larger number + const size_t pivot = (x_size > y_size ? x_size : y_size) / 2; + + // Results of each step + bigint_t *x1 = NULL, *x0 = NULL; + bigint_t *y1 = NULL, *y0 = NULL; + bigint_t *z0 = NULL, *z2 = NULL; + bigint_t *x_sum = NULL, *y_sum = NULL; + bigint_t *z1_temp = NULL, *z1_sub1 = NULL, *z1 = NULL; + bigint_t *z2_shifted = NULL, *z1_shifted = NULL; + bigint_t *temp = NULL, *product = NULL; + + // Split x = x1 * BASE^pivot + x0 + tmp_res = bigint_split(x, pivot, &x1, &x0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // Split y = y1 * BASE^pivot + y0 + tmp_res = bigint_split(y, pivot, &y1, &y0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // Perform karatsuba's trick + tmp_res = bigint_karatsuba(x0, y0); // z0 = x0 * y0 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z0 = tmp_res.value.number; + + tmp_res = bigint_karatsuba(x1, y1); // z2 = x1 * y1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z2 = tmp_res.value.number; + + // z1 = (x0 + x1) * (y0 + y1) - z0 - z2 + tmp_res = bigint_add(x0, x1); // x_sum = x0 + x1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + x_sum = tmp_res.value.number; + + tmp_res = bigint_add(y0, y1); // y_sum = y0 + y1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + y_sum = tmp_res.value.number; + + tmp_res = bigint_karatsuba(x_sum, y_sum); // z1_temp = (x0 + x1) * (y0 + y1) + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_temp = tmp_res.value.number; + + tmp_res = bigint_sub(z1_temp, z0); // z1_sub1 = z1_temp - z0 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_sub1 = tmp_res.value.number; + + tmp_res = bigint_sub(z1_sub1, z2); // z1 = z1_sub1 - z2 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1 = tmp_res.value.number; + + tmp_res = bigint_shift_left(z2, 2 * pivot); // z2_shifted = z2 << (2 * pivot) + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z2_shifted = tmp_res.value.number; + + tmp_res = bigint_shift_left(z1, pivot); // z1_shifted = z1 << pivot + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_shifted = tmp_res.value.number; + + tmp_res = bigint_add(z2_shifted, z1_shifted); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + temp = tmp_res.value.number; + + tmp_res = bigint_add(temp, z0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + product = tmp_res.value.number; + + // Destroy intermediate allocations except for the product + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0); bigint_destroy(z2); + bigint_destroy(x_sum); bigint_destroy(y_sum); + bigint_destroy(z1_temp); bigint_destroy(z1_sub1); + bigint_destroy(z1); bigint_destroy(z2_shifted); + bigint_destroy(z1_shifted); bigint_destroy(temp); + + result.value.number = product; + result.status = BIGINT_OK; + SET_MSG(result, "Product between big integers was successful"); + +cleanup: // Destroy intermediate allocations on error + if (x1) { bigint_destroy(x1); } + if (x0) { bigint_destroy(x0); } + if (y1) { bigint_destroy(y1); } + if (y0) { bigint_destroy(y0); } + if (z0) { bigint_destroy(z0); } + if (z2) { bigint_destroy(z2); } + if (x_sum) { bigint_destroy(x_sum); } + if (y_sum) { bigint_destroy(y_sum); } + if (z1_temp) { bigint_destroy(z1_temp); } + if (z1_sub1) { bigint_destroy(z1_sub1); } + if (z1) { bigint_destroy(z1); } + if (z2_shifted) { bigint_destroy(z2_shifted); } + if (z1_shifted) { bigint_destroy(z1_shifted); } + if (temp) { bigint_destroy(temp); } + if (product) { bigint_destroy(product); } + + return result; +} + +/** + * bigint_dev + * @x: a valid non-null big integer (dividend) + * @y: a valid non-null big integer (divisor) + * + * Computes division using long division algorithm in O(n^2) + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + bigint_result_t tmp_res = {0}; + + bigint_t *quotient = NULL; + bigint_t *remainder = NULL; + bigint_t *abs_y = NULL; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big numbers"); + + return result; + } + + // Check for division by zero + const size_t y_size = vector_size(y->digits); + if (y_size == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Cannot divide by zero"); + + return result; + } + + if (y_size == 1) { + vector_result_t y_val_res = vector_get(y->digits, 0); + if (y_val_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_val_res.message); + + return result; + } + + int *y_val = (int*)y_val_res.value.element; + if (*y_val == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Cannot divide by zero"); + + return result; + } + } + + // If |x| < |y| then result is zero + tmp_res = bigint_compare_abs(x, y); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + + if (tmp_res.value.compare_status < 0) { + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + + result.value.number = tmp_res.value.number; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + // Initialize quotient and remainder + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + quotient = tmp_res.value.number; + + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { bigint_destroy(quotient); return tmp_res; } + remainder = tmp_res.value.number; + + // Create absolute value of y for later comparisons + tmp_res = bigint_clone(y); + if (tmp_res.status != BIGINT_OK) { + bigint_destroy(quotient); + bigint_destroy(remainder); + + return tmp_res; + } + + abs_y = tmp_res.value.number; + abs_y->is_negative = false; + + // Long division algorithm applied from MSB to LSB + const size_t x_size = vector_size(x->digits); + for (int idx = (int)x_size - 1; idx >= 0; idx--) { + // Shift remainder left by one base digit (multiplication by BASE) + tmp_res = bigint_shift_left(remainder, 1); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_t *shifted_remainder = tmp_res.value.number; + bigint_destroy(remainder); + remainder = shifted_remainder; + + // Add current digit of 'x' to the least significant position of remainder + vector_result_t digit_res = vector_get(x->digits, idx); + if (digit_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, digit_res.message); + + goto cleanup; + } + + int *x_digit = (int*)digit_res.value.element; + + vector_result_t set_res = vector_set(remainder->digits, 0, x_digit); + if (set_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, set_res.message); + + goto cleanup; + } + + tmp_res = bigint_trim_zeros(remainder); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // COunt how many times 'y' fits into current remainder + size_t count = 0; + while (1) { + tmp_res = bigint_compare_abs(remainder, abs_y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + if (tmp_res.value.compare_status < 0) { break; } // remainder < abs_y + + // remainder = remainder - abs_y + tmp_res = bigint_sub_abs(remainder, abs_y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_t *new_remainder = tmp_res.value.number; + bigint_destroy(remainder); + remainder = new_remainder; + count++; + } + + // Add count to quotient digits + vector_result_t push_res = vector_push(quotient->digits, &count); + if (push_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + goto cleanup; + } + } + + // Reverse quotient digits + const size_t q_size = vector_size(quotient->digits); + for (size_t idx = 0; idx < q_size / 2; idx++) { + vector_result_t left_res = vector_get(quotient->digits, idx); + vector_result_t right_res = vector_get(quotient->digits, q_size - 1 - idx); + + if (left_res.status != VECTOR_OK || right_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Failed to access vector elements"); + + goto cleanup; + } + + int *left = (int*)left_res.value.element; + int *right = (int*)right_res.value.element; + int temp = *left; + + vector_set(quotient->digits, idx, right); + vector_set(quotient->digits, q_size - 1 - idx, &temp); + } + + quotient->is_negative = (x->is_negative != y->is_negative); + + tmp_res = bigint_trim_zeros(quotient); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_destroy(remainder); + bigint_destroy(abs_y); + + result.value.number = quotient; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + +cleanup: + if (quotient) { bigint_destroy(quotient); } + if (remainder) { bigint_destroy(remainder); } + if (abs_y) { bigint_destroy(abs_y); } + + return result; +} + +/** + * bigint_destroy + * @number: a valid non-null big integer + * + * Deletes the big integer from the memory + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_destroy(bigint_t *number) { + bigint_result_t result = {0}; + + if (number == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + vector_destroy(number->digits); + free(number); + + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully deleted"); + + return result; +} + +/** + * bigint_printf + * @format: format string + * @...: variadic arguments + * + * Prints a bigint integer to stdout using the custom '%B' placeholder + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_printf(const char *format, ...) { + bigint_result_t result = {0}; + + if (format == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid format string"); + + return result; + } + + va_list args; + va_start(args, format); + + // Process string char by char + for (const char *p = format; *p != '\0'; p++) { + if (*p == '%' && *(p + 1) == 'B') { + // Process a big number + bigint_t *num = va_arg(args, bigint_t*); + if (num == NULL) { + printf(""); + } else { + bigint_result_t num_str_res = bigint_to_string(num); + if (num_str_res.status != BIGINT_OK) { + va_end(args); + return num_str_res; + } + + char* const number_str = num_str_res.value.string_num; + printf("%s", number_str); + free(number_str); + } + p++; + } else if (*p == '%' && *(p + 1) != '%') { + // Handle common printf placeholders + p++; + char placeholder = *p; + + switch (placeholder) { + case 'd': + case 'i': { + int val = va_arg(args, int); + printf("%d", val); + + break; + } + case 'u': { + unsigned int val = va_arg(args, unsigned int); + printf("%u", val); + + break; + } + case 'l': { + if (*(p + 1) == 'd' || *(p + 1) == 'i') { + long val = va_arg(args, long); + printf("%ld", val); + p++; + } else if (*(p + 1) == 'l' && (*(p + 2) == 'd' || *(p + 2) == 'i')) { + long long val = va_arg(args, long long); + printf("%lld", val); + p += 2; + } else if (*(p + 1) == 'u') { + unsigned long val = va_arg(args, unsigned long); + printf("%lu", val); + p++; + } + break; + } + case 's': { + char *val = va_arg(args, char*); + printf("%s", val ? val : ""); + break; + } + case 'c': { + int val = va_arg(args, int); + printf("%c", val); + break; + } + case 'f': { + double val = va_arg(args, double); + printf("%f", val); + break; + } + case 'p': { + void *val = va_arg(args, void*); + printf("%p", val); + break; + } + case 'x': { + unsigned int val = va_arg(args, unsigned int); + printf("%x", val); + break; + } + case 'X': { + unsigned int val = va_arg(args, unsigned int); + printf("%X", val); + break; + } + default: // Unsupported placeholder so we just print it + printf("%%%c", placeholder); + break; + } + } else if (*p == '%' && *(p + 1) == '%') { + // print the percent character as is + putchar('%'); + p++; + } else { // Print ASCII character + putchar(*p); + } + } + + va_end(args); + + result.status = BIGINT_OK; + SET_MSG(result, "Printf completed successfully"); + + return result; +} diff --git a/src/bigint.h b/src/bigint.h new file mode 100644 index 0000000..40b9c34 --- /dev/null +++ b/src/bigint.h @@ -0,0 +1,64 @@ +#ifndef BIGINT_H +#define BIGINT_H + +#define RESULT_MSG_SIZE 64 + +// Numerical base (10^9) +#define BIGINT_BASE 1000000000 +// Each digit stores values from 0 to 999,999,999 +#define BIGINT_BASE_DIGITS 9 + +#include +#include +#include "vector.h" + +typedef enum { + BIGINT_OK = 0x0, + BIGINT_ERR_ALLOCATE, + BIGINT_ERR_DIV_BY_ZERO, + BIGINT_ERR_INVALID +} bigint_status_t; + +typedef struct { + vector_t *digits; + bool is_negative; +} bigint_t; + +typedef struct { + bigint_t *quotient; + bigint_t *remainder; +} div_result_t; + +typedef struct { + bigint_status_t status; + uint8_t message[RESULT_MSG_SIZE]; + union { + bigint_t *number; + div_result_t division; + int8_t compare_status; + char *string_num; + } value; +} bigint_result_t; + +#ifdef __cplusplus +extern "C" { +#endif + +bigint_result_t bigint_from_int(long long value); +bigint_result_t bigint_from_string(const char *string_num); +bigint_result_t bigint_to_string(const bigint_t *number); +bigint_result_t bigint_clone(const bigint_t *number); +bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_mod(const bigint_t *x, const bigint_t *y); +bigint_result_t bigint_destroy(bigint_t *number); +bigint_result_t bigint_printf(const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/map.c b/src/map.c index d752697..542065c 100644 --- a/src/map.c +++ b/src/map.c @@ -1,5 +1,7 @@ #define SET_MSG(result, msg) \ - snprintf((char *)result.message, RESULT_MSG_SIZE, msg) + do { \ + snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \ + } while (0) #include #include @@ -35,7 +37,7 @@ uint64_t hash_key(const char *key) { * * Returns a map_result_t data type containing a new hash map */ -map_result_t map_new() { +map_result_t map_new(void) { map_result_t result = {0}; map_t *map = malloc(sizeof(map_t)); diff --git a/src/map.h b/src/map.h index 704b820..a7f3643 100644 --- a/src/map.h +++ b/src/map.h @@ -53,7 +53,7 @@ typedef struct { extern "C" { #endif -map_result_t map_new(); +map_result_t map_new(void); map_result_t map_add(map_t *map, const char *key, void *value); map_result_t map_get(const map_t *map, const char *key); map_result_t map_remove(map_t *map, const char *key); diff --git a/src/vector.c b/src/vector.c index 8eb8d85..089b750 100644 --- a/src/vector.c +++ b/src/vector.c @@ -1,5 +1,7 @@ #define SET_MSG(result, msg) \ - snprintf((char *)result.message, RESULT_MSG_SIZE, msg) + do { \ + snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \ + } while (0) #include #include diff --git a/tests/test_bigint.c b/tests/test_bigint.c new file mode 100644 index 0000000..0cc5aad --- /dev/null +++ b/tests/test_bigint.c @@ -0,0 +1,381 @@ +/* + * Unit tests for BigInt data type +*/ + +#define TEST(NAME) do { \ + printf("Running test_%s...", #NAME); \ + test_##NAME(); \ + printf(" PASSED\n"); \ +} while(0) + +#include +#include +#include +#include + +#include "../src/bigint.h" + +static void bigint_eq(const bigint_t *number, const char *expected) { + bigint_result_t exp_num_res = bigint_from_string(expected); + assert(exp_num_res.status == BIGINT_OK); + bigint_t *exp_num = exp_num_res.value.number; + + bigint_result_t cmp_res = bigint_compare(number, exp_num); + assert(cmp_res.status == BIGINT_OK); + + const int8_t cmp = cmp_res.value.compare_status; + assert(cmp == 0); + + bigint_destroy(exp_num); +} + +// Test creating big integers from int +void test_bigint_from_int(void) { + bigint_result_t res = bigint_from_int(0); + + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "0"); + bigint_destroy(res.value.number); + + res = bigint_from_int(10); + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "10"); + bigint_destroy(res.value.number); + + res = bigint_from_int(-12345678900LL); + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "-12345678900"); + bigint_destroy(res.value.number); +} + +// Test creating big integers from string +void test_bigint_from_string(void) { + bigint_result_t res = bigint_from_string("00000123"); + + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "123"); + bigint_destroy(res.value.number); + + res = bigint_from_string("-00000456789"); + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "-456789"); + bigint_destroy(res.value.number); +} + +// Test sum between big integers +void test_bigint_add(void) { + bigint_result_t x = bigint_from_int(123); + bigint_result_t y = bigint_from_int(456); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t sum = bigint_add(x.value.number, y.value.number); + assert(sum.status == BIGINT_OK); + bigint_eq(sum.value.number, "579"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(sum.value.number); +} + +// Test difference between big numbers +void test_bigint_sub(void) { + bigint_result_t x = bigint_from_int(456); + bigint_result_t y = bigint_from_int(123); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t diff = bigint_sub(x.value.number, y.value.number); + assert(diff.status == BIGINT_OK); + bigint_eq(diff.value.number, "333"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(diff.value.number); +} + +// Test difference between big numbers with negative result +void test_bigint_sub_neg(void) { + bigint_result_t x = bigint_from_int(123); + bigint_result_t y = bigint_from_int(456); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t diff = bigint_sub(x.value.number, y.value.number); + assert(diff.status == BIGINT_OK); + bigint_eq(diff.value.number, "-333"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(diff.value.number); +} + +// Test difference between mixed big numbers +void test_bigint_sub_mixed(void) { + bigint_result_t x = bigint_from_int(456); + bigint_result_t y = bigint_from_int(-123); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t diff = bigint_sub(x.value.number, y.value.number); + assert(diff.status == BIGINT_OK); + bigint_eq(diff.value.number, "579"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(diff.value.number); +} + +// Test product between big numbers +void test_bigint_prod(void) { + bigint_result_t x = bigint_from_int(1234); + bigint_result_t y = bigint_from_int(56789); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t prod = bigint_prod(x.value.number, y.value.number); + assert(prod.status == BIGINT_OK); + bigint_eq(prod.value.number, "70077626"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(prod.value.number); +} + +// Test product between mixed negative big numbers +void test_bigint_prod_mixed(void) { + bigint_result_t x = bigint_from_int(-1234); + bigint_result_t y = bigint_from_int(56789); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t prod = bigint_prod(x.value.number, y.value.number); + assert(prod.status == BIGINT_OK); + bigint_eq(prod.value.number, "-70077626"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(prod.value.number); +} + +// Test product between negative big numbers +void test_bigint_prod_neg(void) { + bigint_result_t x = bigint_from_int(-1234); + bigint_result_t y = bigint_from_int(-56789); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t prod = bigint_prod(x.value.number, y.value.number); + assert(prod.status == BIGINT_OK); + bigint_eq(prod.value.number, "70077626"); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + bigint_destroy(prod.value.number); +} + +// Test division between big numbers +void test_bigint_div(void) { + bigint_result_t x = bigint_from_int(100); + bigint_result_t y = bigint_from_int(2); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t div = bigint_divmod(x.value.number, y.value.number); + assert(div.status == BIGINT_OK); + + bigint_t* const quotient = div.value.division.quotient; + bigint_t* const remainder = div.value.division.remainder; + + bigint_eq(quotient, "50"); + bigint_eq(remainder, "0"); + + bigint_destroy(quotient); + bigint_destroy(remainder); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test division between big numbers with negative dividend +// This library follows C-style divison such that sign(remainder) = sign(dividend) +void test_bigint_div_dividend(void) { + bigint_result_t x = bigint_from_int(-100); + bigint_result_t y = bigint_from_int(3); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t div = bigint_divmod(x.value.number, y.value.number); + assert(div.status == BIGINT_OK); + + bigint_t* const quotient = div.value.division.quotient; + bigint_t* const remainder = div.value.division.remainder; + + bigint_eq(quotient, "-33"); + bigint_eq(remainder, "-1"); + + bigint_destroy(quotient); + bigint_destroy(remainder); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test division between big numbers with negative divisor +// This library follows C-style divison such that sign(remainder) = sign(dividend) +void test_bigint_div_divisor(void) { + bigint_result_t x = bigint_from_int(13); + bigint_result_t y = bigint_from_int(-4); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t div = bigint_divmod(x.value.number, y.value.number); + assert(div.status == BIGINT_OK); + + bigint_t* const quotient = div.value.division.quotient; + bigint_t* const remainder = div.value.division.remainder; + + bigint_eq(quotient, "-3"); + bigint_eq(remainder, "1"); + + bigint_destroy(quotient); + bigint_destroy(remainder); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test division between big numbers with negative numbers +// This library follows C-style divison such that sign(remainder) = sign(dividend) +void test_bigint_div_neg(void) { + bigint_result_t x = bigint_from_int(-100); + bigint_result_t y = bigint_from_int(-3); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t div = bigint_divmod(x.value.number, y.value.number); + assert(div.status == BIGINT_OK); + + bigint_t* const quotient = div.value.division.quotient; + bigint_t* const remainder = div.value.division.remainder; + + bigint_eq(quotient, "33"); + bigint_eq(remainder, "-1"); + + bigint_destroy(quotient); + bigint_destroy(remainder); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test division by zero +void test_bigint_div_by_zero(void) { + bigint_result_t x = bigint_from_int(-100); + bigint_result_t y = bigint_from_int(0); + + assert(x.status == BIGINT_OK && y.status == BIGINT_OK); + + bigint_result_t div = bigint_divmod(x.value.number, y.value.number); + assert(div.status == BIGINT_ERR_DIV_BY_ZERO); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test cloning of big numbers +void test_bigint_clone(void) { + bigint_result_t x = bigint_from_string("0010101010"); + + assert(x.status == BIGINT_OK); + + bigint_result_t cloned = bigint_clone(x.value.number); + assert(cloned.status == BIGINT_OK); + + bigint_eq(cloned.value.number, "10101010"); + + bigint_destroy(x.value.number); + bigint_destroy(cloned.value.number); +} + +// Test comparison between equal numbers +void test_bigint_compare_eq(void) { + bigint_result_t x = bigint_from_int(123); + bigint_result_t y = bigint_from_int(123); + + assert(x.status == BIGINT_OK); + assert(y.status == BIGINT_OK); + + bigint_result_t cmp_res = bigint_compare(x.value.number, y.value.number); + assert(cmp_res.status == BIGINT_OK); + + const int8_t cmp = cmp_res.value.compare_status; + assert(cmp == 0); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test comparison between numbers (less than) +void test_bigint_compare_lt(void) { + bigint_result_t x = bigint_from_int(-123); + bigint_result_t y = bigint_from_int(0); + + assert(x.status == BIGINT_OK); + assert(y.status == BIGINT_OK); + + bigint_result_t cmp_res = bigint_compare(x.value.number, y.value.number); + assert(cmp_res.status == BIGINT_OK); + + const int8_t cmp = cmp_res.value.compare_status; + assert(cmp == -1); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + +// Test comparison between numbers (greater than) +void test_bigint_compare_gt(void) { + bigint_result_t x = bigint_from_int(123); + bigint_result_t y = bigint_from_int(-5); + + assert(x.status == BIGINT_OK); + assert(y.status == BIGINT_OK); + + bigint_result_t cmp_res = bigint_compare(x.value.number, y.value.number); + assert(cmp_res.status == BIGINT_OK); + + const int8_t cmp = cmp_res.value.compare_status; + assert(cmp == 1); + + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); +} + + +int main(void) { + printf("=== Running BigInt unit tests ===\n\n"); + + TEST(bigint_from_int); + TEST(bigint_from_string); + TEST(bigint_add); + TEST(bigint_sub); + TEST(bigint_sub_neg); + TEST(bigint_sub_mixed); + TEST(bigint_prod); + TEST(bigint_prod_mixed); + TEST(bigint_prod_neg); + TEST(bigint_div); + TEST(bigint_div_dividend); + TEST(bigint_div_divisor); + TEST(bigint_div_neg); + TEST(bigint_div_by_zero); + TEST(bigint_clone); + TEST(bigint_compare_eq); + TEST(bigint_compare_lt); + TEST(bigint_compare_gt); + + printf("\n=== All tests passed ===\n"); + + return 0; +} diff --git a/tests/test_map.c b/tests/test_map.c index 3a936f5..47085b9 100644 --- a/tests/test_map.c +++ b/tests/test_map.c @@ -15,7 +15,7 @@ #include "../src/map.h" // Create a new map -void test_map_new() { +void test_map_new(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -27,7 +27,7 @@ void test_map_new() { } // Add elements to map -void test_map_add() { +void test_map_add(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -47,7 +47,7 @@ void test_map_add() { } // Add multiple elements to the map -void test_map_add_multiple() { +void test_map_add_multiple(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -68,7 +68,7 @@ void test_map_add_multiple() { } // Get map element -void test_map_get() { +void test_map_get(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -85,7 +85,7 @@ void test_map_get() { } // Get non-existing key from map -void test_map_get_invalid() { +void test_map_get_invalid(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -98,7 +98,7 @@ void test_map_get_invalid() { } // Map with heterogeneous types -void test_map_mixed() { +void test_map_mixed(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -126,7 +126,7 @@ void test_map_mixed() { } // Update existing map key -void test_map_update() { +void test_map_update(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -149,7 +149,7 @@ void test_map_update() { } // Remove an element from map -void test_map_remove() { +void test_map_remove(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -177,7 +177,7 @@ void test_map_remove() { } // Remove non-existing key from map -void test_map_remove_invalid() { +void test_map_remove_invalid(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -190,7 +190,7 @@ void test_map_remove_invalid() { } // Clear the map -void test_map_clear() { +void test_map_clear(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -212,7 +212,7 @@ void test_map_clear() { } // Clear empty map -void test_map_clear_empty() { +void test_map_clear_empty(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -226,7 +226,7 @@ void test_map_clear_empty() { } // Multiple operations in sequence (add, update, delete and clear) -void test_map_sequence() { +void test_map_sequence(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -266,7 +266,7 @@ typedef struct { short age; } Person; -void test_map_struct() { +void test_map_struct(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); @@ -298,7 +298,7 @@ void test_map_struct() { } // Test map capacity tracking -void test_map_cap() { +void test_map_cap(void) { map_result_t res = map_new(); assert(res.status == MAP_OK); diff --git a/tests/test_vector b/tests/test_vector deleted file mode 100755 index c9066fd..0000000 Binary files a/tests/test_vector and /dev/null differ diff --git a/tests/test_vector.c b/tests/test_vector.c index fd1ca59..52fdcac 100644 --- a/tests/test_vector.c +++ b/tests/test_vector.c @@ -15,7 +15,7 @@ #include "../src/vector.h" // Create a new vector -void test_vector_new() { +void test_vector_new(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -27,14 +27,14 @@ void test_vector_new() { } // Create a vector with zero capacity -void test_vector_new_zcap() { +void test_vector_new_zcap(void) { vector_result_t res = vector_new(0, sizeof(int)); assert(res.status == VECTOR_ERR_ALLOCATE); } // Push elements to vector -void test_vector_push() { +void test_vector_push(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -54,7 +54,7 @@ void test_vector_push() { } // Trigger vector reallocation -void test_vector_push_realloc() { +void test_vector_push_realloc(void) { vector_result_t res = vector_new(1, sizeof(int)); assert(res.status == VECTOR_OK); @@ -72,7 +72,7 @@ void test_vector_push_realloc() { } // Get vector elements -void test_vector_get() { +void test_vector_get(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -89,7 +89,7 @@ void test_vector_get() { } // Test out of bounds -void test_vector_get_ofb() { +void test_vector_get_ofb(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -119,7 +119,7 @@ vector_order_t cmp_int_desc(const void *x, const void *y) { return cmp_int_asc(y, x); } -void test_vector_sort_int_asc() { +void test_vector_sort_int_asc(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -134,7 +134,9 @@ void test_vector_sort_int_asc() { assert(sort_res.status == VECTOR_OK); const int expected[] = { -7, 1, 4, 6, 12, 25, 25, 71 }; - for (size_t idx = 0; idx < vector_size(v); idx++) { + + const size_t sz = vector_size(v); + for (size_t idx = 0; idx < sz; idx++) { int *val = (int*)vector_get(v, idx).value.element; assert(*val == expected[idx]); } @@ -142,7 +144,7 @@ void test_vector_sort_int_asc() { vector_destroy(v); } -void test_vector_sort_int_desc() { +void test_vector_sort_int_desc(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -157,7 +159,9 @@ void test_vector_sort_int_desc() { assert(sort_res.status == VECTOR_OK); const int expected[] = { 71, 25, 25, 12, 6, 4, 1, -7 }; - for (size_t idx = 0; idx < vector_size(v); idx++) { + + const size_t sz = vector_size(v); + for (size_t idx = 0; idx < sz; idx++) { int *val = (int*)vector_get(v, idx).value.element; assert(*val == expected[idx]); } @@ -183,7 +187,7 @@ vector_order_t cmp_string_desc(const void *x, const void *y) { return VECTOR_ORDER_EQ; } -void test_vector_sort_string() { +void test_vector_sort_string(void) { vector_result_t res = vector_new(5, sizeof(char*)); assert(res.status == VECTOR_OK); @@ -198,7 +202,9 @@ void test_vector_sort_string() { assert(sort_res.status == VECTOR_OK); const char *expected[] = { "world!", "system-programming", "hello", "foo", "embedded", "bar"}; - for (size_t idx = 0; idx < vector_size(v); idx++) { + + const size_t sz = vector_size(v); + for (size_t idx = 0; idx < sz; idx++) { const char *val = *(const char**)vector_get(v, idx).value.element; assert(!strcmp(val, expected[idx])); } @@ -234,7 +240,7 @@ vector_order_t cmp_person_by_name(const void *x, const void *y) { return VECTOR_ORDER_EQ; } -void test_vector_sort_struct_by_age() { +void test_vector_sort_struct_by_age(void) { vector_result_t res = vector_new(5, sizeof(Person)); assert(res.status == VECTOR_OK); @@ -257,7 +263,8 @@ void test_vector_sort_struct_by_age() { { .name = "Bob", .age = 45 } }; - for (size_t idx = 0; idx < vector_size(people); idx++) { + const size_t sz = sizeof(expected) / sizeof(expected[0]); + for (size_t idx = 0; idx < sz; idx++) { Person *p = (Person*)vector_get(people, idx).value.element; assert(!strcmp(p->name, expected[idx].name)); assert(p->age == expected[idx].age); @@ -266,7 +273,7 @@ void test_vector_sort_struct_by_age() { vector_destroy(people); } -void test_vector_sort_struct_by_name() { +void test_vector_sort_struct_by_name(void) { vector_result_t res = vector_new(5, sizeof(Person)); assert(res.status == VECTOR_OK); @@ -295,7 +302,8 @@ void test_vector_sort_struct_by_name() { { .name = "Sophia", .age = 45 } }; - for (size_t idx = 0; idx < vector_size(people); idx++) { + const size_t sz = vector_size(people); + for (size_t idx = 0; idx < sz; idx++) { Person *p = (Person*)vector_get(people, idx).value.element; assert(!strcmp(p->name, expected[idx].name)); assert(p->age == expected[idx].age); @@ -305,7 +313,7 @@ void test_vector_sort_struct_by_name() { } // Set vector element -void test_vector_set() { +void test_vector_set(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -324,7 +332,7 @@ void test_vector_set() { } // Set vector element out of bounds -void test_vector_set_ofb() { +void test_vector_set_ofb(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -340,7 +348,7 @@ void test_vector_set_ofb() { } // Pop element from vector -void test_vector_pop() { +void test_vector_pop(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -361,7 +369,7 @@ void test_vector_pop() { } // Test pop element from empty vector -void test_vector_pop_empty() { +void test_vector_pop_empty(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -374,7 +382,7 @@ void test_vector_pop_empty() { } // Clear vector -void test_vector_clear() { +void test_vector_clear(void) { vector_result_t res = vector_new(5, sizeof(int)); assert(res.status == VECTOR_OK); @@ -396,7 +404,7 @@ void test_vector_clear() { } // Multiple operations in sequence (push, set, pop and clear) -void test_vector_sequence() { +void test_vector_sequence(void) { vector_result_t res = vector_new(2, sizeof(int)); assert(res.status == VECTOR_OK); @@ -426,7 +434,7 @@ void test_vector_sequence() { } // Vector with chars -void test_vector_char() { +void test_vector_char(void) { vector_result_t res = vector_new(5, sizeof(char)); assert(res.status == VECTOR_OK); @@ -449,7 +457,7 @@ typedef struct { int y; } Point; -void test_vector_struct() { +void test_vector_struct(void) { vector_result_t res = vector_new(5, sizeof(Point)); assert(res.status == VECTOR_OK); diff --git a/usage.c b/usage.c index c0f561c..4db9d0a 100644 --- a/usage.c +++ b/usage.c @@ -20,10 +20,14 @@ #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 int vector_usage(); -static int map_usage(); 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) { int st; @@ -36,6 +40,11 @@ int main(void) { st = map_usage(); if (st) { return st; } + SEP(50); + + st = bigint_usage(); + if (st) { return st; } + return 0; } @@ -53,7 +62,7 @@ vector_order_t cmp_int_desc(const void *x, const void *y) { return cmp_int_asc(y, x); } -int vector_usage() { +int vector_usage(void) { // Create a vector of 3 integers vector_result_t res = vector_new(3, sizeof(int)); if (res.status != VECTOR_OK) { @@ -79,7 +88,8 @@ int vector_usage() { printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector)); // Print the whole vector - for (size_t idx = 0; idx < vector_size(vector); idx++) { + 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); @@ -127,7 +137,9 @@ int vector_usage() { } printf("Added new elements. Before sort: "); - for (size_t idx = 0; idx < vector_size(vector); idx++) { + + 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); @@ -148,7 +160,7 @@ int vector_usage() { } printf("After sort in ascending order: "); - for (size_t idx = 0; idx < vector_size(vector); idx++) { + 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); @@ -170,7 +182,7 @@ int vector_usage() { } printf("After sort in descending order: "); - for (size_t idx = 0; idx < vector_size(vector); idx++) { + 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); @@ -194,7 +206,7 @@ int vector_usage() { return 0; } -int map_usage() { +int map_usage(void) { // Create a new map map_result_t res = map_new(); if (res.status != MAP_OK) { @@ -225,7 +237,7 @@ int map_usage() { // 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)); + printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map)); // Retrieve keys map_result_t get_res = map_get(map, "x"); @@ -282,6 +294,8 @@ int map_usage() { 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) { @@ -292,3 +306,90 @@ int map_usage() { return 0; } + +int bigint_usage(void) { + // Create two big integers + bigint_result_t x_res = bigint_from_string("123456789"); + 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("987654321"); + 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("123456789 + 987654321 (should be 1,111,111,110) = %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("123456789 - 987654321 (should be -864,197,532) = %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("123456789 * 987654321 (should be 121,932,631,112,635,269) = %B\n", prod); + + bigint_t *a = bigint_from_string("457349545684946456456456567567").value.number; + bigint_t *b = bigint_from_string("43569678678678678678678432").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( + "457349545684946456456456567567 / 43569678678678678678678432 (should be 10,496) = %B\ + \n457349545684946456456456567567 %% 43569678678678678678678432 (should be 42,198,273,535,045,045,047,745,295) = %B\n", + quotient, remainder); + + // Destroy big numbers + 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); + + return 0; +}