2 * Split up network at articulation points
3 * Copyright (C) 1993-2003,2005,2012,2014,2015 Olly Betts
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 # define DEBUG_INVALID 1
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
44 typedef struct articulation
{
45 struct articulation
*next
;
49 typedef struct component
{
50 struct component
*next
;
54 static component
*component_list
;
56 static articulation
*articulation_list
;
60 static node
*fixedlist
;
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.
69 /* This is the number of station in stnlist which is a ceiling on how
70 * deep visit will recurse */
71 static unsigned long cMaxVisits
;
73 static unsigned char *dirn_stack
= NULL
;
74 static long *min_stack
= NULL
;
77 visit(node
*stn
, int back
)
81 unsigned long tos
= 0;
82 SVX_ASSERT(dirn_stack
&& min_stack
);
84 printf("visit(%p, %d) called\n", stn
, back
);
88 min_colour
= stn
->colour
= ++colour
;
90 printf("visit: stn [%p] ", stn
);
91 print_prefix(stn
->name
);
92 printf(" set to colour %ld -> min\n", colour
);
94 for (i
= 0; i
<= 2 && stn
->leg
[i
]; i
++) {
96 node
*to
= stn
->leg
[i
]->l
.to
;
97 long col
= to
->colour
;
99 SVX_ASSERT(tos
< cMaxVisits
);
100 dirn_stack
[tos
] = back
;
101 min_stack
[tos
] = min_colour
;
103 back
= reverse_leg_dirn(stn
->leg
[i
]);
109 i
= reverse_leg_dirn(stn
->leg
[back
]);
111 stn
= to
->leg
[back
]->l
.to
;
112 back
= dirn_stack
[tos
];
113 if (min_stack
[tos
] < min_colour
) min_colour
= min_stack
[tos
];
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");
124 remove_stn_from_list(&stnlist
, to
);
125 add_stn_to_list(&artlist
, to
);
127 if (to
->colour
<= min_colour
) {
130 min_colour
= to
->colour
;
132 /* FIXME: note down leg (<-), remove and replace:
134 * [fixed point(s)] *-* -> [..] )
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
;
142 /* start new articulation */
143 art
= osnew(articulation
);
144 art
->stnlist
= artlist
;
145 art
->next
= articulation_list
;
146 articulation_list
= art
;
150 printf("Articulate *-");
151 print_prefix(stn
->name
);
153 print_prefix(to
->name
);
160 /* we've found a fixed point */
164 /* Removing this solves Graham Mullan's problem and makes more
165 * sense since it means we'll recheck this point for further
168 printf("Putting FOUND FIXED stn ");
169 print_prefix(to
->name
);
170 printf(" on artlist\n");
172 remove_stn_from_list(&fixedlist
, to
);
173 add_stn_to_list(&artlist
, to
);
177 if (col
< min_colour
) min_colour
= col
;
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);
186 if (tos
> 0) goto uniter
;
189 printf("Putting stn ");
190 print_prefix(stn
->name
);
191 printf(" on artlist\n");
193 remove_stn_from_list(&stnlist
, stn
);
194 add_stn_to_list(&artlist
, stn
);
201 node
*stn
, *stnStart
;
205 component_list
= NULL
;
206 articulation_list
= NULL
;
210 /* find articulation points and components */
214 FOR_EACH_STN(stn
, stnlist
) {
216 remove_stn_from_list(&stnlist
, stn
);
217 add_stn_to_list(&fixedlist
, stn
);
219 stn
->colour
= -colour
;
221 printf("Putting stn ");
222 print_prefix(stn
->name
);
223 printf(" on fixedlist\n");
230 dirn_stack
= osmalloc(cMaxVisits
);
231 min_stack
= osmalloc(cMaxVisits
* sizeof(long));
233 /* fixedlist can be NULL here if we've had a *solve followed by survey
234 * which is all hanging. */
238 stnStart
= fixedlist
;
241 /* see if this is a fresh component - it may not be, we may be
242 * processing the other way from a fixed point cut-line */
243 if (stn
->colour
< 0) {
245 printf("new component\n");
247 stn
->colour
= -stn
->colour
; /* fixed points are negative until we colour from them */
250 /* FIXME: logic to count components isn't the same as the logic
251 * to start a new one - we should start a new one for a fixed point
252 * cut-line (see below) */
257 art
= osnew(articulation
);
258 art
->stnlist
= artlist
;
259 art
->next
= articulation_list
;
260 articulation_list
= art
;
263 comp
= osnew(component
);
264 comp
->next
= component_list
;
265 comp
->artic
= articulation_list
;
266 component_list
= comp
;
267 articulation_list
= NULL
;
271 print_prefix(stn
->name
);
272 printf(" [%p] is root of component %ld\n", stn
, cComponents
);
273 printf(" and colour = %d/%d\n", stn
->colour
, cFixed
);
278 for (i
= 0; i
<= 2 && stn
->leg
[i
]; i
++) {
279 node
*stn2
= stn
->leg
[i
]->l
.to
;
280 if (stn2
->colour
< 0) {
281 stn2
->colour
= -stn2
->colour
;
282 } else if (stn2
->colour
== 0) {
283 /* Special case to check if start station is an articulation point
284 * which it is iff we have to colour from it in more than one dirn
286 * We're looking for articulation legs - these are those where
287 * colouring from here doesn't reach a fixed point (including
288 * stn - the fixed point we've started from)
290 * FIXME: this is a "fixed point cut-line" case where we could
291 * start a new component.
293 long col
= visit(stn2
, reverse_leg_dirn(stn
->leg
[i
]));
295 print_prefix(stn
->name
);
297 print_prefix(stn2
->name
);
298 printf(" col %d cFixed %d\n", col
, cFixed
);
301 /* start new articulation - FIXME - overeager */
302 articulation
*art
= osnew(articulation
);
303 art
->stnlist
= artlist
;
304 art
->next
= articulation_list
;
305 articulation_list
= art
;
313 /* had to colour in 2 or 3 directions from start point */
314 case 3: case 5: case 6: case 7:
316 print_prefix(stn
->name
);
317 printf(" is a special case start articulation point [%d]\n", c
);
319 for (i
= 0; i
<= 2 && stn
->leg
[i
]; i
++) {
321 /* flag leg as an articulation for loop error reporting */
322 stn
->leg
[i
]->l
.reverse
|= FLAG_ARTICULATION
;
324 print_prefix(stn
->leg
[i
]->l
.to
->name
);
327 reverse_leg(stn
->leg
[i
])->l
.reverse
|= FLAG_ARTICULATION
;
333 printf("Putting FIXED stn ");
334 print_prefix(stn
->name
);
335 printf(" on artlist\n");
337 remove_stn_from_list(&fixedlist
, stn
);
338 add_stn_to_list(&artlist
, stn
);
340 if (stnStart
->colour
== 1) {
342 printf("%ld components\n",cComponents
);
354 articulation
*art
= osnew(articulation
);
355 art
->stnlist
= artlist
;
356 art
->next
= articulation_list
;
357 articulation_list
= art
;
360 if (articulation_list
) {
361 component
*comp
= osnew(component
);
362 comp
->next
= component_list
;
363 comp
->artic
= articulation_list
;
364 component_list
= comp
;
365 articulation_list
= NULL
;
369 /* Any stations still in stnlist are unfixed, which is means we have
370 * one or more hanging surveys.
372 * The cause of the problem is pretty likely to be a typo, so run the
373 * checks which report errors and warnings about issues which such a
374 * typo is likely to result in.
378 bool fNotAttached
= false;
379 /* TRANSLATORS: At the end of processing (or if a *SOLVE command is used)
380 * cavern will issue this warning if there are any sections of the survey
381 * network which are hanging. */
382 warning(/*Survey not all connected to fixed stations*/45);
383 FOR_EACH_STN(stn
, stnlist
) {
384 prefix
*name
= find_non_anon_stn(stn
)->name
;
385 if (TSTBIT(name
->sflags
, SFLAGS_HANGING
)) {
386 /* Already reported this name as hanging. */
389 name
->sflags
|= BIT(SFLAGS_HANGING
);
393 /* TRANSLATORS: Here "station" is a survey station, not a train
395 puts(msg(/*The following survey stations are not attached to a fixed point:*/71));
397 printf("%s:%d: %s: ", name
->filename
, name
->line
, msg(/*info*/485));
403 hanging_surveys
= true;
407 component
*comp
= component_list
;
410 printf("\nDump of %d components:\n", cComponents
);
413 node
*list
= NULL
, *listend
= NULL
;
415 component
* old_comp
;
417 printf("Component:\n");
419 SVX_ASSERT(comp
->artic
);
422 articulation
* old_art
;
424 printf(" Articulation (%p):\n", art
->stnlist
);
426 SVX_ASSERT(art
->stnlist
);
428 listend
->next
= art
->stnlist
;
429 art
->stnlist
->prev
= listend
;
434 FOR_EACH_STN(stn
, art
->stnlist
) {
436 printf(" %d %p (", stn
->colour
, stn
);
437 print_prefix(stn
->name
);
448 FOR_EACH_STN(stn
, list
) {
449 printf("MX: %c %p (", fixed(stn
)?'*':' ', stn
);
450 print_prefix(stn
->name
);
457 FOR_EACH_STN(stn
, list
) {
458 printf("%c %p (", fixed(stn
)?'*':' ', stn
);
459 print_prefix(stn
->name
);
463 listend
->next
= stnlist
;
464 if (stnlist
) stnlist
->prev
= listend
;
472 printf("done articulating\n");
477 /* test articulation */
478 FOR_EACH_STN(stn
, stnlist
) {
481 if (stn
->name
->ident
&& TSTBIT(stn
->name
->sflags
, SFLAGS_FIXED
)) {
487 for (d
= 0; d
< 3; d
++) {
490 printf("awooga - gap in legs\n");
492 if (stn
->leg
[d
]->l
.reverse
& FLAG_ARTICULATION
) {
493 if (!(reverse_leg(stn
->leg
[d
])->l
.reverse
& FLAG_ARTICULATION
)) {
494 printf("awooga - bad articulation (one way art)\n");
497 if (reverse_leg(stn
->leg
[d
])->l
.reverse
& FLAG_ARTICULATION
) {
498 printf("awooga - bad articulation (one way art)\n");
514 FOR_EACH_STN(stn
, stnlist
) {
517 for (d
= 0; d
< 3; d
++) {
519 node
*stn2
= stn
->leg
[d
]->l
.to
;
521 printf("awooga - gap in legs\n");
524 if (!(stn
->leg
[d
]->l
.reverse
& FLAG_ARTICULATION
)) {
525 if (stn
->colour
== 0) {
526 stn
->colour
= stn2
->colour
;
539 FOR_EACH_STN(stn
, stnlist
) {
540 if (stn
->colour
== 0) break;
542 if (!stn
) break; /* all coloured */
544 stn
->colour
= colour
++;
547 FOR_EACH_STN(stn
, stnlist
) {
551 for (d
= 0; d
< 3; d
++) {
554 printf("awooga - gap in legs\n");
557 if (stn
->leg
[d
]->l
.reverse
& FLAG_ARTICULATION
) {
558 node
*stn2
= stn
->leg
[d
]->l
.to
;
559 printf("art: %ld %ld [%p] ", stn
->colour
, stn2
->colour
, stn
);
560 print_prefix(stn
->name
);
561 printf(" - [%p] ", stn2
);
562 print_prefix(stn2
->name
);
572 FOR_EACH_STN(stn
, stnlist
) {
573 SVX_ASSERT(fixed(stn
));