Skip to content

Commit fbf47a4

Browse files
authored
Feature/graphs (#28)
* first cut; not complete * fix the test template * additional graph functionality and tests * add the graph test to ci * more tests * add iterate over verticies macro * add iterators and test them; add pointers to out edges for each vertex for easier traversal * valgrind fixes * update naming convention; document funtions * update readme and docs * fix the need for success and failure macros * add some graph examples; a few more functions * add tests for updating metadata * add a few additional edge case tests * typo
1 parent 8c684dd commit fbf47a4

File tree

8 files changed

+1161
-18
lines changed

8 files changed

+1161
-18
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ script:
8888
- ./dist/timing
8989
- ./dist/linkedlist
9090
- ./dist/doublelinkedlist
91+
- ./dist/graph
9192
- if [ $TRAVIS_OS_NAME == linux ]; then cppcheck --error-exitcode=1 --enable=warning,performance,information,style ./src/ ; fi
9293
- gcov ./dist/*.gcno
9394

Makefile

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,9 @@ EXAMPLEDIR=examples
55
COMPFLAGS=-Wall -Wpedantic -Winline -Wextra
66

77

8-
all: libraries examples
9-
$(CC) $(STD) $(DISTDIR)/stringlib.o $(TESTDIR)/stringlib_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/strlib
10-
$(CC) $(STD) $(TESTDIR)/timing_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/timing
11-
$(CC) $(STD) $(DISTDIR)/bitarray-lib.o $(TESTDIR)/bitarray_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/bitarray
12-
$(CC) $(STD) $(DISTDIR)/fileutils-lib.o $(TESTDIR)/fileutils_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/fileutils
13-
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(TESTDIR)/linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/linkedlist
14-
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(TESTDIR)/doubly_linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/doublelinkedlist
8+
all: libraries examples test
159

16-
libraries: string bitarray fileutils linkedlist doublylinkedlist
10+
libraries: string bitarray fileutils linkedlist doublylinkedlist graph
1711

1812
string:
1913
$(CC) $(STD) -c $(SRCDIR)/stringlib.c -o $(DISTDIR)/stringlib.o $(CCFLAGS) $(COMPFLAGS)
@@ -30,11 +24,21 @@ linkedlist:
3024
doublylinkedlist:
3125
$(CC) $(STD) -c $(SRCDIR)/dllist.c -o $(DISTDIR)/dllist-lib.o $(CCFLAGS) $(COMPFLAGS)
3226

27+
graph:
28+
$(CC) $(STD) -c $(SRCDIR)/graph.c -o $(DISTDIR)/graph-lib.o $(CCFLAGS) $(COMPFLAGS)
29+
3330
debug: CCFLAGS += -g
3431
debug: all
3532

3633
test: CCFLAGS += -coverage
37-
test: all
34+
test: libraries
35+
$(CC) $(STD) $(DISTDIR)/stringlib.o $(TESTDIR)/stringlib_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/strlib
36+
$(CC) $(STD) $(TESTDIR)/timing_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/timing
37+
$(CC) $(STD) $(DISTDIR)/bitarray-lib.o $(TESTDIR)/bitarray_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/bitarray
38+
$(CC) $(STD) $(DISTDIR)/fileutils-lib.o $(TESTDIR)/fileutils_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/fileutils
39+
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(TESTDIR)/linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/linkedlist
40+
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(TESTDIR)/doubly_linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/doublelinkedlist
41+
$(CC) $(STD) $(DISTDIR)/graph-lib.o $(TESTDIR)/graph_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/graph
3842

3943
examples: libraries
4044
$(CC) $(STD) $(EXAMPLEDIR)/timing_example.c $(CCFLAGS) $(COMPFLAGS) -lm -o ./$(DISTDIR)/ex_timing
@@ -43,6 +47,7 @@ examples: libraries
4347
$(CC) $(STD) $(DISTDIR)/stringlib.o $(EXAMPLEDIR)/stringlib_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_stringlib
4448
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(EXAMPLEDIR)/linkedlist_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_linkedlist
4549
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(EXAMPLEDIR)/doublylinkedlist_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_doublylinkedlist
50+
$(CC) $(STD) $(DISTDIR)/graph-lib.o $(EXAMPLEDIR)/graph_example.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/ex_graph
4651

4752
clean:
4853
rm -rf ./$(DISTDIR)/*

README.md

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![codecov](https://codecov.io/gh/barrust/c-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/barrust/c-utils)
55

66

7-
This project provides a collection of utility libraries to help reduce the need to write similar code for each project on an ad-hoc basis. The need is based on what I have needed in most projects but are ended up written, as needed, and usually differently each time and without unit tests. The goal is to provide a single place to store each of these libraries and to provide unit tests.
7+
This project provides a collection of utility libraries to help reduce the need to write similar code for each project on an ad-hoc basis. The need is based on what I have needed in most projects but are ended up written, as needed, and usually differently each time and without unit tests. The goal is to provide a single place to store each of these libraries and to provide unit tests.
88

99
If there are other commonly used code or data-structures that should be added, please add a feature request!
1010

@@ -14,6 +14,7 @@ If there are other commonly used code or data-structures that should be added, p
1414
* [bitarray](#bitarray)
1515
* [linked list](#linkedlist)
1616
* [doubly linked list](#doublylinkedlist)
17+
* [graph](#graph)
1718
* [timing-c](#timing-c)
1819

1920
##### Recommended External Libraries
@@ -23,29 +24,29 @@ If there are other commonly used code or data-structures that should be added, p
2324

2425
##### Unit tests
2526

26-
Unit tests are provided using the [minunit](https://github.com/siu/minunit) library. Each function is, **hopefully**, fully covered. Any help in getting as close to 100% coverage would be great!
27+
Unit tests are provided using the [minunit](https://github.com/siu/minunit) library. Each function is, **hopefully**, fully covered. Any help in getting as close to 100% coverage would be much appreciated!
2728

28-
To run the unittest suite, simply compile the test files using the provided `Makefile` with the command `make`. Then you can execute the tests using `./dist/bitarray`, `./dist/strlib`, `./dist/fileutils`, or `./dist/timing` executables.
29+
To run the unittest suite, simply compile the test files using the provided `Makefile` with the command `make test`. Then you can execute the tests using the executables `./dist/bitarray`, `./dist/strlib`, `./dist/fileutils`, `./dist/graph`, or `./dist/timing`.
2930

3031
#### Issues
3132

32-
If an unexpected outcome occurs, please submit an issue with a ***minimal code example*** that encapsulates the error.
33+
If an unexpected outcome occurs, please submit an issue on github. Please also provide a ***minimal code example*** that encapsulates the error.
3334

34-
A great issue would provide the following:
35-
> s_remove_unwanted_chars does shows duplicate entries after removal.
35+
A great [issue](https://github.com/barrust/c-utils/issues) would provide the following:
36+
> s_remove_unwanted_chars shows duplicate entries after removal.
3637
> ``` c
3738
> char* test[] = "This is a test";
3839
> // expect "Ths s a es"
3940
> // get "Ths s a esest"
40-
> // Notice the extra `est`; likely due to not erasing
41+
> // Notice the extra `est`; likely due to not erasing the trailing chars
4142
> s_remove_unwanted_chars(test, "ti");
4243
> ```
4344
4445
#### Examples
4546
4647
Example programs are provided in the `./examples` folder. You can compile these examples using `make examples`. They can be run using `./dist/ex_timing`, `./dist/ex_bitarray`, `./dist/ex_fileutils`, `./dist/ex_stringlib`, `./dist/ex_linkedlist`, and `./dist/ex_doublylinkedlist`.
4748
48-
Not all functionality is demonstrated for all libraries, but hopefully enough is present to help make using these libraries easier. All functionality for each library is documented in the `.h` files.
49+
Not all functionality is demonstrated for all libraries, but hopefully enough is present to help make using these libraries easier. All functionality for each library is documented in the `.h` files.
4950
5051
5152
## stringlib
@@ -261,6 +262,55 @@ while (node != NULL) {
261262
dll_free_alt(l, true); // even free the data field
262263
```
263264
265+
## graph
266+
267+
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.
268+
269+
All functions are documented within the `graph.h` file.
270+
271+
#### Compiler Flags
272+
273+
***NONE*** - There are no needed compiler flags for the `graph` library
274+
275+
#### Usage
276+
277+
To use, simply copy the `graph.h` and `graph.c` files into your project and include it where needed.
278+
279+
``` c
280+
#include "graph.h"
281+
282+
graph_t g = g_init();
283+
284+
// add some verticies
285+
g_vertex_add(g, "Washington D.C.");
286+
g_vertex_add(g, "Raleigh NC");
287+
g_vertex_add(g, "Boston, Mass");
288+
g_vertex_add(g, "Cincinati, OH");
289+
290+
// add edges
291+
g_edge_add(g, 0, 1, "250 miles"); // washington to raliegh
292+
g_edge_add(g, 0, 2, "150 miles"); // washington to boston
293+
g_edge_add(g, 0, 3, "300 miles"); // washington to cincinati
294+
g_edge_add(g, 1, 3, "500 miles"); // raliegh to cincinati
295+
g_edge_add(g, 2, 3, "400 miles"); // boston to cincinati
296+
297+
// iterate over the verticies
298+
vertex_t v;
299+
unsigned int i, j;
300+
g_iterate_verticies(g, v, i) {
301+
printf("idx: %d\tcity: %s\n", i, g_vertex_metadata(v));
302+
303+
// iterate over the edges!
304+
edge_t e;
305+
g_iterate_edges(v, e, j) {
306+
vertex_t dest = g_vertex_get(g_edge_dest(e));
307+
printf("\tto: %s\tdistance: %s\n", g_vertex_metadata(dest), g_edge_metadata(e));
308+
}
309+
}
310+
311+
g_free_alt(g, false);
312+
```
313+
264314
265315
## timing-c
266316

examples/graph_example.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <stdbool.h>
5+
#include <string.h>
6+
#include <assert.h>
7+
#include "../src/graph.h"
8+
9+
10+
typedef struct city {
11+
char* name;
12+
double latitude;
13+
double longitude;
14+
} city;
15+
16+
typedef struct highway {
17+
int miles;
18+
} highway;
19+
20+
21+
/* Private Functions */
22+
static void __add_city_as_vertex(graph_t g, char* name, double latitude, double longitude); /* we get to always make N and W.*/
23+
static void __print_city(city* c);
24+
static void __free_city(city* c);
25+
static void __add_highway_as_edge(graph_t g, int src, int dest, int miles);
26+
static char* __str_duplicate(const char* s);
27+
28+
unsigned int* __breadth_first_search(graph_t g, vertex_t start);
29+
30+
31+
32+
int main(int argc, char const *argv[]) {
33+
bool verbose = false;
34+
35+
if (argc == 2 && strcmp(argv[1], "-v") == 0) {
36+
verbose = true;
37+
}
38+
39+
graph_t g = g_init();
40+
vertex_t v;
41+
unsigned int i;
42+
43+
/* add the vertices */
44+
__add_city_as_vertex(g, "New York", 40.7128, 74.0060);
45+
__add_city_as_vertex(g, "Washington D.C.", 38.9072, 77.0369);
46+
__add_city_as_vertex(g, "Cleveland", 41.4993, 81.6944);
47+
__add_city_as_vertex(g, "Detroit", 42.3314, 83.0458);
48+
__add_city_as_vertex(g, "Atlanta", 33.7490, 84.3880);
49+
__add_city_as_vertex(g, "St. Louis", 38.6270, 90.1994);
50+
__add_city_as_vertex(g, "Dallas", 32.7767, 96.7970);
51+
__add_city_as_vertex(g, "Salt Lake", 40.7608, 111.8910);
52+
__add_city_as_vertex(g, "Pheonix", 33.4484, 112.0740);
53+
__add_city_as_vertex(g, "Las Vegas", 36.1699, 115.1398);
54+
__add_city_as_vertex(g, "Los Angeles", 34.0522, 118.2437);
55+
__add_city_as_vertex(g, "San Fransisco", 37.7749, 122.4194);
56+
57+
/* add edges between the vertices */
58+
__add_highway_as_edge(g, 0, 1, 227); /* NY - DC */
59+
__add_highway_as_edge(g, 1, 0, 227); /* DC - NY */
60+
__add_highway_as_edge(g, 1, 2, 371); /* DC - Cleveland */
61+
__add_highway_as_edge(g, 1, 4, 639); /* DC - Atlanta */
62+
__add_highway_as_edge(g, 4, 1, 639); /* Atlanta - DC */
63+
__add_highway_as_edge(g, 2, 3, 168); /* Cleveland - Detroit */
64+
__add_highway_as_edge(g, 2, 4, 557); /* Cleveland - St. Louis */
65+
__add_highway_as_edge(g, 4, 5, 630); /* St. Louis - Dallas */
66+
__add_highway_as_edge(g, 5, 7, 1064); /* Dallas - Pheonix */
67+
__add_highway_as_edge(g, 7, 8, 300); /* Pheonix - LV */
68+
__add_highway_as_edge(g, 7, 9, 372); /* Pheonix - LA */
69+
__add_highway_as_edge(g, 9, 7, 372); /* LA - Pheonix */
70+
__add_highway_as_edge(g, 9, 10, 381); /* LA - SF */
71+
__add_highway_as_edge(g, 8, 6, 420); /* LV - SLC */
72+
__add_highway_as_edge(g, 6, 10, 735); /* SLC - SF */
73+
74+
printf("Breadth First Search; starting at New York: \n");
75+
unsigned int* bfs = __breadth_first_search(g, g_vertex_get(g, 0));
76+
for (i = 0; i < g_vertices_inserted(g); i++) {
77+
printf("%d, ", bfs[i]);
78+
}
79+
printf("\n");
80+
81+
for (i = 0; i < g_vertices_inserted(g); i++) {
82+
v = g_vertex_get(g, bfs[i]);
83+
city* c = g_vertex_metadata(v);
84+
__print_city(c);
85+
}
86+
87+
/* free things */
88+
g_iterate_vertices(g, v, i) {
89+
city* c = g_vertex_metadata(v);
90+
/*__print_city(c); */
91+
__free_city(c);
92+
g_vertex_metadata_update(v, NULL);
93+
}
94+
g_free(g);
95+
free(bfs);
96+
97+
if (verbose == true)
98+
printf("Completed!\n");
99+
100+
return 0;
101+
}
102+
103+
104+
/* PRIVATE FUNCTIONS */
105+
static void __add_city_as_vertex(graph_t g, char* name, double latitude, double longitude) {
106+
city* c = calloc(1, sizeof(city));
107+
108+
c->name = __str_duplicate(name);
109+
c->latitude = latitude;
110+
c->longitude = longitude;
111+
g_vertex_add(g, c);
112+
}
113+
114+
static void __print_city(city* c) {
115+
printf("City Name: %s\tLatitude: %f°N\tLongitude: %f°W\n", c->name, c->latitude, c->longitude);
116+
}
117+
118+
static void __free_city(city* c) {
119+
free(c->name);
120+
c->latitude = 0.0;
121+
c->longitude = 0.0;
122+
free(c);
123+
}
124+
125+
static void __add_highway_as_edge(graph_t g, int src, int dest, int miles) {
126+
highway* h = calloc(1, sizeof(highway));
127+
h->miles = miles;
128+
g_edge_add(g, src, dest, h);
129+
}
130+
131+
static char* __str_duplicate(const char* s) {
132+
size_t len = strlen(s);
133+
char* buf = malloc((len + 1) * sizeof(char));
134+
if (buf == NULL)
135+
return NULL;
136+
strcpy(buf, s);
137+
buf[len] = '\0';
138+
return buf;
139+
}
140+
141+
142+
#define SET_BIT(A,k) (A[((k) / 8)] |= (1 << ((k) % 8)))
143+
#define CHECK_BIT(A, k) (A[((k) / 8)] & (1 << ((k) % 8)))
144+
145+
unsigned int* __breadth_first_search(graph_t g, vertex_t start) {
146+
unsigned int* ret = calloc(g_vertices_inserted(g), sizeof(unsigned int));
147+
148+
/* Use a bitarray to track which nodes we have visited
149+
NOTE: this is not always correct, but it will work since it is larger! */
150+
char* bitarray = calloc(g_vertices_inserted(g) / 8 + 1, sizeof(char));
151+
unsigned int id = g_vertex_id(start);
152+
SET_BIT(bitarray, id);
153+
154+
int cur_pos = 0;
155+
int pos = 0;
156+
ret[pos++] = id;
157+
158+
edge_t e;
159+
unsigned int i;
160+
while (cur_pos != pos) {
161+
vertex_t v = g_vertex_get(g, ret[cur_pos]);
162+
g_iterate_edges(v, e, i) {
163+
id = g_edge_dest(e);
164+
if (CHECK_BIT(bitarray, id) != 0)
165+
continue; /* already visited */
166+
SET_BIT(bitarray, id);
167+
ret[pos++] = id;
168+
}
169+
cur_pos++;
170+
}
171+
172+
free(bitarray);
173+
return ret;
174+
}

0 commit comments

Comments
 (0)