gb_books.w
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:23k
源码类别:

通讯编程

开发平台:

Visual C++

  1. % This file is part of the Stanford GraphBase (c) Stanford University 1993
  2. @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
  3. @i gb_types.w
  4. deftitle{GB_,BOOKS}
  5. def<#1>{hbox{$langle$rm#1$rangle$}}
  6. prerequisites{GB_,GRAPH}{GB_,IO}
  7. @* Introduction. This GraphBase module contains the |book|
  8. subroutine, which creates a family of undirected graphs that are based on
  9. classic works of literature.  It also contains the |bi_book|
  10. subroutine, which creates a related family of bipartite graphs.
  11. An example of the use of |book| can be found in the demonstration
  12. program {sc BOOK_kern.05emCOMPONENTS}.
  13. @(gb_books.h@>=
  14. extern Graph *book();
  15. extern Graph *bi_book();
  16. @ The subroutine call |book(@[@t<title>@>@],n,x,first_chapter,last_chapter,
  17. in_weight,out_weight,seed)|
  18. constructs a graph based on the information in <title>.{.dat},
  19. where <title> is either .{"anna"} (for {sl Anna Karenina/}),
  20. .{"david"} (for {sl David Copperfield/}),
  21. .{"jean"} (for {sl Les Mis'erables/}),
  22. .{"huck"} (for {sl Huckleberry Finn/}), or
  23. .{"homer"} (for {sl The Iliad/}).
  24. Each vertex of the graph corresponds to one of the characters in the
  25. selected book. Edges between vertices correspond to encounters between
  26. those characters. The length of each edge is~1.
  27. Subsets of the book can be selected by specifying that the edge data should be
  28. restricted to chapters between |first_chapter| and |last_chapter|,
  29. inclusive. If |first_chapter=0|, the result is the same as if
  30. |first_chapter=1|. If |last_chapter=0| or if |last_chapter| exceeds
  31. the total number of chapters in the book, the result is the same as
  32. if |last_chapter| were the number of the book's final chapter.
  33. The constructed graph will have $min(n,N)-x$ vertices, where $N$ is the
  34. total number of characters in the selected book.
  35. However, if |n| is zero, |n| is automatically made equal to the maximum
  36. possible value,~$N$. If |n| is less than~$N$, the |n-x| characters will be
  37. selected by assigning  a weight to each character and choosing the |n| with
  38. largest weight, then excluding the largest~|x| of these,
  39. using random numbers to break ties in case of equal weights.
  40. Weights are computed by the formula
  41. $$ |in_weight|cdot\{chapters_in}+|out_weight|cdot\{chapters_out}, $$
  42. where \{chapters_in} is the number of chapters between |first_chapter|
  43. and |last_chapter| in which a particular character appears, and
  44. \{chapters_out} is the number of other chapters in which that
  45. character appears. Both |in_weight| and |out_weight| must be at most
  46. 1,000,000 in absolute value.
  47. Vertices of the graph will appear in order of decreasing weight.
  48. The |seed| parameter defines the pseudo-random numbers used wherever
  49. a ``random'' choice between equal-weight vertices needs to be made.
  50. As usual with GraphBase routines, different choices of |seed|
  51. will in general produce different selections,
  52. but in a system-independent manner; identical results will be obtained on
  53. all computers when identical parameters have been specified.
  54. Any |seed| value between 0 and $2^{31}-1$ is permissible.
  55. @ Examples: The call |book("anna",0,0,0,0,0,0,0)| will construct a
  56. graph on 138 vertices that represent all 138 characters of Tolstoy's
  57. {sl Anna Karenina/}, as recorded in .{anna.dat}. Two vertices will
  58. be adjacent if the corresponding characters
  59. encounter each other anywhere in the book. The call
  60. |book("anna",50,0,0,0,1,1,0)| is similar, but it is restricted to
  61. the 50 characters that occur most frequently, i.e., in the most chapters.
  62. The call |book("anna",50,0,10,120,1,1,0)| has the same vertices, but it
  63. has edges only for encounters that take place between chapter~10
  64. and chapter~120, inclusive. The call |book("anna",50,0,10,120,1,0,0)| is
  65. similar, but its vertices are the 50 characters that occur most often in
  66. chapters 10 through~120, without regard to how often they occur in
  67. the rest of the book. The call |book("anna",50,0,10,120,0,0,0)| is
  68. also similar, but it chooses 50 characters completely at random
  69. (possibly from those that don't occur in the selected chapters at all).
  70. Parameter |x|, which causes the |x| vertices of highest weight to be
  71. excluded, is usually either 0 or~1. It is provided primarily so that
  72. users can set |x=1| with respect to {sl David Copperfield/} and {sl
  73. Huckleberry Finn}; those novels are narrated by their principal
  74. character, so they have edges between the principal character and
  75. almost everybody else. (Characters cannot get into the action of a
  76. first-person account unless they encounter the narrator or unless the
  77. narrator is quoting some other person's story.) The corresponding
  78. graphs tend to have more interesting connectivity properties if we
  79. leave the narrator out by setting |x=1|. For example, there are 87
  80. characters in {sl David Copperfield/}; the call
  81. |book("david",0,1,0,0,1,1,0)| produces a graph with 86 vertices, one
  82. for every character except David Copperfield himself.
  83. @ The subroutine call |bi_book(@[@t<title>@>@],n,x,first_chapter,last_chapter,
  84. in_weight,out_weight,seed)| produces a bipartite graph in which the
  85. vertices of the first part are exactly the same as the vertices of the
  86. graph returned by |book|, while the vertices of the second part are
  87. the selected chapters. For example,
  88. $|bi_book|(|"anna"|,allowbreak 50,0,10,120,1,1,0)$
  89. creates a bipartite graph with $50+111$ vertices. There is an edge between
  90. each character and the chapters in which that character appears.
  91. @ Chapter numbering needs further explanation. {sl Anna Karenina/}
  92. has 239 chapters, which are numbered 1.1 through 8.19 in the
  93. work itself but renumbered 1 through 239 as far as the |book| routine
  94. is concerned. Thus, setting |first_chapter=10| and |last_chapter=120|
  95. turns out to be equivalent to selecting chapters 1.10 through 4.19
  96. (more precisely, chapter~10 of book~1 through chapter~19 of book~4).
  97. {sl Les Mis'erables/} has an even more involved scheme; its
  98. 356 chapters range from 1.1.1 (part~1, book~1, chapter~1) to
  99. 5.9.6 (part~5, book~9, chapter~6). After |book| or |bi_book| has created
  100. a graph, the external integer variable |chapters| will contain the total
  101. number of chapters, and |chap_name| will be an array of strings
  102. containing the structured chapter numbers. For example, after
  103. |book("jean",@[@tdots@>@])|, we will have |chapters=356|,
  104. |chap_name[1]="1.1.1"|, dots, |chap_name[356]="5.9.6"|;
  105. |chap_name[0]| will be~|""|.
  106. @d MAX_CHAPS 360 /* no book will have this many chapters */
  107. @<External variables@>=
  108. long chapters; /* the total number of chapters in the selected book */
  109. char *chap_name[MAX_CHAPS]={""}; /* string names of those chapters */
  110. @ As usual, we put declarations of the external variables into the header file
  111. for users to {bf include}.
  112. @(gb_books.h@>=
  113. extern long chapters; /* the total number of chapters in the selected book */
  114. extern char *chap_name[]; /* string names of those chapters */
  115. @ If the |book| or |bi_book| routine encounters a problem, it
  116. returns |NULL| (.{NULL}),
  117. after putting a code number into the external variable
  118. |panic_code|. This code number identifies the type of failure.
  119. Otherwise |book| returns a pointer to the newly created graph, which
  120. will be represented with the data structures explained in {sc GB_,GRAPH}.
  121. (The external variable |panic_code| is itself defined in {sc GB_,GRAPH}.)
  122. @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+}
  123. @#
  124. @f node long /* the &{node} type is defined below */
  125. @ The CEE/ file .{gb_books.c} has the overall shape shown here.
  126. It makes use of an internal subroutine
  127. called |bgraph|, which combines the work of |book| and |bi_book|.
  128. @p
  129. #include "gb_io.h" /* we will use the {sc GB_,IO} routines for input */
  130. #include "gb_flip.h" /* we will use the {sc GB_,FLIP} routines
  131.                         for random numbers */
  132. #include "gb_graph.h" /* we will use the {sc GB_,GRAPH} data structures */
  133. #include "gb_sort.h" /* and the |gb_linksort| routine */
  134. @h@#
  135. @<Type declarations@>@;
  136. @<Private variables@>@;
  137. @<External variables@>@;
  138. @#
  139. static Graph *bgraph(bipartite,
  140.     title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed)
  141.   long bipartite; /* should we make the graph bipartite? */
  142.   char *title; /* identification of the selected book */
  143.   unsigned long n; /* number of vertices desired before exclusion */
  144.   unsigned long x; /* number of vertices to exclude */
  145.   unsigned long first_chapter, last_chapter;
  146.     /* interval of chapters leading to edges */
  147.   long in_weight; /* weight coefficient pertaining to chapters
  148.                           in that interval */
  149.   long out_weight; /* weight coefficient pertaining to chapters
  150.                           not in that interval */
  151.   long seed; /* random number seed */
  152. {@+@<Local variables@>@;@#
  153.   gb_init_rand(seed);
  154.   @<Check that the parameters are valid@>;
  155.   @<Skim the data file, recording the characters and computing their
  156.     statistics@>;
  157.   @<Choose the vertices and put them into an empty graph@>;
  158.   @<Read the data file more carefully and fill the graph as instructed@>;
  159.   if (gb_trouble_code) {
  160.     gb_recycle(new_graph);
  161.     panic(alloc_fault); /* (expletive deleted)
  162.                      we ran out of memory somewhere back there */
  163.   }
  164.   return new_graph;
  165. }
  166. @#
  167. Graph *book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed)
  168.   char *title;
  169.   unsigned long n, x, first_chapter, last_chapter;
  170.   long in_weight,out_weight,seed;
  171. {@+return bgraph(0L,title,n,x,first_chapter,last_chapter,
  172.   in_weight,out_weight,seed);@+}
  173. Graph *bi_book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed)
  174.   char *title;
  175.   unsigned long n, x, first_chapter, last_chapter;
  176.   long in_weight,out_weight,seed;
  177. {@+return bgraph(1L,title,n,x,first_chapter,last_chapter,
  178.     in_weight,out_weight,seed);@+}
  179. @ @<Local var...@>=
  180. Graph *new_graph; /* the graph constructed by |book| or |bi_book| */
  181. register long j,k; /* all-purpose indices */
  182. long characters; /* the total number of characters in the selected book */
  183. register node *p; /* information about the current character */
  184. @ @d MAX_CHARS 600 /* there won't be more characters than this */
  185. @<Check that the parameters are valid@>=
  186. if (n==0) n=MAX_CHARS;
  187. if (first_chapter==0) first_chapter=1;
  188. if (last_chapter==0) last_chapter=MAX_CHAPS;
  189. if (in_weight>1000000 || in_weight<-1000000 ||
  190.      out_weight>1000000 || out_weight<-1000000)
  191.   panic(bad_specs); /* the magnitude of at least one weight is too big */
  192. sprintf(file_name,"%.6s.dat",title);
  193. if (gb_open(file_name)!=0)
  194.   panic(early_data_fault); /* couldn't open the file; |io_errors| tells why */
  195. @ @<Priv...@>=
  196. static char file_name[]="xxxxxx.dat";
  197. @*Vertices.
  198. Each character in a book has been given a two-letter code name for
  199. internal use. The code names are explained at the beginning of each
  200. data file by a number of lines that look like this:
  201. $$hbox{tt XX <name>,<description>}$$
  202. For example, here's one of the lines near the beginning of |"anna.dat"|:
  203. $$hbox{tt AL Alexey Alexandrovitch Karenin, minister of state}$$
  204. The <name> does not contain a comma; the <description> might.
  205. A blank line follows the cast of characters.
  206. Internally, we will think of the two-letter code as a radix-36 integer.
  207. Thus .{AA} will be the number $10times36+10$, and .{ZZ} will be
  208. $35times36+35$. The |gb_number| routine in {sc GB_,IO} is set up to
  209. input radix-36 integers just as it does hexadecimal ones.
  210. In {sl The Iliad}, many of the minor characters have numeric digits
  211. in their code names because the total number of characters is too
  212. large to permit mnemonic codes for everybody.
  213. @d MAX_CODE 1296 /* $36times36$, the number of two-digit codes in radix 36 */
  214. @ In order to choose the vertices, we want to represent each character
  215. as a node whose key corresponds to its weight; then the |gb_linksort|
  216. routine of {sc GB_,SORT} will provide the desired rank-ordering. We will
  217. find it convenient to use these nodes for all the data processing that
  218. |bgraph| has to do.
  219. @<Type dec...@>=
  220. typedef struct node_struct { /* records to be sorted by |gb_linksort| */
  221.   long key; /* the nonnegative sort key (weight plus $2^{30}$) */
  222.   struct node_struct *link; /* pointer to next record */
  223.   long code; /* code number of this character */
  224.   long in; /* number of occurrences in selected chapters */
  225.   long out; /* number of occurrences in unselected chapters */
  226.   long chap; /* seen most recently in this chapter */
  227.   Vertex *vert; /* vertex corresponding to this character */
  228. } node;
  229. @ Not only do nodes point to codes, we also want codes to point to nodes.
  230. @<Priv...@>=
  231. static node node_block[MAX_CHARS]; /* array of nodes for working storage */
  232. static node *xnode[MAX_CODE]; /* the node, if any, having a given code */
  233. @ We will read the data file twice, once quickly (to collect statistics)
  234. and once more thoroughly (to record detailed information). Here is the
  235. quick version.
  236. @<Skim the data file, recording the characters...@>=
  237. @<Read the character codes at the beginning of the data file, and
  238.   prepare a node for each one@>;
  239. @<Skim the chapter information, counting the number of chapters in
  240.   which each character appears@>;
  241. if (gb_close()!=0)
  242.   panic(late_data_fault);
  243.     /* checksum or other failure in data file; see |io_errors| */
  244. @ @<Read the character codes...@>=
  245. for (k=0;k<MAX_CODE;k++) xnode[k]=NULL;
  246. {@+register long c; /* current code entering the system */
  247.   p=node_block; /* current node entering the system */
  248.   while ((c=gb_number(36))!=0) { /* note that .{00} is not a legal code */
  249.     if (c>=MAX_CODE || gb_char()!=' ') panic(syntax_error);
  250.                                      /* unreadable line in data file */
  251.     if (p>=&node_block[MAX_CHARS])
  252.       panic(syntax_error+1); /* data has too many characters */
  253.     p->link=(p==node_block?NULL:p-1);
  254.     p->code=c;
  255.     xnode[c]=p;
  256.     p->in=p->out=p->chap=0;
  257.     p->vert=NULL;
  258.     p++;
  259.     gb_newline();
  260.   }
  261.   characters=p-node_block;
  262.   gb_newline(); /* bypass the blank line that terminates the character data */
  263. }
  264. @ Later we will read through this part of the file again, extracting
  265. additional information if it turns out to be relevant. The
  266. <description> string is provided to users in a |desc| field,
  267. in case anybody cares to look at it. The |in| and |out| statistics
  268. are also made available in utility fields called |in_count| and |out_count|.
  269. The code value is placed in the |short_code| field.
  270. @d desc z.S /* utility field |z| points to the <description> string */
  271. @d in_count y.I /* utility field |y| counts appearances in selected chapters */
  272. @d out_count x.I /* utility field |x| counts appearances in other chapters */
  273. @d short_code u.I /* utility field |u| contains a radix-36 number */
  274. @<Read the data about characters again, noting vertex names and the
  275.   associated descriptions@>=
  276. {@+register long c; /* current code entering the system a second time */
  277.   while ((c=gb_number(36))!=0) {@+register Vertex *v=xnode[c]->vert;
  278.     if (v) {
  279.       if (gb_char()!=' ') panic(impossible); /* can't happen */
  280.       gb_string(str_buf,','); /* scan the <name> part */
  281.       v->name=gb_save_string(str_buf);
  282.       if (gb_char()!=',')
  283.         panic(syntax_error+2); /* missing comma after <name> */
  284.       if (gb_char()!=' ')
  285.         panic(syntax_error+3); /* missing space after comma */
  286.       gb_string(str_buf,'n'); /* scan the <description> part */
  287.       v->desc=gb_save_string(str_buf);
  288.       v->in_count=xnode[c]->in;
  289.       v->out_count=xnode[c]->out;
  290.       v->short_code=c;
  291.     }
  292.     gb_newline();
  293.   }
  294.   gb_newline(); /* bypass the blank line that terminates the character data */
  295. }  
  296. @ @(gb_books.h@>=
  297. #define desc @tquad@> z.S /* utility field definitions for the header file */
  298. #define in_count @tquad@> y.I
  299. #define out_count @tquad@> x.I
  300. #define short_code @tquad@> u.I
  301. @*Edges.
  302. The second part of the data file has a line for each chapter, containing
  303. ``cliques of encounters.'' For example, the line
  304. $$hbox{tt3.22:AA,BB,CC,DD;CC,DD,EE;AA,FF}$$
  305. means that, in chapter 22 of book 3, there were encounters between the pairs
  306. $$def\{{rm,} }
  307. hbox{tt AA-BB\AA-CC\AA-DD\BB-CC\BB-DD\CC-DD\CC-EE\DD-EE\{rm and }%
  308. AA-FFrm.}$$
  309. (The encounter .{CC-DD} is specified twice, once in the clique
  310. .{AA,BB,CC,DD} and once in .{CC,DD,EE}; this does not imply anything about
  311. the actual number of encounters between .{CC} and .{DD} in the chapter.)
  312. A clique might involve one character only, when that character is featured
  313. in sort of a soliloquy.
  314. A chapter might contain no references to characters at all. In such a case
  315. the `.:' following the chapter number is omitted.
  316. There might be more encounters than will fit on a single line. In such cases,
  317. continuation lines begin with `.{&:}'. This convention turns out to be
  318. needed only in .{homer.dat}; chapters in {sl The Iliad/} are
  319. substantially more complex than the chapters in other GraphBase books.
  320. On our first pass over the data, we simply want to compute statistics about
  321. who appears in what chapters, so we ignore the distinction between
  322. commas and semicolons.
  323. @<Skim the chapter information, counting the number of chapters in
  324.   which each character appears@>=
  325. for (k=1; k<MAX_CHAPS && !gb_eof(); k++) {
  326.   gb_string(str_buf,':'); /* read past the chapter number */
  327.   if (str_buf[0]=='&') k--; /* continuation of previous chapter */
  328.   while (gb_char()!='n') {@+register long c=gb_number(36);
  329.     if (c>=MAX_CODE)
  330.       panic(syntax_error+4); /* missing punctuation between characters */
  331.     p=xnode[c];
  332.     if (p==NULL) panic(syntax_error+5); /* unknown character */
  333.     if (p->chap!=k) {
  334.       p->chap=k;
  335.       if (k>=first_chapter && k<=last_chapter) p->in++;
  336.       else p->out++;
  337.     }
  338.   }
  339.   gb_newline();
  340. }
  341. if (k==MAX_CHAPS) panic(syntax_error+6); /* too many chapters */
  342. chapters=k-1;
  343. @ Our second pass over the data is very similar to the first, if we
  344. are simply computing a bipartite graph. In that case we add an edge
  345. to the graph between each selected chapter and each selected character
  346. in that chapter. Local variable |chap_base| will point to a
  347. vertex such that |chap_base+k| is the vertex corresponding to chapter~|k|.
  348. The |in_count| of a chapter vertex is the degree of that vertex, i.e., the
  349. number of selected characters that appear in the corresponding chapter.
  350. The |out_count| is the number of characters that appear in the
  351. chapter but were omitted from the graph. Thus the |in_count| and
  352. |out_count| for chapters are analogous to the |in_count| and |out_count|
  353. for characters.
  354. @<Read the chapter information a second time and create the
  355.   appropriate bipartite edges@>=
  356. {
  357.   for (p=node_block;p<node_block+characters;p++) p->chap=0;
  358.   for (k=1; !gb_eof(); k++) {
  359.     gb_string(str_buf,':'); /* read the chapter number */
  360.     if (str_buf[0]=='&') k--;
  361.     else chap_name[k]=gb_save_string(str_buf);
  362.     if (k>=first_chapter && k<=last_chapter) {@+register Vertex *u=chap_base+k;
  363.       if (str_buf[0]!='&') {
  364.         u->name=chap_name[k];
  365.         u->desc=null_string;
  366.         u->in_count=u->out_count=0;
  367.       }
  368.       while (gb_char()!='n') {@+register long c=gb_number(36);
  369.         p=xnode[c];
  370.         if (p->chap!=k) {@+register Vertex *v=p->vert;
  371.           p->chap=k;
  372.           if (v) {@+
  373.             gb_new_edge(v,u,1L);
  374.             u->in_count++;
  375.           }@+else u->out_count++;
  376.         }
  377.       }
  378.     }
  379.     gb_newline();
  380.   }
  381. }
  382. @ @<Local variables@>=
  383. Vertex *chap_base;
  384.   /* the bipartite vertex for chapter~|k| is |chap_base+k| */
  385. @ The second pass has to work a little harder when we are recording
  386. encounters from cliques, but the logic isn't difficult really.
  387. We insert a reference to the first chapter that generated each edge, in
  388. utility field |chap_no| of the corresponding |Arc| record.
  389. @d chap_no a.I /* utility field |a| holds a chapter number */
  390. @<Read the chapter information a second time and create the
  391.   appropriate edges for encounters@>=
  392. for (k=1; !gb_eof(); k++) {@+char *s;
  393.   s=gb_string(str_buf,':'); /* read the chapter number */
  394.   if (str_buf[0]=='&') k--;
  395.   else {@+if (*(s-2)=='n') *(s-2)='';
  396.     chap_name[k]=gb_save_string(str_buf);
  397.   }
  398.   if (k>=first_chapter && k<=last_chapter) {@+register long c=gb_char();
  399.     while (c!='n') {@+register Vertex **pp=clique_table;
  400.       register Vertex **qq,**rr; /* pointers within the clique table */
  401.       do@+{@+
  402.         c=gb_number(36); /* set |c| to code for next character of clique */
  403.         if (xnode[c]->vert) /* is that character a selected vertex? */
  404.           *pp++=xnode[c]->vert;
  405.             /* if so, that vertex joins the current clique */
  406.         c=gb_char();
  407.       }@+while (c==','); /* repeat until end of the clique */
  408.       for (qq=clique_table;qq+1<pp;qq++)
  409.         for (rr=qq+1;rr<pp;rr++)
  410.           @<Make the vertices |*qq| and |*rr| adjacent,
  411.               if they aren't already@>;
  412.     }
  413.   }
  414.   gb_newline();
  415. }
  416. @ @(gb_books.h@>=
  417. #define chap_no @[a.I@] /* utility field definition in the header file */
  418. @ @<Priv...@>=
  419. static Vertex *clique_table[30];
  420.  /* pointers to vertices in the current clique */
  421. @ @<Make the vertices |*qq| and |*rr| adjacent...@>=
  422. {@+register Vertex *u=*qq, *v=*rr;
  423.   register Arc *a;
  424.   for (a=u->arcs; a; a=a->next)
  425.     if (a->tip==v) goto found;
  426.   gb_new_edge(u,v,1L); /* not found, so they weren't already adjacent */
  427.   if (u<v) a=u->arcs;
  428.   else a=v->arcs; /* the new edge consists of arcs |a| and |a+1| */
  429.   a->chap_no=(a+1)->chap_no=k;
  430. found:;
  431. }
  432. @*Administration.
  433. The program is now complete except for a few missing organizational details.
  434. I will add these after lunch.
  435. @^out to lunch@>
  436. @ OK, I'm back; what needs to be done? The main thing is to create
  437. the graph itself.
  438. @<Choose the vertices and put them into an empty graph@>=
  439. if (n>characters) n=characters;
  440. if (x>n) x=n;
  441. if (last_chapter>chapters) last_chapter=chapters;
  442. if (first_chapter>last_chapter) first_chapter=last_chapter+1;
  443. new_graph=gb_new_graph(n-x+(bipartite?last_chapter-first_chapter+1:0));
  444. if (new_graph==NULL) panic(no_room); /* out of memory already */
  445. strcpy(new_graph->util_types,"IZZIISIZZZZZZZ");
  446.               /* declare the types of utility fields */
  447. sprintf(new_graph->id,"%sbook("%s",%lu,%lu,%lu,%lu,%ld,%ld,%ld)",
  448.   bipartite?"bi_":"",title,n,x,first_chapter,last_chapter,
  449.   in_weight,out_weight,seed);
  450. if (bipartite) {
  451.   mark_bipartite(new_graph,n-x);
  452.   chap_base=new_graph->vertices+(new_graph->n_1-first_chapter);
  453. }
  454. @<Compute the weights and assign vertices to chosen nodes@>;
  455. @ @<Compute the weights and assign vertices to chosen nodes@>=
  456. for (p=node_block; p<node_block+characters; p++)
  457.   p->key=in_weight*(p->in)+out_weight*(p->out)+0x40000000;
  458. gb_linksort(node_block+characters-1);
  459. k=n; /* we will look at this many nodes */
  460. {@+register Vertex *v=new_graph->vertices; /* the next vertex to define */
  461.   for (j=127; j>=0; j--)
  462.     for (p=(node*)gb_sorted[j]; p; p=p->link) {
  463.       if (x>0) x--; /* ignore this node */
  464.       else p->vert=v++; /* choose this node */
  465.       if (--k==0) goto done;
  466.     }
  467. }
  468. done:;
  469. @ Once the graph is there, we're ready to fill it in.
  470. @<Read the data file more carefully and fill the graph as instructed@>=
  471. if (gb_open(file_name)!=0)
  472.   panic(impossible+1);
  473.     /* this can't happen, because we were successful before */
  474. @<Read the data about characters again, noting vertex names and the
  475.   associated descriptions@>;
  476. if (bipartite)
  477.   @<Read the chapter information a second time and create the
  478.     appropriate bipartite edges@>@;
  479. else @<Read the chapter information a second time and create the
  480.   appropriate edges for encounters@>;
  481. if (gb_close()!=0)
  482.   panic(impossible+2); /* again, can hardly happen the second time around */
  483. @* Index. As usual, we close with an index that
  484. shows where the identifiers of \{gb_books} are defined and used.