Compare commits
16 Commits
7d95b32e52
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 78b5ff5955 | |||
|
de22c706af
|
|||
|
f8a77a6056
|
|||
|
6110fc963c
|
|||
|
1871035cd6
|
|||
|
b4a704f1b4
|
|||
|
086446039b
|
|||
|
55b14ee949
|
|||
|
110ccae8b9
|
|||
| c7f6ed82c9 | |||
|
40d343c02b
|
|||
|
eb670e26a5
|
|||
|
a02f2dff40
|
|||
|
ea9ef9de4b
|
|||
|
dd6e7a9c9e
|
|||
|
6cd90467c6
|
20
Makefile
20
Makefile
@@ -13,7 +13,6 @@ BENCH_OBJ_DIR = bench_obj
|
|||||||
|
|
||||||
TESTS_SRC = tests
|
TESTS_SRC = tests
|
||||||
|
|
||||||
TARGET = usage
|
|
||||||
TEST_V_TARGET = test_vector
|
TEST_V_TARGET = test_vector
|
||||||
TEST_M_TARGET = test_map
|
TEST_M_TARGET = test_map
|
||||||
TEST_B_TARGET = test_bigint
|
TEST_B_TARGET = test_bigint
|
||||||
@@ -21,16 +20,12 @@ TEST_S_TARGET = test_string
|
|||||||
BENCH_TARGET = benchmark_datum
|
BENCH_TARGET = benchmark_datum
|
||||||
|
|
||||||
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/string.o
|
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/string.o
|
||||||
PROG_OBJS = $(OBJ_DIR)/usage.o
|
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean examples
|
||||||
|
|
||||||
all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
|
all: $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) examples
|
||||||
bench: $(BENCH_TARGET)
|
bench: $(BENCH_TARGET)
|
||||||
|
|
||||||
$(TARGET): $(PROG_OBJS) $(LIB_OBJS)
|
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
|
||||||
|
|
||||||
$(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o
|
$(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
@@ -43,10 +38,10 @@ $(TEST_B_TARGET): $(OBJ_DIR)/test_bigint.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector
|
|||||||
$(TEST_S_TARGET): $(OBJ_DIR)/test_string.o $(OBJ_DIR)/string.o
|
$(TEST_S_TARGET): $(OBJ_DIR)/test_string.o $(OBJ_DIR)/string.o
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
examples: $(LIB_OBJS)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(MAKE) -C examples
|
||||||
|
|
||||||
$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR)
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
|
$(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
|
||||||
@@ -56,7 +51,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_OBJ_DIR)/string.o
|
$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o $(BENCH_OBJ_DIR)/bigint.o $(BENCH_OBJ_DIR)/string.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)
|
||||||
@@ -69,4 +64,5 @@ $(BENCH_OBJ_DIR):
|
|||||||
mkdir -p $(BENCH_OBJ_DIR)
|
mkdir -p $(BENCH_OBJ_DIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
|
rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
|
||||||
|
$(MAKE) -C examples clean
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -4,16 +4,15 @@
|
|||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Datum is a collection of dynamic and generic data structures implemented from scratch in C with no external dependencies beyond
|
Datum is a collection of dynamic and generic data structures implemented from scratch in C with no external dependencies beyond
|
||||||
the standard library. It currently features:
|
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 of generic heterogenous data types;
|
||||||
- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers;
|
- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers;
|
||||||
- [**String**](/docs/string.md): an immutable string type with partial UTF-8 support.
|
- [**String**](/docs/string.md): an immutable, null-terminated string type with partial UTF-8 support.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
At its simplest, you can use this library as follows:
|
At its simplest, you can use this library as follows:
|
||||||
@@ -112,9 +111,9 @@ int main(void) {
|
|||||||
#include "src/bigint.h"
|
#include "src/bigint.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile with: gcc -O3 main.c src/bigint.c src/vector.c
|
* Compile with: clang -O3 fact.c src/bigint.c src/vector.c -o fact
|
||||||
* Output: 20000! = 1819206320230345134827641...
|
* Output: 20000! = 1819206320230345134827641...
|
||||||
* Time: 4.01s user 0.00s system 99% cpu 4.021 total
|
* Time: 1.49s user 0.00s system 99% cpu 1.501 total
|
||||||
*/
|
*/
|
||||||
int main(void) {
|
int main(void) {
|
||||||
const int n = 20000;
|
const int n = 20000;
|
||||||
@@ -136,8 +135,7 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `String` usage
|
||||||
### `String` usage:
|
|
||||||
```c
|
```c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -169,22 +167,12 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For a more exhaustive example, refer to the `usage.c` file. There, you will find a program with proper error management
|
For additional usage samples, refer to the [`examples/`](/examples) directory. You can compile these example programs with the following command:
|
||||||
and a sample usage for every available method. To run it, first issue the following command:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ make clean all
|
$ 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
|
## Documentation
|
||||||
For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs).
|
For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs).
|
||||||
|
|
||||||
@@ -199,14 +187,16 @@ $ ./test_bigint
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Benchmark
|
## Benchmark
|
||||||
Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector`, `Map` and the `String` data structures.
|
Under the [`benchmark/`](/benchmark/) folder, you can find a very simple benchmark program that stress the data structures.
|
||||||
You can run it by issuing the following command:
|
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: 19 ms
|
Computing Vector average time...average time: 6 ms
|
||||||
Computing Map average time...average time: 55 ms
|
Computing Map average time...average time: 49 ms
|
||||||
Computing String average time...average time: 24 ms
|
Computing BigInt average time...average time: 67 ms
|
||||||
|
Computing String average time...average time: 13 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -8,6 +6,7 @@
|
|||||||
|
|
||||||
#include "../src/vector.h"
|
#include "../src/vector.h"
|
||||||
#include "../src/map.h"
|
#include "../src/map.h"
|
||||||
|
#include "../src/bigint.h"
|
||||||
#include "../src/string.h"
|
#include "../src/string.h"
|
||||||
|
|
||||||
typedef void (*test_fn_t)(size_t iterations);
|
typedef void (*test_fn_t)(size_t iterations);
|
||||||
@@ -16,10 +15,10 @@ void test_vector(size_t iterations) {
|
|||||||
vector_t *vec = vector_new(16, sizeof(int)).value.vector;
|
vector_t *vec = vector_new(16, sizeof(int)).value.vector;
|
||||||
|
|
||||||
for (size_t idx = 0; idx < iterations; idx++) {
|
for (size_t idx = 0; idx < iterations; idx++) {
|
||||||
vector_push(vec, &(int){idx});
|
vector_push(vec, &idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile uint64_t sum = 0;
|
volatile uint64_t sum = 0; // prevent the compiler from optimizing away the sum
|
||||||
for (size_t idx = 0; idx < iterations; idx++) {
|
for (size_t idx = 0; idx < iterations; idx++) {
|
||||||
const int *val = (int*)vector_get(vec, idx).value.element;
|
const int *val = (int*)vector_get(vec, idx).value.element;
|
||||||
sum += *val;
|
sum += *val;
|
||||||
@@ -41,7 +40,7 @@ void test_map(size_t iterations) {
|
|||||||
map_add(map, key, (void*)value);
|
map_add(map, key, (void*)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile uint64_t sum = 0;
|
volatile uint64_t sum = 0; // prevent the compiler from optimizing away the sum
|
||||||
for (size_t idx = 0; idx < iterations; idx++) {
|
for (size_t idx = 0; idx < iterations; idx++) {
|
||||||
snprintf(key, sizeof(key), "key_%zu", idx);
|
snprintf(key, sizeof(key), "key_%zu", idx);
|
||||||
|
|
||||||
@@ -62,6 +61,62 @@ void test_map(size_t iterations) {
|
|||||||
map_destroy(map);
|
map_destroy(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_bigint(size_t iterations) {
|
||||||
|
volatile uint64_t accumulator = 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void test_string(size_t iterations) {
|
void test_string(size_t iterations) {
|
||||||
volatile size_t total_len = 0;
|
volatile size_t total_len = 0;
|
||||||
|
|
||||||
@@ -112,7 +167,7 @@ int main(void) {
|
|||||||
// Do a warmup run
|
// Do a warmup run
|
||||||
test_vector(1000);
|
test_vector(1000);
|
||||||
test_map(1000);
|
test_map(1000);
|
||||||
test_string(1000);
|
test_bigint(1000);
|
||||||
|
|
||||||
printf("Computing Vector average time...");
|
printf("Computing Vector average time...");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@@ -122,6 +177,10 @@ 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));
|
||||||
|
|
||||||
printf("Computing String average time...");
|
printf("Computing String average time...");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
printf("average time: %lld ms\n", benchmark(test_string, 1e5, 30));
|
printf("average time: %lld ms\n", benchmark(test_string, 1e5, 30));
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -79,8 +80,6 @@ Each method that returns such type indicates whether the operation was successfu
|
|||||||
by setting the `status` field and by providing a descriptive message on the `message`
|
by setting the `status` field and by providing a descriptive message on the `message`
|
||||||
field. If the operation was successful (that is, `status == BIGINT_OK`), you can either
|
field. If the operation was successful (that is, `status == BIGINT_OK`), you can either
|
||||||
move on with the rest of the program or read the returned value from the sum data type.
|
move on with the rest of the program or read the returned value from the sum data type.
|
||||||
Of course, you can choose to ignore the return value (if you're brave enough :D) as
|
|
||||||
illustrated on the first part of the README.
|
|
||||||
|
|
||||||
The sum data type (i.e., the `value` union) defines four different variables. Each
|
The sum data type (i.e., the `value` union) defines four different variables. Each
|
||||||
of them has an unique scope as described below:
|
of them has an unique scope as described below:
|
||||||
@@ -90,12 +89,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.
|
|
||||||
|
|
||||||
|
|||||||
15
docs/map.md
15
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).
|
||||||
|
|
||||||
@@ -72,5 +72,4 @@ typedef struct {
|
|||||||
Each method that returns such type indicates whether the operation was successful or not by setting
|
Each method that returns such type indicates whether the operation was successful or not by setting
|
||||||
the `status` field and by providing a descriptive message on the `message` field. If the operation was
|
the `status` field and by providing a descriptive message on the `message` field. If the operation was
|
||||||
successful (that is, `status == MAP_OK`), you can either move on with the rest of the program or read
|
successful (that is, `status == MAP_OK`), you can either move on with the rest of the program or read
|
||||||
the returned value from the sum data type. Of course, you can choose to ignore the return value (if you're brave enough :D) as illustrated
|
the returned value from the sum data type.
|
||||||
on the first part of the README.
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# String Technical Details
|
# String Technical Details
|
||||||
In this document you can find a quick overview of the technical
|
In this document you can find a quick overview of the technical aspects (internal design, memory layout, etc.) of the `String` data structure.
|
||||||
aspects (internal design, memory layout, etc.) of the `String` data structure.
|
|
||||||
|
|
||||||
`String` is an immutable string data type with partial UTF-8 support.
|
`String` is an immutable, null-terminated string data type with partial UTF-8 support.
|
||||||
This means that methods return a new string instance rather than modifying the string in-place.
|
This means that methods return a new string instance rather than modifying the string in-place.
|
||||||
Internally, this data structure is represented by the following layout:
|
Internally, this data structure is represented by the following layout:
|
||||||
|
|
||||||
@@ -15,22 +14,14 @@ typedef struct {
|
|||||||
} string_t;
|
} string_t;
|
||||||
```
|
```
|
||||||
|
|
||||||
where the `data` variable represents the actual string (represented as a pointer to `char`),
|
where the `data` field represent the actual string, `byte_size`
|
||||||
the `byte_size` variable indicates the actual size (in bytes) of the string, the
|
indicates the actual size (in bytes), `byte_capacity` represents the total number
|
||||||
`byte_capacity` variable represents the total number of allocated memory (in bytes) and the
|
of allocated memory (in bytes) and `char_count` represents the number of symbols.
|
||||||
`char_count` variable represent the number of logical characters, that is the number of
|
|
||||||
symbols.
|
|
||||||
|
|
||||||
As mentioned earlier, this library provides partial UTF-8 support. It is able to recognize
|
As mentioned earlier, this data type provides partial UTF-8 support. It is able
|
||||||
UTF-8 byte sequences as individual Unicode code points, which allows it to correctly distinguish
|
to recognize UTF-8 byte sequences as individual Unicode code points and has
|
||||||
between byte length and character count. It fully supports Unicode symbols and emojis, while
|
full support for Unicode symbols such as emojis. However, it does not support
|
||||||
remaining backward compatible with ASCII strings.
|
localization. In particular, it does not perform local-aware conversions. For instance, uppercase/lowercase transformations are limited to ASCII characters only. As a result, the German scharfes S (`ß`) is not convert to `SS`, the Spanish `Ñ` is not converted to `ñ` and the Italian `é` (and its variants) is not treated as a single symbol but rather as a base letter combined with an accent.
|
||||||
|
|
||||||
However, this data structure does not support localization. In particular, it does not perform
|
|
||||||
locale-aware conversion; for instance, uppercase/lowercase transformations are limited to ASCII
|
|
||||||
characters only. As a result, the German scharfes S (`ß`) is not convert to `SS`, the Spanish
|
|
||||||
`Ñ` is not converted to `ñ` and the Italian `é` (and its variants) is not treated as a single
|
|
||||||
symbol, but rather as a base letter combined with an accent.
|
|
||||||
|
|
||||||
At the time being, `String` supports the following methods:
|
At the time being, `String` supports the following methods:
|
||||||
|
|
||||||
@@ -67,11 +58,11 @@ typedef struct {
|
|||||||
string_status_t status;
|
string_status_t status;
|
||||||
uint8_t message[RESULT_MSG_SIZE];
|
uint8_t message[RESULT_MSG_SIZE];
|
||||||
union {
|
union {
|
||||||
string_t *string; // For new, clone, slice, reverse, trim
|
string_t *string;
|
||||||
char *symbol; // For get_at
|
char *symbol;
|
||||||
int64_t idx; // For contains
|
int64_t idx;
|
||||||
bool is_equ; // For comparison
|
bool is_equ;
|
||||||
struct { // For split
|
struct {
|
||||||
string_t **strings;
|
string_t **strings;
|
||||||
size_t count;
|
size_t count;
|
||||||
} split;
|
} split;
|
||||||
@@ -83,8 +74,6 @@ Each method that returns such type indicates whether the operation was successfu
|
|||||||
by setting the `status` field and by providing a descriptive message on the `message`
|
by setting the `status` field and by providing a descriptive message on the `message`
|
||||||
field. If the operation was successful (that is, `status == STRING_OK`) you can either
|
field. If the operation was successful (that is, `status == STRING_OK`) you can either
|
||||||
move on with the rest of your program or read the returned value from the sum data type.
|
move on with the rest of your program or read the returned value from the sum data type.
|
||||||
Of course, you can choose to ignore the return value (if you're brave enough :D) as illustrated
|
|
||||||
on the first part of the README.
|
|
||||||
|
|
||||||
The sum data type (i.e., the `value` union) defines five different variables.
|
The sum data type (i.e., the `value` union) defines five different variables.
|
||||||
Each of them has an unique scope as described below:
|
Each of them has an unique scope as described below:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -65,8 +65,7 @@ typedef struct {
|
|||||||
Each method that returns such type indicates whether the operation was successful or not
|
Each method that returns such type indicates whether the operation was successful or not
|
||||||
by setting the `status` field and by providing a descriptive message on the `message`
|
by setting the `status` field and by providing a descriptive message on the `message`
|
||||||
field. If the operation was successful (that is, `status == VECTOR_OK`), you can either
|
field. If the operation was successful (that is, `status == VECTOR_OK`), you can either
|
||||||
move on with the rest of the program or read the returned value from the sum data type. Of course, you can choose to
|
move on with the rest of the program or read the returned value from the sum data type.
|
||||||
ignore the return value (if you're brave enough :D) as illustrated on the first part of the README.
|
|
||||||
|
|
||||||
## Functional methods
|
## Functional methods
|
||||||
`Vector` provides three functional methods called `map`, `filter` and `reduce` which allow the caller to apply a computation to the vector,
|
`Vector` provides three functional methods called `map`, `filter` and `reduce` which allow the caller to apply a computation to the vector,
|
||||||
|
|||||||
37
examples/Makefile
Normal file
37
examples/Makefile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \
|
||||||
|
-fsanitize=address -fsanitize=undefined -fstack-clash-protection \
|
||||||
|
-Wwrite-strings -g -std=c99
|
||||||
|
|
||||||
|
SRC_DIR = ../src
|
||||||
|
OBJ_DIR = ../obj
|
||||||
|
|
||||||
|
TARGETS = vector_basic vector_sorting vector_functional map_basic bigint_operations string_basic string_advanced
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
vector_basic: vector_basic.c $(OBJ_DIR)/vector.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
vector_sorting: vector_sorting.c $(OBJ_DIR)/vector.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
vector_functional: vector_functional.c $(OBJ_DIR)/vector.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
map_basic: map_basic.c $(OBJ_DIR)/map.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
bigint_operations: bigint_operations.c $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
string_basic: string_basic.c $(OBJ_DIR)/string.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
string_advanced: string_advanced.c $(OBJ_DIR)/string.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGETS)
|
||||||
118
examples/bigint_operations.c
Normal file
118
examples/bigint_operations.c
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Bigint operations example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../src/bigint.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
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(large_y);
|
||||||
|
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
|
||||||
|
bigint_printf("Sum result = %B\n", sum);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
bigint_printf("difference result = %B\n", diff);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
bigint_printf("multiplication result = %B\n", prod);
|
||||||
|
|
||||||
|
bigint_t *a = bigint_from_string(large_x).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);
|
||||||
|
if (div_res.status != BIGINT_OK) {
|
||||||
|
printf("Error while dividing two big numbers: %s\n", div_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bigint_t *quotient = div_res.value.division.quotient;
|
||||||
|
bigint_t *remainder = div_res.value.division.remainder;
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
bigint_printf(
|
||||||
|
"division result = %B\
|
||||||
|
\nmod result = %B\n",
|
||||||
|
quotient, remainder);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
101
examples/map_basic.c
Normal file
101
examples/map_basic.c
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Basic map operations example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/map.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a new map
|
||||||
|
map_result_t res = map_new();
|
||||||
|
if (res.status != MAP_OK) {
|
||||||
|
printf("Error while creating map: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_t *map = res.value.map;
|
||||||
|
|
||||||
|
// Add some values
|
||||||
|
const int x = 0xB00B5;
|
||||||
|
const char *y = "Hello";
|
||||||
|
|
||||||
|
map_result_t add_res = map_add(map, "x", (void*)&x);
|
||||||
|
if (add_res.status != MAP_OK) {
|
||||||
|
printf("Error while adding elements: %s\n", add_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_res = map_add(map, "y", (void*)y);
|
||||||
|
if (add_res.status != MAP_OK) {
|
||||||
|
printf("Error while adding elements: %s\n", add_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print size and capacity
|
||||||
|
printf("Map size (should be 2): %zu\n", map_size(map));
|
||||||
|
printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map));
|
||||||
|
|
||||||
|
// Retrieve keys
|
||||||
|
map_result_t get_res = map_get(map, "x");
|
||||||
|
if (get_res.status != MAP_OK) {
|
||||||
|
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
const int *val = (const int*)get_res.value.element;
|
||||||
|
printf("Key 'x' contains (should be 'B00B5'): %X\n", *val);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_res = map_get(map, "y");
|
||||||
|
if (get_res.status != MAP_OK) {
|
||||||
|
printf("Cannot retrieve map element 'y': %s\n", get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
const char *val = (const char*)get_res.value.element;
|
||||||
|
printf("Key 'y' contains (should be 'Hello') : %s\n\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update key
|
||||||
|
const int new_x = 0xC0FFEE;
|
||||||
|
map_result_t up_res = map_add(map, "x", (void*)&new_x);
|
||||||
|
|
||||||
|
up_res = map_get(map, "x");
|
||||||
|
if (get_res.status != MAP_OK) {
|
||||||
|
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
const int *val = (const int*)up_res.value.element;
|
||||||
|
printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove an element
|
||||||
|
map_result_t rm_res = map_remove(map, "y");
|
||||||
|
if (rm_res.status != MAP_OK) {
|
||||||
|
printf("Cannot remove map element 'y': %s\n", rm_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the map
|
||||||
|
map_result_t clear_res = map_clear(map);
|
||||||
|
if (clear_res.status != MAP_OK) {
|
||||||
|
printf("Cannot clear map: %s\n", clear_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("Map cleared (size should be 0): %zu\n", map_size(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Delete the map
|
||||||
|
map_result_t del_res = map_destroy(map);
|
||||||
|
if (del_res.status != MAP_OK) {
|
||||||
|
printf("Error while destroying the map: %s\n", del_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
103
examples/string_advanced.c
Normal file
103
examples/string_advanced.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Advanced string manipulation example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/string.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a string for manipulation
|
||||||
|
string_result_t res = string_new("Hello, World! 😜");
|
||||||
|
if (res.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t *str = res.value.string;
|
||||||
|
printf("Original string: \"%s\"\n\n", str->data);
|
||||||
|
|
||||||
|
// Uppercase string
|
||||||
|
string_result_t res_upper = string_to_upper(str);
|
||||||
|
if (res_upper.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_upper.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Uppercase: \"%s\"\n", res_upper.value.string->data);
|
||||||
|
string_destroy(res_upper.value.string);
|
||||||
|
|
||||||
|
// Lowercase string
|
||||||
|
string_result_t res_lower = string_to_lower(str);
|
||||||
|
if (res_lower.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_lower.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Lowercase: \"%s\"\n\n", res_lower.value.string->data);
|
||||||
|
string_destroy(res_lower.value.string);
|
||||||
|
|
||||||
|
// Reverse string
|
||||||
|
string_result_t res_rev = string_reverse(str);
|
||||||
|
if (res_rev.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_rev.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Reversed: \"%s\"\n\n", res_rev.value.string->data);
|
||||||
|
string_destroy(res_rev.value.string);
|
||||||
|
|
||||||
|
// Change first character of the string
|
||||||
|
string_result_t res_set = string_set_at(str, 0, "J");
|
||||||
|
if (res_set.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_set.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Updated string: \"%s\"\n\n", res_set.value.string->data);
|
||||||
|
string_destroy(res_set.value.string);
|
||||||
|
|
||||||
|
// Get character from string (the emoji)
|
||||||
|
string_result_t res_get = string_get_at(str, 14);
|
||||||
|
if (res_get.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_get.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("Extracted symbol: \"%s\"\n", res_get.value.symbol);
|
||||||
|
free(res_get.value.symbol);
|
||||||
|
|
||||||
|
// Trim string
|
||||||
|
string_t *to_trim = string_new(" foo ").value.string;
|
||||||
|
string_result_t res_trim = string_trim(to_trim);
|
||||||
|
if (res_trim.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_trim.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Trimmed string: \"%s\"\n\n", res_trim.value.string->data);
|
||||||
|
string_destroy(to_trim);
|
||||||
|
string_destroy(res_trim.value.string);
|
||||||
|
|
||||||
|
// Split string
|
||||||
|
string_t *to_split = string_new("foo/bar/biz").value.string;
|
||||||
|
string_result_t res_split = string_split(to_split, "/");
|
||||||
|
if (res_split.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_split.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t count = res_split.value.split.count;
|
||||||
|
string_t **strings = res_split.value.split.strings;
|
||||||
|
|
||||||
|
printf("Original string: \"%s\"\nSplitted string: ", to_split->data);
|
||||||
|
for (size_t idx = 0; idx < count; idx++) {
|
||||||
|
printf("\"%s\" ", strings[idx]->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
string_split_destroy(strings, count);
|
||||||
|
string_destroy(to_split);
|
||||||
|
|
||||||
|
string_destroy(str);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
95
examples/string_basic.c
Normal file
95
examples/string_basic.c
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Basic string operations example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/string.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a new string
|
||||||
|
string_result_t res = string_new("Hello, ");
|
||||||
|
if (res.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t *str1 = res.value.string;
|
||||||
|
printf("Created string: \"%s\"\n", str1->data);
|
||||||
|
printf("Character count: %zu (%zu actual bytes)\n", string_size(str1), str1->byte_size);
|
||||||
|
|
||||||
|
string_result_t res_clone = string_clone(str1);
|
||||||
|
if (res_clone.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t *cloned = res_clone.value.string;
|
||||||
|
printf("Cloned string: \"%s\"\n\n", cloned->data);
|
||||||
|
string_destroy(cloned);
|
||||||
|
|
||||||
|
// Concatenation of strings
|
||||||
|
string_result_t res_suffix = string_new("World! 😜");
|
||||||
|
if (res_suffix.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t *suffix = res_suffix.value.string;
|
||||||
|
printf("Created another string: \"%s\"\n", suffix->data);
|
||||||
|
printf("Character count: %zu (%zu actual bytes)\n\n", string_size(suffix), suffix->byte_size);
|
||||||
|
|
||||||
|
string_result_t res_cat = string_concat(str1, suffix);
|
||||||
|
if (res_cat.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_cat.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
string_destroy(suffix);
|
||||||
|
|
||||||
|
string_t *concat_str = res_cat.value.string;
|
||||||
|
printf("Concatenation result: \"%s\"\n\n", concat_str->data);
|
||||||
|
|
||||||
|
// String contains
|
||||||
|
string_t *haystack = string_new("The quick brown fox jumps over the lazy dog.").value.string;
|
||||||
|
string_t *needle = string_new("brown fox").value.string;
|
||||||
|
|
||||||
|
string_result_t res_contains = string_contains(haystack, needle);
|
||||||
|
if (res_contains.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_contains.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res_contains.value.idx != -1) {
|
||||||
|
printf("Substring found. Starting at index %ld\n\n", res_contains.value.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
string_destroy(haystack);
|
||||||
|
string_destroy(needle);
|
||||||
|
|
||||||
|
// String slicing
|
||||||
|
string_result_t res_slice = string_slice(concat_str, 7, 14);
|
||||||
|
if (res_slice.status != STRING_OK) {
|
||||||
|
printf("Error: %s\n", res_slice.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Slice of string: \"%s\"\n\n", res_slice.value.string->data);
|
||||||
|
string_destroy(res_slice.value.string);
|
||||||
|
|
||||||
|
// String equality
|
||||||
|
string_t *compare = string_new("hello, World! 😜").value.string;
|
||||||
|
string_result_t res_eq = string_eq(concat_str, compare, true);
|
||||||
|
if (res_eq.value.is_equ) {
|
||||||
|
printf("The two strings are equal\n\n");
|
||||||
|
} else {
|
||||||
|
printf("The two strings are not equal\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
string_destroy(compare);
|
||||||
|
string_destroy(concat_str);
|
||||||
|
string_destroy(str1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
78
examples/vector_basic.c
Normal file
78
examples/vector_basic.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Basic vector operations example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/vector.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a vector of 3 integers
|
||||||
|
vector_result_t res = vector_new(3, sizeof(int));
|
||||||
|
if (res.status != VECTOR_OK) {
|
||||||
|
printf("Error while creating vector: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_t *vector = res.value.vector;
|
||||||
|
|
||||||
|
// Push some values to trigger reallocation
|
||||||
|
for (int idx = 0; idx < 5; idx++) {
|
||||||
|
vector_result_t add_res = vector_push(vector, &idx);
|
||||||
|
if (add_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while adding elements: %s\n", add_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print vector size and capacity
|
||||||
|
printf("Vector size (should be 5): %zu\n", vector_size(vector));
|
||||||
|
printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector));
|
||||||
|
|
||||||
|
// Print the whole vector
|
||||||
|
size_t sz = vector_size(vector);
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t get_res = vector_get(vector, idx);
|
||||||
|
if (get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int val = *(int *)get_res.value.element;
|
||||||
|
printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an element at index 2
|
||||||
|
int new_val = 0xBABE;
|
||||||
|
vector_result_t set_res = vector_set(vector, 2, &new_val);
|
||||||
|
if (set_res.status == VECTOR_OK) {
|
||||||
|
printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop last element
|
||||||
|
vector_result_t pop_res = vector_pop(vector);
|
||||||
|
if (pop_res.status == VECTOR_OK) {
|
||||||
|
int val = *(int *)pop_res.value.element;
|
||||||
|
printf("Popped value (should be 5) : %d\n\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear vector
|
||||||
|
vector_result_t clear_res = vector_clear(vector);
|
||||||
|
if (clear_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot clear vector: %s\n", clear_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free vector
|
||||||
|
vector_result_t del_res = vector_destroy(vector);
|
||||||
|
if (del_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while destroying the vector: %s\n", del_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
118
examples/vector_functional.c
Normal file
118
examples/vector_functional.c
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Vector functional operations example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/vector.h"
|
||||||
|
|
||||||
|
#define UNUSED(X) (void)(X)
|
||||||
|
|
||||||
|
static void square(void *element, void *env);
|
||||||
|
static int is_even(const void *element, void *env);
|
||||||
|
static void adder(void *accumulator, const void *element, void *env);
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a vector
|
||||||
|
vector_result_t res = vector_new(1, sizeof(int));
|
||||||
|
if (res.status != VECTOR_OK) {
|
||||||
|
printf("Error while creating vector: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_t *vector = res.value.vector;
|
||||||
|
|
||||||
|
// Map vector elements
|
||||||
|
for (size_t idx = 1; idx <= 5; idx++) {
|
||||||
|
vector_result_t map_push_res = vector_push(vector, &idx);
|
||||||
|
if (map_push_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while adding elements: %s\n", map_push_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sz = vector_size(vector);
|
||||||
|
|
||||||
|
// Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25]
|
||||||
|
vector_result_t map_res = vector_map(vector, square, NULL);
|
||||||
|
if (map_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while mapping vector: %s\n", map_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Squared vector: ");
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t map_get_res = vector_get(vector, idx);
|
||||||
|
if (map_get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int *val = (int*)map_get_res.value.element;
|
||||||
|
printf("%d ", *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16]
|
||||||
|
vector_result_t filter_res = vector_filter(vector, is_even, NULL);
|
||||||
|
if (filter_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while filtering vector: %s\n", filter_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sz = vector_size(vector);
|
||||||
|
|
||||||
|
printf("Filtered vector: ");
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t map_get_res = vector_get(vector, idx);
|
||||||
|
if (map_get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int *val = (int*)map_get_res.value.element;
|
||||||
|
printf("%d ", *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Reduce vector elements: [4, 16] -> 20
|
||||||
|
int sum = 0;
|
||||||
|
vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL);
|
||||||
|
if (reduce_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while reducing vector: %s\n", reduce_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Sum of vector: %d\n", sum);
|
||||||
|
|
||||||
|
// Free vector
|
||||||
|
vector_result_t del_res = vector_destroy(vector);
|
||||||
|
if (del_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while destroying the vector: %s\n", del_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void square(void *element, void *env) {
|
||||||
|
UNUSED(env);
|
||||||
|
int *value = (int*)element;
|
||||||
|
*value = (*value) * (*value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_even(const void *element, void *env) {
|
||||||
|
UNUSED(env);
|
||||||
|
int value = *(int*)element;
|
||||||
|
|
||||||
|
return (value % 2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void adder(void *accumulator, const void *element, void *env) {
|
||||||
|
UNUSED(env);
|
||||||
|
*(int*)accumulator += *(int*)element;
|
||||||
|
}
|
||||||
|
|
||||||
110
examples/vector_sorting.c
Normal file
110
examples/vector_sorting.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Vector sorting example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../src/vector.h"
|
||||||
|
|
||||||
|
static vector_order_t cmp_int_asc(const void *x, const void *y);
|
||||||
|
static vector_order_t cmp_int_desc(const void *x, const void *y);
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Create a vector
|
||||||
|
vector_result_t res = vector_new(1, sizeof(int));
|
||||||
|
if (res.status != VECTOR_OK) {
|
||||||
|
printf("Error while creating vector: %s\n", res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_t *vector = res.value.vector;
|
||||||
|
|
||||||
|
// Sort vector in ascending order
|
||||||
|
int values[] = {5, 10, -9, 3, 1, 0, 4};
|
||||||
|
for (size_t idx = 0; idx < 7; idx++) {
|
||||||
|
vector_result_t sort_push_res = vector_push(vector, &values[idx]);
|
||||||
|
if (sort_push_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while adding elements: %s\n", sort_push_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Added new elements. Before sort: ");
|
||||||
|
|
||||||
|
size_t sz = vector_size(vector);
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t sort_get_res = vector_get(vector, idx);
|
||||||
|
if (sort_get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
const int *val = (int*)sort_get_res.value.element;
|
||||||
|
printf("%d ", *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc);
|
||||||
|
if (sort_asc_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot sort array: %s\n", sort_asc_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("After sort in ascending order: ");
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t sort_get_res = vector_get(vector, idx);
|
||||||
|
if (sort_get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int *val = (int*)sort_get_res.value.element;
|
||||||
|
printf("%d ", *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Sort vector in descending order
|
||||||
|
vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc);
|
||||||
|
if (sort_desc_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot sort array: %s\n", sort_desc_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("After sort in descending order: ");
|
||||||
|
for (size_t idx = 0; idx < sz; idx++) {
|
||||||
|
vector_result_t sort_get_res = vector_get(vector, idx);
|
||||||
|
if (sort_get_res.status != VECTOR_OK) {
|
||||||
|
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int *val = (int*)sort_get_res.value.element;
|
||||||
|
printf("%d ", *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
// Free vector
|
||||||
|
vector_result_t del_res = vector_destroy(vector);
|
||||||
|
if (del_res.status != VECTOR_OK) {
|
||||||
|
printf("Error while destroying the vector: %s\n", del_res.message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_order_t cmp_int_asc(const void *x, const void *y) {
|
||||||
|
int x_int = *(const int*)x;
|
||||||
|
int y_int = *(const int*)y;
|
||||||
|
|
||||||
|
if (x_int < y_int) return VECTOR_ORDER_LT;
|
||||||
|
if (x_int > y_int) return VECTOR_ORDER_GT;
|
||||||
|
|
||||||
|
return VECTOR_ORDER_EQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_order_t cmp_int_desc(const void *x, const void *y) {
|
||||||
|
return cmp_int_asc(y, x);
|
||||||
|
}
|
||||||
|
|
||||||
409
src/bigint.c
409
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>
|
||||||
@@ -19,7 +23,6 @@
|
|||||||
#include "bigint.h"
|
#include "bigint.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
// Internal methods
|
|
||||||
/**
|
/**
|
||||||
* bigint_trim_zeros
|
* bigint_trim_zeros
|
||||||
* @number: a non-null big integer
|
* @number: a non-null big integer
|
||||||
@@ -842,30 +845,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 +880,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 +897,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 +966,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 +1141,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};
|
||||||
@@ -1555,14 +1672,14 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bigint_divmod
|
* bigint_divmod
|
||||||
* @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 +1687,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 +1698,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;
|
||||||
}
|
}
|
||||||
@@ -1603,13 +1718,13 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
|
|||||||
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 +1739,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 +1750,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 +1762,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;
|
||||||
@@ -1753,11 +1883,15 @@ bigint_result_t bigint_printf(const char *format, ...) {
|
|||||||
|
|
||||||
// Process string char by char
|
// Process string char by char
|
||||||
for (const char *p = format; *p != '\0'; p++) {
|
for (const char *p = format; *p != '\0'; p++) {
|
||||||
if (*p == '%' && *(p + 1) == 'B') {
|
if (*p == '%' && *(p + 1) != '%') {
|
||||||
// Process a big number
|
p++;
|
||||||
|
const char placeholder = *p;
|
||||||
|
|
||||||
|
switch (placeholder) {
|
||||||
|
case 'B': {
|
||||||
bigint_t *num = va_arg(args, bigint_t*);
|
bigint_t *num = va_arg(args, bigint_t*);
|
||||||
if (num == NULL) {
|
if (num == NULL) {
|
||||||
printf("<invalid string>");
|
for (const char *s = "<invalid big integer>"; *s != '\0'; s++) { putchar(*s); }
|
||||||
} else {
|
} else {
|
||||||
bigint_result_t num_str_res = bigint_to_string(num);
|
bigint_result_t num_str_res = bigint_to_string(num);
|
||||||
if (num_str_res.status != BIGINT_OK) {
|
if (num_str_res.status != BIGINT_OK) {
|
||||||
@@ -1765,28 +1899,21 @@ bigint_result_t bigint_printf(const char *format, ...) {
|
|||||||
return num_str_res;
|
return num_str_res;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* const number_str = num_str_res.value.string_num;
|
char *number_str = num_str_res.value.string_num;
|
||||||
printf("%s", number_str);
|
for (const char *s = number_str; *s != '\0'; s++) { putchar(*s); }
|
||||||
free(number_str);
|
free(number_str);
|
||||||
}
|
}
|
||||||
p++;
|
break;
|
||||||
} else if (*p == '%' && *(p + 1) != '%') {
|
}
|
||||||
// Handle common printf placeholders
|
|
||||||
p++;
|
|
||||||
char placeholder = *p;
|
|
||||||
|
|
||||||
switch (placeholder) {
|
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'i': {
|
case 'i': {
|
||||||
int val = va_arg(args, int);
|
int val = va_arg(args, int);
|
||||||
printf("%d", val);
|
printf("%d", val);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'u': {
|
case 'u': {
|
||||||
unsigned int val = va_arg(args, unsigned int);
|
unsigned int val = va_arg(args, unsigned int);
|
||||||
printf("%u", val);
|
printf("%u", val);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'l': {
|
case 'l': {
|
||||||
@@ -1807,12 +1934,16 @@ bigint_result_t bigint_printf(const char *format, ...) {
|
|||||||
}
|
}
|
||||||
case 's': {
|
case 's': {
|
||||||
char* val = va_arg(args, char*);
|
char* val = va_arg(args, char*);
|
||||||
printf("%s", val ? val : "<invalid string>");
|
if (val) {
|
||||||
|
for (const char *s = val; *s != '\0'; s++) { putchar(*s); }
|
||||||
|
} else {
|
||||||
|
for (const char *s = "<invalid string>"; *s != '\0'; s++) { putchar(*s); }
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'c': {
|
case 'c': {
|
||||||
int val = va_arg(args, int);
|
int val = va_arg(args, int);
|
||||||
printf("%c", val);
|
putchar(val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'f': {
|
case 'f': {
|
||||||
@@ -1821,7 +1952,7 @@ bigint_result_t bigint_printf(const char *format, ...) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'p': {
|
case 'p': {
|
||||||
void *val = va_arg(args, void*);
|
void* const val = va_arg(args, void*);
|
||||||
printf("%p", val);
|
printf("%p", val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
|
|
||||||
// Internal methods
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hash_key
|
* hash_key
|
||||||
* @key: The input string for the hash function
|
* @key: The input string for the hash function
|
||||||
|
|||||||
41
src/string.c
41
src/string.c
@@ -17,7 +17,7 @@ static inline bool is_space(unsigned char c) {
|
|||||||
c == '\f' || c == '\v');
|
c == '\f' || c == '\v');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get byte length of a UTF-8 character/symbol
|
// Get byte length of an UTF-8 sequence
|
||||||
static inline int utf8_char_len(unsigned char byte) {
|
static inline int utf8_char_len(unsigned char byte) {
|
||||||
if ((byte & 0x80) == 0x00) return 1;
|
if ((byte & 0x80) == 0x00) return 1;
|
||||||
if ((byte & 0xE0) == 0xC0) return 2;
|
if ((byte & 0xE0) == 0xC0) return 2;
|
||||||
@@ -33,7 +33,7 @@ static bool utf8_is_char_valid(const char *utf8_char, int *out_len) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len = utf8_char_len((unsigned char)utf8_char[0]);
|
const size_t len = utf8_char_len((unsigned char)utf8_char[0]);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ static bool utf8_is_char_valid(const char *utf8_char, int *out_len) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate an UTF-8 symbol and measure byte length and character count in one pass
|
// Validate an UTF-8 symbol and measure byte length and character count
|
||||||
static bool utf8_scan(const char *str, size_t *out_byte_size, size_t *out_char_count) {
|
static bool utf8_scan(const char *str, size_t *out_byte_size, size_t *out_char_count) {
|
||||||
size_t b_size = 0;
|
size_t b_size = 0;
|
||||||
size_t c_count = 0;
|
size_t c_count = 0;
|
||||||
@@ -120,6 +120,7 @@ static uint32_t utf8_decode(const char *str, int *char_len) {
|
|||||||
static int utf8_encode(uint32_t codepoint, char *out) {
|
static int utf8_encode(uint32_t codepoint, char *out) {
|
||||||
if (codepoint <= 0x7F) {
|
if (codepoint <= 0x7F) {
|
||||||
out[0] = (char)codepoint;
|
out[0] = (char)codepoint;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +162,7 @@ string_result_t string_new(const char *c_str) {
|
|||||||
|
|
||||||
if (c_str == NULL) {
|
if (c_str == NULL) {
|
||||||
result.status = STRING_ERR_INVALID;
|
result.status = STRING_ERR_INVALID;
|
||||||
SET_MSG(result, "Invalid null input string");
|
SET_MSG(result, "Invalid input string");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -191,14 +192,15 @@ string_result_t string_new(const char *c_str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(str->data, c_str, b_size + 1);
|
memcpy(str->data, c_str, b_size);
|
||||||
|
str->data[b_size] = '\0';
|
||||||
str->byte_size = b_size;
|
str->byte_size = b_size;
|
||||||
str->byte_capacity = b_size + 1;
|
str->byte_capacity = b_size + 1;
|
||||||
str->char_count = c_count;
|
str->char_count = c_count;
|
||||||
|
|
||||||
result.status = STRING_OK;
|
result.status = STRING_OK;
|
||||||
SET_MSG(result, "String successfully created");
|
|
||||||
result.value.string = str;
|
result.value.string = str;
|
||||||
|
SET_MSG(result, "String successfully created");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -238,7 +240,8 @@ string_result_t string_clone(const string_t *str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(str_copy->data, str->data, str->byte_size + 1);
|
memcpy(str_copy->data, str->data, str->byte_size);
|
||||||
|
str_copy->data[str->byte_size] = '\0';
|
||||||
str_copy->byte_size = str->byte_size;
|
str_copy->byte_size = str->byte_size;
|
||||||
str_copy->byte_capacity = str->byte_size + 1;
|
str_copy->byte_capacity = str->byte_size + 1;
|
||||||
str_copy->char_count = str->char_count;
|
str_copy->char_count = str->char_count;
|
||||||
@@ -291,6 +294,8 @@ string_result_t string_concat(const string_t *x, const string_t *y) {
|
|||||||
result = string_new(buf);
|
result = string_new(buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
|
SET_MSG(result, "String successfully concatenated");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +377,7 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
|
|||||||
end_byte_offset += utf8_char_len((unsigned char)str->data[end_byte_offset]);
|
end_byte_offset += utf8_char_len((unsigned char)str->data[end_byte_offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t slice_byte_size = end_byte_offset - start_byte_offset;
|
const size_t slice_byte_size = (end_byte_offset - start_byte_offset);
|
||||||
|
|
||||||
string_t *slice = malloc(sizeof(string_t));
|
string_t *slice = malloc(sizeof(string_t));
|
||||||
if (slice == NULL) {
|
if (slice == NULL) {
|
||||||
@@ -384,6 +389,7 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
|
|||||||
|
|
||||||
slice->data = malloc(slice_byte_size + 1);
|
slice->data = malloc(slice_byte_size + 1);
|
||||||
if (slice->data == NULL) {
|
if (slice->data == NULL) {
|
||||||
|
free(slice);
|
||||||
result.status = STRING_ERR_ALLOCATE;
|
result.status = STRING_ERR_ALLOCATE;
|
||||||
SET_MSG(result, "Cannot allocate memory");
|
SET_MSG(result, "Cannot allocate memory");
|
||||||
|
|
||||||
@@ -428,8 +434,9 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (x->char_count != y->char_count) {
|
if (x->char_count != y->char_count) {
|
||||||
result.status = STRING_ERR_INVALID;
|
result.value.is_equ = false;
|
||||||
SET_MSG(result, "Strings differ in length");
|
result.status = STRING_OK;
|
||||||
|
SET_MSG(result, "Comparison completed successfully");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -448,12 +455,14 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
|
|||||||
|
|
||||||
if (c1 != c2) {
|
if (c1 != c2) {
|
||||||
result.value.is_equ = false;
|
result.value.is_equ = false;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 += l1;
|
p1 += l1;
|
||||||
p2 += l2;
|
p2 += l2;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.value.is_equ = (*p1 == *p2);
|
result.value.is_equ = (*p1 == *p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,7 +643,6 @@ string_result_t string_to_lower(const string_t *str) {
|
|||||||
result = string_new(buf);
|
result = string_new(buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
result.status = STRING_OK;
|
|
||||||
SET_MSG(result, "String successfully converted to lowercase");
|
SET_MSG(result, "String successfully converted to lowercase");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -679,7 +687,6 @@ string_result_t string_to_upper(const string_t *str) {
|
|||||||
result = string_new(buf);
|
result = string_new(buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
result.status = STRING_OK;
|
|
||||||
SET_MSG(result, "String successfully converted to uppercase");
|
SET_MSG(result, "String successfully converted to uppercase");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -713,6 +720,7 @@ string_result_t string_reverse(const string_t *str) {
|
|||||||
|
|
||||||
const char **pos = malloc(str->char_count * sizeof(char *));
|
const char **pos = malloc(str->char_count * sizeof(char *));
|
||||||
if (pos == NULL) {
|
if (pos == NULL) {
|
||||||
|
free(buf);
|
||||||
result.status = STRING_ERR_ALLOCATE;
|
result.status = STRING_ERR_ALLOCATE;
|
||||||
SET_MSG(result, "Cannot allocate memory");
|
SET_MSG(result, "Cannot allocate memory");
|
||||||
|
|
||||||
@@ -839,7 +847,7 @@ string_result_t string_split(const string_t *str, const char *delim) {
|
|||||||
char *tmp = malloc(part_len + 1);
|
char *tmp = malloc(part_len + 1);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
result.status = STRING_ERR_ALLOCATE;
|
result.status = STRING_ERR_ALLOCATE;
|
||||||
SET_MSG(result, "Cannot allocated memory");
|
SET_MSG(result, "Cannot allocate memory");
|
||||||
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -866,10 +874,9 @@ string_result_t string_split(const string_t *str, const char *delim) {
|
|||||||
SET_MSG(result, "String successfully split");
|
SET_MSG(result, "String successfully split");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
for (size_t j = 0; j < idx; j++) {
|
for (size_t j = 0; j < idx; j++) { string_destroy(string_array[j]); }
|
||||||
string_destroy(string_array[j]);
|
|
||||||
}
|
|
||||||
free(string_array);
|
free(string_array);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -903,7 +910,7 @@ string_result_t string_destroy(string_t *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* string_split_destory
|
* string_split_destroy
|
||||||
* @split: an array of pointers of String
|
* @split: an array of pointers of String
|
||||||
* @count: the number of elements
|
* @count: the number of elements
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *data;
|
char *data;
|
||||||
size_t byte_size; // Size in bytes excluding NULL terminator
|
size_t byte_size; // Size in bytes minus the NULL terminator
|
||||||
size_t byte_capacity; // Total allocated memory
|
size_t byte_capacity; // Total allocated memory
|
||||||
size_t char_count; // Number of symbols
|
size_t char_count; // Number of symbols
|
||||||
} string_t;
|
} string_t;
|
||||||
@@ -38,7 +38,7 @@ typedef struct {
|
|||||||
} string_result_t;
|
} string_result_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
#extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Public APIs
|
// Public APIs
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
// Internal methods
|
|
||||||
/**
|
/**
|
||||||
* vector_resize
|
* vector_resize
|
||||||
* @vector: a non-null vector
|
* @vector: a non-null vector
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ void test_string_concat(void) {
|
|||||||
string_destroy(res.value.string);
|
string_destroy(res.value.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test if string contains a substring
|
// Test if string contains substring
|
||||||
void test_string_contains(void) {
|
void test_string_contains(void) {
|
||||||
string_t *haystack = string_new("Hello 🌍 World").value.string;
|
string_t *haystack = string_new("Hello 🌍 World").value.string;
|
||||||
string_t *needle_ascii = string_new("World").value.string;
|
string_t *needle_ascii = string_new("World").value.string;
|
||||||
@@ -108,17 +108,17 @@ void test_string_slice(void) {
|
|||||||
assert(res1.value.string->char_count == 3);
|
assert(res1.value.string->char_count == 3);
|
||||||
|
|
||||||
// UTF-8 slice
|
// UTF-8 slice
|
||||||
string_t *str2 = string_new("AB😆🌍").value.string;
|
string_t *str2 = string_new("AB😀🌍").value.string;
|
||||||
string_result_t res2 = string_slice(str2, 2, 2);
|
string_result_t res2 = string_slice(str2, 2, 2);
|
||||||
|
|
||||||
assert(res2.status == STRING_OK);
|
assert(res2.status == STRING_OK);
|
||||||
assert(strcmp(res2.value.string->data, "😆") == 0);
|
assert(strcmp(res2.value.string->data, "😀") == 0);
|
||||||
assert(res2.value.string->byte_size == 4); // emoji = 4 bytes
|
assert(res2.value.string->byte_size == 4); // emoji = 4 bytes
|
||||||
|
|
||||||
// UTF-8 + ASCII slice
|
// UTF-8 + ASCII slice
|
||||||
string_result_t res3 = string_slice(str2, 0, 2);
|
string_result_t res3 = string_slice(str2, 0, 2);
|
||||||
assert(res3.status == STRING_OK);
|
assert(res3.status == STRING_OK);
|
||||||
assert(strcmp(res3.value.string->data, "AB😆") == 0);
|
assert(strcmp(res3.value.string->data, "AB😀") == 0);
|
||||||
|
|
||||||
// Invalid bounds
|
// Invalid bounds
|
||||||
string_result_t res4 = string_slice(str1, 5, 2);
|
string_result_t res4 = string_slice(str1, 5, 2);
|
||||||
@@ -165,12 +165,12 @@ void test_string_reverse_utf8(void) {
|
|||||||
|
|
||||||
// Test string get_at
|
// Test string get_at
|
||||||
void test_string_get_at(void) {
|
void test_string_get_at(void) {
|
||||||
string_t *str = string_new("AB😆🌍").value.string;
|
string_t *str = string_new("AB😀🌍").value.string;
|
||||||
|
|
||||||
// 😆 is at index 2
|
// 😀 is at index 2
|
||||||
string_result_t res1 = string_get_at(str, 2);
|
string_result_t res1 = string_get_at(str, 2);
|
||||||
assert(res1.status == STRING_OK);
|
assert(res1.status == STRING_OK);
|
||||||
assert(strcmp((char*)res1.value.symbol, "😆") == 0);
|
assert(strcmp((char*)res1.value.symbol, "😀") == 0);
|
||||||
free(res1.value.symbol);
|
free(res1.value.symbol);
|
||||||
|
|
||||||
// 🌍 is at index 3
|
// 🌍 is at index 3
|
||||||
@@ -196,12 +196,12 @@ void test_string_get_at_overflow(void) {
|
|||||||
void test_string_set_at(void) {
|
void test_string_set_at(void) {
|
||||||
string_t *str = string_new("ABC").value.string;
|
string_t *str = string_new("ABC").value.string;
|
||||||
|
|
||||||
// Replace 'B' with emoji
|
// Replace 'B' with an emoji
|
||||||
string_result_t res = string_set_at(str, 1, "😆");
|
string_result_t res = string_set_at(str, 1, "😀");
|
||||||
string_t *altered = res.value.string;
|
string_t *altered = res.value.string;
|
||||||
|
|
||||||
assert(res.status == STRING_OK);
|
assert(res.status == STRING_OK);
|
||||||
assert(strcmp(altered->data, "A😆C") == 0);
|
assert(strcmp(altered->data, "A😀C") == 0);
|
||||||
assert(string_size(altered) == 3);
|
assert(string_size(altered) == 3);
|
||||||
assert(altered->byte_size == 6); // that is: A (1B) + emoji (4B) + C (1B)
|
assert(altered->byte_size == 6); // that is: A (1B) + emoji (4B) + C (1B)
|
||||||
|
|
||||||
|
|||||||
711
usage.c
711
usage.c
@@ -1,711 +0,0 @@
|
|||||||
/*
|
|
||||||
* Sample usage of the Datum library.
|
|
||||||
*
|
|
||||||
* This program is a complete example on how to use Datum
|
|
||||||
* with *verbose* error checking. For a more minimal usage, you may want to ignore
|
|
||||||
* return messages/codes and get straight to the actual result. See the early
|
|
||||||
* part of the README.md file for such example (use it at your own risk).
|
|
||||||
*
|
|
||||||
* Developed by Marco Cetica (c) 2025, <email@marcocetica.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define SEP(SIZE) do { \
|
|
||||||
for (size_t i = 0; i < SIZE; i++) { \
|
|
||||||
printf("="); \
|
|
||||||
}; \
|
|
||||||
puts("\n"); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define UNUSED(X) (void)(X)
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "src/vector.h"
|
|
||||||
#include "src/map.h"
|
|
||||||
#include "src/bigint.h"
|
|
||||||
#include "src/string.h"
|
|
||||||
|
|
||||||
static int vector_usage(void);
|
|
||||||
static int map_usage(void);
|
|
||||||
static int bigint_usage(void);
|
|
||||||
static int string_usage(void);
|
|
||||||
|
|
||||||
static vector_order_t cmp_int_asc(const void *x, const void *y);
|
|
||||||
static vector_order_t cmp_int_desc(const void *x, const void *y);
|
|
||||||
static void square(void *element, void *env);
|
|
||||||
static int is_even(const void *element, void *env);
|
|
||||||
static void adder(void *accumulator, const void *element, void *env);
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int st;
|
|
||||||
|
|
||||||
st = vector_usage();
|
|
||||||
if (st) { return st; }
|
|
||||||
|
|
||||||
SEP(50);
|
|
||||||
|
|
||||||
st = map_usage();
|
|
||||||
if (st) { return st; }
|
|
||||||
|
|
||||||
SEP(50);
|
|
||||||
|
|
||||||
st = bigint_usage();
|
|
||||||
if (st) { return st; }
|
|
||||||
|
|
||||||
SEP(50);
|
|
||||||
|
|
||||||
st = string_usage();
|
|
||||||
if (st) { return st; }
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_order_t cmp_int_asc(const void *x, const void *y) {
|
|
||||||
int x_int = *(const int*)x;
|
|
||||||
int y_int = *(const int*)y;
|
|
||||||
|
|
||||||
if (x_int < y_int) return VECTOR_ORDER_LT;
|
|
||||||
if (x_int > y_int) return VECTOR_ORDER_GT;
|
|
||||||
|
|
||||||
return VECTOR_ORDER_EQ;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_order_t cmp_int_desc(const void *x, const void *y) {
|
|
||||||
return cmp_int_asc(y, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void square(void *element, void *env) {
|
|
||||||
UNUSED(env);
|
|
||||||
int *value = (int*)element;
|
|
||||||
*value = (*value) * (*value);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_even(const void *element, void *env) {
|
|
||||||
UNUSED(env);
|
|
||||||
int value = *(int*)element;
|
|
||||||
|
|
||||||
return (value % 2) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void adder(void *accumulator, const void *element, void *env) {
|
|
||||||
UNUSED(env);
|
|
||||||
*(int*)accumulator += *(int*)element;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vector_usage(void) {
|
|
||||||
// Create a vector of 3 integers
|
|
||||||
vector_result_t res = vector_new(3, sizeof(int));
|
|
||||||
if (res.status != VECTOR_OK) {
|
|
||||||
printf("Error while creating vector: %s\n", res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_t *vector = res.value.vector;
|
|
||||||
|
|
||||||
// Push some values to trigger reallocation
|
|
||||||
for (int idx = 0; idx < 5; idx++) {
|
|
||||||
vector_result_t add_res = vector_push(vector, &idx);
|
|
||||||
if (add_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while adding elements: %s\n", add_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print vector size and capacity
|
|
||||||
printf("Vector size (should be 5): %zu\n", vector_size(vector));
|
|
||||||
printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector));
|
|
||||||
|
|
||||||
// Print the whole vector
|
|
||||||
size_t sz = vector_size(vector);
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t get_res = vector_get(vector, idx);
|
|
||||||
if (get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int val = *(int *)get_res.value.element;
|
|
||||||
printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set an element at index 2
|
|
||||||
int new_val = 0xBABE;
|
|
||||||
vector_result_t set_res = vector_set(vector, 2, &new_val);
|
|
||||||
if (set_res.status == VECTOR_OK) {
|
|
||||||
printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop last element
|
|
||||||
vector_result_t pop_res = vector_pop(vector);
|
|
||||||
if (pop_res.status == VECTOR_OK) {
|
|
||||||
int val = *(int *)pop_res.value.element;
|
|
||||||
printf("Popped value (should be 5) : %d\n\n", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear vector
|
|
||||||
vector_result_t clear_res = vector_clear(vector);
|
|
||||||
if (clear_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot clear vector: %s\n", clear_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort vector in ascending order
|
|
||||||
int values[] = {5, 10, -9, 3, 1, 0, 4};
|
|
||||||
for (size_t idx = 0; idx < 7; idx++) {
|
|
||||||
vector_result_t sort_push_res = vector_push(vector, &values[idx]);
|
|
||||||
if (sort_push_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while adding elements: %s\n", sort_push_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Added new elements. Before sort: ");
|
|
||||||
|
|
||||||
sz = vector_size(vector);
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t sort_get_res = vector_get(vector, idx);
|
|
||||||
if (sort_get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
const int *val = (int*)sort_get_res.value.element;
|
|
||||||
printf("%d ", *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc);
|
|
||||||
if (sort_asc_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot sort array: %s\n", sort_asc_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("After sort in ascending order: ");
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t sort_get_res = vector_get(vector, idx);
|
|
||||||
if (sort_get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int *val = (int*)sort_get_res.value.element;
|
|
||||||
printf("%d ", *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Sort vector in descending order
|
|
||||||
vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc);
|
|
||||||
if (sort_desc_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot sort array: %s\n", sort_desc_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("After sort in descending order: ");
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t sort_get_res = vector_get(vector, idx);
|
|
||||||
if (sort_get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int *val = (int*)sort_get_res.value.element;
|
|
||||||
printf("%d ", *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n\n");
|
|
||||||
|
|
||||||
vector_result_t map_clear_res = vector_clear(vector);
|
|
||||||
if (map_clear_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot clear vector: %s\n", map_clear_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map vector elements
|
|
||||||
for (size_t idx = 1; idx <= 5; idx++) {
|
|
||||||
vector_result_t map_push_res = vector_push(vector, &idx);
|
|
||||||
if (map_push_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while adding elements: %s\n", map_push_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sz = vector_size(vector);
|
|
||||||
|
|
||||||
// Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25]
|
|
||||||
vector_result_t map_res = vector_map(vector, square, NULL);
|
|
||||||
if (map_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while mapping vector: %s\n", map_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Squared vector: ");
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t map_get_res = vector_get(vector, idx);
|
|
||||||
if (map_get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int *val = (int*)map_get_res.value.element;
|
|
||||||
printf("%d ", *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16]
|
|
||||||
vector_result_t filter_res = vector_filter(vector, is_even, NULL);
|
|
||||||
if (filter_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while filtering vector: %s\n", filter_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sz = vector_size(vector);
|
|
||||||
|
|
||||||
printf("Filtered vector: ");
|
|
||||||
for (size_t idx = 0; idx < sz; idx++) {
|
|
||||||
vector_result_t map_get_res = vector_get(vector, idx);
|
|
||||||
if (map_get_res.status != VECTOR_OK) {
|
|
||||||
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int *val = (int*)map_get_res.value.element;
|
|
||||||
printf("%d ", *val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Reduce vector elements: [4, 16] -> 20
|
|
||||||
int sum = 0;
|
|
||||||
vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL);
|
|
||||||
if (reduce_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while reducing vector: %s\n", reduce_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Sum of vector: %d\n\n", sum);
|
|
||||||
|
|
||||||
// Free vector
|
|
||||||
vector_result_t del_res = vector_destroy(vector);
|
|
||||||
if (del_res.status != VECTOR_OK) {
|
|
||||||
printf("Error while destroying the vector: %s\n", del_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int map_usage(void) {
|
|
||||||
// Create a new map
|
|
||||||
map_result_t res = map_new();
|
|
||||||
if (res.status != MAP_OK) {
|
|
||||||
printf("Error while creating map: %s\n", res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
map_t *map = res.value.map;
|
|
||||||
|
|
||||||
// Add some values
|
|
||||||
const int x = 0xB00B5;
|
|
||||||
const char *y = "Hello";
|
|
||||||
|
|
||||||
map_result_t add_res = map_add(map, "x", (void*)&x);
|
|
||||||
if (add_res.status != MAP_OK) {
|
|
||||||
printf("Error while adding elements: %s\n", add_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_res = map_add(map, "y", (void*)y);
|
|
||||||
if (add_res.status != MAP_OK) {
|
|
||||||
printf("Error while adding elements: %s\n", add_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print size and capacity
|
|
||||||
printf("Map size (should be 2): %zu\n", map_size(map));
|
|
||||||
printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map));
|
|
||||||
|
|
||||||
// Retrieve keys
|
|
||||||
map_result_t get_res = map_get(map, "x");
|
|
||||||
if (get_res.status != MAP_OK) {
|
|
||||||
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
const int *val = (const int*)get_res.value.element;
|
|
||||||
printf("Key 'x' contains (should be 'B00B5'): %X\n", *val);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_res = map_get(map, "y");
|
|
||||||
if (get_res.status != MAP_OK) {
|
|
||||||
printf("Cannot retrieve map element 'y': %s\n", get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
const char *val = (const char*)get_res.value.element;
|
|
||||||
printf("Key 'y' contains (should be 'Hello') : %s\n\n", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update key
|
|
||||||
const int new_x = 0xC0FFEE;
|
|
||||||
map_result_t up_res = map_add(map, "x", (void*)&new_x);
|
|
||||||
|
|
||||||
up_res = map_get(map, "x");
|
|
||||||
if (get_res.status != MAP_OK) {
|
|
||||||
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
const int *val = (const int*)up_res.value.element;
|
|
||||||
printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove an element
|
|
||||||
map_result_t rm_res = map_remove(map, "y");
|
|
||||||
if (rm_res.status != MAP_OK) {
|
|
||||||
printf("Cannot remove map element 'y': %s\n", rm_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the map
|
|
||||||
map_result_t clear_res = map_clear(map);
|
|
||||||
if (clear_res.status != MAP_OK) {
|
|
||||||
printf("Cannot clear map: %s\n", clear_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
printf("Map cleared (size should be 0): %zu\n", map_size(map));
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Delete the map
|
|
||||||
map_result_t del_res = map_destroy(map);
|
|
||||||
if (del_res.status != MAP_OK) {
|
|
||||||
printf("Error while destroying the map: %s\n", del_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bigint_usage(void) {
|
|
||||||
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(large_y);
|
|
||||||
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
|
|
||||||
bigint_printf("Sum result = %B\n", sum);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
bigint_printf("difference result = %B\n", diff);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
bigint_printf("multiplication result = %B\n", prod);
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (div_res.status != BIGINT_OK) {
|
|
||||||
printf("Error while dividing two big numbers: %s\n", div_res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bigint_t *quotient = div_res.value.division.quotient;
|
|
||||||
bigint_t *remainder = div_res.value.division.remainder;
|
|
||||||
|
|
||||||
// Print result
|
|
||||||
bigint_printf(
|
|
||||||
"division result = %B\
|
|
||||||
\nmod result = %B\n",
|
|
||||||
quotient, remainder);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int string_usage(void) {
|
|
||||||
// Create a new string
|
|
||||||
string_result_t res = string_new("Hello, ");
|
|
||||||
if (res.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t *str1 = res.value.string;
|
|
||||||
printf("Created string: \"%s\"\n", str1->data);
|
|
||||||
printf("Character count: %zu (%zu actual bytes)\n", string_size(str1), str1->byte_size);
|
|
||||||
|
|
||||||
string_result_t res_clone = string_clone(str1);
|
|
||||||
if (res_clone.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t *cloned = res_clone.value.string;
|
|
||||||
printf("Cloned string: \"%s\"\n\n", cloned->data);
|
|
||||||
string_destroy(cloned);
|
|
||||||
|
|
||||||
// Concatenation of strings
|
|
||||||
string_result_t res_suffix = string_new("World! 🦜");
|
|
||||||
if (res_suffix.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t *suffix = res_suffix.value.string;
|
|
||||||
printf("Created another string: \"%s\"\n", suffix->data);
|
|
||||||
printf("Character count: %zu (%zu actual bytes)\n\n", string_size(suffix), suffix->byte_size);
|
|
||||||
|
|
||||||
string_result_t res_cat = string_concat(str1, suffix);
|
|
||||||
if (res_cat.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_cat.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
string_destroy(suffix);
|
|
||||||
|
|
||||||
string_t *concat_str = res_cat.value.string;
|
|
||||||
printf("Concatenation result: \"%s\"\n\n", concat_str->data);
|
|
||||||
|
|
||||||
// String contains
|
|
||||||
string_t *haystack = string_new("The quick brown fox jumps over the lazy dog.").value.string;
|
|
||||||
string_t *needle = string_new("brown fox").value.string;
|
|
||||||
|
|
||||||
string_result_t res_contains = string_contains(haystack, needle);
|
|
||||||
if (res_contains.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_contains.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res_contains.value.idx != -1) {
|
|
||||||
printf("Substring found. Starting at index %zu\n\n", res_contains.value.idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
string_destroy(haystack);
|
|
||||||
string_destroy(needle);
|
|
||||||
|
|
||||||
// String slicing
|
|
||||||
string_result_t res_slice = string_slice(concat_str, 7, 14);
|
|
||||||
if (res_slice.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_slice.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Slice of string: \"%s\"\n\n", res_slice.value.string->data);
|
|
||||||
string_destroy(res_slice.value.string);
|
|
||||||
|
|
||||||
// String equality
|
|
||||||
string_t *compare = string_new("hello, World! 🦜").value.string;
|
|
||||||
string_result_t res_eq = string_eq(concat_str, compare, true);
|
|
||||||
if (res_eq.value.is_equ) {
|
|
||||||
printf("The two strings are equal\n\n");
|
|
||||||
} else {
|
|
||||||
printf("The two strings are not equal\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
string_destroy(compare);
|
|
||||||
|
|
||||||
// Uppercase string
|
|
||||||
string_result_t res_upper = string_to_upper(concat_str);
|
|
||||||
if (res_upper.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_upper.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Uppercase: \"%s\"\n", res_upper.value.string->data);
|
|
||||||
string_destroy(res_upper.value.string);
|
|
||||||
|
|
||||||
// Lowercase string
|
|
||||||
string_result_t res_lower = string_to_lower(concat_str);
|
|
||||||
if (res_lower.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_lower.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Lowercase: \"%s\"\n\n", res_lower.value.string->data);
|
|
||||||
string_destroy(res_lower.value.string);
|
|
||||||
|
|
||||||
// Reverse string
|
|
||||||
string_result_t res_rev = string_reverse(concat_str);
|
|
||||||
if (res_rev.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_rev.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Reversed: \"%s\"\n\n", res_rev.value.string->data);
|
|
||||||
string_destroy(res_rev.value.string);
|
|
||||||
|
|
||||||
// Change first character of the string
|
|
||||||
string_result_t res_set = string_set_at(concat_str, 0, "J");
|
|
||||||
if (res_set.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_set.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Updated string: \"%s\"\n\n", res_set.value.string->data);
|
|
||||||
string_destroy(res_set.value.string);
|
|
||||||
|
|
||||||
// Get character from string (the emoji)
|
|
||||||
string_result_t res_get = string_get_at(concat_str, 14);
|
|
||||||
if (res_get.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_get.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
printf("Extracted symbol: \"%s\"\n", res_get.value.symbol);
|
|
||||||
free(res_get.value.symbol);
|
|
||||||
|
|
||||||
// Trim string
|
|
||||||
string_t *to_trim = string_new(" foo ").value.string;
|
|
||||||
string_result_t res_trim = string_trim(to_trim);
|
|
||||||
if (res_trim.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_trim.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Trimmed string: \"%s\"\n\n", res_trim.value.string->data);
|
|
||||||
string_destroy(to_trim);
|
|
||||||
string_destroy(res_trim.value.string);
|
|
||||||
|
|
||||||
// Split string
|
|
||||||
string_t *to_split = string_new("foo/bar/biz").value.string;
|
|
||||||
string_result_t res_split = string_split(to_split, "/");
|
|
||||||
if (res_split.status != STRING_OK) {
|
|
||||||
printf("Error: %s\n", res_split.message);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t count = res_split.value.split.count;
|
|
||||||
string_t **strings = res_split.value.split.strings;
|
|
||||||
|
|
||||||
printf("Original string: \"%s\"\nSplitted string: ", to_split->data);
|
|
||||||
for (size_t idx = 0; idx < count; idx++) {
|
|
||||||
printf("\"%s\" ", strings[idx]->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
string_split_destroy(strings, count);
|
|
||||||
string_destroy(to_split);
|
|
||||||
|
|
||||||
string_destroy(concat_str);
|
|
||||||
string_destroy(str1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user