source: git/src/netartic.c @ 4ba80e0

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 4ba80e0 was 64d37a3, checked in by Olly Betts <olly@…>, 20 years ago

Sync with 1.0 branch.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@2632 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 13.6 KB
Line 
1/* netartic.c
2 * Split up network at articulation points
3 * Copyright (C) 1993-2003 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20#if 0
21# define DEBUG_INVALID 1
22# define DEBUG_ARTIC
23#endif
24
25#ifdef HAVE_CONFIG_H
26# include <config.h>
27#endif
28
29#include "debug.h"
30#include "cavern.h"
31#include "filename.h"
32#include "message.h"
33#include "netartic.h"
34#include "netbits.h"
35#include "matrix.h"
36#include "out.h"
37
38/* We want to split station list into a list of components, each of which
39 * consists of a list of "articulations" - the first has all the fixed points
40 * in that component, and the others attach sequentially to produce the whole
41 * component
42 */
43
44typedef struct articulation {
45   struct articulation *next;
46   node *stnlist;
47} articulation;
48
49typedef struct component {
50   struct component *next;
51   articulation *artic;
52} component;
53
54static component *component_list;
55
56static articulation *articulation_list;
57
58static node *artlist;
59
60static node *fixedlist;
61
62static long colour;
63
64/* The goto iter/uniter avoids using recursion which could lead to stack
65 * overflow.  Instead we use 5 bytes of memory per iteration in malloc-ed
66 * blocks which will probably use an awful lot less memory on most platforms.
67 */
68
69/* This is the number of station in stnlist which is a ceiling on how
70 * deep visit will recurse */
71static unsigned long cMaxVisits;
72
73static unsigned char *dirn_stack = NULL;
74static long *min_stack = NULL;
75
76static unsigned long
77visit(node *stn, int back)
78{
79   long min_colour;
80   int i;
81   unsigned long tos = 0;
82   SVX_ASSERT(dirn_stack && min_stack);
83#ifdef DEBUG_ARTIC
84   printf("visit(%p, %d) called\n", stn, back);
85#endif
86
87iter:
88   min_colour = stn->colour = ++colour;
89#ifdef DEBUG_ARTIC
90   printf("visit: stn [%p] ", stn);
91   print_prefix(stn->name);
92   printf(" set to colour %ld -> min\n", colour);
93#endif
94   for (i = 0; i <= 2 && stn->leg[i]; i++) {
95      if (i != back) {
96         node *to = stn->leg[i]->l.to;
97         long col = to->colour;
98         if (col == 0) {
99            SVX_ASSERT(tos < cMaxVisits);
100            dirn_stack[tos] = back;
101            min_stack[tos] = min_colour;
102            tos++;
103            back = reverse_leg_dirn(stn->leg[i]);
104            stn = to;
105            goto iter;
106uniter:
107            SVX_ASSERT(tos > 0);
108            --tos;
109            i = reverse_leg_dirn(stn->leg[back]);
110            to = stn;
111            stn = to->leg[back]->l.to;
112            back = dirn_stack[tos];
113            if (min_stack[tos] < min_colour) min_colour = min_stack[tos];
114
115#ifdef DEBUG_ARTIC
116            printf("unwind: stn [%p] ", stn);
117            print_prefix(stn->name);
118            printf(" colour %d, min %d, station after %d\n", stn->colour,
119                   min_colour, to->colour);
120            printf("Putting stn ");
121            print_prefix(to->name);
122            printf(" on artlist\n");
123#endif
124            remove_stn_from_list(&stnlist, to);
125            add_stn_to_list(&artlist, to);
126
127            if (to->colour <= min_colour) {
128               articulation *art;
129
130               min_colour = to->colour;
131
132               /* FIXME: note down leg (<-), remove and replace:
133                *                 /\   /        /\
134                * [fixed point(s)]  *-*  -> [..]  )
135                *                 \/   \        \/
136                *                 stn to
137                */
138               /* flag leg as an articulation for loop error reporting */
139               to->leg[dirn_stack[tos]]->l.reverse |= FLAG_ARTICULATION;
140               stn->leg[i]->l.reverse |= FLAG_ARTICULATION;
141
142               /* start new articulation */
143               art = osnew(articulation);
144               art->stnlist = artlist;
145               art->next = articulation_list;
146               articulation_list = art;
147               artlist = NULL;
148
149#ifdef DEBUG_ARTIC
150               printf("Articulate *-");
151               print_prefix(stn->name);
152               printf("-");
153               print_prefix(to->name);
154               printf("-...\n");
155#endif
156            }
157         } else {
158            /* back edge case */
159            if (col < 0) {
160               /* we've found a fixed point */
161               col = -col;
162               to->colour = col;
163#if 0
164               /* Removing this solves Graham Mullan's problem and makes more
165                * sense since it means we'll recheck this point for further
166                * legs. */
167#ifdef DEBUG_ARTIC
168               printf("Putting FOUND FIXED stn ");
169               print_prefix(to->name);
170               printf(" on artlist\n");
171#endif
172               remove_stn_from_list(&fixedlist, to);
173               add_stn_to_list(&artlist, to);
174#endif
175            }
176
177            if (col < min_colour) min_colour = col;
178         }
179      }
180   }
181
182   SVX_ASSERT(!stn->leg[0] || stn->leg[0]->l.to->colour > 0);
183   SVX_ASSERT(!stn->leg[1] || stn->leg[1]->l.to->colour > 0);
184   SVX_ASSERT(!stn->leg[2] || stn->leg[2]->l.to->colour > 0);
185
186   if (tos > 0) goto uniter;
187
188#ifdef DEBUG_ARTIC
189   printf("Putting stn ");
190   print_prefix(stn->name);
191   printf(" on artlist\n");
192#endif
193   remove_stn_from_list(&stnlist, stn);
194   add_stn_to_list(&artlist, stn);
195   return min_colour;
196}
197
198extern void
199articulate(void)
200{
201   node *stn, *stnStart;
202   int i;
203   long cFixed;
204
205   component_list = NULL;
206   articulation_list = NULL;
207   artlist = NULL;
208   fixedlist = NULL;
209
210   /* find articulation points and components */
211   colour = 0;
212   stnStart = NULL;
213   cMaxVisits = 0;
214   FOR_EACH_STN(stn, stnlist) {
215      if (fixed(stn)) {
216         remove_stn_from_list(&stnlist, stn);
217         add_stn_to_list(&fixedlist, stn);
218         colour++;
219         stn->colour = -colour;
220#ifdef DEBUG_ARTIC
221         printf("Putting stn ");
222         print_prefix(stn->name);
223         printf(" on fixedlist\n");
224#endif
225      } else {
226         cMaxVisits++;
227         stn->colour = 0;
228      }
229   }
230   dirn_stack = osmalloc(cMaxVisits);
231   min_stack = osmalloc(cMaxVisits * sizeof(long));
232
233   stnStart = fixedlist;
234   SVX_ASSERT2(stnStart,"no fixed points!");
235   cFixed = colour;
236   while (1) {
237      int c;
238      stn = stnStart;
239
240      /* see if this is a fresh component - it may not be, we may be
241       * processing the other way from a fixed point cut-line */
242      if (stn->colour < 0) {
243#ifdef DEBUG_ARTIC
244         printf("new component\n");
245#endif
246         stn->colour = -stn->colour; /* fixed points are negative until we colour from them */
247         cComponents++;
248
249         /* FIXME: logic to count components isn't the same as the logic
250          * to start a new one - we should start a new one for a fixed point
251          * cut-line (see below) */
252         if (artlist) {
253             component *comp;
254             articulation *art;
255
256             art = osnew(articulation);
257             art->stnlist = artlist;
258             art->next = articulation_list;
259             articulation_list = art;
260             artlist = NULL;
261
262             comp = osnew(component);
263             comp->next = component_list;
264             comp->artic = articulation_list;
265             component_list = comp;
266             articulation_list = NULL;
267         }
268
269#ifdef DEBUG_ARTIC
270         print_prefix(stn->name);
271         printf(" [%p] is root of component %ld\n", stn, cComponents);
272         printf(" and colour = %d/%d\n", stn->colour, cFixed);
273#endif
274      }
275
276      c = 0;
277      for (i = 0; i <= 2 && stn->leg[i]; i++) {
278         node *stn2 = stn->leg[i]->l.to;
279         if (stn2->colour < 0) {
280            stn2->colour = -stn2->colour;
281         } else if (stn2->colour == 0) {
282            /* Special case to check if start station is an articulation point
283             * which it is iff we have to colour from it in more than one dirn
284             *
285             * We're looking for articulation legs - these are those where
286             * colouring from here doesn't reach a fixed point (including
287             * stn - the fixed point we've started from)
288             *
289             * FIXME: this is a "fixed point cut-line" case where we could
290             * start a new component.
291             */
292            long col = visit(stn2, reverse_leg_dirn(stn->leg[i]));
293#ifdef DEBUG_ARTIC
294            print_prefix(stn->name);
295            printf(" -> ");
296            print_prefix(stn2->name);
297            printf(" col %d cFixed %d\n", col, cFixed);
298#endif
299            if (col > cFixed) {
300                /* start new articulation - FIXME - overeager */
301                articulation *art = osnew(articulation);
302                art->stnlist = artlist;
303                art->next = articulation_list;
304                articulation_list = art;
305                artlist = NULL;
306                c |= 1 << i;
307            }
308         }
309      }
310
311      switch (c) {
312       /* had to colour in 2 or 3 directions from start point */
313       case 3: case 5: case 6: case 7:
314#ifdef DEBUG_ARTIC
315         print_prefix(stn->name);
316         printf(" is a special case start articulation point [%d]\n", c);
317#endif
318         for (i = 0; i <= 2 && stn->leg[i]; i++) {
319            if (TSTBIT(c, i)) {
320               /* flag leg as an articulation for loop error reporting */
321               stn->leg[i]->l.reverse |= FLAG_ARTICULATION;
322#ifdef DEBUG_ARTIC
323               print_prefix(stn->leg[i]->l.to->name);
324               putnl();
325#endif
326               reverse_leg(stn->leg[i])->l.reverse |= FLAG_ARTICULATION;
327            }
328         }
329      }
330
331#ifdef DEBUG_ARTIC
332      printf("Putting FIXED stn ");
333      print_prefix(stn->name);
334      printf(" on artlist\n");
335#endif
336      remove_stn_from_list(&fixedlist, stn);
337      add_stn_to_list(&artlist, stn);
338
339      if (stnStart->colour == 1) {
340#ifdef DEBUG_ARTIC
341         printf("%ld components\n",cComponents);
342#endif
343         break;
344      }
345
346      stnStart = fixedlist;
347      if (!stnStart) break;
348   }
349
350   osfree(dirn_stack);
351   dirn_stack = NULL;
352   osfree(min_stack);
353   min_stack = NULL;
354
355   if (artlist) {
356      articulation *art = osnew(articulation);
357      art->stnlist = artlist;
358      art->next = articulation_list;
359      articulation_list = art;
360      artlist = NULL;
361   }
362   if (articulation_list) {
363      component *comp = osnew(component);
364      comp->next = component_list;
365      comp->artic = articulation_list;
366      component_list = comp;
367      articulation_list = NULL;
368   }
369
370   if (stnlist) {
371      /* Actually this error is fatal, but we want to list the survey
372       * stations which aren't connected, so we report it as an error
373       * and die after listing them...
374       */
375      bool fNotAttached = fFalse;
376      error(/*Survey not all connected to fixed stations*/45);
377      FOR_EACH_STN(stn, stnlist) {
378         if (stn->name->ident) {
379            if (!fNotAttached) {
380               fNotAttached = fTrue;
381               puts(msg(/*The following survey stations are not attached to a fixed point:*/71));
382            }
383            puts(sprint_prefix(stn->name));
384         }
385      }
386      exit(EXIT_FAILURE);
387   }
388
389   {
390      component *comp;
391
392#ifdef DEBUG_ARTIC
393      printf("\nDump of %d components:\n", cComponents);
394#endif
395      for (comp = component_list; comp; comp = comp->next) {
396         node *list = NULL, *listend = NULL;
397         articulation *art;
398#ifdef DEBUG_ARTIC
399         printf("Component:\n");
400#endif
401         SVX_ASSERT(comp->artic);
402         for (art = comp->artic; art; art = art->next) {
403#ifdef DEBUG_ARTIC
404            printf("  Articulation (%p):\n", art->stnlist);
405#endif
406            SVX_ASSERT(art->stnlist);
407            if (listend) {
408               listend->next = art->stnlist;
409               art->stnlist->prev = listend;
410            } else {
411               list = art->stnlist;
412            }
413
414            FOR_EACH_STN(stn, art->stnlist) {
415#ifdef DEBUG_ARTIC
416               printf("    %d %p (", stn->colour, stn);
417               print_prefix(stn->name);
418               printf(")\n");
419#endif
420               listend = stn;
421            }
422         }
423#ifdef DEBUG_ARTIC
424         putnl();
425         FOR_EACH_STN(stn, list) {
426            printf("MX: %c %p (", fixed(stn)?'*':' ', stn);
427            print_prefix(stn->name);
428            printf(")\n");
429         }
430#endif
431         solve_matrix(list);
432#ifdef DEBUG_ARTIC
433         putnl();
434         FOR_EACH_STN(stn, list) {
435            printf("%c %p (", fixed(stn)?'*':' ', stn);
436            print_prefix(stn->name);
437            printf(")\n");
438         }
439#endif
440         listend->next = stnlist;
441         if (stnlist) stnlist->prev = listend;
442         stnlist = list;
443      }
444#ifdef DEBUG_ARTIC
445      printf("done articulating\n");
446#endif
447   }
448
449#ifdef DEBUG_ARTIC
450   /* test articulation */
451   FOR_EACH_STN(stn, stnlist) {
452      int d;
453      int f;
454      if (stn->name->ident && stn->name->sflags & BIT(SFLAGS_FIXED)) {
455         stn->colour = 1;
456      } else {
457         stn->colour = 0;
458      }
459      f = 0;
460      for (d = 0; d < 3; d++) {
461         if (stn->leg[d]) {
462            if (f) {
463               printf("awooga - gap in legs\n");
464            }
465            if (stn->leg[d]->l.reverse & FLAG_ARTICULATION) {
466               if (!(reverse_leg(stn->leg[d])->l.reverse & FLAG_ARTICULATION)) {
467                  printf("awooga - bad articulation (one way art)\n");
468               }
469            } else {
470               if (reverse_leg(stn->leg[d])->l.reverse & FLAG_ARTICULATION) {
471                  printf("awooga - bad articulation (one way art)\n");
472               }
473            }
474         } else {
475            f = 1;
476         }
477      }
478   }
479
480   colour = 2;
481
482   while (1) {
483      int c;
484      int f;
485      do {
486         c = 0;
487         FOR_EACH_STN(stn, stnlist) {
488            int d;
489            f = 0;
490            for (d = 0; d < 3; d++) {
491               if (stn->leg[d]) {
492                  node *stn2 = stn->leg[d]->l.to;
493                  if (f) {
494                     printf("awooga - gap in legs\n");
495                  }
496                  if (stn2->colour) {
497                     if (!(stn->leg[d]->l.reverse & FLAG_ARTICULATION)) {
498                        if (stn->colour == 0) {
499                           stn->colour = stn2->colour;
500                           c++;
501                        }
502                     }
503                  }
504               } else {
505                  f = 1;
506               }
507            }
508         }
509      } while (c);
510
511      /* colour bits */
512      FOR_EACH_STN(stn, stnlist) {
513         if (stn->colour == 0) break;
514      }
515      if (!stn) break; /* all coloured */
516
517      stn->colour = colour++;
518   }
519
520   FOR_EACH_STN(stn, stnlist) {
521      int d;
522      int f;
523      f = 0;
524      for (d = 0; d < 3; d++) {
525         if (stn->leg[d]) {
526            if (f) {
527               printf("awooga - gap in legs\n");
528            }
529#ifdef DEBUG_ARTIC
530            if (stn->leg[d]->l.reverse & FLAG_ARTICULATION) {
531               node *stn2 = stn->leg[d]->l.to;
532               printf("art: %ld %ld [%p] ", stn->colour, stn2->colour, stn);
533               print_prefix(stn->name);
534               printf(" - [%p] ", stn2);
535               print_prefix(stn2->name);
536               printf("\n");
537            }
538#endif
539         } else {
540            f = 1;
541         }
542      }
543   }
544#endif
545   FOR_EACH_STN(stn, stnlist) {
546      SVX_ASSERT(fixed(stn));
547   }
548}
Note: See TracBrowser for help on using the repository browser.