Working version of Kruskal's algorithm. The running time is very bad though.
[tinc] / src / graph.c
1 /*
2     graph.c -- graph algorithms
3     Copyright (C) 2001 Guus Sliepen <guus@sliepen.warande.net>,
4                   2001 Ivo Timmermans <itimmermans@bigfoot.com>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: graph.c,v 1.1.2.3 2001/10/29 13:14:57 guus Exp $
21 */
22
23 /* We need to generate two trees from the graph:
24
25    1. A minimum spanning tree for broadcasts,
26    2. A single-source shortest path tree for unicasts.
27
28    Actually, the first one alone would suffice but would make unicast packets
29    take longer routes than necessary.
30
31    For the MST algorithm we can choose from Prim's or Kruskal's. I personally
32    favour Kruskal's, because we make an extra AVL tree of edges sorted on
33    weights (metric). That tree only has to be updated when an edge is added or
34    removed, and during the MST algorithm we just have go linearly through that
35    tree, adding safe edges until #edges = #nodes - 1.
36
37    For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
38    simple breadth-first search is presented here.
39 */
40
41 #include <syslog.h>
42 #include "config.h"
43 #include <string.h>
44
45 #include <avl_tree.h>
46
47 #include "node.h"
48 #include "edge.h"
49 #include "connection.h"
50
51 #include "system.h"
52
53 /* Implementation of Kruskal's algorithm.
54    Running time: O(EN)
55    Please note that sorting on weight is already done by add_edge().
56 */
57
58 void mst_kruskal(void)
59 {
60   avl_node_t *node;
61   edge_t *e;
62   node_t *n;
63   connection_t *c;
64   int nodes = 0;
65   int safe_edges = 0;
66   int skipped;
67
68   syslog(LOG_DEBUG, _("Running Kruskal's algorithm:"));
69
70   /* Clear visited status on nodes */
71
72   for(node = node_tree->head; node; node = node->next)
73     {
74       n = (node_t *)node->data;
75       n->status.visited = 0;
76       nodes++;
77     }
78
79   /* Starting point */
80   
81   ((edge_t *)edge_weight_tree->head->data)->from->status.visited = 1;
82
83   /* Clear MST status on connections */
84
85   for(node = connection_tree->head; node; node = node->next)
86     {
87       c = (connection_t *)node->data;
88       c->status.mst = 0;
89     }
90
91   /* Add safe edges */
92
93   while(safe_edges < nodes - 1)
94   for(skipped = 0, node = edge_weight_tree->head; node; node = node->next)
95     {
96 // Algorithm should work without this:
97 //      if(safe_edges = nodes - 1)
98 //        break;
99
100       e = (edge_t *)node->data;
101
102       if(e->from->status.visited == e->to->status.visited)
103         {
104           skipped = 1;
105           continue;
106         }
107
108       e->from->status.visited = 1;
109       e->to->status.visited = 1;
110       if(e->connection)
111         e->connection->status.mst = 1;
112
113       safe_edges++;
114
115       syslog(LOG_DEBUG, _("Adding safe edge %s - %s weight %d"), e->from->name, e->to->name, e->weight);
116
117       if(skipped)
118         break;
119     }
120
121   syslog(LOG_DEBUG, _("Done."));
122
123   if(safe_edges != nodes - 1)
124     {
125       syslog(LOG_ERR, _("Implementation of Kruskal's algorithm is screwed: %d nodes, found %d safe edges"), nodes, safe_edges);
126     }
127 }
128
129 /* Implementation of a simple breadth-first search algorithm.
130    Running time: O(E)
131 */
132
133 void sssp_bfs(void)
134 {
135   avl_node_t *node, *from, *next, *to;
136   edge_t *e;
137   node_t *n, *check;
138   int nodes = 0;
139   int visited = 0;
140   avl_tree_t *todo_tree;
141
142   syslog(LOG_DEBUG, _("Running BFS algorithm:"));
143
144   todo_tree = avl_alloc_tree(NULL, NULL);
145
146   /* Clear visited status on nodes */
147
148   for(node = node_tree->head; node; node = node->next)
149     {
150       n = (node_t *)node->data;
151       n->status.visited = 0;
152       nodes++;
153     }
154
155   /* Begin with myself */
156
157   myself->status.visited = 1;
158   myself->nexthop = myself;
159   myself->via = myself;
160   node = avl_alloc_node();
161   node->data = myself;
162   avl_insert_top(todo_tree, node);
163   visited++;
164
165   /* Loop while todo_tree is filled */
166
167   while(todo_tree->head)
168     {
169       for(from = todo_tree->head; from; from = next)
170         {
171           next = from->next;
172           n = (node_t *)from->data;
173
174           for(to = n->edge_tree->head; to; to = to->next)
175             {
176               e = (edge_t *)to->data;
177
178               if(e->from == n)
179                 check = e->to;
180               else
181                 check = e->from;
182
183               if(!check->status.visited)
184                 {
185                   check->status.visited = 1;
186                   check->nexthop = (n->nexthop == myself) ? n : n->nexthop;
187                   check->via = check; /* FIXME: only if !(e->options & INDIRECT), otherwise use n->via */
188                   node = avl_alloc_node();
189                   node->data = check;
190                   avl_insert_before(todo_tree, from, node);
191                   visited++;
192                   syslog(LOG_DEBUG, _("Node %s nexthop %s via %s"), check->name, check->nexthop->name, check->via->name);
193                 }
194             }
195
196            avl_delete_node(todo_tree, from);
197         }
198     }
199
200   syslog(LOG_DEBUG, _("Done."));
201
202   avl_free_tree(todo_tree);
203
204   if(visited != nodes)
205     {
206       syslog(LOG_ERR, _("Implementation of BFS algorithm is screwed: %d nodes, visited %d"), nodes, visited);
207     }
208 }