From 6308936e65accb69fb76a0d6edd12e4f7b7f2e04 Mon Sep 17 00:00:00 2001 From: Leonardo Vencovsky Date: Fri, 26 Jun 2020 16:12:53 -0300 Subject: [PATCH] HashMultiMap no longer depends on ITER --- .../C++ Comparison/hashtable/hashmultimap.c | 3 +- src/cmc/bitset.h | 10 +- src/cmc/hashmap.h | 8 +- src/cmc/hashmultimap.h | 160 ++++++++++-------- src/cmc/hashmultiset.h | 4 +- src/cmc/hashset.h | 4 +- src/ext/cmc/treeset.h | 1 - tests/src/cmc/hashmultimap.c | 130 ++++++++------ tests/unt/cmc/hashmultimap.h | 97 +++++++++++ 9 files changed, 278 insertions(+), 139 deletions(-) diff --git a/benchmarks/C++ Comparison/hashtable/hashmultimap.c b/benchmarks/C++ Comparison/hashtable/hashmultimap.c index 2bac19a..4f25db7 100644 --- a/benchmarks/C++ Comparison/hashtable/hashmultimap.c +++ b/benchmarks/C++ Comparison/hashtable/hashmultimap.c @@ -8,7 +8,8 @@ #include "../../util/twister.c" -C_MACRO_COLLECTIONS_ALL(CMC, HASHMULTIMAP, (hmm, hashmultimap, , uint64_t, uint64_t)) +CMC_CMC_HASHMULTIMAP_CORE(PUBLIC, HEADER, (hmm, hashmultimap, , uint64_t, uint64_t)) +CMC_CMC_HASHMULTIMAP_CORE(PUBLIC, SOURCE, (hmm, hashmultimap, , uint64_t, uint64_t)) int cmp(uint64_t a, uint64_t b) { diff --git a/src/cmc/bitset.h b/src/cmc/bitset.h index 796d016..4e2ef04 100644 --- a/src/cmc/bitset.h +++ b/src/cmc/bitset.h @@ -86,10 +86,10 @@ /* Current array capacity */ \ size_t capacity; \ \ - /* Currently used bits */\ - /* This should always be true: */\ - /* capacity - count < sizeof(cmc_bitset_word) * CHAR_BIT */\ - size_t count;\ + /* Currently used bits */ \ + /* This should always be true: */ \ + /* capacity - count < sizeof(cmc_bitset_word) * CHAR_BIT */ \ + size_t count; \ \ /* Flags indicating errors or success */ \ int flag; \ @@ -156,7 +156,7 @@ /* Iterator Initialization */ \ /* Iterator State */ \ /* Iterator Movement */ \ - /* Iterator Access */ \ + /* Iterator Access */ /* ------------------------------------------------------------------------- * Source diff --git a/src/cmc/hashmap.h b/src/cmc/hashmap.h index 0406261..587370b 100644 --- a/src/cmc/hashmap.h +++ b/src/cmc/hashmap.h @@ -461,8 +461,8 @@ return false; \ } \ \ - K max_key = (K) { 0 }; \ - V max_val = (V) { 0 }; \ + K max_key = (K){ 0 }; \ + V max_val = (V){ 0 }; \ size_t first = 0; \ \ for (; first < _map_->capacity; first++) \ @@ -505,8 +505,8 @@ return false; \ } \ \ - K min_key = (K) { 0 }; \ - V min_val = (V) { 0 }; \ + K min_key = (K){ 0 }; \ + V min_val = (V){ 0 }; \ size_t first = 0; \ \ for (; first < _map_->capacity; first++) \ diff --git a/src/cmc/hashmultimap.h b/src/cmc/hashmultimap.h index 053f55f..b1dbd55 100644 --- a/src/cmc/hashmultimap.h +++ b/src/cmc/hashmultimap.h @@ -300,7 +300,7 @@ { \ struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; \ \ - while (scan != NULL) \ + while (scan) \ { \ struct CMC_DEF_ENTRY(SNAME) *next = scan->next; \ \ @@ -329,7 +329,7 @@ { \ struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[index][0]; \ \ - if (scan != NULL) \ + if (scan) \ { \ if (scan->next == NULL && scan->prev == NULL) \ { \ @@ -342,7 +342,7 @@ } \ else \ { \ - while (scan != NULL) \ + while (scan) \ { \ struct CMC_DEF_ENTRY(SNAME) *tmp = scan; \ \ @@ -481,7 +481,7 @@ \ size_t index = 0; \ \ - while (entry != NULL) \ + while (entry) \ { \ if (_map_->f_key->cmp(entry->key, key) == 0) \ { \ @@ -543,7 +543,7 @@ { \ bool found = false; \ \ - while (entry != NULL) \ + while (entry) \ { \ if (_map_->f_key->cmp(entry->key, key) == 0) \ { \ @@ -552,9 +552,9 @@ if (*tail == entry) \ *tail = entry->prev; \ \ - if (entry->prev != NULL) \ + if (entry->prev) \ entry->prev->next = entry->next; \ - if (entry->next != NULL) \ + if (entry->next) \ entry->next->prev = entry->prev; \ \ if (out_value) \ @@ -639,7 +639,7 @@ } \ else \ { \ - while (entry != NULL) \ + while (entry) \ { \ if (_map_->f_key->cmp(entry->key, key) == 0) \ { \ @@ -650,9 +650,9 @@ \ struct CMC_DEF_ENTRY(SNAME) *next = entry->next; \ \ - if (entry->prev != NULL) \ + if (entry->prev) \ entry->prev->next = entry->next; \ - if (entry->next != NULL) \ + if (entry->next) \ entry->next->prev = entry->prev; \ \ if (out_values) \ @@ -684,27 +684,30 @@ return false; \ } \ \ - K max_key = (K) { 0 }; \ - V max_val = (V) { 0 }; \ + bool first = true; \ + K max_key = (K){ 0 }; \ + V max_val = (V){ 0 }; \ \ - struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ -\ - /* TODO Turn this into a normal loop */ \ - for (; !CMC_(PFX, _iter_at_end)(&iter); CMC_(PFX, _iter_next)(&iter)) \ + for (size_t i = 0; i < _map_->capacity; i++) \ { \ - K result_key = CMC_(PFX, _iter_key)(&iter); \ - V result_value = CMC_(PFX, _iter_value)(&iter); \ - size_t index = CMC_(PFX, _iter_index)(&iter); \ + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; \ \ - if (index == 0) \ + while (scan) \ { \ - max_key = result_key; \ - max_val = result_value; \ - } \ - else if (_map_->f_key->cmp(result_key, max_key) > 0) \ - { \ - max_key = result_key; \ - max_val = result_value; \ + if (first) \ + { \ + max_key = scan->key; \ + max_val = scan->value; \ +\ + first = false; \ + } \ + else if (_map_->f_key->cmp(scan->key, max_key) > 0) \ + { \ + max_key = scan->key; \ + max_val = scan->value; \ + } \ +\ + scan = scan->next; \ } \ } \ \ @@ -728,27 +731,30 @@ return false; \ } \ \ - K min_key = (K) { 0 }; \ - V min_val = (V) { 0 }; \ + bool first = true; \ + K min_key = (K){ 0 }; \ + V min_val = (V){ 0 }; \ \ - struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ -\ - /* TODO Turn this into a normal loop */ \ - for (; !CMC_(PFX, _iter_at_end)(&iter); CMC_(PFX, _iter_next)(&iter)) \ + for (size_t i = 0; i < _map_->capacity; i++) \ { \ - K result_key = CMC_(PFX, _iter_key)(&iter); \ - V result_value = CMC_(PFX, _iter_value)(&iter); \ - size_t index = CMC_(PFX, _iter_index)(&iter); \ + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; \ \ - if (index == 0) \ + while (scan) \ { \ - min_key = result_key; \ - min_val = result_value; \ - } \ - else if (_map_->f_key->cmp(result_key, min_key) < 0) \ - { \ - min_key = result_key; \ - min_val = result_value; \ + if (first) \ + { \ + min_key = scan->key; \ + min_val = scan->value; \ +\ + first = false; \ + } \ + else if (_map_->f_key->cmp(scan->key, min_key) < 0) \ + { \ + min_key = scan->key; \ + min_val = scan->value; \ + } \ +\ + scan = scan->next; \ } \ } \ \ @@ -893,16 +899,17 @@ return false; \ } \ \ - struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ -\ - /* TODO Turn this into a normal loop */ \ - for (; !CMC_(PFX, _iter_at_end)(&iter); CMC_(PFX, _iter_next)(&iter)) \ + for (size_t i = 0; i < _map_->capacity; i++) \ { \ - K key = CMC_(PFX, _iter_key)(&iter); \ - V value = CMC_(PFX, _iter_value)(&iter); \ + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; \ \ - /* TODO check for errors */ \ - CMC_(PFX, _insert)(_new_map_, key, value); \ + while (scan) \ + { \ + /* TODO check for errors */ \ + CMC_(PFX, _insert)(_new_map_, scan->key, scan->value); \ +\ + scan = scan->next; \ + } \ } \ \ if (_map_->count != _new_map_->count) \ @@ -946,23 +953,28 @@ return NULL; \ } \ \ - struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ -\ - if (!CMC_(PFX, _empty)(_map_)) \ + for (size_t i = 0; i < _map_->capacity; i++) \ { \ - /* TODO Turn this into a normal loop */ \ - for (; !CMC_(PFX, _iter_at_end)(&iter); CMC_(PFX, _iter_next)(&iter)) \ + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; \ +\ + while (scan) \ { \ - K key = CMC_(PFX, _iter_key)(&iter); \ - V value = CMC_(PFX, _iter_value)(&iter); \ + K key;\ + V value;\ \ if (_map_->f_key->cpy) \ - key = _map_->f_key->cpy(key); \ + key = _map_->f_key->cpy(scan->key); \ + else\ + key = scan->key;\ if (_map_->f_val->cpy) \ - value = _map_->f_val->cpy(value); \ + value = _map_->f_val->cpy(scan->value); \ + else\ + value = scan->value;\ \ /* TODO check for errors */ \ CMC_(PFX, _insert)(result, key, value); \ +\ + scan = scan->next; \ } \ } \ \ @@ -980,15 +992,25 @@ if (_map1_->count != _map2_->count) \ return false; \ \ - struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map1_); \ + /* Optimize loop using the smallest hashtable */ \ + struct SNAME *_map_a_; \ + struct SNAME *_map_b_; \ \ - /* TODO Turn this into a normal loop */ \ - for (; !CMC_(PFX, _iter_at_end)(&iter); CMC_(PFX, _iter_next)(&iter)) \ + _map_a_ = _map1_->capacity < _map2_->capacity ? _map1_ : _map2_; \ + _map_b_ = _map_a_ == _map1_ ? _map2_ : _map1_; \ +\ + for (size_t i = 0; i < _map_a_->capacity; i++) \ { \ - K key = CMC_(PFX, _iter_key)(&iter); \ + struct CMC_DEF_ENTRY(SNAME) *scan = _map_a_->buffer[i][0]; \ \ - if (CMC_(PFX, _impl_key_count)(_map1_, key) != CMC_(PFX, _impl_key_count)(_map2_, key)) \ - return false; \ + while (scan) \ + { \ + /* OPTIMIZE - This is calling key_count for repeating keys */\ + if (CMC_(PFX, _impl_key_count)(_map_a_, scan->key) != CMC_(PFX, _impl_key_count)(_map_b_, scan->key))\ + return false;\ +\ + scan = scan->next; \ + } \ } \ \ return true; \ @@ -1015,7 +1037,7 @@ \ struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[hash % _map_->capacity][0]; \ \ - while (entry != NULL) \ + while (entry) \ { \ if (_map_->f_key->cmp(entry->key, key) == 0) \ return entry; \ @@ -1037,7 +1059,7 @@ if (!entry) \ return total_count; \ \ - while (entry != NULL) \ + while (entry) \ { \ if (_map_->f_key->cmp(entry->key, key) == 0) \ total_count++; \ diff --git a/src/cmc/hashmultiset.h b/src/cmc/hashmultiset.h index be14e5f..eaa3f08 100644 --- a/src/cmc/hashmultiset.h +++ b/src/cmc/hashmultiset.h @@ -477,7 +477,7 @@ return false; \ } \ \ - V max_val = (V) { 0 }; \ + V max_val = (V){ 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO transform this into a normal loop */ \ @@ -510,7 +510,7 @@ return false; \ } \ \ - V min_val = (V) { 0 }; \ + V min_val = (V){ 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO transform this into a normal loop */ \ diff --git a/src/cmc/hashset.h b/src/cmc/hashset.h index 47bdf4f..35b2fa9 100644 --- a/src/cmc/hashset.h +++ b/src/cmc/hashset.h @@ -378,7 +378,7 @@ return false; \ } \ \ - V max_val = (V) { 0 }; \ + V max_val = (V){ 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO turn this into a normal loop */ \ @@ -411,7 +411,7 @@ return false; \ } \ \ - V min_val = (V) { 0 }; \ + V min_val = (V){ 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO turn this into a normal loop */ \ diff --git a/src/ext/cmc/treeset.h b/src/ext/cmc/treeset.h index 674fef4..9cc5ce5 100644 --- a/src/ext/cmc/treeset.h +++ b/src/ext/cmc/treeset.h @@ -557,7 +557,6 @@ return true; \ } - /** * STR */ diff --git a/tests/src/cmc/hashmultimap.c b/tests/src/cmc/hashmultimap.c index 41d0817..f99130c 100644 --- a/tests/src/cmc/hashmultimap.c +++ b/tests/src/cmc/hashmultimap.c @@ -48,7 +48,7 @@ void hmm_clear(struct hashmultimap *_map_) for (size_t i = 0; i < _map_->capacity; i++) { struct hashmultimap_entry *scan = _map_->buffer[i][0]; - while (scan != ((void *)0)) + while (scan) { struct hashmultimap_entry *next = scan->next; if (_map_->f_key->free) @@ -69,7 +69,7 @@ void hmm_free(struct hashmultimap *_map_) while (index < _map_->capacity) { struct hashmultimap_entry *scan = _map_->buffer[index][0]; - if (scan != ((void *)0)) + if (scan) { if (scan->next == ((void *)0) && scan->prev == ((void *)0)) { @@ -81,7 +81,7 @@ void hmm_free(struct hashmultimap *_map_) } else { - while (scan != ((void *)0)) + while (scan) { struct hashmultimap_entry *tmp = scan; scan = scan->next; @@ -188,7 +188,7 @@ size_t hmm_update_all(struct hashmultimap *_map_, size_t key, size_t new_value, } } size_t index = 0; - while (entry != ((void *)0)) + while (entry) { if (_map_->f_key->cmp(entry->key, key) == 0) { @@ -239,7 +239,7 @@ _Bool hmm_remove(struct hashmultimap *_map_, size_t key, size_t *out_value) else { _Bool found = 0; - while (entry != ((void *)0)) + while (entry) { if (_map_->f_key->cmp(entry->key, key) == 0) { @@ -247,9 +247,9 @@ _Bool hmm_remove(struct hashmultimap *_map_, size_t key, size_t *out_value) *head = entry->next; if (*tail == entry) *tail = entry->prev; - if (entry->prev != ((void *)0)) + if (entry->prev) entry->prev->next = entry->next; - if (entry->next != ((void *)0)) + if (entry->next) entry->next->prev = entry->prev; if (out_value) *out_value = entry->value; @@ -316,7 +316,7 @@ size_t hmm_remove_all(struct hashmultimap *_map_, size_t key, size_t **out_value } else { - while (entry != ((void *)0)) + while (entry) { if (_map_->f_key->cmp(entry->key, key) == 0) { @@ -325,9 +325,9 @@ size_t hmm_remove_all(struct hashmultimap *_map_, size_t key, size_t **out_value if (*tail == entry) *tail = entry->prev; struct hashmultimap_entry *next = entry->next; - if (entry->prev != ((void *)0)) + if (entry->prev) entry->prev->next = entry->next; - if (entry->next != ((void *)0)) + if (entry->next) entry->next->prev = entry->prev; if (out_values) (*out_values)[index] = entry->value; @@ -353,23 +353,26 @@ _Bool hmm_max(struct hashmultimap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } + _Bool first = 1; size_t max_key = (size_t){ 0 }; size_t max_val = (size_t){ 0 }; - struct hashmultimap_iter iter = hmm_iter_start(_map_); - for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) + for (size_t i = 0; i < _map_->capacity; i++) { - size_t result_key = hmm_iter_key(&iter); - size_t result_value = hmm_iter_value(&iter); - size_t index = hmm_iter_index(&iter); - if (index == 0) + struct hashmultimap_entry *scan = _map_->buffer[i][0]; + while (scan) { - max_key = result_key; - max_val = result_value; - } - else if (_map_->f_key->cmp(result_key, max_key) > 0) - { - max_key = result_key; - max_val = result_value; + if (first) + { + max_key = scan->key; + max_val = scan->value; + first = 0; + } + else if (_map_->f_key->cmp(scan->key, max_key) > 0) + { + max_key = scan->key; + max_val = scan->value; + } + scan = scan->next; } } if (key) @@ -389,23 +392,26 @@ _Bool hmm_min(struct hashmultimap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } + _Bool first = 1; size_t min_key = (size_t){ 0 }; size_t min_val = (size_t){ 0 }; - struct hashmultimap_iter iter = hmm_iter_start(_map_); - for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) + for (size_t i = 0; i < _map_->capacity; i++) { - size_t result_key = hmm_iter_key(&iter); - size_t result_value = hmm_iter_value(&iter); - size_t index = hmm_iter_index(&iter); - if (index == 0) + struct hashmultimap_entry *scan = _map_->buffer[i][0]; + while (scan) { - min_key = result_key; - min_val = result_value; - } - else if (_map_->f_key->cmp(result_key, min_key) < 0) - { - min_key = result_key; - min_val = result_value; + if (first) + { + min_key = scan->key; + min_val = scan->value; + first = 0; + } + else if (_map_->f_key->cmp(scan->key, min_key) < 0) + { + min_key = scan->key; + min_val = scan->value; + } + scan = scan->next; } } if (key) @@ -518,12 +524,14 @@ _Bool hmm_resize(struct hashmultimap *_map_, size_t capacity) _map_->flag = CMC_FLAG_ALLOC; return 0; } - struct hashmultimap_iter iter = hmm_iter_start(_map_); - for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) + for (size_t i = 0; i < _map_->capacity; i++) { - size_t key = hmm_iter_key(&iter); - size_t value = hmm_iter_value(&iter); - hmm_insert(_new_map_, key, value); + struct hashmultimap_entry *scan = _map_->buffer[i][0]; + while (scan) + { + hmm_insert(_new_map_, scan->key, scan->value); + scan = scan->next; + } } if (_map_->count != _new_map_->count) { @@ -555,18 +563,23 @@ struct hashmultimap *hmm_copy_of(struct hashmultimap *_map_) _map_->flag = CMC_FLAG_ERROR; return ((void *)0); } - struct hashmultimap_iter iter = hmm_iter_start(_map_); - if (!hmm_empty(_map_)) + for (size_t i = 0; i < _map_->capacity; i++) { - for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) + struct hashmultimap_entry *scan = _map_->buffer[i][0]; + while (scan) { - size_t key = hmm_iter_key(&iter); - size_t value = hmm_iter_value(&iter); + size_t key; + size_t value; if (_map_->f_key->cpy) - key = _map_->f_key->cpy(key); + key = _map_->f_key->cpy(scan->key); + else + key = scan->key; if (_map_->f_val->cpy) - value = _map_->f_val->cpy(value); + value = _map_->f_val->cpy(scan->value); + else + value = scan->value; hmm_insert(result, key, value); + scan = scan->next; } } (result)->callbacks = _map_->callbacks; @@ -579,12 +592,19 @@ _Bool hmm_equals(struct hashmultimap *_map1_, struct hashmultimap *_map2_) _map2_->flag = CMC_FLAG_OK; if (_map1_->count != _map2_->count) return 0; - struct hashmultimap_iter iter = hmm_iter_start(_map1_); - for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) + struct hashmultimap *_map_a_; + struct hashmultimap *_map_b_; + _map_a_ = _map1_->capacity < _map2_->capacity ? _map1_ : _map2_; + _map_b_ = _map_a_ == _map1_ ? _map2_ : _map1_; + for (size_t i = 0; i < _map_a_->capacity; i++) { - size_t key = hmm_iter_key(&iter); - if (hmm_impl_key_count(_map1_, key) != hmm_impl_key_count(_map2_, key)) - return 0; + struct hashmultimap_entry *scan = _map_a_->buffer[i][0]; + while (scan) + { + if (hmm_impl_key_count(_map_a_, scan->key) != hmm_impl_key_count(_map_b_, scan->key)) + return 0; + scan = scan->next; + } } return 1; } @@ -603,7 +623,7 @@ struct hashmultimap_entry *hmm_impl_get_entry(struct hashmultimap *_map_, size_t { size_t hash = _map_->f_key->hash(key); struct hashmultimap_entry *entry = _map_->buffer[hash % _map_->capacity][0]; - while (entry != ((void *)0)) + while (entry) { if (_map_->f_key->cmp(entry->key, key) == 0) return entry; @@ -618,7 +638,7 @@ size_t hmm_impl_key_count(struct hashmultimap *_map_, size_t key) size_t total_count = 0; if (!entry) return total_count; - while (entry != ((void *)0)) + while (entry) { if (_map_->f_key->cmp(entry->key, key) == 0) total_count++; diff --git a/tests/unt/cmc/hashmultimap.h b/tests/unt/cmc/hashmultimap.h index 343ff93..c901a48 100644 --- a/tests/unt/cmc/hashmultimap.h +++ b/tests/unt/cmc/hashmultimap.h @@ -227,6 +227,103 @@ CMC_CREATE_UNIT(CMCHashMultiMap, true, { hmm_free(map); }); + CMC_CREATE_TEST(PFX##_max(), { + struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval); + + size_t max_key; + size_t max_val; + cmc_assert(!hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, map->flag); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 1, max_key); + cmc_assert_equals(size_t, 2, max_val); + + cmc_assert(hmm_insert(map, 2, 3)); + cmc_assert(hmm_insert(map, 0, 1)); + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 2, max_key); + cmc_assert_equals(size_t, 3, max_val); + + for (size_t i = 3; i <= 100; i++) + { + cmc_assert(hmm_insert(map, i, i)); + } + + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 100, max_key); + cmc_assert_equals(size_t, 100, max_val); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_min(), { + struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval); + + size_t min_key; + size_t min_val; + cmc_assert(!hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, map->flag); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 1, min_key); + cmc_assert_equals(size_t, 2, min_val); + + cmc_assert(hmm_insert(map, 2, 3)); + cmc_assert(hmm_insert(map, 0, 1)); + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 0, min_key); + cmc_assert_equals(size_t, 1, min_val); + + for (size_t i = 3; i <= 100; i++) + { + cmc_assert(hmm_insert(map, i, i)); + } + + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 0, min_key); + cmc_assert_equals(size_t, 1, min_val); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_copy_of(), { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + for (size_t i = 0; i < 1000; i++) + hmm_insert(map, i, i); + + struct hashmultimap *map2 = hmm_copy_of(map); + + cmc_assert_equals(size_t, map->count, map2->count); + cmc_assert(hmm_equals(map, map2)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_equals(), { + struct hashmultimap *map1 = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + struct hashmultimap *map2 = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert(hmm_equals(map1, map2)); + + cmc_assert(hmm_insert(map1, 1, 1)); + cmc_assert(hmm_insert(map2, 1, 1)); + cmc_assert(hmm_equals(map1, map2)); + + for (size_t i = 2; i < 200; i++) + { + cmc_assert(hmm_insert(map1, i, i) && hmm_insert(map2, i, i)); + } + + cmc_assert(hmm_equals(map1, map2)); + + hmm_free(map1); + hmm_free(map2); + }); + CMC_CREATE_TEST(key_count, { struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval);