-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfloorplan-to-twintree.w
440 lines (381 loc) · 15.3 KB
/
floorplan-to-twintree.w
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
@*Intro. This (hastily written) program computes the twintree that corresponds
to a given floorplan specification.
See exercises MPR--135 and 7.2.2.1--372 in Volume~4B of {\sl The Art of
Computer Programming\/} for an introduction to the relevant concepts and
terminology.
Each room of the floorplan is specified on |stdin| by a line that
gives its name, followed by the
names of its top bound, bottom bound, left bound, and right bound. For example,
the following ten lines specify the example in that exercise:
$$\vcenter{\halign{\tt #\cr
A h0 h3 v0 v1\cr
B h0 h1 v1 v5\cr
C h1 h3 v1 v3\cr
D h3 h5 v0 v2\cr
E h5 h6 v0 v2\cr
F h3 h6 v2 v3\cr
G h1 h2 v3 v5\cr
H h2 h4 v3 v4\cr
I h4 h6 v3 v4\cr
J h2 h6 v4 v5\cr
}}$$
Each name should have at most seven characters (visible ASCII).
The rooms can be listed in any order.
The output consists of the corresponding twintrees $T_0$ and $T_1$.
(Each root is identified, followed by
the node names and left/right child links,
in symmetric order. A null link is rendered `\.{/\\}'.)
@d bufsize 80 /* maximum length of input lines */
@d maxrooms 1024
@d maxnames (2*maxrooms+3)
@d maxjuncs (2*maxrooms+3)
@d panic(m,s) {@+fprintf(stderr,"%s! (%s)\n",
m,s);@+exit(-666);@+} /* rudely reject bad data */
@d pan(m) {@+fprintf(stderr,"%s!\n",
m);@+exit(-66);@+} /* rudely stop on inconsistency */
@d panicic(m,s1,s2) {@+fprintf(stderr,"%s! (%s and %s)\n",
m,s1,s2);@+exit(-666);@+} /* rudely stop with two reasons */
@c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@<Global variables@>;
@<Subroutines@>;
void main() {
register int i,j,k,l,m,n,q,nameloc,nametyp,rooms,hbounds,vbounds,todo;
@<Input the floorplan@>;
@<Find the junctions@>;
@<Create the twintree@>;
@<Output the twintree@>;
}
@*The input phase. We begin with the easy stuff.
Names are remembered in the |name| array, and classified as either
rooms or bounds. We store five things for each room, namely
the relevant indices |top[i]| and |bot[i]| which point into |hbound|,
the relevant indices |lft[i]| and |rt[i]| which point into |vbound|,
and the index |room[i]| of its name.
@<Input the floorplan@>=
rooms=hbounds=vbounds=0;
while (1) {
if (!fgets(buf,bufsize,stdin)) break;
k=0;
@<Scan the name of |room[i]|@>;
@<Scan the name of its top bound, |top[i]|@>;
@<Scan the name of its bottom bound, |bot[i]|@>;
@<Scan the name of its left bound, |lft[i]|@>;
@<Scan the name of its right bound, |rt[i]|@>;
}
fprintf(stderr,"(OK, I've read the specs for %d rooms, %d horizontal bounds,",
rooms,hbounds);
fprintf(stderr," %d vertical bounds)\n",vbounds);
if (hbounds+vbounds!=rooms+3)
panic("but those totals can't be right","not h+v=r+3");
@ @<Glob...@>=
char buf[bufsize];
char name[maxnames+1][8];
char typ[maxnames]; /* $1=\rm room$, $2=\rm horiz\ bound$,
$3=\rm vert\ bound$ */
int nameptr; /* we've seen this many names so far */
int inx[maxnames]; /* pointer into |room| or |hbound| or |vbound| */
int room[maxrooms+1],hbound[maxrooms+1],vbound[maxrooms+1];
/* pointers back to names */
int top[maxrooms],bot[maxrooms],lft[maxrooms],rt[maxrooms];
/* the room's boundaries */
@ @<Scan the name of |room[i]|@>=
@<Scan a name@>;
if (nametyp) panic("duplicate room name",name[nameloc]);
i=rooms,room[rooms++]=nameloc;
typ[nameloc]=1,inx[nameloc]=i;
@ @<Scan a name@>=
while (buf[k]==' ') k++;
if (buf[k]<' ' || buf[k]>'~') panic("input line must have five names",buf);
for (j=0;buf[k]>' ' && buf[k]<='~';j++,k++) {
if (j==7) panic("name longer than seven characters",name[nameptr]);
name[nameptr][j]=buf[k];
}
name[nameptr][j]='\0';
for (nameloc=0;strcmp(name[nameloc],name[nameptr]);nameloc++);
if (nameloc<nameptr) nametyp=typ[nameloc];
else { /* name not seen before */
nametyp=0;
if (++nameptr>maxnames) panic("too many names","recompile?");
}
@ The |j|th horizontal bound is named |name[hbound[j]]|. It adjoins
|tnbrs[j]| rooms above and |bnbrs[j]| rooms below. Those neighbors
appear in arrays called |tnbr[j]| and |bnbr[j]|.
@<Scan the name of its top bound, |top[i]|@>=
@<Scan a name@>;
if (!nametyp) typ[nameloc]=2,inx[nameloc]=hbounds,hbound[hbounds++]=nameloc;
else if (nametyp!=2) panic("not a horizontal bound",name[nameloc]);
j=top[i]=inx[nameloc];
bnbr[j][bnbrs[j]++]=i;
@ @<Glob...@>=
int tnbr[maxrooms+1][maxrooms],bnbr[maxrooms+1][maxrooms];
int tnbrs[maxrooms+1],bnbrs[maxrooms+1];
@ @<Scan the name of its bottom bound, |bot[i]|@>=
@<Scan a name@>;
if (!nametyp) typ[nameloc]=2,inx[nameloc]=hbounds,hbound[hbounds++]=nameloc;
else if (nametyp!=2) panic("not a horizontal bound",name[nameloc]);
j=bot[i]=inx[nameloc];
tnbr[j][tnbrs[j]++]=i;
if (bot[i]==top[i]) panic("room of zero height",name[i]);
@ Similarly, the |j|th vertical bound is named |name[vbound[j]]|. It adjoins
|lnbrs[j]| rooms to its left and |rnbrs[j]| rooms to its right. Those neighbors
appear in arrays called |lnbr[j]| and |rnbr[j]|.
@<Scan the name of its left bound, |lft[i]|@>=
@<Scan a name@>;
if (!nametyp) typ[nameloc]=3,inx[nameloc]=vbounds,vbound[vbounds++]=nameloc;
else if (nametyp!=3) panic("not a vertical bound",name[nameloc]);
j=lft[i]=inx[nameloc];
rnbr[j][rnbrs[j]++]=i;
@ @<Glob...@>=
int lnbr[maxrooms+1][maxrooms],rnbr[maxrooms+1][maxrooms];
int lnbrs[maxrooms+1],rnbrs[maxrooms+1];
@ @<Scan the name of its right bound, |rt[i]|@>=
@<Scan a name@>;
if (!nametyp) typ[nameloc]=3,inx[nameloc]=vbounds,vbound[vbounds++]=nameloc;
else if (nametyp!=3) panic("not a vertical bound",name[nameloc]);
j=rt[i]=inx[nameloc];
lnbr[j][lnbrs[j]++]=i;
if (lft[i]==rt[i]) panic("room of zero width",name[i]);
@*The setup phase.
Now we want to discover the junction points, where a horizontal bound
meets a vertical bound. Every horizontal bound runs from a `$\vdash$'
junction on its left to a `$\dashv$' junction on its right.
(Well, this isn't strictly true for the topmost and bottommost horizontal
lines; but we shall treat the floorplan's corners as if they were
junctions of two different kinds.)
At each junction point~|j| we'll
determine two of the rooms that adjoin it in northeast, southeast,
southwest, and northwest directions. Those rooms will be called
|ne[j]|, |se[j]|, |sw[j]|, and |nw[j]|, respectively.
We set only |nw[j]| and |ne[j]| if |j| is a `$\bot$';
we set only |nw[j]| and |sw[j]| if |j| is a `$\dashv$';
we set only |sw[j]| and |se[j]| if |j| is a `$\top$';
we set only |ne[j]| and |se[j]| if |j| is a `$\vdash$'.
The two unset rooms aren't always known, and in any case they're irrelevant.
Empty space surrounding the floorplan is considered to be in a room
with the nonexistent number |rooms|.
(It shows up only in the four junctions at the extreme corner points.)
The strategy we'll use is quite simple: First we identify the
bottom-right corner. Then we work from right to left for every
$\dashv$ junction that we know, and from bottom to top for every
$\bot$ junction that we know, finding the mates of those junctions
as we discover new ones.
Of course many floorplan specifications are actually impossible,
or disconnected, etc. We'll want to detect any such anomalies
as we go.
@<Find the junctions@>=
@<Locate the bottom-right room and bounds@>;
@<Process each bound that's connected to a known junction@>;
@<Make every room point to its corner junctions@>;
@ @<Locate the bottom-right room and bounds@>=
@<Set |i| to the number of the rightmost vertical bound@>;
@<Set |j| to the number of the bottom horizontal bound@>;
@<Set |l| to the number of the bottom-right room@>;
@ @<Set |i| to the number of the rightmost vertical bound@>=
for (i=-1,k=0;k<vbounds;k++) if (!rnbrs[k]) {
/* a vertical with no neighbor on the right */
if (i>=0) panicic("both are rightmost",name[vbound[i]],name[vbound[k]]);
i=k;
}
if (i<0) pan("there's no rightmost bound");
@ @<Set |j| to the number of the bottom horizontal bound@>=
for (j=-1,k=0;k<hbounds;k++) if (!bnbrs[k]) {
/* a horizontal with no neighbor below */
if (j>=0) panicic("both are at the bottom",name[hbound[j]],name[hbound[k]]);
j=k;
}
if (j<0) pan("there's no bottom line");
@ @<Set |l| to the number of the bottom-right room@>=
for (l=-1,k=0;k<tnbrs[j];k++) if (rt[tnbr[j][k]]==i) {
if (l>=0) panicic("both are at bottom-right",
name[room[l]],name[room[rt[tnbr[j][k]]]]);
l=tnbr[j][k];
}
if (l<0) pan("there's no bottom-right room");
@ @<Process each bound that's connected to a known junction@>=
nw[0]=l,ne[0]=sw[0]=rooms; /* the rooms touching |junc[0]| */
vjunc[i]=hjunc[j]=0;
jtyp[0]=0x8,vstack[0]=i,hstack[0]=j;
jptr=hptr=vptr=1;
todo=hbounds+vbounds;
while (hptr+vptr) {
if (hptr) {
j=hstack[--hptr];
@<Process horizontal bound |j|@>;
todo--;
}@+else {
i=vstack[--vptr];
@<Process vertical bound |i|@>;
todo--;
}
}
if (todo) pan("disconnected floorplan");
@ At this point we know that horizontal bound |j| has its right end at the
$\dashv$ junction |hjunc[j]|. We want to rearrange its lists
of neighbors, and to establish new junctions that we encounter
along the way.
@<Process horizontal bound |j|@>=
@<Rearrange the rooms just below bound |j|@>;
@<Rearrange the rooms just above bound |j|@>;
@<Establish the $\vdash$ junction at the left of bound |j|@>;
@<Launch new $\bot$ junctions in bound |j|@>;
@ I use the simplest possible ``brute force'' approach when rearranging
rooms within the neighbor lists. So the rearrangements done here might
take quadratic time.
However, if the floorplan specifications are input in the diagonal
order of rooms, no rearrangements will be needed, and this entire
algorithm will take linear time.
@<Rearrange the rooms just below bound |j|@>=
l=sw[hjunc[j]]; /* rightmost room below |j| */
if (l<rooms) {
for (q=rt[l],i=bnbrs[j]-1;i;i--) {
for (k=0;k<=i;k++) if (rt[bnbr[j][k]]==q) break;
if (k>i) panicic("can't find NE room",name[hbound[j]],name[vbound[q]]);
if (k<i) q=bnbr[j][k],bnbr[j][k]=bnbr[j][i],bnbr[j][i]=q;
q=lft[bnbr[j][i]];
}
}
@ @<Rearrange the rooms just above bound |j|@>=
l=nw[hjunc[j]]; /* rightmost room above |j| */
if (l<rooms) {
for (q=rt[l],i=tnbrs[j]-1;i;i--) {
for (k=0;k<=i;k++) if (rt[tnbr[j][k]]==q) break;
if (k>i) panicic("can't find NW room",name[hbound[j]],name[vbound[q]]);
if (k<i) q=tnbr[j][k],tnbr[j][k]=tnbr[j][i],tnbr[j][i]=q;
q=lft[tnbr[j][i]];
}
}
@ Interesting subtleties arise here:
We need to launch the vertical bound at the
extreme left, if |j| is the horizontal bound at the very bottom.
(This actually happens if and only if |jptr=1|, because that horizontal
bound was placed on the stack first when we began.)
That vertical bound will, similarly, launch the horizontal bound at
the extreme top, and it will determine the top left corner (called |tlc|)
at that time. When we're processing {\it that\/} horizontal bound,
we don't want to make another junction at the top left corner.
@<Establish the $\vdash$ junction at the left of bound |j|@>=
ne[jptr]=tnbr[j][0],se[jptr]=bnbr[j][0];
if (!tnbrs[j]) {
if (se[jptr]!=se[tlc]) pan("this can't happen");
}@+else if (!bnbrs[j]) se[jptr]=nw[jptr]=rooms,q=lft[ne[jptr]],
vjunc[q]=jptr,vstack[vptr++]=q,jtyp[jptr++]=0x4;
else jtyp[jptr++]=0x6;
@ If $k$ rooms are above |j|, we launch $k-1$ junctions and put
the relevant vertical bounds on |vstack|.
@<Launch new $\bot$ junctions in bound |j|@>=
for (k=1;k<tnbrs[j];k++) {
q=lft[tnbr[j][k]],vjunc[q]=jptr,vstack[vptr++]=q;
nw[jptr]=tnbr[j][k-1],ne[jptr]=tnbr[j][k],jtyp[jptr]=0xc,jptr++;
}
@ Vertical bounds are treated the same, but with dimensions swapped.
@<Process vertical bound |i|@>=
@<Rearrange the rooms just right of bound |i|@>;
@<Rearrange the rooms just left of bound |i|@>;
@<Establish the $\top$ junction at the top of bound |i|@>;
@<Launch new $\dashv$ junctions in bound |i|@>;
@ @<Rearrange the rooms just right of bound |i|@>=
l=ne[vjunc[i]]; /* lowest room to the right of |i| */
if (l<rooms) {
for (q=bot[l],j=rnbrs[i]-1;j;j--) {
for (k=0;k<=j;k++) if (bot[rnbr[i][k]]==q) break;
if (k>j) panicic("can't find SW room",name[hbound[q]],name[vbound[i]]);
if (k<j) q=rnbr[i][k],rnbr[i][k]=rnbr[i][j],rnbr[i][j]=q;
q=top[rnbr[i][j]];
}
}
@ @<Rearrange the rooms just left of bound |i|@>=
l=nw[vjunc[i]]; /* lowest room to the left of |i| */
if (l<rooms) {
for (q=bot[l],j=lnbrs[i]-1;j;j--) {
for (k=0;k<=j;k++) if (bot[lnbr[i][k]]==q) break;
if (k>j) panicic("can't find SE room",name[hbound[q]],name[vbound[i]]);
if (k<j) q=lnbr[i][k],lnbr[i][k]=lnbr[i][j],lnbr[i][j]=q;
q=top[lnbr[i][j]];
}
}
@ @<Establish the $\top$ junction at the top of bound |i|@>=
sw[jptr]=lnbr[i][0],se[jptr]=rnbr[i][0];
if (!lnbrs[i]) sw[jptr]=ne[jptr]=rooms,tlc=jptr,jtyp[jptr]=0x2;
else if (!rnbrs[i]) se[jptr]=nw[jptr]=rooms,q=top[sw[jptr]],
hjunc[q]=jptr,hstack[hptr++]=q,jtyp[jptr]=0x1;
else jtyp[jptr]=0x3;
jptr++;
@ If $k$ rooms are left of |i|, we launch $k-1$ junctions and put
the relevant horizontal bounds on |hstack|.
@<Launch new $\dashv$ junctions in bound |i|@>=
for (k=1;k<lnbrs[i];k++) {
q=top[lnbr[i][k]],hjunc[q]=jptr,hstack[hptr++]=q;
nw[jptr]=lnbr[i][k-1],sw[jptr]=lnbr[i][k],jtyp[jptr]=0x9,jptr++;
}
@ Finally, each junction identifies itself to the rooms that it knows.
@<Make every room point to its corner junctions@>=
for (k=0;k<jptr;k++) {
q=jtyp[k];
if (q&0x1) tr[sw[k]]=k;
if (q&0x2) tl[se[k]]=k;
if (q&0x4) bl[ne[k]]=k;
if (q&0x8) br[nw[k]]=k;
}
@ @<Glob...@>=
int hjunc[maxrooms+1],vjunc[maxrooms+1];
int hstack[maxrooms+1],vstack[maxrooms+1];
int hptr,vptr; /* sizes of the stacks */
int junc[maxjuncs];
int jptr; /* we've seen this many junctions so far */
char jtyp[maxjuncs]; /* $|0x3|=\top$, $|0xc|=\bot$,
$|0x6|={\vdash}$, $|0x9|=\dashv$ */
int nw[maxjuncs],ne[maxjuncs],se[maxjuncs],sw[maxjuncs];
int tl[maxrooms], tr[maxrooms], bl[maxrooms], br[maxrooms];
/* top left, top right, bottom left, and bottom-right junctions */
int tlc; /* the top-left corner junction */
@*The cool phase.
Now we're ready to construct the twintree, using a reformulation of
the remarkably simple method discovered by
Bo~Yao, Hongyu~Chen, Chung-Kuan Cheng,
and Ronald Graham in {\sl ACM Transactions on Design Automation
of Electronic Systems\/ \bf8} (2003), 55--80.
From this construction we see that many of the arrays above are
superfluous, and we needn't have bothered to compute them!
@<Create the twintree@>=
null=rooms;
for (k=0;k<rooms;k++) {
j=tl[k];
if (jtyp[j]==0x3) l0[k]=null,l1[k]=sw[j];
else l0[k]=ne[j],l1[k]=null;
j=br[k];
if (jtyp[j]==0x9) r0[k]=null,r1[k]=sw[j];
else r0[k]=ne[j],r1[k]=null;
}
root0=ne[1],root1=sw[tlc+1];
@ @<Glob...@>=
int root0,l0[maxrooms],r0[maxrooms],root1,l1[maxrooms],r1[maxrooms];
int null; /* the null room */
@*The output phase.
@d rjustname(k) (int)(8-strlen(name[room[k]])),"",name[room[k]]
@<Subroutines@>=
void inorder0(int root) {
if (l0[root]!=null) inorder0(l0[root]);
printf("%*s%s: %*s%s, %*s%s\n",
rjustname(root),rjustname(l0[root]),rjustname(r0[root]));
if (r0[root]!=null) inorder0(r0[root]);
}
@#
void inorder1(int root) {
if (l1[root]!=null) inorder1(l1[root]);
printf("%*s%s: %*s%s, %*s%s\n",
rjustname(root),rjustname(l1[root]),rjustname(r1[root]));
if (r1[root]!=null) inorder1(r1[root]);
}
@ @<Output the twintree@>=
room[rooms]=nameptr;
strcpy(name[nameptr],"/\\");
printf("T0 (rooted at %s)\n",
name[room[root0]]);
inorder0(root0);
printf("T1 (rooted at %s)\n",
name[room[root1]]);
inorder1(root1);
@*Index.