diff --git a/CMakeLists.txt b/CMakeLists.txt index b8c6fac..e9ae4be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/sprs.h b/include/sprs.h new file mode 100644 index 0000000..f7960b2 --- /dev/null +++ b/include/sprs.h @@ -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 diff --git a/src/sprs.c b/src/sprs.c new file mode 100644 index 0000000..ac9bd33 --- /dev/null +++ b/src/sprs.c @@ -0,0 +1,70 @@ +#include "sprs.h" +#include + +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; +}