fast_div_proto #1
2
Makefile
2
Makefile
@@ -52,7 +52,7 @@ $(OBJ_DIR):
|
|||||||
mkdir -p $(OBJ_DIR)
|
mkdir -p $(OBJ_DIR)
|
||||||
|
|
||||||
# Benchmark rules
|
# Benchmark rules
|
||||||
$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o
|
$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o $(BENCH_OBJ_DIR)/bigint.o
|
||||||
$(CC) $(BENCH_FLAGS) -o $@ $^
|
$(CC) $(BENCH_FLAGS) -o $@ $^
|
||||||
|
|
||||||
$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR)
|
$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR)
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -145,9 +145,7 @@ This will compile the library as well as the `usage.c` file, the unit tests and
|
|||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This project is primarily developed for learning purposes and was not created with industrial
|
> 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.
|
> or production use in mind. As such, it is not intended to compete with any existing C library such as the
|
||||||
> 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).
|
> GNU Multiple Precision Arithmetic Library (GMP).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
@@ -167,9 +165,11 @@ $ ./test_bigint
|
|||||||
Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector` and the `Map` data structures. You can run it by issuing the following command:
|
Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector` and the `Map` data structures. You can run it by issuing the following command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
$ make clean all CC=clang
|
||||||
$ ./benchmark_datum
|
$ ./benchmark_datum
|
||||||
Computing Vector average time...average time: 18 ms
|
omputing Vector average time...average time: 8 ms
|
||||||
Computing Map average time...average time: 31 ms
|
Computing Map average time...average time: 53 ms
|
||||||
|
Computing BigInt average time...average time: 76 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "../src/vector.h"
|
#include "../src/vector.h"
|
||||||
#include "../src/map.h"
|
#include "../src/map.h"
|
||||||
|
#include "../src/bigint.h"
|
||||||
|
|
||||||
typedef void (*test_fn_t)(size_t iterations);
|
typedef void (*test_fn_t)(size_t iterations);
|
||||||
|
|
||||||
@@ -22,11 +23,6 @@ void test_vector(size_t iterations) {
|
|||||||
sum += *val;
|
sum += *val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Another trick to prevent compiler optimization
|
|
||||||
if (sum == 0xB00B5) {
|
|
||||||
printf("sum = %llu\n", (unsigned long long)sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_destroy(vec);
|
vector_destroy(vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,32 +49,99 @@ void test_map(size_t iterations) {
|
|||||||
|
|
||||||
// Cleanup values
|
// Cleanup values
|
||||||
for (size_t idx = 0; idx < map->capacity; idx++) {
|
for (size_t idx = 0; idx < map->capacity; idx++) {
|
||||||
if (map->elements[idx].state == ENTRY_OCCUPIED) {
|
snprintf(key, sizeof(key), "key_%zu", idx);
|
||||||
int *val = (int*)map->elements[idx].value;
|
|
||||||
|
int *val = (int *)map_get(map, key).value.element;
|
||||||
free(val);
|
free(val);
|
||||||
}
|
|
||||||
|
map_remove(map, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
map_destroy(map);
|
map_destroy(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
long long benchmark(test_fn_t fun, size_t iterations, size_t runs) {
|
void test_bigint(size_t iterations) {
|
||||||
long long total = 0;
|
volatile uint64_t accumulator = 0;
|
||||||
for (size_t idx = 0; idx < runs; idx++) {
|
|
||||||
clock_t start = clock();
|
|
||||||
fun(iterations);
|
|
||||||
clock_t end = clock();
|
|
||||||
|
|
||||||
total += (long long)((end - start) * 1000 / CLOCKS_PER_SEC);
|
for (size_t idx = 1; idx <= iterations; idx++) {
|
||||||
|
long long a_val = (long long)idx * 123456789LL;
|
||||||
|
long long b_val = (long long)idx * 17777LL;
|
||||||
|
|
||||||
|
bigint_result_t a_res = bigint_from_int(a_val);
|
||||||
|
bigint_result_t b_res = bigint_from_int(b_val);
|
||||||
|
|
||||||
|
if (a_res.status != BIGINT_OK || b_res.status != BIGINT_OK) {
|
||||||
|
bigint_destroy(a_res.value.number);
|
||||||
|
bigint_destroy(b_res.value.number);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return total / runs;
|
bigint_t *a = a_res.value.number;
|
||||||
|
bigint_t *b = b_res.value.number;
|
||||||
|
|
||||||
|
// Addition
|
||||||
|
bigint_result_t add_res = bigint_add(a, b);
|
||||||
|
if (add_res.status == BIGINT_OK) {
|
||||||
|
vector_result_t v = vector_get(add_res.value.number->digits, 0);
|
||||||
|
if (v.status == VECTOR_OK) { accumulator += *(int *)v.value.element; }
|
||||||
|
bigint_destroy(add_res.value.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substraction
|
||||||
|
bigint_result_t sub_res = bigint_sub(a, b);
|
||||||
|
if (sub_res.status == BIGINT_OK) {
|
||||||
|
vector_result_t v = vector_get(sub_res.value.number->digits, 0);
|
||||||
|
if (v.status == VECTOR_OK) { accumulator += *(int *)v.value.element; }
|
||||||
|
bigint_destroy(sub_res.value.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplication
|
||||||
|
bigint_result_t mul_res = bigint_prod(a, b);
|
||||||
|
if (mul_res.status == BIGINT_OK) {
|
||||||
|
vector_result_t v = vector_get(mul_res.value.number->digits, 0);
|
||||||
|
if (v.status == VECTOR_OK) { accumulator += *(int *)v.value.element; }
|
||||||
|
bigint_destroy(mul_res.value.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Division
|
||||||
|
bigint_result_t div_res = bigint_divmod(a, b);
|
||||||
|
if (div_res.status == BIGINT_OK) {
|
||||||
|
vector_result_t v = vector_get(div_res.value.division.quotient->digits, 0);
|
||||||
|
if (v.status == VECTOR_OK) { accumulator += *(int *)v.value.element; }
|
||||||
|
bigint_destroy(div_res.value.division.quotient);
|
||||||
|
bigint_destroy(div_res.value.division.remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_destroy(a); bigint_destroy(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t now_ns(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
|
||||||
|
return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long benchmark(test_fn_t fun, size_t iterations, size_t runs) {
|
||||||
|
long long total = 0;
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < runs; idx++) {
|
||||||
|
uint64_t start = now_ns();
|
||||||
|
fun(iterations);
|
||||||
|
uint64_t end = now_ns();
|
||||||
|
|
||||||
|
total += (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (long long)(total / runs / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// Do a warmup run
|
// Do a warmup run
|
||||||
test_vector(1000);
|
test_vector(1000);
|
||||||
test_map(1000);
|
test_map(1000);
|
||||||
|
test_bigint(1000);
|
||||||
|
|
||||||
printf("Computing Vector average time...");
|
printf("Computing Vector average time...");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@@ -88,5 +151,9 @@ int main(void) {
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
printf("average time: %lld ms\n", benchmark(test_map, 1e5, 30));
|
printf("average time: %lld ms\n", benchmark(test_map, 1e5, 30));
|
||||||
|
|
||||||
|
printf("Computing BigInt average time...");
|
||||||
|
fflush(stdout);
|
||||||
|
printf("average time: %lld ms\n", benchmark(test_bigint, 1e5, 30));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -33,17 +33,18 @@ and the boolean `is_negative` variable denotes its sign.
|
|||||||
|
|
||||||
The `BigInt` data structure supports the following methods:
|
The `BigInt` data structure supports the following methods:
|
||||||
|
|
||||||
- `bigint_result_t bigint_from_int(value)`: create a big integer from a primitive `int` type;
|
- `bigint_result_t bigint_from_int(value)`: creates a big integer from a primitive `int` type;
|
||||||
- `bigint_result_t bigint_from_string(string_num)`: create a big integer from a C string;
|
- `bigint_result_t bigint_from_string(string_num)`: creates a big integer from a C string;
|
||||||
- `bigint_result_t bigint_to_string(number)`: convert a big integer to a C string;
|
- `bigint_result_t bigint_to_string(number)`: converts a big integer to a C string;
|
||||||
- `bigint_result_t bigint_clone(number)`: clone a big integer;
|
- `bigint_result_t bigint_clone(number)`: clones a big integer;
|
||||||
- `bigint_result_t bigint_compare(x, y)`: compare two big integers, returning either `-1`, `0` or `1` if the first is less than, equal than or greater than the second, respectively;
|
- `bigint_result_t bigint_compare(x, y)`: compares two big integers, returning either `-1`, `0` or `1` if the first is less than, equal than or greater than the second, respectively;
|
||||||
- `bigint_result_t bigint_add(x, y)`: add two big integers together in $\mathcal{O}(n)$;
|
- `bigint_result_t bigint_add(x, y)`: adds two big integers together in $\mathcal{O}(n)$;
|
||||||
- `bigint_result_t bigint_sub(x, y)`: subtract two big integers in $\mathcal{O}(n)$;
|
- `bigint_result_t bigint_sub(x, y)`: subtracts two big integers in $\mathcal{O}(n)$;
|
||||||
- `bigint_result_t bigint_prod(x, y)`: multiply two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$;
|
- `bigint_result_t bigint_prod(x, y)`: multiplies two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$;
|
||||||
- `bigint_result_t bigint_divmod(x, y)`: divide two big integers using *long division* algorithm in $\mathcal{O}(n^2)$, returning both the quotient and the remainder;
|
- `bigint_result_t bigint_divmod(x, y)`: divides two big integers using _Knuth's Algorithm D_ in $\mathcal{O}(n \times m)$ where $n$ and $m$ are the number of base-10^9
|
||||||
- `bigint_result_t bigint_mod(x, y)`: computes modulo of two big integers using *long division* algorithm in $\mathcal{O}(n^2)$;
|
parts/limbs in the divisor and the quotient, respectively. This method returns both the quotient and the remainder;
|
||||||
- `bigint_result_t bigint_destroy(number)`: delete the big number;
|
- `bigint_result_t bigint_mod(x, y)`: calls `bigint_divmod`, discards the quotient and yields the remainder;
|
||||||
|
- `bigint_result_t bigint_destroy(number)`: deletes the big number;
|
||||||
- `bigint_result_t bigint_printf(format, ...)`: `printf` wrapper that introduces the `%B` placeholder to print big numbers. It supports variadic parameters.
|
- `bigint_result_t bigint_printf(format, ...)`: `printf` wrapper that introduces the `%B` placeholder to print big numbers. It supports variadic parameters.
|
||||||
|
|
||||||
As you can see from the previous function signatures, methods that operate on the
|
As you can see from the previous function signatures, methods that operate on the
|
||||||
@@ -90,12 +91,3 @@ 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.
|
|
||||||
|
|
||||||
|
|||||||
12
docs/map.md
12
docs/map.md
@@ -37,12 +37,12 @@ free them before removing the keys or destroying the map.
|
|||||||
|
|
||||||
The `Map` data structure supports the following methods:
|
The `Map` data structure supports the following methods:
|
||||||
|
|
||||||
- `map_result_t map_new()`: initialize a new map;
|
- `map_result_t map_new()`: initializes a new map;
|
||||||
- `map_result_t map_add(map, key, value)`: add a `(key, value)` pair to the map;
|
- `map_result_t map_add(map, key, value)`: adds a `(key, value)` pair to the map;
|
||||||
- `map_result_t map_get(map, key)`: retrieve a values indexed by `key` if it exists;
|
- `map_result_t map_get(map, key)`: retrieves a values indexed by `key` if it exists;
|
||||||
- `map_result_t map_remove(map, key)`: remove a key from the map if it exists;
|
- `map_result_t map_remove(map, key)`: removes a key from the map if it exists;
|
||||||
- `map_result_t map_clear(map)`: reset the map state;
|
- `map_result_t map_clear(map)`: resets the map state;
|
||||||
- `map_result_t map_destroy(map)`: delete the map;
|
- `map_result_t map_destroy(map)`: deletes the map;
|
||||||
- `size_t map_size(map)`: returns map size (i.e., the number of elements);
|
- `size_t map_size(map)`: returns map size (i.e., the number of elements);
|
||||||
- `size_t map_capacity(map)`: returns map capacity (i.e., map total size).
|
- `size_t map_capacity(map)`: returns map capacity (i.e., map total size).
|
||||||
|
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ deletion.
|
|||||||
|
|
||||||
At the time being, `Vector` supports the following methods:
|
At the time being, `Vector` supports the following methods:
|
||||||
|
|
||||||
- `vector_result_t vector_new(size, data_size)`: create a new vector;
|
- `vector_result_t vector_new(size, data_size)`: creates a new vector;
|
||||||
- `vector_result_t vector_push(vector, value)`: add a new value to the vector;
|
- `vector_result_t vector_push(vector, value)`: adds a new value to the vector;
|
||||||
- `vector_result_t vector_set(vector, index, value)`: update the value of a given index if it exists;
|
- `vector_result_t vector_set(vector, index, value)`: updates the value of a given index if it exists;
|
||||||
- `vector_result_t vector_get(vector, index)`: return the value indexed by `index` if it exists;
|
- `vector_result_t vector_get(vector, index)`: returns the value indexed by `index` if it exists;
|
||||||
- `vector_result_t vector_sort(vector, cmp)`: sort vector using `cmp` function;
|
- `vector_result_t vector_sort(vector, cmp)`: sorts vector using `cmp` function;
|
||||||
- `vector_result_t vector_pop(vector)`: pop last element from the vector following the LIFO policy;
|
- `vector_result_t vector_pop(vector)`: pops last element from the vector following the LIFO policy;
|
||||||
- `vector_result_t vector_map(vector, callback, env)`: apply `callback` function to vector (in-place);
|
- `vector_result_t vector_map(vector, callback, env)`: applies `callback` function to vector (in-place);
|
||||||
- `vector_result_t vector_filter(vector, callback, env)`: filter vector using `callback` (in-place);
|
- `vector_result_t vector_filter(vector, callback, env)`: filters vector using `callback` (in-place);
|
||||||
- `vector_result_t vector_reduce(vector, accumulator, callback, env)`: fold/reduce vector using `callback`;
|
- `vector_result_t vector_reduce(vector, accumulator, callback, env)`: folds/reduces vector using `callback`;
|
||||||
- `vector_result_t vector_clear(vector)`: logically reset the vector. That is, new pushes will overwrite the memory;
|
- `vector_result_t vector_clear(vector)`: resets the vector logically. That is, new pushes will overwrite the memory;
|
||||||
- `vector_result_t vector_destroy(vector)`: delete the vector;
|
- `vector_result_t vector_destroy(vector)`: deletes the vector;
|
||||||
- `size_t vector_size(vector)`: return vector size (i.e., the number of elements);
|
- `size_t vector_size(vector)`: returns vector size (i.e., the number of elements);
|
||||||
- `size_t vector_capacity(vector)`: return vector capacity (i.e., vector total size).
|
- `size_t vector_capacity(vector)`: returns vector capacity (i.e., vector total size).
|
||||||
|
|
||||||
As you can see from the previous function signatures, most methods that operate
|
As you can see from the previous function signatures, most methods that operate
|
||||||
on the `Vector` data type return a custom type called `vector_result_t` which is
|
on the `Vector` data type return a custom type called `vector_result_t` which is
|
||||||
|
|||||||
373
src/bigint.c
373
src/bigint.c
@@ -9,6 +9,10 @@
|
|||||||
(result).message[RESULT_MSG_SIZE - 1] = '\0'; \
|
(result).message[RESULT_MSG_SIZE - 1] = '\0'; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define REMOVE(ptr) \
|
||||||
|
free(ptr); \
|
||||||
|
ptr = NULL
|
||||||
|
|
||||||
#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9')
|
#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9')
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -842,30 +846,32 @@ cleanup: // Destroy intermediate allocations on error
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bigint_dev
|
* bigint_div
|
||||||
* @x: a valid non-null big integer (dividend)
|
* @x: a non-null big integer acting as a dividend
|
||||||
* @y: a valid non-null big integer (divisor)
|
* @y: a non-null big integer acting as a divisor
|
||||||
*
|
*
|
||||||
* Computes division using long division algorithm in O(n^2)
|
* 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 data type
|
* 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) {
|
static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
|
||||||
bigint_result_t result = {0};
|
bigint_result_t result = {0};
|
||||||
bigint_result_t tmp_res = {0};
|
bigint_result_t tmp_res = {0};
|
||||||
|
|
||||||
bigint_t *quotient = NULL;
|
bigint_t *quotient = NULL;
|
||||||
bigint_t *remainder = NULL;
|
long long *u = NULL, *v = NULL, *q = NULL;
|
||||||
bigint_t *abs_y = NULL;
|
|
||||||
|
|
||||||
if (x == NULL || y == NULL) {
|
if (x == NULL || y == NULL) {
|
||||||
result.status = BIGINT_ERR_INVALID;
|
result.status = BIGINT_ERR_INVALID;
|
||||||
SET_MSG(result, "Invalid big numbers");
|
SET_MSG(result, "Invalid big integers");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for division by zero
|
|
||||||
const size_t y_size = vector_size(y->digits);
|
const size_t y_size = vector_size(y->digits);
|
||||||
if (y_size == 0) {
|
if (y_size == 0) {
|
||||||
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
||||||
@@ -875,16 +881,16 @@ static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (y_size == 1) {
|
if (y_size == 1) {
|
||||||
vector_result_t y_val_res = vector_get(y->digits, 0);
|
vector_result_t y0_res = vector_get(y->digits, 0);
|
||||||
if (y_val_res.status != VECTOR_OK) {
|
if (y0_res.status != VECTOR_OK) {
|
||||||
result.status = BIGINT_ERR_INVALID;
|
result.status = BIGINT_ERR_INVALID;
|
||||||
COPY_MSG(result, y_val_res.message);
|
COPY_MSG(result, y0_res.message);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int *y_val = (int*)y_val_res.value.element;
|
int *y0 = (int *)y0_res.value.element;
|
||||||
if (*y_val == 0) {
|
if (*y0 == 0) {
|
||||||
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
||||||
SET_MSG(result, "Cannot divide by zero");
|
SET_MSG(result, "Cannot divide by zero");
|
||||||
|
|
||||||
@@ -892,94 +898,67 @@ static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If |x| < |y| then result is zero
|
|
||||||
tmp_res = bigint_compare_abs(x, y);
|
tmp_res = bigint_compare_abs(x, y);
|
||||||
if (tmp_res.status != BIGINT_OK) { return tmp_res; }
|
|
||||||
|
|
||||||
if (tmp_res.value.compare_status < 0) {
|
|
||||||
tmp_res = bigint_from_int(0);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { return tmp_res; }
|
|
||||||
|
|
||||||
result.value.number = tmp_res.value.number;
|
|
||||||
result.status = BIGINT_OK;
|
|
||||||
SET_MSG(result, "Division between big integers was successful");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize quotient and remainder
|
|
||||||
tmp_res = bigint_from_int(0);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { return tmp_res; }
|
|
||||||
quotient = tmp_res.value.number;
|
|
||||||
|
|
||||||
tmp_res = bigint_from_int(0);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { bigint_destroy(quotient); return tmp_res; }
|
|
||||||
remainder = tmp_res.value.number;
|
|
||||||
|
|
||||||
// Create absolute value of y for later comparisons
|
|
||||||
tmp_res = bigint_clone(y);
|
|
||||||
if (tmp_res.status != BIGINT_OK) {
|
if (tmp_res.status != BIGINT_OK) {
|
||||||
bigint_destroy(quotient);
|
|
||||||
bigint_destroy(remainder);
|
|
||||||
|
|
||||||
return tmp_res;
|
return tmp_res;
|
||||||
}
|
}
|
||||||
|
|
||||||
abs_y = tmp_res.value.number;
|
if (tmp_res.value.compare_status < 0) {
|
||||||
abs_y->is_negative = false;
|
return bigint_from_int(0);
|
||||||
|
}
|
||||||
|
|
||||||
// Long division algorithm applied from MSB to LSB
|
|
||||||
const size_t x_size = vector_size(x->digits);
|
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--) {
|
for (int idx = (int)x_size - 1; idx >= 0; idx--) {
|
||||||
// Shift remainder left by one base digit (multiplication by BASE)
|
vector_result_t xidx_res = vector_get(x->digits, idx);
|
||||||
tmp_res = bigint_shift_left(remainder, 1);
|
if (xidx_res.status != VECTOR_OK) {
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
|
||||||
|
|
||||||
bigint_t *shifted_remainder = tmp_res.value.number;
|
|
||||||
bigint_destroy(remainder);
|
|
||||||
remainder = shifted_remainder;
|
|
||||||
|
|
||||||
// Add current digit of 'x' to the least significant position of remainder
|
|
||||||
vector_result_t digit_res = vector_get(x->digits, idx);
|
|
||||||
if (digit_res.status != VECTOR_OK) {
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
result.status = BIGINT_ERR_INVALID;
|
||||||
COPY_MSG(result, digit_res.message);
|
COPY_MSG(result, xidx_res.message);
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
int *x_digit = (int*)digit_res.value.element;
|
long long current = remainder * BASE + *(int *)xidx_res.value.element;
|
||||||
|
int q_idx = (int)(current / divisor);
|
||||||
|
remainder = current % divisor;
|
||||||
|
|
||||||
vector_result_t set_res = vector_set(remainder->digits, 0, x_digit);
|
vector_result_t push_res = vector_push(quotient->digits, &q_idx);
|
||||||
if (set_res.status != VECTOR_OK) {
|
|
||||||
result.status = BIGINT_ERR_INVALID;
|
|
||||||
COPY_MSG(result, set_res.message);
|
|
||||||
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp_res = bigint_trim_zeros(remainder);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
|
||||||
|
|
||||||
// COunt how many times 'y' fits into current remainder
|
|
||||||
size_t count = 0;
|
|
||||||
while (1) {
|
|
||||||
tmp_res = bigint_compare_abs(remainder, abs_y);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
|
||||||
if (tmp_res.value.compare_status < 0) { break; } // remainder < abs_y
|
|
||||||
|
|
||||||
// remainder = remainder - abs_y
|
|
||||||
tmp_res = bigint_sub_abs(remainder, abs_y);
|
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
|
||||||
|
|
||||||
bigint_t *new_remainder = tmp_res.value.number;
|
|
||||||
bigint_destroy(remainder);
|
|
||||||
remainder = new_remainder;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add count to quotient digits
|
|
||||||
vector_result_t push_res = vector_push(quotient->digits, &count);
|
|
||||||
if (push_res.status != VECTOR_OK) {
|
if (push_res.status != VECTOR_OK) {
|
||||||
result.status = BIGINT_ERR_INVALID;
|
result.status = BIGINT_ERR_INVALID;
|
||||||
COPY_MSG(result, push_res.message);
|
COPY_MSG(result, push_res.message);
|
||||||
@@ -988,34 +967,173 @@ static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse quotient digits
|
// Restore the LSB-first order
|
||||||
const size_t q_size = vector_size(quotient->digits);
|
const size_t q_size = vector_size(quotient->digits);
|
||||||
for (size_t idx = 0; idx < q_size / 2; idx++) {
|
for (size_t lo = 0, hi = q_size - 1; lo < hi; hi--) {
|
||||||
vector_result_t left_res = vector_get(quotient->digits, idx);
|
vector_result_t lr = vector_get(quotient->digits, lo);
|
||||||
vector_result_t right_res = vector_get(quotient->digits, q_size - 1 - idx);
|
vector_result_t hr = vector_get(quotient->digits, hi);
|
||||||
|
|
||||||
if (left_res.status != VECTOR_OK || right_res.status != VECTOR_OK) {
|
if (lr.status != VECTOR_OK || hr.status != VECTOR_OK) {
|
||||||
result.status = BIGINT_ERR_INVALID;
|
result.status = BIGINT_ERR_INVALID;
|
||||||
SET_MSG(result, "Failed to access vector elements");
|
SET_MSG(result, "Failed to reverse quotient digits");
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
int *left = (int*)left_res.value.element;
|
int lower_val = *(int *)lr.value.element;
|
||||||
int *right = (int*)right_res.value.element;
|
int higher_val = *(int *)hr.value.element;
|
||||||
int temp = *left;
|
vector_set(quotient->digits, lo, &higher_val);
|
||||||
|
vector_set(quotient->digits, hi, &lower_val);
|
||||||
vector_set(quotient->digits, idx, right);
|
|
||||||
vector_set(quotient->digits, q_size - 1 - idx, &temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quotient->is_negative = (x->is_negative != y->is_negative);
|
bigint_result_t trim_res = bigint_trim_zeros(quotient);
|
||||||
|
if (trim_res.status != BIGINT_OK) { result = trim_res; goto cleanup; }
|
||||||
|
|
||||||
tmp_res = bigint_trim_zeros(quotient);
|
result.value.number = quotient;
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
result.status = BIGINT_OK;
|
||||||
|
SET_MSG(result, "Division between big integers was successful");
|
||||||
|
|
||||||
bigint_destroy(remainder);
|
return result;
|
||||||
bigint_destroy(abs_y);
|
}
|
||||||
|
|
||||||
|
/* 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.value.number = quotient;
|
||||||
result.status = BIGINT_OK;
|
result.status = BIGINT_OK;
|
||||||
@@ -1024,20 +1142,20 @@ static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
free(u); free(v); free(q);
|
||||||
if (quotient) { bigint_destroy(quotient); }
|
if (quotient) { bigint_destroy(quotient); }
|
||||||
if (remainder) { bigint_destroy(remainder); }
|
|
||||||
if (abs_y) { bigint_destroy(abs_y); }
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bigint_from_int
|
* bigint_from_int
|
||||||
* @value: an integer value
|
* @value: an integer value
|
||||||
*
|
*
|
||||||
* Takes an integer and convert it to a big integer
|
* Takes an integer and convert it to a big integer
|
||||||
*
|
*
|
||||||
* Returns a big_int_result_t data type containing a new 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 bigint_from_int(long long value) {
|
||||||
bigint_result_t result = {0};
|
bigint_result_t result = {0};
|
||||||
@@ -1562,7 +1680,9 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) {
|
|||||||
* @x: a valid non-null big integer
|
* @x: a valid non-null big integer
|
||||||
* @y: a valid non-null big integer
|
* @y: a valid non-null big integer
|
||||||
*
|
*
|
||||||
* Computes division with remainder
|
* 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
|
* Returns a bigint_result_t data type
|
||||||
*/
|
*/
|
||||||
@@ -1570,7 +1690,6 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
bigint_result_t result = {0};
|
bigint_result_t result = {0};
|
||||||
bigint_result_t tmp_res = {0};
|
bigint_result_t tmp_res = {0};
|
||||||
|
|
||||||
// Intermediate results
|
|
||||||
bigint_t *quotient = NULL;
|
bigint_t *quotient = NULL;
|
||||||
bigint_t *y_times_q = NULL;
|
bigint_t *y_times_q = NULL;
|
||||||
bigint_t *remainder = NULL;
|
bigint_t *remainder = NULL;
|
||||||
@@ -1582,11 +1701,10 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for division by zero
|
|
||||||
const size_t y_size = vector_size(y->digits);
|
const size_t y_size = vector_size(y->digits);
|
||||||
if (y_size == 0) {
|
if (y_size == 0) {
|
||||||
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
||||||
SET_MSG(result, "Division by zero");
|
SET_MSG(result, "Cannot divide by zero");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1600,16 +1718,16 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int *y_val = (int*)y_val_res.value.element;
|
int *y_val = (int *)y_val_res.value.element;
|
||||||
if (*y_val == 0) {
|
if (*y_val == 0) {
|
||||||
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
result.status = BIGINT_ERR_DIV_BY_ZERO;
|
||||||
SET_MSG(result, "Division by zero");
|
SET_MSG(result, "Cannot divide by zero");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// |x| < |y| then quotient is 0 and remainder is x
|
// |x| < |y|: quotient is 0, remainder is x
|
||||||
tmp_res = bigint_compare_abs(x, y);
|
tmp_res = bigint_compare_abs(x, y);
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
|
|
||||||
@@ -1624,6 +1742,7 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
|
|
||||||
result.value.division.quotient = quotient;
|
result.value.division.quotient = quotient;
|
||||||
result.value.division.remainder = remainder;
|
result.value.division.remainder = remainder;
|
||||||
|
|
||||||
result.status = BIGINT_OK;
|
result.status = BIGINT_OK;
|
||||||
SET_MSG(result, "Division between big integers was successful");
|
SET_MSG(result, "Division between big integers was successful");
|
||||||
|
|
||||||
@@ -1634,7 +1753,10 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
quotient = tmp_res.value.number;
|
quotient = tmp_res.value.number;
|
||||||
|
|
||||||
// Compute r = x - y * q
|
// 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);
|
tmp_res = bigint_prod(y, quotient);
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
y_times_q = tmp_res.value.number;
|
y_times_q = tmp_res.value.number;
|
||||||
@@ -1643,13 +1765,24 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
||||||
remainder = tmp_res.value.number;
|
remainder = tmp_res.value.number;
|
||||||
|
|
||||||
// Ensure that remainder has correct sign (i.e., same as dividend x)
|
|
||||||
// In C-style division, sign(remainder) = sign(dividend)
|
|
||||||
remainder->is_negative = x->is_negative;
|
|
||||||
|
|
||||||
tmp_res = bigint_trim_zeros(remainder);
|
tmp_res = bigint_trim_zeros(remainder);
|
||||||
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
|
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.quotient = quotient;
|
||||||
result.value.division.remainder = remainder;
|
result.value.division.remainder = remainder;
|
||||||
result.status = BIGINT_OK;
|
result.status = BIGINT_OK;
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ void test_bigint_prod_neg(void) {
|
|||||||
bigint_destroy(prod.value.number);
|
bigint_destroy(prod.value.number);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test division between big numbers
|
// Test division between big numbers where divisor is a single limb big number
|
||||||
void test_bigint_div(void) {
|
void test_bigint_div_single_limb(void) {
|
||||||
bigint_result_t x = bigint_from_int(100);
|
bigint_result_t x = bigint_from_int(100);
|
||||||
bigint_result_t y = bigint_from_int(2);
|
bigint_result_t y = bigint_from_int(2);
|
||||||
|
|
||||||
@@ -229,11 +229,33 @@ void test_bigint_div(void) {
|
|||||||
bigint_eq(quotient, "50");
|
bigint_eq(quotient, "50");
|
||||||
bigint_eq(remainder, "0");
|
bigint_eq(remainder, "0");
|
||||||
|
|
||||||
bigint_destroy(quotient);
|
bigint_destroy(quotient); bigint_destroy(remainder);
|
||||||
bigint_destroy(remainder);
|
bigint_destroy(x.value.number); bigint_destroy(y.value.number);
|
||||||
|
}
|
||||||
|
|
||||||
bigint_destroy(x.value.number);
|
// Test division between big numbers using Knuth's algorithm
|
||||||
bigint_destroy(y.value.number);
|
void test_bigint_div_knuth(void) {
|
||||||
|
// (1...9) x 8
|
||||||
|
const char *x_origin = "123456789123456789123456789123456789123456789123456789123456789123456789";
|
||||||
|
// (9...1) x 5
|
||||||
|
const char *y_origin = "987654321987654321987654321987654321987654321";
|
||||||
|
|
||||||
|
bigint_result_t x = bigint_from_string(x_origin);
|
||||||
|
bigint_result_t y = bigint_from_string(y_origin);
|
||||||
|
|
||||||
|
assert(x.status == BIGINT_OK && y.status == BIGINT_OK);
|
||||||
|
|
||||||
|
bigint_result_t div = bigint_divmod(x.value.number, y.value.number);
|
||||||
|
assert(div.status == BIGINT_OK);
|
||||||
|
|
||||||
|
bigint_t* const quotient = div.value.division.quotient;
|
||||||
|
bigint_t* const remainder = div.value.division.remainder;
|
||||||
|
|
||||||
|
bigint_eq(quotient, "124999998860937500014238281");
|
||||||
|
bigint_eq(remainder, "246737799246737799370194588370194588370194588");
|
||||||
|
|
||||||
|
bigint_destroy(quotient); bigint_destroy(remainder);
|
||||||
|
bigint_destroy(x.value.number); bigint_destroy(y.value.number);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test division between big numbers with negative dividend
|
// Test division between big numbers with negative dividend
|
||||||
@@ -262,7 +284,7 @@ void test_bigint_div_dividend(void) {
|
|||||||
|
|
||||||
// Test division between big numbers with negative divisor
|
// Test division between big numbers with negative divisor
|
||||||
// This library follows C-style divison such that sign(remainder) = sign(dividend)
|
// This library follows C-style divison such that sign(remainder) = sign(dividend)
|
||||||
void test_bigint_div_divisor(void) {
|
void test_bigint_div_neg_divisor(void) {
|
||||||
bigint_result_t x = bigint_from_int(13);
|
bigint_result_t x = bigint_from_int(13);
|
||||||
bigint_result_t y = bigint_from_int(-4);
|
bigint_result_t y = bigint_from_int(-4);
|
||||||
|
|
||||||
@@ -405,9 +427,10 @@ int main(void) {
|
|||||||
TEST(bigint_very_large_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_single_limb);
|
||||||
|
TEST(bigint_div_knuth);
|
||||||
TEST(bigint_div_dividend);
|
TEST(bigint_div_dividend);
|
||||||
TEST(bigint_div_divisor);
|
TEST(bigint_div_neg_divisor);
|
||||||
TEST(bigint_div_neg);
|
TEST(bigint_div_neg);
|
||||||
TEST(bigint_div_by_zero);
|
TEST(bigint_div_by_zero);
|
||||||
TEST(bigint_clone);
|
TEST(bigint_clone);
|
||||||
|
|||||||
2
usage.c
2
usage.c
@@ -495,7 +495,7 @@ int bigint_usage(void) {
|
|||||||
// Print result
|
// Print result
|
||||||
bigint_printf("multiplication result = %B\n", prod);
|
bigint_printf("multiplication result = %B\n", prod);
|
||||||
|
|
||||||
bigint_t *a = bigint_from_string(x_origin).value.number;
|
bigint_t *a = bigint_from_string(large_x).value.number;
|
||||||
bigint_t *b = bigint_from_string(y_origin).value.number;
|
bigint_t *b = bigint_from_string(y_origin).value.number;
|
||||||
|
|
||||||
// Divide two big integers
|
// Divide two big integers
|
||||||
|
|||||||
Reference in New Issue
Block a user