From 6a2cb70fb5e57f03ea0fae34ce916bdd450d668f Mon Sep 17 00:00:00 2001 From: Leonardo Date: Tue, 2 Jul 2019 01:05:16 -0300 Subject: [PATCH] Tests update --- tests/main/Makefile | 6 + tests/main/main.c | 6 +- tests/main/src/deque.c | 2 +- tests/main/src/hashmap.c | 425 +++++++++++++++++++++++++++++++++ tests/main/src/hashset.c | 499 +++++++++++++++++++++++++++++++++++++++ tests/main/src/list.c | 2 +- tests/main/src/utl.c | 23 ++ tests/main/unt/deque.c | 47 +++- tests/main/unt/hashmap.c | 56 +++++ tests/main/unt/hashset.c | 56 +++++ tests/main/unt/list.c | 43 +++- 11 files changed, 1157 insertions(+), 8 deletions(-) create mode 100644 tests/main/src/hashmap.c create mode 100644 tests/main/src/hashset.c create mode 100644 tests/main/src/utl.c create mode 100644 tests/main/unt/hashmap.c create mode 100644 tests/main/unt/hashset.c diff --git a/tests/main/Makefile b/tests/main/Makefile index b7dca90..4e9a717 100644 --- a/tests/main/Makefile +++ b/tests/main/Makefile @@ -8,3 +8,9 @@ main: $(CC) main.c -o main $(CFLAGS) $(CVFLAGS) -I $(INCLUDE) ./main gcov ./main.c + +dev: + $(CC) main.c -o main $(CFLAGS) $(CVFLAGS) -I $(INCLUDE) + ./main + gcov ./main.c -n + rm *.gcda *.gcno *.exe diff --git a/tests/main/main.c b/tests/main/main.c index 0876148..903bedd 100644 --- a/tests/main/main.c +++ b/tests/main/main.c @@ -1,9 +1,13 @@ -#include "unt/list.c" #include "unt/deque.c" +#include "unt/hashmap.c" +#include "unt/hashset.c" +#include "unt/list.c" int main(void) { deque_test(); + hashmap_test(); + hashset_test(); list_test(); return 0; diff --git a/tests/main/src/deque.c b/tests/main/src/deque.c index d581617..77e620e 100644 --- a/tests/main/src/deque.c +++ b/tests/main/src/deque.c @@ -1,4 +1,4 @@ -#include "macro_collections.h" +#include "cmc/deque.h" typedef struct deque_s { diff --git a/tests/main/src/hashmap.c b/tests/main/src/hashmap.c new file mode 100644 index 0000000..5b30ab1 --- /dev/null +++ b/tests/main/src/hashmap.c @@ -0,0 +1,425 @@ +#include "cmc/hashmap.h" + +typedef struct hashmap_s +{ + struct hashmap_entry_s *buffer; + size_t capacity; + size_t count; + double load; + int (*cmp)(size_t, size_t); + size_t (*hash)(size_t); + struct hashmap_iter_s (*it_start)(struct hashmap_s *); + struct hashmap_iter_s (*it_end)(struct hashmap_s *); +} hashmap, *hashmap_ptr; +typedef struct hashmap_entry_s +{ + size_t key; + size_t value; + size_t dist; + enum EntryState_e state; +} hashmap_entry, *hashmap_entry_ptr; +typedef struct hashmap_iter_s +{ + struct hashmap_s *target; + size_t cursor; + size_t index; + size_t first; + size_t last; + bool start; + bool end; +} hashmap_iter, *hashmap_iter_ptr; +hashmap *hm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t)); +void hm_clear(hashmap *_map_); +void hm_free(hashmap *_map_); +bool hm_insert(hashmap *_map_, size_t key, size_t value); +bool hm_remove(hashmap *_map_, size_t key, size_t *value); +bool hm_insert_if(hashmap *_map_, size_t key, size_t value, bool condition); +bool hm_remove_if(hashmap *_map_, size_t key, size_t *value, bool condition); +bool hm_max(hashmap *_map_, size_t *key, size_t *value); +bool hm_min(hashmap *_map_, size_t *key, size_t *value); +size_t hm_get(hashmap *_map_, size_t key); +size_t *hm_get_ref(hashmap *_map_, size_t key); +bool hm_contains(hashmap *_map_, size_t key); +bool hm_empty(hashmap *_map_); +size_t hm_count(hashmap *_map_); +size_t hm_capacity(hashmap *_map_); +hashmap_iter *hm_iter_new(hashmap *target); +void hm_iter_free(hashmap_iter *iter); +void hm_iter_init(hashmap_iter *iter, hashmap *target); +bool hm_iter_start(hashmap_iter *iter); +bool hm_iter_end(hashmap_iter *iter); +void hm_iter_to_start(hashmap_iter *iter); +void hm_iter_to_end(hashmap_iter *iter); +bool hm_iter_next(hashmap_iter *iter); +bool hm_iter_prev(hashmap_iter *iter); +size_t hm_iter_key(hashmap_iter *iter); +size_t hm_iter_value(hashmap_iter *iter); +size_t *hm_iter_rvalue(hashmap_iter *iter); +size_t hm_iter_index(hashmap_iter *iter); +static inline size_t hm_impl_default_key(void) +{ + size_t _empty_key_; + memset(&_empty_key_, 0, sizeof(size_t)); + return _empty_key_; +} +static inline size_t hm_impl_default_value(void) +{ + size_t _empty_value_; + memset(&_empty_value_, 0, sizeof(size_t)); + return _empty_value_; +} +static bool hm_impl_grow(hashmap *_map_); +static hashmap_entry *hm_impl_get_entry(hashmap *_map_, size_t key); +static size_t hm_impl_calculate_size(size_t required); +static hashmap_iter hm_impl_it_start(hashmap *_map_); +static hashmap_iter hm_impl_it_end(hashmap *_map_); +hashmap *hm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t)) +{ + if (capacity == 0 || load <= 0 || load >= 1) + return 0; + size_t real_capacity = hm_impl_calculate_size(capacity / load); + hashmap *_map_ = malloc(sizeof(hashmap)); + if (!_map_) + return 0; + _map_->buffer = malloc(sizeof(hashmap_entry) * real_capacity); + if (!_map_->buffer) + { + free(_map_); + return 0; + } + memset(_map_->buffer, 0, sizeof(hashmap_entry) * real_capacity); + _map_->count = 0; + _map_->capacity = real_capacity; + _map_->load = load; + _map_->cmp = compare; + _map_->hash = hash; + _map_->it_start = hm_impl_it_start; + _map_->it_end = hm_impl_it_end; + return _map_; +} +void hm_clear(hashmap *_map_) +{ + memset(_map_->buffer, 0, sizeof(hashmap_entry) * _map_->capacity); + _map_->count = 0; +} +void hm_free(hashmap *_map_) +{ + free(_map_->buffer); + free(_map_); +} +bool hm_insert(hashmap *_map_, size_t key, size_t value) +{ + if ((double)_map_->capacity * _map_->load <= (double)_map_->count) + { + if (!hm_impl_grow(_map_)) + return false; + } + size_t hash = _map_->hash(key); + size_t original_pos = hash % _map_->capacity; + size_t pos = original_pos; + hashmap_entry *target = &(_map_->buffer[pos]); + if (hm_impl_get_entry(_map_, key) != 0) + return false; + if (target->state == ES_EMPTY || target->state == ES_DELETED) + { + target->key = key; + target->value = value; + target->dist = original_pos - pos; + target->state = ES_FILLED; + } + else + { + while (true) + { + pos++; + target = &(_map_->buffer[pos % _map_->capacity]); + if (target->state == ES_EMPTY || target->state == ES_DELETED) + { + target->key = key; + target->value = value; + target->dist = pos - original_pos; + target->state = ES_FILLED; + break; + } + else if (target->dist < original_pos - pos) + { + size_t tmp_k = target->key; + size_t tmp_v = target->value; + size_t tmp_dist = target->dist; + target->key = key; + target->value = value; + target->dist = pos - original_pos; + key = tmp_k; + value = tmp_v; + original_pos = pos - tmp_dist; + } + } + } + _map_->count++; + return true; +} +bool hm_remove(hashmap *_map_, size_t key, size_t *value) +{ + hashmap_entry *result = hm_impl_get_entry(_map_, key); + if (result == 0) + return false; + *value = result->value; + result->key = hm_impl_default_key(); + result->value = hm_impl_default_value(); + result->dist = 0; + result->state = ES_DELETED; + _map_->count--; + return true; +} +bool hm_insert_if(hashmap *_map_, size_t key, size_t value, bool condition) +{ + if (condition) + return hm_insert(_map_, key, value); + return false; +} +bool hm_remove_if(hashmap *_map_, size_t key, size_t *value, bool condition) +{ + if (condition) + return hm_remove(_map_, key, value); + return false; +} +bool hm_max(hashmap *_map_, size_t *key, size_t *value) +{ + if (hm_empty(_map_)) + return false; + hashmap_iter iter; + for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter)) + { + size_t result_key = hm_iter_key(&iter); + size_t result_value = hm_iter_value(&iter); + size_t index = hm_iter_index(&iter); + if (index == 0) + { + *key = result_key; + *value = result_value; + } + else if (_map_->cmp(result_key, *key) > 0) + { + *key = result_key; + *value = result_value; + } + } + return true; +} +bool hm_min(hashmap *_map_, size_t *key, size_t *value) +{ + if (hm_empty(_map_)) + return false; + hashmap_iter iter; + for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter)) + { + size_t result_key = hm_iter_key(&iter); + size_t result_value = hm_iter_value(&iter); + size_t index = hm_iter_index(&iter); + if (index == 0) + { + *key = result_key; + *value = result_value; + } + else if (_map_->cmp(result_key, *key) < 0) + { + *key = result_key; + *value = result_value; + } + } + return true; +} +size_t hm_get(hashmap *_map_, size_t key) +{ + hashmap_entry *entry = hm_impl_get_entry(_map_, key); + if (!entry) + hm_impl_default_value(); + return entry->value; +} +size_t *hm_get_ref(hashmap *_map_, size_t key) +{ + hashmap_entry *entry = hm_impl_get_entry(_map_, key); + if (!entry) + return 0; + return &(entry->value); +} +bool hm_contains(hashmap *_map_, size_t key) { return hm_impl_get_entry(_map_, key) != 0; } +bool hm_empty(hashmap *_map_) { return _map_->count == 0; } +size_t hm_count(hashmap *_map_) { return _map_->count; } +size_t hm_capacity(hashmap *_map_) { return _map_->capacity; } +hashmap_iter *hm_iter_new(hashmap *target) +{ + hashmap_iter *iter = malloc(sizeof(hashmap_iter)); + if (!iter) + return 0; + hm_iter_init(iter, target); + return iter; +} +void hm_iter_free(hashmap_iter *iter) { free(iter); } +void hm_iter_init(hashmap_iter *iter, hashmap *target) +{ + iter->target = target; + iter->cursor = 0; + iter->index = 0; + iter->start = true; + iter->end = hm_empty(target); + if (!hm_empty(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + if (target->buffer[i].state == ES_FILLED) + { + iter->first = i; + break; + } + } + iter->cursor = iter->first; + for (size_t i = target->capacity; i > 0; i--) + { + if (target->buffer[i - 1].state == ES_FILLED) + { + iter->last = i - 1; + break; + } + } + } +} +bool hm_iter_start(hashmap_iter *iter) { return hm_empty(iter->target) || iter->start; } +bool hm_iter_end(hashmap_iter *iter) { return hm_empty(iter->target) || iter->end; } +void hm_iter_to_start(hashmap_iter *iter) +{ + iter->cursor = iter->first; + iter->index = 0; + iter->start = true; + iter->end = hm_empty(iter->target); +} +void hm_iter_to_end(hashmap_iter *iter) +{ + iter->cursor = iter->last; + iter->index = iter->target->count - 1; + iter->start = hm_empty(iter->target); + iter->end = true; +} +bool hm_iter_next(hashmap_iter *iter) +{ + if (iter->end) + return false; + hashmap_entry *scan = &(iter->target->buffer[iter->cursor]); + if (iter->cursor == iter->last) + { + iter->end = true; + return false; + } + iter->index++; + while (1) + { + iter->cursor++; + scan = &(iter->target->buffer[iter->cursor]); + if (scan->state == ES_FILLED) + break; + } + iter->start = hm_empty(iter->target); + return true; +} +bool hm_iter_prev(hashmap_iter *iter) +{ + if (iter->start) + return false; + hashmap_entry *scan = &(iter->target->buffer[iter->cursor]); + if (iter->cursor == iter->first) + { + iter->start = true; + return false; + } + iter->index--; + while (1) + { + iter->cursor--; + scan = &(iter->target->buffer[iter->cursor]); + if (scan->state == ES_FILLED) + break; + } + iter->end = hm_empty(iter->target); + return true; +} +size_t hm_iter_key(hashmap_iter *iter) +{ + if (hm_empty(iter->target)) + hm_impl_default_key(); + return iter->target->buffer[iter->cursor].key; +} +size_t hm_iter_value(hashmap_iter *iter) +{ + if (hm_empty(iter->target)) + hm_impl_default_value(); + return iter->target->buffer[iter->cursor].value; +} +size_t *hm_iter_rvalue(hashmap_iter *iter) +{ + if (hm_empty(iter->target)) + return 0; + return &(iter->target->buffer[iter->cursor].value); +} +size_t hm_iter_index(hashmap_iter *iter) { return iter->index; } +static bool hm_impl_grow(hashmap *_map_) +{ + size_t new_size = hm_impl_calculate_size(_map_->capacity + _map_->capacity / 2); + hashmap *_new_map_ = hm_new(new_size, _map_->load, _map_->cmp, _map_->hash); + if (!_new_map_) + return false; + hashmap_iter iter; + for (hm_iter_init(&iter, _map_); !hm_iter_end(&iter); hm_iter_next(&iter)) + { + size_t key = hm_iter_key(&iter); + size_t value = hm_iter_value(&iter); + hm_insert(_new_map_, key, value); + } + if (_map_->count != _new_map_->count) + { + hm_free(_new_map_); + return false; + } + hashmap_entry *tmp = _map_->buffer; + _map_->buffer = _new_map_->buffer; + _new_map_->buffer = tmp; + _map_->capacity = _new_map_->capacity; + hm_free(_new_map_); + return true; +} +static hashmap_entry *hm_impl_get_entry(hashmap *_map_, size_t key) +{ + size_t hash = _map_->hash(key); + size_t pos = hash % _map_->capacity; + hashmap_entry *target = &(_map_->buffer[pos]); + while (target->state == ES_FILLED || target->state == ES_DELETED) + { + if (_map_->cmp(target->key, key) == 0) + return target; + pos++; + target = &(_map_->buffer[pos % _map_->capacity]); + } + return 0; +} +static size_t hm_impl_calculate_size(size_t required) +{ + const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); + if (cmc_hashtable_primes[count - 1] < required) + return required; + size_t i = 0; + while (cmc_hashtable_primes[i] < required) + i++; + return cmc_hashtable_primes[i]; +} +static hashmap_iter hm_impl_it_start(hashmap *_map_) +{ + hashmap_iter iter; + hm_iter_init(&iter, _map_); + return iter; +} +static hashmap_iter hm_impl_it_end(hashmap *_map_) +{ + hashmap_iter iter; + hm_iter_init(&iter, _map_); + hm_iter_to_end(&iter); + return iter; +} diff --git a/tests/main/src/hashset.c b/tests/main/src/hashset.c new file mode 100644 index 0000000..daf100b --- /dev/null +++ b/tests/main/src/hashset.c @@ -0,0 +1,499 @@ +#include "cmc/hashset.h" + +typedef struct hashset_s +{ + struct hashset_entry_s *buffer; + size_t capacity; + size_t count; + double load; + int (*cmp)(size_t, size_t); + size_t (*hash)(size_t); + struct hashset_iter_s (*it_start)(struct hashset_s *); + struct hashset_iter_s (*it_end)(struct hashset_s *); +} hashset, *hashset_ptr; +typedef struct hashset_entry_s +{ + size_t value; + size_t dist; + enum EntryState_e state; +} hashset_entry, *hashset_entry_ptr; +typedef struct hashset_iter_s +{ + struct hashset_s *target; + size_t cursor; + size_t index; + size_t first; + size_t last; + bool start; + bool end; +} hashset_iter, *hashset_iter_ptr; +hashset *hs_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t)); +void hs_clear(hashset *_set_); +void hs_free(hashset *_set_); +bool hs_insert(hashset *_set_, size_t element); +bool hs_remove(hashset *_set_, size_t element); +bool hs_insert_if(hashset *_set_, size_t element, bool condition); +bool hs_remove_if(hashset *_set_, size_t element, bool condition); +bool hs_max(hashset *_set_, size_t *value); +bool hs_min(hashset *_set_, size_t *value); +bool hs_contains(hashset *_set_, size_t element); +bool hs_empty(hashset *_set_); +size_t hs_count(hashset *_set_); +size_t hs_capacity(hashset *_set_); +hashset *hs_union(hashset *_set1_, hashset *_set2_); +hashset *hs_intersection(hashset *_set1_, hashset *_set2_); +hashset *hs_difference(hashset *_set1_, hashset *_set2_); +hashset *hs_symmetric_difference(hashset *_set1_, hashset *_set2_); +bool hs_is_subset(hashset *_set1_, hashset *_set2_); +bool hs_is_superset(hashset *_set1_, hashset *_set2_); +bool hs_is_proper_subset(hashset *_set1_, hashset *_set2_); +bool hs_is_proper_superset(hashset *_set1_, hashset *_set2_); +bool hs_is_disjointset(hashset *_set1_, hashset *_set2_); +hashset_iter *hs_iter_new(hashset *target); +void hs_iter_free(hashset_iter *iter); +void hs_iter_init(hashset_iter *iter, hashset *target); +bool hs_iter_start(hashset_iter *iter); +bool hs_iter_end(hashset_iter *iter); +void hs_iter_to_start(hashset_iter *iter); +void hs_iter_to_end(hashset_iter *iter); +bool hs_iter_next(hashset_iter *iter); +bool hs_iter_prev(hashset_iter *iter); +size_t hs_iter_value(hashset_iter *iter); +size_t hs_iter_index(hashset_iter *iter); +static inline size_t hs_impl_default_value(void) +{ + size_t _empty_value_; + memset(&_empty_value_, 0, sizeof(size_t)); + return _empty_value_; +} +static bool hs_impl_grow(hashset *_set_); +static hashset_entry *hs_impl_get_entry(hashset *_set_, size_t element); +static size_t hs_impl_calculate_size(size_t required); +static hashset_iter hs_impl_it_start(hashset *_set_); +static hashset_iter hs_impl_it_end(hashset *_set_); +hashset *hs_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t)) +{ + if (capacity == 0 || load <= 0 || load >= 1) + return NULL; + size_t real_capacity = hs_impl_calculate_size(capacity / load); + hashset *_set_ = malloc(sizeof(hashset)); + if (!_set_) + return NULL; + _set_->buffer = malloc(sizeof(hashset_entry) * real_capacity); + if (!_set_->buffer) + { + free(_set_); + return NULL; + } + memset(_set_->buffer, 0, sizeof(hashset_entry) * real_capacity); + _set_->count = 0; + _set_->capacity = real_capacity; + _set_->load = load; + _set_->cmp = compare; + _set_->hash = hash; + _set_->it_start = hs_impl_it_start; + _set_->it_end = hs_impl_it_end; + return _set_; +} +void hs_clear(hashset *_set_) +{ + memset(_set_->buffer, 0, sizeof(hashset_entry) * _set_->capacity); + _set_->count = 0; +} +void hs_free(hashset *_set_) +{ + free(_set_->buffer); + free(_set_); +} +bool hs_insert(hashset *_set_, size_t element) +{ + if ((double)_set_->capacity * _set_->load <= (double)_set_->count) + { + if (!hs_impl_grow(_set_)) + return false; + } + size_t hash = _set_->hash(element); + size_t original_pos = hash % _set_->capacity; + size_t pos = original_pos; + hashset_entry *target = &(_set_->buffer[pos]); + if (hs_impl_get_entry(_set_, element) != NULL) + return false; + if (target->state == ES_EMPTY || target->state == ES_DELETED) + { + target->value = element; + target->dist = pos - original_pos; + target->state = ES_FILLED; + } + else + { + while (true) + { + pos++; + target = &(_set_->buffer[pos % _set_->capacity]); + if (target->state == ES_EMPTY || target->state == ES_DELETED) + { + target->value = element; + target->dist = pos - original_pos; + target->state = ES_FILLED; + break; + } + else if (target->dist < pos - original_pos) + { + size_t tmp = target->value; + size_t tmp_dist = target->dist; + target->value = element; + target->dist = pos - original_pos; + element = tmp; + original_pos = pos - tmp_dist; + } + } + } + _set_->count++; + return true; +} +bool hs_remove(hashset *_set_, size_t element) +{ + hashset_entry *result = hs_impl_get_entry(_set_, element); + if (result == NULL) + return false; + result->value = hs_impl_default_value(); + result->dist = 0; + result->state = ES_DELETED; + _set_->count--; + return true; +} +bool hs_insert_if(hashset *_set_, size_t element, bool condition) +{ + if (condition) + return hs_insert(_set_, element); + return false; +} +bool hs_remove_if(hashset *_set_, size_t element, bool condition) +{ + if (condition) + return hs_remove(_set_, element); + return false; +} +bool hs_max(hashset *_set_, size_t *value) +{ + if (hs_empty(_set_)) + return false; + hashset_iter iter; + for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t result = hs_iter_value(&iter); + size_t index = hs_iter_index(&iter); + if (index == 0) + *value = result; + else if (_set_->cmp(result, *value) > 0) + *value = result; + } + return true; +} +bool hs_min(hashset *_set_, size_t *value) +{ + if (hs_empty(_set_)) + return false; + hashset_iter iter; + for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t result = hs_iter_value(&iter); + size_t index = hs_iter_index(&iter); + if (index == 0) + *value = result; + else if (_set_->cmp(result, *value) < 0) + *value = result; + } + return true; +} +bool hs_contains(hashset *_set_, size_t element) { return hs_impl_get_entry(_set_, element) != NULL; } +bool hs_empty(hashset *_set_) { return _set_->count == 0; } +size_t hs_count(hashset *_set_) { return _set_->count; } +size_t hs_capacity(hashset *_set_) { return _set_->capacity; } +hashset *hs_union(hashset *_set1_, hashset *_set2_) +{ + hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); + if (!_set_r_) + return false; + hashset_iter iter1, iter2; + hs_iter_init(&iter1, _set1_); + hs_iter_init(&iter2, _set2_); + for (hs_iter_to_start(&iter1); !hs_iter_end(&iter1); hs_iter_next(&iter1)) + { + hs_insert(_set_r_, hs_iter_value(&iter1)); + } + for (hs_iter_to_start(&iter2); !hs_iter_end(&iter2); hs_iter_next(&iter2)) + { + hs_insert(_set_r_, hs_iter_value(&iter2)); + } + return _set_r_; +} +hashset *hs_intersection(hashset *_set1_, hashset *_set2_) +{ + hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); + if (!_set_r_) + return false; + hashset *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_; + hashset *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_; + hashset_iter iter; + hs_iter_init(&iter, _set_A_); + for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t value = hs_iter_value(&iter); + if (hs_impl_get_entry(_set_B_, value) != NULL) + hs_insert(_set_r_, value); + } + return _set_r_; +} +hashset *hs_difference(hashset *_set1_, hashset *_set2_) +{ + hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); + if (!_set_r_) + return false; + hashset_iter iter; + hs_iter_init(&iter, _set1_); + for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t value = hs_iter_value(&iter); + if (hs_impl_get_entry(_set2_, value) == NULL) + hs_insert(_set_r_, value); + } + return _set_r_; +} +hashset *hs_symmetric_difference(hashset *_set1_, hashset *_set2_) +{ + hashset_iter iter1, iter2; + hashset *_set_r_ = hs_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); + if (!_set_r_) + return false; + hs_iter_init(&iter1, _set1_); + hs_iter_init(&iter2, _set2_); + for (hs_iter_to_start(&iter1); !hs_iter_end(&iter1); hs_iter_next(&iter1)) + { + size_t value = hs_iter_value(&iter1); + if (hs_impl_get_entry(_set2_, value) == NULL) + hs_insert(_set_r_, value); + } + for (hs_iter_to_start(&iter2); !hs_iter_end(&iter2); hs_iter_next(&iter2)) + { + size_t value = hs_iter_value(&iter2); + if (hs_impl_get_entry(_set1_, value) == NULL) + hs_insert(_set_r_, value); + } + return _set_r_; +} +bool hs_is_subset(hashset *_set1_, hashset *_set2_) +{ + if (hs_count(_set1_) > hs_count(_set2_)) + return false; + if (hs_empty(_set1_)) + return true; + hashset_iter iter; + hs_iter_init(&iter, _set1_); + for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t value = hs_iter_value(&iter); + if (!hs_contains(_set2_, value)) + return false; + } + return true; +} +bool hs_is_superset(hashset *_set1_, hashset *_set2_) { return hs_is_subset(_set2_, _set1_); } +bool hs_is_proper_subset(hashset *_set1_, hashset *_set2_) +{ + if (hs_count(_set1_) >= hs_count(_set2_)) + return false; + if (hs_empty(_set1_)) + { + if (!hs_empty(_set2_)) + return true; + else + return false; + } + hashset_iter iter; + hs_iter_init(&iter, _set1_); + for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t value = hs_iter_value(&iter); + if (!hs_contains(_set2_, value)) + return false; + } + return true; +} +bool hs_is_proper_superset(hashset *_set1_, hashset *_set2_) { return hs_is_proper_subset(_set2_, _set1_); } +bool hs_is_disjointset(hashset *_set1_, hashset *_set2_) +{ + if (hs_empty(_set1_)) + return true; + hashset_iter iter; + hs_iter_init(&iter, _set1_); + for (hs_iter_to_start(&iter); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + size_t value = hs_iter_value(&iter); + if (hs_contains(_set2_, value)) + return false; + } + return true; +} +hashset_iter *hs_iter_new(hashset *target) +{ + hashset_iter *iter = malloc(sizeof(hashset_iter)); + if (!iter) + return NULL; + hs_iter_init(iter, target); + return iter; +} +void hs_iter_free(hashset_iter *iter) { free(iter); } +void hs_iter_init(hashset_iter *iter, hashset *target) +{ + iter->target = target; + iter->cursor = 0; + iter->index = 0; + iter->start = true; + iter->end = hs_empty(target); + if (!hs_empty(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + if (target->buffer[i].state == ES_FILLED) + { + iter->first = i; + break; + } + } + iter->cursor = iter->first; + for (size_t i = target->capacity; i > 0; i--) + { + if (target->buffer[i - 1].state == ES_FILLED) + { + iter->last = i - 1; + break; + } + } + } +} +bool hs_iter_start(hashset_iter *iter) { return hs_empty(iter->target) || iter->start; } +bool hs_iter_end(hashset_iter *iter) { return hs_empty(iter->target) || iter->end; } +void hs_iter_to_start(hashset_iter *iter) +{ + iter->cursor = iter->first; + iter->index = 0; + iter->start = true; + iter->end = hs_empty(iter->target); +} +void hs_iter_to_end(hashset_iter *iter) +{ + iter->cursor = iter->last; + iter->index = iter->target->count - 1; + iter->start = hs_empty(iter->target); + iter->end = true; +} +bool hs_iter_next(hashset_iter *iter) +{ + if (iter->end) + return false; + hashset_entry *scan = &(iter->target->buffer[iter->cursor]); + if (iter->cursor == iter->last) + iter->end = true; + else + { + iter->index++; + while (1) + { + iter->cursor++; + scan = &(iter->target->buffer[iter->cursor]); + if (scan->state == ES_FILLED) + break; + } + } + iter->start = hs_empty(iter->target); + return true; +} +bool hs_iter_prev(hashset_iter *iter) +{ + if (iter->start) + return false; + hashset_entry *scan = &(iter->target->buffer[iter->cursor]); + if (iter->cursor == iter->first) + iter->start = true; + else + { + iter->index--; + while (1) + { + iter->cursor--; + scan = &(iter->target->buffer[iter->cursor]); + if (scan->state == ES_FILLED) + break; + } + } + iter->end = hs_empty(iter->target); + return true; +} +size_t hs_iter_value(hashset_iter *iter) +{ + if (hs_empty(iter->target)) + return hs_impl_default_value(); + return iter->target->buffer[iter->cursor].value; +} +size_t hs_iter_index(hashset_iter *iter) { return iter->index; } +static bool hs_impl_grow(hashset *_set_) +{ + size_t new_size = hs_impl_calculate_size(_set_->capacity + _set_->capacity / 2); + hashset *_new_set_ = hs_new(new_size, _set_->load, _set_->cmp, _set_->hash); + if (!_new_set_) + return false; + hashset_iter iter; + for (hs_iter_init(&iter, _set_); !hs_iter_end(&iter); hs_iter_next(&iter)) + { + hs_insert(_new_set_, hs_iter_value(&iter)); + } + if (_set_->count != _new_set_->count) + { + hs_free(_new_set_); + return false; + } + hashset_entry *tmp = _set_->buffer; + _set_->buffer = _new_set_->buffer; + _new_set_->buffer = tmp; + _set_->capacity = _new_set_->capacity; + hs_free(_new_set_); + return true; +} +static hashset_entry *hs_impl_get_entry(hashset *_set_, size_t element) +{ + size_t hash = _set_->hash(element); + size_t pos = hash % _set_->capacity; + hashset_entry *target = &(_set_->buffer[pos]); + while (target->state == ES_FILLED || target->state == ES_DELETED) + { + if (_set_->cmp(target->value, element) == 0) + return target; + pos++; + target = &(_set_->buffer[pos % _set_->capacity]); + } + return NULL; +} +static size_t hs_impl_calculate_size(size_t required) +{ + const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); + if (cmc_hashtable_primes[count - 1] < required) + return required; + size_t i = 0; + while (cmc_hashtable_primes[i] < required) + i++; + return cmc_hashtable_primes[i]; +} +static hashset_iter hs_impl_it_start(hashset *_set_) +{ + hashset_iter iter; + hs_iter_init(&iter, _set_); + return iter; +} +static hashset_iter hs_impl_it_end(hashset *_set_) +{ + hashset_iter iter; + hs_iter_init(&iter, _set_); + hs_iter_to_end(&iter); + return iter; +} diff --git a/tests/main/src/list.c b/tests/main/src/list.c index c848d01..82ff1c5 100644 --- a/tests/main/src/list.c +++ b/tests/main/src/list.c @@ -1,4 +1,4 @@ -#include "macro_collections.h" +#include "cmc/list.h" typedef struct list_s { diff --git a/tests/main/src/utl.c b/tests/main/src/utl.c new file mode 100644 index 0000000..efba4e2 --- /dev/null +++ b/tests/main/src/utl.c @@ -0,0 +1,23 @@ +/* Utility folder to be included in the unit tests */ +#ifndef CMC_UNIT_TEST_UTL__ +#define CMC_UNIT_TEST_UTL__ + +#include + +int cmp(size_t a, size_t b) +{ + return (a > b) - (a < b); +} + +size_t hash(size_t a) +{ + a += ~(a << 15); + a ^= (a >> 10); + a += (a << 3); + a ^= (a >> 6); + a += ~(a << 11); + a ^= (a >> 16); + return a; +} + +#endif /* CMC_UNIT_TEST_UTL__ */ diff --git a/tests/main/unt/deque.c b/tests/main/unt/deque.c index 6a2bca5..07cfccb 100644 --- a/tests/main/unt/deque.c +++ b/tests/main/unt/deque.c @@ -1,3 +1,5 @@ +#include "utl/assert.h" +#include "utl/test.h" #include "../src/deque.c" CMC_CREATE_UNIT(deque_test, true, { @@ -16,7 +18,7 @@ CMC_CREATE_UNIT(deque_test, true, { d_free(d); }); - + CMC_CREATE_TEST(new[edge_case:capacity = 0], { deque *d = d_new(0); @@ -25,4 +27,47 @@ CMC_CREATE_UNIT(deque_test, true, { CMC_TEST_PASS_ELSE_FAIL(d == NULL); }); + CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], { + deque *d = d_new(UINT64_MAX); + + cmc_assert_equals(ptr, NULL, d); + + CMC_TEST_PASS_ELSE_FAIL(d == NULL); + }); + + CMC_CREATE_TEST(clear[count capacity], { + deque *d = d_new(100); + + cmc_assert_not_equals(ptr, NULL, d); + + for (size_t i = 0; i < 50; i++) + d_push_back(d, i); + + cmc_assert_equals(size_t, 50, d_count(d)); + + d_clear(d); + + cmc_assert_equals(size_t, 0, d_count(d)); + cmc_assert_equals(size_t, 100, d_capacity(d)); + + CMC_TEST_PASS_ELSE_FAIL(d_count(d) == 0 && d_capacity(d) == 100); + + d_free(d); + }); + + CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], { + deque *d = d_new(1); + + cmc_assert_not_equals(ptr, NULL, d); + + for (size_t i = 0; i < 50; i++) + d_push_back(d, i); + + cmc_assert_equals(size_t, 50, d_count(d)); + + CMC_TEST_PASS_ELSE_FAIL(d_count(d) == 50); + + d_free(d); + }); + }); diff --git a/tests/main/unt/hashmap.c b/tests/main/unt/hashmap.c new file mode 100644 index 0000000..0cfb5c7 --- /dev/null +++ b/tests/main/unt/hashmap.c @@ -0,0 +1,56 @@ +#include "utl/assert.h" +#include "utl/test.h" +#include "../src/hashmap.c" + +#include "../src/utl.c" + +CMC_CREATE_UNIT(hashmap_test, true, { + CMC_CREATE_TEST(new, { + hashmap *map = hm_new(943722, 0.6, cmp, hash); + + bool passed = (map->capacity >= (943722 / 0.6)) && map->count == 0 && map->buffer; + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert(hm_capacity(map) > (943722 / 0.6)); + + CMC_TEST_PASS_ELSE_FAIL(passed); + + hm_free(map); + }); + + CMC_CREATE_TEST(new[edge_case:capacity = 0], { + hashmap *map = hm_new(0, 0.6, cmp, hash); + + cmc_assert_equals(ptr, NULL, map); + + CMC_TEST_PASS_ELSE_FAIL(map == NULL); + }); + + CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], { + hashmap *map = hm_new(UINT64_MAX, 0.6, cmp, hash); + + cmc_assert_equals(ptr, NULL, map); + + CMC_TEST_PASS_ELSE_FAIL(map == NULL); + }); + + CMC_CREATE_TEST(clear[count capacity], { + hashmap *map = hm_new(100, 0.6, cmp, hash); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 50; i++) + hm_insert(map, i, i); + + cmc_assert_equals(size_t, 50, hm_count(map)); + + hm_clear(map); + + cmc_assert_equals(size_t, 0, hm_count(map)); + + CMC_TEST_PASS_ELSE_FAIL(hm_count(map) == 0); + + hm_free(map); + }); + +}); diff --git a/tests/main/unt/hashset.c b/tests/main/unt/hashset.c new file mode 100644 index 0000000..f96ad3d --- /dev/null +++ b/tests/main/unt/hashset.c @@ -0,0 +1,56 @@ +#include "utl/assert.h" +#include "utl/test.h" +#include "../src/hashset.c" + +#include "../src/utl.c" + +CMC_CREATE_UNIT(hashset_test, true, { + CMC_CREATE_TEST(new, { + hashset *set = hs_new(943722, 0.6, cmp, hash); + + bool passed = (set->capacity >= (943722 / 0.6)) && set->count == 0 && set->buffer; + + cmc_assert_not_equals(ptr, NULL, set); + cmc_assert(hs_capacity(set) > (943722 / 0.6)); + + CMC_TEST_PASS_ELSE_FAIL(passed); + + hs_free(set); + }); + + CMC_CREATE_TEST(new[edge_case:capacity = 0], { + hashset *set = hs_new(0, 0.6, cmp, hash); + + cmc_assert_equals(ptr, NULL, set); + + CMC_TEST_PASS_ELSE_FAIL(set == NULL); + }); + + CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], { + hashset *set = hs_new(UINT64_MAX, 0.6, cmp, hash); + + cmc_assert_equals(ptr, NULL, set); + + CMC_TEST_PASS_ELSE_FAIL(set == NULL); + }); + + CMC_CREATE_TEST(clear[count capacity], { + hashset *set = hs_new(100, 0.6, cmp, hash); + + cmc_assert_not_equals(ptr, NULL, set); + + for (size_t i = 0; i < 50; i++) + hs_insert(set, i); + + cmc_assert_equals(size_t, 50, hs_count(set)); + + hs_clear(set); + + cmc_assert_equals(size_t, 0, hs_count(set)); + + CMC_TEST_PASS_ELSE_FAIL(hs_count(set) == 0); + + hs_free(set); + }); + +}); diff --git a/tests/main/unt/list.c b/tests/main/unt/list.c index 7cf1dea..be2c08b 100644 --- a/tests/main/unt/list.c +++ b/tests/main/unt/list.c @@ -1,3 +1,5 @@ +#include "utl/assert.h" +#include "utl/test.h" #include "../src/list.c" CMC_CREATE_UNIT(list_test, true, { @@ -12,10 +14,7 @@ CMC_CREATE_UNIT(list_test, true, { cmc_assert_equals(size_t, 0, l_count(l)); cmc_assert_not_equals(ptr, NULL, l->buffer); - if (passed) - CMC_TEST_PASS(); - else - CMC_TEST_FAIL(); + CMC_TEST_PASS_ELSE_FAIL(passed); l_free(l); }); @@ -28,6 +27,14 @@ CMC_CREATE_UNIT(list_test, true, { CMC_TEST_PASS_ELSE_FAIL(l == NULL); }); + CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], { + list *l = l_new(UINT64_MAX); + + cmc_assert_equals(ptr, NULL, l); + + CMC_TEST_PASS_ELSE_FAIL(l == NULL); + }); + CMC_CREATE_TEST(new_from[count item_preservation], { size_t v[5]; @@ -76,13 +83,41 @@ CMC_CREATE_UNIT(list_test, true, { for (size_t i = 0; i < 50; i++) l_push_back(l, i); + cmc_assert_equals(size_t, 50, l_count(l)); + l_clear(l); + cmc_assert_equals(size_t, 0, l_count(l)); + cmc_assert_equals(size_t, 100, l_capacity(l)); + CMC_TEST_PASS_ELSE_FAIL(l_count(l) == 0 && l_capacity(l) == 100); l_free(l); }); + CMC_CREATE_TEST(buffer_growth[edge_case:capacity = 1], { + list *l = l_new(1); + + cmc_assert_not_equals(ptr, NULL, l); + + for (size_t i = 0; i < 100; i++) + l_push_back(l, i); + + cmc_assert_equals(size_t, 100, l_count(l)); + + bool passed = true; + + for (size_t i = 0; i < 100; i++) + { + passed = passed && l_get(l, i) == i; + cmc_assert_equals(size_t, i, l_get(l, i)); + } + + CMC_TEST_PASS_ELSE_FAIL(passed); + + l_free(l); + }); + CMC_CREATE_TEST(push_front[count capacity item_preservation grow], { list *l = l_new(100);