Compare commits

...

2 Commits

Author SHA1 Message Date
5247394d35 Add unit tests for sparse array functions. 2017-01-21 16:43:47 -08:00
3aedc61da4 Implement a sparse array / set. 2017-01-21 01:20:00 -08:00
4 changed files with 295 additions and 6 deletions

View File

@@ -4,7 +4,7 @@ project(libds)
set_property(GLOBAL PROPERTY C_STANDARD 90)
ADD_COMPILE_OPTIONS(-pedantic -Wall)
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c)
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c src/sprs.c)
add_executable(libds src/main.c)
target_include_directories(ds PUBLIC include)

142
include/sprs.h Normal file
View File

@@ -0,0 +1,142 @@
#ifndef LIBDS_SPRS_HEADER
#define LIBDS_SPRS_HEADER
#include "libds.h"
/**
* Sparse set implementation with the capacity to store data.
* Sparse sets are initially defined as having two arrays of integers,
* one "dense" array, into which new elements are added sequentially, and another
* "sparse" array into which the elements' indexes in the dense array are put at
* their index.
*
* For instance, after adding the elements 1, 0, and 3 to a sparse array,
* it may look like:
* (Size) (Capacity)
* | |
* [1 ][0 ][3 ][..][..][..] (Dense)
* [1 ][0 ][..][2 ][..][..] (Sparse)
* As seen in the above representation, the element in the dense array at index 0 is 1,
* and the element at index 1 in the sparse array points to its location in the dense array.
* It's very quick to check whether an element is part of this sparse set:
* if sparse[element] < size && dense[sparse[element]] == element.
* Iteration is also O(n), thanks to the dense array, and no initial memory clearing is required.
*
* The below implementation adds an additional piece of data to the sparse array, a void* so that an item
* can be associated with an integer index and stored / retrieved / ismember'ed together with it.
*/
/**
* A single element in a sparse array.
*/
struct sprs_element_s {
/**
* The index of this element in the dense array.
*/
int index;
/**
* A piece of data (optional) associated with this index.
*/
void* data;
};
/**
* A sparse array / set.
*/
struct sprs_s {
/**
* The maximum size of the set. This is the limit for integer indices.
*/
int capacity;
/**
* The current size of the sparse set, and the next available index in the dense array.
*/
int size;
/**
* The dense array.
* This is a pointer so that an array of a certain size can be allocated as needed.
*/
int* dense;
/**
* The sparse array.
* This is a pointer so that an array of a certain size can be allocated as needed.
*/
struct sprs_element_s* sparse;
};
typedef struct sprs_s sprs;
/**
* Initializes a sparse set with default values.
* This does not allocate any memory for the arrays.
* @param sprs the sparse set / array to initialize.
*/
void sprs_init(sprs* sprs);
/**
* Allocates the necessary memory for a sparse set's
* dense and sparse arrays.
* @param sprs the sparse set for which memory should be allocated.
* @param size the maximum capacity of the sparse set to use.
* @return LIBDS_SUCCESS if all goes well, or LIBDS_MALLOC if an allocation failed.
*/
libds_result sprs_setup(sprs* sprs, int size);
/**
* Frees memory allocated by the sparse set, and resets its capacity and size.
* @param sprs the sparse set to free.
*/
void sprs_free(sprs* sprs);
/**
* Stores a value in the sparse set under a given index.
* @param sprs the sparse set into which to store the value.
* @param index the index at which to store the value
* @param data the value to store.
*/
void sprs_put(sprs* sprs, int index, void* data);
/**
* Checks if the sparse set contains data under a given index.
* @param sprs the sparse set to check.
* @param index the index for which to check.
* @return 1 if the index exists, 0 if not.
*/
int sprs_contains(sprs* sprs, int index);
/**
* Gets the value stored under the given sparse set index.
* This will check for whether the index is in the sparse set first,
* and return NULL if it's not.
* Please not that this exhibit the !exact same! behavior if a NULL was stored
* under the index or if the index was never populated. use sprs_contains to
* check whether the index is populated if this is important.
* @param sprs the sparse set to check.
* @param index the index from under which to retrieve the value.
* @return the value stored under the index, or NULL if there is nothing there.
*/
void* sprs_get(sprs* sprs, int index);
/**
* Runs through every element in the sparse set, and compares it against the
* given data using the given comparison function. If the comparison function returns
* true, returns the element that was passed to it. If the comparison function returns
* true for no element, returns NULL.
* @param sprs the sparse set to iterate through.
* @param data the data to compare elements against
* @param compare the comparison function
* @return the first element that is matched by the comparison function, or NULL if none are matched.
*/
void* sprs_find(sprs* sprs, void* data, compare_func compare);
/**
* Runs through every element in the sparse set, and compares it against the
* given data using the given comparison function. If the comparison function returns
* true, calls the foreach function, passing it the element and the variable argument list.
* Stops if any foreach function returns a nonzero code.
* @param sprs the sparse set to perform the operation on
* @param data the data to pass the comparison function
* @param compare the comparison function operating on the data and each element in the list.
* @param foreach the function to be called on every element recognized by the comparison function
* @param ... variable arguments to be passed on to the foreach function
* @return 0 if all goes well, or the first nonzero code returned by foreach.
*/
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...);
#endif

View File

