Compare commits

15 Commits

Author SHA1 Message Date
de22c706af Added String benchmark function
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 21s
clang-build / clang-build (pull_request) Successful in 31s
gcc-build / gcc-build (pull_request) Successful in 20s
2026-03-16 09:53:07 +01:00
f8a77a6056 Added String type documentation
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 19s
2026-03-16 09:42:15 +01:00
6110fc963c Fixed a bug and added unit tests for String type
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 18s
2026-03-13 18:27:00 +01:00
1871035cd6 Added string_{trim,split,destroy,split_destroy}
All checks were successful
clang-build / clang-build (push) Successful in 28s
gcc-build / gcc-build (push) Successful in 19s
2026-03-13 17:36:37 +01:00
b4a704f1b4 Added `string_{get_at,set_at,to_lower,to_upper,reverse}
Some checks failed
clang-build / clang-build (push) Failing after 18s
gcc-build / gcc-build (push) Successful in 19s
2026-03-13 17:16:02 +01:00
086446039b Fixed a bug about string comparison
Some checks failed
clang-build / clang-build (push) Failing after 20s
gcc-build / gcc-build (push) Failing after 5s
2026-03-13 11:48:42 +01:00
55b14ee949 Added string_{new,clone,concat,contains,slice,eq}
Some checks failed
clang-build / clang-build (push) Failing after 24s
gcc-build / gcc-build (push) Failing after 4s
2026-03-13 11:39:50 +01:00
110ccae8b9 Added UTF-8 helper functions
All checks were successful
clang-build / clang-build (push) Successful in 1m25s
gcc-build / gcc-build (push) Successful in 18s
2026-03-13 10:43:36 +01:00
c7f6ed82c9 Merge pull request 'fast_div_proto' (#1) from fast_div_proto into master
All checks were successful
clang-build / clang-build (push) Successful in 38s
gcc-build / gcc-build (push) Successful in 19s
Reviewed-on: #1
2026-02-26 08:56:50 +00:00
40d343c02b Updated documentation
All checks were successful
clang-build / clang-build (push) Successful in 36s
gcc-build / gcc-build (push) Successful in 18s
clang-build / clang-build (pull_request) Successful in 36s
gcc-build / gcc-build (pull_request) Successful in 21s
2026-02-26 09:46:48 +01:00
eb670e26a5 Improved bigint_printf method
All checks were successful
clang-build / clang-build (push) Successful in 37s
gcc-build / gcc-build (push) Successful in 18s
2026-02-26 09:36:46 +01:00
a02f2dff40 Added Knuth's "Algorithm D" from TAOCP "Seminumerical algorithms"
All checks were successful
clang-build / clang-build (push) Successful in 41s
gcc-build / gcc-build (push) Successful in 19s
2026-02-25 17:13:49 +01:00
ea9ef9de4b Updated documentation
All checks were successful
clang-build / clang-build (push) Successful in 36s
gcc-build / gcc-build (push) Successful in 8s
2026-01-23 17:06:13 +01:00
dd6e7a9c9e Updated documentation 2026-01-12 11:58:32 +01:00
6cd90467c6 General refactoring 2026-01-07 11:08:53 +01:00
24 changed files with 1310 additions and 1081 deletions

View File

@@ -13,7 +13,6 @@ BENCH_OBJ_DIR = bench_obj
TESTS_SRC = tests
TARGET = usage
TEST_V_TARGET = test_vector
TEST_M_TARGET = test_map
TEST_B_TARGET = test_bigint
@@ -21,16 +20,12 @@ TEST_S_TARGET = test_string
BENCH_TARGET = benchmark_datum
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)
$(TARGET): $(PROG_OBJS) $(LIB_OBJS)
$(CC) $(CFLAGS) -o $@ $^
$(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.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
$(CC) $(CFLAGS) -o $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
examples: $(LIB_OBJS)
$(MAKE) -C examples
$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
@@ -56,7 +51,7 @@ $(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# 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 $@ $^
$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR)
@@ -69,4 +64,5 @@ $(BENCH_OBJ_DIR):
mkdir -p $(BENCH_OBJ_DIR)
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

View File

@@ -4,16 +4,15 @@
![](https://git.marcocetica.com/marco/datum/actions/workflows/gcc-build.yml/badge.svg)
![](https://git.marcocetica.com/marco/datum/actions/workflows/clang-build.yml/badge.svg)
</div>
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:
- [**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;
- [**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
At its simplest, you can use this library as follows:
@@ -112,9 +111,9 @@ int main(void) {
#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...
* 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) {
const int n = 20000;
@@ -136,8 +135,7 @@ int main(void) {
}
```
### `String` usage:
### `String` usage
```c
#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
and a sample usage for every available method. To run it, first issue the following command:
For additional usage samples, refer to the [`examples/`](/examples) directory. You can compile these example programs with the following command:
```sh
$ make clean all
```
This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`.
> [!NOTE]
> This project is primarily developed for learning purposes and was not created with industrial
> or production use in mind. As such, it is not intended to compete with any existing C library.
> In particular, the big number implementation does not aim to match the design, the maturity and
> the performance of established solutions such as the
> GNU Multiple Precision Arithmetic Library (GMP).
## Documentation
For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs).
@@ -199,14 +187,16 @@ $ ./test_bigint
```
## 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:
```sh
$ make clean all CC=clang
$ ./benchmark_datum
Computing Vector average time...average time: 19 ms
Computing Map average time...average time: 55 ms
Computing String average time...average time: 24 ms
Computing Vector average time...average time: 6 ms
Computing Map average time...average time: 49 ms
Computing BigInt average time...average time: 67 ms
Computing String average time...average time: 13 ms
```

View File

@@ -1,5 +1,3 @@
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -8,6 +6,7 @@
#include "../src/vector.h"
#include "../src/map.h"
#include "../src/bigint.h"
#include "../src/string.h"
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;
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++) {
const int *val = (int*)vector_get(vec, idx).value.element;
sum += *val;
@@ -41,7 +40,7 @@ void test_map(size_t iterations) {
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++) {
snprintf(key, sizeof(key), "key_%zu", idx);
@@ -53,7 +52,7 @@ void test_map(size_t iterations) {
for (size_t idx = 0; idx < map->capacity; idx++) {
snprintf(key, sizeof(key), "key_%zu", idx);
int *val = (int*)map_get(map, key).value.element;
int *val = (int *)map_get(map, key).value.element;
free(val);
map_remove(map, key);
@@ -62,6 +61,62 @@ void test_map(size_t iterations) {
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) {
volatile size_t total_len = 0;
@@ -112,7 +167,7 @@ int main(void) {
// Do a warmup run
test_vector(1000);
test_map(1000);
test_string(1000);
test_bigint(1000);
printf("Computing Vector average time...");
fflush(stdout);
@@ -122,6 +177,10 @@ int main(void) {
fflush(stdout);
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...");
fflush(stdout);
printf("average time: %lld ms\n", benchmark(test_string, 1e5, 30));

View File

@@ -33,17 +33,18 @@ and the boolean `is_negative` variable denotes its sign.
The `BigInt` data structure supports the following methods:
- `bigint_result_t bigint_from_int(value)`: create a big integer from a primitive `int` type;
- `bigint_result_t bigint_from_string(string_num)`: create a big integer from a C string;
- `bigint_result_t bigint_to_string(number)`: convert a big integer to a C string;
- `bigint_result_t bigint_clone(number)`: clone a big integer;
- `bigint_result_t bigint_compare(x, y)`: compare two big integers, returning either `-1`, `0` or `1` if the first is less than, equal than or greater than the second, respectively;
- `bigint_result_t bigint_add(x, y)`: add two big integers together in $\mathcal{O}(n)$;
- `bigint_result_t bigint_sub(x, y)`: subtract two big integers in $\mathcal{O}(n)$;
- `bigint_result_t bigint_prod(x, y)`: multiply two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$;
- `bigint_result_t bigint_divmod(x, y)`: divide two big integers using *long division* algorithm in $\mathcal{O}(n^2)$, returning both the quotient and the remainder;
- `bigint_result_t bigint_mod(x, y)`: computes modulo of two big integers using *long division* algorithm in $\mathcal{O}(n^2)$;
- `bigint_result_t bigint_destroy(number)`: delete the big number;
- `bigint_result_t bigint_from_int(value)`: creates a big integer from a primitive `int` type;
- `bigint_result_t bigint_from_string(string_num)`: creates a big integer from a C string;
- `bigint_result_t bigint_to_string(number)`: converts a big integer to a C string;
- `bigint_result_t bigint_clone(number)`: clones a big integer;
- `bigint_result_t bigint_compare(x, y)`: compares two big integers, returning either `-1`, `0` or `1` if the first is less than, equal than or greater than the second, respectively;
- `bigint_result_t bigint_add(x, y)`: adds two big integers together in $\mathcal{O}(n)$;
- `bigint_result_t bigint_sub(x, y)`: subtracts two big integers in $\mathcal{O}(n)$;
- `bigint_result_t bigint_prod(x, y)`: multiplies two big integers using Karatsuba's algorithm in $\mathcal{O}(n^{1.585})$;
- `bigint_result_t bigint_divmod(x, y)`: divides two big integers using _Knuth's Algorithm D_ in $\mathcal{O}(n \times m)$ where $n$ and $m$ are the number of base-10^9
parts/limbs in the divisor and the quotient, respectively. This method returns both the quotient and the remainder;
- `bigint_result_t bigint_mod(x, y)`: calls `bigint_divmod`, discards the quotient and yields the remainder;
- `bigint_result_t bigint_destroy(number)`: deletes the big number;
- `bigint_result_t bigint_printf(format, ...)`: `printf` wrapper that introduces the `%B` placeholder to print big numbers. It supports variadic parameters.
As you can see from the previous function signatures, methods that operate on the
@@ -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`
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.
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
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`;
- `string_num`: result of `bigint_to_string`.
> [!IMPORTANT]
> Currently, the division implementation employs a quadratic-time algorithm derived from the conventional _"grade school"_ long-division method.
> This approach performs adequately for integers of modest size (up to approximately 200 digits) but becomes highly inefficient when handling
> substantially larger integers (~1500 digits).
>
> Improving the efficiency of this algorithm would require further research into advanced
> numerical algorithms, which is something that I currently not inclined to pursue.

View File

@@ -37,12 +37,12 @@ free them before removing the keys or destroying the map.
The `Map` data structure supports the following methods:
- `map_result_t map_new()`: initialize a new map;
- `map_result_t map_add(map, key, value)`: add a `(key, value)` pair to the map;
- `map_result_t map_get(map, key)`: retrieve a values indexed by `key` if it exists;
- `map_result_t map_remove(map, key)`: remove a key from the map if it exists;
- `map_result_t map_clear(map)`: reset the map state;
- `map_result_t map_destroy(map)`: delete the map;
- `map_result_t map_new()`: initializes a new map;
- `map_result_t map_add(map, key, value)`: adds a `(key, value)` pair to the map;
- `map_result_t map_get(map, key)`: retrieves a values indexed by `key` if it exists;
- `map_result_t map_remove(map, key)`: removes a key from the map if it exists;
- `map_result_t map_clear(map)`: resets the map state;
- `map_result_t map_destroy(map)`: deletes the map;
- `size_t map_size(map)`: returns map size (i.e., the number of elements);
- `size_t map_capacity(map)`: returns map capacity (i.e., map total size).
@@ -72,5 +72,4 @@ typedef struct {
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
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
on the first part of the README.
the returned value from the sum data type.

View File

@@ -1,8 +1,7 @@
# String Technical Details
In this document you can find a quick overview of the technical
aspects (internal design, memory layout, etc.) of the `String` data structure.
In this document you can find a quick overview of the technical 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.
Internally, this data structure is represented by the following layout:
@@ -15,22 +14,14 @@ typedef struct {
} string_t;
```
where the `data` variable represents the actual string (represented as a pointer to `char`),
the `byte_size` variable indicates the actual size (in bytes) of the string, the
`byte_capacity` variable represents the total number of allocated memory (in bytes) and the
`char_count` variable represent the number of logical characters, that is the number of
symbols.
where the `data` field represent the actual string, `byte_size`
indicates the actual size (in bytes), `byte_capacity` represents the total number
of allocated memory (in bytes) and `char_count` represents the number of symbols.
As mentioned earlier, this library provides partial UTF-8 support. It is able to recognize
UTF-8 byte sequences as individual Unicode code points, which allows it to correctly distinguish
between byte length and character count. It fully supports Unicode symbols and emojis, while
remaining backward compatible with ASCII strings.
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.
As mentioned earlier, this data type provides partial UTF-8 support. It is able
to recognize UTF-8 byte sequences as individual Unicode code points and has
full support for Unicode symbols such as emojis. However, it does not support
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.
At the time being, `String` supports the following methods:
@@ -67,11 +58,11 @@ typedef struct {
string_status_t status;
uint8_t message[RESULT_MSG_SIZE];
union {
string_t *string; // For new, clone, slice, reverse, trim
char *symbol; // For get_at
int64_t idx; // For contains
bool is_equ; // For comparison
struct { // For split
string_t *string;
char *symbol;
int64_t idx;
bool is_equ;
struct {
string_t **strings;
size_t count;
} 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`
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.
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.
Each of them has an unique scope as described below:

View File

@@ -25,19 +25,19 @@ deletion.
At the time being, `Vector` supports the following methods:
- `vector_result_t vector_new(size, data_size)`: create a new vector;
- `vector_result_t vector_push(vector, value)`: add a new value to the vector;
- `vector_result_t vector_set(vector, index, value)`: update the value of a given index if it exists;
- `vector_result_t vector_get(vector, index)`: return the value indexed by `index` if it exists;
- `vector_result_t vector_sort(vector, cmp)`: sort vector using `cmp` function;
- `vector_result_t vector_pop(vector)`: pop last element from the vector following the LIFO policy;
- `vector_result_t vector_map(vector, callback, env)`: apply `callback` function to vector (in-place);
- `vector_result_t vector_filter(vector, callback, env)`: filter vector using `callback` (in-place);
- `vector_result_t vector_reduce(vector, accumulator, callback, env)`: fold/reduce vector using `callback`;
- `vector_result_t vector_clear(vector)`: logically reset the vector. That is, new pushes will overwrite the memory;
- `vector_result_t vector_destroy(vector)`: delete the vector;
- `size_t vector_size(vector)`: return vector size (i.e., the number of elements);
- `size_t vector_capacity(vector)`: return vector capacity (i.e., vector total size).
- `vector_result_t vector_new(size, data_size)`: creates a new vector;
- `vector_result_t vector_push(vector, value)`: adds a new value to the vector;
- `vector_result_t vector_set(vector, index, value)`: updates the value of a given index if it exists;
- `vector_result_t vector_get(vector, index)`: returns the value indexed by `index` if it exists;
- `vector_result_t vector_sort(vector, cmp)`: sorts vector using `cmp` function;
- `vector_result_t vector_pop(vector)`: pops last element from the vector following the LIFO policy;
- `vector_result_t vector_map(vector, callback, env)`: applies `callback` function to vector (in-place);
- `vector_result_t vector_filter(vector, callback, env)`: filters vector using `callback` (in-place);
- `vector_result_t vector_reduce(vector, accumulator, callback, env)`: folds/reduces vector using `callback`;
- `vector_result_t vector_clear(vector)`: resets the vector logically. That is, new pushes will overwrite the memory;
- `vector_result_t vector_destroy(vector)`: deletes the vector;
- `size_t vector_size(vector)`: returns vector size (i.e., the number of elements);
- `size_t vector_capacity(vector)`: returns vector capacity (i.e., vector total size).
As you can see from the previous function signatures, most methods that operate
on the `Vector` data type return a custom type called `vector_result_t` which is
@@ -65,8 +65,7 @@ typedef struct {
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 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
ignore the return value (if you're brave enough :D) as illustrated on the first part of the README.
move on with the rest of the program or read the returned value from the sum data type.
## Functional methods
`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
View 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)

View 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
View 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
View 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
View 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
View 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;
}

View 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
View 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);
}

View File

@@ -9,6 +9,10 @@
(result).message[RESULT_MSG_SIZE - 1] = '\0'; \
} while (0)
#define REMOVE(ptr) \
free(ptr); \
ptr = NULL
#define IS_DIGIT(c) ((c) >= '0') && ((c) <= '9')
#include <stdio.h>
@@ -19,7 +23,6 @@
#include "bigint.h"
#include "vector.h"
// Internal methods
/**
* bigint_trim_zeros
* @number: a non-null big integer
@@ -842,30 +845,32 @@ cleanup: // Destroy intermediate allocations on error
}
/**
* bigint_dev
* @x: a valid non-null big integer (dividend)
* @y: a valid non-null big integer (divisor)
* bigint_div
* @x: a non-null big integer acting as a dividend
* @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) {
bigint_result_t result = {0};
bigint_result_t tmp_res = {0};
bigint_t *quotient = NULL;
bigint_t *remainder = NULL;
bigint_t *abs_y = NULL;
long long *u = NULL, *v = NULL, *q = NULL;
if (x == NULL || y == NULL) {
result.status = BIGINT_ERR_INVALID;
SET_MSG(result, "Invalid big numbers");
SET_MSG(result, "Invalid big integers");
return result;
}
// Check for division by zero
const size_t y_size = vector_size(y->digits);
if (y_size == 0) {
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) {
vector_result_t y_val_res = vector_get(y->digits, 0);
if (y_val_res.status != VECTOR_OK) {
vector_result_t y0_res = vector_get(y->digits, 0);
if (y0_res.status != VECTOR_OK) {
result.status = BIGINT_ERR_INVALID;
COPY_MSG(result, y_val_res.message);
COPY_MSG(result, y0_res.message);
return result;
}
int *y_val = (int*)y_val_res.value.element;
if (*y_val == 0) {
int *y0 = (int *)y0_res.value.element;
if (*y0 == 0) {
result.status = BIGINT_ERR_DIV_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);
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) {
bigint_destroy(quotient);
bigint_destroy(remainder);
return tmp_res;
}
abs_y = tmp_res.value.number;
abs_y->is_negative = false;
if (tmp_res.value.compare_status < 0) {
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 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--) {
// Shift remainder left by one base digit (multiplication by BASE)
tmp_res = bigint_shift_left(remainder, 1);
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) {
vector_result_t xidx_res = vector_get(x->digits, idx);
if (xidx_res.status != VECTOR_OK) {
result.status = BIGINT_ERR_INVALID;
COPY_MSG(result, digit_res.message);
COPY_MSG(result, xidx_res.message);
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);
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);
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);
@@ -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);
for (size_t idx = 0; idx < q_size / 2; idx++) {
vector_result_t left_res = vector_get(quotient->digits, idx);
vector_result_t right_res = vector_get(quotient->digits, q_size - 1 - idx);
for (size_t lo = 0, hi = q_size - 1; lo < hi; hi--) {
vector_result_t lr = vector_get(quotient->digits, lo);
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;
SET_MSG(result, "Failed to access vector elements");
SET_MSG(result, "Failed to reverse quotient digits");
goto cleanup;
}
int *left = (int*)left_res.value.element;
int *right = (int*)right_res.value.element;
int temp = *left;
vector_set(quotient->digits, idx, right);
vector_set(quotient->digits, q_size - 1 - idx, &temp);
int lower_val = *(int *)lr.value.element;
int higher_val = *(int *)hr.value.element;
vector_set(quotient->digits, lo, &higher_val);
vector_set(quotient->digits, hi, &lower_val);
}
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);
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
result.value.number = quotient;
result.status = BIGINT_OK;
SET_MSG(result, "Division between big integers was successful");
bigint_destroy(remainder);
bigint_destroy(abs_y);
return result;
}
/* 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.status = BIGINT_OK;
@@ -1024,20 +1141,20 @@ static bigint_result_t bigint_div(const bigint_t *x, const bigint_t *y) {
return result;
cleanup:
free(u); free(v); free(q);
if (quotient) { bigint_destroy(quotient); }
if (remainder) { bigint_destroy(remainder); }
if (abs_y) { bigint_destroy(abs_y); }
return result;
}
/**
* bigint_from_int
* @value: an integer value
*
* 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 result = {0};
@@ -1555,14 +1672,14 @@ bigint_result_t bigint_prod(const bigint_t *x, const bigint_t *y) {
return result;
}
/**
* bigint_divmod
* @x: 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
*/
@@ -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 tmp_res = {0};
// Intermediate results
bigint_t *quotient = NULL;
bigint_t *y_times_q = NULL;
bigint_t *remainder = NULL;
@@ -1582,11 +1698,10 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
return result;
}
// Check for division by zero
const size_t y_size = vector_size(y->digits);
if (y_size == 0) {
result.status = BIGINT_ERR_DIV_BY_ZERO;
SET_MSG(result, "Division by zero");
SET_MSG(result, "Cannot divide by zero");
return result;
}
@@ -1600,16 +1715,16 @@ bigint_result_t bigint_divmod(const bigint_t *x, const bigint_t *y) {
return result;
}
int *y_val = (int*)y_val_res.value.element;
int *y_val = (int *)y_val_res.value.element;
if (*y_val == 0) {
result.status = BIGINT_ERR_DIV_BY_ZERO;
SET_MSG(result, "Division by zero");
SET_MSG(result, "Cannot divide by zero");
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);
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.remainder = remainder;
result.status = BIGINT_OK;
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; }
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);
if (tmp_res.status != BIGINT_OK) { result = tmp_res; goto cleanup; }
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; }
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);
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.remainder = remainder;
result.status = BIGINT_OK;
@@ -1753,11 +1883,15 @@ bigint_result_t bigint_printf(const char *format, ...) {
// Process string char by char
for (const char *p = format; *p != '\0'; p++) {
if (*p == '%' && *(p + 1) == 'B') {
// Process a big number
if (*p == '%' && *(p + 1) != '%') {
p++;
const char placeholder = *p;
switch (placeholder) {
case 'B': {
bigint_t *num = va_arg(args, bigint_t*);
if (num == NULL) {
printf("<invalid string>");
for (const char *s = "<invalid big integer>"; *s != '\0'; s++) { putchar(*s); }
} else {
bigint_result_t num_str_res = bigint_to_string(num);
if (num_str_res.status != BIGINT_OK) {
@@ -1765,28 +1899,21 @@ bigint_result_t bigint_printf(const char *format, ...) {
return num_str_res;
}
char* const number_str = num_str_res.value.string_num;
printf("%s", number_str);
char *number_str = num_str_res.value.string_num;
for (const char *s = number_str; *s != '\0'; s++) { putchar(*s); }
free(number_str);
}
p++;
} else if (*p == '%' && *(p + 1) != '%') {
// Handle common printf placeholders
p++;
char placeholder = *p;
switch (placeholder) {
break;
}
case 'd':
case 'i': {
int val = va_arg(args, int);
printf("%d", val);
break;
}
case 'u': {
unsigned int val = va_arg(args, unsigned int);
printf("%u", val);
break;
}
case 'l': {
@@ -1806,13 +1933,17 @@ bigint_result_t bigint_printf(const char *format, ...) {
break;
}
case 's': {
char *val = va_arg(args, char*);
printf("%s", val ? val : "<invalid string>");
char* val = va_arg(args, char*);
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;
}
case 'c': {
int val = va_arg(args, int);
printf("%c", val);
putchar(val);
break;
}
case 'f': {
@@ -1821,7 +1952,7 @@ bigint_result_t bigint_printf(const char *format, ...) {
break;
}
case 'p': {
void *val = va_arg(args, void*);
void* const val = va_arg(args, void*);
printf("%p", val);
break;
}

View File

@@ -10,8 +10,6 @@
#include "map.h"
// Internal methods
/**
* hash_key
* @key: The input string for the hash function

View File

@@ -17,7 +17,7 @@ static inline bool is_space(unsigned char c) {
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) {
if ((byte & 0x80) == 0x00) return 1;
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;
}
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) {
return false;
}
@@ -55,7 +55,7 @@ static bool utf8_is_char_valid(const char *utf8_char, int *out_len) {
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) {
size_t b_size = 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) {
if (codepoint <= 0x7F) {
out[0] = (char)codepoint;
return 1;
}
@@ -161,7 +162,7 @@ string_result_t string_new(const char *c_str) {
if (c_str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid null input string");
SET_MSG(result, "Invalid input string");
return result;
}
@@ -191,14 +192,15 @@ string_result_t string_new(const char *c_str) {
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_capacity = b_size + 1;
str->char_count = c_count;
result.status = STRING_OK;
SET_MSG(result, "String successfully created");
result.value.string = str;
SET_MSG(result, "String successfully created");
return result;
}
@@ -238,7 +240,8 @@ string_result_t string_clone(const string_t *str) {
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_capacity = str->byte_size + 1;
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);
free(buf);
SET_MSG(result, "String successfully concatenated");
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]);
}
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));
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);
if (slice->data == NULL) {
free(slice);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
@@ -414,7 +420,7 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
*
* Returns a string_result_t containing the comparison result
*/
string_result_t string_eq(const string_t *x, const string_t *y, bool case_sensitive) {
string_result_t string_eq(const string_t *x, const string_t *y, bool case_sensitive) {
string_result_t result = {
.status = STRING_OK,
.value.is_equ = false
@@ -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) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Strings differ in length");
result.value.is_equ = false;
result.status = STRING_OK;
SET_MSG(result, "Comparison completed successfully");
return result;
}
@@ -448,12 +455,14 @@ string_result_t string_slice(const string_t *str, size_t start, size_t end) {
if (c1 != c2) {
result.value.is_equ = false;
return result;
}
p1 += l1;
p2 += l2;
}
result.value.is_equ = (*p1 == *p2);
}
@@ -634,7 +643,6 @@ string_result_t string_to_lower(const string_t *str) {
result = string_new(buf);
free(buf);
result.status = STRING_OK;
SET_MSG(result, "String successfully converted to lowercase");
return result;
@@ -679,7 +687,6 @@ string_result_t string_to_upper(const string_t *str) {
result = string_new(buf);
free(buf);
result.status = STRING_OK;
SET_MSG(result, "String successfully converted to uppercase");
return result;
@@ -713,6 +720,7 @@ string_result_t string_reverse(const string_t *str) {
const char **pos = malloc(str->char_count * sizeof(char *));
if (pos == NULL) {
free(buf);
result.status = STRING_ERR_ALLOCATE;
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);
if (tmp == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocated memory");
SET_MSG(result, "Cannot allocate memory");
goto cleanup;
}
@@ -866,10 +874,9 @@ string_result_t string_split(const string_t *str, const char *delim) {
SET_MSG(result, "String successfully split");
return result;
cleanup:
for (size_t j = 0; j < idx; j++) {
string_destroy(string_array[j]);
}
for (size_t j = 0; j < idx; j++) { string_destroy(string_array[j]); }
free(string_array);
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
* @count: the number of elements
*

View File

@@ -17,7 +17,7 @@ typedef enum {
typedef struct {
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 char_count; // Number of symbols
} string_t;
@@ -38,7 +38,7 @@ typedef struct {
} string_result_t;
#ifdef __cplusplus
extern "C" {
#extern "C" {
#endif
// Public APIs

View File

@@ -9,7 +9,6 @@
#include "vector.h"
// Internal methods
/**
* vector_resize
* @vector: a non-null vector

View File

@@ -213,8 +213,8 @@ void test_bigint_prod_neg(void) {
bigint_destroy(prod.value.number);
}
// Test division between big numbers
void test_bigint_div(void) {
// Test division between big numbers where divisor is a single limb big number
void test_bigint_div_single_limb(void) {
bigint_result_t x = bigint_from_int(100);
bigint_result_t y = bigint_from_int(2);
@@ -229,11 +229,33 @@ void test_bigint_div(void) {
bigint_eq(quotient, "50");
bigint_eq(remainder, "0");
bigint_destroy(quotient);
bigint_destroy(remainder);
bigint_destroy(quotient); bigint_destroy(remainder);
bigint_destroy(x.value.number); bigint_destroy(y.value.number);
}
bigint_destroy(x.value.number);
bigint_destroy(y.value.number);
// Test division between big numbers using Knuth's algorithm
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
@@ -262,7 +284,7 @@ void test_bigint_div_dividend(void) {
// Test division between big numbers with negative divisor
// 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 y = bigint_from_int(-4);
@@ -405,9 +427,10 @@ int main(void) {
TEST(bigint_very_large_prod);
TEST(bigint_prod_mixed);
TEST(bigint_prod_neg);
TEST(bigint_div);
TEST(bigint_div_single_limb);
TEST(bigint_div_knuth);
TEST(bigint_div_dividend);
TEST(bigint_div_divisor);
TEST(bigint_div_neg_divisor);
TEST(bigint_div_neg);
TEST(bigint_div_by_zero);
TEST(bigint_clone);

View File

@@ -1,5 +1,5 @@
/*
* Unit tests for String data type
* Unit tests for String data type
*/
#define TEST(NAME) do { \
@@ -69,7 +69,7 @@ void test_string_concat(void) {
string_destroy(res.value.string);
}
// Test if string contains a substring
// Test if string contains substring
void test_string_contains(void) {
string_t *haystack = string_new("Hello 🌍 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);
// 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);
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
// UTF-8 + ASCII slice
string_result_t res3 = string_slice(str2, 0, 2);
assert(res3.status == STRING_OK);
assert(strcmp(res3.value.string->data, "AB😆") == 0);
assert(strcmp(res3.value.string->data, "AB😀") == 0);
// Invalid bounds
string_result_t res4 = string_slice(str1, 5, 2);
@@ -165,12 +165,12 @@ void test_string_reverse_utf8(void) {
// Test string get_at
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);
assert(res1.status == STRING_OK);
assert(strcmp((char*)res1.value.symbol, "😆") == 0);
assert(strcmp((char*)res1.value.symbol, "😀") == 0);
free(res1.value.symbol);
// 🌍 is at index 3
@@ -196,12 +196,12 @@ void test_string_get_at_overflow(void) {
void test_string_set_at(void) {
string_t *str = string_new("ABC").value.string;
// Replace 'B' with emoji
string_result_t res = string_set_at(str, 1, "😆");
// Replace 'B' with an emoji
string_result_t res = string_set_at(str, 1, "😀");
string_t *altered = res.value.string;
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(altered->byte_size == 6); // that is: A (1B) + emoji (4B) + C (1B)
@@ -303,7 +303,7 @@ void test_string_destroy(void) {
}
int main(void) {
printf("=== Running String unit tests ===\n\n");
printf("=== Running String unit tests ===\n\n");
TEST(string_new);
TEST(string_new_empty);

711
usage.c
View File

@@ -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;
}