diff --git a/src/bigint.c b/src/bigint.c index 6cc491e..dd73dcb 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -20,15 +20,1016 @@ #include "vector.h" // Internal methods -static bigint_result_t bigint_trim_zeros(bigint_t *number); -static bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y); -static bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y); -static bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y); -static bigint_result_t bigint_shift_left(const bigint_t *num, size_t n); -static bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, bigint_t **low); -static bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y); -static bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y); -static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y); +/** + * bigint_trim_zeros + * @number: a non-null big integer + * + * Helper function to remove leading zeros + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_trim_zeros(bigint_t *number) { + bigint_result_t result = {0}; + + size_t number_len = vector_size(number->digits); + + while (number_len > 1) { + vector_result_t get_res = vector_get(number->digits, number_len - 1); + if (get_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *last = (int*)get_res.value.element; + if (*last != 0) { + break; + } + + vector_result_t pop_res = vector_pop(number->digits); + if (pop_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + number_len--; + } + + if (number_len == 1) { + vector_result_t get_res = vector_get(number->digits, number_len - 1); + if (get_res.status != VECTOR_OK) { + vector_destroy(number->digits); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *first = (int*)get_res.value.element; + if (*first == 0) { + number->is_negative = false; + } + } + + result.status = BIGINT_OK; + SET_MSG(result, "Big integer successfully trimmed"); + + return result; + +} + +/** + * bigint_compare_abs + * @x: a non-null big integer + * @y: a non-null big integer + * + * Compares absolute value of two big integers + * if |x| < |y| => -1 + * if |x| == |y| => 0 + * if |x| > |y| => 1 + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + if (x_size != y_size) { + result.value.compare_status = (x_size > y_size) ? 1 : -1; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + + // Start to compare from the MSB + for (int idx = (int)(x_size - 1); idx >= 0; idx--) { + vector_result_t x_get = vector_get(x->digits, idx); + if (x_get.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, x_get.message); + + return result; + } + + vector_result_t y_get = vector_get(y->digits, idx); + if (y_get.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_get.message); + + return result; + } + + int *x_digit = (int*)x_get.value.element; + int *y_digit = (int*)y_get.value.element; + + if (*x_digit != *y_digit) { + result.value.compare_status = (*x_digit > *y_digit) ? 1 : -1; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; + } + } + + result.value.compare_status = 0; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer comparison was successful"); + + return result; +} + +/** + * bigint_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 + */ +static bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_t *sum = malloc(sizeof(bigint_t)); + if (sum == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Cannot allocate memory for big integer"); + + return result; + } + + const size_t max_size = vector_size(x->digits) > vector_size(y->digits) ? + vector_size(x->digits) : vector_size(y->digits); + + vector_result_t vec_res = vector_new(max_size + 1, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + sum->digits = vec_res.value.vector; + sum->is_negative = false; + + long long carry = 0; + size_t idx = 0; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + while (idx < x_size || idx < y_size || carry) { + long long partial_sum = carry; + + if (idx < x_size) { + vector_result_t get_res = vector_get(x->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *x_digit = (int*)get_res.value.element; + partial_sum += *x_digit; + } + + if (idx < y_size) { + vector_result_t get_res = vector_get(y->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *y_digit = (int*)get_res.value.element; + partial_sum += *y_digit; + } + + int digit = partial_sum % BIGINT_BASE; + carry = partial_sum / BIGINT_BASE; + + vector_result_t push_res = vector_push(sum->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(sum->digits); + free(sum); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + idx++; + } + + bigint_result_t trim_res = bigint_trim_zeros(sum); + if (trim_res.status != BIGINT_OK) { + vector_destroy(sum->digits); + free(sum); + + return trim_res; + } + + result.value.number = sum; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully added"); + + return result; +} + +/** + * bigint_sub_abs + * @x: a non-null big integer + * @y: a non-null big integer + * + * Subtracts two absolute values assuming that |x| >= |y| + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_t *difference = malloc(sizeof(bigint_t)); + if (difference == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Cannot allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, vec_res.message); + + return result; + } + + difference->digits = vec_res.value.vector; + difference->is_negative = false; + + long long borrow = 0; + + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + for (size_t idx = 0; idx < x_size; idx++) { + vector_result_t x_get_res = vector_get(x->digits, idx); + if (x_get_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, x_get_res.message); + + return result; + } + + int *x_digit = (int*)x_get_res.value.element; + long long partial_difference = *x_digit - borrow; + + if (idx < y_size) { + vector_result_t y_get_res = vector_get(y->digits, idx); + if (y_get_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_get_res.message); + + return result; + } + + int *y_digit = (int*)y_get_res.value.element; + partial_difference -= *y_digit; + } + + if (partial_difference < 0) { + partial_difference += BIGINT_BASE; + borrow = 1; + } else { + borrow = 0; + } + + int digit = partial_difference; + vector_result_t push_res = vector_push(difference->digits, &digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(difference->digits); + free(difference); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + bigint_result_t trim_res = bigint_trim_zeros(difference); + if (trim_res.status != BIGINT_OK) { + vector_destroy(difference->digits); + free(difference); + + return trim_res; + } + + result.value.number = difference; + result.status = BIGINT_OK; + SET_MSG(result, "Big integers successfully subtracted"); + + return result; +} + +/** + * bigint_shift_left + * @num: a non-null big integer + * @n: number of digits to shift + * + * Shifts left by @n digits (i.e., multiply by BASE^n) + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { + bigint_result_t result = {0}; + + if (n == 0) { + return bigint_clone(num); + } + + bigint_t *shifted = malloc(sizeof(bigint_t)); + if (shifted == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t vec_res = vector_new(vector_size(num->digits) + n, sizeof(int)); + if (vec_res.status != VECTOR_OK) { + free(shifted); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, vec_res.message); + + return result; + } + + shifted->digits = vec_res.value.vector; + shifted->is_negative = num->is_negative; + + // Add 'n' zeros by starting from the LSB + int zero = 0; + for (size_t idx = 0; idx < n; idx++) { + vector_result_t push_res = vector_push(shifted->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // Copy back original digits + const size_t num_size = vector_size(num->digits); + for (size_t idx = 0; idx < num_size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push(shifted->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy(shifted->digits); + free(shifted); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + result.value.number = shifted; + result.status = BIGINT_OK; + SET_MSG(result, "Big integer shifted successfully"); + + return result; +} + +/** + * bigint_split + * @num: a non-null big integers + * @m: the pivot/position where to split + * @high: digits \in [0, m) + * @low: digits \in [m, size) + * + * Splits number into @high and @low parts at position @m + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, bigint_t **low) { + bigint_result_t result = {0}; + + const size_t size = vector_size(num->digits); + + // Low part: digits \in [0, m) + *low = malloc(sizeof(bigint_t)); + if (*low == NULL) { + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t low_res = vector_new(m ? m : 1, sizeof(int)); + if (low_res.status != VECTOR_OK) { + free(*low); + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, low_res.message); + + return result; + } + + (*low)->digits = low_res.value.vector; + (*low)->is_negative = false; + + for (size_t idx = 0; idx < m && idx < size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push((*low)->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + if (vector_size((*low)->digits) == 0) { + int zero = 0; + vector_result_t push_res = vector_push((*low)->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // First pass of zero trimming + bigint_result_t first_trim_res = bigint_trim_zeros(*low); + if (first_trim_res.status != BIGINT_OK) { + vector_destroy((*low)->digits); + free(*low); + + return first_trim_res; + } + + // High part: digits \in [m, size) + *high = malloc(sizeof(bigint_t)); + if (*high == NULL) { + vector_destroy((*low)->digits); + free(*low); + result.status = BIGINT_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for big integer"); + + return result; + } + + vector_result_t high_res = vector_new(size > m ? (size - m) : 1, sizeof(int)); + if (high_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, low_res.message); + + return result; + } + + (*high)->digits = high_res.value.vector; + (*high)->is_negative = false; + + if (size > m) { + for (size_t idx = m; idx < size; idx++) { + vector_result_t get_res = vector_get(num->digits, idx); + if (get_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *digit = (int*)get_res.value.element; + vector_result_t push_res = vector_push((*high)->digits, digit); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + } else { + int zero = 0; + vector_result_t push_res = vector_push((*high)->digits, &zero); + if (push_res.status != VECTOR_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + + // Second pass of zero trimming + bigint_result_t second_trim_res = bigint_trim_zeros(*high); + if (second_trim_res.status != BIGINT_OK) { + vector_destroy((*low)->digits); + vector_destroy((*high)->digits); + free(*low); + free(*high); + + return second_trim_res; + } + + result.status = BIGINT_OK; + SET_MSG(result, "Big number successfully splitted"); + + return result; +} + +/** + * bigint_karatsuba_base + * @x: a non-null big integer + * @y: a non-null big integer + * + * Base case of the Karatsuba recursive algorithm + * which uses a "grade school" multiplication. + * Its complexity is O(n^2) + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + + bigint_result_t prod_res = bigint_from_int(0); + if (prod_res.status != BIGINT_OK) { + result.status = BIGINT_ERR_ALLOCATE; + COPY_MSG(result, prod_res.message); + + return result; + } + + bigint_t *product = prod_res.value.number; + const size_t x_size = vector_size(x->digits); + const size_t y_size = vector_size(y->digits); + + for (size_t i = 0; i < x_size; i++) { + long long carry = 0; + + vector_result_t get_res = vector_get(x->digits, i); + if (get_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, get_res.message); + + return result; + } + + int *x_digit = (int*)get_res.value.element; + for (size_t j = 0; j < y_size || carry; j++) { + int *y_digit = NULL; + int *curr = NULL; + + if (j < y_size) { + vector_result_t y_res = vector_get(y->digits, j); + if (y_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_res.message); + + return result; + } + + y_digit = (int*)y_res.value.element; + } + + if ((i + j) < vector_size(product->digits)) { + vector_result_t curr_res = vector_get(product->digits, i + j); + if (curr_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, curr_res.message); + + return result; + } + + curr = (int*)curr_res.value.element; + } + + long long partial_prod = carry; + if (curr) { partial_prod += *curr; } + if (y_digit) { partial_prod += (long long)(*x_digit) * (*y_digit); } + + int new_digit =(int)(partial_prod % BIGINT_BASE); + carry = partial_prod / BIGINT_BASE; + + if (curr) { + vector_result_t set_res = vector_set(product->digits, i + j, &new_digit); + if (set_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, set_res.message); + + return result; + } + } else { + vector_result_t push_res = vector_push(product->digits, &new_digit); + if (push_res.status != VECTOR_OK) { + bigint_destroy(product); + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + return result; + } + } + } + } + + bigint_result_t trim_res = bigint_trim_zeros(product); + if (trim_res.status != BIGINT_OK) { + bigint_destroy(product); + + return trim_res; + } + + result.value.number = product; + result.status = BIGINT_OK; + SET_MSG(result, "Product between big integers was successful"); + + return result; +} + +/** + * bigint_karatusba + * @x: a non-null big integer + * @y: a non-null big integer + * + * Perform a multiplication using Karatsuba recursive algorithm + * in O(n^{\log_2 3}) \approx O(n^{1.585}) + */ +static 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); + const size_t min_size = x_size < y_size ? x_size : y_size; + const size_t max_size = x_size > y_size ? x_size : y_size; + + // Base case using "grade school" quadratic algorithm + if (min_size <= 32 || max_size / min_size > 2) { + return bigint_karatsuba_base(x, y); + } + + // Split the big integer at approximately half the size of the larger number + const size_t pivot = (x_size > y_size ? x_size : y_size) / 2; + + // Results of each step + bigint_t *x1 = NULL, *x0 = NULL; + bigint_t *y1 = NULL, *y0 = NULL; + bigint_t *z0 = NULL, *z2 = NULL; + bigint_t *x_sum = NULL, *y_sum = NULL; + bigint_t *z1_temp = NULL, *z1_sub1 = NULL, *z1 = NULL; + bigint_t *z2_shifted = NULL, *z1_shifted = NULL; + bigint_t *temp = NULL, *product = NULL; + + // Split x = x1 * BASE^pivot + x0 + tmp_res = bigint_split(x, pivot, &x1, &x0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // Split y = y1 * BASE^pivot + y0 + tmp_res = bigint_split(y, pivot, &y1, &y0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // Perform karatsuba's trick + tmp_res = bigint_karatsuba(x0, y0); // z0 = x0 * y0 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z0 = tmp_res.value.number; + + tmp_res = bigint_karatsuba(x1, y1); // z2 = x1 * y1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z2 = tmp_res.value.number; + + // z1 = (x0 + x1) * (y0 + y1) - z0 - z2 + tmp_res = bigint_add(x0, x1); // x_sum = x0 + x1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + x_sum = tmp_res.value.number; + + tmp_res = bigint_add(y0, y1); // y_sum = y0 + y1 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + y_sum = tmp_res.value.number; + + tmp_res = bigint_karatsuba(x_sum, y_sum); // z1_temp = (x0 + x1) * (y0 + y1) + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_temp = tmp_res.value.number; + + tmp_res = bigint_sub(z1_temp, z0); // z1_sub1 = z1_temp - z0 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_sub1 = tmp_res.value.number; + + tmp_res = bigint_sub(z1_sub1, z2); // z1 = z1_sub1 - z2 + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1 = tmp_res.value.number; + + tmp_res = bigint_shift_left(z2, 2 * pivot); // z2_shifted = z2 << (2 * pivot) + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z2_shifted = tmp_res.value.number; + + tmp_res = bigint_shift_left(z1, pivot); // z1_shifted = z1 << pivot + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + z1_shifted = tmp_res.value.number; + + tmp_res = bigint_add(z2_shifted, z1_shifted); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + temp = tmp_res.value.number; + + tmp_res = bigint_add(temp, z0); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + product = tmp_res.value.number; + + // Destroy intermediate allocations except for the product + bigint_destroy(x1); bigint_destroy(x0); + bigint_destroy(y1); bigint_destroy(y0); + bigint_destroy(z0); bigint_destroy(z2); + bigint_destroy(x_sum); bigint_destroy(y_sum); + bigint_destroy(z1_temp); bigint_destroy(z1_sub1); + bigint_destroy(z1); bigint_destroy(z2_shifted); + bigint_destroy(z1_shifted); bigint_destroy(temp); + + result.value.number = product; + result.status = BIGINT_OK; + SET_MSG(result, "Product between big integers was successful"); + + return result; + +cleanup: // Destroy intermediate allocations on error + if (x1) { bigint_destroy(x1); } + if (x0) { bigint_destroy(x0); } + if (y1) { bigint_destroy(y1); } + if (y0) { bigint_destroy(y0); } + if (z0) { bigint_destroy(z0); } + if (z2) { bigint_destroy(z2); } + if (x_sum) { bigint_destroy(x_sum); } + if (y_sum) { bigint_destroy(y_sum); } + if (z1_temp) { bigint_destroy(z1_temp); } + if (z1_sub1) { bigint_destroy(z1_sub1); } + if (z1) { bigint_destroy(z1); } + if (z2_shifted) { bigint_destroy(z2_shifted); } + if (z1_shifted) { bigint_destroy(z1_shifted); } + if (temp) { bigint_destroy(temp); } + if (product) { bigint_destroy(product); } + + return result; +} + +/** + * bigint_dev + * @x: a valid non-null big integer (dividend) + * @y: a valid non-null big integer (divisor) + * + * Computes division using long division algorithm in O(n^2) + * + * Returns a bigint_result_t data type + */ +static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) { + bigint_result_t result = {0}; + bigint_result_t tmp_res = {0}; + + bigint_t *quotient = NULL; + bigint_t *remainder = NULL; + bigint_t *abs_y = NULL; + + if (x == NULL || y == NULL) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Invalid big numbers"); + + return result; + } + + // Check for division by zero + const size_t y_size = vector_size(y->digits); + if (y_size == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Cannot divide by zero"); + + return result; + } + + if (y_size == 1) { + vector_result_t y_val_res = vector_get(y->digits, 0); + if (y_val_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, y_val_res.message); + + return result; + } + + int *y_val = (int*)y_val_res.value.element; + if (*y_val == 0) { + result.status = BIGINT_ERR_DIV_BY_ZERO; + SET_MSG(result, "Cannot divide by zero"); + + return result; + } + } + + // If |x| < |y| then result is zero + tmp_res = bigint_compare_abs(x, y); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + + if (tmp_res.value.compare_status < 0) { + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + + result.value.number = tmp_res.value.number; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + } + + // Initialize quotient and remainder + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { return tmp_res; } + quotient = tmp_res.value.number; + + tmp_res = bigint_from_int(0); + if (tmp_res.status != BIGINT_OK) { bigint_destroy(quotient); return tmp_res; } + remainder = tmp_res.value.number; + + // Create absolute value of y for later comparisons + tmp_res = bigint_clone(y); + if (tmp_res.status != BIGINT_OK) { + bigint_destroy(quotient); + bigint_destroy(remainder); + + return tmp_res; + } + + abs_y = tmp_res.value.number; + abs_y->is_negative = false; + + // Long division algorithm applied from MSB to LSB + const size_t x_size = vector_size(x->digits); + for (int idx = (int)x_size - 1; idx >= 0; idx--) { + // Shift remainder left by one base digit (multiplication by BASE) + tmp_res = bigint_shift_left(remainder, 1); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_t *shifted_remainder = tmp_res.value.number; + bigint_destroy(remainder); + remainder = shifted_remainder; + + // Add current digit of 'x' to the least significant position of remainder + vector_result_t digit_res = vector_get(x->digits, idx); + if (digit_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, digit_res.message); + + goto cleanup; + } + + int *x_digit = (int*)digit_res.value.element; + + vector_result_t set_res = vector_set(remainder->digits, 0, x_digit); + if (set_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, set_res.message); + + goto cleanup; + } + + tmp_res = bigint_trim_zeros(remainder); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + // COunt how many times 'y' fits into current remainder + size_t count = 0; + while (1) { + tmp_res = bigint_compare_abs(remainder, abs_y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + if (tmp_res.value.compare_status < 0) { break; } // remainder < abs_y + + // remainder = remainder - abs_y + tmp_res = bigint_sub_abs(remainder, abs_y); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_t *new_remainder = tmp_res.value.number; + bigint_destroy(remainder); + remainder = new_remainder; + count++; + } + + // Add count to quotient digits + vector_result_t push_res = vector_push(quotient->digits, &count); + if (push_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + COPY_MSG(result, push_res.message); + + goto cleanup; + } + } + + // Reverse quotient digits + const size_t q_size = vector_size(quotient->digits); + for (size_t idx = 0; idx < q_size / 2; idx++) { + vector_result_t left_res = vector_get(quotient->digits, idx); + vector_result_t right_res = vector_get(quotient->digits, q_size - 1 - idx); + + if (left_res.status != VECTOR_OK || right_res.status != VECTOR_OK) { + result.status = BIGINT_ERR_INVALID; + SET_MSG(result, "Failed to access vector elements"); + + goto cleanup; + } + + int *left = (int*)left_res.value.element; + int *right = (int*)right_res.value.element; + int temp = *left; + + vector_set(quotient->digits, idx, right); + vector_set(quotient->digits, q_size - 1 - idx, &temp); + } + + quotient->is_negative = (x->is_negative != y->is_negative); + + tmp_res = bigint_trim_zeros(quotient); + if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } + + bigint_destroy(remainder); + bigint_destroy(abs_y); + + result.value.number = quotient; + result.status = BIGINT_OK; + SET_MSG(result, "Division between big integers was successful"); + + return result; + +cleanup: + if (quotient) { bigint_destroy(quotient); } + if (remainder) { bigint_destroy(remainder); } + if (abs_y) { bigint_destroy(abs_y); } + + return result; +} /** * bigint_from_int @@ -347,131 +1348,6 @@ bigint_result_t bigint_clone(const bigint_t *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); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *last = (int*)get_res.value.element; - if (*last != 0) { - break; - } - - vector_result_t pop_res = vector_pop(number->digits); - if (pop_res.status != VECTOR_OK) { - vector_destroy(number->digits); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - number_len--; - } - - if (number_len == 1) { - vector_result_t get_res = vector_get(number->digits, number_len - 1); - if (get_res.status != VECTOR_OK) { - vector_destroy(number->digits); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *first = (int*)get_res.value.element; - if (*first == 0) { - number->is_negative = false; - } - } - - result.status = BIGINT_OK; - SET_MSG(result, "Big integer successfully trimmed"); - - return result; - -} - -/** - * bigint_compare_abs - * @x: a non-null big integer - * @y: a non-null big integer - * - * Compares absolute value of two big integers - * if |x| < |y| => -1 - * if |x| == |y| => 0 - * if |x| > |y| => 1 - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_compare_abs(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - - const size_t x_size = vector_size(x->digits); - const size_t y_size = vector_size(y->digits); - - if (x_size != y_size) { - result.value.compare_status = (x_size > y_size) ? 1 : -1; - result.status = BIGINT_OK; - SET_MSG(result, "Big integer comparison was successful"); - - return result; - } - - // Start to compare from the MSB - for (int idx = (int)(x_size - 1); idx >= 0; idx--) { - vector_result_t x_get = vector_get(x->digits, idx); - if (x_get.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, x_get.message); - - return result; - } - - vector_result_t y_get = vector_get(y->digits, idx); - if (y_get.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, y_get.message); - - return result; - } - - int *x_digit = (int*)x_get.value.element; - int *y_digit = (int*)y_get.value.element; - - if (*x_digit != *y_digit) { - result.value.compare_status = (*x_digit > *y_digit) ? 1 : -1; - result.status = BIGINT_OK; - SET_MSG(result, "Big integer comparison was successful"); - - return result; - } - } - - result.value.compare_status = 0; - result.status = BIGINT_OK; - SET_MSG(result, "Big integer comparison was successful"); - - return result; -} - /** * bigint_compare * @x: a valid non-null big integer @@ -509,208 +1385,6 @@ bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y) { return result; } -/** - * bigint_add_abs - * @x: a non-null big integer - * @y: a non-null big integer - * - * Adds two absolute values together - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_add_abs(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - - bigint_t *sum = malloc(sizeof(bigint_t)); - if (sum == NULL) { - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Cannot allocate memory for big integer"); - - return result; - } - - const size_t max_size = vector_size(x->digits) > vector_size(y->digits) ? - vector_size(x->digits) : vector_size(y->digits); - - vector_result_t vec_res = vector_new(max_size + 1, sizeof(int)); - if (vec_res.status != VECTOR_OK) { - free(sum); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, vec_res.message); - - return result; - } - - sum->digits = vec_res.value.vector; - sum->is_negative = false; - - long long carry = 0; - size_t idx = 0; - - const size_t x_size = vector_size(x->digits); - const size_t y_size = vector_size(y->digits); - while (idx < x_size || idx < y_size || carry) { - long long partial_sum = carry; - - if (idx < x_size) { - vector_result_t get_res = vector_get(x->digits, idx); - if (get_res.status != VECTOR_OK) { - vector_destroy(sum->digits); - free(sum); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *x_digit = (int*)get_res.value.element; - partial_sum += *x_digit; - } - - if (idx < y_size) { - vector_result_t get_res = vector_get(y->digits, idx); - if (get_res.status != VECTOR_OK) { - vector_destroy(sum->digits); - free(sum); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *y_digit = (int*)get_res.value.element; - partial_sum += *y_digit; - } - - int digit = partial_sum % BIGINT_BASE; - carry = partial_sum / BIGINT_BASE; - - vector_result_t push_res = vector_push(sum->digits, &digit); - if (push_res.status != VECTOR_OK) { - vector_destroy(sum->digits); - free(sum); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - idx++; - } - - bigint_result_t trim_res = bigint_trim_zeros(sum); - if (trim_res.status != BIGINT_OK) { - vector_destroy(sum->digits); - free(sum); - - return trim_res; - } - - result.value.number = sum; - result.status = BIGINT_OK; - SET_MSG(result, "Big integers successfully added"); - - return result; -} - -/** - * bigint_sub_abs - * @x: a non-null big integer - * @y: a non-null big integer - * - * Subtracts two absolute values assuming that |x| >= |y| - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_sub_abs(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - - bigint_t *difference = malloc(sizeof(bigint_t)); - if (difference == NULL) { - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Cannot allocate memory for big integer"); - - return result; - } - - vector_result_t vec_res = vector_new(vector_size(x->digits), sizeof(int)); - if (vec_res.status != VECTOR_OK) { - free(difference); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, vec_res.message); - - return result; - } - - difference->digits = vec_res.value.vector; - difference->is_negative = false; - - long long borrow = 0; - - const size_t x_size = vector_size(x->digits); - const size_t y_size = vector_size(y->digits); - for (size_t idx = 0; idx < x_size; idx++) { - vector_result_t x_get_res = vector_get(x->digits, idx); - if (x_get_res.status != VECTOR_OK) { - vector_destroy(difference->digits); - free(difference); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, x_get_res.message); - - return result; - } - - int *x_digit = (int*)x_get_res.value.element; - long long partial_difference = *x_digit - borrow; - - if (idx < y_size) { - vector_result_t y_get_res = vector_get(y->digits, idx); - if (y_get_res.status != VECTOR_OK) { - vector_destroy(difference->digits); - free(difference); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, y_get_res.message); - - return result; - } - - int *y_digit = (int*)y_get_res.value.element; - partial_difference -= *y_digit; - } - - if (partial_difference < 0) { - partial_difference += BIGINT_BASE; - borrow = 1; - } else { - borrow = 0; - } - - int digit = partial_difference; - vector_result_t push_res = vector_push(difference->digits, &digit); - if (push_res.status != VECTOR_OK) { - vector_destroy(difference->digits); - free(difference); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - bigint_result_t trim_res = bigint_trim_zeros(difference); - if (trim_res.status != BIGINT_OK) { - vector_destroy(difference->digits); - free(difference); - - return trim_res; - } - - result.value.number = difference; - result.status = BIGINT_OK; - SET_MSG(result, "Big integers successfully subtracted"); - - return result; -} - /** * bigint_add * @x: a non-null big integer @@ -1028,690 +1702,6 @@ bigint_result_t bigint_mod(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 - * - * Shifts left by @n digits (i.e., multiply by BASE^n) - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_shift_left(const bigint_t *num, size_t n) { - bigint_result_t result = {0}; - - if (n == 0) { - return bigint_clone(num); - } - - bigint_t *shifted = malloc(sizeof(bigint_t)); - if (shifted == NULL) { - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for big integer"); - - return result; - } - - vector_result_t vec_res = vector_new(vector_size(num->digits) + n, sizeof(int)); - if (vec_res.status != VECTOR_OK) { - free(shifted); - result.status = BIGINT_ERR_ALLOCATE; - COPY_MSG(result, vec_res.message); - - return result; - } - - shifted->digits = vec_res.value.vector; - shifted->is_negative = num->is_negative; - - // Add 'n' zeros by starting from the LSB - int zero = 0; - for (size_t idx = 0; idx < n; idx++) { - vector_result_t push_res = vector_push(shifted->digits, &zero); - if (push_res.status != VECTOR_OK) { - vector_destroy(shifted->digits); - free(shifted); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - // Copy back original digits - const size_t num_size = vector_size(num->digits); - for (size_t idx = 0; idx < num_size; idx++) { - vector_result_t get_res = vector_get(num->digits, idx); - if (get_res.status != VECTOR_OK) { - vector_destroy(shifted->digits); - free(shifted); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *digit = (int*)get_res.value.element; - vector_result_t push_res = vector_push(shifted->digits, digit); - if (push_res.status != VECTOR_OK) { - vector_destroy(shifted->digits); - free(shifted); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - result.value.number = shifted; - result.status = BIGINT_OK; - SET_MSG(result, "Big integer shifted successfully"); - - return result; -} - -/** - * bigint_split - * @num: a non-null big integers - * @m: the pivot/position where to split - * @high: digits \in [0, m) - * @low: digits \in [m, size) - * - * Splits number into @high and @low parts at position @m - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_split(const bigint_t *num, size_t m, bigint_t **high, bigint_t **low) { - bigint_result_t result = {0}; - - const size_t size = vector_size(num->digits); - - // Low part: digits \in [0, m) - *low = malloc(sizeof(bigint_t)); - if (*low == NULL) { - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for big integer"); - - return result; - } - - vector_result_t low_res = vector_new(m ? m : 1, sizeof(int)); - if (low_res.status != VECTOR_OK) { - free(*low); - result.status = BIGINT_ERR_ALLOCATE; - COPY_MSG(result, low_res.message); - - return result; - } - - (*low)->digits = low_res.value.vector; - (*low)->is_negative = false; - - for (size_t idx = 0; idx < m && idx < size; idx++) { - vector_result_t get_res = vector_get(num->digits, idx); - if (get_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - free(*low); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *digit = (int*)get_res.value.element; - vector_result_t push_res = vector_push((*low)->digits, digit); - if (push_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - free(*low); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - if (vector_size((*low)->digits) == 0) { - int zero = 0; - vector_result_t push_res = vector_push((*low)->digits, &zero); - if (push_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - free(*low); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - // First pass of zero trimming - bigint_result_t first_trim_res = bigint_trim_zeros(*low); - if (first_trim_res.status != BIGINT_OK) { - vector_destroy((*low)->digits); - free(*low); - - return first_trim_res; - } - - // High part: digits \in [m, size) - *high = malloc(sizeof(bigint_t)); - if (*high == NULL) { - vector_destroy((*low)->digits); - free(*low); - result.status = BIGINT_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for big integer"); - - return result; - } - - vector_result_t high_res = vector_new(size > m ? (size - m) : 1, sizeof(int)); - if (high_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - free(*low); - free(*high); - - result.status = BIGINT_ERR_ALLOCATE; - COPY_MSG(result, low_res.message); - - return result; - } - - (*high)->digits = high_res.value.vector; - (*high)->is_negative = false; - - if (size > m) { - for (size_t idx = m; idx < size; idx++) { - vector_result_t get_res = vector_get(num->digits, idx); - if (get_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - vector_destroy((*high)->digits); - free(*low); - free(*high); - - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *digit = (int*)get_res.value.element; - vector_result_t push_res = vector_push((*high)->digits, digit); - if (push_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - vector_destroy((*high)->digits); - free(*low); - free(*high); - - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - } else { - int zero = 0; - vector_result_t push_res = vector_push((*high)->digits, &zero); - if (push_res.status != VECTOR_OK) { - vector_destroy((*low)->digits); - vector_destroy((*high)->digits); - free(*low); - free(*high); - - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - - // Second pass of zero trimming - bigint_result_t second_trim_res = bigint_trim_zeros(*high); - if (second_trim_res.status != BIGINT_OK) { - vector_destroy((*low)->digits); - vector_destroy((*high)->digits); - free(*low); - free(*high); - - return second_trim_res; - } - - result.status = BIGINT_OK; - SET_MSG(result, "Big number successfully splitted"); - - return result; -} - -/** - * bigint_karatsuba_base - * @x: a non-null big integer - * @y: a non-null big integer - * - * Base case of the Karatsuba recursive algorithm - * which uses a "grade school" multiplication. - * Its complexity is O(n^2) - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_karatsuba_base(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - - bigint_result_t prod_res = bigint_from_int(0); - if (prod_res.status != BIGINT_OK) { - result.status = BIGINT_ERR_ALLOCATE; - COPY_MSG(result, prod_res.message); - - return result; - } - - bigint_t *product = prod_res.value.number; - const size_t x_size = vector_size(x->digits); - const size_t y_size = vector_size(y->digits); - - for (size_t i = 0; i < x_size; i++) { - long long carry = 0; - - vector_result_t get_res = vector_get(x->digits, i); - if (get_res.status != VECTOR_OK) { - bigint_destroy(product); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, get_res.message); - - return result; - } - - int *x_digit = (int*)get_res.value.element; - for (size_t j = 0; j < y_size || carry; j++) { - int *y_digit = NULL; - int *curr = NULL; - - if (j < y_size) { - vector_result_t y_res = vector_get(y->digits, j); - if (y_res.status != VECTOR_OK) { - bigint_destroy(product); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, y_res.message); - - return result; - } - - y_digit = (int*)y_res.value.element; - } - - if ((i + j) < vector_size(product->digits)) { - vector_result_t curr_res = vector_get(product->digits, i + j); - if (curr_res.status != VECTOR_OK) { - bigint_destroy(product); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, curr_res.message); - - return result; - } - - curr = (int*)curr_res.value.element; - } - - long long partial_prod = carry; - if (curr) { partial_prod += *curr; } - if (y_digit) { partial_prod += (long long)(*x_digit) * (*y_digit); } - - int new_digit =(int)(partial_prod % BIGINT_BASE); - carry = partial_prod / BIGINT_BASE; - - if (curr) { - vector_result_t set_res = vector_set(product->digits, i + j, &new_digit); - if (set_res.status != VECTOR_OK) { - bigint_destroy(product); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, set_res.message); - - return result; - } - } else { - vector_result_t push_res = vector_push(product->digits, &new_digit); - if (push_res.status != VECTOR_OK) { - bigint_destroy(product); - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - return result; - } - } - } - } - - bigint_result_t trim_res = bigint_trim_zeros(product); - if (trim_res.status != BIGINT_OK) { - bigint_destroy(product); - - return trim_res; - } - - result.value.number = product; - result.status = BIGINT_OK; - SET_MSG(result, "Product between big integers was successful"); - - return result; -} - -/** - * bigint_karatusba - * @x: a non-null big integer - * @y: a non-null big integer - * - * Perform a multiplication using Karatsuba recursive algorithm - * in O(n^{\log_2 3}) \approx O(n^{1.585}) - */ -bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - bigint_result_t tmp_res = {0}; - - if (x == NULL || y == NULL) { - result.status = BIGINT_ERR_INVALID; - SET_MSG(result, "Invalid big integers"); - - return result; - } - - const size_t x_size = vector_size(x->digits); - const size_t y_size = vector_size(y->digits); - const size_t min_size = x_size < y_size ? x_size : y_size; - const size_t max_size = x_size > y_size ? x_size : y_size; - - // Base case using "grade school" quadratic algorithm - if (min_size <= 32 || max_size / min_size > 2) { - return bigint_karatsuba_base(x, y); - } - - // Split the big integer at approximately half the size of the larger number - const size_t pivot = (x_size > y_size ? x_size : y_size) / 2; - - // Results of each step - bigint_t *x1 = NULL, *x0 = NULL; - bigint_t *y1 = NULL, *y0 = NULL; - bigint_t *z0 = NULL, *z2 = NULL; - bigint_t *x_sum = NULL, *y_sum = NULL; - bigint_t *z1_temp = NULL, *z1_sub1 = NULL, *z1 = NULL; - bigint_t *z2_shifted = NULL, *z1_shifted = NULL; - bigint_t *temp = NULL, *product = NULL; - - // Split x = x1 * BASE^pivot + x0 - tmp_res = bigint_split(x, pivot, &x1, &x0); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - // Split y = y1 * BASE^pivot + y0 - tmp_res = bigint_split(y, pivot, &y1, &y0); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - // Perform karatsuba's trick - tmp_res = bigint_karatsuba(x0, y0); // z0 = x0 * y0 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z0 = tmp_res.value.number; - - tmp_res = bigint_karatsuba(x1, y1); // z2 = x1 * y1 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z2 = tmp_res.value.number; - - // z1 = (x0 + x1) * (y0 + y1) - z0 - z2 - tmp_res = bigint_add(x0, x1); // x_sum = x0 + x1 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - x_sum = tmp_res.value.number; - - tmp_res = bigint_add(y0, y1); // y_sum = y0 + y1 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - y_sum = tmp_res.value.number; - - tmp_res = bigint_karatsuba(x_sum, y_sum); // z1_temp = (x0 + x1) * (y0 + y1) - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z1_temp = tmp_res.value.number; - - tmp_res = bigint_sub(z1_temp, z0); // z1_sub1 = z1_temp - z0 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z1_sub1 = tmp_res.value.number; - - tmp_res = bigint_sub(z1_sub1, z2); // z1 = z1_sub1 - z2 - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z1 = tmp_res.value.number; - - tmp_res = bigint_shift_left(z2, 2 * pivot); // z2_shifted = z2 << (2 * pivot) - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z2_shifted = tmp_res.value.number; - - tmp_res = bigint_shift_left(z1, pivot); // z1_shifted = z1 << pivot - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - z1_shifted = tmp_res.value.number; - - tmp_res = bigint_add(z2_shifted, z1_shifted); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - temp = tmp_res.value.number; - - tmp_res = bigint_add(temp, z0); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - product = tmp_res.value.number; - - // Destroy intermediate allocations except for the product - bigint_destroy(x1); bigint_destroy(x0); - bigint_destroy(y1); bigint_destroy(y0); - bigint_destroy(z0); bigint_destroy(z2); - bigint_destroy(x_sum); bigint_destroy(y_sum); - bigint_destroy(z1_temp); bigint_destroy(z1_sub1); - bigint_destroy(z1); bigint_destroy(z2_shifted); - bigint_destroy(z1_shifted); bigint_destroy(temp); - - result.value.number = product; - result.status = BIGINT_OK; - SET_MSG(result, "Product between big integers was successful"); - - return result; - -cleanup: // Destroy intermediate allocations on error - if (x1) { bigint_destroy(x1); } - if (x0) { bigint_destroy(x0); } - if (y1) { bigint_destroy(y1); } - if (y0) { bigint_destroy(y0); } - if (z0) { bigint_destroy(z0); } - if (z2) { bigint_destroy(z2); } - if (x_sum) { bigint_destroy(x_sum); } - if (y_sum) { bigint_destroy(y_sum); } - if (z1_temp) { bigint_destroy(z1_temp); } - if (z1_sub1) { bigint_destroy(z1_sub1); } - if (z1) { bigint_destroy(z1); } - if (z2_shifted) { bigint_destroy(z2_shifted); } - if (z1_shifted) { bigint_destroy(z1_shifted); } - if (temp) { bigint_destroy(temp); } - if (product) { bigint_destroy(product); } - - return result; -} - -/** - * bigint_dev - * @x: a valid non-null big integer (dividend) - * @y: a valid non-null big integer (divisor) - * - * Computes division using long division algorithm in O(n^2) - * - * Returns a bigint_result_t data type - */ -bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) { - bigint_result_t result = {0}; - bigint_result_t tmp_res = {0}; - - bigint_t *quotient = NULL; - bigint_t *remainder = NULL; - bigint_t *abs_y = NULL; - - if (x == NULL || y == NULL) { - result.status = BIGINT_ERR_INVALID; - SET_MSG(result, "Invalid big numbers"); - - return result; - } - - // Check for division by zero - const size_t y_size = vector_size(y->digits); - if (y_size == 0) { - result.status = BIGINT_ERR_DIV_BY_ZERO; - SET_MSG(result, "Cannot divide by zero"); - - return result; - } - - if (y_size == 1) { - vector_result_t y_val_res = vector_get(y->digits, 0); - if (y_val_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, y_val_res.message); - - return result; - } - - int *y_val = (int*)y_val_res.value.element; - if (*y_val == 0) { - result.status = BIGINT_ERR_DIV_BY_ZERO; - SET_MSG(result, "Cannot divide by zero"); - - return result; - } - } - - // If |x| < |y| then result is zero - tmp_res = bigint_compare_abs(x, y); - if (tmp_res.status != BIGINT_OK) { return tmp_res; } - - if (tmp_res.value.compare_status < 0) { - tmp_res = bigint_from_int(0); - if (tmp_res.status != BIGINT_OK) { return tmp_res; } - - result.value.number = tmp_res.value.number; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - } - - // Initialize quotient and remainder - tmp_res = bigint_from_int(0); - if (tmp_res.status != BIGINT_OK) { return tmp_res; } - quotient = tmp_res.value.number; - - tmp_res = bigint_from_int(0); - if (tmp_res.status != BIGINT_OK) { bigint_destroy(quotient); return tmp_res; } - remainder = tmp_res.value.number; - - // Create absolute value of y for later comparisons - tmp_res = bigint_clone(y); - if (tmp_res.status != BIGINT_OK) { - bigint_destroy(quotient); - bigint_destroy(remainder); - - return tmp_res; - } - - abs_y = tmp_res.value.number; - abs_y->is_negative = false; - - // Long division algorithm applied from MSB to LSB - const size_t x_size = vector_size(x->digits); - for (int idx = (int)x_size - 1; idx >= 0; idx--) { - // Shift remainder left by one base digit (multiplication by BASE) - tmp_res = bigint_shift_left(remainder, 1); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - bigint_t *shifted_remainder = tmp_res.value.number; - bigint_destroy(remainder); - remainder = shifted_remainder; - - // Add current digit of 'x' to the least significant position of remainder - vector_result_t digit_res = vector_get(x->digits, idx); - if (digit_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, digit_res.message); - - goto cleanup; - } - - int *x_digit = (int*)digit_res.value.element; - - vector_result_t set_res = vector_set(remainder->digits, 0, x_digit); - if (set_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, set_res.message); - - goto cleanup; - } - - tmp_res = bigint_trim_zeros(remainder); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - // COunt how many times 'y' fits into current remainder - size_t count = 0; - while (1) { - tmp_res = bigint_compare_abs(remainder, abs_y); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - if (tmp_res.value.compare_status < 0) { break; } // remainder < abs_y - - // remainder = remainder - abs_y - tmp_res = bigint_sub_abs(remainder, abs_y); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - bigint_t *new_remainder = tmp_res.value.number; - bigint_destroy(remainder); - remainder = new_remainder; - count++; - } - - // Add count to quotient digits - vector_result_t push_res = vector_push(quotient->digits, &count); - if (push_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - COPY_MSG(result, push_res.message); - - goto cleanup; - } - } - - // Reverse quotient digits - const size_t q_size = vector_size(quotient->digits); - for (size_t idx = 0; idx < q_size / 2; idx++) { - vector_result_t left_res = vector_get(quotient->digits, idx); - vector_result_t right_res = vector_get(quotient->digits, q_size - 1 - idx); - - if (left_res.status != VECTOR_OK || right_res.status != VECTOR_OK) { - result.status = BIGINT_ERR_INVALID; - SET_MSG(result, "Failed to access vector elements"); - - goto cleanup; - } - - int *left = (int*)left_res.value.element; - int *right = (int*)right_res.value.element; - int temp = *left; - - vector_set(quotient->digits, idx, right); - vector_set(quotient->digits, q_size - 1 - idx, &temp); - } - - quotient->is_negative = (x->is_negative != y->is_negative); - - tmp_res = bigint_trim_zeros(quotient); - if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } - - bigint_destroy(remainder); - bigint_destroy(abs_y); - - result.value.number = quotient; - result.status = BIGINT_OK; - SET_MSG(result, "Division between big integers was successful"); - - return result; - -cleanup: - if (quotient) { bigint_destroy(quotient); } - if (remainder) { bigint_destroy(remainder); } - if (abs_y) { bigint_destroy(abs_y); } - - return result; -} - /** * bigint_destroy * @number: a valid non-null big integer diff --git a/src/map.c b/src/map.c index 1a69f93..7576a5b 100644 --- a/src/map.c +++ b/src/map.c @@ -11,10 +11,6 @@ #include "map.h" // Internal methods -static uint64_t hash_key(const char *key); -static size_t map_insert_index(const map_t *map, const char *key); -static size_t map_find_index(const map_t *map, const char *key); -static map_result_t map_resize(map_t *map); /** * hash_key @@ -22,7 +18,7 @@ static map_result_t map_resize(map_t *map); * * Returns the digest of @key using the Fowler-Noll-Vo hashing algorithm */ -uint64_t hash_key(const char *key) { +static uint64_t hash_key(const char *key) { uint64_t hash = FNV_OFFSET_BASIS_64; while (*key) { @@ -33,43 +29,6 @@ uint64_t hash_key(const char *key) { return hash; } -/** - * map_new - * - * Returns a map_result_t data type containing a new hash map - */ -map_result_t map_new(void) { - map_result_t result = {0}; - - map_t *map = malloc(sizeof(map_t)); - if (map == NULL) { - result.status = MAP_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for map"); - - return result; - } - - map->elements = calloc(INITIAL_CAP, sizeof(map_element_t)); - if (map->elements == NULL) { - free(map); - result.status = MAP_ERR_ALLOCATE; - SET_MSG(result, "Failed to allocate memory for map elements"); - - return result; - } - - // Initialize map - map->capacity = INITIAL_CAP; - map->size = 0; - map->tombstone_count = 0; - - result.status = MAP_OK; - SET_MSG(result, "Map successfully created"); - result.value.map = map; - - return result; -} - /** * map_insert_index * @map: a non-null map @@ -80,7 +39,7 @@ map_result_t map_new(void) { * * Returns the index of available slot or SIZE_MAX otherwise */ -size_t map_insert_index(const map_t *map, const char *key) { +static size_t map_insert_index(const map_t *map, const char *key) { const uint64_t key_digest = hash_key(key); size_t idx = key_digest % map->capacity; size_t delete_tracker = map->capacity; // Fallback index @@ -113,7 +72,7 @@ size_t map_insert_index(const map_t *map, const char *key) { * * Returns a a map_result_t data type containing the status */ -map_result_t map_resize(map_t *map) { +static map_result_t map_resize(map_t *map) { map_result_t result = {0}; const size_t old_capacity = map->capacity; @@ -174,6 +133,43 @@ map_result_t map_resize(map_t *map) { return result; } +/** + * map_new + * + * Returns a map_result_t data type containing a new hash map + */ +map_result_t map_new(void) { + map_result_t result = {0}; + + map_t *map = malloc(sizeof(map_t)); + if (map == NULL) { + result.status = MAP_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for map"); + + return result; + } + + map->elements = calloc(INITIAL_CAP, sizeof(map_element_t)); + if (map->elements == NULL) { + free(map); + result.status = MAP_ERR_ALLOCATE; + SET_MSG(result, "Failed to allocate memory for map elements"); + + return result; + } + + // Initialize map + map->capacity = INITIAL_CAP; + map->size = 0; + map->tombstone_count = 0; + + result.status = MAP_OK; + SET_MSG(result, "Map successfully created"); + result.value.map = map; + + return result; +} + /** * map_add * @map: a non-null map diff --git a/src/string.c b/src/string.c index 1ea2340..3fb1d5b 100644 --- a/src/string.c +++ b/src/string.c @@ -1,4 +1,3 @@ -#include #define SET_MSG(result, msg) \ do { \ snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \ @@ -6,6 +5,7 @@ #include #include +#include #include #include "string.h" diff --git a/src/vector.c b/src/vector.c index c999ee6..acb2807 100644 --- a/src/vector.c +++ b/src/vector.c @@ -10,10 +10,112 @@ #include "vector.h" // Internal methods -static vector_result_t vector_resize(vector_t *vector); -static void swap(void *x, void *y, size_t size); -static size_t partition(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp); -static void quicksort(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp); +/** + * vector_resize + * @vector: a non-null vector + * + * Increases the size of @vector + * + * Returns a vector_result_t data type containing the status + */ +static vector_result_t vector_resize(vector_t *vector) { + vector_result_t result = {0}; + + const size_t old_capacity = vector->capacity; + const size_t new_capacity = old_capacity > 0 ? old_capacity * 2 : 1; + + // Check for stack overflow errors + if (new_capacity > SIZE_MAX / vector->data_size) { + result.status = VECTOR_ERR_OVERFLOW; + SET_MSG(result, "Exceeded maximum size while resizing vector"); + + return result; + } + + void *new_elements = realloc(vector->elements, new_capacity * vector->data_size); + if (new_elements == NULL) { + result.status = VECTOR_ERR_ALLOCATE; + SET_MSG(result, "Failed to reallocate memory for vector"); + + return result; + } + + vector->elements = new_elements; + vector->capacity = new_capacity; + + result.status = VECTOR_OK; + SET_MSG(result, "Vector successfully resized"); + + return result; +} + +/** + * swap + * @x: first element + * @y: second element + * + * Swaps @x and @y + */ +static void swap(void *x, void *y, size_t size) { + uint8_t temp[size]; + + memcpy(temp, x, size); + memcpy(x, y, size); + memcpy(y, temp, size); +} + +/** + * partition + * @base: the array/partition + * @low: lower index + * @high: higher index + * @size: data size + * @cmp: comparison function + * + * Divides an array into two partitions + * + * Returns the pivot index + */ +static size_t partition(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp) { + uint8_t *arr = (uint8_t*)base; + void *pivot = arr + (high * size); + size_t i = low; + + for (size_t j = low; j < high; j++) { + vector_order_t order = cmp(arr + (j * size), pivot); + + if (order == VECTOR_ORDER_LT || order == VECTOR_ORDER_EQ) { + swap(arr + (i * size), arr + (j * size), size); + i++; + } + } + + swap(arr + (i * size), arr + (high * size), size); + + return i; +} + +/** + * quicksort + * @base: the base array/partition + * @low: lower index + * @high: higher index + * @size: data size + * @cmp: comparision function + * + * Recursively sorts an array/partition using the Quicksort algorithm + */ +static void quicksort(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp) { + if (low < high) { + const size_t pivot = partition(base, low, high, size, cmp); + + if (pivot > 0) { + quicksort(base, low, pivot - 1, size, cmp); + } + quicksort(base, pivot + 1, high, size, cmp); + } +} + /** * vector_new @@ -61,112 +163,6 @@ vector_result_t vector_new(size_t size, size_t data_size) { return result; } -/** - * vector_resize - * @vector: a non-null vector - * - * Increases the size of @vector - * - * Returns a vector_result_t data type containing the status - */ -vector_result_t vector_resize(vector_t *vector) { - vector_result_t result = {0}; - - const size_t old_capacity = vector->capacity; - const size_t new_capacity = old_capacity > 0 ? old_capacity * 2 : 1; - - // Check for stack overflow errors - if (new_capacity > SIZE_MAX / vector->data_size) { - result.status = VECTOR_ERR_OVERFLOW; - SET_MSG(result, "Exceeded maximum size while resizing vector"); - - return result; - } - - void *new_elements = realloc(vector->elements, new_capacity * vector->data_size); - if (new_elements == NULL) { - result.status = VECTOR_ERR_ALLOCATE; - SET_MSG(result, "Failed to reallocate memory for vector"); - - return result; - } - - vector->elements = new_elements; - vector->capacity = new_capacity; - - result.status = VECTOR_OK; - SET_MSG(result, "Vector successfully resized"); - - return result; -} - -/** - * swap - * @x: first element - * @y: second element - * - * Swaps @x and @y - */ -void swap(void *x, void *y, size_t size) { - uint8_t temp[size]; - - memcpy(temp, x, size); - memcpy(x, y, size); - memcpy(y, temp, size); -} - -/** - * partition - * @base: the array/partition - * @low: lower index - * @high: higher index - * @size: data size - * @cmp: comparison function - * - * Divides an array into two partitions - * - * Returns the pivot index - */ -size_t partition(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp) { - uint8_t *arr = (uint8_t*)base; - void *pivot = arr + (high * size); - size_t i = low; - - for (size_t j = low; j < high; j++) { - vector_order_t order = cmp(arr + (j * size), pivot); - - if (order == VECTOR_ORDER_LT || order == VECTOR_ORDER_EQ) { - swap(arr + (i * size), arr + (j * size), size); - i++; - } - } - - swap(arr + (i * size), arr + (high * size), size); - - return i; -} - -/** - * quicksort - * @base: the base array/partition - * @low: lower index - * @high: higher index - * @size: data size - * @cmp: comparision function - * - * Recursively sorts an array/partition using the Quicksort algorithm - */ -void quicksort(void *base, size_t low, size_t high, size_t size, vector_cmp_fn cmp) { - if (low < high) { - const size_t pivot = partition(base, low, high, size, cmp); - - if (pivot > 0) { - quicksort(base, low, pivot - 1, size, cmp); - } - quicksort(base, pivot + 1, high, size, cmp); - } -} - /** * vector_push * @vector: a non-null vector