@@ -5,6 +5,7 @@
#include "ht.h"
#include "ll.h"
#include "vec.h"
#include "sprs.h"
int _test_vec_foreach_func(void* data, va_list args) {
char* real_data = data;
@@ -252,6 +253,82 @@ int test_ll_poptail(){
return return_code;
}
int _test_sprs_foreach_count(void* data, va_list args){
(*(va_arg(args, int*)))++;
return 0;
}
int test_sprs_basic(){
sprs sprs;
sprs_init(&sprs);
sprs_setup(&sprs, 1);
sprs_free(&sprs);
return 1;
}
int test_sprs_put(){
int return_code;
sprs sprs;
sprs_init(&sprs);
sprs_setup(&sprs, 4);
sprs_put(&sprs, 2, NULL);
return_code = sprs.dense[0] == 2 && sprs.sparse[2].index == 0 && sprs.sparse[2].data == NULL;
sprs_free(&sprs);
return return_code;
}
int test_sprs_contains(){
int return_code;
sprs sprs;
sprs_init(&sprs);
sprs_setup(&sprs, 4);
sprs_put(&sprs, 2, NULL);
sprs_put(&sprs, 3, NULL);
return_code = (sprs_contains(&sprs, 2) == 1) && (sprs_contains(&sprs, 3) == 1) && (sprs_contains(&sprs, 0) == 0);
sprs_free(&sprs);
return return_code;
}
int test_sprs_get(){
int return_code;
sprs sprs;
char* test_string = "test";
sprs_init(&sprs);
sprs_setup(&sprs, 4);
sprs_put(&sprs, 2, test_string);
sprs_put(&sprs, 3, NULL);
return_code = sprs_get(&sprs, 2) == test_string && sprs_get(&sprs, 3) == NULL && sprs_get(&sprs, 0) == NULL;
sprs_free(&sprs);
return return_code;
}
int test_sprs_find(){
int return_code;
sprs sprs;
char* test_string = "test";
sprs_init(&sprs);
sprs_setup(&sprs, 4);
sprs_put(&sprs, 3, test_string);
return_code = sprs_find(&sprs, test_string, compare_pointer) == test_string;
sprs_free(&sprs);
return return_code;
}
int test_sprs_foreach(){
int return_code;
sprs sprs;
int count = 0;
sprs_init(&sprs);
sprs_setup(&sprs, 4);
sprs_put(&sprs, 0, NULL);
sprs_put(&sprs, 2, NULL);
sprs_put(&sprs, 3, NULL);
sprs_foreach(&sprs, NULL, compare_always, _test_sprs_foreach_count, &count);
return_code = count == 3;
sprs_free(&sprs);
return return_code;
}
int run_test(char* test_name, int (*test_func)()) {
int success = test_func();
printf("Running test %-15s . . . ", test_name);
@@ -260,18 +337,18 @@ int run_test(char* test_name, int (*test_func)()) {
}
int main(int argc, char** argv) {
char* test_names[19] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
char* test_names[25] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
"vec_foreach", "vec_index", "ht_basic", "ht_put",
"ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail"};
"ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail", "sprs_basic", "sprs_put", "sprs_contains", "sprs_get", "sprs_find", "sprs_foreach"};
int (*test_functions[19])() = {
int (*test_functions[25])() = {
test_vec_basic, test_vec_add, test_vec_remove, test_vec_find,
test_vec_foreach, test_vec_index, test_ht_basic, test_ht_put,
test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail};
test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail, test_sprs_basic, test_sprs_put, test_sprs_contains, test_sprs_get, test_sprs_find, test_sprs_foreach};
int test_index = 0;
int result = 1;
for (; test_index < 19 && result; test_index++) {
for (; test_index < 25 && result; test_index++) {
result = run_test(test_names[test_index], test_functions[test_index]);
}
return result ? EXIT_SUCCESS : EXIT_FAILURE;

70
src/sprs.c Normal file
View File

@@ -0,0 +1,70 @@
#include "sprs.h"
#include <stdlib.h>
void sprs_init(sprs* sprs){
sprs->capacity = 0;
sprs->size = 0;
sprs->dense = NULL;
sprs->sparse = NULL;
}
libds_result sprs_setup(sprs* sprs, int size){
libds_result result = LIBDS_SUCCESS;
sprs->sparse = malloc(sizeof(*(sprs->sparse)) * size);
sprs->dense = malloc(sizeof(*(sprs->dense)) * size);
if(sprs->sparse == NULL || sprs->dense == NULL){
free(sprs->sparse);
free(sprs->dense);
result = LIBDS_MALLOC;
} else {
sprs->capacity = size;
}
return result;
}
void sprs_free(sprs* sprs){
free(sprs->sparse);
free(sprs->dense);
sprs->capacity = 0;
sprs->size = 0;
sprs->dense = NULL;
sprs->sparse = NULL;
}
void sprs_put(sprs* sprs, int index, void* data){
if(index < sprs->capacity && sprs->size < sprs->capacity){
sprs->dense[sprs->size++] = index;
sprs->sparse[index].index = sprs->size - 1;
sprs->sparse[index].data = data;
}
}
int sprs_contains(sprs* sprs, int index){
return sprs->sparse[index].index < sprs->size && sprs->dense[sprs->sparse[index].index] == index;
}
void* sprs_get(sprs* sprs, int index){
void* data = NULL;
if(sprs_contains(sprs, index)){
data = sprs->sparse[index].data;
}
return data;
}
void* sprs_find(sprs* sprs, void* data, compare_func compare){
int index = 0;
void* to_return = NULL;
for(; index < sprs->size && to_return == NULL; index++){
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
to_return = sprs->sparse[sprs->dense[index]].data;
}
}
return to_return;
}
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...){
int index = 0;
int return_code = 0;
va_list args;
for(; index < sprs->size && return_code == 0; index++){
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
va_start(args, foreach);
return_code = foreach(sprs->sparse[sprs->dense[index]].data, args);
va_end(args);
}
}
return return_code;
}