Commit a626dbca authored by Chris Müller's avatar Chris Müller

tools: add graph visualizer for red_black_trees with GraphViz.

parent 755a9913
...@@ -13,5 +13,9 @@ include_directories(src) ...@@ -13,5 +13,9 @@ include_directories(src)
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(test) add_subdirectory(test)
if(CRYSTAL_TOOLS)
add_subdirectory(tools)
endif()
configure_file(pkgconfig.pc.in ${CMAKE_BINARY_DIR}/pkgconfig.pc @ONLY) configure_file(pkgconfig.pc.in ${CMAKE_BINARY_DIR}/pkgconfig.pc @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/pkgconfig.pc DESTINATION lib/pkgconfig RENAME ${TARGET_NAME}.pc) install(FILES ${CMAKE_BINARY_DIR}/pkgconfig.pc DESTINATION lib/pkgconfig RENAME ${TARGET_NAME}.pc)
pkg_dependency(GRAPHVIZ libgraph libgvc REQUIRED)
add_executable(graphview graphview.c)
target_link_libraries(graphview crystal ${GRAPHVIZ})
#include <stdio.h>
#include <stdlib.h>
#include <graphviz/gvc.h>
#include "standard.h"
#include "string.h"
#include "structures/structures.h"
#include "structures/red_black_tree.h"
static const char* RB_TREE_IDENTIFIER = "rbtree";
static struct RBTree*
file_parse_red_black_tree(FILE* file)
{
struct RBTree* tree = rb_tree_new(cry_int_compare);
struct RBNode* node = 0;
char buffer[64];
int* integer = 0;
int tmp = 0;
memset(buffer, '0', 64);
while( *buffer != 'f' && fgets(buffer, sizeof(buffer), file) != 0) {
*strchr(buffer, '\n') = '\0';
switch(*buffer) {
case '+':
integer = cry_malloc(int);
*integer = atoi(buffer + 2);
if(rb_tree_insert(tree, integer, 0) == 0) {
fprintf(stderr, "Value %d already added.\n", *integer);
cry_free(integer);
}
break;
case '-':
tmp = atoi(buffer + 2);
if( (node = rb_tree_remove(tree, &tmp)) == 0)
fprintf(stderr, "Value %d is not in tree.\n", tmp);
else
rb_node_free(node, free, 0);
break;
default:
*buffer = 'f';
break;
}
}
printf("[%ld nodes read. Show graph]\n", tree->nodes);
return tree;
}
static Agnode_t*
red_black_tree_graph(Agraph_t* graph, struct RBNode* node)
{
char key[32];
snprintf(key, sizeof(key), "%d", *cry_cast(int*, node->key));
Agnode_t* n = agnode(graph, key);
agset(n, "label", key);
if(node->color == BLACK)
agsafeset(n, "color", "black", "");
else
agsafeset(n, "color", "red", "");
if(node->left != 0) {
Agnode_t* child = red_black_tree_graph(graph, node->left);
agedge(graph, n, child);
} else {
snprintf(key, sizeof(key), "%d_left", *cry_cast(int*, node->key));
Agnode_t* child = agnode(graph, key);
agsafeset(child, "shape", "point", "");
agedge(graph, n, child);
}
if(node->right != 0) {
Agnode_t* child = red_black_tree_graph(graph, node->right);
agedge(graph, n, child);
} else {
snprintf(key, sizeof(key), "%d_right", *cry_cast(int*, node->key));
Agnode_t* child = agnode(graph, key);
agsafeset(child, "shape", "point", "");
agedge(graph, n, child);
}
return n;
}
static void
graph_generator(FILE* opfile, GVC_t* gvc, const char* graphtype)
{
Agraph_t* graph = 0;
if(strcmp(graphtype, RB_TREE_IDENTIFIER) == 0) {
struct RBTree* tree = file_parse_red_black_tree(opfile);
graph = agopen("red_black_tree", AGRAPH);
if(tree->root != 0)
red_black_tree_graph(graph, tree->root);
rb_tree_clear(tree, free, 0);
cry_free(tree);
if(graph != 0) {
gvLayout(gvc, graph, "dot");
gvRenderFilename(gvc, graph, "xlib", "rbtree.png");
}
} else {
fprintf(stderr, "Graphtype identifier %s not known.\n", graphtype);
}
if(graph != 0) {
gvFreeLayout(gvc, graph);
agclose(graph);
}
}
int main(int argc, char** argv)
{
aginit();
FILE* fd = 0;
char buffer[64];
if(argc < 2) {
printf("[Choices: rbtree]\n");
fd = stdin;
} else {
FILE* fd = 0;
if((fd = fopen(argv[1], "r")) == 0) {
fprintf(stderr, "Opfile '%s' could not be opened.\n", argv[1]);
return EXIT_FAILURE;
}
fclose(fd);
}
if(fgets(buffer, sizeof(buffer), fd) != 0) {
*strchr(buffer, '\n') = '\0';
printf("[Commands: + INT, - INT]\n");
GVC_t* gvc = gvContext();
graph_generator(fd, gvc, buffer);
gvFreeContext(gvc);
} else {
fprintf(stderr, "Couldn't load graphtype header fronm '%s'.\n", argv[1]);
fclose(fd);
return EXIT_FAILURE;
}
if(fd != stdin)
fclose(fd);
return EXIT_SUCCESS;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment