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/graphs #28

Merged
merged 15 commits into from
Jan 20, 2020
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ script:
- ./dist/timing
- ./dist/linkedlist
- ./dist/doublelinkedlist
- ./dist/graph
- if [ $TRAVIS_OS_NAME == linux ]; then cppcheck --error-exitcode=1 --enable=warning,performance,information,style ./src/ ; fi
- gcov ./dist/*.gcno

Expand Down
23 changes: 14 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@ EXAMPLEDIR=examples
COMPFLAGS=-Wall -Wpedantic -Winline -Wextra


all: libraries examples
$(CC) $(STD) $(DISTDIR)/stringlib.o $(TESTDIR)/stringlib_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/strlib
$(CC) $(STD) $(TESTDIR)/timing_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/timing
$(CC) $(STD) $(DISTDIR)/bitarray-lib.o $(TESTDIR)/bitarray_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/bitarray
$(CC) $(STD) $(DISTDIR)/fileutils-lib.o $(TESTDIR)/fileutils_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/fileutils
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(TESTDIR)/linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/linkedlist
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(TESTDIR)/doubly_linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/doublelinkedlist
all: libraries examples test

libraries: string bitarray fileutils linkedlist doublylinkedlist
libraries: string bitarray fileutils linkedlist doublylinkedlist graph

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

graph:
$(CC) $(STD) -c $(SRCDIR)/graph.c -o $(DISTDIR)/graph-lib.o $(CCFLAGS) $(COMPFLAGS)

debug: CCFLAGS += -g
debug: all

test: CCFLAGS += -coverage
test: all
test: libraries
$(CC) $(STD) $(DISTDIR)/stringlib.o $(TESTDIR)/stringlib_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/strlib
$(CC) $(STD) $(TESTDIR)/timing_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/timing
$(CC) $(STD) $(DISTDIR)/bitarray-lib.o $(TESTDIR)/bitarray_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/bitarray
$(CC) $(STD) $(DISTDIR)/fileutils-lib.o $(TESTDIR)/fileutils_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/fileutils
$(CC) $(STD) $(DISTDIR)/llist-lib.o $(TESTDIR)/linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/linkedlist
$(CC) $(STD) $(DISTDIR)/dllist-lib.o $(TESTDIR)/doubly_linked_list_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/doublelinkedlist
$(CC) $(STD) $(DISTDIR)/graph-lib.o $(TESTDIR)/graph_test.c $(CCFLAGS) $(COMPFLAGS) -o ./$(DISTDIR)/graph

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

clean:
rm -rf ./$(DISTDIR)/*
Expand Down
66 changes: 58 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![codecov](https://codecov.io/gh/barrust/c-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/barrust/c-utils)


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.
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.

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

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

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

##### Unit tests

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!
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!

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.
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`.

#### Issues

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

A great issue would provide the following:
> s_remove_unwanted_chars does shows duplicate entries after removal.
A great [issue](https://github.com/barrust/c-utils/issues) would provide the following:
> s_remove_unwanted_chars shows duplicate entries after removal.
> ``` c
> char* test[] = "This is a test";
> // expect "Ths s a es"
> // get "Ths s a esest"
> // Notice the extra `est`; likely due to not erasing
> // Notice the extra `est`; likely due to not erasing the trailing chars
> s_remove_unwanted_chars(test, "ti");
> ```

#### Examples

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`.

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.
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.


## stringlib
Expand Down Expand Up @@ -261,6 +262,55 @@ while (node != NULL) {
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.

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

#### Compiler Flags

***NONE*** - There are no needed compiler flags for the `graph` library

#### Usage

To use, simply copy the `graph.h` and `graph.c` files into your project and include it where needed.

``` c
#include "graph.h"

graph_t g = g_init();

// add some verticies
g_vertex_add(g, "Washington D.C.");
g_vertex_add(g, "Raleigh NC");
g_vertex_add(g, "Boston, Mass");
g_vertex_add(g, "Cincinati, OH");

// add edges
g_edge_add(g, 0, 1, "250 miles"); // washington to raliegh
g_edge_add(g, 0, 2, "150 miles"); // washington to boston
g_edge_add(g, 0, 3, "300 miles"); // washington to cincinati
g_edge_add(g, 1, 3, "500 miles"); // raliegh to cincinati
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) {
printf("idx: %d\tcity: %s\n", i, g_vertex_metadata(v));

// iterate over the edges!
edge_t e;
g_iterate_edges(v, e, j) {
vertex_t dest = g_vertex_get(g_edge_dest(e));
printf("\tto: %s\tdistance: %s\n", g_vertex_metadata(dest), g_edge_metadata(e));
}
}

g_free_alt(g, false);
```


## timing-c

Expand Down
174 changes: 174 additions & 0 deletions examples/graph_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include "../src/graph.h"


typedef struct city {
char* name;
double latitude;
double longitude;
} city;

typedef struct highway {
int miles;
} highway;


/* Private Functions */
static void __add_city_as_vertex(graph_t g, char* name, double latitude, double longitude); /* we get to always make N and W.*/
static void __print_city(city* c);
static void __free_city(city* c);
static void __add_highway_as_edge(graph_t g, int src, int dest, int miles);
static char* __str_duplicate(const char* s);

