Fixed bug about bigint product, updated unit tests and sample usage.
This commit is contained in:
@@ -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`.
|
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
|
## Documentation
|
||||||
For additional details about this library (internal design, memory
|
For additional details about this library (internal design, memory
|
||||||
management, data ownership, etc.) go to the [docs folder](/docs).
|
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
|
Computing Map average time...average time: 31 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This library is released under the GPLv3 license. You can find a copy of the license with this repository or by visiting
|
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/).
|
[the following link](https://choosealicense.com/licenses/gpl-3.0/).
|
||||||
|
|||||||
@@ -90,3 +90,12 @@ of them has an unique scope as described below:
|
|||||||
- `compare_status`: result of `bigint_compare`;
|
- `compare_status`: result of `bigint_compare`;
|
||||||
- `string_num`: result of `bigint_to_string`.
|
- `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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 x_size = vector_size(x->digits);
|
||||||
const size_t y_size = vector_size(y->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
|
// 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);
|
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;
|
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 on error
|
cleanup: // Destroy intermediate allocations on error
|
||||||
if (x1) { bigint_destroy(x1); }
|
if (x1) { bigint_destroy(x1); }
|
||||||
if (x0) { bigint_destroy(x0); }
|
if (x0) { bigint_destroy(x0); }
|
||||||
|
|||||||
@@ -142,6 +142,45 @@ void test_bigint_prod(void) {
|
|||||||
bigint_destroy(prod.value.number);
|
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
|
// Test product between mixed negative big numbers
|
||||||
void test_bigint_prod_mixed(void) {
|
void test_bigint_prod_mixed(void) {
|
||||||
bigint_result_t x = bigint_from_int(-1234);
|
bigint_result_t x = bigint_from_int(-1234);
|
||||||
@@ -363,6 +402,7 @@ int main(void) {
|
|||||||
TEST(bigint_sub_neg);
|
TEST(bigint_sub_neg);
|
||||||
TEST(bigint_sub_mixed);
|
TEST(bigint_sub_mixed);
|
||||||
TEST(bigint_prod);
|
TEST(bigint_prod);
|
||||||
|
TEST(bigint_very_large_prod);
|
||||||
TEST(bigint_prod_mixed);
|
TEST(bigint_prod_mixed);
|
||||||
TEST(bigint_prod_neg);
|
TEST(bigint_prod_neg);
|
||||||
TEST(bigint_div);
|
TEST(bigint_div);
|
||||||
|
|||||||
52
usage.c
52
usage.c
@@ -17,6 +17,8 @@
|
|||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "src/vector.h"
|
#include "src/vector.h"
|
||||||
#include "src/map.h"
|
#include "src/map.h"
|
||||||
@@ -308,15 +310,42 @@ int map_usage(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int bigint_usage(void) {
|
int bigint_usage(void) {
|
||||||
// Create two big integers
|
const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355";
|
||||||
bigint_result_t x_res = bigint_from_string("123456789");
|
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) {
|
if (x_res.status != BIGINT_OK) {
|
||||||
printf("Error while creating big number: %s\n", x_res.message);
|
printf("Error while creating big number: %s\n", x_res.message);
|
||||||
|
|
||||||
return 1;
|
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) {
|
if (x_res.status != BIGINT_OK) {
|
||||||
printf("Error while creating big number: %s\n", x_res.message);
|
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;
|
bigint_t *sum = sum_res.value.number;
|
||||||
|
|
||||||
// Print result
|
// 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
|
// Subtract two big integers
|
||||||
bigint_result_t diff_res = bigint_sub(x, y);
|
bigint_result_t diff_res = bigint_sub(x, y);
|
||||||
@@ -350,7 +379,7 @@ int bigint_usage(void) {
|
|||||||
bigint_t *diff = diff_res.value.number;
|
bigint_t *diff = diff_res.value.number;
|
||||||
|
|
||||||
// Print result
|
// 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
|
// Multiply two big integers
|
||||||
bigint_result_t prod_res = bigint_prod(x, y);
|
bigint_result_t prod_res = bigint_prod(x, y);
|
||||||
@@ -363,10 +392,10 @@ int bigint_usage(void) {
|
|||||||
bigint_t *prod = prod_res.value.number;
|
bigint_t *prod = prod_res.value.number;
|
||||||
|
|
||||||
// Print result
|
// 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 *a = bigint_from_string(x_origin).value.number;
|
||||||
bigint_t *b = bigint_from_string("43569678678678678678678432").value.number;
|
bigint_t *b = bigint_from_string(y_origin).value.number;
|
||||||
|
|
||||||
// Divide two big integers
|
// Divide two big integers
|
||||||
bigint_result_t div_res = bigint_divmod(a, b);
|
bigint_result_t div_res = bigint_divmod(a, b);
|
||||||
@@ -381,15 +410,16 @@ int bigint_usage(void) {
|
|||||||
|
|
||||||
// Print result
|
// Print result
|
||||||
bigint_printf(
|
bigint_printf(
|
||||||
"457349545684946456456456567567 / 43569678678678678678678432 (should be 10,496) = %B\
|
"division result = %B\
|
||||||
\n457349545684946456456456567567 %% 43569678678678678678678432 (should be 42,198,273,535,045,045,047,745,295) = %B\n",
|
\nmod result = %B\n",
|
||||||
quotient, remainder);
|
quotient, remainder);
|
||||||
|
|
||||||
// Destroy big numbers
|
// Destroy big numbers and strings
|
||||||
bigint_destroy(x); bigint_destroy(y);
|
bigint_destroy(x); bigint_destroy(y);
|
||||||
bigint_destroy(a); bigint_destroy(b);
|
bigint_destroy(a); bigint_destroy(b);
|
||||||
bigint_destroy(sum); bigint_destroy(diff);
|
bigint_destroy(sum); bigint_destroy(diff);
|
||||||
bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder);
|
bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder);
|
||||||
|
free(large_x); free(large_y);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user