Added Knuth's "Algorithm D" from TAOCP "Seminumerical algorithms"
All checks were successful
clang-build / clang-build (push) Successful in 41s
gcc-build / gcc-build (push) Successful in 19s

This commit is contained in:
2026-02-25 17:13:49 +01:00
parent ea9ef9de4b
commit a02f2dff40
9 changed files with 403 additions and 188 deletions

View File

@@ -33,17 +33,18 @@ and the boolean `is_negative` variable denotes its sign.
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_string(string_num)`: create 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_clone(number)`: clone 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_add(x, y)`: add 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_prod(x, y)`: multiply 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_mod(x, y)`: computes modulo of two big integers using *long division* algorithm in $\mathcal{O}(n^2)$;
- `bigint_result_t bigint_destroy(number)`: delete the big number;
- `bigint_result_t bigint_from_int(value)`: creates a big integer from a primitive `int` type;
- `bigint_result_t bigint_from_string(string_num)`: creates a big integer from a C string;
- `bigint_result_t bigint_to_string(number)`: converts a big integer to a C string;
- `bigint_result_t bigint_clone(number)`: clones a big integer;
- `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)`: adds two big integers together 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)`: multiplies two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$;
- `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
parts/limbs in the divisor and the quotient, respectively. This method returns both the quotient and the remainder;
- `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.
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`;
- `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.

View File

@@ -37,12 +37,12 @@ free them before removing the keys or destroying the map.
The `Map` data structure supports the following methods:
- `map_result_t map_new()`: initialize a new map;
- `map_result_t map_add(map, key, value)`: add 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_remove(map, key)`: remove a key from the map if it exists;
- `map_result_t map_clear(map)`: reset the map state;
- `map_result_t map_destroy(map)`: delete the map;
- `map_result_t map_new()`: initializes a new map;
- `map_result_t map_add(map, key, value)`: adds a `(key, value)` pair to the map;
- `map_result_t map_get(map, key)`: retrieves a values indexed by `key` 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)`: resets the map state;
- `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_capacity(map)`: returns map capacity (i.e., map total size).

View File

@@ -25,19 +25,19 @@ deletion.
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_push(vector, value)`: add 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_get(vector, index)`: return the value indexed by `index` if it exists;
- `vector_result_t vector_sort(vector, cmp)`: sort vector using `cmp` function;
- `vector_result_t vector_pop(vector)`: pop 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_filter(vector, callback, env)`: filter vector using `callback` (in-place);
- `vector_result_t vector_reduce(vector, accumulator, callback, env)`: fold/reduce 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_destroy(vector)`: delete the vector;
- `size_t vector_size(vector)`: return vector size (i.e., the number of elements);
- `size_t vector_capacity(vector)`: return vector capacity (i.e., vector total size).
- `vector_result_t vector_new(size, data_size)`: creates a new vector;
- `vector_result_t vector_push(vector, value)`: adds a new value to the vector;
- `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)`: returns the value indexed by `index` if it exists;
- `vector_result_t vector_sort(vector, cmp)`: sorts vector using `cmp` function;
- `vector_result_t vector_pop(vector)`: pops last element from the vector following the LIFO policy;
- `vector_result_t vector_map(vector, callback, env)`: applies `callback` function to vector (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)`: folds/reduces vector using `callback`;
- `vector_result_t vector_clear(vector)`: resets the vector logically. That is, new pushes will overwrite the memory;
- `vector_result_t vector_destroy(vector)`: deletes the vector;
- `size_t vector_size(vector)`: returns vector size (i.e., the number of elements);
- `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
on the `Vector` data type return a custom type called `vector_result_t` which is