unsigned int* __breadth_first_search(graph_t g, vertex_t start);



int main(int argc, char const *argv[]) {
bool verbose = false;

if (argc == 2 && strcmp(argv[1], "-v") == 0) {
verbose = true;
}

graph_t g = g_init();
vertex_t v;
unsigned int i;

/* add the vertices */
__add_city_as_vertex(g, "New York", 40.7128, 74.0060);
__add_city_as_vertex(g, "Washington D.C.", 38.9072, 77.0369);
__add_city_as_vertex(g, "Cleveland", 41.4993, 81.6944);
__add_city_as_vertex(g, "Detroit", 42.3314, 83.0458);
__add_city_as_vertex(g, "Atlanta", 33.7490, 84.3880);
__add_city_as_vertex(g, "St. Louis", 38.6270, 90.1994);
__add_city_as_vertex(g, "Dallas", 32.7767, 96.7970);
__add_city_as_vertex(g, "Salt Lake", 40.7608, 111.8910);
__add_city_as_vertex(g, "Pheonix", 33.4484, 112.0740);
__add_city_as_vertex(g, "Las Vegas", 36.1699, 115.1398);
__add_city_as_vertex(g, "Los Angeles", 34.0522, 118.2437);
__add_city_as_vertex(g, "San Fransisco", 37.7749, 122.4194);

/* add edges between the vertices */
__add_highway_as_edge(g, 0, 1, 227); /* NY - DC */
__add_highway_as_edge(g, 1, 0, 227); /* DC - NY */
__add_highway_as_edge(g, 1, 2, 371); /* DC - Cleveland */
__add_highway_as_edge(g, 1, 4, 639); /* DC - Atlanta */
__add_highway_as_edge(g, 4, 1, 639); /* Atlanta - DC */
__add_highway_as_edge(g, 2, 3, 168); /* Cleveland - Detroit */
__add_highway_as_edge(g, 2, 4, 557); /* Cleveland - St. Louis */
__add_highway_as_edge(g, 4, 5, 630); /* St. Louis - Dallas */
__add_highway_as_edge(g, 5, 7, 1064); /* Dallas - Pheonix */
__add_highway_as_edge(g, 7, 8, 300); /* Pheonix - LV */
__add_highway_as_edge(g, 7, 9, 372); /* Pheonix - LA */
__add_highway_as_edge(g, 9, 7, 372); /* LA - Pheonix */
__add_highway_as_edge(g, 9, 10, 381); /* LA - SF */
__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("%d, ", bfs[i]);
}
printf("\n");

for (i = 0; i < g_vertices_inserted(g); i++) {
v = g_vertex_get(g, bfs[i]);
city* c = g_vertex_metadata(v);
__print_city(c);
}

/* free things */
g_iterate_vertices(g, v, i) {
city* c = g_vertex_metadata(v);
/*__print_city(c); */
__free_city(c);
g_vertex_metadata_update(v, NULL);
}
g_free(g);
free(bfs);

if (verbose == true)
printf("Completed!\n");

return 0;
}


/* PRIVATE FUNCTIONS */
static void __add_city_as_vertex(graph_t g, char* name, double latitude, double longitude) {
city* c = calloc(1, sizeof(city));

c->name = __str_duplicate(name);
c->latitude = latitude;
c->longitude = longitude;
g_vertex_add(g, c);
}

static void __print_city(city* c) {
printf("City Name: %s\tLatitude: %f°N\tLongitude: %f°W\n", c->name, c->latitude, c->longitude);
}

static void __free_city(city* c) {
free(c->name);
c->latitude = 0.0;
c->longitude = 0.0;
free(c);
}

static void __add_highway_as_edge(graph_t g, int src, int dest, int miles) {
highway* h = calloc(1, sizeof(highway));
h->miles = miles;
g_edge_add(g, src, dest, h);
}

static char* __str_duplicate(const char* s) {
size_t len = strlen(s);
char* buf = malloc((len + 1) * sizeof(char));
if (buf == NULL)
return NULL;
strcpy(buf, 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;
}
Loading