Refactored Karatsuba's algorithm and added sample usage
This commit is contained in:
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ TARGET = usage
|
|||||||
TEST_V_TARGET = test_vector
|
TEST_V_TARGET = test_vector
|
||||||
TEST_M_TARGET = test_map
|
TEST_M_TARGET = test_map
|
||||||
|
|
||||||
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o
|
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o
|
||||||
PROG_OBJS = $(OBJ_DIR)/usage.o
|
PROG_OBJS = $(OBJ_DIR)/usage.o
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|||||||
39
README.md
39
README.md
@@ -10,11 +10,12 @@ the standard library. It currently features:
|
|||||||
|
|
||||||
- [**Vector**](/docs/vector.md): a growable, contiguous array of homogenous generic data types;
|
- [**Vector**](/docs/vector.md): a growable, contiguous array of homogenous generic data types;
|
||||||
- [**Map**](/docs/map.md): an associative array that handles generic heterogenous data types;
|
- [**Map**](/docs/map.md): an associative array that handles generic heterogenous data types;
|
||||||
|
- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
At its simplest, you can use this library as follows:
|
At its simplest, you can use this library as follows:
|
||||||
|
|
||||||
### `Vector`'s usage
|
### `Vector` usage
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -51,7 +52,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `Map`'s usage
|
### `Map` usage
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -95,6 +96,40 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `BigInt` usage
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "src/bigint.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile with: gcc main.c src/bigint.c
|
||||||
|
* Output: 20000! = 1819206320230345134827641...
|
||||||
|
* Time: real 0m5.482s user 0m5.453s sys 0m0.017
|
||||||
|
*/
|
||||||
|
int main(void) {
|
||||||
|
const int n = 20000;
|
||||||
|
bigint_t *fact = bigint_from_int(1).value.number;
|
||||||
|
|
||||||
|
for (int idx = 2; idx<=n; idx++) {
|
||||||
|
bigint_t *big_idx = bigint_from_int(idx).value.number;
|
||||||
|
bigint_t *partial_fact = bigint_prod(fact, big_idx).value.number;
|
||||||
|
|
||||||
|
bigint_destroy(fact);
|
||||||
|
bigint_destroy(big_idx);
|
||||||
|
fact = partial_fact;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%ld! = ", n);
|
||||||
|
bigint_print(fact);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
bigint_destroy(fact);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
For a more exhaustive example, refer to the `usage.c` file. There, you will find a program with proper error management
|
For a more exhaustive example, refer to the `usage.c` file. There, you will find a program with proper error management
|
||||||
and a sample usage for every available method. To run it, first issue the following command:
|
and a sample usage for every available method. To run it, first issue the following command:
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,19 @@
|
|||||||
|
|
||||||
#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9')
|
#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9')
|
||||||
|
|
||||||
|
#define DESTROY_IF(p) \
|
||||||
|
do { \
|
||||||
|
if ((p) && (p) != result.value.number) { \
|
||||||
|
bigint_destroy((p)); \
|
||||||
|
(p) = NULL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "bignum.h"
|
#include "bigint.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
@@ -96,6 +104,254 @@ bigint_result_t bigint_from_int(long long value) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bigint_from_string
|
||||||
|
* @string_num: an array of chars representing a number
|
||||||
|
*
|
||||||
|
* Takes a string containing a number and convert it to big integer
|
||||||
|
*
|
||||||
|
* Returns a bigint_result_t data type containing a new big integer
|
||||||
|
*/
|
||||||
|
bigint_result_t bigint_from_string(const char *string_num) {
|
||||||
|
bigint_result_t result = {0};
|
||||||
|
|
||||||
|
if (string_num == NULL || *string_num == 0) {
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
SET_MSG(result, "Invalid string");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *number = malloc(sizeof(bigint_t));
|
||||||
|
if (number == NULL) {
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
SET_MSG(result, "Failed to allocate memory for big integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_result_t vec_res = vector_new(4, sizeof(int));
|
||||||
|
if (vec_res.status != VECTOR_OK) {
|
||||||
|
free(number);
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
COPY_MSG(result, vec_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
number->digits = vec_res.value.vector;
|
||||||
|
|
||||||
|
number->is_negative = false;
|
||||||
|
if (*string_num == '-') {
|
||||||
|
number->is_negative = true;
|
||||||
|
string_num++;
|
||||||
|
} else if (*string_num == '+') {
|
||||||
|
string_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the integer is valid or not
|
||||||
|
if (*string_num == '\0') {
|
||||||
|
vector_destroy(number->digits);
|
||||||
|
free(number);
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
SET_MSG(result, "Invalid integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether characters are digits
|
||||||
|
for (const char *p = string_num; *p; ++p) {
|
||||||
|
if (!IS_DIGIT((unsigned char)*p)) {
|
||||||
|
vector_destroy(number->digits);
|
||||||
|
free(number);
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
SET_MSG(result, "Invalid integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip leading zeros
|
||||||
|
while (*string_num == '0' && *(string_num + 1) != '\0') {
|
||||||
|
string_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t number_len = strlen(string_num);
|
||||||
|
|
||||||
|
// Process digits from right to left by chunks of the representation base
|
||||||
|
for (int i = number_len; i > 0; i -= BIGINT_BASE_DIGITS) {
|
||||||
|
const int start = (i - BIGINT_BASE_DIGITS > 0) ? i - BIGINT_BASE_DIGITS : 0;
|
||||||
|
const int chunk_len = (i - start);
|
||||||
|
|
||||||
|
int digit = 0;
|
||||||
|
for (int j = 0; j < chunk_len; j++) {
|
||||||
|
// digit *= 10 + (string_num[start + j] - '0');
|
||||||
|
digit = digit * 10 + (string_num[start + j] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_result_t push_res = vector_push(number->digits, &digit);
|
||||||
|
if (push_res.status != VECTOR_OK) {
|
||||||
|
vector_destroy(number->digits);
|
||||||
|
free(number);
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
COPY_MSG(result, push_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_result_t trim_res = bigint_trim_zeros(number);
|
||||||
|
if (trim_res.status != BIGINT_OK) {
|
||||||
|
vector_destroy(number->digits);
|
||||||
|
free(number);
|
||||||
|
|
||||||
|
return trim_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.value.number = number;
|
||||||
|
result.status = BIGINT_OK;
|
||||||
|
SET_MSG(result, "Big integer successfully created");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bigint_to_string
|
||||||
|
* @number: a valid non-null big number
|
||||||
|
*
|
||||||
|
* Converts a big integer to a C string
|
||||||
|
*
|
||||||
|
* Returns a bigint_result_t data type
|
||||||
|
*/
|
||||||
|
bigint_result_t bigint_to_string(const bigint_t *number) {
|
||||||
|
bigint_result_t result = {0};
|
||||||
|
|
||||||
|
if (number == NULL) {
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
SET_MSG(result, "Invalid big integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t size = vector_size(number->digits);
|
||||||
|
const size_t max_len = (size * BIGINT_BASE_DIGITS) + 2; // +2 for sign and terminator
|
||||||
|
|
||||||
|
char *str = malloc(max_len);
|
||||||
|
if (str == NULL) {
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
SET_MSG(result, "Failed to allocate memory for string");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ptr = str;
|
||||||
|
if (number->is_negative) {
|
||||||
|
*ptr++ = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print MSB without leading zeros
|
||||||
|
vector_result_t msb_res = vector_get(number->digits, size - 1);
|
||||||
|
if (msb_res.status != VECTOR_OK) {
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
COPY_MSG(result, msb_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *msb = (int*)msb_res.value.element;
|
||||||
|
ptr += sprintf(ptr, "%d", *msb);
|
||||||
|
|
||||||
|
// Print remaining digits with leading zeros
|
||||||
|
for (int idx = size - 2; idx >= 0; idx--) {
|
||||||
|
vector_result_t digit_res = vector_get(number->digits, idx);
|
||||||
|
if (digit_res.status != VECTOR_OK) {
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
COPY_MSG(result, digit_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *digit = (int*)digit_res.value.element;
|
||||||
|
ptr += sprintf(ptr, "%09d", *digit);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.value.string_num = str;
|
||||||
|
result.status = BIGINT_OK;
|
||||||
|
SET_MSG(result, "Big integer successfully converted");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bigint_clone
|
||||||
|
* @number: a valid non-null big integer
|
||||||
|
*
|
||||||
|
* Clones a big integer
|
||||||
|
*
|
||||||
|
* Returns a bigint_result_t data type containing the new big integer
|
||||||
|
*/
|
||||||
|
bigint_result_t bigint_clone(const bigint_t *number) {
|
||||||
|
bigint_result_t result = {0};
|
||||||
|
|
||||||
|
if (number == NULL) {
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
SET_MSG(result, "Invalid big integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *cloned = malloc(sizeof(bigint_t));
|
||||||
|
if (cloned == NULL) {
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
SET_MSG(result, "Failed to allocate memory for big integer");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_result_t vec_res = vector_new(vector_size(number->digits), sizeof(int));
|
||||||
|
if (vec_res.status != VECTOR_OK) {
|
||||||
|
free(cloned);
|
||||||
|
result.status = BIGINT_ERR_ALLOCATE;
|
||||||
|
COPY_MSG(result, vec_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned->digits = vec_res.value.vector;
|
||||||
|
cloned->is_negative = number->is_negative;
|
||||||
|
|
||||||
|
// Copy digits
|
||||||
|
for (size_t idx = 0; idx < vector_size(number->digits); idx++) {
|
||||||
|
vector_result_t get_res = vector_get(number->digits, idx);
|
||||||
|
if (get_res.status != VECTOR_OK) {
|
||||||
|
vector_destroy(cloned->digits);
|
||||||
|
free(cloned);
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
COPY_MSG(result, get_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *digit = (int*)get_res.value.element;
|
||||||
|
|
||||||
|
vector_result_t push_res = vector_push(cloned->digits, digit);
|
||||||
|
if (push_res.status != VECTOR_OK) {
|
||||||
|
vector_destroy(cloned->digits);
|
||||||
|
free(cloned);
|
||||||
|
result.status = BIGINT_ERR_INVALID;
|
||||||
|
COPY_MSG(result, push_res.message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.value.number = cloned;
|
||||||
|
result.status = BIGINT_OK;
|
||||||
|
SET_MSG(result, "Big integer successfully cloned");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bigint_trim_zeros
|
* bigint_trim_zeros
|
||||||
* @number: a non-null big integer
|
* @number: a non-null big integer
|
||||||
@@ -1011,360 +1267,83 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) {
|
|||||||
// Split the big integer at approximately half the size of the larger number
|
// 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;
|
const size_t pivot = (x_size > y_size ? x_size : y_size) / 2;
|
||||||
|
|
||||||
// Split x = x1 * BASE^pivot + x0
|
// Results of each step
|
||||||
bigint_t *x1 = NULL, *x0 = NULL;
|
bigint_t *x1 = NULL, *x0 = NULL;
|
||||||
bigint_result_t x_split_res = bigint_split(x, pivot, &x1, &x0);
|
bigint_t *y1 = NULL, *y0 = NULL;
|
||||||
if (x_split_res.status != BIGINT_OK) {
|
bigint_t *z0 = NULL, *z2 = NULL;
|
||||||
return x_split_res;
|
bigint_t *x_sum = NULL, *y_sum = NULL;
|
||||||
}
|
bigint_t *z1_temp = NULL, *z1_sub1 = NULL, *z1 = NULL;
|
||||||
|
bigint_t *z2_shifted = NULL, *z1_shifted = NULL;
|
||||||
|
bigint_t *temp = NULL, *product = NULL;
|
||||||
|
|
||||||
|
bigint_result_t tmp_res = {0};
|
||||||
|
|
||||||
|
// Split x = x1 * BASE^pivot + x0
|
||||||
|
tmp_res = bigint_split(x, pivot, &x1, &x0);
|
||||||
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
|
||||||
// Split y = y1 * BASE^pivot + y0
|
// Split y = y1 * BASE^pivot + y0
|
||||||
bigint_t *y1 = NULL, *y0 = NULL;
|
tmp_res = bigint_split(x, pivot, &y1, &y0);
|
||||||
bigint_result_t y_split_res = bigint_split(y, pivot, &y1, &y0);
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
if (y_split_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
|
|
||||||
return y_split_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform karatsuba's trick
|
// Perform karatsuba's trick
|
||||||
bigint_result_t z0_res = bigint_karatsuba(x0, y0); // x0 * y0
|
tmp_res = bigint_karatsuba(x0, y0); // z0 = x0 * y0
|
||||||
if (z0_res.status != BIGINT_OK) {
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
z0 = tmp_res.value.number;
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
|
|
||||||
return z0_res;
|
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;
|
||||||
bigint_result_t z2_res = bigint_karatsuba(x1, y1); // x1 * y1
|
|
||||||
if (z2_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0_res.value.number);
|
|
||||||
|
|
||||||
return z2_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *z0 = z0_res.value.number;
|
|
||||||
bigint_t *z2 = z2_res.value.number;
|
|
||||||
|
|
||||||
// z1 = (x0 + x1) * (y0 + y1) - z0 - z2
|
// z1 = (x0 + x1) * (y0 + y1) - z0 - z2
|
||||||
bigint_result_t x_sum_res = bigint_add(x0, x1);
|
tmp_res = bigint_add(x0, x1); // x_sum = x0 + x1
|
||||||
if (x_sum_res.status != BIGINT_OK) {
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
x_sum = tmp_res.value.number;
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
|
|
||||||
return x_sum_res;
|
tmp_res = bigint_add(y0, y1); // y_sum = y0 + y1
|
||||||
}
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
y_sum = tmp_res.value.number;
|
||||||
|
|
||||||
bigint_t *x_sum = x_sum_res.value.number;
|
tmp_res = bigint_karatsuba(x_sum, y_sum); // z1_temp = (x0 + x1) * (y0 + y1)
|
||||||
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
z1_temp = tmp_res.value.number;
|
||||||
|
|
||||||
bigint_result_t y_sum_res = bigint_add(y0, y1);
|
tmp_res = bigint_sub(z1_temp, z0); // z1_sub1 = z1_temp - z0
|
||||||
if (y_sum_res.status != BIGINT_OK) {
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
z1_sub1 = tmp_res.value.number;
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum);
|
|
||||||
|
|
||||||
return y_sum_res;
|
tmp_res = bigint_sub(z1_sub1, z2); // z1 = z1_sub1 - z2
|
||||||
}
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
z1 = tmp_res.value.number;
|
||||||
|
|
||||||
bigint_t *y_sum = y_sum_res.value.number;
|
tmp_res = bigint_shift_left(z2, 2 * pivot); // z2_shifted = z2 << (2 * pivot)
|
||||||
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
z2_shifted = tmp_res.value.number;
|
||||||
|
|
||||||
// (x0 + x1) * (y0 + y1)
|
tmp_res = bigint_shift_left(z1, pivot); // z1_shifted = z1 << pivot
|
||||||
bigint_result_t z1_temp_res = bigint_karatsuba(x_sum ,y_sum);
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
if (z1_temp_res.status != BIGINT_OK) {
|
z1_shifted = tmp_res.value.number;
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
|
|
||||||
return z1_temp_res;
|
tmp_res = bigint_add(z2_shifted, z1_shifted);
|
||||||
}
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
temp = tmp_res.value.number;
|
||||||
|
|
||||||
bigint_t *z1_temp = z1_temp_res.value.number;
|
tmp_res = bigint_add(temp, z0);
|
||||||
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
// z1 = ... - z0
|
product = tmp_res.value.number;
|
||||||
bigint_result_t z1_sub1_res = bigint_sub(z1_temp, z0);
|
|
||||||
if (z1_sub1_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp);
|
|
||||||
|
|
||||||
return z1_sub1_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *z1_sub1 = z1_sub1_res.value.number;
|
|
||||||
|
|
||||||
// z1 = ... - z2
|
|
||||||
bigint_result_t z1_sub2_res = bigint_sub(z1_sub1, z2);
|
|
||||||
if (z1_sub2_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
|
|
||||||
return z1_sub2_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *z1 = z1_sub2_res.value.number;
|
|
||||||
|
|
||||||
// product = z^2 * BASE^(2pivot) + z1 * BASE^pivot + z0
|
|
||||||
bigint_result_t z2_shift_res = bigint_shift_left(z2, 2*pivot);
|
|
||||||
if (z2_shift_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
bigint_destroy(z1);
|
|
||||||
|
|
||||||
return z2_shift_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_result_t z1_shift_res = bigint_shift_left(z1, pivot);
|
|
||||||
if (z1_shift_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
bigint_destroy(z1);
|
|
||||||
|
|
||||||
return z1_shift_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *z2_shifted = z2_shift_res.value.number;
|
|
||||||
bigint_t *z1_shifted = z1_shift_res.value.number;
|
|
||||||
|
|
||||||
bigint_result_t temp_res = bigint_add(z2_shifted, z1_shifted);
|
|
||||||
if (temp_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
bigint_destroy(z1); bigint_destroy(z2_shifted);
|
|
||||||
bigint_destroy(z1_shifted);
|
|
||||||
|
|
||||||
return temp_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *temp = temp_res.value.number;
|
|
||||||
|
|
||||||
// product = ... + z0
|
|
||||||
bigint_result_t product_res = bigint_add(temp, z0);
|
|
||||||
if (product_res.status != BIGINT_OK) {
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
bigint_destroy(z1); bigint_destroy(z2_shifted);
|
|
||||||
bigint_destroy(z1_shifted); bigint_destroy(temp);
|
|
||||||
|
|
||||||
return product_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *product = product_res.value.number;
|
|
||||||
|
|
||||||
// Clean allocated numbers
|
|
||||||
bigint_destroy(x1); bigint_destroy(x0);
|
|
||||||
bigint_destroy(y1); bigint_destroy(y0);
|
|
||||||
bigint_destroy(z0); bigint_destroy(z1); bigint_destroy(z2);
|
|
||||||
bigint_destroy(x_sum); bigint_destroy(y_sum);
|
|
||||||
bigint_destroy(z1_temp); bigint_destroy(z1_sub1);
|
|
||||||
bigint_destroy(z2_shifted); bigint_destroy(z1_shifted);
|
|
||||||
bigint_destroy(temp);
|
|
||||||
|
|
||||||
result.value.number = product;
|
result.value.number = product;
|
||||||
result.status = BIGINT_OK;
|
result.status = BIGINT_OK;
|
||||||
SET_MSG(result, "Product between big integers was successful");
|
SET_MSG(result, "Product between big integers was successful");
|
||||||
|
|
||||||
return result;
|
cleanup: // Destroy intermediate allocations except for the product
|
||||||
}
|
DESTROY_IF(x1); DESTROY_IF(x0);
|
||||||
|
DESTROY_IF(y1); DESTROY_IF(y0);
|
||||||
/**
|
DESTROY_IF(z0); DESTROY_IF(z2);
|
||||||
* bigint_from_string
|
DESTROY_IF(x_sum); DESTROY_IF(y_sum);
|
||||||
* @string_num: an array of chars representing a number
|
DESTROY_IF(z1_temp); DESTROY_IF(z1_sub1); DESTROY_IF(z1);
|
||||||
*
|
DESTROY_IF(z2_shifted); DESTROY_IF(z1_shifted);
|
||||||
* Takes a string containing a number and convert it to big integer
|
DESTROY_IF(temp);
|
||||||
*
|
|
||||||
* Returns a bigint_result_t data type containing a new big integer
|
|
||||||
*/
|
|
||||||
bigint_result_t bigint_from_string(const char *string_num) {
|
|
||||||
bigint_result_t result = {0};
|
|
||||||
|
|
||||||
if (string_num == NULL || *string_num == 0) {
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
SET_MSG(result, "Invalid string");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *number = malloc(sizeof(bigint_t));
|
|
||||||
if (number == NULL) {
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
SET_MSG(result, "Failed to allocate memory for big integer");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_result_t vec_res = vector_new(4, sizeof(int));
|
|
||||||
if (vec_res.status != VECTOR_OK) {
|
|
||||||
free(number);
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
COPY_MSG(result, vec_res.message);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
number->digits = vec_res.value.vector;
|
|
||||||
|
|
||||||
number->is_negative = false;
|
|
||||||
if (*string_num == '-') {
|
|
||||||
number->is_negative = true;
|
|
||||||
string_num++;
|
|
||||||
} else if (*string_num == '+') {
|
|
||||||
string_num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the integer is valid or not
|
|
||||||
if (*string_num == '\0') {
|
|
||||||
vector_destroy(number->digits);
|
|
||||||
free(number);
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
SET_MSG(result, "Invalid integer");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether characters are digits
|
|
||||||
for (const char *p = string_num; *p; ++p) {
|
|
||||||
if (!IS_DIGIT((unsigned char)*p)) {
|
|
||||||
vector_destroy(number->digits);
|
|
||||||
free(number);
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
SET_MSG(result, "Invalid integer");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip leading zeros
|
|
||||||
while (*string_num == '0' && *(string_num + 1) != '\0') {
|
|
||||||
string_num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t number_len = strlen(string_num);
|
|
||||||
|
|
||||||
// Process digits from right to left by chunks of the representation base
|
|
||||||
for (int i = number_len; i > 0; i -= BIGINT_BASE_DIGITS) {
|
|
||||||
const int start = (i - BIGINT_BASE_DIGITS > 0) ? i - BIGINT_BASE_DIGITS : 0;
|
|
||||||
const int chunk_len = (i - start);
|
|
||||||
|
|
||||||
int digit = 0;
|
|
||||||
for (int j = 0; j < chunk_len; j++) {
|
|
||||||
// digit *= 10 + (string_num[start + j] - '0');
|
|
||||||
digit = digit * 10 + (string_num[start + j] - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_result_t push_res = vector_push(number->digits, &digit);
|
|
||||||
if (push_res.status != VECTOR_OK) {
|
|
||||||
vector_destroy(number->digits);
|
|
||||||
free(number);
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
COPY_MSG(result, push_res.message);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_result_t trim_res = bigint_trim_zeros(number);
|
|
||||||
if (trim_res.status != BIGINT_OK) {
|
|
||||||
vector_destroy(number->digits);
|
|
||||||
free(number);
|
|
||||||
|
|
||||||
return trim_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.value.number = number;
|
|
||||||
result.status = BIGINT_OK;
|
|
||||||
SET_MSG(result, "Big integer successfully created");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bigint_clone
|
|
||||||
* @number: a valid non-null big integer
|
|
||||||
*
|
|
||||||
* Clones a big integer
|
|
||||||
*
|
|
||||||
* Returns a bigint_result_t data type containing the new big integer
|
|
||||||
*/
|
|
||||||
bigint_result_t bigint_clone(const bigint_t *number) {
|
|
||||||
bigint_result_t result = {0};
|
|
||||||
|
|
||||||
if (number == NULL) {
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
SET_MSG(result, "Invalid big integer");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *cloned = malloc(sizeof(bigint_t));
|
|
||||||
if (cloned == NULL) {
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
SET_MSG(result, "Failed to allocate memory for big integer");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_result_t vec_res = vector_new(vector_size(number->digits), sizeof(int));
|
|
||||||
if (vec_res.status != VECTOR_OK) {
|
|
||||||
free(cloned);
|
|
||||||
result.status = BIGINT_ERR_ALLOCATE;
|
|
||||||
COPY_MSG(result, vec_res.message);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
cloned->digits = vec_res.value.vector;
|
|
||||||
cloned->is_negative = number->is_negative;
|
|
||||||
|
|
||||||
// Copy digits
|
|
||||||
for (size_t idx = 0; idx < vector_size(number->digits); idx++) {
|
|
||||||
vector_result_t get_res = vector_get(number->digits, idx);
|
|
||||||
if (get_res.status != VECTOR_OK) {
|
|
||||||
vector_destroy(cloned->digits);
|
|
||||||
free(cloned);
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
COPY_MSG(result, get_res.message);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int *digit = (int*)get_res.value.element;
|
|
||||||
|
|
||||||
vector_result_t push_res = vector_push(cloned->digits, digit);
|
|
||||||
if (push_res.status != VECTOR_OK) {
|
|
||||||
vector_destroy(cloned->digits);
|
|
||||||
free(cloned);
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
COPY_MSG(result, push_res.message);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.value.number = cloned;
|
|
||||||
result.status = BIGINT_OK;
|
|
||||||
SET_MSG(result, "Big integer successfully cloned");
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1395,3 +1374,30 @@ bigint_result_t bigint_destroy(bigint_t *number) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bigint_print
|
||||||
|
* @number: a valid non-null big integer
|
||||||
|
*
|
||||||
|
* Prints @number to standard output
|
||||||
|
*
|
||||||
|
* Returns a bigint_result_t data type
|
||||||
|
*/
|
||||||
|
bigint_result_t bigint_print(const bigint_t *number) {
|
||||||
|
bigint_result_t result = {0};
|
||||||
|
|
||||||
|
bigint_result_t num_str_res = bigint_to_string(number);
|
||||||
|
if (num_str_res.status != BIGINT_OK) {
|
||||||
|
return num_str_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *number_str = num_str_res.value.string_num;
|
||||||
|
|
||||||
|
printf("%s", number_str);
|
||||||
|
free(number_str);
|
||||||
|
|
||||||
|
result.status = BIGINT_OK;
|
||||||
|
SET_MSG(result, "Big integer successfully printed");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef BIGNUM_H
|
#ifndef BIGINT_H
|
||||||
#define BIGNUM_H
|
#define BIGINT_H
|
||||||
|
|
||||||
#define RESULT_MSG_SIZE 64
|
#define RESULT_MSG_SIZE 64
|
||||||
|
|
||||||
@@ -9,10 +9,9 @@
|
|||||||
#define BIGINT_BASE_DIGITS 9
|
#define BIGINT_BASE_DIGITS 9
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
typedef enum { false = 0x0, true } bool;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BIGINT_OK = 0x0,
|
BIGINT_OK = 0x0,
|
||||||
BIGINT_ERR_ALLOCATE,
|
BIGINT_ERR_ALLOCATE,
|
||||||
@@ -41,12 +40,14 @@ extern "C" {
|
|||||||
|
|
||||||
bigint_result_t bigint_from_int(long long value);
|
bigint_result_t bigint_from_int(long long value);
|
||||||
bigint_result_t bigint_from_string(const char *string_num);
|
bigint_result_t bigint_from_string(const char *string_num);
|
||||||
|
bigint_result_t bigint_to_string(const bigint_t *number);
|
||||||
bigint_result_t bigint_clone(const bigint_t *number);
|
bigint_result_t bigint_clone(const bigint_t *number);
|
||||||
bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y);
|
bigint_result_t bigint_compare(const bigint_t *x, const bigint_t *y);
|
||||||
bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y);
|
bigint_result_t bigint_add(const bigint_t *x, const bigint_t *y);
|
||||||
bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y);
|
bigint_result_t bigint_sub(const bigint_t *x, const bigint_t *y);
|
||||||
bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y);
|
bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y);
|
||||||
bigint_result_t bigint_destroy(bigint_t *number);
|
bigint_result_t bigint_destroy(bigint_t *number);
|
||||||
|
bigint_result_t bigint_print(const bigint_t *number);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
80
usage.c
80
usage.c
@@ -20,9 +20,12 @@
|
|||||||
|
|
||||||
#include "src/vector.h"
|
#include "src/vector.h"
|
||||||
#include "src/map.h"
|
#include "src/map.h"
|
||||||
|
#include "src/bigint.h"
|
||||||
|
|
||||||
static int vector_usage();
|
static int vector_usage();
|
||||||
static int map_usage();
|
static int map_usage();
|
||||||
|
static int bigint_usage();
|
||||||
|
|
||||||
static vector_order_t cmp_int_asc(const void *x, const void *y);
|
static vector_order_t cmp_int_asc(const void *x, const void *y);
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@@ -36,6 +39,11 @@ int main(void) {
|
|||||||
st = map_usage();
|
st = map_usage();
|
||||||
if (st) { return st; }
|
if (st) { return st; }
|
||||||
|
|
||||||
|
SEP(50);
|
||||||
|
|
||||||
|
st = bigint_usage();
|
||||||
|
if (st) { return st; }
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +290,8 @@ int map_usage() {
|
|||||||
printf("Map cleared (size should be 0): %zu\n", map_size(map));
|
printf("Map cleared (size should be 0): %zu\n", map_size(map));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
// Delete the map
|
// Delete the map
|
||||||
map_result_t del_res = map_destroy(map);
|
map_result_t del_res = map_destroy(map);
|
||||||
if (del_res.status != MAP_OK) {
|
if (del_res.status != MAP_OK) {
|
||||||
@@ -292,3 +302,73 @@ int map_usage() {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bigint_usage() {
|
||||||
|
// Create a big integer
|
||||||
|
bigint_result_t x_res = bigint_from_string("123456789");
|
||||||
|
if (x_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while creating big number: %s\n", x_res.message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_result_t y_res = bigint_from_string("987654321");
|
||||||
|
if (x_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while creating big number: %s\n", x_res.message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *x = x_res.value.number;
|
||||||
|
bigint_t *y = y_res.value.number;
|
||||||
|
|
||||||
|
// Sum two big integers
|
||||||
|
bigint_result_t sum_res = bigint_add(x, y);
|
||||||
|
if (sum_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while summing two big numbers: %s\n", sum_res.message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *sum = sum_res.value.number;
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
printf("123456789 + 987654321 (should be 1,111,111,110) = ");
|
||||||
|
bigint_print(sum);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Subtract two big integers
|
||||||
|
bigint_result_t diff_res = bigint_sub(x, y);
|
||||||
|
if (diff_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while subtracting two big numbers: %s\n", diff_res.message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *diff = diff_res.value.number;
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
printf("123456789 - 987654321 (should be -864,197,532) = ");
|
||||||
|
bigint_print(diff);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Multiply two big integers
|
||||||
|
bigint_result_t prod_res = bigint_prod(x, y);
|
||||||
|
if (prod_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while multiplying two big numbers: %s\n", prod_res.message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *prod = prod_res.value.number;
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
printf("123456789 * 987654321 (should be 121,932,631,112,635,269) = ");
|
||||||
|
bigint_print(prod);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
bigint_destroy(x); bigint_destroy(y);
|
||||||
|
bigint_destroy(sum); bigint_destroy(diff); bigint_destroy(prod);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user