Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/graph traversal #30

Merged
merged 7 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ jobs:
packages:
- g++-4.9
env:
- MATRIX_EVAL="CC=gcc-4.9 && STD=-std=gnu99"
- MATRIX_EVAL="CC=gcc-4.9"
- STD="-std=gnu99"

# works on Precise and Trusty
- os: linux
Expand Down Expand Up @@ -89,7 +90,7 @@ script:
- ./dist/linkedlist
- ./dist/doublelinkedlist
- ./dist/graph
- if [ $TRAVIS_OS_NAME == linux ]; then cppcheck --error-exitcode=1 --enable=warning,performance,information,style ./src/ ; fi
- if [ $TRAVIS_OS_NAME == linux ]; then cppcheck --error-exitcode=1 --inline-suppr --enable=warning,performance,information,style --template='{file}:{line},{severity},{id},{message}' ./src/ ; fi
- gcov ./dist/*.gcno

after_success:
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ dll_free_alt(l, true); // even free the data field

## graph

This library adds a directed graph implementation that allows for any data type to be used for vertex or edge metadata. It tracks all the verticies and edges inserted into the graph and helps ensure that there are no dangling edges. Macros are provided to allow for iterating over vertcies or over the edges that emanate from the vertex.
This library adds a directed graph implementation that allows for any data type to be used for vertex or edge metadata. It tracks all the vertices and edges inserted into the graph and helps ensure that there are no dangling edges.

There are several ways to traverse the graph or to easily loop over vertices and edges. Macros are provided to allow for iterating over vertices or over the edges that emanate from the vertex: `g_iterate_vertices` and `g_iterate_edges`. There are also to helper functions to do either a breadth first or depth first traverse starting from a particular vertex: `g_breadth_first_traverse` and `g_depth_first_traverse`.

All functions are documented within the `graph.h` file.

Expand Down Expand Up @@ -297,7 +299,7 @@ g_edge_add(g, 2, 3, "400 miles"); // boston to cincinati
// iterate over the verticies
vertex_t v;
unsigned int i, j;
g_iterate_verticies(g, v, i) {
g_iterate_vertices(g, v, i) {
printf("idx: %d\tcity: %s\n", i, g_vertex_metadata(v));

// iterate over the edges!
Expand Down
56 changes: 16 additions & 40 deletions examples/graph_example.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,24 @@ int main(int argc, char const *argv[]) {
__add_highway_as_edge(g, 8, 6, 420); /* LV - SLC */
__add_highway_as_edge(g, 6, 10, 735); /* SLC - SF */

