From 4240f5db66dcce06885a1795bcf349bd63b66936 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Mon, 10 Nov 2025 16:23:50 +0100 Subject: [PATCH 01/11] Started 'bigint_t' implementation --- src/bignum.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/bignum.h | 49 ++++++++++ src/map.c | 4 +- src/vector.c | 4 +- usage.c | 2 +- 5 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 src/bignum.c create mode 100644 src/bignum.h diff --git a/src/bignum.c b/src/bignum.c new file mode 100644 index 0000000..d7637b1 --- /dev/null +++ b/src/bignum.c @@ -0,0 +1,272 @@ +#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) + +#include +#include +#include + +#include "bignum.h" +#include "vector.h" + +// Internal methods +static bigint_result_t bigint_trim_zeros(bigint_t *number); + +/** + * 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 = BIGNUM_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 = BIGNUM_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + number->digits = vec_res.value.vector; + number->is_negative = (value < 0); + + if (value < 0) { + value = -value; + } else if (value == 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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + } else { + while (value > 0) { + int digit = value % 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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + value /= BIGINT_BASE; + } + } + + result.status = BIGNUM_OK; + SET_MSG(result, "Big integer successfully created"); + result.value.number = number; + + 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); + free(number); + result.status = BIGNUM_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); + free(number); + result.status = BIGNUM_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); + free(number); + result.status = BIGNUM_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 = BIGNUM_OK; + SET_MSG(result, "Big integer successfully trimmed"); + + 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 = BIGNUM_ERR_INVALID; + SET_MSG(result, "Invalid string"); + + return result; + } + + bigint_t *number = malloc(sizeof(bigint_t)); + if (number == NULL) { + result.status = BIGNUM_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) { + vector_destroy(number->digits); + free(number); + result.status = BIGNUM_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 = BIGNUM_ERR_ALLOCATE; + 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 *= 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 = BIGNUM_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + } + + bigint_trim_zeros(number); + + result.status = BIGNUM_OK; + SET_MSG(result, "Big integer successfully created"); + + return result; +} + +/** + * bigint_destroy + * @number: a valid 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 = BIGNUM_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + vector_destroy(number->digits); + free(number); + + result.status = BIGNUM_OK; + SET_MSG(result, "Big integer successfully deleted"); + + return result; +} \ No newline at end of file diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 0000000..07de64b --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,49 @@ +#ifndef BIGNUM_H +#define BIGNUM_H + +#define RESULT_MSG_SIZE 64 + +// Big numbers 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 "vector.h" + +typedef enum { false = 0x0, true } bool; + +typedef enum { + BIGNUM_OK = 0x0, + BIGNUM_ERR_ALLOCATE, + BIGNUM_ERR_DIV_BY_ZERO, + BIGNUM_ERR_INVALID +} bignum_status_t; + +typedef struct { + vector_t *digits; + bool is_negative; +} bigint_t; + +typedef struct { + bignum_status_t status; + uint8_t message[RESULT_MSG_SIZE]; + union { + bigint_t *number; + 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_destroy(bigint_t *number); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/map.c b/src/map.c index d752697..88a3887 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 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/usage.c b/usage.c index c0f561c..eec26ee 100644 --- a/usage.c +++ b/usage.c @@ -225,7 +225,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"); From 119ed5c33f712a8edb2d90a9dfd75f9d029cfcc7 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Tue, 11 Nov 2025 11:42:01 +0100 Subject: [PATCH 02/11] Added add, sub, compare and clone methods for big integers --- src/bignum.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/bignum.h | 5 + 2 files changed, 489 insertions(+), 5 deletions(-) diff --git a/src/bignum.c b/src/bignum.c index d7637b1..07099d4 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -18,6 +18,9 @@ // 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); /** * bigint_from_int @@ -104,7 +107,6 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t get_res = vector_get(number->digits, number_len - 1); if (get_res.status != VECTOR_OK) { vector_destroy(number->digits); - free(number); result.status = BIGNUM_ERR_INVALID; COPY_MSG(result, get_res.message); @@ -119,7 +121,6 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t pop_res = vector_pop(number->digits); if (pop_res.status != VECTOR_OK) { vector_destroy(number->digits); - free(number); result.status = BIGNUM_ERR_INVALID; COPY_MSG(result, get_res.message); @@ -132,7 +133,6 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t get_res = vector_get(number->digits, number_len - 1); if (get_res.status != VECTOR_OK) { vector_destroy(number->digits); - free(number); result.status = BIGNUM_ERR_INVALID; COPY_MSG(result, get_res.message); @@ -152,6 +152,437 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { } +/** + * 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 = BIGNUM_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + + // Start to compare from the MSB + for (int idx = (x_size - 1); idx >= 0; idx--) { + vector_result_t x_get = vector_get(x->digits, idx); + if (x_get.status != VECTOR_OK) { + vector_destroy(x->digits); + vector_destroy(y->digits); + result.status = BIGNUM_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) { + vector_destroy(x->digits); + vector_destroy(y->digits); + result.status = BIGNUM_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 = BIGNUM_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + } + + result.value.compare_status = 0; + result.status = BIGNUM_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 = BIGNUM_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 != BIGNUM_OK) { + vector_destroy(x->digits); + vector_destroy(y->digits); + + return cmp_res; + } + + const uint8_t abs_cmp = cmp_res.value.compare_status; + + result.value.compare_status = x->is_negative ? -abs_cmp : abs_cmp; + result.status = BIGNUM_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 = BIGNUM_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 = BIGNUM_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; + + while (idx < vector_size(x->digits) || idx < vector_size(y->digits) || carry) { + long long partial_sum = carry; + + if (idx < vector_size(x->digits)) { + vector_result_t get_res = vector_get(x->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGNUM_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *x_digit = (int*)get_res.value.element; + partial_sum += *x_digit; + } + + if (idx < vector_size(y->digits)) { + vector_result_t get_res = vector_get(y->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGNUM_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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + idx++; + } + + bigint_result_t trim_res = bigint_trim_zeros(sum); + if (trim_res.status != BIGNUM_OK) { + vector_destroy(sum->digits); + free(sum); + + return trim_res; + } + + result.value.number = sum; + result.status = BIGNUM_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 = BIGNUM_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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + difference->digits = vec_res.value.vector; + difference->is_negative = false; + + long long borrow = 0; + + for (size_t idx = 0; idx < vector_size(x->digits); 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 = BIGNUM_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 < vector_size(y->digits)) { + 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 = BIGNUM_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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + bigint_result_t trim_res = bigint_trim_zeros(difference); + if (trim_res.status != BIGNUM_OK) { + vector_destroy(difference->digits); + free(difference); + + return trim_res; + } + + result.value.number = difference; + result.status = BIGNUM_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 = BIGNUM_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 != BIGNUM_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 = BIGNUM_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 != BIGNUM_OK) { + return cmp_res; + } + + const uint8_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 != BIGNUM_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 = BIGNUM_OK; + SET_MSG(result, "Big integers successfully added"); + } else { + bigint_result_t sub_res = bigint_sub_abs(y, x); + if (sub_res.status != BIGNUM_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 = BIGNUM_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 = BIGNUM_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 != BIGNUM_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 != BIGNUM_OK) { + return difference_res; + } + + bigint_destroy(neg_y); + bigint_t *difference = difference_res.value.number; + + result.value.number = difference; + result.status = BIGNUM_OK; + SET_MSG(result, "Big integers successfully subtracted"); + + return result; +} + /** * bigint_from_string * @string_num: an array of chars representing a number @@ -236,7 +667,13 @@ bigint_result_t bigint_from_string(const char *string_num) { } } - bigint_trim_zeros(number); + bigint_result_t trim_res = bigint_trim_zeros(number); + if (trim_res.status != BIGNUM_OK) { + vector_destroy(number->digits); + free(number); + + return trim_res; + } result.status = BIGNUM_OK; SET_MSG(result, "Big integer successfully created"); @@ -244,9 +681,51 @@ bigint_result_t bigint_from_string(const char *string_num) { 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 = BIGNUM_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + bigint_t *cloned = malloc(sizeof(bigint_t)); + if (cloned == NULL) { + result.status = BIGNUM_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 = BIGNUM_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + result.value.number = cloned; + result.status = BIGNUM_OK; + SET_MSG(result, "Big integer successfully cloned"); + + return result; +} + /** * bigint_destroy - * @number: a valid big integer + * @number: a valid non-null big integer * * Deletes the big integer from the memory * diff --git a/src/bignum.h b/src/bignum.h index 07de64b..3a4fd55 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -30,6 +30,7 @@ typedef struct { uint8_t message[RESULT_MSG_SIZE]; union { bigint_t *number; + uint8_t compare_status; char *string_num; } value; } bigint_result_t; @@ -40,6 +41,10 @@ extern "C" { bigint_result_t bigint_from_int(long long value); bigint_result_t bigint_from_string(const char *string_num); +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_destroy(bigint_t *number); #ifdef __cplusplus From 87cae7d2becbea8c75c7259c4b83601e32f94b43 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Tue, 11 Nov 2025 14:51:03 +0100 Subject: [PATCH 03/11] Added requirements for Karatsuba's algorithm --- src/bignum.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 3 deletions(-) diff --git a/src/bignum.c b/src/bignum.c index 07099d4..a52eb9f 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -21,6 +21,9 @@ 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(const bigint_t *x, const bigint_t *y); /** * bigint_from_int @@ -158,9 +161,9 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { * @y: a non-null big integer * * Compares absolute value of two big integers - * if |x| < |y| => -1 + * if |x| < |y| => -1 * if |x| == |y| => 0 - * if |x| > |y| => 1 + * if |x| > |y| => 1 * * Returns a bigint_result_t data type */ @@ -583,6 +586,272 @@ bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { return result; } +/** + * bigint_shift_left + * @num: a non-null big integer + * @n: number of digits to shift + * + * Shift 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 = BIGNUM_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 = BIGNUM_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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // Copy back original digits + for (size_t idx = 0; idx < vector_size(num->digits); 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 = BIGNUM_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 = BIGNUM_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + result.value.number = shifted; + result.status = BIGNUM_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 = BIGNUM_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t low_res = vector_new(m, sizeof(int)); + if (low_res.status != VECTOR_OK) { + free(low); + result.status = BIGNUM_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 = BIGNUM_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 = BIGNUM_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 = BIGNUM_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 != BIGNUM_OK) { + vector_destroy((*low)->digits); + free(low); + + return first_trim_res; + } + + // High part: digits \in [m, size) + *high = malloc(sizeof(bigint_t)); + if (low == NULL) { + vector_destroy((*low)->digits); + free(low); + result.status = BIGNUM_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 = BIGNUM_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 = BIGNUM_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 = BIGNUM_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 = BIGNUM_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 != BIGNUM_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(low); + free(high); + + return second_trim_res; + } + + result.status = BIGNUM_OK; + SET_MSG(result, "Big number successfully splitted"); + + 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) { + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + // TODO: enough for today! +} + /** * bigint_from_string * @string_num: an array of chars representing a number @@ -611,7 +880,6 @@ bigint_result_t bigint_from_string(const char *string_num) { vector_result_t vec_res = vector_new(4, sizeof(int)); if (vec_res.status != VECTOR_OK) { - vector_destroy(number->digits); free(number); result.status = BIGNUM_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); From 2d10a7076a353a365c2a13d8ae1a4d5c427bf15b Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Wed, 12 Nov 2025 12:17:06 +0100 Subject: [PATCH 04/11] Implemented Karatsuba's algorithm and fixed many critical bugs --- src/bignum.c | 592 +++++++++++++++++++++++++++++++++++++++++---------- src/bignum.h | 15 +- 2 files changed, 493 insertions(+), 114 deletions(-) diff --git a/src/bignum.c b/src/bignum.c index a52eb9f..10c5601 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -9,6 +9,8 @@ (result).message[RESULT_MSG_SIZE - 1] = '\0'; \ } while (0) +#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9') + #include #include #include @@ -23,6 +25,7 @@ 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); /** @@ -38,7 +41,7 @@ bigint_result_t bigint_from_int(long long value) { bigint_t *number = malloc(sizeof(bigint_t)); if (number == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; @@ -47,7 +50,7 @@ bigint_result_t bigint_from_int(long long value) { vector_result_t vec_res = vector_new(4, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(number); - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; @@ -64,7 +67,7 @@ bigint_result_t bigint_from_int(long long value) { if (push_res.status != VECTOR_OK) { vector_destroy(number->digits); free(number); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, vec_res.message); return result; @@ -76,7 +79,7 @@ bigint_result_t bigint_from_int(long long value) { if (push_res.status != VECTOR_OK) { vector_destroy(number->digits); free(number); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, vec_res.message); return result; @@ -86,7 +89,7 @@ bigint_result_t bigint_from_int(long long value) { } } - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully created"); result.value.number = number; @@ -110,7 +113,7 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t get_res = vector_get(number->digits, number_len - 1); if (get_res.status != VECTOR_OK) { vector_destroy(number->digits); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -124,7 +127,7 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t pop_res = vector_pop(number->digits); if (pop_res.status != VECTOR_OK) { vector_destroy(number->digits); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -136,7 +139,7 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { vector_result_t get_res = vector_get(number->digits, number_len - 1); if (get_res.status != VECTOR_OK) { vector_destroy(number->digits); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -148,7 +151,7 @@ static bigint_result_t bigint_trim_zeros(bigint_t *number) { } } - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully trimmed"); return result; @@ -175,19 +178,17 @@ bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { if (x_size != y_size) { result.value.compare_status = (x_size > y_size) ? 1 : -1; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; } // Start to compare from the MSB - for (int idx = (x_size - 1); idx >= 0; idx--) { + 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) { - vector_destroy(x->digits); - vector_destroy(y->digits); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, x_get.message); return result; @@ -195,9 +196,7 @@ bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { vector_result_t y_get = vector_get(y->digits, idx); if (y_get.status != VECTOR_OK) { - vector_destroy(x->digits); - vector_destroy(y->digits); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, y_get.message); return result; @@ -208,7 +207,7 @@ bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { if (*x_digit != *y_digit) { result.value.compare_status = (*x_digit > *y_digit) ? 1 : -1; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; @@ -216,7 +215,7 @@ bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { } result.value.compare_status = 0; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; @@ -239,24 +238,21 @@ bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y) { if (x->is_negative != y->is_negative) { result.value.compare_status = x->is_negative ? -1 : 1; - result.status = BIGNUM_OK; + 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 != BIGNUM_OK) { - vector_destroy(x->digits); - vector_destroy(y->digits); - + if (cmp_res.status != BIGINT_OK) { return cmp_res; } - const uint8_t abs_cmp = cmp_res.value.compare_status; + const int8_t abs_cmp = cmp_res.value.compare_status; result.value.compare_status = x->is_negative ? -abs_cmp : abs_cmp; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; @@ -276,7 +272,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { bigint_t *sum = malloc(sizeof(bigint_t)); if (sum == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Cannot allocate memory for big integer"); return result; @@ -288,7 +284,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { vector_result_t vec_res = vector_new(max_size + 1, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(sum); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, vec_res.message); return result; @@ -308,7 +304,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { if (get_res.status != VECTOR_OK) { vector_destroy(sum->digits); free(sum); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -323,7 +319,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { if (get_res.status != VECTOR_OK) { vector_destroy(sum->digits); free(sum); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -340,7 +336,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { if (push_res.status != VECTOR_OK) { vector_destroy(sum->digits); free(sum); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -349,7 +345,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { } bigint_result_t trim_res = bigint_trim_zeros(sum); - if (trim_res.status != BIGNUM_OK) { + if (trim_res.status != BIGINT_OK) { vector_destroy(sum->digits); free(sum); @@ -357,7 +353,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { } result.value.number = sum; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); return result; @@ -377,7 +373,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { bigint_t *difference = malloc(sizeof(bigint_t)); if (difference == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Cannot allocate memory for big integer"); return result; @@ -386,7 +382,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); if (vec_res.status != VECTOR_OK) { free(difference); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, vec_res.message); return result; @@ -402,7 +398,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { if (x_get_res.status != VECTOR_OK) { vector_destroy(difference->digits); free(difference); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, x_get_res.message); return result; @@ -416,7 +412,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { if (y_get_res.status != VECTOR_OK) { vector_destroy(difference->digits); free(difference); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, y_get_res.message); return result; @@ -438,7 +434,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { if (push_res.status != VECTOR_OK) { vector_destroy(difference->digits); free(difference); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -446,7 +442,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { } bigint_result_t trim_res = bigint_trim_zeros(difference); - if (trim_res.status != BIGNUM_OK) { + if (trim_res.status != BIGINT_OK) { vector_destroy(difference->digits); free(difference); @@ -454,7 +450,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { } result.value.number = difference; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully subtracted"); return result; @@ -473,7 +469,7 @@ 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 = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; @@ -482,7 +478,7 @@ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { // 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 != BIGNUM_OK) { + if (sum_res.status != BIGINT_OK) { return sum_res; } @@ -492,7 +488,7 @@ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { } result.value.number = sum; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); return result; @@ -500,16 +496,16 @@ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { // Different signs: subtract smaller from larger bigint_result_t cmp_res = bigint_compare_abs(x, y); - if (cmp_res.status != BIGNUM_OK) { + if (cmp_res.status != BIGINT_OK) { return cmp_res; } - const uint8_t cmp = cmp_res.value.compare_status; + 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 != BIGNUM_OK) { + if (sub_res.status != BIGINT_OK) { return sub_res; } @@ -519,11 +515,11 @@ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { } result.value.number = sub; - result.status = BIGNUM_OK; + 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 != BIGNUM_OK) { + if (sub_res.status != BIGINT_OK) { return sub_res; } @@ -533,7 +529,7 @@ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { } result.value.number = sub; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); } @@ -553,7 +549,7 @@ 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 = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; @@ -564,7 +560,7 @@ bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { * x - y = x + (-y) */ bigint_result_t neg_y_res = bigint_clone(y); - if (neg_y_res.status != BIGNUM_OK) { + if (neg_y_res.status != BIGINT_OK) { return neg_y_res; } @@ -572,7 +568,9 @@ bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { neg_y->is_negative = !neg_y->is_negative; bigint_result_t difference_res = bigint_add(x, neg_y); - if (difference_res.status != BIGNUM_OK) { + if (difference_res.status != BIGINT_OK) { + bigint_destroy(neg_y); + return difference_res; } @@ -580,12 +578,54 @@ bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { bigint_t *difference = difference_res.value.number; result.value.number = difference; - result.status = BIGNUM_OK; + 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_shift_left * @num: a non-null big integer @@ -604,7 +644,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { bigint_t *shifted = malloc(sizeof(bigint_t)); if (shifted == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; @@ -613,7 +653,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { vector_result_t vec_res = vector_new(vector_size(num->digits) + n, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(shifted); - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; @@ -629,7 +669,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { if (push_res.status != VECTOR_OK) { vector_destroy(shifted->digits); free(shifted); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -642,7 +682,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { if (get_res.status != VECTOR_OK) { vector_destroy(shifted->digits); free(shifted); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -653,7 +693,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { if (push_res.status != VECTOR_OK) { vector_destroy(shifted->digits); free(shifted); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -661,7 +701,7 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { } result.value.number = shifted; - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer shifted successfully"); return result; @@ -685,17 +725,17 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big // Low part: digits \in [0, m) *low = malloc(sizeof(bigint_t)); - if (low == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + 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, sizeof(int)); + vector_result_t low_res = vector_new(m ? m : 1, sizeof(int)); if (low_res.status != VECTOR_OK) { - free(low); - result.status = BIGNUM_ERR_ALLOCATE; + free(*low); + result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, low_res.message); return result; @@ -708,8 +748,8 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big vector_result_t get_res = vector_get(num->digits, idx); if (get_res.status != VECTOR_OK) { vector_destroy((*low)->digits); - free(low); - result.status = BIGNUM_ERR_INVALID; + free(*low); + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -719,8 +759,8 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big vector_result_t push_res = vector_push((*low)->digits, digit); if (push_res.status != VECTOR_OK) { vector_destroy((*low)->digits); - free(low); - result.status = BIGNUM_ERR_INVALID; + free(*low); + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -732,8 +772,8 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big vector_result_t push_res = vector_push((*low)->digits, &zero); if (push_res.status != VECTOR_OK) { vector_destroy((*low)->digits); - free(low); - result.status = BIGNUM_ERR_INVALID; + free(*low); + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -742,19 +782,19 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big // First pass of zero trimming bigint_result_t first_trim_res = bigint_trim_zeros(*low); - if (first_trim_res.status != BIGNUM_OK) { + if (first_trim_res.status != BIGINT_OK) { vector_destroy((*low)->digits); - free(low); + free(*low); return first_trim_res; } // High part: digits \in [m, size) *high = malloc(sizeof(bigint_t)); - if (low == NULL) { + if (*high == NULL) { vector_destroy((*low)->digits); - free(low); - result.status = BIGNUM_ERR_ALLOCATE; + free(*low); + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; @@ -763,10 +803,10 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big 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); + free(*low); + free(*high); - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, low_res.message); return result; @@ -781,10 +821,10 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big if (get_res.status != VECTOR_OK) { vector_destroy((*low)->digits); vector_destroy((*high)->digits); - free(low); - free(high); + free(*low); + free(*high); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; @@ -795,10 +835,10 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big if (push_res.status != VECTOR_OK) { vector_destroy((*low)->digits); vector_destroy((*high)->digits); - free(low); - free(high); + free(*low); + free(*high); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -810,10 +850,10 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big if (push_res.status != VECTOR_OK) { vector_destroy((*low)->digits); vector_destroy((*high)->digits); - free(low); - free(high); + free(*low); + free(*high); - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; @@ -822,21 +862,133 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big // Second pass of zero trimming bigint_result_t second_trim_res = bigint_trim_zeros(*high); - if (second_trim_res.status != BIGNUM_OK) { + if (second_trim_res.status != BIGINT_OK) { vector_destroy((*low)->digits); vector_destroy((*high)->digits); - free(low); - free(high); + free(*low); + free(*high); return second_trim_res; } - result.status = BIGNUM_OK; + 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 @@ -846,10 +998,194 @@ bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, big * 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}; + const size_t x_size = vector_size(x->digits); const size_t y_size = vector_size(y->digits); - // TODO: enough for today! + // 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; + + // Split x = x1 * BASE^pivot + x0 + bigint_t *x1 = NULL, *x0 = NULL; + bigint_result_t x_split_res = bigint_split(x, pivot, &x1, &x0); + if (x_split_res.status != BIGINT_OK) { + return x_split_res; + } + + // Split y = y1 * BASE^pivot + y0 + bigint_t *y1 = NULL, *y0 = NULL; + bigint_result_t y_split_res = bigint_split(y, pivot, &y1, &y0); + if (y_split_res.status != BIGINT_OK) { + bigint_destroy(x1); bigint_destroy(x0); + + return y_split_res; + } + + // Perform karatsuba's trick + bigint_result_t z0_res = bigint_karatsuba(x0, y0); // x0 * y0 + if (z0_res.status != BIGINT_OK) { + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + + return z0_res; + } + + bigint_result_t z2_res = bigint_karatsuba(x1, y1); // x1 * y1 + if (z2_res.status != BIGINT_OK) { + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0_res.value.number); + + return z2_res; + } + + bigint_t *z0 = z0_res.value.number; + bigint_t *z2 = z2_res.value.number; + + // z1 = (x0 + x1) * (y0 + y1) - z0 - z2 + bigint_result_t x_sum_res = bigint_add(x0, x1); + if (x_sum_res.status != BIGINT_OK) { + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0); bigint_destroy(z2); + + return x_sum_res; + } + + bigint_t *x_sum = x_sum_res.value.number; + + bigint_result_t y_sum_res = bigint_add(y0, y1); + if (y_sum_res.status != BIGINT_OK) { + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0); bigint_destroy(z2); + bigint_destroy(x_sum); + + return y_sum_res; + } + + bigint_t *y_sum = y_sum_res.value.number; + + // (x0 + x1) * (y0 + y1) + bigint_result_t z1_temp_res = bigint_karatsuba(x_sum ,y_sum); + if (z1_temp_res.status != BIGINT_OK) { + 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); + + return z1_temp_res; + } + + bigint_t *z1_temp = z1_temp_res.value.number; + + // z1 = ... - z0 + bigint_result_t z1_sub1_res = bigint_sub(z1_temp, z0); + if (z1_sub1_res.status != BIGINT_OK) { + 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); + + return z1_sub1_res; + } + + bigint_t *z1_sub1 = z1_sub1_res.value.number; + + // z1 = ... - z2 + bigint_result_t z1_sub2_res = bigint_sub(z1_sub1, z2); + if (z1_sub2_res.status != BIGINT_OK) { + 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); + + return z1_sub2_res; + } + + bigint_t *z1 = z1_sub2_res.value.number; + + // product = z^2 * BASE^(2pivot) + z1 * BASE^pivot + z0 + bigint_result_t z2_shift_res = bigint_shift_left(z2, 2*pivot); + if (z2_shift_res.status != BIGINT_OK) { + 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); + + return z2_shift_res; + } + + bigint_result_t z1_shift_res = bigint_shift_left(z1, pivot); + if (z1_shift_res.status != BIGINT_OK) { + 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); + + return z1_shift_res; + } + + bigint_t *z2_shifted = z2_shift_res.value.number; + bigint_t *z1_shifted = z1_shift_res.value.number; + + bigint_result_t temp_res = bigint_add(z2_shifted, z1_shifted); + if (temp_res.status != BIGINT_OK) { + 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); + + return temp_res; + } + + bigint_t *temp = temp_res.value.number; + + // product = ... + z0 + bigint_result_t product_res = bigint_add(temp, z0); + if (product_res.status != BIGINT_OK) { + 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); + + return product_res; + } + + bigint_t *product = product_res.value.number; + + // Clean allocated numbers + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0); bigint_destroy(z1); bigint_destroy(z2); + bigint_destroy(x_sum); bigint_destroy(y_sum); + bigint_destroy(z1_temp); bigint_destroy(z1_sub1); + 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"); + + return result; } /** @@ -864,7 +1200,7 @@ bigint_result_t bigint_from_string(const char *string_num) { bigint_result_t result = {0}; if (string_num == NULL || *string_num == 0) { - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid string"); return result; @@ -872,7 +1208,7 @@ bigint_result_t bigint_from_string(const char *string_num) { bigint_t *number = malloc(sizeof(bigint_t)); if (number == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; @@ -881,7 +1217,7 @@ bigint_result_t bigint_from_string(const char *string_num) { vector_result_t vec_res = vector_new(4, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(number); - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; @@ -901,12 +1237,24 @@ bigint_result_t bigint_from_string(const char *string_num) { if (*string_num == '\0') { vector_destroy(number->digits); free(number); - result.status = BIGNUM_ERR_ALLOCATE; + 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++; @@ -921,29 +1269,31 @@ bigint_result_t bigint_from_string(const char *string_num) { int digit = 0; for (int j = 0; j < chunk_len; j++) { - digit *= 10 + (string_num[start + j] - '0'); + // digit *= 10 + (string_num[start + j] - '0'); + 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 = BIGNUM_ERR_ALLOCATE; - COPY_MSG(result, vec_res.message); + 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 != BIGNUM_OK) { + if (trim_res.status != BIGINT_OK) { vector_destroy(number->digits); free(number); return trim_res; } - result.status = BIGNUM_OK; + result.value.number = number; + result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully created"); return result; @@ -961,7 +1311,7 @@ bigint_result_t bigint_clone(const bigint_t *number) { bigint_result_t result = {0}; if (number == NULL) { - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integer"); return result; @@ -969,7 +1319,7 @@ bigint_result_t bigint_clone(const bigint_t *number) { bigint_t *cloned = malloc(sizeof(bigint_t)); if (cloned == NULL) { - result.status = BIGNUM_ERR_ALLOCATE; + result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; @@ -978,14 +1328,42 @@ bigint_result_t bigint_clone(const bigint_t *number) { vector_result_t vec_res = vector_new(vector_size(number->digits), sizeof(int)); if (vec_res.status != VECTOR_OK) { free(cloned); - result.status = BIGNUM_ERR_ALLOCATE; + 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 + for (size_t idx = 0; idx < vector_size(number->digits); 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 = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully cloned"); return result; @@ -1003,7 +1381,7 @@ bigint_result_t bigint_destroy(bigint_t *number) { bigint_result_t result = {0}; if (number == NULL) { - result.status = BIGNUM_ERR_INVALID; + result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integer"); return result; @@ -1012,7 +1390,7 @@ bigint_result_t bigint_destroy(bigint_t *number) { vector_destroy(number->digits); free(number); - result.status = BIGNUM_OK; + result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully deleted"); return result; diff --git a/src/bignum.h b/src/bignum.h index 3a4fd55..483251a 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -14,11 +14,11 @@ typedef enum { false = 0x0, true } bool; typedef enum { - BIGNUM_OK = 0x0, - BIGNUM_ERR_ALLOCATE, - BIGNUM_ERR_DIV_BY_ZERO, - BIGNUM_ERR_INVALID -} bignum_status_t; + BIGINT_OK = 0x0, + BIGINT_ERR_ALLOCATE, + BIGINT_ERR_DIV_BY_ZERO, + BIGINT_ERR_INVALID +} bigint_status_t; typedef struct { vector_t *digits; @@ -26,11 +26,11 @@ typedef struct { } bigint_t; typedef struct { - bignum_status_t status; + bigint_status_t status; uint8_t message[RESULT_MSG_SIZE]; union { bigint_t *number; - uint8_t compare_status; + int8_t compare_status; char *string_num; } value; } bigint_result_t; @@ -45,6 +45,7 @@ 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_destroy(bigint_t *number); #ifdef __cplusplus From 77f68f2328eef23723281d91cb7d19a3df8dcf0a Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Wed, 12 Nov 2025 16:27:16 +0100 Subject: [PATCH 05/11] Refactored Karatsuba's algorithm and added sample usage --- Makefile | 2 +- README.md | 39 ++- src/{bignum.c => bigint.c} | 674 +++++++++++++++++++------------------ src/{bignum.h => bigint.h} | 9 +- usage.c | 80 +++++ 5 files changed, 463 insertions(+), 341 deletions(-) rename src/{bignum.c => bigint.c} (86%) rename src/{bignum.h => bigint.h} (87%) diff --git a/Makefile b/Makefile index 0b1e840..ae99f38 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TARGET = usage TEST_V_TARGET = test_vector TEST_M_TARGET = test_map -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 diff --git a/README.md b/README.md index 303eb64..1f29857 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,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 +52,7 @@ int main(void) { } ``` -### `Map`'s usage +### `Map` usage ```c #include @@ -95,6 +96,40 @@ int main(void) { } ``` +### `BigInt` usage +```c +#include +#include "src/bigint.h" + +/* + * Compile with: gcc main.c src/bigint.c + * Output: 20000! = 1819206320230345134827641... + * Time: real 0m5.482s user 0m5.453s sys 0m0.017 + */ +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; + } + + printf("%ld! = ", n); + bigint_print(fact); + printf("\n"); + + 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: diff --git a/src/bignum.c b/src/bigint.c similarity index 86% rename from src/bignum.c rename to src/bigint.c index 10c5601..a2d6ac3 100644 --- a/src/bignum.c +++ b/src/bigint.c @@ -11,11 +11,19 @@ #define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9') +#define DESTROY_IF(p) \ + do { \ + if ((p) && (p) != result.value.number) { \ + bigint_destroy((p)); \ + (p) = NULL; \ + } \ + } while (0) + #include #include #include -#include "bignum.h" +#include "bigint.h" #include "vector.h" // Internal methods @@ -96,6 +104,254 @@ bigint_result_t bigint_from_int(long long value) { 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 *= 10 + (string_num[start + j] - '0'); + 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 + for (size_t idx = 0; idx < vector_size(number->digits); 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 @@ -1011,360 +1267,83 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *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; - // Split x = x1 * BASE^pivot + x0 + // Results of each step bigint_t *x1 = NULL, *x0 = NULL; - bigint_result_t x_split_res = bigint_split(x, pivot, &x1, &x0); - if (x_split_res.status != BIGINT_OK) { - return x_split_res; - } + 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; + + bigint_result_t tmp_res = {0}; + + // 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 - bigint_t *y1 = NULL, *y0 = NULL; - bigint_result_t y_split_res = bigint_split(y, pivot, &y1, &y0); - if (y_split_res.status != BIGINT_OK) { - bigint_destroy(x1); bigint_destroy(x0); - - return y_split_res; - } + tmp_res = bigint_split(x, pivot, &y1, &y0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } // Perform karatsuba's trick - bigint_result_t z0_res = bigint_karatsuba(x0, y0); // x0 * y0 - if (z0_res.status != BIGINT_OK) { - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); + 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; - return z0_res; - } - - bigint_result_t z2_res = bigint_karatsuba(x1, y1); // x1 * y1 - if (z2_res.status != BIGINT_OK) { - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); - bigint_destroy(z0_res.value.number); - - return z2_res; - } - - bigint_t *z0 = z0_res.value.number; - bigint_t *z2 = z2_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 - bigint_result_t x_sum_res = bigint_add(x0, x1); - if (x_sum_res.status != BIGINT_OK) { - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); - bigint_destroy(z0); bigint_destroy(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; - return x_sum_res; - } + 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; - bigint_t *x_sum = x_sum_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; - bigint_result_t y_sum_res = bigint_add(y0, y1); - if (y_sum_res.status != BIGINT_OK) { - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); - bigint_destroy(z0); bigint_destroy(z2); - bigint_destroy(x_sum); + 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; - return y_sum_res; - } + 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; - bigint_t *y_sum = y_sum_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; - // (x0 + x1) * (y0 + y1) - bigint_result_t z1_temp_res = bigint_karatsuba(x_sum ,y_sum); - if (z1_temp_res.status != BIGINT_OK) { - 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); + 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; - return z1_temp_res; - } + tmp_res = bigint_add(z2_shifted, z1_shifted); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + temp = tmp_res.value.number; - bigint_t *z1_temp = z1_temp_res.value.number; - - // z1 = ... - z0 - bigint_result_t z1_sub1_res = bigint_sub(z1_temp, z0); - if (z1_sub1_res.status != BIGINT_OK) { - 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); - - return z1_sub1_res; - } - - bigint_t *z1_sub1 = z1_sub1_res.value.number; - - // z1 = ... - z2 - bigint_result_t z1_sub2_res = bigint_sub(z1_sub1, z2); - if (z1_sub2_res.status != BIGINT_OK) { - 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); - - return z1_sub2_res; - } - - bigint_t *z1 = z1_sub2_res.value.number; - - // product = z^2 * BASE^(2pivot) + z1 * BASE^pivot + z0 - bigint_result_t z2_shift_res = bigint_shift_left(z2, 2*pivot); - if (z2_shift_res.status != BIGINT_OK) { - 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); - - return z2_shift_res; - } - - bigint_result_t z1_shift_res = bigint_shift_left(z1, pivot); - if (z1_shift_res.status != BIGINT_OK) { - 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); - - return z1_shift_res; - } - - bigint_t *z2_shifted = z2_shift_res.value.number; - bigint_t *z1_shifted = z1_shift_res.value.number; - - bigint_result_t temp_res = bigint_add(z2_shifted, z1_shifted); - if (temp_res.status != BIGINT_OK) { - 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); - - return temp_res; - } - - bigint_t *temp = temp_res.value.number; - - // product = ... + z0 - bigint_result_t product_res = bigint_add(temp, z0); - if (product_res.status != BIGINT_OK) { - 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); - - return product_res; - } - - bigint_t *product = product_res.value.number; - - // Clean allocated numbers - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); - bigint_destroy(z0); bigint_destroy(z1); bigint_destroy(z2); - bigint_destroy(x_sum); bigint_destroy(y_sum); - bigint_destroy(z1_temp); bigint_destroy(z1_sub1); - bigint_destroy(z2_shifted); bigint_destroy(z1_shifted); - bigint_destroy(temp); + tmp_res = bigint_add(temp, z0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + product = tmp_res.value.number; result.value.number = product; result.status = BIGINT_OK; SET_MSG(result, "Product between big integers was successful"); - 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 *= 10 + (string_num[start + j] - '0'); - 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_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 - for (size_t idx = 0; idx < vector_size(number->digits); 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"); +cleanup: // Destroy intermediate allocations except for the product + DESTROY_IF(x1); DESTROY_IF(x0); + DESTROY_IF(y1); DESTROY_IF(y0); + DESTROY_IF(z0); DESTROY_IF(z2); + DESTROY_IF(x_sum); DESTROY_IF(y_sum); + DESTROY_IF(z1_temp); DESTROY_IF(z1_sub1); DESTROY_IF(z1); + DESTROY_IF(z2_shifted); DESTROY_IF(z1_shifted); + DESTROY_IF(temp); return result; } @@ -1393,5 +1372,32 @@ bigint_result_t bigint_destroy(bigint_t *number) { result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully deleted"); + return result; +} + +/** + * bigint_print + * @number: a valid non-null big integer + * + * Prints @number to standard output + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_print(const bigint_t *number) { + bigint_result_t result = {0}; + + bigint_result_t num_str_res = bigint_to_string(number); + if (num_str_res.status != BIGINT_OK) { + return num_str_res; + } + + char *number_str = num_str_res.value.string_num; + + printf("%s", number_str); + free(number_str); + + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully printed"); + return result; } \ No newline at end of file diff --git a/src/bignum.h b/src/bigint.h similarity index 87% rename from src/bignum.h rename to src/bigint.h index 483251a..dc06267 100644 --- a/src/bignum.h +++ b/src/bigint.h @@ -1,5 +1,5 @@ -#ifndef BIGNUM_H -#define BIGNUM_H +#ifndef BIGINT_H +#define BIGINT_H #define RESULT_MSG_SIZE 64 @@ -9,10 +9,9 @@ #define BIGINT_BASE_DIGITS 9 #include +#include #include "vector.h" -typedef enum { false = 0x0, true } bool; - typedef enum { BIGINT_OK = 0x0, BIGINT_ERR_ALLOCATE, @@ -41,12 +40,14 @@ extern "C" { 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_destroy(bigint_t *number); +bigint_result_t bigint_print(const bigint_t *number); #ifdef __cplusplus } diff --git a/usage.c b/usage.c index eec26ee..a121518 100644 --- a/usage.c +++ b/usage.c @@ -20,9 +20,12 @@ #include "src/vector.h" #include "src/map.h" +#include "src/bigint.h" static int vector_usage(); static int map_usage(); +static int bigint_usage(); + static vector_order_t cmp_int_asc(const void *x, const void *y); int main(void) { @@ -36,6 +39,11 @@ int main(void) { st = map_usage(); if (st) { return st; } + SEP(50); + + st = bigint_usage(); + if (st) { return st; } + return 0; } @@ -282,6 +290,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 +302,73 @@ int map_usage() { return 0; } + +int bigint_usage() { + // Create a big integer + 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 + printf("123456789 + 987654321 (should be 1,111,111,110) = "); + bigint_print(sum); + printf("\n"); + + // 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 + printf("123456789 - 987654321 (should be -864,197,532) = "); + bigint_print(diff); + printf("\n"); + + // 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 + printf("123456789 * 987654321 (should be 121,932,631,112,635,269) = "); + bigint_print(prod); + printf("\n"); + + bigint_destroy(x); bigint_destroy(y); + bigint_destroy(sum); bigint_destroy(diff); bigint_destroy(prod); + + return 0; +} \ No newline at end of file From 5af5699e66fa59693be0fc50211370965a28c6da Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Thu, 13 Nov 2025 11:46:14 +0100 Subject: [PATCH 06/11] Added requirements for division and fixed critical bugs --- README.md | 2 +- src/bigint.c | 381 +++++++++++++++++++++++++++++++++++++++++++--- src/bigint.h | 7 + tests/test_vector | Bin 64976 -> 0 bytes 4 files changed, 370 insertions(+), 20 deletions(-) delete mode 100755 tests/test_vector diff --git a/README.md b/README.md index 1f29857..9a28b4f 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ int main(void) { fact = partial_fact; } - printf("%ld! = ", n); + printf("%d! = ", n); bigint_print(fact); printf("\n"); diff --git a/src/bigint.c b/src/bigint.c index a2d6ac3..e2b0c82 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -11,14 +11,6 @@ #define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9') -#define DESTROY_IF(p) \ - do { \ - if ((p) && (p) != result.value.number) { \ - bigint_destroy((p)); \ - (p) = NULL; \ - } \ - } while (0) - #include #include #include @@ -35,6 +27,8 @@ 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_shift_right(const bigint_t *num, size_t n); +static bigint_result_t bigint_reciprocal(const bigint_t *num, size_t precision); /** * bigint_from_int @@ -882,6 +876,116 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) { 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; + + // Computed 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_shift_left * @num: a non-null big integer @@ -1255,6 +1359,14 @@ bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y) { */ 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); @@ -1276,14 +1388,12 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { bigint_t *z2_shifted = NULL, *z1_shifted = NULL; bigint_t *temp = NULL, *product = NULL; - bigint_result_t tmp_res = {0}; - // 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(x, pivot, &y1, &y0); + tmp_res = bigint_split(y, pivot, &y1, &y0); if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } // Perform karatsuba's trick @@ -1332,18 +1442,251 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { 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 except for the product - DESTROY_IF(x1); DESTROY_IF(x0); - DESTROY_IF(y1); DESTROY_IF(y0); - DESTROY_IF(z0); DESTROY_IF(z2); - DESTROY_IF(x_sum); DESTROY_IF(y_sum); - DESTROY_IF(z1_temp); DESTROY_IF(z1_sub1); DESTROY_IF(z1); - DESTROY_IF(z2_shifted); DESTROY_IF(z1_shifted); - DESTROY_IF(temp); +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_shift_right + * @num: a valid non-null big integer + * @n: number of digits to shift + * + * Shifts right by @n digits (i.e., divide by BASE^n) + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_shift_right(const bigint_t *num, size_t n) { + bigint_result_t result = {0}; + + const size_t size = vector_size(num->digits); + + if (n >= size) return bigint_from_int(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(size - n, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + shifted->digits = vec_res.value.vector; + shifted->is_negative = num->is_negative; + + // Copy digits from position 'n' onwards + for (size_t idx = n; idx < size; idx++) { + vector_result_t vec_res = vector_get(num->digits, idx); + if (vec_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + int *digit = (int*)vec_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; + } + } + + bigint_result_t trim_res = bigint_trim_zeros(shifted); + if (trim_res.status != BIGINT_OK) { + vector_destroy(shifted->digits); + free(shifted); + + return trim_res; + } + + result.value.number = shifted; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer shifted successfully"); + + return result; +} + +/** + * bigint_reciprocal + * @num: a valid non-null big integer + * @precision: the precision of the computation + * + * Compute the reciprocal using Newton-Raphson algorithm. + * It calculates 1/num with precision @precision, returning + * floor(BASE^(2 * @precision) / num) + * + * Returns a bigint_result_t data type + */ +bigint_result_t bigint_reciprocal(const bigint_t *num, size_t precision) { + bigint_result_t result = {0}; + bigint_result_t tmp_res = {0}; + + // Results of each steps + bigint_t *x = NULL; + bigint_t *scale = NULL; + bigint_t *two = NULL; + bigint_t *two_scaled = NULL; + bigint_t *dx = NULL; + bigint_t *two_minus_dx = NULL; + bigint_t *x_new_tmp = NULL; + bigint_t *x_new = NULL; + + if (num == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big integer"); + + return result; + } + + const size_t num_size = vector_size(num->digits); + // Get most significant digit + vector_result_t msd_res = vector_get(num->digits, num_size - 1); + if (msd_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, msd_res.message); + + return result; + } + + int *msd = (int*)msd_res.value.element; + + // x = floor(BASE^2 / (msd + 1)) + const long long initial_val = ((long long)BIGINT_BASE * (long long)BIGINT_BASE) / ((long long)(*msd) + 1LL); + tmp_res = bigint_from_int(initial_val); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + x = tmp_res.value.number; + + tmp_res = bigint_from_int(1); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + scale = tmp_res.value.number; + + // Scale to proper precision. That is scale x by BASE^(2 * precision - 2) + // in order to reach BASE^(2 * precision) magnitude + if (precision > 1) { + tmp_res = bigint_shift_left(scale, 2 * precision - 2); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + bigint_destroy(scale); + scale = tmp_res.value.number; + + tmp_res = bigint_prod(x, scale); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + bigint_destroy(x); + x = tmp_res.value.number; + } + + // two_scaled = 2 * BASE^(2 * precision) + tmp_res = bigint_from_int(2); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + two = tmp_res.value.number; + + tmp_res = bigint_shift_left(two, 2 * precision); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_destroy(two); + two = NULL; + two_scaled = tmp_res.value.number; + + // Determine the number of Newton-Raphson iterations + size_t iterations = 0; + size_t target = precision; + while ((1ULL << iterations) < target) { iterations++; } + iterations += 2; // Add a few more just to be sure + + // x_{n+1} = x_n * (2 * BASE^(2P) - d * x_n) / BASE^(2P) + for (size_t it = 0; it < iterations; it++) { + // dx = d * x + tmp_res = bigint_prod(num, x); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + dx = tmp_res.value.number; + + // two_minus_dx = 2 * BASE^(2P) - dx + tmp_res = bigint_sub(two_scaled, dx); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + two_minus_dx = tmp_res.value.number; + + // x_new_temp = x * (two_minus_dx) + tmp_res = bigint_prod(x, two_minus_dx); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + x_new_tmp = tmp_res.value.number; + + // x_new = x_new_temp >> (2 * precision) + tmp_res = bigint_shift_right(x_new_tmp, 2 * precision); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + x_new = tmp_res.value.number; + + // Rotation pass: replace x with x_new and free intermediates + bigint_destroy(x); + x = x_new; + x_new = NULL; + + bigint_destroy(dx); dx = NULL; + bigint_destroy(two_minus_dx); two_minus_dx = NULL; + bigint_destroy(x_new_tmp); x_new_tmp = NULL; + } + + bigint_destroy(scale); + bigint_destroy(two_scaled); + + result.value.number = x; + result.status = BIGINT_OK; + SET_MSG(result, "Reciprocal computed successfully"); + return result; + +cleanup: + if (x) { bigint_destroy(x); } + if (scale) { bigint_destroy(scale); } + if (two) { bigint_destroy(two); } + if (two_scaled) { bigint_destroy(two_scaled); } + if (dx) { bigint_destroy(dx); } + if (two_minus_dx) { bigint_destroy(two_minus_dx); } + if (x_new_tmp) { bigint_destroy(x_new_tmp); } + if (x_new) { bigint_destroy(x_new); } return result; } diff --git a/src/bigint.h b/src/bigint.h index dc06267..8e60b49 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -24,11 +24,17 @@ typedef struct { 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; @@ -46,6 +52,7 @@ 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_destroy(bigint_t *number); bigint_result_t bigint_print(const bigint_t *number); diff --git a/tests/test_vector b/tests/test_vector deleted file mode 100755 index c9066fd93a29669f4a64f9b25b037a060f5a07ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64976 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 From fd07e27337d48605dec159fc84fef4a1f37e4de3 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Thu, 13 Nov 2025 16:22:56 +0100 Subject: [PATCH 07/11] Added division algorithm (not working yet) --- README.md | 2 +- src/bigint.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/bigint.h | 1 + usage.c | 26 ++++- 4 files changed, 330 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9a28b4f..49ea15e 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ This will compile the library as well as the `usage.c` file and the unit tests. ## Documentation For additional details about this library (internal design, memory -management, data ownership, etc.) go to the `docs/` folder. +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: diff --git a/src/bigint.c b/src/bigint.c index e2b0c82..340ad89 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -29,6 +29,7 @@ static bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t * static bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y); static bigint_result_t bigint_shift_right(const bigint_t *num, size_t n); static bigint_result_t bigint_reciprocal(const bigint_t *num, size_t precision); +static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y); /** * bigint_from_int @@ -179,7 +180,6 @@ bigint_result_t bigint_from_string(const char *string_num) { int digit = 0; for (int j = 0; j < chunk_len; j++) { - // digit *= 10 + (string_num[start + j] - '0'); digit = digit * 10 + (string_num[start + j] - '0'); } @@ -876,6 +876,271 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) { return result; } +/** + * bigint_div + * @x: a valid non-null big integer + * @y: a valid non-null big integer + * + * Internal method to compute divisions using Newton-Raphson + * algorithm for reciprocal + * + * 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}; + + // Intermediate results + bigint_t *base_result = NULL; + bigint_t *recip = NULL; + bigint_t *q_temp = NULL; + bigint_t *quotient = NULL; + bigint_t *check = NULL; + bigint_t *remainder = NULL; + bigint_t *one = NULL; + bigint_t *new_quotient = 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; + } + } + + // If |x| < |y| then result is zero + tmp_res = bigint_compare_abs(x, y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } + + if (tmp_res.value.compare_status < 0) { + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } + + result.value.number = tmp_res.value.number; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + // Use "grade-school division" for small divisors + if (y_size <= 100) { + vector_result_t y_digit_res = vector_get(y->digits, 0); + if (y_digit_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_digit_res.message); + + return result; + } + + int *y_digit = (int*)y_digit_res.value.element; + + // special case: division by 1 + if (*y_digit == 1) { + tmp_res = bigint_clone(x); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } + + base_result = tmp_res.value.number; + base_result->is_negative = (x->is_negative != y->is_negative); + + result.value.number = base_result; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + // Single digit division + base_result = malloc(sizeof(bigint_t)); + if (base_result == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for result"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); + if (vec_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + free(base_result); + + return result; + } + + base_result->digits = vec_res.value.vector; + base_result->is_negative = false; + + long long remainder_val = 0; + long long divisor = *y_digit; + + for (int idx = vector_size(x->digits) - 1; idx >= 0; idx--) { + vector_result_t x_digit_res = vector_get(x->digits, idx); + if (x_digit_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, x_digit_res.message); + bigint_destroy(base_result); + + return result; + } + + int *x_digit = (int*)x_digit_res.value.element; + + remainder_val = remainder_val * BIGINT_BASE + *x_digit; + int quotient_digit = remainder_val / divisor; + remainder_val %= divisor; + + vector_result_t push_res = vector_push(base_result->digits, "ient_digit); + if (push_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + bigint_destroy(base_result); + + return result; + } + } + + // Reverse the digits + const size_t rev_size = vector_size(base_result->digits); + for (size_t idx = 0; idx < rev_size / 2; idx++) { + vector_result_t left_res = vector_get(base_result->digits, idx); + vector_result_t right_res = vector_get(base_result->digits, rev_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"); + bigint_destroy(base_result); + + return result; + } + + int *left = (int*)left_res.value.element; + int *right = (int*)right_res.value.element; + int temp = *left; + + // We ignore return status since we already checked that indexes are valid + vector_set(base_result->digits, idx, right); + vector_set(base_result->digits, rev_size - 1 - idx, &temp); + } + + base_result->is_negative = (x->is_negative != y->is_negative); + + tmp_res = bigint_trim_zeros(base_result); + if (tmp_res.status != BIGINT_OK) { + result = tmp_res; + bigint_destroy(base_result); + + return result; + } + + result.value.number = base_result; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + // Otherwise, use Newton-Raphson algorithm + const size_t precision = vector_size(x->digits) + 1; + + // Compute reciprocal of y: r = floor(BASE^(2 * precision) / y) + tmp_res = bigint_reciprocal(y, precision); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + recip = tmp_res.value.number; + + // Multiply x by reciprocal: x = x * r + tmp_res = bigint_prod(x, recip); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + q_temp = tmp_res.value.number; + + // Scale down by BASE^(2 * precision) to get quotient + tmp_res = bigint_shift_right(q_temp, 2 * precision); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + quotient = tmp_res.value.number; + + // Adjust if necessary since quotient might be off by 1 + tmp_res = bigint_prod(quotient, y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + check = tmp_res.value.number; + + tmp_res = bigint_sub(x, check); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + remainder = tmp_res.value.number; + + // If remainder >= y then increment quotient + tmp_res = bigint_compare_abs(remainder, y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + if (tmp_res.value.compare_status >= 0) { + tmp_res = bigint_from_int(1); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + one = tmp_res.value.number; + + tmp_res = bigint_add(quotient, one); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + new_quotient = tmp_res.value.number; + + bigint_destroy(quotient); + quotient = new_quotient; + new_quotient = NULL; + } + + 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; } + + // Destroy intermediate allocations except for the quotient + bigint_destroy(recip); + bigint_destroy(q_temp); + bigint_destroy(check); + bigint_destroy(remainder); + bigint_destroy(one); + + result.value.number = quotient; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + +cleanup: // Destroy intermediate allocations + if (recip) { bigint_destroy(recip); } + if (q_temp) { bigint_destroy(q_temp); } + if (quotient) { bigint_destroy(quotient); } + if (check) { bigint_destroy(check); } + if (remainder) { bigint_destroy(remainder); } + if (one) { bigint_destroy(one); } + if (new_quotient) { bigint_destroy(new_quotient); } + + return result; +} + /** * bigint_divmod * @x: a valid non-null big integer @@ -986,12 +1251,47 @@ cleanup: 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 * - * Shift left by @n digits (i.e., multiply by BASE^n) + * Shifts left by @n digits (i.e., multiply by BASE^n) * * Returns a bigint_result_t data type */ @@ -1734,7 +2034,7 @@ bigint_result_t bigint_print(const bigint_t *number) { return num_str_res; } - char *number_str = num_str_res.value.string_num; + char* const number_str = num_str_res.value.string_num; printf("%s", number_str); free(number_str); diff --git a/src/bigint.h b/src/bigint.h index 8e60b49..d33186f 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -53,6 +53,7 @@ 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_print(const bigint_t *number); diff --git a/usage.c b/usage.c index a121518..2bffa89 100644 --- a/usage.c +++ b/usage.c @@ -304,7 +304,7 @@ int map_usage() { } int bigint_usage() { - // Create a big integer + // 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); @@ -367,6 +367,30 @@ int bigint_usage() { bigint_print(prod); printf("\n"); + // Divide two big integers + bigint_t *a = bigint_from_string("4573495456849").value.number; + bigint_t *b = bigint_from_string("4356987654321").value.number; + 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 *q = div_res.value.division.quotient; + bigint_t *r = div_res.value.division.remainder; + + // Print result + printf("a / b = "); + bigint_print(q); + printf("\n"); + bigint_print(r); + printf("\n"); + + bigint_destroy(a); bigint_destroy(b); + bigint_destroy(q); bigint_destroy(r); + + bigint_destroy(x); bigint_destroy(y); bigint_destroy(sum); bigint_destroy(diff); bigint_destroy(prod); From 3ff89c8d3557a06758a2add32ab8d4da9a207e59 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Mon, 17 Nov 2025 10:54:23 +0100 Subject: [PATCH 08/11] Implemented long division algorithm and added printf for big integers --- src/bigint.c | 726 ++++++++++++++++---------------------------- src/bigint.h | 2 +- tests/test_vector.c | 2 +- usage.c | 40 ++- 4 files changed, 287 insertions(+), 483 deletions(-) diff --git a/src/bigint.c b/src/bigint.c index 340ad89..31069d2 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "bigint.h" #include "vector.h" @@ -27,8 +28,6 @@ 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_shift_right(const bigint_t *num, size_t n); -static bigint_result_t bigint_reciprocal(const bigint_t *num, size_t precision); static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y); /** @@ -876,270 +875,7 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) { return result; } -/** - * bigint_div - * @x: a valid non-null big integer - * @y: a valid non-null big integer - * - * Internal method to compute divisions using Newton-Raphson - * algorithm for reciprocal - * - * 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}; - // Intermediate results - bigint_t *base_result = NULL; - bigint_t *recip = NULL; - bigint_t *q_temp = NULL; - bigint_t *quotient = NULL; - bigint_t *check = NULL; - bigint_t *remainder = NULL; - bigint_t *one = NULL; - bigint_t *new_quotient = 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; - } - } - - // If |x| < |y| then result is zero - tmp_res = bigint_compare_abs(x, y); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } - - if (tmp_res.value.compare_status < 0) { - tmp_res = bigint_from_int(0); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } - - result.value.number = tmp_res.value.number; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - } - - // Use "grade-school division" for small divisors - if (y_size <= 100) { - vector_result_t y_digit_res = vector_get(y->digits, 0); - if (y_digit_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, y_digit_res.message); - - return result; - } - - int *y_digit = (int*)y_digit_res.value.element; - - // special case: division by 1 - if (*y_digit == 1) { - tmp_res = bigint_clone(x); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; return result; } - - base_result = tmp_res.value.number; - base_result->is_negative = (x->is_negative != y->is_negative); - - result.value.number = base_result; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - } - - // Single digit division - base_result = malloc(sizeof(bigint_t)); - if (base_result == NULL) { - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for result"); - - return result; - } - - vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); - if (vec_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_ALLOCATE; - COPY_MSG(result, vec_res.message); - free(base_result); - - return result; - } - - base_result->digits = vec_res.value.vector; - base_result->is_negative = false; - - long long remainder_val = 0; - long long divisor = *y_digit; - - for (int idx = vector_size(x->digits) - 1; idx >= 0; idx--) { - vector_result_t x_digit_res = vector_get(x->digits, idx); - if (x_digit_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, x_digit_res.message); - bigint_destroy(base_result); - - return result; - } - - int *x_digit = (int*)x_digit_res.value.element; - - remainder_val = remainder_val * BIGINT_BASE + *x_digit; - int quotient_digit = remainder_val / divisor; - remainder_val %= divisor; - - vector_result_t push_res = vector_push(base_result->digits, "ient_digit); - if (push_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - bigint_destroy(base_result); - - return result; - } - } - - // Reverse the digits - const size_t rev_size = vector_size(base_result->digits); - for (size_t idx = 0; idx < rev_size / 2; idx++) { - vector_result_t left_res = vector_get(base_result->digits, idx); - vector_result_t right_res = vector_get(base_result->digits, rev_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"); - bigint_destroy(base_result); - - return result; - } - - int *left = (int*)left_res.value.element; - int *right = (int*)right_res.value.element; - int temp = *left; - - // We ignore return status since we already checked that indexes are valid - vector_set(base_result->digits, idx, right); - vector_set(base_result->digits, rev_size - 1 - idx, &temp); - } - - base_result->is_negative = (x->is_negative != y->is_negative); - - tmp_res = bigint_trim_zeros(base_result); - if (tmp_res.status != BIGINT_OK) { - result = tmp_res; - bigint_destroy(base_result); - - return result; - } - - result.value.number = base_result; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - } - - // Otherwise, use Newton-Raphson algorithm - const size_t precision = vector_size(x->digits) + 1; - - // Compute reciprocal of y: r = floor(BASE^(2 * precision) / y) - tmp_res = bigint_reciprocal(y, precision); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - recip = tmp_res.value.number; - - // Multiply x by reciprocal: x = x * r - tmp_res = bigint_prod(x, recip); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - q_temp = tmp_res.value.number; - - // Scale down by BASE^(2 * precision) to get quotient - tmp_res = bigint_shift_right(q_temp, 2 * precision); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - quotient = tmp_res.value.number; - - // Adjust if necessary since quotient might be off by 1 - tmp_res = bigint_prod(quotient, y); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - check = tmp_res.value.number; - - tmp_res = bigint_sub(x, check); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - remainder = tmp_res.value.number; - - // If remainder >= y then increment quotient - tmp_res = bigint_compare_abs(remainder, y); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - if (tmp_res.value.compare_status >= 0) { - tmp_res = bigint_from_int(1); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - one = tmp_res.value.number; - - tmp_res = bigint_add(quotient, one); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - new_quotient = tmp_res.value.number; - - bigint_destroy(quotient); - quotient = new_quotient; - new_quotient = NULL; - } - - 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; } - - // Destroy intermediate allocations except for the quotient - bigint_destroy(recip); - bigint_destroy(q_temp); - bigint_destroy(check); - bigint_destroy(remainder); - bigint_destroy(one); - - result.value.number = quotient; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - -cleanup: // Destroy intermediate allocations - if (recip) { bigint_destroy(recip); } - if (q_temp) { bigint_destroy(q_temp); } - if (quotient) { bigint_destroy(quotient); } - if (check) { bigint_destroy(check); } - if (remainder) { bigint_destroy(remainder); } - if (one) { bigint_destroy(one); } - if (new_quotient) { bigint_destroy(new_quotient); } - - return result; -} /** * bigint_divmod @@ -1218,7 +954,7 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) { if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } quotient = tmp_res.value.number; - // Computed r = x - y * q + // 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; @@ -1776,217 +1512,191 @@ cleanup: // Destroy intermediate allocations on error } /** - * bigint_shift_right - * @num: a valid non-null big integer - * @n: number of digits to shift - * - * Shifts right by @n digits (i.e., divide by BASE^n) - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_shift_right(const bigint_t *num, size_t n) { - bigint_result_t result = {0}; - - const size_t size = vector_size(num->digits); - - if (n >= size) return bigint_from_int(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(size - n, sizeof(int)); - if (vec_res.status != VECTOR_OK) { - free(shifted); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, vec_res.message); - - return result; - } - - shifted->digits = vec_res.value.vector; - shifted->is_negative = num->is_negative; - - // Copy digits from position 'n' onwards - for (size_t idx = n; idx < size; idx++) { - vector_result_t vec_res = vector_get(num->digits, idx); - if (vec_res.status != VECTOR_OK) { - vector_destroy(shifted->digits); - free(shifted); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, vec_res.message); - - return result; - } - - int *digit = (int*)vec_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; - } - } - - bigint_result_t trim_res = bigint_trim_zeros(shifted); - if (trim_res.status != BIGINT_OK) { - vector_destroy(shifted->digits); - free(shifted); - - return trim_res; - } - - result.value.number = shifted; - result.status = BIGINT_OK; - SET_MSG(result, "Big integer shifted successfully"); - - return result; -} - -/** - * bigint_reciprocal - * @num: a valid non-null big integer - * @precision: the precision of the computation - * - * Compute the reciprocal using Newton-Raphson algorithm. - * It calculates 1/num with precision @precision, returning - * floor(BASE^(2 * @precision) / num) + * 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_reciprocal(const bigint_t *num, size_t precision) { +bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; bigint_result_t tmp_res = {0}; - // Results of each steps - bigint_t *x = NULL; - bigint_t *scale = NULL; - bigint_t *two = NULL; - bigint_t *two_scaled = NULL; - bigint_t *dx = NULL; - bigint_t *two_minus_dx = NULL; - bigint_t *x_new_tmp = NULL; - bigint_t *x_new = NULL; - - if (num == NULL) { + 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 integer"); + SET_MSG(result, "Invalid big numbers"); return result; } - const size_t num_size = vector_size(num->digits); - // Get most significant digit - vector_result_t msd_res = vector_get(num->digits, num_size - 1); - if (msd_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, msd_res.message); + // 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; } - int *msd = (int*)msd_res.value.element; + 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); - // x = floor(BASE^2 / (msd + 1)) - const long long initial_val = ((long long)BIGINT_BASE * (long long)BIGINT_BASE) / ((long long)(*msd) + 1LL); - tmp_res = bigint_from_int(initial_val); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - x = tmp_res.value.number; + return result; + } - tmp_res = bigint_from_int(1); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - scale = tmp_res.value.number; + 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"); - // Scale to proper precision. That is scale x by BASE^(2 * precision - 2) - // in order to reach BASE^(2 * precision) magnitude - if (precision > 1) { - tmp_res = bigint_shift_left(scale, 2 * precision - 2); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - bigint_destroy(scale); - scale = tmp_res.value.number; - - tmp_res = bigint_prod(x, scale); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - bigint_destroy(x); - x = tmp_res.value.number; + return result; + } } - // two_scaled = 2 * BASE^(2 * precision) - tmp_res = bigint_from_int(2); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - two = tmp_res.value.number; + // If |x| < |y| then result is zero + tmp_res = bigint_compare_abs(x, y); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } - tmp_res = bigint_shift_left(two, 2 * precision); - 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) { return tmp_res; } - bigint_destroy(two); - two = NULL; - two_scaled = tmp_res.value.number; + result.value.number = tmp_res.value.number; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); - // Determine the number of Newton-Raphson iterations - size_t iterations = 0; - size_t target = precision; - while ((1ULL << iterations) < target) { iterations++; } - iterations += 2; // Add a few more just to be sure - - // x_{n+1} = x_n * (2 * BASE^(2P) - d * x_n) / BASE^(2P) - for (size_t it = 0; it < iterations; it++) { - // dx = d * x - tmp_res = bigint_prod(num, x); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - dx = tmp_res.value.number; - - // two_minus_dx = 2 * BASE^(2P) - dx - tmp_res = bigint_sub(two_scaled, dx); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - two_minus_dx = tmp_res.value.number; - - // x_new_temp = x * (two_minus_dx) - tmp_res = bigint_prod(x, two_minus_dx); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - x_new_tmp = tmp_res.value.number; - - // x_new = x_new_temp >> (2 * precision) - tmp_res = bigint_shift_right(x_new_tmp, 2 * precision); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - x_new = tmp_res.value.number; - - // Rotation pass: replace x with x_new and free intermediates - bigint_destroy(x); - x = x_new; - x_new = NULL; - - bigint_destroy(dx); dx = NULL; - bigint_destroy(two_minus_dx); two_minus_dx = NULL; - bigint_destroy(x_new_tmp); x_new_tmp = NULL; + return result; } - bigint_destroy(scale); - bigint_destroy(two_scaled); + // Initialize quotient and remainder + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + quotient = tmp_res.value.number; - result.value.number = x; + 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, "Reciprocal computed successfully"); + SET_MSG(result, "Division between big integers was successful"); + return result; cleanup: - if (x) { bigint_destroy(x); } - if (scale) { bigint_destroy(scale); } - if (two) { bigint_destroy(two); } - if (two_scaled) { bigint_destroy(two_scaled); } - if (dx) { bigint_destroy(dx); } - if (two_minus_dx) { bigint_destroy(two_minus_dx); } - if (x_new_tmp) { bigint_destroy(x_new_tmp); } - if (x_new) { bigint_destroy(x_new); } + if (quotient) { bigint_destroy(quotient); } + if (remainder) { bigint_destroy(remainder); } + if (abs_y) { bigint_destroy(abs_y); } return result; } @@ -2019,28 +1729,128 @@ bigint_result_t bigint_destroy(bigint_t *number) { } /** - * bigint_print - * @number: a valid non-null big integer + * bigint_printf + * @format: format string + * @...: variadic arguments * - * Prints @number to standard output + * Prints a bigint integer to stdout using the custom '%B' placeholder * * Returns a bigint_result_t data type */ -bigint_result_t bigint_print(const bigint_t *number) { +bigint_result_t bigint_printf(const char *format, ...) { bigint_result_t result = {0}; - bigint_result_t num_str_res = bigint_to_string(number); - if (num_str_res.status != BIGINT_OK) { - return num_str_res; + if (format == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid format string"); + + return result; } - char* const number_str = num_str_res.value.string_num; + 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); + } + } - printf("%s", number_str); - free(number_str); + va_end(args); result.status = BIGINT_OK; - SET_MSG(result, "Big integer successfully printed"); + SET_MSG(result, "Printf completed successfully"); return result; } \ No newline at end of file diff --git a/src/bigint.h b/src/bigint.h index d33186f..984e0fa 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -55,7 +55,7 @@ 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_print(const bigint_t *number); +bigint_result_t bigint_printf(const char *format, ...); #ifdef __cplusplus } diff --git a/tests/test_vector.c b/tests/test_vector.c index 4dc3032..fd1ca59 100644 --- a/tests/test_vector.c +++ b/tests/test_vector.c @@ -208,7 +208,7 @@ void test_vector_sort_string() { // Sort vector with custom data type typedef struct { - char name[256]; + const char *name; int age; } Person; diff --git a/usage.c b/usage.c index 2bffa89..f74cd6d 100644 --- a/usage.c +++ b/usage.c @@ -27,6 +27,7 @@ static int map_usage(); static int bigint_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; @@ -333,9 +334,7 @@ int bigint_usage() { bigint_t *sum = sum_res.value.number; // Print result - printf("123456789 + 987654321 (should be 1,111,111,110) = "); - bigint_print(sum); - printf("\n"); + 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); @@ -348,9 +347,7 @@ int bigint_usage() { bigint_t *diff = diff_res.value.number; // Print result - printf("123456789 - 987654321 (should be -864,197,532) = "); - bigint_print(diff); - printf("\n"); + 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); @@ -363,13 +360,12 @@ int bigint_usage() { bigint_t *prod = prod_res.value.number; // Print result - printf("123456789 * 987654321 (should be 121,932,631,112,635,269) = "); - bigint_print(prod); - printf("\n"); + 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_t *a = bigint_from_string("4573495456849").value.number; - bigint_t *b = bigint_from_string("4356987654321").value.number; 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); @@ -377,22 +373,20 @@ int bigint_usage() { return 1; } - bigint_t *q = div_res.value.division.quotient; - bigint_t *r = div_res.value.division.remainder; + bigint_t *quotient = div_res.value.division.quotient; + bigint_t *remainder = div_res.value.division.remainder; // Print result - printf("a / b = "); - bigint_print(q); - printf("\n"); - bigint_print(r); - printf("\n"); - - bigint_destroy(a); bigint_destroy(b); - bigint_destroy(q); bigint_destroy(r); - + 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(sum); bigint_destroy(diff); bigint_destroy(prod); + bigint_destroy(a); bigint_destroy(b); + bigint_destroy(sum); bigint_destroy(diff); + bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder); return 0; } \ No newline at end of file From f625862ad2cef64d77d5ca75c58622088648a19a Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Mon, 17 Nov 2025 12:22:29 +0100 Subject: [PATCH 09/11] Added new unit tests which lead to bug fix --- Makefile | 8 ++++-- src/bigint.c | 17 ++++++------ tests/test_bigint.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 tests/test_bigint.c diff --git a/Makefile b/Makefile index ae99f38..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 $(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/src/bigint.c b/src/bigint.c index 31069d2..89a042f 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -61,33 +61,34 @@ bigint_result_t bigint_from_int(long long value) { number->digits = vec_res.value.vector; number->is_negative = (value < 0); - if (value < 0) { - value = -value; - } else if (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, vec_res.message); + COPY_MSG(result, push_res.message); return result; } } else { - while (value > 0) { - int digit = value % BIGINT_BASE; + 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, vec_res.message); + COPY_MSG(result, push_res.message); return result; } - value /= BIGINT_BASE; + abs_val /= BIGINT_BASE; } } diff --git a/tests/test_bigint.c b/tests/test_bigint.c new file mode 100644 index 0000000..711a446 --- /dev/null +++ b/tests/test_bigint.c @@ -0,0 +1,63 @@ +/* + * 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 to_str = bigint_to_string(number); + assert(to_str.status == BIGINT_OK); + assert(!strcmp(to_str.value.string_num, expected)); + + free(to_str.value.string_num); +} + +// Test creating big integers from int +void test_bigint_from_int() { + 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() { + bigint_result_t res = bigint_from_string("00000123"); + + assert(res.status == BIGINT_OK); + bigint_eq(res.value.number, "123"); + bigint_destroy(res.value.number); +} + +int main(void) { + printf("=== Running BigInt unit tests ===\n\n"); + + TEST(bigint_from_int); + TEST(bigint_from_string); + + printf("\n=== All tests passed ===\n"); + + return 0; +} \ No newline at end of file From 9e419d09ace77a1f96d1724069068d4f141977ac Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Mon, 17 Nov 2025 16:41:09 +0100 Subject: [PATCH 10/11] Added unit tests for BigInt data type and updated docs --- .github/workflows/clang-build.yml | 19 + .../workflows/{datum.yml => gcc-build.yml} | 13 +- README.md | 19 +- src/bigint.c | 2 +- src/bigint.h | 2 +- src/map.c | 2 +- src/map.h | 2 +- tests/test_bigint.c | 332 +++++++++++++++++- tests/test_map.c | 28 +- tests/test_vector.c | 38 +- usage.c | 14 +- 11 files changed, 402 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/clang-build.yml rename .github/workflows/{datum.yml => gcc-build.yml} (59%) diff --git a/.github/workflows/clang-build.yml b/.github/workflows/clang-build.yml new file mode 100644 index 0000000..e2f1f56 --- /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 + + - 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/README.md b/README.md index 49ea15e..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 @@ -98,19 +99,18 @@ int main(void) { ### `BigInt` usage ```c -#include #include "src/bigint.h" /* - * Compile with: gcc main.c src/bigint.c + * Compile with: gcc -O3 main.c src/bigint.c src/vector.c * Output: 20000! = 1819206320230345134827641... - * Time: real 0m5.482s user 0m5.453s sys 0m0.017 + * 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++) { + 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; @@ -119,17 +119,13 @@ int main(void) { fact = partial_fact; } - printf("%d! = ", n); - bigint_print(fact); - printf("\n"); + 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: @@ -144,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/src/bigint.c b/src/bigint.c index 89a042f..448fdf6 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -1854,4 +1854,4 @@ bigint_result_t bigint_printf(const char *format, ...) { SET_MSG(result, "Printf completed successfully"); return result; -} \ No newline at end of file +} diff --git a/src/bigint.h b/src/bigint.h index 984e0fa..25e119d 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -61,4 +61,4 @@ bigint_result_t bigint_printf(const char *format, ...); } #endif -#endif \ No newline at end of file +#endif diff --git a/src/map.c b/src/map.c index 88a3887..542065c 100644 --- a/src/map.c +++ b/src/map.c @@ -37,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/tests/test_bigint.c b/tests/test_bigint.c index 711a446..0cc5aad 100644 --- a/tests/test_bigint.c +++ b/tests/test_bigint.c @@ -16,15 +16,21 @@ #include "../src/bigint.h" static void bigint_eq(const bigint_t *number, const char *expected) { - bigint_result_t to_str = bigint_to_string(number); - assert(to_str.status == BIGINT_OK); - assert(!strcmp(to_str.value.string_num, 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); - free(to_str.value.string_num); + 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 test_bigint_from_int(void) { bigint_result_t res = bigint_from_int(0); assert(res.status == BIGINT_OK); @@ -43,21 +49,333 @@ void test_bigint_from_int() { } // Test creating big integers from string -void test_bigint_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; -} \ No newline at end of file +} 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.c b/tests/test_vector.c index fd1ca59..875c9cf 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); @@ -142,7 +142,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); @@ -183,7 +183,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); @@ -234,7 +234,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); @@ -266,7 +266,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); @@ -305,7 +305,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 +324,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 +340,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 +361,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 +374,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 +396,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 +426,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 +449,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 f74cd6d..d1eb05e 100644 --- a/usage.c +++ b/usage.c @@ -22,9 +22,9 @@ #include "src/map.h" #include "src/bigint.h" -static int vector_usage(); -static int map_usage(); -static int bigint_usage(); +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); @@ -62,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) { @@ -203,7 +203,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) { @@ -304,7 +304,7 @@ int map_usage() { return 0; } -int bigint_usage() { +int bigint_usage(void) { // Create two big integers bigint_result_t x_res = bigint_from_string("123456789"); if (x_res.status != BIGINT_OK) { @@ -389,4 +389,4 @@ int bigint_usage() { bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder); return 0; -} \ No newline at end of file +} From c658c931825c4e5bbd2fd21b67ceb877dc62c44d Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Tue, 18 Nov 2025 11:27:34 +0100 Subject: [PATCH 11/11] Fixed minor bugs and added documentation for big integers --- .github/workflows/clang-build.yml | 2 +- docs/bigint.md | 92 +++++++++++++++++++++++++++++++ docs/map.md | 2 +- docs/sort.md | 18 +++--- docs/vector.md | 2 +- src/bigint.c | 20 ++++--- src/bigint.h | 2 +- tests/test_vector.c | 20 +++++-- usage.c | 11 ++-- 9 files changed, 141 insertions(+), 28 deletions(-) create mode 100644 docs/bigint.md diff --git a/.github/workflows/clang-build.yml b/.github/workflows/clang-build.yml index e2f1f56..1e7b286 100644 --- a/.github/workflows/clang-build.yml +++ b/.github/workflows/clang-build.yml @@ -12,7 +12,7 @@ jobs: - name: Build Datum run: | - make clean all + make clean all CC=clang - name: Run unit tests run: | 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 index 448fdf6..e7835b5 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -315,7 +315,8 @@ bigint_result_t bigint_clone(const bigint_t *number) { cloned->is_negative = number->is_negative; // Copy digits - for (size_t idx = 0; idx < vector_size(number->digits); idx++) { + 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); @@ -546,10 +547,12 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { long long carry = 0; size_t idx = 0; - while (idx < vector_size(x->digits) || idx < vector_size(y->digits) || carry) { + 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 < vector_size(x->digits)) { + if (idx < x_size) { vector_result_t get_res = vector_get(x->digits, idx); if (get_res.status != VECTOR_OK) { vector_destroy(sum->digits); @@ -564,7 +567,7 @@ bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { partial_sum += *x_digit; } - if (idx < vector_size(y->digits)) { + if (idx < y_size) { vector_result_t get_res = vector_get(y->digits, idx); if (get_res.status != VECTOR_OK) { vector_destroy(sum->digits); @@ -643,7 +646,9 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { long long borrow = 0; - for (size_t idx = 0; idx < vector_size(x->digits); idx++) { + 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); @@ -657,7 +662,7 @@ bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { int *x_digit = (int*)x_get_res.value.element; long long partial_difference = *x_digit - borrow; - if (idx < vector_size(y->digits)) { + 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); @@ -1074,7 +1079,8 @@ bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { } // Copy back original digits - for (size_t idx = 0; idx < vector_size(num->digits); idx++) { + 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); diff --git a/src/bigint.h b/src/bigint.h index 25e119d..40b9c34 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -3,7 +3,7 @@ #define RESULT_MSG_SIZE 64 -// Big numbers numerical base (10^9) +// Numerical base (10^9) #define BIGINT_BASE 1000000000 // Each digit stores values from 0 to 999,999,999 #define BIGINT_BASE_DIGITS 9 diff --git a/tests/test_vector.c b/tests/test_vector.c index 875c9cf..a6d6163 100644 --- a/tests/test_vector.c +++ b/tests/test_vector.c @@ -134,7 +134,9 @@ void test_vector_sort_int_asc(void) { 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]); } @@ -157,7 +159,9 @@ void test_vector_sort_int_desc(void) { 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]); } @@ -198,7 +202,9 @@ void test_vector_sort_string(void) { 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])); } @@ -208,7 +214,7 @@ void test_vector_sort_string(void) { // Sort vector with custom data type typedef struct { - const char *name; + char name[256]; int age; } Person; @@ -257,7 +263,8 @@ void test_vector_sort_struct_by_age(void) { { .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); @@ -295,7 +302,8 @@ void test_vector_sort_struct_by_name(void) { { .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); diff --git a/usage.c b/usage.c index d1eb05e..4db9d0a 100644 --- a/usage.c +++ b/usage.c @@ -88,7 +88,8 @@ int vector_usage(void) { 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); @@ -136,7 +137,9 @@ int vector_usage(void) { } 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); @@ -157,7 +160,7 @@ int vector_usage(void) { } 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); @@ -179,7 +182,7 @@ int vector_usage(void) { } 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);