#define SET_MSG(result, msg) \ do { \ snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \ } while (0) #define COPY_MSG(result, msg) \ do { \ strncpy((char *)(result).message, (const char *)(msg), RESULT_MSG_SIZE - 1); \ (result).message[RESULT_MSG_SIZE - 1] = '\0'; \ } while (0) #define REMOVE(ptr) \ free(ptr); \ ptr = NULL #define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9') #include #include #include #include #include "bigint.h" #include "vector.h" /** * 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_div * @x: a non-null big integer acting as a dividend * @y: a non-null big integer acting as a divisor * * Computers the quotient floor (i.e., |X| / |Y|) using Knuth's Algorithm D * Adaoted from p. 273 of Don Knuth's TAoCP Vol. 2 * The complexity is O(n * m) where 'n' and 'm' are the number of base-10^9 * "parts" (the limbs in the code below) in the divisor and the quotient, respectively. * * Returns a bigint_result_t containing the quotient. * The called of this function will be responsible for applying the sign. */ 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; long long *u = NULL, *v = NULL, *q = NULL; if (x == NULL || y == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; } 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 y0_res = vector_get(y->digits, 0); if (y0_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, y0_res.message); return result; } int *y0 = (int *)y0_res.value.element; if (*y0 == 0) { result.status = BIGINT_ERR_DIV_BY_ZERO; SET_MSG(result, "Cannot divide by zero"); return result; } } tmp_res = bigint_compare_abs(x, y); if (tmp_res.status != BIGINT_OK) { return tmp_res; } if (tmp_res.value.compare_status < 0) { return bigint_from_int(0); } const size_t x_size = vector_size(x->digits); const size_t n = y_size; const long long BASE = (long long)BIGINT_BASE; quotient = malloc(sizeof(bigint_t)); if (quotient == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Cannot allocate memory for big integer"); goto cleanup; } quotient->digits = NULL; quotient->is_negative = false; // Single-limb divisor case. Here, we scan using 64-bit arithmetic in O(n) if (y_size == 1) { vector_result_t y0_res = vector_get(y->digits, 0); if (y0_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, y0_res.message); goto cleanup; } long long divisor = *(int *)y0_res.value.element; vector_result_t vec_res = vector_new(x_size, sizeof(int)); if (vec_res.status != VECTOR_OK) { result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); goto cleanup; } quotient->digits = vec_res.value.vector; long long remainder = 0; for (int idx = (int)x_size - 1; idx >= 0; idx--) { vector_result_t xidx_res = vector_get(x->digits, idx); if (xidx_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, xidx_res.message); goto cleanup; } long long current = remainder * BASE + *(int *)xidx_res.value.element; int q_idx = (int)(current / divisor); remainder = current % divisor; vector_result_t push_res = vector_push(quotient->digits, &q_idx); if (push_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); goto cleanup; } } // Restore the LSB-first order const size_t q_size = vector_size(quotient->digits); for (size_t lo = 0, hi = q_size - 1; lo < hi; hi--) { vector_result_t lr = vector_get(quotient->digits, lo); vector_result_t hr = vector_get(quotient->digits, hi); if (lr.status != VECTOR_OK || hr.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Failed to reverse quotient digits"); goto cleanup; } int lower_val = *(int *)lr.value.element; int higher_val = *(int *)hr.value.element; vector_set(quotient->digits, lo, &higher_val); vector_set(quotient->digits, hi, &lower_val); } bigint_result_t trim_res = bigint_trim_zeros(quotient); if (trim_res.status != BIGINT_OK) { result = trim_res; goto cleanup; } result.value.number = quotient; result.status = BIGINT_OK; SET_MSG(result, "Division between big integers was successful"); return result; } /* General case using Knuth's Algorithm * First, some definitions: * index 0 -> least significant limb; * n -> limb count of divisor y * m -> limb count of quotient (x_size - n) * u[0 ... m + n] -> working copy of the (scaled) dividend +1 sentinel limb * v[0 ... n - 1] -> working copy of the (scaled) divisor * q[0 ... m] -> output quotient limbs */ const size_t m = x_size - n; u = calloc(m + n + 1, sizeof(long long)); v = calloc(n, sizeof(long long)); q = calloc(m + 1, sizeof(long long)); if (u == NULL || v == NULL || q == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Cannot allocate scratch arrays for division"); goto cleanup; } for (size_t idx = 0; idx < x_size; idx++) { vector_result_t get_res = vector_get(x->digits, idx); if (get_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); goto cleanup; } u[idx] = *(int *)get_res.value.element; } for (size_t idx = 0; idx < n; idx++) { vector_result_t get_res = vector_get(y->digits, idx); if (get_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); goto cleanup; } v[idx] = *(int *)get_res.value.element; } // D1 (normalize): choose 'd' so that v[n - 1] >= BASE / 2 (after scaling) const long long d = BASE / (v[n - 1] + 1); long long carry = 0; for (size_t idx = 0; idx < x_size; idx++) { long long current = u[idx] * d + carry; u[idx] = current % BASE; carry = current / BASE; } u[x_size] = carry; carry = 0; for (size_t idx = 0; idx < n; idx++) { long long current = v[idx] * d + carry; v[idx] = current % BASE; carry = current / BASE; } // D2-D6: the main loop. One iteration produces one quotient limb for (long long j = (long long)m; j >= 0; j--) { size_t jj = (size_t)j; // D3: 2-by-1 trial quotient long long two_limb = u[jj + n] * BASE + u[jj + n - 1]; long long q_hat = two_limb / v[n - 1]; long long r_hat = two_limb % v[n - 1]; while (q_hat >= BASE || ((n >= 2) && (q_hat * v[n - 2]) > (BASE * r_hat + u[jj + n - 2]))) { q_hat--; r_hat += v[n - 1]; if (r_hat >= BASE) { break; } } // D4: multiply-subtract u[j ... j + n] -= q_hat * v[0 ... n - 1] long long borrow = 0; for (size_t idx = 0; idx < n; idx++) { long long product = q_hat * v[idx] + borrow; borrow = product / BASE; long long diff = u[jj + idx] - (product % BASE); if (diff < 0) { diff += BASE; borrow++; } u[jj + idx] = diff; } u[jj + n] -= borrow; // D5: store quotient digit q[jj] = q_hat; // D6: if 'u' went negative, add 'v' back once and decrement q[j] if (u[jj + n] < 0) { q[jj]--; carry = 0; for (size_t idx = 0; idx < n; idx++) { long long sum = u[jj + idx] + v[idx] + carry; u[jj + idx] = sum % BASE; carry = sum / BASE; } u[jj + n] += carry; } } // Delete working copy from memory REMOVE(u); REMOVE(v); // Build the bigint quotient from q[0 ... m] (index 0 = LSB) vector_result_t vec_res = vector_new(m + 1, sizeof(int)); if (vec_res.status != VECTOR_OK) { result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); goto cleanup; } quotient->digits = vec_res.value.vector; for (size_t idx = 0; idx <= m; idx++) { int q_idx = (int)q[idx]; vector_result_t push_res = vector_push(quotient->digits, &q_idx); if (push_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); goto cleanup; } } REMOVE(q); bigint_result_t trim_res = bigint_trim_zeros(quotient); if (trim_res.status != BIGINT_OK) { result = trim_res; goto cleanup; } result.value.number = quotient; result.status = BIGINT_OK; SET_MSG(result, "Division between big integers was successful"); return result; cleanup: free(u); free(v); free(q); if (quotient) { bigint_destroy(quotient); } return result; } /** * bigint_from_int * @value: an integer value * * Takes an integer and convert it to a big integer * * Returns a bigint_result_t data type containing a new big integer */ bigint_result_t bigint_from_int(long long value) { bigint_result_t result = {0}; bigint_t *number = malloc(sizeof(bigint_t)); if (number == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; } vector_result_t vec_res = vector_new(4, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(number); result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; } number->digits = vec_res.value.vector; number->is_negative = (value < 0); // Discard the sign since we don't need it anymore unsigned long long abs_val = value < 0 ? -(unsigned long long)value : (unsigned long long)value; if(abs_val == 0) { int zero = 0; vector_result_t push_res = vector_push(number->digits, &zero); if (push_res.status != VECTOR_OK) { vector_destroy(number->digits); free(number); result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; } } else { while (abs_val != 0) { int digit = abs_val % BIGINT_BASE; vector_result_t push_res = vector_push(number->digits, &digit); if (push_res.status != VECTOR_OK) { vector_destroy(number->digits); free(number); result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; } abs_val /= BIGINT_BASE; } } result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully created"); result.value.number = number; return result; } /** * bigint_from_string * @string_num: an array of chars representing a number * * Takes a string containing a number and convert it to big integer * * Returns a bigint_result_t data type containing a new big integer */ bigint_result_t bigint_from_string(const char *string_num) { bigint_result_t result = {0}; if (string_num == NULL || *string_num == 0) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid string"); return result; } bigint_t *number = malloc(sizeof(bigint_t)); if (number == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; } vector_result_t vec_res = vector_new(4, sizeof(int)); if (vec_res.status != VECTOR_OK) { free(number); result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; } number->digits = vec_res.value.vector; number->is_negative = false; if (*string_num == '-') { number->is_negative = true; string_num++; } else if (*string_num == '+') { string_num++; } // Check whether the integer is valid or not if (*string_num == '\0') { vector_destroy(number->digits); free(number); result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Invalid integer"); return result; } // Check whether characters are digits for (const char *p = string_num; *p; ++p) { if (!IS_DIGIT((unsigned char)*p)) { vector_destroy(number->digits); free(number); result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid integer"); return result; } } // Skip leading zeros while (*string_num == '0' && *(string_num + 1) != '\0') { string_num++; } const size_t number_len = strlen(string_num); // Process digits from right to left by chunks of the representation base for (int i = number_len; i > 0; i -= BIGINT_BASE_DIGITS) { const int start = (i - BIGINT_BASE_DIGITS > 0) ? i - BIGINT_BASE_DIGITS : 0; const int chunk_len = (i - start); int digit = 0; for (int j = 0; j < chunk_len; j++) { digit = digit * 10 + (string_num[start + j] - '0'); } vector_result_t push_res = vector_push(number->digits, &digit); if (push_res.status != VECTOR_OK) { vector_destroy(number->digits); free(number); result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, push_res.message); return result; } } bigint_result_t trim_res = bigint_trim_zeros(number); if (trim_res.status != BIGINT_OK) { vector_destroy(number->digits); free(number); return trim_res; } result.value.number = number; result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully created"); return result; } /** * bigint_to_string * @number: a valid non-null big number * * Converts a big integer to a C string * * Returns a bigint_result_t data type */ bigint_result_t bigint_to_string(const bigint_t *number) { bigint_result_t result = {0}; if (number == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integer"); return result; } const size_t size = vector_size(number->digits); const size_t max_len = (size * BIGINT_BASE_DIGITS) + 2; // +2 for sign and terminator char *str = malloc(max_len); if (str == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for string"); return result; } char *ptr = str; if (number->is_negative) { *ptr++ = '-'; } // Print MSB without leading zeros vector_result_t msb_res = vector_get(number->digits, size - 1); if (msb_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, msb_res.message); return result; } int *msb = (int*)msb_res.value.element; ptr += sprintf(ptr, "%d", *msb); // Print remaining digits with leading zeros for (int idx = size - 2; idx >= 0; idx--) { vector_result_t digit_res = vector_get(number->digits, idx); if (digit_res.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, digit_res.message); return result; } int *digit = (int*)digit_res.value.element; ptr += sprintf(ptr, "%09d", *digit); } result.value.string_num = str; result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully converted"); return result; } /** * bigint_clone * @number: a valid non-null big integer * * Clones a big integer * * Returns a bigint_result_t data type containing the new big integer */ bigint_result_t bigint_clone(const bigint_t *number) { bigint_result_t result = {0}; if (number == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integer"); return result; } bigint_t *cloned = malloc(sizeof(bigint_t)); if (cloned == NULL) { result.status = BIGINT_ERR_ALLOCATE; SET_MSG(result, "Failed to allocate memory for big integer"); return result; } vector_result_t vec_res = vector_new(vector_size(number->digits), sizeof(int)); if (vec_res.status != VECTOR_OK) { free(cloned); result.status = BIGINT_ERR_ALLOCATE; COPY_MSG(result, vec_res.message); return result; } cloned->digits = vec_res.value.vector; cloned->is_negative = number->is_negative; // Copy digits const size_t sz = vector_size(number->digits); for (size_t idx = 0; idx < sz; idx++) { vector_result_t get_res = vector_get(number->digits, idx); if (get_res.status != VECTOR_OK) { vector_destroy(cloned->digits); free(cloned); result.status = BIGINT_ERR_INVALID; COPY_MSG(result, get_res.message); return result; } int *digit = (int*)get_res.value.element; vector_result_t push_res = vector_push(cloned->digits, digit); if (push_res.status != VECTOR_OK) { vector_destroy(cloned->digits); free(cloned); result.status = BIGINT_ERR_INVALID; COPY_MSG(result, push_res.message); return result; } } result.value.number = cloned; result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully cloned"); return result; } /** * bigint_compare * @x: a valid non-null big integer * @y: a valid non-null big integer * * Compares two big integers * if x < y => -1 * if x == y => 0 * if x > y => 1 * * Returns a bigint_result_t data type */ bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; if (x->is_negative != y->is_negative) { result.value.compare_status = x->is_negative ? -1 : 1; result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; } bigint_result_t cmp_res = bigint_compare_abs(x, y); if (cmp_res.status != BIGINT_OK) { return cmp_res; } const int8_t abs_cmp = cmp_res.value.compare_status; result.value.compare_status = x->is_negative ? -abs_cmp : abs_cmp; result.status = BIGINT_OK; SET_MSG(result, "Big integer comparison was successful"); return result; } /** * bigint_add * @x: a non-null big integer * @y: a non-null big integer * * Adds two big integers together * * Returns a bigint_result_t data type */ bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; if (x == NULL || y == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; } // Same sign: add absolute values if (x->is_negative == y->is_negative) { bigint_result_t sum_res = bigint_add_abs(x, y); if (sum_res.status != BIGINT_OK) { return sum_res; } bigint_t *sum = sum_res.value.number; if (sum) { sum->is_negative = x->is_negative; } result.value.number = sum; result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); return result; } // Different signs: subtract smaller from larger bigint_result_t cmp_res = bigint_compare_abs(x, y); if (cmp_res.status != BIGINT_OK) { return cmp_res; } const int8_t cmp = cmp_res.value.compare_status; if (cmp == 0) { return bigint_from_int(0); } else if (cmp > 0) { bigint_result_t sub_res = bigint_sub_abs(x, y); if (sub_res.status != BIGINT_OK) { return sub_res; } bigint_t *sub = sub_res.value.number; if (sub) { sub->is_negative = x->is_negative; } result.value.number = sub; result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); } else { bigint_result_t sub_res = bigint_sub_abs(y, x); if (sub_res.status != BIGINT_OK) { return sub_res; } bigint_t *sub = sub_res.value.number; if (sub) { sub->is_negative = y->is_negative; } result.value.number = sub; result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully added"); } return result; } /** * bigint_sub * @x: a non-null big integer * @y: a non-null big integer * * Subtracts two big integers together * * Returns a bigint_result_t data type */ bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; if (x == NULL || y == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; } /* To subtract two big integers we can consider * the following equivalence: * x - y = x + (-y) */ bigint_result_t neg_y_res = bigint_clone(y); if (neg_y_res.status != BIGINT_OK) { return neg_y_res; } bigint_t *neg_y = neg_y_res.value.number; neg_y->is_negative = !neg_y->is_negative; bigint_result_t difference_res = bigint_add(x, neg_y); if (difference_res.status != BIGINT_OK) { bigint_destroy(neg_y); return difference_res; } bigint_destroy(neg_y); bigint_t *difference = difference_res.value.number; result.value.number = difference; result.status = BIGINT_OK; SET_MSG(result, "Big integers successfully subtracted"); return result; } /** * bigint_prod * @x: a non-null big integer * @y: a non-null big integer * * Perform a multiplication between @a and @b * using Karatsuba's algorithm * * Returns a bigint_result_t data type */ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; if (x == NULL || y == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integers"); return result; } bigint_result_t product_res = bigint_karatsuba(x, y); if (product_res.status != BIGINT_OK) { return product_res; } bigint_t *product = product_res.value.number; product->is_negative = (x->is_negative != y->is_negative); bigint_result_t trim_res = bigint_trim_zeros(product); if (trim_res.status != BIGINT_OK) { bigint_destroy(product); return trim_res; } result.value.number = product; result.status = BIGINT_OK; SET_MSG(result, "Product between big integers was successful"); return result; } /** * bigint_divmod * @x: a valid non-null big integer * @y: a valid non-null big integer * * Computes truncated division with remainder. That is: * quotient = trunc(x / y) sign = sign(x) XOR sign(y) * remainder = x - y * quotient sign = sign(x) * * 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}; 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; } 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; } } // |x| < |y|: quotient is 0, 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; // Set quotient sign accordingly quotient->is_negative = (x->is_negative != y->is_negative); // Compute remainder using 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; tmp_res = bigint_trim_zeros(remainder); if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; } // Set remainder sign accordingly vector_result_t r0 = vector_get(remainder->digits, 0); if (r0.status != VECTOR_OK) { result.status = BIGINT_ERR_INVALID; COPY_MSG(result, r0.message); goto cleanup; } bool rem_is_zero = (vector_size(remainder->digits) == 1 && *(int *)r0.value.element == 0); if (!rem_is_zero) { remainder->is_negative = x->is_negative; } result.value.division.quotient = quotient; result.value.division.remainder = remainder; result.status = BIGINT_OK; SET_MSG(result, "Division between big integers was successful"); bigint_destroy(y_times_q); return result; cleanup: if (quotient) { bigint_destroy(quotient); } if (y_times_q) { bigint_destroy(y_times_q); } if (remainder) { bigint_destroy(remainder); } return result; } /** * bigint_mod * @x: a valid non-null big integer * @y: a valid non-null big integer * * Computes @x mod @y * * Returns a bigint_result_t data type */ bigint_result_t bigint_mod(const bigint_t *x, const bigint_t *y) { bigint_result_t result = {0}; if (x == NULL || y == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big numbers"); return result; } bigint_result_t div_res = bigint_divmod(x, y); if (div_res.status != BIGINT_OK) { return div_res; } bigint_t* const quotient = div_res.value.division.quotient; bigint_t* const remainder = div_res.value.division.remainder; // Discard quotient bigint_destroy(quotient); result.value.number = remainder; result.status = BIGINT_OK; SET_MSG(result, "Division between big integers was successful"); return result; } /** * bigint_destroy * @number: a valid non-null big integer * * Deletes the big integer from the memory * * Returns a bigint_result_t data type */ bigint_result_t bigint_destroy(bigint_t *number) { bigint_result_t result = {0}; if (number == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid big integer"); return result; } vector_destroy(number->digits); free(number); result.status = BIGINT_OK; SET_MSG(result, "Big integer successfully deleted"); return result; } /** * bigint_printf * @format: format string * @...: variadic arguments * * Prints a bigint integer to stdout using the custom '%B' placeholder * * Returns a bigint_result_t data type */ bigint_result_t bigint_printf(const char *format, ...) { bigint_result_t result = {0}; if (format == NULL) { result.status = BIGINT_ERR_INVALID; SET_MSG(result, "Invalid format string"); return result; } va_list args; va_start(args, format); // Process string char by char for (const char *p = format; *p != '\0'; p++) { if (*p == '%' && *(p + 1) != '%') { p++; const char placeholder = *p; switch (placeholder) { case 'B': { bigint_t *num = va_arg(args, bigint_t*); if (num == NULL) { for (const char *s = ""; *s != '\0'; s++) { putchar(*s); } } 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 *number_str = num_str_res.value.string_num; for (const char *s = number_str; *s != '\0'; s++) { putchar(*s); } free(number_str); } break; } 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*); if (val) { for (const char *s = val; *s != '\0'; s++) { putchar(*s); } } else { for (const char *s = ""; *s != '\0'; s++) { putchar(*s); } } break; } case 'c': { int val = va_arg(args, int); putchar(val); break; } case 'f': { double val = va_arg(args, double); printf("%f", val); break; } case 'p': { void* const val = va_arg(args, void*); printf("%p", val); break; } case 'x': { unsigned int val = va_arg(args, unsigned int); printf("%x", val); break; } case 'X': { unsigned int val = va_arg(args, unsigned int); printf("%X", val); break; } default: // Unsupported placeholder so we just print it printf("%%%c", placeholder); break; } } else if (*p == '%' && *(p + 1) == '%') { // print the percent character as is putchar('%'); p++; } else { // Print ASCII character putchar(*p); } } va_end(args); result.status = BIGINT_OK; SET_MSG(result, "Printf completed successfully"); return result; }