printf("Breadth First Search; starting at New York: \n");
unsigned int* bfs = __breadth_first_search(g, g_vertex_get(g, 0));
for (i = 0; i < g_vertices_inserted(g); i++) {
printf("Breadth First Traverse; starting at New York: \n");
unsigned int size, len;
unsigned int* bfs = g_breadth_first_traverse(g, g_vertex_get(g, 0), &size);
unsigned int* dfs = g_depth_first_traverse(g, g_vertex_get(g, 0), &len);
printf("Size: %u\n", size);
for (i = 0; i < size; i++) {
printf("%d, ", bfs[i]);
}
printf("\n");
printf("\n\n\n");

for (i = 0; i < g_vertices_inserted(g); i++) {
printf("Depth First Traverse: starting from New York: \n");
printf("Size: %u\n", len);
for (i = 0; i < size; i++) {
printf("%d, ", dfs[i]);
}
printf("\n\n\n");

for (i = 0; i < size; i++) {
v = g_vertex_get(g, bfs[i]);
city* c = g_vertex_metadata(v);
__print_city(c);
Expand All @@ -93,6 +103,7 @@ int main(int argc, char const *argv[]) {
}
g_free(g);
free(bfs);
free(dfs);

if (verbose == true)
printf("Completed!\n");
Expand Down Expand Up @@ -137,38 +148,3 @@ static char* __str_duplicate(const char* s) {
buf[len] = '\0';
return buf;
}


#define SET_BIT(A,k) (A[((k) / 8)] |= (1 << ((k) % 8)))
#define CHECK_BIT(A, k) (A[((k) / 8)] & (1 << ((k) % 8)))

unsigned int* __breadth_first_search(graph_t g, vertex_t start) {
unsigned int* ret = calloc(g_vertices_inserted(g), sizeof(unsigned int));

/* Use a bitarray to track which nodes we have visited
NOTE: this is not always correct, but it will work since it is larger! */
char* bitarray = calloc(g_vertices_inserted(g) / 8 + 1, sizeof(char));
unsigned int id = g_vertex_id(start);
SET_BIT(bitarray, id);

int cur_pos = 0;
int pos = 0;
ret[pos++] = id;

edge_t e;
unsigned int i;
while (cur_pos != pos) {
vertex_t v = g_vertex_get(g, ret[cur_pos]);
g_iterate_edges(v, e, i) {
id = g_edge_dest(e);
if (CHECK_BIT(bitarray, id) != 0)
continue; /* already visited */
SET_BIT(bitarray, id);
ret[pos++] = id;
}
cur_pos++;
}

free(bitarray);
return ret;
}
2 changes: 1 addition & 1 deletion src/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static int __str_find_any(const char* s, const char* s2);
static size_t __str_find_cnt_any(const char* s, const char* s2);
static void __parse_file_info(const char* full_filepath, char** filepath, char** filename);
static void __free_double_array(char** arr, size_t num_elms);
static int __cmp_str(const void* a, const void* b);
static int __cmp_str(const void* a, const void* b);
/* wrapper functions for windows and posix systems support */
static int __fs_mkdir(const char* path, mode_t mode);
static int __fs_rmdir(const char* path);
Expand Down
72 changes: 71 additions & 1 deletion src/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ typedef struct __edge_node{
void* metadata;
} Edge;

/* private functions */
static void __traverse_depth_first(graph_t g, unsigned int* res, char* bitarray, unsigned int* size);

/*******************************************************************************
* Graph Properties / Functions
Expand Down Expand Up @@ -210,7 +212,7 @@ edge_t g_edge_add(graph_t g, unsigned int src, unsigned int dest, void* metadata
vertex_t v_src = g->verts[src];
unsigned int outs = (v_src->num_edges_out)++;
if (outs >= v_src->_max_edges) {
unsigned int new_num_edges = g->_max_edges * 2; /* double */
unsigned int new_num_edges = v_src->_max_edges * 2; /* double */
edge_t* tmp = realloc(v_src->edges, new_num_edges * sizeof(edge_t));
for (i = outs; i < new_num_edges; i++)
tmp[i] = NULL;
Expand Down Expand Up @@ -331,3 +333,71 @@ void g_edge_free_alt(edge_t e, bool free_metadata) {
free(e->metadata);
free(e);
}


/*******************************************************************************
* traversals
*******************************************************************************/
#define SET_BIT(A,k) (A[((k) / 8)] |= (1 << ((k) % 8)))
#define CHECK_BIT(A, k) (A[((k) / 8)] & (1 << ((k) % 8)))
#define CEILING(n,d) ((n / d) + (n % d > 0))
unsigned int* g_breadth_first_traverse(graph_t g, vertex_t v, unsigned int* size) {
*size = 0;
unsigned int* ret = calloc(g_num_vertices(g), sizeof(unsigned int));
/* we will use a bitarray to track which vertices have been visited */
char* bitarray = calloc(CEILING(g_vertices_inserted(g), 8), sizeof(char));
unsigned int id = g_vertex_id(v);
SET_BIT(bitarray, id);

int cur_pos = 0, pos = 0;
ret[pos++] = id;

edge_t e;
unsigned int i;
while (cur_pos != pos) { /* this signifies that we have hit the end */
vertex_t v = g_vertex_get(g, ret[cur_pos]);
g_iterate_edges(v, e, i) {
id = g_edge_dest(e);
if (CHECK_BIT(bitarray, id) != 0)
continue; /* already visited */
SET_BIT(bitarray, id);
ret[pos++] = id;
}
++cur_pos;
}

free(bitarray);
*size = pos;
return ret;
}


unsigned int* g_depth_first_traverse(graph_t g, vertex_t v, unsigned int* size) {
*size = 0;
unsigned int* ret = calloc(g_num_vertices(g), sizeof(unsigned int));
char* bitarray = calloc(CEILING(g_vertices_inserted(g), 8), sizeof(char));
unsigned int id = g_vertex_id(v);
SET_BIT(bitarray, id);
ret[0] = id;
*size = *size + 1;
__traverse_depth_first(g, ret, bitarray, size);
free(bitarray);
return ret;
}


static void __traverse_depth_first(graph_t g, unsigned int* res, char* bitarray, unsigned int* size) {
vertex_t v = g_vertex_get(g, res[*size - 1]);
// cppcheck-suppress variableScope
edge_t e;
unsigned int i, id;
g_iterate_edges(v, e, i) {
id = g_edge_dest(e);
if (CHECK_BIT(bitarray, id) != 0)
continue; /* already visited */
SET_BIT(bitarray, id);
res[*size] = id;
*size = *size + 1;
__traverse_depth_first(g, res, bitarray, size);
}
}
25 changes: 18 additions & 7 deletions src/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ void g_edge_free_alt(edge_t e, bool free_metadata);


/*******************************************************************************
*
* Iterators - Iterate over the vertices and edges of a vertex easily
*******************************************************************************/
/* Macro to easily iterate over all the vertices in the graph
NOTE:
Expand All @@ -151,11 +151,22 @@ void g_edge_free_alt(edge_t e, bool free_metadata);
v - The vertex
e - An edge_t pointer that will hold the edges in the loop
i - An unsigned int that will be modified during the loop */
#define g_iterate_edges(v, e, i) for (i = 0; i < g_vertex_num_edges_out(v); i++) if ((e = g_vertex_edge(v, i)) != NULL)



// unsigned int* g_depth_first_search(graph_t g, vertex_t v);
// unsigned int* g_breadth_first_search(graph_t g, vertex_t v);
#define g_iterate_edges(v, e, i) for (i = 0; i < g_vertex_num_edges_out(v); i++) if ((e = g_vertex_edge(v, i)) != NULL)

/* Return an array with a listing of the vertices in bredth first fashion;
this is useful for finding what order one should traverse the list starting
at vertex v in a bredth first fashion.
NOTE: Up to the caller to free the corresponding memory.
NOTE: size is set to the number of elements in the unsigned int array
NOTE: The returned array contains the vertex ids of each vertex, in order */
unsigned int* g_breadth_first_traverse(graph_t g, vertex_t v, unsigned int* size);

/* Return an array with a listing of the vertices in depth first fashion;
this is useful for finding what order one should traverse the graph
starting at vertex v in a depth first fashion.
NOTE: Up to the caller to free the corresponding memory
NOTE: size is set to the number of elements in the unsigned int array
NOTE: The returned array contains the vertex ids of each vertex, in order */
unsigned int* g_depth_first_traverse(graph_t g, vertex_t v, unsigned int* size);

#endif /* BARRUST_GRAPH_H__ */
60 changes: 60 additions & 0 deletions tests/graph_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,62 @@ MU_TEST(test_g_vertex_edge_error) {
mu_assert_string_eq(NULL, (char*)g_vertex_edge(v, 17));
}

/*******************************************************************************
* Test iterating with breadth and depth
*******************************************************************************/
MU_TEST(test_g_breadth_first_traverse) {
__add_vertices(g, 15);
__add_edge(g, 0, 1, 0);
__add_edge(g, 0, 3, 0);
__add_edge(g, 0, 2, 0);
__add_edge(g, 1, 4, 0);
__add_edge(g, 1, 5, 0);
__add_edge(g, 2, 9, 0);
__add_edge(g, 3, 10, 0);
__add_edge(g, 10, 6, 0);
__add_edge(g, 4, 8, 0);
__add_edge(g, 4, 12, 0);
__add_edge(g, 6, 14, 0);
__add_edge(g, 9, 13, 0);
__add_edge(g, 9, 1, 0);

int answers[] = {0, 1, 3, 2, 4, 5, 10, 9, 8, 12, 6, 13, 14};
unsigned int len, i;
unsigned int* res = g_breadth_first_traverse(g, g_vertex_get(g, 0), &len);

mu_assert_int_eq(13, len);
for (i = 0; i < len; i++) {
mu_assert_int_eq(answers[i], res[i]);
}
free(res);
}


MU_TEST(test_g_depth_first_traverse) {
__add_vertices(g, 15);
__add_edge(g, 0, 1, 0);
__add_edge(g, 0, 3, 0);
__add_edge(g, 0, 2, 0);
__add_edge(g, 1, 4, 0);
__add_edge(g, 1, 5, 0);
__add_edge(g, 2, 9, 0);
__add_edge(g, 3, 10, 0);
__add_edge(g, 10, 6, 0);
__add_edge(g, 4, 8, 0);
__add_edge(g, 4, 12, 0);
__add_edge(g, 6, 14, 0);
__add_edge(g, 9, 13, 0);
__add_edge(g, 9, 1, 0);

int answers[] = {0, 1, 4, 8, 12, 5, 3, 10, 6, 14, 2, 9, 13};
unsigned int len, i;
unsigned int* res = g_depth_first_traverse(g, g_vertex_get(g, 0), &len);
mu_assert_int_eq(13, len);
for (i = 0; i < len; i++) {
mu_assert_int_eq(answers[i], res[i]);
}
free(res);
}

/*******************************************************************************
* Test Suite Setup
Expand Down Expand Up @@ -380,6 +436,10 @@ MU_TEST_SUITE(test_suite) {
MU_RUN_TEST(test_updating_edge_metadata);
MU_RUN_TEST(test_iterate_edges_some_removed);
MU_RUN_TEST(test_iterate_edges_some_removed_add_back);

/* Traversals */
MU_RUN_TEST(test_g_breadth_first_traverse);
MU_RUN_TEST(test_g_depth_first_traverse);
}


Expand Down