-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgb_basic.w
2446 lines (2173 loc) · 97.8 KB
/
gb_basic.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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% This file is part of the Stanford GraphBase (c) Stanford University 1993
@i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
@i gb_types.w
\def\title{GB\_\,BASIC}
\prerequisite{GB\_\,GRAPH}
@* Introduction. This GraphBase module contains six subroutines that generate
standard graphs of various types, together with six routines that combine or
transform existing graphs.
Simple examples of the use of these routines can be found in the
demonstration programs {\sc QUEEN} and {\sc QUEEN\_WRAP}.
@<gb_basic.h@>=
extern Graph *board(); /* moves on generalized chessboards */
extern Graph *simplex(); /* generalized triangular configurations */
extern Graph *subsets(); /* patterns of subset intersection */
extern Graph *perms(); /* permutations of a multiset */
extern Graph *parts(); /* partitions of an integer */
extern Graph *binary(); /* binary trees */
@#
extern Graph *complement(); /* the complement of a graph */
extern Graph *gunion(); /* the union of two graphs */
extern Graph *intersection(); /* the intersection of two graphs */
extern Graph *lines(); /* the line graph of a graph */
extern Graph *product(); /* the product of two graphs */
extern Graph *induced(); /* a graph induced from another */
@ The \CEE/ file \.{gb\_basic.c} has the following overall shape:
@p
#include "gb_graph.h" /* we use the {\sc GB\_\,GRAPH} data structures */
@h@#
@<Private variables@>@;
@<Basic subroutines@>@;
@<Applications of basic subroutines@>@;
@ Several of the programs below allocate arrays that will be freed again
before the routine is finished.
@<Private variables@>=
static Area working_storage;
@ If a graph-generating subroutine encounters a problem, it returns |NULL|
(that is, \.{NULL}), after putting a code number into the external variable
|panic_code|. This code number identifies the type of failure.
Otherwise the routine returns a pointer to the newly created graph, which
will be represented with the data structures explained in {\sc GB\_\,GRAPH}.
(The external variable |panic_code| is itself defined in {\sc GB\_\,GRAPH}.)
@d panic(c)
{@+panic_code=c;
gb_free(working_storage);
gb_trouble_code=0;
return NULL;
}
@ The names of vertices are sometimes formed from the names of other
vertices, or from potentially long sequences of numbers. We assemble
them in the |buffer| array, which is sufficiently long that the
vast majority of applications will be unconstrained by size limitations.
The programs assume that |BUF_SIZE| is rather large, but in cases of
doubt they ensure that |BUF_SIZE| will never be exceeded.
@d BUF_SIZE 4096
@<Private v...@>=
static char buffer[BUF_SIZE];
@*Grids and game boards. The subroutine call
|board(n1,n2,n3,n4,piece,wrap,directed)|
constructs a graph based on the moves of generalized chesspieces on a
generalized rectangular board. Each vertex of the graph corresponds to a
position on the board. Each arc of the graph corresponds to a move from
one position to another.
The first parameters, |n1| through |n4|, specify the size of the board.
If, for example, a two-dimensional board with $n_1$ rows and $n_2$ columns
is desired, you set $|n1|=n_1$, $|n2|=n_2$, and $|n3|=0$; the resulting
graph will have $n_1n_2$ vertices. If you want a three-dimensional
board with $n_3$ layers, set $|n3|=n_3$ and $n_4=0$. If you want
a 4-{\mc D} board, put the number of 4th coordinates in~|n4|.
If you want a $d$-dimensional board with $2^d$ positions, set |n1=2|
and |n2=-d|.
In general, the |board| subroutine determines the dimensions by scanning the
sequence |(n1,n2,n3,n4,0)=@t$(n_1,n_2,n_3,n_4,0)$@>| from left to right
until coming to the first nonpositive parameter $n_{k+1}$. If $k=0$
(i.e., if |n1<=0|), the default size $8\times8$ will be used; this is
an ordinary chessboard with 8~rows and 8~columns. Otherwise if $n_{k+1}=0$,
the board will have $k$~dimensions $n_1$, \dots,~$n_k$. Otherwise
we must have $n_{k+1}<0$; in this case, the board will have $d=\vert n_{k+1}
\vert$ dimensions, chosen as the first $d$ elements of the infinite
periodic sequence $(n_1,\ldots,n_k,n_1,\ldots,n_k,n_1,\ldots\,)$.
For example, the specification |(n1,n2,n3,n4)=(2,3,5,-7)| is about as
tricky as you can get. It produces a seven-dimensional board with
dimensions $(n_1,\ldots,n_7)=(2,3,5,2,3,5,2)$, hence a graph with
$2\cdot3\cdot5\cdot2\cdot3\cdot5\cdot2=1800$ vertices.
The |piece| parameter specifies the legal moves of a generalized chesspiece.
If |piece>0|, a move from position~|u| to position~|v| is considered legal
if and only if the Euclidean distance between points |u| and~|v| is
equal to $\sqrt{\vphantom1\smash{|piece|}}$.
For example, if |piece=1| and if we have a
two-dimensional board, the legal moves from $(x,y)$ are to $(x,y\pm1)$ and
$(x\pm1,y)$; these are the moves of a so-called wazir, the only moves that
a king and a rook can both make. If |piece=2|, the legal moves from $(x,y)$
are to $(x\pm1,y\pm1)$; these are the four moves that a king and a bishop
can both make. (A piece that can make only these moves was called a ``fers''
in ancient Muslim chess.) If |piece=5|, the legal moves are those of a
knight, from $(x,y)$ to $(x\pm1,y\pm2)$ or to $(x\pm2,y\pm1)$. If |piece=3|,
there are no legal moves on a two-dimensional board; but moves from
$(x,y,z)$ to $(x\pm1,y\pm1,z\pm1)$ would be legal in three dimensions.
If |piece=0|, it is changed to the default value |piece=1|.
If the value of |piece| is negative, arbitrary multiples of the basic moves
for $\vert|piece|\vert$ are permitted. For example, |piece=-1| defines the
moves of a rook, from $(x,y)$ to $(x\pm a,y)$ or to $(x,y\pm a)$ for all
$a>0$; |piece=-2| defines the moves of a bishop, from $(x,y)$ to
$(x\pm a,y\pm a)$. The literature of ``fairy chess'' assigns standard names
to the following |piece| values: $\rm wazir=1$, $\rm fers=2$, $\rm dabbaba=4$,
$\rm knight=5$, $\rm alfil=8$, $\rm camel=10$, $\rm zebra=13$, $\rm giraffe
=17$, $\rm fiveleaper=25$, $\hbox{root-50-leaper}=50$, etc.; $\rm rook=-1$,
$\rm bishop=-2$, $\rm unicorn=-3$, $\rm dabbabarider=-4$, $\rm nightrider=-5$,
$\rm alfilrider=-8$, $\rm camelrider=-10$, etc.
To generate a board with the moves of a king, you can use the |gunion|
subroutine below to take the union of boards with |piece=1| and
|piece=2|. Similarly, you can get queen moves by taking the union of
boards with |piece=-1| and |piece=-2|.
If |piece>0|, all arcs of the graph will have length~1. If |piece<0|, the
length of each arc will be the number of multiples of a basic move that
produced the arc.
@ If the |wrap| parameter is nonzero, it specifies a subset of coordinates
in which values are computed modulo the corresponding size.
For example, the coordinates $(x,y)$ for vertices on a two-dimensional
board are restricted to the range $0\le x<n_1$, $0\le y<n_2$; therefore
when |wrap=0|, a move from $(x,y)$ to $(x+\delta_1,y+\delta_2)$ is
legal only if $0\le x+\delta_1<n_1$ and $0\le y+\delta_2<n_2$.
But when |wrap=1|, the $x$~coordinates are allowed to ``wrap around'';
the move would then be made to $((x+\delta_1)\bmod n_1,y+\delta_2)$,
provided that $0\le y+\delta_2<n_2$. Setting |wrap=1| effectively
makes the board into a cylinder instead of a rectangle. Similarly, the
$y$~coordinates are allowed to wrap around when |wrap=2|. Both $x$ and
$y$ coordinates are treated modulo their corresponding sizes when
|wrap=3|; the board is then effectively a torus. In general,
coordinates $k_1$, $k_2$, \dots~ will wrap around when
$|wrap|=2^{k_1-1}+2^{k_2-1}+\cdots\,$. Setting |wrap=-1| causes all
coordinates to be computed modulo their size.
The graph constructed by |board| will be undirected unless |directed!=0|.
Directed |board| graphs will be acyclic when |wrap=0|, but they may
have cycles when |wrap!=0|. Precise rules defining the directed arcs
are given below.
Several important special cases are worth noting. To get the complete graph
on |n| vertices, you can say |board(n,0,0,0,-1,0,0)|. To get the
transitive tournament on |n| vertices, i.e., the directed graph
with arcs from |u| to |v| when |u<v|, you can say |board(n,0,0,0,-1,0,1)|.
To get the empty graph on |n| vertices, you can say |board(n,0,0,0,2,0,0)|.
To get a circuit (undirected) or a cycle (directed) of length~|n|,
you can say |board(n,0,0,0,1,1,0)| and |board(n,0,0,0,1,1,1)|,
respectively.
@(gb_basic.h@>=
#define complete(n) @[board((long)(n),0L,0L,0L,-1L,0L,0L)@]
#define transitive(n) @[board((long)(n),0L,0L,0L,-1L,0L,1L)@]
#define empty(n) @[board((long)(n),0L,0L,0L,2L,0L,0L)@]
#define circuit(n) @[board((long)(n),0L,0L,0L,1L,1L,0L)@]
#define cycle(n) @[board((long)(n),0L,0L,0L,1L,1L,1L)@]
@ @<Basic subroutines@>=
Graph *board(n1,n2,n3,n4,piece,wrap,directed)
long n1,n2,n3,n4; /* size of board desired */
long piece; /* type of moves desired */
long wrap; /* mask for coordinate positions that wrap around */
long directed; /* should the graph be directed? */
{@+@<Vanilla local variables@>@;@+@q{@>@t}\6{@>@q}@>
long n; /* total number of vertices */
long p; /* $\vert|piece|\vert$ */
long l; /* length of current arc */
@<Normalize the board-size parameters@>;
@<Set up a graph with |n| vertices@>;
@<Insert arcs or edges for all legal moves@>;
if (gb_trouble_code) {
gb_recycle(new_graph);
panic(alloc_fault); /* alas, we ran out of memory somewhere back there */
}
return new_graph;
}
@ Most of the subroutines in {\sc GB\_\,BASIC} use the following local
variables.
@<Vanilla local variables@>=
Graph *new_graph; /* the graph being constructed */
register long i,j,k; /* all-purpose indices */
register long d; /* the number of dimensions */
register Vertex *v; /* the current vertex of interest */
register long s; /* accumulator */
@ Several arrays will facilitate the calculations that |board| needs to make.
The number of distinct values in coordinate position~$k$ will be |nn[k]|;
this coordinate position will wrap around if and only if |wr[k]!=0|.
The current moves under consideration will be from $(x_1,\ldots,x_d)$
to $(x_1+\delta_1,\ldots, x_d+\delta_d)$, where $\delta_k$ is stored
in |del[k]|. An auxiliary array |sig| holds the sums
$\sigma_k=\delta_1^2+\cdots+\delta_{k-1}^2$. Additional arrays |xx|
and |yy| hold coordinates of vertices before and after a move is made.
Some of these arrays are also used for other purposes by other programs
besides |board|; we will meet those programs later.
We limit the number of dimensions to 91 or less. This is hardly a limitation,
since the number of vertices would be astronomical even if the dimensionality
were only half this big. But some of our later programs will be able
to make good use of 40 or 50 dimensions and perhaps more; the number 91
is an upper limit imposed by the number of standard printable characters
(see the convention for vertex names in the |perms| routine).
@d MAX_D 91
@<Private...@>=
static long nn[MAX_D+1]; /* component sizes */
static long wr[MAX_D+1]; /* does this component wrap around? */
static long del[MAX_D+1]; /* displacements for the current move */
static long sig[MAX_D+2]; /* partial sums of squares of displacements */
static long xx[MAX_D+1], yy[MAX_D+1]; /* coordinate values */
@ @<Normalize the board-size parameters@>=
if (piece==0) piece=1;
if (n1<=0) {@+n1=n2=8;@+n3=0;@+}
nn[1]=n1;
if (n2<=0) {@+k=2;@+d=-n2;@+n3=n4=0;@+}
else {
nn[2]=n2;
if (n3<=0) {@+k=3;@+d=-n3;@+n4=0;@+}
else {
nn[3]=n3;
if (n4<=0) {@+k=4;@+d=-n4;@+}
else {@+nn[4]=n4;@+d=4;@+goto done;@+}
}
}
if (d==0) {@+d=k-1;@+goto done;@+}
@<Compute component sizes periodically for |d| dimensions@>;
done: /* now |nn[1]| through |nn[d]| are set up */
@ At this point, |nn[1]| through |nn[k-1]| are the component sizes
that should be replicated periodically. In unusual cases, the number
of dimensions might not be as large as the number of specifications.
@<Compute component sizes periodically...@>=
if (d>MAX_D) panic(bad_specs); /* too many dimensions */
for (j=1; k<=d; j++,k++) nn[k]=nn[j];
@ We want to make the subroutine idiot-proof, so we use floating-point
arithmetic to make sure that boards with more than a billion cells have
not been specified.
@d MAX_NNN 1000000000.0
@<Set up a graph with |n| vertices@>=
{@+float nnn; /* approximate size */
for (n=1,nnn=1.0,j=1; j<=d; j++) {
nnn *= (float)nn[j];
if (nnn>MAX_NNN) panic(very_bad_specs); /* way too big */
n *= nn[j]; /* this multiplication cannot cause integer overflow */
}
new_graph=gb_new_graph(n);
if (new_graph==NULL)
panic(no_room); /* out of memory before we're even started */
sprintf(new_graph->id,"board(%ld,%ld,%ld,%ld,%ld,%ld,%d)",
n1,n2,n3,n4,piece,wrap,directed?1:0);
strcpy(new_graph->util_types,"ZZZIIIZZZZZZZZ");
@<Give names to the vertices@>;
}
@ The symbolic name of a board position like $(3,1)$ will be the string
`\.{3.1}'. The first three coordinates are also stored as integers, in
utility fields |x.I|, |y.I|, and |z.I|, because immediate access to
those values will be helpful in certain applications. (The coordinates can,
of course, always be recovered in a slower fashion from the vertex name,
via |sscanf|.)
The process of assigning coordinate values and names is equivalent to
adding unity in a mixed-radix number system. Vertex $(x_1,\ldots,x_d)$
will be in position $x_1n_2\ldots n_d+\cdots+x_{d-1}n_d+x_d$ relative
to the first vertex of the new graph; therefore it is also possible to
deduce the coordinates of a vertex from its address.
@<Give names...@>=
{@+register char *q; /* string pointer */
nn[0]=xx[0]=xx[1]=xx[2]=xx[3]=0;
for (k=4;k<=d;k++) xx[k]=0;
for (v=new_graph->vertices;;v++) {
q=buffer;
for (k=1;k<=d;k++) {
sprintf(q,".%ld",xx[k]);
while (*q) q++;
}
v->name=gb_save_string(&buffer[1]); /* omit |buffer[0]|, which is |'.'| */
v->x.I=xx[1];@+v->y.I=xx[2];@+v->z.I=xx[3];
for (k=d;xx[k]+1==nn[k];k--) xx[k]=0;
if (k==0) break; /* a ``carry'' has occurred all the way to the left */
xx[k]++; /* increase coordinate |k| */
}
}
@ Now we come to a slightly tricky part of the routine: the move generator.
Let $p=\vert|piece|\vert$. The outer loop of this procedure runs through all
solutions of the equation $\delta_1^2+\cdots+\delta_d^2=p$, where the
$\delta$'s are nonnegative integers. Within that loop, we attach signs
to the $\delta$'s, but we always leave $\delta_k$ positive if $\delta_1=
\cdots=\delta_{k-1}=0$. For every such vector~$\delta$, we generate moves
from |v| to $v+\delta$ for every vertex |v|. When |directed=0|,
we use |gb_new_edge| instead of |gb_new_arc|, so that the reverse arc
from $v+\delta$ to~|v| is also generated.
@<Insert arcs or edges for all legal moves@>=
@<Initialize the |wr|, |sig|, and |del| tables@>;
p=piece;
if (p<0) p=-p;
while (1) {
@<Advance to the next nonnegative |del| vector, or |break| if done@>;
while (1) {
@<Generate moves for the current |del| vector@>;
@<Advance to the next signed |del| vector, or restore |del|
to nonnegative values and |break|@>;
}
}
@ The \CEE/ language does not define |>>| unambiguously. If |w| is negative,
the assignment `|w>>=1|' here should keep |w| negative.
(However, this technicality doesn't matter except in highly unusual cases
when there are more than 32 dimensions.)
@^system dependencies@>
@<Initialize the |wr|, |sig|, and |del| tables@>=
{@+register long w=wrap;
for (k=1;k<=d;k++,w>>=1) {
wr[k]=w&1;
del[k]=sig[k]=0;
}
sig[0]=del[0]=sig[d+1]=0;
}
@ The |sig| array makes it easy to backtrack through all partitions
of |p| into an ordered sum of squares.
@<Advance to the next nonnegative |del|...@>=
for (k=d;sig[k]+(del[k]+1)*(del[k]+1)>p;k--) del[k]=0;
if (k==0) break;
del[k]++;
sig[k+1]=sig[k]+del[k]*del[k];
for (k++;k<=d;k++) sig[k+1]=sig[k];
if (sig[d+1]<p) continue;
@ @<Advance to the next signed |del| vector, or restore |del|
to nonnegative values and |break|@>=
for (k=d;del[k]<=0;k--) del[k]=-del[k];
if (sig[k]==0) break; /* all but |del[k]| were negative or zero */
del[k]=-del[k]; /* some entry preceding |del[k]| is positive */
@ We use the mixed-radix addition technique again when generating moves.
@<Generate moves for the current |del| vector@>=
for (k=1;k<=d;k++) xx[k]=0;
for (v=new_graph->vertices;;v++) {
@<Generate moves from |v| corresponding to |del|@>;
for (k=d;xx[k]+1==nn[k];k--) xx[k]=0;
if (k==0) break; /* a ``carry'' has occurred all the way to the left */
xx[k]++; /* increase coordinate |k| */
}
@ The legal moves when |piece| is negative are derived as follows, in
the presence of possible wraparound: Starting at $(x_1,\ldots,x_d)$, we
move to $(x_1+\delta_1,\ldots,x_d+\delta_d)$, $(x_1+2\delta_1,\ldots,
x_d+2\delta_d)$,~\dots, until either coming to a position with a nonwrapped
coordinate out of range or coming back to the original point.
A subtle technicality should be noted: When coordinates are wrapped and
|piece>0|, self-loops are possible---for example, in |board(1,0,0,0,1,1,1)|.
But self-loops never arise when |piece<0|.
@<Generate moves from |v|...@>=
for (k=1;k<=d;k++) yy[k]=xx[k]+del[k];
for (l=1;;l++) {
@<Correct for wraparound, or |goto no_more| if off the board@>;
if (piece<0) @<Go to |no_more| if |yy=xx|@>;
@<Record a legal move from |xx| to |yy|@>;
if (piece>0) goto no_more;
for (k=1;k<=d;k++) yy[k]+=del[k];
}
no_more:@;
@ @<Go to |no_more|...@>=
{
for (k=1;k<=d;k++) if (yy[k]!=xx[k]) goto unequal;
goto no_more;
unequal:;
}
@ @<Correct for wraparound, or |goto no_more| if off the board@>=
for (k=1;k<=d;k++) {
if (yy[k]<0) {
if (!wr[k]) goto no_more;
do yy[k]+=nn[k];@+ while (yy[k]<0);
}@+else if (yy[k]>=nn[k]) {
if (!wr[k]) goto no_more;
do yy[k]-=nn[k];@+ while (yy[k]>=nn[k]);
}
}
@ @<Record a legal move from |xx| to |yy|@>=
for (k=2,j=yy[1];k<=d;k++) j=nn[k]*j+yy[k];
if (directed) gb_new_arc(v,new_graph->vertices+j,l);
else gb_new_edge(v,new_graph->vertices+j,l);
@* Generalized triangular boards. The subroutine call
|simplex(n,n0,n1,n2,n3,n4,directed)| creates a graph based on
generalized triangular or tetrahedral configurations. Such graphs are
similar in spirit to the game boards created by |board|, but they
pertain to nonrectangular grids like those in Chinese checkers. As
with |board| in the case |piece=1|, the vertices represent board positions
and the arcs run from board positions to their nearest neighbors. Each arc has
length~1.{\tolerance=1000\par}
More formally, the vertices can be defined as sequences of nonnegative
integers $(x_0,x_1,\ldots,x_d)$ whose sum is~|n|, where two sequences
are considered adjacent if and only if they differ by $\pm1$ in exactly
two components---equivalently, if the Euclidean distance between them
is~$\sqrt2$. When $d=2$, for example, the vertices can be visualized
as a triangular array
$$\vcenter{\halign{&\hbox to 2em{\hss$#$\hss}\cr
&&&(0,0,3)\cr
&&(0,1,2)&&(1,0,2)\cr
&(0,2,1)&&(1,1,1)&&(2,0,1)\cr
(0,3,0)&&(1,2,0)&&(2,1,0)&&(3,0,0)\cr}}$$
containing $(n+1)(n+2)/2$ elements, illustrated here when $n=3$; each vertex of
the array has up to 6 neighbors. When $d=3$ the vertices form a tetrahedral
array, a stack of triangular layers, and they can have as many as 12
neighbors. In general, a vertex in a $d$-simplicial array will have up to
$d(d+1)$ neighbors.
If the |directed| parameter is nonzero, arcs run only from vertices to
neighbors that are lexicographically greater---for example, downward
or to the right in the triangular array shown. The directed graph is
therefore acyclic, and a vertex of a $d$-simplicial array has
out-degree at most $d(d+1)/2$.
@ The first parameter, |n|, specifies the sum of the coordinates
$(x_0,x_1,\ldots,x_d)$. The following parameters |n0| through |n4|
specify upper bounds on those coordinates, and they also specify the
dimensionality~|d|.
If, for example, |n0|, |n1|, and |n2| are positive while |n3=0|, the
value of~|d| will be~2 and the coordinates will be constrained to
satisfy $0\le x_0\le|n0|$, $0\le x_1\le|n1|$, $0\le x_2\le|n2|$. These
upper bounds essentially lop off the corners of the triangular array.
We obtain a hexagonal board with $6m$ boundary cells by asking for
|simplex(3m,2m,2m,2m,0,0,0)|. We obtain the diamond-shaped board used
in the game of Hex [Martin Gardner, {\sl The Scientific American
@^Gardner, Martin@>
Book of Mathematical Puzzles {\char`\&} Diversions\/} (Simon {\char`\&}
Schuster, 1959), Chapter~8] by calling |simplex(20,10,20,10,0,0,0)|.
In general, |simplex| determines |d| and upper bounds $(n_0,n_1,\ldots,n_d)$
in the following way: Let the first nonpositive entry of the sequence
|(n0,n1,n2,n3,n4,0)|$\null=(n_0,n_1,n_2,n_3,n_4,0)$ be~$n_k$. If $k>0$
and $n_k=0$, the value of~$d$ will be $k-1$ and the coordinates will be
bounded by the given numbers $(n_0,\ldots,n_d)$. If $k>0$ and $n_k<0$,
the value of~$d$ will be $\vert n_k\vert$ and the coordinates will be
bounded by the first $d+1$ elements of the infinite periodic sequence
$(n_0,\ldots,n_{k-1},n_0,\ldots,n_{k-1},n_0,\ldots\,)$. If $k=0$ and
$n_0<0$, the value of~$d$ will be $\vert n_0\vert$ and the coordinates
will be unbounded; equivalently, we may set $n_0=\cdots=n_d=n$. In
this case the number of vertices will be $n+d\choose d$. Finally,
if $k=0$ and $n_0=0$, we have the default case of a triangular array
with $3n$ boundary cells, exactly as if $n_0=-2$.
For example, the specification |n0=3|, |n1=-5| will produce all vertices
$(x_0,x_1,\ldots,x_5)$ such that $x_0+x_1+\cdots+x_5=n$ and $0\le x_j\le3$.
The specification |n0=1|, |n1=-d| will essentially produce all $n$-element
subsets of the $(d+1)$-element set $\{0,1,\ldots,d\}$, because we can
regard an element~$k$ as being present in the set if $x_k=1$ and absent
if $x_k=0$. In that case two subsets are adjacent if and only if
they have exactly $n-1$ elements in common.
@ @<Basic subroutines@>=
Graph *simplex(n,n0,n1,n2,n3,n4,directed)
unsigned long n; /* the constant sum of all coordinates */
long n0,n1,n2,n3,n4; /* constraints on coordinates */
long directed; /* should the graph be directed? */
{@+@<Vanilla local variables@>@;@#
@<Normalize the simplex parameters@>;
@<Create a graph with one vertex for each point@>;
@<Name the points and create the arcs or edges@>;
if (gb_trouble_code) {
gb_recycle(new_graph);
panic(alloc_fault); /* darn, we ran out of memory somewhere back there */
}
return new_graph;
}
@ @<Normalize the simplex parameters@>=
if (n0==0) n0=-2;
if (n0<0) {@+k=2;@+nn[0]=n;@+d=-n0;@+n1=n2=n3=n4=0;@+}
else {
if (n0>n) n0=n;
nn[0]=n0;
if (n1<=0) {@+k=2;@+d=-n1;@+n2=n3=n4=0;@+}
else {
if (n1>n) n1=n;
nn[1]=n1;
if (n2<=0) {@+k=3;@+d=-n2;@+n3=n4=0;@+}
else {
if (n2>n) n2=n;
nn[2]=n2;
if (n3<=0) {@+k=4;@+d=-n3;@+n4=0;@+}
else {
if (n3>n) n3=n;
nn[3]=n3;
if (n4<=0) {@+k=5;@+d=-n4;@+}
else {@+if (n4>n) n4=n;
nn[4]=n4;@+d=4;@+goto done;@+}
}
}
}
}
if (d==0) {@+d=k-2;@+goto done;@+}
nn[k-1]=nn[0];
@<Compute component sizes periodically...@>;
done: /* now |nn[0]| through |nn[d]| are set up */
@ @<Create a graph with one vertex for each point@>=
@<Determine the number of feasible $(x_0,\ldots,x_d)$,
and allocate the graph@>;
sprintf(new_graph->id,"simplex(%lu,%ld,%ld,%ld,%ld,%ld,%d)",
n,n0,n1,n2,n3,n4,directed?1:0);
strcpy(new_graph->util_types,"VVZIIIZZZZZZZZ"); /* hash table will be used */
@ We determine the number of vertices by determining the coefficient of~$z^n$
in the power series
$$(1+z+\cdots+z^{n_0})(1+z+\cdots+z^{n_1})\ldots(1+z+\cdots+z^{n_d}).$$
@<Determine the number of feasible $(x_0,\ldots,x_d)$...@>=
{@+long nverts; /* the number of vertices */
register long *coef=gb_typed_alloc(n+1,long,working_storage);
if (gb_trouble_code) panic(no_room+1); /* can't allocate |coef| array */
for (k=0;k<=nn[0];k++) coef[k]=1;
/* now |coef| represents the coefficients of $1+z+\cdots+z^{n_0}$ */
for (j=1;j<=d;j++)
@<Multiply the power series coefficients by $1+z+\cdots+z^{n_j}$@>;
nverts=coef[n];
gb_free(working_storage); /* recycle the |coef| array */
new_graph=gb_new_graph(nverts);
if (new_graph==NULL)
panic(no_room); /* out of memory before we're even started */
}
@ There's a neat way to multiply by $1+z+\cdots+z^{n_j}$: We multiply
first by $1-z^{n_j+1}$, then sum the coefficients.
We want to detect impossibly large specifications without risking
integer overflow. It is easy to do this because multiplication is being
done via addition.
@<Multiply the power series coefficients by $1+z+\cdots+z^{n_j}$@>=
{
for (k=n,i=n-nn[j]-1;i>=0;k--,i--) coef[k]-=coef[i];
s=1;
for (k=1;k<=n;k++) {
s+=coef[k];
if (s>1000000000) panic(very_bad_specs); /* way too big */
coef[k]=s;
}
}
@ As we generate the vertices, it proves convenient to precompute an
array containing the numbers $y_j=n_j+\cdots+n_d$, which represent the
largest possible sums $x_j+\cdots+x_d$. We also want to maintain
the numbers $\sigma_j=n-(x_0+\cdots+x_{j-1})=x_j+\cdots+x_d$. The
conditions
$$0\le x_j\le n_j, \qquad \sigma_j-y_{j+1}\le x_j\le \sigma_j$$
are necessary and sufficient, in the sense that we can find at least
one way to complete a partial solution $(x_0,\ldots,x_k)$ to a full
solution $(x_0,\ldots,x_d)$ if and only if the conditions hold for
all $j\le k$.
There is at least one solution if and only if $n\le y_0$.
We enter the name string into a hash table, using the |hash_in|
routine of {\sc GB\_\,GRAPH}, because there is no simple way to compute the
location of a vertex from its coordinates.
@<Name the points and create the arcs or edges@>=
v=new_graph->vertices;
yy[d+1]=0;@+sig[0]=n;
for (k=d;k>=0;k--) yy[k]=yy[k+1]+nn[k];
if (yy[0]>=n) {
k=0;@+xx[0]=(yy[1]>=n? 0: n-yy[1]);
while (1) {
@<Complete the partial solution $(x_0,\ldots,x_k)$@>;
@<Assign a symbolic name for $(x_0,\ldots,x_d)$ to vertex~|v|@>;
hash_in(v); /* enter |v->name| into the hash table
(via utility fields |u,v|) */
@<Create arcs or edges from previous points to~|v|@>;
v++;
@<Advance to the next partial solution $(x_0,\ldots,x_k)$, where |k| is
as large as possible; |goto last| if there are no more solutions@>;
}
}
last:@+if (v!=new_graph->vertices+new_graph->n)
panic(impossible); /* can't happen */
@ @<Complete the partial solution $(x_0,\ldots,x_k)$@>=
for (s=sig[k]-xx[k],k++;k<=d;s-=xx[k],k++) {
sig[k]=s;
if (s<=yy[k+1]) xx[k]=0;
else xx[k]=s-yy[k+1];
}
if (s!=0) panic(impossible+1) /* can't happen */
@ Here we seek the largest $k$ such that $x_k$ can be increased without
violating the necessary and sufficient conditions stated earlier.
@<Advance to the next partial solution $(x_0,\ldots,x_k)$...@>=
for (k=d-1;;k--) {
if (xx[k]<sig[k] && xx[k]<nn[k]) break;
if (k==0) goto last;
}
xx[k]++;
@ As in the |board| routine, we represent the sequence of coordinates
$(2,0,1)$ by the string `\.{2.0.1}'.
The string won't exceed |BUF_SIZE|, because the ratio |BUF_SIZE/MAX_D| is
plenty big.
The first three coordinate values, $(x_0,x_1,x_2)$, are placed into
utility fields |x|, |y|, and |z|, so that they can be accessed immediately
if an application needs them.
@<Assign a symbolic name for $(x_0,\ldots,x_d)$ to vertex~|v|@>=
{@+register char *p=buffer; /* string pointer */
for (k=0;k<=d;k++) {
sprintf(p,".%ld",xx[k]);
while (*p) p++;
}
v->name=gb_save_string(&buffer[1]); /* omit |buffer[0]|, which is |'.'| */
v->x.I=xx[0];@+v->y.I=xx[1];@+v->z.I=xx[2];
}
@ Since we are generating the vertices in lexicographic order of their
coordinates, it is easy to identify all adjacent vertices that
precede the current setting of $(x_0,x_1,\ldots,x_d)$. We locate them
via their symbolic names.
@<Create arcs or edges from previous points to~|v|@>=
for (j=0;j<d;j++)
if (xx[j]) {@+register Vertex *u; /* previous vertex adjacent to |v| */
xx[j]--;
for (k=j+1;k<=d;k++)
if (xx[k]<nn[k]) {@+register char *p=buffer; /* string pointer */
xx[k]++;
for (i=0;i<=d;i++) {
sprintf(p,".%ld",xx[i]);
while (*p) p++;
}
u=hash_out(&buffer[1]);
if (u==NULL) panic(impossible+2); /* can't happen */
if (directed) gb_new_arc(u,v,1L);
else gb_new_edge(u,v,1L);
xx[k]--;
}
xx[j]++;
}
@* Subset graphs. The subroutine call {\advance\thinmuskip 0mu plus 2mu
|subsets(n,n0,n1,n2,n3,n4,size_bits,directed)|}
creates a graph having the same vertices as
|simplex(n,n0,n1,n2,n3,n4,directed)| but with a quite different notion
of adjacency. In this we interpret a solution $(x_0,x_1,\ldots,x_d)$ to
the conditions $x_0+x_1+\cdots+x_d=n$ and $0\le x_j\le n_j$ not as a
position on a game board but as an $n$-element submultiset of the multiset
$\{n_0\cdot0,n_1\cdot 1,\ldots,n_d\cdot d\}$ that has $x_j$ elements
equal to~$j$. (If each $n_j=1$, the multiset is a set; this is an
important special case.) Two vertices are adjacent if and only if
their intersection has a cardinality that matches one of the bits in
|size_bits|, which is an unsigned integer. Each arc has length~1.
For example, suppose $n=3$ and |(n0,n1,n2,n3)=(2,2,2,0)|. Then the vertices
are the 3-element submultisets of $\{0,0,1,1,2,2\}$, namely
$$\{0,0,1\},\quad \{0,0,2\},\quad \{0,1,1\},\quad \{0,1,2\},\quad
\{0,2,2\},\quad \{1,1,2\},\quad \{1,2,2\},$$
which are represented by the respective vectors
$$(2,1,0),\quad (2,0,1),\quad (1,2,0),\quad (1,1,1),\quad
(1,0,2),\quad (0,2,1),\quad (0,1,2).$$
The intersection of multisets represented by $(x_0,x_1,\ldots,x_d)$ and
$(y_0,y_1,\ldots,y_d)$ is $$\bigl(\min(x_0,y_0),\min(x_1,y_1),\ldots,
\min(x_d,y_d)\bigr);$$ each element occurs as often as it occurs
in both multisets being intersected. If now |size_bits=3|, the
multisets will be considered adjacent whenever their
intersection contains exactly 0 or~1 elements, because $3=2^0+2^1$.
The vertices adjacent to $\{0,0,1\}$, for example, will be
$\{0,2,2\}$, $\{1,1,2\}$,
and $\{1,2,2\}$. In this case, every pair of submultisets
has a nonempty intersection, so the same graph would be obtained
if |size_bits=2|.
If |directed| is nonzero, the graph will have directed arcs, from |u|
to~|v| only if $u\le v$. Notice that the graph will have self-loops if
and only if the binary representation of |size_bits| contains the term
$2^n$, in which case there will be a loop from every vertex to itself.
(In an undirected graph, such loops are represented by two arcs.)
We define a macro |disjoint_subsets(n,k)| for the case
of $n\choose k$ vertices, adjacent if and only if they represent
disjoint $k$-subsets of an $n$-set.
One important special case is the Petersen graph, whose vertices
@^Petersen, Julius Peter Christian, graph@>
are the 2-element subsets of $\{0,1,2,3,4\}$, adjacent when they
are disjoint. This graph is remarkable because it contains 10 vertices,
each of degree~3, but it has no circuits of length less than~5.
@(gb_basic.h@>=
#define disjoint_subsets(n,k) @[subsets((long)(k),1L,(long)(1-(n)),0L,0L,0L,1L,0L)@]
#define petersen() @[disjoint_subsets(5,2)@]
@ @<Basic subroutines@>=
Graph *subsets(n,n0,n1,n2,n3,n4,size_bits,directed)
unsigned long n; /* the number of elements in the multiset */
long n0,n1,n2,n3,n4; /* multiplicities of elements */
unsigned long size_bits; /* intersection sizes that trigger arcs */
long directed; /* should the graph be directed? */
{@+@<Vanilla local variables@>@;@#
@<Normalize the simplex parameters@>;
@<Create a graph with one vertex for each subset@>;
@<Name the subsets and create the arcs or edges@>;
if (gb_trouble_code) {
gb_recycle(new_graph);
panic(alloc_fault); /* rats, we ran out of memory somewhere back there */
}
return new_graph;
}
@ @<Create a graph with one vertex for each subset@>=
@<Determine the number of feasible $(x_0,\ldots,x_d)$,
and allocate the graph@>;
sprintf(new_graph->id,"subsets(%lu,%ld,%ld,%ld,%ld,%ld,0x%lx,%d)",
n,n0,n1,n2,n3,n4,size_bits,directed?1:0);
strcpy(new_graph->util_types,"ZZZIIIZZZZZZZZ");
/* hash table will not be used */
@ We generate the vertices with exactly the logic used in |simplex|.
@<Name the subsets and create the arcs or edges@>=
v=new_graph->vertices;
yy[d+1]=0;@+sig[0]=n;
for (k=d;k>=0;k--) yy[k]=yy[k+1]+nn[k];
if (yy[0]>=n) {
k=0;@+xx[0]=(yy[1]>=n? 0: n-yy[1]);
while (1) {
@<Complete the partial solution $(x_0,\ldots,x_k)$@>;
@<Assign a symbolic name for $(x_0,\ldots,x_d)$ to vertex~|v|@>;
@<Create arcs or edges from previous subsets to~|v|@>;
v++;
@<Advance to the next partial solution $(x_0,\ldots,x_k)$, where |k| is
as large as possible; |goto last| if there are no more solutions@>;
}
}
last:@+if (v!=new_graph->vertices+new_graph->n)
panic(impossible); /* can't happen */
@ The only difference is that we generate the arcs or edges by brute
force, examining each pair of vertices to see if they are adjacent or not.
The code here is character-set dependent: It assumes that `\..' and null
have a character code less than `\.0', as in ASCII. It also assumes
that characters occupy exactly eight bits.
@^system dependencies@>
@d UL_BITS 8*sizeof(unsigned long) /* the number of bits in |size_bits| */
@<Create arcs or edges from previous subsets to~|v|@>=
{@+register Vertex *u;
for (u=new_graph->vertices;u<=v;u++) {@+register char *p=u->name;
long ss=0; /* the number of elements common to |u| and |v| */
for (j=0;j<=d;j++,p++) {
for (s=(*p++)-'0';*p>='0';p++) s=10*s+*p-'0'; /* |sscanf(p,"%ld",&s)| */
@^character-set dependencies@>
if (xx[j]<s) ss+=xx[j];
else ss+=s;
}
if (ss<UL_BITS && (size_bits&(((unsigned long)1)<<ss))) {
if (directed) gb_new_arc(u,v,1L);
else gb_new_edge(u,v,1L);
}
}
}
@* Permutation graphs. The subroutine call
|perms(n0,n1,n2,n3,n4,max_inv,directed)|
creates a graph whose vertices represent the permutations of a
multiset that have at most |max_inv| inversions. Two permutations are adjacent
in the graph if one is obtained from the other by interchanging two
adjacent elements. Each arc has length~1.
For example, the multiset $\{0,0,1,2\}$ has the following twelve permutations:
$$\vcenter{\halign{#&&\quad#\cr
0012,&0021,&0102,&0120,&0201,&0210,\cr
1002,&1020,&1200,&2001,&2010,&2100.\cr}}$$
The first of these, 0012, has two neighbors, 0021 and 0102.
The number of inversions is the number of pairs of elements $xy$ such
that $x>y$ and $x$ precedes $y$ from left to right, counting
multiplicity. For example, 2010 has four inversions, corresponding to
$xy\in\{20,21,20,10\}$. It is not difficult to verify that the number
of inversions of a permutation is the distance in the graph from that
permutation to the lexicographically first permutation.
Parameters |n0| through |n4| specify the composition of the multiset,
just as in the |subsets| routine.
Roughly speaking, there are |n0| elements equal to~0, |n1| elements
equal to~1, and so on. The multiset $\{0,0,1,2,3,3\}$, for example, would
be represented by |(n0,n1,n2,n3,n4)=(2,1,1,2,0)|.
Of course, we sometimes want to have multisets with more than five distinct
elements; when there are $d+1$ distinct elements, the multiset should have
$n_k$ elements equal to~$k$ and $n=n_0+n_1+\cdots+n_d$ elements in all.
Larger values of $d$ can be specified by using |-d| as a parameter:
If |n0=-d|, each multiplicity $n_k$ is taken to be~1; if |n0>0| and |n1=-d|,
each multiplicity $n_k$ is taken to be equal to~|n0|;
if |n0>0|, |n1>0|, and |n2=-d|, the multiplicities are alternately
$(|n0|,|n1|,|n0|,|n1|,|n0|,\ldots\,)$; if |n0>0|, |n1>0|, |n2>0|, and
|n3=-d|, the multiplicities are the first~|d+1| elements of the
periodic sequence $(|n0|,|n1|,|n2|,|n0|,|n1|,\ldots\,)$; and if all
but |n4| are positive, while |n4=-d|, the multiplicities again
are periodic.
An example like |(n0,n1,n2,n3,n4)=(1,2,3,4,-8)| is about as tricky
as you can get. It specifies the multiset $\{0,1,1,2,2,2,3,3,3,3,4,5,5,
6,6,6,7,7,7,7,8\}$.
If any of the multiplicity parameters is negative or zero, the
remaining multiplicities are ignored. For example, if |n2<=0|, the
subroutine does not look at |n3| or~|n4|.
You probably don't want to try |perms(n0,0,0,0,0,max_inv,directed)|
when |n0>0|, because a multiset with |n0| identical elements has only
one permutation.
The special case when you want all $n!$ permutations of an $n$-element set
can be obtained by calling |all_perms(n,directed)|.
@(gb_basic.h@>=
#define all_perms(n,directed) @[perms((long)(1-(n)),0L,0L,0L,0L,0L,\
(long)(directed))@]
@ If |max_inv=0|, all permutations will be considered, regardless of
the number of inversions. In that case the total number of vertices in
the graph will be the multinomial coefficient $${n\choose
n_0,n_1,\ldots,n_d}\,,\qquad n=n_0+n_1+\cdots+n_d.$$ The maximum
number of inversions in general is the number of inversions of the
lexicographically last permutation, namely ${n\choose2}-{n_0\choose2}-
{n_1\choose2}-\cdots-{n_d\choose2}=\sum_{0\le j<k\le d}n_jn_k$.
\vskip1pt
Notice that in the case $d=1$, we essentially obtain all combinations of
|n0+n1| elements taken |n1| at a time. The positions of the 1's correspond
to the elements of a subset or sample.
If |directed| is nonzero, the graph will contain only directed arcs
from permutations to neighboring permutations that have exactly one more
inversion. In this case the graph corresponds to a partial ordering that is a
lattice with interesting properties; see the article by Bennett and Birkhoff
@^Bennett, Mary Katherine@>
@^Birkhoff, Garrett@>
in {\sl Algebra Universalis\/ \bf32} (1994), 115--144.
@ The program for |perms| is very similar in structure to the program
for |simplex| already considered.
@<Basic subroutines@>=
Graph *perms(n0,n1,n2,n3,n4,max_inv,directed)
long n0,n1,n2,n3,n4; /* composition of the multiset */
unsigned long max_inv; /* maximum number of inversions */
long directed; /* should the graph be directed? */
{@+@<Vanilla local variables@>@;@+@q{@>@t}\6{@>@q}@>
register long n; /* total number of elements in multiset */
@<Normalize the permutation parameters@>;
@<Create a graph with one vertex for each permutation@>;
@<Name the permutations and create the arcs or edges@>;
if (gb_trouble_code) {
gb_recycle(new_graph);
panic(alloc_fault); /* shucks, we ran out of memory somewhere back there */
}
return new_graph;
}
@ @<Normalize the permutation parameters@>=
if (n0==0) {@+n0=1;@+n1=0;@+} /* convert the empty set into $\{0\}$ */
else if (n0<0) {@+n1=n0;@+n0=1;@+}
n=BUF_SIZE; /* this allows us to borrow code from |simplex|, already written */
@<Normalize the simplex parameters@>;
@<Determine |n| and the maximum possible number of inversions@>;
@ Here we want to set |max_inv| to the maximum possible number of
inversions, if the given value of |max_inv| is zero or
if it exceeds that maximum number.
@<Determine |n| and the maximum possible number of inversions@>=
{@+register long ss; /* max inversions known to be possible */
for (k=0,s=ss=0;k<=d;ss+=s*nn[k],s+=nn[k],k++)
if (nn[k]>=BUF_SIZE) panic(bad_specs);
/* too many elements in the multiset */
if (s>=BUF_SIZE) panic(bad_specs+1); /* too many elements in the multiset */
n=s;
if (max_inv==0 || max_inv>ss) max_inv=ss;
}
@ To determine the number of vertices, we sum the first |max_inv+1|
coefficients of a power series in which the coefficient of~$z^j$
is the number of permutations having $j$ inversions. It is known
[{\sl Sorting and Searching}, exercise 5.1.2--16] that this power series
is the ``$z$-multinomial coefficient''
$${n\choose n_0,\ldots,n_d}_{\!z}={n!_z\over n_0!_z\ldots n_d!_z}\,,
\qquad\hbox{where}\qquad m!_z=\prod_{k=1}^m{1-z^k\over 1-z}\,.$$
@<Create a graph with one vertex for each permutation@>=
{@+long nverts; /* the number of vertices */
register long *coef=gb_typed_alloc(max_inv+1,long,working_storage);
if (gb_trouble_code) panic(no_room+1); /* can't allocate |coef| array */
coef[0]=1;
for (j=1,s=nn[0];j<=d;s+=nn[j],j++)
@<Multiply the power series coefficients by
$\prod_{1\le k\le n_j}(1-z^{s+k})/(1-z^k)$@>;
for (k=1,nverts=1;k<=max_inv;k++) {
nverts+=coef[k];
if (nverts>1000000000) panic(very_bad_specs); /* way too big */
}
gb_free(working_storage); /* recycle the |coef| array */
new_graph=gb_new_graph(nverts);
if (new_graph==NULL)
panic(no_room); /* out of memory before we're even started */
sprintf(new_graph->id,"perms(%ld,%ld,%ld,%ld,%ld,%lu,%d)",
n0,n1,n2,n3,n4,max_inv,directed?1:0);
strcpy(new_graph->util_types,"VVZZZZZZZZZZZZ"); /* hash table will be used */
}
@ After multiplication by $(1-z^{k+s})/(1-z^k)$, the coefficients of the
power series will be nonnegative, because they are the coefficients of
a $z$-multinomial coefficient.
@<Multiply the power series coefficients by
$\prod_{1\le k\le n_j}(1-z^{s+k})/(1-z^k)$@>=
for (k=1;k<=nn[j];k++) {@+register long ii;
for (i=max_inv,ii=i-k-s;ii>=0;ii--,i--) coef[i]-=coef[ii];
for (i=k,ii=0;i<=max_inv;i++,ii++) {
coef[i]+=coef[ii];
if (coef[i]>1000000000) panic(very_bad_specs+1); /* way too big */
}
}
@ As we generate the permutations, we maintain a table $(y_1,\ldots,y_n)$,
where $y_k$ is the number of
inversions whose first element is the $k$th element of the multiset.
For example, if the multiset is $\{0,0,1,2\}$ and the current permutation is
$(2,0,1,0)$, the inversion table is $(y_1,y_2,y_3,y_4)=(0,0,1,3)$. Clearly
$0\le y_k<k$, and $y_k\le y_{k-1}$ when the $k$th element of the multiset
is the same as the $(k-1)$st element. These conditions are necessary
and sufficient to define a valid inversion table. We will generate
permutations in lexicographic order of their inversion tables.
For convenience, we set up another array~|z|, which holds the
initial inversion-free permutation.
@<Name the permutations and create the arcs or edges@>=
{@+register long *xtab,*ytab,*ztab; /* permutations and their inversions */
long m=0; /* current number of inversions */
@<Initialize |xtab|, |ytab|, and |ztab|@>;
v=new_graph->vertices;
while (1) {
@<Assign a symbolic name for $(x_1,\ldots,x_n)$ to vertex~|v|@>;
@<Create arcs or edges from previous permutations to~|v|@>;
v++;
@<Advance to the next perm; |goto last| if there are no more solutions@>;
}
last:@+if (v!=new_graph->vertices+new_graph->n)
panic(impossible); /* can't happen */
gb_free(working_storage);
}
@ @<Initialize |xtab|, |ytab|, and |ztab|@>=
xtab=gb_typed_alloc(3*n+3,long,working_storage);
if (gb_trouble_code) { /* can't allocate |xtab| */
gb_recycle(new_graph);@+panic(no_room+2);@+}
ytab=xtab+(n+1);