From 7cc2615f8ba25c0fea02c54c7df07865860969a8 Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Wed, 17 Dec 2025 17:04:16 +0100 Subject: [PATCH] Fixed bug about bigint product, updated unit tests and sample usage. --- README.md | 8 +++++++ docs/bigint.md | 9 ++++++++ src/bigint.c | 6 +++++- tests/test_bigint.c | 40 ++++++++++++++++++++++++++++++++++ usage.c | 52 +++++++++++++++++++++++++++++++++++---------- 5 files changed, 103 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 44cb523..417088d 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,13 @@ $ make clean all This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`. +> [!NOTE] +> This project is primarily developed for learning purposes and was not created with industrial +> or production use in mind. As such, it is not intended to compete with any existing C library. +> In particular, the big number implementation does not aim to match the design, the maturity and +> the performance of established solutions such as the +> GNU Multiple Precision Arithmetic Library (GMP). + ## Documentation For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs). @@ -158,6 +165,7 @@ Computing Vector average time...average time: 18 ms Computing Map average time...average time: 31 ms ``` + ## License This library is released under the GPLv3 license. You can find a copy of the license with this repository or by visiting [the following link](https://choosealicense.com/licenses/gpl-3.0/). diff --git a/docs/bigint.md b/docs/bigint.md index 3f07731..e51980a 100644 --- a/docs/bigint.md +++ b/docs/bigint.md @@ -90,3 +90,12 @@ of them has an unique scope as described below: - `compare_status`: result of `bigint_compare`; - `string_num`: result of `bigint_to_string`. + +> [!IMPORTANT] +> Currently, the division implementation employs a quadratic-time algorithm derived from the conventional _"grade school"_ long-division method. +> This approach performs adequately for integers of modest size (up to approximately 200 digits) but becomes highly inefficient when handling +> substantially larger integers (~1500 digits). +> +> Improving the efficiency of this algorithm would require further research into advanced +> numerical algorithms, which is something that I currently not inclined to pursue. + diff --git a/src/bigint.c b/src/bigint.c index e7835b5..6cc491e 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -1413,9 +1413,11 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { const size_t x_size = vector_size(x->digits); const size_t y_size = vector_size(y->digits); + 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 (x_size <= 32 || y_size <= 32) { + if (min_size <= 32 || max_size / min_size > 2) { return bigint_karatsuba_base(x, y); } @@ -1498,6 +1500,8 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) { 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); } diff --git a/tests/test_bigint.c b/tests/test_bigint.c index 0cc5aad..6dba103 100644 --- a/tests/test_bigint.c +++ b/tests/test_bigint.c @@ -142,6 +142,45 @@ void test_bigint_prod(void) { bigint_destroy(prod.value.number); } +// Test product betweem very big numbers (i.e., use Karatsuba) +void test_bigint_very_large_prod(void) { + const char *big_x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355"; + const char *big_y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937"; + size_t x_len = strlen(big_x_origin); + size_t y_len = strlen(big_y_origin); + size_t large_x_size = x_len * 100 + 1; + size_t large_y_size = y_len * 100 + 1; + + char *large_x = malloc(large_x_size); + char *large_y = malloc(large_y_size); + + assert(large_x != NULL); + assert(large_y != NULL); + + large_x[0] = '\0'; + large_y[0] = '\0'; + + for (size_t idx = 0; idx < 50; idx++) { + strcat(large_x, big_x_origin); + strcat(large_y, big_y_origin); + } + + bigint_result_t x = bigint_from_string(large_x); + bigint_result_t y = bigint_from_string(large_y); + + assert(x.status == BIGINT_OK); + assert(y.status == BIGINT_OK); + + bigint_result_t product_res = bigint_prod(x.value.number, y.value.number); + + assert(product_res.status == BIGINT_OK); + + bigint_destroy(product_res.value.number); + bigint_destroy(x.value.number); + bigint_destroy(y.value.number); + free(large_x); free(large_y); +} + // Test product between mixed negative big numbers void test_bigint_prod_mixed(void) { bigint_result_t x = bigint_from_int(-1234); @@ -363,6 +402,7 @@ int main(void) { TEST(bigint_sub_neg); TEST(bigint_sub_mixed); TEST(bigint_prod); + TEST(bigint_very_large_prod); TEST(bigint_prod_mixed); TEST(bigint_prod_neg); TEST(bigint_div); diff --git a/usage.c b/usage.c index 4db9d0a..e771fb5 100644 --- a/usage.c +++ b/usage.c @@ -17,6 +17,8 @@ } while(0) #include +#include +#include #include "src/vector.h" #include "src/map.h" @@ -308,15 +310,42 @@ int map_usage(void) { } int bigint_usage(void) { - // Create two big integers - bigint_result_t x_res = bigint_from_string("123456789"); + const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355"; + const char *y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937"; + const size_t x_len = strlen(x_origin); + const size_t y_len = strlen(y_origin); + const size_t large_x_size = x_len * 100 + 1; + const size_t large_y_size = y_len * 100 + 1; + + char *large_x = malloc(large_x_size); + char *large_y = malloc(large_y_size); + + if (large_x == NULL || large_y == NULL) { + printf("Error while allocating memory for strings\n"); + free(large_x); + free(large_y); + + return 1; + } + + large_x[0] = '\0'; + large_y[0] = '\0'; + + // Concatenate 100 times + for (size_t idx = 0; idx < 100; idx++) { + strcat(large_x, x_origin); + strcat(large_y, y_origin); + } + + // Create two big integers from previous strings + bigint_result_t x_res = bigint_from_string(large_x); 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"); + bigint_result_t y_res = bigint_from_string(large_y); if (x_res.status != BIGINT_OK) { printf("Error while creating big number: %s\n", x_res.message); @@ -337,7 +366,7 @@ int bigint_usage(void) { bigint_t *sum = sum_res.value.number; // Print result - bigint_printf("123456789 + 987654321 (should be 1,111,111,110) = %B\n", sum); + bigint_printf("Sum result = %B\n", sum); // Subtract two big integers bigint_result_t diff_res = bigint_sub(x, y); @@ -350,7 +379,7 @@ int bigint_usage(void) { bigint_t *diff = diff_res.value.number; // Print result - bigint_printf("123456789 - 987654321 (should be -864,197,532) = %B\n", diff); + bigint_printf("difference result = %B\n", diff); // Multiply two big integers bigint_result_t prod_res = bigint_prod(x, y); @@ -363,10 +392,10 @@ int bigint_usage(void) { bigint_t *prod = prod_res.value.number; // Print result - bigint_printf("123456789 * 987654321 (should be 121,932,631,112,635,269) = %B\n", prod); + bigint_printf("multiplication result = %B\n", prod); - bigint_t *a = bigint_from_string("457349545684946456456456567567").value.number; - bigint_t *b = bigint_from_string("43569678678678678678678432").value.number; + bigint_t *a = bigint_from_string(x_origin).value.number; + bigint_t *b = bigint_from_string(y_origin).value.number; // Divide two big integers bigint_result_t div_res = bigint_divmod(a, b); @@ -381,15 +410,16 @@ int bigint_usage(void) { // Print result bigint_printf( - "457349545684946456456456567567 / 43569678678678678678678432 (should be 10,496) = %B\ - \n457349545684946456456456567567 %% 43569678678678678678678432 (should be 42,198,273,535,045,045,047,745,295) = %B\n", + "division result = %B\ + \nmod result = %B\n", quotient, remainder); - // Destroy big numbers + // Destroy big numbers and strings bigint_destroy(x); bigint_destroy(y); bigint_destroy(a); bigint_destroy(b); bigint_destroy(sum); bigint_destroy(diff); bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder); + free(large_x); free(large_y); return 0; }