Multimap tests

This commit is contained in:
Leonardo committed 2019-07-09 23:51:01 -03:00
1 parent 9c23c3ca77
commit 0c883b0a74
3 files changed
+713

No files matched your search

+5
View File
@@ -9,6 +9,8 @@
#include "unt/treemap.c"
#include "unt/treeset.c"
#include "unt/multimap.c"
int main(void)
{
deque_test();
@@ -22,5 +24,8 @@ int main(void)
treemap_test();
treeset_test();
/* ext */
multimap_test();
return 0;
}
+535
View File
@@ -0,0 +1,535 @@
#include "ext/multimap.h"
typedef struct multimap_s
{
struct multimap_entry_s *(*buffer)[2];
size_t capacity;
size_t count;
double load;
int (*cmp)(size_t, size_t);
size_t (*hash)(size_t);
struct multimap_iter_s (*it_start)(struct multimap_s *);
struct multimap_iter_s (*it_end)(struct multimap_s *);
} multimap, *multimap_ptr;
typedef struct multimap_entry_s
{
size_t key;
size_t value;
struct multimap_entry_s *next;
struct multimap_entry_s *prev;
} multimap_entry, *multimap_entry_ptr;
typedef struct multimap_iter_s
{
struct multimap_s *target;
struct multimap_entry_s *curr_entry;
size_t cursor;
size_t index;
size_t first;
size_t last;
bool start;
bool end;
} multimap_iter, *multimap_iter_ptr;
multimap *mm_new(size_t size, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t));
void mm_clear(multimap *_map_);
void mm_free(multimap *_map_);
bool mm_insert(multimap *_map_, size_t key, size_t value);
bool mm_remove(multimap *_map_, size_t key, size_t *value);
size_t mm_remove_all(multimap *_map_, size_t key);
bool mm_insert_if(multimap *_map_, size_t key, size_t value, bool condition);
bool mm_remove_if(multimap *_map_, size_t key, size_t *value, bool condition);
bool mm_max(multimap *_map_, size_t *key, size_t *value);
bool mm_min(multimap *_map_, size_t *key, size_t *value);
size_t mm_get(multimap *_map_, size_t key);
size_t *mm_get_ref(multimap *_map_, size_t key);
bool mm_contains(multimap *_map_, size_t key);
bool mm_empty(multimap *_map_);
size_t mm_count(multimap *_map_);
size_t mm_capacity(multimap *_map_);
multimap_iter *mm_iter_new(multimap *target);
void mm_iter_free(multimap_iter *iter);
void mm_iter_init(multimap_iter *iter, multimap *target);
bool mm_iter_start(multimap_iter *iter);
bool mm_iter_end(multimap_iter *iter);
void mm_iter_to_start(multimap_iter *iter);
void mm_iter_to_end(multimap_iter *iter);
bool mm_iter_next(multimap_iter *iter);
bool mm_iter_prev(multimap_iter *iter);
size_t mm_iter_key(multimap_iter *iter);
size_t mm_iter_value(multimap_iter *iter);
size_t *mm_iter_rvalue(multimap_iter *iter);
size_t mm_iter_index(multimap_iter *iter);
static inline size_t mm_impl_default_key(void)
{
size_t _empty_key_;
memset(&_empty_key_, 0, sizeof(size_t));
return _empty_key_;
}
static inline size_t mm_impl_default_value(void)
{
size_t _empty_value_;
memset(&_empty_value_, 0, sizeof(size_t));
return _empty_value_;
}
bool mm_impl_grow(multimap *_map_);
multimap_entry *mm_impl_new_entry(size_t key, size_t value);
multimap_entry *mm_impl_get_entry(multimap *_map_, size_t key);
size_t mm_impl_calculate_size(size_t required);
static multimap_iter mm_impl_it_start(multimap *_map_);
static multimap_iter mm_impl_it_end(multimap *_map_);
multimap *mm_new(size_t capacity, double load, int (*compare)(size_t, size_t), size_t (*hash)(size_t))
{
if (capacity == 0 || load <= 0)
return NULL;
size_t real_capacity = mm_impl_calculate_size(capacity / load);
multimap *_map_ = malloc(sizeof(multimap));
if (!_map_)
return NULL;
_map_->buffer = malloc(sizeof(multimap_entry_ptr[2]) * real_capacity);
if (!_map_->buffer)
{
free(_map_);
return NULL;
}
memset(_map_->buffer, 0, sizeof(multimap_entry_ptr[2]) * real_capacity);
_map_->count = 0;
_map_->capacity = real_capacity;
_map_->load = load;
_map_->cmp = compare;
_map_->hash = hash;
return _map_;
}
void mm_clear(multimap *_map_)
{
size_t index = 0;
multimap_entry *scan;
while (index < _map_->capacity)
{
scan = _map_->buffer[index][0];
if (scan != NULL)
{
if (scan->next == NULL && scan->prev == NULL)
free(scan);
else
{
while (scan != NULL)
{
multimap_entry *tmp = scan;
scan = scan->next;
free(tmp);
}
}
}
index++;
}
memset(_map_->buffer, 0, sizeof(multimap_entry_ptr[2]) * _map_->capacity);
_map_->count = 0;
}
void mm_free(multimap *_map_)
{
mm_clear(_map_);
free(_map_->buffer);
free(_map_);
}
bool mm_insert(multimap *_map_, size_t key, size_t value)
{
if ((double)_map_->capacity * _map_->load <= (double)_map_->count)
{
if (!mm_impl_grow(_map_))
return false;
}
size_t hash = _map_->hash(key);
size_t pos = hash % _map_->capacity;
multimap_entry *entry = mm_impl_new_entry(key, value);
if (_map_->buffer[pos][0] == NULL)
{
_map_->buffer[pos][0] = entry;
_map_->buffer[pos][1] = entry;
}
else
{
entry->prev = _map_->buffer[pos][1];
_map_->buffer[pos][1]->next = entry;
_map_->buffer[pos][1] = entry;
}
_map_->count++;
return true;
}
bool mm_remove(multimap *_map_, size_t key, size_t *value)
{
size_t hash = _map_->hash(key);
multimap_entry **head = &(_map_->buffer[hash % _map_->capacity][0]);
multimap_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]);
if (*head == NULL)
return false;
multimap_entry *entry = *head;
if (entry->next == NULL && entry->prev == NULL)
{
if (_map_->cmp(entry->key, key) == 0)
{
*head = NULL;
*tail = NULL;
*value = entry->value;
}
else
return false;
}
else
{
bool found = false;
while (entry != NULL)
{
if (_map_->cmp(entry->key, key) == 0)
{
if (*head == entry)
*head = entry->next;
if (*tail == entry)
*tail = entry->prev;
if (entry->prev != NULL)
entry->prev->next = entry->next;
if (entry->next != NULL)
entry->next->prev = entry->prev;
*value = entry->value;
found = true;
break;
}
else
entry = entry->next;
}
if (!found)
return false;
}
free(entry);
_map_->count--;
return true;
}
size_t mm_remove_all(multimap *_map_, size_t key)
{
size_t total = 0;
size_t hash = _map_->hash(key);
multimap_entry **head = &(_map_->buffer[hash % _map_->capacity][0]);
multimap_entry **tail = &(_map_->buffer[hash % _map_->capacity][1]);
if (*head == NULL)
return total;
multimap_entry *entry = *head;
if (entry->next == NULL)
{
*head = NULL;
*tail = NULL;
free(entry);
total++;
}
else
{
while (entry != NULL)
{
if (_map_->cmp(entry->key, key) == 0)
{
if (*head == entry)
*head = entry->next;
if (*tail == entry)
*tail = entry->prev;
multimap_entry *temp = entry->next;
if (entry->prev != NULL)
entry->prev->next = entry->next;
if (entry->next != NULL)
entry->next->prev = entry->prev;
free(entry);
entry = temp;
total++;
}
else
entry = entry->next;
}
}
_map_->count -= total;
return total;
}
bool mm_insert_if(multimap *_map_, size_t key, size_t value, bool condition)
{
if (condition)
return mm_insert(_map_, key, value);
return false;
}
bool mm_remove_if(multimap *_map_, size_t key, size_t *value, bool condition)
{
if (condition)
return mm_remove(_map_, key, value);
return false;
}
bool mm_max(multimap *_map_, size_t *key, size_t *value)
{
if (mm_empty(_map_))
return false;
multimap_iter iter;
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
{
size_t result_key = mm_iter_key(&iter);
size_t result_value = mm_iter_value(&iter);
size_t index = mm_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 mm_min(multimap *_map_, size_t *key, size_t *value)
{
if (mm_empty(_map_))
return false;
multimap_iter iter;
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
{
size_t result_key = mm_iter_key(&iter);
size_t result_value = mm_iter_value(&iter);
size_t index = mm_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 mm_get(multimap *_map_, size_t key)
{
multimap_entry *entry = mm_impl_get_entry(_map_, key);
if (!entry)
return mm_impl_default_value();
return entry->value;
}
size_t *mm_get_ref(multimap *_map_, size_t key)
{
multimap_entry *entry = mm_impl_get_entry(_map_, key);
if (!entry)
return NULL;
return &(entry->value);
}
bool mm_contains(multimap *_map_, size_t key) { return mm_impl_get_entry(_map_, key) != NULL; }
bool mm_empty(multimap *_map_) { return _map_->count == 0; }
size_t mm_count(multimap *_map_) { return _map_->count; }
size_t mm_capacity(multimap *_map_) { return _map_->capacity; }
multimap_iter *mm_iter_new(multimap *target)
{
multimap_iter *iter = malloc(sizeof(multimap_iter));
if (!iter)
return NULL;
mm_iter_init(iter, target);
return iter;
}
void mm_iter_free(multimap_iter *iter) { free(iter); }
void mm_iter_init(multimap_iter *iter, multimap *target)
{
iter->target = target;
iter->cursor = 0;
iter->index = 0;
iter->start = true;
iter->end = mm_empty(target);
if (!mm_empty(target))
{
for (size_t i = 0; i < target->capacity; i++)
{
if (target->buffer[i][0] != NULL)
{
iter->curr_entry = target->buffer[i][0];
iter->first = i;
break;
}
}
iter->cursor = iter->first;
for (size_t i = target->capacity; i > 0; i--)
{
if (target->buffer[i - 1][0] != NULL)
{
iter->last = i - 1;
break;
}
}
}
}
bool mm_iter_start(multimap_iter *iter) { return mm_empty(iter->target) || iter->start; }
bool mm_iter_end(multimap_iter *iter) { return mm_empty(iter->target) || iter->end; }
void mm_iter_to_start(multimap_iter *iter)
{
iter->cursor = iter->first;
iter->index = 0;
iter->start = true;
iter->end = mm_empty(iter->target);
iter->curr_entry = iter->target->buffer[iter->first][0];
}
void mm_iter_to_end(multimap_iter *iter)
{
iter->cursor = iter->last;
iter->index = iter->target->count - 1;
iter->start = mm_empty(iter->target);
iter->end = true;
iter->curr_entry = iter->target->buffer[iter->last][1];
}
bool mm_iter_next(multimap_iter *iter)
{
if (iter->end)
return false;
if (iter->curr_entry->next != NULL)
{
iter->curr_entry = iter->curr_entry->next;
iter->index++;
}
else
{
if (iter->cursor == iter->last)
{
iter->end = true;
return false;
}
else
{
iter->cursor++;
while (iter->target->buffer[iter->cursor][0] == NULL)
iter->cursor++;
iter->curr_entry = iter->target->buffer[iter->cursor][0];
iter->index++;
}
}
iter->start = mm_empty(iter->target);
return true;
}
bool mm_iter_prev(multimap_iter *iter)
{
if (iter->start)
return false;
if (iter->curr_entry->prev != NULL)
{
iter->curr_entry = iter->curr_entry->prev;
iter->index--;
}
else
{
if (iter->cursor == iter->first)
{
iter->start = true;
return false;
}
else
{
iter->cursor--;
while (iter->target->buffer[iter->cursor][1] == NULL)
iter->cursor--;
iter->curr_entry = iter->target->buffer[iter->cursor][1];
iter->index--;
}
}
iter->end = mm_empty(iter->target);
return true;
}
size_t mm_iter_key(multimap_iter *iter)
{
if (mm_empty(iter->target))
return mm_impl_default_key();
return iter->curr_entry->key;
}
size_t mm_iter_value(multimap_iter *iter)
{
if (mm_empty(iter->target))
return mm_impl_default_value();
return iter->curr_entry->value;
}
size_t *mm_iter_rvalue(multimap_iter *iter)
{
if (mm_empty(iter->target))
return NULL;
return &(iter->curr_entry->value);
}
size_t mm_iter_index(multimap_iter *iter) { return iter->index; }
bool mm_impl_grow(multimap *_map_)
{
size_t new_capacity = mm_impl_calculate_size(_map_->capacity * 1.5);
multimap *_new_map_ = mm_new(new_capacity, _map_->load, _map_->cmp, _map_->hash);
if (!_new_map_)
return false;
multimap_iter iter;
for (mm_iter_init(&iter, _map_); !mm_iter_end(&iter); mm_iter_next(&iter))
{
size_t key = mm_iter_key(&iter);
size_t value = mm_iter_value(&iter);
mm_insert(_new_map_, key, value);
}
if (_map_->count != _new_map_->count)
{
mm_free(_new_map_);
return false;
}
multimap_entry *(*tmp_b)[2] = _map_->buffer;
_map_->buffer = _new_map_->buffer;
_new_map_->buffer = tmp_b;
size_t tmp_c = _map_->capacity;
_map_->capacity = _new_map_->capacity;
_new_map_->capacity = tmp_c;
mm_free(_new_map_);
return true;
}
multimap_entry *mm_impl_new_entry(size_t key, size_t value)
{
multimap_entry *entry = malloc(sizeof(multimap_entry));
if (!entry)
return NULL;
entry->key = key;
entry->value = value;
entry->next = NULL;
entry->prev = NULL;
return entry;
}
multimap_entry *mm_impl_get_entry(multimap *_map_, size_t key)
{
size_t hash = _map_->hash(key);
multimap_entry *entry = _map_->buffer[hash % _map_->capacity][0];
if (entry == NULL)
return NULL;
if (entry->next == NULL && entry->prev == NULL)
{
if (_map_->cmp(entry->key, key) == 0)
return entry;
else
return NULL;
}
while (entry != NULL)
{
if (_map_->cmp(entry->key, key) == 0)
return entry;
entry = entry->next;
}
return NULL;
}
size_t mm_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 multimap_iter mm_impl_it_start(multimap *_map_)
{
multimap_iter iter;
mm_iter_init(&iter, _map_);
return iter;
}
static multimap_iter mm_impl_it_end(multimap *_map_)
{
multimap_iter iter;
mm_iter_init(&iter, _map_);
mm_iter_to_end(&iter);
return iter;
}
+173
View File
@@ -0,0 +1,173 @@
#include "utl/assert.h"
#include "utl/test.h"
#include "../src/multimap.c"
#include "utl.c"
CMC_CREATE_UNIT(multimap_test, true, {
CMC_CREATE_TEST(new, {
multimap *map = mm_new(943722, 0.8, cmp, hash);
bool passed = (map->capacity >= (943722 / 0.8)) && map->count == 0 && map->buffer;
cmc_assert_not_equals(ptr, NULL, map);
cmc_assert_greater_equals(size_t, (943722 / 0.8), mm_capacity(map));
CMC_TEST_PASS_ELSE_FAIL(passed);
mm_free(map);
});
CMC_CREATE_TEST(new[edge_case:capacity = 0], {
multimap *map = mm_new(0, 0.8, cmp, hash);
cmc_assert_equals(ptr, NULL, map);
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
});
CMC_CREATE_TEST(new[edge_case:capacity = UINT64_MAX], {
multimap *map = mm_new(UINT64_MAX, 0.8, cmp, hash);
cmc_assert_equals(ptr, NULL, map);
CMC_TEST_PASS_ELSE_FAIL(map == NULL);
});
CMC_CREATE_TEST(clear[count capacity], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
for (size_t i = 0; i < 50; i++)
cmc_assert(mm_insert(map, i, i));
cmc_assert_equals(size_t, 50, mm_count(map));
mm_clear(map);
cmc_assert_equals(size_t, 0, mm_count(map));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 0);
mm_free(map);
});
CMC_CREATE_TEST(insert[count], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
for (size_t i = 0; i < 200; i++)
cmc_assert(mm_insert(map, i % 20, i % 20));
cmc_assert_equals(size_t, 200, mm_count(map));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 200);
mm_free(map);
});
CMC_CREATE_TEST(remove[count], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
for (size_t i = 0; i < 200; i++)
cmc_assert(mm_insert(map, i % 20, i % 20));
size_t r;
for (size_t i = 0; i < 60; i++)
cmc_assert(mm_remove(map, i % 20, &r));
cmc_assert_equals(size_t, 140, mm_count(map));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 140);
mm_free(map);
});
CMC_CREATE_TEST(remove[edge_case:count = 0], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
size_t r;
cmc_assert(!mm_remove(map, 10, &r));
CMC_TEST_PASS_ELSE_FAIL(!mm_remove(map, 10, &r));
mm_free(map);
});
CMC_CREATE_TEST(remove[edge_case:count = 1], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
size_t r;
cmc_assert(mm_insert(map, 10, 11));
cmc_assert(mm_remove(map, 10, &r));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 0);
mm_free(map);
});
CMC_CREATE_TEST(insert_if, {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
for (size_t i = 1; i <= 100; i++)
cmc_assert(mm_insert_if(map, i % 20, i, i % 2 == 0) || i % 2 != 0);
cmc_assert_equals(size_t, 50, mm_count(map));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 50);
mm_free(map);
});
CMC_CREATE_TEST(remove_if, {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
for (size_t i = 1; i <= 100; i++)
cmc_assert(mm_insert(map, i % 20, i));
size_t r;
for (size_t i = 1; i <= 100; i++)
cmc_assert(mm_remove_if(map, i % 20, &r, i % 2 == 0) || i % 2 != 0);
cmc_assert_equals(size_t, 50, mm_count(map));
CMC_TEST_PASS_ELSE_FAIL(mm_count(map) == 50);
mm_free(map);
});
CMC_CREATE_TEST(get[key_ordering], {
multimap *map = mm_new(100, 0.8, cmp, hash);
cmc_assert_not_equals(ptr, NULL, map);
/* Assert that the value 100 is accessed before the value 101 for the
* same key */
cmc_assert(mm_insert(map, 10, 100));
cmc_assert(mm_insert(map, 20, 102));
cmc_assert(mm_insert(map, 10, 101));
cmc_assert_equals(size_t, 100, mm_get(map, 10));
CMC_TEST_PASS_ELSE_FAIL(mm_get(map, 10) == 100);
mm_free(map);
});
});