9 Commits

Author SHA1 Message Date
36616c30b5 Added String data type and its features 2025-12-23 15:48:20 +01:00
e8563e5043 Added documentation about closures 2025-12-23 12:24:49 +01:00
feb136d393 Added functional methods (map, filter and reduce) 2025-12-22 12:23:35 +01:00
7cc2615f8b Fixed bug about bigint product, updated unit tests and sample usage. 2025-12-17 17:04:16 +01:00
65358fc766 Added benchmark program 2025-11-25 16:46:20 +01:00
5aa15f2276 Fixed multiple bugs in hashmap implementation.
1. Fixed infinite loop issue that occurred when map was full of
   tombstones;
2. Fixed bug related to tombstone count decrement when adding a new
   key on a deleted slot;
3. Added proper map resize when keys are added or removed;
4. Fixed inconsistent error messages;
5. Added proper NULL check on map_remove method.
2025-11-25 14:16:32 +01:00
b885d93b6f Merge pull request #1 from ceticamarco/bignum_experimental
Adding BigInt support
2025-11-18 11:46:42 +01:00
86f2fb20dd Fixed bug in vector unit test 2025-11-17 09:28:30 +01:00
9e52b7bca4 Updated documentation 2025-11-11 13:54:12 +01:00
20 changed files with 1074 additions and 244 deletions

View File

@@ -16,4 +16,8 @@ jobs:
- name: Run unit tests
run: |
./test_vector && ./test_map && ./test_bigint
./test_vector && ./test_map && ./test_bigint
- name: Run benchmarks
run: |
./benchmark_datum

View File

@@ -13,4 +13,8 @@ jobs:
- name: Run unit tests
run: |
./test_vector && ./test_map && ./test_bigint
./test_vector && ./test_map && ./test_bigint
- name: Run benchmarks
run: |
./benchmark_datum

View File

@@ -3,21 +3,29 @@ CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \
-fsanitize=address -fsanitize=undefined -fstack-clash-protection \
-Wwrite-strings -g -std=c99
BENCH_FLAGS = -Wall -Wextra -Werror -O3
SRC_DIR = src
BENCH_SRC = benchmark
OBJ_DIR = obj
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
BENCH_TARGET = benchmark_datum
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o
PROG_OBJS = $(OBJ_DIR)/usage.o
.PHONY: all clean
all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET)
all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(BENCH_TARGET)
bench: $(BENCH_TARGET)
$(TARGET): $(PROG_OBJS) $(LIB_OBJS)
$(CC) $(CFLAGS) -o $@ $^
@@ -43,5 +51,18 @@ $(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
$(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
$(CC) $(BENCH_FLAGS) -o $@ $^
$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR)
$(CC) $(BENCH_FLAGS) -c -o $@ $<
$(BENCH_OBJ_DIR)/bench.o: $(BENCH_SRC)/benchmark.c | $(BENCH_OBJ_DIR)
$(CC) $(BENCH_FLAGS) -c -o $@ $<
$(BENCH_OBJ_DIR):
mkdir -p $(BENCH_OBJ_DIR)
clean:
rm -rf $(OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET)
rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(BENCH_TARGET)

View File

@@ -24,24 +24,37 @@ At its simplest, you can use this library as follows:
/*
* Compile with: gcc main.c src/vector.c
* Output: First element: 5
* Head of vector: 6, size is now: 1
* Output: First element: 1
* Head of vector: 16, size is now: 1
*/
// Callback functions
vector_order_t cmp_int_asc(const void *x, const void *y);
void square(void *element, void *env);
int is_even(const void *element, void *env);
void add(void *accumulator, const void *element, void *env);
int main(void) {
// Create an integer vector of initial capacity equal to 5
vector_t *vec = vector_new(5, sizeof(int)).value.vector;
// Add two numbers
int val = 5;
vector_push(vec, &val);
// Equivalent as above
vector_push(vec, &(int){6});
// Add some elements
vector_push(vec, &(int){1}); // Equivalent as below
int nums[] = {5, 2, 4, 3};
for (int idx = 0; idx < 4; idx++) { vector_push(vec, &nums[idx]); }
// Sort array in ascending order: [1, 2, 3, 4, 5]
vector_sort(vec, cmp_int_asc);
// Print 1st element
const int first = *(int*)vector_get(vec, 0).value.element;
printf("First element: %d\n", first);
int sum = 0;
vector_map(vec, square, NULL); // Square elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25]
vector_filter(vec, is_even, NULL); // Filter even elements: [1, 4, 9, 16, 25] -> [4, 16]
vector_reduce(vec, &sum, add, NULL); // Sum elements: [4, 16] -> 20
// Pop second element using LIFO policy
const int head = *(int*)vector_pop(vec).value.element;
printf("Head of vector: %d, size is now: %zu\n", head, vector_size(vec));
@@ -51,6 +64,34 @@ int main(void) {
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;
}
void square(void *element, void *env) {
(void)(env);
int *value = (int*)element;
*value = (*value) * (*value);
}
int is_even(const void *element, void *env) {
(void)(env);
int value = *(int*)element;
return (value % 2) == 0;
}
void add(void *accumulator, const void *element, void *env) {
(void)(env);
*(int*)accumulator += *(int*)element;
}
```
### `Map` usage
@@ -133,7 +174,14 @@ and a sample usage for every available method. To run it, first issue the follow
$ make clean all
```
This will compile the library as well as the `usage.c` file and the unit tests. After that, you can run it by typing `./usage`.
This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`.
> [!NOTE]
> This project is primarily developed for learning purposes and was not created with industrial
> or production use in mind. As such, it is not intended to compete with any existing C library.
> In particular, the big number implementation does not aim to match the design, the maturity and
> the performance of established solutions such as the
> GNU Multiple Precision Arithmetic Library (GMP).
## Documentation
For additional details about this library (internal design, memory
@@ -149,6 +197,16 @@ $ ./test_map
$ ./test_bigint
```
## Benchmark
Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector` and the `Map` data structures. You can run it by issuing the following command:
```sh
$ ./benchmark_datum
Computing Vector average time...average time: 18 ms
Computing Map average time...average time: 31 ms
```
## License
This library is released under the GPLv3 license. You can find a copy of the license with this repository or by visiting
[the following link](https://choosealicense.com/licenses/gpl-3.0/).

92
benchmark/benchmark.c Normal file
View File

@@ -0,0 +1,92 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdint.h>
#include "../src/vector.h"
#include "../src/map.h"
typedef void (*test_fn_t)(size_t iterations);
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, &idx);
}
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;
}
// Another trick to prevent compiler optimization
if (sum == 0xB00B5) {
printf("sum = %llu\n", (unsigned long long)sum);
}
vector_destroy(vec);
}
void test_map(size_t iterations) {
map_t *map = map_new().value.map;
char key[64];
for (size_t idx = 0; idx < iterations; idx++) {
snprintf(key, sizeof(key), "key_%zu", idx);
int *value = malloc(sizeof(int));
*value = (int)idx;
map_add(map, key, (void*)value);
}
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);
const int *val = (const int*)map_get(map, key).value.element;
sum += *val;
}
// Cleanup values
for (size_t idx = 0; idx < map->capacity; idx++) {
if (map->elements[idx].state == ENTRY_OCCUPIED) {
int *val = (int*)map->elements[idx].value;
free(val);
}
}
map_destroy(map);
}
long long benchmark(test_fn_t fun, size_t iterations, size_t runs) {
long long total = 0;
for (size_t idx = 0; idx < runs; idx++) {
clock_t start = clock();
fun(iterations);
clock_t end = clock();
total += (long long)((end - start) * 1000 / CLOCKS_PER_SEC);
}
return total / runs;
}
int main(void) {
// Do a warmup run
test_vector(1000);
test_map(1000);
printf("Computing Vector average time...");
fflush(stdout);
printf("average time: %lld ms\n", benchmark(test_vector, 1e6, 30));
printf("Computing Map average time...");
fflush(stdout);
printf("average time: %lld ms\n", benchmark(test_map, 1e5, 30));
return 0;
}

View File

@@ -7,5 +7,4 @@ At the time being, this documentation includes the following pages:
- [vector.md](vector.md): vector documentation;
- [map.md](map.md): map documentation;
- [sort.md](sort.md): how to use the `vector_sort` method.
- [bigint.md](bigint.md): bigint documentation.

View File

@@ -90,3 +90,12 @@ 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

@@ -54,6 +54,7 @@ defined as follows:
typedef enum {
MAP_OK = 0x0,
MAP_ERR_ALLOCATE,
MAP_ERR_OVERFLOW,
MAP_ERR_INVALID,
MAP_ERR_NOT_FOUND
} map_status_t;

View File

@@ -1,177 +0,0 @@
# Sorting
As indicated in the [its documentation](/docs/vector.md), the `Vector` data type
provides an efficient in-place sorting function called `vector_sort` that uses
a builtin implementation of the [Quicksort algorithm](https://en.wikipedia.org/wiki/Quicksort). This method requires an user-defined comparison procedure which allows the
caller to customize the sorting behavior. The comparison procedure must adhere to the
following specification:
1. Must return `vector_order_t`, which is defined as follows:
```c
typedef enum {
VECTOR_ORDER_LT = 0x0, // First element should come before the second
VECTOR_ORDER_EQ, // The two elements are equivalent
VECTOR_ORDER_GT // First element should come after the second
} vector_order_t;
```
and indicates the ordering relationship between any two elements.
2. Must accept two `const void*` parameters representing two elements to compare;
3. Must be self-contained and handle all its own resources.
Let's look at some examples. For instance, let's say that we want to sort an array
of integers in ascending and descending order:
```c
#include <stdio.h>
#include "src/vector.h"
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);
}
/*
* Compile with: gcc main.c src/vector.c
* Output: Before sorting: -8 20 -10 125 34 9
* After sorting (ascending order): -10 -8 9 20 34 125
* After sorting (descending order): 125 34 20 9 -8 -10
*/
int main(void) {
vector_t *v = vector_new(5, sizeof(int)).value.vector;
int values[] = { -8, 20, -10, 125, 34, 9 };
for (size_t idx = 0; idx < 6; idx++) {
vector_push(v, &values[idx]);
}
const size_t sz = vector_size(v);
// Print unsorted array
printf("Before sorting: ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
// Sort array in ascending order
vector_sort(v, cmp_int_asc);
// Print sorted array
printf("\nAfter sorting (ascending order): ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
// Sort array in descending order
vector_sort(v, cmp_int_desc);
// Print sorted array
printf("\nAfter sorting (descending order): ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
printf("\n");
vector_destroy(v);
return 0;
}
```
Obviously, you can use the `vector_sort` method on custom data type as well.
For instance, let's suppose that you have a structure representing the employees of
a company and you wish to sort them based on their age and on their name (lexicographic sort):
```c
#include <stdio.h>
#include <string.h>
#include "src/vector.h"
typedef struct {
char name[256];
int age;
} Employee;
vector_order_t cmp_person_by_age(const void *x, const void *y) {
const Employee *x_person = (const Employee*)x;
const Employee *y_person = (const Employee*)y;
if (x_person->age < y_person->age) return VECTOR_ORDER_LT;
if (x_person->age > y_person->age) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
vector_order_t cmp_person_by_name(const void *x, const void *y) {
const Employee *x_person = (const Employee*)x;
const Employee *y_person = (const Employee*)y;
const int result = strcmp(x_person->name, y_person->name);
if(result < 0) return VECTOR_ORDER_LT;
if(result > 0) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
/*
* Compile with: gcc main.c src/vector.c
* Output: Sort by age:
* Name: Marco, Age: 25
* Name: Alice, Age: 28
* Name: Bob, Age: 45
*
* Sort by name:
* Name: Alice, Age: 28
* Name: Bob, Age: 45
* Name: Marco, Age: 25
*/
int main(void) {
vector_t *employees = vector_new(5, sizeof(Employee)).value.vector;
Employee e1 = { .name = "Bob", .age = 45 };
Employee e2 = { .name = "Alice", .age = 28 };
Employee e3 = { .name = "Marco", .age = 25 };
vector_push(employees, &e1);
vector_push(employees, &e2);
vector_push(employees, &e3);
// Sort array by age
vector_sort(employees, cmp_person_by_age);
const size_t sz = vector_size(employees);
// Print sorted array
printf("Sort by age:\n");
for (size_t idx = 0; idx < sz; idx++) {
Employee *p = (Employee*)vector_get(employees, idx).value.element;
printf("Name: %s, Age: %d\n", p->name, p->age);
}
// Sort array by name
vector_sort(employees, cmp_person_by_name);
// Print sorted array
printf("\nSort by name:\n");
for (size_t idx = 0; idx < sz; idx++) {
Employee *p = (Employee*)vector_get(employees, idx).value.element;
printf("Name: %s, Age: %d\n", p->name, p->age);
}
vector_destroy(employees);
return 0;
}
```

View File

@@ -31,8 +31,10 @@ At the time being, `Vector` supports the following methods:
- `vector_result_t vector_get(vector, index)`: return the value indexed by `index` if it exists;
- `map_result_t vector_sort(map, cmp)`: sort array using `cmp` function;
- `vector_result_t vector_pop(vector)`: pop last element from the vector following the LIFO policy;
- `vector_result_t vector_clear(vector)`: logically reset the vector. That is, new pushes
will overwrite the memory;
- `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).
@@ -66,5 +68,199 @@ field. If the operation was successful (that is, `status == VECTOR_OK`), you can
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 in the first part of the README.
The documentation for the `vector_sort(map, cmp)` method can be found
in [the following document](/docs/sort.md).
## Functional methods
`Vector` provides three functional methods called `map`, `filter` and `reduce` which allow the caller to apply a computation to the vector,
filter the vector according to a function and fold the vector to a single value according to a custom function, respectively.
The caller is responsible to define a custom `callback` function that satisfy the following constraints:
```c
typedef void (*map_callback_fn)(void *element, void *env);
typedef int (*vector_filter_fn)(const void *element, void *env);
typedef void (*vector_reduce_fn)(void *accumulator, const void *element, void *env);
```
In particular, you should be aware of the following design choices:
- The `vector_reduce` callback method requires the caller to initialize an _"accumulator"_ variable before calling this method;
- The `vector_filter` callback method is expected to return non-zero to keep the element and zero to filter it out.
- The `env` argument is an optional parameter to pass the external environment to the callback function. It is used to mock the behavior of closures, where
the lexical environment is captured when the closure is created.
## Sorting
As indicated in the [its documentation](/docs/vector.md), the `Vector` data type
provides an efficient in-place sorting function called `vector_sort` that uses
a builtin implementation of the [Quicksort algorithm](https://en.wikipedia.org/wiki/Quicksort). This method requires an user-defined comparison procedure which allows the
caller to customize the sorting behavior. The comparison procedure must adhere to the
following specification:
1. Must return `vector_order_t`, which is defined as follows:
```c
typedef enum {
VECTOR_ORDER_LT = 0x0, // First element should come before the second
VECTOR_ORDER_EQ, // The two elements are equivalent
VECTOR_ORDER_GT // First element should come after the second
} vector_order_t;
```
and indicates the ordering relationship between any two elements.
2. Must accept two `const void*` parameters representing two elements to compare;
3. Must be self-contained and handle all its own resources.
Let's look at some examples. For instance, let's say that we want to sort an array
of integers in ascending and descending order:
```c
#include <stdio.h>
#include "src/vector.h"
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);
}
/*
* Compile with: gcc main.c src/vector.c
* Output: Before sorting: -8 20 -10 125 34 9
* After sorting (ascending order): -10 -8 9 20 34 125
* After sorting (descending order): 125 34 20 9 -8 -10
*/
int main(void) {
vector_t *v = vector_new(5, sizeof(int)).value.vector;
int values[] = { -8, 20, -10, 125, 34, 9 };
for (size_t idx = 0; idx < 6; idx++) {
vector_push(v, &values[idx]);
}
const size_t sz = vector_size(v);
// Print unsorted array
printf("Before sorting: ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
// Sort array in ascending order
vector_sort(v, cmp_int_asc);
// Print sorted array
printf("\nAfter sorting (ascending order): ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
// Sort array in descending order
vector_sort(v, cmp_int_desc);
// Print sorted array
printf("\nAfter sorting (descending order): ");
for (size_t idx = 0; idx < sz; idx++) {
printf("%d ", *(int*)vector_get(v, idx).value.element);
}
printf("\n");
vector_destroy(v);
return 0;
}
```
Obviously, you can use the `vector_sort` method on custom data type as well.
For instance, let's suppose that you have a structure representing the employees of
a company and you wish to sort them based on their age and on their name (lexicographic sort):
```c
#include <stdio.h>
#include <string.h>
#include "src/vector.h"
typedef struct {
char name[256];
int age;
} Employee;
vector_order_t cmp_person_by_age(const void *x, const void *y) {
const Employee *x_person = (const Employee*)x;
const Employee *y_person = (const Employee*)y;
if (x_person->age < y_person->age) return VECTOR_ORDER_LT;
if (x_person->age > y_person->age) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
vector_order_t cmp_person_by_name(const void *x, const void *y) {
const Employee *x_person = (const Employee*)x;
const Employee *y_person = (const Employee*)y;
const int result = strcmp(x_person->name, y_person->name);
if(result < 0) return VECTOR_ORDER_LT;
if(result > 0) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
/*
* Compile with: gcc main.c src/vector.c
* Output: Sort by age:
* Name: Marco, Age: 25
* Name: Alice, Age: 28
* Name: Bob, Age: 45
*
* Sort by name:
* Name: Alice, Age: 28
* Name: Bob, Age: 45
* Name: Marco, Age: 25
*/
int main(void) {
vector_t *employees = vector_new(5, sizeof(Employee)).value.vector;
Employee e1 = { .name = "Bob", .age = 45 };
Employee e2 = { .name = "Alice", .age = 28 };
Employee e3 = { .name = "Marco", .age = 25 };
vector_push(employees, &e1);
vector_push(employees, &e2);
vector_push(employees, &e3);
// Sort array by age
vector_sort(employees, cmp_person_by_age);
const size_t sz = vector_size(employees);
// Print sorted array
printf("Sort by age:\n");
for (size_t idx = 0; idx < sz; idx++) {
Employee *p = (Employee*)vector_get(employees, idx).value.element;
printf("Name: %s, Age: %d\n", p->name, p->age);
}
// Sort array by name
vector_sort(employees, cmp_person_by_name);
// Print sorted array
printf("\nSort by name:\n");
for (size_t idx = 0; idx < sz; idx++) {
Employee *p = (Employee*)vector_get(employees, idx).value.element;
printf("Name: %s, Age: %d\n", p->name, p->age);
}
vector_destroy(employees);
return 0;
}
```

View File

@@ -1413,9 +1413,11 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) {
const size_t x_size = vector_size(x->digits);
const size_t y_size = vector_size(y->digits);
const size_t min_size = x_size < y_size ? x_size : y_size;
const size_t max_size = x_size > y_size ? x_size : y_size;
// Base case using "grade school" quadratic algorithm
if (x_size <= 32 || y_size <= 32) {
if (min_size <= 32 || max_size / min_size > 2) {
return bigint_karatsuba_base(x, y);
}
@@ -1498,6 +1500,8 @@ bigint_result_t bigint_karatsuba(const bigint_t *x, const bigint_t *y) {
result.status = BIGINT_OK;
SET_MSG(result, "Product between big integers was successful");
return result;
cleanup: // Destroy intermediate allocations on error
if (x1) { bigint_destroy(x1); }
if (x0) { bigint_destroy(x0); }

156
src/map.c
View File

@@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "map.h"
@@ -74,24 +75,35 @@ map_result_t map_new(void) {
* @map: a non-null map
* @key: a string representing the key to find
*
* Finds next available slot for insertion
* Finds next available slot for insertion (empty or deleted)
* or the slot containing an existing key
*
* Returns the index of available slot
* Returns the index of available slot or SIZE_MAX otherwise
*/
size_t map_insert_index(const map_t *map, const char *key) {
const uint64_t key_digest = hash_key(key);
size_t idx = key_digest % map->capacity;
size_t delete_tracker = map->capacity; // Fallback index
while (map->elements[idx].state == ENTRY_OCCUPIED) {
if (strcmp(map->elements[idx].key, key) == 0) {
// In this case the key already exists, thus we replace it
return idx;
for (size_t probes = 0; probes < map->capacity; probes++) {
if (map->elements[idx].state == ENTRY_EMPTY) {
return (delete_tracker != map->capacity) ? delete_tracker : idx;
}
if (map->elements[idx].state == ENTRY_OCCUPIED) {
if (!strcmp(map->elements[idx].key, key)) {
return idx;
}
} else if (map->elements[idx].state == ENTRY_DELETED) {
if (delete_tracker == map->capacity) {
delete_tracker = idx;
}
}
idx = (idx + 1) % map->capacity;
}
return idx;
return SIZE_MAX;
}
/**
@@ -105,8 +117,17 @@ map_result_t map_resize(map_t *map) {
map_result_t result = {0};
const size_t old_capacity = map->capacity;
const size_t old_size = map->size;
const size_t old_tombstone = map->tombstone_count;
map_element_t *old_elements = map->elements;
if (map->capacity > SIZE_MAX / 2) {
result.status = MAP_ERR_OVERFLOW;
SET_MSG(result, "Capacity overflow on map resize");
return result;
}
map->capacity *= 2;
map->elements = calloc(map->capacity, sizeof(map_element_t));
if (map->elements == NULL) {
@@ -127,10 +148,21 @@ map_result_t map_resize(map_t *map) {
for (size_t idx = 0; idx < old_capacity; idx++) {
if (old_elements[idx].state == ENTRY_OCCUPIED) {
size_t new_idx = map_insert_index(map, old_elements[idx].key);
if (new_idx == SIZE_MAX) {
// if we can't find a free slot, restore previous state and fail
free(map->elements);
map->elements = old_elements;
map->capacity = old_capacity;
map->size = old_size;
map->tombstone_count = old_tombstone;
result.status = MAP_ERR_OVERFLOW;
SET_MSG(result, "Failed to rehash elements during resize");
return result;
}
map->elements[new_idx] = old_elements[idx];
map->size++;
} else if (old_elements[idx].state == ENTRY_DELETED) {
free(old_elements[idx].key);
}
}
@@ -172,7 +204,28 @@ map_result_t map_add(map_t *map, const char *key, void *value) {
}
// Find next available slot for insertion
const size_t idx = map_insert_index(map, key);
size_t idx = map_insert_index(map, key);
// if index is SIZE_MAX then the map is full
if (idx == SIZE_MAX) {
map_result_t resize_res = map_resize(map);
if (resize_res.status != MAP_OK) {
result.status = MAP_ERR_OVERFLOW;
SET_MSG(result, "The map is full and resize has failed");
return result;
}
idx = map_insert_index(map, key);
// This is very uncommon but still...
if (idx == SIZE_MAX) {
result.status = MAP_ERR_OVERFLOW;
SET_MSG(result, "The map is full after resize(!)");
return result;
}
}
// If slot is occupied, it means that the key already exists.
// Therefore we can update it
@@ -185,16 +238,23 @@ map_result_t map_add(map_t *map, const char *key, void *value) {
return result;
}
// Otherwise, the key doesn't exist. Therefore we need to allocate a new key
map->elements[idx].key = malloc(strlen(key) + 1);
if (map->elements[idx].key == NULL) {
// Allocate a new key
char *new_key = malloc(strlen(key) + 1);
if (new_key == NULL) {
result.status = MAP_ERR_ALLOCATE;
SET_MSG(result, "Failed to allocate memory for map key");
return result;
}
strcpy(map->elements[idx].key, key);
strcpy(new_key, key);
// If we're reusing a deleted slot, decrement the tombstone count
if (map->elements[idx].state == ENTRY_DELETED) {
if (map->tombstone_count > 0) { map->tombstone_count--; }
}
map->elements[idx].key = new_key;
map->elements[idx].value = value;
map->elements[idx].state = ENTRY_OCCUPIED;
map->size++;
@@ -212,21 +272,31 @@ map_result_t map_add(map_t *map, const char *key, void *value) {
*
* Finds the index where a key is located using linear probing to handle collisions
*
* Returns the index of the key if it is found
* Returns the index of the key if it is found or SIZE_MAX otherwise
*/
size_t map_find_index(const map_t *map, const char *key) {
const uint64_t key_digest = hash_key(key);
size_t idx = key_digest % map->capacity;
const size_t start_idx = key_digest % map->capacity;
while (map->elements[idx].state != ENTRY_EMPTY) {
if ((map->elements[idx].state == ENTRY_OCCUPIED) &&
(strcmp(map->elements[idx].key, key) == 0)) {
for (size_t probes = 0; probes < map->capacity; probes++) {
size_t idx = (start_idx + probes) % map->capacity;
if (map->elements[idx].state == ENTRY_EMPTY) {
// The key is not on the map
return SIZE_MAX;
}
if ((map->elements[idx].state == ENTRY_OCCUPIED) &&
(!strcmp(map->elements[idx].key, key))) {
// The key has been found
return idx;
}
idx = (idx + 1) % map->capacity;
}
return idx;
// If we fail to find an ENTRY_EMPTY slot after probing the entire table,
// fall back by returning SIZE_MAX. This should never
// happen because the map is resized whenever an element is inserted or removed.
return SIZE_MAX;
}
/**
@@ -250,17 +320,20 @@ map_result_t map_get(const map_t *map, const char *key) {
const size_t idx = map_find_index(map, key);
// If slot status is 'occupied' then the key exists
if (map->elements[idx].state == ENTRY_OCCUPIED) {
// otherwise the idx is set to SIZE_MAX
if (idx == SIZE_MAX) {
result.status = MAP_ERR_NOT_FOUND;
SET_MSG(result, "Element not found");
} else if (map->elements[idx].state == ENTRY_OCCUPIED) {
result.status = MAP_OK;
SET_MSG(result, "Value successfully retrieved");
result.value.element = map->elements[idx].value;
return result;
} else {
// Fallback case. Shouldn't happen but better safe than sorry
result.status = MAP_ERR_NOT_FOUND;
SET_MSG(result, "Element not found");
}
result.status = MAP_ERR_NOT_FOUND;
SET_MSG(result, "Element not found");
return result;
}
@@ -276,18 +349,18 @@ map_result_t map_get(const map_t *map, const char *key) {
map_result_t map_remove(map_t *map, const char *key) {
map_result_t result = {0};
if (map == NULL) {
if (map == NULL || key == NULL) {
result.status = MAP_ERR_INVALID;
SET_MSG(result, "Invalid map");
SET_MSG(result, "Invalid map or key");
return result;
}
const size_t idx = map_find_index(map, key);
if (map->elements[idx].state != ENTRY_OCCUPIED) {
result.status = MAP_ERR_INVALID;
SET_MSG(result, "Cannot delete this element");
if (idx == SIZE_MAX || map->elements[idx].state != ENTRY_OCCUPIED) {
result.status = MAP_ERR_NOT_FOUND;
SET_MSG(result, "Element not found");
return result;
}
@@ -304,6 +377,19 @@ map_result_t map_remove(map_t *map, const char *key) {
map->size--;
map->tombstone_count++;
// Check if there are too many tombstone entries
const double load_factor = (double)(map->size + map->tombstone_count) / map->capacity;
if (load_factor > LOAD_FACTOR_THRESHOLD) {
map_result_t resize_res = map_resize(map);
if (resize_res.status != MAP_OK) {
result.status = resize_res.status;
SET_MSG(result, "Key successfully deleted. Resize has failed");
return result;
}
}
result.status = MAP_OK;
SET_MSG(result, "Key successfully deleted");
@@ -329,8 +415,7 @@ map_result_t map_clear(map_t *map) {
}
for (size_t idx = 0; idx < map->capacity; idx++) {
if (map->elements[idx].state == ENTRY_OCCUPIED ||
map->elements[idx].state == ENTRY_DELETED) {
if (map->elements[idx].state == ENTRY_OCCUPIED) {
free(map->elements[idx].key);
map->elements[idx].key = NULL;
map->elements[idx].value = NULL;
@@ -368,8 +453,7 @@ map_result_t map_destroy(map_t *map) {
}
for (size_t idx = 0; idx < map->capacity; idx++) {
if (map->elements[idx].state == ENTRY_OCCUPIED ||
map->elements[idx].state == ENTRY_DELETED) {
if (map->elements[idx].state == ENTRY_OCCUPIED) {
free(map->elements[idx].key);
}
}

View File

@@ -17,6 +17,7 @@
typedef enum {
MAP_OK = 0x0,
MAP_ERR_ALLOCATE,
MAP_ERR_OVERFLOW,
MAP_ERR_INVALID,
MAP_ERR_NOT_FOUND
} map_status_t;

55
src/string.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef STRING_H
#define STRING_H
#define RESULT_MSG_SIZE 64
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
typedef enum {
STRING_OK = 0x0,
STRING_ERR_ALLOCATE,
STRING_ERR_INVALID,
STRING_ERR_INVALID_UTF8,
STRING_ERR_OVERFLOW
} string_status_t;
typedef struct {
char *data;
size_t byte_size; // Size in bytes excluding NULL terminator
size_t byte_capacity; // total allocated memory
size_t char_count; // Number of symbols
} string_t;
typedef struct {
string_status_t status;
uint8_t message[RESULT_MSG_SIZE];
union {
string_t *string; // For new, reverse, trim
char *c_str; // For get_at
int64_t idx; // For substring search
bool is_equ; // For comparison
struct { // For split
string_t **strings;
size_t count;
} split;
} value;
} string_result_t;
// Public APIs
string_result_t string_new(const char *c_str);
string_result_t string_concat(const string_t *x, const string_t *y);
string_result_t string_substring(const string_t *haystack, const string_t *needle);
string_result_t string_eq(const string_t *x, const string_t *y, bool case_sensitive);
string_result_t string_get_at(const string_t *str, size_t idx);
string_result_t string_set_at(string_t *str, size_t idx);
string_result_t string_to_lower(const string_t *str);
string_result_t string_to_upper(const string_t *str);
string_result_t string_reverse(const string_t *str);
string_result_t string_trim(const string_t *str);
string_result_t string_split(const string_t *str, const char *delim);
string_result_t string_destroy(string_t *str);
string_result_t string_split_destroy(string_t **split, size_t counT);
#endif

View File

@@ -383,6 +383,143 @@ vector_result_t vector_pop(vector_t *vector) {
return result;
}
/**
* vector_map
* @vector: a non-null vector
* @callback: callback function
* @env: optional captured environment
*
* Transforms each element of @vector in place by applying @callback
*
* Returns a vector_result_t data type
*/
vector_result_t vector_map(vector_t *vector, map_callback_fn callback, void *env) {
vector_result_t result = {0};
if (vector == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid vector");
return result;
}
if (callback == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid callback function");
return result;
}
for (size_t idx = 0; idx < vector->size; idx++) {
void *element = (uint8_t*)vector->elements + (idx * vector->data_size);
callback(element, env);
}
result.status = VECTOR_OK;
SET_MSG(result, "Vector successfully mapped");
return result;
}
/**
* vector_filter
* @vector: a non-null vector
* @callback: callback function
* @env: optional captured environment
*
* Filters elements from @vector using @callback.
* Elements are shifted in place, vector size is updated.
*
* Returns a vector_result_t data type
*/
vector_result_t vector_filter(vector_t *vector, vector_filter_fn callback, void *env) {
vector_result_t result = {0};
if (vector == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid vector");
return result;
}
if (callback == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid callback function");
return result;
}
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < vector->size; read_idx++) {
void *element = (uint8_t*)vector->elements + (read_idx * vector->data_size);
// Remove elements from @vector for which @callback returns zero
// If @callback returns non-zero, element is kept
if (callback(element, env)) {
if (read_idx != write_idx) {
void *dest = (uint8_t*)vector->elements + (write_idx * vector->data_size);
memcpy(dest, element, vector->data_size);
}
write_idx++;
}
}
// Update vector size
vector->size = write_idx;
result.status = VECTOR_OK;
SET_MSG(result, "Vector successfully filtered");
return result;
}
/**
* vecto_reduce
* @vector: a non-null vector
* @accumulator: pointer to accumulator value
* @callback: callback function
* @env: optional captured environment
*
* Reduces @vector to a single value by repeatedly applying @callback
* The @accumulator value should be initialized by the caller before invoking this function
*
* Returns a vector_result_t data type
*/
vector_result_t vector_reduce(const vector_t *vector, void *accumulator, vector_reduce_fn callback, void *env) {
vector_result_t result = {0};
if (vector == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid vector");
return result;
}
if (accumulator == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid accumulator");
return result;
}
if (callback == NULL) {
result.status = VECTOR_ERR_INVALID;
SET_MSG(result, "Invalid callback function");
return result;
}
for (size_t idx = 0; idx < vector->size; idx++) {
const void *element = (uint8_t*)vector->elements + (idx * vector->data_size);
callback(accumulator, element, env);
}
result.status = VECTOR_OK;
SET_MSG(result, "Vector successfully reduced");
return result;
}
/**
* vector_clear
* @vector: a non-null vector

View File

@@ -36,7 +36,11 @@ typedef enum {
VECTOR_ORDER_GT
} vector_order_t;
// Callback functions
typedef vector_order_t (*vector_cmp_fn)(const void *x, const void *y);
typedef void (*map_callback_fn)(void *element, void *env);
typedef int (*vector_filter_fn)(const void *element, void *env);
typedef void (*vector_reduce_fn)(void *accumulator, const void *element, void *env);
#ifdef __cplusplus
extern "C" {
@@ -49,6 +53,9 @@ vector_result_t vector_set(vector_t *vector, size_t index, void *value);
vector_result_t vector_get(vector_t *vector, size_t index);
vector_result_t vector_sort(vector_t *vector, vector_cmp_fn cmp);
vector_result_t vector_pop(vector_t *vector);
vector_result_t vector_map(vector_t *vector, map_callback_fn callback, void *env);
vector_result_t vector_filter(vector_t *vector, vector_filter_fn callback, void *env);
vector_result_t vector_reduce(const vector_t *vector, void *accumulator, vector_reduce_fn callback, void *env);
vector_result_t vector_clear(vector_t *vector);
vector_result_t vector_destroy(vector_t *vector);

View File

@@ -142,6 +142,45 @@ void test_bigint_prod(void) {
bigint_destroy(prod.value.number);
}
// Test product betweem very big numbers (i.e., use Karatsuba)
void test_bigint_very_large_prod(void) {
const char *big_x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355";
const char *big_y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937";
size_t x_len = strlen(big_x_origin);
size_t y_len = strlen(big_y_origin);
size_t large_x_size = x_len * 100 + 1;
size_t large_y_size = y_len * 100 + 1;
char *large_x = malloc(large_x_size);
char *large_y = malloc(large_y_size);
assert(large_x != NULL);
assert(large_y != NULL);
large_x[0] = '\0';
large_y[0] = '\0';
for (size_t idx = 0; idx < 50; idx++) {
strcat(large_x, big_x_origin);
strcat(large_y, big_y_origin);
}
bigint_result_t x = bigint_from_string(large_x);
bigint_result_t y = bigint_from_string(large_y);
assert(x.status == BIGINT_OK);
assert(y.status == BIGINT_OK);
bigint_result_t product_res = bigint_prod(x.value.number, y.value.number);
assert(product_res.status == BIGINT_OK);
bigint_destroy(product_res.value.number);
bigint_destroy(x.value.number);
bigint_destroy(y.value.number);
free(large_x); free(large_y);
}
// Test product between mixed negative big numbers
void test_bigint_prod_mixed(void) {
bigint_result_t x = bigint_from_int(-1234);
@@ -363,6 +402,7 @@ int main(void) {
TEST(bigint_sub_neg);
TEST(bigint_sub_mixed);
TEST(bigint_prod);
TEST(bigint_very_large_prod);
TEST(bigint_prod_mixed);
TEST(bigint_prod_neg);
TEST(bigint_div);

View File

@@ -97,6 +97,58 @@ void test_map_get_invalid(void) {
map_destroy(map);
}
// Get from map full of deleted slots
// If the table contains no ENTRY_EMPTY slots
// (i.e., the table is full of ENTRY_DELETED slots),
// map_get and map_remove should NOT loop forever
void test_map_get_deleted_slots(void) {
map_result_t res = map_new();
assert(res.status == MAP_OK);
map_t *map = res.value.map;
// Fill INITIAL_CAP (=4) without trigger resizing
map_add(map, "x", (void*)1);
map_add(map, "y", (void*)2);
map_add(map, "z", (void*)3);
map_add(map, "j", (void*)4);
// Remove all ENTRY_OCCUPIED slots.
// This function should resize the map when the load factor is too big
// and should also garbage-collect all the ENTRY_DELETED entries.
// Tombstone count should therefore be equal to 3 and capacity should be doubled
map_remove(map, "x");
map_remove(map, "y");
map_remove(map, "z");
map_remove(map, "j");
assert(map->tombstone_count == 3);
assert(map->capacity == 8);
assert(map->size == 0);
// Retrieving a deleted element should return an error
// but should not loop forever
map_result_t get_deleted_res = map_get(map, "y");
assert(get_deleted_res.status == MAP_ERR_NOT_FOUND);
// Adding a new element should increase the size
// and should not loop forever
const int k = 5;
map_result_t add_res = map_add(map, "k", (void*)&k);
assert(add_res.status == MAP_OK);
assert(map->tombstone_count < map->capacity);
assert(map->capacity == 8);
assert(map->size == 1);
// Retrieving an ENTRY_OCCUPIED element should works normally
map_result_t get_res = map_get(map, "k");
assert(get_res.status == MAP_OK);
assert(*(int*)get_res.value.element == 5);
map_destroy(map);
}
// Map with heterogeneous types
void test_map_mixed(void) {
map_result_t res = map_new();
@@ -324,6 +376,7 @@ int main(void) {
TEST(map_add_multiple);
TEST(map_get);
TEST(map_get_invalid);
TEST(map_get_deleted_slots);
TEST(map_mixed);
TEST(map_update);
TEST(map_remove);

View File

@@ -214,7 +214,7 @@ void test_vector_sort_string(void) {
// Sort vector with custom data type
typedef struct {
char name[256];
const char *name;
int age;
} Person;
@@ -312,6 +312,114 @@ void test_vector_sort_struct_by_name(void) {
vector_destroy(people);
}
// Map vector elements
void square(void *element, void *env) {
(void)(env);
int *value = (int*)element;
*value = (*value) * (*value);
}
void test_vector_map(void) {
vector_result_t res = vector_new(5, sizeof(int));
assert(res.status == VECTOR_OK);
vector_t *v = res.value.vector;
int values[] = { 25, 4, 3, 12, 19, 45 };
for (size_t idx = 0; idx < 6; idx++) {
vector_push(v, &values[idx]);
}
vector_result_t square_res = vector_map(v, square, NULL);
assert(square_res.status == VECTOR_OK);
const int expected[] = { 625, 16, 9, 144, 361, 2025 };
const size_t sz = vector_size(v);
for (size_t idx = 0; idx < sz; idx++) {
int *val = (int*)vector_get(v, idx).value.element;
assert(*val == expected[idx]);
}
vector_destroy(v);
}
// Filter vector elements
typedef struct {
double temperature;
uint64_t timestamp;
} weather_record_t;
typedef struct {
double min_temp;
double max_temp;
} temp_threshold_t;
int is_temp_in_range(const void *element, void *env) {
const weather_record_t *weather = (const weather_record_t*)element;
temp_threshold_t *threshold = (temp_threshold_t*)env;
return weather->temperature >= threshold->min_temp &&
weather->temperature <= threshold->max_temp;
}
void test_vector_filter(void) {
vector_result_t res = vector_new(5, sizeof(weather_record_t));
assert(res.status == VECTOR_OK);
vector_t *v = res.value.vector;
for (size_t idx = 0; idx < 10; idx++) {
weather_record_t record = {
.temperature = 20.0 + (idx * 2.5), // between 20.0C and 42.5C
.timestamp = 1234567890 + idx
};
vector_push(v, &record);
}
// Filter elements outside the threshold
temp_threshold_t threshold = {
.min_temp = 15.0,
.max_temp = 40.0
};
vector_result_t filter_res = vector_filter(v, is_temp_in_range, &threshold);
assert(filter_res.status == VECTOR_OK);
for (size_t idx = 0; idx < vector_size(v); idx++) {
double *val = (double*)vector_get(v, idx).value.element;
assert((*val >= 20.0) && (*val <= 40));
}
vector_destroy(v);
}
// Test reduce
void add(void *accumulator, const void *element, void *env) {
(void)(env);
*(int*)accumulator += *(int*)element;
}
void test_vector_reduce(void) {
vector_result_t res = vector_new(5, sizeof(int));
assert(res.status == VECTOR_OK);
vector_t *v = res.value.vector;
int values[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (size_t idx = 0; idx < 10; idx++) {
vector_push(v, &values[idx]);
}
int sum = 0;
vector_result_t reduce_res = vector_reduce(v, &sum, add, NULL);
assert(reduce_res.status == VECTOR_OK);
assert(sum == ((10 * 11) / 2));
vector_destroy(v);
}
// Set vector element
void test_vector_set(void) {
vector_result_t res = vector_new(5, sizeof(int));
@@ -491,6 +599,9 @@ int main(void) {
TEST(vector_sort_string);
TEST(vector_sort_struct_by_age);
TEST(vector_sort_struct_by_name);
TEST(vector_map);
TEST(vector_filter);
TEST(vector_reduce);
TEST(vector_set);
TEST(vector_set_ofb);
TEST(vector_pop);

153
usage.c
View File

@@ -16,7 +16,11 @@
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"
@@ -28,6 +32,9 @@ static int bigint_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;
@@ -62,6 +69,24 @@ 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));
@@ -195,6 +220,84 @@ int vector_usage(void) {
}
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) {
@@ -308,15 +411,42 @@ int map_usage(void) {
}
int bigint_usage(void) {
// Create two big integers
bigint_result_t x_res = bigint_from_string("123456789");
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("987654321");
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);
@@ -337,7 +467,7 @@ int bigint_usage(void) {
bigint_t *sum = sum_res.value.number;
// Print result
bigint_printf("123456789 + 987654321 (should be 1,111,111,110) = %B\n", sum);
bigint_printf("Sum result = %B\n", sum);
// Subtract two big integers
bigint_result_t diff_res = bigint_sub(x, y);
@@ -350,7 +480,7 @@ int bigint_usage(void) {
bigint_t *diff = diff_res.value.number;
// Print result
bigint_printf("123456789 - 987654321 (should be -864,197,532) = %B\n", diff);
bigint_printf("difference result = %B\n", diff);
// Multiply two big integers
bigint_result_t prod_res = bigint_prod(x, y);
@@ -363,10 +493,10 @@ int bigint_usage(void) {
bigint_t *prod = prod_res.value.number;
// Print result
bigint_printf("123456789 * 987654321 (should be 121,932,631,112,635,269) = %B\n", prod);
bigint_printf("multiplication result = %B\n", prod);
bigint_t *a = bigint_from_string("457349545684946456456456567567").value.number;
bigint_t *b = bigint_from_string("43569678678678678678678432").value.number;
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);
@@ -381,15 +511,16 @@ int bigint_usage(void) {
// Print result
bigint_printf(
"457349545684946456456456567567 / 43569678678678678678678432 (should be 10,496) = %B\
\n457349545684946456456456567567 %% 43569678678678678678678432 (should be 42,198,273,535,045,045,047,745,295) = %B\n",
"division result = %B\
\nmod result = %B\n",
quotient, remainder);
// Destroy big numbers
// 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;
}