2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
16 * @file src/regex/regex_test_graph.c
17 * @brief functions for creating .dot graphs from regexes
18 * @author Maximilian Szengel
21 #include "regex_internal_lib.h"
22 #include "regex_test_lib.h"
23 #include "regex_internal.h"
26 * Context for graph creation. Passed as the cls to
27 * REGEX_TEST_automaton_save_graph_step.
29 struct REGEX_TEST_Graph_Context
32 * File pointer to the dot file used for output.
37 * Verbose flag, if it's set to GNUNET_YES additional info will be printed in
43 * Coloring flag, if set to GNUNET_YES SCCs will be colored.
50 * Recursive function doing DFS with 'v' as a start, detecting all SCCs inside
51 * the subgraph reachable from 'v'. Used with scc_tarjan function to detect all
52 * SCCs inside an automaton.
54 * @param scc_counter counter for numbering the sccs
55 * @param v start vertex
56 * @param index current index
57 * @param stack stack for saving all SCCs
58 * @param stack_size current size of the stack
61 scc_tarjan_strongconnect (unsigned int *scc_counter,
62 struct REGEX_INTERNAL_State *v, unsigned int *index,
63 struct REGEX_INTERNAL_State **stack,
64 unsigned int *stack_size)
66 struct REGEX_INTERNAL_State *w;
67 struct REGEX_INTERNAL_Transition *t;
72 stack[(*stack_size)++] = v;
75 for (t = v->transitions_head; NULL != t; t = t->next)
84 scc_tarjan_strongconnect (scc_counter, w, index, stack, stack_size);
85 v->lowlink = (v->lowlink > w->lowlink) ? w->lowlink : v->lowlink;
87 else if (1 == w->contained)
88 v->lowlink = (v->lowlink > w->index) ? w->index : v->lowlink;
91 if (v->lowlink == v->index)
96 w = stack[--(*stack_size)];
98 w->scc_id = *scc_counter;
106 * Detect all SCCs (Strongly Connected Components) inside the given automaton.
107 * SCCs will be marked using the scc_id on each state.
109 * @param a the automaton for which SCCs should be computed and assigned.
112 scc_tarjan (struct REGEX_INTERNAL_Automaton *a)
115 unsigned int scc_counter;
116 struct REGEX_INTERNAL_State *v;
117 struct REGEX_INTERNAL_State *stack[a->state_count];
118 unsigned int stack_size;
120 for (v = a->states_head; NULL != v; v = v->next)
131 for (v = a->states_head; NULL != v; v = v->next)
134 scc_tarjan_strongconnect (&scc_counter, v, &index, stack, &stack_size);
140 * Save a state to an open file pointer. cls is expected to be a file pointer to
141 * an open file. Used only in conjunction with
142 * REGEX_TEST_automaton_save_graph.
144 * @param cls file pointer.
145 * @param count current count of the state, not used.
149 REGEX_TEST_automaton_save_graph_step (void *cls, unsigned int count,
150 struct REGEX_INTERNAL_State *s)
152 struct REGEX_TEST_Graph_Context *ctx = cls;
153 struct REGEX_INTERNAL_Transition *ctran;
159 if (GNUNET_YES == ctx->verbose)
160 GNUNET_asprintf (&name, "%i (%s) (%s) (%s)", s->dfs_id, s->name, s->proof,
161 GNUNET_h2s (&s->hash));
163 GNUNET_asprintf (&name, "%i", s->dfs_id);
167 if (GNUNET_YES == ctx->coloring)
169 GNUNET_asprintf (&s_acc,
170 "\"%s\" [shape=doublecircle, color=\"0.%i 0.8 0.95\"];\n",
171 name, s->scc_id * s->scc_id);
175 GNUNET_asprintf (&s_acc, "\"%s\" [shape=doublecircle];\n", name,
179 else if (GNUNET_YES == ctx->coloring)
181 GNUNET_asprintf (&s_acc,
182 "\"%s\" [shape=circle, color=\"0.%i 0.8 0.95\"];\n", name,
183 s->scc_id * s->scc_id);
187 GNUNET_asprintf (&s_acc, "\"%s\" [shape=circle];\n", name, s->scc_id);
190 GNUNET_assert (NULL != s_acc);
192 fwrite (s_acc, strlen (s_acc), 1, ctx->filep);
196 for (ctran = s->transitions_head; NULL != ctran; ctran = ctran->next)
198 if (NULL == ctran->to_state)
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Transition from State %i has no state for transitioning\n",
206 if (GNUNET_YES == ctx->verbose)
208 GNUNET_asprintf (&to_name, "%i (%s) (%s) (%s)", ctran->to_state->dfs_id,
209 ctran->to_state->name, ctran->to_state->proof,
210 GNUNET_h2s (&ctran->to_state->hash));
213 GNUNET_asprintf (&to_name, "%i", ctran->to_state->dfs_id);
215 if (NULL == ctran->label)
217 if (GNUNET_YES == ctx->coloring)
219 GNUNET_asprintf (&s_tran,
220 "\"%s\" -> \"%s\" [label = \"ε\", color=\"0.%i 0.8 0.95\"];\n",
221 name, to_name, s->scc_id * s->scc_id);
225 GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"ε\"];\n", name,
231 if (GNUNET_YES == ctx->coloring)
233 GNUNET_asprintf (&s_tran,
234 "\"%s\" -> \"%s\" [label = \"%s\", color=\"0.%i 0.8 0.95\"];\n",
235 name, to_name, ctran->label, s->scc_id * s->scc_id);
239 GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"%s\"];\n", name,
240 to_name, ctran->label, s->scc_id);
244 GNUNET_free (to_name);
246 GNUNET_assert (NULL != s_tran);
248 fwrite (s_tran, strlen (s_tran), 1, ctx->filep);
249 GNUNET_free (s_tran);
258 * Save the given automaton as a GraphViz dot file.
260 * @param a the automaton to be saved.
261 * @param filename where to save the file.
262 * @param options options for graph generation that include coloring or verbose
266 REGEX_TEST_automaton_save_graph (struct REGEX_INTERNAL_Automaton *a,
267 const char *filename,
268 enum REGEX_TEST_GraphSavingOptions options)
272 struct REGEX_TEST_Graph_Context ctx;
276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print NFA, was NULL!");
280 if (NULL == filename || strlen (filename) < 1)
282 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No Filename given!");
286 ctx.filep = fopen (filename, "w");
288 (0 == (options & REGEX_TEST_GRAPH_VERBOSE)) ? GNUNET_NO : GNUNET_YES;
290 (0 == (options & REGEX_TEST_GRAPH_COLORING)) ? GNUNET_NO : GNUNET_YES;
292 if (NULL == ctx.filep)
294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not open file for writing: %s",
299 /* First add the SCCs to the automaton, so we can color them nicely */
300 if (GNUNET_YES == ctx.coloring)
303 start = "digraph G {\nrankdir=LR\n";
304 fwrite (start, strlen (start), 1, ctx.filep);
306 REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL,
307 ®EX_TEST_automaton_save_graph_step,
311 fwrite (end, strlen (end), 1, ctx.filep);